#  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 Symmetry
# A polytope which is generated by a group and a generating set of inequalities (+equations) 
# or points (+input_lineality).
# The polytope is the intersection or the convex hull of all inequalities or points in 
# the orbit of the generating set under the [[GENERATING_GROUP]].
# @tparam Scalar default: [[Rational]]
declare object SymmetricPolytope<Scalar=Rational> : SymmetricCone<Scalar>, Polytope<Scalar> {

########################################################
# properties
########################################################

property GEN_POINTS = override GEN_INPUT_RAYS;


########################################################
# rules
########################################################

rule initial : FEASIBLE : GENERATING_GROUP.COORDINATES_IN_ORBITS {
  my $orbits = $this->GENERATING_GROUP->COORDINATES_IN_ORBITS;

  # safe lookup of possibly undefined properties
  # we know that at least one of them exists by the precondition of this rule
  my $genIneqs;
  my $genEqs;
  if (defined($this->lookup('GEN_INEQUALITIES'))) {
    $genIneqs = $this->GEN_INEQUALITIES;
  } else {
    $genIneqs = new Matrix(0, $this->GEN_EQUATIONS->cols);
  }
  if (defined($this->lookup('GEN_EQUATIONS'))) {
    $genEqs = $this->GEN_EQUATIONS;
  } else {
    $genEqs = new Matrix(0, $this->GEN_INEQUALITIES->cols);
  }

  # project polytope onto the fixed space
  # projection is done row-wise for inequalties and equations
  # a symmetric polytope is feasible if and only if
  # its intersection(=projection) with the fixed space is feasible
  my $projIneqs = new Matrix<Scalar>($genIneqs->rows, $orbits->size + 1);
  my $projEqs = new Matrix<Scalar>($genEqs->rows, $orbits->size + 1);
  for (my $o = 0; $o < $orbits->size(); $o++) {
    for (my $i = 0; $i < $genIneqs->rows; $i++) {
      $projIneqs->[$i]->[0] = $genIneqs->[$i]->[0];
      foreach my $oi (@{$orbits->[$o]}) {
        $projIneqs->[$i]->[$o+1] += $genIneqs->[$i]->[$oi+1];
      }
    }
    for (my $i = 0; $i < $genEqs->rows; $i++) {
      $projEqs->[$i]->[0] = $genEqs->[$i]->[0];
      foreach my $oi (@{$orbits->[$o]}) {
        $projEqs->[$i]->[$o+1] += $genEqs->[$i]->[$oi+1];
      }
    }
  }
  my $dummyPolytope = new Polytope<Scalar>(INEQUALITIES=>$projIneqs, EQUATIONS=>$projEqs);
  $this->FEASIBLE = $dummyPolytope->FEASIBLE;
}
precondition : exists(GEN_INEQUALITIES | GEN_EQUATIONS);

rule initial : FEASIBLE : {
  $this->FEASIBLE = 1;
}
precondition : exists(GEN_POINTS | GEN_INPUT_LINEALITY);


########################################################
# user methods
########################################################


# must be copied (from common.rules) since SymmetricPolytope is derived from both objects, SymmetricCone and Polytope
user_method AMBIENT_DIM {
  my ($self)=@_;
  return ($self->CONE_AMBIENT_DIM-1);
}

# must be copied (from common.rules) since SymmetricPolytope is derived from both objects, SymmetricCone and Polytope
user_method DIM {
  my ($self)=@_;
  if (!defined ($self->lookup("LINEALITY_SPACE | INPUT_LINEALITY | INPUT_RAYS | RAYS | INEQUALITIES | EQUATIONS | FACETS | LINEAR_SPAN"))) {
    return $self->COMBINATORIAL_DIM;
  }
  return $self->CONE_DIM-1;
}

}

# @category Symmetry
# Constructs the orbit polytope of a given point //gen_point//
# with respect to a given permutation group //group//.
# @param Vector<Rational> gen_point the basis point of the orbit polytope
# @param group::GroupOfPolytope group a group acting on coordinates
# @return OrbitPolytope the orbit polytope of //gen_point// w.r.t. //group//
user_function orbit_polytope($,group::GroupOfPolytope) {

my ($gen_point, $group) = @_;
my $dim=$gen_point->dim-1;

if ( $dim != $group->DEGREE ) {
    croak("Wrong degree of the group with respect to the dimension of the given generating point!");
}

if ( $group->DOMAIN != $polytope::domain_OnCoords ) {
    croak("The domain of the group must be domain_OnCoords!");
}

my $p=new OrbitPolytope(GEN_POINT=>$gen_point,GENERATING_GROUP=>$group);
return $p;

}


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