#  Copyright (c) 1997-2010
#  Ewgenij Gawrilow, Michael Joswig (Technische Universitaet Darmstadt, Germany)
#  http://www.polymake.de
#
#  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.
#-------------------------------------------------------------------------------
#  $Project: polymake $$Id: common.rules 9539 2010-03-08 00:43:21Z gawrilow $

function far_points(Matrix) : c++ (include => [ 'linalg.h' ]);

object Polytope {

rule VERTEX_LABELS : N_VERTICES {
   my @labels = (0..$this->N_VERTICES-1);
   $this->VERTEX_LABELS="@labels";
}
weight 4.10;

rule FACET_LABELS : VERTEX_LABELS, VERTICES_IN_FACETS {
   $this->FACET_LABELS=induced_labels($this->VERTEX_LABELS, $this->VERTICES_IN_FACETS);
}

rule AMBIENT_DIM : VERTICES | POINTS {
   $this->AMBIENT_DIM=($this->VERTICES | POINTS)->cols-1;
}
weight 0.10;

rule AMBIENT_DIM : INEQUALITIES | EQUATIONS | FACETS {
   $this->AMBIENT_DIM=($this->INEQUALITIES | EQUATIONS | FACETS)->cols-1;
}
weight 0.10;

rule DIM : AMBIENT_DIM, AFFINE_HULL {
   $this->DIM= $this->AMBIENT_DIM - $this->AFFINE_HULL->rows;
}
precondition : FEASIBLE;
weight 0.10;

rule AFFINE_HULL : {
   $this->AFFINE_HULL=new Matrix<Scalar>;
}
precondition : DIM, AMBIENT_DIM {
   $this->DIM==$this->AMBIENT_DIM
}
weight 0.10;

rule DIM : VERTICES | POINTS {
   my $cmp=$this->prepare_computations;
   $this->DIM=rank($this->VERTICES | POINTS)-1;
}
weight 1.10;

rule POINTED : { $this->POINTED=1; }
precondition : N_VERTICES | N_POINTS;
weight 0.10;

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

rule POINTED : LINEALITY_DIM {
   $this->POINTED=!$this->LINEALITY_DIM;
}
weight 0.10;

rule INEQUALITIES : EQUATIONS {
   $this->INEQUALITIES(temporary)=new Matrix<Scalar>(0,$this->EQUATIONS->cols);
}

rule LINEALITY_SPACE : FACETS | INEQUALITIES {
   my $F;
   if (defined (my $AH=$this->lookup("AFFINE_HULL | EQUATIONS"))) {
      $F=($this->FACETS | INEQUALITIES) / $AH;
   } else {
      $F=$this->FACETS | INEQUALITIES;
   }
   my $cmp=$this->prepare_computations;
   $this->LINEALITY_SPACE=common::lineality_space($F);
}
precondition : AMBIENT_DIM {
   $this->AMBIENT_DIM>=0;
}
weight 1.10;

rule LINEALITY_DIM, LINEALITY_SPACE : {
   $this->LINEALITY_DIM=0;
   $this->LINEALITY_SPACE=new Matrix<Scalar>;
}
precondition : POINTED;
weight 0.10;

rule ONE_VERTEX : VERTICES, N_VERTICES {
  $this->ONE_VERTEX= $this->N_VERTICES ? $this->VERTICES->[0] : undef;
}
weight 0.10;

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

rule FEASIBLE : { $this->FEASIBLE=1 }
precondition : N_VERTICES | N_POINTS;
weight 0.10;

rule N_POINTS : POINTS {
   $this->N_POINTS=$this->POINTS->rows;
}
weight 0.10;

rule N_VERTICES : VERTICES {
   $this->N_VERTICES=$this->VERTICES->rows;
}
weight 0.10;

rule N_VERTICES : VERTICES_IN_FACETS {
   $this->N_VERTICES=$this->VERTICES_IN_FACETS->cols;
}
weight 0.10;

rule N_INEQUALITIES : INEQUALITIES {
   $this->N_INEQUALITIES=$this->INEQUALITIES->rows;
}
weight 0.10;

rule N_FACETS : FACETS {
   $this->N_FACETS=$this->FACETS->rows;
}
weight 0.10;

rule N_FACETS : VERTICES_IN_FACETS {
   $this->N_FACETS=$this->VERTICES_IN_FACETS->rows;
}
weight 0.10;

rule DIM, N_VERTICES, N_FACETS : {
   $this->DIM=-1;
   $this->N_VERTICES=0;
   $this->N_FACETS=0;
}
precondition : !FEASIBLE;
weight 0.10;

rule GRAPH.NODE_LABELS = VERTEX_LABELS;

rule DUAL_GRAPH.NODE_LABELS = FACET_LABELS;

# @category Backward compatibility
# The number of edges of the [[GRAPH]]
user_method N_EDGES = GRAPH.N_EDGES;

# @category Combinatorics
# The number of ridges (faces of codimension 2) of the polytope
# equals the number of edges of the [[DUAL_GRAPH]]
user_method N_RIDGES = DUAL_GRAPH.N_EDGES;

# @category Combinatorics
# Vertex degrees of the polytope
# @return Vector<Int> - in the same order as [[VERTICES]]
user_method VERTEX_DEGREES = GRAPH.NODE_DEGREES;

# @category Combinatorics
# Facet degrees of the polytope.
# The __degree__ of a facet is the number of adjacent facets.
# @return Vector<Int> - in the same order as [[FACETS]]
user_method FACET_DEGREES = DUAL_GRAPH.NODE_DEGREES;

# @category Backward compatibility
# The diameter of the [[GRAPH]] of the polytope
user_method DIAMETER = GRAPH.DIAMETER;

# @category Backward compatibility
# The diameter of the [[DUAL_GRAPH]]
user_method DUAL_DIAMETER = DUAL_GRAPH.DIAMETER;

# @category Backward compatibility
# True if the [[GRAPH]] contains no triangle
user_method TRIANGLE_FREE = GRAPH.TRIANGLE_FREE;

# @category Backward compatibility
# True if the [[DUAL_GRAPH]] contains no triangle
user_method DUAL_TRIANGLE_FREE = DUAL_GRAPH.TRIANGLE_FREE;

# @category Combinatorics
# True if the [[GRAPH]] is bipartite
user_method EVEN = GRAPH.BIPARTITE;

# @category Combinatorics
# True if the [[DUAL_GRAPH]] is bipartite
user_method DUAL_EVEN = DUAL_GRAPH.BIPARTITE;

# @category Topology
# Difference of the black and white nodes if the [[GRAPH]] is [[BIPARTITE]].
# Otherwise -1.
user_method GRAPH_SIGNATURE = GRAPH.SIGNATURE;

# @category Topology
# Difference of the black and white nodes if the [[DUAL_GRAPH]] is [[BIPARTITE]].
# Otherwise -1.
user_method DUAL_GRAPH_SIGNATURE = DUAL_GRAPH.SIGNATURE;

# @category Combinatorics
# Connectivity of the [[GRAPH]]
# this is the minimum number of nodes that have to be removed from the [[GRAPH]] to make it disconnected
user_method CONNECTIVITY = GRAPH.CONNECTIVITY;

# @category Combinatorics
# Connectivity of the [[DUAL_GRAPH]]
# this is the minimum number of nodes that have to be removed from the [[DUAL_GRAPH]] to make it disconnected
user_method DUAL_CONNECTIVITY = DUAL_GRAPH.CONNECTIVITY;


rule N_VERTEX_FACET_INC : VERTICES_IN_FACETS {
   my $n=0;
   foreach (@{$this->VERTICES_IN_FACETS}) { $n+=@$_ }
   $this->N_VERTEX_FACET_INC=$n;
}
weight 1.10;

rule N_VERTEX_FACET_INC : F2_VECTOR {
   $this->N_VERTEX_FACET_INC=$this->F2_VECTOR->[0]->[-1];
}
weight 0.10;

rule FACETS_THRU_VERTICES : VERTICES_IN_FACETS {
   $this->FACETS_THRU_VERTICES(temporary)=transpose($this->VERTICES_IN_FACETS);
}
weight 1.10;

rule INEQUALITIES_THRU_VERTICES : VERTICES_IN_INEQUALITIES {
   $this->INEQUALITIES_THRU_VERTICES(temporary)=transpose($this->VERTICES_IN_INEQUALITIES);
}
weight 1.10;

rule SIMPLICIAL : { $this->SIMPLICIAL=1 }
precondition : ESSENTIALLY_GENERIC;
weight 0.10;

rule SIMPLICIAL : DIM, VERTICES_IN_FACETS {
   foreach (@{$this->VERTICES_IN_FACETS}) {
      if (@$_ != $this->DIM) {
	 $this->SIMPLICIAL=0;
	 return;
      }
   }
   $this->SIMPLICIAL=1;
}
weight 1.10;

rule SIMPLE : DIM, VERTICES_IN_FACETS {
   foreach (@{transpose($this->VERTICES_IN_FACETS)}) {
      if (@$_ != $this->DIM) {
	 $this->SIMPLE=0;
	 return;
      }
   }
   $this->SIMPLE=1;
}
weight 1.10;

rule F2_VECTOR : HASSE_DIAGRAM {
   $this->F2_VECTOR=f2_vector($this->HASSE_DIAGRAM);
}
weight 3.10;

rule F_VECTOR : F2_VECTOR {
   $this->F_VECTOR=$this->F2_VECTOR->diagonal;
}
weight 0.10;

rule F_VECTOR : HASSE_DIAGRAM {
   my @d=@{$this->HASSE_DIAGRAM->DIMS};
   my @f=map { $d[$_]-$d[$_-1] } 1..$#d;
   $this->F_VECTOR= $this->HASSE_DIAGRAM->top_node ? \@f : [ reverse @f ];
}
weight 0.10;

# simplex case
rule SIMPLICITY, SIMPLICIALITY : DIM {
   $this->SIMPLICIALITY=$this->SIMPLICITY=$this->DIM;
}
precondition : DIM, N_VERTICES {
   $this->DIM >= 0 && $this->DIM+1 == $this->N_VERTICES
}
weight 0.10;

rule SIMPLICIALITY : F_VECTOR, F2_VECTOR {
   # a polytope is k-simplicial if each k-face is a simplex,
   # i.e. f_{0,k} = (k+1) * f_k
   my $k=0;
   my $d=$#{$this->F_VECTOR};
   ++$k while ($k<=$d && $this->F2_VECTOR->(0,$k) == ($k+1)*$this->F_VECTOR->[$k]);
   $this->SIMPLICIALITY=$k-1;
}
precondition : DIM, N_VERTICES {
   $this->DIM+1 != $this->N_VERTICES
}

rule SIMPLICITY : F_VECTOR, F2_VECTOR {
   # dual notion to simpliciality,
   # i.e. f_{k,d-1} = (k+1) * f_k
   my $k=0;
   my $d=$#{$this->F_VECTOR};
   ++$k while ($k<=$d && $this->F2_VECTOR->($d-$k,$d) == ($k+1)*$this->F_VECTOR->[$d-$k]);
   $this->SIMPLICITY=$k-1;
}
precondition : DIM, N_VERTICES {
   $this->DIM+1 != $this->N_VERTICES
}

rule FACE_SIMPLICITY : DIM {
   $this->FACE_SIMPLICITY=$this->DIM;
}
precondition : SIMPLE;

rule FACE_SIMPLICITY : F2_VECTOR {
   # a polytope is k-face-simple if all k-faces are simple
   # i.e. 2 f_{1,k} = f_{0,1,k} = k * f_{0,k}
   my $k=1;
   my $dim=$this->F2_VECTOR->rows-1;
   ++$k while ($k<=$dim && 2 * $this->F2_VECTOR->($k,1) == $k * $this->F2_VECTOR->($k,0));
   $this->FACE_SIMPLICITY=$k-1;
}
precondition : !SIMPLE;

rule CUBICALITY : F2_VECTOR {
   # a polytope is k-cubical if its graph is bipartite and all j-faces have 2^j vertices for all j<=k
   # i.e. f_{j,0} = 2^j * f_j
   my $k=1;
   my $k2=2; # 2^k
   my $dim=$this->F2_VECTOR->rows-1;
   my $n_vertices=$this->F2_VECTOR->(0,0);
   while ($k<$dim) {
      $k2*=2;
      last if $this->F2_VECTOR->($k+1,0) != $k2 * $this->F2_VECTOR->($k+1,$k+1);
      ++$k;
   }
   ++$k if ($k==$dim && $n_vertices==2*$k2);	# it's a cube
   $this->CUBICALITY=$k;
}
precondition : GRAPH.BIPARTITE;

rule CUBICAL : FACET_SIZES {
   # this is a special case of the previous rule which is useful for 4-polytopes with a very large face lattice
   my $cubical=1;
   foreach my $fs (@{$this->FACET_SIZES}) {
      $cubical=0, last if $fs != 8;
   }
   $this->CUBICAL=$cubical;
}
precondition : GRAPH.BIPARTITE, DIM {
  $this->GRAPH->BIPARTITE && $this->DIM==4
}
weight 1.10;

rule CUBICALITY : DIM {
   $this->CUBICALITY= $this->DIM>0;
}
precondition : !GRAPH.BIPARTITE;
weight 0.10;

rule CUBICAL : CUBICALITY, DIM {
   $this->CUBICAL= $this->CUBICALITY >= $this->DIM-1;
}
weight 0.10;

rule COCUBICALITY : F2_VECTOR {
   # a polytope is k-cocubical if its dual is k-cubical
   # f_{d-1-j,d-1} = 2^j * f_j for all j<=k
   my $k=1;
   my $k2=2; # 2^k
   my $dim=$this->F2_VECTOR->rows-1;
   my $n_facets=$this->F2_VECTOR->($dim,$dim);
   while ($k<$dim) {
      $k2*=2;
      last if $this->F2_VECTOR->($dim-($k+1),$dim) != $k2 * $this->F2_VECTOR->($dim-($k+1),$dim-($k+1));
      ++$k;
   }
   ++$k if ($k==$dim && $n_facets==2*$k2);	# it's a cross polytope
   $this->COCUBICALITY=$k;
}
precondition : DUAL_GRAPH.BIPARTITE;

rule COCUBICALITY : DIM {
   $this->COCUBICALITY= $this->DIM>0;
}
precondition : !DUAL_GRAPH.BIPARTITE;
weight 0.10;

rule COCUBICAL : COCUBICALITY, DIM {
   $this->COCUBICAL= $this->COCUBICALITY >= $this->DIM-1;
}
weight 0.10;

rule NEIGHBORLINESS : F_VECTOR {
   # a polytope is k-neighborly if any k vertices form a face (which is necessarily simplicial)
   # i.e. f_{k-1} = binomial(n,k), where n = N_VERTICES
   my $k=1;
   my $d=$#{$this->F_VECTOR};
   my $n_vertices=$this->F_VECTOR->[0];
   ++$k while ($k<=$d && $this->F_VECTOR->[$k] == binomial($n_vertices, $k+1));
   $this->NEIGHBORLINESS=$k;
}

rule BALANCE : F_VECTOR {
   # dual notion to neighborliness
   # i.e. f_{d-k} = binomial(n,k), where n = N_FACETS
   my $k=0;
   my $d=$#{$this->F_VECTOR};
   my $n_facets=$this->F_VECTOR->[$d];
   ++$k while ($k<=$d && $this->F_VECTOR->[$d-$k] == binomial($n_facets, $k+1));
   $this->BALANCE=$k;
}

rule SIMPLICIAL : SIMPLICIALITY, DIM {
   $this->SIMPLICIAL= $this->SIMPLICIALITY >= $this->DIM-1;
}
weight 0.10;

rule SIMPLE : SIMPLICITY, DIM {
   $this->SIMPLE= $this->SIMPLICITY >= $this->DIM-1;
}
weight 0.10;

rule NEIGHBORLY : NEIGHBORLINESS, DIM {
   $this->NEIGHBORLY= $this->NEIGHBORLINESS >= $this->DIM/2;
}
weight 0.10;

rule BALANCED : BALANCE, DIM {
   $this->BALANCED= $this->BALANCE >= $this->DIM/2;
}
weight 0.10;

rule FATNESS : F_VECTOR {
   $this->FATNESS = (new Float($this->F_VECTOR->[1]+$this->F_VECTOR->[2]-20))/(new Float($this->F_VECTOR->[0]+$this->F_VECTOR->[3]-10));
}
precondition : DIM { $this->DIM==4 }

rule COMPLEXITY : F_VECTOR, F2_VECTOR {
   $this->COMPLEXITY = (new Float($this->F2_VECTOR->(0,3)-20))/(new Float($this->F_VECTOR->[0]+$this->F_VECTOR->[3]-10));
}
precondition : DIM { $this->DIM==4 }

rule TWO_FACE_SIZES : HASSE_DIAGRAM {
   $this->TWO_FACE_SIZES=two_face_sizes($this->HASSE_DIAGRAM);
}
precondition : DIM{
$this->DIM>=2
}

rule TWO_FACE_SIZES : GRAPH.ADJACENCY, VERTICES_IN_FACETS {
   two_face_sizes_simple($this);
}
precondition : SIMPLE;
precondition : DIM{
$this->DIM>=2
}

rule SUBRIDGE_SIZES : HASSE_DIAGRAM {
   $this->SUBRIDGE_SIZES=subridge_sizes($this->HASSE_DIAGRAM);
}
precondition : DIM{
$this->DIM>=2
}

rule SUBRIDGE_SIZES : DUAL_GRAPH.ADJACENCY, VERTICES_IN_FACETS {
   subridge_sizes_simple($this);
}
precondition : SIMPLICIAL;
precondition : DIM{
$this->DIM>=2
}
rule DIM : VERTICES_IN_FACETS {
   $this->DIM=dim_from_incidence($this->VERTICES_IN_FACETS);
}
weight 3.10;

rule GRAPH.ADJACENCY : VERTICES_IN_FACETS {
   $this->GRAPH->ADJACENCY=graph_from_incidence($this->VERTICES_IN_FACETS);
}
weight 3.10;

rule DUAL_GRAPH.ADJACENCY : VERTICES_IN_FACETS {
   $this->DUAL_GRAPH->ADJACENCY=dual_graph_from_incidence($this->VERTICES_IN_FACETS);
}
weight 3.10;

rule SELF_DUAL : VERTICES_IN_FACETS {
   $this->SELF_DUAL=is_self_dual($this);
}

rule HASSE_DIAGRAM : VERTICES_IN_FACETS {
   $this->HASSE_DIAGRAM=hasse_diagram($this->VERTICES_IN_FACETS);
}
weight 6.20;

rule DIM : HASSE_DIAGRAM {
   $this->DIM=$this->HASSE_DIAGRAM->dim;
}
weight 1.10;

rule GRAPH.ADJACENCY : HASSE_DIAGRAM {
   $this->GRAPH->ADJACENCY=vertex_graph($this->HASSE_DIAGRAM);
}
precondition : DIM { $this->DIM>=2 };

rule GRAPH.EDGE_DIRECTIONS : GRAPH.ADJACENCY, VERTICES, FAR_FACE {
   $this->GRAPH->EDGE_DIRECTIONS = edge_directions($this->GRAPH,$this->VERTICES,$this->FAR_FACE);
}

rule DUAL_GRAPH.ADJACENCY : HASSE_DIAGRAM {
   $this->DUAL_GRAPH->ADJACENCY=facet_graph($this->HASSE_DIAGRAM);
}
precondition : DIM { $this->DIM>=2 };

rule VERTEX_SIZES : VERTICES_IN_FACETS {
   $this->VERTEX_SIZES(temporary)=[ map { scalar(@$_) } @{transpose($this->VERTICES_IN_FACETS)} ];
}
weight 1.10;

rule FACET_SIZES : VERTICES_IN_FACETS {
   $this->FACET_SIZES(temporary)=[ map { scalar(@$_) } @{$this->VERTICES_IN_FACETS} ];
}
weight 1.10;

rule BOUNDED : VERTICES | POINTS {
   foreach my $v (@{$this->VERTICES | POINTS}) {
      if (! $v->[0]) {
	 $this->BOUNDED=0;
	 return;
      }
   }
   $this->BOUNDED=1;
}
weight 1.10;

rule SPLITS : VERTICES, FACETS, GRAPH.ADJACENCY, DIM {
   $this->SPLITS = splits($this->VERTICES,$this->GRAPH->ADJACENCY,$this->FACETS,$this->DIM);
}
precondition : DIM {
   $this->DIM > 0;
}
 
rule SPLIT_COMPATIBILITY_GRAPH.ADJACENCY : FACETS, AFFINE_HULL, SPLITS {
   $this->SPLIT_COMPATIBILITY_GRAPH->ADJACENCY = split_compatibility_graph($this->SPLITS,$this);
}

rule SPLITS_IN_SUBDIVISION : SPLITS, VERTICES, POLYTOPAL_SUBDIVISION {
   $this->SPLITS_IN_SUBDIVISION=splits_in_subdivision($this->VERTICES,$this->POLYTOPAL_SUBDIVISION,$this->SPLITS);
}

rule TRIANGULATION.REFINED_SPLITS : SPLITS, VERTICES, TRIANGULATION.FACETS {
   $this->TRIANGULATION->REFINED_SPLITS=splits_in_subdivision($this->VERTICES,$this->TRIANGULATION->FACETS,$this->SPLITS);
}


rule CENTERED : FACETS, AFFINE_HULL {
   foreach my $f (@{$this->FACETS}) {
      if ($f->[0] <= 0) {
	 $this->CENTERED=0;
	 return;
      }
   }
   foreach my $f (@{$this->AFFINE_HULL}) {
      if ($f->[0]) {
	 $this->CENTERED=0;
	 return;
      }
   }
   $this->CENTERED=1;
}
weight 1.10;


rule POSITIVE : VERTICES | POINTS {
   foreach my $v (@{$this->VERTICES | POINTS}) {
      foreach my $x (@$v) {
	 if ($x<0) {
	    $this->POSITIVE=0;
	    return;
	 }
      }
   }
   $this->POSITIVE=1;
}
weight 1.10;

rule POINTS : ZONOTOPE_INPUT_VECTORS {
   $this->POINTS=zonotope($this->ZONOTOPE_INPUT_VECTORS);
}
weight 5.10;

rule ALTSHULER_DET : VERTICES_IN_FACETS {
   $this->ALTSHULER_DET=altshuler_det($this->VERTICES_IN_FACETS);
}

rule TRIANGULATION.BOUNDARY.FACETS, TRIANGULATION.FACET_TRIANGULATIONS : TRIANGULATION.FACETS, VERTICES_IN_FACETS {
   ($this->TRIANGULATION->BOUNDARY->FACETS, $this->TRIANGULATION->FACET_TRIANGULATIONS)=
     triang_boundary($this->TRIANGULATION->FACETS, $this->VERTICES_IN_FACETS);
}

rule TRIANGULATION.BOUNDARY.FACETS, TRIANGULATION.FACET_TRIANGULATIONS : VERTICES_IN_FACETS, N_FACETS{
   $this->TRIANGULATION->BOUNDARY->FACETS=[ @{$this->VERTICES_IN_FACETS} ];
   my $ft=new Array<Set<Int> >($this->N_FACETS);
   for (my $i=0; $i<$this->N_FACETS; $i++) {
      $ft->[$i]=scalar2set($i);
   }
   $this->TRIANGULATION->FACET_TRIANGULATIONS=$ft;
}
precondition : SIMPLICIAL;
weight 1.10;

# helper clients for various visualization tasks

rule VIF_CYCLIC_NORMAL, NEIGHBOR_FACETS_CYCLIC_NORMAL : DIM, VERTICES, AFFINE_HULL, VERTICES_IN_FACETS, DUAL_GRAPH.ADJACENCY {
   neighbors_cyclic_normal_primal($this);
}
precondition : BOUNDED;
precondition : DIM { $this->DIM>=2 && $this->DIM<=3 }

rule VIF_CYCLIC_NORMAL = VERTICES_IN_FACETS;
precondition : DIM { $this->DIM<=1 }

rule FTV_CYCLIC_NORMAL, NEIGHBOR_VERTICES_CYCLIC_NORMAL : DIM, FACETS, VERTICES_IN_FACETS, GRAPH.ADJACENCY {
   neighbors_cyclic_normal_dual($this);
}
precondition : CENTERED;

# @category Triangulation and volume
# the orientation of the simplices of [[TRIANGULATION_INT]] in the given order of the [[POINTS]]
# @return Array<Int> - +1/-1 array specifying the sign of the determinant of each simplex
user_method TRIANGULATION_INT_SIGNS : TRIANGULATION_INT, POINTS {
   my $this=shift;
   triang_sign($this->TRIANGULATION_INT, $this->POINTS);
}


# clients capable of handling different coordinate types

rule FEASIBLE : DIM {
   $this->FEASIBLE = $this->DIM>=0;
}
weight 0.10;

rule AFFINE_HULL : VERTICES | POINTS {
   my $cmp=$this->prepare_computations;
   $this->AFFINE_HULL=null_space($this->VERTICES | POINTS);
}
weight 1.10;

rule AFFINE_HULL : AMBIENT_DIM {
   $this->AFFINE_HULL=unit_matrix<Scalar>($this->AMBIENT_DIM+1);
}
precondition : !FEASIBLE;

rule VERTICES_IN_FACETS : VERTICES, FACETS {
   my $cmp=$this->prepare_computations;
   $this->VERTICES_IN_FACETS=incidence_matrix($this->FACETS, $this->VERTICES);
}

rule POINTS_IN_FACETS : POINTS, FACETS {
   my $cmp=$this->prepare_computations;
   $this->POINTS_IN_FACETS=incidence_matrix($this->FACETS, $this->POINTS);
}

rule VERTICES_IN_INEQUALITIES : INEQUALITIES, VERTICES {
   my $cmp=$this->prepare_computations;
   $this->VERTICES_IN_INEQUALITIES=incidence_matrix($this->INEQUALITIES, $this->VERTICES);
}

rule VERTICES_IN_FACETS, VERTICES : POINTS_IN_FACETS, POINTS {
   my $cmp=$this->prepare_computations;
   compress_incidence_primal($this);
   $this->remove("POINTS_IN_FACETS");
}
permutation : VertexPerm;

rule VERTICES_IN_FACETS, FACETS, AFFINE_HULL : VERTICES_IN_INEQUALITIES, INEQUALITIES, FAR_FACE {
   my $cmp=$this->prepare_computations;
   compress_incidence_dual($this);
   $this->remove("VERTICES_IN_INEQUALITIES");
}
permutation : FacetPerm;

rule FACETS, AFFINE_HULL : VERTICES, VERTICES_IN_FACETS {
   my $cmp=$this->prepare_computations;
   facets_from_incidence($this);
}
precondition : N_VERTICES;

rule VERTICES : FACETS, AFFINE_HULL, VERTICES_IN_FACETS {
   my $cmp=$this->prepare_computations;
   vertices_from_incidence($this);
}

rule VERTEX_BARYCENTER : VERTICES, N_VERTICES {
   $this->VERTEX_BARYCENTER=$this->N_VERTICES ? barycenter($this->VERTICES) : undef;
}
precondition : BOUNDED;
weight 1.10;

rule CENTROID, VOLUME : VERTICES, TRIANGULATION.FACETS {
   centroid($this, $this->VERTICES, $this->TRIANGULATION->FACETS);
}
precondition : BOUNDED;
precondition : DIM, AMBIENT_DIM { 
   $this->DIM==$this->AMBIENT_DIM;
}
weight 2.30;

rule default.volume: VOLUME : VERTICES, TRIANGULATION.FACETS {
   $this->VOLUME=volume($this->VERTICES, $this->TRIANGULATION->FACETS);
}
precondition : BOUNDED;
precondition : DIM, AMBIENT_DIM {
   $this->DIM==$this->AMBIENT_DIM && $this->DIM>0;
}
weight 2.20;

rule default.volume: VOLUME : POINTS, TRIANGULATION_INT {
   $this->VOLUME=volume($this->POINTS, $this->TRIANGULATION_INT);
}
precondition : BOUNDED;
precondition : DIM, AMBIENT_DIM {
   $this->DIM==$this->AMBIENT_DIM && $this->DIM>0;
}
weight 2.30;

rule VOLUME : {
   $this->VOLUME=0;
}
precondition : BOUNDED;
precondition : DIM, AMBIENT_DIM {
   $this->DIM < max($this->AMBIENT_DIM,1);
}
weight 0.10;

rule MINIMAL_VERTEX_ANGLE : VERTICES, VERTEX_BARYCENTER {
   $this->MINIMAL_VERTEX_ANGLE = minimal_vertex_angle($this);
}

# Triangulation

rule TRIANGULATION.VERTEX_LABELS = VERTEX_LABELS;

rule TRIANGULATION.GEOMETRIC_REALIZATION : VERTICES {
   $this->TRIANGULATION->GEOMETRIC_REALIZATION(temporary)=$this->VERTICES->minor(All,~[0]);
}
weight 0.10;

rule TRIANGULATION.BALL : {
   $this->TRIANGULATION->BALL=1;
}
weight 0.10;

rule TRIANGULATION.VOLUME = VOLUME;

rule TRIANGULATION.G_DIM = AMBIENT_DIM;


rule TRIANGULATION.BOUNDARY.GEOMETRIC_REALIZATION : VERTICES {
   $this->TRIANGULATION->BOUNDARY->GEOMETRIC_REALIZATION(temporary)=$this->VERTICES->minor(All,~[0]);
}
weight 0.10;

rule TRIANGULATION.BOUNDARY.SPHERE : {
   $this->TRIANGULATION->BOUNDARY->SPHERE=1;
}
weight 0.10;

rule TRIANGULATION.BOUNDARY.G_DIM = AMBIENT_DIM;


}

# Local Variables:
# mode: perl
# c-basic-offset:3
# End:
