/*  _________________________________________________________________________
 *
 *  Acro: A Common Repository for Optimizers
 *  Copyright (c) 2008 Sandia Corporation.
 *  This software is distributed under the BSD License.
 *  Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
 *  the U.S. Government retains certain rights in this software.
 *  For more information, see the README.txt file in the top Acro directory.
 *  _________________________________________________________________________
 */

#include "CoinError.hpp"
#include "OsiCuts.hpp"
#include "OsiClpSolverInterface.hpp"
#include "CoinWarmStartBasis.hpp"

#include "CglKnapsackCover.hpp"
#include "CglSimpleRounding.hpp"
#include "CglGomory.hpp"

#include <iostream>
#include <iomanip>

int main()
{
  std::cout << "Starting..." << std::endl;

  try 
    {
      OsiClpSolverInterface si;
    
      std::cout << "------------------------------------------------------------" << std::endl;
      //      std::cout << "READING SOMECUTS VERSION" << std::endl;
      //      si.readMps("./somecuts.mps");

      std::cout << "READING NOCUTS VERSION" << std::endl;
      si.readMps("./nocuts.mps");
      std::cout << "------------------------------------------------------------" << std::endl;

      si.setObjSense(1.0);

      int numberRows = si.getNumRows();
    
      si.initialSolve();

      double origLpObj = si.getObjValue();
      std::cout << "Relaxation objective value=" << origLpObj << std::endl;

      // make sure we're dealing with a non-integer solution.
      const double * solution = si.getColSolution();
      std::cout << "Initial solution: ";
      for (size_t i = 0; i < si.getNumCols(); ++i)
	{
	  std::cout << solution[i] << " ";
	}
      std::cout << std::endl;
    
      int totalNumberApplied = 0;
    
      CglGomory cg1;
      cg1.setLimit(si.getNumCols());
      cg1.setAway(0.00001);
    
      //---------------------------------------------------
      // Keep applying cuts until 
      //   1. no more cuts are generated
      // or
      //   2. the objective function value doesn't change
      //---------------------------------------------------
      bool equalObj;
      CoinRelFltEq eq(0.0001);
      OsiSolverInterface::ApplyCutsReturnCode acRc;
      double obj;
    
      do {
	// Get current solution value
	obj = si.getObjValue();
      
	// Generate and apply cuts
	OsiCuts cuts;
	//	cg1.generateCuts(si,cuts);
	//	cg2.generateCuts(si,cuts);
	cg1.generateCuts(si,cuts);
	std::cout << "BEFORE APPLYING CUTS, NUMBER OF CUTS=" << cuts.sizeCuts() << std::endl;
	acRc = si.applyCuts(cuts,0.0);
      
	// Print applyCuts return code
	std::cout <<std::endl <<std::endl;
	std::cout <<cuts.sizeCuts() <<" cuts were generated" <<std::endl;
	std::cout <<"  " <<acRc.getNumInconsistent() <<" were inconsistent" <<std::endl;
	std::cout <<"  " <<acRc.getNumInconsistentWrtIntegerModel() 
	     <<" were inconsistent for this problem" <<std::endl;
	std::cout <<"  " <<acRc.getNumInfeasible() <<" were infeasible" <<std::endl;
	std::cout <<"  " <<acRc.getNumIneffective() <<" were ineffective" <<std::endl;
	std::cout <<"  " <<acRc.getNumApplied() <<" were applied" <<std::endl;
	std::cout <<std::endl <<std::endl;
      
	// Increment the counter of total cuts applied
	totalNumberApplied += acRc.getNumApplied();
      
	// If no cuts were applied, then done
	if ( acRc.getNumApplied()==0 ) break;
      
	// Resolve
	si.resolve();
      
	std::cout <<std::endl;
	std::cout <<"After applying cuts, objective value changed from "
	     <<obj <<" to " <<si.getObjValue() <<std::endl <<std::endl;

	// Take off slack cuts
	int numberRowsNow = si.getNumRows();
	int * del = new int [numberRowsNow-numberRows];
	const CoinWarmStartBasis* basis = dynamic_cast<const CoinWarmStartBasis*>(si.getWarmStart()) ;
	assert (basis);
	int nDelete=0;
	for (int i=numberRows;i<numberRowsNow;i++) {
	  CoinWarmStartBasis::Status status = basis->getArtifStatus(i);
	  if (status == CoinWarmStartBasis::basic)
	    del[nDelete++] = i;
	}
	delete basis;
	if (nDelete) {
	  si.deleteRows(nDelete,del);
	  // should take zero iterations
	  si.resolve();
	  std::cout << nDelete << " rows deleted as basic - resolve took " 
	       << si.getIterationCount() <<" iterations"
	       <<std::endl;
	}
	delete [] del;
      
	// -----------------------------------------------
	// Set Boolean flag to true if new objective is 
	// almost equal to prior value.
	//
	// The test is:
	// abs(oldObj-newObj) <= 0.0001*(max(abs(oldObj),abs(newObj))+1.);
	// see CoinRelFloatEqual.h 
	// -----------------------------------------------
	equalObj = eq( si.getObjValue(), obj );
      } while( !equalObj );
    
      // Print total number of cuts applied, 
      // and total improvement in the lp objective value
      std::cout <<std::endl <<std::endl;
      std::cout << "----------------------------------------------------------" 
	   <<std::endl;
      std::cout << "Cut generation phase completed:" <<std::endl;
      std::cout << "   " << totalNumberApplied << " cuts were applied in total," 
	   <<std::endl;
      std::cout << "   changing the lp objective value from " << origLpObj 
	   << " to " << si.getObjValue() <<std::endl;
      std::cout << "----------------------------------------------------------" 
	   <<std::endl;
      std::cout <<std::endl <<std::endl;
    }
  catch ( CoinError e ) 
    {
      std::cout <<e.className() <<"::" <<e.methodName() <<" - " <<e.message() <<std::endl;
    }

  std::cout << "Done!" << std::endl;

  return 0;
}
