- /* 
-     Texel - A UCI chess engine. 
-     Copyright (C) 2013-2014  Peter Ă–sterlund, peterosterlund2@gmail.com 
-   
-     This program is free software: you can redistribute it and/or modify 
-     it under the terms of the GNU General Public License as published by 
-     the Free Software Foundation, either version 3 of the License, or 
-     (at your option) any later version. 
-   
-     This program is distributed in the hope that it will be useful, 
-     but WITHOUT ANY WARRANTY; without even the implied warranty of 
-     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
-     GNU General Public License for more details. 
-   
-     You should have received a copy of the GNU General Public License 
-     along with this program.  If not, see <http://www.gnu.org/licenses/>. 
- */ 
-   
- /* 
-  * parallel.cpp 
-  * 
-  *  Created on: Jul 9, 2013 
-  *      Author: petero 
-  */ 
-   
- #include "parallel.hpp" 
- #include "numa.hpp" 
- #include "search.hpp" 
- #include "tbprobe.hpp" 
- #include "textio.hpp" 
- #include "util/logger.hpp" 
-   
- #include <cmath> 
- #include <cassert> 
-   
- using namespace Logger; 
-   
- U64 SplitPoint::nextSeqNo = 0; 
-   
- // ---------------------------------------------------------------------------- 
-   
- WorkerThread::WorkerThread(int threadNo0, ParallelData& pd0, 
-                            TranspositionTable& tt0) 
-     : threadNo(threadNo0), pd(pd0), tt(tt0), 
-       pUseful(0.0) { 
- } 
-   
- WorkerThread::~WorkerThread() { 
-     assert(!thread); 
- } 
-   
- void 
- WorkerThread::start() { 
-     assert(!thread); 
-     const int minProbeDepth = TBProbe::tbEnabled() ? UciParams::minProbeDepth->getIntPar() : 100; 
-     thread = std::make_shared<std::thread>([this,minProbeDepth](){ mainLoop(minProbeDepth); }); 
- } 
-   
- void 
- WorkerThread::join() { 
-     if (thread) { 
-         thread->join(); 
-         thread.reset(); 
-     } 
- } 
-   
- class ThreadStopHandler : public Search::StopHandler { 
- public: 
-     ThreadStopHandler(WorkerThread& wt, ParallelData& pd, 
-                       const SplitPoint& sp, const SplitPointMove& spm, 
-                       const Search& sc, int initialAlpha, 
-                       S64 totalNodes, int myPrio); 
-   
-     /** Destructor. Report searched nodes to ParallelData object. */ 
-     ~ThreadStopHandler(); 
-   
-     ThreadStopHandler(const ThreadStopHandler&) = delete; 
-     ThreadStopHandler& operator=(const ThreadStopHandler&) = delete; 
-   
-     bool shouldStop(); 
-   
- private: 
-     /** Report searched nodes since last call to ParallelData object. */ 
-     void reportNodes(bool force); 
-   
-     const WorkerThread& wt; 
-     ParallelData& pd; 
-     const SplitPoint& sp; 
-     const SplitPointMove& spMove; 
-     const Search& sc; 
-     int counter;             // Counts number of calls to shouldStop 
-     int nextProbCheck;       // Next time test for SplitPoint switch should be performed 
-     S64 lastReportedNodes; 
-     S64 lastReportedTbHits; 
-     int initialAlpha; 
-     const S64 totalNodes; 
-     const int myPrio; 
- }; 
-   
- ThreadStopHandler::ThreadStopHandler(WorkerThread& wt0, ParallelData& pd0, 
-                                      const SplitPoint& sp0, const SplitPointMove& spm0, 
-                                      const Search& sc0, int initialAlpha0, 
-                                      S64 totalNodes0, int myPrio0) 
-     : wt(wt0), pd(pd0), sp(sp0), spMove(spm0), 
-       sc(sc0), counter(0), nextProbCheck(1), 
-       lastReportedNodes(0), lastReportedTbHits(0), 
-       initialAlpha(initialAlpha0), totalNodes(totalNodes0), myPrio(myPrio0) { 
- } 
-   
- ThreadStopHandler::~ThreadStopHandler() { 
-     reportNodes(true); 
- } 
-   
- bool 
- ThreadStopHandler::shouldStop() { 
-     if (pd.wq.isStopped() || spMove.isCanceled()) 
-         return true; 
-     if (sp.getAlpha() != initialAlpha) 
-         return true; 
-   
-     counter++; 
-     if (counter >= nextProbCheck) { 
-         nextProbCheck = counter + 1 + counter / 4; 
-         int bestPrio = pd.wq.getBestPrio(); 
- //        log([&](std::ostream& os){os << "shouldStop, th:" << wt.getThreadNo() << " myP:" << myPrio << " bestP:" << bestPrio;}); 
-         if ((bestPrio > myPrio + 20) && (bestPrio >= (myPrio + (1000 - myPrio) * 0.25)) && 
-             (sp.owningThread() != wt.getThreadNo())) 
-             return true; 
-         reportNodes(false); 
-     } 
-   
-     return false; 
- } 
-   
- void 
- ThreadStopHandler::reportNodes(bool force) { 
-     S64 totNodes = sc.getTotalNodesThisThread(); 
-     S64 nodes = totNodes - lastReportedNodes; 
-     if (force || (nodes * 1024 > totalNodes)) { 
-         lastReportedNodes = totNodes; 
-         pd.addSearchedNodes(nodes); 
-         S64 totTbHits = sc.getTbHitsThisThread(); 
-         S64 tbHits = totTbHits - lastReportedTbHits; 
-         lastReportedTbHits = totTbHits; 
-         pd.addTbHits(tbHits); 
-     } 
- } 
-   
- void 
- WorkerThread::mainLoop(int minProbeDepth) { 
- //    log([&](std::ostream& os){os << "mainLoop, th:" << threadNo;}); 
-     Numa::instance().bindThread(threadNo); 
-     if (!et) 
-         et = Evaluate::getEvalHashTables(); 
-     if (!kt) 
-         kt = std::make_shared<KillerTable>(); 
-     if (!ht) 
-         ht = std::make_shared<History>(); 
-   
-     TreeLogger logFile; 
-     logFile.open("/home/petero/treelog.dmp", pd, threadNo); 
-   
- //    UtilizationTimer uTimer; 
-     std::mutex m; 
-     std::unique_lock<std::mutex> lock(m); 
-     Position pos; 
-     std::shared_ptr<SplitPoint> sp; 
-     for (int iter = 0; ; iter++) { 
-         const bool doTiming = (iter & 15) == 0; 
-         int moveNo = -1; 
- //        uTimer.setPUseful(-1); 
-         const double t0 = doTiming ? currentTime() : -1; 
-         int prio; 
-         std::shared_ptr<SplitPoint> newSp = pd.wq.getWork(moveNo, pd, threadNo, prio, pUseful); 
-         if (!newSp) 
-             break; 
-         const double tStart = doTiming ? currentTime() : -1; 
-   
-         const SplitPointMove& spMove = newSp->getSpMove(moveNo); 
-         const int depth = spMove.getDepth(); 
-         if (depth < 0) { // Move skipped by forward pruning or legality check 
-             pd.wq.moveFinished(newSp, moveNo, false, SearchConst::UNKNOWN_SCORE); 
-             continue; 
-         } 
-         if (sp != newSp) { 
-             sp = newSp; 
-             *ht = sp->getHistory(); 
-             *kt = sp->getKillerTable(); 
-         } 
-         Search::SearchTables st(tt, *kt, *ht, *et); 
-         sp->getPos(pos, spMove.getMove()); 
-         std::vector<U64> posHashList; 
-         int posHashListSize; 
-         sp->getPosHashList(pos, posHashList, posHashListSize); 
-         Search sc(pos, posHashList, posHashListSize, st, pd, sp, logFile); 
-         const U64 rootNodeIdx = logFile.logPosition(pos, sp->owningThread(), 
-                                                     sp->getSearchTreeInfo().nodeIdx, moveNo); 
-         sc.setThreadNo(threadNo); 
-         sc.setMinProbeDepth(minProbeDepth); 
-         const int alpha = sp->getAlpha(); 
-         const int beta = sp->getBeta(); 
-         const S64 nodes0 = pd.getNumSearchedNodes(); 
-         auto stopHandler(std::make_shared<ThreadStopHandler>(*this, pd, *sp, spMove, 
-                                                              sc, alpha, nodes0, prio)); 
-         sc.setStopHandler(stopHandler); 
-         const int ply = sp->getPly(); 
-         const int lmr = spMove.getLMR(); 
-         const int captSquare = spMove.getRecaptureSquare(); 
-         const bool inCheck = spMove.getInCheck(); 
-         sc.setSearchTreeInfo(ply, sp->getSearchTreeInfo(), spMove.getMove(), moveNo, lmr, rootNodeIdx); 
-         try { 
- //            log([&](std::ostream& os){os << "th:" << threadNo << " seqNo:" << sp->getSeqNo() << " ply:" << ply 
- //                                         << " c:" << sp->getCurrMoveNo() << " m:" << moveNo 
- //                                         << " a:" << alpha << " b:" << beta 
- //                                         << " d:" << depth/SearchConst::plyScale 
- //                                         << " p:" << sp->getPMoveUseful(pd.fhInfo, moveNo) << " start";}); 
- //            uTimer.setPUseful(pUseful); 
-             const bool smp = pd.numHelperThreads() > 1; 
-             int score = -sc.negaScout(smp, true, -(alpha+1), -alpha, ply+1, 
-                                       depth, captSquare, inCheck); 
-             if (((lmr > 0) && (score > alpha)) || 
-                     ((score > alpha) && (score < beta))) { 
-                 sc.setSearchTreeInfo(ply, sp->getSearchTreeInfo(), spMove.getMove(), moveNo, 0, rootNodeIdx); 
-                 score = -sc.negaScout(smp, true, -beta, -alpha, ply+1, 
-                                       depth + lmr, captSquare, inCheck); 
-             } 
- //            uTimer.setPUseful(0); 
-             bool cancelRemaining = score >= beta; 
- //            log([&](std::ostream& os){os << "th:" << threadNo << " seqNo:" << sp->getSeqNo() << " ply:" << ply 
- //                                         << " c:" << sp->getCurrMoveNo() << " m:" << moveNo 
- //                                         << " a:" << alpha << " b:" << beta << " s:" << score 
- //                                         << " d:" << depth/SearchConst::plyScale << " n:" << sc.getTotalNodesThisThread();}); 
-             pd.wq.moveFinished(sp, moveNo, cancelRemaining, score); 
-         } catch (const Search::StopSearch&) { 
- //            log([&](std::ostream& os){os << "th:" << threadNo << " seqNo:" << sp->getSeqNo() << " m:" << moveNo 
- //                                         << " aborted n:" << sc.getTotalNodesThisThread();}); 
-             if (!spMove.isCanceled() && !pd.wq.isStopped()) 
-                 pd.wq.returnMove(sp, moveNo); 
-         } 
-         if (doTiming) { 
-             const double tEnd = currentTime(); 
-             pd.npsInfo.addData(sp->getDepth(), sc.getTotalNodesThisThread(), tStart - t0, tEnd - tStart); 
-         } 
-         pUseful = 0.0; 
-     } 
- //    double tElapsed, tUseful, tSleep; 
- //    uTimer.getStats(tElapsed, tUseful, tSleep); 
- //    log([&](std::ostream& os){ 
- //        os << "~mainLoop, th:" << threadNo << " useful:" << tUseful / tElapsed 
- //           << " sleep:" << tSleep / tElapsed; 
- //    }); 
- //    log([&](std::ostream& os){os << "~mainLoop, th:" << threadNo;}); 
- } 
-   
- // ---------------------------------------------------------------------------- 
-   
- void 
- WorkQueue::setStopped(bool stop) { 
-     Lock L(this); 
-     stopped = stop; 
-     if (stopped) 
-         cv.notify_all(); 
- } 
-   
- void 
- WorkQueue::addWork(const std::shared_ptr<SplitPoint>& sp) { 
-     Lock L(this); 
- //    ScopedTimeSample sts { getAddWorkStat(sp->owningThread()) }; 
-     addWorkInternal(sp); 
- } 
-   
- void 
- WorkQueue::tryAddWork(const std::shared_ptr<SplitPoint>& sp, 
-                       std::vector<std::shared_ptr<SplitPoint>>& pending) { 
-     std::unique_lock<std::mutex> lock(mutex, std::defer_lock); 
-     if (!lock.try_lock()) { 
-         pending.push_back(sp); 
-         return; 
-     } 
-     for (const auto& sp : pending) 
-         addWorkInternal(sp); 
-     pending.clear(); 
-     addWorkInternal(sp); 
- } 
-   
- void 
- WorkQueue::addWorkInternal(const std::shared_ptr<SplitPoint>& sp) { 
-     sp->setSeqNo(); 
-     std::shared_ptr<SplitPoint> parent = sp->getParent(); 
-     if (parent) { 
-         if (parent->isCanceled()) 
-             sp->cancel(); 
-         else 
-             parent->addChild(sp); 
-     } 
-     if (sp->hasUnFinishedMove()) { 
-         sp->computeProbabilities(fhInfo, npsInfo); 
-         insertInQueue(sp); 
-     } 
- } 
-   
- std::shared_ptr<SplitPoint> 
- WorkQueue::getWork(int& spMove, ParallelData& pd, int threadNo) { 
-     int prio; 
-     double pUseful; 
-     return getWork(spMove, pd, threadNo, prio, pUseful); 
- } 
-   
- std::shared_ptr<SplitPoint> 
- WorkQueue::getWork(int& spMove, ParallelData& pd, int threadNo, int& prio, double& pUseful) { 
-     Lock L(this); 
-     while (true) { 
-         while (queue.empty() && !isStopped()) 
-             L.wait(cv); 
- //        ScopedTimeSample sts { getGetWorkStat(threadNo) }; 
-         if (isStopped()) 
-             return nullptr; 
-         std::shared_ptr<SplitPoint> ret = queue.front(); 
-         spMove = ret->getNextMove(pd.fhInfo); 
-         if (spMove < 0) { 
-             L.wait(cv); 
-             continue; 
-         } 
- //        log([&](std::ostream& os){printSpTree(os, pd, threadNo, ret, spMove);}); 
-         prio = ret->getPrio(); 
-         updateProbabilities(ret); 
-         pUseful = ret->getPMoveUseful(pd.fhInfo, spMove); 
-         return ret; 
-     } 
- } 
-   
- void 
- WorkQueue::returnMove(const std::shared_ptr<SplitPoint>& sp, int moveNo) { 
-     Lock L(this); 
-     sp->returnMove(moveNo); 
-     updateProbabilities(sp); 
- } 
-   
- int 
- WorkQueue::setOwnerCurrMove(const std::shared_ptr<SplitPoint>& sp, int moveNo, int alpha) { 
-     Lock L(this); 
-     int score = sp->setOwnerCurrMove(moveNo, alpha); 
-     updateProbabilities(sp); 
-     return score; 
- } 
-   
- void 
- WorkQueue::cancel(const std::shared_ptr<SplitPoint>& sp) { 
-     Lock L(this); 
-     cancelInternal(sp); 
- } 
-   
- void 
- WorkQueue::moveFinished(const std::shared_ptr<SplitPoint>& sp, int moveNo, 
-                         bool cancelRemaining, int score) { 
-     Lock L(this); 
-     sp->moveFinished(moveNo, cancelRemaining, score); 
-     updateProbabilities(sp); 
- } 
-   
- double 
- WorkQueue::getBestProbability(std::shared_ptr<SplitPoint>& bestSp) const { 
-     Lock L(this); 
-     if (queue.empty()) 
-         return 0.0; 
-     bestSp = queue.front(); 
-     return bestSp->getPNextMoveUseful(); 
- } 
-   
- int 
- WorkQueue::getBestPrio() const { 
-     Lock L(this); 
-     if (queue.empty()) 
-         return -1; 
-     return queue.front()->getPrio(); 
- } 
-   
- double 
- WorkQueue::getBestProbability() const { 
-     std::shared_ptr<SplitPoint> bestSp; 
-     return getBestProbability(bestSp); 
- } 
-   
- void 
- WorkQueue::updateProbabilities(const std::shared_ptr<SplitPoint>& sp) { 
-     if (!sp->hasUnFinishedMove()) 
-         queue.remove(sp); 
-     sp->computeProbabilities(fhInfo, npsInfo); 
- } 
-   
- void 
- WorkQueue::cancelInternal(const std::shared_ptr<SplitPoint>& sp) { 
-     sp->cancel(); 
-     queue.remove(sp); 
-   
-     for (const auto& wChild : sp->getChildren()) { 
-         std::shared_ptr<SplitPoint> child = wChild.lock(); 
-         if (child) 
-             cancelInternal(child); 
-     } 
- } 
-   
- static std::string toPercentStr(double p) { 
-     std::stringstream ss; 
-     int pc = (int)(p * 100); 
-     if (pc == 100) 
-         pc = 99; 
-     ss << std::setfill('0') << std::setw(2) << pc; 
-     return ss.str(); 
- } 
-   
- void 
- WorkQueue::printSpTree(std::ostream& os, const ParallelData& pd, 
-                        int threadNo, const std::shared_ptr<SplitPoint> selectedSp, 
-                        int selectedMove) { 
-     const int numThreads = pd.numHelperThreads() + 1; 
-     std::shared_ptr<SplitPoint> rootSp = queue.front(); 
-     assert(rootSp); 
-     while (rootSp->getParent()) 
-         rootSp = rootSp->getParent(); 
-     std::vector<int> parentThreads(numThreads, -1); 
-     std::vector<std::shared_ptr<SplitPoint>> leaves(numThreads, nullptr); 
-     findLeaves(rootSp, parentThreads, leaves); 
-   
-     os << "th:" << threadNo << " m: " << selectedMove << ':' 
-        << TextIO::moveToUCIString(selectedSp->getSpMove(selectedMove).getMove()) 
-        << " p:" << selectedSp->getPMoveUseful(pd.fhInfo, selectedMove) 
-        << std::endl; 
-     for (int i = 0; i < numThreads; i++) { 
-         std::vector<std::shared_ptr<SplitPoint>> thVec; 
-         for (auto sp = leaves[i]; sp; sp = sp->getParent()) 
-             thVec.push_back(sp); 
-         std::reverse(thVec.begin(), thVec.end()); 
-         os << "th " << i << ' '; 
-         if (parentThreads[i] < 0) 
-             os << '-'; 
-         else 
-             os << parentThreads[i]; 
-         os << ' ' << toPercentStr(i == 0 ? 1 : pd.getHelperThread(i-1).getPUseful()); 
-         if (!thVec.empty()) { 
-             for (const auto& sp : thVec) 
-                 if (sp->owningThread() == i) { 
-                     os << ' ' << std::setw(6) << sp->getSeqNo(); 
-                     break; 
-                 } 
-         } 
-         for (const auto& sp : thVec) { 
-             if (sp->owningThread() == i) { 
-                 int pMove = sp->getParentMoveNo(); 
-                 os << ' ' << std::setw(2) << pMove << (sp == selectedSp ? '*' : ':'); 
-                 if (pMove < 0) 
-                     os << "null"; 
-                 else if (sp->getParent()) 
-                     os << TextIO::moveToUCIString(sp->getParent()->getSpMove(pMove).getMove()); 
-                 else 
-                     os << TextIO::moveToUCIString(pd.topMove); 
-                 os << ',' << toPercentStr(sp->getPSpUseful()) 
-                    << ':' << toPercentStr(sp->getPNextMoveUseful()); 
-                 os << ',' << std::setw(2) << sp->getCurrMoveNo() << ':' << std::setw(2) << sp->findNextMove(pd.fhInfo); 
-             } else { 
-                 os << "                    "; 
-             } 
-         } 
-         os << std::endl; 
-     } 
- } 
-   
- void 
- WorkQueue::findLeaves(const std::shared_ptr<SplitPoint>& sp, 
-                       std::vector<int>& parentThreads, 
-                       std::vector<std::shared_ptr<SplitPoint>>& leaves) { 
-     bool isLeaf = true; 
-     const std::vector<std::weak_ptr<SplitPoint>>& children = sp->getChildren(); 
-     for (const auto& wChild : children) { 
-         std::shared_ptr<SplitPoint> child = wChild.lock(); 
-         if (child && !child->isCanceled()) { 
-             if (child->owningThread() == sp->owningThread()) { 
-                 isLeaf = false; 
-             } else { 
-                 assert(parentThreads[child->owningThread()] == -1); 
-                 parentThreads[child->owningThread()] = sp->owningThread(); 
-             } 
-             findLeaves(child, parentThreads, leaves); 
-         } 
-     } 
-     if (isLeaf) { 
-         assert(sp->owningThread() >= 0); 
-         assert(sp->owningThread() < (int)leaves.size()); 
-         leaves[sp->owningThread()] = sp; 
-     } 
- } 
-   
- WorkQueue::Lock::Lock(const WorkQueue* wq0) 
-     : wq(*wq0), lock(wq.mutex, std::defer_lock) { 
-     bool contended = false; 
-     if (!lock.try_lock()) { 
-         contended = true; 
-         lock.lock(); 
-     } 
-     if (wq.queue.empty()) 
-         contended = false; 
-     U64 c = wq.nContended; 
-     U64 n = wq.nNonContended; 
-     if (contended) 
-         c++; 
-     else 
-         n++; 
-     if (n + c > 30000) { 
-         c /= 2; 
-         n /= 2; 
-         if (c * 100 > n * 50) { 
-             wq.minSplitDepth++; 
- //            std::cout << "contended stat: " << wq.minSplitDepth << " " << c << " " << n << std::endl; 
-         } else if ((c * 100 < n * 25) && (wq.minSplitDepth > SearchConst::MIN_SMP_DEPTH)) { 
-             wq.minSplitDepth--; 
- //            std::cout << "contended stat: " << wq.minSplitDepth << " " << c << " " << n << std::endl; 
-         } 
-     } 
-     wq.nContended = c; 
-     wq.nNonContended = n; 
- } 
-   
- // ---------------------------------------------------------------------------- 
-   
- void 
- ParallelData::addRemoveWorkers(int numWorkers) { 
-     while (numWorkers < (int)threads.size()) { 
-         assert(!threads.back()->threadRunning()); 
-         threads.pop_back(); 
-     } 
-     for (int i = threads.size(); i < numWorkers; i++) 
-         threads.push_back(std::make_shared<WorkerThread>(i+1, *this, tt)); 
- } 
-   
- void 
- ParallelData::startAll() { 
-     totalHelperNodes = 0; 
-     helperTbHits = 0; 
-     wq.setStopped(false); 
-     for (auto& thread : threads) 
-         thread->start(); 
- } 
-   
- void 
- ParallelData::stopAll() { 
-     wq.setStopped(true); 
-     for (auto& thread : threads) 
-         thread->join(); 
- } 
-   
- // ---------------------------------------------------------------------------- 
-   
- SplitPoint::SplitPoint(int threadNo0, 
-                        const std::shared_ptr<SplitPoint>& parentSp0, int parentMoveNo0, 
-                        const Position& pos0, const std::vector<U64>& posHashList0, 
-                        int posHashListSize0, const SearchTreeInfo& sti0, 
-                        const KillerTable& kt0, const History& ht0, 
-                        int alpha0, int beta0, int ply0, int depth0) 
-     : pos(pos0), posHashList(posHashList0), posHashListSize(posHashListSize0), 
-       searchTreeInfo(sti0), kt(kt0), ht(ht0), 
-       alpha(alpha0), beta(beta0), ply(ply0), depth(depth0), 
-       isPV(beta0 > alpha0 + 1), 
-       pSpUseful(0.0), pNextMoveUseful(0.0), 
-       threadNo(threadNo0), parent(parentSp0), parentMoveNo(parentMoveNo0), 
-       seqNo(0), currMoveNo(0), inserted(false), canceled(false) { 
- } 
-   
- void 
- SplitPoint::addMove(int moveNo, const SplitPointMove& spMove) { 
-     assert(moveNo >= (int)spMoves.size()); 
-     while ((int)spMoves.size() < moveNo) 
-         spMoves.push_back(SplitPointMove(Move(), 0, -1, -1, false)); 
-     spMoves.push_back(spMove); 
- } 
-   
- void 
- SplitPoint::computeProbabilities(const FailHighInfo& fhInfo, const DepthNpsInfo& npsInfo) { 
-     if (parent) { 
-         double pMoveUseful = 1.0; 
-         if (parentMoveNo >= 0) 
-             pMoveUseful = parent->getMoveNeededProbability(fhInfo, parentMoveNo); 
-         pSpUseful = parent->pSpUseful * pMoveUseful; 
-     } else { 
-         pSpUseful = 1.0; 
-     } 
-     double pNextUseful = getMoveNeededProbability(fhInfo, findNextMove(fhInfo)); 
-     pNextMoveUseful = pSpUseful * pNextUseful; 
-     newPrio(getSpPrio(npsInfo)); 
-   
-     bool deleted = false; 
-     for (const auto& wChild : children) { 
-         std::shared_ptr<SplitPoint> child = wChild.lock(); 
-         if (child) 
-             child->computeProbabilities(fhInfo, npsInfo); 
-         else 
-             deleted = true; 
-     } 
-     if (deleted) 
-         cleanUpChildren(); 
- } 
-   
- double 
- SplitPoint::getPMoveUseful(const FailHighInfo& fhInfo, int moveNo) const { 
-     return pSpUseful * getMoveNeededProbability(fhInfo, moveNo); 
- } 
-   
- void 
- SplitPoint::getPos(Position& pos, const Move& move) const { 
-     pos = this->pos; 
-     UndoInfo ui; 
-     pos.makeMove(move, ui); 
- } 
-   
- void 
- SplitPoint::getPosHashList(const Position& pos, std::vector<U64>& posHashList, 
-                            int& posHashListSize) const { 
-     posHashList = this->posHashList; 
-     posHashListSize = this->posHashListSize; 
-     posHashList[posHashListSize++] = pos.zobristHash(); 
- } 
-   
- int 
- SplitPoint::getNextMove(const FailHighInfo& fhInfo) { 
-     int m = findNextMove(fhInfo); 
-     if (m < 0) 
-         return m; 
-     spMoves[m].setSearching(true); 
-     return m; 
- } 
-   
- void 
- SplitPoint::moveFinished(int moveNo, bool cancelRemaining, int score) { 
-     assert((moveNo >= 0) && (moveNo < (int)spMoves.size())); 
-     spMoves[moveNo].setScore(score); 
-     spMoves[moveNo].setSearching(false); 
-     spMoves[moveNo].setCanceled(true); 
-     if (cancelRemaining) 
-         for (int i = moveNo+1; i < (int)spMoves.size(); i++) 
-             spMoves[i].setCanceled(true); 
- } 
-   
- bool 
- SplitPoint::hasUnStartedMove() const { 
-     if (canceled) 
-         return false; 
-     for (int i = currMoveNo + 1; i < (int)spMoves.size(); i++) 
-         if (!spMoves[i].isCanceled() && !spMoves[i].isSearching()) 
-             return true; 
-     return false; 
- } 
-   
- bool 
- SplitPoint::hasUnFinishedMove() const { 
-     if (canceled) 
-         return false; 
-     for (int i = currMoveNo + 1; i < (int)spMoves.size(); i++) 
-         if (!spMoves[i].isCanceled()) 
-             return true; 
-     return false; 
- } 
-   
- int 
- SplitPoint::findNextMove(const FailHighInfo& fhInfo) const { 
-     int i0 = -1; 
-     const double pGood = 0.98; 
-     for (int i = currMoveNo+1; i < (int)spMoves.size(); i++) 
-         if (!spMoves[i].isCanceled() && !spMoves[i].isSearching()) { 
-             if ((getPNextMoveUseful() > pGood) && (i0 == -1)) 
-                 i0 = i; 
-             else { 
-                 if ((i0 != -1) && (getPMoveUseful(fhInfo, i) <= pGood)) 
-                     return i0; 
-                 return i; 
-             } 
-         } 
-     return i0; 
- } 
-   
- double 
- SplitPoint::getMoveNeededProbability(const FailHighInfo& fhInfo, int moveNo) const { 
-     if (isPvNode()) 
-         return fhInfo.getMoveNeededProbabilityPv(currMoveNo, moveNo); 
-     else 
-         return fhInfo.getMoveNeededProbability(parentMoveNo, 
-                                                currMoveNo, 
-                                                moveNo, isAllNode()); 
- } 
-   
- void 
- SplitPoint::cleanUpChildren() { 
-     std::vector<std::weak_ptr<SplitPoint>> toKeep; 
-     for (const auto& wChild : children) 
-         if (wChild.lock()) 
-             toKeep.push_back(wChild); 
-     children = toKeep; 
- } 
-   
- bool 
- SplitPoint::hasHelperThread() const { 
-     for (const auto& wChild : children) { 
-         std::shared_ptr<SplitPoint> child = wChild.lock(); 
-         if (child && child->owningThread() != owningThread()) 
-             return true; 
-     } 
-     return false; 
- } 
-   
- bool 
- SplitPoint::isAncestorTo(const SplitPoint& sp) const { 
-     const SplitPoint* tmp = &sp; 
-     while (tmp) { 
-         if (tmp == this) 
-             return true; 
-         tmp = &*(tmp->parent); 
-     } 
-     return false; 
- } 
-   
- bool 
- SplitPoint::isAllNode() const { 
-     int nFirst = 0; 
-     const SplitPoint* tmp = this; 
-     while (tmp) { 
-         if (tmp->parentMoveNo == 0) 
-             nFirst++; 
-         else 
-             break; 
-         tmp = &*(tmp->parent); 
-     } 
-     return (nFirst % 2) != 0; 
- } 
-   
- void 
- SplitPoint::print(std::ostream& os, int level, const FailHighInfo& fhInfo) const { 
-     std::string pad(level*2, ' '); 
-     os << pad << "seq:" << seqNo << " pos:" << TextIO::toFEN(pos) << std::endl; 
-     os << pad << "parent:" << parentMoveNo << " hashListSize:" << posHashListSize << 
-         " a:" << alpha << " b:" << beta << " ply:" << ply << " canceled:" << canceled << std::endl; 
-     os << pad << "p1:" << pSpUseful << " p2:" << pNextMoveUseful << " curr:" << currMoveNo << std::endl; 
-     os << pad << "moves:"; 
-     for (int mi = 0; mi < (int)spMoves.size(); mi++) { 
-         const auto& spm = spMoves[mi]; 
-         os << ' ' << TextIO::moveToUCIString(spm.getMove()); 
-         if (spm.isCanceled()) 
-             os << ",c"; 
-         if (spm.isSearching()) 
-             os << ",s"; 
-         os << "," << getMoveNeededProbability(fhInfo, mi); 
-     } 
-     os << std::endl; 
-     for (const auto& wChild : children) { 
-         std::shared_ptr<SplitPoint> child = wChild.lock(); 
-         if (child) 
-             child->print(os, level+1, fhInfo); 
-     } 
- } 
-   
- // ---------------------------------------------------------------------------- 
-   
- void 
- SplitPointHolder::setSp(const std::shared_ptr<SplitPoint>& sp0) { 
-     assert(state == State::EMPTY); 
-     assert(sp0); 
-     sp = sp0; 
-     if (sp->getBeta() > sp->getAlpha() + 1) 
-         pd.fhInfo.addPvData(-1, false); 
-     state = State::CREATED; 
- } 
-   
- void 
- SplitPointHolder::addToQueue() { 
-     assert(state == State::CREATED); 
-     pd.wq.tryAddWork(sp, pending); 
-     spVec.push_back(sp); 
- //    log([&](std::ostream& os){os << "add seqNo:" << sp->getSeqNo() << " ply:" << sp->getPly() 
- //                                 << " pNext:" << sp->getPNextMoveUseful() 
- //                                 << " pMove:" << sp->getParentMoveNo() << " vec:" << spVec.size();}); 
-     state = State::QUEUED; 
- } 
-   
- // ---------------------------------------------------------------------------- 
-   
- FailHighInfo::FailHighInfo() 
-     : totCount(0) { 
-     for (int i = 0; i < NUM_NODE_TYPES; i++) 
-         failLoCount[i] = 0; 
-     for (int j = 0; j < NUM_STAT_MOVES; j++) 
-         newAlpha[j] = 0; 
-     totPvCount = 0; 
- } 
-   
- double 
- FailHighInfo::getMoveNeededProbability(int parentMoveNo, 
-                                        int currMoveNo, int moveNo, bool allNode) const { 
-     const int pIdx = getNodeType(parentMoveNo, allNode); 
-     moveNo = std::min(moveNo, NUM_STAT_MOVES-1); 
-     if (moveNo < 0) 
-         return 0.0; 
-   
-     int nNeeded = failLoCount[pIdx] + failHiCount[pIdx].sum(moveNo, NUM_STAT_MOVES); 
-     int nTotal = nNeeded + failHiCount[pIdx].sum(currMoveNo, moveNo); 
-   
-     return (nTotal > 0) ? nNeeded / (double)nTotal : 0.5; 
- } 
-   
- double 
- FailHighInfo::getMoveNeededProbabilityPv(int currMoveNo, int moveNo) const { 
-     moveNo = std::min(moveNo, NUM_STAT_MOVES-1); 
-     if (moveNo < 0) 
-         return 0.0; 
-     if (totPvCount <= 0) 
-         return 0.5; 
-   
-     double prob = 1.0; 
-     double inv = 1.0 / totPvCount; 
-     for (int i = currMoveNo; i < moveNo; i++) 
-         prob *= std::max(0.0, 1.0 - newAlpha[i] * inv); 
-     return prob; 
- } 
-   
- void 
- FailHighInfo::addData(int parentMoveNo, int nSearched, bool failHigh, bool allNode) { 
-     if (nSearched < 0) 
-         return; 
-     const int pIdx = getNodeType(parentMoveNo, allNode); 
-     if (failHigh) { 
-         nSearched = std::min(nSearched, NUM_STAT_MOVES-1); 
-         failHiCount[pIdx].add(nSearched, 1); 
-     } else { 
-         failLoCount[pIdx]++; 
-     } 
-     totCount++; 
-     if (totCount >= 1000000) { 
-         std::lock_guard<std::mutex> L(mutex); 
-         if (totCount >= 1000000) 
-             reScaleInternal(2); 
-     } 
- } 
-   
- void 
- FailHighInfo::addPvData(int nSearched, bool alphaChanged) { 
-     if (nSearched >= 0) { 
-         if (alphaChanged && nSearched < NUM_STAT_MOVES) 
-             newAlpha[nSearched]++; 
-     } else { 
-         totPvCount++; 
-         if (totPvCount >= 10000) { 
-             std::lock_guard<std::mutex> L(mutex); 
-             if (totPvCount >= 10000) 
-                 reScalePv(2); 
-         } 
-     } 
- } 
-   
- void 
- FailHighInfo::reScale() { 
-     reScaleInternal(4); 
-     reScalePv(4); 
- } 
-   
- void 
- FailHighInfo::reScaleInternal(int factor) { 
-     for (int i = 0; i < NUM_NODE_TYPES; i++) { 
-         for (int j = 0; j < NUM_STAT_MOVES; j++) { 
-             int val = failHiCount[i].get(j); 
-             failHiCount[i].add(j, val / factor - val); 
-         } 
-         failLoCount[i] = failLoCount[i] / factor; 
-     } 
-     totCount = totCount / factor; 
- } 
-   
- void 
- FailHighInfo::reScalePv(int factor) { 
-     for (int j = 0; j < NUM_STAT_MOVES; j++) 
-         newAlpha[j] = newAlpha[j] / factor; 
-     totPvCount = totPvCount / factor; 
- } 
-   
- void 
- FailHighInfo::print(std::ostream& os) const { 
-     for (int i = 0; i < NUM_NODE_TYPES; i++) { 
-         os << "fhInfo: " << i << ' ' << std::setw(6) << failLoCount[i]; 
-         for (int j = 0; j < NUM_STAT_MOVES; j++) 
-             os << ' ' << std::setw(6) << failHiCount[i].get(j); 
-         os << std::endl; 
-     } 
-     os << "fhInfo: " << NUM_NODE_TYPES << ' ' << std::setw(6) << totPvCount; 
-     for (int j = 0; j < NUM_STAT_MOVES; j++) 
-         os << ' ' << std::setw(6) << newAlpha[j]; 
-     os << std::endl; 
- } 
-   
- // ---------------------------------------------------------------------------- 
-   
- void 
- DepthNpsInfo::print(std::ostream& os, int iterDepth) { 
-     std::lock_guard<std::mutex> L(mutex); 
-     os << "npsInfo: depth:" << iterDepth << " nps0:" << nps0 
-        << " wait:" << waitTime / nSearches << std::endl; 
-     for (int i = SearchConst::MIN_SMP_DEPTH; i < maxDepth; i++) { 
-         os << "npsInfo: d:" << i 
-            << " n:" << npsData[i].nSearches 
-            << " nodes:" << npsData[i].nodes 
-            << " time:" << npsData[i].time 
-            << " nps:" << npsData[i].nodes / npsData[i].time 
-            << " ts:" << npsData[i].nodes / (double)npsData[i].nSearches 
-            << " eff:" << efficiencyInternal(i) << std::endl; 
-     } 
- } 
-