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

# @category Geometry
# An object of type VectorConfiguration deals with properties of row vectors,
# assembled into an n x d matrix called [[VECTORS]]. 
# The entries of these row vectors are interpreted as non-homogeneous coordinates. 
# In particular, the coordinates of a VECTOR will *NOT* be normalized to have a leading 1.
# @tparam Scalar default: [[Rational]]
declare object VectorConfiguration<Scalar=Rational>;

INCLUDE
  vector_configuration_properties.rules

object VectorConfiguration {

method construct(polytope::Cone) {
  my $cone=$_[1];
  return new VectorConfiguration(VECTORS=>$cone->RAYS/$cone->LINEALITY_SPACE/-$cone->LINEALITY_SPACE);
}

rule MULTIPLE_VECTORS : VECTORS {
   $this->MULTIPLE_VECTORS=detect_multiple($this->VECTORS); 
}
weight 1.10;

rule N_VECTORS : VECTORS {
    $this->N_VECTORS=$this->VECTORS->rows;
}
weight 0.1;

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

# The purpose of the following rule is to have simpler preconditions in subsequent rules.
# The scheduler efficiently handles preconditions which amount to checking a boolean.
rule FULL_DIM : VECTOR_AMBIENT_DIM, VECTOR_DIM {
  $this->FULL_DIM=($this->VECTOR_AMBIENT_DIM==$this->VECTOR_DIM);
}
weight 0.1;

rule VECTOR_AMBIENT_DIM : VECTORS {
   $this->VECTOR_AMBIENT_DIM=$this->VECTORS->cols;
}
weight 0.1;

rule VECTOR_DIM : VECTOR_AMBIENT_DIM, LINEAR_SPAN {
   $this->VECTOR_DIM = $this->VECTOR_AMBIENT_DIM - $this->LINEAR_SPAN->rows;
}
weight 0.1;

rule LINEAR_SPAN : VECTOR_AMBIENT_DIM {
   $this->LINEAR_SPAN=new Matrix<Scalar>(0, $this->VECTOR_AMBIENT_DIM);
}
precondition : FULL_DIM;
weight 0.1;

rule VECTOR_DIM : VECTORS {
   $this->VECTOR_DIM=rank($this->VECTORS);
}
weight 1.10;


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

rule LINEAR_SPAN : VECTORS {
   $this->LINEAR_SPAN=null_space($this->VECTORS);
}
weight 1.10;


}

# @category Combinatorics
# Convert [[CIRCUITS]] or [[COCIRCUITS]] to a 0/+1/-1 matrix, with one row for each circuit/cocircuit, 
# and as many columns as there are VECTORs/POINTS.
# @param Set<Pair<Set<Int>,Set<Int>>> co/circuits a set of circuits or cocircuits
# @return SparseMatrix<Rational>
user_function circuits2matrix(Set<Pair<Set<Int>,Set<Int>>>) {
   my $circuits = shift;
   my $m = $circuits->size();
   my $n = 0;
   my $mat = new SparseMatrix($m, $n+1);
   my $i=0;
   for (my $e=entire($circuits); $e; ++$e, $i++) {
      if ($$e->first->size()) {
	 $n = max($n, $$e->first->back());
      }
      if ($$e->second->size()) {
	 $n = max($n, $$e->second->back());
      }
      $mat->resize($m, $n+1);
      for (my $pos = entire($$e->first); $pos; ++$pos) {
	 $mat->[$i]->[$$pos] = 1;
      }
      for (my $neg = entire($$e->second); $neg; ++$neg) {
	 $mat->[$i]->[$$neg] = -1;
      }
   }
   return $mat;
}


# @category Combinatorics
# Contract a vector configuration //C// along a specified vector //v//.
# @param VectorConfiguration C
# @param Int v index of the vector to contract
user_function contraction(VectorConfiguration $) {
   my ($vc, $index) = @_;
   my $vcc = new Matrix($vc->VECTORS);
   my $vcm = new Matrix($vc->VECTORS->minor([$index], All));
   project_to_orthogonal_complement($vcc, $vcm);
   my @labels = @{$vc->LABELS};
   splice @labels, $index, 1;
   return new VectorConfiguration(VECTORS => $vcc->minor(~[$index],All), LABELS => "@labels");
}

# @category Combinatorics
# Delete a specified vector //v// from a vector configuration //C//.
# @param VectorConfiguration C
# @param Int v index of the vector to delete
user_function deletion(VectorConfiguration $) {
   my ($vc, $index) = @_;
   my @labels = @{$vc->LABELS};
   splice @labels, $index, 1;
   return new VectorConfiguration(VECTORS => $vc->VECTORS->minor(~[$index],All), LABELS => "@labels");
}

# @category Producing a vector configuration
# Orthogonally project a vector configuration to a coordinate subspace.
# 
# The subspace the VectorConfiguration //P// is projected on is given by indices in the set //indices//.
# The option //revert// inverts the coordinate list.
# @param VectorConfiguration P
# @param Array<Int> indices
# @option Bool revert inverts the coordinate list
# @return VectorConfiguration
user_function projection<Scalar>(VectorConfiguration<Scalar>; $=[ ], { revert=>0 }) {
   my ($P, $indices, $options) = @_;
   my $first_coord = ($P->isa("PointConfiguration") ? 1 : 0);
   projection_impl($P, "VECTOR", "AFFINE_HULL", $first_coord, $indices, $options);
}


# @category Producing a vector configuration
# Orthogonally project a vector configuration to a coordinate subspace such that redundant columns are omitted,
# i.e., the projection becomes full-dimensional without changing the combinatorial type.
# @param VectorConfiguration P
# @option Bool no_labels Do not copy [[VERTEX_LABELS]] to the projection. default: 0
# @return VectorConfiguration
user_function projection_full<Scalar>(VectorConfiguration<Scalar>; {no_labels=>0}) { 
   projection(@_); 
}


# @category Producing a vector configuration
# Construct a new vector configuration that projects to a given array of vector configurations
# If the n vector configurations are d_1, d_2, ..., d_n-dimensional and all have m vectors,
# the resulting vector configuration is (d_1+...+d_n)-dimensional, has m vectors, and
# the projection to the i-th d_i coordinates gives the i-th input vector configuration.
# @param Array<VectorConfiguration> P_Array
# @return VectorConfiguration
user_function projection_preimage<Scalar>(VectorConfiguration<Scalar> +) {
    my $a = new Array<VectorConfiguration<Scalar>>(@_);
    projection_preimage_impl($a);
}

# @category Producing a vector configuration
# Construct the free sum of two vector configurations.
# 
# @param VectorConfiguration P1
# @param VectorConfiguration P2
# @option Bool force_centered if the input polytopes must be centered. Defaults to true.
# @option Bool no_coordinates produces a pure combinatorial description. Defaults to false.
# @return VectorConfiguration
user_function free_sum<Scalar>(VectorConfiguration<Scalar> VectorConfiguration<Scalar>; { force_centered=>1, no_coordinates=>0 }) {
   my ($P1, $P2, $options) = @_;
   if (!$P1->isa("PointConfiguration") && $P2->isa("PointConfiguration") ||
       !$P2->isa("PointConfiguration") && $P1->isa("PointConfiguration")) {
       die "free_sum: cannot mix point and vector configurations";
   }
   my $first_coord = ($P1->isa("PointConfiguration") ? 1 : 0);
   free_sum_impl($P1, $P2, "VECTOR", "AFFINE_HULL", $first_coord, $options);
}

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