#  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.
#-------------------------------------------------------------------------------

# @category Topology
#
# A group is encoded as a sequence ( { (t<sub>1</sub> m<sub>1</sub>) ...  (t<sub>n</sub>  m<sub>n</sub>) } f) of non-negative integers,
# with t<sub>1</sub> > t<sub>2</sub> > ... > t<sub>n</sub> > 1, plus an extra non-negative integer f.
#
# The group is isomorphic to (Z/t<sub>1</sub>)<sup>m<sub>1</sub></sup> × ... × (Z/t<sub>n</sub>)<sup>m<sub>n</sub></sup> × Z<sup>f</sup>, 
# where Z<sup>0</sup> is the trivial group.
#
# @field List<Pair<Scalar, Int>> torsion list of Z-groups
# @field Int betti_number the number //f//
# @tparam Scalar integer type of torsion coefficients
declare property_type HomologyGroup<Scalar> : c++ (include => "polymake/topaz/HomologyComplex.h");

# @category Topology
#
# A group is encoded as a pair of an integer matrix and a vector of faces.
# The elements of the group can be obtained by symbolic multiplication of both.
#
# @field SparseMatrix<Scalar> coeff the integer matrix
# @field Array<Set<Int>> faces the faces
# @tparam Scalar integer type of matrix elements

declare property_type CycleGroup<Scalar> : c++ (include => "polymake/topaz/HomologyComplex.h");

# @category Topology
#
# Parity and signature of the intersection form of a closed oriented 4k-manifold.
# See [[INTERSECTION_FORM]].

declare property_type IntersectionForm : c++ (include => "polymake/topaz/IntersectionForm.h");

object SimplicialComplex {

# permuting the vertices
permutation VertexPerm : PermBase ;

# @category Input property
# Any description of the faces of a simplicial complex with vertices v_0 < v_1 < v_2 < ... arbitrary.  Redundant faces allowed.

property INPUT_FACES : Array<Set> {

  # for testing purposes, properties are equivalent regardless of the order
  sub equal {
     my ($hf1, $hf2)=map { new HashSet<Set>($_) } @_;
     $hf1==$hf2;
  }
}

# @category Combinatorics
# Indices of the vertices from [[INPUT_FACES]].  That is, the map i \mapsto v_i.

property VERTEX_INDICES : Array<Int>;

# @category Visualization
# Labels of the vertices.

property VERTEX_LABELS : Array<String>;

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

# @category Combinatorics
# Find the vertices by given labels.
# @param String label ... vertex labels
# @return Set<Int> vertex indices

user_method labeled_vertices {
   my $this=shift;
   if (defined (my $labels=$this->lookup("VERTEX_LABELS"))) {
      new Set([ find_labels($labels, @_) ]);
   } else {
      die "No VERTEX_LABELS stored in this complex";
   }
}

# @category Combinatorics
# Output the boundary matrix of dimension __d__. Indexing is according to
# the face indices in the HASSE_DIAGRAM of the complex.
# The matrix is a map via multiplying it to a vector from the __left__.
# Beware, this computes the whole face lattice of your complex, which is expensive.
# @param Int d Dimension of the boundary matrix.
# @return SparseMatrix<Integer>
# @example This prints the boundary matrix of the 3-simplex:
# > print simplex(3)->boundary_matrix(1);
# | -1 1 0 0
# | -1 0 1 0
# | 0 -1 1 0
# | -1 0 0 1
# | 0 -1 0 1
# | 0 0 -1 1
# The output can be interpreted like this: the zeroth column of the matrix corresponds
# to the facet with index 0, which contains the edges with indices 0,1 and 3.
user_method boundary_matrix {
   return boundary_matrix_cpp(@_);
}

# @category Combinatorics
# Number of vertices.

property N_VERTICES : Int;


# @category Combinatorics
# Faces which are maximal with respect to inclusion, encoded as their ordered set of vertices.
# The vertices must be numbered 0, ..., n-1.

property FACETS : Array<Set>;

rule FACETS : VertexPerm.FACETS, VertexPerm.PERMUTATION {
   $this->FACETS=permuted_elements($this->VertexPerm->FACETS, $this->VertexPerm->PERMUTATION);
}
weight 1.20;

# permuting the [[FACETS]]
permutation FacetPerm : PermBase;

rule FacetPerm.PERMUTATION : FacetPerm.FACETS, FACETS {
   $this->FacetPerm->PERMUTATION = find_permutation($this->FacetPerm->FACETS, $this->FACETS)
      // die "no permutation";
}

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

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

property N_FACETS : Int;


# @category Combinatorics
# Inclusion minimal non-faces (vertex subsets which are not faces of the simplicial complex).

property MINIMAL_NON_FACES : Array<Set>;

rule MINIMAL_NON_FACES : VertexPerm.MINIMAL_NON_FACES, VertexPerm.PERMUTATION {
   $this->MINIMAL_NON_FACES=permuted_elements($this->VertexPerm->MINIMAL_NON_FACES, $this->VertexPerm->PERMUTATION);
}
weight 1.20;


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

property N_MINIMAL_NON_FACES : Int;


# @category Combinatorics
# Maximal dimension of the [[FACETS]], where the dimension of a facet is defined as
# the number of its vertices minus one.

property DIM : Int;


# @category Combinatorics
# A simplicial complex is __pure__ if all its facets have the same dimension.

property PURE : Bool;


# @category Combinatorics
# f<sub>k</sub> is the number of k-faces, for k = 0,... , d, where d is the dimension.

property F_VECTOR : Array<Int>;

# @category Combinatorics
# The h-vector of the simplicial complex.

property H_VECTOR : Array<Int>;

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

property F2_VECTOR : Matrix<Int>;

# @category Combinatorics
# True if this is shellable.

property SHELLABLE : Bool;

# @category Combinatorics
# An ordered list of facets constituting a shelling.

property SHELLING : Array<Set>;

# @category Combinatorics
# Subcomplex generated by faces of codimension 2 that are contained in an odd
# number of faces of codimension 1.

property ODD_SUBCOMPLEX : SimplicialComplex;

rule ODD_SUBCOMPLEX.VertexPerm.PERMUTATION = VertexPerm.PERMUTATION;


# @category Combinatorics
# True if [[GRAPH]] is ([[DIM]] + 1)-colorable.

property FOLDABLE : Bool;



# @category Topology
# Reduced Euler characteristic.  Alternating sum of the [[F_VECTOR]] minus 1.

property EULER_CHARACTERISTIC : Int;


# @category Topology
# Reduced simplicial homology groups H<sub>0</sub>, ..., H<sub>d</sub> (integer coefficients), listed in increasing dimension order.
# See [[HomologyGroup]] for explanation of encoding of each group.

property HOMOLOGY : Array<HomologyGroup<Integer>>;


# @category Topology
# Reduced cohomology groups, listed in increasing codimension order.
# See [[HomologyGroup]] for explanation of encoding of each group.

property COHOMOLOGY : Array<HomologyGroup<Integer>>;


# @category Topology
# Representatives of cycle groups, listed in increasing dimension order.
# See [[CycleGroup]] for explanation of encoding of each group.

property CYCLES : Array<CycleGroup<Integer>>;

### FIXME: needs a client to permute since CycleGroup embedded c++ type
rule nonexistent : CYCLES : VertexPerm.CYCLES, VertexPerm.PERMUTATION;


# @category Topology
# Representatives of cocycle groups, listed in increasing codimension order.
# See [[CycleGroup]] for explanation of encoding of each group.

property COCYCLES : Array<CycleGroup<Integer>>;

### FIXME: needs a client to permute since CycleGroup embedded c++ type
rule nonexistent : COCYCLES : VertexPerm.COCYCLES, VertexPerm.PERMUTATION;


# @category Topology
#
# The integral quadratic form obtained from restricting the multiplication of the cohomology of a closed
# 4k-manifold to H^{2k} x H^{2k} -> H^{4k} = Z.  As a quadratic form over the reals it is characterized
# by its dimension and its index of inertia (or, equivalenty, by the number of positive and negative ones
# in its canonical form).  An integral quadratic form is even if it takes values in 2Z.
property INTERSECTION_FORM : IntersectionForm;


# @category Topology
# Mod 2 Stiefel-Whitney homology classes per dimension.  Each cycle is represented as a set of simplices.
property STIEFEL_WHITNEY : Array<Set<Set<Int>>>;

### FIXME: could be done
rule nonexistent : STIEFEL_WHITNEY : VertexPerm.STIEFEL_WHITNEY, VertexPerm.PERMUTATION;


# @category Topology
# Determines if this is a compact simplicial manifold with boundary.
# Depends on heuristic [[SPHERE]] recognition.
# May be true or false or undef (if heuristic does not succeed).

property MANIFOLD : Bool;


# @category Topology
# True if this is a [[PURE]] simplicial complex with the property that each ridge is
# contained in either one or two facets.

property PSEUDO_MANIFOLD: Bool;


# @category Topology
# True if this is a [[PURE]] simplicial complex with the property that each ridge is
# contained in exactly two facets.

property CLOSED_PSEUDO_MANIFOLD : Bool;


# @category Topology
# True if this is a [[PSEUDO_MANIFOLD]] with top level homology isomorphic to Z.

property ORIENTED_PSEUDO_MANIFOLD : Bool;


# @category Topology
# An orientation of the facets of an [[ORIENTED_PSEUDO_MANIFOLD]], such that the induced orientations
# of a common ridge of two neighboring facets cancel each other out. Each facet is marked with //1//
# if the orientation agrees with the (chosen) orientation of the first facet, and is marked with //-1// otherwise.

property ORIENTATION : Array<Int>;

rule nonexistent : ORIENTATION : VertexPerm.ORIENTATION, VertexPerm.PERMUTATION;

rule ORIENTATION : FacetPerm.ORIENTATION, FacetPerm.PERMUTATION {
   $this->ORIENTATION=permuted($this->FacetPerm->ORIENTATION, $this->VertexPerm->PERMUTATION);
}
weight 1.10;

# @category Topology
# A representation of the facets that prints them using the VERTEX_LABELS

property LABELED_FACETS : Array<String>;

# @category Topology
# A representation of the orientation that prints each facet using the VERTEX_LABELS, followed by its sign

property LABELED_ORIENTATION : Array<String>;

# @category Topology
# Determines if this is homeomorphic to a sphere.
# In general, this is undecidable; therefore, the implementation depends on heuristics.
# May be true or false or undef (if heuristic does not succeed).

property SPHERE : Bool;


# @category Topology
# Determines if this is homeomorphic to a ball.
# In general, this is undecidable; therefore, the implementation depends on heuristics.
# May be true or false or undef (if heuristic does not succeed).
# @example Simplicial Complex is a ball.
# > $b= new SimplicialComplex(INPUT_FACES=>[[0,1,2,3],[1,2,3,4]]);
# > print $b->BALL;
# | true


property BALL : Bool;


# @category Combinatorics
# The subcomplex consisting of all 1-faces.

property GRAPH : Graph;

rule GRAPH.NodePerm.PERMUTATION = VertexPerm.PERMUTATION;

# @category Combinatorics
# A coloring of the vertices.

property COLORING : Array<Int>;

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

# @category Combinatorics
# The graph of facet neighborhood.
# Two [[FACETS]] are neighbors if they share a (d-1)-dimensional face.

property DUAL_GRAPH : Graph {

   property COLORING : NodeMap<Undirected,Int> : construct(ADJACENCY);

}

rule DUAL_GRAPH.NodePerm.PERMUTATION = FacetPerm.PERMUTATION;

# @category Combinatorics
# The face lattice of the simplical complex
# organized as a directed graph.  Each node corresponds to some face
# of the simplical complex. It is represented as the list of vertices
# comprising the face. The outgoing arcs point to the containing faces
# of the next dimension. An artificial top node is added to represent
# the entire complex.

property HASSE_DIAGRAM : Lattice<BasicDecoration> {

   user_method dim {
      return shift->rank()-1;
   }

   user_method nodes_of_dim($) {
      my ($this,$d) = @_;
      return shift->nodes_of_rank($d+1);
   }

   user_method nodes_of_dim_range($,$) {
      my ($this,$d1,$d2) = @_;
      return shift->nodes_of_rank_range($d1+1,$d2+1);
   }

}

# @category Topology
# True if this is a [[CONNECTED]] [[MANIFOLD]] of dimension 2.

property SURFACE : Bool;


# @category Topology
# The __genus__ of a surface.

property GENUS : Int;


# @category Topology
# True if the vertex star of each vertex is [[DUAL_CONNECTED]].

property LOCALLY_STRONGLY_CONNECTED : Bool;

#compute the VERTEX_MAP of a subcomplex given a permutation and the VertexPerm.VERTEX_MAP
sub compute_vertex_map_perm{
   my ($perm, $perm_map) = (@_);
   my $s = $perm_map->size();
   my $map = new Array<Int>($s);

   for(my $i = 0; $i < $s; ++$i){
      $map->[$i] = $perm->[$perm_map->[$i]];
   }
   return $map;
}


# @category Combinatorics
# Codimension-1-faces of a [[PSEUDO_MANIFOLD]] which are contained in one facet only.

property BOUNDARY : self {

   # @category Combinatorics
   # Maps vertices of the boundary complex to the corresponding ones in the supercomplex
   property VERTEX_MAP : Array<Int>;

}

rule BOUNDARY.VERTEX_MAP : VertexPerm.PERMUTATION, VertexPerm.BOUNDARY.VERTEX_MAP {
   $this->BOUNDARY->VERTEX_MAP = compute_vertex_map_perm($this->VertexPerm->PERMUTATION, $this->VertexPerm->BOUNDARY->VERTEX_MAP);
}

# @category Topology
# One-dimensional subcomplex which forms a knot or link, i.e., a collection of pairwise disjoint cycles.
# Usually that complex is a 3-sphere or a 3-ball.                            

property KNOT : SimplicialComplex : multiple;

rule KNOT.FACETS : VertexPerm.KNOT.FACETS, VertexPerm.PERMUTATION {
   $this->KNOT->FACETS = permuted_elements($this->VertexPerm->KNOT->FACETS, $this->VertexPerm->PERMUTATION);
}
weight 1.20;


# @category Combinatorics
# Orbit decomposition of the group of projectivities acting on the set of vertices of facet 0.

property PROJ_ORBITS : Set<Set<Int>>;

rule PROJ_ORBITS : VertexPerm.PROJ_ORBITS, VertexPerm.PERMUTATION {
   $this->PROJ_ORBITS = permuted_elements($this->VertexPerm->PROJ_ORBITS, $this->VertexPerm->PERMUTATION);
}
weight 1.20;


# @category Combinatorics
# For each vertex the corresponding vertex of facet 0 with respect to the action of the group of projectivities.

property PROJ_DICTIONARY : Array<Int>;

rule nonexistent : PROJ_DICTIONARY : VertexPerm.PROJ_DICTIONARY, VertexPerm.PERMUTATION;

# @category Symmetry
property GROUP : group::Group : multiple;

}

# A geometric simplicial complex, i.e., a simplicial complex with a geometric realization.
# //Scalar// is the numeric data type used for the coordinates.
# @tparam Scalar default: [[Rational]]
declare object GeometricSimplicialComplex<Scalar=Rational> : SimplicialComplex {


# Coordinates for the vertices of the simplicial complex, such that the complex is
# embedded without crossings in some R<sup>e</sup>.  Vector (x<sub>1</sub>, .... x<sub>e</sub>) represents a point
# in Euclidean e-space.
property COORDINATES : Matrix<Scalar>;

rule COORDINATES : VertexPerm.COORDINATES, VertexPerm.PERMUTATION {
   $this->COORDINATES = permuted_rows($this->VertexPerm->COORDINATES, $this->VertexPerm->PERMUTATION);
}
weight 1.20;

# Dimension e of the space in which the [[COORDINATES]] of the complex is embedded.
property G_DIM : Int;

# Volume of a geometric simplicial complex.
property VOLUME : Scalar;

# A geometric simplicial complex is unimodular if all simplices have unit normalized volume.
# @example Unit square, triangulated.
# > $C = new GeometricSimplicialComplex(COORDINATES=>[[0,0],[1,0],[0,1],[1,1]], FACETS=>[[0,1,2],[1,2,3]]);
# > print $C->UNIMODULAR
# | true
property UNIMODULAR : Bool;

# Count how many simplices of a geometric simplicial complex are unimodular.
# @example Non-regular quadrangle, triangulated.
# > $C = new GeometricSimplicialComplex(COORDINATES=>[[0,0],[1,0],[0,1],[2,1]],FACETS=>[[0,1,2],[1,2,3]]);
# > print $C->N_UNIMODULAR
# | 1
property N_UNIMODULAR : Int;

# Signature of a geometric simplicial complex embedded in the integer lattice.
# Like [[DUAL_GRAPH_SIGNATURE]], but only simplices with odd normalized volume are counted.
property SIGNATURE : Int;


rule G_DIM : COORDINATES {
   $this->G_DIM=$this->COORDINATES->cols;
}
weight 0.1;


rule SIGNATURE : DUAL_GRAPH.ADJACENCY, COORDINATES, FACETS {
   $this->SIGNATURE=signature($this);
}
precondition : DIM, G_DIM {
   $this->DIM==$this->G_DIM;
}

rule DUAL_GRAPH.BIPARTITE : SIGNATURE {
  $this->DUAL_GRAPH->BIPARTITE = $this->SIGNATURE >= 0;
}
weight 0.1;

rule VOLUME : FACETS, COORDINATES, DIM {
  $this->VOLUME=volume($this);
}

rule UNIMODULAR : FACETS, COORDINATES {
    my $C = $this->COORDINATES;
    my $n = $C->rows();
    $this->UNIMODULAR=unimodular( ones_vector<Rational>($n)|$C, $this->FACETS );
}

rule N_UNIMODULAR : FACETS, COORDINATES {
    my $C = $this->COORDINATES;
    my $n = $C->rows();
    $this->N_UNIMODULAR=n_unimodular( ones_vector<Rational>($n)|$C, $this->FACETS );
}

property BOUNDARY = override : self;

rule BOUNDARY.COORDINATES : COORDINATES, BOUNDARY.VERTEX_MAP {
   $this->BOUNDARY->COORDINATES(temporary) = $this->COORDINATES->minor($this->BOUNDARY->VERTEX_MAP, All);
}

rule BOUNDARY.G_DIM = G_DIM;


}

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