// Copyright (C) 2005, International Business Machines
// Corporation and others.  All Rights Reserved.
#if defined(_MSC_VER)
// Turn off compiler warning about long names
#  pragma warning(disable:4786)
#endif

#include <cassert>
#include <iomanip>


// For Branch and bound
#include "CbcModel.hpp"
#include "CbcBranchLotsize.hpp"
#include "OsiClpSolverInterface.hpp"

// Time
#include "CoinTime.hpp"


/************************************************************************

This main program reads in an integer model from an mps file.

It then replaces all 0-1 variables by lotsizing variables
which can take values 0.0,0.45-0.55 or 1.0

*************************************************************************/
int main (int argc, const char *argv[])
{
  
  // Define your favorite OsiSolver
  
  OsiClpSolverInterface solver1;
  
  // Read in model using argv[1]
  // and assert that it is a clean model
  std::string mpsFileName = "../../Data/miplib3/10teams";
  if (argc>=2) mpsFileName = argv[1];
  int numMpsReadErrors = solver1.readMps(mpsFileName.c_str(),"");
  assert(numMpsReadErrors==0);
  
  int iColumn;
  int numberColumns = solver1.getNumCols();
  int numberLot=0;
  char * mark = new char[numberColumns];
  // take off integers but find where they are
  for (iColumn=0;iColumn<numberColumns;iColumn++) {
    if (solver1.isBinary(iColumn)) {
      solver1.setContinuous(iColumn);
      mark[iColumn]=1;
      numberLot++;
    } else {
      mark[iColumn]=0;
    }
  }
  CbcModel model(solver1);
  // Do lotsizing
  CbcObject ** objects = new CbcObject * [numberLot];
  numberLot=0;
  /* For semi-continuous variables numberRanges is 2
     and ranges[]={0.0,0.0,K,COIN_DBL_MAX};
  */
  // valid ranges are 0.0 to 0.0, 0.45 to 0.55, 1.0 to 1.0
  double ranges[]={0.0,0.0,0.45,0.55,1.0,1.0};
  for (iColumn=0;iColumn<numberColumns;iColumn++) {
    if (mark[iColumn])
    objects[numberLot++]= new CbcLotsize(&model,iColumn,3,ranges,true);
  }
  delete [] mark;
  model.addObjects(numberLot,objects);
  for (iColumn=0;iColumn<numberLot;iColumn++)
    delete objects[iColumn];
  delete [] objects;

  // If time is given then stop after that number of minutes
  if (argc>2) {
    int minutes = atoi(argv[2]);
    std::cout<<"Stopping after "<<minutes<<" minutes"<<std::endl;
    assert (minutes>=0);
    model.setDblParam(CbcModel::CbcMaximumSeconds,60.0*minutes);
  }
  // Switch off most output
  model.solver()->setHintParam(OsiDoReducePrint,true,OsiHintTry);
  if (model.getNumCols()<3000) {
    model.messageHandler()->setLogLevel(1);
    //model.solver()->messageHandler()->setLogLevel(0);
  } else {
    model.messageHandler()->setLogLevel(2);
    model.solver()->messageHandler()->setLogLevel(1);
  }
  model.messageHandler()->setLogLevel(1);
  
  double time1 = CoinCpuTime();

  // Do complete search
  
  model.branchAndBound();

  std::cout<<mpsFileName<<" took "<<CoinCpuTime()-time1<<" seconds, "
	   <<model.getNodeCount()<<" nodes with objective "
	   <<model.getObjValue()
	   <<(!model.status() ? " Finished" : " Not finished")
	   <<std::endl;

  // Print solution - we can't get names from Osi!

  if (model.getMinimizationObjValue()<1.0e50) {
    int numberColumns = model.solver()->getNumCols();
    
    const double * solution = model.solver()->getColSolution();
    
    int iColumn;
    std::cout<<std::setiosflags(std::ios::fixed|std::ios::showpoint)<<std::setw(14);
    
    std::cout<<"--------------------------------------"<<std::endl;
    for (iColumn=0;iColumn<numberColumns;iColumn++) {
      double value=solution[iColumn];
      if (fabs(value)>1.0e-7) 
	std::cout<<std::setw(6)<<iColumn<<" "<<value<<std::endl;
    }
    std::cout<<"--------------------------------------"<<std::endl;
  
    std::cout<<std::resetiosflags(std::ios::fixed|std::ios::showpoint|std::ios::scientific);
  }
  return 0;
}    
