Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/GAP/pkg/fining/lib/   (Algebra von RWTH Aachen Version 4.15.1©)  Datei vom 27.6.2023 mit Größe 54 kB image not shown  

Quelle  affinespace.gi   Sprache: unbekannt

 
#############################################################################
##
##  affinespace.gi              FinInG package
##                                                              John Bamberg
##                                                              Anton Betten
##                                                              Jan De Beule
##                                                             Philippe Cara
##                                                            Michel Lavrauw
##                                                           Max Neunhoeffer
##
##  Copyright 2018 Colorado State University
##                  Sabancı Üniversitesi
##     Università degli Studi di Padova
##     Universiteit Gent
##     University of St. Andrews
##     University of Western Australia
##                  Vrije Universiteit Brussel
##
##
##  Implementation stuff for affine spaces
##
#############################################################################

#############################################################################
#
# Construction of affine spaces
#
#############################################################################

# CHECKED 12/03/12 jdb
#############################################################################
#O  AffineSpace( <d>, <f> )
# returns AG( <d>, <f> )
##
InstallMethod( AffineSpace, 
 "for a dimension and a field",
  [ IsPosInt, IsField ],
 function( d, f )
  local geo;
  geo := rec( dimension := d, basefield := f, 
                vectorspace := FullRowSpace(f, d) );
  Objectify( NewType( GeometriesFamily,
                        IsAffineSpace and IsAffineSpaceRep ), geo );
  SetAmbientSpace(geo,geo);
  SetRankAttr(geo,d); #this makes Rank applicable without adding more code in this file
  return geo;
 end );

# CHECKED 12/03/12 jdb
#############################################################################
#O  AffineSpace( <d>, <q> )
# returns AG( <d>, GF(<q>) )
##
InstallMethod( AffineSpace, 
 "for a dimension and a prime power",
 [ IsPosInt, IsPosInt ],
 function( d, q )
  return AffineSpace(d, GF(q));
 end );

############################################################################
#
# ViewObj/PrintObj for affine spaces
#
#############################################################################


InstallMethod( ViewObj, [ IsAffineSpace and IsAffineSpaceRep ],
  function( p )
    Print("AG(",p!.dimension,", ",Size(p!.basefield),")");
  end );

InstallMethod( PrintObj, [ IsAffineSpace and IsAffineSpaceRep ],
  function( p )
    Print("AffineSpace(",p!.dimension,",",p!.basefield,")");
  end );  

# ADDED 16/12/14 jdb, this is needed to make one check in ShadowOfElement
#############################################################################
#O  \=( <ag1>, <ag2> )
# Code taken from \= for projective spaces.
##
InstallMethod( \=, 
 "for two projective spaces",
 [IsAffineSpace, IsAffineSpace],
 function(ag1,ag2);
  return UnderlyingVectorSpace(ag1) = UnderlyingVectorSpace(ag2);
 end );

#############################################################################
#
# Attributes of affine spaces
#
#############################################################################

# CHECKED 20/03/11 jdb
#############################################################################
#A  Dimension( <as> )
# returns the projective dimension of <ps>
##
InstallMethod( Dimension, 
 "for an affine space",
 [ IsAffineSpace and IsAffineSpaceRep ],
 as -> as!.dimension
 );

# CHECKED 20/03/12 jdb
#############################################################################
#O  UnderlyingVectorSpace( <as> )
#returns the projective dimension of <ps>
##
InstallMethod( UnderlyingVectorSpace, 
 "for an affine space",
 [ IsAffineSpace and IsAffineSpaceRep ],
 as -> as!.vectorspace
 );
 
#############################################################################
#O  AmbientSpace( <subspace> ) returns the ambient space of <subspace>
##
InstallMethod( AmbientSpace,
 "for a subspace of an affine space",
 [IsSubspaceOfAffineSpace],
 function(subspace)
  return subspace!.geo;
 end );

# CHECKED 20/03/12 jdb
#############################################################################
#O  BaseField( <as> )
# returns the basefield of <as>
##
InstallMethod( BaseField, 
 "for an affine space", 
 [IsAffineSpace and IsAffineSpaceRep],
 ag -> ag!.basefield );

#############################################################################
#O  BaseField( <sub> )
# returns the basefield of an element of a projective space
##
InstallMethod( BaseField, 
 "for an element of a projective space", 
 [IsSubspaceOfAffineSpace],
 sub -> AmbientSpace(sub)!.basefield );

# CHECKED 12/03/12 jdb
#############################################################################
#O  TypesOfElementsOfIncidenceStructure( <ps> )
# returns the names of the types of the elements of the projective space <ps>
# the is a helper operation.
##
InstallMethod( TypesOfElementsOfIncidenceStructure, 
 "for an affine space", 
 [IsAffineSpace],
 function( as )
  local d,i,types;
  types := ["point"];
  d := Rank(as); #this line replaces next line, since next line assumes IsAffineSpaceRep
 #d := ps!.dimension;
  if d >= 2 then Add(types,"line"); fi;
  if d >= 3 then Add(types,"plane"); fi;
  if d >= 4 then Add(types,"solid"); fi;
  for i in [5..d] do
   Add(types,Concatenation("affine subspace of dim. ",String(i-1)));
  od;
  return types;
 end );

# CHECKED 12/03/12 jdb
#############################################################################
#O  TypesOfElementsOfIncidenceStructurePlural( <ps> )
# returns the plural of the names of the types of the elements of the projective space <ps>
# the is a helper operation.
##
InstallMethod( TypesOfElementsOfIncidenceStructurePlural, 
 "for an affine space",
 [IsAffineSpace],
 function( as )
  local d,i,types;
  types := ["points"];
      d := Rank(as); #this line replaces next line, since next line assumes IsAffineSpaceRep
 #d := ps!.dimension;
  if d >= 2 then Add(types,"lines"); fi;
  if d >= 3 then Add(types,"planes"); fi;
  if d >= 4 then Add(types,"solids"); fi;
  for i in [5..d] do
   Add(types,Concatenation("affine. subspaces of dim. ",String(i-1)));
  od;
  return types;
 end );

#############################################################################
# helper methods to construct affine subspacs.
#############################################################################

#############################################################################
#O  VectorSpaceTransversalElement( <space>, <subspace>, <v> )
# This is an internal subroutine which is not expected to be used by the user;
# Returns a canonical vector from <v>+<subspace>
##
InstallMethod( VectorSpaceTransversalElement,
 "for a vector space, a matric, a vector",
 [IsVectorSpace, IsFFECollColl, IsVector],
 function(space, subspace, v)
  local basis;
  basis := SemiEchelonBasis( Subspace(space, subspace) );
  return SiftedVector(basis, v);
 end );

#############################################################################
#O  VectorSpaceTransversal( <space>, <subspace> )
# This is an internal subroutine which is not expected to be used by the user;
# Returns a typed object containing a record with two components
# Note: IsVectorSpaceTransversal is a subfilter of IsSubspacesOfVectorSpace
##
InstallMethod( VectorSpaceTransversal, 
 "for a vector space, a matrix",
 [IsVectorSpace, IsFFECollColl],
 function(space, subspace)
  return Objectify(
               NewType( CollectionsFamily( FamilyObj( space ) ),
                    IsVectorSpaceTransversal and IsVectorSpaceTransversalRep),
           rec( vectorspace := space, subspace := subspace ) );    
 end );

#############################################################################
#O  ViewObj( <trans> )
InstallMethod( ViewObj, 
 "for a vector space transversal",
 [ IsVectorSpaceTransversal and IsVectorSpaceTransversalRep ],
 function( trans )
  Print("<vector space transversal of ", trans!.vectorspace,">");
 end );
 
#############################################################################
#O  PrintObj( <trans> )
InstallMethod( PrintObj, 
 "for a vector space transversal",
 [ IsVectorSpaceTransversal and IsVectorSpaceTransversalRep ],
 function( trans )
  Print("<vector space transversal of ", trans!.vectorspace," subspace spanned by ",trans!.subspace, ">");
 end );

#############################################################################
#
#  Methods for making subspaces
#
#  Affine subspaces can be constructed in the following ways:
#
# (1) from a vector v -> affine point v 
# (2) from a vector and a matrix [v, M] -> affine subspace v + <M>

# From these two options, we have the following list of 
# arguments for "AffineSubspace" (for now...)
#
# [ IsAffineSpace, IsRowVector ]
# [ IsAffineSpace, IsRowVector, IsPlistRep ]
# [ IsAffineSpace, IsRowVector, Is8BitMatrixRep ]
# [ IsAffineSpace, IsRowVector, IsGF2MatrixRep ]
#
# The treatment of the matrix representing the subspace at infinity, is similar to 
# the treatment in the methods for VectorSpaceToElement in projectivespace.gi
#
# 24/3/2014
# cvec/cmat changes lead to the methods for 
# [ IsAffineSpace, IsCVecRep ]
# [ IsAffineSpace, IsCVecRep, IsCMatRep ]
#
# more methods might be necessary in the future.
# Note that an affine point is represented by just one vector, while an affine 
# space by an object [v,m], where v is vector and m is a matrix. Currently we
# will change this into a cvec/cmat. 
#
#############################################################################

# CHECKED 12/03/12 jdb
# But I am unhappy with this code. It seems obsolete if you compare this with
# the method installed in geometry.gi for Wrap.
#############################################################################
#O  Wrap( <geo>, <type>, <o> )
# This is an internal subroutine which is not expected to be used by the user;
# they would be using AffineSubspace. Recall that Wrap is declared in 
# geometry.gd. 
##
InstallMethod( Wrap, 
 "for an affine space and an object",
 [IsAffineSpace, IsPosInt, IsObject],
 function( geo, type, o )
  local w;
  w := rec( geo := geo, type := type, obj := o );
  Objectify( NewType( SoASFamily, IsElementOfIncidenceStructure and
   IsElementOfIncidenceStructureRep and IsSubspaceOfAffineSpace ), w );
  return w;
 end );

#############################################################################
#
# ViewObj/PrintObjDisplay of subspaces of affine spaces
#
#############################################################################

InstallMethod( ViewObj, 
 [ IsSubspacesOfAffineSpace and IsSubspacesOfAffineSpaceRep ],
 function( vs )
  Print("<",TypesOfElementsOfIncidenceStructurePlural(vs!.geometry)[vs!.type]," of ");
  ViewObj(vs!.geometry);
  Print(">");
 end );

InstallMethod( PrintObj, 
 [ IsSubspacesOfAffineSpace and IsAllSubspacesOfProjectiveSpaceRep ],
 function( vs )
  Print("Elements( ",vs!.geometry," , ",vs!.type,")");
 end );

# special method to view an affine subspace as it is usually thought of

InstallMethod( Display, 
 [ IsSubspaceOfAffineSpace ],
 function( x )
  local u, t;
  u := x!.obj;
  if x!.type = 1 then
   Print("Affine point: ");
   Display( u );
  else
   if x!.type in [2, 3, 4] then
    Print("Affine ", TypesOfElementsOfIncidenceStructure(x!.geo)[x!.type], ":\n");
   else
    Print("Affine ", x!.type, "-space :\n");
   fi;
   Print("Coset representative: ", u[1], "\n" );
   Print("Coset (direction): ", u[2], "\n" );      
  fi;
 end );

#############################################################################
#
# User methods to construct subspaces of affine spaces.
#
#############################################################################

# CHECKED 12/03/12 jdb
# but I am unhappy with the fact that an empty v just returns [].
# 24/3/2014. I changed it now, entering [] gives an error.
# cvec/cmat change.
# changed 19/01/16 (jdb): by a change of IsPlistRep, this method gets also
# called when using a row vector, causing a problem with TriangulizeMat.
# a solution was to add IsMatrix.
#############################################################################
#O  AffineSubspace( <geom>, <v>, <m> )
# returns the subspace of <geom>, with representative <v> and subspace at infinity
# determined by <m>. 
##
InstallMethod( AffineSubspace, 
 "for a row vector and Plist",
    [IsAffineSpace, IsRowVector, IsPlistRep and IsMatrix],
 function( geom, v, m )
  local  x, n, i, gf, v2;
  gf := geom!.basefield;
  ## when v is empty... 
  #if IsEmpty(v) then
  # return [];
  #fi;
        ## dimension should be correct
        x := MutableCopyMat(m);
  if Length(v) <> geom!.dimension or Length(v) <> Length(x[1]) then
   Error("Dimensions are incompatible");
  fi;
  TriangulizeMat(x);

  ## Remove zero rows. It is possible the the user
  ## has inputted a matrix which does not have full rank
        n := Length(x);
  i := 0;
  while i < n and ForAll(x[n-i], IsZero) do
   i := i+1; 
  od;
  #if i = n then #this case corresponds simple with the "empty subspace at infinity", so a point must be returned.
  # return [];
  #fi;
  x := x{[1..n-i]};
  if Length(x) = geom!.dimension then
   return geom;
  fi;   
        ## It is possible that (a) the user has entered a
  ## matrix with one row, or that (b) the user has
  ## entered a matrix with rank 1 (thus at this stage
  ## we will have a matrix with one row).
        ## We must also compress our vector/matrix.
        ##note that IsZero([[]])=IsZero([])=true.
        if IsZero(x) then
  ## return an affine point
   v2 := NewMatrix(IsCMatRep,gf,geom!.dimension,[v])[1];
   #ConvertToVectorRep(v2, gf); #useless now.
   return Wrap(geom, 1, v2);
  else
  ## find transversal element
   v2 := VectorSpaceTransversalElement( geom!.vectorspace, x, v );
   v2 := NewMatrix(IsCMatRep,gf,geom!.dimension,[v2])[1];
   #ConvertToVectorRep(x, gf);
   #ConvertToMatrixRep(x, gf); #should have been ConvertToMatrixRep(v2,gf) ?
   x := NewMatrix(IsCMatRep,gf,geom!.dimension,x);
   return Wrap(geom, Length(x)+1, [v2,x]);
  fi;
 end );

# CHECKED 24/3/3014 jdb
#############################################################################
#O  AffineSubspace( <geom>, <v>  )
# returns the point in the affine space <geom> determined by <v> 
##
InstallMethod( AffineSubspace, 
 "for a row vector",
    [IsAffineSpace, IsRowVector],
 function( geom, v )
        if Length(v) <> geom!.dimension then
   Error("Dimensions are incompatible");
  fi;
  return Wrap(geom, 1, NewMatrix(IsCMatRep,geom!.basefield,geom!.dimension,[v])[1] );
 end );
 
# CHECKED 24/3/3014 jdb
#############################################################################
#O  AffineSubspace( <geom>, <v>  )
# returns the point in the affine space <geom> determined by <v> 
##
InstallMethod( AffineSubspace, 
 "for a row vector",
    [IsAffineSpace, IsCVecRep],
 function( geom, v )
        if Length(v) <> geom!.dimension then
   Error("Dimensions are incompatible");
  fi;
  return Wrap(geom, 1, v);
 end );

# CHECKED 24/3/3014 jdb
#############################################################################
#O  AffineSubspace( <geom>, <v>, <m> )
# returns the subspace of <geom>, with representative <v> and subspace at infinity
# determined by <m>. 
##
InstallMethod( AffineSubspace, 
 "for a row vector and 8-bit matrix",
    [IsAffineSpace, IsRowVector, Is8BitMatrixRep],
 function( geom, v, m )
  ## We have simply copied the code that was for IsPlistRep
  local  x, n, i, gf, v2;
  gf := geom!.basefield;
        x := MutableCopyMat(m);
  if Length(v) <> geom!.dimension or Length(v) <> Length(x[1]) then
   Error("Dimensions are incompatible");
  fi;
  TriangulizeMat(x);
        n := Length(x);
  i := 0;
  while i < n and ForAll(x[n-i], IsZero) do
   i := i+1; 
  od;
  x := x{[1..n-i]};
  if Length(x) = geom!.dimension then
   return geom;
  fi;   
        if IsZero(x) then
   v2 := NewMatrix(IsCMatRep,gf,geom!.dimension,[v])[1];
   return Wrap(geom, 1, v2);
  else
   v2 := VectorSpaceTransversalElement( geom!.vectorspace, x, v );
   v2 := NewMatrix(IsCMatRep,gf,geom!.dimension,[v2])[1];
   x := NewMatrix(IsCMatRep,gf,geom!.dimension,x);
   return Wrap(geom, Length(x)+1, [v2,x]);
  fi;
 end ); 
  
# CHECKED 24/3/3014 jdb
#############################################################################
#O  AffineSubspace( <geom>, <v>, <m> )
# returns the subspace of <geom>, with representative <v> and subspace at infinity
# determined by <m>. 
##
InstallMethod( AffineSubspace, 
 "for a row vector and 8-bit matrix",
    [IsAffineSpace, IsRowVector, IsGF2MatrixRep],
 function( geom, v, m )
    ## We have simply copied the code that was for IsPlistRep
  local  x, n, i, gf, v2;
  gf := geom!.basefield;
        x := MutableCopyMat(m);
  if Length(v) <> geom!.dimension or Length(v) <> Length(x[1]) then
   Error("Dimensions are incompatible");
  fi;
  TriangulizeMat(x);
        n := Length(x);
  i := 0;
  while i < n and ForAll(x[n-i], IsZero) do
   i := i+1; 
  od;
  x := x{[1..n-i]};
  if Length(x) = geom!.dimension then
   return geom;
  fi;   
        if IsZero(x) then
   v2 := NewMatrix(IsCMatRep,gf,geom!.dimension,[v])[1];
   return Wrap(geom, 1, v2);
  else
   v2 := VectorSpaceTransversalElement( geom!.vectorspace, x, v );
   v2 := NewMatrix(IsCMatRep,gf,geom!.dimension,[v2])[1];
   x := NewMatrix(IsCMatRep,gf,geom!.dimension,x);
   return Wrap(geom, Length(x)+1, [v2,x]);
  fi;
  end ); 
  
# ADDED 24/3/3014 jdb
#############################################################################
#O  AffineSubspace( <geom>, <v>, <m> )
# returns the subspace of <geom>, with representative <v> and subspace at infinity
# determined by <m>. 
##
InstallMethod( AffineSubspace, 
 "for a row vector and 8-bit matrix",
    [IsAffineSpace, IsCVecRep, IsCMatRep],
 function( geom, v, m )
  local  x, n, i, gf, v2;
  gf := geom!.basefield;
        x := MutableCopyMat(Unpack(m)); #cmat might give trouble with {}
  if Length(v) <> geom!.dimension or Length(v) <> Length(x[1]) then
   Error("Dimensions are incompatible");
  fi;
  TriangulizeMat(x);
        n := Length(x);
  i := 0;
  while i < n and ForAll(x[n-i], IsZero) do
   i := i+1; 
  od;
  x := x{[1..n-i]};
  if Length(x) = geom!.dimension then
   return geom;
  fi;   
        if IsZero(x) then
   v2 := NewMatrix(IsCMatRep,gf,geom!.dimension,[v])[1];
   return Wrap(geom, 1, v2);
  else
   v2 := VectorSpaceTransversalElement( geom!.vectorspace, x, v );
   v2 := NewMatrix(IsCMatRep,gf,geom!.dimension,[v2])[1];
   x := NewMatrix(IsCMatRep,gf,geom!.dimension,x);
   return Wrap(geom, Length(x)+1, [v2,x]);
  fi;
  end ); 

# added 31/7/2014 for reasons of consistency jdb
#############################################################################
#O  ObjecToElement( <geom>, <obj> )
# returns the subspace of <geom>, with representative <v> and subspace at infinity
# determined by <m> if and only if <obj> is the list [v,m].
##
InstallMethod( ObjectToElement,
 "for an affine space and an object",
 [ IsAffineSpace, IsList],
 function(as, obj)
  if Length(obj) = 2 then
   return AffineSubspace(as, obj[1], obj[2]);
  else
   Error("<obj> does not determine a subspace of <as>");
  fi;
 end );
   
# added 31/7/2014 for reasons of consistency jdb
#############################################################################
#O  ObjecToElement( <geom>, <type>, <obj> )
# returns the subspace of <geom>, with representative <v> and subspace at infinity
# determined by <m> if and only if <obj> is the list [v,m].
##
InstallMethod( ObjectToElement,
 "for an affine space and an object",
 [ IsAffineSpace, IsPosInt, IsList],
 function(as, t, obj)
  local el;
  if Length(obj) = 2 then
   el :=  AffineSubspace(as, obj[1], obj[2]);
  else
   Error("<obj> does not determine a subspace of <as>");
  fi;
  if el!.type <> t then
   Error("<obj> does not determine a subspace of <as> of given type <t>");
  else
   return el;
  fi;
 end );

# CHECKED 13/03/12 jdb
#############################################################################
#O  RandomSubspace( <as>, <d> )
# returns an affine subspace of dimension <d> in the affine space <as>
##
InstallMethod( RandomSubspace, 
 "for an affine space and a dimension",
    [ IsAffineSpace, IsInt ],
 function(as, d)
  local vspace, w, sub;
        if d > RankAttr(as) then
   Error("The dimension of the subspace is larger than that of the affine space");
  fi;
  if IsNegInt(d) then
   Error("The dimension of the subspace must be at least 0!");
  fi;
  vspace := as!.vectorspace;
  w := Random(vspace);
  if d = 1 then
   return AffineSubspace( as, w );
  else
   sub := BasisVectors(  Basis( RandomSubspace( vspace, d-1 ) ) );
   return AffineSubspace( as, w, sub );
  fi;
 end );  
  
# CHECKED 13/03/12 jdb
#############################################################################
#O  Random( <subs> )
# returns a random element in the collection <subs>
##
InstallMethod( Random, 
 "for a collection of subspaces of an affine space",
    [ IsSubspacesOfAffineSpace ],
    # chooses a random element out of the collection of subspaces of given
    # dimension of an affine space
 function( subs )
  local x;
  x := RandomSubspace( subs!.geometry, subs!.type );
  return x;
 end );
  
#############################################################################
#
#  ElementsOfIncidenceStructure, enumerators, iterators
#
#############################################################################

# CHECKED 13/03/12 jdb
#############################################################################
#O  ElementsOfIncidenceStructure( <as> )
# returns the collection of all the elements of the affine space <as> 
## 
InstallMethod( ElementsOfIncidenceStructure, 
 "for an affine space",
 [IsAffineSpace],
 function( as )
  return Objectify( NewType( ElementsCollFamily, IsAllElementsOfIncidenceStructure ),
   rec( geometry := as ) );
 end );

# CHECKED 20/03/12 jdb
#############################################################################
#O  ElementsOfIncidenceStructure( <as>, <j> )
# returns the collection of all the elements of type <j> of the affine space <as> 
## 
InstallMethod( ElementsOfIncidenceStructure, 
 "for an affine space and an integer",
 [ IsAffineSpace, IsPosInt],
 function( as, j )
  local r;
  r := Rank(as);
  if j > r then
   Error("<as> has no elements of type <j>");
  else
   return Objectify( NewType( ElementsCollFamily, IsElementsOfIncidenceStructure and
                                IsSubspacesOfAffineSpace and IsSubspacesOfAffineSpaceRep ),
        #IsAllSubspacesOfAffineSpace and IsAllSubspacesOfAffineSpaceRep),
   rec( geometry := as, type := j, size := Size(Subspaces(as!.vectorspace, j-1)) * 
                  Size(as!.basefield)^(as!.dimension - j + 1) ) );
  fi;
 end );

#############################################################################
# User friendly named operations for points, lines, planes, solids
# for affine spaces. These operations are not checking if the affine space
# really contains the elements of the asked type, since ElementsOfIncidenceStructure 
# does. 
#############################################################################

# CHECKED 13/03/12 jdb
#############################################################################
#O  Points( <as> )
# returns ElementsOfIncidenceStructure(as,1), <as> a an affine space
## 
InstallMethod( Points, 
 "for an affine space",
 [IsAffineSpace],
 function( as )
  return ElementsOfIncidenceStructure(as, 1);
 end);

# CHECKED 13/03/12 jdb
#############################################################################
#O  Lines( <as> )
# returns ElementsOfIncidenceStructure(as,2), <as> a an affine space
## 
InstallMethod( Lines, 
 "for an affine space",
 [IsAffineSpace],
 function( as )
  return ElementsOfIncidenceStructure(as, 2);
 end);

# CHECKED 13/03/12 jdb
#############################################################################
#O  Planes( <as> )
# returns ElementsOfIncidenceStructure(as,3), <as> a an affine space
## 
InstallMethod( Planes, 
 "for an affine space",
 [IsAffineSpace],
 function( as )
  return ElementsOfIncidenceStructure(as, 3);
 end);

# CHECKED 13/03/12 jdb
#############################################################################
#O  Solids( <as> )
# returns ElementsOfIncidenceStructure(as,4), <as> a an affine space
## 
InstallMethod( Solids, 
 "for an affine space",
 [IsAffineSpace],
 function( as )
  return ElementsOfIncidenceStructure(as, 4);
 end);

# CHECKED 20/03/12 jdb
#############################################################################
#O  Solids( <as> )
# returns ElementsOfIncidenceStructure(as,1), <as> a an affine space
## 
InstallMethod( Hyperplanes, 
 "for an affine space",
 [IsAffineSpace],
 function( as )
  return ElementsOfIncidenceStructure(as, as!.dimension);
 end);

# CHECKED 13/03/12 jdb
#############################################################################
#O  Size( <vs> )
# returns the number of elements in the collection <vs>
## 
InstallMethod(Size, 
 "for a collection of subspaces of an affine space",
    [IsSubspacesOfAffineSpace],
 function( vs ) 
  return vs!.size; 
 end );
    
#############################################################################
# Methods to create flags.
#############################################################################

#############################################################################
#O  FlagOfIncidenceStructure( <as>, <els> )
# returns the flag of the projective space <ps> with elements in <els>.
# the method checks whether the input really determines a flag.
##
InstallMethod( FlagOfIncidenceStructure,
 "for an affine space and list of subspaces of the affine space",
 [ IsAffineSpace, IsSubspaceOfAffineSpaceCollection ],
 function(as,els)
  local list,i,test,type,flag;
  list := Set(ShallowCopy(els));
  if Length(list) > Rank(as) then
    Error("A flag ca at most Rank(<as>) elements");
  fi;
        test := List(list,x->AmbientGeometry(x));
        if not ForAll(test,x->x=as) then
            Error("not all elements have <as> as ambient geometry");
        fi;
  test := Set(List([1..Length(list)-1],i -> IsIncident(list[i],list[i+1])));
  if (test <> [ true ] and test <> []) then
    Error("<els> does not determine a flag>");
  fi;
  flag := rec(geo := as, types := List(list,x->x!.type), els := list);
  ObjectifyWithAttributes(flag, IsFlagOfASType, IsEmptyFlag, false);
  return flag;
 end);

#############################################################################
#O  FlagOfIncidenceStructure( <as>, <els> )
# returns the empty flag of the projective space <ps>.
##
InstallMethod( FlagOfIncidenceStructure,
 "for an affine space and an empty list",
 [ IsAffineSpace, IsList and IsEmpty ],
 function(as,els)
  local flag;
  flag := rec(geo := as, types := [], els := []);
  ObjectifyWithAttributes(flag, IsFlagOfASType, IsEmptyFlag, true);
  return flag;
 end);

#############################################################################
# View/Print/Display methods for flags
#############################################################################

InstallMethod( ViewObj, 
 "for a flag of an affine space",
 [ IsFlagOfAffineSpace and IsFlagOfIncidenceStructureRep ],
 function( flag )
  Print("<a flag of AffineSpace(",flag!.geo!.dimension,", ",Size(flag!.geo!.basefield),")>");
 end );

InstallMethod( PrintObj,
 "for a flag of an affine space",
 [ IsFlagOfAffineSpace and IsFlagOfIncidenceStructureRep ],
 function( flag )
  PrintObj(flag!.els);
 end );

InstallMethod( Display, 
 "for a flag of an affine space",
 [ IsFlagOfAffineSpace and IsFlagOfIncidenceStructureRep ],
 function( flag )
  if IsEmptyFlag(flag) then
   Print("<empty flag of AffineSpace(",flag!.geo!.dimension,", ",Size(flag!.geo!.basefield),")>\n");
  else
   Print("<a flag of AffineSpace(",flag!.geo!.dimension,", ",Size(flag!.geo!.basefield),")> with elements of types ",flag!.types,"\n");
   Print("respectively spanned by\n");
   Display(flag!.els);
  fi;
 end );

#############################################################################
# Enumerator method(s)
#############################################################################

#############################################################################
#O  Enumerator( <trans> )
# return an Enumerator for a vector space transversal.
##
InstallMethod( Enumerator, 
 "for a vector space transversal", 
 [ IsVectorSpaceTransversal ],
 function( trans )  
    
    # returns an enumerator for the canonical elements of all cosets of 
    # <subspace> in <space>. 
    
    local complement, enumcomp, enum, space, subspace;
    space := trans!.vectorspace;
    subspace := trans!.subspace;
    complement := ComplementSpace( space, subspace );
    enumcomp := Enumerator( complement );
    enum := EnumeratorByFunctions( trans, rec(            
            ElementNumber := function(e, n)
    local v;
    v := enumcomp[n];
    return VectorSpaceTransversalElement(space, subspace, v);
            end,
            NumberElement := function(e,v)
    local n;
    n := Position(enumcomp,v);
    return n;
   end,
   #NumberElement := enumcomp!.NumberElement,
            #Length := e -> enumcomp!.Length ));   # silly enumerator of v.spaces doesn't always have "Length"
            Length := e -> Size(complement) ));
    return enum;
 end );

# 24/3/2014. cmat adapted.
#############################################################################
#O  Enumerator( <vs> )
# return an Enumerator for subspaces of an affine space of given type.
##
InstallMethod( Enumerator, 
 "for subspaces of an affine space",
    [ IsSubspacesOfAffineSpace ],  
 function( vs )
 ## An affine subspace will be represented by a pair (vector,direction).
 ## So for example, an affine plane x+<W> will be represented by
 ## (x', proj. line)  (where x' is the transversal rep corresponding to x).
    local as, j, vars, vec, subs, f, enum, enumV, classsize;
    as := vs!.geometry;
    j := vs!.type;
    vec := as!.vectorspace;
    f := as!.basefield;
    if j = 1 then 
       enumV := Enumerator( vec );
       enum := EnumeratorByFunctions( vs, rec(
            ElementNumber := function(e, n)
              local v;
              v := enumV[n]; 
              #ConvertToVectorRep(v, f);
              return Wrap(as, 1, NewMatrix(IsCMatRep,f,as!.dimension,[v])[1]); #looks ugly, avoids an extra call.
            end,
            NumberElement := function(e, x)
              local v;
              v := Unpack(x!.obj);
              return Position(enumV, v);
            end ));    
    else
       enumV := Enumerator( Subspaces( vec, j-1 ) ); 
       classsize := Size(f)^(Dimension(vec)-j+1);
       enum := EnumeratorByFunctions( vs, rec(
            ElementNumber := function(e, n)
               local l, k, enumtrans, v; 
               
               ## The way this works is that n is the position
               ## of the l-th coset incident with the k-th (j-1)-space
               ## of as.      
               l := n mod classsize;
               if l = 0 then l := classsize; fi;  
               k := (n-l) / classsize + 1;  
               v := NewMatrix(IsCMatRep,f,as!.dimension,BasisVectors(Basis(enumV[k])));
               enumtrans := Enumerator( VectorSpaceTransversal(vec, Unpack(v)) );
               return Wrap(as, j, [ NewMatrix(IsCMatRep,f,as!.dimension,[enumtrans[l]])[1], v ] );
            end,
            NumberElement := function( e, x )
              local w, vw, k, enumtrans, l;
              ## Here we must first find the unique direction of x
              ## incident with x, and then find its place in the ordering.
              w :=Unpack(x!.obj[2]); #x!.obj = [cvec,cmat]        
              vw := Subspace(vec,w);
     k := Position(enumV, vw);  
              enumtrans := Enumerator( VectorSpaceTransversal(vec, w) );
              l := Position(enumtrans, Unpack(x!.obj[1]));
              return (k-1)*classsize + l;
            end ) );
     fi;
    return enum;
 end );    

# cmat adapted.
#############################################################################
#O  Iterator( <vs> )
# iterator for affine subspaces of a given type.
##
InstallMethod( Iterator, 
 "for subspaces of an affine space",
    [IsSubspacesOfAffineSpace],  
 function( vs )
 ## An affine subspace will be represented by a pair (vector,direction).
 ## So for example, an affine plane x+<W> will be represented by
 ## (x', proj. line)  (where x' is the transversal rep corresponding to x).
  local ps, j, vars, vec, subs, f;
  ps := vs!.geometry;
  j := vs!.type;
  vec := ps!.vectorspace;
  f := ps!.basefield;
  if j = 1 then 
   vars := List(vec, x -> AffineSubspace(ps, x));
   return IteratorList( vars );
  else
 ## we need a transversal for each subspace
   if j = 2 then 
    subs := List(ElementsOfIncidenceStructure(ProjectiveSpace(ps!.dimension-1,f), 1), 
                     x -> [Unpack(x!.obj)]);
   else
    subs := List(ElementsOfIncidenceStructure(ProjectiveSpace(ps!.dimension-1,f), j-1), 
                     x -> Unpack(x!.obj));
   fi;
   vars := Union(List(subs, x -> 
                 List(VectorSpaceTransversal(vec, x), y -> AffineSubspace(ps,y,x))));
   return IteratorList( vars );
  fi;
  end );


#############################################################################
#
# Basic methods: \in (set theoretic containment for elements), 
# IsIncident, Span, Meet, IsParallel, ProjectiveCompletion
#
#############################################################################

# CHECKED 13/03/12 jdb
#############################################################################
#O  \in( <x>, <as> )
# returns true if <x> is an element of the affine space <as>
##
InstallMethod( \in, 
 "for an element of an affine space and an affine space",
 [IsSubspaceOfAffineSpace, IsAffineSpace],
 function( x, as )
  local s;
  s := x!.geo;
  return s!.dimension = as!.dimension and s!.basefield = as!.basefield;
 end );

#############################################################################
#O  \in( <x>, <y> )
# set theoretic containment for an affine space and a subspace. 
##
InstallOtherMethod( \in, 
 "for an affine space and an element of an affine space",
 [ IsAffineSpace, IsSubspaceOfAffineSpace ],
 function( x, y )
  if x = y!.geo then
   return false;
  else
   Error( "<x> is different from the ambient space of <y>" );
  fi;
 end );

# CREATED 20/3/2012 jdb
#############################################################################
#O  \in( <x>, <y> )
# returns true if <x> is contained in <y>, from the set theoretic point of view.
##
InstallMethod( \in,  
 "for two subspaces of an affine space",
 [IsSubspaceOfAffineSpace, IsSubspaceOfAffineSpace],
 function( x, y )
  local ambx, amby, typx, typy, mat, flag,
          zero, nrows, ncols, vectors, 
          nvectors, i, j, z, nzheads, row;
  ambx := x!.geo;
  amby := y!.geo;
  typx := x!.type;
  typy := y!.type;
  #set theoretic containment makes only sense if the dimension of x is at most the dimension of y.
  if typx > typy then
   return false;
  elif typx = typy then
   return x=y;
  fi;
   
  ## x + A in y + B iff y-x in B and A subset of B
  # x+a in y+B for all a => (y-x) in B and A subset of B 

  if ambx!.vectorspace = amby!.vectorspace then   

  ## First step: check that the translations are compatible,
  ## that is, that x!.obj[1] - y!.obj[1] is in the subspace
  ## spanned by "vectors"

   if typx = 1 and typy > 1 then
    return x!.obj - y!.obj[1] in Subspace(ambx!.vectorspace, y!.obj[2]);
   else 
    flag := x!.obj[1] - y!.obj[1] in Subspace(ambx!.vectorspace, y!.obj[2]);
   fi;
   if not flag then return false; fi;

  ## Second step: checking that the directions are compatible.
  ## Algorithm is the same as for projective spaces.
  ## Note that here we will have typx, typy > 1.

   vectors := y!.obj[2];
   nvectors := typy-1;
   mat := MutableCopyMat(x!.obj[2]);
   nrows := typx - 1;

   ncols:= amby!.dimension ;
   zero:= ZeroOfBaseDomain( mat );

  # here we are going to treat "vectors" as a list of basis vectors. first
  # figure out which column is the first nonzero column for each row
   nzheads := [];
   for i in [ 1 .. nvectors ] do
    row := vectors[i];
    j := PositionNonZero( row );
    Add(nzheads,j);
   od;

  # now try to reduce each row of "mat" with the basis vectors
   for i in [ 1 .. nrows ] do
    row := mat[i];
    for j in [ 1 .. Length(nzheads) ] do
     z := row[nzheads[j]];
     if z <> zero then
      AddRowVector( row, vectors[ j ], - z );
     fi;
    od;

  # if the row is now not zero then y is not a subspace of x
    j := PositionNonZero( row );
    if j <= ncols then
     flag := false; break;
    fi;
   od;
      
   return flag;
  else
   Error( "type is unknown or not implemented" );
  fi;
  return false;
  end );

# CREATED 20/3/2012 jdb
#############################################################################
#O  IsIncident( <x>, <y> )
# returns true if and only if <x> is incident with <y>. Relies on set theoretic
# containment.
##
InstallMethod( IsIncident,  
  [IsSubspaceOfAffineSpace, IsSubspaceOfAffineSpace],
        function(x,y)
                return x in y or y in x;
        end );


## An affine space is a complete lattice 
## (with the empty set as bottom element).

#############################################################################
#O  Span( <x>, <y> )
# cvec/cmat: I took no risk and unpacked everything to do linear algebra.
##
InstallMethod( Span,
 "for two affine subspaces",
 [IsSubspaceOfAffineSpace, IsSubspaceOfAffineSpace],
 function( x, y )  
  local ux1, uy1, ux2, uy2, ambx, amby, typx, typy, span, temp;
  ambx := AmbientSpace(x!.geo);
  amby := AmbientSpace(y!.geo);
  typx := x!.type;
  typy := y!.type;

  ## affine span of x + A and y + B is 
  ##  x + <y-x, A,B>

  if not (ambx!.vectorspace = amby!.vectorspace) then
   Error("Subspaces belong to different ambient spaces");
  fi;

  if typx = 1 then 
   ux1 := Unpack(x!.obj); 
   ux2 := [];
  else 
   ux1 := Unpack(x!.obj[1]); 
   ux2 := Unpack(x!.obj[2]);
  fi;
  if typy = 1  then 
   uy1 := Unpack(y!.obj); 
   uy2 := [];
  else 
   uy1 := Unpack(y!.obj[1]); 
   uy2 := Unpack(y!.obj[2]);
  fi;  
  span := MutableCopyMat(ux2);
  Append(span, uy2); #this is the reason to unpack everything, if span would be cmat, and uy2 e.g. [], this becomes very ugly.
  Append(span, [uy1-ux1]); 
  span := MutableCopyMat(SemiEchelonMat(span).vectors);
   
  # if the span is [], then x=y, so return x.
  
  if Length(span) = 0 then
   return x;
  fi;
   
  # if the span is the whole space, return that.
  if Length(span) = ambx!.dimension + 1 then
   return ambx;
  fi;      
  #TriangulizeMat(span); #AffineSubspace will do this now.
  return AffineSubspace(ambx, VectorSpaceTransversalElement(ambx!.vectorspace,span,ux1), span); #makes sure cvec/cmat is used.
  end );


#############################################################################
#O  Meet( <x>, <y> )
# cvec/cmat: I took no risk and unpacked everything to do linear algebra.
##
InstallMethod( Meet, 
 "for two affine subspaces",
 [IsSubspaceOfAffineSpace, IsSubspaceOfAffineSpace],
 function( x, y )
  local ag, ux1, uy1, ux2, uy2, typx, typy, int, 
          rep, t, f, vec, rk, trans, ambx, amby, m, mat;
  ag := x!.geo;
  ambx := AmbientSpace(x);
  amby := AmbientSpace(x);
  if not (ambx!.vectorspace = amby!.vectorspace) then
   Error("Subspaces belong to different ambient spaces");
  fi;

  typx := x!.type;
  typy := y!.type; 
  f := ag!.basefield; 
  vec := ag!.vectorspace;

  ## Cases for the intersection of x + A and y + B
  ## (i) A int B = 0 => (x+A) int (y+B) is a point or empty
  ## (ii) dim(A int B) = 1 => (x+A) int (y+B) is a point or empty
  ## (iii) dim(A int B) > 1 => (x+A) int (y+B) is subspace of 
  ## dimension dim(A int B), or empty
  ## (iv) A empty or B empty => equal or empty 

 ## redundant cases 
  if x = y then 
   return x;
  fi;
  # we can assume now that x<>y
  if typx = 1 and typy = 1 then
   return [];
  elif (typx = 1 or typy = 1) then
   if typx = 1 then
    ux1 := Unpack(x!.obj); 
   else
    ux1 := Unpack(x!.obj[1]); 
    mat := Unpack(x!.obj[2]);
   fi;
   if typy = 1 then
    uy1 := Unpack(y!.obj);
    mat := [ ]; 
   else
    uy1 := Unpack(y!.obj[1]); 
    mat := Unpack(y!.obj[2]);
   fi;
      
   if IsZero( VectorSpaceTransversalElement( vec, mat, ux1 - uy1) ) then
    return Minimum(x, y);
   else 
       return [];
   fi;
  fi;

  ux1 := Unpack(x!.obj[1]); 
  ux2 := Unpack(x!.obj[2]);
  uy1 := Unpack(y!.obj[1]); 
  uy2 := Unpack(y!.obj[2]);
 ## parallel spaces, case x=y is handled above.
  if ux2 = uy2 then 
   return []; 
  fi;
 
 ## find intersection of two spaces
  int := SumIntersectionMat(ux2, uy2)[2];
  
  if not IsEmpty(int) and Rank(int) > 0 then 
   int := MutableCopyMat(int);
   TriangulizeMat(int);
   rk := Rank(int) + 1;        
  ## Now check to see if the affine intersection
  ## is empty. We will need a representative anyway.
   trans := VectorSpaceTransversal(vec, int);
   rep := 0;
   for t in trans do
    if Rank(Union([t-ux1], ux2)) = Rank(ux2) and
     Rank(Union([t-uy1], uy2)) = Rank(uy2) then
     rep := t; break;
    fi;
   od;
   if rep = 0 then 
    return []; 
   elif rk = 1 then  
    return AffineSubspace( ag, rep);
   fi;
   return AffineSubspace( ag, rep, int);
  else   ## case (i)
   rep := 0;
   for t in vec do
    if not IsZero(t) and Rank(Union([t-ux1], ux2)) = Rank(ux2) and
     Rank(Union([t-uy1], uy2)) = Rank(uy2) then
     rep := t; break;
    fi;
   od;
   if rep = 0 then 
    return [];
   else
    return AffineSubspace( ag, rep);
   fi;
  fi;
 end );


# CHECKED 20/3/2012 jdb
#############################################################################
#O  IsParallel( <x>, <y> )
# returns true if and only if <x> is parallel with <y>. 
##
#InstallMethod( IsParallel, 
# "for two affine subspaces",
# [ IsSubspaceOfAffineSpace, IsSubspaceOfAffineSpace ],
# function( a, b );
#  if a!.type <> b!.type then
#   Error("Subspaces must be of the same dimension");
#  fi;
#  if a!.geo <> a!.geo then
#   Error("Ambient affine spaces must be the same");
#  fi;
#  if a!.type = 1 then
#   return true;
#  else
#   return a!.obj[2] = b!.obj[2];
#  fi;
# end );

# WRITTEN 12/9/2014 jb
# Two affine subspaces of possibly different dimensions are parallel if
# and only if the direction of one contains the direction of the other.

InstallMethod( IsParallel, 
 "for two affine subspaces",
 [ IsSubspaceOfAffineSpace, IsSubspaceOfAffineSpace ],
 function( a, b )
  local vectors, nvectors, mat, nrows, ncols, zero, row, i, j, nzheads, z, flag, x, y;
  if a!.geo <> b!.geo then
   Error("Ambient affine spaces must be the same");
  fi;
  if a!.type = 1 or b!.type = 1 then
   return true;
  fi;
  
  ## checking that the directions are incident.
  ## Algorithm is the same as for projective spaces.
  ## Note that here we will have typx, typy > 1.
  x := SortedList([a,b])[1];
  y := SortedList([a,b])[2];
  flag := true;
  vectors := y!.obj[2];
  nvectors := y!.type-1;
  mat := MutableCopyMat(x!.obj[2]);
  nrows := x!.type - 1;
  ncols:= y!.geo!.dimension ;
  zero:= ZeroOfBaseDomain( mat );

  # here we are going to treat "vectors" as a list of basis vectors. first
  # figure out which column is the first nonzero column for each row
  nzheads := [];
  for i in [ 1 .. nvectors ] do
   row := vectors[i];
   j := PositionNonZero( row );
   Add(nzheads,j);
  od;

  # now try to reduce each row of "mat" with the basis vectors
  for i in [ 1 .. nrows ] do
   row := mat[i];
   for j in [ 1 .. Length(nzheads) ] do
    z := row[nzheads[j]];
    if z <> zero then
     AddRowVector( row, vectors[ j ], - z );
    fi;
   od;

   # if the row is now not zero then y is not a subspace of x
   j := PositionNonZero( row );
   if j <= ncols then
    flag := false; break;
   fi;
  od;
      
  return flag;
 end );
 

#############################################################################
#O  ProjectiveCompletion( <x>, <y> )
# geometry morphism. Usual unpack to avoid potential problems.
# to be checked later.
##
InstallMethod( ProjectiveCompletion, 
 "for an affine space",
 [ IsAffineSpace ],
 function( as )
    # Returns an embedding of an affine space
    # into a projective space (its projective completion). 
    # For example, the point (x, y, z) goes to <(1, x, y, z)>    
    # An intertwiner is unnecessary, CollineationGroup(as) is 
    # subgroup of CollineationGroup(ps).

    local d, gf, ps, func, pre, map, morphism, vec, one, hom, infinity;
    d := as!.dimension;
    gf := as!.basefield;
    ps := ProjectiveSpace(d, gf);
    one := One(gf);    
    vec := as!.vectorspace;
  infinity := VectorSpaceToElement(ps,ShallowCopy(IdentityMat(d+1,gf)){[2..d+1]});
 func := function( x )
  local repx, subspace, n, trans, new, i, j;
  repx := x!.obj;
  n := x!.type;
  if not x in as then 
   Error("Subspace is not an element of the domain (affine space)");
  fi;
  if n > 1 then      
   subspace := Unpack(repx[2]);
   trans := Unpack(repx[1]);
   # simply put all vectors together and put 0's in the first column
   new := NullMat(n, d+1, gf);
   new{[1..n-1]}{[2..d+1]} := subspace;
   new[n][1] := one;
   new[n]{[2..d+1]} := trans; 
  else
   new := Concatenation([one], Unpack(repx)); 
  fi;
  return VectorSpaceToElement(ps, new);
    end;
 
    pre := function( y )
  local n, repy, subspace, trans, new, zerov, elm, hyp;
  if not y in ps then 
   Error("Subspace is not an element of the range (projective space)");
  fi;
  if y * infinity then
   Error("Subspace is an element at infinity");
  fi;
  n := y!.type;
  repy := y!.obj;
  if n > 1 then
   repy := List(repy,x->Unpack(x));
   zerov := NullMat(1,d,gf);
   hyp := TransposedMat(Concatenation(zerov, IdentityMat(d, gf)));
   subspace := SumIntersectionMat(hyp, repy)[2];
#   repy := SortedList(y!.obj);  ## JB: 11/09/2014 (found the bug here)
   repy := SortedList(repy);
   # Make use of lexicographic ordering
   # Zero at front gives the parallel class       
   trans := repy[n];
      
   # curtail
   trans := trans{[2..d+1]};
   subspace := subspace{[1..n-1]}{[2..d+1]}; 
   new := VectorSpaceTransversalElement(vec, subspace, trans);
   elm := AffineSubspace(as, new, subspace);
  else
   trans := repy{[2..d+1]};
   elm := AffineSubspace(as, trans);
  fi;
  return elm;
 end;

    map := GeometryMorphismByFunction(ElementsOfIncidenceStructure(as), 
                                      ElementsOfIncidenceStructure(ps), func, false, pre);
    SetIsInjective(map, true);  
    return map;
end );

#############################################################################
#
#  Methods for shadows and parallel classes
#
#  (We use the completion to the projective space)
#
#############################################################################


#############################################################################
#O  ShadowOfElement( <as>, <v>, <j> )
# Returns the elements of type j incidence with <v>, a usual shadow object 
# in FinInG.
##
InstallMethod( ShadowOfElement, 
 "for an affine space, an affine subspace, a positive integer",
 [IsAffineSpace, IsSubspaceOfAffineSpace, IsPosInt],
 function( as, v, j )   
        if not AmbientGeometry(v) = as then
            Error("Ambient geometry of <v> is not <as>");
        fi;
        return Objectify(
   NewType( ElementsCollFamily, IsElementsOfIncidenceStructure and
                                   IsShadowSubspacesOfAffineSpace and
                                   IsShadowSubspacesOfAffineSpaceRep),
     rec( geometry := as, type := j, list := [v] ) );
 end );
  

#############################################################################
#O  ShadowOfFlag( <as>, <v>, <j> )
# Returns the elements of type j incidence with a flag, a usual shadow object 
# in FinInG.
##
InstallMethod( ShadowOfFlag, 
 "for an affine space, a flag of affine elements, a positive integer",
 [IsAffineSpace, IsFlagOfIncidenceStructure, IsPosInt],
 function( as, flag, j )
        #   empty flag - return all subspaces of the right type
  if IsEmptyFlag(flag) then
   return ElementsOfIncidenceStructure(as, j);
  fi;
  return Objectify(
   NewType( ElementsCollFamily, IsElementsOfIncidenceStructure and
                                   IsShadowSubspacesOfAffineSpace and
                                   IsShadowSubspacesOfAffineSpaceRep),
    rec( geometry := as, type := j, list := flag!.els ) #JDB: added !.els here (flags used to be lists, are objects now).
     );
 end);
  
#############################################################################
#O  ParallelClass( <as>, <v> )
# returns the collection of elements parallel with <v>
##
InstallMethod( ParallelClass, 
 "for an affine space and subspace",
 [IsAffineSpace, IsSubspaceOfAffineSpace], 
 function( as, v )
  #if v!.type = 0 then
  # Error("Subspace must be nontrivial"); #this never occurs, it is not possible te constuct elements of type 0.
  if v!.type = 1 then
   return Points( as );     
  else
   return Objectify(
    NewType( ElementsCollFamily, IsElementsOfIncidenceStructure and
                                   IsParallelClassOfAffineSpace and
                                   IsParallelClassOfAffineSpaceRep),
      rec( geometry := as, element := v, type := v!.type ) );
                        # Added the type to the parallelclass 
                        # ml 12/09/2014
  fi;      
 end );
  
#############################################################################
#O  ParallelClass( <v> )
# returns the collection of elements parallel with <v>
##
InstallMethod( ParallelClass, 
 "for an affine subspace", 
 [ IsSubspaceOfAffineSpace ], 
 x -> ParallelClass( x!.geo, x ) );


#############################################################################
#O  Iterator( <pclass> )
# iterator for a parallel class of an element of an affine space.
##
InstallMethod( Iterator, 
 "for a parallel class of an affine space",
 [IsParallelClassOfAffineSpace and IsParallelClassOfAffineSpaceRep ],
 function( pclass )
  local as, v, type, direction, vec, elms;
  as := pclass!.geometry;
  v := pclass!.element;
  type := v!.type;
  direction := Unpack(v!.obj[2]); #a parallel class of a point is not in IsParallelClassOfAffineSpace(Rep)
  vec := as!.vectorspace;
        ## Do the trivial cases:
  #if type = 1 then #cannot occur
        ## it already has an iterator...
        #return Iterator( pclass );
        #fi;
  elms := List(VectorSpaceTransversal(vec, direction), y -> AffineSubspace(as, y,direction) );
  return IteratorList( elms ); 
 end );


#############################################################################
#O  Size( <vs> )
# number of elements in a shadow.
##
InstallMethod( Size, 
 "for a shadow of an element of an affine subspace",
 [IsShadowSubspacesOfAffineSpace and IsShadowSubspacesOfAffineSpaceRep ],
 function( vs )
  local ps, list, map, shad, j, as;
  as := vs!.geometry;
  j := vs!.type;
  map := ProjectiveCompletion(as);
  ps := Range(map)!.geometry;
  list := vs!.list;    
  if Size( list ) = 1 then
   shad := ShadowOfElement( ps, ImageElm(map, list[1]), j);
  else
   shad := ShadowOfFlag( ps, ImagesSet(map, list), j);
  fi;
  return Size( shad );
  end);


#############################################################################
#O  Iterator( <shadow> )
# iterator for a shadow of an element in an affine space.
##
InstallMethod( Iterator, 
 "for a shadow in an affine space",
 [IsShadowSubspacesOfAffineSpace and IsShadowSubspacesOfAffineSpaceRep ],
 function( vs )
  local as, i, j, ps, map, iter, list, dim, hyperplane, x, newfinish, assoc;
  as := vs!.geometry;
  j := vs!.type;
  map := ProjectiveCompletion(as);
  ps := Range(map)!.geometry;
  list := vs!.list;  
  dim := ps!.dimension+1;
  hyperplane := VectorSpaceToElement(ps, IdentityMat(dim,ps!.basefield){[2..dim]});
  if Size( list ) = 1 then
   x := list[1];
   i := x!.type;
   iter := StructuralCopy(Iterator( ShadowOfElement( ps, ImageElm(map, x), j) ));
   #
   #  We simple change the IsDoneIterator in iter.
   #  It took me ages to figure out how this all works!
   # 
   newfinish := Maximum(  [ Binomial(i-1,j-1), Binomial(dim-i,j-i) ] );   ##JB: Happy that this works!
   assoc := iter!.S!.associatedIterator;
   assoc!.choiceiter!.IsDoneIterator := 
    iter -> iter!.pos = newfinish and IsDoneIterator(assoc!.spaceiter);
  else
   # still need to truncate the iterator of this one ...
   Print("Iterators of shadows of flags in affine spaces are not complete in this version\n");
   iter := Iterator( ShadowOfFlag( ps, ImagesSet(map, list), j) );
  fi;
  return IteratorByFunctions( rec(
         NextIterator := function(iter)
          local x;
          repeat
           x := NextIterator(iter!.S);
          until not x in hyperplane;
          return PreImageElm(map, x);
         end,
         IsDoneIterator := iter -> IsDoneIterator(iter!.S),
         ShallowCopy := iter -> rec( S := ShallowCopy(iter!.S) ),
         S := iter )    
        );
 end);


#############################################################################
#
# Some view methods for shadows and parallel classes
#
#############################################################################


InstallMethod( ViewObj, 
 [ IsShadowSubspacesOfAffineSpace and IsShadowSubspacesOfAffineSpaceRep ],
 function( vs )
  Print("<shadow ",TypesOfElementsOfIncidenceStructurePlural(vs!.geometry)[vs!.type]," in ");
  ViewObj(vs!.geometry);
  Print(">");
 end );
  
InstallMethod( ViewObj, 
 [ IsParallelClassOfAffineSpace and IsParallelClassOfAffineSpaceRep ],
 function( vs )
  Print("<parallel class of ",
  TypesOfElementsOfIncidenceStructurePlural(vs!.geometry)[vs!.element!.type]," in ");
  ViewObj(vs!.geometry);
  Print(">");
 end );

#############################################################################
#
# Nice shorthand methods for shadows of elements
#
#############################################################################


InstallMethod( Points, [ IsSubspaceOfAffineSpace ],
  function( var )
    return ShadowOfElement(var!.geo, var, 1);
  end );

InstallMethod( Points, [ IsAffineSpace, IsSubspaceOfAffineSpace ],
  function( geo, var )
    return ShadowOfElement(geo, var, 1);
  end );

InstallMethod( Lines, [ IsSubspaceOfAffineSpace ],
  function( var )
    return ShadowOfElement(var!.geo, var, 2);
  end );

InstallMethod( Lines, [ IsAffineSpace, IsSubspaceOfAffineSpace ],
  function( geo, var )
    return ShadowOfElement(geo, var, 2);
  end );

InstallMethod( Planes, [ IsSubspaceOfAffineSpace ],
  function( var )
    return ShadowOfElement(var!.geo, var, 3);
  end );

InstallMethod( Planes, [ IsAffineSpace, IsSubspaceOfAffineSpace ],
  function( geo, var )
    return ShadowOfElement(geo, var, 3);
  end );

InstallMethod( Solids, [ IsSubspaceOfAffineSpace ],
  function( var )
    return ShadowOfElement(var!.geo, var, 4);
  end );

InstallMethod( Solids, [ IsAffineSpace, IsSubspaceOfAffineSpace ],
  function( geo, var )
    return ShadowOfElement(geo, var, 4);
  end );

#############################################################################
#O  IncidenceGraph( <gp> )
# Note that computing the collineation group of a projective space is zero
# computation time. So useless to print the warning here if the group is not
# yet computed.
###
InstallMethod( IncidenceGraph,
    "for a projective space",
    [ IsAffineSpace ],
    function( as )
        local elements, graph, adj, coll, sz;
  if IsBound(as!.IncidenceGraphAttr) then
            return as!.IncidenceGraphAttr;
        fi;
  coll := CollineationGroup(as);
  elements := Concatenation(List([1..Rank(as)], i -> List(AsList(ElementsOfIncidenceStructure(as,i)))));
        adj := function(x,y)
            if x!.type <> y!.type then
                return IsIncident(x,y);
            else
                return false;
            fi;
        end;
        graph := Graph(coll,elements,OnAffineSubspaces,adj,true);
        Setter( IncidenceGraphAttr )( as, graph );
        return graph;
    end );





[ Dauer der Verarbeitung: 0.50 Sekunden  (vorverarbeitet)  ]