//==================================================================================================
// L i n e a r _ s y s t e m                                                              Interface
// A m p l
//                                                                                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 AMPL software. */

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

// Guardian //--------------------------------------------------------------------------------------
#ifndef guLinearSystemAmpl
#define guLinearSystemAmpl

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

namespace bpp {

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

// Namespaces //------------------------------------------------------------------------------------
#define public_area  linearSystemAmpl
#define private_area linearSystemAmpl_private

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

extern_module_name;

// Initialization //--------------------------------------------------------------------------------
#define iniLinearSystemAmpl
has_initializer;

// Macrocommands //---------------------------------------------------------------------------------

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

namespace private_area {}

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

// Errors //----------------------------------------------------------------------------------------
namespace public_area {
 /*ERROR*/ extern_error erAmplUnknownError;
 /* An unknown error has occurred in the AMPL software. */

 ampl_no (
  /*ERROR*/ extern_error erAmplNotInstalled;
  /* The library is not built for the use of the AMPL software. */
 )
}

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

extern_dynamic_constant(private,clString,goDataLocation,?);

// A m p l S o l v e r  Interface //----------------------------------------------------------------
namespace public_area {
 /*CLASS clAmplSolver */ /* Represents the AMPL solver. */
 template <class prContent> class clAmplSolver : public linearSystemSolver::clSolver<prContent> {
  //-----------------------------------------------------------------------------------------Private
  private_property clAmplSolver & operator = (const clAmplSolver &);
  //------------------------------------------------------------------------------------------Public
  public_property constructor clAmplSolver(void);
  public_property constructor clAmplSolver(const clAmplSolver &);
  public_property destructor  clAmplSolver(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 {}

// A m p l S o l v e r  Inline //-------------------------------------------------------------------
namespace public_area {
 //--------------------------------------------------------------------------------------Constructor
 /*METHOD clAmplSolver */ /* Builds an AMPL solver. */
 template <class prContent>
 inline clAmplSolver<prContent>::clAmplSolver(void) : linearSystemSolver::clSolver<prContent>() {}
 //--------------------------------------------------------------------------------------Constructor
 /*METHOD clAmplSolver */ /* Builds an AMPL solver from another one. */
 template <class prContent>
 inline clAmplSolver<prContent>::clAmplSolver(const clAmplSolver<prContent> &)
 : clSolver<prContent>() {}
 //---------------------------------------------------------------------------------------Destructor
 /*METHOD clAmplSolver */ /* Destructs the AMPL solver. */
 template <class prContent> inline clAmplSolver<prContent>::~clAmplSolver(void) {}
 //----------------------------------------------------------------------------------------Available
 /*METHOD clAmplSolver */ /* Indicates if the AMPL solver is available. */
 template <class prContent> inline tyBoolean clAmplSolver<prContent>::available(void) const
 { return (exchangeAmpl::valid()); }
 //---------------------------------------------------------------------------------------------Name
 /*METHOD clAmplSolver */ /* Returns the name of the solver. It is always "AMPL". */
 template <class prContent>
 inline tcString clAmplSolver<prContent>::name(void) const { return ("AMPL"); }
}

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

// A m p l S o l v e r  Implementation //-----------------------------------------------------------
namespace public_area {
 //------------------------------------------------------------------------------------AnalyzeResult
 /*METHOD clAmplSolver */
 /* Analyzes from a stream the results given by the AMPL software and stores them in a linear
    program structure. */
 template <class prContent>
 tyInteger clAmplSolver<prContent>::analyzeResult(clInStream & agStream,
                                                  clLinearSystem<prContent> & agSystem) const {
  method_name("amplSolver::analyzeResult");

  tyReal        lcObjective;
  clString      lcString;
  tyVariableKey lcKey;

  agSystem.solved()=false;
  agStream >> lcString;
  if (lcString!=exchangeAmpl::solver()) return (-1);

  // Objective //
  agStream >> lcString;
  while (lcString!="obj") agStream >> lcString;
  agStream >> lcString >> lcObjective;
  if (agStream.fail()) send_error(erStreamReading);

  // Constraints //
  agStream >> lcString;

  while (lcString!="") {
   lcKey=cardinal(lcString.data()+1);
   agStream >> lcString;
   agStream >> agSystem.variable(lcKey).value();
   if (agStream.fail()) send_error(erStreamReading);
   agStream >> lcString;
  }

  if (agStream.fail()) send_error(erStreamReading);
  agSystem.solved()=true;
  return (0);
 }
 //----------------------------------------------------------------------------------GenerateCommand
 /*METHOD clAmplSolver */
 /* Generates a file containing the commands sent to the AMPL software to solve a given linear
    program. */
 template <class prContent>
 void clAmplSolver<prContent>::generateCommand(tcString agCommandFileName,tcString agModelFileName,
                                               const clLinearSystem<prContent> & agSystem) const {
  method_name("amplSolver::generateCommand");

  typedef typename clLinearSystem<prContent>::cpVariableX::const_iterator cpIterator;

  cpIterator  lcCurrentVariable = agSystem.variables().begin();
  cpIterator  lcLastVariable    = agSystem.variables().end();

  clOutFile lcFile;

  open(lcFile,agCommandFileName,ios::out|ios::trunc);
  lcFile << "option solver xlsol;" << end_line;
  lcFile << "model " << agModelFileName << ';' << end_line;
  lcFile << "solve;" << end_line;
  lcFile << "display obj;" << end_line;
  lcFile << "display ";

  while (lcCurrentVariable!=lcLastVariable) {
   lcFile << 'x' << (*lcCurrentVariable).first;
   lcCurrentVariable++;
   if (lcCurrentVariable==lcLastVariable) lcFile << ';'; else lcFile << ',';
  }

  lcFile << end_line;
  lcFile << "quit;" << end_line;

  close(lcFile);

  if (lcFile.fail()) send_error(erFileWriting);
 }
 //------------------------------------------------------------------------------------GenerateModel
 /*METHOD clAmplSolver */
 /* Generates into a stream a model of a linear program that the AMPL software can understand. */
 template <class prContent>
 void clAmplSolver<prContent>::generateModel(clOutStream & agStream,
                                             const clLinearSystem<prContent> & agSystem) const {
  method_name("amplSolver::generateModel");

  typedef typename clLinearSystem<prContent>::cpVariableX::const_iterator cpIterator;
  typedef clCoefficientX::const_iterator                                  clIterator;

  tyCardinal lcConstraintNo       = 0;
  clIterator lcCurrentCoefficient = agSystem.objective().coefficients().begin();
  cpIterator lcCurrentVariable    = agSystem.variables().begin();
  tyBoolean  lcFirstCoefficient   = true;
  clIterator lcLastCoefficient    = agSystem.objective().coefficients().end();
  cpIterator lcLastVariable       = agSystem.variables().end();
  tyCardinal lcNbConstraint       = agSystem.constraints().size();

  const clConstraint<prContent> * lcConstraint;
  tyReal                          lcValue;

  // Variables //
  while (lcCurrentVariable!=lcLastVariable) {
   agStream << "var x" << (*lcCurrentVariable).first;
   if ((*lcCurrentVariable).second->kind()==integralVariable) agStream << " integer";
   agStream << " >= 0;" << end_line;
   lcCurrentVariable++;
  }

  agStream << end_line;

  // Objective //
  if (agSystem.objective().kind()==maximum) agStream << "maximize";
  else agStream << "minimize";

  agStream << " obj: ";

  while (lcCurrentCoefficient!=lcLastCoefficient) {
   lcValue=(*lcCurrentCoefficient).second;

   if (lcFirstCoefficient) {
    agStream << lcValue << " * x" << (*lcCurrentCoefficient).first;
    lcFirstCoefficient=false;
   }
   else if (lcValue<0.0) agStream << " - " << -lcValue << " * x" << (*lcCurrentCoefficient).first;
   else if (lcValue>0) agStream << " + " << lcValue << " * x" << (*lcCurrentCoefficient).first;

   lcCurrentCoefficient++;
  }

  agStream << ';' << end_line << end_line;

  // Constraints //
  while (lcConstraintNo<lcNbConstraint) {
   agStream << "subject to c" << lcConstraintNo << ": ";
   lcFirstCoefficient=true;
   lcConstraint=&(agSystem[lcConstraintNo]);
   lcCurrentCoefficient=lcConstraint->coefficients().begin();
   lcLastCoefficient=lcConstraint->coefficients().end();

   while (lcCurrentCoefficient!=lcLastCoefficient) {
    lcValue=(*lcCurrentCoefficient).second;

    if (lcFirstCoefficient) {
     if (lcValue!=0) {
      agStream << lcValue << " * x" << (*lcCurrentCoefficient).first;
      lcFirstCoefficient=false;
     }
    }
    else if (lcValue<0) agStream << " - " << -lcValue << " * x" << (*lcCurrentCoefficient).first;
    else if (lcValue>0) agStream << " + " << lcValue << " * x" << (*lcCurrentCoefficient).first;

    lcCurrentCoefficient++;
   }

   if (lcConstraint->kind()==inferiority) agStream << " <= ";
   else if (lcConstraint->kind()==superiority) agStream << " >= ";
   else agStream << " = ";

   agStream << lcConstraint->boundary() << ';' << end_line;
   lcConstraintNo++;
  }

  if (agStream.fail()) send_error(erStreamWriting);
 }
 //-------------------------------------------------------------------------------------OutTemporary
 /*METHOD clAmplSolver */
 /* Writes into a stream the last intermediate files used and generated by the AMPL software to
    solve a linear program. */
 template <class prContent>
 void clAmplSolver<prContent>::outTemporary(clOutStream & agStream) const {
  method_name("amplSolver::outTemporary");

  clInFile lcFile;

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

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

  ampl_yes (
   using namespace private_area;

   clOutFile lcFile1;
   clInFile  lcFile2;
   tyInteger lcNbIteration;

   // Generation //
   open(lcFile1,goTempoModelFileName->data(),ios::out|ios::trunc);
   generateModel(lcFile1,agSystem);
   close(lcFile1);
   if (lcFile1.fail()) send_error(erFileWriting);

   // Execution //
   generateCommand(goTempoCommandFileName->data(),goTempoModelFileName->data(),agSystem);

   exchangeAmpl::
   execute((clString("< ")+*goTempoCommandFileName+" > "+*goTempoResultFileName).data());

   // Analysis //
   open(lcFile2,goTempoResultFileName->data(),ios::in);
   lcNbIteration=analyzeResult(lcFile2,agSystem);
   close(lcFile2);
   if (lcFile2.fail()) send_error(erFileReading);

   if (not linearSystemSolver_private::goTemporaryKept) {
    removeFile(goTempoCommandFileName->data());
    removeFile(goTempoModelFileName->data());
    removeFile(goTempoResultFileName->data());
   }

   return (lcNbIteration);
  )

  ampl_no (
   send_error(erAmplNotInstalled);
   return (0);
  )
 }
}

namespace private_area {}

// End //-------------------------------------------------------------------------------------------
}
#undef dll_export
#undef public_area
#undef private_area
#endif
 
//==================================================================================================
// L i n e a r _ s y s t e m                                                         Implementation
// A m p l
//                                                                                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/ampl.cpp"

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

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

namespace bpp {

// Namespaces //------------------------------------------------------------------------------------
#define public_area  linearSystemAmpl
#define private_area linearSystemAmpl_private
#define dll_export   DLL_EXPORT

namespace public_area  {}
namespace private_area {}

static_module_name("Linear_system/Ampl");

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

// Errors //----------------------------------------------------------------------------------------
namespace public_area {
 static_error erAmplUnknownError;
 ampl_no ( static_error erAmplNotInstalled; )
}

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

dynamic_constant(clString,goDataLocation);

// 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);

    erAmplUnknownError.create("AMPL - An unknown error has occurred.");
    ampl_no ( erAmplNotInstalled.create("AMPL isn't installed."); )

    goTempoCommandFileName = new_object(clString(environment::temporaryLocation()+
                             fileNameSeparator()+LINEAR_SYSTEM_AMPL_TEMPORARY_FILE_1));

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

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

    goDataLocation = new_object(clString(environment::dataLocation()+fileNameSeparator()+
                     "linear_system"+fileNameSeparator()+"ampl"));
   }

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

   delete_object(goTempoCommandFileName);
   delete_object(goTempoModelFileName);
   delete_object(goTempoResultFileName);
   delete_object(goDataLocation);
  }

  initializer_catch;
 }
}

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