// $Id: CbcHeuristicDW.cpp 1899 2013-04-09 18:12:08Z stefan $
// Copyright (C) 2006, 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 <cassert>
#include <cstdlib>
#include <cmath>
#include <cfloat>

#include "OsiSolverInterface.hpp"
#include "OsiClpSolverInterface.hpp"
#include "CbcModel.hpp"
#include "CbcMessage.hpp"
#include "CbcHeuristicDW.hpp"
#include "CbcStrategy.hpp"
#include "ClpPresolve.hpp"
#include "CglProbing.hpp"

static int dummyCallBack(CbcHeuristicDW * /*heuristic*/,
  CbcModel * /*thisModel*/, int /*whereFrom*/)
{
  return 0;
}

// Default Constructor
CbcHeuristicDW::CbcHeuristicDW()
  : CbcHeuristic()
{
  setDefaults();
}

// Constructor with model - assumed before cuts

CbcHeuristicDW::CbcHeuristicDW(CbcModel &model, int keepContinuous)
  : CbcHeuristic(model)
{
  setDefaults();
  functionPointer_ = dummyCallBack;
  assert(model.solver());
  solver_ = model.solver()->clone();
  findStructure();
}
/* Constructor with model - assumed before cuts
 */
CbcHeuristicDW::CbcHeuristicDW(CbcModel &model,
  int callBack(CbcHeuristicDW *currentHeuristic,
    CbcModel *thisModel,
    int whereFrom),
  int keepContinuous)
  : CbcHeuristic(model)
{
  setDefaults();
  functionPointer_ = callBack;
  assert(model.solver());
  solver_ = model.solver()->clone();
  findStructure();
}
// Set default values
void CbcHeuristicDW::setDefaults()
{
  targetObjective_ = -COIN_DBL_MAX;
  bestObjective_ = COIN_DBL_MAX;
  lastObjective_ = COIN_DBL_MAX;
  fullDWEverySoOften_ = 0;
  numberPasses_ = 0;
  howOften_ = 100;
  decayFactor_ = 0.5;
  functionPointer_ = NULL;
  solver_ = NULL;
  dwSolver_ = NULL;
  bestSolution_ = NULL;
  continuousSolution_ = NULL;
  fixedDj_ = NULL;
  saveLower_ = NULL;
  saveUpper_ = NULL;
  random_ = NULL;
  affinity_ = NULL;
  weights_ = NULL;
  objectiveDW_ = NULL;
  numberColumnsDW_ = NULL;
  whichRowBlock_ = NULL;
  whichColumnBlock_ = NULL;
  dwBlock_ = NULL;
  backwardRow_ = NULL;
  rowsInBlock_ = NULL;
  columnsInBlock_ = NULL;
  startRowBlock_ = NULL;
  startColumnBlock_ = NULL;
  intsInBlock_ = NULL;
  fingerPrint_ = NULL;
  fullDWEverySoOften_ = 0;
  numberPasses_ = 0;
  numberBadPasses_ = COIN_INT_MAX;
  maximumDW_ = 0;
  numberDW_ = 0;
  numberDWTimes_ = 0;
  sizeFingerPrint_ = 0;
  numberMasterColumns_ = 0;
  numberMasterRows_ = 0;
  numberBlocks_ = 0;
  keepContinuous_ = 0;
  phase_ = 0;
  pass_ = 0;
  nNeededBase_ = 200;
  nNodesBase_ = 500;
  nNeeded_ = nNeededBase_;
  nNodes_ = nNodesBase_;
  solveState_ = 0;
}
// Guts of copy
void CbcHeuristicDW::gutsOfCopy(const CbcHeuristicDW &rhs)
{
  targetObjective_ = rhs.targetObjective_;
  bestObjective_ = rhs.bestObjective_;
  lastObjective_ = rhs.lastObjective_;
  fullDWEverySoOften_ = rhs.fullDWEverySoOften_;
  numberPasses_ = rhs.numberPasses_;
  numberBadPasses_ = rhs.numberBadPasses_;
  howOften_ = rhs.howOften_;
  decayFactor_ = rhs.decayFactor_;
  fullDWEverySoOften_ = rhs.fullDWEverySoOften_;
  numberPasses_ = rhs.numberPasses_;
  maximumDW_ = rhs.maximumDW_;
  numberDW_ = rhs.numberDW_;
  numberDWTimes_ = rhs.numberDWTimes_;
  sizeFingerPrint_ = rhs.sizeFingerPrint_;
  numberMasterColumns_ = rhs.numberMasterColumns_;
  numberMasterRows_ = rhs.numberMasterRows_;
  numberBlocks_ = rhs.numberBlocks_;
  keepContinuous_ = rhs.keepContinuous_;
  phase_ = rhs.phase_;
  pass_ = rhs.pass_;
  nNeededBase_ = rhs.nNeededBase_;
  nNodesBase_ = rhs.nNodesBase_;
  nNeeded_ = rhs.nNeeded_;
  nNodes_ = rhs.nNodes_;
  solveState_ = rhs.solveState_;
  functionPointer_ = rhs.functionPointer_;
  if (rhs.solver_)
    solver_ = rhs.solver_->clone();
  else
    solver_ = NULL;
  if (rhs.dwSolver_)
    dwSolver_ = rhs.dwSolver_->clone();
  else
    dwSolver_ = NULL;
  if (rhs.saveLower_) {
    int numberColumns = solver_->getNumCols();
    int numberRows = solver_->getNumRows();
    saveLower_ = CoinCopyOfArray(rhs.saveLower_, numberColumns);
    saveUpper_ = CoinCopyOfArray(rhs.saveUpper_, numberColumns);
    whichColumnBlock_ = CoinCopyOfArray(rhs.whichColumnBlock_, numberColumns);
    columnsInBlock_ = CoinCopyOfArray(rhs.columnsInBlock_, numberColumns);
    whichRowBlock_ = CoinCopyOfArray(rhs.whichRowBlock_, numberRows);
    rowsInBlock_ = CoinCopyOfArray(rhs.rowsInBlock_, numberRows);
    if (rhs.affinity_)
      affinity_ = CoinCopyOfArray(rhs.affinity_, numberBlocks_ * numberBlocks_);
    else
      affinity_ = NULL;
    backwardRow_ = CoinCopyOfArray(rhs.backwardRow_, numberRows);
    startRowBlock_ = CoinCopyOfArray(rhs.startRowBlock_, numberBlocks_ + 1);
    startColumnBlock_ = CoinCopyOfArray(rhs.startColumnBlock_, numberBlocks_ + 1);
    intsInBlock_ = CoinCopyOfArray(rhs.intsInBlock_, numberBlocks_);
  } else {
    saveLower_ = NULL;
    saveUpper_ = NULL;
    affinity_ = NULL;
    whichRowBlock_ = NULL;
    whichColumnBlock_ = NULL;
    backwardRow_ = NULL;
    rowsInBlock_ = NULL;
    columnsInBlock_ = NULL;
    startRowBlock_ = NULL;
    startColumnBlock_ = NULL;
    intsInBlock_ = NULL;
  }
  if (rhs.weights_) {
    assert(maximumDW_);
    weights_ = CoinCopyOfArray(rhs.weights_, maximumDW_);
    random_ = CoinCopyOfArray(rhs.random_, numberMasterRows_);
    dwBlock_ = CoinCopyOfArray(rhs.dwBlock_, maximumDW_);
    fingerPrint_ = CoinCopyOfArray(rhs.fingerPrint_,
      sizeFingerPrint_ * maximumDW_);
    objectiveDW_ = CoinCopyOfArray(rhs.objectiveDW_, numberDWTimes_);
    numberColumnsDW_ = CoinCopyOfArray(rhs.numberColumnsDW_, numberDWTimes_);
  } else {
    random_ = NULL;
    weights_ = NULL;
    objectiveDW_ = NULL;
    numberColumnsDW_ = NULL;
    dwBlock_ = NULL;
    fingerPrint_ = NULL;
  }
  if (rhs.bestSolution_) {
    int numberColumns = solver_->getNumCols();
    bestSolution_ = CoinCopyOfArray(rhs.bestSolution_, numberColumns);
  } else {
    bestSolution_ = NULL;
  }
  if (rhs.continuousSolution_) {
    int numberColumns = solver_->getNumCols();
    continuousSolution_ = CoinCopyOfArray(rhs.continuousSolution_, numberColumns);
  } else {
    continuousSolution_ = NULL;
  }
  if (rhs.fixedDj_) {
    int numberColumns = solver_->getNumCols();
    fixedDj_ = CoinCopyOfArray(rhs.fixedDj_, numberColumns);
  } else {
    fixedDj_ = NULL;
  }
}
// Guts of delete
void CbcHeuristicDW::gutsOfDelete()
{
  delete solver_;
  delete dwSolver_;
  delete[] bestSolution_;
  delete[] continuousSolution_;
  delete[] fixedDj_;
  delete[] saveLower_;
  delete[] saveUpper_;
  delete[] random_;
  delete[] affinity_;
  delete[] weights_;
  delete[] objectiveDW_;
  delete[] numberColumnsDW_;
  delete[] whichRowBlock_;
  delete[] whichColumnBlock_;
  delete[] dwBlock_;
  delete[] backwardRow_;
  delete[] rowsInBlock_;
  delete[] columnsInBlock_;
  delete[] startRowBlock_;
  delete[] startColumnBlock_;
  delete[] intsInBlock_;
  delete[] fingerPrint_;
  //functionPointer_ = NULL;
  solver_ = NULL;
  dwSolver_ = NULL;
  bestSolution_ = NULL;
  continuousSolution_ = NULL;
  fixedDj_ = NULL;
  saveLower_ = NULL;
  saveUpper_ = NULL;
  random_ = NULL;
  affinity_ = NULL;
  weights_ = NULL;
  objectiveDW_ = NULL;
  numberColumnsDW_ = NULL;
  whichRowBlock_ = NULL;
  whichColumnBlock_ = NULL;
  dwBlock_ = NULL;
  backwardRow_ = NULL;
  rowsInBlock_ = NULL;
  columnsInBlock_ = NULL;
  startRowBlock_ = NULL;
  startColumnBlock_ = NULL;
  intsInBlock_ = NULL;
  fingerPrint_ = NULL;
  numberBlocks_ = 0;
}

// Destructor
CbcHeuristicDW::~CbcHeuristicDW()
{
  gutsOfDelete();
}

// Clone
CbcHeuristic *
CbcHeuristicDW::clone() const
{
  return new CbcHeuristicDW(*this);
}

// Assignment operator
CbcHeuristicDW &
CbcHeuristicDW::operator=(const CbcHeuristicDW &rhs)
{
  if (this != &rhs) {
    CbcHeuristic::operator=(rhs);
    gutsOfDelete();
    gutsOfCopy(rhs);
  }
  return *this;
}

// Create C++ lines to get to current state
void CbcHeuristicDW::generateCpp(FILE *fp)
{
  abort();
}

// Copy constructor
CbcHeuristicDW::CbcHeuristicDW(const CbcHeuristicDW &rhs)
  : CbcHeuristic(rhs)
{
  gutsOfCopy(rhs);
}
// Resets stuff if model changes
void CbcHeuristicDW::resetModel(CbcModel *model)
{
  if (model_ && numberBlocks_ && model->getNumCols() != model->getNumCols())
    abort();
  model_ = model;
}
/*
  First tries setting a variable to better value.  If feasible then
  tries setting others.  If not feasible then tries swaps
  Returns 1 if solution, 0 if not */
int CbcHeuristicDW::solution(double &solutionValue,
  double *betterSolution)
{
  numCouldRun_++;
  int returnCode = 0;
  const double *bestSolutionIn = model_->bestSolution();
  if (!bestSolutionIn && !bestSolution_)
    return 0; // No solution found yet
  if (numberBlocks_ < 3)
    return 0; // no point
#ifdef HEURISTIC_INFORM
  printf("Entering heuristic %s - nRuns %d numCould %d when %d\n",
    heuristicName(), numRuns_, numCouldRun_, when_);
#endif
  if (bestSolutionIn && objectiveValue(bestSolutionIn) < bestObjective_ - 1.0e-5)
    passInSolution(bestSolutionIn);
  char dwPrint[200];
  sprintf(dwPrint, "Before PASSes objective is %g",
    bestObjective_);
  lastObjective_ = bestObjective_;
  model_->messageHandler()->message(CBC_FPUMP1, model_->messages())
    << dwPrint
    << CoinMessageEol;
  double startTime = CoinCpuTime();
  double startTimeElapsed = CoinGetTimeOfDay();
  CoinWarmStart *basis = NULL;
  lastObjective_ = COIN_DBL_MAX;
  int passesToDW = dwSolver_ ? 0 : -1;
  bool goodSolution = true;
  int numberColumns = solver_->getNumCols();
  int logLevel = model_->messageHandler()->logLevel();
  // For moment just OsiClp
  OsiClpSolverInterface *solver = dynamic_cast< OsiClpSolverInterface * >(solver_);
  ClpSimplex *simplex = solver->getModelPtr();
  double *columnLower = simplex->columnLower();
  double *columnUpper = simplex->columnUpper();
  const double *cost = solver->getObjCoefficients();
  const double *dj = solver->getReducedCost();
  assert(solver);
  if (!continuousSolution_) {
    bool takeHint;
    OsiHintStrength strength;
    solver->getHintParam(OsiDoDualInResolve, takeHint, strength);
    solver->setHintParam(OsiDoDualInResolve, false, OsiHintDo);
    solver->resolve();
    solver->setHintParam(OsiDoDualInResolve, takeHint, OsiHintDo);
    continuousSolution_ = CoinCopyOfArray(solver->getColSolution(),
      numberColumns);
  }
  // data arrays
  // Block order for choosing (after sort)
  int *whichBlock = new int[8 * numberBlocks_];
  memset(whichBlock, 0, 8 * numberBlocks_ * sizeof(int));
  // Count of number of times block chosen
  int *doneBlock = whichBlock + numberBlocks_;
  // Pass at which block last used
  int *whenBlock = doneBlock + numberBlocks_;
  // Number of Big Djs' (? artificial costs) in block
  int *bigDjBlock = whenBlock + numberBlocks_;
  // Number of times block has helped improve solution
  int *goodBlock = bigDjBlock + numberBlocks_;
  int *priorityBlock = goodBlock + numberBlocks_;
  int *orderBlock = priorityBlock + numberBlocks_;
  // block can be fixed if nothing in master rows, maybe always same as continuous
  int *fixedBlock = orderBlock + numberBlocks_;
  // Mixture of stuff to sort blocks on
  double *blockSort = new double[4 * numberBlocks_];
  // Reduced cost (d sub j was old notation) contribution
  double *blockDj = blockSort + numberBlocks_;
  // Difference between current best and continuous solutions
  double *blockDiff = blockDj + numberBlocks_;
  // Difference between current best and continuous solutions (just integers)
  double *blockDiffInt = blockDiff + numberBlocks_;
  delete[] fixedDj_;
  fixedDj_ = CoinCopyOfArray(dj, numberColumns);
  int numberImproving = 0;
  int *whenBetter = new int[numberPasses_];
  double *improvement = new double[numberPasses_];
  // First has number
  int **improvingBlocks = new int *[numberPasses_];
  int numberBlocksUsed = numberBlocks_;
  // Get basic priority order
  for (int i = 0; i < numberBlocks_; i++) {
    whenBlock[i] = -intsInBlock_[i];
    orderBlock[i] = i;
  }
  CoinSort_2(whenBlock, whenBlock + numberBlocks_, orderBlock);
  for (int i = 0; i < numberBlocks_; i++) {
    whenBlock[i] = -100;
    doneBlock[i] = 0;
    whichBlock[i] = i;
  }
  bestObjective_ = objectiveValue(bestSolution_);
  double startTime2 = CoinCpuTime();
  double startTime2Elapsed = CoinGetTimeOfDay();
  // make sure all master columns freed up
  for (int iColumn = 0; iColumn < numberColumns; ++iColumn) {
    if (whichColumnBlock_[iColumn] < 0) {
      columnLower[iColumn] = saveLower_[iColumn];
      columnUpper[iColumn] = saveUpper_[iColumn];
    }
  }
  int numberNoMaster = 0;
  int numberSameAsContinuous = 0;
  int numberSameAsContinuousJustInts = 0;
  // Column copy
  //const double * element = solver->getMatrixByCol()->getElements();
  const int *row = solver->getMatrixByCol()->getIndices();
  const CoinBigIndex *columnStart = solver->getMatrixByCol()->getVectorStarts();
  const int *columnLength = solver->getMatrixByCol()->getVectorLengths();
  for (int iBlock = 0; iBlock < numberBlocks_; iBlock++) {
    int nElInMaster = 0;
    int numberDifferentContinuous = 0;
    int numberDifferentContinuousJustInts = 0;
    int start = startColumnBlock_[iBlock];
    int end = startColumnBlock_[iBlock + 1];
    for (int i = start; i < end; i++) {
      int iColumn = columnsInBlock_[i];
      for (CoinBigIndex j = columnStart[iColumn];
           j < columnStart[iColumn] + columnLength[iColumn]; j++) {
        int iRow = row[j];
        iRow = backwardRow_[iRow];
        if (iRow >= 0)
          nElInMaster++;
      }
      if (fabs(bestSolution_[iColumn] - continuousSolution_[iColumn]) < 1.0e-5) {
        numberDifferentContinuous++;
        if (solver->isInteger(iColumn))
          numberDifferentContinuousJustInts++;
      }
    }
    if (!nElInMaster) {
      fixedBlock[iBlock] = 10;
      numberNoMaster++;
    } else if (!numberDifferentContinuous) {
      fixedBlock[iBlock] = 2;
      numberSameAsContinuous++;
    } else if (!numberDifferentContinuousJustInts) {
      fixedBlock[iBlock] = 1;
      numberSameAsContinuousJustInts++;
    }
  }
  if (numberNoMaster) {
    sprintf(dwPrint, "*** %d blocks have no elements in master - can be solved separately",
      numberNoMaster);
    model_->messageHandler()->message(CBC_FPUMP1, model_->messages())
      << dwPrint
      << CoinMessageEol;
  }
  sprintf(dwPrint, "With initial best solution %d blocks were same as continuous, %d when just looking at integers",
    numberSameAsContinuous, numberSameAsContinuousJustInts);
  model_->messageHandler()->message(CBC_FPUMP1, model_->messages())
    << dwPrint
    << CoinMessageEol;
  int lastGoodPass = 0;
  for (pass_ = 0; pass_ < numberPasses_; pass_++) {
    double endTime2 = CoinCpuTime();
    double endTime2Elapsed = CoinGetTimeOfDay();
#ifndef SCALE_FACTOR
#define SCALE_FACTOR 1.0
#endif
    if (pass_) {
      sprintf(dwPrint, "PASS %d changed objective from %g to %g in %g seconds (%g elapsed) - total %g (%g elapsed) - current needed %d nodes %d - %s",
        pass_, lastObjective_ * SCALE_FACTOR,
        bestObjective_ * SCALE_FACTOR, endTime2 - startTime2,
        endTime2Elapsed - startTime2Elapsed,
        endTime2 - startTime, endTime2Elapsed - startTimeElapsed,
        nNeeded_, nNodes_,
        lastObjective_ > bestObjective_ + 1.0e-3 ? "improving" : "");
      model_->messageHandler()->message(CBC_FPUMP1, model_->messages())
        << dwPrint
        << CoinMessageEol;
    }
    if ((pass_ % 10) == 9) {
      for (int iImp = CoinMax(1, numberImproving - 10); iImp < numberImproving; iImp++) {
        int *blocks = improvingBlocks[iImp];
        int nBlocks = blocks[0];
        blocks++;
        sprintf(dwPrint, "Pass %d improved objective by %g using %d blocks - ",
          whenBetter[iImp], improvement[iImp], nBlocks);
        model_->messageHandler()->message(CBC_FPUMP1, model_->messages())
          << dwPrint
          << CoinMessageEol;
        if (logLevel > 1) {
          for (int i = 0; i < nBlocks; i++)
            printf("%d ", blocks[i]);
          printf("\n");
        }
      }
      if (logLevel > 1) {
        int *count = new int[numberImproving + 1];
        memset(count, 0, (numberImproving + 1) * sizeof(int));
        for (int i = 0; i < numberBlocks_; i++)
          count[goodBlock[i]]++;
        for (int i = 0; i < numberImproving; i++) {
          if (count[i])
            printf("%d blocks were involved in improvement %d times\n",
              count[i], i);
        }
        delete[] count;
      }
    }
    startTime2 = CoinCpuTime();
    startTime2Elapsed = CoinGetTimeOfDay();
    if (pass_ - lastGoodPass > numberBadPasses_) {
      sprintf(dwPrint, "Exiting on lack of progress");
      model_->messageHandler()->message(CBC_FPUMP1, model_->messages())
        << dwPrint
        << CoinMessageEol;
      break;
    }
    if (model_->getNodeCount() >= model_->getMaximumNodes() || model_->maximumSecondsReached()) {
      sprintf(dwPrint, "Exiting on time or interrupt");
      model_->messageHandler()->message(CBC_FPUMP1, model_->messages())
        << dwPrint
        << CoinMessageEol;
      break;
    }
    if (bestObjective_ >= lastObjective_ - 1.0e-3) {
      // what now
      // 0 - fine, 1 can't be better, 2 max node
      //assert(solveState);
      if (solveState_ < 2) {
        // more in
        sprintf(dwPrint, "No improvement - think we need more variables ");
        model_->messageHandler()->message(CBC_FPUMP1, model_->messages())
          << dwPrint
          << CoinMessageEol;
        nNeeded_ += nNeeded_ / 10;
        nNeeded_ = CoinMin(nNeeded_, 800);
        nNodes_ = nNodesBase_;
        (*(functionPointer_))(this, NULL, 6);
      } else {
        // more nodes fewer in
        sprintf(dwPrint, "No improvement - stopped on nodes - think we need more nodes ");
        model_->messageHandler()->message(CBC_FPUMP1, model_->messages())
          << dwPrint
          << CoinMessageEol;
        if (phase_) {
          nNodes_ += nNodes_ / 5;
          nNodes_ = CoinMin(nNodes_, 1000);
        }
        nNeeded_ -= nNeeded_ / 20;
        nNeeded_ = CoinMax(nNeeded_, 50);
        (*(functionPointer_))(this, NULL, 7);
      }
    } else {
      // improving
      (*(functionPointer_))(this, NULL, 4);
      solveState_ = 0;
      //lastObjective_=bestObjective_;
      if (phase_) {
        //nNeededBase_ += nNeededBase_/50;
        //nNodesBase_ += nNodesBase_/50;
      }
      nNeeded_ -= nNeeded_ / 10;
      nNeeded_ = CoinMax(nNeededBase_, nNeeded_);
      nNodes_ = nNodesBase_;
      (*(functionPointer_))(this, NULL, 8);
    }
    sprintf(dwPrint, "new needed %d, nodes %d", nNeeded_, nNodes_);
    model_->messageHandler()->message(CBC_FPUMP1, model_->messages())
      << dwPrint
      << CoinMessageEol;
    for (int i = 0; i < numberColumns; ++i) {
      if (solver_->isInteger(i)) {
        double value = floor(bestSolution_[i] + 0.5);
        columnLower[i] = value;
        columnUpper[i] = value;
      } else {
        columnLower[i] = saveLower_[i];
        columnUpper[i] = saveUpper_[i];
      }
    }
    if (goodSolution) {
      lastGoodPass = pass_;
      int lastNumberDW = numberDW_;
      solver->setColSolution(bestSolution_);
      solver->setHintParam(OsiDoReducePrint, false, OsiHintDo, 0);
      if (basis) {
        solver->setWarmStart(basis);
        delete basis;
        basis = NULL;
      }
      solver->resolve();
      solver->setHintParam(OsiDoReducePrint, true, OsiHintDo, 0);
      if (solver->getObjValue() < lastObjective_ - 1.0e-5) {
        memcpy(bestSolution_, solver->getColSolution(),
          numberColumns * sizeof(double));
        bestObjective_ = solver->getObjValue();
        int *blocks = new int[numberBlocksUsed + 1];
        blocks[0] = numberBlocksUsed;
        memcpy(blocks + 1, whichBlock, numberBlocksUsed * sizeof(int));
        improvingBlocks[numberImproving] = blocks;
        whenBetter[numberImproving] = pass_;
        improvement[numberImproving] = lastObjective_ - bestObjective_;
        numberImproving++;
        lastObjective_ = bestObjective_;
        if (pass_) {
          // update good
          for (int i = 0; i < numberBlocksUsed; i++)
            goodBlock[whichBlock[i]]++;
        }
      }
      goodSolution = false;
      if (fullDWEverySoOften_ > 0) {
        addDW(bestSolution_, numberBlocksUsed,
          whichBlock);
      }
      if (passesToDW == 0) {
        passesToDW = fullDWEverySoOften_;
        const double *duals = solver->getRowPrice();
        double *bestSolution2 = CoinCopyOfArray(bestSolution_,
          numberColumns);
        // Column copy
        const double *element = solver->getMatrixByCol()->getElements();
        const int *row = solver->getMatrixByCol()->getIndices();
        const CoinBigIndex *columnStart = solver->getMatrixByCol()->getVectorStarts();
        const int *columnLength = solver->getMatrixByCol()->getVectorLengths();
        int numberUsed = 0;
        for (int iBlock = 0; iBlock < numberBlocks_; iBlock++) {
          int start = startColumnBlock_[iBlock];
          int end = startColumnBlock_[iBlock + 1];
          ClpSimplex *tempModel = new ClpSimplex(solver->getModelPtr(),
            startRowBlock_[iBlock + 1] - startRowBlock_[iBlock],
            rowsInBlock_ + startRowBlock_[iBlock],
            end - start,
            columnsInBlock_ + startColumnBlock_[iBlock]);
          tempModel->setLogLevel(0);
          tempModel->setDualObjectiveLimit(COIN_DBL_MAX);
          double *objectiveX = tempModel->objective();
          double *columnLowerX = tempModel->columnLower();
          double *columnUpperX = tempModel->columnUpper();
          for (int i = start; i < end; i++) {
            int jColumn = i - start;
            int iColumn = columnsInBlock_[i];
            columnLowerX[jColumn] = CoinMax(saveLower_[iColumn], -1.0e12);
            columnUpperX[jColumn] = CoinMin(saveUpper_[iColumn], 1.0e12);
            if (solver->isInteger(iColumn))
              tempModel->setInteger(jColumn);
            double cost = objectiveX[jColumn];
            for (CoinBigIndex j = columnStart[iColumn];
                 j < columnStart[iColumn] + columnLength[iColumn]; j++) {
              int iRow = row[j];
              double elementValue = element[j];
              if (backwardRow_[iRow] >= 0) {
                cost -= elementValue * duals[iRow];
              }
            }
            objectiveX[jColumn] = cost;
          }
          OsiClpSolverInterface solverX(tempModel, true);
          CbcModel modelX(solverX);
          modelX.setLogLevel(1);
          modelX.setMoreSpecialOptions2(57);
          // need to stop after solutions and nodes
          //modelX.setMaximumNodes(nNodes_);
          modelX.setMaximumSolutions(1);
          modelX.branchAndBound();
          const double *bestSolutionX = modelX.bestSolution();
          if (bestSolutionX) {
            whichBlock[numberUsed++] = iBlock;
            for (int i = start; i < end; i++) {
              int jColumn = i - start;
              int iColumn = columnsInBlock_[i];
              bestSolution2[iColumn] = bestSolutionX[jColumn];
            }
          }
        }
        addDW(bestSolution2, numberUsed, whichBlock);
        if (!pass_ && false) {
          // see if gives a solution
          for (int i = 0; i < numberColumns; ++i) {
            if (solver_->isInteger(i)) {
              double value = floor(bestSolution2[i] + 0.5);
              columnLower[i] = value;
              columnUpper[i] = value;
            } else {
              columnLower[i] = saveLower_[i];
              columnUpper[i] = saveUpper_[i];
            }
          }
          solver_->resolve();
          if (solver_->isProvenOptimal()) {
            printf("DW1 sol %g\n", solver->getObjValue());
          }
        }
        // now try purer DW
        bool takeHint;
        OsiHintStrength strength;
        dwSolver_->getHintParam(OsiDoDualInResolve, takeHint, strength);
        dwSolver_->setHintParam(OsiDoDualInResolve, false, OsiHintDo);
        dwSolver_->resolve();
        dwSolver_->setHintParam(OsiDoDualInResolve, takeHint, OsiHintDo);
        duals = dwSolver_->getRowPrice();
        numberUsed = 0;
        for (int iBlock = 0; iBlock < numberBlocks_; iBlock++) {
          int start = startColumnBlock_[iBlock];
          int end = startColumnBlock_[iBlock + 1];
          ClpSimplex *tempModel = new ClpSimplex(solver->getModelPtr(),
            startRowBlock_[iBlock + 1] - startRowBlock_[iBlock],
            rowsInBlock_ + startRowBlock_[iBlock],
            end - start,
            columnsInBlock_ + startColumnBlock_[iBlock]);
          tempModel->setLogLevel(0);
          tempModel->setDualObjectiveLimit(COIN_DBL_MAX);
          double *objectiveX = tempModel->objective();
          double *columnLowerX = tempModel->columnLower();
          double *columnUpperX = tempModel->columnUpper();
          double convexityDual = duals[numberMasterRows_ + iBlock];
          for (int i = start; i < end; i++) {
            int jColumn = i - start;
            int iColumn = columnsInBlock_[i];
            columnLowerX[jColumn] = CoinMax(saveLower_[iColumn], -1.0e12);
            columnUpperX[jColumn] = CoinMin(saveUpper_[iColumn], 1.0e12);
            if (solver->isInteger(iColumn))
              tempModel->setInteger(jColumn);
            double cost = objectiveX[jColumn];
            for (CoinBigIndex j = columnStart[iColumn];
                 j < columnStart[iColumn] + columnLength[iColumn]; j++) {
              int iRow = row[j];
              double elementValue = element[j];
              if (backwardRow_[iRow] >= 0) {
                // duals are from dw
                cost -= elementValue * duals[backwardRow_[iRow]];
              }
            }
            objectiveX[jColumn] = cost;
          }
          OsiClpSolverInterface solverX(tempModel, true);
          solverX.initialSolve();
          double cObj = solverX.getObjValue();
          CbcModel modelX(solverX);
          modelX.setLogLevel(1);
          modelX.setMoreSpecialOptions2(57);
          modelX.setMaximumSolutions(1);
          modelX.branchAndBound();
          sprintf(dwPrint, "Block %d contobj %g intobj %g convdual %g",
            iBlock, cObj, modelX.getObjValue(), convexityDual);
          model_->messageHandler()->message(CBC_FPUMP2, model_->messages())
            << dwPrint
            << CoinMessageEol;
          const double *bestSolutionX = modelX.bestSolution();
          if (bestSolutionX) {
            whichBlock[numberUsed++] = iBlock;
            for (int i = start; i < end; i++) {
              int iColumn = columnsInBlock_[i];
              bestSolution2[iColumn] = bestSolutionX[i - start];
            }
          }
        }
        addDW(bestSolution2, numberUsed, whichBlock);
        if (!pass_ && false) {
          // see if gives a solution
          for (int i = 0; i < numberColumns; ++i) {
            if (solver_->isInteger(i)) {
              double value = floor(bestSolution2[i] + 0.5);
              columnLower[i] = value;
              columnUpper[i] = value;
            } else {
              columnLower[i] = saveLower_[i];
              columnUpper[i] = saveUpper_[i];
            }
          }
          solver_->resolve();
          if (solver_->isProvenOptimal()) {
            printf("DW sol %g\n", solver->getObjValue());
          }
        }
        delete[] bestSolution2;
      }
      passesToDW--;
      if (numberDW_ > lastNumberDW) {
        intArray_ = NULL;
        doubleArray_ = NULL;
        (*(functionPointer_))(this, NULL, 3);
      }
    }
    for (int i = 0; i < numberBlocks_; i++) {
      blockDj[i] = 0.0;
      blockDiff[i] = 0.0;
      blockDiffInt[i] = 0.0;
      bigDjBlock[i] = 0;
    }
    for (int i = 0; i < numberColumns; ++i) {
      int kBlock = whichColumnBlock_[i];
      if (kBlock >= 0) {
        columnLower[i] = bestSolution_[i];
        columnUpper[i] = bestSolution_[i];
      }
    }
    for (int iBlock = 0; iBlock < numberBlocks_; iBlock++) {
      for (int i = startColumnBlock_[iBlock];
           i < startColumnBlock_[iBlock + 1]; i++) {
        int iColumn = columnsInBlock_[i];
        if (continuousSolution_) {
          blockDiff[iBlock] += fabs((bestSolution_[iColumn] - continuousSolution_[iColumn]))
            * (fabs(cost[iColumn]) + 1.0e-5);
          if (solver->isInteger(iColumn))
            blockDiffInt[iBlock] += fabs((bestSolution_[iColumn] - continuousSolution_[iColumn]))
              * (fabs(cost[iColumn]) + 1.0e-5);
        }
        if (solver->isInteger(iColumn)) {
          if (bestSolution_[iColumn] < saveUpper_[iColumn] - 1.0e-1) {
            if (fixedDj_[iColumn] < -1.0e-5)
              blockDj[iBlock] += fixedDj_[iColumn];
            if (fixedDj_[iColumn] < -1.0e4)
              bigDjBlock[iBlock]++;
          }
          if (bestSolution_[iColumn] > saveLower_[iColumn] + 1.0e-1) {
            if (fixedDj_[iColumn] > 1.0e-5)
              blockDj[iBlock] -= fixedDj_[iColumn];
            if (fixedDj_[iColumn] > 1.0e4)
              bigDjBlock[iBlock]++;
          }
        }
      }
    }
    // Get average dj and difference
    int numberInDj = 0;
    double averageDj = 1.0e-12;
    int numberInDiff = 0;
    double averageDiff = 1.0e-12;
    for (int i = 0; i < numberBlocks_; i++) {
      assert(blockDj[i] <= 0.0);
      if (blockDj[i] < 0.0) {
        numberInDj++;
        averageDj -= blockDj[i];
      }
      assert(blockDiff[i] >= 0.0);
      if (blockDiff[i] > 0.0) {
        numberInDiff++;
        averageDiff += blockDiff[i];
      }
    }
    if (numberInDj)
      averageDj /= static_cast< double >(numberInDj);
    if (numberInDiff)
      averageDiff /= static_cast< double >(numberInDiff);
    double ratioDiff = averageDj / averageDiff;
    // downplay
    ratioDiff *= 1.0e-3;
    for (int i = 0; i < numberBlocks_; i++) {
      whichBlock[i] = i;
      assert(intsInBlock_[i]);
      blockSort[i] = blockDj[i];
#define TRY_ADJUST 3
#if TRY_ADJUST == 1
      blockSort[i] *= CoinDrand48() + 5.0e-1;
#elif TRY_ADJUST == 2
      blockSort[i] -= ratioDiff * blockDiff[i];
#elif TRY_ADJUST == 3
      blockSort[i] -= ratioDiff * blockDiff[i];
      blockSort[i] *= 0.1 * CoinDrand48() + 0.9;
#endif
      if (phase_ == 99)
        blockSort[i] -= 2.0 * averageDj * goodBlock[i];
      blockSort[i] /= static_cast< double >(intsInBlock_[i]);
      //blockSort[i] /= sqrt(static_cast<double>(intsInBlock_[i]));
      if (doneBlock[i]) {
        blockSort[i] /= static_cast< double >(doneBlock[i] + 1);
        if (whenBlock[i] > pass_ - 10)
          blockSort[i] += 1.0e2 * averageDj;
      }
    }
    CoinSort_2(blockSort, blockSort + numberBlocks_, whichBlock);
    // allow user to modify
    intArray_ = whichBlock;
    doubleArray_ = blockSort;
    (*(functionPointer_))(this, NULL, 1);
    int numberBlocksIn = 0;
    for (int iTry = 0; iTry < 2; iTry++) {
      int nFreed = 0;
      int nBigDjBlock = 0;
      numberBlocksUsed = 0;
      for (int i = 0; i < numberBlocks_; i++) {
        int iBlock = whichBlock[i];
        bool skipBlock = false;
        assert(iBlock >= 0);
        if ((doneBlock[iBlock] && !phase_) || !blockSort[i]) {
          //printf("already done block %d - dj %g\n",iBlock,blockDj[i]);
          skipBlock = true;
        } else if (bigDjBlock[iBlock]) {
          nBigDjBlock++;
          if (nBigDjBlock > 20 && !phase_) {
            skipBlock = true;
          }
        }
        int returnCode = (*(functionPointer_))(this, NULL, 5);
        if (returnCode < 0)
          skipBlock = true;
        else if (returnCode > 0)
          skipBlock = false;
        if (skipBlock) {
          whichBlock[i] -= 1000000;
        } else {
          // free up
          sprintf(dwPrint, "freeing block %d (already freed %d times) - dj %g, diff %g, diffint %g - %d columns (%d integer)",
            iBlock, doneBlock[iBlock], blockDj[iBlock],
            blockDiff[iBlock], blockDiffInt[iBlock],
            startColumnBlock_[iBlock + 1] - startColumnBlock_[iBlock],
            intsInBlock_[iBlock]);
          model_->messageHandler()->message(CBC_FPUMP2, model_->messages())
            << dwPrint
            << CoinMessageEol;
          numberBlocksIn++;
          doneBlock[iBlock]++;
          whenBlock[iBlock] = pass_;
          nFreed += intsInBlock_[iBlock];
          for (int j = startColumnBlock_[iBlock];
               j < startColumnBlock_[iBlock + 1]; j++) {
            int iColumn = columnsInBlock_[j];
            columnLower[iColumn] = saveLower_[iColumn];
            columnUpper[iColumn] = saveUpper_[iColumn];
          }
          if (!numberBlocksUsed && affinity_) {
            // re-sort rest using affinity
            const unsigned short int *aff = affinity_ + iBlock * numberBlocks_;
            for (int j = i + 1; j < numberBlocks_; j++) {
              int jBlock = whichBlock[j];
              blockSort[j] -= 1.0e2 * aff[jBlock];
            }
            CoinSort_2(blockSort + i + 1, blockSort + numberBlocks_, whichBlock + i + 1);
          }
          numberBlocksUsed = i + 1;
          if (nFreed >= nNeeded_ && numberBlocksIn > 3)
            break;
        }
      }
      sprintf(dwPrint, "%d big dj blocks found", nBigDjBlock);
      model_->messageHandler()->message(CBC_FPUMP2, model_->messages())
        << dwPrint
        << CoinMessageEol;
      if (nFreed)
        break;
      phase_ = 1; // round again
      sprintf(dwPrint, "Changing phase");
      model_->messageHandler()->message(CBC_FPUMP1, model_->messages())
        << dwPrint
        << CoinMessageEol;
      for (int i = 0; i < numberBlocks_; i++)
        whichBlock[i] += 1000000;
      //nNeeded=500; // allow more
    }
    if (numberBlocksIn == numberBlocks_)
      numberPasses_ = 1; // no point
    // pack down blocks
    int n = 0;
    for (int i = 0; i < numberBlocksUsed; i++) {
      if (whichBlock[i] >= 0)
        whichBlock[n++] = whichBlock[i];
    }
    numberBlocksUsed = n;
    int nFixedInts = 0;
    int nFixedContinuous = 0;
    int nFreeInts = 0;
    int nFreeContinuous = 0;
    int nFreeMaster = 0;
    int nFixedMaster = 0;
    for (int i = 0; i < numberColumns; ++i) {
      int kBlock = whichColumnBlock_[i];
      if (columnUpper[i] > columnLower[i]) {
        if (kBlock >= 0) {
          if (solver->isInteger(i))
            nFreeInts++;
          else
            nFreeContinuous++;
        } else {
          nFreeMaster++;
        }
      } else {
        if (kBlock >= 0) {
          if (solver->isInteger(i))
            nFixedInts++;
          else
            nFixedContinuous++;
        } else {
          nFixedMaster++;
        }
      }
    }
    sprintf(dwPrint, "Fixed %d ints, %d c, %d m - free %d, %d, %d",
      nFixedInts, nFixedContinuous, nFixedMaster,
      nFreeInts, nFreeContinuous, nFreeMaster);
    model_->messageHandler()->message(CBC_FPUMP1, model_->messages())
      << dwPrint
      << CoinMessageEol;
    // But free up and then fix again
    for (int i = 0; i < numberColumns; ++i) {
      columnLower[i] = saveLower_[i];
      columnUpper[i] = saveUpper_[i];
      int kBlock = whichColumnBlock_[i];
      if (kBlock >= 0 && solver->isInteger(i) && whenBlock[kBlock] != pass_) {
        columnLower[i] = bestSolution_[i];
        columnUpper[i] = bestSolution_[i];
      }
    }
    bool takeHint;
    OsiHintStrength strength;
    solver->getHintParam(OsiDoDualInResolve, takeHint, strength);
    solver->setHintParam(OsiDoDualInResolve, false, OsiHintDo);
    solver->messageHandler()->setLogLevel(1);
    solver->setHintParam(OsiDoReducePrint, false, OsiHintDo, 0);
    solver->resolve();
    solver->setHintParam(OsiDoReducePrint, true, OsiHintDo, 0);
    //solver->messageHandler()->setLogLevel(0) ;
    solver->setHintParam(OsiDoDualInResolve, takeHint, strength);
    if (solver->getObjValue() > bestObjective_ + 1.0e-5 * (1.0 + fabs(bestObjective_))) {
      // trouble
      if (logLevel > 1) {
        for (int i = 0; i < numberBlocks_; i++) {
          if (whenBlock[i] == pass_) {
            printf("Block %d free\n", i);
          }
        }
      }
      solver->writeMps("bad", "mps");
      const double *lower = solver->getColLower();
      const double *upper = solver->getColUpper();
      if (logLevel > 1) {
        printf("best obj %g\n", objectiveValue(bestSolution_));
        for (int i = 0; i < numberColumns; ++i) {
          double value = bestSolution_[i];
          if (value < lower[i] - 1.0e-5 || value > upper[i] + 1.0e-5)
            printf("column %d (block %d) %g %g <= %g <= %g %g\n",
              i, whichColumnBlock_[i], saveLower_[i],
              lower[i], value, upper[i], saveUpper_[i]);
        }
      }
      //abort();
      sprintf(dwPrint, "**** Continuous below best - bad solution passed in?!");
      model_->messageHandler()->message(CBC_FPUMP1, model_->messages())
        << dwPrint
        << CoinMessageEol;
      // give up
      break;
    }
    const double *tempSol = solver->getColSolution();
    // But free up and then fix again
    for (int i = 0; i < numberColumns; ++i) {
      int kBlock = whichColumnBlock_[i];
      if (kBlock >= 0 && !solver->isInteger(i) && whenBlock[kBlock] != pass_) {
        columnLower[i] = tempSol[i];
        columnUpper[i] = tempSol[i];
      }
    }
    solver->getHintParam(OsiDoDualInResolve, takeHint, strength);
    solver->setHintParam(OsiDoDualInResolve, false, OsiHintDo);
    solver->messageHandler()->setLogLevel(1);
    solver->setHintParam(OsiDoReducePrint, false, OsiHintDo, 0);
    solver->resolve();
    solver->setHintParam(OsiDoReducePrint, true, OsiHintDo, 0);
    //solver->messageHandler()->setLogLevel(0) ;
    solver->setHintParam(OsiDoDualInResolve, takeHint, strength);
    //solver->messageHandler()->setLogLevel(0) ;
    //lp->setLogLevel(0);
    ClpPresolve pinfo;
    // fix small infeasibilities
    pinfo.setPresolveActions(pinfo.presolveActions() | 0x4000);
    int numberPasses = 2; // can change this
    ClpSimplex *model2 = pinfo.presolvedModel(*simplex, 1.0e-8,
      true,
      numberPasses, true);
    if (!model2) {
      abort();
    } else {
      sprintf(dwPrint, "Reduced model has %d rows and %d columns",
        model2->numberRows(), model2->numberColumns());
      model_->messageHandler()->message(CBC_FPUMP1, model_->messages())
        << dwPrint
        << CoinMessageEol;
      //model2->setLogLevel(0);
      OsiClpSolverInterface solver2(model2);
      solver2.setWarmStart(NULL);
      //solver2.messageHandler()->setLogLevel(0) ;
      CbcModel model(solver2);
      model.setMaximumNodes(nNodes_);
      //model.setMaximumSolutions(2);
      model.solver()->setHintParam(OsiDoReducePrint, false, OsiHintDo, 0);
      model.solver()->setHintParam(OsiDoPresolveInInitial, false, OsiHintDo, 0);
      //model.solver()->setHintParam(OsiDoPresolveInResolve, false, OsiHintDo, 0) ;
      model.initialSolve();
      if (bestObjective_ > model.solver()->getObjValue() + 1.0e-1) {
        const double *dj = model.solver()->getReducedCost();
        int nFix = 0;
        const double *lower = model.solver()->getColLower();
        const double *upper = model.solver()->getColUpper();
        const double *solution = model.solver()->getColSolution();
        double gap = CoinMax(bestObjective_ - model.solver()->getObjValue(),
          1.0e-3);
        int numberColumns2 = model.solver()->getNumCols();
#ifdef HOT_START
        // Set up hot start
        const int *originalColumns = pinfo.originalColumns();
        double *hot = new double[2 * numberColumns2];
        int *hotPriorities = new int[numberColumns2 * 2];
        double *hotWeight = hot + numberColumns2;
        memset(hot, 0, numberColumns2 * sizeof(double));
        int *sort = hotPriorities + numberColumns2;
        for (int i = 0; i < numberColumns2; i++) {
          hotWeight[i] = COIN_DBL_MAX;
          sort[i] = i;
          if (solver2.isInteger(i)) {
            int iColumn = originalColumns[i];
            hot[i] = bestSolution_[iColumn];
            hotWeight[i] = -fabs(fixedDj_[iColumn]);
            if (bestSolution_[i] < saveLower_[iColumn] + 1.0e-6) {
              if (fixedDj_[i] > 0.0)
                hotWeight[i] = fixedDj_[iColumn];
            } else if (bestSolution_[i] > saveUpper_[iColumn] - 1.0e-6) {
              if (fixedDj_[i] < 0.0)
                hotWeight[i] = -fixedDj_[iColumn];
            }
            if (solution[i] < saveLower_[iColumn] + 1.0e-6) {
              if (dj[i] > gap) {
                solver2.setColUpper(i, saveLower_[iColumn]);
                nFix++;
              }
            } else if (solution[i] > saveUpper_[iColumn] - 1.0e-6) {
              if (-dj[i] > gap) {
                solver2.setColLower(i, saveUpper_[iColumn]);
                nFix++;
              }
            }
          }
        }
        CoinSort_2(hotWeight, hotWeight + numberColumns2, sort);
        for (int i = 0; i < numberColumns2; i++) {
          hotPriorities[sort[i]] = i + 1;
        }
        model.setHotstartSolution(hot, hotPriorities);
        delete[] hot;
        delete[] hotPriorities;
#endif
        if (logLevel > 1) {
          if (nFix)
            printf("Fixed another %d integers\n", nFix);
        }
        {
          // priorities
          memset(priorityBlock, 0, numberBlocks_ * sizeof(int));
          for (int i = 0; i < numberBlocks_; i++) {
            int iBlock = whichBlock[i];
            if (iBlock >= 0)
              priorityBlock[iBlock] = i + 1;
          }
#if 1
          assert(numberBlocks_ < 4000);
          // But make sure we do one block before next
          for (int i = 0; i < numberBlocks_; i++) {
            priorityBlock[i] += 4000 * orderBlock[i];
          }
#else
          // just do ones with many first
          for (int i = 0; i < numberBlocks; i++) {
            priorityBlock[i] = 10000 - intsInBlock_[i];
          }
#endif
          int numberColumns2 = model.solver()->getNumCols();
          const int *original = pinfo.originalColumns();
          int *priorities = new int[numberColumns2];
          int n = 0;
          for (int i = 0; i < numberColumns2; i++) {
            if (solver2.isInteger(i)) {
              int iBlock = whichColumnBlock_[original[i]];
              priorities[n++] = priorityBlock[iBlock];
            }
          }
          model.passInPriorities(priorities, false);
          delete[] priorities;
        }
        CglProbing probingGen;
        probingGen.setUsingObjective(1);
        probingGen.setMaxPass(1);
        probingGen.setMaxPassRoot(1);
        // Number of unsatisfied variables to look at
        probingGen.setMaxProbe(10);
        probingGen.setMaxProbeRoot(50);
        // How far to follow the consequences
        probingGen.setMaxLook(10);
        probingGen.setMaxLookRoot(50);
        probingGen.setMaxLookRoot(10);
        // Only look at rows with fewer than this number of elements
        probingGen.setMaxElements(200);
        probingGen.setMaxElementsRoot(300);
        probingGen.setRowCuts(0);
        model.addCutGenerator(&probingGen, 5, "Probing");
        model.setNumberThreads(model_->getNumberThreads());
        model.solver()->setHintParam(OsiDoReducePrint, true, OsiHintDo, 0);
        model.setPrintingMode(1);
        model.setCutoff(lastObjective_ - model_->getCutoffIncrement());
        model.setLogLevel(1);
        intArray_ = NULL;
        doubleArray_ = NULL;
        (*(functionPointer_))(this, &model, 2);
        model.branchAndBound();
        if (logLevel > 1) {
          printf("After B&B status %d objective %g\n", model.status(),
            model.getMinimizationObjValue());
        }
        int modelStatus = model.status();
        model.solver()->setHintParam(OsiDoReducePrint, false, OsiHintDo, 0);
        if (model.bestSolution() && model.getMinimizationObjValue() < lastObjective_) {
          const int *original = pinfo.originalColumns();
          int n2 = solver2.getNumCols();
          const double *bestSolution2 = model.bestSolution();
          for (int i = 0; i < n2; i++) {
            int iColumn = original[i];
            if (solver2.isInteger(i)) {
              double value = floor(bestSolution2[i] + 0.5);
              if (fabs(bestSolution2[i] - value) > 1.0e-5) {
                if (logLevel > 1) {
                  printf("bad %d %g\n", i, bestSolution2[i]);
                }
              } else {
                solver->setColLower(iColumn, value);
                solver->setColUpper(iColumn, value);
              }
            }
          }
          pinfo.postsolve(true);
          delete model2;
          bool takeHint;
          OsiHintStrength strength;
          solver->getHintParam(OsiDoDualInResolve, takeHint, strength);
          solver->setHintParam(OsiDoDualInResolve, false, OsiHintDo);
          solver->messageHandler()->setLogLevel(1);
          solver->setHintParam(OsiDoReducePrint, false, OsiHintDo, 0);
          solver->resolve();
          if (basis)
            delete basis;
          basis = solver->getWarmStart();
          memcpy(fixedDj_, solver->getReducedCost(),
            numberColumns * sizeof(double));
          solver->setHintParam(OsiDoReducePrint, true, OsiHintDo, 0);
          //solver->messageHandler()->setLogLevel(0) ;
          solver->setHintParam(OsiDoDualInResolve, takeHint, strength);
          //solver->
          CbcModel model(*solver);
          model.setNumberBeforeTrust(-1);
          model.setNumberStrong(0);
          model.setMoreSpecialOptions2(57);
          model.branchAndBound();
          if (model.getMinimizationObjValue() < lastObjective_) {
            memcpy(bestSolution_, model.bestSolution(),
              numberColumns * sizeof(double));
            bestObjective_ = model.getObjValue();
            if (logLevel > 1) {
              for (int i = 0; i < numberColumns; i++) {
                if (simplex->isInteger(i)) {
                  if (fabs(bestSolution_[i] - floor(bestSolution_[i] + 0.5)) > 1.0e-5) {
                    printf("bad after %d %g\n", i, bestSolution_[i]);
                  }
                }
              }
            }
            goodSolution = true;
          }
        } else if (modelStatus != 0) {
          // stopped on nodes
          // 0 - fine, 1 can't be better, 2 max node
          solveState_ = 2;
          pinfo.destroyPresolve();
          delete model2;
        } else {
          // complete search
          solveState_ = 1;
          pinfo.destroyPresolve();
          delete model2;
        }
      } else {
        // can't be better
        // 0 - fine, 1 can't be better, 2 max node
        solveState_ = 1;
        pinfo.destroyPresolve();
        delete model2;
      }
    }
  }
  delete[] whichBlock;
  delete[] blockSort;
  delete[] whenBetter;
  delete[] improvement;
  for (int i = 0; i < numberImproving; i++)
    delete[] improvingBlocks[i];
  delete[] improvingBlocks;
  delete basis;
  if (bestObjective_ < solutionValue) {
    solutionValue = bestObjective_;
    memcpy(betterSolution, bestSolution_,
      solver_->getNumCols() * sizeof(double));
  }
  return returnCode;
}
// update model
void CbcHeuristicDW::setModel(CbcModel *model)
{
  if (model != model_) {
    gutsOfDelete();
    model_ = model;
    assert(model->solver());
    solver_ = model->solver()->clone();
    findStructure();
  }
}
// Find structure
void CbcHeuristicDW::findStructure()
{
  int numberRows = solver_->getNumRows();
  int numberColumns = solver_->getNumCols();
  // look for DW
  int *blockStart = new int[3 * (numberRows + numberColumns) + 1];
  int *columnBlock = blockStart + numberRows;
  int *nextColumn = columnBlock + numberColumns;
  int *blockCount = nextColumn + numberColumns;
  int *blockEls = blockCount + numberRows + 1;
  int *countIntegers = blockEls + numberRows;
  memset(countIntegers, 0, numberColumns * sizeof(int));
  int direction[2] = { -1, 1 };
  int bestBreak = -1;
  double bestValue = 0.0;
  int iPass = 0;
  int halfway = (numberRows + 1) / 2;
  int firstMaster = -1;
  int lastMaster = -2;
  char dwPrint[200];
  // Column copy
  const CoinPackedMatrix *columnCopy = solver_->getMatrixByCol();
  //const double * element = columnCopy->getElements();
  const int *row = columnCopy->getIndices();
  const CoinBigIndex *columnStart = columnCopy->getVectorStarts();
  const int *columnLength = columnCopy->getVectorLengths();
  // Row copy
  const CoinPackedMatrix *rowCopy = solver_->getMatrixByRow();
  const int *column = rowCopy->getIndices();
  const int *rowLength = rowCopy->getVectorLengths();
  const CoinBigIndex *rowStart = rowCopy->getVectorStarts();
  //const double * elementByRow = rowCopy->getElements();
  while (iPass < 2) {
    int increment = direction[iPass];
    int start = increment > 0 ? 0 : numberRows - 1;
    int stop = increment > 0 ? numberRows : -1;
    int numberBlocks = 0;
    int thisBestBreak = -1;
    double thisBestValue = COIN_DBL_MAX;
    int numberRowsDone = 0;
    int numberMarkedColumns = 0;
    int maximumBlockSize = 0;
    for (int i = 0; i < numberRows + 2 * numberColumns; i++)
      blockStart[i] = -1;
    for (int i = 0; i < numberRows + 1; i++)
      blockCount[i] = 0;
    for (int iRow = start; iRow != stop; iRow += increment) {
      int iBlock = -1;
      for (CoinBigIndex j = rowStart[iRow]; j < rowStart[iRow] + rowLength[iRow]; j++) {
        int iColumn = column[j];
        int whichColumnBlock = columnBlock[iColumn];
        if (whichColumnBlock >= 0) {
          // column marked
          if (iBlock < 0) {
            // put row in that block
            iBlock = whichColumnBlock;
          } else if (iBlock != whichColumnBlock) {
            // merge
            blockCount[iBlock] += blockCount[whichColumnBlock];
            blockCount[whichColumnBlock] = 0;
            int jColumn = blockStart[whichColumnBlock];
            while (jColumn >= 0) {
              columnBlock[jColumn] = iBlock;
              iColumn = jColumn;
              jColumn = nextColumn[jColumn];
            }
            nextColumn[iColumn] = blockStart[iBlock];
            blockStart[iBlock] = blockStart[whichColumnBlock];
            blockStart[whichColumnBlock] = -1;
          }
        }
      }
      int n = numberMarkedColumns;
      if (iBlock < 0) {
        //new block
        if (rowLength[iRow]) {
          numberBlocks++;
          iBlock = numberBlocks;
          int jColumn = column[rowStart[iRow]];
          columnBlock[jColumn] = iBlock;
          blockStart[iBlock] = jColumn;
          numberMarkedColumns++;
          for (CoinBigIndex j = rowStart[iRow] + 1; j < rowStart[iRow] + rowLength[iRow]; j++) {
            int iColumn = column[j];
            columnBlock[iColumn] = iBlock;
            numberMarkedColumns++;
            nextColumn[jColumn] = iColumn;
            jColumn = iColumn;
          }
          blockCount[iBlock] = numberMarkedColumns - n;
        } else {
          // empty
          iBlock = numberRows;
        }
      } else {
        // put in existing block
        int jColumn = blockStart[iBlock];
        for (CoinBigIndex j = rowStart[iRow]; j < rowStart[iRow] + rowLength[iRow]; j++) {
          int iColumn = column[j];
          assert(columnBlock[iColumn] < 0 || columnBlock[iColumn] == iBlock);
          if (columnBlock[iColumn] < 0) {
            columnBlock[iColumn] = iBlock;
            numberMarkedColumns++;
            nextColumn[iColumn] = jColumn;
            jColumn = iColumn;
          }
        }
        blockStart[iBlock] = jColumn;
        blockCount[iBlock] += numberMarkedColumns - n;
      }
      maximumBlockSize = CoinMax(maximumBlockSize, blockCount[iBlock]);
      numberRowsDone++;
      if (thisBestValue * numberRowsDone > maximumBlockSize && numberRowsDone > halfway) {
        thisBestBreak = iRow;
        thisBestValue = static_cast< double >(maximumBlockSize) / static_cast< double >(numberRowsDone);
      }
    }
    if (thisBestBreak == stop)
      thisBestValue = COIN_DBL_MAX;
    iPass++;
    if (iPass == 1) {
      bestBreak = thisBestBreak;
      bestValue = thisBestValue;
    } else {
      if (bestValue < thisBestValue) {
        firstMaster = 0;
        lastMaster = bestBreak;
      } else {
        firstMaster = thisBestBreak; // ? +1
        lastMaster = numberRows;
      }
    }
  }
  numberBlocks_ = 0;
  if (firstMaster < lastMaster) {
    sprintf(dwPrint, "%d master rows %d <= < %d", lastMaster - firstMaster,
      firstMaster, lastMaster);
    model_->messageHandler()->message(CBC_FPUMP1, model_->messages())
      << dwPrint
      << CoinMessageEol;
    for (int i = 0; i < numberRows + 2 * numberColumns; i++)
      blockStart[i] = -1;
    for (int i = firstMaster; i < lastMaster; i++)
      blockStart[i] = -2;
    int firstRow = 0;
    int numberBlocks = -1;
    while (true) {
      for (; firstRow < numberRows; firstRow++) {
        if (blockStart[firstRow] == -1)
          break;
      }
      if (firstRow == numberRows)
        break;
      int nRows = 0;
      numberBlocks++;
      int numberStack = 1;
      blockCount[0] = firstRow;
      while (numberStack) {
        int iRow = blockCount[--numberStack];
        for (CoinBigIndex j = rowStart[iRow]; j < rowStart[iRow] + rowLength[iRow]; j++) {
          int iColumn = column[j];
          int iBlock = columnBlock[iColumn];
          if (iBlock < 0) {
            columnBlock[iColumn] = numberBlocks;
            for (CoinBigIndex k = columnStart[iColumn];
                 k < columnStart[iColumn] + columnLength[iColumn]; k++) {
              int jRow = row[k];
              int rowBlock = blockStart[jRow];
              if (rowBlock == -1) {
                nRows++;
                blockStart[jRow] = numberBlocks;
                blockCount[numberStack++] = jRow;
              }
            }
          }
        }
      }
      if (!nRows) {
        // empty!!
        numberBlocks--;
      }
      firstRow++;
    }
    // adjust
    numberBlocks++;
    for (int i = 0; i < numberBlocks; i++) {
      blockCount[i] = 0;
      nextColumn[i] = 0;
    }
    int numberEmpty = 0;
    int numberMaster = 0;
    memset(blockEls, 0, numberBlocks * sizeof(int));
    for (int iRow = 0; iRow < numberRows; iRow++) {
      int iBlock = blockStart[iRow];
      if (iBlock >= 0) {
        blockCount[iBlock]++;
        blockEls[iBlock] += rowLength[iRow];
      } else {
        if (iBlock == -2)
          numberMaster++;
        else
          numberEmpty++;
      }
    }
    int numberEmptyColumns = 0;
    int numberMasterColumns = 0;
    int numberMasterIntegers = 0;
    for (int iColumn = 0; iColumn < numberColumns; iColumn++) {
      int iBlock = columnBlock[iColumn];
      bool isInteger = (solver_->isInteger(iColumn));
      if (iBlock >= 0) {
        nextColumn[iBlock]++;
        if (isInteger)
          countIntegers[iBlock]++;
      } else {
        if (isInteger)
          numberMasterIntegers++;
        if (columnLength[iColumn])
          numberMasterColumns++;
        else
          numberEmptyColumns++;
      }
    }
    int largestRows = 0;
    int largestColumns = 0;
    for (int i = 0; i < numberBlocks; i++) {
      if (blockCount[i] + nextColumn[i] > largestRows + largestColumns) {
        largestRows = blockCount[i];
        largestColumns = nextColumn[i];
      }
    }
    bool useful = true;
    if (numberMaster > halfway || largestRows * 3 > numberRows)
      useful = false;
    sprintf(dwPrint, "%s %d blocks (largest %d,%d), %d master rows (%d empty) out of %d, %d master columns (%d empty, %d integer) out of %d",
      useful ? "**Useful" : "NoGood",
      numberBlocks, largestRows, largestColumns, numberMaster, numberEmpty, numberRows,
      numberMasterColumns, numberEmptyColumns, numberMasterIntegers,
      numberColumns);
    model_->messageHandler()->message(CBC_FPUMP1, model_->messages())
      << dwPrint
      << CoinMessageEol;
    // columnBlock is columnBlock and blockStart is rowBlock
    // See if we want to compress
    if (!keepContinuous_) {
      // use blockEls
      int newNumber = 0;
      for (int i = 0; i < numberBlocks; i++) {
        sprintf(dwPrint, "Block %d has %d rows and %d columns (%d elements, %d integers)",
          i, blockCount[i], nextColumn[i], blockEls[i], countIntegers[i]);
        model_->messageHandler()->message(CBC_FPUMP2, model_->messages())
          << dwPrint
          << CoinMessageEol;
        if (countIntegers[i]) {
          blockEls[i] = newNumber;
          newNumber++;
        } else {
          blockEls[i] = -1;
        }
      }
      for (int i = 0; i < numberRows; i++) {
        int iBlock = blockStart[i];
        if (iBlock >= 0)
          blockStart[i] = blockEls[iBlock];
      }
      for (int i = 0; i < numberColumns; i++) {
        int iBlock = columnBlock[i];
        if (iBlock >= 0)
          columnBlock[i] = blockEls[iBlock];
      }
      if (newNumber < numberBlocks) {
        sprintf(dwPrint, "Number of blocks reduced from %d to %d",
          numberBlocks, newNumber);
        model_->messageHandler()->message(CBC_FPUMP1, model_->messages())
          << dwPrint
          << CoinMessageEol;
      }
      numberBlocks = newNumber;
    }
    // now set up structures
    numberBlocks_ = numberBlocks;
    // so callBack can modify
    whichRowBlock_ = blockStart;
    whichColumnBlock_ = columnBlock;
    intArray_ = NULL;
    doubleArray_ = NULL;
    (*(functionPointer_))(this, NULL, 0);

    saveLower_ = CoinCopyOfArray(solver_->getColLower(), numberColumns);
    saveUpper_ = CoinCopyOfArray(solver_->getColUpper(), numberColumns);
    startRowBlock_ = new int[numberBlocks_ + 2];
    backwardRow_ = new int[numberRows];
    rowsInBlock_ = new int[numberRows];
    whichRowBlock_ = new int[numberRows];
    startColumnBlock_ = new int[numberBlocks_ + 2];
    columnsInBlock_ = new int[numberColumns];
    whichColumnBlock_ = new int[numberColumns];
    intsInBlock_ = new int[numberBlocks_];
    // use for counts
    memset(rowsInBlock_, 0, numberBlocks_ * sizeof(int));
    numberMasterRows_ = 0;
    for (int i = 0; i < numberRows; i++) {
      int iBlock = blockStart[i];
      if (iBlock >= 0) {
        rowsInBlock_[iBlock]++;
        whichRowBlock_[i] = iBlock;
        backwardRow_[i] = -1;
      } else {
        whichRowBlock_[i] = -1;
        backwardRow_[i] = numberMasterRows_;
        numberMasterRows_++;
      }
    }
    memset(columnsInBlock_, 0, numberBlocks_ * sizeof(int));
    memset(intsInBlock_, 0, numberBlocks_ * sizeof(int));
    numberMasterColumns_ = 0;
    for (int i = 0; i < numberColumns; i++) {
      int iBlock = columnBlock[i];
      if (iBlock >= 0) {
        columnsInBlock_[iBlock]++;
        whichColumnBlock_[i] = iBlock;
        if (solver_->isInteger(i))
          intsInBlock_[iBlock]++;
      } else {
        whichColumnBlock_[i] = -1;
        numberMasterColumns_++;
      }
    }
    // starts
    int nRow = 0;
    int nColumn = 0;
    int maxIntsInBlock = 0;
    for (int i = 0; i < numberBlocks_; i++) {
      maxIntsInBlock = CoinMax(maxIntsInBlock, intsInBlock_[i]);
      startRowBlock_[i] = nRow;
      startColumnBlock_[i] = nColumn;
      nRow += rowsInBlock_[i];
      nColumn += columnsInBlock_[i];
    }
    // may not be used - but set anyway
    sizeFingerPrint_ = (maxIntsInBlock + 31) / 32;
    startRowBlock_[numberBlocks_] = nRow;
    startColumnBlock_[numberBlocks_] = nColumn;
    startRowBlock_[numberBlocks_ + 1] = numberRows;
    startColumnBlock_[numberBlocks_ + 1] = numberColumns;
    // do lists
    for (int i = 0; i < numberRows; ++i) {
      int iBlock = whichRowBlock_[i];
      if (iBlock < 0)
        iBlock = numberBlocks_;
      int k = startRowBlock_[iBlock];
      startRowBlock_[iBlock] = k + 1;
      rowsInBlock_[k] = i;
    }
    for (int i = numberBlocks + 1; i > 0; i--)
      startRowBlock_[i] = startRowBlock_[i - 1];
    startRowBlock_[0] = 0;
    for (int i = 0; i < numberColumns; ++i) {
      int iBlock = whichColumnBlock_[i];
      if (iBlock < 0)
        iBlock = numberBlocks_;
      int k = startColumnBlock_[iBlock];
      startColumnBlock_[iBlock] = k + 1;
      columnsInBlock_[k] = i;
    }
    for (int i = numberBlocks + 1; i > 0; i--)
      startColumnBlock_[i] = startColumnBlock_[i - 1];
    startColumnBlock_[0] = 0;
    if (numberBlocks_ < 10000) {
      affinity_ = new unsigned short[numberBlocks_ * numberBlocks_];
      // compute space needed
      int *build = new int[numberMasterRows_];
      memset(build, 0, numberMasterRows_ * sizeof(int));
      int nSpace = 0;
      for (int iBlock = 0; iBlock < numberBlocks_; iBlock++) {
        int start = startColumnBlock_[iBlock];
        int end = startColumnBlock_[iBlock + 1];
        for (int i = start; i < end; i++) {
          int iColumn = columnsInBlock_[i];
          for (CoinBigIndex j = columnStart[iColumn];
               j < columnStart[iColumn] + columnLength[iColumn]; j++) {
            int iRow = row[j];
            iRow = backwardRow_[iRow];
            if (iRow >= 0)
              build[iRow]++;
          }
        }
        for (int i = 0; i < numberMasterRows_; i++) {
          int value = build[i];
          if (value) {
            build[i] = 0;
            nSpace++;
          }
        }
      }
      // get arrays
      int *starts = new int[numberBlocks_ + 1 + 2 * nSpace];
      memset(affinity_, 0, numberBlocks_ * numberBlocks_ * sizeof(unsigned short));
      // fill arrays
      int *rowM = starts + numberBlocks_ + 1;
      int *sumM = rowM + nSpace;
      nSpace = 0;
      starts[0] = 0;
      for (int iBlock = 0; iBlock < numberBlocks_; iBlock++) {
        int start = startColumnBlock_[iBlock];
        int end = startColumnBlock_[iBlock + 1];
        for (int i = start; i < end; i++) {
          int iColumn = columnsInBlock_[i];
          for (CoinBigIndex j = columnStart[iColumn];
               j < columnStart[iColumn] + columnLength[iColumn]; j++) {
            int iRow = row[j];
            iRow = backwardRow_[iRow];
            if (iRow >= 0)
              build[iRow]++;
          }
        }
        for (int i = 0; i < numberMasterRows_; i++) {
          int value = build[i];
          if (value) {
            build[i] = 0;
            sumM[nSpace] = value;
            rowM[nSpace++] = i;
          }
        }
        starts[iBlock + 1] = nSpace;
      }
      for (int iBlock = 0; iBlock < numberBlocks_; iBlock++) {
        int startI = starts[iBlock];
        int endI = starts[iBlock + 1];
        if (endI == startI)
          continue;
        for (int jBlock = iBlock + 1; jBlock < numberBlocks_; jBlock++) {
          int startJ = starts[jBlock];
          int endJ = starts[jBlock + 1];
          if (endJ == startJ)
            continue;
          double sum = 0.0;
          int i = startI;
          int j = startJ;
          int rowI = rowM[i];
          int rowJ = rowM[j];
          while (rowI != COIN_INT_MAX && rowJ != COIN_INT_MAX) {
            if (rowI < rowJ) {
              i++;
              if (i < endI)
                rowI = rowM[i];
              else
                rowI = COIN_INT_MAX;
            } else if (rowI > rowJ) {
              j++;
              if (j < endJ)
                rowJ = rowM[j];
              else
                rowJ = COIN_INT_MAX;
            } else {
              // bias ????????
              sum += sumM[i] * sumM[j];
              i++;
              if (i < endI)
                rowI = rowM[i];
              else
                rowI = COIN_INT_MAX;
              j++;
              if (j < endJ)
                rowJ = rowM[j];
              else
                rowJ = COIN_INT_MAX;
            }
          }
          if (sum > 65535)
            sum = 65535;
          unsigned short value = static_cast< unsigned short >(sum);
          affinity_[iBlock * numberBlocks + jBlock] = value;
          affinity_[jBlock * numberBlocks + iBlock] = value;
        }
      }
      // statistics
      int nTotalZero = 0;
      int base = 0;
      for (int iBlock = 0; iBlock < numberBlocks_; iBlock++) {
        int aff = 0;
        int nZero = 0;
        for (int jBlock = 0; jBlock < numberBlocks_; jBlock++) {
          if (iBlock != jBlock) {
            if (affinity_[base + jBlock])
              aff += affinity_[base + jBlock];
            else
              nZero++;
          }
        }
        //printf("Block %d has affinity %d but zero with %d blocks",
        //     iBlock,aff,nZero);
        nTotalZero += nZero;
        base += numberBlocks;
      }
      sprintf(dwPrint, "Total not affinity %d - average %g%%",
        nTotalZero, 100.0 * (static_cast< double >(nTotalZero) / (numberBlocks * numberBlocks)));
      model_->messageHandler()->message(CBC_FPUMP1, model_->messages())
        << dwPrint
        << CoinMessageEol;

      delete[] starts;
      delete[] build;
    } else {
      sprintf(dwPrint, "Too many blocks - no affinity");
      model_->messageHandler()->message(CBC_FPUMP1, model_->messages())
        << dwPrint
        << CoinMessageEol;
    }
    if (fullDWEverySoOften_ > 0) {
      setupDWStructures();
    }
  }
  delete[] blockStart;
}
// Add DW proposals
int CbcHeuristicDW::addDW(const double *solution, int numberBlocksUsed,
  const int *whichBlocks)
{
  char dwPrint[200];
  if (numberDW_ + numberBlocksUsed > maximumDW_) {
    // extend
    int n = maximumDW_ + 5 * numberBlocks_;
    double *weightsT = new double[n];
    int *dwBlockT = new int[n];
    unsigned int *fingerT = new unsigned int[n * sizeFingerPrint_];
    memcpy(weightsT, weights_, numberDW_ * sizeof(double));
    memcpy(dwBlockT, dwBlock_, numberDW_ * sizeof(int));
    memcpy(fingerT, fingerPrint_,
      numberDW_ * sizeFingerPrint_ * sizeof(unsigned int));
    delete[] weights_;
    weights_ = weightsT;
    delete[] dwBlock_;
    dwBlock_ = dwBlockT;
    delete[] fingerPrint_;
    fingerPrint_ = fingerT;
    maximumDW_ = n;
  }
  //int numberColumns = solver_->getNumCols();
  //int numberRows = solver_->getNumRows();
  // get space to add elements
#define MAX_ADD 100000
  CoinBigIndex *startsDW = new CoinBigIndex[numberBlocks_ + 1 + MAX_ADD];
  int *rowDW = reinterpret_cast< int * >(startsDW + numberBlocks_ + 1);
  double *elementDW = new double[MAX_ADD + 3 * numberBlocks_ + numberMasterRows_];
  double *newCost = elementDW + MAX_ADD;
  double *newLower = newCost + numberBlocks_;
  double *newUpper = newLower + numberBlocks_;
  double *build = newUpper + numberBlocks_;
  memset(build, 0, numberMasterRows_ * sizeof(double));
  int nAdd = 0;
  int nTotalAdded = 0;
  int nEls = 0;
  startsDW[0] = 0;
  // Column copy
  const double *element = solver_->getMatrixByCol()->getElements();
  const int *row = solver_->getMatrixByCol()->getIndices();
  const CoinBigIndex *columnStart = solver_->getMatrixByCol()->getVectorStarts();
  const int *columnLength = solver_->getMatrixByCol()->getVectorLengths();
  const double *objective = solver_->getObjCoefficients();
  for (int jBlock = 0; jBlock < numberBlocksUsed; jBlock++) {
    double thisWeight = 0.0;
    double thisWeightC = 0.0;
    double thisCost = 0.0;
    int nElInMaster = 0;
    int nElIntInMaster = 0;
    int nElIntInMaster1 = 0;
    int iBlock = whichBlocks[jBlock];
    int start = startColumnBlock_[iBlock];
    int end = startColumnBlock_[iBlock + 1];
    unsigned int *finger = fingerPrint_ + sizeFingerPrint_ * (nAdd + numberDW_);
    memset(finger, 0, sizeFingerPrint_ * sizeof(unsigned int));
    int iBit = 0;
    for (int i = start; i < end; i++) {
      int iColumn = columnsInBlock_[i];
      bool isInteger = solver_->isInteger(iColumn);
      double value = solution[iColumn];
      if (isInteger) {
        if (value > 1.0e-6)
          finger[0] |= 1 << iBit;
        iBit++;
        if (iBit == 32) {
          finger++;
          iBit++;
        }
      }
      thisCost += value * objective[iColumn];
      for (CoinBigIndex j = columnStart[iColumn];
           j < columnStart[iColumn] + columnLength[iColumn]; j++) {
        int iRow = row[j];
        iRow = backwardRow_[iRow];
        if (iRow >= 0) {
          nElInMaster++;
          double elementValue = element[j];
          build[iRow] += value * elementValue;
          if (isInteger) {
            nElIntInMaster++;
            if (value)
              nElIntInMaster1++;
            thisWeight += random_[iRow] * value * elementValue;
          } else {
            value = 0.0001 * floor(value * 10000.0 + 0.5);
          }
          thisWeightC += random_[iRow] * value * elementValue;
        }
      }
    }
    // see if already in
    sprintf(dwPrint, "block %d nel %d nelInt %d nelInt1 %d - weight %g (%g)",
      iBlock, nElInMaster, nElIntInMaster, nElIntInMaster1,
      thisWeight, thisWeightC);
    model_->messageHandler()->message(CBC_FPUMP2, model_->messages())
      << dwPrint
      << CoinMessageEol;
    int iProposal;
    for (iProposal = 0; iProposal < numberDW_; iProposal++) {
      if (iBlock == dwBlock_[iProposal] && weights_[iProposal] == thisWeightC)
        break;
    }
    if (iProposal < numberDW_) {
      sprintf(dwPrint, "above looks like duplicate");
      model_->messageHandler()->message(CBC_FPUMP2, model_->messages())
        << dwPrint
        << CoinMessageEol;
      memset(build, 0, numberMasterRows_ * sizeof(double));
      //iProposal=numberDW;
    }
    if (iProposal == numberDW_) {
      // new
      // could build in stages
      assert(nEls + numberMasterRows_ < MAX_ADD);
      for (int i = 0; i < numberMasterRows_; i++) {
        double value = build[i];
        if (value) {
          build[i] = 0.0;
          if (fabs(value) > 1.0e-10) {
            elementDW[nEls] = value;
            rowDW[nEls++] = i;
          }
        }
      }
      // convexity
      elementDW[nEls] = 1.0;
      rowDW[nEls++] = numberMasterRows_ + iBlock;
      weights_[nAdd + numberDW_] = thisWeightC;
      dwBlock_[nAdd + numberDW_] = iBlock;
      newLower[nAdd] = 0.0;
      newUpper[nAdd] = 1.0;
      newCost[nAdd++] = thisCost;
      startsDW[nAdd] = nEls;
    }
    if (nEls + numberMasterRows_ > MAX_ADD) {
      sprintf(dwPrint, "Adding %d proposals with %d elements - out of room",
        nAdd, nEls);
      model_->messageHandler()->message(CBC_FPUMP1, model_->messages())
        << dwPrint
        << CoinMessageEol;
      dwSolver_->addCols(nAdd, startsDW, rowDW, elementDW, newLower,
        newUpper, newCost);
      numberDW_ += nAdd;
      nTotalAdded += nAdd;
      nAdd = 0;
      nEls = 0;
    }
  }
  if (nAdd) {
    sprintf(dwPrint, "Adding %d proposals with %d elements",
      nAdd, nEls);
    model_->messageHandler()->message(CBC_FPUMP1, model_->messages())
      << dwPrint
      << CoinMessageEol;
    dwSolver_->addCols(nAdd, startsDW, rowDW, elementDW, newLower,
      newUpper, newCost);
    nTotalAdded += nAdd;
    numberDW_ += nAdd;
  }
  delete[] startsDW;
  delete[] elementDW;
  if (nTotalAdded) {
    double *objs = new double[numberDWTimes_ + 1];
    memcpy(objs, objectiveDW_, numberDWTimes_ * sizeof(double));
    delete[] objectiveDW_;
    objectiveDW_ = objs;
    int *temp = new int[numberDWTimes_ + 1];
    memcpy(temp, numberColumnsDW_, numberDWTimes_ * sizeof(int));
    delete[] numberColumnsDW_;
    numberColumnsDW_ = temp;
    numberColumnsDW_[numberDWTimes_] = dwSolver_->getNumCols();
    objectiveDW_[numberDWTimes_++] = objectiveValue(solution);
  }
  return nTotalAdded;
}
// Pass in a solution
void CbcHeuristicDW::passInSolution(const double *solution)
{
  if (fullDWEverySoOften_ > 0) {
    int *which = new int[numberBlocks_];
    for (int i = 0; i < numberBlocks_; i++)
      which[i] = i;
    addDW(solution, numberBlocks_, which);
    delete[] which;
  }
  if (objectiveValue(solution) < bestObjective_ - 1.0e-5) {
    bestObjective_ = objectiveValue(solution);
    int numberColumns = solver_->getNumCols();
    if (!bestSolution_)
      bestSolution_ = new double[numberColumns];
    memcpy(bestSolution_, solution, numberColumns * sizeof(double));
  }
}
// Pass in continuous solution
void CbcHeuristicDW::passInContinuousSolution(const double *solution)
{
  int numberColumns = solver_->getNumCols();
  if (!continuousSolution_)
    continuousSolution_ = new double[numberColumns];
  memcpy(continuousSolution_, solution, numberColumns * sizeof(double));
}
// Objective value (could also check validity)
double
CbcHeuristicDW::objectiveValue(const double *solution)
{
  // compute objective value
  double objOffset = 0.0;
  solver_->getDblParam(OsiObjOffset, objOffset);
  double objectiveValue = -objOffset;
  int numberColumns = solver_->getNumCols();
  const double *objective = solver_->getObjCoefficients();
  int logLevel = model_->messageHandler()->logLevel();
  for (int i = 0; i < numberColumns; i++) {
    double value = solution[i];
    if (logLevel > 1) {
      if (solver_->isInteger(i)) {
        if (fabs(value - floor(value + 0.5)) > 1.0e-7)
          printf("Bad integer value for %d of %g\n", i, value);
      }
    }
    objectiveValue += objective[i] * value;
  }
  return objectiveValue;
}
// Objective value when whichDw created
double
CbcHeuristicDW::objectiveValueWhen(int whichDW) const
{
  if (whichDW >= numberDWTimes_)
    return COIN_DBL_MAX;
  else
    return objectiveDW_[whichDW];
}
// Number of columns in DW
int CbcHeuristicDW::numberColumnsDW(int whichDW) const
{
  if (whichDW >= numberDWTimes_)
    return COIN_INT_MAX;
  else
    return numberColumnsDW_[whichDW];
}
// DW model (user must delete)
OsiSolverInterface *
CbcHeuristicDW::DWModel(int whichDW) const
{
  if (whichDW >= numberDWTimes_)
    return NULL;
  OsiSolverInterface *newSolver = dwSolver_->clone();
  int numberColumns2 = newSolver->getNumCols();
  int numberColumns = numberColumnsDW_[whichDW];
  if (numberColumns < numberColumns2) {
    int *del = new int[numberColumns2 - numberColumns];
    for (int i = numberColumns; i < numberColumns2; i++)
      del[i - numberColumns] = i;
    newSolver->deleteCols(numberColumns2 - numberColumns, del);
    delete[] del;
  }
  // Set all to integer that need setting
  for (int i = numberMasterColumns_; i < numberColumns; i++) {
    newSolver->setContinuous(i);
  }
  int numberDW = numberColumns - numberMasterColumns_;
  for (int iBlock = 0; iBlock < numberBlocks_; iBlock++) {
    bool allSame = true;
    unsigned int *finger = fingerPrint_;
    unsigned int *fingerTest = NULL;
    for (int i = 0; i < numberDW; i++) {
      if (dwBlock_[i] == iBlock) {
        if (fingerTest) {
          for (int j = 0; j < sizeFingerPrint_; j++) {
            if (finger[j] != fingerTest[j]) {
              allSame = false;
              break;
            }
          }
          if (!allSame)
            break;
        } else {
          fingerTest = finger;
        }
      }
      finger += sizeFingerPrint_;
    }
    if (!allSame) {
      // Set all to integer that need setting
      for (int i = 0; i < numberDW; i++) {
        if (iBlock == dwBlock_[i]) {
          int iColumn = numberMasterColumns_ + i;
          newSolver->setInteger(iColumn);
        }
      }
    }
  }
  //newSolver->writeMps("dw","mps");
  return newSolver;
}
/* DW Proposal actions
   fullDWEverySoOften -
   0 - off
   k - every k times solution gets better
*/
void CbcHeuristicDW::setProposalActions(int fullDWEverySoOften)
{
  fullDWEverySoOften_ = fullDWEverySoOften;
  if (fullDWEverySoOften_ > 0 && !random_)
    setupDWStructures();
}
// Set up DW structure
void CbcHeuristicDW::setupDWStructures()
{
  char dwPrint[200];
  random_ = new double[numberMasterRows_];
  for (int i = 0; i < numberMasterRows_; i++)
    random_[i] = CoinDrand48();
  weights_ = new double[numberBlocks_];
  dwBlock_ = new int[numberBlocks_];
  fingerPrint_ = new unsigned int[numberBlocks_ * sizeFingerPrint_];
  // create dwSolver
  int numberColumns = solver_->getNumCols();
  int numberRows = solver_->getNumRows();
  int *tempRow = new int[numberRows + numberColumns];
  int *tempColumn = tempRow + numberRows;
  int numberMasterRows = 0;
  for (int i = 0; i < numberRows; i++) {
    int iBlock = whichRowBlock_[i];
    if (iBlock < 0)
      tempRow[numberMasterRows++] = i;
  }
  int numberMasterColumns = 0;
  for (int i = 0; i < numberColumns; i++) {
    int iBlock = whichColumnBlock_[i];
    if (iBlock < 0)
      tempColumn[numberMasterColumns++] = i;
  }
  OsiClpSolverInterface *solver = dynamic_cast< OsiClpSolverInterface * >(solver_);
  ClpSimplex *tempModel = new ClpSimplex(solver->getModelPtr(),
    numberMasterRows, tempRow,
    numberMasterColumns, tempColumn);
  // add convexity constraints
  double *rhs = new double[numberBlocks_];
  for (int i = 0; i < numberBlocks_; i++)
    rhs[i] = 1.0;
  tempModel->addRows(numberBlocks_, rhs, rhs, NULL, NULL, NULL);
  delete[] rhs;
  OsiClpSolverInterface *clpSolver = new OsiClpSolverInterface(tempModel, true);
  clpSolver->getModelPtr()->setDualObjectiveLimit(COIN_DBL_MAX);
  dwSolver_ = clpSolver;
  sprintf(dwPrint, "DW model has %d master rows, %d master columns and %d convexity rows",
    numberMasterRows, numberMasterColumns, numberBlocks_);
  model_->messageHandler()->message(CBC_FPUMP1, model_->messages())
    << dwPrint
    << CoinMessageEol;
  // do master integers
  for (int i = 0; i < numberMasterColumns; i++) {
    int iColumn = tempColumn[i];
    if (solver->isInteger(iColumn))
      dwSolver_->setInteger(i);
  }
  delete[] tempRow;
}

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