#############################################################################
##
##  Polytope.gi         ConvexForHomalg package         Sebastian Gutsche
##
##  Copyright 2011 Lehrstuhl B für Mathematik, RWTH Aachen
##
##  Cones for ConvexForHomalg.
##
#############################################################################

####################################
##
## Reps
##
####################################

DeclareRepresentation( "IsExternalPolytopeRep",
                       IsPolytope and IsExternalConvexObjectRep,
                       [ ]
                      );

DeclareRepresentation( "IsPolymakePolytopeRep",
                       IsExternalPolytopeRep,
                       [ ]
                      );

DeclareRepresentation( "IsInternalPolytopeRep",
                       IsPolytope and IsInternalConvexObjectRep,
                       [ ]
                      );

####################################
##
## Types and Families
##
####################################


BindGlobal( "TheFamilyOfPolytopes",
        NewFamily( "TheFamilyOfPolytopes" , IsPolytope ) );

BindGlobal( "TheTypeExternalPolytope",
        NewType( TheFamilyOfPolytopes,
                 IsPolytope and IsExternalPolytopeRep ) );

BindGlobal( "TheTypePolymakePolytope",
        NewType( TheFamilyOfPolytopes,
                 IsPolymakePolytopeRep ) );

BindGlobal( "TheTypeInternalPolytope",
        NewType( TheFamilyOfPolytopes,
                 IsInternalPolytopeRep ) );

####################################
##
## Properties
##
####################################

##
InstallMethod( IsNotEmpty,
               "for polytopes",
               [ IsPolytope ],
               
  function( polytope )
    
    if IsBound( polytope!.input_points ) and Length( polytope!.input_points ) > 0 then
        
        return true;
        
    fi;
    
    TryNextMethod();
    
end );

##
InstallMethod( IsNotEmpty,
               " for external polytopes.",
               [ IsExternalPolytopeRep ],
               
  function( polytope )
    
    return EXT_IS_NOT_EMPTY_POLYTOPE( ExternalObject( polytope ) );
    
end );

##
InstallMethod( IsVeryAmple,
               " for external polytopes.",
               [ IsExternalPolytopeRep ],
               
  function( polytope )
    
    return EXT_IS_VERY_AMPLE_POLYTOPE( ExternalObject( polytope ) );
    
end );

##
InstallMethod( IsNormalPolytope,
               " for external polytopes.",
               [ IsExternalPolytopeRep ],
               
  function( polytope )
    
    return EXT_IS_NORMAL_POLYTOPE( ExternalObject( polytope ) );
    
end );

##
InstallMethod( IsSimplicial,
               " for external polytopes.",
               [ IsExternalPolytopeRep ],
               
  function( polytope )
    
    return EXT_IS_SIMPLICIAL_POLYTOPE( ExternalObject( polytope ) );
    
end );

##
InstallMethod( IsSimplePolytope,
               " for external polytopes.",
               [ IsExternalPolytopeRep ],
               
  function( polytope )
    
    return EXT_IS_SIMPLE_POLYTOPE( ExternalObject( polytope ) );
    
end );

InstallMethod( IsLatticePolytope,
               "for polytopes",
               [ IsPolytope ],
               
  function( polytope )
    
    if IsBound( polytope!.input_points ) and ForAll( Flat( polytope!.input_points ), IsInt ) then
        
        return true;
        
    fi;
    
end );

##
InstallMethod( IsLatticePolytope,
               " for external polytopes.",
               [ IsExternalPolytopeRep ],
               
  function( polytope )
    
    return EXT_IS_LATTICE_POLYTOPE( ExternalObject( polytope ) );
    
end );

##
InstallMethod( IsBounded,
               " for external polytopes.",
               [ IsExternalPolytopeRep ],
               
  function( polytope )
    
    return EXT_IS_BOUNDED_POLYTOPE( ExternalObject( polytope ) );
    
end );

####################################
##
## Attribute
##
####################################

##
InstallMethod( ExternalObject,
               "for external polytopes",
               [ IsExternalPolytopeRep ],
               
  function( polytope )
    
    if IsBound( polytope!.input_points ) and IsBound( polytope!.input_ineqs ) then
        
        Error( "points and inequalities at the same time are not supported\n" );
        
    fi;
    
    if IsBound( polytope!.input_points ) then
        
        return EXT_CREATE_POLYTOPE_BY_POINTS( polytope!.input_points );
        
    elif IsBound( polytope!.input_ineqs ) then
        
        return EXT_CREATE_POLYTOPE_BY_INEQUALITIES( polytope!.input_ineqs );
        
    else
        
        Error( "something went wrong\n" );
        
    fi;
    
end );

##
InstallMethod( LatticePoints,
               "for polytopes (fallback)",
               [ IsPolytope ],
               
  function( polytope )
    local vertices, max, ineqs, points, min_vec, lenght, max_vec, i, j, k;
    
    if not HasDefiningInequalities( polytope ) and IsBound( polytope!.input_ineqs ) then
        
        ineqs := polytope!.input_ineqs;
        
    else
        
        ineqs := DefiningInequalities( polytope );
        
    fi;
    
    if HasVertices( polytope ) then
        
        vertices := Vertices( polytope );
        
        min_vec := List( TransposedMat( vertices ), k -> Minimum( k ) );
        
        max_vec := List( TransposedMat( vertices ), k -> Maximum( k ) );
        
    else
        
        max := Maximum( List( ineqs, i -> AbsoluteValue( i[ 1 ] ) ) );
        
        min_vec := List( [ 1 .. Length( ineqs[ 1 ] ) - 1 ], i -> - max );
        
        max_vec := List( [ 1 .. Length( ineqs[ 1 ] ) - 1 ], i -> max );
        
    fi;
    
    ## GAP has changed the behavior on list. Shit!
    i := ShallowCopy( min_vec );
    
    lenght := Length( min_vec );
    
    points := [ ];
    
    while i[ lenght ] <= max_vec[ lenght ] do
        
        if ForAll( ineqs, j -> Sum( [ 1 .. lenght ], k -> j[ k + 1 ] * i[ k ] ) >= - j[ 1 ] ) then
            
            Add( points, ShallowCopy( i ) );
            
        fi;
        
        k := 1;
        
        while k <= lenght and i[ k ] = max_vec[ k ] do
            
            k := k + 1;
            
        od;
        
        if k > lenght then
            
            break;
            
        fi;
        
        i[ k ] := i[ k ] + 1;
        
        for j in [ 1 .. k - 1 ] do
            
            i[ j ] := min_vec[ j ];
            
        od;
        
    od;
    
    return points;
    
end );

##
InstallMethod( LatticePoints,
               "for external polytopes",
               [ IsExternalPolytopeRep ],
               
  function( polytope )
    
    if not IsBounded( polytope ) then
        
        Error( "calculating lattice points for unbounded polyhedron, output is useless and false\n" );
        
    fi;
    
    return EXT_LATTICE_POINTS_OF_POLYTOPE( ExternalObject( polytope ) );
    
end );

##
InstallMethod( VerticesOfPolytope,
               "for internal polytopes",
               [ IsInternalPolytopeRep ],
               
  function( polytope )
    local inequalities, dimension, inequality_combinations, intersection_point, ineq, rhs, lhs, vertices;
    
    if IsBound( polytope!.input_points ) then
        
        return polytope!.input_points;
        
    fi;
    
    vertices := [ ];
    
    inequalities := FacetInequalities( polytope );
    
    dimension := AmbientSpaceDimension( polytope );
    
    inequality_combinations := Combinations( inequalities, dimension );
    
    for ineq in inequality_combinations do
        
        rhs := List( ineq, i -> - i[ 1 ] );
        
        lhs := List( ineq , i -> i{ [ 2 .. Length( i ) ] } );
        
        rhs := HomalgMatrix( rhs, 1, Length( rhs ), HOMALG_MATRICES.ZZ );
        
        lhs := HomalgMatrix( TransposedMat( lhs ), HOMALG_MATRICES.ZZ );
        
        ## RightDivide( B, A ) solves XA = B. -_-
        intersection_point := RightDivide( rhs, lhs );
        
        if intersection_point = fail then
            
            continue;
            
        fi;
        
        intersection_point := EntriesOfHomalgMatrix( intersection_point );
        
        if ForAll( Difference( inequalities, ineq ), i -> Sum( [ 2 .. Length( i ) ], j -> i[ j ] * intersection_point[ j - 1 ] ) >= - i[ 1 ] ) then
            
            Add( vertices, intersection_point );
            
        fi;
        
    od;
    
    return vertices;
    
end );

##
InstallMethod( VerticesOfPolytope,
               "for external polytopes",
               [ IsExternalPolytopeRep ],
               
  function( polytope )
    
    return EXT_VERTICES_OF_POLYTOPE( ExternalObject( polytope ) );
    
end );

##
InstallMethod( Vertices,
               "for compatibility",
               [ IsPolytope ],
               
  VerticesOfPolytope
  
);

##
InstallMethod( SetVertices,
               "for compatibility",
               [ IsPolytope, IsObject ],
               
  SetVerticesOfPolytope
  
);

##
InstallMethod( HasVertices,
               "for compatibility",
               [ IsPolytope ],
               
  HasVerticesOfPolytope
  
);

##
InstallMethod( FacetInequalities,
               "for internal polytopes",
               [ IsInternalPolytopeRep ],
               
  function( polytope )
    local vertices, vertices_collection, i, linear_subspace, kernel, inequality, j, k, testA, testB,
          sum_of_vert, new_ineqs;
    
    if IsBound( polytope!.input_ineqs ) then
        
        return polytope!.input_ineqs;
        
    fi;
    
    vertices := Vertices( polytope );
    
    vertices_collection := Combinations( vertices, AmbientSpaceDimension( polytope ) );
    
    new_ineqs := [ ];
    
    for i in vertices_collection do
        
        linear_subspace := List( [ 2 .. Length( i ) ], k -> i[ k ] - i[ 1 ] );
        
        linear_subspace := HomalgMatrix( linear_subspace, HOMALG_MATRICES.ZZ );
        
        kernel := KernelSubobject( HomalgMap( Involution( linear_subspace ), "free", "free" ) );
        
        kernel := GeneratingElements( kernel );
        
        if Length( kernel ) > 1 then
            
            Error( "Wrong kernel, this should not happen\n" );
            
        fi;
        
        if Length( kernel ) < 1 then
            
            continue;
            
        fi;
        
        kernel := UnderlyingListOfRingElements( kernel[ 1 ] );
        
        inequality := Sum( [ 1 .. Length( kernel ) ], k -> i[ 1 ][ k ] * kernel[ k ] );
        
        testA := true;
        
        testB := true;
        
        for k in Difference( vertices, i ) do
            
            sum_of_vert := Sum( [ 1 .. Length( k ) ], j -> k[ j ] * kernel[ j ] );
            
            testA := testA and sum_of_vert >= inequality;
            
            testB := testB and sum_of_vert <= inequality;
            
        od;
        
        ## Both can be fulfilled
        if testA then
            
            Add( new_ineqs, Concatenation( [ - inequality ], kernel ) );
            
        fi;
        
        if testB then
            
            Add( new_ineqs, Concatenation( [ inequality ], -kernel ) );
            
        fi;
        
    od;
    
    return new_ineqs;
    
end );

##
InstallMethod( FacetInequalities,
               " for external polytopes",
               [ IsExternalPolytopeRep ],
               
  function( polytope )
    
    return EXT_FACET_INEQUALITIES_OF_POLYTOPE( ExternalObject( polytope ) );
    
end );

##
InstallMethod( VerticesInFacets,
               "for polytopes",
               [ IsPolytope ],
               
  function( polytope )
    local ineqs, vertices, dim, incidence_matrix, i, j;
    
    ineqs := FacetInequalities( polytope );
    
    vertices := Vertices( polytope );
    
    if Length( vertices ) = 0 then
        
        return ListWithIdenticalEntries( Length( ineqs ), [ ] );
        
    fi;
    
    dim := Length( vertices[ 1 ] );
    
    incidence_matrix := List( [ 1 .. Length( ineqs ) ], i -> ListWithIdenticalEntries( Length( vertices ), 0 ) );
    
    for i in [ 1 .. Length( incidence_matrix ) ] do
        
        for j in [ 1 .. Length( vertices ) ] do
            
            if Sum( [ 1 .. dim ], k -> ineqs[ i ][ k + 1 ] * vertices[ j ][ k ] ) = - ineqs[ i ][ 1 ] then
                
                incidence_matrix[ i ][ j ] := 1;
                
            fi;
            
        od;
        
    od;
    
    return incidence_matrix;
    
end );

##
InstallMethod( VerticesInFacets,
               " for external polytopes",
               [ IsExternalPolytopeRep ],
               
  function( polytope )
    
    return EXT_VERTICES_IN_FACETS( ExternalObject( polytope ) );
    
end );

##
InstallMethod( NormalFan,
               " for external polytopes",
               [ IsPolytope ],
               
  function( polytope )
    local ineqs, vertsinfacs, fan, i, aktcone, j;
    
    ineqs := FacetInequalities( polytope );
    
    ineqs := List( ineqs, i -> i{ [ 2 .. Length( i ) ] } );
    
    vertsinfacs := VerticesInFacets( polytope );
    
    vertsinfacs := TransposedMat( vertsinfacs );
    
    fan := [ ];
    
    for i in vertsinfacs do
        
        aktcone := [ ];
        
        for j in [ 1 .. Length( i ) ] do
            
            if i[ j ] = 1 then
                
                Add( aktcone,  j  );
                
            fi;
            
        od;
        
        Add( fan, aktcone );
        
    od;
    
    fan := Fan( ineqs, fan );
    
    SetIsRegularFan( fan, true );
    
    SetIsComplete( fan, true );
    
    return fan;
    
end );

##
InstallMethod( AffineCone,
               " for homalg polytopes",
               [ IsPolytope ],
               
  function( polytope )
    local cone, newcone, i, j;
    
    cone := Vertices( polytope );
    
    newcone := [ ];
    
    for i in cone do
        
        j := ShallowCopy( i );
        
        Add( j, 1 );
        
        Add( newcone, j );
        
    od;
    
    return Cone( newcone );
    
end );

##
InstallMethod( RelativeInteriorLatticePoints,
               "for polytopes",
               [ IsInternalPolytopeRep ],
               
  function( polytope )
    local lattice_points, ineqs, inner_points, i, j;
    
    lattice_points := LatticePoints( polytope );
    
    ineqs := FacetInequalities( polytope );
    
    inner_points := [ ];
    
    for i in lattice_points do
        
        if ForAll( ineqs, j -> Sum( [ 1 .. Length( i ) ], k -> j[ k + 1 ] * i[ k ] ) > - j[ 1 ] ) then
            
            Add( inner_points, i );
            
        fi;
        
    od;
    
    return inner_points;
    
end );

##
InstallMethod( RelativeInteriorLatticePoints,
               " for external polytopes",
               [ IsExternalPolytopeRep ],
               
  function( polytope )
    
    return EXT_INT_LATTICE_POINTS( ExternalObject( polytope ) );
    
end );

##
InstallMethod( EqualitiesOfPolytope,
               "for external polytopes",
               [ IsExternalPolytopeRep ],
               
  function( polytope )
    
    return EXT_EQUALITIES_OF_POLYTOPE( ExternalObject( polytope ) );
    
end );

##
InstallMethod( DefiningInequalities,
               "for polytope",
               [ IsPolytope ],
               
  function( polytope )
    
    return Concatenation( FacetInequalities( polytope ), EqualitiesOfPolytope( polytope ), - EqualitiesOfPolytope( polytope ) );
    
end );

##
InstallMethod( LatticePointsGenerators,
               "for polytopes",
               [ IsExternalPolytopeRep ],
               
  function( polytope )
    
    return EXT_LATTICE_POINTS_GENERATORS( ExternalObject( polytope ) );
    
end );

####################################
##
## Methods
##
####################################

##
InstallMethod( \*,
               "for polytopes",
               [ IsPolytope, IsPolytope ],
               
  function( polytope1, polytope2 )
    local vertices1, vertices2, new_vertices, i, j;
    
    vertices1 := Vertices( polytope1 );
    
    vertices2 := Vertices( polytope2 );
    
    new_vertices := [ ];
    
    for i in vertices1 do
        
        for j in vertices2 do
            
            Add( new_vertices, Concatenation( i, j ) );
            
        od;
        
    od;
    
    return Polytope( new_vertices );
    
end );

##
InstallMethod( \+,
               "for polytopes",
               [ IsExternalPolytopeRep, IsExternalPolytopeRep ],
               
  function( polytope1, polytope2 )
    local new_polytope, new_ext;
    
    ##Maybe same grid, but this might be complicated
    if not Rank( ContainingGrid( polytope1 ) ) = Rank( ContainingGrid( polytope2 ) ) then
        
        Error( "polytopes are not of the same dimension" );
        
    fi;
    
    new_polytope := rec();
    
    new_ext := EXT_MINKOWSKI_SUM( ExternalObject( polytope1 ), ExternalObject( polytope2 ) );
    
    ObjectifyWithAttributes( new_polytope, TheTypeExternalPolytope,
                             ExternalObject, new_ext );
    
    SetContainingGrid( new_polytope, ContainingGrid( polytope1 ) );
    
    SetAmbientSpaceDimension( new_polytope, AmbientSpaceDimension( polytope1 ) );
    
    return new_polytope;
    
end );

##
InstallMethod( \+,
               "for polytopes",
               [ IsPolytope, IsPolytope ],
               
  function( polytope1, polytope2 )
    local vertices1, vertices2, new_polytope;
    
    ##Maybe same grid, but this might be complicated
    if not Rank( ContainingGrid( polytope1 ) ) = Rank( ContainingGrid( polytope2 ) ) then
        
        Error( "polytopes are not of the same dimension" );
        
    fi;
    
    vertices1 := Vertices( polytope1 );
    
    vertices2 := Vertices( polytope2 );
    
    new_polytope := Concatenation( List( vertices1, i -> List( vertices2, j -> i + j ) ) );
    
    new_polytope := Polytope( new_polytope );
    
    SetContainingGrid( new_polytope, ContainingGrid( polytope1 ) );
    
    return new_polytope;
    
end );

##
InstallMethod( IntersectionOfPolytopes,
               "for homalg cones",
               [ IsExternalPolytopeRep, IsExternalPolytopeRep ],
               
  function( cone1, cone2 )
    local cone, ext_cone;
    
    ## Fix this later!!
    if not IsIdenticalObj( AmbientSpaceDimension( cone1 ), AmbientSpaceDimension( cone2 ) ) then
        
        Error( "polytope are not from the same grid" );
        
    fi;
    
    ext_cone := EXT_INTERSECTION_OF_POLYTOPES( ExternalObject( cone1 ), ExternalObject( cone2 ) );
    
    cone := Polytope( ext_cone );
    
    SetContainingGrid( cone, ContainingGrid( cone1 ) );
    
    SetAmbientSpaceDimension( cone, AmbientSpaceDimension( cone1 ) );
    
    return cone;
    
end );

####################################
##
## Constructors
##
####################################

##
InstallMethod( PolymakePolytope,
               "creates a PolymakePolytope.",
               [ IsList ],
               
  function( pointlist )
    local polyt, extpoly;
    
    polyt := rec( input_points := pointlist );
    
     ObjectifyWithAttributes( 
        polyt, TheTypePolymakePolytope,
        IsBounded, true
     );
     
     if not pointlist = [ ] then
        
        SetAmbientSpaceDimension( polyt, Length( pointlist[ 1 ] ) );
        
     fi;
     
     return polyt;
     
end );


##
InstallMethod( PolymakePolytopeByInequalities,
               "creates a PolymakePolytope.",
               [ IsList ],
               
  function( pointlist )
    local extpoly, polyt;
    
    polyt := rec( input_ineqs := pointlist );
    
    ObjectifyWithAttributes( 
        polyt, TheTypePolymakePolytope
    );
    
    if not pointlist = [ ] then
        
        SetAmbientSpaceDimension( polyt, Length( pointlist[ 1 ] ) -1 );
        
    fi;
     
     return polyt;
     
end );

##
InstallMethod( InternalPolytope,
               "creates an internal polytope.",
               [ IsList ],
               
  function( pointlist )
    local polyt, extpoly;
    
    polyt := rec( input_points := pointlist );
    
     ObjectifyWithAttributes( 
        polyt, TheTypeInternalPolytope,
        IsBounded, true
     );
     
     if not pointlist = [ ] then
        
        SetAmbientSpaceDimension( polyt, Length( pointlist[ 1 ] ) );
        
     fi;
     
     return polyt;
     
end );


##
InstallMethod( InternalPolytopeByInequalities,
               "creates a internal polytope.",
               [ IsList ],
               
  function( pointlist )
    local extpoly, polyt;
    
    polyt := rec( input_ineqs := pointlist );
    
    ObjectifyWithAttributes( 
        polyt, TheTypeInternalPolytope
    );
    
    if not pointlist = [ ] then
        
        SetAmbientSpaceDimension( polyt, Length( pointlist[ 1 ] ) -1 );
        
    fi;
     
     return polyt;
     
end );

if IsPackageMarkedForLoading( "PolymakeInterface", "2012.03.01" ) = true then
    
    ##
    InstallMethod( Polytope,
                  "creates polytope.",
                  [ IsList ],
                  
      PolymakePolytope
      
    );
    
    ##
    InstallMethod( PolytopeByInequalities,
                  "creates polytope.",
                  [ IsList ],
                  
      PolymakePolytopeByInequalities
      
    );
    
else
    
    ##
    InstallMethod( Polytope,
                  "creates polytope.",
                  [ IsList ],
                  
      InternalPolytope
      
    );
    
    ##
    InstallMethod( PolytopeByInequalities,
                  "creates polytope.",
                  [ IsList ],
                  
      InternalPolytopeByInequalities
      
    );
    
fi;


##
InstallMethod( Polytope,
               "constructor for given Pointers",
               [ IsExternalPolymakePolytope ],
               
  function( conepointer )
    local cone;
    
    cone := rec( );
    
    ObjectifyWithAttributes(
      cone, TheTypePolymakePolytope,
      ExternalObject, conepointer
    );
    
    return cone;
    
end );

####################################
##
## Display Methods
##
####################################

##
InstallMethod( ViewObj,
               "for homalg polytopes",
               [ IsPolytope ],
               
  function( polytope )
    local str;
    
    Print( "<A" );
    
    if HasIsNotEmpty( polytope ) then
        
        if IsNotEmpty( polytope ) then
            
            Print( " not empty" );
            
        fi;
    
    fi;
    
    if HasIsNormalPolytope( polytope ) then
        
        if IsNormalPolytope( polytope ) then
            
            Print( " normal" );
            
        fi;
    
    fi;
    
    if HasIsSimplicial( polytope ) then
        
        if IsSimplicial( polytope ) then
            
            Print( " simplicial" );
            
        fi;
    
    fi;
    
    if HasIsSimplePolytope( polytope ) then
        
        if IsSimplePolytope( polytope ) then
            
            Print( " simple" );
            
        fi;
    
    fi;
    
    if HasIsVeryAmple( polytope ) then
        
        if IsVeryAmple( polytope ) then
            
            Print( " very ample" );
            
        fi;
    
    fi;
    
    Print( " " );
    
    if HasIsLatticePolytope( polytope) then
        
        if IsLatticePolytope( polytope ) then
            
            Print( "lattice" );
            
        fi;
        
    fi;
    
    Print( "polytope in |R^" );
    
    Print( String( AmbientSpaceDimension( polytope ) ) );
    
    if HasVertices( polytope ) then
        
        Print( " with ", String( Length( Vertices( polytope ) ) )," vertices" );
        
    fi;
    
    Print( ">" );
    
end );

##
InstallMethod( Display,
               "for homalg polytopes",
               [ IsPolytope ],
               
  function( polytope )
    local str;
    
    Print( "A" );
    
    if HasIsNotEmpty( polytope ) then
        
        if IsNotEmpty( polytope ) then
            
            Print( " not empty" );
            
        fi;
    
    fi;
    
    if HasIsNormalPolytope( polytope ) then
        
        if IsNormalPolytope( polytope ) then
            
            Print( " normal" );
            
        fi;
    
    fi;
    
    if HasIsSimplicial( polytope ) then
        
        if IsSimplicial( polytope ) then
            
            Print( " simplicial" );
            
        fi;
    
    fi;
    
    if HasIsSimplePolytope( polytope ) then
        
        if IsSimplePolytope( polytope ) then
            
            Print( " simple" );
            
        fi;
    
    fi;
    
    if HasIsVeryAmple( polytope ) then
        
        if IsVeryAmple( polytope ) then
            
            Print( " very ample" );
            
        fi;
    
    fi;
    
    Print( " " );
    
    if HasIsLatticePolytope( polytope) then
        
        if IsLatticePolytope( polytope ) then
            
            Print( "lattice" );
            
        fi;
        
    fi;
    
    Print( "polytope in |R^" );
    
    Print( String( AmbientSpaceDimension( polytope ) ) );
    
    if HasVertices( polytope ) then
        
        Print( " with ", String( Length( Vertices( polytope ) ) )," vertices" );
        
    fi;
    
    Print( ".\n" );
    
end );