#  Copyright (c) 1997-2018
#  Ewgenij Gawrilow, Michael Joswig (Technische Universitaet Berlin, Germany)
#  http://www.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 (as used by cplex & Co.)
# and convert it to a [[Polytope<Scalar>]] object.
#
# **WARNING** The property FEASIBLE is **NOT** computed upon creation.
# This is done to avoid possibly long computation times before the object becomes available to the caller.
# This is **NOT** in keeping with standard practice in polymake, but after, all, these are linear programs
# and not polytopes.
#
# @tparam Scalar coordinate type of the resulting polytope; default is [[Rational]].
# @param 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 Int nocheck Do not automatically compute [[FEASIBLE]] for the polytope (recommended for very large LPs)
# @return Polytope<Scalar>

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

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

   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;

   # FIXME #702: change the following kludge to 
   # $P->INEQUALITIES = new SparseMatrix<Scalar>($parser->Ineq)
   my @AoH = $parser->Ineq;
   my $Mineq = new SparseMatrix<Scalar>(scalar @AoH, $d);
   my $i=0;
   foreach my $ineq (@AoH) {
      keys %{$ineq};
      while (my ($k,$v) = each %{$ineq}) {
         $Mineq->[$i]->[$k] = $v;
      }
      $i++;
   }
   $P->INEQUALITIES = $Mineq;

   # FIXME #702: change the following kludge to 
   # $P->EQUATIONS = new SparseMatrix<Scalar>($parser->Eq)
   @AoH = $parser->Eq;
   my $MEq = new SparseMatrix<Scalar>(scalar @AoH, $d);
   $i=0;
   foreach my $eq (@AoH) {
      keys %{$eq};
      while (my ($k,$v) = each %{$eq}) {
         $MEq->[$i]->[$k] = $v;
      }
      $i++;
   }
   $P->EQUATIONS = $MEq;

   my $lp=new LinearProgram<Scalar>;
   $lp->description="Objective sense was ".($parser->objsense eq "+" ? "MAXIMIZE" : "MINIMIZE")."\n";

   # FIXME #702: change the following kludge to 
   # $P->LINEAR_OBJECTIVE = new SparseVector<Scalar>($parser->Obj)
   my $ObjHash = $parser->Obj;
   my $ObjVect = new SparseVector<Scalar>($d);
   keys %{$ObjHash};
   while (my ($k,$v) = each %{$ObjHash}) {
      $ObjVect->[$k] = $v;
   }
   $lp->LINEAR_OBJECTIVE=$ObjVect;

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


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