//==================================================================================================
// G r a p h _ p r o b l e m                                                              Interface
// M i n _ c o s t _ t e n s i o n
// H e u r i s t i c _ b i n 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 heuristics to solve the minimum cost tension problem with binary costs,
   as defined by the <CODE>clBinaryArcData</CODE> class of the <CODE>Structure</CODE> module. */

// File Name //-------------------------------------------------------------------------------------
#line __LINE__ "graph_problem/min_cost_tension/heuristic_binary.hpp"

// Guardian //--------------------------------------------------------------------------------------
#ifndef guGraphProblemMinCostTensionHeuristicBinary
#define guGraphProblemMinCostTensionHeuristicBinary

// Headers //---------------------------------------------------------------------------------------
#include <bpp/graph_problem/min_cost_tension/algorithm.hpp> /*INCLUDE*/
#include <bpp/graph_problem/min_cost_tension/linear_system.hpp> /*INCLUDE*/

namespace bpp {

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

// Namespaces //------------------------------------------------------------------------------------
#define public_area  graphProblemMinCostTensionHeuristicBinary
#define private_area graphProblemMinCostTensionHeuristicBinary_private

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

extern_module_name;

// Initialization //--------------------------------------------------------------------------------

// Macrocommands //---------------------------------------------------------------------------------
/*ALIAS*/
#define tdGraph class prArcData,class prNodeData //

/*ALIAS*/
#define tuGraph prArcData,prNodeData //

// Types & Classes //-------------------------------------------------------------------------------
namespace public_area {
 //------------------------------------------------------------------------------------------Classes
 template <tdGraph> class clSolveAlgo;
 template <tdGraph> class clGreedyAlgoI;
 template <tdGraph> class clGreedyAlgoII;
 template <tdGraph> class clLagrangeanAlgo;
}

namespace private_area {
 struct tyGreedyGlobal {
  std_vector(clTensionConstraint *)       constraints;
  std_map(clTensionVariable *,tyCardinal) indexes;
  tyInteger                               iteration;
  std_vector(tyCardinal)                  scores;
  std_vector(clTensionVariable *)         variables;
 };

 class clVariableSignature;
}

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

namespace private_area {
 function tyCardinal computeScore(clTensionSystem &,clTensionVariable * &);
 function tyBoolean  fixBetterVariable(clTensionSystem &,tyCardinal,tyGreedyGlobal &);

 inline tyBoolean operator < (const clVariableSignature &,const clVariableSignature &);

 testing_mode ( function void test(void); )
}

// Errors //----------------------------------------------------------------------------------------
namespace public_area {}

// Constants & Variables //-------------------------------------------------------------------------
namespace public_area  {}
namespace private_area {}

// S o l v e A l g o  Interface //------------------------------------------------------------------
namespace public_area {
 /*CLASS clSolveAlgo */
 /* Represents an algorithm to solve the minimum binary cost tension problem in a graph using a
    heuristic approach. It is an abstract class. */
 template <tdGraph> class clSolveAlgo : public clBinarySolver<tuGraph> {
  //-----------------------------------------------------------------------------------------Private
  private_property constructor clSolveAlgo(const clSolveAlgo &);
  private_property clSolveAlgo & operator = (const clSolveAlgo &);
  //------------------------------------------------------------------------------------------Public
  public_property constructor clSolveAlgo(void);
  public_property virtual destructor clSolveAlgo(void);

  /*AMETHOD clSolveAlgo */
  /* Solves the minimum cost tension problem of a graph. Abstract method. */
  public_property virtual tyInteger run(clGraph<tuGraph> & agGraph) const = 0;

  public_property static tyInteger defaultRun(clGraph<tuGraph> &);
 };
}

// G r e e d y A l g o I  Interface //--------------------------------------------------------------
namespace public_area {
 /*CLASS clGreedyAlgoI */
 /* Represents an algorithm to solve the minimum binary cost tension problem in a graph using
    a greedy heuristic based on linear programming (approach I). */
 template <tdGraph> class clGreedyAlgoI : public clSolveAlgo<tuGraph> {
  //-------------------------------------------------------------------------------------------Types
  /*TYPE clGreedyAlgoI */ /* Type of the binary solver used by the algorithm. */
  public_property
  typedef graphProblemMinCostTensionLinearSystem::clBinarySolveAlgo<tuGraph> cpBinarySolver;
  //-----------------------------------------------------------------------------------------Private
  private_property constructor clGreedyAlgoI(const clGreedyAlgoI &);
  private_property clGreedyAlgoI & operator = (const clGreedyAlgoI &);
  //------------------------------------------------------------------------------------------Public
  public_property constructor clGreedyAlgoI(void);
  public_property destructor clGreedyAlgoI(void);

  public_property tyInteger run(clGraph<tuGraph> &) const;

  public_property tyInteger run(clGraph<tuGraph> &,clTensionSystem &,tyReal=0.005,tyBoolean=false,
                                tyBoolean=false) const;

  public_property tyInteger run(clGraph<tuGraph> &,const cpBinarySolver &,
                                tyReal=0.005,tyBoolean=false,tyBoolean=false) const;
 };
}

// G r e e d y A l g o I I  Interface //------------------------------------------------------------
namespace public_area {
 /*CLASS clGreedyAlgoII */
 /* Represents an algorithm to solve the minimum binary cost tension problem in a graph using
    a greedy heuristic based on linear programming (approach II). */
 template <tdGraph> class clGreedyAlgoII : public clSolveAlgo<tuGraph> {
  //-------------------------------------------------------------------------------------------Types
  /*TYPE clGreedyAlgoII */ /* Type of the binary solver used by the algorithm. */
  public_property
  typedef graphProblemMinCostTensionLinearSystem::clBinarySolveAlgo<tuGraph> cpBinarySolver;
  //-----------------------------------------------------------------------------------------Private
  private_property constructor clGreedyAlgoII(const clGreedyAlgoII &);
  private_property clGreedyAlgoII & operator = (const clGreedyAlgoII &);
  //------------------------------------------------------------------------------------------Public
  public_property constructor clGreedyAlgoII(void);
  public_property destructor clGreedyAlgoII(void);

  public_property tyInteger run(clGraph<tuGraph> &) const;
  public_property tyInteger run(clGraph<tuGraph> &,clTensionSystem &,tyReal=0.005) const;
  public_property tyInteger run(clGraph<tuGraph> &,const cpBinarySolver &,tyReal=0.005) const;
 };
}

// L a g r a n g e a n A l g o  Interface //--------------------------------------------------------
namespace public_area {
 /*CLASS clLagrangeanAlgo */
 /* Represents an algorithm to solve the minimum binary cost tension problem in a graph using
    greedy heuristic II of the module and a Lagrangean relaxation of the problem. */
 template <tdGraph> class clLagrangeanAlgo : public clSolveAlgo<tuGraph> {
  //-------------------------------------------------------------------------------------------Types
  /*TYPE clLagrangeanAlgo */ /* Type of the binary solver used by the algorithm. */
  public_property
  typedef graphProblemMinCostTensionLinearSystem::clBinarySolveAlgo<tuGraph> cpBinarySolver;
  //-----------------------------------------------------------------------------------------Private
  private_property constructor clLagrangeanAlgo(const clLagrangeanAlgo &);
  private_property clLagrangeanAlgo & operator = (const clLagrangeanAlgo &);
  //------------------------------------------------------------------------------------------Public
  public_property constructor clLagrangeanAlgo(void);
  public_property destructor clLagrangeanAlgo(void);

  public_property tyInteger run(clGraph<tuGraph> &) const;

  public_property tyInteger run(clGraph<tuGraph> &,const cpBinarySolver & agBinarySolver,
                                tyReal,tyCardinal,tyCardinal) const;
 };
}

// V a r i a b l e S i g n a t u r e  Interface //--------------------------------------------------
namespace private_area {
 class clVariableSignature {
  //------------------------------------------------------------------------------------------Public
  read_write_attribute(tyCardinal,atScore,score);
  read_write_attribute(tyReal,atValue,value);
  read_write_attribute(tyCardinal,atIndex,index);

  public_property constructor clVariableSignature(void);
  public_property constructor clVariableSignature(const clVariableSignature &);
  public_property destructor clVariableSignature(void) {}

  public_property clVariableSignature & operator = (const clVariableSignature &);
 };
}

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

namespace private_area {
 //-------------------------------------------------------------------Operator < (VariableSignature)
 inline tyBoolean operator < (const clVariableSignature & agSignature1,
                              const clVariableSignature & agSignature2) {
  if (agSignature1.score()<agSignature2.score()) return (true);
  else if (agSignature1.score()==agSignature2.score())
   return (agSignature1.value()<agSignature2.value());

  return (false);
 }
}

// S o l v e A l g o  Inline //---------------------------------------------------------------------
namespace public_area {
 //--------------------------------------------------------------------------------------Constructor
 /*METHOD clSolveAlgo */ /* Builds an algorithm to solve the minimum cost tension problem. */
 template <tdGraph> inline clSolveAlgo<tuGraph>::clSolveAlgo(void) {}
 //---------------------------------------------------------------------------------------Destructor
 /*METHOD clSolveAlgo */ /* Destructs the algorithm. */
 template <tdGraph> inline clSolveAlgo<tuGraph>::~clSolveAlgo(void) {}
 //---------------------------------------------------------------------------------------DefaultRun
 /*METHOD clSolveAlgo */
 /* Solves the minimum cost tension problem of a graph using the default version of the algorithm
    (greedy heuristic based on linear programming, approach I). Static method. */
 template <tdGraph> inline tyInteger clSolveAlgo<tuGraph>::defaultRun(clGraph<tuGraph> & agGraph) {
  clGreedyAlgoI<tuGraph> lcGreedyAlgo;

  return (lcGreedyAlgo.run(agGraph));
 }
}

// G r e e d y A l g o I  Inline //-----------------------------------------------------------------
namespace public_area {
 //--------------------------------------------------------------------------------------Constructor
 /*METHOD clGreedyAlgoI */ /* Builds an algorithm to solve the minimum cost tension problem. */
 template <tdGraph> inline clGreedyAlgoI<tuGraph>::clGreedyAlgoI(void) {}
 //---------------------------------------------------------------------------------------Destructor
 /*METHOD clGreedyAlgoI */ /* Destructs the algorithm. */
 template <tdGraph> inline clGreedyAlgoI<tuGraph>::~clGreedyAlgoI(void) {}
 //----------------------------------------------------------------------------------------------Run
 /*METHOD clGreedyAlgoI */
 /* Solves the minimum cost tension problem of a graph, with default parameters to tune the
    heuristic. */
 template <tdGraph> inline
 tyInteger clGreedyAlgoI<tuGraph>::run(clGraph<tuGraph> & agGraph) const {
  graphProblemMinCostTensionLinearSystem::clBinarySolveAlgoI<tuGraph> lcSolveAlgo;

  return (run(agGraph,lcSolveAlgo));
 }
 //----------------------------------------------------------------------------------------------Run
 /*METHOD clGreedyAlgoI */
 /* Solves the minimum cost tension problem of a graph. Several parameters must be specified to
    tune the heuristic: a binary solver to use to get the linear model, a threshold (default
    <I>0.005</I>) to decide when a variable must be fixed and two booleans (default
    <CODE>false</CODE> and <CODE>false</CODE>) to indicate the fixing strategy (whether or not
    fixing the variables when equal to one, whether or not using scores based on the cycle
    topology to intervene in the fixing policy). */
 template <tdGraph> inline tyInteger
 clGreedyAlgoI<tuGraph>::run(clGraph<tuGraph> & agGraph,const cpBinarySolver & agBinarySolver,
                             tyReal agThreshold,tyBoolean agOnesFixed,
                             tyBoolean agCycleScoreUsed) const {
  clTensionSystem lcSystem;

  agBinarySolver.buildLinearSystem(lcSystem,agGraph);
  return (run(agGraph,lcSystem,agThreshold,agOnesFixed,agCycleScoreUsed));
 }
}

// G r e e d y A l g o I I  Inline //---------------------------------------------------------------
namespace public_area {
 //--------------------------------------------------------------------------------------Constructor
 /*METHOD clGreedyAlgoII */ /* Builds an algorithm to solve the minimum cost tension problem. */
 template <tdGraph> inline clGreedyAlgoII<tuGraph>::clGreedyAlgoII(void) {}
 //---------------------------------------------------------------------------------------Destructor
 /*METHOD clGreedyAlgoII */ /* Destructs the algorithm. */
 template <tdGraph> inline clGreedyAlgoII<tuGraph>::~clGreedyAlgoII(void) {}
 //----------------------------------------------------------------------------------------------Run
 /*METHOD clGreedyAlgoII */
 /* Solves the minimum cost tension problem of a graph, with default parameters to tune the
    heuristic. */
 template <tdGraph> inline
 tyInteger clGreedyAlgoII<tuGraph>::run(clGraph<tuGraph> & agGraph) const {
  graphProblemMinCostTensionLinearSystem::clBinarySolveAlgoI<tuGraph> lcSolveAlgo;

  return (run(agGraph,lcSolveAlgo));
 }
 //----------------------------------------------------------------------------------------------Run
 /*METHOD clGreedyAlgoII */
 /* Solves the minimum cost tension problem of a graph. Several parameters must be specified to
    tune the heuristic: a binary solver to use to get the linear model and a threshold (default
    <I>0.005</I>) to decide when a variable must be fixed. */
 template <tdGraph> inline tyInteger
 clGreedyAlgoII<tuGraph>::run(clGraph<tuGraph> & agGraph,const cpBinarySolver & agBinarySolver,
                              tyReal agThreshold) const {
  clTensionSystem lcSystem;

  agBinarySolver.buildLinearSystem(lcSystem,agGraph);
  return (run(agGraph,lcSystem,agThreshold));
 }
}

// L a g r a n g e a n A l g o  Inline //-----------------------------------------------------------
namespace public_area {
 //--------------------------------------------------------------------------------------Constructor
 /*METHOD clLagrangeanAlgo */ /* Builds an algorithm to solve the minimum cost tension problem. */
 template <tdGraph> inline clLagrangeanAlgo<tuGraph>::clLagrangeanAlgo(void) {}
 //---------------------------------------------------------------------------------------Destructor
 /*METHOD clLagrangeanAlgo */ /* Destructs the algorithm. */
 template <tdGraph> inline clLagrangeanAlgo<tuGraph>::~clLagrangeanAlgo(void) {}
 //----------------------------------------------------------------------------------------------Run
 /*METHOD clLagrangeanAlgo */
 /* Solves the minimum cost tension problem of a graph, with default parameters to tune the
    heuristic. */
 template <tdGraph> inline
 tyInteger clLagrangeanAlgo<tuGraph>::run(clGraph<tuGraph> & agGraph) const {
  graphProblemMinCostTensionLinearSystem::clBinarySolveAlgoI<tuGraph> lcSolveAlgo;

  return (run(agGraph,lcSolveAlgo,0.005,100*agGraph.arcs().size(),agGraph.arcs().size()));
 }
}

// V a r i a b l e S i g n a t u r e  Inline //-----------------------------------------------------
namespace private_area {
 //--------------------------------------------------------------------------------------Constructor
 inline clVariableSignature::clVariableSignature(void) : atScore(0),atValue(0.0),atIndex(0) {}
 //--------------------------------------------------------------------------------------Constructor
 inline clVariableSignature::clVariableSignature(const clVariableSignature & agSignature)
 : atScore(agSignature.atScore),atValue(agSignature.atValue),atIndex(agSignature.atIndex) {}
 //---------------------------------------------------------------------------------------Operator =
 inline
 clVariableSignature & clVariableSignature::operator = (const clVariableSignature & agSignature) {
  atScore=agSignature.atScore;
  atValue=agSignature.atValue;
  atIndex=agSignature.atIndex;

  return (*this);
 }
}

// G r e e d y A l g o I  Implementation //---------------------------------------------------------
namespace public_area {
 //----------------------------------------------------------------------------------------------Run
 /*METHOD clGreedyAlgoI */
 /* Solves the minimum cost tension problem of a graph. Several parameters must be specified to
    tune the heuristic: a linear model of the problem, a threshold (default <I>0.005</I>) to decide
    when a variable must be fixed and two booleans (default <CODE>false</CODE> and
    <CODE>false</CODE>) to indicate the fixing strategy (whether or not fixing the variables when
    equal to one, whether or not using scores based on the cycle topology to intervene in the
    fixing policy). The linear model will be modified during the process. */
 template <tdGraph>
 tyInteger clGreedyAlgoI<tuGraph>::run(clGraph<tuGraph> & agGraph,clTensionSystem & agSystem,
                                       tyReal agThreshold,tyBoolean agOnesFixed,
                                       tyBoolean agCycleScoreUsed) const {
  typedef graphProblemMinCostTensionStructure::clVariableContent clVariableContent;
  typedef clTensionSystem::cpVariableX::const_iterator           clVariableIterator;
  typedef linearSystemSolver::clSolver<clVariableContent>        clLinearSystemSolver;

  clBinaryArc *                lcArc;
  clTensionConstraint *        lcConstraint;
  clVariableIterator           lcCurrentVariable;
  private_area::tyGreedyGlobal lcGlobal;
  clVariableIterator           lcLastVariable;
  clTensionVariable *          lcMaximumVariable;
  clTensionVariable *          lcMinimumVariable;
  clTensionVariable *          lcVariable;

  tyCardinal lcIndex;
  tyInteger  lcLocalIteration;
  tyCardinal lcMaximumIndex;
  tyCardinal lcMinimumIndex;
  tyCardinal lcNbLoose;

  std_vector(clTensionConstraint *) & lcConstraintS    = lcGlobal.constraints;
  clLinearSystemSolver &              lcLinearSolver   = clLinearSystemSolver::defaultSolver();
  tyInteger &                         lcTotalIteration = lcGlobal.iteration;

  // Relaxed Problem Building //
  lcCurrentVariable=agSystem.variables().begin();
  lcLastVariable=agSystem.variables().end();

  while (lcCurrentVariable!=lcLastVariable) {
   lcVariable=(*lcCurrentVariable).second;

   if (lcVariable->content().significance()==binary) {
    lcVariable->kind()=linearSystem::realVariable;
    lcGlobal.constraints.push_back(nil);
    lcGlobal.scores.push_back(private_area::computeScore(agSystem,lcVariable));
    lcGlobal.variables.push_back(lcVariable);
    lcGlobal.indexes.insert(std_make_pair(lcGlobal.variables.back(),lcGlobal.variables.size()-1));
   }

   ++lcCurrentVariable;
  }

  lcNbLoose=lcGlobal.constraints.size();

  // Relaxed Problem Resolution //
  lcTotalIteration=lcLinearSolver.run(agSystem);
  agGraph.solved()=false;
  if (not agSystem.solved()) return (-1);

  // Main Loop //
  while (lcNbLoose>0) {
   // Bounds Search & Null Variables Fixing //
   lcCurrentVariable=agSystem.variables().begin();
   lcMinimumVariable=nil;
   lcMaximumVariable=nil;
   lcMinimumIndex=0;
   lcMaximumIndex=0;
   lcIndex=0;

   while (lcCurrentVariable!=lcLastVariable) {
    lcVariable=(*lcCurrentVariable).second;

    if (lcVariable->content().significance()==binary) {
     if (lcConstraintS[lcIndex]==nil) {
      if (isEqual(lcVariable->value(),0.0)) {
       lcConstraintS[lcIndex]=new_object(clTensionConstraint(agSystem,linearSystem::equality));
       lcConstraintS[lcIndex]->setCoefficient(lcVariable->key(),1.0);
       --lcNbLoose;
      }
      else if (isEqual(lcVariable->value(),1.0) and agOnesFixed) {
       lcConstraintS[lcIndex]=new_object(clTensionConstraint(agSystem,linearSystem::equality));
       lcConstraintS[lcIndex]->setCoefficient(lcVariable->key(),1.0);
       lcConstraintS[lcIndex]->boundary()=1.0;
       --lcNbLoose;
      }
      else {
       if (lcMinimumVariable==nil or lcVariable->value()<lcMinimumVariable->value()) {
        lcMinimumVariable=lcVariable;
        lcMinimumIndex=lcIndex;
       }

       if (lcMaximumVariable==nil or lcVariable->value()>lcMaximumVariable->value()) {
        lcMaximumVariable=lcVariable;
        lcMaximumIndex=lcIndex;
       }
      }
     }

     ++lcIndex;
    }

    ++lcCurrentVariable;
   }

   // Variable Fixing Policy //
   if (lcNbLoose>0) {
    lcConstraint=new_object(clTensionConstraint(agSystem,linearSystem::equality));

    if (lcMinimumVariable->value()<agThreshold) {
     lcConstraint->setCoefficient(lcMinimumVariable->key(),1.0);
     lcLocalIteration=lcLinearSolver.run(agSystem);
     lcConstraintS[lcMinimumIndex]=lcConstraint;

     if (not agSystem.solved()) {
      if (not agCycleScoreUsed
          or not private_area::fixBetterVariable(agSystem,lcMinimumIndex,lcGlobal)) {
       lcConstraint->boundary()=1.0;
       lcTotalIteration+=lcLinearSolver.run(agSystem);
      }
     }
     else lcTotalIteration+=lcLocalIteration;
    }
    else if (lcMaximumVariable->value() < 1.0-agThreshold) {
     lcConstraint->setCoefficient(lcMaximumVariable->key(),1.0);
     lcConstraint->boundary()=1.0;
     lcConstraintS[lcMaximumIndex]=lcConstraint;
     lcTotalIteration+=lcLinearSolver.run(agSystem);
    }
    else if (lcMinimumVariable->value()<0.5) {
     lcConstraint->setCoefficient(lcMinimumVariable->key(),1.0);
     lcLocalIteration=lcLinearSolver.run(agSystem);
     lcConstraintS[lcMinimumIndex]=lcConstraint;

     if (not agSystem.solved()) {
      if (not agCycleScoreUsed
          or not private_area::fixBetterVariable(agSystem,lcMinimumIndex,lcGlobal)) {
       lcConstraint->boundary()=1.0;
       lcTotalIteration+=lcLinearSolver.run(agSystem);
      }
     }
     else lcTotalIteration+=lcLocalIteration;
    }
    else {
     lcConstraint->setCoefficient(lcMaximumVariable->key(),1.0);
     lcConstraint->boundary()=1.0;
     lcConstraintS[lcMaximumIndex]=lcConstraint;
     lcTotalIteration+=lcLinearSolver.run(agSystem);
    }

    --lcNbLoose;
   }
  }

  // Connection With The Graph //
  lcCurrentVariable=agSystem.variables().begin();
  lcLastVariable=agSystem.variables().end();

  while (lcCurrentVariable!=lcLastVariable) {
   lcVariable=(*lcCurrentVariable).second;

   if (lcVariable->content().significance()==tension) {
    lcArc=&(agGraph.arc(lcVariable->content().arc()));
    lcArc->data().expected()=lcVariable->value();
   }

   ++lcCurrentVariable;
  }

  agGraph.solved()=true;
  return (lcTotalIteration);
 }
}

// G r e e d y A l g o I I  Implementation //-------------------------------------------------------
namespace public_area {
 //----------------------------------------------------------------------------------------------Run
 /*METHOD clGreedyAlgoII */
 /* Solves the minimum cost tension problem of a graph. Several parameters must be specified to
    tune the heuristic: a linear model of the problem and a threshold (default <I>0.005</I>) to
    decide when a variable must be fixed. The linear model will be modified during the process. */
 template <tdGraph>
 tyInteger clGreedyAlgoII<tuGraph>::run(clGraph<tuGraph> & agGraph,clTensionSystem & agSystem,
                                        tyReal agThreshold) const {
  typedef graphProblemMinCostTensionStructure::clVariableContent              clVariableContent;
  typedef clTensionSystem::cpVariableX::const_iterator                        clVariableIterator;
  typedef linearSystemSolver::clSolver<clVariableContent>                     clLinearSystemSolver;
  typedef std_multimap(private_area::clVariableSignature,clTensionVariable *) clCandidateX;
  typedef clCandidateX::const_iterator                                        clCandidateIterator;

  clBinaryArc *                     lcArc;
  clCandidateX                      lcCandidateX;
  clTensionConstraint *             lcConstraint;
  std_vector(clTensionConstraint *) lcConstraintS;
  clCandidateIterator               lcCurrentCandidate;
  clVariableIterator                lcCurrentVariable;
  clCandidateIterator               lcLastCandidate;
  clVariableIterator                lcLastVariable;
  std_vector(tyCardinal)            lcScoreS;
  private_area::clVariableSignature lcSignature;
  clTensionVariable *               lcVariable;

  tyBoolean  lcFixed;
  tyCardinal lcIndex;
  tyInteger  lcLocalIteration;
  tyCardinal lcNbLoose;
  tyReal     lcThreshold;
  tyInteger  lcTotalIteration;

  clLinearSystemSolver & lcLinearSolver = clLinearSystemSolver::defaultSolver();

  // Relaxed Problem Building //
  lcCurrentVariable=agSystem.variables().begin();
  lcLastVariable=agSystem.variables().end();

  while (lcCurrentVariable!=lcLastVariable) {
   lcVariable=(*lcCurrentVariable).second;

   if (lcVariable->content().significance()==binary) {
    lcVariable->kind()=linearSystem::realVariable;
    lcConstraintS.push_back(nil);
    lcScoreS.push_back(private_area::computeScore(agSystem,lcVariable));
   }

   ++lcCurrentVariable;
  }

  lcNbLoose=lcConstraintS.size();

  // Relaxed Problem Resolution //
  lcTotalIteration=lcLinearSolver.run(agSystem);
  agGraph.solved()=false;
  if (not agSystem.solved()) return (-1);

  // Main Loop //
  while (lcNbLoose>0) {
   // Variables Sorting //
   lcCandidateX.erase(lcCandidateX.begin(),lcCandidateX.end());
   lcCurrentVariable=agSystem.variables().begin();
   lcIndex=0;

   while (lcCurrentVariable!=lcLastVariable) {
    lcVariable=(*lcCurrentVariable).second;

    if (lcVariable->content().significance()==binary) {
     if (lcConstraintS[lcIndex]==nil) {
      lcSignature.score()=lcScoreS[lcIndex];
      lcSignature.value()=lcVariable->value();
      lcSignature.index()=lcIndex;
      lcCandidateX.insert(std_make_pair(lcSignature,lcVariable));
     }

     ++lcIndex;
    }

    ++lcCurrentVariable;
   }

   // Variable Fixing To Zero //
   lcFixed=false;
   lcThreshold=agThreshold;

   while (lcThreshold<=1.0 and not lcFixed) {
    lcCurrentCandidate=lcCandidateX.begin();
    lcLastCandidate=lcCandidateX.end();

    while (lcCurrentCandidate!=lcLastCandidate and not lcFixed) {
     lcVariable=(*lcCurrentCandidate).second;
     lcIndex=(*lcCurrentCandidate).first.index();

     if (lcVariable->value()<=lcThreshold) {
      lcConstraint=new_object(clTensionConstraint(agSystem,linearSystem::equality));
      lcConstraint->setCoefficient(lcVariable->key(),1.0);
      lcLocalIteration=lcLinearSolver.run(agSystem);

      if (not agSystem.solved()) {
       lcConstraint->boundary()=1.0;
       lcLocalIteration=lcLinearSolver.run(agSystem);
      }

      lcConstraintS[lcIndex]=lcConstraint;
      lcTotalIteration+=lcLocalIteration;
      lcFixed=true;
      --lcNbLoose;
     }

     ++lcCurrentCandidate;
    }

    if (not lcFixed) lcThreshold=2*lcThreshold;
   }

   // Variable Fixing To One //
   if (not lcFixed) {
    lcVariable=(*(lcCandidateX.begin())).second;
    lcIndex=(*(lcCandidateX.begin())).first.index();
    lcConstraintS[lcIndex]=new_object(clTensionConstraint(agSystem,linearSystem::equality));
    lcConstraintS[lcIndex]->boundary()=1.0;
    lcConstraintS[lcIndex]->setCoefficient(lcVariable->key(),1.0);
    lcTotalIteration+=lcLinearSolver.run(agSystem);
    --lcNbLoose;
   }
  }

  // Connection With The Graph //
  lcCurrentVariable=agSystem.variables().begin();
  lcLastVariable=agSystem.variables().end();

  while (lcCurrentVariable!=lcLastVariable) {
   lcVariable=(*lcCurrentVariable).second;

   if (lcVariable->content().significance()==tension) {
    lcArc=&(agGraph.arc(lcVariable->content().arc()));
    lcArc->data().expected()=lcVariable->value();
   }

   ++lcCurrentVariable;
  }

  agGraph.solved()=true;
  return (lcTotalIteration);
 }
}

// L a g r a n g e a n A l g o  Implementation //---------------------------------------------------
namespace public_area {
 //----------------------------------------------------------------------------------------------Run
 /*METHOD clLagrangeanAlgo */
 /* Solves the minimum cost tension problem of a graph. Several parameters must be specified to
    tune the heuristic: a binary solver to use to get the linear model, a threshold to decide
    when a variable must be fixed, the maximum and per-block numbers of iterations for the
    subgradient. */
 template <tdGraph>
 tyInteger clLagrangeanAlgo<tuGraph>::run(clGraph<tuGraph> & agGraph,
                                          const cpBinarySolver & agBinarySolver,
                                          tyReal agThreshold,tyCardinal agMaxIteration,
                                          tyCardinal agBlockIteration) const {
  typedef clArc<tuGraph>                                         cpArc;
  typedef typename clGraph<tuGraph>::cpArcX::const_iterator      cpArcIterator;
  typedef std_map(cpArc *,tyPairReal)                            cpCoefficientX;
  typedef graphProblemMinCostTensionStructure::clVariableContent clVariableContent;
  typedef linearSystemSolver::clSolver<clVariableContent>        clLinearSystemSolver;

  cpArc *                 lcArc;
  tyReal                  lcBound;
  tyPairReal              lcCoefficient;
  cpCoefficientX          lcCoefficientX;
  tyReal                  lcCost;
  clGreedyAlgoII<tuGraph> lcGreedyAlgo;
  tyReal                  lcOptimum;

  std_vector(tyReal) lcBestSolution(agGraph.arcs().size());
  std_vector(tyReal) lcWeightS(agGraph.arcs().size());

  tyReal                 lcBestBound            = realMax();
  tyCardinal             lcCounter              = 0;
  cpArcIterator          lcCurrentArc           = agGraph.arcs().begin();
  cpArcIterator          lcLastArc              = agGraph.arcs().end();
  clLinearSystemSolver & lcLinearSolver         = clLinearSystemSolver::defaultSolver();
  tyReal                 lcStep                 = 2.0;
  tyCardinal             lcSubgradientIteration = 0;
  tyInteger              lcTotalIteration       = 0;

  // Weights Storage & Lagrangean Coefficients Initialization //
  while (lcCurrentArc!=lcLastArc) {
   lcArc=(*lcCurrentArc).second;
   lcWeightS[lcCounter]=lcArc->data().weight();
   lcCoefficientX.insert(std_make_pair(lcArc,standard::make_pair(0.0,0.0)));
   ++lcCounter;
   ++lcCurrentArc;
  }

  do {
   // Greedy Algorithm Execution //
   lcTotalIteration+=lcGreedyAlgo.run(agGraph,agBinarySolver,agThreshold);
   if (not agGraph.solved()) return (-1);

   // Bound Computation & Weights Restoration //
   lcCurrentArc=agGraph.arcs().begin();
   lcCounter=0;
   lcBound=0.0;

   while (lcCurrentArc!=lcLastArc) {
    lcArc=(*lcCurrentArc).second;
    lcArc->data().weight()=lcWeightS[lcCounter];
    lcBound+=lcArc->data().cost();
    ++lcCounter;
    ++lcCurrentArc;
   }

   // Best Solution Storage //
   if (lcBound<lcBestBound) {
    lcBestBound=lcBound;
    lcCurrentArc=agGraph.arcs().begin();
    lcCounter=0;

    while (lcCurrentArc!=lcLastArc) {
     lcBestSolution[lcCounter]=(*lcCurrentArc).second->data().tension();
     ++lcCounter;
     ++lcCurrentArc;
    }
   }

   // Lagrangean Relaxation Resolution //
   lcTotalIteration+=agBinarySolver.solveLagrangeanRelaxation(agGraph,lcLinearSolver,
                                                              agBlockIteration,agBlockIteration,
                                                              lcStep,lcBound,lcCoefficientX);

   if (not agGraph.solved()) return (-1);
   lcSubgradientIteration+=agBlockIteration;

   // Reduced Costs Computation //
   lcCurrentArc=agGraph.arcs().begin();
   lcCounter=0;

   while (lcCurrentArc!=lcLastArc) {
    lcArc=(*lcCurrentArc).second;
    lcOptimum=lcArc->data().optimum();
    lcCoefficient=lcCoefficientX[lcArc];

    if (randomCardinal(2)==0) {
     lcArc->data().weight()=lcWeightS[lcCounter]
                            -lcCoefficient.first*(lcOptimum-lcArc->data().minimum())
                            -lcCoefficient.second*(lcArc->data().maximum()-lcOptimum);
    }
    else {
     if (lcArc->data().tension()<lcOptimum) {
      lcCost=lcOptimum-lcArc->data().minimum();
      if (lcCost!=0.0) lcCost=(lcOptimum-lcArc->data().tension())/lcCost;
     }
     else {
      lcCost=lcArc->data().maximum()-lcOptimum;
      if (lcCost!=0.0) lcCost=(lcArc->data().tension()-lcOptimum)/lcCost;
     }

     lcArc->data().weight()=(1.0-lcCost)*lcWeightS[lcCounter];
    }

    ++lcCounter;
    ++lcCurrentArc;
   }
  }
  while (lcSubgradientIteration<agMaxIteration);

  // Best Solution & Weights Restoration //
  lcCurrentArc=agGraph.arcs().begin();
  lcCounter=0;

  while (lcCurrentArc!=lcLastArc) {
   lcArc=(*lcCurrentArc).second;
   lcArc->data().weight()=lcWeightS[lcCounter];
   lcArc->data().tension()=lcBestSolution[lcCounter];
   ++lcCounter;
   ++lcCurrentArc;
  }

  agGraph.solved()=true;
  return (lcTotalIteration);
 }
}

// End //-------------------------------------------------------------------------------------------
}
#undef dll_export
#undef tdGraph
#undef tuGraph
#undef public_area
#undef private_area
#endif
 
//==================================================================================================
// G r a p h _ p r o b l e m                                                         Implementation
// M i n _ c o s t _ t e n s i o n
// H e u r i s t i c _ b i n 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__ "graph_problem/min_cost_tension/heuristic_binary.cpp"

// DLL Belonging //---------------------------------------------------------------------------------
#define GRAPH_PROBLEM_MIN_COST_TENSION_DLL

// Headers //---------------------------------------------------------------------------------------
#include <bpp/graph_problem/min_cost_tension/heuristic_binary.hpp> /*INTERFACE*/

namespace bpp {

// Namespaces //------------------------------------------------------------------------------------
#define public_area  graphProblemMinCostTensionHeuristicBinary
#define private_area graphProblemMinCostTensionHeuristicBinary_private
#define dll_export   DLL_EXPORT

namespace public_area  {}
namespace private_area {}

static_module_name("Graph_problem/Min_cost_tension/Heuristic_binary");

// Initialization //--------------------------------------------------------------------------------

// Errors //----------------------------------------------------------------------------------------
namespace public_area {}

// Constants & Variables //-------------------------------------------------------------------------
namespace public_area  {}
namespace private_area {}

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

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

namespace private_area {
 //-------------------------------------------------------------------------------------ComputeScore
 property tyCardinal computeScore(clTensionSystem & agSystem,clTensionVariable * & agVariable) {
  typedef clTensionSystem::cpConstraintS::const_iterator clConstraintIterator;
  typedef clTensionSystem::cpVariableX::const_iterator   clVariableIterator;

  clConstraintIterator lcCurrentConstraint = agSystem.constraints().begin();
  clVariableIterator   lcCurrentVariable   = agSystem.variables().begin();
  tyBoolean            lcFound             = false;
  clConstraintIterator lcLastConstraint    = agSystem.constraints().end();
  clVariableIterator   lcLastVariable      = agSystem.variables().end();
  tyCardinal           lcScore             = 0;

  while (lcCurrentVariable!=lcLastVariable and not lcFound) {
   if ((*lcCurrentVariable).second->content().significance()==tension
       and (*lcCurrentVariable).second->content().arc()==agVariable->content().arc()) {
    agVariable=(*lcCurrentVariable).second;
    lcFound=true;
   }

   ++lcCurrentVariable;
  }

  if (agVariable!=nil) {
   while (lcCurrentConstraint!=lcLastConstraint) {
    if ((**lcCurrentConstraint)[agVariable->key()]>0.0) ++lcScore;
    ++lcCurrentConstraint;
   }
  }

  return (lcScore);
 }
 //--------------------------------------------------------------------------------FixBetterVariable
 property tyBoolean fixBetterVariable(clTensionSystem & agSystem,tyCardinal agIndex,
                                      tyGreedyGlobal & agGlobal) {

  typedef clTensionSystem::cpConstraintS::const_iterator  clConstraintIterator;
  typedef clTensionSystem::cpVariableX::const_iterator    clVariableIterator;
  typedef linearSystemSolver::clSolver<clVariableContent> clLinearSystemSolver;
  typedef std_multimap(tyInteger,clTensionVariable *)     clCandidateX;
  typedef clCandidateX::const_iterator                    clCandidateIterator;

  clCandidateX          lcCandidateX;
  clTensionConstraint * lcConstraint;
  clCandidateIterator   lcCurrentCandidate;
  clVariableIterator    lcCurrentVariable;
  tyCardinal            lcIndex;
  tyInteger             lcIteration;
  clVariableIterator    lcLastVariable;
  clCandidateIterator   lcLastCandidate;
  clTensionVariable *   lcVariable;

  tyBoolean            lcFixed             = false;
  clConstraintIterator lcCurrentConstraint = agSystem.constraints().begin();
  clConstraintIterator lcLastConstraint    = agSystem.constraints().end();

  // Candidate Variables Search //
  while (lcCurrentConstraint!=lcLastConstraint) {
   lcConstraint=*lcCurrentConstraint;

   if (isEqual((*lcConstraint)[agGlobal.variables[agIndex]->key()],1.0)) {
    lcCurrentVariable=agSystem.variables().begin();
    lcLastVariable=agSystem.variables().end();

    while (lcCurrentVariable!=lcLastVariable) {
     lcVariable=(*lcCurrentVariable).second;

     if (lcVariable->content().significance()==tension
         and lcVariable!=agGlobal.variables[agIndex]) {
      lcIndex=agGlobal.indexes[lcVariable];

      if (agGlobal.scores[lcIndex]>agGlobal.scores[agIndex]
          and agGlobal.constraints[lcIndex]!=nil
          and agGlobal.constraints[lcIndex]->boundary()==0.0
          and (*lcConstraint)[lcVariable->key()]>0.0)
       lcCandidateX.insert(std_make_pair(-tyInteger(agGlobal.scores[agIndex]),lcVariable));
     }

     ++lcCurrentVariable;
    }
   }

   ++lcCurrentConstraint;
  }

  // Candidate Fixing Attempt //
  lcCurrentCandidate=lcCandidateX.begin();
  lcLastCandidate=lcCandidateX.end();

  while (lcCurrentCandidate!=lcLastCandidate and not lcFixed) {
   lcIndex=agGlobal.indexes[(*lcCurrentCandidate).second];
   agGlobal.constraints[lcIndex]->boundary()=1.0;
   lcIteration=clLinearSystemSolver::defaultSolver().run(agSystem);

   if (not agSystem.solved()) agGlobal.constraints[lcIndex]->boundary()=0.0;
   else {
    lcFixed=true;
    agGlobal.iteration+=lcIteration;
   }

   ++lcCurrentCandidate;
  }

  return (lcFixed);
 }
}

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

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