#  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 Cone {

   # A Cone defined by [[RAYS]] only gets a trivial [[LINEALITY_SPACE]] assigned.
   rule LINEALITY_SPACE : RAYS {
      $this->LINEALITY_SPACE=new Matrix<Scalar>(0, $this->RAYS->cols);
   }
   weight 0.10;

   # A Cone defined by [[FACETS]] only gets a trivial [[LINEAR_SPAN]] assigned.
   rule LINEAR_SPAN : FACETS {
      $this->LINEAR_SPAN=new Matrix<Scalar>(0, $this->FACETS->cols);
   }
   weight 0.10;

   # Defining a cone or polytope with one of the linear span/lineality properties only is not allowed
   # For the non-reduced properties [[INPUT_LINEALITY]] or [[EQUATIONS]] we require that [[INPUT_RAYS]] or [[INEQUALITIES]] are defined, resp.
   #
   # We specifically do not want the same kind of rules for the canonical versions [[LINEALITY_SPACE]] and [[AFFINE_HULL]]:  There are
   # too many different cases to check and we do not want an unnnessary slow-down.
   #
   # Here are reasons for the set-up chosen:
   # * To define an initial rule that silently adds the vertex/facet property we would have to distingiush between cones and polytopes:
   #   the latter always need one entry in [[POINTS]]/[[VERTICES]] or [[INEQUALITIES]]/[[FACETS]] that represents the 
   #   pointed part (i.e. the restriction to x0>=0 in the homogenization.
   # * Another problem of silently adding this would be clients that define a new polytope and 
   #   e.g. write [[VERTICES]]/[[LINEALITY_SPACE]] and [[AFFINE_HULL]], but not [[FACETS]]. 
   # * An initial rule checking for [[AFFINE_HULL]] without [[FACETS]] would erroneously make [[FACETS]] empty.

   rule initial : {
     croak("\ninvalid input: to define a linear or affine space you have to specify INPUT_RAYS/POINTS explicitely\nThis should be an empty matrix for an object of type Cone and a single point for an object of type Polytope");

   }
   precondition : exists(INPUT_LINEALITY);
   precondition : !exists(INPUT_RAYS);

   rule initial : {
     croak("\ninvalid input: to define a linear or affine space you have to specify INEQUALITIES explicitely\nThis should be an empty matrix for an object of type Cone and the single inequality [1,0,0,...] for an object of type Polytope");

   }
   precondition : exists(EQUATIONS);
   precondition : !exists(INEQUALITIES);

   # check for zero rows in input properties.
   rule initial : {
      foreach my $prop (qw(INPUT_RAYS INEQUALITIES INPUT_LINEALITY EQUATIONS)){
         if(defined(my $m = $this->lookup($prop))){
            foreach my $r (@{$m}){
               if(is_zero( $r )){ croak("\ninvalid input: zero vector is not a valid entry for ",$prop); }
            }
         }
      }
   }
   precondition : exists(INPUT_RAYS | INEQUALITIES | INPUT_LINEALITY | EQUATIONS);

   rule CONE_AMBIENT_DIM : RAYS | FACETS | LINEAR_SPAN | LINEALITY_SPACE | INPUT_RAYS | INPUT_LINEALITY | INEQUALITIES | EQUATIONS {
      my $d=0;
      foreach (qw(RAYS FACETS LINEAR_SPAN LINEALITY_SPACE INPUT_RAYS INPUT_LINEALITY INEQUALITIES EQUATIONS)) {
         my $m=$this->lookup($_);
         last if defined($m) && ($d=$m->cols) > 0;
      }
      $this->CONE_AMBIENT_DIM=$d;
   }
   weight 0.1;

}

# bundled:cdd overloads this for Rational and Float as that is faster
function H_input_is_feasible(Polytope) {
   to_input_feasible($_[0]);
}

object Polytope {

   # A Polytope defined in terms of [[INEQUALITIES]] and/or [[EQUATIONS]] is checked for [[FEASIBLE]] right away.
   rule FEASIBLE : INEQUALITIES | EQUATIONS {
      $this->FEASIBLE = H_input_is_feasible($this);
   }

   # A Polytope defined by a non-empty set of [[VERTICES]] or [[POINTS]] is always [[FEASIBLE]].
   rule FEASIBLE : VERTICES | POINTS {
      $this->FEASIBLE = $this->give("VERTICES | POINTS")->rows > 0;
   }
   weight 0.1;

   rule FEASIBLE : { $this->FEASIBLE=1 }
   precondition : defined(ONE_VERTEX | VALID_POINT);
   weight 0.1;

   # A Polytope defined by a non-empty set of [[FACETS]] is [[FEASIBLE]].
   rule FEASIBLE : FACETS {
      $this->FEASIBLE = $this->FACETS->rows > 0;
   }
   weight 0.1;

}

object Cone {

   # Sanity checks for all initial properties related to coordinates

   # first those for cones and polytopes
   # the non-input properties might also be empty
   rule initial: CONE_AMBIENT_DIM {
      my $dim=$this->CONE_AMBIENT_DIM;
      my $M;
      foreach (qw(RAYS LINEALITY_SPACE FACETS LINEAR_SPAN INPUT_RAYS INPUT_LINEALITY INEQUALITIES EQUATIONS)) {
         if (defined ($M = $this->lookup($_))) {
            if ($M->cols != $dim) {
               if ($M->rows==0 && $M->cols==0) {
                  $this->take($_, new Matrix<Scalar>(0, $dim));
               } else {
                  die "dimension mismatch between $_ and CONE_AMBIENT_DIM\n";
               }
            }
         }
      }
   }
   precondition : exists(RAYS | LINEALITY_SPACE | FACETS | LINEAR_SPAN | INPUT_RAYS | INPUT_LINEALITY | INEQUALITIES | EQUATIONS);

   rule initial: CONE_AMBIENT_DIM {
      my $dim=$this->CONE_AMBIENT_DIM;
      my $A;
      foreach (qw(COORDINATE_LABELS)) {
         if (defined ($A = $this->lookup($_)) && $A->size != $dim) {
            die "size mismatch between $_ and CONE_AMBIENT_DIM\n";
         }
      }
   }
   precondition : exists(COORDINATE_LABELS);
}


object Polytope {

   # sanity checks which apply only to polytopes

   rule initial: CONE_AMBIENT_DIM {
      my $dim=$this->CONE_AMBIENT_DIM;
      my $V;
      foreach (qw(VALID_POINT ONE_VERTEX)) {
         if (defined ($V = $this->lookup($_)) && $V->dim != $dim) {
            die "dimension mismatch between $_ and CONE_AMBIENT_DIM\n";
         }
      }
   }
   precondition : exists(VALID_POINT | ONE_VERTEX);


   # Unique representation of infeasible polytopes

   rule FACETS, AFFINE_HULL, CONE_DIM : CONE_AMBIENT_DIM {
      my $dim=$this->CONE_AMBIENT_DIM;
      my $empty=new Matrix<Scalar>(0, $dim);
      $this->FACETS = $empty;
      $this->AFFINE_HULL = $empty;
      $this->CONE_DIM = 0;
   }
   precondition : !FEASIBLE;
   weight 0.10;

   rule VERTICES, LINEALITY_SPACE, CONE_DIM : CONE_AMBIENT_DIM {
      my $dim=$this->CONE_AMBIENT_DIM;
      my $empty=new Matrix<Scalar>(0, $dim);
      $this->VERTICES = $empty;
      $this->LINEALITY_SPACE = $empty;
      $this->CONE_DIM = 0;
   }
   precondition : !FEASIBLE;
   weight 0.10;

}

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