#  Copyright (c) 1997-2015
#  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 PolyhedralFan {

# to be overloaded in special cases like Scalar==Float
sub prepare_computations { undef }

# @category Geometry
# Dimension of the space which contains the polyhedral fan.

property FAN_AMBIENT_DIM : Int;


# @category Geometry
# Dimension of the polyhedral fan.

property FAN_DIM : Int;

# @category Combinatorics
# Combinatorial dimension.

property COMBINATORIAL_DIM : Int;

# @category Geometry
# True if [[FAN_DIM]] and [[FAN_AMBIENT_DIM]] coincide.

property FULL_DIM : Bool;

# @category Geometry
# True if [[LINEALITY_SPACE]] is trivial.

property POINTED : Bool;

# @category Input property
# Rays from which the cones are formed.  May be redundant. All vectors in the input must be non-zero.
# You also need to provide [[INPUT_CONES]] to define a fan completely.
#
# Input section only. Ask for [[RAYS]] if you want a list of non-redundant rays.

property INPUT_RAYS : Matrix<Scalar>;


# @category Input property
# Maybe redundant list of not necessarily maximal cones.  Indices refer to [[INPUT_RAYS]].
# Each cone must list all rays of [[INPUT_RAYS]] it contains.
# The cones are allowed to contain lineality.
# Cones which do not have any rays correspond to the trivial cone (contains only the origin).
# An empty fan does not have any cones.
#
# Input section only. Ask for [[MAXIMAL_CONES]] if you want to know the maximal cones (indexed by [[RAYS]]).

property INPUT_CONES : Array<Set<Int> >;

# @category Input property
# Vectors whose linear span defines a subset of the lineality space of the fan; 
# redundancies are allowed.
#
# Input section only.  Ask for [[LINEALITY_SPACE]] if you want to know the lineality space.

property INPUT_LINEALITY : Matrix<Scalar>;


# @category Geometry
# Rays from which the cones are formed.  Non-redundant.  Co-exists with [[LINEALITY_SPACE]].

property RAYS : Matrix<Scalar> {
   sub canonical { &canonicalize_rays; }
}

declare permutation RaysPerm {

   # transforming [[RAYS]] from this into basic object
   property PERMUTATION : Array<Int>;
}

rule RaysPerm.PERMUTATION : RaysPerm.RAYS, RAYS  {
   my $cmp=$this->prepare_computations;
   $this->RaysPerm->PERMUTATION=find_representation_permutation($this->RaysPerm->RAYS, $this->RAYS,$this->LINEALITY_SPACE,0);
}

rule RAYS : RaysPerm.RAYS, RaysPerm.PERMUTATION {
   $this->RAYS=permuted_rows($this->RaysPerm->RAYS, $this->RaysPerm->PERMUTATION);
}
weight 1.10;

# @category Geometry
# Number of [[RAYS]].

property N_RAYS : Int;

# @category Geometry
# Number of [[INPUT_RAYS]].

property N_INPUT_RAYS : Int;

# @category Combinatorics
# Non redundant list of maximal cones.  Indices refer to [[RAYS]].
# Cones which do not have any rays correspond to the trivial cone (contains only the origin).
# An empty fan does not have any cones.
property MAXIMAL_CONES : IncidenceMatrix;

rule MAXIMAL_CONES : RaysPerm.MAXIMAL_CONES, RaysPerm.PERMUTATION {
   $this->MAXIMAL_CONES=permuted_cols($this->RaysPerm->MAXIMAL_CONES, $this->RaysPerm->PERMUTATION);
}

declare permutation ConesPerm {

   # transforming [[MAXIMAL_CONES]] from this into basic object
   property PERMUTATION : Array<Int>;
}

rule ConesPerm.PERMUTATION : ConesPerm.MAXIMAL_CONES, MAXIMAL_CONES  {
   $this->ConesPerm->PERMUTATION=find_permutation((new Array<Set<Int> >(rows($this->ConesPerm->MAXIMAL_CONES))), (new Array<Set<Int> >(rows($this->MAXIMAL_CONES))));
}
weight 1.10;


rule MAXIMAL_CONES : ConesPerm.MAXIMAL_CONES, ConesPerm.PERMUTATION {
   $this->MAXIMAL_CONES=permuted_rows($this->ConesPerm->MAXIMAL_CONES, $this->ConesPerm->PERMUTATION);
}
weight 1.10;


# @category Combinatorics
# List of all cones of the fan of each dimension.  Indices refer to [[RAYS]].
property CONES : Array<IncidenceMatrix>;

# @category Combinatorics
# Number of [[MAXIMAL_CONES]].

property N_MAXIMAL_CONES : Int;

# @category Combinatorics
# Array of incidence matrices of all [[MAXIMAL_CONES|maximal cones]].

property MAXIMAL_CONES_INCIDENCES : Array<IncidenceMatrix>;

rule MAXIMAL_CONES_INCIDENCES : ConesPerm.MAXIMAL_CONES_INCIDENCES, ConesPerm.PERMUTATION {
   $this->MAXIMAL_CONES_INCIDENCES=permuted($this->ConesPerm->MAXIMAL_CONES_INCIDENCES, $this->ConesPerm->PERMUTATION);
}
weight 1.10;

rule MAXIMAL_CONES_INCIDENCES : RaysPerm.MAXIMAL_CONES_INCIDENCES, RaysPerm.PERMUTATION {
  my $n_mc=scalar(@{$this->RaysPerm->MAXIMAL_CONES_INCIDENCES});
  my $mci=new Array<IncidenceMatrix>($n_mc);
  foreach my $i (0..$n_mc-1) {
      $mci->[$i]=permuted_cols($this->RaysPerm->MAXIMAL_CONES_INCIDENCES->[$i], $this->RaysPerm->PERMUTATION);
    }
  $this->MAXIMAL_CONES_INCIDENCES=$mci;
}
weight 1.10;

# @category Combinatorics
# The combinatorial dimensions of the [[MAXIMAL_CONES|maximal cones]].
property MAXIMAL_CONES_COMBINATORIAL_DIMS : Array<Int>;

rule MAXIMAL_CONES_COMBINATORIAL_DIMS : ConesPerm.MAXIMAL_CONES_COMBINATORIAL_DIMS, ConesPerm.PERMUTATION {
   $this->MAXIMAL_CONES_COMBINATORIAL_DIMS=permuted($this->ConesPerm->MAXIMAL_CONES_COMBINATORIAL_DIMS, $this->ConesPerm->PERMUTATION);
}
weight 1.10;


# @category Combinatorics
# The dimensions of the [[MAXIMAL_CONES|maximal cones]].
# @return Array<Int>
user_method MAXIMAL_CONES_DIMS : FAN_DIM, MAXIMAL_CONES_COMBINATORIAL_DIMS {
    my $this=shift;
    my @mcd;
    my $cd=$this->COMBINATORIAL_DIM;
    my $d=0;
    if ( defined ($this->lookup( "RAYS | INPUT_RAYS | INPUT_LINEALITY | LINEALITY_SPACE | LINEALITY_DIM | FAN_DIM | FACET_NORMALS | ORTH_LINEALITY_SPACE" ) ) ) {
        $d=$this->FAN_DIM-$cd;
    }
    foreach (@{$this->MAXIMAL_CONES_COMBINATORIAL_DIMS}) {
        push @mcd, $_+$d;
    }
    return new Array<Int>(@mcd);
}


# @category Geometry
# Since we do not require our cones to be pointed: a basis of the lineality space of the fan. Co-exists with [[RAYS]].

property LINEALITY_SPACE : Matrix<Scalar>;


# @category Geometry
# A basis of the orthogonal complement to [[LINEALITY_SPACE]].

property ORTH_LINEALITY_SPACE : Matrix<Scalar>;


# @category Geometry
# Dimension of [[LINEALITY_SPACE]].

property LINEALITY_DIM : Int;


# @category Visualization
# Unique names assigned to the [[RAYS]].
# If specified, they are shown by visualization tools instead of vertex indices.
#
# For a polyhedral fan built from scratch, you should create this property by yourself,
# either manually in a text editor, or with a client program.

property RAY_LABELS : Array<String> : mutable;

rule RAY_LABELS : RaysPerm.RAY_LABELS, RaysPerm.PERMUTATION {
   $this->RAY_LABELS=permuted($this->RaysPerm->RAY_LABELS, $this->RaysPerm->PERMUTATION);
}
weight 1.10;

# @category Visualization
# Unique names assigned to the [[INPUT_RAYS]].  Similar to [[RAY_LABELS]] for [[RAYS]].

property INPUT_RAY_LABELS : Array<String> : mutable;

# @category Visualization
# Unique names assigned to the [[MAXIMAL_CONES]].  Similar to [[RAY_LABELS]] for [[RAYS]].

property MAXIMAL_CONE_LABELS : Array<String> : mutable;

# @category Combinatorics
# The poset of subcones of the polyhedral fan organized as a directed graph.
# Each node corresponds to some proper subcone of the fan.
# The nodes corresponding to the rays and maximal cones appear in the same order
# as the elements of [[RAYS]] and [[MAXIMAL_CONES]] properties.
#
# One special node represents the origin.

property HASSE_DIAGRAM : FaceLattice;

rule HASSE_DIAGRAM : RaysPerm.HASSE_DIAGRAM, RaysPerm.PERMUTATION {
   $this->HASSE_DIAGRAM=permuted_atoms($this->RaysPerm->HASSE_DIAGRAM, $this->RaysPerm->PERMUTATION);
}

rule HASSE_DIAGRAM : ConesPerm.HASSE_DIAGRAM, ConesPerm.PERMUTATION {
   $this->HASSE_DIAGRAM=permuted_coatoms($this->ConesPerm->HASSE_DIAGRAM, $this->ConesPerm->PERMUTATION);
}

# @category Combinatorics
# f<sub>ik</sub> is the number of incident pairs of i-dimensional cones and k-dimensional cones; the main diagonal contains the [[F_VECTOR]].

property F2_VECTOR : Matrix<Integer>;


# @category Combinatorics
# f<sub>k</sub> is the number of k-dimensional cones starting from dimension k=1.

property F_VECTOR : Vector<Integer>;

# @category Combinatorics
# The polyhedral fan is __pure__ if all [[MAXIMAL_CONES|maximal cones]] are of the same dimension.

property PURE : Bool;

# @category Combinatorics
# The polyhedral fan is __complete__ if its suport is the whole space.
#
# Due to undecidability issues this is checked heuristically only.
# See the remarks on [[topaz::SimplicialComplex::SPHERE|SPHERE]] for details.

property COMPLETE : Bool;


# @category Combinatorics
# The polyhedral fan is __simplicial__ if all [[MAXIMAL_CONES|maximal cones]] are [[Cone::SIMPLICIAL|simplicial]].

property SIMPLICIAL : Bool;

# @category Geometry
# True if the fan is the [[normal_fan|normal fan]] of a polyhedron.

property REGULAR : Bool;

# @category Combinatorics
# The graph of the fan intersected with a sphere, that is,
# the vertices are the rays which are connected if they are
# contained in a common two-dimensional cone.

property GRAPH : objects::Graph;

rule GRAPH.NodePerm.PERMUTATION = RaysPerm.PERMUTATION;

# @category Combinatorics
# The graph whose nodes are the maximal cones which
# are connected if they share a common facet.

property DUAL_GRAPH : objects::Graph;

rule DUAL_GRAPH.NodePerm.PERMUTATION = ConesPerm.PERMUTATION;

# @category Geometry
# The possible facet normals of all maximal cones.

property FACET_NORMALS : Matrix<Scalar>;

# @category Geometry
# The number of possible facet normals of all maximal cones.

property N_FACET_NORMALS : Int;

# @category Geometry
# Tells for each maximal cone what are its facets.
# A negative number means that the corresponding row of
# [[FACET_NORMALS]] has to be negated.

property MAXIMAL_CONES_FACETS : SparseMatrix<Int>;

rule MAXIMAL_CONES_FACETS : ConesPerm.MAXIMAL_CONES_FACETS, ConesPerm.PERMUTATION {
   $this->MAXIMAL_CONES_FACETS=permuted_rows($this->ConesPerm->MAXIMAL_CONES_FACETS, $this->ConesPerm->PERMUTATION);
}
weight 1.10;

# @category Geometry
# The possible linear span normals of all maximal cones.

property LINEAR_SPAN_NORMALS : Matrix<Scalar>;

# @category Geometry
# Tells for each maximal cone what is its linear span.
# Indices refer to [[LINEAR_SPAN_NORMALS]].
# Rows correspond to [[MAXIMAL_CONES_FACETS]].

property MAXIMAL_CONES_LINEAR_SPAN_NORMALS : IncidenceMatrix;

rule MAXIMAL_CONES_LINEAR_SPAN_NORMALS : LINEAR_SPAN_NORMALS, ConesPerm.MAXIMAL_CONES_LINEAR_SPAN_NORMALS, ConesPerm.PERMUTATION {
   if ($this->LINEAR_SPAN_NORMALS->rows) {
      $this->MAXIMAL_CONES_LINEAR_SPAN_NORMALS = permuted_rows($this->ConesPerm->MAXIMAL_CONES_LINEAR_SPAN_NORMALS, $this->ConesPerm->PERMUTATION);
   } else {
      # the linear span matrix is empty, nothing to permute
      $this->MAXIMAL_CONES_LINEAR_SPAN_NORMALS = $this->ConesPerm->MAXIMAL_CONES_LINEAR_SPAN_NORMALS;
   }
}
weight 1.10;

# @category Topology
# If the fan is [[SIMPLICIAL]] the simplicial complex obtained by intersection
# the fan with the unit sphere.
# If the fan is not [[SIMPLICIAL]] the crosscut complex of the intersection.

property INTERSECTION_COMPLEX : topaz::SimplicialComplex;


# @category Topology
# The homology of the intersection of the fan with the unit sphere.

property HOMOLOGY : Array<topaz::HomologyGroup<Integer>>;

rule HOMOLOGY = INTERSECTION_COMPLEX.HOMOLOGY;

# @category Combinatorics
# Returns the //i//-th maximal cone.
# @param Int i
# @return Cone
user_method cone($) : MAXIMAL_CONES {
   my ($self,$i)=@_;
   my $c=new Cone<Scalar>;

   my $a_dim=$c->CONE_AMBIENT_DIM=$self->FAN_AMBIENT_DIM;
   if (defined(my $ls=$self->lookup("LINEALITY_SPACE"))) {
      $c->LINEALITY_SPACE=$ls;
   }
   if (defined(my $ld=$self->lookup("LINEALITY_DIM"))) {
      $c->LINEALITY_DIM=$ld;
   }
   if (defined(my $r=$self->lookup("RAYS")) and defined(my $mc=$self->lookup("MAXIMAL_CONES"))) {
      $c->RAYS=$r->minor($mc->[$i],All);
   }
   if (defined(my $mcf=$self->lookup("MAXIMAL_CONES_FACETS")) and defined(my $fn=$self->lookup("FACET_NORMALS"))) {
      my @facets;
      my $a=0;
      foreach (@{$mcf->row($i)}) {
         if($_!=0) {push(@facets,$_*$fn->row($a)); }
         ++$a;
      }
      $c->FACETS=\@facets;
   }
   if (defined(my $mclsn=$self->lookup("MAXIMAL_CONES_LINEAR_SPAN_NORMALS")) and defined(my $lsn=$self->lookup("LINEAR_SPAN_NORMALS"))) {
      if ($mclsn->rows==0) {
         $c->LINEAR_SPAN=new Matrix<Scalar>(0,$a_dim);
      }
      else {
         $c->LINEAR_SPAN=$lsn->minor($mclsn->[$i],All);
      }
   }
   if (defined(my $mci=$self->lookup("MAXIMAL_CONES_INCIDENCES"))) {
      $c->RAYS_IN_FACETS=$mci->[$i];
   }
   if (defined(my $mccd=$self->lookup("MAXIMAL_CONES_COMBINATORIAL_DIMS"))) {
      $c->COMBINATORIAL_DIM=$mccd->[$i];
   }
   if (defined(my $simp=$self->lookup("SIMPLICIAL"))) {
      if ($simp) {
         $c->SIMPLICIAL=$simp;
      }
   
      if (defined(my $pure=$self->lookup("PURE"))) {
         if ($pure) {
            if (defined(my $fd=$self->lookup("FAN_DIM"))) {
               $c->CONE_DIM=$fd;
            }
            if (defined(my $cd=$self->lookup("COMBINATORIAL_DIM"))) {
               $c->COMBINATORIAL_DIM=$cd;
            }
         }
      }
   }
   return $c;
}


# @category Geometry
# Returns the dimension of the ambient space.
# @return Int
user_method AMBIENT_DIM {
  my ($self)=@_;
  return $self->FAN_AMBIENT_DIM;
}

# @category Geometry
# Returns the dimension of the linear space spanned by the fan.
# @return Int
user_method DIM {
  my ($self)=@_;
  if (!defined ($self->lookup("LINEALITY_SPACE | INPUT_LINEALITY | INPUT_RAYS | RAYS | FACET_NORMALS | LINEAR_SPAN_NORMALS"))) {
    return $self->COMBINATORIAL_DIM;
 }
  return $self->FAN_DIM;
}

}

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