/* $Id: CbcStrategy.cpp 1641 2011-04-17 15:08:40Z forrest $ */
// Copyright (C) 2005, 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"

#include <cassert>
#include <cstdlib>
#include <cmath>
#include <cfloat>

#include "OsiSolverInterface.hpp"
#ifdef COIN_HAS_CLP
#include "OsiClpSolverInterface.hpp"
#endif
#include "CbcModel.hpp"
#include "CbcMessage.hpp"
#include "CbcStrategy.hpp"
#include "CbcCutGenerator.hpp"
#include "CbcBranchActual.hpp"
#include "CbcNode.hpp"
#include "CoinWarmStart.hpp"
#include "CglPreProcess.hpp"
// Cuts

#include "CglGomory.hpp"
#include "CglProbing.hpp"
#include "CglKnapsackCover.hpp"
#include "CglOddHole.hpp"
#include "CglClique.hpp"
#include "CglFlowCover.hpp"
#include "CglMixedIntegerRounding2.hpp"

// Heuristics

#include "CbcHeuristic.hpp"
#include "CbcHeuristicLocal.hpp"
#include "CbcHeuristicRINS.hpp"

// Default Constructor
CbcStrategy::CbcStrategy()
        : depth_(0),
        preProcessState_(0),
        process_(NULL)
{
}

// Destructor
CbcStrategy::~CbcStrategy ()
{
    delete process_;
}
// Delete pre-processing object to save memory
void
CbcStrategy::deletePreProcess()
{
    delete process_;
    process_ = NULL;
}
// Return a new Full node information pointer (descendant of CbcFullNodeInfo)
CbcNodeInfo *
CbcStrategy::fullNodeInfo(CbcModel * model, int numberRowsAtContinuous) const
{
    return new CbcFullNodeInfo(model, numberRowsAtContinuous);
}
// Return a new Partial node information pointer (descendant of CbcPartialNodeInfo)
CbcNodeInfo *
CbcStrategy::partialNodeInfo(CbcModel * /*model*/,
                             CbcNodeInfo * parent, CbcNode * owner,
                             int numberChangedBounds, const int * variables,
                             const double * boundChanges,
                             const CoinWarmStartDiff *basisDiff) const
{
    return new CbcPartialNodeInfo(parent, owner, numberChangedBounds, variables,
                                  boundChanges, basisDiff);
}
/* After a CbcModel::resolve this can return a status
   -1 no effect
   0 treat as optimal
   1 as 0 but do not do any more resolves (i.e. no more cuts)
   2 treat as infeasible
*/
int
CbcStrategy::status(CbcModel * /*model*/, CbcNodeInfo * /*parent*/,
                    int /*whereFrom*/)
{
    return -1;
}

// Default Constructor
CbcStrategyDefault::CbcStrategyDefault(int cutsOnlyAtRoot,
                                       int numberStrong,
                                       int numberBeforeTrust,
                                       int printLevel)
        : CbcStrategy(),
        cutsOnlyAtRoot_(cutsOnlyAtRoot),
        numberStrong_(numberStrong),
        numberBeforeTrust_(numberBeforeTrust),
        printLevel_(printLevel),
        desiredPreProcess_(0),
        preProcessPasses_(0)
{
}


// Destructor
CbcStrategyDefault::~CbcStrategyDefault ()
{
}

// Clone
CbcStrategy *
CbcStrategyDefault::clone() const
{
    return new CbcStrategyDefault(*this);
}

// Copy constructor
CbcStrategyDefault::CbcStrategyDefault(const CbcStrategyDefault & rhs)
        :
        CbcStrategy(rhs),
        cutsOnlyAtRoot_(rhs.cutsOnlyAtRoot_),
        numberStrong_(rhs.numberStrong_),
        numberBeforeTrust_(rhs.numberBeforeTrust_),
        printLevel_(rhs.printLevel_),
        desiredPreProcess_(rhs.desiredPreProcess_),
        preProcessPasses_(rhs.preProcessPasses_)
{
    setNested(rhs.getNested());
}

/*
  Set up cut generators. Will instantiate Probing, Gomory, Knapsack, Clique,
  FlowCover, and MIR2 generators. Probing should be the first in the vector
  of generators as it tightens bounds on continuous variables.

  Cut generators already installed will dominate cut generators instantiated
  here.

  There's a classic magic number overloaded parameter example here. The
  variable genFlags below is interpreted as single-bit flags to control
  whether a cut generator will be instantiated: Probing:1, Gomory:2,
  Knapsack:4, Clique:8, FlowCover:16, MIR2:32. Normally it's hardcoded to 63.
  If CBC_GENERATE_TEST is defined, and the model's node limit is set between
  190000 and 190064, genFlags is loaded with the low-order bits.
*/
void
CbcStrategyDefault::setupCutGenerators(CbcModel & model)
{
    if (cutsOnlyAtRoot_ < 0)
        return; // no cuts wanted

    // Magic number overloaded parameter -- see comment at head.
    int genFlags = 63;
#   ifdef CBC_GENERATE_TEST
    int nNodes = model.getMaximumNodes();
    if (nNodes >= 190000 && nNodes < 190064)
        genFlags = nNodes - 190000;
#   endif

    CglProbing generator1;
    generator1.setUsingObjective(true);
    generator1.setMaxPass(1);
    generator1.setMaxPassRoot(1);
    // Number of unsatisfied variables to look at
    generator1.setMaxProbe(10);
    // How far to follow the consequences
    generator1.setMaxLook(10);
    // Only look at rows with fewer than this number of elements
    generator1.setMaxElements(200);
    generator1.setMaxElementsRoot(300);
    //generator1.setRowCuts(3);

    CglGomory generator2;
    // try larger limit
    generator2.setLimit(300);

    CglKnapsackCover generator3;

    //CglOddHole generator4;
    //generator4.setMinimumViolation(0.005);
    //generator4.setMinimumViolationPer(0.00002);
    // try larger limit
    //generator4.setMaximumEntries(200);

    CglClique generator5;
    generator5.setStarCliqueReport(false);
    generator5.setRowCliqueReport(false);

    CglMixedIntegerRounding2 mixedGen;
    CglFlowCover flowGen;

    /*
      Add in generators. Do not override generators already installed.
    */
    int setting = cutsOnlyAtRoot_ ? -99 : -1;
    int numberGenerators = model.numberCutGenerators();
    int iGenerator;
    bool found;
    found = false;
    for (iGenerator = 0; iGenerator < numberGenerators; iGenerator++) {
        CglCutGenerator * generator = model.cutGenerator(iGenerator)->generator();
        CglProbing * cgl = dynamic_cast<CglProbing *>(generator);
        if (cgl) {
            found = true;
            break;
        }
    }
    if (!found && (genFlags&1) != 0)
        model.addCutGenerator(&generator1, setting, "Probing");
    found = false;

    for (iGenerator = 0; iGenerator < numberGenerators; iGenerator++) {
        CglCutGenerator * generator = model.cutGenerator(iGenerator)->generator();
        CglGomory * cgl = dynamic_cast<CglGomory *>(generator);
        if (cgl) {
            found = true;
            break;
        }
    }
    if (!found && (genFlags&2) != 0)
        model.addCutGenerator(&generator2, setting, "Gomory");

    found = false;
    for (iGenerator = 0; iGenerator < numberGenerators; iGenerator++) {
        CglCutGenerator * generator = model.cutGenerator(iGenerator)->generator();
        CglKnapsackCover * cgl = dynamic_cast<CglKnapsackCover *>(generator);
        if (cgl) {
            found = true;
            break;
        }
    }
    if (!found && (genFlags&4) != 0)
        model.addCutGenerator(&generator3, setting, "Knapsack");
    //model.addCutGenerator(&generator4,setting,"OddHole");

    found = false;
    for (iGenerator = 0; iGenerator < numberGenerators; iGenerator++) {
        CglCutGenerator * generator = model.cutGenerator(iGenerator)->generator();
        CglClique * cgl = dynamic_cast<CglClique *>(generator);
        if (cgl) {
            found = true;
            break;
        }
    }
    if (!found && (genFlags&8) != 0)
        model.addCutGenerator(&generator5, setting, "Clique");

    found = false;
    for (iGenerator = 0; iGenerator < numberGenerators; iGenerator++) {
        CglCutGenerator * generator = model.cutGenerator(iGenerator)->generator();
        CglFlowCover * cgl = dynamic_cast<CglFlowCover *>(generator);
        if (cgl) {
            found = true;
            break;
        }
    }
    if (!found && (genFlags&16) != 0)
        model.addCutGenerator(&flowGen, setting, "FlowCover");

    found = false;
    for (iGenerator = 0; iGenerator < numberGenerators; iGenerator++) {
        CglCutGenerator * generator = model.cutGenerator(iGenerator)->generator();
        CglMixedIntegerRounding2 * cgl = dynamic_cast<CglMixedIntegerRounding2 *>(generator);
        if (cgl) {
            found = true;
            break;
        }
    }
    if (!found && (genFlags&32) != 0)
        model.addCutGenerator(&mixedGen, setting, "MixedIntegerRounding2");

    // Say we want timings
    int newNumberGenerators = model.numberCutGenerators();
    for (iGenerator = numberGenerators; iGenerator < newNumberGenerators; iGenerator++) {
        CbcCutGenerator * generator = model.cutGenerator(iGenerator);
        generator->setTiming(true);
    }

    // Caution! Undocumented magic numbers.
    int currentPasses = model.getMaximumCutPassesAtRoot();
    if (currentPasses >= 0) {
        if (model.getNumCols() < 5000)
            model.setMaximumCutPassesAtRoot(CoinMax(50, currentPasses)); // use minimum drop
        else
            model.setMaximumCutPassesAtRoot(CoinMax(20, currentPasses));
    } else {
        currentPasses = -currentPasses;
        if (model.getNumCols() < 500)
            model.setMaximumCutPassesAtRoot(-CoinMax(100, currentPasses)); // always do 100 if possible
        else
            model.setMaximumCutPassesAtRoot(-CoinMax(20, currentPasses));
    }
}
// Setup heuristics
void
CbcStrategyDefault::setupHeuristics(CbcModel & model)
{
    // Allow rounding heuristic

    CbcRounding heuristic1(model);
    heuristic1.setHeuristicName("rounding");
    int numberHeuristics = model.numberHeuristics();
    int iHeuristic;
    bool found;
    found = false;
    for (iHeuristic = 0; iHeuristic < numberHeuristics; iHeuristic++) {
        CbcHeuristic * heuristic = model.heuristic(iHeuristic);
        CbcRounding * cgl = dynamic_cast<CbcRounding *>(heuristic);
        if (cgl) {
            found = true;
            break;
        }
    }
    if (!found)
        model.addHeuristic(&heuristic1);
#ifdef JJF_ZERO
    // Allow join solutions
    CbcHeuristicLocal heuristic2(model);
    heuristic2.setHeuristicName("join solutions");
    heuristic2.setSearchType(1);
    found = false;
    for (iHeuristic = 0; iHeuristic < numberHeuristics; iHeuristic++) {
        CbcHeuristic * heuristic = model.heuristic(iHeuristic);
        CbcHeuristicLocal * cgl = dynamic_cast<CbcHeuristicLocal *>(heuristic);
        if (cgl) {
            found = true;
            break;
        }
    }
    if (!found)
        model.addHeuristic(&heuristic2);
#endif
}
// Do printing stuff
void
CbcStrategyDefault::setupPrinting(CbcModel & model, int modelLogLevel)
{
    if (!modelLogLevel) {
        model.solver()->setHintParam(OsiDoReducePrint, true, OsiHintTry);
        model.messageHandler()->setLogLevel(0);
        model.solver()->messageHandler()->setLogLevel(0);
    } else if (modelLogLevel == 1) {
        model.solver()->setHintParam(OsiDoReducePrint, true, OsiHintTry);
        model.messageHandler()->setLogLevel(1);
        model.solver()->messageHandler()->setLogLevel(0);
    } else {
        model.messageHandler()->setLogLevel(CoinMax(2, model.messageHandler()->logLevel()));
        model.solver()->messageHandler()->setLogLevel(CoinMax(1, model.solver()->messageHandler()->logLevel()));
        model.setPrintFrequency(CoinMin(50, model.printFrequency()));
    }
}

/*
 Aside from setting CbcModel::numberStrong_ and numberBeforeTrust, the big
 activity is integer preprocessing. Surely this code to do preprocessing
 duplicates code to do preprocessing up in the solver main routine. Most of the
 effort goes into manipulating SOS sets.
*/
// Other stuff e.g. strong branching
void
CbcStrategyDefault::setupOther(CbcModel & model)
{
    // See if preprocessing wanted
    if (desiredPreProcess_) {
        delete process_;
        /*
          Inaccurate as of 080122 --- assignSolver (below) can now be instructed not to
          delete the existing solver when the preprocessed solver is assigned to the
          model. 'Course, we do need to hold on to a pointer somewhere, and that must
          be captured before this call.
        */
        // solver_ should have been cloned outside
        CglPreProcess * process = new CglPreProcess();
        // Pass in models message handler
        process->passInMessageHandler(model.messageHandler());
        OsiSolverInterface * solver = model.solver();
#ifdef COIN_HAS_CLP
        OsiClpSolverInterface * clpSolver = dynamic_cast< OsiClpSolverInterface*> (solver);
        if (clpSolver && false) {
            // see if all coefficients multiple of 0.01 (close enough)
            CoinPackedMatrix * matrix = clpSolver->getModelPtr()->matrix();
            double * element = matrix->getMutableElements();
            //const int * row = matrix->getIndices();
            const CoinBigIndex * columnStart = matrix->getVectorStarts();
            const int * columnLength = matrix->getVectorLengths();
            int numberInt = 0;
            int numberNon = 0;
            int numberClose = 0;
            int numberColumns = clpSolver->getNumCols();
            int iColumn;
            for (iColumn = 0; iColumn < numberColumns; iColumn++) {
                for (int j = columnStart[iColumn];
                        j < columnStart[iColumn] + columnLength[iColumn]; j++) {
                    //int iRow = row[j];
                    double value1 = element[j];
                    double value = fabs(value1);
                    if (value > 1.0e7) {
                        if (value != floor(value))
                            numberNon++;
                        else
                            numberInt++;
                    } else {
                        int iValue = static_cast<int>( 100 * (value + 0.005));
                        double value2 = iValue;
                        if (value2 == 100.0*value) {
                            numberInt++;
                        } else if (fabs(value2 - 100.0*value) < 1.0e-5) {
                            numberClose++;
                        } else {
                            numberNon++;
                        }
                    }
                }
            }
            if (!numberNon && numberClose) {
                COIN_DETAIL_PRINT(printf("Tidying %d multiples of 0.01, %d close\n",
					 numberInt, numberClose));
                for (iColumn = 0; iColumn < numberColumns; iColumn++) {
                    for (int j = columnStart[iColumn];
                            j < columnStart[iColumn] + columnLength[iColumn]; j++) {
                        //int iRow = row[j];
                        double value1 = element[j];
                        double value = fabs(value1);
                        if (value < 1.0e7) {
                            int iValue = static_cast<int>( 100 * (value + 0.005));
                            double value2 = iValue;
                            if (value2 != 100.0*value) {
                                value2 *= 0.01;
                                if (fabs(value - floor(value + 0.5)) <= 1.0e-7)
                                    value2 = floor(value + 0.5);
                                if (value1 < 0.0)
                                    value2 = -value2;
                                element[j] = value2;
                            }
                        }
                    }
                }
            }
        }
#endif
        {
            // mark some columns as ineligible for presolve
            int numberColumns = solver->getNumCols();
            char * prohibited = new char[numberColumns];
            memset(prohibited, 0, numberColumns);
            int numberProhibited = 0;
            /*
              Create CbcSimpleInteger objects would be more accurate in the general
              case.  The `false' parameter says we won't delete existing objects.

              Only Clp will produce SOS objects in findIntegers (080122), and that's
              where a possible conversion can occur. If clp is holding OsiSOS objects,
              they'll be converted to CbcSOS objects.
            */
            // convert to Cbc integers
            model.findIntegers(false);
            int numberObjects = model.numberObjects();
            if (numberObjects) {
                OsiObject ** objects = model.objects();
                for (int iObject = 0; iObject < numberObjects; iObject++) {
                    CbcSOS * obj =
                        dynamic_cast <CbcSOS *>(objects[iObject]) ;
                    if (obj) {
                        // SOS
                        int n = obj->numberMembers();
                        const int * which = obj->members();
                        for (int i = 0; i < n; i++) {
                            int iColumn = which[i];
                            prohibited[iColumn] = 1;
                            numberProhibited++;
                        }
                    }
                }
            }
            if (numberProhibited)
                process->passInProhibited(prohibited, numberColumns);
            delete [] prohibited;
        }
        int logLevel = model.messageHandler()->logLevel();
#ifdef COIN_HAS_CLP
        //OsiClpSolverInterface * clpSolver = dynamic_cast< OsiClpSolverInterface*> (solver);
        ClpSimplex * lpSolver = NULL;
        if (clpSolver) {
            if (clpSolver->messageHandler()->logLevel())
                clpSolver->messageHandler()->setLogLevel(1);
            if (logLevel > -1)
                clpSolver->messageHandler()->setLogLevel(CoinMin(logLevel, clpSolver->messageHandler()->logLevel()));
            lpSolver = clpSolver->getModelPtr();
            /// If user left factorization frequency then compute
            lpSolver->defaultFactorizationFrequency();
        }
#endif
        // Tell solver we are in Branch and Cut
        solver->setHintParam(OsiDoInBranchAndCut, true, OsiHintDo) ;
        // Default set of cut generators
	// Limited set that could reduce problem size (drop rows / fix values)
        CglProbing generator1;
        generator1.setUsingObjective(true);
        generator1.setMaxPass(1);
        generator1.setMaxPassRoot(1);
        generator1.setMaxProbeRoot(CoinMin(3000, solver->getNumCols()));
        generator1.setMaxProbeRoot(123);
        generator1.setMaxElements(100);
        generator1.setMaxElementsRoot(200);
        generator1.setMaxLookRoot(50);
        generator1.setRowCuts(3);
        //generator1.messageHandler()->setLogLevel(logLevel);
        // Not needed with pass in process->messageHandler()->setLogLevel(logLevel);
        // Add in generators
        process->addCutGenerator(&generator1);
        int translate[] = {9999, 0, 2, -2, 3, 4, 4, 4};
        OsiSolverInterface * solver2 =
            process->preProcessNonDefault(*solver,
                                          translate[desiredPreProcess_], preProcessPasses_, 6);
        // Tell solver we are not in Branch and Cut
        solver->setHintParam(OsiDoInBranchAndCut, false, OsiHintDo) ;
        if (solver2)
            solver2->setHintParam(OsiDoInBranchAndCut, false, OsiHintDo) ;
        bool feasible = true;
        if (!solver2) {
            feasible = false;
            //printf("Pre-processing says infeasible\n");
            delete process;
            preProcessState_ = -1;
            process_ = NULL;
        } else {
            // now tighten bounds
#ifdef COIN_HAS_CLP
            if (clpSolver) {
                // model has changed
                solver = model.solver();
                OsiClpSolverInterface * clpSolver = dynamic_cast< OsiClpSolverInterface*> (solver);
                ClpSimplex * lpSolver = clpSolver->getModelPtr();
                lpSolver->passInMessageHandler(solver->messageHandler());
                if (lpSolver->tightenPrimalBounds() == 0) {
                    lpSolver->dual();
                } else {
                    feasible = false;
                }
            }
#endif
            if (feasible) {
                preProcessState_ = 1;
                process_ = process;
                /* Note that original solver will be kept (with false)
                   and that final solver will also be kept.
                   This is for post-processing

		   Keep in mind when examining this that linear presolve does not
		   understand SOS.
                */
                OsiSolverInterface * solver3 = solver2->clone();
                model.assignSolver(solver3, false);
                if (process_->numberSOS()) {
                    int numberSOS = process_->numberSOS();
                    int numberIntegers = model.numberIntegers();
                    /* model may not have created objects
                       If none then create
                       NOTE - put back to original column numbers as
                       CbcModel will pack down ALL as it doesn't know where from
                    */
                    bool someObjects = model.numberObjects() > 0;
                    if (!numberIntegers || !model.numberObjects()) {
                        model.findIntegers(true);
                        numberIntegers = model.numberIntegers();
                    }
                    OsiObject ** oldObjects = model.objects();
                    // Do sets and priorities
                    OsiObject ** objects = new OsiObject * [numberSOS];
                    // set old objects to have low priority
                    int numberOldObjects = model.numberObjects();
                    int numberColumns = model.getNumCols();
                    for (int iObj = 0; iObj < numberOldObjects; iObj++) {
                        int oldPriority = oldObjects[iObj]->priority();
                        oldObjects[iObj]->setPriority(numberColumns + oldPriority);
                    }
                    const int * starts = process_->startSOS();
                    const int * which = process_->whichSOS();
                    const int * type = process_->typeSOS();
                    const double * weight = process_->weightSOS();
                    int iSOS;
                    for (iSOS = 0; iSOS < numberSOS; iSOS++) {
                        int iStart = starts[iSOS];
                        int n = starts[iSOS+1] - iStart;
                        objects[iSOS] = new CbcSOS(&model, n, which + iStart, weight + iStart,
                                                   iSOS, type[iSOS]);
                        // branch on long sets first
                        objects[iSOS]->setPriority(numberColumns - n);
                    }
                    model.addObjects(numberSOS, objects);
                    for (iSOS = 0; iSOS < numberSOS; iSOS++)
                        delete objects[iSOS];
                    delete [] objects;
                    if (!someObjects) {
                        // put back old column numbers
                        const int * originalColumns = process_->originalColumns();
                        // use reverse lookup to fake it
                        int n = originalColumns[numberColumns-1] + 1;
                        int * fake = new int[n];
                        int i;
                        // This was wrong (now is correct) - so could never have been called
                        abort();
                        for ( i = 0; i < n; i++)
                            fake[i] = -1;
                        for (i = 0; i < numberColumns; i++)
                            fake[originalColumns[i]] = i;
                        for (int iObject = 0; iObject < model.numberObjects(); iObject++) {
                            // redo ids etc
                            CbcSimpleInteger * obj =
                                dynamic_cast <CbcSimpleInteger *>(model.modifiableObject(iObject)) ;
                            if (obj) {
                                obj->resetSequenceEtc(n, fake);
                            } else {
                                // redo ids etc
                                CbcObject * obj =
                                    dynamic_cast <CbcObject *>(model.modifiableObject(iObject)) ;
                                assert (obj);
                                obj->redoSequenceEtc(&model, n, fake);
                            }
                        }
                        delete [] fake;
                    }
                }
            } else {
                //printf("Pre-processing says infeasible\n");
                delete process;
                preProcessState_ = -1;
                process_ = NULL;
            }
        }
    }
    model.setNumberStrong(numberStrong_);
    model.setNumberBeforeTrust(numberBeforeTrust_);
}

// Create C++ lines to get to current state
void
CbcStrategyDefault::generateCpp( FILE * fp)
{
    fprintf(fp, "0#include \"CbcStrategy.hpp\"\n");
    fprintf(fp, "3  CbcStrategyDefault strategy(%s,%d,%d,%d);\n",
            cutsOnlyAtRoot_ ? "1" : "0",
            numberStrong_,
            numberBeforeTrust_,
            printLevel_);
    fprintf(fp, "3  strategy.setupPreProcessing(%d,%d);\n",
            desiredPreProcess_, preProcessPasses_);
}
// Default Constructor
CbcStrategyDefaultSubTree::CbcStrategyDefaultSubTree(CbcModel * parent ,
        int cutsOnlyAtRoot,
        int numberStrong,
        int numberBeforeTrust,
        int printLevel)
        : CbcStrategy(),
        parentModel_(parent),
        cutsOnlyAtRoot_(cutsOnlyAtRoot),
        numberStrong_(numberStrong),
        numberBeforeTrust_(numberBeforeTrust),
        printLevel_(printLevel)
{
}


// Destructor
CbcStrategyDefaultSubTree::~CbcStrategyDefaultSubTree ()
{
}

// Clone
CbcStrategy *
CbcStrategyDefaultSubTree::clone() const
{
    return new CbcStrategyDefaultSubTree(*this);
}

// Copy constructor
CbcStrategyDefaultSubTree::CbcStrategyDefaultSubTree(const CbcStrategyDefaultSubTree & rhs)
        :
        CbcStrategy(rhs),
        parentModel_(rhs.parentModel_),
        cutsOnlyAtRoot_(rhs.cutsOnlyAtRoot_),
        numberStrong_(rhs.numberStrong_),
        numberBeforeTrust_(rhs.numberBeforeTrust_),
        printLevel_(rhs.printLevel_)
{
    setNested(rhs.getNested());
}

// Setup cut generators
void
CbcStrategyDefaultSubTree::setupCutGenerators(CbcModel & model)
{
    // Set up some cut generators and defaults
    if (cutsOnlyAtRoot_ < 0)
        return; // no cuts wanted
    // Probing first as gets tight bounds on continuous

    CglProbing generator1;
    generator1.setUsingObjective(true);
    generator1.setMaxPass(1);
    // Number of unsatisfied variables to look at
    generator1.setMaxProbe(10);
    // How far to follow the consequences
    generator1.setMaxLook(10);
    // Only look at rows with fewer than this number of elements
    generator1.setMaxElements(200);
    //generator1.setRowCuts(3);

    CglGomory generator2;
    // try larger limit
    generator2.setLimit(300);

    CglKnapsackCover generator3;

    //CglOddHole generator4;
    //generator4.setMinimumViolation(0.005);
    //generator4.setMinimumViolationPer(0.00002);
    // try larger limit
    //generator4.setMaximumEntries(200);

    CglClique generator5;
    generator5.setStarCliqueReport(false);
    generator5.setRowCliqueReport(false);

    CglMixedIntegerRounding2 mixedGen;
    CglFlowCover flowGen;

    // Add in generators
    int setting = cutsOnlyAtRoot_ ? -99 : -1;
    int numberGenerators = model.numberCutGenerators();
    int numberParentGenerators = parentModel_->numberCutGenerators();
    int iGenerator;
    bool found;
    found = false;
    int howOften = 0;
    for (iGenerator = 0; iGenerator < numberParentGenerators; iGenerator++) {
        CglCutGenerator * generator = parentModel_->cutGenerator(iGenerator)->generator();
        CglProbing * cgl = dynamic_cast<CglProbing *>(generator);
        if (cgl) {
            found = true;
            howOften = parentModel_->cutGenerator(iGenerator)->howOften();
            break;
        }
    }

    if (found && (howOften >= -1 || howOften == -98)) {
        found = false;
        for (iGenerator = 0; iGenerator < numberGenerators; iGenerator++) {
            CglCutGenerator * generator = model.cutGenerator(iGenerator)->generator();
            CglProbing * cgl = dynamic_cast<CglProbing *>(generator);
            if (cgl) {
                found = true;
                break;
            }
        }
        if (!found) {
            if (howOften == -1)
                howOften = -98;
            else if (howOften == -98)
                howOften = -99;
            model.addCutGenerator(&generator1, setting, "Probing");
            CbcCutGenerator * generator =
                model.cutGenerator(numberGenerators);
            generator->setHowOften(howOften);
            numberGenerators++;
        }
    }
    found = false;
    for (iGenerator = 0; iGenerator < numberParentGenerators; iGenerator++) {
        CglCutGenerator * generator = parentModel_->cutGenerator(iGenerator)->generator();
        CglGomory * cgl = dynamic_cast<CglGomory *>(generator);
        if (cgl) {
            found = true;
            howOften = parentModel_->cutGenerator(iGenerator)->howOften();
            break;
        }
    }
    if (found && howOften >= 0) {
        found = false;
        for (iGenerator = 0; iGenerator < numberGenerators; iGenerator++) {
            CglCutGenerator * generator = model.cutGenerator(iGenerator)->generator();
            CglGomory * cgl = dynamic_cast<CglGomory *>(generator);
            if (cgl) {
                found = true;
                break;
            }
        }
        if (!found)
            model.addCutGenerator(&generator2, setting, "Gomory");
    }
    found = false;
    for (iGenerator = 0; iGenerator < numberParentGenerators; iGenerator++) {
        CglCutGenerator * generator = parentModel_->cutGenerator(iGenerator)->generator();
        CglKnapsackCover * cgl = dynamic_cast<CglKnapsackCover *>(generator);
        if (cgl) {
            found = true;
            howOften = parentModel_->cutGenerator(iGenerator)->howOften();
            break;
        }
    }
    if (found && howOften >= 0) {
        found = false;
        for (iGenerator = 0; iGenerator < numberGenerators; iGenerator++) {
            CglCutGenerator * generator = model.cutGenerator(iGenerator)->generator();
            CglKnapsackCover * cgl = dynamic_cast<CglKnapsackCover *>(generator);
            if (cgl) {
                found = true;
                break;
            }
        }
        if (!found)
            model.addCutGenerator(&generator3, setting, "Knapsack");
    }
    found = false;
    for (iGenerator = 0; iGenerator < numberParentGenerators; iGenerator++) {
        CglCutGenerator * generator = parentModel_->cutGenerator(iGenerator)->generator();
        CglClique * cgl = dynamic_cast<CglClique *>(generator);
        if (cgl) {
            found = true;
            howOften = parentModel_->cutGenerator(iGenerator)->howOften();
            break;
        }
    }
    if (found && howOften >= 0) {
        found = false;
        for (iGenerator = 0; iGenerator < numberGenerators; iGenerator++) {
            CglCutGenerator * generator = model.cutGenerator(iGenerator)->generator();
            CglClique * cgl = dynamic_cast<CglClique *>(generator);
            if (cgl) {
                found = true;
                break;
            }
        }
        if (!found)
            model.addCutGenerator(&generator5, setting, "Clique");
    }
    found = false;
    for (iGenerator = 0; iGenerator < numberParentGenerators; iGenerator++) {
        CglCutGenerator * generator = parentModel_->cutGenerator(iGenerator)->generator();
        CglFlowCover * cgl = dynamic_cast<CglFlowCover *>(generator);
        if (cgl) {
            found = true;
            howOften = parentModel_->cutGenerator(iGenerator)->howOften();
            break;
        }
    }
    if (found && howOften >= 0) {
        found = false;
        for (iGenerator = 0; iGenerator < numberGenerators; iGenerator++) {
            CglCutGenerator * generator = model.cutGenerator(iGenerator)->generator();
            CglFlowCover * cgl = dynamic_cast<CglFlowCover *>(generator);
            if (cgl) {
                found = true;
                break;
            }
        }
        if (!found)
            model.addCutGenerator(&flowGen, setting, "FlowCover");
        found = false;
    }
    for (iGenerator = 0; iGenerator < numberParentGenerators; iGenerator++) {
        CglCutGenerator * generator = parentModel_->cutGenerator(iGenerator)->generator();
        CglMixedIntegerRounding2 * cgl = dynamic_cast<CglMixedIntegerRounding2 *>(generator);
        if (cgl) {
            found = true;
            howOften = parentModel_->cutGenerator(iGenerator)->howOften();
            break;
        }
    }
    if (found && howOften >= 0) {
        found = false;
        for (iGenerator = 0; iGenerator < numberGenerators; iGenerator++) {
            CglCutGenerator * generator = model.cutGenerator(iGenerator)->generator();
            CglMixedIntegerRounding2 * cgl = dynamic_cast<CglMixedIntegerRounding2 *>(generator);
            if (cgl) {
                found = true;
                break;
            }
        }
        if (!found)
            model.addCutGenerator(&mixedGen, setting, "MixedIntegerRounding2");
    }
#ifdef JJF_ZERO
    // Say we want timings
    int newNumberGenerators = model.numberCutGenerators();
    for (iGenerator = numberGenerators; iGenerator < newNumberGenerators; iGenerator++) {
        CbcCutGenerator * generator = model.cutGenerator(iGenerator);
        generator->setTiming(true);
    }
#endif
    if (model.getNumCols() < -500)
        model.setMaximumCutPassesAtRoot(-100); // always do 100 if possible
    else if (model.getNumCols() < 5000)
        model.setMaximumCutPassesAtRoot(100); // use minimum drop
    else
        model.setMaximumCutPassesAtRoot(20);
}
// Setup heuristics
void
CbcStrategyDefaultSubTree::setupHeuristics(CbcModel & model)
{
    // Allow rounding heuristic

    CbcRounding heuristic1(model);
    heuristic1.setHeuristicName("rounding");
    int numberHeuristics = model.numberHeuristics();
    int iHeuristic;
    bool found;
    found = false;
    for (iHeuristic = 0; iHeuristic < numberHeuristics; iHeuristic++) {
        CbcHeuristic * heuristic = model.heuristic(iHeuristic);
        CbcRounding * cgl = dynamic_cast<CbcRounding *>(heuristic);
        if (cgl) {
            found = true;
            break;
        }
    }
    if (!found)
        model.addHeuristic(&heuristic1);
    if ((model.moreSpecialOptions()&32768)!=0) {
      // Allow join solutions
      CbcHeuristicLocal heuristic2(model);
      heuristic2.setHeuristicName("join solutions");
      //sheuristic2.setSearchType(1);
      found = false;
      for (iHeuristic = 0; iHeuristic < numberHeuristics; iHeuristic++) {
        CbcHeuristic * heuristic = model.heuristic(iHeuristic);
        CbcHeuristicLocal * cgl = dynamic_cast<CbcHeuristicLocal *>(heuristic);
        if (cgl) {
	  found = true;
	  break;
        }
      }
      if (!found)
        model.addHeuristic(&heuristic2);
      // Allow RINS
      CbcHeuristicRINS heuristic5(model);
      heuristic5.setHeuristicName("RINS");
      heuristic5.setFractionSmall(0.5);
      heuristic5.setDecayFactor(5.0);
      //heuristic5.setSearchType(1);
      found = false;
      for (iHeuristic = 0; iHeuristic < numberHeuristics; iHeuristic++) {
        CbcHeuristic * heuristic = model.heuristic(iHeuristic);
        CbcHeuristicLocal * cgl = dynamic_cast<CbcHeuristicLocal *>(heuristic);
        if (cgl) {
	  found = true;
	  break;
        }
      }
      if (!found)
        model.addHeuristic(&heuristic5);
    }
}
// Do printing stuff
void
CbcStrategyDefaultSubTree::setupPrinting(CbcModel & model, int modelLogLevel)
{
    if (!modelLogLevel) {
        model.solver()->setHintParam(OsiDoReducePrint, true, OsiHintTry);
        model.messageHandler()->setLogLevel(0);
        model.solver()->messageHandler()->setLogLevel(0);
    } else if (modelLogLevel == 1) {
        model.solver()->setHintParam(OsiDoReducePrint, true, OsiHintTry);
        model.messageHandler()->setLogLevel(1);
        model.solver()->messageHandler()->setLogLevel(0);
    } else {
        model.messageHandler()->setLogLevel(2);
        model.solver()->messageHandler()->setLogLevel(1);
        model.setPrintFrequency(50);
    }
}
// Other stuff e.g. strong branching
void
CbcStrategyDefaultSubTree::setupOther(CbcModel & model)
{
    model.setNumberStrong(numberStrong_);
    model.setNumberBeforeTrust(numberBeforeTrust_);
}
// For uniform setting of cut and heuristic options
void
setCutAndHeuristicOptions(CbcModel & model)
{
    int numberGenerators = model.numberCutGenerators();
    int iGenerator;
    for (iGenerator = 0; iGenerator < numberGenerators; iGenerator++) {
        CglCutGenerator * generator = model.cutGenerator(iGenerator)->generator();
        CglProbing * cglProbing = dynamic_cast<CglProbing *>(generator);
        if (cglProbing) {
            cglProbing->setUsingObjective(1);
            cglProbing->setMaxPass(1);
            cglProbing->setMaxPassRoot(1);
            // Number of unsatisfied variables to look at
            cglProbing->setMaxProbe(10);
            cglProbing->setMaxProbeRoot(50);
            //cglProbing->setMaxProbeRoot(123);
            // How far to follow the consequences
            cglProbing->setMaxLook(5);
            cglProbing->setMaxLookRoot(50);
            cglProbing->setMaxLookRoot(10);
            // Only look at rows with fewer than this number of elements
            cglProbing->setMaxElements(200);
            cglProbing->setMaxElementsRoot(300);
            cglProbing->setRowCuts(3);
        }
#ifdef JJF_ZERO
        CglGomory * cglGomory = dynamic_cast<CglGomory *>(generator);
        if (cglGomory) {
            // try larger limit
            cglGomory->setLimitAtRoot(1000);
            cglGomory->setLimit(50);
        }
        CglKnapsackCover * cglKnapsackCover = dynamic_cast<CglKnapsackCover *>(generator);
        if (cglKnapsackCover) {
        }
#endif
    }
}



