//==================================================================================================
// L i n e a r _ s y s t e m                                                              Interface
// C p l e x _ l i b r a r y
//                                                                                By Bruno Bachelet
//==================================================================================================
// Copyright (c) 1999-2016
// Bruno Bachelet - bruno@nawouak.net - http://www.nawouak.net
//
// This file is part of the B++ Library. This library is free software; you can redistribute it
// and/or modify it under the terms of the GNU Library General Public License as published by the
// Free Software Foundation; either version 2 of the License, or (at your option) any later
// version.
//
// This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
// the GNU Library General Public License for more details (http://www.gnu.org).

/*DESCRIPTION*/
/* This module provides facilities to solve linear programs with the CPLEX Callable Library. */

// File Name //-------------------------------------------------------------------------------------
#line __LINE__ "linear_system/cplex_library.hpp"

// Guardian //--------------------------------------------------------------------------------------
#ifndef guLinearSystemCplexLibrary
#define guLinearSystemCplexLibrary

// Headers //---------------------------------------------------------------------------------------
#include <bpp/linear_system/solver.hpp> /*INCLUDE*/

#ifdef CPLEX_LIBRARY_YES
 extern "C" {
  #ifdef CPLEX_X
   #include <ilcplex/cplexx.h> /*INCLUDE*/
  #else
   #define deprecated
   #include <ilcplex/cplex.h> /*INCLUDE*/
  #endif
 }
#endif

// Importation/Exportation //-----------------------------------------------------------------------
#ifdef LINEAR_SYSTEM_DLL
 #define dll_export DLL_EXPORT
#else
 #define dll_export DLL_IMPORT
#endif

// Low-Level Wrapper //-----------------------------------------------------------------------------
#ifdef CPLEX_LIBRARY_YES
 #undef CPLEX_LL_WRAPPER

 #ifdef CPLEX_LL_WRAPPER_STATIC
  #define CPLEX_LL_WRAPPER
 #endif

 #ifdef CPLEX_LL_WRAPPER_DYNAMIC
  #define CPLEX_LL_WRAPPER
 #endif

 #ifdef CPLEX_LL_WRAPPER
  #include <contribution/cplex_wrapper.hpp>
  #define cplex_call(prSymbol) CPLEX##prSymbol
 #else
  #ifdef CPLEX_X
   #define cplex_call(prSymbol) CPXX##prSymbol
  #else
   #define cplex_call(prSymbol) CPX##prSymbol
  #endif
 #endif
#endif

namespace bpp {

// Namespaces //------------------------------------------------------------------------------------
#define public_area  linearSystemCplexLibrary
#define private_area linearSystemCplexLibrary_private

namespace public_area  { /*NAMESPACE*/ using namespace linearSystemSolver; }
namespace private_area { using namespace public_area; }

extern_module_name;

// Initialization //--------------------------------------------------------------------------------
#define iniLinearSystemCplexLibrary
has_initializer;

// Macrocommands //---------------------------------------------------------------------------------
#ifdef CPLEX_LIBRARY_YES
 /*MACROCOMMAND*/
 /* The expression <CODE>prLine</CODE> is integrated into the source code if CPLEX Callable
    Library is used. */
 #define cplex_library_yes(prLine) prLine

 /*MACROCOMMAND*/
 /* The expression <CODE>prLine</CODE> is integrated into the source code if CPLEX Callable
    Library is not used. */
 #define cplex_library_no(prLine)
#else
 #define cplex_library_yes(prLine)
 #define cplex_library_no(prLine) prLine
#endif

// Types & Classes //-------------------------------------------------------------------------------
namespace public_area {
 //------------------------------------------------------------------------------------------Classes
 template <class prContent> class clCplexLibrarySolver;
}

namespace private_area {}

// Functions Interface //---------------------------------------------------------------------------
namespace public_area  {}
namespace private_area {}

// Errors //----------------------------------------------------------------------------------------
namespace public_area {
 /*ERROR*/ extern_error erCplexLibraryImproperUse; /* Improper use of the CPLEX library. */

 /*ERROR*/ extern_error erCplexLibraryIterationLimitExceeded;
 /* The maximum number of iterations is reached. */

 /*ERROR*/ extern_error erCplexLibraryNodeLimitExceeded;
 /* The maximum number of nodes is reached. */

 /*ERROR*/ extern_error erCplexLibraryNumericalProblem;
 /* Numerical problem to solve the linear program. */

 /*ERROR*/ extern_error erCplexLibraryOpening; /* Can not open the CPLEX environment. */

 /*ERROR*/ extern_error erCplexLibraryTimeLimitExceeded;
 /* The maximum time of execution is reached. */

 /*ERROR*/ extern_error erCplexLibraryUnknownError;
 /* An unknown error has occurred in the CPLEX library. */

 cplex_library_no (
  /*ERROR*/ extern_error erCplexLibraryNotInstalled;
  /* The library is not built for the use of the CPLEX library. */
 )
}

// Constants & Variables //-------------------------------------------------------------------------
extern_dynamic_constant(private,clString,goTempoModelFileName,?);
extern_dynamic_constant(private,clString,goTempoResultFileName,?);

// C p l e x L i b r a r y S o l v e r  Interface //------------------------------------------------
namespace public_area {
 /*CLASS clCplexLibrarySolver */ /* Represents the CPLEX solver (using the Callable Library). */
 template <class prContent> class clCplexLibrarySolver
 : public linearSystemSolver::clSolver<prContent> {
  //-----------------------------------------------------------------------------------------Private
  cplex_library_yes ( private_property CPXENVptr atEnvironment; )

  private_property clCplexLibrarySolver & operator = (const clCplexLibrarySolver &);
  //------------------------------------------------------------------------------------------Public
  public_property constructor clCplexLibrarySolver(void);
  public_property constructor clCplexLibrarySolver(const clCplexLibrarySolver &);
  public_property destructor  clCplexLibrarySolver(void);

  public_property tyBoolean available(void) const;
  public_property tyInteger analyzeResult(clInStream &,clLinearSystem<prContent> &) const;

  public_property void generateCommand(tcString,tcString,const clLinearSystem<prContent> &) const;

  public_property void      generateModel(clOutStream &,const clLinearSystem<prContent> &) const;
  public_property tcString  name(void) const;
  public_property void      outTemporary(clOutStream &) const;
  public_property void      removeTemporary(void) const;
  public_property tyInteger run(clLinearSystem<prContent> &) const;
 };
}

// Functions Inline //------------------------------------------------------------------------------
namespace public_area  {}
namespace private_area {}

// C p l e x L i b r a r y S o l v e r  Inline //---------------------------------------------------
namespace public_area {
 //--------------------------------------------------------------------------------------Constructor
 /*METHOD clCplexLibrarySolver */ /* Builds a CPLEX solver. */
 template <class prContent> inline clCplexLibrarySolver<prContent>::clCplexLibrarySolver(void)
 : linearSystemSolver::clSolver<prContent>() {
  cplex_library_yes (
   method_name("cplexLibrarySolver::constructor");

   tyCharacter lcMessage[1024];
   tyReturn    lcStatus;

   atEnvironment=cplex_call(openCPLEX)(&lcStatus);

   if (atEnvironment==nil) {
    environment::warn("CPLEX environment can't be open.");
    cplex_call(geterrorstring)(atEnvironment,lcStatus,lcMessage);
    environment::out(lcMessage,false,true);
    send_error(erCplexLibraryOpening);
   }
  )
 }
 //--------------------------------------------------------------------------------------Constructor
 /*METHOD clCplexLibrarySolver */ /* Builds a CPLEX solver from another one. */
 template <class prContent> inline
 clCplexLibrarySolver<prContent>::clCplexLibrarySolver(const clCplexLibrarySolver<prContent> &
                                                       cplex_library_yes(agSolver))
 : clSolver<prContent>() { cplex_library_yes(atEnvironment=agSolver.atEnvironment); }
 //---------------------------------------------------------------------------------------Destructor
 /*METHOD clCplexLibrarySolver */ /* Destructs the solver. */
 template <class prContent> inline clCplexLibrarySolver<prContent>::~clCplexLibrarySolver(void) {
  cplex_library_yes (
   method_name("cplexLibrarySolver::destructor");

   tyReturn lcStatus;

   if (atEnvironment!=nil) {
    lcStatus=cplex_call(closeCPLEX)(&atEnvironment);
    if (lcStatus) send_error(erCplexLibraryUnknownError);
   }
  )
 }
 //----------------------------------------------------------------------------------------Available
 /*METHOD clCplexLibrarySolver */ /* Indicates if the CPLEX solver is available. */
 template <class prContent>
 inline tyBoolean clCplexLibrarySolver<prContent>::available(void) const {
  cplex_library_yes ( return (true); )
  cplex_library_no ( return (false); )
 }
 //------------------------------------------------------------------------------------AnalyzeResult
 /*METHOD clCplexLibrarySolver */ /* Not used, throws an exception if called. */
 template <class prContent> inline
 tyInteger clCplexLibrarySolver<prContent>::analyzeResult(clInStream &,
                                                          clLinearSystem<prContent> &) const {
  send_inline_error(erCplexLibraryImproperUse,"cplexLibrarySolver::analyzeResult");
  return (0);
 }
 //----------------------------------------------------------------------------------GenerateCommand
 /*METHOD clCplexLibrarySolver */ /* Not used, throws an exception if called. */
 template <class prContent> inline
 void clCplexLibrarySolver<prContent>::generateCommand(tcString,tcString,
                                                       const clLinearSystem<prContent> &) const
 { send_inline_error(erCplexLibraryImproperUse,"cplexLibrarySolver::generateCommand"); }
 //------------------------------------------------------------------------------------GenerateModel
 /*METHOD clCplexLibrarySolver */ /* Not used, throws an exception if called. */
 template <class prContent> inline
 void clCplexLibrarySolver<prContent>::generateModel(clOutStream &,
                                                     const clLinearSystem<prContent> &) const
 { send_inline_error(erCplexLibraryImproperUse,"cplexLibrarySolver::generateModel"); }
 //---------------------------------------------------------------------------------------------Name
 /*METHOD clCplexLibrarySolver */
 /* Returns the name of the solver. It is always "CPLEX Callable Library". */
 template <class prContent> inline tcString clCplexLibrarySolver<prContent>::name(void) const
 { return ("CPLEX Callable Library"); }
}

// Function Implementation //-----------------------------------------------------------------------
namespace public_area  {}
namespace private_area {}

// C p l e x L i b r a r y S o l v e r  Implementation //-------------------------------------------
namespace public_area {
 //-------------------------------------------------------------------------------------OutTemporary
 /*METHOD clCplexLibrarySolver */
 /* Writes into a stream the last intermediate files used and generated by the CPLEX library to
    solve a linear program. */
 template <class prContent>
 void clCplexLibrarySolver<prContent>::outTemporary(clOutStream & agStream) const {
  method_name("cplexLibrarySolver::outTemporary");

  clInFile lcFile;

  agStream << "[>] Temporary Model File:" << end_line;
  open(lcFile,private_area::goTempoModelFileName->data(),ios::in|ios::binary);
  copy(agStream,lcFile,false);
  close(lcFile);
  if (lcFile.fail()) send_error(erFileReading);

  agStream << end_line;
  agStream << "[>] Temporary Result File:" << end_line;
  open(lcFile,private_area::goTempoResultFileName->data(),ios::in|ios::binary);
  copy(agStream,lcFile,false);
  agStream << end_line;
  close(lcFile);
  if (lcFile.fail()) send_error(erFileReading);
 }
 //----------------------------------------------------------------------------------RemoveTemporary
 /*METHOD clCplexLibrarySolver */
 /* Removes the last intermediate files used and generated by the CPLEX library to solve a linear
    program. */
 template <class prContent> void clCplexLibrarySolver<prContent>::removeTemporary(void) const {
  removeFile(private_area::goTempoModelFileName->data());
  removeFile(private_area::goTempoResultFileName->data());
 }
 //----------------------------------------------------------------------------------------------Run
 /*METHOD clCplexLibrarySolver */ /* Solves a linear program with the CPLEX library. */
 template <class prContent> tyInteger
 clCplexLibrarySolver<prContent>::run(clLinearSystem<prContent> & cplex_library_yes(agSystem))
 const {
  method_name("cplexLibrarySolver::run");

  #define cplex_out(prMessage)           \
   if (lcFile!=nil) {                    \
    std::fprintf(lcFile,prMessage "\n"); \
    std::fflush(lcFile);                 \
   }                                     \
   else                                  \

  #ifdef CPLEX_LIBRARY_YES
   typedef typename clLinearSystem<prContent>::cpVariableX::const_iterator cpIterator;
   typedef clCoefficientX::const_iterator                                  clIterator;
   typedef std_map(tyVariableKey,tyCardinal)                               clVariableX;

   typedef CPXLPptr tyLinearSystem;

   tyCardinal     lcConstraintNo    = 0;
   cpIterator     lcCurrentVariable = agSystem.variables().begin();
   clError *      lcError           = &erCplexLibraryUnknownError;
   FILE *         lcFile            = nil;
   tyInteger      lcIteration       = 0;
   cpIterator     lcLastVariable    = agSystem.variables().end();
   tyBoolean      lcMixedProblem    = false;
   tyCardinal     lcNbConstraint    = agSystem.constraints().size();
   tyLinearSystem lcSystem          = nil;
   tyCardinal     lcVariableNo      = 0;

   clConstraint<prContent> * lcConstraint;
   clIterator                lcCurrentCoefficient;
   tyVariableKey             lcKey;
   clIterator                lcLastCoefficient;
   tyReturn                  lcStatus;
   clString                  lcString;
   tyReal                    lcValue;
   clVariableX               lcVariableX;

   char   lcChar;
   double lcDouble;
   int    lcInt;
   char * lcPointer;

   const_cast<clCplexLibrarySolver<prContent> *>(this)->atLastBranchingNodesNumber=0;

   // Output Initialization //
   if (linearSystemSolver_private::goTemporaryKept)
    lcFile=fopen(private_area::goTempoResultFileName->data(),"w");

   // Environment Settings //
   lcStatus=cplex_call(setintparam)(atEnvironment,CPX_PARAM_SCRIND,CPX_OFF);

   if (lcStatus) cplex_out("Can't turn off the screen indicator.");
   else cplex_out("Screen indicator turned off.");

   lcStatus=cplex_call(setintparam)(atEnvironment,CPX_PARAM_DATACHECK,CPX_ON);

   if (lcStatus) cplex_out("Can't turn on the data checking.");
   else cplex_out("Data checking turned on.");

   // Problem Creation //
   lcSystem=cplex_call(createprob)(atEnvironment,&lcStatus,"BPP");

   if (lcSystem==nil) {
    cplex_out("Can't create the problem.");
    goto lbError;
   }
   else cplex_out("Problem created.");

   // MIP Program Checking //
   while (lcCurrentVariable!=lcLastVariable and not lcMixedProblem) {
    if ((*lcCurrentVariable).second->kind()==integralVariable) lcMixedProblem=true;
    ++lcCurrentVariable;
   }

   if (lcMixedProblem) cplex_out("It's a mixed integer problem.");
   else cplex_out("It's a single linear problem.");

   // Objective //
   if (agSystem.objective().kind()==maximum) cplex_call(chgobjsen)(atEnvironment,lcSystem,CPX_MAX);
   else cplex_call(chgobjsen)(atEnvironment,lcSystem,CPX_MIN);

   cplex_out("Objective sense changed.");

   // Variables Creation //
   lcStatus=cplex_call(newcols)(atEnvironment,lcSystem,agSystem.variables().size(),
                                nil,nil,nil,nil,nil);

   if (lcStatus) {
    cplex_out("Can't create variables.");
    goto lbError;
   }
   else cplex_out("Variables created.");

   // Variables Initialization //
   lcCurrentVariable=agSystem.variables().begin();

   while (lcCurrentVariable!=lcLastVariable and not lcStatus) {
    lcKey=(*lcCurrentVariable).first;
    lcString=clString("x")+string(lcKey);
    lcPointer=(char *)lcString.data();
    lcInt=(int)lcVariableNo;
    lcStatus=cplex_call(chgcolname)(atEnvironment,lcSystem,1,&lcInt,&lcPointer);

    if (not lcStatus) {
     lcDouble=(double)agSystem.objective()[lcKey];
     lcStatus=cplex_call(chgobj)(atEnvironment,lcSystem,1,&lcInt,&lcDouble);

     if (not lcStatus) {
      lcVariableX.insert(std_make_pair(lcKey,lcVariableNo));

      if (lcMixedProblem) {
       lcChar=((*lcCurrentVariable).second->kind()==integralVariable ? 'I' : 'C');
       lcStatus=cplex_call(chgctype)(atEnvironment,lcSystem,1,&lcInt,&lcChar);
      }
     }
    }

    ++lcCurrentVariable;
    ++lcVariableNo;
   }

   if (lcStatus) {
    cplex_out("Can't initialize the variables.");
    goto lbError;
   }
   else cplex_out("Variables initialized.");

   // Constraints Creation //
   lcStatus=cplex_call(newrows)(atEnvironment,lcSystem,lcNbConstraint,nil,nil,nil,nil);

   if (lcStatus) {
    cplex_out("Can't create constraints.");
    goto lbError;
   }
   else cplex_out("Constraints created.");

   // Constraints Initialization //
   while (lcConstraintNo<lcNbConstraint and not lcStatus) {
    lcString=clString("c"+string(lcConstraintNo));
    lcPointer=(char *)lcString.data();
    lcInt=(int)lcConstraintNo;
    lcStatus=cplex_call(chgrowname)(atEnvironment,lcSystem,1,&lcInt,&lcPointer);

    if (not lcStatus) {
     lcConstraint=&(agSystem[lcConstraintNo]);
     lcCurrentCoefficient=lcConstraint->coefficients().begin();
     lcLastCoefficient=lcConstraint->coefficients().end();

     while (lcCurrentCoefficient!=lcLastCoefficient and not lcStatus) {
      lcValue=(*lcCurrentCoefficient).second;
      lcVariableNo=lcVariableX[(*lcCurrentCoefficient).first];

      if (not isEqual(lcValue,0.0)) {
       lcDouble=(double)lcValue;
       lcStatus=cplex_call(chgcoef)(atEnvironment,lcSystem,lcConstraintNo,lcVariableNo,lcDouble);
      }

      ++lcCurrentCoefficient;
     }

     if (not lcStatus) {
      if (lcConstraint->kind()==inferiority) lcChar='L';
      else if (lcConstraint->kind()==superiority) lcChar='G';
      else lcChar='E';

      lcStatus=cplex_call(chgsense)(atEnvironment,lcSystem,1,&lcInt,&lcChar);

      if (not lcStatus) {
       lcDouble=(double)lcConstraint->boundary();
       lcStatus=cplex_call(chgrhs)(atEnvironment,lcSystem,1,&lcInt,&lcDouble);
      }
     }
    }

    ++lcConstraintNo;
   }

   // Problem Writing //
   if (linearSystemSolver_private::goTemporaryKept) {
    lcStatus=cplex_call(writeprob)(atEnvironment,lcSystem,
                                   private_area::goTempoModelFileName->data(),"LP");

    if (lcStatus) {
     cplex_out("Can't write the problem.");
     goto lbError;
    }
    else cplex_out("Problem written.");
   }

   // MIP Resolution //
   if (lcMixedProblem) {
    lcStatus=cplex_call(mipopt)(atEnvironment,lcSystem);

    if (lcStatus) {
     cplex_out("Problem can't be optimized.");
     goto lbError;
    }
    else cplex_out("Problem optimized (mixed integer optimization).");

    lcStatus=cplex_call(getstat)(atEnvironment,lcSystem);

    if (lcStatus==CPXMIP_OPTIMAL or lcStatus==CPXMIP_OPTIMAL_TOL) {
     cplex_out("Optimal solution found.");
     agSystem.solved()=true;
    }
    else if (lcStatus==CPXMIP_INFEASIBLE or lcStatus==CPXMIP_UNBOUNDED
             or lcStatus==CPXMIP_INForUNBD) {
     cplex_out("No optimal solution found.");
     agSystem.solved()=false;
    }
    else {
     cplex_out("Problem during resolution.");

     switch (lcStatus) {
      case CPXMIP_OPTIMAL_INFEAS:
       lcError=&erCplexLibraryNumericalProblem;
       goto lbError;
      case CPXMIP_SOL_LIM:
       lcError=&erCplexLibraryIterationLimitExceeded;
       goto lbError;
      case CPXMIP_TIME_LIM_FEAS:
      case CPXMIP_TIME_LIM_INFEAS:
       lcError=&erCplexLibraryTimeLimitExceeded;
       goto lbError;
      case CPXMIP_NODE_LIM_FEAS:
      case CPXMIP_NODE_LIM_INFEAS:
       lcError=&erCplexLibraryNodeLimitExceeded;
       goto lbError;
      default:
       goto lbError;
     }
    }
   }

   // Linear Resolution //
   else {
    lcStatus=cplex_call(lpopt)(atEnvironment,lcSystem);

    if (lcStatus) {
     cplex_out("Problem can't be optimized.");
     goto lbError;
    }
    else cplex_out("Problem optimized (primal simplex method).");

    lcStatus=cplex_call(getstat)(atEnvironment,lcSystem);

    if (lcStatus==CPX_STAT_OPTIMAL) {
     cplex_out("Optimal solution found.");
     agSystem.solved()=true;
    }
    else if (lcStatus==CPX_STAT_UNBOUNDED or lcStatus==CPX_STAT_INFEASIBLE
             or lcStatus==CPX_STAT_INForUNBD) {
     cplex_out("No optimal solution found.");
     agSystem.solved()=false;
    }
    else {
     cplex_out("Problem during resolution.");

     switch (lcStatus) {
      case CPX_STAT_OPTIMAL_INFEAS:
      case CPX_STAT_NUM_BEST:
       lcError=&erCplexLibraryNumericalProblem;
       goto lbError;
      case CPX_STAT_ABORT_IT_LIM:
       lcError=&erCplexLibraryIterationLimitExceeded;
       goto lbError;
      case CPX_STAT_ABORT_TIME_LIM:
       lcError=&erCplexLibraryTimeLimitExceeded;
       goto lbError;
      default:
       goto lbError;
     }
    }
   }

   // Result Analysis //
   lcStatus=0;

   if (agSystem.solved()) {
    if (lcMixedProblem) {
     lcIteration=cplex_call(getmipitcnt)(atEnvironment,lcSystem);

     const_cast<clCplexLibrarySolver<prContent> *>(this)->atLastBranchingNodesNumber
     =cplex_call(getnodecnt)(atEnvironment,lcSystem);
    }
    else lcIteration=cplex_call(getitcnt)(atEnvironment,lcSystem);

    lcCurrentVariable=agSystem.variables().begin();
    lcVariableNo=0;

    while (lcCurrentVariable!=lcLastVariable and not lcStatus) {
     #if CPX_VERSION<900
      if (lcMixedProblem)
       lcStatus=cplex_call(getmipx)(atEnvironment,lcSystem,&lcDouble,lcVariableNo,lcVariableNo);
      else
       lcStatus=cplex_call(getx)(atEnvironment,lcSystem,&lcDouble,lcVariableNo,lcVariableNo);
     #else
      lcStatus=cplex_call(getx)(atEnvironment,lcSystem,&lcDouble,lcVariableNo,lcVariableNo);
     #endif

     if (not lcStatus) (*lcCurrentVariable).second->value()=lcDouble;
     ++lcCurrentVariable;
     ++lcVariableNo;
    }

    if (lcStatus) {
     cplex_out("Can't get the solution.");
     goto lbError;
    }
    else cplex_out("Solution retrieved.");
   }

   // Problem Destruction //
   if (lcSystem!=nil) {
    lcStatus=cplex_call(freeprob)(atEnvironment,&lcSystem);

    if (lcStatus) {
     lcSystem=nil;
     goto lbError;
    }

    cplex_out("Problem destructed.");
    lcSystem=nil;
   }

   if (lcFile!=nil) fclose(lcFile);
   return (lcIteration);

   // Error //
   lbError:
   agSystem.solved()=false;
   if (lcSystem!=nil) lcStatus=cplex_call(freeprob)(atEnvironment,&lcSystem);
   if (lcFile!=nil) fclose(lcFile);
   send_error(*lcError);

   return (-1);
  #endif

  #undef cplex_out

  cplex_library_no (
   send_error(erCplexLibraryNotInstalled);
   return (0);
  )
 }
}

// End //-------------------------------------------------------------------------------------------
}

#undef dll_export
#undef public_area
#undef private_area
#endif
 
//==================================================================================================
// L i n e a r _ s y s t e m                                                         Implementation
// C p l e x _ l i b r a r y
//                                                                                By Bruno Bachelet
//==================================================================================================
// Copyright (c) 1999-2016
// Bruno Bachelet - bruno@nawouak.net - http://www.nawouak.net
//
// This file is part of the B++ Library. This library is free software; you can redistribute it
// and/or modify it under the terms of the GNU Library General Public License as published by the
// Free Software Foundation; either version 2 of the License, or (at your option) any later
// version.
//
// This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
// the GNU Library General Public License for more details (http://www.gnu.org).

// File Name //-------------------------------------------------------------------------------------
#line __LINE__ "linear_system/cplex_library.cpp"

// DLL Belonging //---------------------------------------------------------------------------------
#define LINEAR_SYSTEM_DLL

// Headers //---------------------------------------------------------------------------------------
#include <bpp/file_name.hpp> /*NEED*/
#include <bpp/linear_system/cplex_library.hpp> /*INTERFACE*/

namespace bpp {

// Namespaces //------------------------------------------------------------------------------------
#define public_area  linearSystemCplexLibrary
#define private_area linearSystemCplexLibrary_private
#define dll_export   DLL_EXPORT

namespace public_area  {}
namespace private_area {}

static_module_name("Linear_system/Cplex_library");

// Initialization //--------------------------------------------------------------------------------
#undef iniLinearSystemCplexLibrary
static_constant(private_area::clInitializer,goInitializer);

// Errors //----------------------------------------------------------------------------------------
namespace public_area {
 static_error erCplexLibraryImproperUse;
 static_error erCplexLibraryIterationLimitExceeded;
 static_error erCplexLibraryNodeLimitExceeded;
 static_error erCplexLibraryNumericalProblem;
 static_error erCplexLibraryOpening;
 static_error erCplexLibraryTimeLimitExceeded;
 static_error erCplexLibraryUnknownError;

 cplex_library_no ( static_error erCplexLibraryNotInstalled; )
}

// Constants & Variables //-------------------------------------------------------------------------
dynamic_constant(clString,goTempoModelFileName);
dynamic_constant(clString,goTempoResultFileName);

// Static Members //--------------------------------------------------------------------------------
namespace public_area  {}
namespace private_area {}

// Functions Implementation //----------------------------------------------------------------------
namespace public_area  {}
namespace private_area {}

// X X X  Implementation //-------------------------------------------------------------------------
namespace {}

// I n i t i a l i z e r  Implementation //---------------------------------------------------------
namespace private_area {
 //--------------------------------------------------------------------------------------------Start
 property void clInitializer::start(void) {
  if (atCounter++ == 0) {
   try {
    #include <bpp/modules.hpp> /*NEED*/
    registerStop(this);
    environment::informInitialization(goModuleName);

    erCplexLibraryImproperUse.create("CPLEX Library - Improper use.");
    erCplexLibraryIterationLimitExceeded.create("CPLEX Library - The maximum number of iterations is reached.");
    erCplexLibraryNodeLimitExceeded.create("CPLEX Library - The maximum number of nodes is reached.");
    erCplexLibraryNumericalProblem.create("CPLEX Library - Numerical problem to solve the linear program.");
    erCplexLibraryOpening.create("CPLEX Library - Can't open the environment.");
    erCplexLibraryTimeLimitExceeded.create("CPLEX Library - The maximum time of execution is reached.");
    erCplexLibraryUnknownError.create("CPLEX Library - An unknown error has occurred.");

    cplex_library_no (
     erCplexLibraryNotInstalled.create("CPLEX Callable Library isn't installed.");
    )

    goTempoModelFileName = new_object(clString(environment::temporaryLocation()+
                           fileNameSeparator()+LINEAR_SYSTEM_CPLEX_LIBRARY_TEMPORARY_FILE_1));

    goTempoResultFileName = new_object(clString(environment::temporaryLocation()+
                            fileNameSeparator()+LINEAR_SYSTEM_CPLEX_LIBRARY_TEMPORARY_FILE_2));

    #ifdef CPLEX_LIBRARY_YES
     #ifdef CPLEX_LL_WRAPPER
      if (not cplex_open()) {
       environment::nextLine();
       environment::out("[!] Warning: CPLEX symbols can't be loaded.",true,true);
       environment::nextLine();
      }
     #endif
    #endif
   }

   initializer_catch;
  }
 }
 //---------------------------------------------------------------------------------------------Stop
 property void clInitializer::stop(void) {
  try {
   environment::informTermination(goModuleName);

   #ifdef CPLEX_LIBRARY_YES
    #ifdef CPLEX_LL_WRAPPER
     cplex_close();
    #endif
   #endif

   delete_object(goTempoModelFileName);
   delete_object(goTempoResultFileName);
  }

  initializer_catch;
 }
}

// End //-------------------------------------------------------------------------------------------
}