#  Copyright (c) 1997-2021
#  Ewgenij Gawrilow, Michael Joswig, and the polymake team
#  Technische Universität Berlin, Germany
#  https://polymake.org
#
#  This program is free software; you can redistribute it and/or modify it
#  under the terms of the GNU General Public License as published by the
#  Free Software Foundation; either version 2, or (at your option) any
#  later version: http://www.gnu.org/licenses/gpl.txt.
#
#  This program 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 General Public License for more details.
#-------------------------------------------------------------------------------


object Polytope {

# @category Optimization
# Linear program applied to the polytope
property LP : LinearProgram<Scalar> : multiple;

# @notest  Rule defined "in stock" - currently without use
rule LP.ABSTRACT_OBJECTIVE : VertexPerm.LP.ABSTRACT_OBJECTIVE, VertexPerm.PERMUTATION {
   $this->LP->ABSTRACT_OBJECTIVE=permuted($this->VertexPerm->LP->ABSTRACT_OBJECTIVE, $this->VertexPerm->PERMUTATION);
}

rule LP.DIRECTED_GRAPH.ADJACENCY, LP.MAXIMAL_VALUE, LP.MINIMAL_VALUE, LP.MAXIMAL_FACE, LP.MINIMAL_FACE \
     : LP.LINEAR_OBJECTIVE, GRAPH.ADJACENCY, VERTICES, FAR_FACE {
   $this->LP->DIRECTED_GRAPH->ADJACENCY=dgraph($this, $this->LP);
}
weight 2.50;
precondition : POINTED;
precondition : FEASIBLE;

rule LP.DIRECTED_GRAPH.ADJACENCY, LP.MAXIMAL_VALUE, LP.MINIMAL_VALUE, LP.MAXIMAL_FACE, LP.MINIMAL_FACE \
     : LP.ABSTRACT_OBJECTIVE, GRAPH.ADJACENCY, VERTICES {
   $this->LP->DIRECTED_GRAPH->ADJACENCY=dgraph($this, $this->LP);
}
weight 2.50;
precondition : POINTED;
precondition : FEASIBLE;

rule LP.MAXIMAL_FACE, LP.MAXIMAL_VALUE : LP.LINEAR_OBJECTIVE, VERTICES, GRAPH.ADJACENCY, FAR_FACE {
   pseudo_simplex($this, $this->LP, 1);
}
precondition : POINTED;
precondition : FEASIBLE;

rule LP.MINIMAL_FACE, LP.MINIMAL_VALUE : LP.LINEAR_OBJECTIVE, VERTICES, GRAPH.ADJACENCY, FAR_FACE {
   pseudo_simplex($this, $this->LP, 0);
}
precondition : POINTED;
precondition : FEASIBLE;

rule LP.MAXIMAL_VERTEX : VERTICES, FAR_FACE, LP.MAXIMAL_FACE {
   my $f=$this->LP->MAXIMAL_FACE-$this->FAR_FACE;
   if (@$f) {
      $this->LP->MAXIMAL_VERTEX=$this->VERTICES->[$f->[0]];
   }
}
weight 0.10;

rule LP.MINIMAL_VERTEX : VERTICES, FAR_FACE, LP.MINIMAL_FACE {
   my $f=$this->LP->MINIMAL_FACE-$this->FAR_FACE;
   if (@$f) {
      $this->LP->MINIMAL_VERTEX=$this->VERTICES->[$f->[0]];
   }
}
weight 0.10;

rule ONE_VERTEX : LP.MINIMAL_VERTEX | LP.MAXIMAL_VERTEX {
  $this->ONE_VERTEX=$this->LP->give("MINIMAL_VERTEX | MAXIMAL_VERTEX");
}
precondition : POINTED;
weight 0.10;

rule ONE_VERTEX, FEASIBLE : FACETS | INEQUALITIES, CONE_AMBIENT_DIM {
   my $lp=$this->add("LP", temporary, LINEAR_OBJECTIVE => new Vector<Scalar>($this->CONE_AMBIENT_DIM));
   $this->ONE_VERTEX=$lp->MINIMAL_VERTEX;
   $this->FEASIBLE=defined($lp->MINIMAL_VERTEX);
}
weight 3.20;

rule VALID_POINT = ONE_VERTEX;

}

object LinearProgram {

rule RANDOM_EDGE_EPL : DIRECTED_GRAPH.ADJACENCY {
   $this->RANDOM_EDGE_EPL=random_edge_epl($this->DIRECTED_GRAPH->ADJACENCY);
}

}


# @category Optimization
# Read a linear programming problem given in LP-Format or MILP-Format (as used by cplex & Co.)
# and convert it to a [[Polytope<Scalar>]] object with an added LP property or MILP property
#
# @tparam Scalar coordinate type of the resulting polytope; default is [[Rational]].
# @param [complete file] String file filename of a linear programming problem in LP-Format
# @param Vector testvec If present, after reading in each line of the LP it is checked whether testvec fulfills it
# @param String prefix If testvec is present, all variables in the LP file are assumed to have the form $prefix$i
# @option Bool nocheck Do not automatically compute [[FEASIBLE]] for the polytope (recommended for very large LPs)
# @option Bool create_lp Create an LP property regardless of the format of the given file. If the file has 
#                        MILP-Format, the created LP property will have an attachment INTEGER_VARIABLES
# @return Polytope<Scalar>

user_function lp2poly<Scalar=Rational>(String; Vector, String, {nocheck => false, create_lp => false}) {
   require LPparser;
   my ($filename, $testvec, $prefix, $options) = @_;

   my $parser = new LPparser($filename, $testvec, $prefix);

   my $P = new Polytope<Scalar>($parser->name);
   $P->description = "Linear problem converted from file " . $parser->LPfile . "\n";

   my $labels = new Array<String>($parser->X);
   my $d = $labels->size();

   # Specifying the dimension first is required for empty inequalities and coordinate labels
   $P->CONE_AMBIENT_DIM = $d;

   $P->COORDINATE_LABELS = $labels;

   push @{$parser->Ineq}, { cols => $d };
   push @{$parser->Eq}, { cols => $d };
   $parser->Obj->{_dim} = $d;

   $P->INEQUALITIES = new SparseMatrix<Scalar>($parser->Ineq);
   $P->EQUATIONS = new SparseMatrix<Scalar>($parser->Eq);
   
   my $lp = new LinearProgram<Scalar>($parser->lp_name // ());
   if (@{$parser->Int} && !$options->{create_lp}){
      $lp = new MixedIntegerLinearProgram<Scalar>($parser->lp_name // ());
      # Convert boolean Array of integer variables to a set for MILP
      my $integer_variables = new Array<Bool>($parser->Int);
      my $integer_set = new Set<Int>();
      for(my $ind = 0; $ind < $integer_variables->size(); $ind++){
         if($integer_variables->[$ind]){
            $integer_set->collect($ind + 1);
         }
      }
      $lp->INTEGER_VARIABLES = $integer_set;
   }
   
   $lp->description = "Objective sense was ".($parser->objsense eq "+" ? "MAXIMIZE" : "MINIMIZE")."\n";

   $lp->LINEAR_OBJECTIVE = new SparseVector<Scalar>($parser->Obj);

   if (@{$parser->Int} && $options->{create_lp}) {
      $lp->attach("INTEGER_VARIABLES", new Array<Bool>($parser->Int));
   }
   $P->commit unless($options->{nocheck});
   if (@{$parser->Int} && !$options->{create_lp}){
      $P->add("MILP", $lp);
   }
   else{
      $P->add("LP", $lp);
   }
   $P;
}


# Local Variables:
# mode: perl
# cperl-indent-level:3
# indent-tabs-mode:nil
# End:
