##########################################################################
##
##  group.gi              FinInG package
##                                                              John Bamberg
##                                                              Anton Betten
##                                                              Jan De Beule
##                                                             Philippe Cara
##                                                            Michel Lavrauw
##                                                           Max Neunhoeffer
##
##  Copyright 2017	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 our "projective groups"
##
############################################################################

## helping function. came from projectivespace.gi

# CHECKED 5/09/11 jdb
# We are currently CVec'ing everything. I decide to keep this function as is, and
# convert its output where appriopriate to a list of cvecs
# This makes that converting the vectors here to a dirty GAP vector is useless, so I
# uncommented the if q <= 256 statement.
#############################################################################
#F  MakeAllProjectivePoints( f,d )
# Global function to compute all points of a projective space. Not for users.
# <f>: field; <d>: *projective* dimension.
##
InstallGlobalFunction( MakeAllProjectivePoints, 
function(f,d)

  # f is a finite field
  # d an integer >= 1
  # This function is used for computing the permutation representation (NiceMonomorphism)
  # quickly and with the least memory used as possible, for a projective group.
  # Later, we will convert everything here to CVec's, which should give us an
  # improved permutation representation. For example:
  #   gap> pg:=PG(5,5);
  #   ProjectiveSpace(5, 5)
  #   gap> g:=ProjectivityGroup(pg);
  #   PGL(6,5)
  #   gap> hom := NiceMonomorphism(g);
  #   <action isomorphism>
  #   gap> omega:=UnderlyingExternalSet(hom);;
  #   gap> Random(omega);
  #   ## returns a compressed vector


  local els,i,j,l,q,sp,v,vs,w,ww,x;
  els := Elements(f);
  q := Length(els);
  v := CVec([1..d+1]*Zero(f),f); # using cvecs (ml 31/03/13) 
  #v := ListWithIdenticalEntries(d+1,Zero(f));
  #if q <= 256 then ConvertToVectorRep(v,q); fi;
  vs := EmptyPlist(q^d);
  sp := EmptyPlist((q^(d+1)-1)/(q-1));
  for x in els do
    w := ShallowCopy(v);
    w[d+1] := x;
    Add(vs,w);
  od;
  w := ShallowCopy(v);
  w[d+1] := One(f);
  Add(sp,w);
  for i in [d,d-1..1] do
    l := Length(vs);
    for j in [1..l] do
      ww := ShallowCopy(vs[j]);
      ww[i] := One(f);
      Add(sp,ww);
      Add(vs,ww);
    od;
    if i > 1 then
        for x in els do
          if not(IsZero(x)) and not(IsOne(x)) then
            for j in [1..l] do
              ww := ShallowCopy(vs[j]);
              ww[i] := x;
              Add(vs,ww);
            od;
          fi;
        od;
    fi;
  od;
  return Set(sp);
end);


## make this a global function? Definitely (jdb, 6/9/11).

# CHECKED 6/09/11 jdb
#############################################################################
#F  IsFiningScalarMatrix( a)
#returns true if <a> is a scalar matrix.
##
InstallGlobalFunction(IsFiningScalarMatrix,
	function( a )
	local n;
	n := a[1][1];
	if IsZero(n) then 
		return false;
	else
		return IsOne(a/n);
	fi;
	end );


###################################################################
# Construction of "projective elements", that is matrices modulo scalars:
###################################################################

## A lot of the following is now obselete. We should consider
## deleting some of it.

# CHECKED 5/09/11 jdb
# We are in the very big cvec operation and will skip all methods for non frob groups right now. 19/3/2014.
# I am not completely happy with the behaviour of this method, since there might
# be some issue with the field.
# On the other hand, it is not intended for the user and might even be obselete
#############################################################################
#O  ProjEl( <mat> )
# method to construct an object in the category IsProjGrpEl, i.e. projectivities, 
# aka "matrices modulo scalars". This method is not intended for the users. 
# it has no checks built in. It relies on DefaultFieldOfMatrix to determine the 
# field to be used.
##
InstallMethod( ProjEl, 
	"for a ffe matrix",
	[IsMatrix and IsFFECollColl],
	function( m )
		local el, m2, f;
	    m2 := ShallowCopy( m );
		f := DefaultFieldOfMatrix(m);
		ConvertToMatrixRepNC( m2, f );
		el := rec( mat := m2, fld := f );
		Objectify( NewType( ProjElsFamily,
							IsProjGrpEl and
							IsProjGrpElRep ), el );
		return el;
	end );

# CHECKED 5/09/11 jdb
#############################################################################
#O  ProjEls( <mat> )
# method to construct objects in the category IsProjGrpEl, i.e. projectivities, 
# aka "matrices modulo scalars". This method is not intended for the users.
# it has no checks built in, and is almost liek ProjEl.
## 
InstallMethod( ProjEls, "for a list of ffe matrices",
  [IsList],
  function( l )
    local el,fld,ll,m,ty,m2;
    fld := FieldOfMatrixList(l);
    ll := [];
    ty := NewType( ProjElsFamily,
                   IsProjGrpEl and
                   IsProjGrpElRep );
    for m in l do
        m2 := ShallowCopy(m);
		ConvertToMatrixRepNC( m2, fld );
        el := rec( mat := m2, fld := fld );
        Objectify( ty, el );
        Add(ll,el);
    od;
    return ll;
  end );

# CHECKED 5/09/11 jdb # changed ml 8/11/12
# changed 19/3/2014 to cmat.
#############################################################################
#O  Projectivity( <mat>, <gf> )
# method to construct an object in the category IsProjGrpElWithFrob, but with
# field automorphism equal to the identity, i.e. a projectivity, 
# This method is intended for the user, and contains
# a check whether the matrix is non-singular.
## 
InstallMethod( Projectivity, [ IsMatrix and IsFFECollColl, IsField],
    ## A bug was found here, during a nice August afternoon involving pigeons,
    ## where the variable m2 was assigned to the size of the field.
    ## jdb 13/12/08, Giessen, cold saturday afternoon. I still remember the
    ## pigeons, so does my computer. I add some lines now to check whether the
    ## matrix is non singular. 
  	function( mat, gf )
		local el, m2, fld, frob, cmat;
		m2 := ShallowCopy(mat);
		#if RowLength(m2) <> Length(m2) then
		#	Error("<mat> must be a square matrix");
		#fi;
		if Rank(m2) <> Size(m2) then
			Error("<mat> must not be singular");
		fi;
		#ConvertToMatrixRep( m2, gf );
		cmat := NewMatrix( IsCMatRep, gf, Size(m2) , m2);
		el := rec( mat := cmat, fld := gf, frob := FrobeniusAutomorphism(gf)^0 );
		Objectify( ProjElsWithFrobType, el );
		return el;
	end );

# added 19/3/2014 (cmat).
#############################################################################
#O  Projectivity( <mat>, <gf> )
# method to construct an object in the category IsProjGrpElWithFrob,
# as above, with input a cmat and field
## 
InstallMethod( Projectivity, [ IsCMatRep and IsFFECollColl, IsField],
  	function( mat, gf )
		local el, m2, fld, frob, cmat;
		m2 := ShallowCopy(mat);
		#if RowLength(m2) <> Length(m2) then 
		#	Error("<mat> must be a square matrix");
		#fi;
		if Rank(m2) <> Length(m2) then
			Error("<mat> must not be singular");
		fi;
		#ConvertToMatrixRep( m2, gf );
		el := rec( mat := m2, fld := gf, frob := FrobeniusAutomorphism(gf)^0 );
		Objectify( ProjElsWithFrobType, el );
		return el;
	end );


# Added ml 8/11/2012
#############################################################################
#O  Projectivity( <pg>, <mat> )
# method to construct an object in the category IsProjGrpEl, i.e. a projectivity, 
# This method is intended for the user, and contains
# a check whether the matrix is non-singular.
## 
InstallMethod( Projectivity, [ IsProjectiveSpace, IsMatrix],
#
  	function( pg, mat )
		local d,gf,m2;
		d:=Dimension(pg);
		gf:=pg!.basefield;
		if d <> Size(mat)-1 then
			Error("The arguments <mat> and <pg> are incompatible");
		fi;
		m2 := ShallowCopy(mat);
		#if RowLength(m2) <> Length(m2) then 
		#	Error("<mat> must be a square matrix");
		#fi;
		if Rank(m2) <> Size(m2) then
			Error("<mat> must not be singular");
		fi;
		return Projectivity(mat,gf);
	end );

# Added ml cmat version 19/3/14.
#############################################################################
#O  Projectivity( <pg>, <mat> )
# method to construct an object in the category IsProjGrpEl, i.e. a projectivity, 
# This method is intended for the user, and contains
# a check whether the matrix is non-singular.
## 
InstallMethod( Projectivity, [ IsProjectiveSpace, IsCMatRep],
#
  	function( pg, mat )
		local d,gf,m2;
		d:=Dimension(pg);
		gf:=pg!.basefield;
		if d <> Size(mat)-1 then
			Error("The arguments <mat> and <pg> are incompatible");
		fi;
		m2 := ShallowCopy(mat);
		#if RowLength(m2) <> Length(m2) then 
		#	Error("<mat> must be a square matrix");
		#fi;
		if Rank(m2) <> Size(m2) then
			Error("<mat> must not be singular");
		fi;
		return Projectivity(mat,gf);
	end );


###################################################################
# Tests whether collineation is a projectivity and so on ...
###################################################################

# Added ml 7/11/2012
#############################################################################
#O  IsProjectivity( <g> )
## 
InstallMethod( IsProjectivity, [ IsProjGrpEl ],
  function( g )
	return true;
  end );

# Added ml 7/11/2012
#############################################################################
#O  IsProjectivity( <g> )
# method to check if a given collineation of a projective space is a projectivity, 
# i.e. if the corresponding frobenius automorphism is the identity
## 
InstallMethod( IsProjectivity, [ IsProjGrpElWithFrob ],
  function( g )
    local F,sigma;
		F:=g!.fld;
		sigma:=g!.frob;
		if sigma = FrobeniusAutomorphism(F)^0
		then return true;
		else return false;
		fi;
  end );

# Added ml 8/11/2012 # changed ml 28/11/2012
#############################################################################
#O  IsStrictlySemilinear( <g> )
## 
InstallMethod( IsStrictlySemilinear, [ IsProjGrpEl],
  function( g )
	return false;
  end );

# Added ml 8/11/2012 # changed ml 28/11/2012
#############################################################################
#O  IsStrictlySemilinear( <g> )
# method to check if a given collineation of a projective space is semilinear, 
# i.e. if the corresponding frobenius automorphism is NOT the identity
## 
InstallMethod( IsStrictlySemilinear, [ IsProjGrpElWithFrob],
  function( g )
    local F,sigma;
		F:=g!.fld;
		sigma:=g!.frob;
		if sigma = FrobeniusAutomorphism(F)^0
		then return false;
		else return true;
		fi;
  end );

  
  
# Added ml 8/11/2012
#############################################################################
#O  IsCollineation( <g> )
## 
InstallMethod( IsCollineation, [ IsProjGrpEl],
  function( g )
	return true;
  end );

# Added ml 8/11/2012
#############################################################################
#O  IsCollineation( <g> )
## 
InstallMethod( IsCollineation, [ IsProjGrpElWithFrob],
  function( g )
    return true;
end );


# Added ml 7/11/2012
#############################################################################
#O  IsProjectivityGroup( <G> )
# method to check if a given projective collineation group G is a projectivity group, 
# i.e. if the corresponding frobenius automorphisms of the generators are the identity
## 
InstallMethod( IsProjectivityGroup, [ IsProjectiveGroupWithFrob],
  function( G )
    local gens, F, set, g;
		gens:=GeneratorsOfMagmaWithInverses(G);
		F:=gens[1]!.fld;
		set:=AsSet(List(gens,g->g!.frob));
		if set = AsSet([FrobeniusAutomorphism(F)^0])
		then return true;
		else return false;
		fi;
  end );


# Added ml 8/11/2012
#############################################################################
#O  IsCollineationGroup( <G> )
##
InstallMethod( IsCollineationGroup, [ IsProjectiveGroupWithFrob],
  function( G )
	return true;
  end );

###################################################################
# Construction of "projective collineation maps", that is matrices 
# modulo scalars with frobenius automorphism
###################################################################

# CHECKED 5/09/11 jdb
# cmat changed 19/3/14
#############################################################################
#O  ProjElWithFrob( <mat>, <frob>, <f> )
# method to construct an object in the category IsProjGrpElWithFrob, i.e. projective 
# semilinear maps aka "matrices modulo scalars with frob". This method is not 
# intended for the users, it has no checks built in.
##
InstallMethod( ProjElWithFrob, 
	"for a cmat/ffe matrix and a Frobenius automorphism, and a field",
	[IsCMatRep and IsFFECollColl, #changed 19/3/14 to cmat.
	IsRingHomomorphism and IsMultiplicativeElementWithInverse,
	IsField],
	function( m, frob, f )
		local el, m2;
		m2 := ShallowCopy(m);
		#ConvertToMatrixRep( m2, f );
		el := rec( mat := m2, fld := f, frob := frob );
		Objectify( ProjElsWithFrobType, el );
		return el;
	end );
	
# added 19/03/14
#############################################################################
#O  ProjElWithFrob( <mat>, <frob>, <f> )
# method to construct an object in the category IsProjGrpElWithFrob, i.e. projective 
# semilinear maps aka "matrices modulo scalars with frob". This method is not 
# intended for the users, it has no checks built in.
##
InstallMethod( ProjElWithFrob, 
	"for a ffe matrix and a Frobenius automorphism, and a field",
	[IsMatrix and IsFFECollColl, 
	IsRingHomomorphism and IsMultiplicativeElementWithInverse,
	IsField],
	function( m, frob, f )
		local el, m2, cmat;
		m2 := ShallowCopy(m);
		#ConvertToMatrixRep( m2, f );
		cmat := NewMatrix(IsCMatRep,f,Length(m2[1]),m2);
		el := rec( mat := cmat, fld := f, frob := frob );
		Objectify( ProjElsWithFrobType, el );
		return el;
	end );
	

# CHECKED 5/09/11 jdb
# cmat changed 19/3/14
#############################################################################
#O  ProjElWithFrob( <mat>, <frob> )
# method to construct an object in the category IsProjGrpElWithFrob, i.e. projective 
# semilinear maps aka "matrices modulo scalars with frob". This method is not 
# intended for the users, although there are some checks built in. 
# This method relies on DefaultFieldOfMatrix and Range(<frbo>) to determine the field to be used
##
InstallMethod( ProjElWithFrob, 
	"for a cmat/ffe matrix and a Frobenius automorphism",
	[IsCMatRep and IsFFECollColl, #changed 19/3/14. 
	IsRingHomomorphism and IsMultiplicativeElementWithInverse], 
	1, #to set higher priority than the next method.
	function ( m, frob )
		local matrixfield, frobfield, mchar, fchar, dim;
		if IsOne(frob) then
		  TryNextMethod();
		fi;
		matrixfield := DefaultFieldOfMatrix(m);
		mchar := Characteristic(matrixfield);
		frobfield := Range(frob);
		fchar := Characteristic(frobfield);
		if mchar <> fchar then
		  Error("the matrix and automorphism do not agree on the characteristic");
		fi;
		
		# figure out a field which contains the matrices and admits the
		# automorphisms (nontrivially)
		dim := Lcm(
		  LogInt(Size(matrixfield),mchar),
			LogInt(Size(frobfield),fchar)
			);
	return ProjElWithFrob( m, frob, GF(mchar^dim) );
	end);
 
# CHECKED 5/09/11 jdb
# cmat changed 19/3/14
#############################################################################
#O  ProjElWithFrob( <mat>, <frob> )
# method to construct an object in the category IsProjGrpElWithFrob, i.e. projective 
# semilinear maps aka "matrices modulo scalars with frob". This method is not 
# intended for the users; in fact this method is almost the same as the first version.
# This method relies on DefaultFieldOfMatrix to determine the field to be used
# This method should only be called from the above one, if IsOne(frob) is true,
# but we built in a little check to abvoid disasters.
##
InstallMethod( ProjElWithFrob, 
	"for a cmat/ffe matrix and a trivial Frobenius automorphism",
	[IsCMatRep and IsFFECollColl,
	IsRingHomomorphism and IsMultiplicativeElementWithInverse], 
	0, #to set lower priority than the previous method.
	function( m, frob )
		local el, m2;
		if not IsOne(frob) then
		  Error("<frob> is not trivial. Something went wrong when calling this method");
		fi;
		m2 := ShallowCopy(m); 
		#ConvertToMatrixRepNC( m2 );
		el := rec( mat := m2, fld := DefaultFieldOfMatrix(m), frob := frob );
		Objectify( ProjElsWithFrobType, el );
		return el;
	end );

# CHECKED 5/09/11 jdb
#############################################################################
#O  ProjElsWithFrob( <l>, <f> )
# method to construct a list of objects in the category IsProjGrpElWithFrob,
# using a list of pairs of matrix/frobenius automorphism, and a field.
# This method relies of ProjElWithFrob, and is not inteded for the user.
# no checks are built in. This could result in e.g. the use of a field that is 
# not compatible with (some of) the matrices, and result in a non user friendly 
# error
##
InstallMethod( ProjElsWithFrob,
	"for a list of pairs of ffe matrices and frobenius automorphisms, and a field",
	[IsList, IsField],
	function( l, f )
	local objectlist, m;
		objectlist := [];
		for m in l do
			Add(objectlist, ProjElWithFrob(m[1],m[2],f));
		od;
		return objectlist;
	end );

# CHECKED 5/09/11 jdb
#############################################################################
#O  ProjElsWithFrob( <l> )
# method to construct a list of objects in the category IsProjGrpElWithFrob,
# using a list of pairs of matrix/frobenius automorphism. It is checked if 
# the given matrices/automorphism pairs can be considered over a common field.
# This method relies eventually also on ProjElWithFrob, and is not inteded for the user,
# although the mechanism of fining the suitable field will display an error 
# if this is not possible.
##
InstallMethod( ProjElsWithFrob, 
	"for a list of pairs of cmat/ffe matrices and Frobenius automorphisms",
	[IsList],
	function( l )
    local matrixfield, frobfield, mchar, fchar, oldchar, f, dim, m, objectlist;
    if(IsEmpty(l)) then
		return [];
		fi;
		dim := 1;

		# get the characteristic of the field that we want to work in.
		# it should be the same for every matrix and every automorphism --
		# if not we will raise an error.
		oldchar := Characteristic(l[1][1]);
		for m in l do
  		matrixfield := DefaultFieldOfMatrix(m[1]);
  		mchar := Characteristic(matrixfield);
  		frobfield := Range(m[2]);
  		fchar := Characteristic(frobfield);
  		if mchar <> fchar or mchar <> oldchar then
  		  Error("matrices and automorphisms do not agree on the characteristic");
  		fi;
		
  		# at each step we increase the dimension of the desired field
		# so that it contains all the matrices and admits all the automorphisms
		# (nontrivially.)
  
  		dim := Lcm( dim, LogInt(Size(matrixfield),mchar),
  			LogInt(Size(frobfield),fchar));
		od;

		f := GF(oldchar ^ dim);
		objectlist := [];
		for m in l do
			Add(objectlist, ProjElWithFrob(m[1],m[2],f));
		od;
		return objectlist;
	end );


# CHECKED 5/09/11 jdb # changed ml 8/11/12
#############################################################################
#O  CollineationOfProjectiveSpace( <mat>, <gf> )
# method to construct an object in the category IsProjGrpElWithFrob, i.e. a 
# collineation of a projective space. 
# This method is intended for the user, and contains
# a check whether the matrix is non-singular. The method relies on ProjElWithFrob,
# which will produce an error if the entries of <mat> do not all belong to <gf>
# the automorphism will be trivial. Mathematically this is a projectivity.
## 
InstallMethod( CollineationOfProjectiveSpace, 
	[ IsMatrix and IsFFECollColl, IsField],
	function( mat, gf )
    if Rank(mat) <> Size(mat) then
		Error("<mat> must not be singular");
    fi;
    return ProjElWithFrob( mat, IdentityMapping(gf), gf);
	end );

# Added ml 8/11/2012
#############################################################################
#O  CollineationOfProjectiveSpace( <pg>, <mat> )
# method to construct an collineation of a projective space.
## 
InstallMethod( CollineationOfProjectiveSpace, [ IsProjectiveSpace, IsMatrix],
#
  	function( pg, mat )
		local d,gf;
		d:=Dimension(pg);
		gf:=pg!.basefield;
		if d <> Size(mat)-1 then
			Error("The arguments <mat> and <pg> are incompatible");
		fi;
		return ProjElWithFrob( mat, IdentityMapping(gf), gf);
	end );

# Added jdb 26/05/2016
# not documented yet
#############################################################################
#O  CollineationOfProjectiveSpace( <pg>, <mat> )
# method to construct an collineation of a projective space with identitymatrix,
# but with user defined field automorphism.
## 
InstallMethod( CollineationOfProjectiveSpace, [ IsProjectiveSpace, IsMapping],
  	function( pg, frob )
		local d,gf,mat;
		d:=Dimension(pg);
		gf:=Range(frob);
        if not gf = pg!.basefield then
            Error("basefield of <pg> does not match with range of <frob>");
        fi;
        mat := IdentityMat(d+1,gf);
		return ProjElWithFrob( mat, frob, gf);
	end );


# Added ml 8/11/2012
#############################################################################
#O  CollineationOfProjectiveSpace( <pg>, <mat>, <frob>)
# method to construct an collineation of a projective space.
## 
InstallMethod( CollineationOfProjectiveSpace, [ IsProjectiveSpace, IsMatrix, IsMapping],
#
  	function( pg, mat, frob )
		local d,gf;
		d:=Dimension(pg);
		gf:=pg!.basefield;
		if d <> Size(mat)-1 then
			Error("The arguments <mat> and <pg> are incompatible");
		fi;
		return ProjElWithFrob( mat, frob, gf);
	end );


# Added ml 8/11/2012
#############################################################################
#O  Collineation( <pg>, <mat> )
# shorter version of the previous method to construct an collineation of a projective space.
## 
InstallMethod( Collineation, [ IsProjectiveSpace, IsMatrix],
#
  	function( pg, mat )
		return CollineationOfProjectiveSpace(pg,mat);
	end );


# Added ml 8/11/2012
#############################################################################
#O  Collineation( <pg>, <mat>, <frob> )
# shorter version of the previous method to construct an collineation of a projective space.
## 
InstallMethod( Collineation, [ IsProjectiveSpace, IsMatrix, IsMapping],
#
  	function( pg, mat, frob )
		return CollineationOfProjectiveSpace(pg,mat,frob);
	end );


# CHECKED 5/09/11 jdb # changed ml 8/11/12
# 5/09/11 jdb: added a check to see if frob has <gf> as source
#############################################################################
#O  CollineationOfProjectiveSpace( <mat>, <frob>, <gf> )
# method to construct an object in the category IsProjGrpElWithFrob, i.e. a 
# collineation of a projective space, aka a projective collineation map. 
# This method is intended for the user, and contains
# a check whether the matrix is non-singular. The method relies on ProjElWithFrob,
# which will produce an error if the entries of <mat> do not all belong to <gf>
## 
InstallMethod( CollineationOfProjectiveSpace,  
	[ IsMatrix and IsFFECollColl, IsRingHomomorphism and
    IsMultiplicativeElementWithInverse, IsField], 
	function( mat, frob, gf )
    if Rank(mat) <> Size(mat) then
		Error("<mat> must not be singular");
    fi;
    if Source(frob) <> gf then
		Error("<frob> must be defined as an automorphis of <gf>");
	fi;
	return ProjElWithFrob( mat, frob, gf);
	end );


# CHECKED 5/09/11 jdb # changed ml 8/11/12
# 5/09/11 jdb: added a check to see if frob has <gf> as source
#############################################################################
#O  ProjectiveSemilinearMap( <mat>, <frob>, <gf> )
# method to construct an object in the category IsProjGrpElWithFrob, i.e. a 
# collineation of a projective space, aka a projective collineation map. 
# This method is intended for the user, and contains
# a check whether the matrix is non-singular. The method relies on ProjElWithFrob,
# which will produce an error if the entries of <mat> do not all belong to <gf>
## 
InstallMethod( ProjectiveSemilinearMap,  
	[ IsMatrix and IsFFECollColl, IsRingHomomorphism and
    IsMultiplicativeElementWithInverse, IsField], 
	function( mat, frob, gf )
		return CollineationOfProjectiveSpace(mat,frob,gf);
	end );


# CHECKED 5/09/11 jdb
#############################################################################
#O  ProjectivityByImageOfStandardFrameNC( <pg>, <image> )
# method to construct a projectivity if the image of a standard frame is given.
# THERE IS NO CHECK TO SEE IF THE GIVEN IMAGE CONSISTS OF N+2 POINTS NO N+1 L.D.
# for this reason, this function is not documented (yet).
# despite its name (projectivity), this function returns a projective semlinear map.
## 
InstallMethod( ProjectivityByImageOfStandardFrameNC, [ IsProjectiveSpace, IsList ],
	function(pg,image)
	# If the dimension of the projective space is n, then
	# given a frame, there is a 
	# unique projectivity mapping the standard frame to this set of points
	# THERE IS NO CHECK TO SEE IF THE GIVEN IMAGE CONSISTS OF N+2 POINTS NO N+1 L.D.
	local d,i,x,vlist,mat,coeffs,mat2;
	if not Length(image)=Dimension(pg)+2 then 
	Error("The argument does not have the required length to be the image of a frame");
	fi;
	d:=Dimension(pg);
	vlist:=List(image,x->x!.obj);
	mat:=List([1..d+1],i->vlist[i]);
	coeffs:=vlist[d+2]*(mat^-1);
	mat2:=List([1..d+1],i->coeffs[i]*mat[i]);
	return CollineationOfProjectiveSpace(mat2,pg!.basefield);
end );

###################################################################
# Some operations for elements (without and with frobenius automorphism
###################################################################

# CHECKED 5/09/11 jdb
#############################################################################
#O  MatrixOfCollineation( <c> )
# returns the underlying matrix of <c> 
##
InstallMethod( MatrixOfCollineation, [ IsProjGrpEl and IsProjGrpElRep],
	c -> c!.mat );

# CHECKED 5/09/11 jdb
#############################################################################
#O  MatrixOfCollineation( <c> )
# returns the underlying matrix of <c> 
##  
InstallMethod( MatrixOfCollineation, [ IsProjGrpElWithFrob and IsProjGrpElWithFrobRep],
	c -> c!.mat );
  
# CHECKED 5/09/11 jdb
#############################################################################
#O  FieldAutomorphism( <c> )
# returns the underlying field automorphism of <c> 
##  
InstallMethod( FieldAutomorphism, [ IsProjGrpElWithFrob and IsProjGrpElWithFrobRep],
	c -> c!.frob );

# CHECKED 5/09/11 jdb
#############################################################################
#O  Representative( <el> )
# returns the underlying matrix of the projectivity <el> 
##  
InstallOtherMethod( Representative, 
	"for a projective group element",
	[IsProjGrpEl and IsProjGrpElRep],
	function( el )
		return el!.mat;
	end );

# CHECKED 5/09/11 jdb
#############################################################################
#O  BaseField( <el> )
# returns the underlying field of <el> 
##  
InstallMethod( BaseField, 
	"for a projective group element",
	[IsProjGrpEl and IsProjGrpElRep],
	function( el )
		return el!.fld;
	end );

# CHECKED 5/09/11 jdb
#############################################################################
#O  Representative( <el> )
# returns the underlying matrix and frobenius automorphism of the projective 
# semilinear element <el> 
## 
InstallOtherMethod( Representative, 
	"for a projective group element with Frobenius",
	[IsProjGrpElWithFrob and IsProjGrpElWithFrobRep],
	function( el )
		return [el!.mat,el!.frob];
	end );

# CHECKED 5/09/11 jdb
#############################################################################
#O  BaseField( <el> )
# returns the underlying field of <el> 
##  
InstallMethod( BaseField, 
	"for a projective group element with Frobenius",
	[IsProjGrpElWithFrob and IsProjGrpElWithFrobRep],
	function( el )
		return el!.fld;
	end );

# CHECKED 5/09/11 jdb
###################################################################
# View, print and display methods for elements (without and with Frobenius)
###################################################################

InstallMethod( ViewObj, "for a projective group element",
  [IsProjGrpEl and IsProjGrpElRep],
  function(el)
    Print("<projective element ");
    ViewObj(el!.mat);
    Print(">");
  end);

InstallMethod( Display, "for a projective group element",
  [IsProjGrpEl and IsProjGrpElRep],
  function(el)
    Print("<projective element, underlying matrix:\n");
    Display(el!.mat);
    Print(">\n");
  end );

InstallMethod( PrintObj, "for a projective group element",
  [IsProjGrpEl and IsProjGrpElRep],
  function(el)
    Print("ProjEl(");
    PrintObj(el!.mat);
    Print(")");
  end );
  
InstallMethod( ViewObj, "for a projective group element with Frobenius",
  [IsProjGrpElWithFrob and IsProjGrpElWithFrobRep],
  function(el)
    Print("< a collineation: ");
    ViewObj(el!.mat);
    if IsOne(el!.frob) then
        Print(", F^0>");
    else
        Print(", F^",el!.frob!.power,">");
    fi;
  end);

InstallMethod( Display, "for a projective group element with Frobenius",
  [IsProjGrpElWithFrob and IsProjGrpElWithFrobRep],
  function(el)
    Print("<a collineation , underlying matrix:\n");
    Display(el!.mat);
    if IsOne(el!.frob) then
        Print(", F^0>\n");
    else
        Print(", F^",el!.frob!.power,">\n");
    fi;
  end );

InstallMethod( PrintObj, "for a projective group element with Frobenius",
  [IsProjGrpElWithFrob and IsProjGrpElWithFrobRep],
  function(el)
    Print("ProjElWithFrob(");
    PrintObj(el!.mat);
    Print(",");
    PrintObj(el!.frob);
    Print(")");
  end );

# CHECKED 5/09/11 jdb
###################################################################
# comparing elements (without and with Frobenius automorphism)
###################################################################

InstallMethod( \=, "for two projective group elements",
  [IsProjGrpEl and IsProjGrpElRep, IsProjGrpEl and IsProjGrpElRep],
  function( a, b )
    local aa,bb,p,s,i;
    if a!.fld <> b!.fld then Error("different base fields"); fi;
    aa := a!.mat;
    bb := b!.mat;
    p := PositionNonZero(aa[1]);
    s := bb[1][p] / aa[1][p];
    for i in [1..Length(aa)] do
        if s*aa[i] <> bb[i] then return false; fi;
    od;
    return true;
  end );

InstallMethod(\<,
  [IsProjGrpEl, IsProjGrpEl],
  function(a,b)
    local aa,bb,pa,pb,sa,sb,i,va,vb;
    if a!.fld <> b!.fld then Error("different base fields"); fi;
    aa := a!.mat;
    bb := b!.mat;
    pa := PositionNonZero(aa[1]);
    pb := PositionNonZero(bb[1]);
    if pa > pb then 
        return true;
    elif pa < pb then
        return false;
    fi;
    sa := aa[1][pa]^-1;
    sb := bb[1][pb]^-1;
    for i in [1..Length(aa)] do
        va := sa*aa[i];
        vb := sb*bb[i];
        if va < vb then return true; fi;
        if vb < va then return false; fi;
    od;
    return false;
  end);

InstallMethod( \=, "for two projective group elements with Frobenius",
  [IsProjGrpElWithFrob and IsProjGrpElWithFrobRep,
   IsProjGrpElWithFrob and IsProjGrpElWithFrobRep],
  function( a, b )
    local aa,bb,p,s,i;
    if a!.fld <> b!.fld then Error("different base fields"); fi;
    if a!.frob <> b!.frob then return false; fi;
    aa := a!.mat;
    bb := b!.mat;
    p := PositionNonZero(aa[1]);
    s := bb[1][p] / aa[1][p];
    if s*aa <> bb then return false; fi;
	#for i in [1..Length(aa)] do
        #if s*aa[i] <> bb[i] then return false; fi;
    #od;
    return true;
  end );

InstallMethod(\<,
  [IsProjGrpElWithFrob, IsProjGrpElWithFrob],
  function(a,b)
    local aa,bb,pa,pb,sa,sb,i,va,vb;
    if a!.fld <> b!.fld then Error("different base fields"); fi;
    if a!.frob < b!.frob then
        return true;
    elif b!.frob < a!.frob then
        return false;
    fi;
    aa := a!.mat;
    bb := b!.mat;
    pa := PositionNonZero(aa[1]);
    pb := PositionNonZero(bb[1]);
    if pa > pb then 
        return true;
    elif pa < pb then
        return false;
    fi;
    sa := aa[1][pa]^-1;
    sb := bb[1][pb]^-1;
    for i in [1..Length(aa)] do
        va := sa*aa[i];
        vb := sb*bb[i];
        if va < vb then return true; fi;
        if vb < va then return false; fi;
    od;
    return false; 
  end);

###################################################################
# More operations for elements (without and with frobenius automorphism)
###################################################################

# CHECKED 5/09/11 jdb
#############################################################################
#O  Order( <a> )
# returns the order of <a>. This function relies on ProjectiveOrder.
##  
InstallMethod( Order, 
	"for a projective group element",
	[IsProjGrpEl and IsProjGrpElRep],
	function( a )
		return ProjectiveOrder(a!.mat)[1];
	end );

# CHECKED 5/09/11 jdb
# 22/04/12 jb: There was a bug here. I added "and i mod ofrob = 0;"
# 3/6/2015: bug fixed by jb.
#############################################################################
#O  Order( <a> )
# returns the order of <a>.
## 
InstallMethod( Order, 
	"for a projective group element with Frobenius",
	[IsProjGrpElWithFrob and IsProjGrpElWithFrobRep],

# This algorithm could be improved by using the ideas of
# Celler and Leedham-Green.

	function( a )
   local b, frob, bfrob, i, ofrob;
	b := a!.mat;
	frob := a!.frob;
	if IsOne(frob) then 
		return ProjectiveOrder(b)[1];
	fi;
	if not IsOne(b) then
		bfrob := b; i := 1;
		ofrob := Order(frob);
		repeat
			bfrob := bfrob^(frob^-1);  ## JB 3/6/2015: Found bug here
			b := b * bfrob;
			i := i + 1;
		until IsFiningScalarMatrix( b ) and i mod ofrob = 0;
		return i;
	else
		return Order(frob);
	fi;
	return 1;
	end );

# CHECKED 5/09/11 jdb
#############################################################################
#O  IsOne( <a> )
# returns true if <a> is the identity.
## 
InstallMethod( IsOne, 
	"for a projective group element",
	[IsProjGrpEl and IsProjGrpElRep],
	function( el )
    local s;
    s := el!.mat[1][1];
    if IsZero(s) then 
		return false; 
	fi;
    s := s^-1;
    return IsOne( s*el!.mat );
	end );

# CHECKED 5/09/11 jdb
#############################################################################
#O  IsOne( <a> )
# returns true if <a> is the identity
## 
InstallMethod( IsOne, 
	"for a projective group element with Frobenius",
	[IsProjGrpElWithFrob and IsProjGrpElWithFrobRep],
	function( el )
    local s;
    if not(IsOne(el!.frob)) then 
		return false; 
	fi;
    s := el!.mat[1][1];
    if IsZero(s) then return false; fi;
    s := s^-1;
    return IsOne( s*el!.mat );
	end );

# CHECKED 6/09/11 jdb
#############################################################################
#O  DegreeFFE( <el> )
# for projectivities. returns the degree of the underlying field over its 
# prime field.
## 
InstallOtherMethod( DegreeFFE, 
	"for projective group element",
	[IsProjGrpEl and IsProjGrpElRep],
	function( el )
		return DegreeOverPrimeField( el!.fld );
	end );

# CHECKED 6/09/11 jdb
#############################################################################
#O  DegreeFFE( <el> )
# for projective collineation maps. returns the degree of the underlying 
# field over its prime field.
## 
InstallOtherMethod( DegreeFFE, 
	"for projective group element with Frobenius",
	[IsProjGrpElWithFrob and IsProjGrpElWithFrobRep],
	function( el )
		return DegreeOverPrimeField( el!.fld );
	end );

# CHECKED 6/09/11 jdb
#############################################################################
#O  Characteristic( <el> )
# for projectivities. returns the characteristic of the underlying field.
## 
InstallMethod( Characteristic, 
	"for projective group element",
	[IsProjGrpEl and IsProjGrpElRep],
	function( el )
		return Characteristic( el!.fld );
	end );

# CHECKED 6/09/11 jdb
#############################################################################
#O  Characteristic( <el> )
# for projective collineation maps. returns the characteristic of the underlying field.
## 
InstallMethod( Characteristic, 
	"for projective group element with Frobenius",
	[IsProjGrpElWithFrob and IsProjGrpElWithFrobRep],
	function( el )
		return Characteristic( el!.fld );
	end );

###################################################################
# The things that make it a group :-) (without Frobenius)
###################################################################

# CHECKED 5/09/11 jdb
#############################################################################
#O  \*( <a>, <b> )
# returns a*b, for IsProjGrpEl
## 
InstallMethod( \*, 
	"for two projective group elements",
	[IsProjGrpEl and IsProjGrpElRep, IsProjGrpEl and IsProjGrpElRep],
	function( a, b )
		local el;
		el := rec( mat := a!.mat * b!.mat, fld := a!.fld );
		Objectify( ProjElsType, el );
		return el;
	end );

# CHECKED 6/09/11 jdb
#############################################################################
#O  InverseSameMutability( <el> )
# returns el^-1, for IsProjGrpEl, keeps mutability.
## 
InstallMethod( InverseSameMutability, 
	"for a projective group element",
	[IsProjGrpEl and IsProjGrpElRep],
	function( el )
		local m;
		m := rec( mat := InverseSameMutability(el!.mat), fld := el!.fld );
		Objectify( ProjElsType, m );
		return m;
	end );

# CHECKED 6/09/11 jdb
#############################################################################
#O  InverseMutable( <el> )
# returns el^-1 (mutable) for IsProjGrpEl
## 
InstallMethod( InverseMutable, 
	"for a projective group element",
	[IsProjGrpEl and IsProjGrpElRep],
	function( el )
		local m;
		m := rec( mat := InverseMutable(el!.mat), fld := el!.fld );
		Objectify( ProjElsType, m );
		return m;
	end );

# CHECKED 6/09/11 jdb
#############################################################################
#O  OneImmutable( <el> )
# returns immutable one of the group of <el>
## 
InstallMethod( OneImmutable, 
	"for a projective group element",
	[IsProjGrpEl and IsProjGrpElRep],
	function( el )
		local o;
		o := rec( mat := OneImmutable( el!.mat ), fld := el!.fld );
		Objectify( NewType(FamilyObj(el), IsProjGrpElRep), o );
		return o;
	end );

# CHECKED 6/09/11 jdb
#############################################################################
#O  OneSameMutability( <el> )
# returns one of the group of <el> with same mutability of <el>.
## 
InstallMethod( OneSameMutability, 
	"for a projective group element",
	[IsProjGrpEl and IsProjGrpElRep],
	function( el )
		local o;
		o := rec( mat := OneImmutable( el!.mat ), fld := el!.fld );
		Objectify( NewType(FamilyObj(el), IsProjGrpElRep), o );
		return o;
	end );

###################################################################
# The things that make it a group :-) (with Frobenius)
###################################################################
# we first need: 
#################################################
# Frobenius automorphisms and groups using them:
#################################################
#12 CHECKED 6/09/11 jdb

InstallOtherMethod( \^, "for a FFE vector and a Frobenius automorphism",
  [ IsVector and IsFFECollection and IsMutable, IsFrobeniusAutomorphism ],
  function( v, f )
    return List(v,x->x^f);
  end );

#cvec version
InstallOtherMethod( \^, "for a cvec/FFE vector and a Frobenius automorphism",
  [ IsCVecRep and IsFFECollection and IsMutable, IsFrobeniusAutomorphism ],
  function( v, f )
    return CVec(List(v,x->x^f),BaseField(v));
  end );

InstallOtherMethod( \^, "for a FFE vector and a Frobenius automorphism",
  [ IsVector and IsFFECollection, IsFrobeniusAutomorphism ],
  function( v, f )
    return MakeImmutable(List(v,x->x^f));
  end );

#cvec version
InstallOtherMethod( \^, "for a cvec/FFE vector and a Frobenius automorphism",
  [ IsCVecRep and IsFFECollection, IsFrobeniusAutomorphism ],
  function( v, f )
    return MakeImmutable(CVec(List(v,x->x^f),BaseField(v)));
  end );

#we think that this method is not needed, worse, should not be used.
#InstallOtherMethod( \^, 
#  "for a mutable FFE vector and a trivial Frobenius automorphism",
#  [ IsVector and IsFFECollection and IsMutable, IsMapping and IsOne ],
#  function( v, f )
#    return v;
#  end );

#cvec version
#InstallOtherMethod( \^, 
#  "for a mutable cvec/FFE vector and a trivial Frobenius automorphism",
#  [ IsCVecRep and IsFFECollection and IsMutable, IsMapping and IsOne ],
#  function( v, f )
#    return v;
#  end );
  
InstallOtherMethod( \^, 
  "for a mutable FFE vector and a trivial Frobenius automorphism",
  [ IsVector and IsFFECollection and IsMutable, IsMapping and IsOne ],
  function( v, f )
    return ShallowCopy(v);
  end );
  
#cvec version
InstallOtherMethod( \^, 
  "for a mutable cvec/FFE vector and a trivial Frobenius automorphism",
  [ IsCVecRep and IsFFECollection and IsMutable, IsMapping and IsOne ],
  function( v, f )
    return ShallowCopy(v);
  end );

#the next operations until the matrix section, the methods will become obsolete as soon as fining is cvec'ed.
  
InstallOtherMethod( \^, 
  "for a compressed GF2 vector and a Frobenius automorphism",
  [ IsVector and IsFFECollection and IsGF2VectorRep, IsFrobeniusAutomorphism ],
  function( v, f )
    local w;
    w := List(v,x->x^f);
    ConvertToVectorRepNC(w,2);
    return MakeImmutable(w);
  end );

InstallOtherMethod( \^, 
  "for a mutable compressed GF2 vector and a Frobenius automorphism",
  [ IsVector and IsFFECollection and IsGF2VectorRep and IsMutable, 
    IsFrobeniusAutomorphism ],
  function( v, f )
    local w;
    w := List(v,x->x^f);
    ConvertToVectorRepNC(w,2);
    return w;
  end );

InstallOtherMethod( \^, 
  "for a compressed GF2 vector and a trivial Frobenius automorphism",
  [ IsVector and IsFFECollection and IsGF2VectorRep, IsMapping and IsOne ],
  function( v, f )
    return v;
  end );

InstallOtherMethod( \^, 
  "for a mutable compressed GF2 vector and a trivial Frobenius automorphism",
  [ IsVector and IsFFECollection and IsGF2VectorRep and IsMutable, 
    IsMapping and IsOne ],
  function( v, f )
    return ShallowCopy(v);
  end );

InstallOtherMethod( \^, 
  "for a compressed 8bit vector and a Frobenius automorphism",
  [ IsVector and IsFFECollection and Is8BitVectorRep, IsFrobeniusAutomorphism ],
  function( v, f )
    local w;
    w := List(v,x->x^f);
    ConvertToVectorRepNC(w,Q_VEC8BIT(v));
    return MakeImmutable(w);
  end );

InstallOtherMethod( \^, 
  "for a mutable compressed 8bit vector and a Frobenius automorphism",
  [ IsVector and IsFFECollection and Is8BitVectorRep and IsMutable, 
    IsFrobeniusAutomorphism ],
  function( v, f )
    local w;
    w := List(v,x->x^f);
    ConvertToVectorRepNC(w,Q_VEC8BIT(v));
    return w;
  end );

InstallOtherMethod( \^, 
  "for a compressed 8bit vector and a trivial Frobenius automorphism",
  [ IsVector and IsFFECollection and Is8BitVectorRep, IsMapping and IsOne ],
  function( v, f )
    return v;
  end );

InstallOtherMethod( \^, 
  "for a mutable compressed 8bit vector and a trivial Frobenius automorphism",
  [ IsVector and IsFFECollection and Is8BitVectorRep and IsMutable, 
    IsMapping and IsOne ],
  function( v, f )
    return ShallowCopy(v);
  end );

#### matrix methods

InstallOtherMethod( \^, "for a FFE matrix and a Frobenius automorphism",
  [ IsMatrix and IsFFECollColl, IsFrobeniusAutomorphism ],
  function( m, f )
    return MakeImmutable(List(m,v->List(v,x->x^f)));
  end );

#cmat
InstallOtherMethod( \^, "for a FFE matrix and a Frobenius automorphism",
  [ IsCMatRep and IsFFECollColl, IsFrobeniusAutomorphism ],
  function( m, f )
    return MakeImmutable(CMat(List(m,v->v^f)));
  end );  
  
#InstallOtherMethod( \^, "for a FFE matrix and a Frobenius automorphism",
#  [ IsMatrix and IsFFECollColl, IsFrobeniusAutomorphism ],
#  function( m, f )
#    return MakeImmutable(List(m,v->List(v,x->x^f)));
#  end );
  
InstallOtherMethod( \^, "for a mutable FFE matrix and a Frobenius automorphism",
  [ IsMatrix and IsFFECollColl and IsMutable, IsFrobeniusAutomorphism ],
  function( m, f )
    return List(m,v->List(v,x->x^f));
  end );

#cmat
InstallOtherMethod( \^, "for a mutable FFE matrix and a Frobenius automorphism",
  [ IsCMatRep and IsFFECollColl and IsMutable, IsFrobeniusAutomorphism ],
  function( m, f )
    return CMat(List(m,v->v^f));
  end );

InstallOtherMethod( \^, "for a FFE matrix and a trivial Frobenius automorphism",
  [ IsMatrix and IsFFECollColl, IsMapping and IsOne ],
  function( m, f )
    return m;
  end );

#cmat
InstallOtherMethod( \^, "for a FFE matrix and a trivial Frobenius automorphism",
  [ IsCMatRep and IsFFECollColl and IsMutable, IsMapping and IsOne ],
  function( m, f )
    return ShallowCopy(m);
  end );

InstallOtherMethod( \^, 
  "for a mutable FFE matrix and a trivial Frobenius automorphism",
  [ IsMatrix and IsFFECollColl, IsMapping and IsOne ],
  function( m, f )
    return MutableCopyMat(m);
  end );

#cmat  
InstallOtherMethod( \^, "for a FFE matrix and a trivial Frobenius automorphism",
  [ IsCMatRep and IsFFECollColl , IsMapping and IsOne ],
  function( m, f )
    return MakeImmutable(ShallowCopy(m));
  end );

#the next matrix methods will become obsolete.

InstallOtherMethod( \^, 
  "for a compressed GF2 matrix and a Frobenius automorphism",
  [ IsMatrix and IsFFECollColl and IsGF2MatrixRep, IsFrobeniusAutomorphism ],
  function( m, f )
    local w,l,i;
    l := [];
    for i in [1..Length(m)] do
        w := List(m[i],x->x^f);
        ConvertToVectorRepNC(w,2);
        Add(l,w);
    od;
    ConvertToMatrixRepNC(l,2);
    return MakeImmutable(l);
  end );

InstallOtherMethod( \^, 
  "for a mutable compressed GF2 matrix and a Frobenius automorphism",
  [ IsMatrix and IsFFECollColl and IsGF2MatrixRep and IsMutable, 
    IsFrobeniusAutomorphism ],
  function( m, f )
    local w,l,i;
    l := [];
    for i in [1..Length(m)] do
        w := List(m[i],x->x^f);
        ConvertToVectorRepNC(w,2);
        Add(l,w);
    od;
    ConvertToMatrixRepNC(l,2);
    return l;
  end );

InstallOtherMethod( \^, 
  "for a compressed GF2 matrix and a trivial Frobenius automorphism",
  [ IsMatrix and IsFFECollColl and IsGF2MatrixRep, IsMapping and IsOne ],
  function( m, f )
    return m;
  end );

InstallOtherMethod( \^, 
  "for a mutable compressed GF2 matrix and a trivial Frobenius automorphism",
  [ IsMatrix and IsFFECollColl and IsGF2MatrixRep and IsMutable, 
    IsMapping and IsOne ],
  function( m, f )
    return MutableCopyMat(m);
  end );

InstallOtherMethod( \^, 
  "for a compressed 8bit matrix and a Frobenius automorphism",
  [ IsMatrix and IsFFECollColl and Is8BitMatrixRep, IsFrobeniusAutomorphism ],
  function( m, f )
    local w,l,i,q;
    l := [];
    q := Q_VEC8BIT(m[1]);
    for i in [1..Length(m)] do
        w := List(m[i],x->x^f);
        ConvertToVectorRepNC(w,q);
        Add(l,w);
    od;
    ConvertToMatrixRepNC(l,q);
    return MakeImmutable(l);
  end );

InstallOtherMethod( \^, 
  "for a mutable compressed 8bit matrix and a Frobenius automorphism",
  [ IsMatrix and IsFFECollColl and Is8BitMatrixRep and IsMutable, 
    IsFrobeniusAutomorphism ],
  function( m, f )
    local w,l,i,q;
    l := [];
    q := Q_VEC8BIT(m[1]);
    for i in [1..Length(m)] do
        w := List(m[i],x->x^f);
        ConvertToVectorRepNC(w,q);
        Add(l,w);
    od;
    ConvertToMatrixRepNC(l,q);
    return MakeImmutable(l);
  end );

InstallOtherMethod( \^, 
  "for a compressed 8bit matrix and a trivial Frobenius automorphism",
  [ IsMatrix and IsFFECollColl and Is8BitMatrixRep, IsMapping and IsOne ],
  function( m, f )
    return m;
  end );

InstallOtherMethod( \^, 
  "for a mutable compressed 8bit matrix and a trivial Frobenius automorphism",
  [ IsMatrix and IsFFECollColl and Is8BitMatrixRep and IsMutable, 
    IsMapping and IsOne ],
  function( m, f )
    return MutableCopyMat(m);
  end );


# CHECKED 6/09/11 jdb
#############################################################################
#O  \*( <a>, <b> )
# returns a*b, for IsProjGrpElWithFrob
## 
#made a change, added ^-1 on march 8 2007, J&J
InstallMethod( \*, "for two projective group element with Frobenious",
  [IsProjGrpElWithFrob and IsProjGrpElWithFrobRep,
   IsProjGrpElWithFrob and IsProjGrpElWithFrobRep],
  function( a, b )
    local el;
    el := rec( mat := a!.mat * (b!.mat^(a!.frob^-1)), fld := a!.fld, 
               frob := a!.frob * b!.frob );
    Objectify( ProjElsWithFrobType, el);
    return el;
  end );

# CHECKED 6/09/11 jdb
#############################################################################
#O  InverseSameMutability( <el> )
# returns el^-1, for IsProjGrpElWithFrob, keeps mutability.
## 
#found a bug 23/09/08 in st. andrews.
#J&J feel a great relief.
#C&P too.
#all after Max concluded that there was a big bug.
InstallMethod( InverseSameMutability, 
  "for a projective group element with Frobenius",
  [IsProjGrpElWithFrob and IsProjGrpElWithFrobRep],
  function( el )
    local m,f;
    f := el!.frob;
    m := rec( mat := (InverseSameMutability(el!.mat))^f, fld := el!.fld,
              frob := f^-1 );
    Objectify( ProjElsWithFrobType, m );
    return m;
  end );

# CHECKED 6/09/11 jdb
#############################################################################
#O  InverseMutable( <el> )
# returns mutable el^-1, for IsProjGrpElWithFrob
## 
InstallMethod( InverseMutable, 
  "for a projective group element with Frobenius",
  [IsProjGrpElWithFrob and IsProjGrpElWithFrobRep],
  function( el )
    local m,f;
    f := el!.frob;
    m := rec( mat := (InverseMutable(el!.mat))^f, fld := el!.fld,
              frob := f^-1 );
    Objectify( ProjElsWithFrobType, m );
    return m;
  end );

# CHECKED 6/09/11 jdb
#############################################################################
#O  OneImmutable( <el> )
# returns immutable one of the group of <el>
## 
InstallMethod( OneImmutable, "for a projective group element with Frobenius",
  [IsProjGrpElWithFrob and IsProjGrpElWithFrobRep],
  function( el )
    local o;
    o := rec( mat := OneImmutable( el!.mat ), fld := el!.fld,
              frob := el!.frob^0 );
    Objectify( ProjElsWithFrobType, o);
    return o;
  end );

# CHECKED 6/09/11 jdb
#############################################################################
#O  OneSameMutability( <el> )
# returns one of the group of <el> with same mutability
## 
InstallMethod( OneSameMutability, 
  "for a projective group element with Frobenius",
  [IsProjGrpElWithFrob and IsProjGrpElWithFrobRep],
  function( el )
    local o;
    o := rec( mat := OneImmutable( el!.mat ), fld := el!.fld,
              frob := el!.frob^0 );
    Objectify( ProjElsWithFrobType, o);
    return o;
  end );

###################################################################
# General methods to deal with projective groups.
# Construction of (projective) groups is done in the appropriate files
# that deal with geometries.
###################################################################

# 10 CHECKED 6/09/11 jdb
###################################################################
# View, print and display methods for projective groups 
# (without and with Frobenius)
###################################################################


#InstallMethod( ViewObj, 
#	"for a projective group",
#	[IsProjectivityGroup],
#	function( g )
#		Print("<projective group>");
#	end );
#
#InstallMethod( ViewObj, 
#	"for a trivial projective group",
#	[IsProjectivityGroup and IsTrivial],
#	function( g )
#		Print("<trivial projective group>");
#	end );
#
#InstallMethod( ViewObj, 
#	"for a projective group with gens",
#	[IsProjectivityGroup and HasGeneratorsOfGroup],
#	function( g )
#		local gens;
#		gens := GeneratorsOfGroup(g);
#		if Length(gens) = 0 then
#			Print("<trivial projective group>");
#		else
#			Print("<projective group with ",Length(gens),
 #             " generators>");
#		fi;
#	end );
#
#InstallMethod( ViewObj, 
#	"for a projective group with size",
#	[IsProjectivityGroup and HasSize],
#	function( g )
#		if Size(g) = 1 then
#			Print("<trivial projective group>");
#		else
#			Print("<projective group of size ",Size(g),">");
#		fi;
#	end );
#
#InstallMethod( ViewObj, 
#	"for a projective group with gens and size",
#	[IsProjectivityGroup and HasGeneratorsOfGroup and HasSize],
#	function( g )
#		local gens;
#		gens := GeneratorsOfGroup(g);
#		if Length(gens) = 0 then
#			Print("<trivial projective group>");
#		else
#			Print("<projective group of size ",Size(g)," with ",
 #             Length(gens)," generators>");
#		fi;
#	end );
	
InstallMethod( ViewObj,	
	"for a projective collineation group",
	[IsProjectiveGroupWithFrob],
	function( g )
		Print("<projective collineation group>");
	end );

InstallMethod( ViewObj, 
	"for a trivial projective collineation group",
	[IsProjectiveGroupWithFrob and IsTrivial],
	function( g )
		Print("<trivial projective collineation group>");
	end );

InstallMethod( ViewObj, 
	"for a projective collineation group with gens",
	[IsProjectiveGroupWithFrob and HasGeneratorsOfGroup],
	function( g )
		local gens;
		gens := GeneratorsOfGroup(g);
		if Length(gens) = 0 then
			Print("<trivial projective collineation group>");
		else
			Print("<projective collineation group with ",Length(gens),
              " generators>");
		fi;
	end );

InstallMethod( ViewObj, 
	"for a projective collineation group with size",
	[IsProjectiveGroupWithFrob and HasSize],
	function( g )
		if Size(g) = 1 then
			Print("<trivial projective collineation group>");
		else
			Print("<projective collineation group of size ",Size(g),">");
		fi;
	end );

InstallMethod( ViewObj, 
	"for a projective collineation group with gens and size",
	[IsProjectiveGroupWithFrob and HasGeneratorsOfGroup and HasSize],
	function( g )
		local gens;
		gens := GeneratorsOfGroup(g);
		if Length(gens) = 0 then
			Print("<trivial projective collineation group>");
		else
			Print("<projective collineation group of size ",Size(g)," with ",
              Length(gens)," generators>");
		fi;
	end );


###################################################################
# Some operations for projective groups (without and with frobenius automorphism)
###################################################################

# CHECKED 6/09/11 jdb
#############################################################################
#O  BaseField( <g> )
# returns the base field of the projective group <g>
## 


# ml 07/11/2012: I have taken out the view, print and display methods
# for projectivity groups, since these are also collineation groups in FinInG
#InstallMethod( BaseField, 
#	"for a projective group",
#	[IsProjectivityGroup],
#	function( g )
#		local f,gens;
#		if IsBound(g!.basefield) then
#			return g!.basefield;
#		fi;
#		if HasParent(g) then
#			f := BaseField(Parent(g));
#			g!.basefield := f;
#			return f;
#		fi;
 #   # Now start to investigate:
#		gens := GeneratorsOfGroup(g);
#		if Length(gens) > 0 then
#			g!.basefield := gens[1]!.fld;
#			return g!.basefield;
#		fi;
 #   # Now we have to give up:
#		Error("base field could not be determined");
#	end );

# CHECKED 6/09/11 jdb
#############################################################################
#O  BaseField( <g> )
# returns the base field of the projective collineation group <g>
## 
InstallMethod( BaseField, 
	"for a projective collineation group",
	[IsProjectiveGroupWithFrob],
	function( g )
		local f,gens,P;
		if IsBound(g!.basefield) then
			return g!.basefield;
		fi;
		# This if statement can cause an infinite loop!
		if HasParent(g) then  # JB 22/03/2014
			P := Parent(g);
			if IsBound(P!.basefield) then
				f := P!.basefield;
				g!.basefield := f;
				return f;
			fi;
		fi;
    # Now start to investigate:
		gens := GeneratorsOfGroup(g);
		if Length(gens) > 0 then
			g!.basefield := gens[1]!.fld;
			return g!.basefield;
		elif IsTrivial(g) then				#JB: 22/03/2014: The trivial group with no generators slipped through.
			g!.basefield := One(g)!.fld;
			return g!.basefield;
		fi;
				
    # Now we have to give up:
		Error("base field could not be determined");
	end );
	
# TO DO (22/03/2014): We ought to set the basefield on creating collineation groups.

# CHECKED 6/09/11 jdb
#############################################################################
#O  Dimension( <g> )
# returns the dimension of the projective group <g>. The dimension of this 
# group is defined as the vector space dimension of the projective space  
# of which <g> was defined as a projective group, or, in other words, as the 
# size of the matrices.
## 

# ml 07/11/2012: I have taken out the view, print and display methods
# for projectivity groups, since these are also collineation groups in FinInG

#InstallMethod( Dimension, 
#	"for a projective group",
#	[IsProjectivityGroup],
#	function( g )
#		local gens;
#		if HasParent(g) then
#			return Dimension(Parent(g));
#		fi;
 #   # Now start to investigate:
#		gens := GeneratorsOfGroup(g);
#		if Length(gens) > 0 then
#			return Length(gens[1]!.mat);
#		fi;
#		Error("dimension could not be determined");
#	end );

# CHECKED 6/09/11 jdb
#############################################################################
#O  Dimension( <g> )
# returns the dimension of the projective collineation group <g>. The dimension of this 
# group is defined as the vector space dimension of the projective space  
# of which <g> was defined as a projective group, or, in other words, as the 
# size of the matrices.
## 
InstallMethod( Dimension, 
	"for a projective collineation group",
	[IsProjectiveGroupWithFrob],
	function( g )
		local gens;
		if HasParent(g) and HasDimension(Parent(g)) then	#JB: 22/03/2014: Made sure the parent had a dimension first
			return Dimension(Parent(g));
		fi;
    # Now start to investigate:
		gens := GeneratorsOfGroup(g);
		if Length(gens) > 0 then
			return Length(gens[1]!.mat);
		elif IsTrivial(g) then				#JB: 22/03/2014: The trivial group with no generators slipped through.
		 	return Length(One(g)!.mat);
		fi;
		Error("dimension could not be determined");
	end );

# CHECKED 6/09/11 jdb
#############################################################################
#O  OneImmutable( <g> )
# returns an immutable one of the projectivity group <g>
## 
# ml 07/11/2012: I have taken out the view, print and display methods
# for projectivity groups, since these are also collineation groups in FinInG

#InstallMethod( OneImmutable, 
#	"for a projective group",
#	# was: [IsGroup and IsProjectivityGroup], I think might be
#	[IsProjectivityGroup],
#	function( g )
#		local gens, o;
#		gens := GeneratorsOfGroup(g);
#		if Length(gens) = 0 then
#			if HasParent(g) then
#				gens := GeneratorsOfGroup(Parent(g));
#			else
#				Error("sorry, no generators, no one");
#			fi;
#		fi;
#		o := rec( mat := OneImmutable( gens[1]!.mat ), fld := gens[1]!.fld );
#		Objectify( NewType(FamilyObj(gens[1]), IsProjGrpElRep), o );
#		return o;
#	end );

# CHECKED 6/09/11 jdb
#############################################################################
#O  OneImmutable( <g> )
# returns immutable one of the projective collineation group <g>
## 
InstallMethod( OneImmutable, 
	"for a projective collineation group",
	# was [IsGroup and IsProjectiveGroupWithFrob], I think might be
	[IsProjectiveGroupWithFrob],
	function( g )
		local gens, o;
		gens := GeneratorsOfGroup(g);
		if Length(gens) = 0 then
			if HasParent(g) and HasOneImmutable(Parent(g)) then	# JB 22/03/2014
				gens := GeneratorsOfGroup(Parent(g));
			else
				Error("sorry, no generators, no one");
			fi;
		fi;
		o := rec( mat := OneImmutable( gens[1]!.mat ), fld := BaseField(g),
				frob := gens[1]!.frob^0 );
		Objectify( ProjElsWithFrobType, o);
		return o;
	end );

###################################################################
# All about actions. But low level stuff. In each appropriate files
# for particular geometries, user-friendly action functions must be
# provided.
###################################################################

# CHECKED 6/09/11 jdb
#############################################################################
#P  CanComputeActionOnPoints( <g> )
# is set true if we consider the computation of the action feasible.
# for projective groups.
##
# ml 07/11/2012: I have taken out the view, print and display methods
# for projectivity groups, since these are also collineation groups in FinInG
 
#InstallMethod( CanComputeActionOnPoints, 
#	"for a projective group",
#	[IsProjectivityGroup],
#	function( g )
#		local d,q;
#		d := Dimension( g );
#		q := Size( BaseField( g ) );
#		if (q^d - 1)/(q-1) > FINING.LimitForCanComputeActionOnPoints then
#			return false;
#		else
#			return true;
#		fi;
#	end );
  
# CHECKED 6/09/11 jdb
#############################################################################
#P  CanComputeActionOnPoints( <g> )
# is set true if we consider the computation of the action feasible.
# for projective collineation groups.
## 
InstallMethod( CanComputeActionOnPoints, 
	"for a projective group with frob",
	[IsProjectiveGroupWithFrob],
	function( g )
		local d,q;
		d := Dimension( g );
		q := Size( BaseField( g ) );
		if (q^d - 1)/(q-1) > FINING.LimitForCanComputeActionOnPoints then
			return false;
		else
			return true;
		fi;
	end );
  
###################################################################
# Action functions for projective groups and projective collineation
# groups. The four action functions here are low level and not 
# intended for the user.
###################################################################

# CHECKED 6/09/11 jdb
#############################################################################
#F  OnProjPoints( <line>, <el> )
# computes <line>^<el> where this action is the "natural" one, and <line> represents
# a projective point. We called it line, since this functions relies on the Gap action
# function OnLines, which is the "natural" actions of a matrix on a vector line.
# Important: despite its natural name, this function is *not* intended for the user.
# <line>: just a row vector, representing a vector line
# <el>: a projective group element (so a projectivity, *not* a projective collineation element.
# normalizing the result is handled by the Gap function OnLines. This function assumes that 
# the input vector is also normalized (says the GAP manual). This became reality on september 19, 2011 :-)
## 
InstallGlobalFunction( OnProjPoints,
	function( line, el )
		return OnLines(line,el!.mat);
	end );

# CHECKED 6/09/11 jdb
# CHANGED 19/09/2011 jdb + ml
# can be shortened if you use that OnLines normalizes the result.
#############################################################################
#F  OnProjPointsWithFrob( <line>, <el> )
# computes <line>^<el> where this action is the "natural" one, and <line> represents
# a projective point. This function relies on the GAP function OnLines (see above),
# the result is hence normalized
# Important: despite its natural name, this function is *not* intended for the user.
# <line>: just a row vector, representing a projective point.
# <el>: a projective collineation element 
## 
InstallGlobalFunction( OnProjPointsWithFrob,
  function( line, el )
    local vec,c;
#    vec := OnPoints(line,el!.mat)^el!.frob;
    vec := OnLines(line,el!.mat)^el!.frob;
    #c := PositionNonZero(vec);
    #if c <= Length( vec )  then
#        if not(IsMutable(vec)) then
#		 vec := ShallowCopy(vec);
#        fi;
#        MultRowVector(vec,Inverse( vec[c] ));
#    fi;
    return vec;
	end );

# CHECKED 6/09/11 jdb
# CHANGED 19/09/2011 jdb + ml
# CHANGED 20/09/2011 jdb + ml (SemiEchelonMat -> EchelonMat).
#############################################################################
#F  OnProjSubspacesNoFrob( <subspace>, <el> )
# computes <subspace>^<el> where this action is the "natural" one, and <subspace> represents
# a projective subspace. This function relies on the GAP action function
# OnSubspacesByCanonicalBasis. This function assumes as arguments a list (mat) of linearly 
# independent row vectors, in Hermite normal form (triangulied), and return the 
# mat*<el> in Hermite normal form. To be used in user action functions, we EchelonMat it, so that
# the output can be used directly in a Wrap.
# Important: despite its natural name, this function is *not* intended for the user.
# <el>: a projective group element (so a projectivity, *not* a projective collineation element.
## 
InstallGlobalFunction( OnProjSubspacesNoFrob,
	function( matrix, el )
		# matrix is a matrix containing the basis vectors of some subspace.
		local mat;
		mat := TriangulizeMat(OnSubspacesByCanonicalBasis(matrix,el!.mat));
		return mat;
		#return EchelonMat(OnSubspacesByCanonicalBasis(matrix,el!.mat)).vectors;
	end );

# CHECKED 6/09/11 jdb
# CHANGED 19/09/2011 jdb + ml
# CHANGED 20/09/2011 jdb + ml (SemiEchelonMat -> EchelonMat).
#############################################################################
#F  OnProjSubspacesWithFrob( <subspace>, <el> )
# computes <subspace>^<el> where this action is the "natural" one, and <subspace> represents
# a projective subspace. This function relies on the GAP action function
# OnRight, which computs the action of a matrix on a sub vector space. Here we have to rely on 
# OnRight, and so we have to EchelonMat afterwards.
# Important: despite its natural name, this function is *not* intended for the user.
# <el>: a projective group element (so a projectivity, *not* a projective collineation element.
##
InstallGlobalFunction( OnProjSubspacesWithFrob,
  function( matrix, el )
	# matrix is a matrix containing the basis vectors of some subspace.
    local vec,c;
    vec := OnRight(matrix,el!.mat)^el!.frob;
    if not(IsMutable(vec)) then
        vec := MutableCopyMat(vec);
    fi;
    TriangulizeMat(vec);
	#return EchelonMat(vec).vectors;
	return vec;
  end );

###################################################################
# Higher level user friendly operations to compute the action of a
# projective and a projective collineation group. These operations
# are based on the low level functions above.
###################################################################

# CHECKED 6/09/11 jdb
#############################################################################
#P  ActionOnAllProjPoints( <g> )
# returns the action of the projective group <g> on the projective points
# of the underlying projective space.
## 

# ml 07/11/2012: I have taken out the view, print and display methods
# for projectivity groups, since these are also collineation groups in FinInG

#InstallMethod( ActionOnAllProjPoints, 
#	"for a projective group",
#	[ IsProjectivityGroup ],
#	function( pg )
#		local a,d,f,orb;
#		f := BaseField(pg);
#		d := Dimension(pg);
#		orb := MakeAllProjectivePoints(f,d);
#		a := ActionHomomorphism(pg,orb,OnProjPoints,"surjective");
#		SetIsInjective(a,true);
#		return a;
#	end );

# CHECKED 6/09/11 jdb
# cvec change 19/3/14
#############################################################################
#O  ActionOnAllProjPoints( <g> )
# returns the action of the projective collineation group <g> on the projective points
# of the underlying projective space.
## 
InstallMethod( ActionOnAllProjPoints, 
    "for a projective collineation group",
	[ IsProjectiveGroupWithFrob ],
	function( pg )
		local a,d,f,o,on,orb,v,zero, m, j;
		Info(InfoFinInG,4,"Using ActionOnAllProjPoints");
		f := BaseField(pg);
		d := Dimension(pg);
		o := One(f);
		on := One(pg);
		v := ZeroMutable(on!.mat[1]);
		v[1] := o;
		#orb := Orbit(pg,v,OnProjPointsWithFrob);
		#orb := Orb(pg,v,OnProjPointsWithFrob);
		zero := Zero(f);
		orb := [];
		for m in f^d do
			j := PositionNot(m, zero);
		if j <= d and m[j] = o then
			Add(orb, CVec(m,f)); #here is the change.
		fi;
		od;
		a := ActionHomomorphism(pg,orb,OnProjPointsWithFrob,"surjective");
		SetIsInjective(a,true);
		return a;
	end );

###################################################################
# NiceMonomorphism material for projective and projective collineation 
# groups. 
###################################################################

# CHECKED 6/09/11 jdb
#############################################################################
#F  NiceMonomorphismByOrbit( <g>, <x>, <op>, <orblen> )
# <g>: projective groups; <x>: an element; <op> operation suitable for <x> and <g>
# important: this functions relies on the GenSS package. 
# As you can probably guess: this is not intended for a user.
##
InstallGlobalFunction( NiceMonomorphismByOrbit,
  function(g,x,op,orblen)
    # g a funny group, Size attribute set!
    # x an element
    # op an operation suitable for x and g
    # It is guaranteed that g acts faithfully on the orbit.
    local cand,h,iso,nr,orb,pgens;
    if orblen <> false then
		orb := Orb(g,x,op,rec(orbsizelimit := orblen, hashlen := 2*orblen,
                              storenumbers := true));
        Enumerate(orb);
    else
        orb := Orb(g,x,op,rec(storenumbers := true));
        Enumerate(orb);
    fi;
	pgens := ActionOnOrbit(orb,GeneratorsOfGroup(g));
    h := GroupWithGenerators(pgens);
    SetSize(h,Size(g));
    nr := Minimum(100,Length(orb));
    cand := rec( points := orb{[1..nr]}, used := 0,
                 ops := ListWithIdenticalEntries(nr,op) );
	iso := GroupHomomorphismByImagesNCStabilizerChain(g,h,pgens,
              rec( Cand := cand ), rec( ) );
    SetIsBijective(iso,true);
    return iso;
  end );

# CHECKED 6/09/11 jdb
#############################################################################
#F  NiceMonomorphismByDomain( <g>, <dom>, <op> )
# <g>: projective groups, size attribute *set* ; <x>: an element; 
# <op> operation suitable for <x> and <g>
# important: this functions relies on the GenSS package. 
##
InstallGlobalFunction( NiceMonomorphismByDomain,
  function(g,dom,op)
    # g a funny group, Size attribute set!
    # dom an orbit of g
    # op the operation suitable for x and g
    # It is guaranteed that g acts faithfully on the orbit.
    local cand,gens,h,ht,i,iso,nr,pgens;
    ht := NewHT(dom[1],Length(dom)*2);
    for i in [1..Length(dom)] do
      AddHT(ht,dom[i],i);
    od;
    pgens := [];
    gens := GeneratorsOfGroup(g);
    for i in [1..Length(gens)] do
        Add(pgens,PermList( List([1..Length(dom)],
                                 j->ValueHT(ht,op(dom[j],gens[i]))) ));
    od;
    h := GroupWithGenerators(pgens);
    SetSize(h,Size(g));
    nr := Minimum(100,Length(dom));
    cand := rec( points := dom{[1..nr]}, used := 0,
                 ops := ListWithIdenticalEntries(nr,op) );
    iso := GroupHomomorphismByImagesNCStabilizerChain(g,h,pgens,
              rec( Cand := cand ), rec( ) );
    SetIsBijective(iso,true);
    return iso;  
  end );

## Is this operation ever used? I think it obselete (JB: 26/09/08)
## John is probably right. A grep of SetAsNiceMono on all *.gi files gives only the InstallMethod( parts. (jdb 6/9/11). 

# ml 07/11/2012: I have taken out the view, print and display methods
# for projectivity groups, since these are also collineation groups in FinInG
#InstallMethod( SetAsNiceMono, "for a projective group and an action hom",
#  [IsProjectivityGroup, IsGroupHomomorphism and IsInjective],
#  function( pg, a )
#    SetNiceMonomorphism(pg,a);
#    SetNiceObject(pg,Image(a));
#  end );
  
InstallMethod( SetAsNiceMono, 
  "for a projective collineation group and an action hom",
  [IsProjectiveGroupWithFrob, IsGroupHomomorphism and IsInjective],
  function( pg, a )
    SetNiceMonomorphism(pg,a);
    SetNiceObject(pg,Image(a));
  end );

# CHECKED 6/09/11 jdb
# cvec change 19/3/14
#############################################################################
#O  NiceMonomorphism( <pg> )
# <pg> is a projective group. This operation returns a nice monomorphism.
##
InstallMethod( NiceMonomorphism, 
	"for a projective group (feasible case)",
	[IsProjectivityGroup and CanComputeActionOnPoints and IsHandledByNiceMonomorphism], 
	50,
	function( pg )
	local hom, dom,bf;
	Info(InfoFinInG,4,"Using NiceMonomorphism for proj. group (feasible)");
	bf := BaseField(pg);
    dom := MakeAllProjectivePoints( bf, Dimension(pg) - 1);
    #dom := List(dom,x->CVec(x,bf)); # (ml 31/03/14) MakeAllProjectivePoints produces already cvecs 
	if FINING.Fast then
	   hom := NiceMonomorphismByDomain( pg, dom, OnProjPointsWithFrob );
    else 
       hom := ActionHomomorphism(pg, dom, OnProjPointsWithFrob, "surjective");    
       SetIsBijective(hom, true);
    fi;
    return hom;
	end );
  
# CHECKED 6/09/11 jdb
# cvec change 19/3/14
#############################################################################
#O  NiceMonomorphism( <pg> )
# <pg> is a projective group. This operation returns a nice monomorphism.
##
InstallMethod( NiceMonomorphism, 
	"for a projective group (nasty case)",
	[IsProjectiveGroupWithFrob and IsHandledByNiceMonomorphism], 
	50,
	function( pg )
		local can, dom, hom, bf;
		Info(InfoFinInG,4,"Using NiceMonomorphism for proj. group (nasty)");
		bf := BaseField(pg);
		can := CanComputeActionOnPoints(pg);
		if not(can) then
			Error("action on projective points not feasible to calculate");
		else
			dom := MakeAllProjectivePoints( BaseField(pg), Dimension(pg) - 1 );
		    #dom := List(dom,x->CVec(x,bf)); # (ml 31/03/14) MakeAllProjectivePoints produces already cvecs 
			if FINING.Fast then
				hom := NiceMonomorphismByDomain( pg, dom, OnProjPointsWithFrob );
			else 
				hom := ActionHomomorphism(pg, dom, OnProjPointsWithFrob, "surjective");    
				SetIsBijective(hom, true);
			fi;
			return hom; 
		fi;
	end );

# CHECKED 6/09/11 jdb
#############################################################################
#O  NiceMonomorphism( <pg> )
# <pg> is a projective collineation group. This operation returns a nice monomorphism.
##
InstallMethod( NiceMonomorphism, 
	"for a projective collineation group (feasible case)",
	[IsProjectiveGroupWithFrob and CanComputeActionOnPoints and
	IsHandledByNiceMonomorphism], 1,
	function( pg )
		return ActionOnAllProjPoints( pg );
	end );
  
# CHECKED 6/09/11 jdb
#############################################################################
#O  NiceMonomorphism( <pg> )
# <pg> is a projective collineation group. This operation returns a nice monomorphism.
##
InstallMethod( NiceMonomorphism, 
	"for a projective collineation group (nasty case)",
	[IsProjectiveGroupWithFrob and IsHandledByNiceMonomorphism], 50,
	function( pg )
		local can;
		can := CanComputeActionOnPoints(pg);
		if not(can) then
			Error("action on projective points not feasible to calculate");
		else
			return ActionOnAllProjPoints( pg );
		fi;
	end );

## FindBasePointCandidates: are these methods also obsolete in the sense that they are never used? No! They are used in GenSS :-)

#############################################################################
#O  FindBasePointCandidates( <g>, <opt>, <i> )
# <pg> is a projective collineation group. <op> and <i> are arguments required
# to be used in GenSS.
##
InstallMethod( FindBasePointCandidates,
  "for a projective group",
  [IsProjectivityGroup,IsRecord,IsInt],
  function(g,opt,i)
    local cand,d,f,gens;
    if IsBound(g!.basepointcandidates) and
       g!.basepointcandidates.used < Length(g!.basepointcandidates.points) then
        return g!.basepointcandidates;
    fi;
    gens := GeneratorsOfGroup(g);
    if IsObjWithMemory(gens[1]) then
        f := BaseField(gens[1]!.el);
        d := Length(gens[1]!.el!.mat);
    else
        f := BaseField(g);
        d := Dimension(g);
    fi;
    cand := rec( points := NewMatrix(IsCMatRep, f,d, IdentityMat(d,f)) , used := 0,
                 ops := ListWithIdenticalEntries(d,OnProjPoints) );
    return cand;
  end );

#############################################################################
#O  FindBasePointCandidates( <g>, <opt>, <i> )
# <pg> is a projective collineation group. <op> and <i> are arguments required
# to be used in GenSS.
##
InstallMethod( FindBasePointCandidates,
  "for a projective collineation group",
  [IsProjectiveGroupWithFrob,IsRecord,IsInt],
  function(g,opt,i)
    local cand,d,f,gens;
    if IsBound(g!.basepointcandidates) and
       g!.basepointcandidates.used < Length(g!.basepointcandidates.points) then
        return g!.basepointcandidates;
    fi;
    gens := GeneratorsOfGroup(g);
    if IsObjWithMemory(gens[1]) then
        f := BaseField(gens[1]!.el);
        d := Length(gens[1]!.el!.mat);
    else
        f := BaseField(g);
        d := Dimension(g);
    fi;
    cand := rec( points := NewMatrix(IsCMatRep, f,d, IdentityMat(d,f)), used := 0,
                 ops := ListWithIdenticalEntries(d,OnProjPointsWithFrob) );
    if d > 1 then
        Add(cand.points,ShallowCopy(cand.points[1]));
        Add(cand.ops,OnProjPointsWithFrob);
        cand.points[d+1][2] := PrimitiveRoot(f);
    fi;
    return cand;
  end );


#############################################################################
#O  FindBasePointCandidates( <g>, <opt>, <i> )
# <pg> is a projective collineation group. <opt>, <i>, <parentS> are arguments 
# required to be used in GenSS.
##
InstallMethod( FindBasePointCandidates,
  "for a projective collineation group",
  [IsProjectiveGroupWithFrob,IsRecord,IsInt,IsObject],
#
# We need a four-argument version of this method for recent versions of GenSS.
# We don't use "parentS" at all here.
#
  function(g,opt,i,parentS)
    local cand,d,f,j,gens;
    if IsBound(g!.basepointcandidates) and
       g!.basepointcandidates.used < Length(g!.basepointcandidates.points) then
        return g!.basepointcandidates;
    fi;
    gens := GeneratorsOfGroup(g);
    if IsObjWithMemory(gens[1]) then
        f := BaseField(gens[1]!.el);
        d := Length(gens[1]!.el!.mat);
    else
        f := BaseField(g);
        d := Dimension(g);
    fi;
    cand := rec( points := NewMatrix(IsCMatRep, f,d, IdentityMat(d,f)), used := 0,
                 ops := ListWithIdenticalEntries(d,OnProjPointsWithFrob) );
    if d > 1 then
        for j in [2..d] do
            Add(cand.points,ShallowCopy(cand.points[1]));
            Add(cand.ops,OnProjPointsWithFrob);
            cand.points[d+j-1][j] := PrimitiveRoot(f);
        od;
    fi;
    return cand;
  end );


#################################################
# Our classical groups:
#################################################


#############################################################################
# Part I: methods for canonical gram matrices and canonical quadratic forms.
# these methods are not intended for the user.
#############################################################################

# CHECKED 21/09/11 jdb
#############################################################################
#O  CanonicalGramMatrix( <type>, <d>, <f> )
## Constructs the canonical gram matrix to construct the canonical 
## forms used in FinInG. See Appendix for exact information on these forms
## and there matrices.
##
InstallMethod( CanonicalGramMatrix, 
	"for a string, an integer, and a field",
	[IsString, IsPosInt, IsField],
	function( type, d, f )
		local one, q, m, i, t, x, w, p;
		one := One( f );
		q := Size(f);

      # Symplectic Gram matrix
		if type = "symplectic" then    
			if IsOddInt(d) then
				Error( "the dimension <d> must be even" );
			fi;    
			m := List( 0 * IdentityMat(d, f), ShallowCopy );
			for i  in [ 1 .. d/2 ]  do
				m[2*i][2*i-1] := -one;
				m[2*i-1][2*i] := one;
			od;
    
      # Unitary Gram matrix      
		elif type = "hermitian" then
			if IsOddInt(PrimePowersInt(Size(f))[2]) then
				Error("field order must be a square");
			fi;
			m := IdentityMat(d, f);
        
      # Orthogonal Gram matrix
		elif type = "hyperbolic" then
			m := List( 0*IdentityMat(d, f), ShallowCopy );
			p := Characteristic(f);
			if IsOddInt(p) and (p + 1) mod 4 = 0 then
				w := one * ((p + 1) / 2);
			else
				w := one;
			fi; 
			for i  in [ 1 .. d/2 ]  do
				m[2*i-1][2*i] := w;
				m[2*i][2*i-1] := w;
			od;
		elif type = "elliptic" then   
			m := List( 0*IdentityMat(d, f), ShallowCopy );
			p := Characteristic(f);
      ## if q is congruent to 5,7 mod 8, then #wrong comment?
      ## the anisotropic part is the primitive root.
			if q mod 4 in [1,2] then 
				t := Z(q);
			else
				t := one;
			fi;
			m{[1,2]}{[1,2]} := [ [ 1, 0 ], [ 0, t ] ] * one;

			if IsOddInt(p) then
				w := one * ((p + 1) / 2);
			else
				w := one;
			fi; 
			for i in [ 2 .. d/2 ]  do
				m[2*i-1][2*i] := w;
				m[2*i][2*i-1] := w;
			od;
		elif type = "parabolic" then 
			m := List( 0*IdentityMat(d, f), ShallowCopy );          
			p := Characteristic(f);
			if IsOddInt(p) then
         ## if q is congruent to 5,7 mod 8, then
         ## the anisotropic part is the primitive root
         ## of the prime subfield.
				if q mod 8 in [5,7] then 
					t := Z(p);
				else
					t := one;
				fi;
				m[1][1] := t;
				w := t * ((p + 1) / 2);
			else
				w := one;
			fi; 
			for i in [ 1 .. (d-1)/2 ]  do
				m[2*i][2*i+1] := w;
				m[2*i+1][2*i] := w;
			od;
		else Error( "type is unknown or not implemented" );
		fi;

    ##  We should return a compressed matrix in order that
    ##  our computations are efficient

		ConvertToMatrixRep( m, f );
		return m;
	end );

# CHECKED 21/09/11 jdb
#############################################################################
#O  CanonicalQuadraticForm( <type>, <d>, <f> )
## Constructs the canonical gram matrix to construct the canonical quadratic
## forms used in FinInG. See Appendix for exact information on these forms
## and there matrices.
####
InstallMethod( CanonicalQuadraticForm, 
	"for a string, an integer and a field",
	[IsString, IsPosInt, IsField],
	function( type, d, f )
		local m, one, q, p, j, x, R;
		one := One( f );
        if type = "hyperbolic" then
			m := MutableCopyMat(0 * IdentityMat(d, f));
			for j in [ 1 .. d/2 ]  do
				m[ 2*j-1 ][ 2*j ] := one;
			od;
		elif type = "elliptic" then
			m := MutableCopyMat(0 * IdentityMat(d, f));
			m[1][1] := one;
			m[2][1] := one; 
			m[d][d-1] := one;
			for j in [ 2 .. d/2-1 ]  do
				m[ 2*j-1 ][ 2*j ] := one;
			od;
			p := Characteristic(f);
			q := Size(f);
			if IsOddInt(Log(q, p)) then
				m[2][2] := one;
			else
				R := PolynomialRing( f, 1 );
				x := Indeterminate( f );
				m[2][2] := Z(q)^First( [ 0 .. q-2 ], u -> 
					Length( Factors( R, x^2+x+PrimitiveRoot( f )^u ) ) = 1 );         
			fi;
		elif type = "parabolic" then
			m := MutableCopyMat(0 * IdentityMat(d, f));
			m[1][1] := one;
			for j in [ 1 .. (d-1)/2 ]  do
				m[ 2*j+1 ][ 2*j ] := one;
			od;
		else Error( "type is unknown or not implemented" );
		fi;

    ##  We should return a compressed matrix in order that
    ##  our computations are efficient

		ConvertToMatrixRep( m, f );
		return m;
	end );


#############################################################################
# Part II: constructor methods.
#############################################################################

###################################################################################
#
#  JB 20/11/2011:  
#  I've done some rigorous testing, and the following work very well:
#    all groups of SymplecticSpace
#    all groups of HyperbolicQuadric
#    all groups of HermitianPolarSpace (hard to check though, things get big fast)
#    all groups of EllipticQuadric (I've recently fixed a bug)
#    all groups of ParabolicQuadric (I've recently fixed a bug)
#
# I guess we could run more tests for higher dimensions and field orders, but I'm
# reasonably confident that it all works well. For example, I tested groups
# of the parabolic quadric up to q^d = 8^6 (d is the projective dimension here).
#    
###################################################################################

#####################################################################
# Isometry groups. In this order: PSO, PGO, PSU, PGU, PSp, and PGSp
#####################################################################

###### Orthogonal groups ######

#############################################################################
#O  SOdesargues( <type>, <d>, <f> )
## returns the projective special orthogonal group, as a projective collineation group.
## The generators of the group are the projective collineation elements that are 
## represented by the matrices that generate SO(type,d,q). The latter is available in GAP
##
InstallMethod( SOdesargues, 
  "for an integer, a positive integer, and a finite field",
  [IsInt, IsPosInt, IsField and IsFinite],
    function(i, d, f)
    local s, m, frob, b, gens, g, q;
    if i = -1 then s := "elliptic";
    elif i = 0 then s := "parabolic";
    elif i = 1 then s := "hyperbolic";
    fi;
    q := Size(f);
    if IsEvenInt(q) then
      m := InvariantQuadraticForm(SO(i,d,q))!.matrix;
      b := BaseChangeOrthogonalQuadratic(m, f)[1];
    else 
      m := InvariantBilinearForm(SO(i,d,q))!.matrix;
      b := BaseChangeOrthogonalBilinear(m, f)[1]; 
    fi; 
    frob := FrobeniusAutomorphism(f);
 
    ## new group elements: x -> b^-1 x b (conjugation by base change)
    ## preserves form... (b x b^-1) (b m b^T) (b x b^-1)^T = x m x^T

    gens := GeneratorsOfGroup( SO(i,d,q) );
    gens := List( gens, y -> b * y * b^-1);
    gens := ProjElsWithFrob( List(gens, x -> [x,frob^0]), f );
    g := GroupWithGenerators( gens );
    SetName( g, Concatenation("PSO(",String(i),",",String(d),",",String(q),")") );
    if i = 0 then 
       SetSize( g, Size(SO(i, d, q)) );  ##/ 2); This might be a mistake in Kleidman and Liebeck!
    else
       SetSize( g, Size(SO(i, d, q)) / GCD_INT(2, q-1) ); 
    fi;

    return g;
  end );

#############################################################################
#O  GOdesargues( <type>, <d>, <f> )
## returns the projective general orthogonal group, as a projective collineation group.
## The generators of the group are the projective collineation elements that are 
## represented by the matrices that generate GO(type,d,q). The latter is available in GAP
##
InstallMethod( GOdesargues, [IsInt, IsPosInt, IsField and IsFinite],
  function(i, d, f)
    local m, frob, b, gens, s, g, q;
    if i = -1 then s := "elliptic";
    elif i = 0 then s := "parabolic";
    elif i = 1 then s := "hyperbolic";
    fi;
    q := Size(f);
    if IsEvenInt(q) then
      m := InvariantQuadraticForm(GO(i,d,q))!.matrix;
      b := BaseChangeOrthogonalQuadratic(m, f)[1];
    else 
      m := InvariantBilinearForm(SO(i,d,q))!.matrix;   
      b := BaseChangeOrthogonalBilinear(m, f)[1]; 
    fi; 
    frob := FrobeniusAutomorphism(f);

    ## new group elements: x -> b x b^-1 (conjugation by base change)
    ## preserves form... (b x b^-1) (b m b^T) (b x b^-1)^T = x m x^T

    gens := GeneratorsOfGroup( GO(i,d,q) );
    gens := List( gens, y -> b * y * b^-1);
    gens := ProjElsWithFrob( List(gens, x -> [x,frob^0]), f );
    g := GroupWithGenerators( gens );
    SetName( g, Concatenation("PGO(",String(i),",",String(d),",",String(q),")") );
    SetSize( g, Size(GO(i, d, q)) / GCD_INT(2,q-1) ); 
    return g;
  end );

###### Unitary groups ######

#############################################################################
#O  SUdesargues( <type>, <d>, <f> )
## returns the projective special unitary group, as a projective collineation group.
## The generators of the group are the projective collineation elements that are 
## represented by the matrices that generate SU(type,d,q). The latter is available in GAP
##
InstallMethod( SUdesargues, [IsPosInt, IsField and IsFinite],
  function(d, f)
    local m, frob, b, gens, g, sqrtq;
    sqrtq := Sqrt(Size(f));
    m := InvariantSesquilinearForm(SU(d,sqrtq))!.matrix;        
    frob := FrobeniusAutomorphism(f);
    b := BaseChangeHermitian(m, f)[1];
    ## new group elements: x -> b x b^-1 (conjugation by base change)
    ## preserves form... (b x b^-1) (b m b^Tfrob) (b x b^-1)^Tfrob = x m x^Tfrob
    gens := GeneratorsOfGroup( SU(d,sqrtq) );
    gens := List( gens, y -> b * y * b^-1);
    gens := ProjElsWithFrob( List(gens, x -> [x,frob^0]), f );
    g := GroupWithGenerators( gens );
    SetName( g, Concatenation("PSU(",String(d),",",String(sqrtq),"^2)") );
    SetSize( g, Size( SU(d, sqrtq) ) / GCD_INT(sqrtq+1,d)  );
    return g;
  end );

#############################################################################
#O  GUdesargues( <type>, <d>, <f> )
## returns the projective general unitary group, as a projective collineation group.
## The generators of the group are the projective collineation elements that are 
## represented by the matrices that generate GU(type,d,q). The latter is available in GAP
##
InstallMethod( GUdesargues, [IsPosInt, IsField and IsFinite],
  function(d, f)
    local m, frob, b, gens, g, sqrtq;
    sqrtq := Sqrt(Size(f));
    m := InvariantSesquilinearForm(GU(d,sqrtq))!.matrix;      
    frob := FrobeniusAutomorphism(f);
    b := BaseChangeHermitian(m, f)[1];
    ## new group elements: x -> b x b^-1 (conjugation by base change)
    gens := GeneratorsOfGroup( GU(d,sqrtq) );
    gens := List( gens, y -> b * y * b^-1);
    gens := ProjElsWithFrob( List(gens, x -> [x,frob^0]), f );
    g := GroupWithGenerators( gens );
    SetName( g, Concatenation("PGU(",String(d),",",String(sqrtq),"^2)") );
    SetSize( g, Size( GU(d, sqrtq) ) / (sqrtq+1) );
    return g;
  end );

###### Symplectic groups ######

#############################################################################
#O  Spdesargues( <d>, <f> )
## returns the projective special symplectic group, as a projective collineation group.
## The generators of the group are the projective collineation elements that are 
## represented by the matrices that generate Sp(d,q). The latter is available in GAP
##
InstallMethod( Spdesargues, [IsPosInt, IsField and IsFinite],
  function(d, f)
    local m, frob, b, gens, g, q, sp;
    q := Size(f);
    m := InvariantBilinearForm(Sp(d,q))!.matrix;   
    b := BaseChangeSymplectic(m, f)[1];  ## change made after new forms code
    frob := FrobeniusAutomorphism(f);
    ## new group elements: x -> b x b^-1 (conjugation by base change)
    sp := Sp(d,q);
    gens := GeneratorsOfGroup( sp );
    gens := List( gens, y -> b * y * b^-1);
    gens := ProjElsWithFrob( List(gens, x -> [x,frob^0]), f );
    g := GroupWithGenerators( gens );
    SetName( g, Concatenation("PSp(",String(d),",",String(q),")") );
    SetSize( g, Size( sp )/GCD_INT(2, q-1) );
    return g;
  end );

#############################################################################
#O  GeneralSymplecticGroup( <d>, <f> )
## returns the general symplectic group. See the internal comment here why
## we add this function.
##
InstallMethod( GeneralSymplecticGroup, [IsPosInt, IsField and IsFinite],
  function(d, f)

## The command "Sp" in the GAP library returns the symplectic 
## isometry group (see classical.gi). However, in odd characteristic,
## the symplectic isometries have index 2 in the symplectic similarity
## group. The preimage in the matrix group of the symplectic 
## similarities is the isometries extended by the cyclic
## group generated by the map which simply multiplies one half
## of the symplectic basis by the primitive element of the field
## and leaves the other half alone. 
  
  local sp, gens, z, delta, i, g, q;
  q := Size(f);
  if IsOddInt(d) then 
     Error("dimension must be an even");
  fi;
  sp := Sp(d, q);
  gens := GeneratorsOfGroup(sp);
  z := PrimitiveRoot( f );
  delta := ShallowCopy(IdentityMat(d, f));
  for i in [1..d/2] do delta[i][i] := z; od;
  gens := Concatenation(gens, [delta]);
  g := GroupWithGenerators( gens );
  SetName( g, Concatenation("GSp(",String(d),",",String(q),")") );
  SetSize( g, (q-1) * Size(sp) );
  return g;
  end );

#############################################################################
#O  GSpdesargues( <d>, <f> )
## returns the projective general symplectic group, as a projective collineation group.
## The generators of the group are the projective collineation elements that are 
## represented by the matrices that generate GSp(d,q). The latter is made possible now (see above method).
##
InstallMethod( GSpdesargues, [IsPosInt, IsField and IsFinite],
  function(d, f)
    local m, frob, b, gens, g, q, gsp;
    q := Size(f);
    m := InvariantBilinearForm(Sp(d,q))!.matrix;     
    b := BaseChangeToCanonical( BilinearFormByMatrix(m, f) );
    frob := FrobeniusAutomorphism(f);
    ## new group elements: x -> b x b^-1 (conjugation by base change)
    gsp := GeneralSymplecticGroup(d,f);
    gens := GeneratorsOfGroup( gsp );
    gens := List( gens, y -> b * y * b^-1);
    gens := ProjElsWithFrob( List(gens, x -> [x,frob^0]), f );
    g := GroupWithGenerators( gens );
    SetName( g, Concatenation("PGSp(",String(d),",",String(q),")") );
    SetSize( g, Size(gsp) / (q-1) );    
    return g;
  end );

#################################################
# Similarity and semi-similarity groups: In this order: PGammaSp, DeltaO (+,-,parabolic), 
#################################################

###### Symplectic group ######

#############################################################################
#O  GammaSp( <d>, <f> )
## returns the projective semi-linear symplectic group.
## We rely on GSp(d,q), and the frobenius automorphism.
##
InstallMethod( GammaSp, [IsPosInt, IsField and IsFinite],
  function(d, f)
    local m, frob, b, gens, g, q, gsp;
    q := Size(f);
    m := InvariantBilinearForm(Sp(d,q))!.matrix;     
    b := BaseChangeToCanonical( BilinearFormByMatrix(m, f) );
    frob := FrobeniusAutomorphism(f);
    ## new group elements: x -> b x b^-1 (conjugation by base change)
    gsp := GeneralSymplecticGroup(d,f);
    gens := GeneratorsOfGroup( gsp );
    gens := List( gens, y -> b * y * b^-1);
    gens := List( gens, x -> [x, frob^0]);
    Add(gens, [IdentityMat(d, f), frob] );
    gens := ProjElsWithFrob( gens, f );
    g := GroupWithGenerators( gens );
    SetName( g, Concatenation("PGammaSp(",String(d),",",String(q),")") );
    SetSize( g, Order(frob) * Size(gsp) / (q - 1) );  
    return g;
  end );

###### Orthogonal (elliptic) groups ######

#############################################################################
#O  DeltaOminus( <d>, <f> )
## returns the projective similarity group of an elliptic orthogonal form
## We rely on GOdesargues.
##
InstallMethod( DeltaOminus, [IsPosInt, IsField and IsFinite],
  function(d, f)
    local go, gens, g, q, one, mat, i, combs, two, a, b, 
          twobytwo, mu, z, zero;  

    ## Note here that for q even, the projective similarity group
    ## is equal to the projective isometry group. For q odd,
    ## the index of the projective isometry group in the projective
    ## similarity group is 2. Moreover, we have two cases modulo 4.
    ## For q = 3 mod 4, we adjoin the matrix (e.g., for d = 6)
    ##    a  b  .  .  .  .  
    ##    b -a  .  .  .  .  
    ##    .  .  .  1  .  .  
    ##    .  . -1  .  .  .  
    ##    .  .  .  .  .  1  
    ##    .  .  .  . -1  .
    ## where a^2+b^2 = Z(q)^((q-1)/2), 
    ## to the isometry group to obtain the similarity group (see
    ## Kleidman and Liebeck, Section 2.8). For q = 1 mod 4, we
    ## use the matrix
    ##    .  1  .  .  .  .  
    ##    z  .  .  .  .  .  
    ##    .  .  .  1  .  .  
    ##    .  .  z  .  .  .  
    ##    .  .  .  .  .  1  
    ##    .  .  .  .  z  .
    ## where z = Z(q).

    one := One(f);
    mat := NullMat(d,d,f);
    q := Size(f);
    z := Z(q);
    go := GOdesargues(-1,d,f);

    if q mod 4 = 3 then
       for i in [2..d/2] do
           mat[2*i-1][2*i] := one;   
           mat[2*i][2*i-1] := -one;  
       od;  
       mu := z^((q-1)/2);;
       combs := Combinations(AsList(f),2);;
       two := First(combs, t -> not IsZero(t[1]) and not IsZero(t[2])
                           and t[1]^2+t[2]^2=mu);
       a := two[1]; b:= two[2];
       twobytwo := [[a,b],[b,-a]];
       mat{[1,2]}{[1,2]} := twobytwo;
    elif q mod 4 = 1 then
       for i in [1..d/2] do
           z := Z(q);
           zero := Zero(f);
           mat{[2*i-1,2*i]}{[2*i-1,2*i]} := [[ zero, one ], [z, zero]];
       od;
    else 
       return go; 
    fi;

    gens := ShallowCopy(GeneratorsOfGroup( go ));
    Add(gens, ProjElWithFrob( mat, IdentityMapping(f), f) );
    g := GroupWithGenerators( gens );
    SetName( g, Concatenation("PDeltaO-(",String(d),",",String(Size(f)),")") );
       ## scalars are completely contained in matrix group, (q-1) cancels with (q-1)
    SetSize( g, Size(GO(-1,d,q)) ); 
    return g;
  end );	


#############################################################################
#O  GammaOminus( <d>, <f> )
## returns the projective semi-similarity group of an elliptic orthogonal form
## We rely on DeltaOminus and the Frobenius automorphism.
##
InstallMethod( GammaOminus, [IsPosInt, IsField and IsFinite],
  function(d, f)
  local q, gram, mat, p, a, go, gens, coll, frob, block, mat2, i; 
  
  ## Works beautifully for odd q! Tested for possible output 
  ## (q,d) in {(4,9),(4,25),(4,27),(4,49),(6,9)}

  ## After some calculations using the information in Section 2.8
  ## of Kleidman and Liebeck, we find that the following matrix M 
  ## (n.b., you should extrapolate the dimension) together with
  ## the Frobenius automorphism of GF(q) defines a semisimilarity
  ## of our canonical form in FinInG:
  ##    a  .  .  .  .  .  
  ##    .  b  .  .  .  .  
  ##    .  .  .  l  .  .  
  ##    .  .  l  .  .  .  
  ##    .  .  .  .  .  l  
  ##    .  .  .  .  l  .
  ## where a = l * alpha^((1-p)/2) and b = l * beta^((1-p)/2), and 
  ## the first block of our Gram matrix is diag(alpha, beta).
  ##  JB 20/11/2011: Fixed the bug for q even. Simply changed the change of basis matrix.

  q := Size(f);
  p := Characteristic( f );
  go := DeltaOminus( d, f );

  if q = p then
     coll := go;
  elif IsOddInt(q) then
     gram := CanonicalGramMatrix("elliptic", d, f);
     mat := MutableCopyMat( gram );
	 mat[1][1] := gram[3][4] * gram[1][1]^((1-p)/2); 
	 mat[2][2] := gram[3][4] * gram[2][2]^((1-p)/2);
	 ConvertToMatrixRep( mat, f );
	 frob := FrobeniusAutomorphism( f );
	 a := ProjElWithFrob( mat, frob, f);
     gens := ShallowCopy( GeneratorsOfGroup(go) );
     Add(gens, a);
     coll := GroupWithGenerators( gens );
	 SetName( coll, Concatenation("PGammaO-(",String(d),",",String(q),")") );
	 SetSize( coll, Size(go) * Order(frob) );
  else
     ## We must find a semisimilarity for the first (2x2) block. Then
     ## simply extend it naturally to fit with the canonical form.

     gram := CanonicalQuadraticForm("elliptic", d, f);
     block :=Forms_RESET( MutableCopyMat( gram{[1,2]}{[1,2]} ), 2, q);
	 frob := FrobeniusAutomorphism( f );
	 mat := BaseChangeOrthogonalQuadratic(block^(frob^-1), f)[1];
	
 	 ## JB is a little bit unsure if this will work all the time. So a test is needed:
	 if not Forms_RESET(mat * block^(frob^-1) *TransposedMat(mat), 2, q) = block then
	    Error("Inappropriate matrix for change of basis");
	 fi;
	
     mat2 := IdentityMat(d,f);  
     mat2{[1,2]}{[1,2]} := mat;
	 a := ProjElWithFrob( mat2, frob, f);
	 gens := ShallowCopy( GeneratorsOfGroup(go) );
     Add(gens, a);
     coll := GroupWithGenerators( gens );
	 SetName( coll, Concatenation("PGammaO-(",String(d),",",String(q),")") );
	 SetSize( coll, Size(go) * Order(frob) );
  fi;
  
  return coll;
  end );

###### Orthogonal (parabolic) groups ######

#############################################################################
#O  GammaO( <d>, <f> )
## returns the projective semi-similarity group of a parabolic orthogonal form
## We rely on GO (available in GAP).
##
InstallMethod( GammaO, [IsPosInt, IsField and IsFinite],
  function(d, f)
    local q, p, go, gens, frob, m, b, lambda, g, w, one;
    one := One(f);
    q := Size(f);
    p := Characteristic(f);
    go := GO(0, d, q);
    gens := ShallowCopy(GeneratorsOfGroup(go));
    frob := FrobeniusAutomorphism( f );
    if IsOddInt(q) then
      m := InvariantBilinearForm(GO(0,d,q))!.matrix;
      b := BaseChangeOrthogonalBilinear(m, f)[1]; 
      gens := List(gens, t->b*t*b^-1);; 
    else 
      m := InvariantQuadraticForm(GO(0,d,q))!.matrix;
      b := BaseChangeOrthogonalQuadratic(m, f)[1];
      gens := List(gens, t->b*t*b^-1);;
    fi;
    if IsOddInt(p) then
       if q mod 8 in [5,7] then 
          w := Z(p) * ((p + 1) / 2);
       else
          w := one * ((p + 1) / 2);
       fi; 
    else
       w := one;
    fi; 
    lambda := First(AsList(f),t->t^2 = w^(p-1));
    gens := List(gens, x -> [x,frob^0]);
    Add(gens, [lambda * IdentityMat(d, f), frob] );
    gens := ProjElsWithFrob( gens, f );
    g := GroupWithGenerators( gens );
    SetName( g, Concatenation("PGammaO(",String(d),",",String(q),")") );  
    SetSize( g, Order(frob) * Size(go) / GCD_INT(2,q-1) );    
       ## Careful to read  Kleidman and Liebeck correctly. For q even, we take the symplectic group.
    return g;
  end );


###### Orthogonal (hyperbolic) groups ######

#############################################################################
#O  DeltaOplus( <d>, <f> )
## returns the projective similarity group of an hyperbolic orthogonal form
## We rely on GO (available in GAP).
##
InstallMethod( DeltaOplus, [IsPosInt, IsField and IsFinite],
  function(d, f)
    local q, go, m, w, mu, i, gens, g, b;  
    q := Size(f);
    go := GO(1, d, q);
    gens := ShallowCopy(GeneratorsOfGroup(go));
  
    if IsOddInt(q) then
      m := InvariantBilinearForm(go)!.matrix;
      b := BaseChangeOrthogonalBilinear(m, f)[1]; 
      w := PrimitiveRoot( f );
    
    ## put primitive root on odd places of diagonal
    ## of identity matrix

      mu := IdentityMat(d,f);
      for i in [1..d/2] do
        mu[2*i-1][2*i-1] := w;
      od;

      gens := List(gens, t->b*t*b^-1);; 
      Add(gens, mu);
    else 
      m := InvariantQuadraticForm(go)!.matrix;
      b := BaseChangeOrthogonalQuadratic(m, f)[1];
      gens := List(gens, t->b*t*b^-1);;
    fi;

    gens := ProjElsWithFrob( List(gens, x -> [x,IdentityMapping(f)]), f );
    g := GroupWithGenerators( gens );
    SetName( g, Concatenation("PDeltaO+(",String(d),",",String(q),")") );
    SetSize( g, 2*q^(d*(d-2)/4)*(q^(d/2)-1)*Product(List([1..d/2-1],
                   i -> (q^(2*i)-1) )) );
    return g;
  end );

#############################################################################
#O  GammaOplus( <d>, <f> )
## returns the projective semi-similarity group of an hyperbolic orthogonal form
## We rely on DeltaOplus and the Frobenius automorphism.
##
InstallMethod( GammaOplus, [IsPosInt, IsField and IsFinite],
  function(d, f)
    local q, deltao, gens, frob, lambda, g;
    q := Size(f);
    deltao := DeltaOplus(d, f);
    gens := ShallowCopy(GeneratorsOfGroup(deltao));
    frob := FrobeniusAutomorphism( f );
    Add(gens, ProjElWithFrob( IdentityMat(d, f), frob, f ));
    g := GroupWithGenerators( gens );
    SetName( g, Concatenation("PGammaO+(",String(d),",",String(q),")") );
    SetSize( g, Log(q, Characteristic(f)) * Size(deltao) );
    return g;
  end );

###### Hermitian groups ######

#############################################################################
#O  GammaU( <d>, <f> )
## returns the projective semi-similarity group of hermitian form
## We rely on GU and the Frobenius automorphism.
##
## Issue here. The centre of GammaU is nontrivial for d=2.
## Our construction of classical groups in FinInG factors out the scalars,
## but not the full centre.
##
InstallMethod( GammaU, [IsPosInt, IsField and IsFinite],
  function(d, f)
   ## bug here, there is more than scalars in the kernel of the action!
    local m, frob, b, gens, g, q, sqrtq, gu;
    q := Size(f);
    sqrtq := Sqrt(Size(f));
    m := InvariantSesquilinearForm(GU(d,sqrtq))!.matrix;     
    b := BaseChangeHermitian(m, f)[1];
    frob := FrobeniusAutomorphism(f);
    gu := GU(d,sqrtq);
    gens := GeneratorsOfGroup( gu );
    gens := List( gens, y -> b * y * b^-1);
    gens := List( gens, x -> [x, frob^0]);
    Add(gens, [IdentityMat(d, f), frob] );
    gens := ProjElsWithFrob( gens, f );
    g := GroupWithGenerators( gens );
    SetSize( g, Order(frob) * Size(gu) / (sqrtq+1) );
    if d = 2 then
       Info(InfoFinInG, 1, "Warning: We have only factored scalars out of GammaU to construct a central cover of PGammaU.\n The centre is thus nontrivial and acts trivially on totally isotropic 1-spaces.");
       Info(InfoFinInG, 2, "So be careful because you're opening a can of worms!");
       SetName( g, Concatenation("2.PGammaU(",String(d),",",String(sqrtq),"^2)") );
    else
       SetName( g, Concatenation("PGammaU(",String(d),",",String(sqrtq),"^2)") ); 
    fi;
    return g;
  end );






