/* $Id: CbcThread.cpp 2699 2019-10-03 14:05:25Z stefan $ */
// Copyright (C) 2002, International Business Machines
// Corporation and others.  All Rights Reserved.
// This code is licensed under the terms of the Eclipse Public License (EPL).

#if defined(_MSC_VER)
// Turn off compiler warning about long names
#pragma warning(disable : 4786)
#endif

#include "CbcConfig.h"

//#define THREAD_DEBUG
#include <string>
#include <cassert>
#include <cmath>
#include <cfloat>

#include "CbcEventHandler.hpp"

#include "OsiSolverInterface.hpp"
#include "OsiRowCutDebugger.hpp"
#include "CbcThread.hpp"
#include "CbcTree.hpp"
#include "CbcHeuristic.hpp"
#include "CbcCutGenerator.hpp"
#include "CbcModel.hpp"
#include "CbcFathom.hpp"
#include "CbcSimpleIntegerDynamicPseudoCost.hpp"
#include "ClpDualRowDantzig.hpp"
#include "OsiAuxInfo.hpp"

#include "CoinTime.hpp"
#ifdef CBC_THREAD
/// Thread functions
static void *doNodesThread(void *voidInfo);
static void *doCutsThread(void *voidInfo);
static void *doHeurThread(void *voidInfo);
// Default Constructor
CbcSpecificThread::CbcSpecificThread()
  : basePointer_(NULL)
  , masterMutex_(NULL)
  , locked_(false)
{
#ifdef CBC_PTHREAD
  pthread_mutex_init(&mutex2_, NULL);
  pthread_cond_init(&condition2_, NULL);
  threadId_.status = 0;
#else
#endif
}
// Useful Constructor
CbcSpecificThread::CbcSpecificThread(CbcSpecificThread *master, pthread_mutex_t *masterMutex)
  : basePointer_(master)
  , masterMutex_(masterMutex)
  , locked_(false)

{
#ifdef CBC_PTHREAD
  pthread_mutex_init(&mutex2_, NULL);
  pthread_cond_init(&condition2_, NULL);
  threadId_.status = 0;
#else
#endif
}
// Useful stuff
void CbcSpecificThread::setUsefulStuff(CbcSpecificThread *master, void *&masterMutex)

{
#ifdef CBC_PTHREAD
  basePointer_ = master;
  if (masterMutex) {
    masterMutex_ = reinterpret_cast< pthread_mutex_t * >(masterMutex);
  } else {
    // create master mutex
    masterMutex_ = new pthread_mutex_t;
    pthread_mutex_init(masterMutex_, NULL);
    masterMutex = reinterpret_cast< void * >(masterMutex_);
  }
#else
#endif
}

CbcSpecificThread::~CbcSpecificThread()
{
#ifdef CBC_PTHREAD
  pthread_mutex_destroy(&mutex2_);
  if (basePointer_ == this) {
    pthread_mutex_destroy(masterMutex_);
    delete masterMutex_;
  }
#else
#endif
}
/*
  Locks a thread if parallel so that stuff like cut pool
  can be updated and/or used.
*/
void CbcSpecificThread::lockThread()
{
#ifdef CBC_PTHREAD
  // Use master mutex
  assert(basePointer_->masterMutex_ == masterMutex_);
  pthread_mutex_lock(masterMutex_);
#else
#endif
}
/*
  Unlocks a thread if parallel to say cut pool stuff not needed
*/
void CbcSpecificThread::unlockThread()
{
#ifdef CBC_PTHREAD
  // Use master mutex
  pthread_mutex_unlock(masterMutex_);
#else
#endif
}
//  Locks a thread for testing whether to start etc
void CbcSpecificThread::lockThread2(bool doAnyway)
{
  if (!locked_ || doAnyway) {
#ifdef CBC_PTHREAD
    pthread_mutex_lock(&mutex2_);
#else
#endif
    locked_ = true;
  }
}
//  Unlocks a thread for testing whether to start etc
void CbcSpecificThread::unlockThread2(bool doAnyway)
{
  if (locked_ || doAnyway) {
#ifdef CBC_PTHREAD
    pthread_mutex_unlock(&mutex2_);
#else
#endif
    locked_ = false;
  }
}
#ifdef HAVE_CLOCK_GETTIME
inline int my_gettime(struct timespec *tp)
{
  return clock_gettime(CLOCK_REALTIME, tp);
}
#else
#ifndef _MSC_VER
inline int my_gettime(struct timespec *tp)
{
  struct timeval tv;
  int ret = gettimeofday(&tv, NULL);
  tp->tv_sec = tv.tv_sec;
  tp->tv_nsec = tv.tv_usec * 1000;
  return ret;
}
#else
inline int my_gettime(struct timespec *tp)
{
  double t = CoinGetTimeOfDay();
  tp->tv_sec = (int)floor(t);
  tp->tv_nsec = (int)((tp->tv_sec - floor(t)) / 1000000.0);
  return 0;
}
#endif
#endif
// Get time
static double getTime()
{
  struct timespec absTime2;
  my_gettime(&absTime2);
  double time2 = static_cast<double>(absTime2.tv_sec)
    + 1.0e-9 * static_cast< double >(absTime2.tv_nsec);
  return time2;
}
// Timed wait in nanoseconds - if negative then seconds
void CbcSpecificThread::timedWait(int time)
{

#ifdef CBC_PTHREAD
  struct timespec absTime;
  my_gettime(&absTime);
  if (time > 0) {
    absTime.tv_nsec += time;
    if (absTime.tv_nsec >= 1000000000) {
      absTime.tv_nsec -= 1000000000;
      absTime.tv_sec++;
    }
  } else {
    absTime.tv_sec -= time;
  }
  pthread_cond_timedwait(&condition2_, &mutex2_, &absTime);
#else
#endif
}
// Signal
void CbcSpecificThread::signal()
{
#ifdef CBC_PTHREAD
  pthread_cond_signal(&condition2_);
#else
#endif
}
// Actually starts a thread
void CbcSpecificThread::startThread(void *(*routine)(void *), CbcThread *thread)
{
#ifdef CBC_PTHREAD
  pthread_create(&(threadId_.thr), NULL, routine, thread);
  threadId_.status = 1;
#else
#endif
}
// Exits thread (from master)
int CbcSpecificThread::exit()
{
#ifdef CBC_PTHREAD
  pthread_cond_signal(&condition2_); // unlock
  return pthread_join(threadId_.thr, NULL);
#else
#endif
}
// Exits thread
void CbcSpecificThread::exitThread()
{
#ifdef CBC_PTHREAD
  pthread_mutex_unlock(&mutex2_);
  pthread_exit(NULL);
#else
#endif
}
// Get status
int CbcSpecificThread::status() const
{
#ifdef CBC_PTHREAD
  return static_cast< int >(threadId_.status);
#else
#endif
}
// Set status
void CbcSpecificThread::setStatus(int value)
{
#ifdef CBC_PTHREAD
  threadId_.status = value;
#else
#endif
}
// Parallel heuristics
void parallelHeuristics(int numberThreads,
  int sizeOfData,
  void *argBundle)
{
  Coin_pthread_t *threadId = new Coin_pthread_t[numberThreads];
  char *args = reinterpret_cast< char * >(argBundle);
  for (int i = 0; i < numberThreads; i++) {
    pthread_create(&(threadId[i].thr), NULL, doHeurThread,
      args + i * sizeOfData);
  }
  // now wait
  for (int i = 0; i < numberThreads; i++) {
    pthread_join(threadId[i].thr, NULL);
  }
  delete[] threadId;
}
// End of specific thread stuff

/// Default constructor
CbcThread::CbcThread()
  : baseModel_(NULL)
  , thisModel_(NULL)
  , node_(NULL)
  , // filled in every time
  createdNode_(NULL)
  , // filled in every time on return
  returnCode_(-1)
  , // -1 available, 0 busy, 1 finished , 2??
  timeLocked_(0.0)
  , timeWaitingToLock_(0.0)
  , timeWaitingToStart_(0.0)
  , timeInThread_(0.0)
  , numberTimesLocked_(0)
  , numberTimesUnlocked_(0)
  , numberTimesWaitingToStart_(0)
  , dantzigState_(0)
  , // 0 unset, -1 waiting to be set, 1 set
  locked_(false)
  , nDeleteNode_(0)
  , delNode_(NULL)
  , maxDeleteNode_(0)
  , nodesThisTime_(0)
  , iterationsThisTime_(0)
  , deterministic_(0)
{
}
void CbcThread::gutsOfDelete()
{
  baseModel_ = NULL;
  thisModel_ = NULL;
  node_ = NULL;
  createdNode_ = NULL;
  delNode_ = NULL;
}
// Destructor
CbcThread::~CbcThread()
{
}
// Fills in useful stuff
void CbcThread::setUsefulStuff(CbcModel *model, int deterministic, CbcModel *baseModel,
  CbcThread *master,
  void *&masterMutex)
{
  baseModel_ = baseModel;
  thisModel_ = model;
  deterministic_ = deterministic;
  threadStuff_.setUsefulStuff(&master->threadStuff_, masterMutex);
  node_ = NULL;
  createdNode_ = NULL;
  master_ = master;
  returnCode_ = -1;
  timeLocked_ = 0.0;
  timeWaitingToLock_ = 0.0;
  timeWaitingToStart_ = 0.0;
  timeInThread_ = 0.0;
  numberTimesLocked_ = 0;
  numberTimesUnlocked_ = 0;
  numberTimesWaitingToStart_ = 0;
  dantzigState_ = 0; // 0 unset, -1 waiting to be set, 1 set
  locked_ = false;
  delNode_ = NULL;
  maxDeleteNode_ = 0;
  nDeleteNode_ = 0;
  nodesThisTime_ = 0;
  iterationsThisTime_ = 0;
  if (model != baseModel) {
    // thread
    thisModel_->setInfoInChild(-3, this);
    if (deterministic_ >= 0)
      thisModel_->moveToModel(baseModel, -1);
    if (deterministic == -1)
      threadStuff_.startThread(doCutsThread, this);
    else
      threadStuff_.startThread(doNodesThread, this);
  }
}
/*
  Locks a thread if parallel so that stuff like cut pool
  can be updated and/or used.
*/
void CbcThread::lockThread()
{
  if (!locked_) {
    double time2 = getTime();
    threadStuff_.lockThread();
    locked_ = true;
    timeWhenLocked_ = getTime();
    timeWaitingToLock_ += timeWhenLocked_ - time2;
    ;
    numberTimesLocked_++;
#ifdef THREAD_DEBUG
    lockCount_++;
#if THREAD_DEBUG > 1
    if (threadNumber_ == -1)
      printf("locking master %d\n", lockCount_);
    else
      printf("locking thread %d %d\n", threadNumber_, lockCount_);
#endif
  } else {
    if (threadNumber_ == -1)
      printf("master already locked %d\n", lockCount_);
    else
      printf("thread already locked %d %d\n", threadNumber_, lockCount_);
#endif
  }
}
/*
  Unlocks a thread if parallel
*/
void CbcThread::unlockThread()
{
  if (locked_) {
    locked_ = false;
    threadStuff_.unlockThread();
    double time2 = getTime();
    timeLocked_ += time2 - timeWhenLocked_;
    numberTimesUnlocked_++;
#ifdef THREAD_DEBUG
#if THREAD_DEBUG > 1
    if (threadNumber_ == -1)
      printf("unlocking master %d\n", lockCount_);
    else
      printf("unlocking thread %d %d\n", threadNumber_, lockCount_);
#endif
  } else {
    if (threadNumber_ == -1)
      printf("master already unlocked %d\n", lockCount_);
    else
      printf("thread already unlocked %d %d\n", threadNumber_, lockCount_);
#endif
  }
}
/* Wait for child to have return code NOT == to currentCode
   type - 0 timed wait
   1 wait
   returns true if return code changed */
bool CbcThread::wait(int type, int currentCode)
{
  if (!type) {
    // just timed wait
    master_->threadStuff_.lockThread2();
    master_->threadStuff_.timedWait(1000000);
    master_->threadStuff_.unlockThread2();
  } else {
    // wait until return code changes
    while (returnCode_ == currentCode) {
      threadStuff_.signal();
      master_->threadStuff_.lockThread2();
      master_->threadStuff_.timedWait(1000000);
      master_->threadStuff_.unlockThread2();
    }
  }
  return (returnCode_ != currentCode);
}
#if 0
pthread_cond_signal(&condition2_);
-
if (!locked_)
{
    pthread_mutex_lock (&mutex2_);
    locked_ = true;
}
-
pthread_cond_timedwait(&condition2_, &mutex2_, &absTime);
-
if (locked_)
{
    pthread_mutex_unlock (&mutex2_);
    locked_ = false;
}
#endif
// Waits until returnCode_ goes to zero
void CbcThread::waitThread()
{
  double time = getTime();
  threadStuff_.lockThread2();
  while (returnCode_) {
    threadStuff_.timedWait(-10); // 10 seconds
  }
  timeWaitingToStart_ += getTime() - time;
  numberTimesWaitingToStart_++;
}
// Just wait for so many nanoseconds
void CbcThread::waitNano(int time)
{
  threadStuff_.lockThread2();
  threadStuff_.timedWait(time);
  threadStuff_.unlockThread2();
}
// Signal child to carry on
void CbcThread::signal()
{
  threadStuff_.signal();
}
// Lock from master with mutex2 and signal before lock
void CbcThread::lockFromMaster()
{
  threadStuff_.signal();
  master_->threadStuff_.lockThread2(true);
}
// Unlock from master with mutex2 and signal after unlock
void CbcThread::unlockFromMaster()
{
  master_->threadStuff_.unlockThread2(true); // unlock anyway
  threadStuff_.signal();
}
// Lock from thread with mutex2 and signal before lock
void CbcThread::lockFromThread()
{
  master_->threadStuff_.signal();
  threadStuff_.lockThread2();
}
// Unlock from thread with mutex2 and signal after unlock
void CbcThread::unlockFromThread()
{
  master_->threadStuff_.signal();
  threadStuff_.unlockThread2();
}
// Exits thread (from master)
int CbcThread::exit()
{
  return threadStuff_.exit();
}
// Exits thread
void CbcThread::exitThread()
{
  threadStuff_.exitThread();
}
// Default constructor
CbcBaseModel::CbcBaseModel()
  : numberThreads_(0)
  , children_(NULL)
  , type_(0)
  , threadCount_(NULL)
  , threadModel_(NULL)
  , numberObjects_(0)
  , saveObjects_(NULL)
  , defaultParallelIterations_(400)
  , defaultParallelNodes_(2)
{
}
// Constructor with model
CbcBaseModel::CbcBaseModel(CbcModel &model, int type)
  : children_(NULL)
  , type_(type)
  , threadCount_(NULL)
  , threadModel_(NULL)
  , numberObjects_(0)
  , saveObjects_(NULL)
  , defaultParallelIterations_(400)
  , defaultParallelNodes_(2)
{
  numberThreads_ = model.getNumberThreads();
  if (numberThreads_) {
    children_ = new CbcThread[numberThreads_ + 1];
    // Do a partial one for base model
    void *mutex_main = NULL;
    children_[numberThreads_].setUsefulStuff(&model, type_, &model,
      children_ + numberThreads_, mutex_main);
#ifdef THREAD_DEBUG
    children_[numberThreads_].threadNumber_ = -1;
    children_[numberThreads_].lockCount_ = 0;
#endif
    threadCount_ = new int[numberThreads_];
    CoinZeroN(threadCount_, numberThreads_);
    threadModel_ = new CbcModel *[numberThreads_ + 1];
    memset(threadStats_, 0, sizeof(threadStats_));
    if (type_ > 0) {
      // May need for deterministic
      numberObjects_ = model.numberObjects();
      saveObjects_ = new OsiObject *[numberObjects_];
      for (int i = 0; i < numberObjects_; i++) {
        saveObjects_[i] = model.object(i)->clone();
      }
    }
    // we don't want a strategy object
    CbcStrategy *saveStrategy = model.strategy();
    model.setStrategy(NULL);
    for (int i = 0; i < numberThreads_; i++) {
      //threadModel_[i] = new CbcModel(model, true);
      threadModel_[i] = model.clone(true);
      threadModel_[i]->synchronizeHandlers(1);
#ifdef COIN_HAS_CLP
      // Solver may need to know about model
      CbcModel *thisModel = threadModel_[i];
      CbcOsiSolver *solver = dynamic_cast< CbcOsiSolver * >(thisModel->solver());
      if (solver)
        solver->setCbcModel(thisModel);
#endif
      children_[i].setUsefulStuff(threadModel_[i], type_, &model,
        children_ + numberThreads_, mutex_main);
#ifdef THREAD_DEBUG
      children_[i].threadNumber_ = i;
      children_[i].lockCount_ = 0;
#endif
    }
    model.setStrategy(saveStrategy);
  }
}
// Stop threads
void CbcBaseModel::stopThreads(int type)
{
  CbcModel *baseModel = children_[0].baseModel();
  if (type < 0) {
    // max nodes ?
    bool finished = false;
    while (!finished) {
      finished = true;
      for (int i = 0; i < numberThreads_; i++) {
        if (abs(children_[i].returnCode()) != 1) {
          children_[i].wait(1, 0);
          finished = false;
        }
      }
    }
    for (int i = 0; i < numberThreads_; i++) {
      baseModel->incrementExtra(threadModel_[i]->getExtraNodeCount(),
        threadModel_[i]->numberExtraIterations(),
        threadModel_[i]->getFathomCount());
      threadModel_[i]->zeroExtra();
    }
    return;
  }
  for (int i = 0; i < numberThreads_; i++) {
    children_[i].wait(1, 0);
    assert(children_[i].returnCode() == -1);
    baseModel->incrementExtra(threadModel_[i]->getExtraNodeCount(),
      threadModel_[i]->numberExtraIterations(),
      threadModel_[i]->getFathomCount());
    threadModel_[i]->setInfoInChild(-2, NULL);
    children_[i].setReturnCode(0);
    children_[i].exit();
    children_[i].setStatus(0);
  }
  // delete models and solvers
  for (int i = 0; i < numberThreads_; i++) {
    threadModel_[i]->setInfoInChild(type_, NULL);
    delete threadModel_[i];
  }
  delete[] children_;
  delete[] threadModel_;
  for (int i = 0; i < numberObjects_; i++)
    delete saveObjects_[i];
  delete[] saveObjects_;
  children_ = NULL;
  threadModel_ = NULL;
  saveObjects_ = NULL;
  numberObjects_ = 0;
  numberThreads_ = 0;
}
// Wait for threads in tree
int CbcBaseModel::waitForThreadsInTree(int type)
{
  CbcModel *baseModel = children_[0].baseModel();
  int anyLeft = 0;
  // May be able to combine parts later
  if (type == 0) {
    bool locked = true;
#ifdef COIN_DEVELOP
    printf("empty\n");
#endif
    // may still be outstanding nodes
    while (true) {
      int iThread;
      for (iThread = 0; iThread < numberThreads_; iThread++) {
        if (children_[iThread].status()) {
          if (children_[iThread].returnCode() == 0) {
            break;
          }
        }
      }
      if (iThread < numberThreads_) {
#ifdef COIN_DEVELOP
        printf("waiting for thread %d code 0\n", iThread);
#endif
        unlockThread();
        locked = false;
        children_[iThread].wait(1, 0);
        assert(children_[iThread].returnCode() == 1);
        threadModel_[iThread]->moveToModel(baseModel, 1);
#ifdef THREAD_PRINT
        printf("off thread2 %d node %x\n", iThread, children_[iThread].node());
#endif
        children_[iThread].setNode(NULL);
        anyLeft = 1;
        assert(children_[iThread].returnCode() == 1);
        if (children_[iThread].dantzigState() == -1) {
          // 0 unset, -1 waiting to be set, 1 set
          children_[iThread].setDantzigState(1);
          CbcModel *model = children_[iThread].thisModel();
          OsiClpSolverInterface *clpSolver2
            = dynamic_cast< OsiClpSolverInterface * >(model->solver());
          assert(clpSolver2);
          ClpSimplex *simplex2 = clpSolver2->getModelPtr();
          ClpDualRowDantzig dantzig;
          simplex2->setDualRowPivotAlgorithm(dantzig);
        }
        // say available
        children_[iThread].setReturnCode(-1);
        threadStats_[4]++;
#ifdef COIN_DEVELOP
        printf("thread %d code now -1\n", iThread);
#endif
        break;
      } else {
#ifdef COIN_DEVELOP
        printf("no threads at code 0 \n");
#endif
        // now check if any have just finished
        for (iThread = 0; iThread < numberThreads_; iThread++) {
          if (children_[iThread].status()) {
            if (children_[iThread].returnCode() == 1)
              break;
          }
        }
        if (iThread < numberThreads_) {
          unlockThread();
          locked = false;
          threadModel_[iThread]->moveToModel(baseModel, 1);
#ifdef THREAD_PRINT
          printf("off thread3 %d node %x\n", iThread, children_[iThread].node());
#endif
          children_[iThread].setNode(NULL);
          anyLeft = 1;
          assert(children_[iThread].returnCode() == 1);
          // say available
          children_[iThread].setReturnCode(-1);
          threadStats_[4]++;
#ifdef COIN_DEVELOP
          printf("thread %d code now -1\n", iThread);
#endif
          break;
        }
      }
      if (!baseModel->tree()->empty()) {
#ifdef COIN_DEVELOP
        printf("tree not empty!!!!!!\n");
#endif
        if (locked)
          unlockThread();
        return 1;
        break;
      }
      for (iThread = 0; iThread < numberThreads_; iThread++) {
        if (children_[iThread].status()) {
          if (children_[iThread].returnCode() != -1) {
            printf("bad end of tree\n");
            abort();
          }
        }
      }
      break;
    }
#ifdef COIN_DEVELOP
    printf("finished ************\n");
#endif
    if (locked)
      unlockThread();
    return anyLeft;
  } else if (type == 1) {
    // normal
    double cutoff = baseModel->getCutoff();
    CbcNode *node = baseModel->tree()->bestNode(cutoff);
    // Possible one on tree worse than cutoff
    if (!node || node->objectiveValue() > cutoff)
      return 1;
    threadStats_[0]++;
    //need to think
    int iThread;
    // Start one off if any available
    for (iThread = 0; iThread < numberThreads_; iThread++) {
      if (children_[iThread].returnCode() == -1) {
        break;
      }
    }
    if (iThread < numberThreads_) {
      children_[iThread].setNode(node);
#ifdef THREAD_PRINT
      printf("empty thread %d node %x\n", iThread, children_[iThread].node());
#endif
      assert(children_[iThread].returnCode() == -1);
      // say in use
      threadModel_[iThread]->moveToModel(baseModel, 0);
      // This has to be AFTER moveToModel
      children_[iThread].setReturnCode(0);
      children_[iThread].signal();
      threadCount_[iThread]++;
    }
    lockThread();
    // see if any finished
    for (iThread = 0; iThread < numberThreads_; iThread++) {
      if (children_[iThread].returnCode() > 0)
        break;
    }
    unlockThread();
    if (iThread < numberThreads_) {
      threadModel_[iThread]->moveToModel(baseModel, 1);
#ifdef THREAD_PRINT
      printf("off thread4 %d node %x\n", iThread, children_[iThread].node());
#endif
      children_[iThread].setNode(NULL);
      anyLeft = 1;
      assert(children_[iThread].returnCode() == 1);
      // say available
      children_[iThread].setReturnCode(-1);
      // carry on
      threadStats_[3]++;
    } else {
      // Start one off if any available
      for (iThread = 0; iThread < numberThreads_; iThread++) {
        if (children_[iThread].returnCode() == -1)
          break;
      }
      if (iThread < numberThreads_) {
        // If any on tree get
        if (!baseModel->tree()->empty()) {
          //node = baseModel->tree()->bestNode(cutoff) ;
          //assert (node);
          threadStats_[1]++;
          return 1; // ** get another node
        }
      }
      // wait (for debug could sleep and use test)
      bool finished = false;
      while (!finished) {
        double time = getTime();
        children_[numberThreads_].wait(0, 0);
        children_[numberThreads_].incrementTimeInThread(getTime() - time);
        for (iThread = 0; iThread < numberThreads_; iThread++) {
          if (children_[iThread].returnCode() > 0) {
            finished = true;
            break;
          } else if (children_[iThread].returnCode() == 0) {
            children_[iThread].signal(); // unlock
          }
        }
      }
      assert(iThread < numberThreads_);
      // move information to model
      threadModel_[iThread]->moveToModel(baseModel, 1);
      anyLeft = 1;
#ifdef THREAD_PRINT
      printf("off thread %d node %x\n", iThread, children_[iThread].node());
#endif
      children_[iThread].setNode(NULL);
      assert(children_[iThread].returnCode() == 1);
      // say available
      children_[iThread].setReturnCode(-1);
    }
    // carry on
    threadStats_[2]++;
    for (int iThread = 0; iThread < numberThreads_; iThread++) {
      if (children_[iThread].status()) {
        if (children_[iThread].returnCode() != -1) {
          anyLeft = 1;
          break;
        }
      }
    }
    return anyLeft;
  } else if (type == 2) {
    if (!baseModel->tree()->empty()) {
      // max nodes ?
      bool finished = false;
      while (!finished) {
        finished = true;
        for (int iThread = 0; iThread < numberThreads_; iThread++) {
          if (children_[iThread].returnCode() == 0) {
            double time = getTime();
            children_[numberThreads_].wait(0, 0);
            children_[numberThreads_].incrementTimeInThread(getTime() - time);
            finished = false;
            children_[iThread].signal(); // unlock
          }
        }
      }
    }
    int i;
    // do statistics
    // Seems to be bug in CoinCpu on Linux - does threads as well despite documentation
    double time = 0.0;
    for (i = 0; i < numberThreads_; i++)
      time += children_[i].timeInThread();
    bool goodTimer = time < (baseModel->getCurrentSeconds());
    for (i = 0; i < numberThreads_; i++) {
      while (children_[i].returnCode() == 0) {
        children_[i].signal();
        double time = getTime();
        children_[numberThreads_].wait(0, 0);
        children_[numberThreads_].incrementTimeInThread(getTime() - time);
      }
      children_[i].lockFromMaster();
      threadModel_[i]->setNumberThreads(0); // say exit
      if (children_[i].deterministic() > 0)
        delete[] children_[i].delNode();
      if (children_[i].node()) {
        delete children_[i].node();
        children_[i].setNode(NULL);
      }
      children_[i].setReturnCode(0);
      children_[i].unlockFromMaster();
#ifndef NDEBUG
      int returnCode = children_[i].exit();
      assert(!returnCode);
#else
      children_[i].exit();
#endif
      children_[i].setStatus(0);
      //else
      threadModel_[i]->moveToModel(baseModel, 2);
      assert(children_[i].numberTimesLocked() == children_[i].numberTimesUnlocked());
      baseModel->messageHandler()->message(CBC_THREAD_STATS, baseModel->messages())
        << "Thread";
      baseModel->messageHandler()->printing(true)
        << i << threadCount_[i] << children_[i].timeWaitingToStart();
      baseModel->messageHandler()->printing(goodTimer) << children_[i].timeInThread();
      baseModel->messageHandler()->printing(false) << 0.0;
      baseModel->messageHandler()->printing(true) << children_[i].numberTimesLocked()
                                                  << children_[i].timeLocked() << children_[i].timeWaitingToLock()
                                                  << CoinMessageEol;
    }
    assert(children_[numberThreads_].numberTimesLocked() == children_[numberThreads_].numberTimesUnlocked());
    baseModel->messageHandler()->message(CBC_THREAD_STATS, baseModel->messages())
      << "Main thread";
    baseModel->messageHandler()->printing(false) << 0 << 0 << 0.0;
    baseModel->messageHandler()->printing(false) << 0.0;
    baseModel->messageHandler()->printing(true) << children_[numberThreads_].timeInThread();
    baseModel->messageHandler()->printing(true) << children_[numberThreads_].numberTimesLocked()
                                                << children_[numberThreads_].timeLocked() << children_[numberThreads_].timeWaitingToLock()
                                                << CoinMessageEol;
    // delete models (here in case some point to others)
    for (i = 0; i < numberThreads_; i++) {
      // make sure handler will be deleted
      threadModel_[i]->setDefaultHandler(true);
      //delete threadModel_[i];
    }
  } else {
    abort();
  }
  return 0;
}
void CbcBaseModel::waitForThreadsInCuts(int type, OsiCuts *eachCuts,
  int whichGenerator)
{
  if (type == 0) {
    // cuts while doing
    bool finished = false;
    int iThread = -1;
    // see if any available
    for (iThread = 0; iThread < numberThreads_; iThread++) {
      if (children_[iThread].returnCode()) {
        finished = true;
        break;
      } else if (children_[iThread].returnCode() == 0) {
        children_[iThread].signal();
      }
    }
    while (!finished) {
      children_[numberThreads_].waitNano(1000000);
      for (iThread = 0; iThread < numberThreads_; iThread++) {
        if (children_[iThread].returnCode() > 0) {
          finished = true;
          break;
        } else if (children_[iThread].returnCode() == 0) {
          children_[iThread].signal();
        }
      }
    }
    assert(iThread < numberThreads_);
    assert(children_[iThread].returnCode());
    // Use dantzigState to signal which generator
    children_[iThread].setDantzigState(whichGenerator);
    // and delNode for eachCuts
    children_[iThread].fakeDelNode(reinterpret_cast< CbcNode ** >(eachCuts));
    // allow to start
    children_[iThread].setReturnCode(0);
    children_[iThread].signal();
  } else if (type == 1) {
    // cuts - finish up
    for (int iThread = 0; iThread < numberThreads_; iThread++) {
      if (children_[iThread].returnCode() == 0) {
        bool finished = false;
        while (!finished) {
          children_[numberThreads_].wait(0, 0);
          if (children_[iThread].returnCode() > 0) {
            finished = true;
            break;
            //#ifndef NEW_STYLE_PTHREAD
            //} else if (children_[iThread].returnCode_ == 0) {
            //pthread_cond_signal(&children_[iThread].threadStuff_.condition2_); // unlock
            //#endif
          }
        }
      }
      assert(children_[iThread].returnCode());
      // say available
      children_[iThread].setReturnCode(-1);
      //delete threadModel_[iThread]->solver();
      //threadModel_[iThread]->setSolver(NULL);
    }
  } else {
    abort();
  }
}
// Returns pointer to master thread
CbcThread *
CbcBaseModel::masterThread() const
{
  return children_ + numberThreads_;
}

// Split model and do work in deterministic parallel
void CbcBaseModel::deterministicParallel()
{
  CbcModel *baseModel = children_[0].baseModel();
  for (int i = 0; i < numberThreads_; i++)
    threadCount_[i]++;
  int saveTreeSize = baseModel->tree()->size();
  // For now create threadModel - later modify splitModel
  CbcModel **threadModel = new CbcModel *[numberThreads_];
  int iThread;
  for (iThread = 0; iThread < numberThreads_; iThread++)
    threadModel[iThread] = children_[iThread].thisModel();

  int nAffected = baseModel->splitModel(numberThreads_, threadModel, defaultParallelNodes_);
  // do all until finished
  for (iThread = 0; iThread < numberThreads_; iThread++) {
    // obviously tune
    children_[iThread].setNDeleteNode(defaultParallelIterations_);
  }
  // Save current state
  int iObject;
  OsiObject **object = baseModel->objects();
  for (iObject = 0; iObject < numberObjects_; iObject++) {
    saveObjects_[iObject]->updateBefore(object[iObject]);
  }
  //#define FAKE_PARALLEL
#ifndef FAKE_PARALLEL
  for (iThread = 0; iThread < numberThreads_; iThread++) {
    children_[iThread].setReturnCode(0);
    children_[iThread].signal();
  }
  // wait
  bool finished = false;
  double time = getTime();
  while (!finished) {
    children_[numberThreads_].waitNano(1000000); // millisecond
    finished = true;
    for (iThread = 0; iThread < numberThreads_; iThread++) {
      if (children_[iThread].returnCode() <= 0) {
        finished = false;
      }
    }
  }
  for (iThread = 0; iThread < numberThreads_; iThread++)
    children_[iThread].setReturnCode(-1);
#else
  // wait
  bool finished = false;
  double time = getTime();
  for (iThread = 0; iThread < numberThreads_; iThread++) {
    children_[iThread].setReturnCode(0);
    children_[iThread].signal();
    while (!finished) {
      children_[numberThreads_].waitNano(1000000); // millisecond
      finished = (children_[iThread].returnCode() > 0);
    }
    children_[iThread].setReturnCode(-1);
    finished = false;
  }
#endif
  children_[numberThreads_].incrementTimeInThread(getTime() - time);
  // Unmark marked
  for (int i = 0; i < nAffected; i++) {
    baseModel->walkback()[i]->unmark();
  }
  int iModel;
  double scaleFactor = 1.0;
  for (iModel = 0; iModel < numberThreads_; iModel++) {
    //printf("model %d tree size %d\n",iModel,threadModel[iModel]->baseModel->tree()->size());
    if (saveTreeSize > 4 * numberThreads_ * defaultParallelNodes_) {
      if (!threadModel[iModel]->tree()->size()) {
        scaleFactor *= 1.05;
      }
    }
    threadModel[iModel]->moveToModel(baseModel, 11);
    // Update base model
    OsiObject **threadObject = threadModel[iModel]->objects();
    for (iObject = 0; iObject < numberObjects_; iObject++) {
      object[iObject]->updateAfter(threadObject[iObject], saveObjects_[iObject]);
    }
  }
  if (scaleFactor != 1.0) {
    int newNumber = static_cast< int >(defaultParallelNodes_ * scaleFactor + 0.5001);
    if (newNumber * 2 < defaultParallelIterations_) {
      if (defaultParallelNodes_ == 1)
        newNumber = 2;
      if (newNumber != defaultParallelNodes_) {
        char general[200];
        sprintf(general, "Changing tree size from %d to %d",
          defaultParallelNodes_, newNumber);
        baseModel->messageHandler()->message(CBC_GENERAL,
          baseModel->messages())
          << general << CoinMessageEol;
        defaultParallelNodes_ = newNumber;
      }
    }
  }
  delete[] threadModel;
}
// Destructor
CbcBaseModel::~CbcBaseModel()
{
  delete[] threadCount_;
#if 1
  for (int i = 0; i < numberThreads_; i++)
    delete threadModel_[i];
  delete[] threadModel_;
  delete[] children_;
#endif
  for (int i = 0; i < numberObjects_; i++)
    delete saveObjects_[i];
  delete[] saveObjects_;
}
// Sets Dantzig state in children
void CbcBaseModel::setDantzigState()
{
  for (int i = 0; i < numberThreads_; i++) {
    children_[i].setDantzigState(-1);
  }
}
static void *doNodesThread(void *voidInfo)
{
  CbcThread *stuff = reinterpret_cast< CbcThread * >(voidInfo);
  CbcModel *thisModel = stuff->thisModel();
  CbcModel *baseModel = stuff->baseModel();
  while (true) {
    stuff->waitThread();
    //printf("start node %x\n",stuff->node);
    int mode = thisModel->getNumberThreads();
    if (mode) {
      // normal
      double time2 = CoinCpuTime();
      assert(stuff->returnCode() == 0);
      if (thisModel->parallelMode() >= 0) {
        CbcNode *node = stuff->node();
        //assert (node->nodeInfo());
        CbcNode *createdNode = stuff->createdNode();
        // try and see if this has slipped through
        if (node) {
          thisModel->doOneNode(baseModel, node, createdNode);
        } else {
          //printf("null node\n");
          createdNode = NULL;
        }
        stuff->setNode(node);
        stuff->setCreatedNode(createdNode);
        stuff->setReturnCode(1);
      } else {
        assert(!stuff->node());
        assert(!stuff->createdNode());
        int numberIterations = stuff->nDeleteNode();
        int nDeleteNode = 0;
        int maxDeleteNode = stuff->maxDeleteNode();
        CbcNode **delNode = stuff->delNode();
        int returnCode = 1;
        // this should be updated by heuristics strong branching etc etc
        assert(numberIterations > 0);
        thisModel->setNumberThreads(0);
        int nodesThisTime = thisModel->getNodeCount();
        int iterationsThisTime = thisModel->getIterationCount();
        int strongThisTime = thisModel->numberStrongIterations();
        thisModel->setStopNumberIterations(thisModel->getIterationCount() + numberIterations);
        int numberColumns = thisModel->getNumCols();
        int *used = CoinCopyOfArray(thisModel->usedInSolution(), numberColumns);
        int numberSolutions = thisModel->getSolutionCount();
        while (true) {
          if (thisModel->tree()->empty()) {
            returnCode = 1 + 1;
#ifdef CLP_INVESTIGATE_2
            printf("%x tree empty - time %18.6f\n", thisModel, CoinGetTimeOfDay() - 1.2348e9);
#endif
            break;
          }
#define NODE_ITERATIONS 2
          int nodesNow = thisModel->getNodeCount();
          int iterationsNow = thisModel->getIterationCount();
          int strongNow = thisModel->numberStrongIterations();
          bool exit1 = (NODE_ITERATIONS * ((nodesNow - nodesThisTime) + ((strongNow - strongThisTime) >> 1)) + (iterationsNow - iterationsThisTime) > numberIterations);
          //bool exit2 =(thisModel->getIterationCount()>thisModel->getStopNumberIterations()) ;
          //assert (exit1==exit2);
          if (exit1 && nodesNow - nodesThisTime >= 10) {
            // out of loop
            //printf("out of loop\n");
#ifdef CLP_INVESTIGATE3
            printf("%x tree %d nodes left, done %d and %d its - time %18.6f\n", thisModel,
              thisModel->tree()->size(), nodesNow - nodesThisTime,
              iterationsNow - iterationsThisTime, CoinGetTimeOfDay() - 1.2348e9);
#endif
            break;
          }
          double cutoff = thisModel->getCutoff();
          CbcNode *node = thisModel->tree()->bestNode(cutoff);
          // Possible one on tree worse than cutoff
          if (!node)
            continue;
          CbcNode *createdNode = NULL;
          // Do real work of node
          thisModel->doOneNode(NULL, node, createdNode);
          assert(createdNode);
          if (!createdNode->active()) {
            delete createdNode;
          } else {
            // Say one more pointing to this **** postpone if marked
            node->nodeInfo()->increment();
            thisModel->tree()->push(createdNode);
          }
          if (node->active()) {
            assert(node->nodeInfo());
            if (node->nodeInfo()->numberBranchesLeft()) {
              thisModel->tree()->push(node);
            } else {
              node->setActive(false);
            }
          } else {
            if (node->nodeInfo()) {
              if (!node->nodeInfo()->numberBranchesLeft())
                node->nodeInfo()->allBranchesGone(); // can clean up
              // So will delete underlying stuff
              node->setActive(true);
            }
            if (nDeleteNode == maxDeleteNode) {
              maxDeleteNode = (3 * maxDeleteNode) / 2 + 10;
              stuff->setMaxDeleteNode(maxDeleteNode);
              stuff->setDelNode(new CbcNode *[maxDeleteNode]);
              for (int i = 0; i < nDeleteNode; i++)
                stuff->delNode()[i] = delNode[i];
              delete[] delNode;
              delNode = stuff->delNode();
            }
            delNode[nDeleteNode++] = node;
          }
        }
        // end of this sub-tree
        int *usedA = thisModel->usedInSolution();
        for (int i = 0; i < numberColumns; i++) {
          usedA[i] -= used[i];
        }
        delete[] used;
        thisModel->setSolutionCount(thisModel->getSolutionCount() - numberSolutions);
        stuff->setNodesThisTime(thisModel->getNodeCount() - nodesThisTime);
        stuff->setIterationsThisTime(thisModel->getIterationCount() - iterationsThisTime);
        stuff->setNDeleteNode(nDeleteNode);
        stuff->setReturnCode(returnCode);
        thisModel->setNumberThreads(mode);
      }
      //printf("end node %x\n",stuff->node);
      stuff->unlockFromThread();
      stuff->incrementTimeInThread(CoinCpuTime() - time2);
    } else {
      // exit
      break;
    }
  }
  //printf("THREAD exiting\n");
  stuff->exitThread();
  return NULL;
}
static void *doHeurThread(void *voidInfo)
{
  typedef struct {
    double solutionValue;
    CbcModel *model;
    double *solution;
    int foundSol;
  } argBundle;
  argBundle *stuff = reinterpret_cast< argBundle * >(voidInfo);
  stuff->foundSol = stuff->model->heuristic(0)->solution(stuff->solutionValue,
    stuff->solution);
  return NULL;
}
static void *doCutsThread(void *voidInfo)
{
  CbcThread *stuff = reinterpret_cast< CbcThread * >(voidInfo);
  CbcModel *thisModel = stuff->thisModel();
  while (true) {
    stuff->waitThread();
    //printf("start node %x\n",stuff->node);
    int mode = thisModel->getNumberThreads();
    if (mode) {
      // normal
      assert(stuff->returnCode() == 0);
      int fullScan = thisModel->getNodeCount() == 0 ? 1 : 0; //? was >0
      CbcCutGenerator *generator = thisModel->cutGenerator(stuff->dantzigState());
      generator->refreshModel(thisModel);
      OsiCuts *cuts = reinterpret_cast< OsiCuts * >(stuff->delNode());
      OsiSolverInterface *thisSolver = thisModel->solver();
      generator->generateCuts(*cuts, fullScan, thisSolver, NULL);
      stuff->setReturnCode(1);
      stuff->unlockFromThread();
    } else {
      // exit
      break;
    }
  }
  stuff->exitThread();
  return NULL;
}
// Split up nodes - returns number of CbcNodeInfo's affected
int CbcModel::splitModel(int numberModels, CbcModel **model,
  int numberNodes)
{
  int iModel;
  int i;
  for (iModel = 0; iModel < numberModels; iModel++) {
    CbcModel *otherModel = model[iModel];
    otherModel->moveToModel(this, 10);
    assert(!otherModel->tree()->size());
    otherModel->tree()->resetNodeNumbers();
    otherModel->bestPossibleObjective_ = bestPossibleObjective_;
    otherModel->sumChangeObjective1_ = sumChangeObjective1_;
    otherModel->sumChangeObjective2_ = sumChangeObjective2_;
    int numberColumns = solver_->getNumCols();
    if (otherModel->bestSolution_) {
      assert(bestSolution_);
      memcpy(otherModel->bestSolution_, bestSolution_, numberColumns * sizeof(double));
    } else if (bestSolution_) {
      otherModel->bestSolution_ = CoinCopyOfArray(bestSolution_, numberColumns);
    }
    otherModel->globalCuts_ = globalCuts_;
    otherModel->numberSolutions_ = numberSolutions_;
    otherModel->numberHeuristicSolutions_ = numberHeuristicSolutions_;
    otherModel->numberNodes_ = numberNodes_;
    otherModel->numberIterations_ = numberIterations_;
#ifdef JJF_ZERO
    if (maximumNumberCuts_ > otherModel->maximumNumberCuts_) {
      otherModel->maximumNumberCuts_ = maximumNumberCuts_;
      delete[] otherModel->addedCuts_;
      otherModel->addedCuts_ = new CbcCountRowCut *[maximumNumberCuts_];
    }
    if (maximumDepth_ > otherModel->maximumDepth_) {
      otherModel->maximumDepth_ = maximumDepth_;
      delete[] otherModel->walkback_;
      otherModel->walkback_ = new CbcNodeInfo *[maximumDepth_];
    }
#endif
    otherModel->currentNumberCuts_ = currentNumberCuts_;
    if (otherModel->usedInSolution_) {
      assert(usedInSolution_);
      memcpy(otherModel->usedInSolution_, usedInSolution_, numberColumns * sizeof(int));
    } else if (usedInSolution_) {
      otherModel->usedInSolution_ = CoinCopyOfArray(usedInSolution_, numberColumns);
    }
    /// ??? tree_;
    // Need flag (stopNumberIterations_>0?) which says don't update cut etc counts
    for (i = 0; i < numberObjects_; i++) {
      otherModel->object_[i]->updateBefore(object_[i]);
    }
    otherModel->maximumDepthActual_ = maximumDepthActual_;
    // Real cuts are in node info
    otherModel->numberOldActiveCuts_ = numberOldActiveCuts_;
    otherModel->numberNewCuts_ = numberNewCuts_;
    otherModel->numberStrongIterations_ = numberStrongIterations_;
  }
  double cutoff = getCutoff();
  int nAffected = 0;
  while (!tree_->empty()) {
    for (iModel = 0; iModel < numberModels; iModel++) {
      if (tree_->empty())
        break;
      CbcModel *otherModel = model[iModel];
      CbcNode *node = tree_->bestNode(cutoff);
      CbcNodeInfo *nodeInfo = node->nodeInfo();
      assert(nodeInfo);
      if (!nodeInfo->marked()) {
        //while (nodeInfo&&!nodeInfo->marked()) {
        if (nAffected == maximumDepth_) {
          redoWalkBack();
        }
        nodeInfo->mark();
        //nodeInfo->incrementCuts(1000000);
        walkback_[nAffected++] = nodeInfo;
        //nodeInfo = nodeInfo->parent() ;
        //}
      }
      // Make node join otherModel
      OsiBranchingObject *bobj = node->modifiableBranchingObject();
      CbcBranchingObject *cbcobj = dynamic_cast< CbcBranchingObject * >(bobj);
      //assert (cbcobj);
      if (cbcobj) {
        CbcObject *object = cbcobj->object();
        assert(object);
        int position = object->position();
        assert(position >= 0);
        assert(object_[position] == object);
        CbcObject *objectNew = dynamic_cast< CbcObject * >(otherModel->object_[position]);
        cbcobj->setOriginalObject(objectNew);
      }
      otherModel->tree_->push(node);
    }
    numberNodes--;
    if (!numberNodes)
      break;
  }
  return nAffected;
}
// Start threads
void CbcModel::startSplitModel(int /*numberIterations*/)
{
  abort();
}
// Merge models
void CbcModel::mergeModels(int /*numberModel*/, CbcModel ** /*model*/,
  int /*numberNodes*/)
{
  abort();
}
/* Move/copy information from one model to another
   -1 - initial setup
   0 - from base model
   1 - to base model (and reset)
   2 - add in final statistics etc (and reset so can do clean destruction)
   10 - from base model (deterministic)
   11 - to base model (deterministic)
*/
void CbcModel::moveToModel(CbcModel *baseModel, int mode)
{
#ifdef THREAD_DEBUG
  {
    CbcThread *stuff = reinterpret_cast< CbcThread * >(masterThread_);
    if (stuff)
      printf("mode %d node_ %p createdNode_ %p - stuff %p\n",
        mode, stuff->node(), stuff->createdNode(), stuff);
    else
      printf("mode %d null stuff\n", mode);
  }
#endif
  if (mode == 0) {
    setCutoff(baseModel->getCutoff());
    bestObjective_ = baseModel->bestObjective_;
    //assert (!baseModel->globalCuts_.sizeRowCuts());
    if (numberSolutions_ < baseModel->numberSolutions_ && baseModel->bestSolution_) {
      int numberColumns = solver_->getNumCols();
      if (!bestSolution_)
        bestSolution_ = new double[numberColumns];
      memcpy(bestSolution_, baseModel->bestSolution_,
        numberColumns * sizeof(double));
      numberSolutions_ = baseModel->numberSolutions_;
    }
    stateOfSearch_ = baseModel->stateOfSearch_;
    numberNodes_ = baseModel->numberNodes_;
    numberIterations_ = baseModel->numberIterations_;
    numberFixedAtRoot_ = numberIterations_; // for statistics
    numberSolves_ = 0;
    phase_ = baseModel->phase_;
    assert(!nextRowCut_);
    nodeCompare_ = baseModel->nodeCompare_;
    tree_ = baseModel->tree_;
    assert(!subTreeModel_);
    //branchingMethod_ = NULL; // need something but what
    numberOldActiveCuts_ = baseModel->numberOldActiveCuts_;
    cutModifier_ = NULL;
    assert(!analyzeResults_);
    CbcThread *stuff = reinterpret_cast< CbcThread * >(masterThread_);
    assert(stuff);
    //if (stuff)
    stuff->setCreatedNode(NULL);
    // ?? searchStrategy_;
    searchStrategy_ = baseModel->searchStrategy_;
    stuff->saveStuff()[0] = searchStrategy_;
    stateOfSearch_ = baseModel->stateOfSearch_;
    stuff->saveStuff()[1] = stateOfSearch_;
    for (int iObject = 0; iObject < numberObjects_; iObject++) {
      CbcSimpleIntegerDynamicPseudoCost *dynamicObject = dynamic_cast< CbcSimpleIntegerDynamicPseudoCost * >(object_[iObject]);
      if (dynamicObject) {
        CbcSimpleIntegerDynamicPseudoCost *baseObject = dynamic_cast< CbcSimpleIntegerDynamicPseudoCost * >(baseModel->object_[iObject]);
        assert(baseObject);
        dynamicObject->copySome(baseObject);
      }
    }
    // add new global cuts
    CbcRowCuts *baseGlobal = baseModel->globalCuts();
    CbcRowCuts *thisGlobal = globalCuts();
    int baseNumberCuts = baseGlobal->sizeRowCuts();
    int thisNumberCuts = thisGlobal->sizeRowCuts();
    for (int i = thisNumberCuts; i < baseNumberCuts; i++) {
      thisGlobal->addCutIfNotDuplicate(*baseGlobal->cut(i));
    }
    numberGlobalCutsIn_ = baseNumberCuts;
  } else if (mode == 1) {
    lockThread();
    CbcThread *stuff = reinterpret_cast< CbcThread * >(masterThread_);
    assert(stuff);
    // deal with hotstart
    static int lastHotDepth = -1;
    if (baseModel->hotstartSolution_) {
      if (!baseModel->numberNodes_) {
        lastHotDepth = -1;
      } else if (stuff->node()) {
        if (stuff->node()->depth() >= lastHotDepth) {
          lastHotDepth = stuff->node()->depth();
          //printf("hotdepth %d\n",lastHotDepth);
        } else {
          // switch off
          delete[] hotstartSolution_;
          hotstartSolution_ = NULL;
          delete[] baseModel->hotstartSolution_;
          baseModel->hotstartSolution_ = NULL;
          //printf("off hotstart\n");
        }
      }
    }
    //stateOfSearch_
    if (stuff->saveStuff()[0] != searchStrategy_) {
#ifdef COIN_DEVELOP
      printf("changing searchStrategy from %d to %d\n",
        baseModel->searchStrategy_, searchStrategy_);
#endif
      baseModel->searchStrategy_ = searchStrategy_;
    }
    if (stuff->saveStuff()[1] != stateOfSearch_) {
#ifdef COIN_DEVELOP
      printf("changing stateOfSearch from %d to %d\n",
        baseModel->stateOfSearch_, stateOfSearch_);
#endif
      baseModel->stateOfSearch_ = stateOfSearch_;
    }
    if (numberUpdateItems_) {
      for (int i = 0; i < numberUpdateItems_; i++) {
        CbcObjectUpdateData *update = updateItems_ + i;
        int objectNumber = update->objectNumber_;
        CbcObject *object = dynamic_cast< CbcObject * >(baseModel->object_[objectNumber]);
        if (object)
          object->updateInformation(*update);
      }
      numberUpdateItems_ = 0;
    }
    if (eventHappened_)
      baseModel->eventHappened_ = true;
    baseModel->numberNodes_++;
    baseModel->numberIterations_ += numberIterations_ - numberFixedAtRoot_;
    baseModel->numberSolves_ += numberSolves_;
    if (stuff->node())
      baseModel->tree_->push(stuff->node());
    if (stuff->createdNode())
      baseModel->tree_->push(stuff->createdNode());
    // add new global cuts to base and take off
    CbcRowCuts *baseGlobal = baseModel->globalCuts();
    CbcRowCuts *thisGlobal = globalCuts();
    int thisNumberCuts = thisGlobal->sizeRowCuts();
    for (int i = thisNumberCuts - 1; i >= numberGlobalCutsIn_; i--) {
      baseGlobal->addCutIfNotDuplicate(*thisGlobal->cut(i), thisGlobal->cut(i)->whichRow());
      thisGlobal->eraseRowCut(i);
    }
    //thisGlobal->truncate(numberGlobalCutsIn_);
    numberGlobalCutsIn_ = 999999;
    unlockThread();
  } else if (mode == 2) {
    baseModel->sumChangeObjective1_ += sumChangeObjective1_;
    baseModel->sumChangeObjective2_ += sumChangeObjective2_;
    //baseModel->numberIterations_ += numberIterations_;
    for (int iGenerator = 0; iGenerator < numberCutGenerators_; iGenerator++) {
      CbcCutGenerator *generator = baseModel->generator_[iGenerator];
      CbcCutGenerator *generator2 = generator_[iGenerator];
      generator->incrementNumberTimesEntered(generator2->numberTimesEntered());
      generator->incrementNumberCutsInTotal(generator2->numberCutsInTotal());
      generator->incrementNumberCutsActive(generator2->numberCutsActive());
      generator->incrementTimeInCutGenerator(generator2->timeInCutGenerator());
    }
    if (parallelMode() >= 0)
      nodeCompare_ = NULL;
    baseModel->maximumDepthActual_ = CoinMax(baseModel->maximumDepthActual_, maximumDepthActual_);
    baseModel->numberDJFixed_ += numberDJFixed_;
    baseModel->numberStrongIterations_ += numberStrongIterations_;
    int i;
    for (i = 0; i < 3; i++)
      baseModel->strongInfo_[i] += strongInfo_[i];
    if (parallelMode() >= 0) {
      walkback_ = NULL;
      lastNodeInfo_ = NULL;
      lastNumberCuts_ = NULL;
      lastCut_ = NULL;
      //addedCuts_ = NULL;
      tree_ = NULL;
    }
    if ((moreSpecialOptions2_ & 32) != 0)
      delete eventHandler_;
    eventHandler_ = NULL;
    delete solverCharacteristics_;
    solverCharacteristics_ = NULL;
    bool newMethod = (baseModel->branchingMethod_ && baseModel->branchingMethod_->chooseMethod());
    if (newMethod) {
      // new method - we were using base models
      numberObjects_ = 0;
      object_ = NULL;
    }
  } else if (mode == -1) {
    delete eventHandler_;
    if ((moreSpecialOptions2_ & 32) == 0 || !baseModel->eventHandler_) {
      eventHandler_ = baseModel->eventHandler_;
    } else {
      eventHandler_ = baseModel->eventHandler_->clone();
      eventHandler_->setModel(this);
    }
    assert(!statistics_);
    assert(baseModel->solverCharacteristics_);
    solverCharacteristics_ = new OsiBabSolver(*baseModel->solverCharacteristics_);
    solverCharacteristics_->setSolver(solver_);
    setMaximumNodes(COIN_INT_MAX);
    if (parallelMode() >= 0) {
      delete[] walkback_;
      //delete [] addedCuts_;
      walkback_ = NULL;
      //addedCuts_ = NULL;
      delete[] lastNodeInfo_;
      lastNodeInfo_ = NULL;
      delete[] lastNumberCuts_;
      lastNumberCuts_ = NULL;
      delete[] lastCut_;
      lastCut_ = NULL;
      delete tree_;
      tree_ = NULL;
      delete nodeCompare_;
      nodeCompare_ = NULL;
    } else {
      delete tree_;
      tree_ = new CbcTree();
      tree_->setComparison(*nodeCompare_);
    }
    delete continuousSolver_;
    continuousSolver_ = baseModel->continuousSolver_->clone();
    // make sure solvers have correct message handler
    solver_->passInMessageHandler(handler_);
    continuousSolver_->passInMessageHandler(handler_);
    bool newMethod = (baseModel->branchingMethod_ && baseModel->branchingMethod_->chooseMethod());
    if (newMethod) {
      // new method uses solver - but point to base model
      // We may update an object in wrong order - shouldn't matter?
      numberObjects_ = baseModel->numberObjects_;
      if (parallelMode() >= 0) {
        object_ = baseModel->object_;
      } else {
        printf("*****WARNING - fix testosi option\n");
        object_ = baseModel->object_;
      }
    }
    int i;
    for (i = 0; i < numberHeuristics_; i++) {
      delete heuristic_[i];
      heuristic_[i] = baseModel->heuristic_[i]->clone();
      heuristic_[i]->setModelOnly(this);
    }
    for (i = 0; i < numberCutGenerators_; i++) {
      bool generatorTiming = baseModel->generator_[i]->timing();
      delete generator_[i];
      generator_[i] = new CbcCutGenerator(*baseModel->generator_[i]);
      // zero out timing
      if (generatorTiming)
	generator_[i]->setTiming(true);
      // refreshModel was overkill as thought too many rows
      if (generator_[i]->needsRefresh())
        generator_[i]->refreshModel(this);
      else
        generator_[i]->setModel(this);
    }
  } else if (mode == 10) {
    setCutoff(baseModel->getCutoff());
    bestObjective_ = baseModel->bestObjective_;
    //assert (!baseModel->globalCuts_.sizeRowCuts());
    numberSolutions_ = baseModel->numberSolutions_;
    assert(usedInSolution_);
    assert(baseModel->usedInSolution_);
    memcpy(usedInSolution_, baseModel->usedInSolution_, solver_->getNumCols() * sizeof(int));
    stateOfSearch_ = baseModel->stateOfSearch_;
    //numberNodes_ = baseModel->numberNodes_;
    //numberIterations_ = baseModel->numberIterations_;
    //numberFixedAtRoot_ = numberIterations_; // for statistics
    phase_ = baseModel->phase_;
    assert(!nextRowCut_);
    delete nodeCompare_;
    nodeCompare_ = baseModel->nodeCompare_->clone();
    tree_->setComparison(*nodeCompare_);
    assert(!subTreeModel_);
    //branchingMethod_ = NULL; // need something but what
    numberOldActiveCuts_ = baseModel->numberOldActiveCuts_;
    cutModifier_ = NULL;
    assert(!analyzeResults_);
    CbcThread *stuff = reinterpret_cast< CbcThread * >(masterThread_);
    assert(stuff);
    //if (stuff)
    stuff->setCreatedNode(NULL);
    // ?? searchStrategy_;
    searchStrategy_ = baseModel->searchStrategy_;
    stuff->saveStuff()[0] = searchStrategy_;
    stateOfSearch_ = baseModel->stateOfSearch_;
    stuff->saveStuff()[1] = stateOfSearch_;
    OsiObject **baseObject = baseModel->object_;
    for (int iObject = 0; iObject < numberObjects_; iObject++) {
      object_[iObject]->updateBefore(baseObject[iObject]);
    }
    //delete [] stuff->nodeCount;
    //stuff->nodeCount = new int [baseModel->maximumDepth_+1];
  } else if (mode == 11) {
    if (parallelMode() < 0) {
      // from deterministic
      CbcThread *stuff = reinterpret_cast< CbcThread * >(masterThread_);
      assert(stuff);
      // Move solution etc
      // might as well mark all including continuous
      int numberColumns = solver_->getNumCols();
      for (int i = 0; i < numberColumns; i++) {
        baseModel->usedInSolution_[i] += usedInSolution_[i];
        //usedInSolution_[i]=0;
      }
      baseModel->numberSolutions_ += numberSolutions_;
      if (bestObjective_ < baseModel->bestObjective_ && bestObjective_ < baseModel->getCutoff()) {
        baseModel->bestObjective_ = bestObjective_;
        int numberColumns = solver_->getNumCols();
        if (!baseModel->bestSolution_)
          baseModel->bestSolution_ = new double[numberColumns];
        CoinCopyN(bestSolution_, numberColumns, baseModel->bestSolution_);
        baseModel->setCutoff(getCutoff());
        baseModel->handler_->message(CBC_ROUNDING, messages_)
          << bestObjective_
          << "heuristic"
          << baseModel->numberIterations_
          << baseModel->numberNodes_ << getCurrentSeconds()
          << CoinMessageEol;
      }
      //stateOfSearch_
      if (stuff->saveStuff()[0] != searchStrategy_) {
#ifdef COIN_DEVELOP
        printf("changing searchStrategy from %d to %d\n",
          baseModel->searchStrategy_, searchStrategy_);
#endif
        baseModel->searchStrategy_ = searchStrategy_;
      }
      if (stuff->saveStuff()[1] != stateOfSearch_) {
#ifdef COIN_DEVELOP
        printf("changing stateOfSearch from %d to %d\n",
          baseModel->stateOfSearch_, stateOfSearch_);
#endif
        baseModel->stateOfSearch_ = stateOfSearch_;
      }
      int i;
      if (eventHappened_)
        baseModel->eventHappened_ = true;
      baseModel->numberNodes_ += stuff->nodesThisTime();
      baseModel->numberIterations_ += stuff->iterationsThisTime();
      double cutoff = baseModel->getCutoff();
      while (!tree_->empty()) {
        CbcNode *node = tree_->bestNode(COIN_DBL_MAX);
        if (node->objectiveValue() < cutoff) {
          assert(node->nodeInfo());
          // Make node join correctly
          OsiBranchingObject *bobj = node->modifiableBranchingObject();
          CbcBranchingObject *cbcobj = dynamic_cast< CbcBranchingObject * >(bobj);
          if (cbcobj) {
            CbcObject *object = cbcobj->object();
            assert(object);
            int position = object->position();
            assert(position >= 0);
            assert(object_[position] == object);
            CbcObject *objectNew = dynamic_cast< CbcObject * >(baseModel->object_[position]);
            cbcobj->setOriginalObject(objectNew);
          }
          baseModel->tree_->push(node);
        } else {
          delete node;
        }
      }
      for (i = 0; i < stuff->nDeleteNode(); i++) {
        //printf("CbcNode %x stuff delete\n",stuff->delNode[i]);
        delete stuff->delNode()[i];
      }
    }
  } else {
    abort();
  }
}
// Generate one round of cuts - parallel mode
int CbcModel::parallelCuts(CbcBaseModel *master, OsiCuts &theseCuts,
  CbcNode * /*node*/, OsiCuts &slackCuts, int lastNumberCuts)
{
  /*
      Is it time to scan the cuts in order to remove redundant cuts? If so, set
      up to do it.
    */
  int fullScan = 0;
  if ((numberNodes_ % SCANCUTS) == 0 || (specialOptions_ & 256) != 0) {
    fullScan = 1;
    if (!numberNodes_ || (specialOptions_ & 256) != 0)
      fullScan = 2;
    specialOptions_ &= ~256; // mark as full scan done
  }
  // do cuts independently
  OsiCuts *eachCuts = new OsiCuts[numberCutGenerators_];
  ;
  int i;
  assert(master);
  for (i = 0; i < numberThreads_; i++) {
    // set solver here after cloning
    master->model(i)->solver_ = solver_->clone();
    master->model(i)->numberNodes_ = (fullScan) ? 1 : 0;
  }
  // generate cuts
  int status = 0;
  const OsiRowCutDebugger *debugger = NULL;
  bool onOptimalPath = false;
  for (i = 0; i < numberCutGenerators_; i++) {
    bool generate = generator_[i]->normal();
    // skip if not optimal and should be (maybe a cut generator has fixed variables)
    if (generator_[i]->needsOptimalBasis() && !solver_->basisIsAvailable())
      generate = false;
    if (generator_[i]->switchedOff())
      generate = false;
    ;
    if (generate) {
      master->waitForThreadsInCuts(0, eachCuts + i, i);
    }
  }
  // wait
  master->waitForThreadsInCuts(1, eachCuts, 0);
  // Now put together
  for (i = 0; i < numberCutGenerators_; i++) {
    // add column cuts
    int numberColumnCutsBefore = theseCuts.sizeColCuts();
    int numberColumnCuts = eachCuts[i].sizeColCuts();
    int numberColumnCutsAfter = numberColumnCutsBefore
      + numberColumnCuts;
    int j;
    for (j = 0; j < numberColumnCuts; j++) {
      theseCuts.insert(eachCuts[i].colCut(j));
    }
    int numberRowCutsBefore = theseCuts.sizeRowCuts();
    int numberRowCuts = eachCuts[i].sizeRowCuts();
    // insert good cuts
    if (numberRowCuts) {
      int n = numberRowCuts;
      numberRowCuts = 0;
      for (j = 0; j < n; j++) {
        const OsiRowCut *thisCut = eachCuts[i].rowCutPtr(j);
        if (thisCut->lb() <= 1.0e10 && thisCut->ub() >= -1.0e10) {
          theseCuts.insert(eachCuts[i].rowCut(j));
          numberRowCuts++;
        }
      }
      if (generator_[i]->mustCallAgain() && status >= 0)
        status = 1; // say must go round
    }
    int numberRowCutsAfter = numberRowCutsBefore
      + numberRowCuts;
    if (numberRowCuts) {
      // Check last cut to see if infeasible
      const OsiRowCut *thisCut = theseCuts.rowCutPtr(numberRowCutsAfter - 1);
      if (thisCut->lb() > thisCut->ub()) {
        status = -1; // sub-problem is infeasible
        break;
      }
    }
#ifdef CBC_DEBUG
    {
      int k;
      for (k = numberRowCutsBefore; k < numberRowCutsAfter; k++) {
        OsiRowCut thisCut = theseCuts.rowCut(k);
        /* check size of elements.
                   We can allow smaller but this helps debug generators as it
                   is unsafe to have small elements */
        int n = thisCut.row().getNumElements();
        const int *column = thisCut.row().getIndices();
        const double *element = thisCut.row().getElements();
        //assert (n);
        for (int i = 0; i < n; i++) {
          double value = element[i];
          assert(fabs(value) > 1.0e-12 && fabs(value) < 1.0e20);
        }
      }
    }
#endif
    if ((specialOptions_ & 1) != 0) {
      if (onOptimalPath) {
        int k;
        for (k = numberRowCutsBefore; k < numberRowCutsAfter; k++) {
          OsiRowCut thisCut = theseCuts.rowCut(k);
          if (debugger->invalidCut(thisCut)) {
            solver_->getRowCutDebuggerAlways()->printOptimalSolution(*solver_);
            solver_->writeMpsNative("badCut.mps", NULL, NULL, 2);
#ifdef NDEBUG
            printf("Cut generator %d (%s) produced invalid cut (%dth in this go)\n",
              i, generator_[i]->cutGeneratorName(), k - numberRowCutsBefore);
            const double *lower = getColLower();
            const double *upper = getColUpper();
            int numberColumns = solver_->getNumCols();
            for (int i = 0; i < numberColumns; i++)
              printf("%d bounds %g,%g\n", i, lower[i], upper[i]);
            abort();
#endif
          }
          assert(!debugger->invalidCut(thisCut));
        }
      }
    }
    /*
          The cut generator has done its thing, and maybe it generated some
          cuts.  Do a bit of bookkeeping: load
          whichGenerator[i] with the index of the generator responsible for a cut,
          and place cuts flagged as global in the global cut pool for the model.

          lastNumberCuts is the sum of cuts added in previous iterations; it's the
          offset to the proper starting position in whichGenerator.
        */
    int numberBefore = numberRowCutsBefore + numberColumnCutsBefore + lastNumberCuts;
    int numberAfter = numberRowCutsAfter + numberColumnCutsAfter + lastNumberCuts;
    // possibly extend whichGenerator
    resizeWhichGenerator(numberBefore, numberAfter);

    for (j = numberRowCutsBefore; j < numberRowCutsAfter; j++) {
      whichGenerator_[numberBefore++] = i;
      const OsiRowCut *thisCut = theseCuts.rowCutPtr(j);
      if (thisCut->lb() > thisCut->ub())
        status = -1; // sub-problem is infeasible
      if (thisCut->globallyValid()) {
        // add to global list
        OsiRowCut newCut(*thisCut);
        newCut.setGloballyValid(true);
        newCut.mutableRow().setTestForDuplicateIndex(false);
        globalCuts_.addCutIfNotDuplicate(newCut);
      }
    }
    for (j = numberColumnCutsBefore; j < numberColumnCutsAfter; j++) {
      //whichGenerator_[numberBefore++] = i ;
      const OsiColCut *thisCut = theseCuts.colCutPtr(j);
      if (thisCut->globallyValid()) {
        // add to global list
        makeGlobalCut(thisCut);
      }
    }
  }
  // Add in any violated saved cuts
  if (!theseCuts.sizeRowCuts() && !theseCuts.sizeColCuts()) {
    int numberOld = theseCuts.sizeRowCuts() + lastNumberCuts;
    int numberCuts = slackCuts.sizeRowCuts();
    int i;
    // possibly extend whichGenerator
    resizeWhichGenerator(numberOld, numberOld + numberCuts);
    double primalTolerance;
    solver_->getDblParam(OsiPrimalTolerance, primalTolerance);
    for (i = 0; i < numberCuts; i++) {
      const OsiRowCut *thisCut = slackCuts.rowCutPtr(i);
      if (thisCut->violated(cbcColSolution_) > 100.0 * primalTolerance) {
        if (messageHandler()->logLevel() > 2)
          printf("Old cut added - violation %g\n",
            thisCut->violated(cbcColSolution_));
        whichGenerator_[numberOld++] = 999;
        theseCuts.insert(*thisCut);
      }
    }
  }
  delete[] eachCuts;
  return status;
}
/*
  Locks a thread if parallel so that stuff like cut pool
  can be updated and/or used.
*/
void CbcModel::lockThread()
{
  if (masterThread_ && (threadMode_ & 1) == 0)
    masterThread_->lockThread();
}
/*
  Unlocks a thread if parallel
*/
void CbcModel::unlockThread()
{
  if (masterThread_ && (threadMode_ & 1) == 0)
    masterThread_->unlockThread();
}
// Returns true if locked
bool CbcModel::isLocked() const
{
  if (masterThread_) {
    return (masterThread_->locked());
  } else {
    return true;
  }
}
// Stop a child
void CbcModel::setInfoInChild(int type, CbcThread *info)
{
  if (type == -3) {
    // set up
    masterThread_ = info;
  } else if (type == -2) {
    numberThreads_ = 0; // signal to stop
  } else {
    // make sure message handler will be deleted
    defaultHandler_ = true;
    ownObjects_ = false;
    delete solverCharacteristics_;
    solverCharacteristics_ = NULL;
    if (type >= 0) {
      delete[] object_; // may be able to when all over to CbcThread
      for (int i = 0; i < numberCutGenerators_; i++) {
        delete generator_[i];
        generator_[i] = NULL;
        //delete virginGenerator_[i];
        //virginGenerator_[i]=NULL;
      }
      //generator_[0] = NULL;
      //delete [] generator_;
      //generator_ = NULL;
      numberCutGenerators_ = 0;
    } else {
      for (int i = 0; i < numberCutGenerators_; i++) {
        generator_[i] = NULL;
      }
    }
    object_ = NULL;
  }
}

/// Indicates whether Cbc library has been compiled with multithreading support
bool CbcModel::haveMultiThreadSupport() { return true; }

#else
// Default constructor
CbcBaseModel::CbcBaseModel() {}

bool CbcModel::haveMultiThreadSupport() { return false; }

bool CbcModel::isLocked() const { return false; }
void CbcModel::lockThread() {}
void CbcModel::unlockThread() {}
void CbcModel::setInfoInChild(int type, CbcThread *info) {}
void CbcModel::moveToModel(CbcModel *baseModel, int mode) {}
int CbcModel::splitModel(int numberModels, CbcModel **model, int numberNodes) { return 0; }
void CbcModel::startSplitModel(int numberIterations) {}
void CbcModel::mergeModels(int numberModel, CbcModel **model, int numberNodes) {}
#endif

/* vi: softtabstop=2 shiftwidth=2 expandtab tabstop=2
*/
