//===========================================================================//
// This file is part of the DIP Solver Framework.                            //
//                                                                           //
// DIP is distributed under the Eclipse Public License as part of the        //
// COIN-OR repository (http://www.coin-or.org).                              //
//                                                                           //
// Author: Matthew Galati, SAS Institute Inc. (matthew.galati@sas.com)       //
//                                                                           //
// Conceptual Design: Matthew Galati, SAS Institute Inc.                     //
//                    Ted Ralphs, Lehigh University                          //
//                                                                           //
// Copyright (C) 2002-2015, Lehigh University, Matthew Galati, and Ted Ralphs//
// All Rights Reserved.                                                      //
//===========================================================================//

//===========================================================================//
#include "DecompAlgo.h"
#include "DecompCutOsi.h"
//===========================================================================//
#include "CglGomory.hpp"
#include "CglProbing.hpp"
#include "CglKnapsackCover.hpp"
#include "CglClique.hpp"
#include "CglFlowCover.hpp"
#include "CglMixedIntegerRounding2.hpp"
#include "CoinPackedMatrix.hpp"

using namespace std;

//===========================================================================//
int DecompAlgoCGL::initGenerators(const int doClique,
                                  const int doOddHole,
                                  const int doFlowCover,
                                  const int doKnapCover,
                                  const int doMixIntRound,
                                  const int doGomory)
{
   int status = DecompStatOk;

   if (doClique) {
      m_genClique = new CglClique;
      m_genClique->setStarCliqueReport(false);
      m_genClique->setRowCliqueReport (false);

      if (!m_genClique) {
         return DecompStatOutOfMemory;
      }
   }

   if (doOddHole) {
      m_genOddHole = new CglOddHole;

      if (!m_genClique) {
         return DecompStatOutOfMemory;
      }
   }

   if (doFlowCover) {
      m_genFlowCover = new CglFlowCover;

      if (!m_genFlowCover) {
         return DecompStatOutOfMemory;
      }
   }

   if (doKnapCover) {
      m_genKnapCover = new CglKnapsackCover;

      if (!m_genKnapCover) {
         return DecompStatOutOfMemory;
      }
   }

   if (doMixIntRound) {
      m_genMixIntRound = new CglMixedIntegerRounding2;

      if (!m_genMixIntRound) {
         return DecompStatOutOfMemory;
      }
   }

   if (doGomory) {
      m_genGomory = new CglGomory;

      if (!m_genGomory) {
         return DecompStatOutOfMemory;
      }
   }

   return status;
}

//===========================================================================//
int DecompAlgoCGL::generateCuts(OsiSolverInterface* cutGenSI,
                                OsiSolverInterface* masterSI,
                                double*              xhat,
                                vector<int>&         integerVars,
                                DecompCutList&       newCuts)
{
   OsiCuts      osiCuts;
   int          status           = DecompStatOk;
   int          nCliqueCuts      = 0;
   int          nOddHoleCuts     = 0;
   int          nFlowCoverCuts   = 0;
   int          nKnapCoverCuts   = 0;
   int          nMixIntRoundCuts = 0;
   int          nGomoryCuts      = 0;
   int          nTotalCuts       = 0;
   //---
   //--- this is typically coming from relaxed master problem
   //---  which has no defined integers (why not?) you are using
   //---  initalSolve, resolve which solves relaxation anyway...
   //---  can't the master and m_cutgenSI just stores the integers?
   //---
   //--- if you set integer on the fly and then unset
   //---   will that break things?
   //--- if master, need to set, if cutgenSI, don't...
   //---
   //int nInts = static_cast<int>(integerVars.size());
   //if(nInts > 0)
   // si->setInteger(&integerVars[0], nInts);
   //---
   //--- some CGLs need row activities too
   //---   currently, no easy way to set this
   //---
   OsiClpSolverInterface* cutGenClpSI =
      dynamic_cast<OsiClpSolverInterface*>(cutGenSI);
   assert(cutGenClpSI);
   const int nRows = cutGenClpSI->getNumRows();
   //---
   //--- calculate activity
   //---
   //TODO: note, this design never does cuts on cuts
   const CoinPackedMatrix* M   = cutGenClpSI->getMatrixByRow();
   double*                  act = new double[nRows];
   assert(act);
   M->times(xhat, act);
   //---
   //--- set primal column solution
   //---
   cutGenClpSI->setColSolution(xhat);
   //---
   //--- set primal row solution (i.e., activities)
   //---
   //write a si->setRowPrice for OsiClp
   //si->setRowPrice(act);//BAD NAME!
   bool            mustDeleteWS = true;
   CoinWarmStart* warmStart    = NULL;

   //TODO: check on crossover code - some speedups possible
   //  with a version that accepts memory - so not alloc/free
   //  too often
   switch (m_algo) {
   case CUT:
      //---
      //--- set master warm start in cgl SI
      //---
      warmStart = masterSI->getPointerToWarmStart(mustDeleteWS);
      cutGenClpSI->setWarmStart(warmStart);
      break;
   case PRICE_AND_CUT:
   case RELAX_AND_CUT:

      if (m_genGomory) {
         //---
         //--- crossover from xhat to basic solution
         //---
         //---
         //--- OsiClp::crossover
         //---   options - 0 no presolve (use primal and dual)
         //---             1 presolve (just use primal)
         //---             2 no presolve (just use primal)
         //---   basis   - 0 use all slack basis
         //---             1 try and put some in basis
         //---
#ifdef TRUNK_BUILD
         int crossOptions = 2;
         int crossBasis   = 1;
         //add obj cut
         //   obj >= dw-master obj - then generate gomory?
         //    or do that all the time for cuts?
         cutGenClpSI->crossover(crossOptions, crossBasis);
#endif
         //cutGenClpSI->resolve();//need?
         ///////////STOP -> getting all kinds of not violated
         ///// maybe try options=2, no presolve as it might be
         ///// screwing up the model?
         break;
      }

   default:
      break;
   }

   /*#ifdef __DECOMP_IP_CPX__
   OsiCpxSolverInterface * subprobSI_Cpx
      = dynamic_cast<OsiCpxSolverInterface*>(si);
   CPXENVptr cpxEnv = subprobSI_Cpx->getEnvironmentPtr();
   CPXLPptr  cpxLp  = subprobSI_Cpx->getLpPtr();

   int err = CPXcopystart( cpxEnv, cpxLp, NULL, NULL,
   		   const_cast<double*>( xhat ),
   		   const_cast<double*>( act  ),
   		   NULL, NULL );
   printf("Err=%d\n",err);fflush(stdout);
   assert(!err);
   #endif*/
   if (m_genClique) {
      UTIL_MSG(m_logLevel, 3,
               (*m_logStream) << "Calling cut generator: cliques\n";
              );
      m_genClique->generateCuts(*cutGenClpSI, osiCuts);
      nCliqueCuts = osiCuts.sizeCuts() - nTotalCuts;
      nTotalCuts  = osiCuts.sizeCuts();
   }

   if (m_genOddHole) {
      UTIL_MSG(m_logLevel, 3,
               (*m_logStream) << "Calling cut generator: cliques\n";
              );
      m_genOddHole->generateCuts(*cutGenClpSI, osiCuts);
      nOddHoleCuts = osiCuts.sizeCuts() - nTotalCuts;
      nTotalCuts   = osiCuts.sizeCuts();
   }

   if (m_genFlowCover) {
      UTIL_MSG(m_logLevel, 3,
               (*m_logStream) << "Calling cut generator: flow-covers\n";
              );
      m_genFlowCover->generateCuts(*cutGenClpSI, osiCuts);
      nFlowCoverCuts = osiCuts.sizeCuts() - nTotalCuts;
      nTotalCuts     = osiCuts.sizeCuts();
   }

   if (m_genKnapCover) {
      UTIL_MSG(m_logLevel, 3,
               (*m_logStream) << "Calling cut generator: knap-covers\n";
              );
      m_genKnapCover->generateCuts(*cutGenClpSI, osiCuts);
      nKnapCoverCuts = osiCuts.sizeCuts() - nTotalCuts;
      nTotalCuts     = osiCuts.sizeCuts();
   }

   if (m_genMixIntRound) {
      UTIL_MSG(m_logLevel, 3,
               (*m_logStream) << "Calling cut generator: mixint-round\n";
              );
      m_genMixIntRound->generateCuts(*cutGenClpSI, osiCuts);
      nMixIntRoundCuts = osiCuts.sizeCuts() - nTotalCuts;
      nTotalCuts       = osiCuts.sizeCuts();
   }

   if (m_genGomory) {
      UTIL_MSG(m_logLevel, 3,
               (*m_logStream) << "Calling cut generator: gomory\n";
              );
      m_genGomory->generateCuts(*cutGenClpSI, osiCuts);
      nGomoryCuts = osiCuts.sizeCuts() - nTotalCuts;
      nTotalCuts  = osiCuts.sizeCuts();
   }

   UTIL_MSG(m_logLevel, 3,
            (*m_logStream)
            << "Num clique     cuts= " << nCliqueCuts      << endl
            << "Num odd-hole   cuts= " << nOddHoleCuts     << endl
            << "Num flow-cover cuts= " << nFlowCoverCuts   << endl
            << "Num knap-cover cuts= " << nKnapCoverCuts   << endl
            << "Num mixed-int  cuts= " << nMixIntRoundCuts << endl
            << "Num gomory     cuts= " << nGomoryCuts      << endl;
           );
   //osiCuts.printCuts();
   int i;

   for (i = 0; i < osiCuts.sizeRowCuts(); i++) {
      CoinAssertDebug(osiCuts.rowCut(i).consistent());
      CoinAssertDebug(osiCuts.rowCut(i).consistent(*cutGenClpSI));
      CoinAssertDebug(!osiCuts.rowCut(i).infeasible(*cutGenClpSI));
      //CoinAssertDebug(osiCuts.rowCut(i).violated(xhat) > 1.e-5);
      DecompCutOsi* decompCut = new DecompCutOsi(osiCuts.rowCut(i));

      if (osiCuts.rowCut(i).violated(xhat) < DecompEpsilon) {
         UTIL_DEBUG(m_logLevel, 3,
                    (*m_logStream) <<
                    "WARNING: CGL cut " << i << " not violated." << endl;
                    osiCuts.rowCut(i).print();
                   );
      } else {
         newCuts.push_back(decompCut);
      }
   }

   UTIL_DEBUG(m_logLevel, 3,

   if (osiCuts.sizeColCuts() > 0) {
   (*m_logStream)
      << "WARNING: " << osiCuts.sizeColCuts()
      << " CGL col cuts found." << endl;
   }
             );

   //   if(nInts > 0)
   // si->setContinuous(&integerVars[0], nInts);
   if (mustDeleteWS && warmStart) {
      UTIL_DELPTR(warmStart);
   }

   UTIL_DELARR(act);
   return status;
}
