size_t DfpnSolver::MID(const DfpnBounds& maxBounds, DfpnHistory& history)
{
    maxBounds.CheckConsistency();
    SG_ASSERT(maxBounds.phi > 1);
    SG_ASSERT(maxBounds.delta > 1);

    ++m_numMIDcalls;
    size_t prevWork = 0;
    SgEmptyBlackWhite colorToMove = GetColorToMove();

    DfpnData data;
    if (TTRead(data)) 
    {
        prevWork = data.m_work;
        if (! maxBounds.GreaterThan(data.m_bounds))
            // Estimated bounds are larger than we had
            // anticipated. The calling state must have computed
            // the max bounds with out of date information, so just
            // return here without doing anything: the caller will
            // now update to this new info and carry on.
            return 0;
    }
    else
    {
        SgEmptyBlackWhite winner = SG_EMPTY;
        if (TerminalState(colorToMove, winner))
        {
            ++m_numTerminal;
            DfpnBounds terminal;
            if (colorToMove == winner)
                DfpnBounds::SetToWinning(terminal);
            else
            {
                SG_ASSERT(SgOppBW(colorToMove) == winner);
                DfpnBounds::SetToLosing(terminal);
            }
            TTWrite(DfpnData(terminal, SG_NULLMOVE, 1));
            return 1;
        }
    }
    
    ++m_generateMoves;
    DfpnChildren children;
    GenerateChildren(children.Children());

    // Not thread safe: perhaps move into while loop below later...
    std::vector<DfpnData> childrenData(children.Size());
    for (size_t i = 0; i < children.Size(); ++i)
        LookupData(childrenData[i], children, i);
    // Index used for progressive widening
    size_t maxChildIndex = ComputeMaxChildIndex(childrenData);

    SgHashCode currentHash = Hash();
    SgMove bestMove = SG_NULLMOVE;
    DfpnBounds currentBounds;
    size_t localWork = 1;
    do
    {
        UpdateBounds(currentBounds, childrenData, maxChildIndex);
        if (! maxBounds.GreaterThan(currentBounds))
            break;

        // Select most proving child
        std::size_t bestIndex = 999999;
        DfpnBoundType delta2 = DfpnBounds::INFTY;
        SelectChild(bestIndex, delta2, childrenData, maxChildIndex);
        bestMove = children.MoveAt(bestIndex);

        // Compute maximum bound for child
        const DfpnBounds childBounds(childrenData[bestIndex].m_bounds);
        DfpnBounds childMaxBounds;
        childMaxBounds.phi = maxBounds.delta 
            - (currentBounds.delta - childBounds.phi);
        childMaxBounds.delta = delta2 == DfpnBounds::INFTY ? maxBounds.phi :
            std::min(maxBounds.phi,
                     std::max(delta2 + 1, DfpnBoundType(delta2 * (1.0 + m_epsilon))));
        SG_ASSERT(childMaxBounds.GreaterThan(childBounds));
        if (delta2 != DfpnBounds::INFTY)
            m_deltaIncrease.Add(float(childMaxBounds.delta-childBounds.delta));

        // Recurse on best child
        PlayMove(bestMove);
        history.Push(bestMove, currentHash);
        localWork += MID(childMaxBounds, history);
        history.Pop();
        UndoMove();

        // Update bounds for best child
        LookupData(childrenData[bestIndex], children, bestIndex);

        // Compute some stats when find winning move
        if (childrenData[bestIndex].m_bounds.IsLosing())
        {
            m_moveOrderingIndex.Add(float(bestIndex));
            m_moveOrderingPercent.Add(float(bestIndex) 
                                      / (float)childrenData.size());
            m_totalWastedWork += prevWork + localWork
                - childrenData[bestIndex].m_work;
        }
        else if (childrenData[bestIndex].m_bounds.IsWinning())
            maxChildIndex = ComputeMaxChildIndex(childrenData);

    } while (! CheckAbort());

    // Find the most delaying move for losing states, and the smallest
    // winning move for winning states.
    if (currentBounds.IsSolved())
    {
        if (currentBounds.IsLosing())
        {
            std::size_t maxWork = 0;
            for (std::size_t i = 0; i < children.Size(); ++i)
            {
                if (childrenData[i].m_work > maxWork)
                {
                    maxWork = childrenData[i].m_work;
                    bestMove = children.MoveAt(i);
                }
            }
        }
        else
        {
            std::size_t minWork = DfpnBounds::INFTY;
            for (std::size_t i = 0; i < children.Size(); ++i)
            {
                if (childrenData[i].m_bounds.IsLosing() 
                    && childrenData[i].m_work < minWork)
                {
                    minWork = childrenData[i].m_work;
                    bestMove = children.MoveAt(i);
                }
            }
        }
    }
    
    // Store search results
    TTWrite(DfpnData(currentBounds, bestMove, localWork + prevWork));
    return localWork;
}
size_t DfpnSolver::MID(const DfpnBounds& maxBounds, DfpnHistory& history)
{
    maxBounds.CheckConsistency();
    HexAssert(maxBounds.phi > 1);
    HexAssert(maxBounds.delta > 1);

    int depth = history.Depth();
    size_t prevWork = 0;
    bitset_t maxProofSet;
    float evaluationScore;
    HexColor colorToMove = m_state->ToPlay();
    DfpnChildren children;
    {
        DfpnData data;
        if (TTRead(*m_state, data)) 
        {
            children = data.m_children;
            maxProofSet = data.m_maxProofSet;
            prevWork = data.m_work;
            evaluationScore = data.m_evaluationScore;
            if (!maxBounds.GreaterThan(data.m_bounds))
                // Estimated bounds are larger than we had
                // anticipated. The calling state must have computed
                // the max bounds with out of date information, so just
                // return here without doing anything: the caller will
                // now update to this new info and carry on.
                return 0;
        }
        else
        {
            m_workBoard->GetPosition().SetPosition(m_state->Position());
            m_workBoard->ComputeAll(colorToMove);
            ++m_numVCbuilds;

            // Compute the maximum possible proof set if colorToMove wins.
            // This data is used to prune siblings of this state.
            maxProofSet = ProofUtil::MaximumProofSet(*m_workBoard, colorToMove);

            if (EndgameUtils::IsDeterminedState(*m_workBoard, colorToMove))
            {
                ++m_numTerminal;
                DfpnBounds terminal;
                if (EndgameUtils::IsWonGame(*m_workBoard, colorToMove))
                    DfpnBounds::SetToWinning(terminal);
                else 
                    DfpnBounds::SetToLosing(terminal);
                
                if (m_useGuiFx && depth == 1)
                {
                    m_guiFx.UpdateCurrentBounds(terminal);
                    m_guiFx.Write();
                }
                TTWrite(*m_state, DfpnData(terminal, DfpnChildren(), 
                                  INVALID_POINT, 1, maxProofSet, 0.0));
                return 1;
            }
            bitset_t childrenBitset 
                = EndgameUtils::MovesToConsider(*m_workBoard, colorToMove);

            m_considerSetSize.Add(childrenBitset.count());
            Resistance resist;
            resist.Evaluate(*m_workBoard);
            evaluationScore = (colorToMove == BLACK) 
                ? resist.Score() : -resist.Score();
            m_allEvaluation.Add(evaluationScore);
            std::vector<std::pair<HexEval, HexPoint> > mvsc;
            for (BitsetIterator it(childrenBitset); it; ++it) 
            {
                HexEval score = resist.Score(*it);
                mvsc.push_back(std::make_pair(-score, *it));
            }
            stable_sort(mvsc.begin(), mvsc.end());
            std::vector<HexPoint> sortedChildren;
            for (size_t i = 0; i < mvsc.size(); ++i)
                sortedChildren.push_back(mvsc[i].second);
            children.SetChildren(sortedChildren);
        }
    }

    ++m_numMIDcalls;
    size_t localWork = 1;

    // Not thread safe: perhaps move into while loop below later...
    std::vector<DfpnData> childrenData(children.Size());
    for (size_t i = 0; i < children.Size(); ++i)
        LookupData(childrenData[i], children, i, *m_state);
    // Index used for progressive widening
    size_t maxChildIndex = ComputeMaxChildIndex(childrenData);

    if (m_useGuiFx && depth == 0)
        m_guiFx.SetChildren(children, childrenData);

    hash_t currentHash = m_state->Hash();
    HexPoint bestMove = INVALID_POINT;
    DfpnBounds currentBounds;
    do
    {
        UpdateBounds(currentBounds, childrenData, maxChildIndex);

        if (m_useGuiFx && depth == 1)
        {
            m_guiFx.UpdateCurrentBounds(currentBounds);
            m_guiFx.Write();
        }

        if (!maxBounds.GreaterThan(currentBounds))
            break;

        // Select most proving child
        int bestIndex = -1;
        DfpnBoundType delta2 = DfpnBounds::INFTY;
        SelectChild(bestIndex, delta2, childrenData, maxChildIndex);
        bestMove = children.FirstMove(bestIndex);

        // Compute maximum bound for child
        const DfpnBounds childBounds(childrenData[bestIndex].m_bounds);
        DfpnBounds childMaxBounds;
        childMaxBounds.phi = maxBounds.delta 
            - (currentBounds.delta - childBounds.phi);
        childMaxBounds.delta = std::min(maxBounds.phi, delta2 + 1);
        HexAssert(childMaxBounds.GreaterThan(childBounds));
        if (delta2 != DfpnBounds::INFTY)
            m_deltaIncrease.Add(childMaxBounds.delta - childBounds.delta);

        // Recurse on best child
        if (m_useGuiFx && depth == 0)
            m_guiFx.PlayMove(colorToMove, bestIndex);
        children.PlayMove(bestIndex, *m_state);
        history.Push(bestMove, currentHash);
        localWork += MID(childMaxBounds, history);
        history.Pop();
        children.UndoMove(bestIndex, *m_state);

        if (m_useGuiFx && depth == 0)
            m_guiFx.UndoMove();

        // Update bounds for best child
        LookupData(childrenData[bestIndex], children, bestIndex, *m_state);

        // Compute some stats when find winning move
        if (childrenData[bestIndex].m_bounds.IsLosing())
        {
            m_moveOrderingIndex.Add(bestIndex);
            m_moveOrderingPercent.Add(bestIndex / (double)childrenData.size());
            m_totalWastedWork += prevWork + localWork
                - childrenData[bestIndex].m_work;
        }
        else if (childrenData[bestIndex].m_bounds.IsWinning())
            maxChildIndex = ComputeMaxChildIndex(childrenData);

        // Shrink children list using knowledge of bestMove child's proof set.
        // That is, if this child is losing, conclude what other children
        // must also be losing (i.e. cannot interfere with the proof set
        // that disproves this child).
        // And of course if this child is winning, no need to explore
        // these other siblings either.
        {
            /* @todo Perhaps track allChildren instead of recomputing? */
            bitset_t allChildren;
            for (std::vector<HexPoint>::iterator it
                     = children.m_children.begin();
                 it != children.m_children.end(); ++it)
            {
                allChildren.set(*it);
            }
            bitset_t canPrune = allChildren
                - childrenData[bestIndex].m_maxProofSet;
            canPrune.reset(bestMove);
            int pruneCount = canPrune.count();

            if (pruneCount)
            {
                m_prunedSiblingStats.Add(pruneCount);
                /*
                LogInfo() << "Pruning " << pruneCount
                          << " moves via " << bestMove
                          << ".\nChildren:\n" << m_brd->Write(allChildren)
                          << "\nRemoving...\n" << m_brd->Write(canPrune)
                          << "\n";
                */
                DeleteChildren(children, childrenData, canPrune);
                maxChildIndex = ComputeMaxChildIndex(childrenData);
                if (m_useGuiFx && depth == 0)
                    m_guiFx.SetChildren(children, childrenData);
            }
        }
    } while (!CheckAbort());

    if (m_useGuiFx && depth == 0)
        m_guiFx.WriteForced();

    // Find the most delaying move for losing states, and the smallest
    // winning move for winning states.
    if (currentBounds.IsSolved())
    {
        m_allSolvedEvaluation.Add(evaluationScore);
        if (currentBounds.IsLosing())
        {
            m_losingEvaluation.Add(evaluationScore);
            std::size_t maxWork = 0;
            for (std::size_t i = 0; i < children.Size(); ++i)
            {
                if (childrenData[i].m_work > maxWork)
                {
                    maxWork = childrenData[i].m_work;
                    bestMove = children.FirstMove(i);
                }
            }
        }
        else
        {
            m_winningEvaluation.Add(evaluationScore);
            std::size_t minWork = DfpnBounds::INFTY;
            for (std::size_t i = 0; i < children.Size(); ++i)
            {
                if (childrenData[i].m_bounds.IsLosing() 
                    && childrenData[i].m_work < minWork)
                {
                    minWork = childrenData[i].m_work;
                    bestMove = children.FirstMove(i);
                }
            }
        }
    }
    
    // Store search results and notify listeners
    DfpnData data(currentBounds, children, bestMove, localWork + prevWork, 
                  maxProofSet, evaluationScore);
    TTWrite(*m_state, data);
    if (data.m_bounds.IsSolved())
        NotifyListeners(history, data);
    return localWork;
}