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

rule FAN_AMBIENT_DIM : {
  my $d=0;
  foreach (qw(RAYS INPUT_RAYS LINEALITY_SPACE INPUT_LINEALITY FACET_NORMALS LINEAR_SPAN_NORMALS)) {
    my $M;
    if (defined ($M=$this->lookup($_)) && $M->cols()>0) {
      $d=$M->cols();
      last;
    }
  }
  $this->FAN_AMBIENT_DIM=$d;
}
precondition : defined(RAYS | INPUT_RAYS | LINEALITY_SPACE | INPUT_LINEALITY | FACET_NORMALS | LINEAR_SPAN_NORMALS);
weight 0.10;

rule N_RAYS : RAYS {
   $this->N_RAYS=$this->RAYS->rows;
}
weight 0.1;

rule N_INPUT_RAYS : INPUT_RAYS {
   $this->N_INPUT_RAYS=$this->INPUT_RAYS->rows;
}
weight 0.1;

rule N_MAXIMAL_CONES : MAXIMAL_CONES {
   $this->N_MAXIMAL_CONES=$this->MAXIMAL_CONES->rows;
}
weight 0.1;

rule N_CONES : CONES {
   $this->N_CONES=$this->CONES->rows;
}
weight 0.1;

rule N_FACET_NORMALS : FACET_NORMALS {
   $this->N_FACET_NORMALS=$this->FACET_NORMALS->rows;
}
weight 0.1;

rule COMBINATORIAL_DIM : FAN_DIM, LINEALITY_DIM {
   $this->COMBINATORIAL_DIM = $this->FAN_DIM - $this->LINEALITY_DIM-1;
}
weight 0.1;

rule FAN_DIM : COMBINATORIAL_DIM, LINEALITY_DIM {
   $this->FAN_DIM = $this->COMBINATORIAL_DIM + $this->LINEALITY_DIM+1;
}
weight 0.1;

#If we know beforehand that the fan is pure, we don't have to compute individual cone dimensions.
rule FAN_DIM : RAYS, MAXIMAL_CONES, LINEALITY_SPACE {
   if ($this->MAXIMAL_CONES->rows() == 0) {
      $this->FAN_DIM = $this->LINEALITY_SPACE->rows();
   }
   else {
      $this->FAN_DIM = (new polytope::Cone(RAYS=>$this->RAYS->minor($this->MAXIMAL_CONES->row(0),All),LINEALITY_SPACE=>$this->LINEALITY_SPACE))->CONE_DIM;
   }
}
precondition : PURE;
weight 2.10;

rule LINEALITY_DIM : COMBINATORIAL_DIM, FAN_DIM {
   $this->LINEALITY_DIM = $this->FAN_DIM - $this->COMBINATORIAL_DIM-1;
}
weight 0.1;

rule LINEALITY_DIM : LINEALITY_SPACE {
   $this->LINEALITY_DIM=$this->LINEALITY_SPACE->rows;
}
weight 0.1;

rule POINTED : LINEALITY_DIM {
   $this->POINTED= $this->LINEALITY_DIM==0;
}
weight 0.1;


rule FULL_DIM : FAN_DIM, FAN_AMBIENT_DIM {
  $this->FULL_DIM= ($this->FAN_DIM==$this->FAN_AMBIENT_DIM);
}
weight 0.1;


# If no [[LINEALITY_SPACE]] is given, it is assumed to be trivial.
# Note: as there is only one target this rule is only called if LINEALITY_SPACE has not been written by the user, but RAYS have been.
rule LINEALITY_SPACE : RAYS {
   $this->LINEALITY_SPACE=new Matrix<Scalar>(0, $this->RAYS->cols);
}
weight 0.10;

rule LINEALITY_SPACE : ORTH_LINEALITY_SPACE {
    $this->LINEALITY_SPACE = null_space($this->ORTH_LINEALITY_SPACE);
}
weight 0.10;

rule ORTH_LINEALITY_SPACE : LINEALITY_SPACE {
    $this->ORTH_LINEALITY_SPACE = null_space($this->LINEALITY_SPACE);
}
weight 0.10;

rule MAXIMAL_CONES_COMBINATORIAL_DIMS : RAYS, MAXIMAL_CONES, LINEALITY_SPACE {
   my @dims = map { (new polytope::Cone(RAYS=>$this->RAYS->minor($_,All),LINEALITY_SPACE=>$this->LINEALITY_SPACE))->COMBINATORIAL_DIM } @{$this->MAXIMAL_CONES};
   $this->MAXIMAL_CONES_COMBINATORIAL_DIMS=\@dims;
}
weight 3.10;

rule MAXIMAL_CONES_COMBINATORIAL_DIMS : COMBINATORIAL_DIM, N_MAXIMAL_CONES {
   my @dims = map { $this->COMBINATORIAL_DIM } 1..$this->N_MAXIMAL_CONES;
   $this->MAXIMAL_CONES_COMBINATORIAL_DIMS(temporary)=\@dims;
}
precondition : PURE;
weight 1.10;

rule COMBINATORIAL_DIM, PURE : MAXIMAL_CONES_COMBINATORIAL_DIMS {
   if ($this->MAXIMAL_CONES_COMBINATORIAL_DIMS->size == 0) {
      $this->PURE=1;
      $this->COMBINATORIAL_DIM=-2;
      return;
   }

   my $dim=$this->MAXIMAL_CONES_COMBINATORIAL_DIMS->[0];
   my $pure=1;
   foreach (@{$this->MAXIMAL_CONES_COMBINATORIAL_DIMS})  {
      if ($_>$dim) {
         $dim =$_;
	 $pure=0;
      } elsif ($_<$dim) {
          $pure=0;
      }
   }
   $this->COMBINATORIAL_DIM=$dim;
   $this->PURE=$pure;
}
weight 1.10;


rule MAXIMAL_CONES_COMBINATORIAL_DIMS : MAXIMAL_CONES_INCIDENCES, N_MAXIMAL_CONES {
  my $mc=$this->N_MAXIMAL_CONES;
  my $inc=$this->MAXIMAL_CONES_INCIDENCES;
  my $dims=new Array<Int>($mc);
  foreach my $i (0..$mc-1) {
    my $m=new IncidenceMatrix($inc->[$i]);
    $m->squeeze_cols;
    $dims->[$i]=$m->cols==0?0:polytope::dim_from_incidence($m);
  }
  $this->MAXIMAL_CONES_COMBINATORIAL_DIMS=$dims;
}
weight 2.10;

# FIXME no testcases with non-pointed cones
rule MAXIMAL_CONES_INCIDENCES : RAYS, MAXIMAL_CONES, FACET_NORMALS, MAXIMAL_CONES_FACETS, N_FACET_NORMALS {
  my $cmp=$this->prepare_computations;
  my $mc=$this->MAXIMAL_CONES;
  my $r=$this->RAYS;
  my $fn=$this->FACET_NORMALS;
  my $nfn=$this->N_FACET_NORMALS;
  my $mcf=$this->MAXIMAL_CONES_FACETS;

  my $n_mc=$mc->rows;

  my $inc=polytope::incidence_matrix($fn,$r);

  my $mci=new Array<IncidenceMatrix>($n_mc);
  foreach my $i (0..$n_mc-1) {
    $mci->[$i]=$inc->minor(indices($mcf->[$i]),All);
    my $sh=new Set<Int>;
    foreach (@{sequence(0,$mc->cols)-$mc->[$i]}) {
      $mci->[$i]->col($_)=$sh;
    }
  }
  $this->MAXIMAL_CONES_INCIDENCES(temporary)=$mci;
}

rule HASSE_DIAGRAM.ADJACENCY, HASSE_DIAGRAM.DECORATION, HASSE_DIAGRAM.INVERSE_RANK_MAP, HASSE_DIAGRAM.TOP_NODE, HASSE_DIAGRAM.BOTTOM_NODE : \
     MAXIMAL_CONES, MAXIMAL_CONES_INCIDENCES, MAXIMAL_CONES_COMBINATORIAL_DIMS, COMBINATORIAL_DIM {
   if ($this->lookup("COMPLETE")) {
      $this->HASSE_DIAGRAM=hasse_diagram($this,1,1);
   } elsif ($this->lookup("PURE")) {
      $this->HASSE_DIAGRAM=hasse_diagram($this,1,0);
   } else {
      $this->HASSE_DIAGRAM=hasse_diagram($this);
   }
}
weight 6.50;

rule MAXIMAL_CONES : HASSE_DIAGRAM.ADJACENCY, HASSE_DIAGRAM.DECORATION, HASSE_DIAGRAM.TOP_NODE {
   my @cones = map { $this->HASSE_DIAGRAM->DECORATION->[$_]->face } @{$this->HASSE_DIAGRAM->ADJACENCY->in_adjacent_nodes($this->HASSE_DIAGRAM->TOP_NODE)};
   $this->MAXIMAL_CONES = new IncidenceMatrix(\@cones);
}
weight 1.10;

rule CONES : HASSE_DIAGRAM { #.DECORATION, HASSE_DIAGRAM.INVERSE_RANK_MAP, HASSE_DIAGRAM.TOP_NODE, HASSE_DIAGRAM.BOTTOM_NODE, N_RAYS {
    my $d=$this->HASSE_DIAGRAM->rank;
    $this->CONES(temporary)=[ map {
                    $this->HASSE_DIAGRAM->cones_of_dim($_)
                       } 1..$d-1 ];
}
weight 1.20;


rule F_VECTOR : HASSE_DIAGRAM.DECORATION, HASSE_DIAGRAM.INVERSE_RANK_MAP, HASSE_DIAGRAM.TOP_NODE, HASSE_DIAGRAM.BOTTOM_NODE {
   my $rk = $this->HASSE_DIAGRAM->rank();
   my @f = map { scalar(@{$this->HASSE_DIAGRAM->nodes_of_rank($_)}) } (1 .. $rk-1);
   $this->F_VECTOR= \@f;
}
weight 0.10;

rule COMPLETE, REGULAR :  {
   $this->REGULAR = false;
   $this->COMPLETE = false;
}
precondition : !DUAL_GRAPH.CONNECTED;
weight 0.1;

rule COMPLETE, REGULAR :  {
   $this->REGULAR = false;
   $this->COMPLETE = false;
}
precondition : !FULL_DIM;
weight 0.1;

rule REGULAR : {
   $this->REGULAR = false;
}
precondition : !COMPLETE;
weight 0.1;

rule REGULAR : {
   $this->REGULAR = false;
}
precondition : !PSEUDO_REGULAR;
weight 0.1;

rule REGULAR = PSEUDO_REGULAR;
precondition : COMPLETE;

rule COMPLETE : {
   $this->COMPLETE = true;
}
precondition : REGULAR;
weight 0.1;

rule PSEUDO_REGULAR : RAYS, MAXIMAL_CONES {
   $this->PSEUDO_REGULAR = pseudo_regular($this)->rows == $this->RAYS->rows;
}
weight 4.10;

rule PSEUDO_REGULAR = REGULAR;
precondition : COMPLETE;

rule INTERSECTION_COMPLEX.FACETS : MAXIMAL_CONES {
  $this->INTERSECTION_COMPLEX->FACETS(temporary)=new Array<Set<Int> >(rows($this->MAXIMAL_CONES));
}
weight 1.10;

rule COMPLETE : FULL_DIM, INTERSECTION_COMPLEX.SPHERE {
   $this->COMPLETE=$this->FULL_DIM && $this->INTERSECTION_COMPLEX->SPHERE;
}
precondition : SIMPLICIAL;
weight 0.1;

rule COMPLETE : MAXIMAL_CONES_FACETS, DUAL_GRAPH.NODE_DEGREES, N_MAXIMAL_CONES {
   for (my $i=0; $i<$this->N_MAXIMAL_CONES; ++$i)  {
      if ($this->MAXIMAL_CONES_FACETS->row($i)->size!=$this->DUAL_GRAPH->NODE_DEGREES->[$i]) {
         $this->COMPLETE=0;
         return;
      }
   }
   $this->COMPLETE=1;
}
precondition : DUAL_GRAPH.CONNECTED;
weight 1.10;

rule GRAPH.ADJACENCY : MAXIMAL_CONES {
   my $n=$this->MAXIMAL_CONES->cols;
   my $G = new GraphAdjacency($n);
   for (my $i=0; $i<$n; ++$i)  {
      foreach (@{$this->MAXIMAL_CONES->col($i)}) {
        foreach (@{$this->MAXIMAL_CONES->row($_)}) {
          if ($i!=$_) {
	    $G->edge($i,$_);
	  }
	}
     }
   }
   $this->GRAPH->ADJACENCY=$G;
}
precondition : SIMPLICIAL;
weight 3.10;

rule GRAPH.ADJACENCY : HASSE_DIAGRAM.ADJACENCY, HASSE_DIAGRAM.DECORATION, HASSE_DIAGRAM.INVERSE_RANK_MAP, HASSE_DIAGRAM.TOP_NODE, HASSE_DIAGRAM.BOTTOM_NODE {
   $this->GRAPH->ADJACENCY=vertex_graph($this->HASSE_DIAGRAM);
}
precondition : FAN_DIM { $this->FAN_DIM>=2 };
weight 1.20;

rule DUAL_GRAPH.ADJACENCY : HASSE_DIAGRAM.ADJACENCY, HASSE_DIAGRAM.DECORATION, HASSE_DIAGRAM.INVERSE_RANK_MAP, HASSE_DIAGRAM.TOP_NODE, HASSE_DIAGRAM.BOTTOM_NODE {
   $this->DUAL_GRAPH->ADJACENCY=facet_graph($this->HASSE_DIAGRAM);
}
precondition : PURE;
weight 1.20;

rule FAN_DIM = FAN_AMBIENT_DIM;
precondition : FULL_DIM;

rule FULL_DIM, DUAL_GRAPH.CONNECTED : {
   $this->FULL_DIM=1;
   $this->DUAL_GRAPH->CONNECTED=1;
}
precondition : COMPLETE;
weight 0.1;

rule SIMPLICIAL : MAXIMAL_CONES, COMBINATORIAL_DIM {
   my $d=$this->COMBINATORIAL_DIM+1;
   my $simplicial=1;
   foreach (@{$this->MAXIMAL_CONES}) {
      $simplicial=0, last if ($_->size!=$d);
   }
   $this->SIMPLICIAL=$simplicial;
}
precondition : PURE;
weight 1.10;

rule SIMPLICIAL : MAXIMAL_CONES_COMBINATORIAL_DIMS, MAXIMAL_CONES {
   my $i=0;
   my $simplicial=1;
   foreach (@{$this->MAXIMAL_CONES}) {
      my $d=$this->MAXIMAL_CONES_COMBINATORIAL_DIMS->[$i++];
      $simplicial=0, last if ($_->size!=$d+1);
   }
   $this->SIMPLICIAL=$simplicial;
}
weight 1.30;


rule RAYS, MAXIMAL_CONES, LINEALITY_SPACE : INPUT_RAYS, INPUT_CONES, FAN_AMBIENT_DIM {
  remove_redundancies($this);
}
weight 3.10;
incurs RaysPerm;


rule FACET_NORMALS, MAXIMAL_CONES_FACETS, LINEAR_SPAN_NORMALS, MAXIMAL_CONES_LINEAR_SPAN_NORMALS : \
       RAYS, MAXIMAL_CONES, FAN_AMBIENT_DIM, LINEALITY_SPACE | INPUT_LINEALITY {
   raysToFacetNormals($this);
}
weight 4.10;

rule RAYS, MAXIMAL_CONES, LINEALITY_SPACE : FACET_NORMALS, MAXIMAL_CONES_FACETS, FAN_AMBIENT_DIM {
   facetsToRays($this);
}
weight 4.10;
incurs RaysPerm;

}


# @category Producing a fan
# Orthogonally project a fan to a coordinate subspace such that redundant
# columns are omitted, i.e., the affine hull of the support of the projection
# is full-dimensional, without changing the combinatorial type.
#
# @param PolyhedralFan P
# @option Bool no_labels Do not copy [[VERTEX_LABELS]] to the projection. default: 0
# @return PolyhedralFan
#
# @example x and y axis in 3-space
# > $f = new PolyhedralFan(INPUT_RAYS=>[[1,0,0],[0,1,0]], INPUT_CONES=>[[0],[1]]);
# > $pf = project_full($f);
# > print $pf->RAYS;
# | 1 0
# | 0 1
# > print $pf->MAXIMAL_CONES;
# | {0}
# | {1}
user_function project_full<Scalar>(PolyhedralFan<Scalar>; {no_labels=>0}) {
   project_full_fan_impl(@_);
}


# @category Geometry
# The facets of a polyhedron that has the fan //P// as its [[normal_fan|normal fan]], or the empty matrix 
# if no such polyhedron exists.
# @param PolyhedralFan P
# @return Matrix<Scalar>
# @example The face fan of the cross polytope is the normal fan of the cube; thus, to obtain the inequalities of the
# 3-cube from the 3-dimensional cross polytope we can do this:
# > $c = generating_polyhedron_facets(face_fan(cross(3)));
user_function generating_polyhedron_facets<Scalar>(PolyhedralFan<Scalar>) { 
   return pseudo_regular(@_);
}



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