#  Copyright (c) 1997-2018
#  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.
#-------------------------------------------------------------------------------

function induced_orbits<Scalar>(Cone<Scalar> $$ { homog_action=>0 }) {
    my ($c, $action_name, $generator_name, $options) = @_;
    return group::induced_orbits_impl($c, $action_name, $generator_name, (new Scalar()), $options);
}

function induced_orbits_on_vectors<Scalar>(Array<Matrix<Scalar>>, Matrix<Scalar>) {
    my ($gens, $vecs) = @_;
    return group::induced_orbits_on_vectors_impl<Scalar>($gens, $vecs);
}
   

object Cone {

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

      # @category Symmetry
       property MATRIX_ACTION : group::MatrixActionOnVectors<Scalar> {

           # @category Symmetry
           property RAYS_ORBITS = override VECTORS_ORBITS;

       };

      # @category Symmetry
      property REPRESENTATIVE_RAYS : Matrix<Scalar>;

      # @category Symmetry
      property REPRESENTATIVE_FACETS : Matrix<Scalar>;

      # First, some rules that apply to all actions

      rule ORDER : RAYS_ACTION.TRANSVERSAL_SIZES | FACETS_ACTION.TRANSVERSAL_SIZES | HOMOGENEOUS_COORDINATE_ACTION.TRANSVERSAL_SIZES {
         my $arr = $this->give("RAYS_ACTION.TRANSVERSAL_SIZES | FACETS_ACTION.TRANSVERSAL_SIZES | HOMOGENEOUS_COORDINATE_ACTION.TRANSVERSAL_SIZES");
         my $i = new Integer(1);
         $i *= $_ foreach(@{$arr});
         $this->ORDER = $i;
      }
      weight 1.10;

      rule CONJUGACY_CLASS_SIZES : RAYS_ACTION.CONJUGACY_CLASSES | FACETS_ACTION.CONJUGACY_CLASSES | \
                                   HOMOGENEOUS_COORDINATE_ACTION.CONJUGACY_CLASSES | SET_ACTION.CONJUGACY_CLASSES | MATRIX_ACTION.CONJUGACY_CLASSES {
         my $arr = $this->give("RAYS_ACTION.CONJUGACY_CLASSES | FACETS_ACTION.CONJUGACY_CLASSES | HOMOGENEOUS_COORDINATE_ACTION.CONJUGACY_CLASSES | SET_ACTION.CONJUGACY_CLASSES | MATRIX_ACTION.CONJUGACY_CLASSES");
         $this->CONJUGACY_CLASS_SIZES = [ map {$_->size} @{$arr} ];
      }
      weight 1.10;

      # *_ACTION->IRREDUCIBLE_DECOMPOSITION, in alphabetical order
      rule HOMOGENEOUS_COORDINATE_ACTION.IRREDUCIBLE_DECOMPOSITION : HOMOGENEOUS_COORDINATE_ACTION.CHARACTER, CHARACTER_TABLE, CONJUGACY_CLASS_SIZES, ORDER {
         $this->HOMOGENEOUS_COORDINATE_ACTION->IRREDUCIBLE_DECOMPOSITION = group::irreducible_decomposition($this->HOMOGENEOUS_COORDINATE_ACTION->CHARACTER, $this);
      }
      weight 1.10;

      rule FACETS_ACTION.IRREDUCIBLE_DECOMPOSITION : FACETS_ACTION.CHARACTER, CHARACTER_TABLE, CONJUGACY_CLASS_SIZES, ORDER {
         $this->FACETS_ACTION->IRREDUCIBLE_DECOMPOSITION = group::irreducible_decomposition($this->FACETS_ACTION->CHARACTER, $this);
      }
      weight 1.10;

      rule MATRIX_ACTION.IRREDUCIBLE_DECOMPOSITION : MATRIX_ACTION.CHARACTER, CHARACTER_TABLE, CONJUGACY_CLASS_SIZES, ORDER {
         $this->MATRIX_ACTION->IRREDUCIBLE_DECOMPOSITION = group::irreducible_decomposition($this->MATRIX_ACTION->CHARACTER, $this);
      }
      weight 1.10;

      rule RAYS_ACTION.IRREDUCIBLE_DECOMPOSITION : RAYS_ACTION.CHARACTER, CHARACTER_TABLE, CONJUGACY_CLASS_SIZES, ORDER {
         $this->RAYS_ACTION->IRREDUCIBLE_DECOMPOSITION = group::irreducible_decomposition($this->RAYS_ACTION->CHARACTER, $this);
      }
      weight 1.10;

      rule SET_ACTION.IRREDUCIBLE_DECOMPOSITION : SET_ACTION.CHARACTER, CHARACTER_TABLE, CONJUGACY_CLASS_SIZES, ORDER {
         $this->SET_ACTION->IRREDUCIBLE_DECOMPOSITION = group::irreducible_decomposition($this->SET_ACTION->CHARACTER, $this);
      }
      weight 1.10;

      #
      # some shortcuts
      #

      # @category Symmetry
      # explicit representatives of equivalence classes of [[polytope::INPUT_RAYS]] under a group action
      # @return Matrix
      user_method REPRESENTATIVE_INPUT_RAYS = INPUT_RAYS_ACTION.EXPLICIT_ORBIT_REPRESENTATIVE_MATRIX;

      # @category Symmetry
      # explicit representatives of equivalence classes of [[polytope::Cone::INEQUALITIES|INEQUALITIES]] under a group action
      # @return Matrix
      user_method REPRESENTATIVE_INEQUALITIES = INEQUALITIES_ACTION.EXPLICIT_ORBIT_REPRESENTATIVE_MATRIX;

      rule REPRESENTATIVE_RAYS = RAYS_ACTION.EXPLICIT_ORBIT_REPRESENTATIVE_MATRIX;

      rule REPRESENTATIVE_FACETS = FACETS_ACTION.EXPLICIT_ORBIT_REPRESENTATIVE_MATRIX;
   }

   #
   # Next, some specific rules
   #

   rule GROUP.SET_ACTION.INDEX_OF : GROUP.SET_ACTION.DOMAIN_NAME, MAX_INTERIOR_SIMPLICES | INTERIOR_RIDGE_SIMPLICES {
      my $dom = $this->GROUP->SET_ACTION->DOMAIN_NAME;
      $this->GROUP->SET_ACTION->INDEX_OF(temporary) = index_of($this->$dom);
   }
   weight 1.10;

   #
   # explicit representatives
   #

   rule GROUP.SET_ACTION.EXPLICIT_ORBIT_REPRESENTATIVES : GROUP.SET_ACTION.DOMAIN_NAME, GROUP.SET_ACTION.ORBIT_REPRESENTATIVES, MAX_INTERIOR_SIMPLICES | INTERIOR_RIDGE_SIMPLICES {
      my $domain_name = $this->GROUP->SET_ACTION->DOMAIN_NAME;
      my @reps = map { $this->$domain_name->[$_] } @{$this->GROUP->SET_ACTION->ORBIT_REPRESENTATIVES};
      $this->GROUP->SET_ACTION->EXPLICIT_ORBIT_REPRESENTATIVES = \@reps;
   }
   weight 1.10;

   rule GROUP.INPUT_RAYS_ACTION.EXPLICIT_ORBIT_REPRESENTATIVE_MATRIX : GROUP.INPUT_RAYS_ACTION.ORBIT_REPRESENTATIVES, INPUT_RAYS {
      $this->GROUP->INPUT_RAYS_ACTION->EXPLICIT_ORBIT_REPRESENTATIVE_MATRIX = $this->INPUT_RAYS->minor($this->GROUP->INPUT_RAYS_ACTION->ORBIT_REPRESENTATIVES, All);
   }
   weight 1.10;

   rule GROUP.RAYS_ACTION.EXPLICIT_ORBIT_REPRESENTATIVE_MATRIX : GROUP.RAYS_ACTION.ORBIT_REPRESENTATIVES, RAYS {
      $this->GROUP->RAYS_ACTION->EXPLICIT_ORBIT_REPRESENTATIVE_MATRIX = $this->RAYS->minor($this->GROUP->RAYS_ACTION->ORBIT_REPRESENTATIVES, All);
   }
   weight 1.10;

   rule GROUP.FACETS_ACTION.EXPLICIT_ORBIT_REPRESENTATIVE_MATRIX : GROUP.FACETS_ACTION.ORBIT_REPRESENTATIVES, FACETS {
      $this->GROUP->FACETS_ACTION->EXPLICIT_ORBIT_REPRESENTATIVE_MATRIX = $this->FACETS->minor($this->GROUP->FACETS_ACTION->ORBIT_REPRESENTATIVES, All);
   }
   weight 1.10;

   rule GROUP.MATRIX_ACTION.RAYS_ORBITS : GROUP.MATRIX_ACTION.GENERATORS, RAYS {
       $this->GROUP->MATRIX_ACTION->RAYS_ORBITS = group::induced_orbits_on_vectors_impl($this->GROUP->MATRIX_ACTION->GENERATORS, $this->RAYS);
   }
   weight 2.10;

   #
   # induce actions from others
   #

   #
   # induce RAYS_ACTION
   #
   rule GROUP.INPUT_RAYS_ACTION : GROUP.HOMOGENEOUS_COORDINATE_ACTION, INPUT_RAYS {
      $this->GROUP->INPUT_RAYS_ACTION = group::induce_permutation_action($this, "HOMOGENEOUS_COORDINATE_ACTION", "INPUT_RAYS", "input_ray_action", "induced from coordinate action", 1);
   }
   weight 1.10;

   rule GROUP.RAYS_ACTION : GROUP.FACETS_ACTION, FACETS_THRU_RAYS {
      $this->GROUP->RAYS_ACTION = group::induce_permutation_action($this, "FACETS_ACTION", "FACETS_THRU_RAYS", "ray_action", "induced from facet action", 0);
   }
   weight 1.10;

   rule GROUP.RAYS_ACTION : GROUP.HOMOGENEOUS_COORDINATE_ACTION, RAYS {
      $this->GROUP->RAYS_ACTION = group::induce_permutation_action($this, "HOMOGENEOUS_COORDINATE_ACTION", "RAYS", "ray_action", "induced from homogeneous coordinate action", 1);
   }
   weight 1.10;


   #
   # induce FACETS_ACTION
   #
   rule GROUP.FACETS_ACTION : GROUP.RAYS_ACTION, RAYS_IN_FACETS {
      $this->GROUP->FACETS_ACTION = group::induce_permutation_action($this, "RAYS_ACTION", "RAYS_IN_FACETS", "facet_action", "induced from ray action", 0);
   }
   weight 1.10;

   rule GROUP.FACETS_ACTION : GROUP.HOMOGENEOUS_COORDINATE_ACTION, FACETS {
      $this->GROUP->FACETS_ACTION = group::induce_permutation_action($this, "HOMOGENEOUS_COORDINATE_ACTION", "FACETS", "facet_action", "induced from homogeneous coordinate action", 1);
   }
   weight 1.10;


   #
   # induce MATRIX_ACTION
   #
   rule GROUP.MATRIX_ACTION : GROUP.INPUT_RAYS_ACTION, INPUT_RAYS {
       $this->GROUP->MATRIX_ACTION = group::induce_matrix_action($this, "INPUT_RAYS_ACTION", "INPUT_RAYS", new Matrix<Scalar>());
   }

   rule GROUP.MATRIX_ACTION : GROUP.RAYS_ACTION, RAYS {
       $this->GROUP->MATRIX_ACTION = group::induce_matrix_action($this, "RAYS_ACTION", "RAYS", new Matrix<Scalar>());
   }

   rule GROUP.MATRIX_ACTION : GROUP.INEQUALITIES_ACTION, INEQUALITIES {
       $this->GROUP->MATRIX_ACTION = group::induce_matrix_action($this, "INEQUALITIES_ACTION", "INEQUALITIES", new Matrix<Scalar>());
   }

   rule GROUP.MATRIX_ACTION : GROUP.FACETS_ACTION, FACETS {
       $this->GROUP->MATRIX_ACTION = group::induce_matrix_action($this, "FACETS_ACTION", "FACETS", new Matrix<Scalar>());
   }
   
   #
   # Others
   #

   rule GROUP.INEQUALITIES_ACTION : GROUP.HOMOGENEOUS_COORDINATE_ACTION, INEQUALITIES {
      $this->GROUP->INEQUALITIES_ACTION = group::induce_permutation_action($this, "HOMOGENEOUS_COORDINATE_ACTION", "INEQUALITIES", "inequalities_action", "induced from homogeneous coordinate action", 1);
   }
   weight 1.10;

   #
   # Orbits induced by coordinate actions
   #

   rule INPUT_RAYS, GROUP.INPUT_RAYS_ACTION.ORBITS, GROUP.INPUT_RAYS_ACTION.EXPLICIT_ORBIT_REPRESENTATIVE_MATRIX : GROUP.HOMOGENEOUS_COORDINATE_ACTION.GENERATORS, GROUP.HOMOGENEOUS_COORDINATE_ACTION.INPUT_RAYS_GENERATORS {
      my ($pts, $a) = induced_orbits($this, "HOMOGENEOUS_COORDINATE_ACTION", "INPUT_RAYS_GENERATORS", homog_action => 1);
      $this->INPUT_RAYS = $pts;
      $this->GROUP->INPUT_RAYS_ACTION = $a;
   }
   weight 2.10;
#    incurs PointsPerm;

   rule INPUT_RAYS, GROUP.INPUT_RAYS_ACTION.ORBITS, GROUP.INPUT_RAYS_ACTION.EXPLICIT_ORBIT_REPRESENTATIVE_MATRIX : GROUP.MATRIX_ACTION.GENERATORS, GROUP.MATRIX_ACTION.INPUT_RAYS_GENERATORS {
      my ($pts, $a) = induced_orbits($this, "MATRIX_ACTION", "INPUT_RAYS_GENERATORS", homog_action => 0);
      $this->INPUT_RAYS = $pts;
      $this->GROUP->INPUT_RAYS_ACTION = $a;
   }
   weight 2.10;
#    incurs PointsPerm;
   
   rule RAYS, GROUP.RAYS_ACTION.ORBITS, GROUP.RAYS_ACTION.EXPLICIT_ORBIT_REPRESENTATIVE_MATRIX : GROUP.HOMOGENEOUS_COORDINATE_ACTION.GENERATORS, GROUP.HOMOGENEOUS_COORDINATE_ACTION.RAYS_GENERATORS {
      my ($pts, $a) = induced_orbits($this, "HOMOGENEOUS_COORDINATE_ACTION", "RAYS_GENERATORS", homog_action => 1);
      $this->RAYS = $pts;
      $this->GROUP->RAYS_ACTION = $a;
   }
   weight 2.10;
   incurs VertexPerm;

   rule INEQUALITIES, GROUP.INEQUALITIES_ACTION.ORBITS, GROUP.INEQUALITIES_ACTION.EXPLICIT_ORBIT_REPRESENTATIVE_MATRIX : GROUP.HOMOGENEOUS_COORDINATE_ACTION.GENERATORS, GROUP.HOMOGENEOUS_COORDINATE_ACTION.INEQUALITIES_GENERATORS {
      my ($pts, $a) = induced_orbits($this, "HOMOGENEOUS_COORDINATE_ACTION", "INEQUALITIES_GENERATORS", homog_action => 1);
      $this->INEQUALITIES = $pts;
      $this->GROUP->INEQUALITIES_ACTION = $a;
   }
   weight 2.10;

   rule FACETS, GROUP.FACETS_ACTION.ORBITS, GROUP.FACETS_ACTION.EXPLICIT_ORBIT_REPRESENTATIVE_MATRIX : GROUP.HOMOGENEOUS_COORDINATE_ACTION.GENERATORS, GROUP.HOMOGENEOUS_COORDINATE_ACTION.FACETS_GENERATORS {
      my ($pts, $a) = induced_orbits($this, "HOMOGENEOUS_COORDINATE_ACTION", "FACETS_GENERATORS", homog_action => 1);
      $this->FACETS = $pts;
      $this->GROUP->FACETS_ACTION = $a;
   }
   weight 2.10;
   incurs FacetPerm;

   rule GROUP.IMPLICIT_SET_ACTION.CHARACTER : GROUP.PERMUTATION_ACTION.GENERATORS, GROUP.PERMUTATION_ACTION.CONJUGACY_CLASS_REPRESENTATIVES, GROUP.IMPLICIT_SET_ACTION.DOMAIN_NAME {
      $this->CHARACTER = implicit_character($this);
   }

}


object Polytope {

   property GROUP {

      # @category Symmetry
      property VERTICES_ACTION = override RAYS_ACTION;

      # @category Symmetry
      property COORDINATE_ACTION = override HOMOGENEOUS_COORDINATE_ACTION {

          property POINTS_GENERATORS = override INPUT_RAYS_GENERATORS;

          property N_POINTS_GENERATORS = override N_INPUT_RAYS_GENERATORS;

          property VERTICES_GENERATORS = override RAYS_GENERATORS;

          property N_VERTICES_GENERATORS = override N_RAYS_GENERATORS;
      }

      # @category Symmetry
      property POINTS_ACTION = override INPUT_RAYS_ACTION;

      # @category Symmetry
      property MATRIX_ACTION {

          property VERTICES_ORBITS = override VECTORS_ORBITS;

      };
      
      # @category Symmetry
      property REPRESENTATIVE_VERTICES = override REPRESENTATIVE_RAYS;

      rule ORDER : VERTICES_ACTION.TRANSVERSAL_SIZES | FACETS_ACTION.TRANSVERSAL_SIZES | COORDINATE_ACTION.TRANSVERSAL_SIZES {
          my $order = new Integer(1);
          $order *= $_ foreach(@{$this->lookup("VERTICES_ACTION.TRANSVERSAL_SIZES | FACETS_ACTION.TRANSVERSAL_SIZES | COORDINATE_ACTION.TRANSVERSAL_SIZES")});
          $this->ORDER = $order;
      }
      weight 1.10;

      user_method REPRESENTATIVE_INEQUALITIES = INEQUALITY_ACTION.EXPLICIT_ORBIT_REPRESENTATIVE_MATRIX;

   }


   rule GROUP.POINTS_ACTION : GROUP.COORDINATE_ACTION, POINTS {
      $this->GROUP->POINTS_ACTION = group::induce_permutation_action($this, "COORDINATE_ACTION", "POINTS", "points_action", "induced from coordinate_action");
   }
   weight 1.10;

   rule GROUP.POINTS_ACTION : GROUP.PERMUTATION_ACTION.GENERATORS {
      $this->GROUP->POINTS_ACTION = $this->GROUP->PERMUTATION_ACTION;
   }
   weight 0.10;
   precondition : GROUP.PERMUTATION_ACTION.DEGREE, N_POINTS { $this->GROUP->PERMUTATION_ACTION->DEGREE == $this->N_POINTS }

   rule GROUP.VERTICES_ACTION : GROUP.COORDINATE_ACTION, VERTICES {
      $this->GROUP->VERTICES_ACTION = group::induce_permutation_action($this, "COORDINATE_ACTION", "VERTICES", "vertices_action", "induced from coordinate_action");
   }
   weight 1.10;

   rule GROUP.VERTICES_ACTION : GROUP.PERMUTATION_ACTION.GENERATORS {
      $this->GROUP->VERTICES_ACTION = $this->GROUP->PERMUTATION_ACTION;
   }
   weight 0.10;
   precondition : GROUP.PERMUTATION_ACTION.DEGREE, N_VERTICES { $this->GROUP->PERMUTATION_ACTION->DEGREE == $this->N_VERTICES }

   rule GROUP.FACETS_ACTION : GROUP.COORDINATE_ACTION, FACETS {
      $this->GROUP->FACETS_ACTION = group::induce_permutation_action($this, "COORDINATE_ACTION", "FACETS", "facets_action", "induced from coordinate_action");
   }
   weight 1.10;

   rule GROUP.INEQUALITIES_ACTION : GROUP.COORDINATE_ACTION, INEQUALITIES {
      $this->GROUP->INEQUALITIES_ACTION = group::induce_permutation_action($this, "COORDINATE_ACTION", "INEQUALITIES", "inequalities_action", "induced from coordinate_action");
   }
   weight 1.10;

   rule POINTS, GROUP.POINTS_ACTION.ORBITS, GROUP.POINTS_ACTION.EXPLICIT_ORBIT_REPRESENTATIVE_MATRIX : GROUP.COORDINATE_ACTION.GENERATORS, GROUP.COORDINATE_ACTION.POINTS_GENERATORS {
      my ($pts, $a) = induced_orbits($this, "COORDINATE_ACTION", "POINTS_GENERATORS", homog_action => 0);
      $this->POINTS = $pts;
      $this->GROUP->POINTS_ACTION = $a;
   }
   weight 2.10;
#   incurs PointsPerm;

   rule VERTICES, GROUP.VERTICES_ACTION.ORBITS, GROUP.VERTICES_ACTION.EXPLICIT_ORBIT_REPRESENTATIVE_MATRIX : GROUP.COORDINATE_ACTION.GENERATORS, GROUP.COORDINATE_ACTION.VERTICES_GENERATORS {
      my ($pts, $a) = induced_orbits($this, "COORDINATE_ACTION", "VERTICES_GENERATORS", homog_action => 0);
      $this->VERTICES = $pts;
      $this->GROUP->VERTICES_ACTION = $a;
   }
   weight 2.10;
   incurs VertexPerm;

   rule INEQUALITIES, GROUP.INEQUALITIES_ACTION.ORBITS, GROUP.INEQUALITIES_ACTION.EXPLICIT_ORBIT_REPRESENTATIVE_MATRIX : GROUP.COORDINATE_ACTION.GENERATORS, GROUP.COORDINATE_ACTION.INEQUALITIES_GENERATORS {
       my ($pts, $a) = induced_orbits($this, "COORDINATE_ACTION", "INEQUALITIES_GENERATORS", homog_action => 0);
       $this->INEQUALITIES = $pts;
       $this->GROUP->INEQUALITIES_ACTION = $a;
   }
   weight 2.10;

   rule FACETS, GROUP.FACETS_ACTION.ORBITS, GROUP.FACETS_ACTION.EXPLICIT_ORBIT_REPRESENTATIVE_MATRIX : GROUP.COORDINATE_ACTION.GENERATORS, GROUP.COORDINATE_ACTION.FACETS_GENERATORS {
       my ($pts, $a) = induced_orbits($this, "COORDINATE_ACTION", "FACETS_GENERATORS", homog_action => 0);
       $this->FACETS = $pts;
       $this->GROUP->FACETS_ACTION = $a;
   }
   weight 2.10;
   incurs FacetPerm;

   rule GROUP.VERTICES_ACTION.EXPLICIT_ORBIT_REPRESENTATIVE_MATRIX : GROUP.VERTICES_ACTION.ORBIT_REPRESENTATIVES, VERTICES {
       $this->GROUP->VERTICES_ACTION->EXPLICIT_ORBIT_REPRESENTATIVE_MATRIX = $this->VERTICES->minor($this->GROUP->VERTICES_ACTION->ORBIT_REPRESENTATIVES, All);
   }

   rule GROUP.POINTS_ACTION.EXPLICIT_ORBIT_REPRESENTATIVE_MATRIX : GROUP.POINTS_ACTION.ORBIT_REPRESENTATIVES, VERTICES {
       $this->GROUP->POINTS_ACTION->EXPLICIT_ORBIT_REPRESENTATIVE_MATRIX = $this->POINTS->minor($this->GROUP->POINTS_ACTION->ORBIT_REPRESENTATIVES, All);
   }
   weight 1.10;

   rule GROUP.VERTICES_ACTION.EXPLICIT_ORBIT_REPRESENTATIVE_MATRIX : GROUP.VERTICES_ACTION.ORBIT_REPRESENTATIVES, VERTICES {
       $this->GROUP->VERTICES_ACTION->EXPLICIT_ORBIT_REPRESENTATIVE_MATRIX = $this->VERTICES->minor($this->GROUP->VERTICES_ACTION->ORBIT_REPRESENTATIVES, All);
   }
   weight 1.10;


   # Generates an array with a specified number of different colors.
   # @param int number      the number of colors
   # @return array          the colors as strings
   sub generateColors {
       my $number=$_[0];
   # not really implemented yet!
       my @FIFTEEN_COLORS=("chocolate1","salmon1","plum1","LightGreen","azure","LightSlateGrey","MidnightBlue","DarkOliveGreen","IndianRed","LavenderBlush","orange","green","red","blue","yellow");
       my @colors=();
       for(my $i=0;$i<$number;$i++) {
           if($i<15) {
               $colors[$i]=$FIFTEEN_COLORS[14-$i];
           } else {
               $colors[$i]="black";
           }
       }
       return @colors;
   }


   # @category Visualization
   # Visualizes the graph of a symmetric cone:
   # All nodes belonging to one orbit get the same color.
   # @return Visual::PolytopeGraph
   user_method VISUAL_ORBIT_COLORED_GRAPH(%Visual::Graph::decorations, { seed => undef }) {
       my ($this, $decor, $seed)=@_;
       my $VG=$this->VISUAL_GRAPH($decor, $seed);
       my @colors=generateColors($this->GROUP->RAYS_ACTION->N_ORBITS);
       my @nodeColors;
       for(my $i=0; $i<$this->GROUP->RAYS_ACTION->N_ORBITS; $i++){
           foreach my $ray_index(@{$this->GROUP->RAYS_ACTION->ORBITS->[$i]}){
               $nodeColors[$ray_index]=$colors[$i]; #each ray gets the color(=number) of its orbit
           }
       }
       $VG->basis_graph->NodeColor=\@nodeColors;
       visualize($VG);
   }

} # end Polytope

object VectorConfiguration {

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

        # @category Symmetry
        property MATRIX_ACTION : group::MatrixActionOnVectors<Scalar>;

        # @category Symmetry
        rule ORDER : VECTOR_ACTION.TRANSVERSAL_SIZES {
            $this->ORDER = $this->order_from_transversals("VECTOR_ACTION.TRANSVERSAL_SIZES");
        }
        weight 1.10;
        
    }

    rule GROUP.CONJUGACY_CLASS_SIZES : GROUP.VECTOR_ACTION.CONJUGACY_CLASSES | GROUP.MATRIX_ACTION.CONJUGACY_CLASSES {
        my $arr = $this->give("GROUP.VECTOR_ACTION.CONJUGACY_CLASSES | GROUP.MATRIX_ACTION.CONJUGACY_CLASSES");
        $this->GROUP->CONJUGACY_CLASS_SIZES = [ map {$_->size} @{$arr} ];
    }
    weight 1.10;

    
   rule GROUP.VECTOR_ACTION : GROUP.PERMUTATION_ACTION.GENERATORS, GROUP.PERMUTATION_ACTION.CONJUGACY_CLASS_REPRESENTATIVES {
      $this->GROUP->VECTOR_ACTION = $this->GROUP->PERMUTATION_ACTION;
   }
   weight 0.10;
   precondition : GROUP.PERMUTATION_ACTION.DEGREE, N_VECTORS { $this->GROUP->PERMUTATION_ACTION->DEGREE == $this->N_VECTORS }

   rule GROUP.MATRIX_ACTION : GROUP.VECTOR_ACTION, VECTORS {
       $this->GROUP->MATRIX_ACTION = group::induce_matrix_action($this, "VECTOR_ACTION", "VECTORS", new Matrix<Scalar>());
   }

   rule GROUP.MATRIX_ACTION.IRREDUCIBLE_DECOMPOSITION : GROUP.MATRIX_ACTION.CHARACTER, GROUP.CHARACTER_TABLE, GROUP.CONJUGACY_CLASS_SIZES, GROUP.ORDER {
        $this->GROUP->MATRIX_ACTION->IRREDUCIBLE_DECOMPOSITION = group::irreducible_decomposition($this->GROUP->MATRIX_ACTION->CHARACTER, $this->GROUP);
   }
    weight 1.10;

    rule GROUP.MATRIX_ACTION.VECTORS_ORBITS : GROUP.MATRIX_ACTION.GENERATORS, VECTORS {
        $this->GROUP->MATRIX_ACTION->VECTORS_ORBITS = group::induced_orbits_on_vectors_impl($this->GROUP->MATRIX_ACTION->GENERATORS, $this->VECTORS);
    }
    weight 2.10;

}

object PointConfiguration {

    # @category Symmetry
    property GROUP {

        # @category Symmetry
        property POINTS_ACTION = override VECTOR_ACTION;

        property MATRIX_ACTION {

            # @category Symmetry
            property POINTS_ORBITS = override VECTORS_ORBITS;

        };
    }
}


# @category Symmetry
# Compute the combinatorial symmetries (i.e., automorphisms of the face lattice) of
# a given polytope //p//. They are stored in terms of a GROUP.VERTICES_ACTION and a GROUP.FACETS_ACTION
# property in //p//, and the GROUP.VERTICES_ACTION is also returned.
# @param Polytope p
# @return group::PermutationAction the action of the combinatorial symmetry group on the vertices
# @example To get the vertex symmetry group of the square and print its generators, type the following:
# > print combinatorial_symmetries(cube(2))->GENERATORS;
# | 2 3 0 1
# | 1 0 2 3
# > $p = cube(2); combinatorial_symmetries($p);
# > print $p->GROUP->VERTICES_ACTION->GENERATORS;
# | 2 3 0 1
# | 1 0 2 3
# > print $p->GROUP->FACETS_ACTION->GENERATORS;
# | 0 2 1 3
# | 1 0 3 2

user_function combinatorial_symmetries(polytope::Cone) {
    my ($p) = @_;
    return group::combinatorial_symmetries_impl($p, "RAYS_IN_FACETS", "RAYS_ACTION", "FACETS_ACTION");
}

# @category Symmetry
# Constructs the orbit polytope of a given point //input_point//
# with respect to a given group action //a//.
# @param Vector input_point the basis point of the orbit polytope
# @param group::PermutationAction a the action of a permutation group on the coordinates of the ambient space
# @return Polytope the orbit polytope of //input_point// w.r.t. the action //a//
# @example The //orbit polytope// of a set of points //A// in affine d-space is the convex hull of the images of //A// under the action of
# a group //G// on the affine space. polymake implements several variations of this concept. The most basic one is the convex hull of the
# orbit of a single point under a set of coordinate permutations.
# For example, consider the cyclic group //C_6// that acts on 6-dimensional space by cyclically permuting the coordinates. This action is
# represented in polymake by group::cyclic_group(6)->PERMUTATION_ACTION. 
# To compute the convex hull of cyclic shifts of the vector //v// = [1,6,0,5,-5,0,-5] in homogeneous coordinates, type
# > $p = orbit_polytope(new Vector([1,6,0,5,-5,0,-5]), group::cyclic_group(6)->PERMUTATION_ACTION);
#
# After this assignment, the orbit polytope is still in implicit form, and the only properties that are defined reside in GROUP->COORDINATE_ACTION:
# > print $p->GROUP->COORDINATE_ACTION->properties();
# | type: PermutationAction<Int, Rational> as Polytope<Rational>::GROUP::COORDINATE_ACTION
# | 
# | GENERATORS
# | 1 2 3 4 5 0
# | 
# | 
# | INPUT_RAYS_GENERATORS
# | 1 6 0 5 -5 0 -5
#
# To calculate the vertices of the orbit polytope explicitly, say
# > print $p->VERTICES;
# | 1 -5 0 -5 6 0 5
# | 1 -5 6 0 5 -5 0
# | 1 0 -5 6 0 5 -5
# | 1 0 5 -5 0 -5 6
# | 1 5 -5 0 -5 6 0
# | 1 6 0 5 -5 0 -5

user_function orbit_polytope(Vector, group::PermutationAction) {
    my ($input_point, $a) = @_;
    return orbit_polytope(vector2row($input_point), $a->GENERATORS);
}

# @category Symmetry
# Constructs the orbit polytope of a given set of points //input_points//
# with respect to a given group action //a//.
# @param Matrix input_points the basis points of the orbit polytope
# @param group::PermutationAction a the action of a permutation group on the coordinates of the ambient space
# @return Polytope the orbit polytope of //input_points// w.r.t. the action //a//
# @example To find the orbit of more than one point under a PermutationAction on the coordinates, say
# > $p = orbit_polytope(new Matrix([ [1,6,0,5,-5,0,-5], [1,1,2,3,4,5,6] ]), new group::PermutationAction(GENERATORS=>[ [1,2,3,4,5,0] ]));
# > print $p->VERTICES;
# | 1 -5 0 -5 6 0 5
# | 1 -5 6 0 5 -5 0
# | 1 0 -5 6 0 5 -5
# | 1 0 5 -5 0 -5 6
# | 1 5 -5 0 -5 6 0
# | 1 6 0 5 -5 0 -5
# | 1 1 2 3 4 5 6
# | 1 2 3 4 5 6 1
# | 1 3 4 5 6 1 2
# | 1 4 5 6 1 2 3
# | 1 5 6 1 2 3 4
# | 1 6 1 2 3 4 5

user_function orbit_polytope(Matrix, group::PermutationAction) {
    my ($input_points, $a) = @_;
    return orbit_polytope($input_points, $a->GENERATORS);
}

# @category Symmetry
# Constructs the orbit polytope of a given point //input_point//
# with respect to a given group action //a//.
# @param Vector input_point the basis point of the orbit polytope
# @param group::Group g a group with a PERMUTATION_ACTION that acts on the coordinates of the ambient space
# @return Polytope the orbit polytope of //input_point// w.r.t. the action of //g//
# @example As a convenience function, you can also directly specify a group::Group that contains a PERMUTATION_ACTION:
# > $p = orbit_polytope(new Vector([1,6,0,5,-5,0,-5]), group::cyclic_group(6));
#
# Up to now, the orbit polytope is still in implicit form. To calculate the vertices explicitly, say
# > print $p->VERTICES;
# | 1 -5 0 -5 6 0 5
# | 1 -5 6 0 5 -5 0
# | 1 0 -5 6 0 5 -5
# | 1 0 5 -5 0 -5 6
# | 1 5 -5 0 -5 6 0
# | 1 6 0 5 -5 0 -5

user_function orbit_polytope(Vector, group::Group) {
    my ($input_point, $a) = @_;
    return orbit_polytope(vector2row($input_point), $a->PERMUTATION_ACTION->GENERATORS);
}

# @category Symmetry
# Constructs the orbit polytope of a given set of points //input_points//
# with respect to a given group action //a//.
# @param Matrix input_points the basis points of the orbit polytope
# @param group::Group g a group with a PERMUTATION_ACTION that acts on the coordinates of the ambient space
# @return Polytope the orbit polytope of //input_points// w.r.t. the action of //g//
# @example As a convenience function, you can also directly specify a group::Group that contains a PERMUTATION_ACTION:
# > $p = orbit_polytope(new Matrix([ [1,6,0,5,-5,0,-5], [1,1,2,3,4,5,6] ]), group::cyclic_group(6));
# > print $p->VERTICES;
# | 1 -5 0 -5 6 0 5
# | 1 -5 6 0 5 -5 0
# | 1 0 -5 6 0 5 -5
# | 1 0 5 -5 0 -5 6
# | 1 5 -5 0 -5 6 0
# | 1 6 0 5 -5 0 -5
# | 1 1 2 3 4 5 6
# | 1 2 3 4 5 6 1
# | 1 3 4 5 6 1 2
# | 1 4 5 6 1 2 3
# | 1 5 6 1 2 3 4
# | 1 6 1 2 3 4 5

user_function orbit_polytope(Matrix, group::Group) {
    my ($input_points, $a) = @_;
    return orbit_polytope($input_points, $a->PERMUTATION_ACTION->GENERATORS);
}

# @category Symmetry
# Constructs the orbit polytope of a given set of points //input_points//
# with respect to a given set of generators //gens//.
# @param Matrix input_points the basis point of the orbit polytope
# @param Array<Array<Int>> gens the generators of a permutation group that acts on the coordinates of the ambient space
# @return Polytope the orbit polytope of //input_points// w.r.t. the coordinate action generated by //gens//
# @example This is a variation where several points are given as the row of a matrix, and the permutation action
# on coordinates is given by explicitly listing the generators. In this example, the matrix has just one row,
# and there is just one generator.
# > print orbit_polytope(new Matrix([ [1,6,0,5,-5,0,-5] ]), [ [1,2,3,4,5,0] ])->VERTICES;
# | 1 -5 0 -5 6 0 5
# | 1 -5 6 0 5 -5 0
# | 1 0 -5 6 0 5 -5
# | 1 0 5 -5 0 -5 6
# | 1 5 -5 0 -5 6 0
# | 1 6 0 5 -5 0 -5


user_function orbit_polytope(Matrix, @) {
    my ($input_points, $gens) = @_;
    my $a = new group::PermutationAction(GENERATORS=>$gens, INPUT_RAYS_GENERATORS=>$input_points);
    my $g = new group::Group;
    my $p = new Polytope;
    $p->add("GROUP", $g, COORDINATE_ACTION=>$a);
    return $p;
}

# @category Symmetry
# Constructs the orbit polytope of a given point //input_point//
# with respect to a given matrix group action //a//.
# @param Vector input_point the generating point of the orbit polytope
# @param group::MatrixActionOnVectors a the action of a matrix group on the coordinates of the ambient space
# @tparam Scalar S the underlying number type
# @return Polytope the orbit polytope of //input_point// w.r.t. the action //a//
# @example polymake also supports orbit polytopes under the action of a group by matrices. 
# To find the orbit of a point in the plane under the symmetry group of the square, say
# > $p = orbit_polytope(new Vector([1,2,1]), cube(2, group=>1)->GROUP->MATRIX_ACTION);
# > print $p->VERTICES;
# | 1 -2 -1
# | 1 -2 1
# | 1 -1 -2
# | 1 -1 2
# | 1 1 -2
# | 1 1 2
# | 1 2 -1
# | 1 2 1

user_function orbit_polytope<Scalar>(Vector<Scalar>, group::MatrixActionOnVectors<Scalar>) {
    my ($input_point, $a) = @_;
    return orbit_polytope(vector2row($input_point), $a);
}

# @category Symmetry
# Constructs the orbit polytope of a given set of points //input_points//
# with respect to a given matrix group action //a//.
# @param Matrix<Scalar> input_points the generating points of the orbit polytope
# @param group::MatrixActionOnVectors<Scalar> a the action of a matrix group on the coordinates of the ambient space
# @tparam Scalar S the underlying number type
# @return Polytope the orbit polytope of the //input_points// w.r.t. the action //a//
# @example To find the orbit of more than one point in the plane under the symmetry group of the square, say
# > $p = orbit_polytope(new Matrix([ [1,2,1], [1,5/2,0] ]), cube(2, group=>1)->GROUP->MATRIX_ACTION);
# > print $p->VERTICES;
# | 1 -2 -1
# | 1 -2 1
# | 1 -1 -2
# | 1 -1 2
# | 1 1 -2
# | 1 1 2
# | 1 2 -1
# | 1 2 1
# | 1 -5/2 0
# | 1 0 -5/2
# | 1 0 5/2
# | 1 5/2 0

user_function orbit_polytope<Scalar>(Matrix<Scalar>, group::MatrixActionOnVectors<Scalar>) {
    my ($input_points, $a) = @_;
    my $aind = new group::MatrixActionOnVectors<Scalar>(GENERATORS=>$a->GENERATORS, INPUT_RAYS_GENERATORS=>$input_points);
    my $g = new group::Group;
    my $p = new Polytope<Scalar>;
    $p->add("GROUP", $g, MATRIX_ACTION=>$aind);
    return $p;
}

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

