//==================================================================================================
// P r o g r a m                                                                          Interface
// S o l v e _ t e n s i o n
// P l u g i n
//                                                                                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 a Java plugin to execute C++ methods of the program. They are only
   accessible through the <CODE>Wrapper</CODE> Java class of the module. */

// File Name //-------------------------------------------------------------------------------------
#line __LINE__ "program/solve_tension/plugin.hpp"

// Guardian //--------------------------------------------------------------------------------------
#ifndef guProgramSolveTensionPlugin
#define guProgramSolveTensionPlugin

// Headers //---------------------------------------------------------------------------------------
#include <bpp/graph_problem/min_cost_tension.hpp> /*INCLUDE*/
#include <jirk/bpp/data_structure/extension/propertymap.hpp> /*INCLUDE*/
#include <jirk/bpp/graph/javagraph.hpp> /*INCLUDE*/
#include <jirk/bpp/program/solve_tension/durationproperty.hpp> /*INCLUDE*/
#include <jirk/formatter/model/time/tf_flexibleduration.hpp> /*INCLUDE*/
#include <jirk/java/lang/string.hpp> /*INCLUDE*/

namespace bpp {

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

// Namespaces //------------------------------------------------------------------------------------
#define public_area  programSolveTensionPlugin
#define private_area programSolveTensionPlugin_private

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

extern_module_name;

// Initialization //--------------------------------------------------------------------------------
#define iniProgramSolveTensionPlugin
has_initializer;

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

// Types & Classes //-------------------------------------------------------------------------------
namespace public_area  {}
namespace private_area {}

// Functions Interface //---------------------------------------------------------------------------
namespace public_area {
 void convertGraph(clLinearGraph &,jirk::bpp::graph::jaJavaGraph);

 template <class prGraph> void assignTension(jirk::bpp::graph::jaJavaGraph,const prGraph &);
}

namespace private_area {}

// Errors //----------------------------------------------------------------------------------------
namespace public_area {
 /*ERROR*/ extern_error erGraphWithCircuit; /* The graph contains a circuit. */
 /*ERROR*/ extern_error erInvalidExpectedDuration; /* The expected duration value is not valid. */
 /*ERROR*/ extern_error erInvalidDurationProperty; /* The duration property is not valid. */
 /*ERROR*/ extern_error erInvalidResolutionMethod; /* The resolution method is not valid. */
}

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

// X X X  Interface //------------------------------------------------------------------------------
namespace {}

// Functions Inline //------------------------------------------------------------------------------
namespace public_area {
 //------------------------------------------------------------------------------------AssignTension
 /*FUNCTION*/
 /* Assigns the tension values on the arcs of a C++ graph to the equivalent arcs
    of a Java graph. */
 template <class prGraph>
 void assignTension(jirk::bpp::graph::jaJavaGraph agJavaGraph,const prGraph & agCppGraph) {
  using namespace jirk::bpp::data_structure::extension;
  using namespace jirk::bpp::graph;
  using namespace jirk::bpp::program::solve_tension;
  using namespace jirk::formatter::model::time;
  using namespace jirk::java::lang;

  typedef typename prGraph::cpArc                  cpArc;
  typedef typename prGraph::cpArcX::const_iterator cpIterator;

  cpArc *               lcCppArc;
  jaTF_Duration         lcDuration;
  jaTF_FlexibleDuration lcFlexibleDuration;
  jaJavaGraph_Arc       lcJavaArc;

  java::clContext lcContext    = java::virtualMachine().context();
  cpIterator      lcCurrentArc = agCppGraph.arcs().begin();
  jaPropertyMap   lcDurationX  = agJavaGraph.j_arcPropertyMap(jaString("duration"));
  cpIterator      lcLastArc    = agCppGraph.arcs().end();

  while (lcCurrentArc!=lcLastArc) {
   lcCppArc=(*lcCurrentArc).second;
   lcJavaArc=agJavaGraph.j_arc(jyLong(lcCppArc->key()));
   lcDuration=jaDurationProperty(lcDurationX.j_get(lcJavaArc)).j_getDuration();

   if (lcContext.isInstance(lcDuration,lcFlexibleDuration.classPointer())) {
    lcFlexibleDuration=lcDuration;
    lcFlexibleDuration.j_setOptimumValue(jyDouble(lcCppArc->data().expected()));
   }

   ++lcCurrentArc;
  }
 }
}

namespace private_area {}

// X X X  Inline //---------------------------------------------------------------------------------
namespace {}

// End //-------------------------------------------------------------------------------------------
}
#undef dll_export
#undef public_area
#undef private_area
#endif
 
//==================================================================================================
// P r o g r a m                                                                     Implementation
// S o l v e _ t e n s i o n
// P l u g i n
//                                                                                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__ "program/solve_tension/plugin.cpp"

// DLL Belonging //---------------------------------------------------------------------------------
#define PROGRAM_SOLVE_TENSION_DLL

// Headers //---------------------------------------------------------------------------------------
#include <bpp/program/solve_tension/plugin.hpp> /*INTERFACE*/
#include <jirk/formatter/model/time/tf_linearcostfunctionduration.hpp> /*NEED*/
#include <jirk/formatter/model/time/tf_unflexibleduration.hpp> /*NEED*/
#include <jirk/java/lang/double.hpp> /*NEED*/
#include <jirk/java/util/collection.hpp> /*NEED*/
#include <jirk/java/util/iterator.hpp> /*NEED*/

namespace bpp {

// Namespaces //------------------------------------------------------------------------------------
#define public_area  programSolveTensionPlugin
#define private_area programSolveTensionPlugin_private
#define dll_export   DLL_EXPORT

namespace public_area  {}
namespace private_area {}

static_module_name("Program/Solve_tension/Plugin");

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

// Errors //----------------------------------------------------------------------------------------
namespace public_area {
 static_error erGraphWithCircuit;
 static_error erInvalidDurationProperty;
 static_error erInvalidExpectedDuration;
 static_error erInvalidResolutionMethod;
}

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

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

// Functions Implementation //----------------------------------------------------------------------
namespace public_area {
 //-------------------------------------------------------------------------------------ConvertGraph
 /*FUNCTION*/
 /* Converts a Java graph describing a hypermedia synchronization problem into a C++ graph modeling
    a minimum linear cost tension problem. An exception is thrown if the conversion can not be
    performed. */
 void convertGraph(clLinearGraph & agCppGraph,jirk::bpp::graph::jaJavaGraph agJavaGraph) {
  method_name("convertGraph");

  using namespace jirk::bpp::data_structure::extension;
  using namespace jirk::bpp::graph;
  using namespace jirk::bpp::program::solve_tension;
  using namespace jirk::formatter::model::time;
  using namespace jirk::java::lang;
  using namespace jirk::java::util;

  java::clContext lcContext = virtualMachine().context();

  jaJavaGraph_Arc                   lcArc;
  clLinearArcData                   lcData;
  jaTF_Duration                     lcDuration;
  jaPropertyMap                     lcDurationX;
  jaTF_LinearCostFunctionDuration   lcFlexibleDuration;
  jaIterator                        lcIterator;
  jaJavaGraph_Node                  lcNode;
  tyArcKey                          lcSource;
  tyArcKey                          lcTarget;
  jaTF_UnflexibleDuration           lcUnflexibleDuration;

  // Graph Cleaning //
  agCppGraph.clear();
  agCppGraph.solved()=false;

  // Nodes Creation //
  lcIterator=jaCollection(agJavaGraph.j_nodes()).j_iterator();

  while (lcIterator.j_hasNext()==java_true) {
   lcNode=jaJavaGraph_Node(lcIterator.j_next());
   new_object(clLinearNode(agCppGraph,tyNodeKey(lcNode.j_key()),clNodeData()));
  }

  // Arcs Creation //
  lcDurationX=jaPropertyMap(agJavaGraph.j_arcPropertyMap(jaString("duration")));
  lcIterator=jaCollection(agJavaGraph.j_arcs()).j_iterator();

  while (lcIterator.j_hasNext()==java_true) {
   lcArc=jaJavaGraph_Arc(lcIterator.j_next());
   lcSource=tyArcKey(jaJavaGraph_Node(lcArc.j_sourceNode()).j_key());
   lcTarget=tyArcKey(jaJavaGraph_Node(lcArc.j_targetNode()).j_key());
   lcDuration=jaDurationProperty(lcDurationX.j_get(lcArc)).j_getDuration();

   // Arc Carrying An Unflexible Duration //
   if (lcContext.isInstance(lcDuration,lcUnflexibleDuration.classPointer())) {
    lcUnflexibleDuration=lcDuration;
    lcData.optimum()=lcUnflexibleDuration.j_getExpectedValue();
    if (lcData.optimum()==jaDouble::j_POSITIVE_INFINITY()) send_error(erInvalidExpectedDuration);
    lcData.minimum()=lcData.optimum();
    lcData.maximum()=lcData.optimum();
    lcData.shrinkingCost()=0.0;
    lcData.stretchingCost()=0.0;
   }

   // Arc Carrying A Flexible Duration //
   else if (lcContext.isInstance(lcDuration,lcFlexibleDuration.classPointer())) {
    lcFlexibleDuration=lcDuration;
    lcData.optimum()=lcFlexibleDuration.j_getExpectedValue();
    lcData.minimum()=lcFlexibleDuration.j_getMinimumValue();
    lcData.maximum()=lcFlexibleDuration.j_getMaximumValue();
    lcData.shrinkingCost()=lcFlexibleDuration.j_getShrinkingCostRate();
    lcData.stretchingCost()=lcFlexibleDuration.j_getStretchingCostRate();
    if (lcData.minimum()==jaDouble::j_NEGATIVE_INFINITY()) lcData.minimum()=realMin();
    if (lcData.maximum()==jaDouble::j_POSITIVE_INFINITY()) lcData.maximum()=realMax();

    if (lcData.optimum()==jaDouble::j_POSITIVE_INFINITY()
        or lcData.optimum()==jaDouble::j_NEGATIVE_INFINITY()
        or lcData.optimum()<lcData.minimum() or lcData.optimum()>lcData.maximum())
     send_error(erInvalidExpectedDuration);
   }

   // Invalid Duration Property //
   else send_error(erInvalidDurationProperty);

   lcData.expected()=0.0;
   lcData.flow()=0.0;
   new_object(clLinearArc(agCppGraph,tyArcKey(lcArc.j_key()),lcData,lcSource,lcTarget));
  }
 }
}

namespace private_area {}

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

// Java Zone //-------------------------------------------------------------------------------------
java_zone {
 //---------------------------------------------------------------------------------------Namespaces
 using namespace graphProblemMinCostTension;
 using namespace java;
 using namespace jirk::bpp::graph;
 //---------------------------------------------------------------------------------------WriteGraph
 java_method(jyBoolean,bpp_program_solve_1tension_Wrapper,writeGraph)
 (jyContext agContext,jyClass,jyObject agGraph) {
  try {
   getVirtualMachine(agContext);

   clLinearGraph lcCppGraph;
   jaJavaGraph   lcJavaGraph(agGraph);

   public_area::convertGraph(lcCppGraph,lcJavaGraph);
   environment::inform("C++ Graph:");
   environment::out(lcCppGraph);
  }

  java_native_catch;
 }
 //------------------------------------------------------------------------------RunLinearCostSolver
 java_method(jyBoolean,bpp_program_solve_1tension_Wrapper,runLinearCostSolver)
 (jyContext agContext,jyClass,jyObject agGraph,jyInteger agMethod) {
  try {
   typedef graphProblemMinCostTensionAggregation::clSolveAlgoII<clLinearArcData,clNodeData>
   clAggregationAlgo;

   typedef graphProblemMinCostTensionDualCostScaling::clSolveAlgo<clLinearArcData,clNodeData>
   clDualCostScalingAlgo;

   typedef graphProblemMinCostTensionConformingLinear::clSolveAlgoI<clLinearArcData,clNodeData>
   clConformingAlgoI;

   typedef graphProblemMinCostTensionConformingLinear::clSolveAlgoII<clLinearArcData,clNodeData>
   clConformingAlgoII;

   typedef graphProblemMinCostTensionLinearSystem::clLinearSolveAlgoI<clLinearArcData,clNodeData>
   clLinearProgrammingAlgoI;

   typedef graphProblemMinCostTensionLinearSystem::clLinearSolveAlgoII<clLinearArcData,clNodeData>
   clLinearProgrammingAlgoII;

   typedef graphProblemMinCostTensionLinearSystem::clLinearSolveAlgoIII<clLinearArcData,clNodeData>
   clLinearProgrammingAlgoIII;

   typedef graphProblemMinCostTension::clTensionSystemSolver tyLinearSolver;

   getVirtualMachine(agContext);

   graphProblemSerialParallel::clDecomposeAlgoI<clLinearArcData,clNodeData>   lcDecomposeAlgo1;
   graphProblemSerialParallel::clDecomposeAlgoII<clLinearArcData,clNodeData>  lcDecomposeAlgo2;
   graphProblemSerialParallel::clDecomposeAlgoIII<clLinearArcData,clNodeData> lcDecomposeAlgo3;

   clLinearGraph              lcCppGraph;
   tyInteger                  lcNbIteration;
   std_vector(clLinearNode *) lcNodeS;

   tyInteger   lcClock     = environment::currentClock();
   jaJavaGraph lcJavaGraph = agGraph;

   // Graph Conversion Java -> C++ //
   public_area::convertGraph(lcCppGraph,lcJavaGraph);

   // Circuit Detection //
   if (not findTopologicalOrdering(lcCppGraph,lcNodeS))
    send_inline_error(public_area::erGraphWithCircuit,"Wrapper.runLinearCostSolver");

   // Resolution Method Execution //
   switch (agMethod) {
    case 1: // Aggregation Method I //
     environment::nextLine();
     environment::inform("Solving Problem (\"Aggregation\" Algorithm, Decomposition I)...");
     lcNbIteration=clAggregationAlgo().run(lcCppGraph,lcDecomposeAlgo1,nil,true);
     break;

    case 2: // Aggregation Method II //
     environment::nextLine();
     environment::inform("Solving Problem (\"Aggregation\" Algorithm, Decomposition II)...");
     lcNbIteration=clAggregationAlgo().run(lcCppGraph,lcDecomposeAlgo2,nil,true);
     break;

    case 3: // Aggregation Method III //
     environment::nextLine();
     environment::inform("Solving Problem (\"Aggregation\" Algorithm, Decomposition III)...");
     lcNbIteration=clAggregationAlgo().run(lcCppGraph,lcDecomposeAlgo2,nil,true);
     break;

    case 4: // Dual Cost-Scaling Method //
     environment::nextLine();
     environment::inform("Solving Problem (Dual Cost-Scaling Algorithm, Linear Cost)...");
     lcNbIteration=clDualCostScalingAlgo().run(lcCppGraph);
     break;

    case 5: // Conforming Method I //
     environment::nextLine();
     environment::inform("Solving Problem (\"Conforming\" Algorithm, Linear Cost, Method I)...");
     lcNbIteration=clConformingAlgoI().run(lcCppGraph);
     break;

    case 6: // Conforming Method II //
     environment::nextLine();
     environment::inform("Solving Problem (\"Conforming\" Algorithm, Linear Cost, Method II)...");
     lcNbIteration=clConformingAlgoII().run(lcCppGraph);
     break;

    case 7: // Linear Programming Method I //
     environment::nextLine();
     environment::inform("Solving Problem (Linear Program Resolution, Modeling I)...");
     lcNbIteration=clLinearProgrammingAlgoI().run(lcCppGraph,tyLinearSolver::defaultSolver());
     break;

    case 8: // Linear Programming Method II //
     environment::nextLine();
     environment::inform("Solving Problem (Linear Program Resolution, Modeling II)...");
     lcNbIteration=clLinearProgrammingAlgoII().run(lcCppGraph,tyLinearSolver::defaultSolver());
     break;

    case 9: // Linear Programming Method III //
     environment::nextLine();
     environment::inform("Solving Problem (Linear Program Resolution, Modeling III)...");
     lcNbIteration=clLinearProgrammingAlgoIII().run(lcCppGraph,tyLinearSolver::defaultSolver());
     break;

    default: // Invalid Resolution Method //
     send_inline_error(public_area::erInvalidResolutionMethod,"Wrapper::runLinearCostSolver");
   }

   // Result Analysis //
   lcClock=environment::currentClock()-lcClock;
   environment::out("Result = ",false,true);

   if (lcNbIteration<0) environment::out("Inconsistent Constraints",true);
   else {
    environment::out("Consistent Constraints",true);
    environment::out("Iterations = ",false,true);
    environment::out(lcNbIteration,true);
    environment::out("Total Cost = ",false,true);
    environment::out(graphProblemMinCostTension::totalCost(lcCppGraph),true);
    environment::out("Resolution Time = ",false,true);
    environment::out(tyInteger(lcClock),true);
    public_area::assignTension(lcJavaGraph,lcCppGraph);
   }
  }

  java_native_catch;
 }
}

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

    erGraphWithCircuit.create("Solve Tension - The graph contains a circuit.");
    erInvalidDurationProperty.create("Solve Tension - The duration property isn't valid.");
    erInvalidExpectedDuration.create("Solve Tension - The expected duration value isn't valid.");
    erInvalidResolutionMethod.create("Solve Tension - The resolution method isn't valid.");
   }

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

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