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 157 kB image not shown  

Impressum gpolygons.gi   Sprache: unbekannt

 
Spracherkennung für: .gi vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

#############################################################################
##
##  gpolygons.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 generalised polygons.
##
#############################################################################

#############################################################################
# Part I: generic part
# This section is generic, i.e. if someone constructs a GP and obeys the representation,
# these methods will work.
#############################################################################
# about IsGeneralisedPolygonRep
# objects belonging to IsGeneralisedPolygonRep should have several fields in their record:
# pointsobj, linesobj, incidence, listelements, shadowofpoint, shadowofline, distance.
#
# pointsobj: a list containing the *underlying objects* for the points of the GP
#
# linesobj: a list containing the *underlying objects* for the lines of the GP
#
# incidence: a function, taking two *elements of the GP* as argument, returning true or false
#    we assume that the elements that are argument of this built in function, belong
#    to the same geometry. See generic method IsIncident.
#
# listelements: a function taking an integer as argument, returning
# a list of all elements of the GP of the given type. This list will be turned into an iterator
# by the method installed for Iterator for elements of GPs.
#
# shadowofpoint: a function, taking a *point of a GP* as argument, returning a list
# of lines incident with the given point.
#
# shadowofline: a function, taking a *line of a GP* as argument, returning a list
# of points incident with the given line.
#  The method for ShadowOfElement should do the necessary checks and pass the appropriate
#  function to the method installed for Iterator for shadow objects.
#
# span: a function taking two *points of a GP* returning fail or the unique line incident with the two points.
#
# meet: a function taking two *lines of a GP* returning fail or the unique point incident with the two lines.
#
# distance: a function taking two *elements of a GP* and returning their distance in the incidence graph.
#
# action: a function describing an action on the *underlying objects*.
#
# gonality: it is not very explicitely documented, but one can actually construct
#   generalised n-gons for n different than 3,4,6,8. To avoid checking the diameter of the underlying
#   graph e.g. for ViewObj, in these case the field gonality is set upon creation. One could say
#   that this field replaces the categories IsProjectivePlaneCategory, IsGeneralisedQuadrangle, IsGeneralisedHexagon
#   and IsGeneralisedOctagon for these arbitrary cases.
#
# Note: - If an object belongs to IsGeneralisedPolygon, then the "generic operations" to explore the GP
#   are *applicable* (does not imply that a specific method is installed or will be working).
#  - If a GP is constructed using a "Generic method", the above fields are created as described, making
#   all the methods for the "generic operations" working.
#  - If a GP is created as an object in IsGeneralisedPolygon and IsGeneralisedPolygonRep, with some of
#   these fields lacking or different, than separate methods need to be installed for certain operations
#   This is not problematic. A typical example are the hexagons: they belong also to IsLieGeometry,
#   so it is easy to get the right methods selected. Furthermore, typical methods for Lie geometries become
#   applicable (but might also need separate methods).
#  - If a GP is constructed using the "generic construction methods", there is always an underlying graph (to
#   check whether the user really constructs a GP. Creating the graph can be time consuming, but there is
#   the possibility to use a group. There is no NC version, either you are a developper, and you want to make
#   a particular GP (e.g. the hexagons) and then you know what you do and there is no need to check whether your
#   developed GP is really a GP, or you are a user and must be protected against yourself.
#   The computed graph is stored as a mutable attribute.
#  - For the particular GPs: of course we know that they are a GP, so on construction, we do not compute
#   the underlying graph.
#  - The classical GQs belong to IsGeneralisedPolygon but *not* to IsGeneralisedPolygonRep. For them there is
#   either an atrtibute set on creation (e.g. Order), or a seperate method for other generic operations.
#
#############################################################################

#############################################################################
#
# Construction of GPs
#
#############################################################################

#############################################################################
#O  GeneralisedPolygonByBlocks( <list> )
# returns a GP, the points are Union(blocks), the lines are the blocks. functions
# for shadows are installed. It is checked whether this is really a GP.
# Note that we allow weak GPs: these are GPs "without order", i.e. bipartite graph,
# grith = 2*diameter, but no constant valency for the vertices in (on of) the components
# of the graph. We can do the necessary checks with LocalParameters.
##
InstallMethod( GeneralisedPolygonByBlocks,
    "for a homogeneous list",
    [ IsHomogeneousList ],
    function( blocks )
        local pts, gp, ty, i, graph, sz, adj, girth, shadpoint, shadline, s, t, dist, vn,
  listels, objs, act, spanoftwopoints, meetoftwolines, bicomp, pointvertices, lp, linevertices;
        pts := Union(blocks);

        i := function( x, y )
        if IsSet( x!.obj ) and not IsSet( y!.obj ) then
            return y!.obj in x!.obj;
        elif IsSet( y!.obj ) and not IsSet( x!.obj ) then
            return x!.obj in y!.obj;
        else
            return x!.obj = y!.obj;
        fi;
        end;

        sz := Size(pts);

  adj := function(x,y)
   if IsSet(x) and not IsSet(y) then
    return y in x;
   elif IsSet(y) and not IsSet(x) then
    return x in y;
   else
    return false;
   fi;
  end;

  act := function(x,g)
   return x;
  end;

        graph := Graph(Group(()), Concatenation(pts,blocks), act, adj );
        girth := Girth(graph);

        if IsBipartite(graph) then
            if not girth = 2*Diameter(graph) then
                Error("<blocks> are not defining a generalised polygon");
            fi;
        else
            Error("<blocks are not defining a generalised polygon");
        fi;

        listels := function( geom, i )
   if i = 1 then
    return List(pts,x->Wrap(geom,i,x));
   else
    return List(blocks,x->Wrap(geom,i,x));
   fi;
  end;

  vn := VertexNames(graph);

        shadpoint := function( pt )
            return List(Filtered(blocks,x->pt!.obj in x),y->Wrap(pt!.geo,2,y));
        end;

        shadline := function( line )
            return List(line!.obj,x->Wrap(line!.geo,1,x));
        end;

  #we have the graph now, the following is easy.
  bicomp := Bicomponents(graph);
  pointvertices := First(bicomp,x->1 in x); #just take all points
  lp := LocalParameters(graph,pointvertices);
  t := lp[1][3]-1; #is the number of lines on a point, is -1 if there is no order, subtract to have t
  linevertices := First(bicomp,x->not 1 in x); #just take all lines
  lp := LocalParameters(graph,linevertices);
  s := lp[1][3]-1; #is the number of points on a line, is -1 if there is no orde, subtract to have s

  dist := function( el1, el2 )
   return Distance(graph,Position(vn,el1!.obj),Position(vn,el2!.obj));
  end;

        spanoftwopoints := function(x,y) #x and y are elements
            local i,j,span,el;
            i := Position(vn,x!.obj);
            j := Position(vn,y!.obj);
            el := Intersection(DistanceSet(graph,[1],i), DistanceSet(graph,[1],j));
            if not Length(el) = 0 then
                span := vn{el};
                return Wrap(x!.geo,2,span[1]);
            else
                Info(InfoFinInG, 1, "<x> and <y> do not span a line of gp");
                return fail;
            fi;
        end;

        meetoftwolines := function(x,y) #x and y are elements
            local i,j,meet,el;
            i := Position(vn,x!.obj);
            j := Position(vn,y!.obj);
            el := Intersection(DistanceSet(graph,[1],i), DistanceSet(graph,[1],j));
            if not Length(el) = 0 then
                meet := vn{el};
                return Wrap(x!.geo,1,meet[1]);
            else
                Info(InfoFinInG, 1, "<x> and <y> do meet in a common point of gp");
                return fail;
            fi;
        end;

        gp := rec( pointsobj := pts, linesobj := blocks, incidence := i, listelements := listels,
     shadowofpoint := shadpoint, shadowofline := shadline, distance := dist,
                    span := spanoftwopoints, meet := meetoftwolines );

        if t = -2 or s = -2 then
   ty := NewType( GeometriesFamily, IsWeakGeneralisedPolygon and IsGeneralisedPolygonRep );
            gp!.gonality := girth/2;
  elif girth = 6 then
            ty := NewType( GeometriesFamily, IsProjectivePlaneCategory and IsGeneralisedPolygonRep );
        elif girth = 8 then
            ty := NewType( GeometriesFamily, IsGeneralisedQuadrangle and IsGeneralisedPolygonRep );
        elif girth = 12 then
            ty := NewType( GeometriesFamily, IsGeneralisedHexagon and IsGeneralisedPolygonRep );
        elif girth = 16 then
            ty := NewType( GeometriesFamily, IsGeneralisedOctagon and IsGeneralisedPolygonRep );
        else
            ty := NewType( GeometriesFamily, IsGeneralisedPolygon and IsGeneralisedPolygonRep );
            gp!.gonality := girth/2;
        fi;

        Objectify( ty, gp );
        SetTypesOfElementsOfIncidenceStructure(gp, ["point","line"]);
        if s <> -2 and t <> -2 then
   SetOrder(gp, [s, t]);
  fi;
        SetRankAttr(gp, 2);
        Setter( IncidenceGraphAttr )( gp, graph );
        Setter( HasGraphWithUnderlyingObjectsAsVertices )( gp, true);
        return gp;
  end );

#############################################################################
#O  GeneralisedPolygonByIncidenceMatrix( <matrix> )
# returns a GP. points are [1..NrRows(matrix)], blocks are sets of entries equal to one.
# Blocks are then used through GeneralisedPolygonByBlocks.
# the commented out check dates from the times that this was only use to construct
# projective planes.
##
InstallMethod( GeneralisedPolygonByIncidenceMatrix,
    "for a matrix",
    [ IsMatrix ],
    function( mat )
    ## Rows represent blocks and columns represent points...
    local v, q, row, blocks, gp;
    v := NrRows(mat);
    #if not ForAll(mat, t->Size(t)=v) then
    #   Error("Matrix is not square");
    #fi;

    blocks := [];
    for row in mat do
        Add(blocks, Positions(row,1));
    od;

    gp := GeneralisedPolygonByBlocks( blocks );
    Setter( IncidenceMatrixOfGeneralisedPolygon )( gp, mat );
    return gp;
  end );

#############################################################################
#O  GeneralisedPolygonByElements( <pts>, <lns>, <inc> )
# <pts>: set of elements of some incidence structure, representing points of GP.
# <lns>: set of elements of some incidence structure, representing points of GP.
# <inc>: incidence function.
# it is checked throug the graph that the incidence structure is a GP.
##
InstallMethod( GeneralisedPolygonByElements,
    "for two sets (points and lines), and an incidence function",
    [ IsSet, IsSet, IsFunction ],
    function( pts, lns, inc )
    local adj, act, graph, ty, girth, shadpoint, shadline, s, t,
 gp, vn, dist, listels, wrapped_incidence, spanoftwopoints, meetoftwolines,
 bicomp, pointvertices, linevertices, lp;

    adj := function(x,y)
    if x in pts and y in pts then
        return false;
    elif x in lns and y in lns then
        return false;
    else
        return inc(x,y);
    fi;
    end;

    # this is the situation where the user gives no group at all. So action function is trivial too.
    act := function(x,g)
        return x;
    end;

    graph := Graph(Group(()), Concatenation(pts,lns), act, adj, true );
    girth := Girth(graph);

    if IsBipartite(graph) then
        if not girth = 2*Diameter(graph) then
            Error("<pts>, <lns>, <inc> are not defining a generalised polygon");
        fi;
    else
        Error("elements are not defining a generalised polygon");
    fi;

 bicomp := Bicomponents(graph);
 pointvertices := First(bicomp,x->1 in x); #just take all points
 lp := LocalParameters(graph,pointvertices);
 t := lp[1][3]-1; #is the number of lines on a point, is -1 if there is no order, subtract to have t
 linevertices := First(bicomp,x->(Size(pts)+1) in x); #just take all lines
 lp := LocalParameters(graph,linevertices);
 s := lp[1][3]-1; #is the number of points on a line, is -1 if there is no order, subtract to have s

 # inc takes in fact underlying objects as arguments. So we must make a new function that takes
 # as arguments elements of this geometry and pipes the underlying objects to inc.

 wrapped_incidence := function(x,y)
  return inc(x!.obj,y!.obj);
 end;

    listels := function( geom, i )
  if i = 1 then
   return List(pts,x->Wrap(geom,i,x));
  else
   return List(lns,x->Wrap(geom,i,x));
  fi;
 end;

    shadpoint := function( pt )
        return List(vn{Adjacency(graph,Position(vn,pt!.obj))},x->Wrap(gp,2,x));
    end;

    shadline := function( line )
        return List(vn{Adjacency(graph,Position(vn,line!.obj))},x->Wrap(gp,1,x));
    end;

    vn := VertexNames(graph);
 dist := function( el1, el2 )
        return Distance(graph,Position(vn,el1!.obj),Position(vn,el2!.obj));
    end;

    spanoftwopoints := function(x,y) #x and y are elements
        local i,j,span,el;
        i := Position(vn,x!.obj);
        j := Position(vn,y!.obj);
        el := Intersection(DistanceSet(graph,[1],i), DistanceSet(graph,[1],j));
        if not Length(el) = 0 then
            span := vn{el};
            return Wrap(x!.geo,2,span[1]);
        else
            Info(InfoFinInG, 1, "<x> and <y> do not span a line of gp");
            return fail;
        fi;
    end;

    meetoftwolines := function(x,y) #x and y are elements
        local i,j,meet,el;
        i := Position(vn,x!.obj);
        j := Position(vn,y!.obj);
        el := Intersection(DistanceSet(graph,[1],i), DistanceSet(graph,[1],j));
        if not Length(el) = 0 then
            meet := vn{el};
            return Wrap(x!.geo,1,meet[1]);
        else
            Info(InfoFinInG, 1, "<x> and <y> do meet in a common point of gp");
            return fail;
        fi;
    end;

    gp := rec( pointsobj := pts, linesobj := lns, incidence := wrapped_incidence, listelements := listels,
    shadowofpoint := shadpoint, shadowofline := shadline, distance := dist,
                span := spanoftwopoints, meet := meetoftwolines );

 if t = -2 or s = -2 then
  ty := NewType( GeometriesFamily, IsWeakGeneralisedPolygon and IsGeneralisedPolygonRep );
  gp!.gonality := girth/2;
 elif girth = 6 then
        ty := NewType( GeometriesFamily, IsProjectivePlaneCategory and IsGeneralisedPolygonRep );
    elif girth = 8 then
        ty := NewType( GeometriesFamily, IsGeneralisedQuadrangle and IsGeneralisedPolygonRep );
    elif girth = 12 then
        ty := NewType( GeometriesFamily, IsGeneralisedHexagon and IsGeneralisedPolygonRep );
    elif girth = 16 then
        ty := NewType( GeometriesFamily, IsGeneralisedOctagon and IsGeneralisedPolygonRep );
    else
        ty := NewType( GeometriesFamily, IsGeneralisedPolygon and IsGeneralisedPolygonRep );
        gp!.gonality := girth/2;
    fi;

    Objectify( ty, gp );
 if s <> -2 and t <> -2 then
  SetOrder(gp, [s, t]);
 fi;
    SetTypesOfElementsOfIncidenceStructure(gp, ["point","line"]);
    SetRankAttr(gp, 2);
    Setter( IncidenceGraphAttr )( gp, graph );
    Setter( HasGraphWithUnderlyingObjectsAsVertices )( gp, true);
    return gp;
end );

#############################################################################
#O  GeneralisedPolygonByElements( <pts>, <lns>, <inc>, <group>, <act> )
# <pts>: set of elements of some incidence structure, representing points of GP.
# <lns>: set of elements of some incidence structure, representing points of GP.
# <inc>: incidence function.
# <group>: group preserving <pts>, <lns> and <inc>
# <act>: action function for group on <pts> and <lns>.
# it is checked throug the graph that the incidence structure is a GP, by using
# the group, this is much more efficient. The user is responsible the <group>
# really preserves <pts> and <lns>
##
InstallMethod( GeneralisedPolygonByElements,
    "for two sets (points and lines), and an incidence function",
    [ IsSet, IsSet, IsFunction, IsGroup, IsFunction ],
    function( pts, lns, inc, group, act )
    local adj, graph, ty, girth, shadpoint, shadline, s, t, gp, vn,
 dist, listels, wrapped_incidence, spanoftwopoints, meetoftwolines,
 bicomp, pointvertices, linevertices, lp;

    adj := function(x,y)
    if x in pts and y in pts then
        return false;
    elif x in lns and y in lns then
        return false;
    else
        return inc(x,y);
    fi;
    end;

    graph := Graph(group, Concatenation(pts,lns), act, adj, true );
    girth := Girth(graph);

    if IsBipartite(graph) then
        if not girth = 2*Diameter(graph) then
            Error("<blocks> are not defining a generalised polygon");
        fi;
    else
        Error("elements are not defining a generalised polygon");
    fi;

 bicomp := Bicomponents(graph);
 pointvertices := First(bicomp,x->1 in x); #just take all points
 lp := LocalParameters(graph,pointvertices);
 t := lp[1][3]-1; #is the number of lines on a point, is -1 if there is no order, subtract -1 to have t
 linevertices := First(bicomp,x->(Size(pts)+1) in x); #just take all lines
 lp := LocalParameters(graph,linevertices);
 s := lp[1][3]-1; #is the number of points on a line, is -1 if there is no order, subtract -1 to have s

    vn := VertexNames(graph);

 # inc takes in fact underlying objects as arguments. So we must make a new function that takes
 # as arguments elements of this geometry and pipes the underlying objects to inc.

 wrapped_incidence := function(x,y)
  return inc(x!.obj,y!.obj);
 end;

    listels := function( geom, i )
  if i = 1 then
   return List(pts,x->Wrap(geom,i,x));
  else
   return List(lns,x->Wrap(geom,i,x));
  fi;
 end;

    shadpoint := function( pt )
        return List(vn{Adjacency(graph,Position(vn,pt!.obj))},x->Wrap(gp,2,x));
    end;

    shadline := function( line )
        return List(vn{Adjacency(graph,Position(vn,line!.obj))},x->Wrap(gp,1,x));
    end;

    dist := function( el1, el2 )
        return Distance(graph,Position(vn,el1!.obj),Position(vn,el2!.obj));
    end;

    spanoftwopoints := function(x,y) #x and y are elements
        local i,j,span,el;
        i := Position(vn,x!.obj);
        j := Position(vn,y!.obj);
        el := Intersection(DistanceSet(graph,[1],i), DistanceSet(graph,[1],j));
        if not Length(el) = 0 then
            span := vn{el};
            return Wrap(x!.geo,2,span[1]);
        else
            Info(InfoFinInG, 1, "<x> and <y> do not span a line of gp");
            return fail;
        fi;
    end;

    meetoftwolines := function(x,y) #x and y are elements
        local i,j,meet,el;
        i := Position(vn,x!.obj);
        j := Position(vn,y!.obj);
        el := Intersection(DistanceSet(graph,[1],i), DistanceSet(graph,[1],j));
        if not Length(el) = 0 then
            meet := vn{el};
            return Wrap(x!.geo,1,meet[1]);
        else
            Info(InfoFinInG, 1, "<x> and <y> do meet in a common point of gp");
            return fail;
        fi;
    end;

    gp := rec( pointsobj := pts, linesobj := lns, incidence := wrapped_incidence, listelements := listels,
    shadowofpoint := shadpoint, shadowofline := shadline, distance := dist, span := spanoftwopoints,
                meet := meetoftwolines, action := act );

 if t = -2 or s = -2 then
  ty := NewType( GeometriesFamily, IsWeakGeneralisedPolygon and IsGeneralisedPolygonRep );
  gp!.gonality := girth/2;
 elif girth = 6 then
        ty := NewType( GeometriesFamily, IsProjectivePlaneCategory and IsGeneralisedPolygonRep );
    elif girth = 8 then
        ty := NewType( GeometriesFamily, IsGeneralisedQuadrangle and IsGeneralisedPolygonRep );
    elif girth = 12 then
        ty := NewType( GeometriesFamily, IsGeneralisedHexagon and IsGeneralisedPolygonRep );
    elif girth = 16 then
        ty := NewType( GeometriesFamily, IsGeneralisedOctagon and IsGeneralisedPolygonRep );
    else
        ty := NewType( GeometriesFamily, IsGeneralisedPolygon and IsGeneralisedPolygonRep );
        gp!.gonality := girth/2;
    fi;

    Objectify( ty, gp );
 if s <> -2 and t <> -2 then
  SetOrder(gp, [s, t]);
 fi;
 SetTypesOfElementsOfIncidenceStructure(gp, ["point","line"]);
    SetRankAttr(gp, 2);
    Setter( IncidenceGraphAttr )( gp, graph );
    Setter( HasGraphWithUnderlyingObjectsAsVertices )( gp, true);
    return gp;
end );

#############################################################################
# View methods for GPs.
#############################################################################

InstallMethod( ViewObj,
 "for a projective plane in GP rep",
 [ IsProjectivePlaneCategory and IsGeneralisedPolygonRep],
 function( p )
        if HasOrder(p) then
            Print("<projective plane order ",Order(p)[1],">");
        else
            Print("<projective plane>");
        fi;
 end );

InstallMethod( ViewObj,
 "for a projective plane in GP rep",
 [ IsGeneralisedQuadrangle and IsGeneralisedPolygonRep],
 function( p )
        if HasOrder(p) then
            Print("<generalised quadrangle of order ",Order(p),">");
        else
            Print("<generalised quadrangle>");
        fi;
 end );

InstallMethod( ViewObj,
 "for a projective plane in GP rep",
 [ IsGeneralisedHexagon and IsGeneralisedPolygonRep],
 function( p )
        if HasOrder(p) then
            Print("<generalised hexagon of order ",Order(p),">");
        else
            Print("<generalised hexagon>");
        fi;
 end );

InstallMethod( ViewObj,
 "for a projective plane in GP rep",
 [ IsGeneralisedOctagon and IsGeneralisedPolygonRep],
 function( p )
        if HasOrder(p) then
            Print("<generalised octagon of order ",Order(p),">");
        else
            Print("<generalised octagon>");
        fi;
 end );

InstallMethod( ViewObj,
 "for a projective plane in GP rep",
 [ IsGeneralisedPolygon and IsGeneralisedPolygonRep],
 function( gp )
        if HasOrder(gp) then
            Print("<generalised polygon of gonality ",String(gp!.gonality)," and order ",Order(gp),">");
        else
            Print("<generalised polygon of gonality ",String(gp!.gonality),">");
        fi;
 end );

InstallMethod( ViewObj,
 "for a projective plane in GP rep",
 [ IsWeakGeneralisedPolygon and IsGeneralisedPolygonRep],
 function( gp )
  Print("<weak generalised polygon of gonality ",String(gp!.gonality),">");
 end );

#############################################################################
#
# Basic methods for elements (including construction and iterator).
#
#############################################################################

#############################################################################
#O  Order( <x> )
##
InstallMethod( Order,
 "for a weak generalised polygon",
 [ IsWeakGeneralisedPolygon ],
 function( gp )
  Error("<gp> is a weak generalised polygon and has no order");
 end );

#############################################################################
#O  UnderlyingObject( <x> )
##
InstallMethod( UnderlyingObject,
 "for an element of a LieGeometry",
 [ IsElementOfGeneralisedPolygon ],
 function( x )
  return x!.obj;
 end );

#############################################################################
#O  ObjectToElement( <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 ageneralised polygon, an integer and an object",
 [ IsGeneralisedPolygon and IsGeneralisedPolygonRep, IsPosInt, IsObject],
 function(gp, t, obj)
  if t=1 then
   if obj in gp!.pointsobj then
    return Wrap(gp,t,obj);
   else
    Error("<obj> does not represent a point of <gp>");
   fi;
  elif t=2 then
   if obj in gp!.linesobj then
    return Wrap(gp,t,obj);
   else
    Error("<obj> does not represent a line of <gp>");
   fi;
  else
   Error("<gp> is a point-line geometry not containing elements of type ",t);
  fi;
 end );

#############################################################################
#O  ObjectToElement( <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 ageneralised polygon and an object",
 [ IsGeneralisedPolygon and IsGeneralisedPolygonRep, IsObject],
 function(gp, obj)
  if obj in gp!.pointsobj then
   return Wrap(gp,1,obj);
  elif obj in gp!.linesobj then
   return Wrap(gp,2,obj);
  else
   Error("<obj> does not represent an element of <gp>");
  fi;
 end );

#############################################################################
#O  ElementsOfIncidenceStructure( <gp>, <j> )
# returns the elements of <gp> of type <j>
##
InstallMethod( ElementsOfIncidenceStructure,
 "for a generalised polygon and a positive integer",
 [IsGeneralisedPolygon and IsGeneralisedPolygonRep, IsPosInt],
 function( gp, j )
  local s, t, sz;
  if j in [1,2] then
   s := Order(gp)[j]; t := Order(gp)[3-j];
  else
   Error("Incorrect type value");
  fi;
  if IsProjectivePlaneCategory(gp) then
   sz := s^2 + s + 1;
  elif IsGeneralisedQuadrangle(gp) then
   sz := (1+s)*(1+s*t);
  elif IsGeneralisedHexagon(gp) then
   sz := (1+s)*(1+s*t+s^2*t^2);
  elif IsGeneralisedOctagon(gp) then
   sz := (1+s)*(1+s*t+s^2*t^2+s^3*t^3);
  else
            if j=1 then
                sz := Length(gp!.pointsobj);
            else
                sz := Length(gp!.linesobj);
            fi;
        fi;
  return Objectify( NewType( ElementsCollFamily, IsElementsOfGeneralisedPolygon and
                                IsElementsOfGeneralisedPolygonRep),
        rec( geometry := gp, type := j, size := sz )
      );
 end );

#############################################################################
#O  ElementsOfIncidenceStructure( <gp>, <j> )
# returns the elements of <gp> of type <j>
##
InstallMethod( ElementsOfIncidenceStructure,
 "for a generalised polygon and a positive integer",
 [IsWeakGeneralisedPolygon and IsGeneralisedPolygonRep, IsPosInt],
 function( gp, j )
  local s, t, sz;
  if not j in [1,2] then
   Error("Incorrect type value");
  fi;
        if j=1 then
   sz := Length(gp!.pointsobj);
  else
   sz := Length(gp!.linesobj);
  fi;
  return Objectify( NewType( ElementsCollFamily, IsElementsOfGeneralisedPolygon and
       IsElementsOfGeneralisedPolygonRep),
      rec( geometry := gp, type := j, size := sz ) );
 end );

#############################################################################
#O  Points( <gp>  )
# returns the points of <gp>.
##
InstallMethod( Points,
 "for a generalised polygon",
 [IsGeneralisedPolygon and IsGeneralisedPolygonRep],
 function( gp )
  return ElementsOfIncidenceStructure(gp, 1);
 end);

#############################################################################
#O  Lines( <gp>  )
# returns the lines of <gp>.
##
InstallMethod( Lines,
 "for a generalised polygon",
 [IsGeneralisedPolygon and IsGeneralisedPolygonRep],
 function( gp )
  return ElementsOfIncidenceStructure(gp, 2);
 end);

#############################################################################
# Display methods: Element collections
#############################################################################

InstallMethod( ViewObj,
 "for elements of a generalised polygon",
 [ IsElementsOfGeneralisedPolygon and IsElementsOfGeneralisedPolygonRep ],
 function( vs )
  local l;
  l := ["points","lines"];
  Print("<", l[vs!.type]," of ");
  ViewObj(vs!.geometry);
  Print(">");
 end );

InstallMethod( PrintObj,
 "for elements of a generalised polygon",
 [ IsElementsOfGeneralisedPolygon and IsElementsOfGeneralisedPolygonRep ],
 function( vs )
  Print("ElementsOfIncidenceStructure( ",vs!.geometry," , ",vs!.type,")");
 end );

#############################################################################
#O  Size( <gp>  )
# returns the size of a collection of elements of a <gp>
##
InstallMethod(Size,
 "for elements of a generalised polygon",
 [IsElementsOfGeneralisedPolygon],
 vs -> vs!.size );

#############################################################################
#O  Iterator( <vs>  )
# returns an iterator for the elements of a gp
##
InstallMethod( Iterator,
 "for elements of a generalised polygon",
 [ IsElementsOfGeneralisedPolygon and IsElementsOfGeneralisedPolygonRep],
 function( vs )
  local gp, j, vars;
  gp := vs!.geometry;
  j := vs!.type;
  if j in [1,2] then
   return IteratorList(gp!.listelements(gp,j)); #looks a bit strange, but correct.
  else
   Error("Element type does not exist");
  fi;
 end );

#############################################################################
#O  Iterator( <shadow>  )
# returns an iterator for a shadow of elements of a gp
##
InstallMethod( Iterator,
 "for shadow elements of a generalised polygon",
 [IsShadowElementsOfGeneralisedPolygon and IsShadowElementsOfGeneralisedPolygonRep ],
 function( vs )
        return IteratorList(vs!.func(vs!.element));
 end);

#############################################################################
#O  Random( <vs>  )
# In general, the list of elements might be stored in the GP itself. Then
# the standard method uses the Iterator. This standard method will be called
# also if our particular GPs do not have a collienation/elation group, the
# iterator will be used, which causes the computation of the collineation
#   group including a nice monomorphism. The next time Random is used, this
# group is used.
##
InstallMethod( Random,
 "for a collection of elements of a generalised polygon",
 [ IsElementsOfGeneralisedPolygon and IsElementsOfGeneralisedPolygonRep ],
 function( vs )
  local geo, type, class, group, rep, act;
  geo := vs!.geometry;
  type := vs!.type;
  if IsClassicalGeneralisedHexagon(geo) and HasCollineationGroup(geo) then
   group := CollineationGroup(geo);
   act := CollineationAction(group);
   rep := RepresentativesOfElements(geo)[type];
   return act(rep,Random(group));
  elif HasElationGroup(geo) then
   group := ElationGroup(geo);
   act := CollineationAction(group);
   rep := RepresentativesOfElements(geo)[type];
   return act(Random(rep),Random(group));
  else
   TryNextMethod();
  fi;
 end );

#############################################################################
#O  IsIncident( <x>, <y>  )
# simply uses the incidence relation that is built in in the gp.
# caveat: check here that elements belong to the same geometry!
##
InstallMethod( IsIncident,
 "for elements of a generalised polygon",
    [IsElementOfGeneralisedPolygon, IsElementOfGeneralisedPolygon],
 function( x, y )
  local inc;
  if not x!.geo = y!.geo then
   Error("The elements <x> and <y> do not belong to the same geometry");
  else
   inc := x!.geo!.incidence;
   return inc(x, y);
  fi;
 end );

#############################################################################
#O  Span( <x>, <y>  )
# return the line spanned by <x> and <y>, if they span a line at all.
##
InstallMethod( Span,
    "for two elements of a generalised polygon",
    [ IsElementOfGeneralisedPolygon, IsElementOfGeneralisedPolygon ],
    function( x, y )
        local graph, vn, el, i, j, span;
        if not x!.type = 1 and y!.type = 1 then
            Error("<x> and <y> must be points of a generalised polygon");
        elif not x!.geo = y!.geo then
            Error("<x> and <y> must belong to the same generalised polygon");
        fi;
        return x!.geo!.span(x,y);
    end );

#############################################################################
#O  Meet( <x>, <y>  )
# return the line spanned by <x> and <y>, if they span a line at all.
##
InstallMethod( Meet,
    "for two elements of a generalised polygon",
    [ IsElementOfGeneralisedPolygon, IsElementOfGeneralisedPolygon ],
    function( x, y )
        local graph, vn, el, i, j, meet;
        if not x!.type = 2 and y!.type = 2 then
            Error("<x> and <y> must be lines of a generalised polygon");
        elif not x!.geo = y!.geo then
            Error("<x> and <y> must belong to the same generalised polygon");
        fi;
        return x!.geo!.meet(x,y);
    end );

#############################################################################
#O  Wrap( <geo>, <type>, <o>  )
# returns the element of <geo> represented by <o>.
# this method is generic, but of course not fool proof.
##
InstallMethod( Wrap,
 "for a generalised polygon and an object",
 [IsGeneralisedPolygon, IsPosInt, IsObject],
 function( geo, type, o )
  local w;
  w := rec( geo := geo, type := type, obj := o );
  Objectify( NewType( ElementsOfIncidenceStructureFamily,
   IsElementOfIncidenceStructureRep and IsElementOfGeneralisedPolygon ), w );
  return w;
  end );

# CHECKED 11/09/11 jdb
#############################################################################
#A  TypesOfElementsOfIncidenceStructure( <gp> )
# returns the names of the types of the elements of the projective space <ps>
# the is a helper operation.
##
InstallMethod( TypesOfElementsOfIncidenceStructurePlural,
 "for a generalised polygon in the general representation",
    [ IsGeneralisedPolygon and IsGeneralisedPolygonRep ],
  x -> ["points", "lines"] );

#############################################################################
#O ShadowOfElement(<gp>, <el>, <j> )
##
InstallMethod( ShadowOfElement,
 "for a generalised polygon, an element, and an integer",
 [IsGeneralisedPolygon and IsGeneralisedPolygonRep, IsElementOfGeneralisedPolygon, IsPosInt],
 function( gp, el, j )
  local shadow, func;
        if not AmbientGeometry(el) = gp then
            Error("ambient geometry of <el> is not <gp>");
        fi;
        if j = el!.type then
            func := x->[x];
        elif j = 1 then
            func := gp!.shadowofline;
        elif j = 2 then
            func := gp!.shadowofpoint;
        else
            Error("<gp> has no shadow elements of type", j );
        fi;

        shadow := rec( geometry := gp, type := j, element := el, func := func );
  return Objectify( NewType( ElementsCollFamily, IsElementsOfIncidenceStructure and
       IsShadowElementsOfGeneralisedPolygon and
       IsShadowElementsOfGeneralisedPolygonRep),
       shadow
      );
 end);

#############################################################################
# View methods for shadow objects.
#############################################################################

InstallMethod( ViewObj,
 "for shadow elements of a generalised polygon",
 [ IsShadowElementsOfGeneralisedPolygon and IsShadowElementsOfGeneralisedPolygonRep ],
 function( vs )
  Print("<shadow ",TypesOfElementsOfIncidenceStructurePlural(vs!.geometry)[vs!.type]," in ");
  ViewObj(vs!.geometry);
  Print(">");
 end );

#############################################################################
#O  Points( <el> )
# returns the points, i.e. elements of type <1> in <el>, relying on ShadowOfElement
# for particular <el>.
##
InstallMethod( Points,
    "for an element of a generalised polygon",
 [ IsElementOfGeneralisedPolygon ],
 function( var )
  return ShadowOfElement(var!.geo, var, 1);
 end );

#############################################################################
#O  Lines( <el> )
# returns the lines, i.e. elements of type <2> in <el>, relying on ShadowOfElement
# for particular <el>.
##
InstallMethod( Lines,
    "for an element of a generalised polygon",
 [ IsElementOfGeneralisedPolygon ],
 function( var )
  return ShadowOfElement(var!.geo, var, 2);
 end );

#############################################################################
#O  DistanceBetweenElements( <gp>, <el1>, <el2> )
# returns the distance in the incidence graph between two elements
# Important notice: the '5' before the function increases the priority of this
# method for a pair of points satisfying the filters. The only situation where
# this is relevant is to compute the distance between elements of the Classical
# hexagons. Elements of these are also IsSubspaceOfClassicalPolarSpace. For
# elements of classical GQs, the above method for IsSubspaceOfClassicalPolarSpace
# must be applied. For elements of classical GHs, the generic method here must be
# used. But elements of classical GHs satisfy both filters, so the rank of the filters
# is used to select the method. As the rank of IsSubspaceOfClassicalPolarSpace is
# larger than the rank of IsElementOfGeneralisedPolygon, the wrong method gets
# selected for elements of GHs. The difference in rank is currently 2, times 2 makes
# 4, so adding 5 here should do the job, and it does.
##
InstallMethod( DistanceBetweenElements,
    "for a gp in gpRep and two of its elements",
 [ IsElementOfGeneralisedPolygon, IsElementOfGeneralisedPolygon],
    5,
 function( p, q )
  local geo;
        geo := p!.geo;
        if not geo = q!.geo then
            Error("<p> and <q> are not elements of the same generalised polygon");
        fi;
        return geo!.distance(p,q);
 end );

#############################################################################
#O  IncidenceGraph( <gp> )
# We could install a generic method. But currently, our particular GPs (hexagons,
# elation GQs, and classical GQs) have a particular method for good reasons. All other GPs
# currently possible to construct, have their incidence graph computed upon construction.
# So we may restrict here to checking whether this attribute is bounded, and return it,
# or print an error message. Note that we deal here with a mutable attribute.
###
InstallMethod( IncidenceGraph,
    "for a generalised polygon (in all possible representations",
    [ IsGeneralisedPolygon ],
    function( gp )
    local points, lines, graph, sz, adj, elations, gg, coll;
    #if not "grape" in RecNames(GAPInfo.PackagesLoaded) then
    #   Error("You must load the GRAPE package\n");
    #fi;
    if IsBound(gp!.IncidenceGraphAttr) then
       return gp!.IncidenceGraphAttr;
    else
        Error("no method installed currently");
    fi;
    end );

#############################################################################
#O  IncidenceMatrixOfGeneralisedPolygon( <gp> )
#
InstallMethod( IncidenceMatrixOfGeneralisedPolygon,
 "for a generalised polygon",
 [ IsGeneralisedPolygon ],
 function( gp )
  local graph, mat, incmat, szpoints, szlines;
  graph := IncidenceGraph( gp );
  mat := CollapsedAdjacencyMat(Group(()), graph);

    ## The matrix above is the adjacency matrix of the
    ## bipartite incidence graph.

  szpoints := Size(Points(gp));
  szlines := Size(Lines(gp));

  incmat := mat{[1..szpoints]}{[szpoints+1..szpoints+szlines]};
  return incmat;
 end );

#############################################################################
#O  CollineationGroup( <gp> )
# This method is generic. Note that:
# - for classical GQs, we have completely different methods to compute their
# collineation group, of course for good reasons;
# - for classical generalised hexagons, the same remark applies;
# - when a GP is constructed through generic methods, the underlying graph is
# always computed, since this is the only way to check if the input is not rubbish.
# But then the VertexNames of the constructed graph are the underlying objects.
# For particular GQs, the underlying graph is not computed upon construction.
#   But computing a graph afterwards, is very naturally done with the elements themselves
#   as VertexNames. This has some technical consequences to compute the collineation
#   group and to define the CollineationAction of it.
# To distinguish in this method, we introduced the property HasGraphWithUnderlyingObjectsAsVertices.
###
InstallMethod( CollineationGroup,
    "for a generalised polygon",
    [ IsGeneralisedPolygon and IsGeneralisedPolygonRep ],
    function( gp )
        local graph, aut, act, stab, coll, ptsn, points, pointsobj;
        graph := IncidenceGraph( gp );
        aut := AutomorphismGroup( graph );
        points := AsList(Points(gp));
        if HasGraphWithUnderlyingObjectsAsVertices(gp) then
            pointsobj := List(points,x->x!.obj);
            ptsn := Set(pointsobj,x->Position(VertexNames(graph),x));
        else
            ptsn := Set(points,x->Position(VertexNames(graph),x));
        fi;
        coll := Stabilizer(aut, ptsn, OnSets);
        if HasGraphWithUnderlyingObjectsAsVertices(gp) then
            act := function(el,g)
                local src,img;
                if el!.type = 1 then
                    src := Position(VertexNames(graph),el!.obj);
                    img := src^g;
                    return Wrap(gp,1,VertexNames(graph)[img]);
                elif el!.type = 2 then
                    src := Position(VertexNames(graph),el!.obj);
                    img := src^g;
                    return Wrap(gp,2,VertexNames(graph)[img]);
                fi;
            end;
        else
            act := function(el,g)
                local src,img;
    src := Position(VertexNames(graph),el); #change wrt generic function which would be el!.obj
    img := src^g;
    return VertexNames(graph)[img];
            end;
        fi;
        SetCollineationAction( coll, act );
  return coll;
    end );

#############################################################################
#O  BlockDesignOfGeneralisedPolygon( <gp> )
# Note about BlockDesign: this is a global (so read-only) function in the
# "design" package. We have created in FinInG a function BlockDesign (not
# through DeclareGlobalFunction, so it can be overwritten at user level and
# through loading other packages), so that the method for BlockDesignOfGeneralisedPolygon
# can be loaded without the "design" package loaded. We make sure that this method checks
# whether design is loaded, and produces an error when this is not the case.
#
InstallMethod( BlockDesignOfGeneralisedPolygon,
    "for a generalised polygon",
    [ IsGeneralisedPolygon and IsGeneralisedPolygonRep ],
    function( gp )
        local points, lines, des, blocks, l, b, elations, gg, orbs;
        if not IsPackageLoaded("design") then #this is a gap4r10 function.
        #if not "design" in RecNames(GAPInfo.PackagesLoaded) then
            Error("You must load the DESIGN package\n");
        fi;
        if IsBound(gp!.BlockDesignOfGeneralisedPolygonAttr) then
            return gp!.BlockDesignOfGeneralisedPolygonAttr;
        fi;
        points := AsList(Points(gp));;
        lines := AsList(Lines(gp));;
        if IsElationGQ(gp) and HasElationGroup( gp ) then
            elations := ElationGroup(gp);
            Info(InfoFinInG, 1, "Computing orbits on lines of gen. polygon...");
            orbs := List( Orbits(elations, lines, CollineationAction(elations)), Representative);
            orbs := List(orbs, l -> Filtered([1..Size(points)], i -> points[i] * l));
            gg := Action(elations, points, CollineationAction( elations ) );
            Info(InfoFinInG, 1, "Computing block design of generalised polygon...");
            des := BlockDesign(Size(points), orbs, gg );
        elif HasCollineationGroup(gp) then
            gg := CollineationGroup(gp);
            orbs := List( Orbits(gg, lines, CollineationAction(gg)), Representative);
            orbs := List(orbs, l -> Filtered([1..Size(points)], i -> points[i] * l));
            gg := Action(gg, points, CollineationAction( gg ) );
            des := BlockDesign(Size(points), orbs, gg );
        else
            blocks := [];
            for l in lines do
                b := Filtered([1..Size(points)], i -> points[i] * l);
                Add(blocks, b);
            od;
            des := BlockDesign(Size(points), Set(blocks));
        fi;
        Setter( BlockDesignOfGeneralisedPolygonAttr )( gp, des );
        return des;
  end );

#############################################################################
#
# Part II: particular models of GPs.
#
#############################################################################

#############################################################################
#
# Desarguesian projective planes. Methods needed:
# - DistanceBetweenElements
# - IncidenceGraph
#
#############################################################################

#############################################################################
#O  DistanceBetweenElements( <v>, <w> )
# It is possible to create points and lines of PG(2,q) in the category
# IsElementsOfGeneralisedPolygon (or even more specified). But this would increase
# the dependency of projectivespace.gi on gpolygons.gd, which we want to avoid.
###
InstallMethod( DistanceBetweenElements,
    "for subspaces of a projective space",
    [ IsSubspaceOfProjectiveSpace, IsSubspaceOfProjectiveSpace ],
    function( v, w )
        if not IsDesarguesianPlane(v!.geo) then
            Error( "Elements must have a generalised polygon as ambient geometry" );
        fi;
        if not v!.geo = w!.geo then
            Error( "Elements must belong to the same generalised polygon ");
        fi;
        if v = w then
            return 0;
        elif v!.type <> w!.type then
   if IsIncident(v,w) then
    return 1;
   else
    return 3;
   fi;
  else
   return 2;
  fi;
 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.
##
# Note that there is actually a method for IsProjectiveSpace, and IsDesarguesianPlane
# is a subcategory of IsProjectiveSpace. But the property HasGraphWithUnderlyingObjectsAsVertices
# is important for IsGeneralisedPolygon. We could check in the method for projective
# spaces whether the spaces is a plane, and set the property there, but we want
# to keep projectivespace.gi independent from gpolygons.gd.
###
InstallMethod( IncidenceGraph,
    "for a Desarguesian plane",
    [ IsDesarguesianPlane ],
    function( gp )
        local points, lines, graph, adj, group, coll, sz;
        #if not "grape" in RecNames(GAPInfo.PackagesLoaded) then
        #    Error("You must load the GRAPE package\n");
        #fi;
        if IsBound(gp!.IncidenceGraphAttr) then
            return gp!.IncidenceGraphAttr;
        fi;
        points := AsList(Points(gp));
        lines := AsList(Lines(gp));
        Setter( HasGraphWithUnderlyingObjectsAsVertices )( gp, false );
        Info(InfoFinInG, 1, "Computing incidence graph of generalised polygon...");
        adj := function(x,y)
            if x!.type <> y!.type then
                return IsIncident(x,y);
            else
                return false;
            fi;
        end;
        group := CollineationGroup(gp);
        graph := Graph(group,Concatenation(points,lines),OnProjSubspaces,adj,true);
        Setter( IncidenceGraphAttr )( gp, graph );
        return graph;
    end );

#############################################################################
#
# Classical GQs. Methods needed:
# - DistanceBetweenElements
# - IncidenceGraph
#
#############################################################################

#############################################################################
#O  DistanceBetweenElements( <v>, <w> )
# It is possible to create points and lines of PG(2,q) in the category
# IsElementsOfGeneralisedPolygon (or even more specified). But this would increase
# the dependency of projectivespace.gi on gpolygons.gd, which we want to avoid.
###
InstallMethod( DistanceBetweenElements,
    "for subspaces of a projective space",
    [ IsSubspaceOfClassicalPolarSpace, IsSubspaceOfClassicalPolarSpace ],
    function( v, w )
        if not IsClassicalGQ(v!.geo) then
            Error( "Elements must have a generalised polygon as ambient geometry" );
        fi;
        if not v!.geo = w!.geo then
            Error( "Elements must belong to the same generalised polygon ");
        fi;
        if v = w then
            return 0;
        elif v!.type <> w!.type then
   if IsIncident(v,w) then
    return 1;
   else
    return 3;
   fi;
  else
   if v!.type = 1 then
    if Span(v,w) in v!.geo then
     return 2;
    else
     return 4;
    fi;
   else
    if ProjectiveDimension(Meet(v,w)) = 0 then
     return 2;
    else
     return 4;
    fi;
   fi;
  fi;
 end );

#############################################################################
#O  IncidenceGraph( <gp> )
##
# Note that there is actually a method for IsProjectiveSpace, and IsDesarguesianPlane
# is a subcategory of IsProjectiveSpace. But the property HasGraphWithUnderlyingObjectsAsVertices
# is important for IsGeneralisedPolygon. We could check in the method for projective
# spaces whether the spaces is a plane, and set the property there, but we want
# to keep polarspace.gi independent from gpolygons.gd.
###
InstallMethod( IncidenceGraph,
    "for a generalised polygon (in all possible representations",
    [ IsClassicalGQ ],
    function( gp )
        local points, lines, graph, adj, group, coll, sz;
        #if not "grape" in RecNames(GAPInfo.PackagesLoaded) then
        #    Error("You must load the GRAPE package\n");
        #fi;
        if IsBound(gp!.IncidenceGraphAttr) then
            return gp!.IncidenceGraphAttr;
        fi;
        if not HasCollineationGroup(gp) then
            Error("No collineation group computed. Please compute collineation group before computing incidence graph\,n");
        else
            points := AsList(Points(gp));
            lines := AsList(Lines(gp));
            Setter( HasGraphWithUnderlyingObjectsAsVertices )( gp, false );
            Info(InfoFinInG, 1, "Computing incidence graph of generalised polygon...");
            adj := function(x,y)
                if x!.type <> y!.type then
                    return IsIncident(x,y);
                else
                    return false;
                fi;
            end;
            group := CollineationGroup(gp);
            graph := Graph(group,Concatenation(points,lines),OnProjSubspaces,adj,true);
            Setter( IncidenceGraphAttr )( gp, graph );
            return graph;
        fi;
  end );

#############################################################################
#
#  Classical Generalised Hexagons
#  This section implements H(q) and T(q,q^3). In FinInG, these geometries
#  are nicely constructed inside polar spaces, but are also constructed
#  formally as generalised polygons, which make typical operations available
#  Both are also Lie geometries, and are hard wired embedded inside the corresponding
#  polar space. See chapter 4 of the documentation.
#
#  For both models we (have to) install methods for Wrap etc. This makes other
#  operations, like OnProjSubspaces (action function) generic. As such, we can fully
#  exploit the fact that these geometries are also Lie geometries.
#
#############################################################################

#############################################################################
#O  Wrap( <geo>, <type>, <o>  )
# returns the element of <geo> represented by <o>
##
InstallMethod( Wrap,
 "for a generalised polygon and an object",
 [IsClassicalGeneralisedHexagon, IsPosInt, IsObject],
 function( geo, type, o )
  local w;
  w := rec( geo := geo, type := type, obj := o );
  Objectify( NewType( SoPSFamily, IsElementOfIncidenceStructureRep and IsElementOfGeneralisedPolygon
   and IsSubspaceOfClassicalPolarSpace ), w );
  return w;
  end );

#############################################################################
# The fixed, hard-coded triality :-)
#############################################################################

#############################################################################
#F  SplitCayleyPointToPlane5( <el> )
# returns a list of vectors spanning the plane of W(5,q): ---, which is the
# image of the point represented by <w> under the fixed triality
###
InstallGlobalFunction( SplitCayleyPointToPlane5,
    function(w, f)
        local z, hyps, q, y, spacevec, hyp, vec, int, n;
        q := Size(f);
        #w := Unpack(elvec);
  y := w{[1..3]};
        y{[5..7]} := w{[4..6]};
        y[4] := (y[1]*y[5]+y[2]*y[6]+y[3]*y[7])^(q/2);
        y[8] := -y[4];
        z := [];
  n := Zero(f);
        z[1] := [n,y[3],-y[2],y[5],y[8],n,n,n];
        z[2] := [-y[3],n,y[1],y[6],n,y[8],n,n];
        z[3] := [y[2],-y[1],n,y[7],n,n,y[8],n];
        z[4] := [n,n,n,-y[4],y[1],y[2],y[3],n];
        z[5] := [y[4],n,n,n,n,y[7],-y[6],y[1]];
        z[6] := [n,y[4],n,n,-y[7],n,y[5],y[2]];
        z[7] := [n,n,y[4],n,y[6],-y[5],n,y[3]];
        z[8] := [y[5],y[6],y[7],n,n,n,n,-y[8]];
        z := Filtered(z,x->not IsZero(x));
        hyp := [0,0,0,1,0,0,0,1]*Z(q)^0;
        Add(z,[0,0,0,1,0,0,0,1]*Z(q)^0);
        spacevec := NullspaceMat(TransposedMat(z));
  int := IdentityMat(8,f){[1..7]};
  int[4][8] := -One(f); #could have been One(f) too, since this is only used in even char...
  vec := SumIntersectionMat(spacevec, int)[2];
        return vec{[1..3]}{[1,2,3,5,6,7]};
    end );

#############################################################################
#F  SplitCayleyPointToPlane( <elvec>, <f> )
# returns a list of vectors spanning the plane of Q(6,q): ---, which is the
# image of the point represented by <elvec> under the fixed triality
##
InstallGlobalFunction( SplitCayleyPointToPlane,
 function(elvec, f)
  local z, hyps, y, spacevec, hyp, vec, int, n;
  y := ShallowCopy(elvec);
  y[8] := -y[4];
  z := [];
  n := Zero(f);
  z[1] := [n,y[3],-y[2],y[5],y[8],n,n,n];
  z[2] := [-y[3],n,y[1],y[6],n,y[8],n,n];
  z[3] := [y[2],-y[1],n,y[7],n,n,y[8],n];
  z[4] := [n,n,n,-y[4],y[1],y[2],y[3],n];
  z[5] := [y[4],n,n,n,n,y[7],-y[6],y[1]];
  z[6] := [n,y[4],n,n,-y[7],n,y[5],y[2]];
  z[7] := [n,n,y[4],n,y[6],-y[5],n,y[3]];
  z[8] := [y[5],y[6],y[7],n,n,n,n,-y[8]];
  z := Filtered(z,x->not IsZero(x));
  hyp := [0,0,0,1,0,0,0,1]*One(f);
  Add(z,[0,0,0,1,0,0,0,1]*One(f));
  spacevec := NullspaceMat(TransposedMat(z));
  int := IdentityMat(8,f){[1..7]};
  int[4,8] := -One(f);
  vec := SumIntersectionMat(spacevec, int)[2];
  return vec{[1..3]}{[1..7]};
 end );

#############################################################################
#F  ZeroPointToOnePointsSpaceByTriality( <elvec>, <frob>, <f> )
# returns a list of vectors spanning the solid of Q+(7,q): ---, which is the
# image of the point represented by <elvec> under the fixed triality
##
InstallGlobalFunction( ZeroPointToOnePointsSpaceByTriality,
 function(elvec,frob,f)
 # elvec represents a point of T(q,q^3)
  local z, hyps, y, spacevec, n;
  n := Zero(f);
  y := elvec^frob;
  z := [];
  z[1] := [n,y[3],-y[2],y[5],y[8],n,n,n];
  z[2] := [-y[3],n,y[1],y[6],n,y[8],n,n];
  z[3] := [y[2],-y[1],n,y[7],n,n,y[8],n];
  z[4] := [n,n,n,-y[4],y[1],y[2],y[3],n];
  z[5] := [y[4],n,n,n,n,y[7],-y[6],y[1]];
  z[6] := [n,y[4],n,n,-y[7],n,y[5],y[2]];
  z[7] := [n,n,y[4],n,y[6],-y[5],n,y[3]];
  z[8] := [y[5],y[6],y[7],n,n,n,n,-y[8]];
  z := Filtered(z,x->not IsZero(x));
  spacevec := NullspaceMat(TransposedMat(z));
  return spacevec;
 end );

#############################################################################
#F  TwistedTrialityHexagonPointToPlaneByTwoTimesTriality( <elvec>, <frob>, <f> )
# elvec represents a point of T(q^3,q). elvec^frob is a one point, elvec^frob^2
# is a two point. This function computes a basis for the intersection of the
# one and two point (which are actually generators of Q+(7,q)). There intersection
# will be a plane containing the q+1 lines through elvec.
##
InstallGlobalFunction( TwistedTrialityHexagonPointToPlaneByTwoTimesTriality,
 function(elvec,frob,f)
  local z, hyps, y, pg, spacevec1, spacevec2, n;
  n := Zero(f);
  #y := Unpack(elvec)^frob;
  y := elvec^frob;
  z := [];
  z[1] := [n,y[3],-y[2],y[5],y[8],n,n,n];
  z[2] := [-y[3],n,y[1],y[6],n,y[8],n,n];
  z[3] := [y[2],-y[1],n,y[7],n,n,y[8],n];
  z[4] := [n,n,n,-y[4],y[1],y[2],y[3],n];
  z[5] := [y[4],n,n,n,n,y[7],-y[6],y[1]];
  z[6] := [n,y[4],n,n,-y[7],n,y[5],y[2]];
  z[7] := [n,n,y[4],n,y[6],-y[5],n,y[3]];
  z[8] := [y[5],y[6],y[7],n,n,n,n,-y[8]];
  z := Filtered(z,x->not IsZero(x));
  spacevec1 := NullspaceMat(TransposedMat(z));
  z := y^frob;
  y := [];
  y[1] := [n,-z[3],z[2],n,z[4],n,n,z[5]];
  y[2] := [z[3],n,-z[1],n,n,z[4],n,z[6]];
  y[3] := [-z[2],z[1],n,n,n,n,z[4],z[7]];
  y[4] := [z[5],z[6],z[7],-z[4],n,n,n,n];
  y[5] := [z[8],n,n,z[1],n,-z[7],z[6],n];
  y[6] := [n,z[8],n,z[2],z[7],n,-z[5],n];
  y[7] := [n,n,z[8],z[3],-z[6],z[5],n,n];
  y[8] := [n,n,n,n,z[1],z[2],z[3],-z[8]];
  y := Filtered(y,x->not IsZero(x));
  spacevec2 := NullspaceMat(TransposedMat(y));
  return SumIntersectionMat(spacevec1, spacevec2)[2];
 end );

#############################################################################
# Constructor operations for the Classical Generalised Hexagons.
#############################################################################

#############################################################################
#O  SplitCayleyHexagon( <f> )
# returns the split cayley hexagon over <f>
##
InstallMethod( SplitCayleyHexagon,
 "for a finite field",
 [ IsField and IsFinite ],
 function( f )
    local geo, ty, repline, reppointvect, reppoint, replinevect, dist,
     hvm, ps, hvmform, form, nonzerof, x, w, listels, shadpoint, shadline;
    if IsOddInt(Size(f)) then
       ## the corresponding sesquilinear form here for
       ## q odd is the matrix
       ## [[0,0,0,0,1,0,0],[0,0,0,0,0,1,0],
       ## [0,0,0,0,0,0,1],[0,0,0,-2,0,0,0],
       ## [1,0,0,0,0,0,0],[0,1,0,0,0,0,0],[0,0,1,0,0,0,0]];

    ## this is Hendrik's form
  hvm := List([1..7], i -> [0,0,0,0,0,0,0]*One(f));
  hvm{[1..3]}{[5..7]} := IdentityMat(3, f);
  hvm{[5..7]}{[1..3]} := IdentityMat(3, f);
  hvm[4][4] := -2*One(f);
  hvmform := BilinearFormByMatrix(hvm, f);
  ps := PolarSpace(hvmform);
  # UnderlyingObject will return a cvec.
  reppointvect := UnderlyingObject(RepresentativesOfElements(ps)[1]);

       ## Hendrik's canonical line is <(1,0,0,0,0,0,0), (0,0,0,0,0,0,1)>
  replinevect := [[1,0,0,0,0,0,0], [0,0,0,0,0,0,1]] * One(f);
  TriangulizeMat(replinevect);
  #ConvertToMatrixRep(replinevect, f); #is useless now.
        shadpoint := function( pt )
            local planevec, flag, plane, f;
            f := BaseField( pt );
            planevec := SplitCayleyPointToPlane( Unpack(pt!.obj), f );
            plane := VectorSpaceToElement(PG(6,f),planevec);
            flag := FlagOfIncidenceStructure(PG(6,f),[pt,plane]);
            return List(ShadowOfFlag(PG(6,f),flag,2),x->Wrap(pt!.geo,2,Unwrap(x)));
        end;

        dist := function(el1,el2)
            local x,y;
            if el1=el2 then
                return 0;
            elif el1!.type = 1 and el2!.type = 1 then
                y := VectorSpaceToElement(PG(6,f), SplitCayleyPointToPlane(Unpack(el2!.obj),f)); #PG(5,f): avoids some unnecessary checks
                if el1 in y then
                    return 2;
                else
                    x := VectorSpaceToElement(PG(6,f), SplitCayleyPointToPlane(Unpack(el1!.obj),f));
                    if ProjectiveDimension(Meet(x,y)) = 0 then
                        return 4;
                    else
                        return 6;
                    fi;
                fi;
            elif el1!.type = 2 and el2!.type = 2 then
                if ProjectiveDimension(Meet(el1,el2)) = 0 then
                    return 2;
                fi;
                x := TangentSpace(ps,el1);
                if ProjectiveDimension(Meet(x,el2)) = 0 then
                    return 4;
                else
                    return 6;
                fi;
            elif el1!.type = 1 and el2!.type = 2 then
                if el1 in el2 then
                    return 1;
                fi;
                x := VectorSpaceToElement(PG(6,f), SplitCayleyPointToPlane(Unpack(el1!.obj),f));
                if ProjectiveDimension(Meet(x,el2)) = 0 then
                    return 3;
                else
                    return 5;
                fi;
            else
                return dist(el2,el1);
            fi;
        end;

    else
       ## Here we embed the hexagon in W(5,q)
       ## Hendrik's form
  hvm := List([1..6], i -> [0,0,0,0,0,0]*One(f));
  hvm{[1..3]}{[4..6]} := IdentityMat(3, f);
  hvm{[4..6]}{[1..3]} := IdentityMat(3, f);
  hvmform := BilinearFormByMatrix(hvm, f);
  ps := PolarSpace(hvmform);
  # UnderlyingObject will return a cvec.
  reppointvect := UnderlyingObject(RepresentativesOfElements(ps)[1]);

  ## Hendrik's canonical line is <(1,0,0,0,0,0), (0,0,0,0,0,1)>
  replinevect := [[1,0,0,0,0,0], [0,0,0,0,0,1]] * One(f);
  TriangulizeMat(replinevect);
  #ConvertToMatrixRep(replinevect, f); #is useless now.
        shadpoint := function( pt )
            local planevec, flag, plane, f;
            f := BaseField( pt );
            planevec := SplitCayleyPointToPlane5( Unpack(pt!.obj), f );
            plane := VectorSpaceToElement(PG(5,f),planevec);
            flag := FlagOfIncidenceStructure(PG(5,f),[pt,plane]);
            return List(ShadowOfFlag(PG(5,f),flag,2),x->Wrap(pt!.geo,2,Unwrap(x)));
        end;

        dist := function(el1,el2)
            local x,y;
            if el1=el2 then
                return 0;
            elif el1!.type = 1 and el2!.type = 1 then
                y := VectorSpaceToElement(PG(5,f), SplitCayleyPointToPlane5(Unpack(el2!.obj),f)); #PG(5,f): avoids some unnecessary checks
                if el1 in y then
                    return 2;
                else
                    x := VectorSpaceToElement(PG(5,f), SplitCayleyPointToPlane5(Unpack(el1!.obj),f));
                    if ProjectiveDimension(Meet(x,y)) = 0 then
                        return 4;
                    else
                        return 6;
                    fi;
                fi;
            elif el1!.type = 2 and el2!.type = 2 then
                if ProjectiveDimension(Meet(el1,el2)) = 0 then
                    return 2;
                fi;
                x := TangentSpace(ps,el1);
                if ProjectiveDimension(Meet(x,el2)) = 0 then
                    return 4;
                else
                    return 6;
                fi;
            elif el1!.type = 1 and el2!.type = 2 then
                if el1 in el2 then
                    return 1;
                fi;
                x := VectorSpaceToElement(PG(5,f), SplitCayleyPointToPlane5(Unpack(el1!.obj),f));
                if ProjectiveDimension(Meet(x,el2)) = 0 then
                    return 3;
                else
                    return 5;
                fi;
            else
                return dist(el2,el1);
            fi;
        end;

    fi;
 #now comes the cmatrixification of the replinevect
 replinevect := NewMatrix(IsCMatRep,f,Length(reppointvect),replinevect);

 listels := function(gp,j)
  local coll,reps;
  coll := CollineationGroup(gp);
  reps := RepresentativesOfElements( gp );
  return Enumerate(Orb(coll, reps[j], OnProjSubspaces));
 end;

 shadline := function( l )
  return List(Points(ElementToElement(AmbientSpace(l),l)),x->Wrap(l!.geo,1,x!.obj));
 end;

 #in the next line, we set the data fields for the geometry. We have to take into account that H(q) will also be
 #a Lie geometry, so it needs more data fields than a GP. But we can derive this information from ps.
 geo := rec( pointsobj := [], linesobj := [], incidence:= \*, listelements := listels, basefield := BaseField(ps),
  dimension := Dimension(ps), vectorspace := UnderlyingVectorSpace(ps), polarspace := ps,
  shadowofpoint := shadpoint, shadowofline := shadline, distance := dist);
    ty := NewType( GeometriesFamily, IsClassicalGeneralisedHexagon and IsGeneralisedPolygonRep );
    Objectify( ty, geo );
    SetAmbientSpace(geo, AmbientSpace(ps));
    SetAmbientPolarSpace(geo,ps);
    SetOrder(geo, [Size(f), Size(f)]);
    SetTypesOfElementsOfIncidenceStructure(geo, ["point","line"]);
    SetRankAttr(geo, 2);

    #now we are ready to pack the representatives of the elements, which are also elements of a polar space.
    #recall that reppointvect and replinevect are triangulized.
 #we can not count here on VectorSpaceToElement (yet). Otherwise I could have left out the "replinevect := NewMatrix(Is..." part above.
    w := rec(geo := geo, type := 1, obj := reppointvect);
    reppoint := Objectify( NewType( SoPSFamily,  IsElementOfGeneralisedPolygon and IsElementOfIncidenceStructureRep
                  and IsSubspaceOfClassicalPolarSpace ), w );
    w := rec(geo := geo, type := 2, obj := replinevect);
    repline := Objectify( NewType( SoPSFamily, IsElementOfGeneralisedPolygon and IsElementOfIncidenceStructureRep
                  and IsSubspaceOfClassicalPolarSpace ), w );
    SetRepresentativesOfElements(geo, [reppoint, repline]);
    #SetName(geo,Concatenation("Split Cayley Hexagon of order ", String(Size(f))));
 SetName(geo,Concatenation("H(",String(Size(f)),")"));
 return geo;
  end );

#############################################################################
#O  SplitCayleyHexagon( <q> )
# shortcut to previous method.
##
InstallMethod( SplitCayleyHexagon,
 "input is a prime power",
 [ IsPosInt ],
 function( q )
  return SplitCayleyHexagon(GF(q));
 end );

# 24/3/2014. cmat changes.
#############################################################################
#O  SplitCayleyHexagon( <ps> )
# returns the split cayley hexagon over <f>
##
InstallMethod( SplitCayleyHexagon,
 "for a classical polar space",
 [ IsClassicalPolarSpace ],
 function( ps )
    local geo, ty, repline, reppointvect, reppoint, replinevect, f, naampje, eq, dist,
     hvm, hvmform, form, nonzerof, x, w, listels, shadpoint, shadline, change, c1, c2;
  f := BaseField(ps);
 if IsParabolicQuadric(ps) and Dimension(ps) = 6 then
  hvm := List([1..7], i -> [0,0,0,0,0,0,0]*One(f));
  hvm{[1..3]}{[5..7]} := IdentityMat(3, f);
  hvm[4][4] := -One(f);
  hvmform := QuadraticFormByMatrix(hvm, f);
  hvm := PolarSpace(hvmform);
  c1 := BaseChangeToCanonical(hvmform);
  if not IsCanonicalPolarSpace(ps) then
   c2 := BaseChangeToCanonical(QuadraticForm(ps));
   change := c1^-1*c2;
  else
   change := c1^-1;
  fi;
  # UnderlyingObject will return a cvec. We must be a bit careful: reppointvect must be normed.
  reppointvect := UnderlyingObject(RepresentativesOfElements(hvm)[1]) * change; #to be changed
  reppointvect := reppointvect / First(reppointvect,x->not IsZero(x));
  replinevect := ([[1,0,0,0,0,0,0], [0,0,0,0,0,0,1]] * One(f)) * change;
  TriangulizeMat(replinevect);

        shadpoint := function( pt )
            local planevec, flag, plane, f;
            f := BaseField( pt );
            planevec := SplitCayleyPointToPlane( Unpack(pt!.obj) * change^-1, f ) * change;
            plane := VectorSpaceToElement(PG(6,f),planevec);
            flag := FlagOfIncidenceStructure(PG(6,f),[pt,plane]);
            return List(ShadowOfFlag(PG(6,f),flag,2),x->Wrap(pt!.geo,2,Unwrap(x)));
        end;
        dist := function(el1,el2)
            local x,y;
            if el1=el2 then
                return 0;
            elif el1!.type = 1 and el2!.type = 1 then
                y := VectorSpaceToElement(PG(6,f), SplitCayleyPointToPlane(Unpack(el2!.obj) * change^-1,f) * change); #PG(6,f): avoids some unnecessary checks
                if el1 in y then
                    return 2;
                else
                    x := VectorSpaceToElement(PG(6,f), SplitCayleyPointToPlane(Unpack(el1!.obj) * change^-1,f) * change);
                    if ProjectiveDimension(Meet(x,y)) = 0 then
                        return 4;
                    else
                        return 6;
                    fi;
                fi;
            elif el1!.type = 2 and el2!.type = 2 then
                if ProjectiveDimension(Meet(el1,el2)) = 0 then
                    return 2;
                fi;
                x := TangentSpace(ps,el1);
                if ProjectiveDimension(Meet(x,el2)) = 0 then
                    return 4;
                else
                    return 6;
                fi;
            elif el1!.type = 1 and el2!.type = 2 then
                if el1 in el2 then
                    return 1;
                fi;
                x := VectorSpaceToElement(PG(6,f), SplitCayleyPointToPlane(Unpack(el1!.obj) * change^-1,f) * change);
                if ProjectiveDimension(Meet(x,el2)) = 0 then
                    return 3;
                else
                    return 5;
                fi;
            else
                return dist(el2,el1);
            fi;
        end;
    elif IsSymplecticSpace(ps) and Dimension(ps) = 5 then
       ## Here we embed the hexagon in W(5,q)
       ## Hendrik's form
  hvm := List([1..6], i -> [0,0,0,0,0,0]*One(f));
  hvm{[1..3]}{[4..6]} := IdentityMat(3, f);
  hvm{[4..6]}{[1..3]} := IdentityMat(3, f);
  hvmform := BilinearFormByMatrix(hvm, f);
  hvm := PolarSpace(hvmform);
  c1 := BaseChangeToCanonical(hvmform);
  if not IsCanonicalPolarSpace(ps) then
   c2 := BaseChangeToCanonical(SesquilinearForm(ps));
   change := c1^-1*c2;
  else
   change := c1^-1;
  fi;
  # UnderlyingObject will return a cvec. We must be a bit carefull: reppointvect must be normed.
  reppointvect := UnderlyingObject(RepresentativesOfElements(hvm)[1]) * change;
  reppointvect := reppointvect / First(reppointvect,x->not IsZero(x));

  ## Hendrik's canonical line is <(1,0,0,0,0,0), (0,0,0,0,0,1)>
  replinevect := ([[1,0,0,0,0,0], [0,0,0,0,0,1]] * One(f)) * change;
  TriangulizeMat(replinevect);
  #ConvertToMatrixRep(replinevect, f); #is useless now.
        shadpoint := function( pt )
            local planevec, flag, plane, f;
            f := BaseField( pt );
            planevec := SplitCayleyPointToPlane5( Unpack(pt!.obj) * change^-1, f ) * change;
            plane := VectorSpaceToElement(PG(5,f),planevec);
            flag := FlagOfIncidenceStructure(PG(5,f),[pt,plane]);
            return List(ShadowOfFlag(PG(5,f),flag,2),x->Wrap(pt!.geo,2,Unwrap(x)));
        end;
        dist := function(el1,el2)
            local x,y;
            if el1=el2 then
                return 0;
            elif el1!.type = 1 and el2!.type = 1 then
                y := VectorSpaceToElement(PG(5,f), SplitCayleyPointToPlane5(Unpack(el2!.obj) * change^-1,f) * change); #PG(5,f): avoids some unnecessary checks
                if el1 in y then
                    return 2;
                else
                    x := VectorSpaceToElement(PG(5,f), SplitCayleyPointToPlane5(Unpack(el1!.obj) * change^-1,f) * change);
                    if ProjectiveDimension(Meet(x,y)) = 0 then
                        return 4;
                    else
                        return 6;
                    fi;
                fi;
            elif el1!.type = 2 and el2!.type = 2 then
                if ProjectiveDimension(Meet(el1,el2)) = 0 then
                    return 2;
                fi;
                x := TangentSpace(ps,el1);
                if ProjectiveDimension(Meet(x,el2)) = 0 then
                    return 4;
                else
                    return 6;
                fi;
            elif el1!.type = 1 and el2!.type = 2 then
                if el1 in el2 then
                    return 1;
                fi;
                x := VectorSpaceToElement(PG(5,f), SplitCayleyPointToPlane5(Unpack(el1!.obj) * change^-1,f) * change);
                if ProjectiveDimension(Meet(x,el2)) = 0 then
                    return 3;
                else
                    return 5;
                fi;
            else
                return dist(el2,el1);
            fi;
        end;
    else
        Error("No embedding of split Cayley hexagon possible in <ps>");
    fi;
 #now comes the cmatrixification of the reppointvect and replinevect
 reppointvect :=  NewMatrix(IsCMatRep,f,Length(reppointvect),[reppointvect])[1];
 replinevect := NewMatrix(IsCMatRep,f,Length(reppointvect),replinevect);

 listels := function(gp,j)
  local coll,reps;
  coll := CollineationGroup(gp);
  reps := RepresentativesOfElements( gp );
  return Enumerate(Orb(coll, reps[j], OnProjSubspaces));
 end;

 shadline := function( l )
  return List(Points(ElementToElement(AmbientSpace(l),l)),x->Wrap(l!.geo,1,x!.obj));
 end;

 #in the next line, we set the data fields for the geometry. We have to take into account that H(q) will also be
 #a Lie geometry, so it needs more data fields than a GP. But we can derive this information from ps.
 geo := rec( pointsobj := [], linesobj := [], incidence:= \*, listelements := listels, basefield := BaseField(ps),
  dimension := Dimension(ps), vectorspace := UnderlyingVectorSpace(ps), polarspace := ps,
  shadowofpoint := shadpoint, shadowofline := shadline, distance := dist, basechange := change);
    ty := NewType( GeometriesFamily, IsClassicalGeneralisedHexagon and IsGeneralisedPolygonRep );
    Objectify( ty, geo );
    SetAmbientSpace(geo, AmbientSpace(ps));
    SetAmbientPolarSpace(geo,ps);
    SetOrder(geo, [Size(f), Size(f)]);
    SetTypesOfElementsOfIncidenceStructure(geo, ["point","line"]);
    SetRankAttr(geo, 2);

    #now we are ready to pack the representatives of the elements, which are also elements of a polar space.
    #recall that reppointvect and replinevect are triangulized.
 #we can not count here on VectorSpaceToElement (yet). Otherwise I could have left out the "replinevect := NewMatrix(Is..." part above.
    w := rec(geo := geo, type := 1, obj := reppointvect);
    reppoint := Objectify( NewType( SoPSFamily,  IsElementOfGeneralisedPolygon and IsElementOfIncidenceStructureRep
                  and IsSubspaceOfClassicalPolarSpace ), w );
    w := rec(geo := geo, type := 2, obj := replinevect);
    repline := Objectify( NewType( SoPSFamily, IsElementOfGeneralisedPolygon and IsElementOfIncidenceStructureRep
                  and IsSubspaceOfClassicalPolarSpace ), w );
    SetRepresentativesOfElements(geo, [reppoint, repline]);
    #it looks a bit exaggerated to do such an effort for the name, but polar spaces do have a Name attribute set...
 if not IsCanonicalPolarSpace(ps) then
  eq := Concatenation(": ",String(EquationForPolarSpace(ps)));
 else
  eq := "";
 fi;
 if ps!.dimension = 5 then
  naampje := Concatenation("W(5, ",String(Size(f)),")",eq);
 else
  naampje := Concatenation("Q(6, ",String(Size(f)),")",eq);
 fi;
 SetName(geo,Concatenation("H(",String(Size(f)),")"," in ",naampje));
 return geo;
  end );

# 24/3/2014. cmat changes. Same principle as SplitCayleyHexagon
#############################################################################
#O  TwistedTrialityHexagon( <f> )
# returns the twisted triality hexagon over <f>
##
InstallMethod( TwistedTrialityHexagon,
 "input is a finite field",
    [ IsField and IsFinite ],
 function( f )
    local geo, ty, points, lines, repline, hvm, ps, orblen, hvmc, c, listels, dist,
          hvmform, form, q, reppoint, reppointvect, replinevect, w, shadline,
          shadpoint, frob;
       ## Field must be GF(q^3);
 frob := FrobeniusAutomorphism(f);
    q := RootInt(Size(f), 3);
 if not q^3 = Size(f) then
       Error("Field order must be a cube of a prime power");
    fi;

       ## Hendrik's form
    hvm := List([1..8], i -> [0,0,0,0,0,0,0,0]*One(f));
    hvm{[1..4]}{[5..8]} := IdentityMat(4, f);
    hvmform := QuadraticFormByMatrix(hvm, f);
    ps := PolarSpace( hvmform );

 ## Hendrik's canonical point is <(1,0,0,0,0,0,0,0)>
    reppointvect := ([1,0,0,0,0,0,0,0] * One(f));
    MultVector(reppointvect,Inverse( reppointvect[PositionNonZero(reppointvect)] ));
 #ConvertToVectorRep(reppointvect, f); #useless now.

 ## Hendrik's canonical line is <(1,0,0,0,0,0,0,0), (0,0,0,0,0,0,1,0)>
    replinevect := ([[1,0,0,0,0,0,0,0], [0,0,0,0,0,0,1,0]] * One(f));
 #ConvertToMatrixRep(replinevect, f); #useless now.

 listels := function(gp,j)
  local coll,reps;
  coll := CollineationGroup(gp);
  reps := RepresentativesOfElements( gp );
  return Enumerate(Orb(coll, reps[j], OnProjSubspaces));
 end;

 shadline := function( l )
  return List(Points(ElementToElement(AmbientSpace(l),l)),x->Wrap(l!.geo,1,x!.obj));
 end;

    shadpoint := function( pt )
        local planevec, flag, plane, f, frob;
        f := BaseField( pt );
        frob := FrobeniusAutomorphism(f);
        planevec := TwistedTrialityHexagonPointToPlaneByTwoTimesTriality( Unpack(pt!.obj), frob, f );
        plane := VectorSpaceToElement(PG(7,f),planevec);
        flag := FlagOfIncidenceStructure(PG(7,f),[pt,plane]);
        # we have q^3+1 lines and have to select q+1 from them. Following can probably done more efficiently,
        # but I need more mathematics then.
        return List(Filtered(ShadowOfFlag(PG(7,f),flag,2),x->x in pt!.geo), y-> Wrap(pt!.geo,2,Unwrap(y)));
    end;

    dist := function(el1,el2)
  local x,y;
        if el1 = el2 then
   return 0;
  elif el1!.type = 1 and el2!.type = 1 then
   y := VectorSpaceToElement(PG(7,f),
    TwistedTrialityHexagonPointToPlaneByTwoTimesTriality(Unpack(el2!.obj),frob,f)); #PG(7,f): avoids some unnecessary checks
   if el1 in y then
    return 2;
   else
    x := VectorSpaceToElement(PG(7,f),
     TwistedTrialityHexagonPointToPlaneByTwoTimesTriality(Unpack(el1!.obj),frob,f));
    if ProjectiveDimension(Meet(x,y)) = 0 then
     return 4;
    else
     return 6;
    fi;
   fi;
  elif el1!.type = 2 and el2!.type = 2 then
   if ProjectiveDimension(Meet(el1,el2)) = 0 then
    return 2;
   fi;
   x := TangentSpace(ps,el1);
   if ProjectiveDimension(Meet(x,el2)) = 0 then
    return 4;
   else
    return 6;
   fi;
  elif el1!.type = 1 and el2!.type = 2 then
   if el1 in el2 then
    return 1;
   fi;
   x := VectorSpaceToElement(PG(7,f),
    TwistedTrialityHexagonPointToPlaneByTwoTimesTriality(Unpack(el1!.obj),frob,f));
   if ProjectiveDimension(Meet(x,el2)) = 0 then
    return 3;
   else
    return 5;
   fi;
  else
   return dist(el2,el1);
  fi;
 end;

    geo := rec( pointsobj := [], linesobj := [], incidence:= \*, listelements := listels, shadowofpoint := shadpoint,
        shadowofline := shadline, basefield := BaseField(ps), dimension := Dimension(ps),
        vectorspace := UnderlyingVectorSpace(ps), polarspace := ps, distance := dist );
    ty := NewType( GeometriesFamily, IsClassicalGeneralisedHexagon and IsGeneralisedPolygonRep );
    Objectify( ty, geo );
    SetAmbientSpace(geo, AmbientSpace(ps));
    SetAmbientPolarSpace(geo,ps);

 #now we are ready to pack the representatives of the elements, which are also elements of a polar space.
 #recall that reppointvect and replinevect are triangulized.
 #now come the cvec/cmat ing of reppointvect and repplinevect.

 reppointvect := CVec(reppointvect,f);
    replinevect := NewMatrix(IsCMatRep,f,Length(reppointvect),replinevect);

 w := rec(geo := geo, type := 1, obj := reppointvect);
    reppoint := Objectify( NewType( SoPSFamily, IsElementOfIncidenceStructure and IsElementOfIncidenceStructureRep and
       IsElementOfGeneralisedPolygon and IsSubspaceOfClassicalPolarSpace ), w );
    w := rec(geo := geo, type := 2, obj := replinevect);
    repline := Objectify( NewType( SoPSFamily, IsElementOfIncidenceStructure and IsElementOfIncidenceStructureRep and
        IsElementOfGeneralisedPolygon and IsSubspaceOfClassicalPolarSpace ), w );

    SetOrder(geo, [q^3, q]);
    SetTypesOfElementsOfIncidenceStructure(geo, ["point","line"]);
    SetRankAttr(geo, 2);
    SetRepresentativesOfElements(geo, [reppoint, repline]);
    #SetName(geo,Concatenation("Twisted Triality Hexagon of order ", String([q^3, q])));
 SetName(geo,Concatenation("T(",String(q^3),", ",String(q),")"));
    return geo;
  end );

#############################################################################
#O  TwistedTrialityHexagon( <q> )
# shortcut to previous method.
##
InstallMethod( TwistedTrialityHexagon,
 "input is a prime power",
 [ IsPosInt ],
 function( q )
  return TwistedTrialityHexagon(GF(q));
 end );

# 24/3/2014. cmat changes. Same principle as SplitCayleyHexagon
#############################################################################
#O  TwistedTrialityHexagon( <ps> )
# returns the twisted triality hexagon over <f>
##
InstallMethod( TwistedTrialityHexagon,
 "for a classical polar space",
 [ IsClassicalPolarSpace ],
 function( ps )
    local geo, ty, points, lines, repline, hvm, orblen, hvmc, c, listels, eq, naampje,
          hvmform, form, q, reppoint, reppointvect, replinevect, w, shadline, f,
          shadpoint, c1, c2, change, dist, frob;
       ## Field must be GF(q^3);
    if not (IsHyperbolicQuadric(ps) and ps!.dimension = 7) then
        Error("No embedding of twisted triality hexagon possible in <ps>");
    fi;
    f := BaseField(ps);
 frob := FrobeniusAutomorphism(f);
    q := RootInt(Size(f), 3);
 if not q^3 = Size(f) then
       Error("Field order must be a cube of a prime power");
    fi;

       ## Hendrik's form
    hvm := List([1..8], i -> [0,0,0,0,0,0,0,0]*One(f));
    hvm{[1..4]}{[5..8]} := IdentityMat(4, f);
    hvmform := QuadraticFormByMatrix(hvm, f);
    hvm := PolarSpace(hvmform);
 c1 := BaseChangeToCanonical(hvmform);
 if not IsCanonicalPolarSpace(ps) then
  c2 := BaseChangeToCanonical(QuadraticForm(ps));
  change := c1^-1*c2;
 else
  change := c1^-1;
 fi;

 ## Hendrik's canonical point is <(1,0,0,0,0,0,0,0)>
    reppointvect := ([1,0,0,0,0,0,0,0] * One(f)) * change;
    MultVector(reppointvect,Inverse( reppointvect[PositionNonZero(reppointvect)] ));
 #ConvertToVectorRep(reppointvect, f); #useless now.

 ## Hendrik's canonical line is <(1,0,0,0,0,0,0,0), (0,0,0,0,0,0,1,0)>
    replinevect := ([[1,0,0,0,0,0,0,0], [0,0,0,0,0,0,1,0]] * One(f)) * change;
 TriangulizeMat(replinevect);

 #ConvertToMatrixRep(replinevect, f); #useless now.

 listels := function(gp,j)
  local coll,reps;
  coll := CollineationGroup(gp);
  reps := RepresentativesOfElements( gp );
  return Enumerate(Orb(coll, reps[j], OnProjSubspaces));
 end;

 shadline := function( l )
  return List(Points(ElementToElement(AmbientSpace(l),l)),x->Wrap(l!.geo,1,x!.obj));
 end;

    shadpoint := function( pt )
        local planevec, flag, plane, f, frob;
        f := BaseField( pt );
        frob := FrobeniusAutomorphism(f);
        planevec := TwistedTrialityHexagonPointToPlaneByTwoTimesTriality( Unpack(pt!.obj) * change^-1, frob, f ) * change;
        plane := VectorSpaceToElement(PG(7,f),planevec);
        flag := FlagOfIncidenceStructure(PG(7,f),[pt,plane]);
        # we have q^3+1 lines and have to select q+1 from them. Following can probably done more efficient,
        # but I need more mathematics then.
        return List(Filtered(ShadowOfFlag(PG(7,f),flag,2),x->x in pt!.geo), y-> Wrap(pt!.geo,2,Unwrap(y)));
    end;

 dist := function(el1,el2)
  local x,y;
        if el1 = el2 then
   return 0;
  elif el1!.type = 1 and el2!.type = 1 then
   y := VectorSpaceToElement(PG(7,f), TwistedTrialityHexagonPointToPlaneByTwoTimesTriality(
    Unpack(el2!.obj) * change^-1,frob,f) * change); #PG(7,f): avoids some unnecessary checks
   if el1 in y then
    return 2;
   else
    x := VectorSpaceToElement(PG(7,f), TwistedTrialityHexagonPointToPlaneByTwoTimesTriality(
     Unpack(el1!.obj) * change^-1,frob,f) * change);
    if ProjectiveDimension(Meet(x,y)) = 0 then
     return 4;
    else
     return 6;
    fi;
   fi;
  elif el1!.type = 2 and el2!.type = 2 then
   if ProjectiveDimension(Meet(el1,el2)) = 0 then
    return 2;
   fi;
   x := TangentSpace(ps,el1);
   if ProjectiveDimension(Meet(x,el2)) = 0 then
    return 4;
   else
    return 6;
   fi;
  elif el1!.type = 1 and el2!.type = 2 then
   if el1 in el2 then
    return 1;
   fi;
   x := VectorSpaceToElement(PG(7,f), TwistedTrialityHexagonPointToPlaneByTwoTimesTriality(
    Unpack(el1!.obj) * change^-1,frob,f) * change);
   if ProjectiveDimension(Meet(x,el2)) = 0 then
    return 3;
   else
    return 5;
   fi;
  else
   return dist(el2,el1);
  fi;
 end;

    geo := rec( pointsobj := [], linesobj := [], incidence:= \*, listelements := listels, shadowofpoint := shadpoint,
        shadowofline := shadline, basefield := BaseField(ps), dimension := Dimension(ps), basechange := change,
        vectorspace := UnderlyingVectorSpace(ps), polarspace := ps, distance := dist );
    ty := NewType( GeometriesFamily, IsClassicalGeneralisedHexagon and IsGeneralisedPolygonRep ); #change by jdb 7/12/11
    Objectify( ty, geo );
    SetAmbientSpace(geo, AmbientSpace(ps));
    SetAmbientPolarSpace(geo,ps);

 #now we are ready to pack the representatives of the elements, which are also elements of a polar space.
 #recall that reppointvect and replinevect are triangulized.
 #now come the cvec/cmat ing of reppointvect and repplinevect.

 #reppointvect := CVec(reppointvect,f);
 reppointvect :=  NewMatrix(IsCMatRep,f,Length(reppointvect),[reppointvect])[1];
    replinevect := NewMatrix(IsCMatRep,f,Length(reppointvect),replinevect);

 w := rec(geo := geo, type := 1, obj := reppointvect);
    reppoint := Objectify( NewType( SoPSFamily, IsElementOfIncidenceStructure and IsElementOfIncidenceStructureRep and
       IsElementOfGeneralisedPolygon and IsSubspaceOfClassicalPolarSpace ), w );
    w := rec(geo := geo, type := 2, obj := replinevect);
    repline := Objectify( NewType( SoPSFamily, IsElementOfIncidenceStructure and IsElementOfIncidenceStructureRep and
        IsElementOfGeneralisedPolygon and IsSubspaceOfClassicalPolarSpace ), w );

    SetOrder(geo, [q^3, q]);
    SetTypesOfElementsOfIncidenceStructure(geo, ["point","line"]);
    SetRepresentativesOfElements(geo, [reppoint, repline]);
    SetRankAttr(geo, 2);
    #SetName(geo,Concatenation("Twisted Triality Hexagon of order ", String([q^3, q])));
 #SetName(geo,Concatenation("T(",String(q^3),", ",String(q),")"));
    if not IsCanonicalPolarSpace(ps) then
  eq := Concatenation(": ",String(EquationForPolarSpace(ps)));
 else
  eq := "";
 fi;
 naampje := Concatenation("Q+(7, ",String(Size(f)),")",eq);
 SetName(geo,Concatenation("T(",String(q^3),", ",String(q),")", " in ",naampje));
    return geo;
  end );

#############################################################################
#O  Display( <egq> )
#  for a classical generalised hexagon
##
InstallMethod( Display,
 "for a classical generalised hexagon",
 [ IsGeneralisedHexagon and IsLieGeometry ],
 function( gp )
  if IsHyperbolicQuadric(AmbientPolarSpace(gp)) then
            Print("Twisted triality hexagon of order ",String(Order(gp)),"\n",
                "with ambient polar space\n", AmbientPolarSpace(gp));
        else
            Print("Split Cayley hexagon of order ",String(Order(gp)),"\n",
                "with ambient polar space\n", AmbientPolarSpace(gp));
        fi;
    end );

##################################################################################
# Groups: we follow the same approach as for polar spaces. There is an operation
# that returns the groups as fining groups, which is used when needed by e.g.
# CollineationGroup( <gh> )
##################################################################################

#############################################################################
#O  G2fining( <d>, <f> )
## returns the Chevalley group G_2(q) (also known as Dickson's group.
# <f> is a finite field. For <d> equals 6, the returned group is a will be a
# subgroup of PGL(7,<f>). When <f> has characteristic 2, <d> equals 5 is allowed,
# then the returned group will be a subgroup of PGL(6,<f>).
##
InstallMethod( G2fining,
 "for a dimension and a field",
 [ IsPosInt, IsField and IsFinite ],
 function(d, f)
 local m, mp, ml, g, gens, nonzerof, newgens;
 if d=6 then
   m:=[[ 0, 0, 0, 0, 0, 1, 0],
             [ 0, 0, 0, 0, 0, 0, 1],
             [ 0, 0, 0, 0, 1, 0, 0],
             [ 0, 0, 0, -1, 0, 0, 0],               ## **
             [ 0, 1, 0, 0, 0, 0, 0],
             [ 0, 0, 1, 0, 0, 0, 0],
             [ 1, 0, 0, 0, 0, 0, 0]]*One(f);
         mp := d ->
   [[1,  0,  0,  0,  0,  d,  0],
             [0,  1,  0,  0, -d,  0,  0],
             [0,  0,  1,  0,  0,  0,  0],
             [0,  0,  -2*d,  1,  0,  0,  0],         ## **
             [0,  0,  0,  0,  1,  0,  0],
             [0,  0,  0,  0,  0,  1,  0],
             [0,  0,d^2,  -d,  0,  0,  1]]*One(f);   ## **
         ml := d ->
   [[1, -d,  0,  0,  0,  0,  0],
             [0,  1,  0,  0,  0,  0,  0],
             [0,  0,  1,  0,  0,  0,  0],
             [0,  0,  0,  1,  0,  0,  0],
             [0,  0,  0,  0,  1,  0,  0],
             [0,  0,  0,  0,  d,  1,  0],
             [0,  0,  0,  0,  0,  0,  1]]*One(f);
  nonzerof := AsList(f){[2..Size(f)]};
  gens := Union([m], List(nonzerof, mp), List(nonzerof, ml));
        #for x in gens do
        #   ConvertToMatrixRep(x, f);
        #od;
 elif d=5 then
  if not IsEvenInt(Characteristic(f)) then
   Error("embedding of G_2(q) in PGL(6,q) is only possible for even q");
  fi;
  m:=[[ 0, 0, 0, 0, 1, 0],
     [ 0, 0, 0, 0, 0, 1],
           [ 0, 0, 0, 1, 0, 0],
           [ 0, 1, 0, 0, 0, 0],
           [ 0, 0, 1, 0, 0, 0],
           [ 1, 0, 0, 0, 0, 0]]*One(f);
  mp := d ->
   [[1,  0,  0,  0,  d,  0],
             [0,  1,  0,  d,  0,  0],
             [0,  0,  1,  0,  0,  0],
             [0,  0,  0,  1,  0,  0],
             [0,  0,  0,  0,  1,  0],
             [0,  0,d^2,  0,  0,  1]]*One(f);
  ml := d ->
   [[1,  d,  0,  0,  0,  0],
             [0,  1,  0,  0,  0,  0],
             [0,  0,  1,  0,  0,  0],
             [0,  0,  0,  1,  0,  0],
             [0,  0,  0,  d,  1,  0],
             [0,  0,  0,  0,  0,  1]]*One(f);
  nonzerof := AsList(f){[2..Size(f)]};
  gens := Union([m], List(nonzerof,mp), List(nonzerof,ml));
 else
  Error("<d> should be 5 or 6");
 fi;
    newgens := List(gens, x -> CollineationOfProjectiveSpace(x, f));
 g := GroupWithGenerators( newgens );
 #SetCollineationAction(coll, OnProjSubspaces);
 SetName(g, Concatenation("G_2(",String(Size(f)),")") );
    return g;
  end );

#############################################################################
#O  3D4fining( <d>, <f> )
##
InstallMethod( 3D4fining,
 "for a finite field",
 [ IsField and IsFinite ],
 function(f)
  local q, pps, frob, sigma, m, mp, ml, nonzerof, nonzeroq, gens, g, newgens;
  q := RootInt(Size(f), 3);
  if not q^3 = Size(f) then
   Error("Field order must be a cube of a prime power");
  fi;
  pps := Characteristic(f);
  frob := FrobeniusAutomorphism(f);
  sigma := frob^LogInt(q,pps);
  # The generators of 3D4(q) were taken from Hendrik
     # Van Maldeghem's book: "Generalized Polygons".

       m:=[[ 0, 0, 0, 0, 0, 1, 0, 0],
           [ 0, 0, 0, 0, 0, 0, 1, 0],
           [ 0, 0, 0, 0, 1, 0, 0, 0],
           [ 0, 0, 0, 0, 0, 0, 0, 1],
           [ 0, 1, 0, 0, 0, 0, 0, 0],
           [ 0, 0, 1, 0, 0, 0, 0, 0],
           [ 1, 0, 0, 0, 0, 0, 0, 0],
           [ 0, 0, 0, 1, 0, 0, 0, 0]]*One(f);
       #ConvertToMatrixRep(m, f);      #jdb 19/09/2018: I think this ConverToMatrixRep is not needed anymore.
       mp:=d->[[1,  0,  0,  0,  0,  d,  0,  0],
               [0,  1,  0,  0, -d,  0,  0,  0],
               [0,  0,  1,  0,  0,  0,  0,  0],
               [0,  0,  -d^sigma,  1,  0,  0,  0,  0],
               [0,  0,  0,  0,  1,  0,  0,  0],
               [0,  0,  0,  0,  0,  1,  0,  0],
               [0,0,d^sigma*d^(sigma^2),-d^(sigma^2),0,0,1,d^sigma],
               [0,0,d^(sigma^2),0,0,0,0,1]]*One(f);
       ml:=d->[[1, -d,  0,  0,  0,  0,  0,  0],
               [0,  1,  0,  0,  0,  0,  0,  0],
               [0,  0,  1,  0,  0,  0,  0,  0],
               [0,  0,  0,  1,  0,  0,  0,  0],
               [0,  0,  0,  0,  1,  0,  0,  0],
               [0,  0,  0,  0,  d,  1,  0,  0],
               [0,  0,  0,  0,  0,  0,  1,  0],
               [0,  0,  0,  0,  0,  0,  0,  1]]*One(f);

  nonzerof := AsList(f){[2..Size(f)]};
  nonzeroq := AsList(GF(q)){[2..q]};
  gens := Union([m], List(nonzerof, mp), List(nonzeroq, ml));
  newgens := List(gens, x -> CollineationOfProjectiveSpace(x,f));
  g := GroupWithGenerators(newgens);
  SetName(g, Concatenation("3D_4(",String(Size(f)),")") );
  return g;
 end );

#############################################################################
#A  CollineationGroup( <hexagon> )
# computes the collineation group of a (classical) generalised hexagon
##
InstallMethod( CollineationGroup,
 "for a generalised hexagon",
 [ IsClassicalGeneralisedHexagon],
 function( hexagon )
  local group, coll, f, gens, newgens, change, d, q, rep, domain, orblen, hom, frob, t,
  sigma, m, mp, ml, nonzerof, nonzeroq,  x;
  f := hexagon!.basefield;
  q := Size(f);
  d := hexagon!.dimension;
  if Size(Set(Order(hexagon))) > 1 then
   f := hexagon!.basefield;
       ## field must be GF(q^3);
   group := 3D4fining(f);
            if IsBound(hexagon!.basechange) then
    change := hexagon!.basechange;
    gens := List(GeneratorsOfGroup(group),x->Unpack(x!.mat));
    newgens := List(gens,x->CollineationOfProjectiveSpace(change^-1 * x * change,f));
            else
    gens := GeneratorsOfGroup(group);
    newgens := ShallowCopy(gens);
            fi;
   coll := GroupWithGenerators(newgens);
   Info(InfoFinInG, 1, "Computing nice monomorphism...");
         t := RootInt(q, 3);
   orblen := (t+1)*(t^8+t^4+1);
   rep := RepresentativesOfElements(hexagon)[2];
   domain := Orb(coll, rep, OnProjSubspaces,
                    rec(orbsizelimit := orblen, hashlen := 2*orblen, storenumbers := true));
   Enumerate(domain);
   Info(InfoFinInG, 1, "Found permutation domain...");
   hom := OrbActionHomomorphism(coll, domain);
   SetIsBijective(hom, true);
   SetNiceObject(coll, Image(hom) );
   SetNiceMonomorphism(coll, hom );
   SetCollineationAction(coll, OnProjSubspaces);
   if not IsBound(hexagon!.basechange) then
    SetName(coll, Concatenation("3D_4(",String(Size(f)),")") );
            fi;
   return coll;
  else
   Info(InfoFinInG, 1, "for Split Cayley Hexagon");
   group := G2fining(d,f);
   if IsBound(hexagon!.basechange) then
    change := hexagon!.basechange;
    gens := List(GeneratorsOfGroup(group),x->Unpack(x!.mat));
    newgens := List(gens,x->CollineationOfProjectiveSpace(change^-1 * x * change,f));
       if not IsPrimeInt(q) then
     frob := FrobeniusAutomorphism(f);
     Add(newgens, CollineationOfProjectiveSpace( change^-1 * IdentityMat(d+1,f) * change^(frob^-1), frob, f ));
    fi;
   else
    gens := GeneratorsOfGroup(group);
    newgens := ShallowCopy(gens);
    if not IsPrimeInt(q) then
     frob := FrobeniusAutomorphism(f);
     Add(newgens, CollineationOfProjectiveSpace( IdentityMat(d+1,f), frob, f ));
    fi;
   fi;
   coll := GroupWithGenerators(newgens);
   Info(InfoFinInG, 1, "Computing nice monomorphism...");
   orblen := (q+1)*(q^4+q^2+1);
   rep := RepresentativesOfElements(hexagon)[1];
   domain := Orb(coll, rep, OnProjSubspaces,
                  rec(orbsizelimit := orblen, hashlen := 2*orblen, storenumbers := true));
   Enumerate(domain);
   Info(InfoFinInG, 1, "Found permutation domain...");
   hom := OrbActionHomomorphism(coll, domain);
   SetIsBijective(hom, true);
   SetNiceObject(coll, Image(hom) );
   SetNiceMonomorphism(coll, hom );
   SetCollineationAction(coll, OnProjSubspaces);
   if not IsBound(hexagon!.basechange) then
    if IsPrime(Size(f)) then
     SetName(coll, Concatenation("G_2(",String(Size(f)),")") );
    else
     SetName(coll, Concatenation("G_2(",String(Size(f)),").", String(Order(frob))) );
    fi;
   fi;
   return coll;
  fi;
 end );

#############################################################################
# Incidence graph for classical generalised hexagons.
#############################################################################

#############################################################################
#O  IncidenceGraph( <gp> )
###
InstallMethod( IncidenceGraph,
    "for an ElationGQ in generic representation",
    [ IsClassicalGeneralisedHexagon and IsGeneralisedPolygonRep ],
    function( gp )
    local points, lines, graph, adj, group, coll, act,sz;
    #if not "grape" in RecNames(GAPInfo.PackagesLoaded) then
    #   Error("You must load the GRAPE package\n");
    #fi;
    if IsBound(gp!.IncidenceGraphAttr) then
       return gp!.IncidenceGraphAttr;
    fi;
    if not HasCollineationGroup(gp) then
        Error("No collineation group computed. Please compute collineation group before computing incidence graph\,n");
    else
        points := AsList(Points(gp));
        lines := AsList(Lines(gp));
        Setter( HasGraphWithUnderlyingObjectsAsVertices )( gp, false );

        Info(InfoFinInG, 1, "Computing incidence graph of generalised polygon...");

        adj := function(x,y)
            if x!.type <> y!.type then
                return IsIncident(x,y);
            else
                return false;
            fi;
        end;
  group := CollineationGroup(gp);
  act := CollineationAction(group);
  graph := Graph(group,Concatenation(points,lines),act,adj,true);
        Setter( IncidenceGraphAttr )( gp, graph );
        return graph;
    fi;
  end );

#############################################################################
# Dealing with elements: constructor operations and membership test.
#############################################################################

# Added 02/08/2014 jdb
#############################################################################
#O  VectorSpaceToElement( <geom>, <v> ) returns the elements in <geom> determined
# by the vectorspace <v>.
##
InstallMethod( VectorSpaceToElement,
 "for a polar space and a cvec",
 [ IsClassicalGeneralisedHexagon, IsCVecRep],
 function( geom, v )
  return VectorSpaceToElement(geom,Unpack(v));
 end );

#############################################################################
#O  VectorSpaceToElement( <geom>, <v> ) returns the element in <geom> determined
# by the rowvector <v>. <geom> is a generalised hexagon, so an ambient polar space
# ps is available. A point of geom is necessary a point of ps, but for T(q^3,q) we need
# to check whether the point of Q+(7,q) is absolute. This method is base on VectorSpaceToElement
# for polar spaces and a row vector.
##
InstallMethod( VectorSpaceToElement,
    "for a generalised hexagon and a row vector",
    [ IsClassicalGeneralisedHexagon, IsRowVector ],
    function(geom,vec)
  local x,y, ps, el, onespace, f, frob, change;
  ## dimension should be correct
  if Length(vec) <> geom!.dimension + 1 then
   Error("Dimensions are incompatible");
  fi;
  x := ShallowCopy(vec);
  if IsZero(x) then
   return EmptySubspace(geom);
  fi;
  MultVector(x,Inverse( x[PositionNonZero(x)] ));
  y := NewMatrix(IsCMatRep,geom!.basefield,Length(x),[x])[1];
  ps := AmbientPolarSpace(geom);
  if geom!.dimension = 5 then
   return Wrap(geom, 1, y);
  elif geom!.dimension = 6 then
   if not IsSingularVector(QuadraticForm(ps),x) then
    Error("<v> does not generate an element of <geom>");
   else
    return Wrap(geom, 1, y);
   fi;
  else
   if not IsSingularVector(QuadraticForm(ps),x) then
    Error("<v> does not generate an element of <geom>");
   fi;
   f := geom!.basefield;
         if IsBound(geom!.basechange) then
                change := geom!.basechange;
            else
                change := IdentityMat(geom!.dimension+1,f);
            fi;
   el := VectorSpaceToElement(ps,y);
   frob := FrobeniusAutomorphism(f);
   onespace := VectorSpaceToElement(AmbientSpace(ps), ZeroPointToOnePointsSpaceByTriality(x * change^-1,frob,f) * change);
   if el in onespace then
    return Wrap(geom,1,y);
   else
    Error("<v> generates an element of the ambient polar space but not of <geom>");
   fi;
  fi;
end );

#############################################################################
#O  VectorSpaceToElement( <geom>, <v> ) returns the elements in <geom> determined
# by the rowvector <v>. Several checks are built in.
##
InstallMethod( VectorSpaceToElement,
 "for a polar space and an 8-bit vector",
 [ IsClassicalGeneralisedHexagon, Is8BitVectorRep ],
    function(geom,vec)
  local x,y, ps, el, onespace, f, frob, change;
  ## dimension should be correct
  if Length(vec) <> geom!.dimension + 1 then
   Error("Dimensions are incompatible");
  fi;
  x := ShallowCopy(vec);
  if IsZero(x) then
   return EmptySubspace(geom);
  fi;
  MultVector(x,Inverse( x[PositionNonZero(x)] ));
  y := NewMatrix(IsCMatRep,geom!.basefield,Length(x),[x])[1];
  ps := AmbientPolarSpace(geom);
  if geom!.dimension = 5 then
   return Wrap(geom, 1, y);
  elif geom!.dimension = 6 then
   if not IsSingularVector(QuadraticForm(ps),x) then
    Error("<v> does not generate an element of <geom>");
   else
    return Wrap(geom, 1, y);
   fi;
  else
   if not IsSingularVector(QuadraticForm(ps),x) then
    Error("<v> does not generate an element of <geom>");
   fi;
   el := VectorSpaceToElement(ps,y);
   f := geom!.basefield;
   frob := FrobeniusAutomorphism(f);
   onespace := VectorSpaceToElement(AmbientSpace(ps), ZeroPointToOnePointsSpaceByTriality(x * change^-1,frob,f) * change);
   if el in onespace then
    return Wrap(geom,1,y);
   else
    Error("<v> generates an element of the ambient polar space but not of <geom>");
   fi;
  fi;
end );

# 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  VectorSpaceToElement( <geom>, <v> ) returns the elements in <geom> determined
# by the vectorspace <v>. Code based on VectorSpaceToElement for polar spaces
# and IsPlistRep.
##
InstallMethod( VectorSpaceToElement,
 "for a polar space and a Plist",
 [ IsClassicalGeneralisedHexagon, IsPlistRep and IsMatrix],
 function( geom, v )
  local  x, n, i, y, ps, f, onespace1, onespace2, p1, p2, change, frob;
  ## when v is empty...
  if IsEmpty(v) then
   Error("<v> does not represent any element");
  fi;
  x := MutableCopyMat(v);
  TriangulizeMat(x);
  ## dimension should be correct
  if Length(v[1]) <> geom!.dimension + 1 then
   Error("Dimensions are incompatible");
  fi;
  ## 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
   return EmptySubspace(geom);
  fi;
  x := x{[1..n-i]};
        if Length(x) = 1 then
            return VectorSpaceToElement(geom, x[1]);
        fi;
  ps := AmbientPolarSpace(geom);
  y := NewMatrix(IsCMatRep,geom!.basefield,Length(x[1]),x);
  f := geom!.basefield;
  if IsBound(geom!.basechange) then
   change := geom!.basechange;
  else
   change := IdentityMat(geom!.dimension+1,f);
  fi;
  if geom!.dimension = 5 then
   if not IsTotallyIsotropicSubspace(SesquilinearForm(ps),x) then
    Error("<x> does not generate an element of the ambient polar space of <geom>");
   fi;
   p1 := Wrap(AmbientSpace(ps),1,y[1]);
   onespace1 := VectorSpaceToElement(AmbientSpace(ps),SplitCayleyPointToPlane5(x[2] * change^-1,f) * change);
   if p1 in onespace1 then
    return Wrap(geom,2,y);
   else
    Error("<x> does not generate an element of <geom>");
   fi;
  elif geom!.dimension = 6 then
   if not IsTotallySingularSubspace(QuadraticForm(ps),x) then
    Error("<x> does not generate an element of the ambient polar space of <geom>");
   fi;
   p1 := Wrap(AmbientSpace(ps),1,y[1]);
   onespace1 := VectorSpaceToElement(AmbientSpace(ps),SplitCayleyPointToPlane(x[2] * change^-1,f) * change);
   if p1 in onespace1 then
    return Wrap(geom,2,y);
   else
    Error("<x> does not generate an element of <geom>");
   fi;
  else
   frob := FrobeniusAutomorphism(f);
   if not IsTotallySingularSubspace(QuadraticForm(ps),x) then
    Error("<x> does not generate an element of the ambient polar space of <geom>");
   fi;
   p1 := Wrap(AmbientSpace(ps),1,y[1]);
   onespace1 := VectorSpaceToElement(AmbientSpace(ps),
     ZeroPointToOnePointsSpaceByTriality(x[1] * change^-1,frob,f) * change);
   p2 := Wrap(AmbientSpace(ps),1,y[2]);
   onespace2 := VectorSpaceToElement(AmbientSpace(ps),
     ZeroPointToOnePointsSpaceByTriality(x[2] * change^-1,frob,f) * change);
   if p1 in onespace1 and p2 in onespace2 and p2 in onespace1 then
    return Wrap(geom,2,y);
   else
    Error("<x> does not generate an element of <geom>");
   fi;
  fi;
 end );

#############################################################################
#O  VectorSpaceToElement( <geom>, <v> )
# if mat is in IsGF2MatrixRep, then Unpack(mat) is in IsPlistRep.
##
InstallMethod( VectorSpaceToElement,
 "for a polar space and a compressed GF(2)-matrix",
 [ IsClassicalGeneralisedHexagon, IsGF2MatrixRep],
 function(geom, mat)
  return VectorSpaceToElement(geom,Unpack(mat));
 end );

#############################################################################
#O  VectorSpaceToElement( <geom>, <v> )
# if mat is in Is8BitMatrixRep, then Unpack(mat) is in IsPlistRep.
##
InstallMethod( VectorSpaceToElement,
 "for a polar space compressed basis of a vector subspace",
 [ IsClassicalGeneralisedHexagon, Is8BitMatrixRep],
 function(geom, mat)
  return VectorSpaceToElement(geom,Unpack(mat));
 end );

#############################################################################
#O  VectorSpaceToElement( <geom>, <v> ) returns the elements in <geom> determined
# by the vectorspace <v>.
##
InstallMethod( VectorSpaceToElement,
 "for a polar space and a cmat",
 [ IsClassicalGeneralisedHexagon, IsCMatRep],
 function( geom, v )
  return VectorSpaceToElement(geom,Unpack(v));
 end );

#############################################################################
#O  \in( <w>, <gp> ) true if the element <w> is contained in <gp>
# We can base this method on VectorSpaceToElement, some checks can be avoided since we
# start from an element already.
InstallMethod( \in,
 "for an element of an incidencestructure a classical generalised hexagon",
 [ IsElementOfIncidenceStructure, IsClassicalGeneralisedHexagon ],
 function( el, gp )
        local vec, onespace, f, frob, p1, p2, onespace1, onespace2, change;
        vec := Unpack(UnderlyingObject(el));
        if not AmbientSpace(el) = AmbientSpace(gp) then
            return false;
        fi;
        if el!.type = 1 then #dealing with a point
            if gp!.dimension = 5 then
                return true;
            elif gp!.dimension = 6 then
                if not IsSingularVector(QuadraticForm(AmbientPolarSpace(gp)),vec) then
                    return false;
                else
                    return true;
                fi;
            else
                if not IsSingularVector(QuadraticForm(AmbientPolarSpace(gp)),vec) then
                    return false;
                fi;
                f := gp!.basefield;
                if IsBound(gp!.basechange) then
                    change := gp!.basechange;
                else
                    change := IdentityMat(gp!.dimension+1,f);
                fi;
                frob := FrobeniusAutomorphism(f);
                #onespace := VectorSpaceToElement(AmbientSpace(gp), ZeroPointToOnePointsSpaceByTriality(vec,frob,f));
                onespace := VectorSpaceToElement(AmbientSpace(gp), ZeroPointToOnePointsSpaceByTriality(vec * change^-1,frob,f) * change);
                if el in onespace then
                    return true;
                else
                    return false;
                fi;
            fi;
        elif el!.type = 2 then #dealing with a line
            f := gp!.basefield;
            if IsBound(gp!.basechange) then
                change := gp!.basechange;
            else
                change := IdentityMat(gp!.dimension+1,f);
            fi;
            if gp!.dimension = 5 then
                if not IsTotallyIsotropicSubspace(SesquilinearForm(AmbientPolarSpace(gp)),vec) then
                    return false;
                fi;
                p1 := VectorSpaceToElement(AmbientSpace(gp),vec[1]);
                onespace1 := VectorSpaceToElement(AmbientSpace(gp),SplitCayleyPointToPlane5(vec[2] * change^-1,f) * change);
                if el in onespace1 then
                    return true;
                else
                    return false;
                fi;
            elif gp!.dimension = 6 then
                if not IsTotallySingularSubspace(QuadraticForm(AmbientPolarSpace(gp)),vec) then
                    return false;
                fi;
                p1 := VectorSpaceToElement(AmbientSpace(gp),vec[1]);
                onespace1 := VectorSpaceToElement(AmbientSpace(gp),SplitCayleyPointToPlane(vec[2] * change^-1,f) * change);
                if p1 in onespace1 then
                    return true;
                else
                    return false;
                fi;
            else
                frob := FrobeniusAutomorphism(f);
                if not IsTotallySingularSubspace(QuadraticForm(AmbientPolarSpace(gp)),vec) then
                    return false;
                fi;
                p1 := VectorSpaceToElement(AmbientSpace(gp),vec[1]);
                onespace1 := VectorSpaceToElement(AmbientSpace(gp),
     ZeroPointToOnePointsSpaceByTriality(vec[1] * change^-1,frob,f) * change);
                p2 := VectorSpaceToElement(AmbientSpace(gp),vec[2]);
                onespace2 := VectorSpaceToElement(AmbientSpace(gp),
     ZeroPointToOnePointsSpaceByTriality(vec[2] * change^-1,frob,f) * change);
                if p1 in onespace1 and p2 in onespace2 and p2 in onespace1 then
                    return true;
                else
                    return false;
                fi;
            fi;
        else
            return false;
        fi;
    end );

#############################################################################
#O  ObjectToElement( <geom>, <v> ) returns the elements in <geom> determined
# by the object <obj>. This is of course just a shortcut to VectorSpaceToElement.
##
InstallMethod( ObjectToElement,
 "for a polar space and a cmat",
 [ IsClassicalGeneralisedHexagon, IsObject],
 function( geom, obj )
  return VectorSpaceToElement(geom,obj);
 end );

#############################################################################
#
# Elation generalised quadrangles.
#
#############################################################################

#############################################################################
#
#  Kantor Families and associated EGQ's
#
#  The setup is a bit different than the standard one.
#  IsElationGQ is a subcategory of IsGeneralisedQuadrangle. IsElationGQByKantorFamily
#  is a subcategory of IsElationGQ. For historical (?) reasons, the elements of a
#  IsElationGQByKantorFamily-GP are in a category called IsElementOfKantorFamily,
#  which is a subcategory of IsElementOfGeneralisedPolygon. Contrary to all other
#  instances of elements of an incidence geometry, an IsElementOfKantorFamily contains
#  also its class (and of course its embient geometry, type and underlying object).
#  This makes typical operations like Wrap different. The same applies to UnderlyingObject
#  and ObjectToElement for IsElationGQByKantorFamily
#
#   components in the EGQByKantor (different from the standard ones for GPs);
# pointsobj, linesobj = []
# group: contains the elation group.
# S, Sstar: contains the two collections of subgroups. Note that g,S,Sstar determine the EGQ.
#
#############################################################################

#############################################################################
# Very particular display methods.
#############################################################################

InstallMethod( ViewObj,
 "for an element of a Kantor family",
 [ IsElementOfKantorFamily ],
 function( v )
  if v!.type = 1 then
   Print("<a point of class ", v!.class," of ",v!.geo,">");
  else
   Print("<a line of class ", v!.class," of ",v!.geo,">");
  fi;
 end );

InstallMethod( PrintObj,
 "for an element of a Kantor family",
 [ IsElementOfKantorFamily ],
 function( v )
  if v!.type = 1 then
   Print("<a point of class ", v!.class," of ",v!.geo,">\n");
  else
   Print("<a line of class ", v!.class," of ",v!.geo,">\n");
  fi;
  Print(v!.obj);
 end );

#############################################################################
#O  Wrap( <geo>, <type>, <class>, <o>  )
# returns the element of <geo> represented by <o>
##
InstallMethod( Wrap,
 "for an EGQ (Kantor family), and an object",
 [IsElationGQByKantorFamily, IsPosInt, IsPosInt, IsObject],
 function( geo, type, class, o )
  local w;
  w := rec( geo := geo, type := type, class := class, obj := o );
  Objectify( NewType( ElementsOfIncidenceStructureFamily,   #ElementsFamily,
   IsElementOfKantorFamilyRep and IsElementOfKantorFamily ), w );
  return w;
 end );

#############################################################################
#O  \=( <a>, <b>  )
# for thwo elements of a Kantor family.
##
InstallMethod( \=,
 "for two elements of a Kantor family",
 [IsElementOfKantorFamily, IsElementOfKantorFamily],
 function( a, b )
  return a!.obj = b!.obj;
 end );

#############################################################################
#O  \<( <a>, <b>  )
# for thwo elements of a Kantor family.
##
InstallMethod( \<,
 "for two elements of a Kantor family",
 [IsElementOfKantorFamily, IsElementOfKantorFamily],
 function( a, b )
  if a!.type <> b!.type then return a!.type < b!.type;
  else
   if a!.class <> b!.class then return a!.class < b!.class;
   else return a!.obj < b!.obj;
   fi;
  fi;
 end );

#############################################################################
#O  \<( <a>, <b>  )
# for a group and two lists.
##
InstallMethod( IsKantorFamily,
 "for a group and two lists of subgroups",
 [IsGroup, IsList, IsList],
 function( g, f, fstar)
  local flag, a, b, c, ab, astar, tplus1;
  tplus1 := Size(f);
  flag := true;
  if not ForAll([1..Size(fstar)], x -> IsSubgroup(fstar[x], f[x])) then
   Error( "second and third arguments are incompatile");
  fi;
  if not ForAll(fstar, x -> IsSubgroup(g, x)) then
   Error( "elements of second argument are not subgroups of first argument" );
  fi;

  Info(InfoFinInG, 1, "Checking tangency condition...");

  ## K2 tangency condition
  for a in [1..tplus1-1] do
   for astar in [a+1..tplus1] do
    flag := IsTrivial(Intersection(fstar[astar], f[a]));
    if not flag then
     Print("Failed tangency condition\n");
     Print(a,"  ",astar,"\n");
     return flag;
    fi;
   od;
  od;

  Info(InfoFinInG, 1, "Checking triple condition...");

  ## K1 triple condition
  for a in [1..tplus1-2] do
   for b in [a+1..tplus1-1] do
    for c in [b+1..tplus1] do
     ab := DoubleCoset(f[a], One(g), f[b]);
     flag := IsTrivial(Intersection(ab, f[c]));
     if not flag then
      Print("Failed triple condition\n");
      Print(a,"  ",b,"  ",c,"\n");
      return flag;
     fi;
    od;
   od;
  od;
  return flag;
 end );

#############################################################################
#F  OnKantorFamily( <v>, <el> ): action function for elation groups on elements.
##
InstallGlobalFunction( OnKantorFamily,
  function( v, el )
    local geo, type, class, new;
    geo := v!.geo;
    type := v!.type;
    class := v!.class;
    if (type = 1 and class = 3) or (type = 2 and class = 2) then
       return v;
    elif (type = 1 and class = 2) or (type = 2 and class = 1) then
       new := [v!.obj[1], CanonicalRightCosetElement(v!.obj[1],v!.obj[2]*el)];
    else
       new := OnRight(v!.obj, el);
    fi;
    return Wrap(geo, type, class, new);
  end );

#############################################################################
#O  EGQByKantorFamily(<g>, <f>, <fstar>)
# for a group and two lists.
# Note: for this model, the underlying objects are IsElementOfKantorFamily.
##
InstallMethod( EGQByKantorFamily,
 "for a group, a list and a list",
 [IsGroup, IsList, IsList],
 function( g, f, fstar)
    local basepoint, inc, listels, shadpoint, shadline,
          x, y, geo, ty, points, lines, pointreps, linereps, dist, spanpts, meetlns;
    ## Some checks first.
    ## We do not check if it's a Kantor family though (this can be rather slow)

    if not ForAll([1..Size(fstar)], x -> IsSubgroup(fstar[x], f[x])) then
       Error( "second and third arguments are incompatible");
       return;
    fi;
    if not ForAll(fstar, x -> IsSubgroup(g, x)) then
       Error( "elements of second argument are not subgroups of first argument" );
       return;
    fi;

    inc := function(x, y)
        if x!.type = y!.type then
            return x = y;
        elif x!.type = 1 and y!.type = 2 then
            if x!.class = 1 and y!.class = 1 then
                return x!.obj*y!.obj[2]^-1 in y!.obj[1];
            elif x!.class = 2 and y!.class = 1 then
                return IsSubset(x!.obj[1], RightCoset(y!.obj[1], y!.obj[2]*x!.obj[2]^-1));
            elif x!.class = 2 and y!.class = 2 then
                return x!.obj[1] = y!.obj;
            elif x!.class = 3 and y!.class = 2 then
                return true;
            else return false;
            fi;
        else
            return inc(y, x);
        fi;
    end;

    geo := rec( pointsobj := [], linesobj := [], incidence := inc, group := g, S := f, Sstar := fstar );
    ty := NewType( GeometriesFamily, IsElationGQByKantorFamily and IsGeneralisedPolygonRep );
    Objectify( ty, geo );

 #obvious information on this gp:
    basepoint := Wrap(geo, 1, 3, 0);
    SetBasePointOfEGQ( geo, basepoint );
    SetOrder(geo, [Index(g,fstar[1]), Size(f)-1]);
    SetCollineationAction(g, OnKantorFamily);
    SetElationGroup(geo, g);
    SetTypesOfElementsOfIncidenceStructure(geo, ["point","line"]);
    pointreps := Concatenation( [Wrap(geo,1,1,One(g))], Set(fstar, b -> Wrap(geo,1,2,[b, One(b)])), [basepoint]);
    linereps := Concatenation(Set(f, a -> Wrap(geo,2,1,[a, One(g)])), Set(fstar, x -> Wrap(geo, 2, 2, x)));
    SetRepresentativesOfElements(geo, [pointreps,linereps]);
    SetName(geo,Concatenation("<EGQ of order ",String([Index(g,fstar[1]), Size(f)-1])," and basepoint 0>"));

    listels := function(gp,i)
        local pts1,pts2,pts3,ls1,ls2;
        if i=1 then
            pts1 := List(gp!.group, x -> Wrap(geo,1,1,x));
            pts2 := Set(gp!.Sstar, b -> Set(RightCosets(g,b),x->[b,CanonicalRightCosetElement(b, Representative(x))]));
            pts2 := Concatenation(pts2);
            pts2 := List(pts2, x -> Wrap(geo,1,2,x));
            pts3 := BasePointOfEGQ(gp);
            return Concatenation(pts1,pts2,[pts3]);
        elif i=2 then
            ls1 := Set(gp!.S, a -> Set(RightCosets(g,a), x -> [a,CanonicalRightCosetElement(a, Representative(x))]));
            ls1 := Concatenation(ls1);
            ls1 := List(ls1, x -> Wrap(geo,2,1,x)); ## this is a strictly sorted list
            ls2 := Set(gp!.Sstar, x -> Wrap(geo, 2, 2, x));
            return Concatenation(ls1, ls2);
        fi;
    end;

 shadpoint := function(pt)
  local cosets, objs, Ss, S, g, lns;
  if pt!.class = 1 then
   cosets := List(f,x->RightCoset(x,pt!.obj));
   objs := List(cosets,x->[ActingDomain(x),CanonicalRightCosetElement(ActingDomain(x),Representative(x))]);
   return List(objs,x->Wrap(pt!.geo,2,1,x));
  elif pt!.class = 2 then
   Ss := pt!.obj[1];
   g := pt!.obj[2];
   S := f[Position(fstar,Ss)];
   cosets := RightCosets(Ss,S);
   objs := List(cosets,x->[ActingDomain(x),CanonicalRightCosetElement(ActingDomain(x),Representative(x))]);
   cosets := List(objs,x->RightCoset(x[1],x[2]*g));
   objs := List(cosets,x->[ActingDomain(x),CanonicalRightCosetElement(ActingDomain(x),Representative(x))]);
   lns := List(objs,x->Wrap(pt!.geo,2,1,x));
   Add(lns,Wrap(pt!.geo,2,2,Ss));
   return lns;
  else
   return List(fstar,x->Wrap(pt!.geo,2,2,x));
  fi;
 end;

 shadline := function(line)
  local coset,cosets,Ss,pts,objs;
  if line!.class = 1 then
   coset := RightCoset(line!.obj[1],line!.obj[2]);
   pts := List(List(coset),x->Wrap(line!.geo,1,1,x));
   Ss := fstar[Position(f,line!.obj[1])];
   coset := RightCoset(Ss,line!.obj[2]);
   Add(pts,Wrap(line!.geo,1,2,
      [ActingDomain(coset),CanonicalRightCosetElement(ActingDomain(coset),Representative(coset))]));
   return pts;
  elif line!.class = 2 then
   cosets := RightCosets(g,line!.obj);
   objs := List(cosets,x->[ActingDomain(x),CanonicalRightCosetElement(ActingDomain(x),Representative(x))]);
   pts := List(objs,x->Wrap(line!.geo,1,2,x));
   Add(pts,BasePointOfEGQ(line!.geo));
   return pts;
  fi;
 end;

    spanpts := function(p,q)
        local class, obj, geo, list, S, coset, r;
        if not p!.geo = q!.geo then
            Error("<p> and <q> should belong to the same ambient geometry");
        fi;
        if p = q then
            return fail;
        fi;
        if p!.class = 1 and q!.class = 1 then
            S := First(f,x->p!.obj*q!.obj^-1 in x);
            if S = fail then
                return fail;
            else
                coset := RightCoset(S,p!.obj);
                r := CanonicalRightCosetElement(S,Representative(coset));
                return Wrap(p!.geo,2,1,[S,r]);
            fi;
        elif p!.class = 1 and q!.class = 2 then
            if p!.obj in RightCoset(q!.obj[1],q!.obj[2]) then
                S := First(f,x->IsSubgroup(q!.obj[1],x));
                coset := RightCoset(S,p!.obj);
                r := CanonicalRightCosetElement(S,Representative(coset));
                return Wrap(p!.geo,2,1,[S,r]);
            else
                return fail;
            fi;
        elif p!.class = 1 and q!.class = 3 then
            return fail;
        elif p!.class = 2 and q!.class = 2 then
            if p!.obj[1] = q!.obj[1] then
                return Wrap(p!.geo,2,2,p!.obj[1]);
            else
                return fail;
            fi;
        elif p!.class = 2 and q!.class = 3 then
            return Wrap(p!.geo,2,2,p!.obj[1]);
        else
            return spanpts(q,p);
        fi;
    end;

    meetlns := function(l,m)
        local class, obj, geo, Sl, Sm, S, coset, r, g, h;
        if not l!.geo = m!.geo then
            Error("<l> and <m> should belong to the same ambient geometry");
        fi;
        if l = m then
            return fail;
        fi;
        if l!.class = 1 and m!.class = 1 then
            Sl := l!.obj[1];
            Sm := m!.obj[1];
            if Sl = Sm then
                S := First(fstar, x->IsSubgroup(x,Sl));
                coset := RightCoset(S,l!.obj[2]);
                r := CanonicalRightCosetElement(S,Representative(coset));
                return Wrap(l!.geo,1,2,[S,r]);
            else
                g := l!.obj[2];
                h := m!.obj[2];
                r := Intersection(RightCoset(Sl,g),RightCoset(Sm,h));
                if Length(r) = 0 then
                    return fail;
                else
                    return Wrap(l!.geo,1,1,r[1]);
                fi;
            fi;
        elif l!.class = 1 and m!.class = 2 then
            if IsSubgroup(m!.obj,l!.obj[1]) then
                coset := RightCoset(m!.obj,l!.obj[2]);
                r := CanonicalRightCosetElement(m!.obj,Representative(coset));
                return Wrap(l!.geo,1,2,[m!.obj,r]);
            else
                return fail;
            fi;
        elif l!.class = 2 and m!.class = 2 then
            return Wrap(l!.geo,1,3,0);
        else
            return meetlns(m,l);
        fi;
    end;

    dist := function(x,y)
        if x!.type <> y!.type then
            if inc(x,y) then
                return 1;
            else
                return 3;
            fi;
        elif x!.type = 1 then
            if spanpts(x,y) = fail then
                return 4;
            else
                return 2;
            fi;
        else
            if meetlns(x,y) = fail then
                return 4;
            else
                return 2;
            fi;
        fi;
    end;

 geo!.listelements := listels;
 geo!.shadowofpoint := shadpoint;
 geo!.shadowofline := shadline;
    geo!.span := spanpts;
    geo!.meet := meetlns;
    geo!.distance := dist;
 return geo;
  end );


#############################################################################
#O  Display( <egq> )
#  for an elation GQ by Kantor family
##
InstallMethod( Display,
 "for an elation GQ by Kantor family",
 [ IsElationGQByKantorFamily ],
 function( gp )
        Print("Elation generalised quadrangle of order ",String(Order(gp)),"\n",
                "with elation group\n");
        View(gp!.group);
    end );

#############################################################################
#O  UnderlyingObject( <el>)
# for IsElementOfKantorFamily.
##
InstallMethod( UnderlyingObject,
    "for an element of a Kantor family",
    [ IsElementOfKantorFamily ],
    function( el )
        if el!.type = 1 then
            if el!.class = 2 then
                return RightCoset(el!.obj[1],el!.obj[2]);
            else
                return el!.obj;
            fi;
        else
            if el!.class = 1 then
                return RightCoset(el!.obj[1],el!.obj[2]);
            else
                return el!.obj;
            fi;
        fi;
    end );

#############################################################################
#O  ObjectToElement( <egq>, <t>, <obj>)
#  for an EGQByKantorFamily, a type and a right coset
##
InstallMethod( ObjectToElement,
    "for an EGQByKantorFamily, a type and a right coset",
    [ IsElationGQByKantorFamily, IsPosInt, IsRightCoset ],
    function( geo, t, obj )
        local S,r;
        S := ActingDomain(obj);
        r := CanonicalRightCosetElement(S,Representative(obj));
        if t = 1 then
            if not S in geo!.Sstar then
                Error(" <obj> does not represent a point of <geo>");
            else
                return Wrap(geo,t,2,[S,r]);
            fi;
        elif t = 2 then
            if not S in geo!.S then
                Error(" <obj> does not represent a line of <geo>");
            else
                return Wrap(geo,t,1,[S,r]);
            fi;
        else
            Error("<geo> has no elements other than points and lines");
        fi;
    end );

#############################################################################
#O  ObjectToElement( <egq>, <t>, <obj>)
#  for an EGQByKantorFamily, a type and a right coset
##
InstallMethod( ObjectToElement,
    "for an EGQByKantorFamily, a type and a right coset",
    [ IsElationGQByKantorFamily, IsRightCoset ],
    function( geo, obj )
        local S,r;
        S := ActingDomain(obj);
        r := CanonicalRightCosetElement(S,Representative(obj));
        if S in geo!.Sstar then
            return Wrap(geo,1,2,[S,r]);
        elif S in geo!.S then
            return Wrap(geo,2,1,[S,r]);
        else
            Error("<obj> does not represent any element of <geo>");
        fi;
    end );

#############################################################################
#O  ObjectToElement( <egq>, <t>, <obj>)
#  for an EGQByKantorFamily, a type and a group element
##
InstallMethod( ObjectToElement,
    "for an EGQByKantorFamily, a type and a group element",
    [ IsElationGQByKantorFamily, IsPosInt, IsMultiplicativeElementWithInverse ],
    function( geo, t, obj )
        if not t = 1 or not obj in geo!.group then
            Error("<obj> does not represent a point");
        else
            return Wrap(geo,1,1,obj);
        fi;
    end );

#############################################################################
#O  ObjectToElement( <egq>, <obj>)
#  for an EGQByKantorFamily and a group element
##
InstallMethod( ObjectToElement,
    "for an EGQByKantorFamily and a group element",
    [ IsElationGQByKantorFamily, IsMultiplicativeElementWithInverse ],
    function( geo, obj )
        if not obj in geo!.group then
            Error("<obj> does not represent a point");
        else
            return Wrap(geo,1,1,obj);
        fi;
    end );

#############################################################################
#O  ObjectToElement( <egq>, <t>, <obj>)
#  for an EGQByKantorFamily, a type and a group element
##
InstallMethod( ObjectToElement,
    "for an EGQByKantorFamily, a type and a group",
    [ IsElationGQByKantorFamily, IsPosInt, IsMagmaWithInverses ],
    function( geo, t, obj )
        if not t = 2 or not obj in geo!.Sstar then
            Error("<obj> does not represent a line");
        else
            return Wrap(geo,2,2,obj);
        fi;
    end );

#############################################################################
#O  ObjectToElement( <egq>, <obj>)
#  for an EGQByKantorFamily and a group element
##
InstallMethod( ObjectToElement,
    "for an EGQByKantorFamily and a group",
    [ IsElationGQByKantorFamily, IsMagmaWithInverses ],
    function( geo, obj )
        if not obj in geo!.Sstar then
            Error("<obj> does not represent a line");
        else
            return Wrap(geo,2,2,obj);
        fi;
    end );

#############################################################################
#
#   q-Clans and EGQ's made from them
#
#############################################################################

#############################################################################
#O  IsAnisotropic( <m>, <f> )
# simply checks if a matrix is anisotropic.
##
InstallMethod( IsAnisotropic,
 "for a matrix and a finite field",
 [IsFFECollColl,  IsField and IsFinite],
 function( m, f )
  local pairs, o;
  o := Zero(f);
  pairs := Difference(AsList(f^2),[[o,o]]);
  return ForAll(pairs, x -> x * m * x <> o);
 end );

#############################################################################
#O  IsqClan( <list>, <f> )
# simply checks if all differences of elements in a set of 2 x 2 matrices is are
# anisotropic.
##
InstallMethod( IsqClan,
 "input are 2x2 matrices",
    [ IsFFECollCollColl,  IsField and IsFinite],
 function( clan, f )
  return ForAll(Combinations(clan,2), x -> IsAnisotropic(x[1]-x[2], f));
 end );

#############################################################################
#O  qClan( <clan>, <f> )
# returns a qClan object from a suitable list of matrices.
##
InstallMethod( qClan,
 "for a list of 2x2 matrices",
 [ IsFFECollCollColl, IsField ],
 function( m, f )
  local qclan;
  ## test to see if it is a qClan
  if not ForAll(Combinations(m, 2), x -> IsAnisotropic(x[1]-x[2], f)) then
   Error("These matrices do not form a q-clan");
  fi;
  qclan := rec( matrices := m, basefield := f );
  Objectify( NewType( qClanFamily, IsqClanObj and IsqClanRep ), qclan );
  return qclan;
 end );

InstallMethod( ViewObj,
 "for a qClan",
 [ IsqClanObj and IsqClanRep ],
 function( x )
  Print("<q-clan over ",x!.basefield,">");
 end );

InstallMethod( PrintObj,
 "for a qClan",
 [ IsqClanObj and IsqClanRep ],
 function( x )
  Print("qClan( ", x!.matrices, ", ", x!.basefield , ")");
 end );

#############################################################################
#O  AsList( <clan> )
# returns the matrices defining a qClan
##
InstallOtherMethod( AsList,
 "for a qClan",
 [IsqClanObj and IsqClanRep],
 function( qclan )
  return qclan!.matrices;
 end );

#############################################################################
#O  AsList( <clan> )
# returns the matrices defining a qClan
##
InstallOtherMethod( AsSet,
 "for a qClan",
 [IsqClanObj and IsqClanRep],
 function( qclan )
  return Set(qclan!.matrices);
 end );

#############################################################################
#O  BaseField( <clan> )
# returns the BaseField of the qClan
##
InstallMethod( BaseField,
 "for a qClan",
 [IsqClanObj and IsqClanRep],
 function( qclan )
  return qclan!.basefield;
 end );

#############################################################################
#O  BaseField( <clan> )
# checks if the qClan is linear.
##
InstallMethod( IsLinearqClan,
 "for a qClan",
 [ IsqClanObj ],
 function( qclan )
  local blt;
  blt := BLTSetByqClan( qclan );
  return ProjectiveDimension(Span(blt)) = 2;
 end );

#############################################################################
#O  LinearqClan( <clan> )
# returns a linear qClan.
##
InstallMethod( LinearqClan,
 "for a prime power",
 [ IsPosInt ],
 function(q)
  local f, g, clan, n;
  if not IsPrimePowerInt(q) then
   Error("Argument must be a prime power");
  fi;
  n := First(GF(q), t -> not IsZero(t) and LogFFE(t, Z(q)^2) = fail);
  if n = fail then
   Error("Couldn't find nonsquare");
  fi;
  f := t -> 0 * Z(q)^0;
  g := t -> -n * t;
  clan := List(GF(q), t -> [[t, f(t)], [f(t), g(t)]]);
  clan := qClan(clan, GF(q));
  SetIsLinearqClan(clan, true);
  return clan;
 end );

#############################################################################
#O  FisherThasWalkerKantorBettenqClan( <q> )
# returns the Fisher Thas Walker Kantor Betten qClan
##
InstallMethod( FisherThasWalkerKantorBettenqClan,
 "for a prime power",
 [ IsPosInt ],
 function(q)
  local f, g, clan;
  if not IsPrimePowerInt(q) then
   Error("Argument must be a prime power");
  fi;
  if q mod 3 <> 2 then
   Error("q must be congruent to 2 mod (3)");
  fi;
  f := t -> 3/2 * t^2;
  g := t -> 3 * t^3;
  clan := List(GF(q), t -> [[t, f(t)], [f(t), g(t)]]);
  return qClan(clan, GF(q));
 end );

#############################################################################
#O  KantorMonomialqClan( <q> )
# returns the Kantor Monomial qClan
##
InstallMethod( KantorMonomialqClan,
 "for a prime power",
 [ IsPosInt ],
 function(q)
  local f, g, clan;
  if not IsPrimePowerInt(q) then
   Error("Argument must be a prime power");
  fi;
  if not q mod 5 in [2, 3] then
   Error("q must be congruent to 2 mod (3)");
  fi;
  f := t -> 5/2 * t^3;
  g := t -> 5 * t^5;
  clan := List(GF(q), t -> [[t, f(t)], [f(t), g(t)]]);
 return qClan(clan, GF(q));
end );

#############################################################################
#O  KantorKnuthqClan( <q> )
# returns the Kantor Knuth qClan
##
InstallMethod( KantorKnuthqClan,
 "for a prime power",
 [ IsPosInt ],
 function(q)
  local f, g, clan, n, sigma;
  if not IsPrimePowerInt(q) then
   Error("Argument must be a prime power");
  fi;
  if IsPrime(q) then
   Error("q is a prime");
  fi;
  sigma := FrobeniusAutomorphism(GF(q));
  n := First(GF(q), t -> not IsZero(t) and LogFFE(t, Z(q)^2) = fail);
  if n = fail then
   Error("Couldn't find nonsquare");
  fi;
  f := t -> 0 * Z(q)^0;
  g := t -> -n * t^sigma;
  clan := List(GF(q), t -> [[t, f(t)], [f(t), g(t)]]);
  return qClan(clan, GF(q));
 end );

#############################################################################
#O  FisherqClan( <q> )
# returns the Fisher qClan
##
InstallMethod( FisherqClan,
 "for a prime power",
 [ IsPosInt ],
 function(q)
  local f, g, clan, n, zeta, omega, squares, nonsquares, i, z, a, t, j;
  if not IsPrimePowerInt(q) or IsEvenInt(q) then
   Error("Argument must be an odd prime power");
    fi;
 squares := ShallowCopy(AsList(Group(Z(q)^2)));; Add(squares, 0*Z(q));
 nonsquares := Difference(AsList(GF(q)),squares);;
 n := First(nonsquares, t -> t-1 in squares);

 zeta := Z(q^2);
 omega := zeta^(q+1);
 i := zeta^((q+1)/2);
 z := zeta^(q-1);
 a := (z+z^q)/2;
 clan := [];
 for t in GF(q) do
     if t^2-2/(1+a) in squares then
        Add(clan, [[t, 0],[0,-omega*t]] * Z(q)^0);
     fi;
 od;

 for j in [0..(q-1)/2] do
     Add(clan, [[-(z^(2*j+1)+z^(-2*j))/(z+1), i*(z^(2*j+1)-z^(-2*j))/(z+1)],
        [i*(z^(2*j+1)-z^(-2*j))/(z+1), -omega*(z^(2*j+1)+z^(-2*j))/(z+1)]] * Z(q)^0 );
 od;

  return qClan(clan, GF(q));
end );

#############################################################################
#O  KantorFamilyByqClan( <clan> )
# returns the Kantor familyt corresponding with the q-Clan <clan>
##
InstallMethod( KantorFamilyByqClan,
 "for a q-Clan",
    [ IsqClanObj and IsqClanRep ],
 function( clan )
    local g, q, f, i, omega, mat, at, ainf, ainfstar, clanmats,
          ainfgens, centregens, as, astars, k;
    f := clan!.basefield;
    clanmats := clan!.matrices;
    q := Size(f);
    i := One(f);
    omega := PrimitiveElement(f);
    mat := function(a1,a2,c,b1,b2)
             return i * [[1, a1, a2,  c], [0,  1,  0, b1],
                         [0,  0,  1, b2], [0,  0,  0,  1]];
           end;
    centregens := [mat(0,0,1,0,0), mat(0,0,omega,0,0)];
    ainfgens := [mat(0,0,0,1,0),mat(0,0,0,0,1),mat(0,0,0,omega,0),mat(0,0,0,0,omega)];
    ainf := Group(ainfgens);
    ainfstar := Group(Union(ainfgens,centregens));

    at := function( m )
       ## returns generators for Kantor family element defined by q-Clan element m
       local a1, a2, k, gens, bas, zero;
       gens := [];
       bas := AsList(Basis(f));
       zero := Zero(f);
       for a1 in bas do
           k := [a1,zero] * (m+TransposedMat(m));
           Add(gens, mat(a1,zero,[a1,zero]*m*[a1,zero],k[1],k[2]) );
       od;

       for a2 in bas do
           k := [zero,a2] * (m+TransposedMat(m));
           Add(gens, mat(zero,a2,[zero,a2]*m*[zero,a2],k[1],k[2]) );
       od;
       return gens;
    end;

    g := Group(Union( ainfgens, centregens, at(clanmats[1]) ));
    as := List(clanmats, m -> Group( at(m) ));
    Add(as, ainf);
    astars := List(clanmats, m -> Group(Union(at(m), centregens)));
    Add(astars, ainfstar);
    return [g, as, astars];
  end );

#############################################################################
#O  EGQByqClan( <clan> )
# returns the EGQ constructed from the q-Clan, using the corresponding Kantor family.
##
InstallMethod( EGQByqClan,
 "for a q-Clan",
 [ IsqClanObj and IsqClanRep ],
 function( clan )
  local kantor;
  kantor := KantorFamilyByqClan( clan );

  Info(InfoFinInG, 1, "Computed Kantor family. Now computing EGQ...");
   return EGQByKantorFamily(kantor[1], kantor[2], kantor[3]);
  end );

#############################################################################
#O  IncidenceGraph( <gp> )
###
InstallMethod( IncidenceGraph,
    "for an ElationGQ in generic representation",
    [ IsElationGQ and IsGeneralisedPolygonRep ],
    function( gp )
    local points, lines, graph, adj, elationgroup, coll, act,sz;
    #if not "grape" in RecNames(GAPInfo.PackagesLoaded) then
    #   Error("You must load the GRAPE package\n");
    #fi;
    if IsBound(gp!.IncidenceGraphAttr) then
       return gp!.IncidenceGraphAttr;
    fi;
    points := AsList(Points(gp));
    lines := AsList(Lines(gp));
    Setter( HasGraphWithUnderlyingObjectsAsVertices )( gp, false);

    Info(InfoFinInG, 1, "Computing incidence graph of generalised polygon...");

 adj := function(x,y)
  if x!.type <> y!.type then
   return IsIncident(x,y);
  else
   return false;
  fi;
 end;
    sz := Size(points);

 if HasCollineationSubgroup(gp) then
  Info(InfoFinInG, 1, "Using subgroup of the collineation group...");
  coll := CollineationSubgroup(gp);
  act := CollineationAction(coll);
  graph := Graph(coll,Concatenation(points,lines),act,adj,true);
 elif HasElationGroup(gp) then
  Info(InfoFinInG, 1, "Using elation of the collineation group...");
  elationgroup := ElationGroup(gp);
  act := CollineationAction(elationgroup);
  graph := Graph(elationgroup,Concatenation(points,lines),act,adj,true);
 else
    graph := Graph( Group(()), [1..sz+Size(lines)], OnPoints, adj );
 fi;

    Setter( IncidenceGraphAttr )( gp, graph );
    return graph;
  end );

#############################################################################
#
# BLT sets and q-Clans.
#
#############################################################################

#############################################################################
#O  BLTSetByqClan( <clan> )
# returns the BLT set corresponding with the q-Clan <clan>
##
InstallMethod( BLTSetByqClan,
 "for a q-Clan",
 [ IsqClanObj and IsqClanRep ],
 function( clan )
    ##
    ## The q-clan must consist only of symmetric matrices
    ##
        local q, i, f,  blt, ps, x, pring, form, poly;
    f := clan!.basefield;
    q := Size(f);
    i := One(f);
    blt := List(clan!.matrices, t -> [i, t[2,2], -t[1,2], t[1,1],  t[1,2]^2 -t[1,1]*t[2,2]]);
    Add(blt, [0,0,0,0,1]*i);  ## last point is distinguished point.
    for x in blt do
  ConvertToVectorRepNC(x,f);
 od;

    ## This BLT-set is in Q(4,q) defined by Gram matrix
    pring := PolynomialRing(GF(q),5);
    poly := pring.1*pring.5+pring.2*pring.4-pring.3^2;
    form := QuadraticFormByPolynomial(poly, pring);
    ps := PolarSpace( form );
    return List(blt, x -> VectorSpaceToElement(ps, x));
end );

#############################################################################
#O  EGQByBLTSet( <blt>, <p>, <solid> )
# helper operation to be called from EGBByBLTSet( <bltset> ), where the latter
# is a BLT set of points in Q(4,q).
# Note: points and lines are always elements of W(5,q). Since we will use shadows
# it is better to wrap them really as elements of W(5,q) rather then leaving them
# an element of PG(5,q). E.g. Planes(x) makes a huge difference when x is a W(5,q)
# line than when x is a PG(5,q) line, and I am to lazy to type Planes(w5q,x) :-)
# extra fields in geo record:
# - planes: contains the planes spanned by the basepoint and the blt lines in W(5,q).
# - basepoint: base point p
# - basepointperp: image of p under the polarity associated with W(5,q).
##
InstallMethod( EGQByBLTSet,
 "constructs an EGQ from a BLT-set of lines of W(3,q) via the Knarr construction",
 [IsList, IsSubspaceOfProjectiveSpace, IsSubspaceOfProjectiveSpace],
 function( blt, p, solid)
 ## The point p is a point of W(5,q).
 ## "solid" is a 3-space contained in P^perp
 ## blt is a BLT-set of lines of W(3,q)

 local w3q, f, q, w5q, perp, pperp, x,em, blt2, pis, pointreps, linereps, reps, lines2,
         geo, points, lines, ty, listels, inc, shadpoint, shadline, vec, r, spanpts,
   dist, meetlns;

 w3q := blt[1]!.geo;
 f := w3q!.basefield;
 q := Size(f);
 w5q := SymplecticSpace(5, f);
 perp := PolarMap(w5q);
 pperp := perp(p);
 em := NaturalEmbeddingBySubspace(w3q, w5q, solid);
 blt2 := List(blt,t->t^em);
 pis := List(blt2, l -> Span(p, l));
 # next one is dirty, see note above.
 pis := List(pis,x->Wrap(w5q,3,UnderlyingObject(x)));
 ## check everything is kosher
 if not solid in pperp then
  Error("Solid is not contained in perp of point");
 fi;
 if p in solid then
  Error("Chosen point is contained in the chosen solid");
 fi;
 vec := ComplementSpace(f^6,Unpack(UnderlyingObject(pperp)));
 r := VectorSpaceToElement(w5q,BasisVectors(Basis(vec)));
 pointreps := Concatenation([r],blt2,[p]);

 linereps := List(pis,x->Span(r,Meet(x,perp(r))));
 linereps := List(linereps,x->Wrap(w5q,3,UnderlyingObject(x)));
 lines2 := List(blt2,x->Span(p,x));
 lines2 := List(lines2,x->Wrap(w5q,3,UnderlyingObject(x)));
 linereps := Concatenation(linereps,lines2);

 listels := function(gp, i)
  local pts,lines,x,points2;
  if i=1 then
   Info(InfoFinInG, 1, "Computing points(1) of Knarr construction...");
   pts := Filtered(Points(gp!.polarspace),x->not x in gp!.basepointperp);
   Add(pts,gp!.basepointobj);
   Info(InfoFinInG, 1, "Computing points(2) of Knarr construction...");
   if IsBound(gp!.points2) then
    Info(InfoFinInG, 1, "...which were computed already");
    Append(pts,gp!.points2);
   else
    points2 := [];
    for x in gp!.planes do
     Append(points2,Filtered(Lines(x),t->not gp!.basepointobj in t));
    od;
    points2 := List(points2,x->Wrap(w5q,2,UnderlyingObject(x)));
    gp!.points2 := ShallowCopy(points2);
    Append(pts,points2);
   fi;
   return List(pts,x->Wrap(gp,1,x));
  elif i=2 then
   Info(InfoFinInG, 1, "Lines(1) of Knarr construction are known...");
   lines := ShallowCopy(gp!.planes);
   if IsBound(gp!.points2) then
    points2 := gp!.points2;
    Info(InfoFinInG, 1, "Taking points(2) of Knarr construction which were computed already...");
   else
    Info(InfoFinInG, 1, "Computing points(2) of Knarr construction (and saving them)...");
    points2 := [];
    for x in gp!.planes do
     Append(points2,Filtered(Lines(x),t->not gp!.basepointobj in t));
    od;
    points2 := List(points2,x->Wrap(w5q,2,UnderlyingObject(x)));
    gp!.points2 := ShallowCopy(points2);
   fi;
   Info(InfoFinInG, 1, "Computing lines(2) of Knarr construction... please wait");
   for x in points2 do
    Append(lines,Filtered(Planes(x), t -> not t in gp!.basepointperp));
   od;
   return List(lines,x->Wrap(gp,2,x));
  fi;
 end;

 inc := function(x,y)
  return IsIncident(x!.obj,y!.obj); #underlying objects are elements of a projective space
 end;

 shadpoint := function(pt)
  local obj,planes,objs,geo,w5q;
  obj := pt!.obj;
  geo := pt!.geo;
  w5q := geo!.polarspace;
  if ProjectiveDimension(obj) = 1 then
   return List(Planes(w5q,obj),x->Wrap(geo,2,x));
  elif pt=BasePointOfEGQ(geo) then
   return List(geo!.planes,x->Wrap(geo,2,x));
  else
   objs := List(geo!.planes,x->Meet(PolarMap(w5q)(obj),x));
   objs := List(objs,x->Span(obj,x));
   objs := List(objs,x->Wrap(w5q,3,UnderlyingObject(x)));
   return List(objs,x->Wrap(geo,2,x));
  fi;
 end;

 shadline := function(line)
  local obj,geo,p,pts,meet,objs;
  obj := line!.obj;
  geo := line!.geo;
  p := BasePointOfEGQ(geo);
  if p!.obj in obj then
   pts := List(Filtered(Lines(obj), t -> not p!.obj in t),x->Wrap(geo,1,x));
   Add(pts,p);
   return pts;
  else
   meet := Meet(obj,PolarMap(w5q)(p!.obj));
   pts := List(Points(meet));
   objs := Difference(List(Points(obj)),pts);
   pts := List(objs,x->Wrap(geo,1,x));
   Add(pts,Wrap(geo,1,meet));
   return pts;
  fi;
 end;

 spanpts := function(p,q)
  local gp,obj,l,perp,lperp,m;
  gp := p!.geo;
  if p = q then
   return fail;
  elif p = BasePointOfEGQ(gp) then
   return spanpts(q,p);
  elif ProjectiveDimension(p!.obj) = 0 then
   if q = BasePointOfEGQ(gp) then
    return fail;
   elif ProjectiveDimension(q!.obj) = 1 then
    obj := Span(p!.obj,q!.obj);
    if obj in gp!.polarspace then
     obj := Wrap(gp!.polarspace,3,UnderlyingObject(obj));
     return Wrap(gp,2,obj);
    else
     return fail;
    fi;
   elif ProjectiveDimension(q!.obj) = 0 then
    l := Span(p!.obj,q!.obj);
    perp := PolarMap(gp!.polarspace);
    lperp := perp(l);
    m := List(gp!.planes,x->Meet(x,lperp));
    m := First(m,x->ProjectiveDimension(x)=1);
    if m=fail then
     return fail;
    else
     l := Span(m,p!.obj);
     l := Wrap(gp!.polarspace,3,UnderlyingObject(l));
     return Wrap(gp,2,l);
    fi;
   fi;
  elif ProjectiveDimension(p!.obj) = 1 then
   if q = BasePointOfEGQ(gp) then
    obj := Span(p!.obj,q!.obj);
    obj := Wrap(gp!.polarspace,3,UnderlyingObject(obj));
    return Wrap(gp,2,obj);
   elif ProjectiveDimension(q!.obj) = 1 then
    obj := Span(p!.obj,q!.obj);
    if ProjectiveDimension(obj) = 2 then
     obj := Wrap(gp!.polarspace,3,UnderlyingObject(obj));
     return Wrap(gp,2,obj);
    else
     return fail;
    fi;
   fi;
  else
   return spanpts(q,p);
  fi;
 end;

 meetlns := function(l,m)
  local gp,obj;
  gp := l!.geo;
  if l = m then
   return fail;
  fi;
  if not gp!.basepointobj in l!.obj then
   obj := Meet(l!.obj,m!.obj);
   if not gp!.basepointobj in m!.obj then
    if ProjectiveDimension(obj) = 0 and not obj in gp!.basepointperp then
     return Wrap(gp,1,obj);
    else
     return fail;
    fi;
   else
    if ProjectiveDimension(obj) = 1 then
     return Wrap(gp,1,obj);
    else
     return fail;
    fi;
   fi;
  else
   if gp!.basepointobj in m!.obj then
    return BasePointOfEGQ(gp);
   else
    return meetlns(m,l);
   fi;
  fi;
 end;

    dist := function(x,y)
        if x!.type <> y!.type then
            if inc(x,y) then
                return 1;
            else
                return 3;
            fi;
        elif x!.type = 1 then
            if spanpts(x,y) = fail then
                return 4;
            else
                return 2;
            fi;
        else
            if meetlns(x,y) = fail then
                return 4;
            else
                return 2;
            fi;
        fi;
    end;

 geo := rec( pointsobj := [], linesobj := [], incidence := inc, planes := pis, polarspace := w5q,
    basepointobj := p, basepointperp := pperp, listelements := listels, shadowofpoint := shadpoint,
    shadowofline := shadline, span := spanpts, meet := meetlns, distance := dist );
 ty := NewType( GeometriesFamily, IsElationGQByBLTSet and IsGeneralisedPolygonRep);
 Objectify( ty, geo );
 pointreps := List(pointreps,x->Wrap(geo,1,x));
 linereps := List(linereps,x->Wrap(geo,2,x));
 SetRepresentativesOfElements(geo,[pointreps,linereps]);
 SetBasePointOfEGQ( geo, Wrap(geo, 1, p) );
 SetOrder(geo, [q^2, q]);
 SetTypesOfElementsOfIncidenceStructure(geo, ["point","line"]);
    SetName(geo,Concatenation("<EGQ of order ",String([q^2,q])," and basepoint in W(5, ",String(q)," ) >"));
 return geo;
end );

#############################################################################
#O  EGQByBLTSet( <blt> )
# User method to construct the EGQ including the elation group.
##
InstallMethod( EGQByBLTSet,
 "constructs an EGQ from a BLT-set of points of Q(4,q) via the Knarr construction",
 [ IsList ],
 function( blt )
 local q4q, f, w3q, duality, w5q, p, pg5, solid,
         q4qcanonical, iso, bltdual, geo, bas, listels,
         mat, elations, a, gens, zero, action, b;
 q4q := AmbientGeometry( blt[1] );
 f := q4q!.basefield;
 w3q := SymplecticSpace(3, f);
 duality := NaturalDuality( w3q );
 w5q := SymplecticSpace(5, f);
 p := VectorSpaceToElement(w5q, [1,0,0,0,0,0] * One(f));
 pg5 := AmbientSpace( w5q );
 solid := VectorSpaceToElement(pg5, [[1,0,0,0,0,1],[0,0,1,0,0,0],
                                       [0,0,0,1,0,0],[0,0,0,0,1,0]]*One(f));
 q4qcanonical := Range(duality)!.geometry;
 iso := IsomorphismPolarSpaces(q4q, q4qcanonical);
 bltdual := PreImagesSet(duality, ImagesSet(iso, blt));

 Info(InfoFinInG, 1, "Now embedding dual BLT-set into W(5,q)...");

 geo := EGQByBLTSet( bltdual, p, solid);

 ## Now we construct the elation group. See Maska Law's Thesis for details
 ## (we have a different form though, so we need to apply a base change).

 Info(InfoFinInG, 1, "Computing elation group...");

 mat := function(a,b,c,d,e)
            local m;
            m := IdentityMat(6, f);
            m[6]{[1..5]} := [e,d,c,-b,-a];
            m[2,1] := a; m[3,1] := b; m[4,1] := c; m[5,1] := d;
            return m;
          end;
 bas := AsList(Basis(f));
 gens := [];
 zero := Zero(f);
 for a in bas do
  Add(gens, mat(a,zero,zero,zero,zero) );
  Add(gens, mat(zero,a,zero,zero,zero) );
  Add(gens, mat(zero,zero,a,zero,zero) );
  Add(gens, mat(zero,zero,zero,a,zero) );
  Add(gens, mat(zero,zero,zero,zero,a) );
 od;
 ## base change
 b := [ [ 1, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 1 ],
   [ 0, 1, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 1, 0 ],
         [ 0, 0, 1, 0, 0, 0 ], [ 0, 0, 0, 1, 0, 0 ] ];
 gens := List(gens, t-> b*t*b^-1);
 gens := List(gens, t -> CollineationOfProjectiveSpace(t,f));
 elations := Group( gens );
    #SetOrder
 SetElationGroup( geo, elations );

 action := function(el, x)
  return Wrap( geo, el!.type, OnProjSubspaces(el!.obj, x));
 end;
 SetCollineationAction( elations, action );

 listels := function(gp,i)
  local reps,group,act;
  Info(InfoFinInG, 1, "Using elation group to enumerate elements");
  group := ElationGroup(gp);
  act := CollineationAction(group);
  reps := RepresentativesOfElements(gp)[i];
  return Union(List(reps,x->List(Enumerate(Orb(group,x,act)))));
 end;
 geo!.listelements := listels;
 return geo;
 end );

#############################################################################
#O  Display( <egq> )
#  for an EGQByBLTSet
##
InstallMethod( Display,
 "for an elation GQ by BLT set",
 [ IsElationGQByBLTSet ],
 function( gp )
        Print("Elation generalised quadrangle of order ",String(Order(gp)),"\n",
                "with defining planes\n");
        View(gp!.planes);
        Print("\nand base point with underlyig object\n");
        View(gp!.basepointobj);
    end );

#############################################################################
#O  DefiningPlanesOfEGQByBLTSet( <egq> )
#  for an EGQByBLTSet
##
InstallMethod( DefiningPlanesOfEGQByBLTSet,
    "for an EGQByBLTSet",
    [ IsElationGQByBLTSet ],
    function( egq )
  return egq!.planes;
 end );

#############################################################################
#O  ObjectToElement( <egq>, <type>, <el> )
#
##
InstallMethod( ObjectToElement,
 "for an egq byt blt, an integer and a subspace of a polar space",
 [ IsElationGQByBLTSet, IsPosInt, IsSubspaceOfClassicalPolarSpace],
 function(egq, t, el)
  local p,planes;
  if not el in egq!.polarspace then
   Error("<el> does not represent an element of <egq>");
  fi;
  planes := egq!.planes;
  if t=1 then
   p := egq!.basepointobj;
   if ProjectiveDimension(el) = 0 then
    if el = p then
     return BasePointOfEGQ(egq);
    elif not IsCollinear(egq!.polarspace,p,el) then
     return Wrap(egq,1,el);
    else
     Error("<el> does not represent a point of <egq>");
    fi;
   elif ProjectiveDimension(el) = 1 then
    if Span(p,el) in planes then
     return Wrap(egq,1,el);
    fi;
   else
    Error("<el> does not represent a point of <egq>");
   fi;
  elif t=2 then
   if el in planes then
    return Wrap(egq,2,el);
   elif Number(planes,x->ProjectiveDimension(Meet(x,el))=1)=1 then
    return Wrap(egq,2,el);
   else
    Error("<el> does not represent a line of <egq>");
   fi;
  else
   Error("<egq> is a point-line geometry not containing elements of type ",t);
  fi;
 end );

#############################################################################
#O  ObjectToElement( <egq>, <el> )
#
##
InstallMethod( ObjectToElement,
 "for an egq byt blt, and a subspace of a polar space",
 [ IsElationGQByBLTSet, IsSubspaceOfClassicalPolarSpace],
 function(egq, el)
  local p,planes;
  if not el in egq!.polarspace then
   Error("<el> does not represent an element of <egq>");
  fi;
  planes := egq!.planes;
  p := egq!.basepointobj;
  if ProjectiveDimension(el) = 2 then
   if el in planes then
    return Wrap(egq,2,el);
   elif Number(planes,x->ProjectiveDimension(Meet(x,el))=1)=1 then
    return Wrap(egq,2,el);
   else
    Error("<el> does not represent an element of <egq>");
   fi;
  elif ProjectiveDimension(el) = 1 then
   if Span(p,el) in planes then
     return Wrap(egq,1,el);
   else
    Error("3<el> does not represent an element of <egq>");
   fi;
  elif ProjectiveDimension(el) = 0 then
   if el = p then
    return BasePointOfEGQ(egq);
   elif not IsCollinear(egq!.polarspace,p,el) then
    return Wrap(egq,1,el);
   else
    Error("<el> does not represent an element of <egq>");
   fi;
  else
   Error("<el> does not represent an element of <egq>");
  fi;
 end );

#############################################################################
#O  CollineationSubgroup( <egq> )
# The setwise stabiliser of the BLT lines is a sub group of the Collineation
# group of the egq-by-blt. This subgroup is of course not the complete
# collineation group, but might be useful, and can be computed much quicker
# than the full collineation group.
##
InstallMethod( CollineationSubgroup,
 "for a generalised polygon, an integer and an object",
 [ IsElationGQByBLTSet ],
 function(egq)
  local ps, stabp, coll;
  ps := egq!.polarspace;
  stabp := FiningStabiliser(CollineationGroup(ps),egq!.basepointobj);
  coll := FiningSetwiseStabiliser(stabp,egq!.planes);
  SetCollineationAction(coll,CollineationAction(ElationGroup(egq)));
  return coll;
 end );


#############################################################################
#O  FlockGQByqClan( <clan> )
# returns the BLT set corresponding with the q-Clan <clan>
# not documented yet.
##
InstallMethod( FlockGQByqClan, [ IsqClanObj ],
 function( qclan )
  local f, q, mat, form, w5, p, blt, x, perp, pperp, pg5, a, bas, gens, zero, elations, action,
        projpoints, gqpoints, gqlines, gqpoints2, gqlines2, res, geo, ty, points, lines, clan,
  pgammasp, stabp, stabblt, hom, omega, imgs, bltvecs;
  f := qclan!.basefield;
  clan := qclan!.matrices;
  q := Size(f);
  if not IsOddInt(q) then
       Error("Invalid input"); return;
  fi;

  mat := [[0,0,0,0,0,1],[0,0,0,0,1,0],[0,0,0,1,0,0],
          [0,0,-1,0,0,0],[0,-1,0,0,0,0],[-1,0,0,0,0,0]] * Z(q)^0;
  form := BilinearFormByMatrix(mat, GF(q));
  w5 := PolarSpace( form );
  p := VectorSpaceToElement(w5, [1,0,0,0,0,0] * Z(q)^0);

  blt := [ VectorSpaceToElement(w5, [[1,0,0,0,0,0], [0,0,0,1,0,0],[0,0,0,0,1,0]]*One(f)) ];

 bltvecs := List(clan,x->[[1,0,0,0,0,0], [0,1,0,x[1][2],x[1][1],0], [0,0,1,x[2][2],x[1][2],0]] * One(f));
 #for x in bltvecs do
 # ConvertToMatrixRepNC(x,f); #jdb 19/09/2018: I think this is not needed anymore.
 #od;

  #for x in clan do
  #    Add(blt, VectorSpaceToElement(w5, [[1,0,0,0,0,0], [0,1,0,x[1][2],x[1][1],0], [0,0,1,x[2][2],x[1][2],0]] * One(f)));
  #od;

 for x in bltvecs do
  Add(blt,VectorSpaceToElement(w5,x));
 od;
    Info(InfoFinInG, 1, "Making flock GQ...");

  perp := PolarMap(w5);;
  pperp := perp(p);
  pg5 := AmbientSpace( w5 );;
  projpoints := Points(pg5);;

 Info(InfoFinInG, 1, "...points outside of perp of P...");

  gqpoints := Filtered(projpoints, x -> not x in pperp);;
  Add(gqpoints, p);

  gqlines := ShallowCopy(blt);;
  gqpoints2 := [];;

    Info(InfoFinInG, 1, "...lines contained in some BLT-set element...");

  for x in gqlines do
      res := ShadowOfElement(pg5, x, 2);
      res := Filtered(res, t -> not p in t);
      Append(gqpoints2, res);
  od;
  gqpoints2 := Set(gqpoints2);;
  gqlines2 := [];;

    Info(InfoFinInG, 1, "...planes meeting some BLT-set element in a line...");
  for x in gqpoints2 do
      res := ShadowOfFlag(pg5, [x,perp(x)], 3);
      res := Filtered(res, t -> not p in t);
      Append(gqlines2, res);
  od;

    Info(InfoFinInG, 1, "...sorting the points and lines...");

  points := SortedList( Concatenation(gqpoints, gqpoints2) );;
  lines := SortedList( Concatenation(gqlines, gqlines2) );;

  geo := rec( points := points, lines := lines, incidence := IsIncident);;

  ty := NewType( GeometriesFamily, IsElationGQ and IsGeneralisedPolygonRep);
  Objectify( ty, geo );
  SetBasePointOfEGQ( geo, Wrap(geo, 1, p) );
  SetAmbientSpace(geo, w5);
  SetOrder(geo, [q^2, q]);
  SetTypesOfElementsOfIncidenceStructure(geo, ["point","line"]);

  Info(InfoFinInG, 1, "Computing collineation group in PGammaSp(6,q)...");
  pgammasp := CollineationGroup( w5 );
  stabp := SetwiseStabilizer(pgammasp, OnProjSubspaces, [p])!.setstab;

  Info(InfoFinInG, 1, "..computed stabiliser of P");

  ## compute the stabiliser of the BLT-set differently...

  hom := ActionHomomorphism(stabp, AsList(Planes(p)), OnProjSubspaces, "surjective");
  omega := HomeEnumerator(UnderlyingExternalSet(hom));;
  imgs := Filtered([1..Size(omega)], x -> omega[x] in blt);;
  stabblt := Stabilizer(Image(hom), imgs, OnSets);
  gens := GeneratorsOfGroup(stabblt);
  gens := List(gens, x -> PreImagesRepresentative(hom, x));
  stabblt := GroupWithGenerators(gens);

  Info(InfoFinInG, 1, "..computed stabiliser of BLT set");

  SetCollineationGroup( geo, stabblt );
  action := function(el, x)
               return Wrap( geo, el!.type, OnProjSubspaces(el!.obj, x));
             end;
  SetCollineationAction( stabblt, action );

  ## Now we construct the elation group. See Maska Law's Thesis for details

  Info(InfoFinInG, 1, "Computing elation group...");
  mat := function(a,b,c,d,e)
            local m;
            m := IdentityMat(6, f);
            m[6]{[1..5]} := [e,d,c,-b,-a];
            m[2,1] := a; m[3,1] := b; m[4,1] := c; m[5,1] := d;
            return m;
         end;
  bas := AsList(Basis(f));
  gens := [];
  zero := Zero(f);
  for a in bas do
       Add(gens, mat(a,zero,zero,zero,zero) );
       Add(gens, mat(zero,a,zero,zero,zero) );
       Add(gens, mat(zero,zero,a,zero,zero) );
       Add(gens, mat(zero,zero,zero,a,zero) );
       Add(gens, mat(zero,zero,zero,zero,a) );
  od;
  gens := List(gens, t -> CollineationOfProjectiveSpace(t,f));
  elations := SubgroupNC( stabblt, gens );
  SetElationGroup( geo, elations );
  SetCollineationAction( elations, action );

  return geo;
end );

[Seitenstruktur0.98Druckenetwas mehr zur Ethik2026-04-26]