#############################################################################
##
##  polarspace.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 polar spaces
##
#############################################################################

#############################################################################
# Low level help methods:
#############################################################################

FINING.LimitForCanComputeActionOnPoints := 1000000;
FINING.Fast := true;

#############################################################################
# Constructor method (not for users)!:
#############################################################################

# CHECKED 20/09/11 jdb
#############################################################################
#O  Wrap( <geo>, <type>, <o> )
# This is an internal subroutine which is not expected to be used by the user;
# they would be using VectorSpaceToElement. Recall that Wrap is declared in 
# geometry.gd. 
##
InstallMethod( Wrap, 
	"for a polar space and an object",
	[IsClassicalPolarSpace, IsPosInt, IsObject],
	function( geo, type, o )
		local w;
		w := rec( geo := geo, type := type, obj := o );
		Objectify( NewType( SoPSFamily, IsElementOfIncidenceStructure and 
		#Objectify( NewType( SoCPSFamily, IsElementOfIncidenceStructure and #keep this, maybe for future use.
		IsElementOfIncidenceStructureRep and IsSubspaceOfClassicalPolarSpace ), w );
    return w;
	end );

#############################################################################
# Constructor methods for polar spaces.
#############################################################################

# CHECKED 11/10/11 jb + jdb
#############################################################################
#O  PolarSpace( <m>, <f>, <g>, <act> )
# This method returns a polar space with a lot of knowledge. most likely not for user.
# <m> is a sesquilinear form. Furthermore, a field <f>, a group <g> and an action 
# function <act> are given.
##
InstallMethod( PolarSpace, 
	"for a sesquilinear form, a field, a group and an action function",
	[ IsSesquilinearForm, IsField, IsGroup, IsFunction ],
	function( m, f, g, act )
		local geo, ty, gram, eq, r, i1, i2, r2, fam, j, flag;
		if IsDegenerateForm( m ) then 
			Error("Form is degenerate");
		elif IsPseudoForm( m ) then
			Error("No Polar space can be associated with a pseudo form");
		fi;
		gram := m!.matrix;
		geo := rec( basefield := f, dimension := Length(gram)-1,
					vectorspace := FullRowSpace(f,Length(gram)) );
		if IsHermitianForm(m) then
			flag := IsHermitianVariety;
		else
			flag := IsQuadraticVariety;
		fi;
		if not IsAlternatingForm(m) then
			if WittIndex(m) = 2 then
				ty := NewType( GeometriesFamily,
						IsClassicalPolarSpace and IsClassicalPolarSpaceRep and IsClassicalGQ and flag);
				else ty := NewType( GeometriesFamily,
							IsClassicalPolarSpace and IsClassicalPolarSpaceRep and flag);
			fi;
		else
			if WittIndex(m) = 2 then
				ty := NewType( GeometriesFamily,
						IsClassicalPolarSpace and IsClassicalPolarSpaceRep and IsClassicalGQ);
				else ty := NewType( GeometriesFamily,
							IsClassicalPolarSpace and IsClassicalPolarSpaceRep);
			fi;
		fi;
		# at this stage, we are sure that if m is a bilinear form and the characteristic is even, 
		# then m will be symplectic and PolynomialOfForm(m) will produce an error.
		# on the other hand, if m is symplectic and the characteristic is odd, then PolynomialOfForm
		# will produce a 0*Z(q). 
		if IsEvenInt(Size(f)) and IsBilinearForm(m) then  
			eq := Zero(f);
		else
			eq := PolynomialOfForm( m );
		fi;
		if IsZero(eq) then
			i1 := List([1..Length(gram)],i->Concatenation("x",String(i)));
			i2 := List([1..Length(gram)],i->Concatenation("y",String(i)));
			#r := PolynomialRing(f,Concatenation(i1,i2):old);     ## there was an error here for gap4r5
			#i2 := IndeterminatesOfPolynomialRing(r){[Length(gram)+1..2*Length(gram)]};
			#there was a proble with the above lines. If say x_4 existed already, and i1 just ran until 3, than
			# x_4 got changed by y_1, which caused all polynomials in r to change also, which caused funny printing behaviour.
			# the 10000 is completely arbitrary, I guess nobody will use the first 10000 variables in forms, since nobody
			# will work in a 10000-dimensional vector space (I guess...).
			r := PolynomialRing(f,i1:old);     ## there was an error here for gap4r5
			i1 := IndeterminatesOfPolynomialRing(r){[1..Length(gram)]};
			r2 := PolynomialRing(f,[10001..10000+Length(gram)]:old);
			fam := FamilyObj(r2.1);
			for j in [1..Length(gram)] do
				SetIndeterminateName(fam,10000+j,i2[j]);
			od;
			i2 := IndeterminatesOfPolynomialRing(r2);
			eq := i1*gram*i2;
		fi;
		ObjectifyWithAttributes( geo, ty, 
								SesquilinearForm, m,
								CollineationGroup, g,
								CollineationAction, act,
								AmbientSpace, ProjectiveSpace(geo.dimension, f),
								EquationForPolarSpace, eq );
		return geo;
	end );

# CHECKED 11/10/11 jb + jdb
#############################################################################
#O  PolarSpaceStandard( <m>, <bool> )
# Method to crete a polar space using sesquilinear form <m>, where we know
# that this form is the standard one used in Fining. Not intended for users, 
# all checks are removed.
##
InstallMethod( PolarSpaceStandard, 
	"for a sesquilinear form",
	[ IsSesquilinearForm, IsBool ],
	function( m, bool )
		local geo, ty, gram, f, eq, r, i1, i2, r2, fam, j, flag;
		gram := m!.matrix;
		f := m!.basefield;
		geo := rec( basefield := f, dimension := Length(gram)-1,
				vectorspace := FullRowSpace(f,Length(gram)) );
		if IsHermitianForm(m) then
			flag := IsHermitianVariety;
		else
			flag := IsQuadraticVariety;
		fi;
		if not IsAlternatingForm(m) then
			if WittIndex(m) = 2 then
				ty := NewType( GeometriesFamily,
						IsClassicalPolarSpace and IsClassicalPolarSpaceRep and IsClassicalGQ and flag);
				else ty := NewType( GeometriesFamily,
							IsClassicalPolarSpace and IsClassicalPolarSpaceRep and flag);
			fi;
		else
			if WittIndex(m) = 2 then
				ty := NewType( GeometriesFamily,
						IsClassicalPolarSpace and IsClassicalPolarSpaceRep and IsClassicalGQ);
				else ty := NewType( GeometriesFamily,
							IsClassicalPolarSpace and IsClassicalPolarSpaceRep);
			fi;
		fi;
		# at this stage, we are sure that if m is a bilinear form and the characteristic is even, 
		# then m will be symplectic and PolynomialOfForm(m) will produce an error.
		# on the other hand, if m is symplectic and the characteristic is odd, then PolynomialOfForm
		# will produce a 0*Z(q). 
		if IsEvenInt(Size(f)) and IsBilinearForm(m) then  
			eq := Zero(f);
		else
			eq := PolynomialOfForm( m );
		fi;
		if IsZero(eq) then
			#Print("eq is zero\n");
			i1 := List([1..Length(gram)],i->Concatenation("x",String(i)));
			i2 := List([1..Length(gram)],i->Concatenation("y",String(i)));
			#r := PolynomialRing(f,Concatenation(i1,i2):old);     ## there was an error here for gap4r5
			#i2 := IndeterminatesOfPolynomialRing(r){[Length(gram)+1..2*Length(gram)]};
			#there was a proble with the above lines. If say x_4 existed already, and i1 just ran until 3, than
			# x_4 got changed by y_1, which caused all polynomials in r to change also, which caused funny printing behaviour.
			# the 10000 is completely arbitrary, I guess nobody will use the first 10000 variables in forms, since nobody
			# will work in a 10000-dimensional vector space (I guess...).
			r := PolynomialRing(f,i1:old);     ## there was an error here for gap4r5
			i1 := IndeterminatesOfPolynomialRing(r){[1..Length(gram)]};
			r2 := PolynomialRing(f,[10001..10000+Length(gram)]:old);
			fam := FamilyObj(r2.1);
			for j in [1..Length(gram)] do
				SetIndeterminateName(fam,10000+j,i2[j]);
			od;
			i2 := IndeterminatesOfPolynomialRing(r2);
			eq := i1*gram*i2;
		fi;
		ObjectifyWithAttributes( geo, ty, 
                            SesquilinearForm, m,
                            AmbientSpace, ProjectiveSpace(geo.dimension, f),
							EquationForPolarSpace, eq );
		if bool then
			SetIsStandardPolarSpace(geo,true); #if bool is false, we can use this operation to construct almost standard polar spaces :-)
		fi;
		return geo;
	end );

#############################################################################
#O  PolarSpaceStandard( <m>, <bool> )
# general method to create a polar space using quadratic form. Not intended 
# for the user. no checks.
##
InstallMethod( PolarSpaceStandard, 
	"for a quadratic form",
	[ IsQuadraticForm, IsBool ],
	function( m, bool )
		local geo, ty, gram, polar, f, flavour, eq, flag;
		f := m!.basefield;
		gram := m!.matrix;
		polar := AssociatedBilinearForm( m );
		geo := rec( basefield := f, dimension := Length(gram)-1,
					vectorspace := FullRowSpace(f,Length(gram)) );
		if WittIndex(m) = 2 then
			ty := NewType( GeometriesFamily,
					IsClassicalPolarSpace and IsClassicalPolarSpaceRep and IsClassicalGQ and IsProjectiveVariety);
		else ty := NewType( GeometriesFamily,
					IsClassicalPolarSpace and IsClassicalPolarSpaceRep and IsProjectiveVariety);
		fi;
		eq := PolynomialOfForm( m );
		ObjectifyWithAttributes( geo, ty, 
								QuadraticForm, m,
								SesquilinearForm, polar,
								AmbientSpace, ProjectiveSpace(geo.dimension, f),
								EquationForPolarSpace, eq );
		if bool then
			SetIsStandardPolarSpace(geo,true);
		fi;
		return geo;
	end );

# CHECKED 20/09/11 jdb
#############################################################################
#O  PolarSpace( <m> )
# general method to crete a polar space using sesquilinear form <m>. It is checked
# whether the form is not pseudo.##
##
InstallMethod( PolarSpace, 
	"for a sesquilinear form",
	[ IsSesquilinearForm ],
	function( m )
		local geo, ty, gram, f, eq, r, i1, i2, r2, fam, j, flag;  
		if IsDegenerateForm( m ) then 
			Error("Form is degenerate");
		elif IsPseudoForm( m ) then
			Error("No Polar space can be associated with a pseudo form");
		fi;
		gram := m!.matrix;
		f := m!.basefield;
		geo := rec( basefield := f, dimension := Length(gram)-1,
				vectorspace := FullRowSpace(f,Length(gram)) );
		if IsHermitianForm(m) then
			flag := IsHermitianVariety;
		else
			flag := IsQuadraticVariety;
		fi;
		if not IsAlternatingForm(m) then
			if WittIndex(m) = 2 then
				ty := NewType( GeometriesFamily,
						IsClassicalPolarSpace and IsClassicalPolarSpaceRep and IsClassicalGQ and flag);
				else ty := NewType( GeometriesFamily,
							IsClassicalPolarSpace and IsClassicalPolarSpaceRep and flag);
			fi;
		else
			if WittIndex(m) = 2 then
				ty := NewType( GeometriesFamily,
						IsClassicalPolarSpace and IsClassicalPolarSpaceRep and IsClassicalGQ);
				else ty := NewType( GeometriesFamily,
							IsClassicalPolarSpace and IsClassicalPolarSpaceRep);
			fi;
		fi;
		# at this stage, we are sure that if m is a bilinear form and the characteristic is even, 
		# then m will be symplectic and PolynomialOfForm(m) will produce an error.
		# on the other hand, if m is symplectic and the characteristic is odd, then PolynomialOfForm
		# will produce a 0*Z(q). 
		if IsEvenInt(Size(f)) and IsBilinearForm(m) then  
			eq := Zero(f);
		else
			eq := PolynomialOfForm( m );
		fi;
		if IsZero(eq) then
			#Print("eq is zero\n");
			i1 := List([1..Length(gram)],i->Concatenation("x",String(i)));
			i2 := List([1..Length(gram)],i->Concatenation("y",String(i)));
			#r := PolynomialRing(f,Concatenation(i1,i2):old);     ## there was an error here for gap4r5
			#i2 := IndeterminatesOfPolynomialRing(r){[Length(gram)+1..2*Length(gram)]};
			#there was a proble with the above lines. If say x_4 existed already, and i1 just ran until 3, than
			# x_4 got changed by y_1, which caused all polynomials in r to change also, which caused funny printing behaviour.
			# the 10000 is completely arbitrary, I guess nobody will use the first 10000 variables in forms, since nobody
			# will work in a 10000-dimensional vector space (I guess...).
			r := PolynomialRing(f,i1:old);     ## there was an error here for gap4r5
			i1 := IndeterminatesOfPolynomialRing(r){[1..Length(gram)]};
			r2 := PolynomialRing(f,[10001..10000+Length(gram)]:old);
			fam := FamilyObj(r2.1);
			for j in [1..Length(gram)] do
				SetIndeterminateName(fam,10000+j,i2[j]);
			od;
			i2 := IndeterminatesOfPolynomialRing(r2);
			eq := i1*gram*i2;
		fi;
		ObjectifyWithAttributes( geo, ty, 
                            SesquilinearForm, m,
                            AmbientSpace, ProjectiveSpace(geo.dimension, f),
							IsStandardPolarSpace, false,
							EquationForPolarSpace, eq );
		return geo;
	end );

# CHECKED 20/09/11 jdb
#############################################################################
#O  PolarSpace( <m> )
#general method to create a polar space using quadratic form. Possible in even
#and odd char.
##
InstallMethod( PolarSpace, 
	"for a quadratic form",
	[ IsQuadraticForm ],
	function( m )
		local geo, ty, gram, polar, f, flavour, eq;
		if IsSingularForm( m ) then 
			Error("Form is singular"); 
		fi;
		f := m!.basefield;
		gram := m!.matrix;
		polar := AssociatedBilinearForm( m );
		geo := rec( basefield := f, dimension := Length(gram)-1,
					vectorspace := FullRowSpace(f,Length(gram)) );
		if WittIndex(m) = 2 then
			ty := NewType( GeometriesFamily,
					IsClassicalPolarSpace and IsClassicalPolarSpaceRep and IsClassicalGQ and IsQuadraticVariety);
		else ty := NewType( GeometriesFamily,
					IsClassicalPolarSpace and IsClassicalPolarSpaceRep and IsQuadraticVariety);
		fi;
		eq := PolynomialOfForm(m);
		ObjectifyWithAttributes( geo, ty, 
								QuadraticForm, m,
								SesquilinearForm, polar,
								AmbientSpace, ProjectiveSpace(geo.dimension, f),
								IsStandardPolarSpace, false,
								EquationForPolarSpace, eq );
		return geo;
	end );

# CHECKED 20/09/11 jdb
#############################################################################
#O  PolarSpace( <m> )
# general method to setup a polar space using hermitian form. Is this method
# still necessary? Maybe not necessary, but maybe usefull, since we know slightly
# more properties of the polar space if we start from a hermitian form rather than
# from a sesquilinear form.
##
InstallMethod( PolarSpace, 
	"for a hermitian form",
	[ IsHermitianForm ],
	function( m )
		local geo, ty, gram, f, eq;
		if IsDegenerateForm( m ) then 
			Error("Form is degenerate");
		fi;
		gram := m!.matrix;
		f := m!.basefield;
		geo := rec( basefield := f, dimension := Length(gram)-1,
					vectorspace := FullRowSpace(f,Length(gram)) );
		if WittIndex(m) = 2 then
			ty := NewType( GeometriesFamily,
                  IsClassicalPolarSpace and IsClassicalPolarSpaceRep and IsClassicalGQ and IsHermitianVariety);
		else ty := NewType( GeometriesFamily,
                  IsClassicalPolarSpace and IsClassicalPolarSpaceRep and IsHermitianVariety);
		fi;
		eq := PolynomialOfForm(m);
		ObjectifyWithAttributes( geo, ty, 
                            SesquilinearForm, m,
                            AmbientSpace, ProjectiveSpace(geo.dimension, f),
							IsStandardPolarSpace, false,
							EquationForPolarSpace, eq );
		return geo;
	end );


#############################################################################
## Constructions of Canonical finite classical Polar Spaces
## Important remark. We use "Canonical" in the mathematical sense, i.e, a polar
## space that equals one of the "standard" polar spaces constructed by the methods
## below. So "Standard" refers to a particular form used to construct the polar space. 
## It is well known that two forms that are similar, i.e. differe on a constant factor,
## will yield the same polar space. So a polar space is canonical iff its forms is similar with
## the form of one of the polar spaces constructed with a method below. A polar space is 
## "standard" iff it is constructed with one of the methods below. So if the user
## uses his favorite form, his polar space can become canonical, but it can never become standard.
#############################################################################

#############################################################################
# Part I: A method to setup the representative of the maximal 
# subspaces for polar spaces of each type.
# Recall that the methods for methods for canonical gram matrices and canonical 
# quadratic forms are found in group.gi. All these methods are not intended for the user.
#############################################################################

# CHECKED 21/09/11 jdb
#############################################################################
#O  CanonicalOrbitRepresentativeForSubspaces( <type>, <d>, <f> )
## This function returns representatives for the
## maximal totally isotropic (singular) subspaces
## of the given canonical polar space
##
InstallMethod( CanonicalOrbitRepresentativeForSubspaces, 
	"for a string, an integer and a field",
	[IsString, IsPosInt, IsField],		
	function( type, d, f )
		local b, i, id, one, w, q, sqrtq;
		q := Size(f);
		id := IdentityMat(d, f);
		if type = "symplectic" then    
			b := List([1..d/2], i-> id[2*i-1] + id[2*i]);
		elif type = "hermitian" then     
			one := One(f);
			sqrtq := Sqrt(q);
      ## find element with norm -1
			w := First(AsList(f), t -> IsZero( t^(sqrtq+1) + one ) );
			if IsEvenInt(d) then   
				b := List([1..d/2], i-> w * id[2*i] + id[2*i-1]);
			else 
				b := List([1..(d-1)/2], i-> w * id[2*i] + id[2*i-1]);
			fi;
		elif type = "parabolic" then 
			b := id{List([1..(d-1)/2], i -> 2*i)};
		elif type = "hyperbolic" then 
			b := id{List([1..d/2], i -> 2*i-1)};
		elif type = "elliptic" then 
			b := id{List([1..(d/2-1)],i -> 2*i+1)};
		else Error( "type is unknown or not implemented");
		fi;
		return [b];
	end );

#############################################################################
# Part II: constructor methods, for the user of course.
#############################################################################

# CHECKED 21/09/11 jdb
# Cmat adapted 20/3/14
#############################################################################
#O  EllipticQuadric( <d>, <f> )
# returns the standard elliptic quadrac. See CanonicalQuadraticForm and CanonicalGramMatrix
# for details on the standard.
##
InstallMethod( EllipticQuadric, 
	"for an integer and a field",
	[ IsPosInt, IsField ],
	function( d, f )
		local eq,m,types,max,reps,q,form,creps;
		q := Size(f);
		if IsEvenInt(d) then
			Error("dimension must be odd");
			return;
		fi;
		if IsEvenInt(q) then
			m := CanonicalQuadraticForm("elliptic", d+1, f);
			form := QuadraticFormByMatrix(m, f);
		else 
			m := CanonicalGramMatrix("elliptic", d+1, f);  
			form := BilinearFormByMatrix(m, f);  
		fi; 
        eq := PolarSpaceStandard( form, true );
		SetRankAttr( eq, (d-1)/2 );
		types := TypesOfElementsOfIncidenceStructure( AmbientSpace(eq) );
		SetTypesOfElementsOfIncidenceStructure(eq, types{[1..(d-1)/2]});
		SetIsEllipticQuadric(eq, true);
		SetIsCanonicalPolarSpace(eq, true);
		SetPolarSpaceType(eq, "elliptic");
		if RankAttr(eq) = 2 then
			SetOrder( eq, [q, q^2]);
		fi;
		max := CanonicalOrbitRepresentativeForSubspaces("elliptic", d+1, f)[1];

	## Here we take the maximal totally isotropic subspace rep
	## max and make representatives for lower dimensional subspaces
	## it could be that max is empty, not Max of course, but the variable max.	

		if not IsEmpty(max) then
			reps := [ [max[1]] ]; #small change
			Append(reps, List([2..(d-1)/2], j -> max{[1..j]})); 
	## We would like the representative to be stored as
	## compressed matrices. Then they will be more efficient
	## to work with.
			
			creps := List(reps,x->NewMatrix(IsCMatRep, f, d+1, x));
			creps[1] := creps[1][1]; #max is not empty, first element should always be a 1xn matrix->cvec.
			
			#for m in reps do
			#	if IsMatrix(m) then
			#		ConvertToMatrixRep(m,f);
			#	else 
			#		ConvertToVectorRep(m,f);
			#	fi;
			#od;   

    ## Wrap 'em up #can be done without using VectorSpaceToElement (and hence withou computations) now :-)
			reps := List([1..(d-1)/2], j -> Wrap(eq, j, creps[j]) ); #one more small change :-)
			SetRepresentativesOfElements(eq, reps);
		else;
			SetRepresentativesOfElements(eq, []);
		fi;
		SetClassicalGroupInfo( eq, rec( degree := (q^((d+1)/2)+1)*(q^((d+1)/2-1)-1)/(q-1) ) );  
		return eq;
	end );

# CHECKED 21/09/11 jdb
#############################################################################
#O  EllipticQuadric( <d>, <q> )
# returns the standard elliptic quadric. See EllipticQuadric method above.
##
InstallMethod( EllipticQuadric,
	"for two positive inteters <d> and <q>",	
	[ IsPosInt, IsPosInt ],
	function( d, q )  
		return EllipticQuadric(d,GF(q));
	end );

# CHECKED 21/09/11 jdb
# Cmat adapted 20/3/14
#############################################################################
#O  SymplecticSpace( <d>, <f> )
# returns the standard symplectic space. See CanonicalGramMatrix
# for details on the standard.
##
InstallMethod( SymplecticSpace,
	"for an integer and a field",
	[ IsPosInt, IsField ],
	function( d, f )
		local w,frob,m,types,max,reps,q,form,creps;
		if IsEvenInt(d) then
			Error("dimension must be odd");
			return;
		fi;

	## put compressed matrices here also
		q := Size(f);
		m := CanonicalGramMatrix("symplectic", d+1, f);     
		w := PolarSpaceStandard( BilinearFormByMatrix(m, f), true );
		SetRankAttr( w, (d+1)/2 );
		types := TypesOfElementsOfIncidenceStructure(AmbientSpace(w));
		if RankAttr(w) = 2 then
			SetOrder( w, [q, q]);
		fi;
		SetTypesOfElementsOfIncidenceStructure(w,types{[1..(d+1)/2]});
		SetIsSymplecticSpace(w,true);
		SetIsCanonicalPolarSpace(w, true);
		SetPolarSpaceType(w, "symplectic");
		max := CanonicalOrbitRepresentativeForSubspaces("symplectic", d+1, w!.basefield)[1];    

    ## Here we take the maximal totally isotropic subspace rep
    ## max and make representatives for lower dimensional subspaces
		reps := [ [ max[1] ] ]; #small change
		Append(reps, List([2..(d+1)/2], j -> max{[1..j]}));  

    ## We would like the representative to be stored as
    ## compressed matrices. Then they will be more efficient
    ## to work with.

		creps := List(reps,x->NewMatrix(IsCMatRep, f, d+1, x));
		creps[1] := creps[1][1]; #max is not empty, first element should always be a 1xn matrix->cvec.

		#for m in reps do
		#	if IsMatrix(m) then
		#		ConvertToMatrixRep(m,f);
		#	else 
		#		ConvertToVectorRep(m,f);
		#	fi;
		#od;   

    ## Wrap 'em up
		reps := List([1..(d+1)/2], j -> Wrap(w, j, creps[j]) ); #one more small change :-)
		SetRepresentativesOfElements(w, reps);
        SetClassicalGroupInfo( w, rec(  degree := (q^(d+1)-1)/(q-1) ) );    
		return w;
	end );


# CHECKED 21/09/11 jdb
#############################################################################
#O  SymplecticSpace( <d>, <q> )
# returns the standard symplectic space. See SymplecticSpace method above.
##
InstallMethod(SymplecticSpace, 
	"for an integer and an integer",
	[ IsPosInt, IsPosInt ],
	function( d, q ) 
		return SymplecticSpace(d, GF(q));
	end );

# CHECKED 21/09/11 jdb
# Cmat adapted 20/3/14
#############################################################################
#O  ParabolicQuadric( <d>, <f> )
# returns the standard parabolic quadric. See CanonicalQuadraticForm and CanonicalGramMatrix
# for details on the standard.
##
InstallMethod( ParabolicQuadric, 
	"for an integer and a field",	
	[ IsPosInt, IsField ],
	function( d, f )
		local pq,m,types,max,reps,q,form,creps;
		if IsOddInt(d) then
			Error("dimension must be even");
			return;
		fi;
		q := Size(f);
		if IsEvenInt(q) then
			m := CanonicalQuadraticForm("parabolic", d+1, f);
			form := QuadraticFormByMatrix(m, f);
		else 
			m := CanonicalGramMatrix("parabolic", d+1, f); 
			form := BilinearFormByMatrix(m, f);   
		fi; 
        pq := PolarSpaceStandard( form, true );
		SetRankAttr( pq, d/2 );
		types := TypesOfElementsOfIncidenceStructure( AmbientSpace(pq) );
		SetTypesOfElementsOfIncidenceStructure(pq, types{[1..d/2]});
		SetIsParabolicQuadric(pq, true);
		SetIsCanonicalPolarSpace(pq, true);
		SetPolarSpaceType(pq, "parabolic");
		if RankAttr(pq) = 2 then
			SetOrder( pq, [q, q]);
		fi;
		max := CanonicalOrbitRepresentativeForSubspaces("parabolic", d+1, f)[1];

    ## Here we take the maximal totally isotropic subspace rep
    ## max and make representatives for lower dimensional subspaces

		reps := [ [ max[1] ] ]; #small change
		Append(reps, List([2..d/2], j -> max{[1..j]}));  

    ## We would like the representative to be stored as
    ## compressed matrices. Then they will be more efficient
    ## to work with.
		creps := List(reps,x->NewMatrix(IsCMatRep, f, d+1, x));
		creps[1] := creps[1][1]; #max is not empty, first element should always be a 1xn matrix->cvec.
		
		#for m in reps do
		#	if IsMatrix(m) then
		#		ConvertToMatrixRep(m,f);
		#	else 
		#		ConvertToVectorRep(m,f);
		#	fi;
		#od;   

    ## Wrap 'em up
		reps := List([1..d/2], j -> Wrap(pq, j, creps[j]) ); #one more small change :-)
		SetRepresentativesOfElements(pq, reps);
        SetClassicalGroupInfo( pq, rec( degree := (q^(d/2)-1)/(q-1)*(q^((d+2)/2-1)+1) ) );   
		return pq;
	end );

# CHECKED 21/09/11 jdb
#############################################################################
#O  ParabolicQuadric( <d>, <q> )
# returns the standard parabolic quadric. See ParabolicQuadric method above.
##
InstallMethod( ParabolicQuadric,
	"for two integers",
	[ IsPosInt, IsPosInt ],
	function( d, q ) 
		return ParabolicQuadric(d, GF(q));
	end );

# CHECKED 21/09/11 jdb
# Cmat adapted 20/3/14
#############################################################################
#O  HyperbolicQuadric( <d>, <f> )
# returns the standard hyperbolic quadric. See CanonicalQuadraticForm and CanonicalGramMatrix
# for details on the standard.
##
InstallMethod( HyperbolicQuadric, 
	"for an integer and a field",	
	[ IsPosInt, IsField ],
	function( d, f )
		local hq,m,types,max,reps,q,form,creps;
		q := Size(f);
		if IsEvenInt(d) then
			Error("dimension must be odd");
			return;
		fi;
		if IsEvenInt(q) then
			m := CanonicalQuadraticForm("hyperbolic", d+1, f);
			form := QuadraticFormByMatrix(m, f);
		else 
			m := CanonicalGramMatrix("hyperbolic", d+1, f);    
			form := BilinearFormByMatrix(m, f);
		fi; 
		hq := PolarSpaceStandard( form, true );
		SetRankAttr( hq, (d+1)/2 );
		types := TypesOfElementsOfIncidenceStructure( AmbientSpace(hq) );
		SetTypesOfElementsOfIncidenceStructure(hq, types{[1..(d+1)/2]});
		SetIsCanonicalPolarSpace(hq, true);
		SetIsHyperbolicQuadric(hq, true);
		SetPolarSpaceType(hq, "hyperbolic");
		if RankAttr(hq) = 2 then
			SetOrder( hq, [q, 1]);
		fi;
		max := CanonicalOrbitRepresentativeForSubspaces("hyperbolic", d+1, f)[1];
  
    ## Here we take the maximal totally isotropic subspace rep
    ## max and make representatives for lower dimensional subspaces

		reps := [ [ max[1] ] ]; #small change
		Append(reps, List([2..(d+1)/2], j -> max{[1..j]}));  

    ## We would like the representative to be stored as
    ## compressed matrices. Then they will be more efficient
    ## to work with.

		creps := List(reps,x->NewMatrix(IsCMatRep, f, d+1, x));
		creps[1] := creps[1][1]; #max is not empty, first element should always be a 1xn matrix->cvec.
  
		#for m in reps do
		#	if IsMatrix(m) then
		#		ConvertToMatrixRep(m,f);
		#	else 
		#		ConvertToVectorRep(m,f);
		#	fi;
		#od;   

    ## Wrap 'em up
		reps := List([1..(d+1)/2], j -> Wrap(hq, j, creps[j]) ); #one more small change :-)
		SetRepresentativesOfElements(hq, reps);
        SetClassicalGroupInfo( hq, rec( degree := (q^((d+1)/2)-1)/(q-1)*(q^((d+1)/2-1)+1)) );
		return hq;
	end );

# CHECKED 21/09/11 jdb
#############################################################################
#O  HyperbolicQuadric( <d>, <q> )
# returns the standard hyperbolic quadric. See HyperbolicQuadric method above.
##
InstallMethod(HyperbolicQuadric, 
	"for a two integers",
	[ IsPosInt, IsPosInt ],
	function( d, q ) 
		return HyperbolicQuadric(d, GF(q));
	end );

# CHECKED 21/09/11 jdb
# Cmat adapted 20/3/14
#############################################################################
#O  HermitianPolarSpace( <d>, <f> )
# returns the standard hermitian variety. See CanonicalGramMatrix
# for details on the standard.
##
InstallMethod( HermitianPolarSpace, 
	"for an integer and a field",	
	[ IsPosInt, IsField ],
	function( d, f )
		local h,m,types,max,reps,q,creps,degree;
		if PrimePowersInt(Size(f))[2] mod 2 <> 0 then
			Error("field order must be a square");
			return;
		fi;
		q := Sqrt(Size(f));
		m := CanonicalGramMatrix("hermitian", d+1, f);   
		h := PolarSpaceStandard( HermitianFormByMatrix(m, f), true );
		if d mod 2 = 0 then  
			SetRankAttr( h, d/2 );
		else
			SetRankAttr( h, (d+1)/2 );    
		fi;    
		types := TypesOfElementsOfIncidenceStructure( AmbientSpace(h) );
		SetTypesOfElementsOfIncidenceStructure(h, types{[1..RankAttr(h)]});
		SetIsHermitianPolarSpace(h, true);
		SetIsCanonicalPolarSpace(h, true);
		SetPolarSpaceType(h, "hermitian");
		if RankAttr(h) = 2 then
			if d = 3 then SetOrder( h, [q^2, q]); fi;
			if d = 4 then SetOrder( h, [q^2, q^3]); fi;
		fi;
		max := CanonicalOrbitRepresentativeForSubspaces("hermitian", d+1, f)[1];

    ## Here we take the maximal totally isotropic subspace rep
    ## max and make representatives for lower dimensional subspaces
		reps := [ [ max[1] ] ]; #small change
		Append(reps, List([2..RankAttr(h)], j -> max{[1..j]}));  

    ## We would like the representative to be stored as
    ## compressed matrices. Then they will be more efficient
    ## to work with.

		creps := List(reps,x->NewMatrix(IsCMatRep, f, d+1, x));
		creps[1] := creps[1][1]; #max is not empty, first element should always be a 1xn matrix->cvec.

		#for m in reps do
		#	if IsMatrix(m) then
		#		ConvertToMatrixRep(m,f);
		#	else
		#		ConvertToVectorRep(m,f);  
		#	fi;
		#od;   

    ## Wrap 'em up
		reps := List([1..RankAttr(h)], j -> Wrap(h, j, creps[j]) ); #one more small change :-)
		SetRepresentativesOfElements(h, reps);
		if d = 3 then
            degree := (q^3+1)*(q+1);
        else
            degree := (q^d-(-1)^d)*(q^(d+1)-(-1)^(d+1))/(q^2-1);
        fi;
        #SetClassicalGroupInfo( h, rec(  degree := (q^d-(-1)^d)*(q^(d+1)-(-1)^(d+1))/(q^2-1)) );
        SetClassicalGroupInfo( h, rec(  degree := degree ));
		return h;
	end );

# CHECKED 21/09/11 jdb
#############################################################################
#O  HermitianPolarSpace( <d>, <q> )
# returns the standard hermitian polar space. See HermitianPolarSpace method above.
##
InstallMethod(HermitianPolarSpace,
	"for an two integers",
	[ IsPosInt, IsPosInt ],
	function( d, q ) 
		return HermitianPolarSpace(d, GF(q));
	end );

# ADDED 7/11/12 jdb
#############################################################################
#O  StandardPolarSpace( <ps> )
# returns the standard polar space isomorphic with <ps>.
##
InstallMethod( StandardPolarSpace, 
	"for a polar space",
	[ IsClassicalPolarSpace ],
	function( ps )
		local type, standard,d,f;
		d := ps!.dimension;
		f := ps!.basefield;
		type := PolarSpaceType( ps );
		if type = "hermitian" then
			standard := HermitianPolarSpace(d, f);               
			SetIsHermitianPolarSpace(ps, true);            
		elif type = "symplectic" then
			standard := SymplecticSpace(d, f);         
			SetIsSymplecticSpace(ps, true);
		elif type = "elliptic" then 
			standard := EllipticQuadric(d, f);         
			SetIsEllipticQuadric(ps, true);
		elif type = "parabolic" then
			standard := ParabolicQuadric(d, f);         
			SetIsParabolicQuadric(ps, true);
		elif type = "hyperbolic" then
			standard := HyperbolicQuadric(d, f);         
			SetIsHyperbolicQuadric(ps, true);
		fi;
		return standard;
	end );

# ADDED 30/3/14 jdb
#############################################################################
#O  IsCanonicalPolarSpace( <ps> )
# returns true if <ps> is canonical. Remind that canonical means similar to
# standard: in geometrical terms: the same geometry as a standard polar space
# with an underlying form that differs a factor with a standard form.
# Calling this operation also makes sure the ClassicalGroupInfo is set and
# the correct property. This mechanism makes sure that when a user constructs
# his favorite-non-standard-but-canonical polar space, it is recognised by
# FinInG, and only once a base change computation by forms will be done.
##
InstallMethod( IsCanonicalPolarSpace,
	"for a polar space",
	[ IsClassicalPolarSpace and IsClassicalPolarSpaceRep ],
	function( ps )
	local type, form, d, bf, st, gram, c1, c2, order, q, result;
	type := PolarSpaceType(ps); #a base change is computed in forms.
	d := ps!.dimension;
	bf := ps!.basefield;
	if HasQuadraticForm(ps) then
		form := QuadraticForm(ps);
		gram := GramMatrix(form);
		st := CanonicalQuadraticForm(type, d+1, bf); #returns a matrix.
	else
		form := SesquilinearForm(ps);
		gram := GramMatrix(form);
		st := CanonicalGramMatrix(type, d+1, bf);
	fi;
	q := Size(bf);
	if type = "elliptic" then
		SetIsEllipticQuadric(ps,true);
		order := (q^((d+1)/2)+1)*(q^((d+1)/2-1)-1)/(q-1);
	elif type = "parabolic" then
		SetIsParabolicQuadric(ps,true);
		order := (q^(d/2)-1)/(q-1)*(q^((d+2)/2-1)+1);
	elif type = "hyperbolic" then
		SetIsHyperbolicQuadric(ps,true);
		order := (q^((d+1)/2)-1)/(q-1)*(q^((d+1)/2-1)+1);
	elif type = "symplectic" then
		SetIsSymplecticSpace(ps,true);
		order := (q^(d+1)-1)/(q-1);
	elif type = "hermitian" then
		SetIsHermitianPolarSpace(ps,true);
		q := Sqrt(q);
        if d=3 then
            order := (q^3+1)*(q+1);
        else
            order := (q^d-(-1)^d)*(q^(d+1)-(-1)^(d+1))/(q^2-1);
        fi;
	fi;
	c1 := First(gram[1],x->not IsZero(x));
	if c1 = fail then
		return false;
	fi;
	c2 := First(st[1],x->not IsZero(x));
	result :=  c1*st = c2*gram;
	if result then
		SetClassicalGroupInfo( ps, rec(  degree := order ) );
	fi;
	return result;
end );

# ADDED 7/11/12 jdb
# Cmat adapted 20/3/14
#############################################################################
#O  CanonicalPolarSpace( <ps> )
# assume that f is the form determining <ps>, then this operation returns a 
# polar space <geo> determined by a form isometric with f, so <geo> is canonical 
# but not necessarily standard.
##
InstallMethod( CanonicalPolarSpace, 
	"for a polar space",
	[ IsClassicalPolarSpace ],
	function( ps )
		local type, canonicalps,d,f,form1,form2,isometric,b,mat1,mat2,canonicalmatrix,canonicalform, pq,types,max,reps,m,q,gram,creps;
		d := ps!.dimension;
		f := ps!.basefield;
		q := Size(f);
		type := PolarSpaceType( ps );
		if type = "hermitian" then
			canonicalps := HermitianPolarSpace(d, f);               
			SetIsHermitianPolarSpace(ps, true);            
		elif type = "symplectic" then
			canonicalps := SymplecticSpace(d, f);         
			SetIsSymplecticSpace(ps, true);
		elif type = "elliptic" then 
			canonicalps := EllipticQuadric(d, f);         
			SetIsEllipticQuadric(ps, true);
		elif type = "parabolic" then
			canonicalps := ParabolicQuadric(d, f);         
			SetIsParabolicQuadric(ps, true);
		elif type = "hyperbolic" then
			canonicalps := HyperbolicQuadric(d, f);         
			SetIsHyperbolicQuadric(ps, true);
		fi;
		if IsOddInt(Size(f)) and type in ["parabolic"] then
			form1 := QuadraticForm( ps );
			isometric := IsometricCanonicalForm(form1); #be careful with the notion Canonical in Forms package, its meaning is different than canonical in FinInG :-)
			gram := GramMatrix(isometric);
			canonicalmatrix := gram[1][1]*CanonicalGramMatrix("parabolic", d+1, f); 
			canonicalform := BilinearFormByMatrix(canonicalmatrix, f);
			canonicalps := PolarSpaceStandard( canonicalform, false );
			SetRankAttr( canonicalps, d/2 );
			types := TypesOfElementsOfIncidenceStructure( AmbientSpace(canonicalps) );
			SetTypesOfElementsOfIncidenceStructure(canonicalps, types{[1..d/2]});
			SetIsParabolicQuadric(canonicalps, true);
			SetIsCanonicalPolarSpace(canonicalps, true);
			SetIsStandardPolarSpace(canonicalps, false);
			SetPolarSpaceType(canonicalps, "parabolic");
			if RankAttr(canonicalps) = 2 then
				SetOrder( canonicalps, [q, q]);
			fi;
			max := CanonicalOrbitRepresentativeForSubspaces("parabolic", d+1, f)[1];

    ## Here we take the maximal totally isotropic subspace rep
    ## max and make representatives for lower dimensional subspaces

			reps := [ [ max[1] ] ]; #small change
			Append(reps, List([2..d/2], j -> max{[1..j]}));  

    ## We would like the representative to be stored as
    ## compressed matrices. Then they will be more efficient
    ## to work with.
  
			creps := List(reps,x->NewMatrix(IsCMatRep, f, d+1, x));
			creps[1] := creps[1][1]; #max is not empty, first element should always be a 1xn matrix->cvec.

			#for m in reps do
			#	if IsMatrix(m) then
			#		ConvertToMatrixRep(m,f);
			#	else 
			#		ConvertToVectorRep(m,f);
			#	fi;
			#od;   

    ## Wrap 'em up
			reps := List([1..d/2], j -> Wrap(canonicalps, j, creps[j]) ); #one more small change :-)
			SetRepresentativesOfElements(canonicalps, reps);
			SetClassicalGroupInfo( canonicalps, rec( degree := (q^(d/2)-1)/(q-1)*(q^((d+2)/2-1)+1) ) );
		fi;
		return canonicalps;
end );			


#############################################################################
# methods for some attributes.
#############################################################################
	
# CHECKED 25/09/11 jdb
#############################################################################
#A  QuadraticForm( <ps> )
# returns the quadratic form of which the polar space <ps> is the geometry.
# this is usefull since there are generic methods (q odd and even) thinkable 
# for orthogonal polar spaces. In such a case, we must use the quadratic form
# in the even case, and we can use it in the odd case. A typical example are 
# the enumerators. 
# IMPORTANT: this method will only be used when q is odd (and <ps> is orthogonal
# of course). In the q even case, the attribute will be set upon creation of the 
# polar space.
##
InstallMethod( QuadraticForm, 
	"for a polar space",
	[ IsClassicalPolarSpace ],
	function(ps)
		local form;
		form := SesquilinearForm(ps);
		if form!.type <> "orthogonal" then
			Error( "No quadratic form can be associated to this polar space" );
		fi;
		return QuadraticFormByBilinearForm(SesquilinearForm(ps));
	end );


# CHECKED 20/09/11 jdb
#############################################################################
#A  PolarSpaceType( <ps> )
# type of a polar space: see manual and conventions for orthogonal polar spaces.
##
InstallMethod( PolarSpaceType, 
	"for a polar space",
	[ IsClassicalPolarSpace and IsClassicalPolarSpaceRep ],
	function( ps )
		local ty, form, sesq;
		if HasQuadraticForm( ps ) then 
			form := QuadraticForm( ps );
		else 
			form := SesquilinearForm( ps );
		fi;
		ty := form!.type;
		if ty = "orthogonal" or ty = "quadratic" then
			if IsParabolicForm( form ) then
				ty := "parabolic";
			elif IsEllipticForm( form ) then
				ty := "elliptic";
			else
				ty := "hyperbolic";
			fi;
		fi;
		return ty;
	end );

# CHECKED 20/09/11 jdb
#############################################################################
#A  CompanionAutomorphism( <ps> )
# companion automorphism of polar space, i.e. the companion automorphism of 
# the sesquilinear form determining the polar space. If the form is quadratic, 
# or not hermitian, then the trivial automorphism is returned.
##
InstallMethod( CompanionAutomorphism, 
	"for a polar space",
	[ IsClassicalPolarSpace ],
	function( ps )
		local aut, type;
		type := PolarSpaceType( ps );
		aut := FrobeniusAutomorphism(ps!.basefield);
		if type = "hermitian" then
			aut := aut ^ (Order(aut)/2);
		else
			aut := aut ^ 0;
		fi;
		return aut;
	end );

#############################################################################
# jdb and ml will change ViewObj/PrintObj/Display methods now.`
# The ViewString is used e.g. in the ViewObj for flags.
#############################################################################

InstallMethod( ViewObj, 
	"for a polar space",
	[ IsClassicalPolarSpace and IsClassicalPolarSpaceRep ],
	function( p )
		Print("<polar space in ",AmbientSpace(p),": ",EquationForPolarSpace(p),"=0 >");
	end );

InstallMethod( ViewString, 
	"for a polar space",
	[ IsClassicalPolarSpace and IsClassicalPolarSpaceRep ],
	function( p )
		return Concatenation("<polar space in ",ViewString(AmbientSpace(p)),": ",String(EquationForPolarSpace(p)),"=0 >");
	end );

InstallMethod( ViewObj,
	"for an elliptic quadric",
	[ IsClassicalPolarSpace and IsClassicalPolarSpaceRep and IsEllipticQuadric],
	function( p )
		Print("Q-(",p!.dimension,", ",Size(p!.basefield),"): ",EquationForPolarSpace(p),"=0");
	end );

InstallMethod( ViewObj,
	"for a standard elliptic quadric",
	[ IsClassicalPolarSpace and IsClassicalPolarSpaceRep and IsEllipticQuadric and IsStandardPolarSpace],
	function( p )
		Print("Q-(",p!.dimension,", ",Size(p!.basefield),")");
	end );
	
InstallMethod( ViewString, 
	"for a standard elliptic quadric",
	[ IsClassicalPolarSpace and IsClassicalPolarSpaceRep and IsEllipticQuadric and IsStandardPolarSpace],
	function( p )
		return Concatenation("Q-(",String(p!.dimension),", ",String(Size(p!.basefield)),")");
	end );

InstallMethod( ViewString, 
	"for an elliptic quadric",
	[ IsClassicalPolarSpace and IsClassicalPolarSpaceRep and IsEllipticQuadric],
	function( p )
		return Concatenation("Q-(",String(p!.dimension),", ",
				String(Size(p!.basefield)),"): ",String(EquationForPolarSpace(p)),"=0");
	end );

InstallMethod( ViewObj,
	"for a symplectic space",
	[ IsClassicalPolarSpace and IsClassicalPolarSpaceRep and IsSymplecticSpace],
    function( p )
		Print("W(",p!.dimension,", ",Size(p!.basefield),"): ",EquationForPolarSpace(p),"=0");
	end );

InstallMethod( ViewObj,
	"for a standard symplectic space",
	[ IsClassicalPolarSpace and IsClassicalPolarSpaceRep and IsSymplecticSpace and IsStandardPolarSpace],
    function( p )
		Print("W(",p!.dimension,", ",Size(p!.basefield),")");
	end );

InstallMethod( ViewString, 
	"for a standard symplectic space",
	[ IsClassicalPolarSpace and IsClassicalPolarSpaceRep and IsSymplecticSpace and IsStandardPolarSpace],
	function( p )
		return Concatenation("W(",String(p!.dimension),", ",String(Size(p!.basefield)),")");
	end );

InstallMethod( ViewString, 
	"for a symplectic space",
	[ IsClassicalPolarSpace and IsClassicalPolarSpaceRep and IsSymplecticSpace],
	function( p )
		return Concatenation("W(",String(p!.dimension),", ",String(Size(p!.basefield)),"): ",
				EquationForPolarSpace(p),"=0");
	end );

InstallMethod( ViewObj,
	"for a parabolic quadric",
	[ IsClassicalPolarSpace and IsClassicalPolarSpaceRep and IsParabolicQuadric ],
    function( p )
		Print("Q(",p!.dimension,", ",Size(p!.basefield),"): ",EquationForPolarSpace(p),"=0");
	end);

InstallMethod( ViewObj,
	"for a standard parabolic quadric",
	[ IsClassicalPolarSpace and IsClassicalPolarSpaceRep and IsParabolicQuadric and IsStandardPolarSpace ],
    function( p )
		Print("Q(",p!.dimension,", ",Size(p!.basefield),")");
	end);

InstallMethod( ViewString, 
	"for a standard parabolic quadric",
	[ IsClassicalPolarSpace and IsClassicalPolarSpaceRep and IsParabolicQuadric and IsStandardPolarSpace ],
	function( p )
		return Concatenation("Q(",String(p!.dimension),", ",String(Size(p!.basefield)),")");
	end );

InstallMethod( ViewString, 
	"for a standard parabolic quadric",
	[ IsClassicalPolarSpace and IsClassicalPolarSpaceRep and IsParabolicQuadric ],
	function( p )
		return Concatenation("Q(",String(p!.dimension),", ",String(Size(p!.basefield)),"): ",
			String(EquationForPolarSpace(p)),"=0");
	end );

InstallMethod( ViewObj,
	"for a parabolic quadric",
	[ IsClassicalPolarSpace and IsClassicalPolarSpaceRep and IsParabolicQuadric ],
    function( p )
		Print("Q(",p!.dimension,", ",Size(p!.basefield),"): ",EquationForPolarSpace(p),"=0");
	end);

InstallMethod( ViewObj,
	"for a hyperbolic quadric",
	[ IsClassicalPolarSpace and IsClassicalPolarSpaceRep and IsHyperbolicQuadric ],
    function( p )
		Print("Q+(",p!.dimension,", ",Size(p!.basefield),"): ",EquationForPolarSpace(p),"=0");
	end);

InstallMethod( ViewObj,
	"for a standard hyperbolic quadric",
	[ IsClassicalPolarSpace and IsClassicalPolarSpaceRep and IsHyperbolicQuadric and IsStandardPolarSpace],
    function( p )
		Print("Q+(",p!.dimension,", ",Size(p!.basefield),")");
	end);

InstallMethod( ViewString, 
	"for a standard hyperbolic quadric",
	[ IsClassicalPolarSpace and IsClassicalPolarSpaceRep and IsHyperbolicQuadric and IsStandardPolarSpace],
	function( p )
		return Concatenation("Q+(",String(p!.dimension),", ",String(Size(p!.basefield)),")");
	end );

InstallMethod( ViewString, 
	"for a hyperbolic quadric",
	[ IsClassicalPolarSpace and IsClassicalPolarSpaceRep and IsHyperbolicQuadric],
	function( p )
		return Concatenation("Q+(",String(p!.dimension),", ",
				String(Size(p!.basefield)),"): ",String(EquationForPolarSpace(p)),"=0");
	end );

InstallMethod( ViewObj,
	"for a hermitian variety",
	[IsClassicalPolarSpace and IsClassicalPolarSpaceRep and IsHermitianPolarSpace ],
    function( p )
		Print("H(",p!.dimension,", ",Sqrt(Size(p!.basefield)),"^2): ",EquationForPolarSpace(p),"=0");
	end);

InstallMethod( ViewObj,
	"for a standard hermitian variety",
	[IsClassicalPolarSpace and IsClassicalPolarSpaceRep and IsHermitianPolarSpace and IsStandardPolarSpace],
    function( p )
		Print("H(",p!.dimension,", ",Sqrt(Size(p!.basefield)),"^2)");
    end);

InstallMethod( ViewString, 
	"for a standard hermitian quadric",
	[IsClassicalPolarSpace and IsClassicalPolarSpaceRep and IsHermitianPolarSpace and IsStandardPolarSpace],
	function( p )
		return Concatenation("H(",String(p!.dimension),", ",String(Sqrt(Size(p!.basefield))),"^2)");
	end );

InstallMethod( ViewString, 
	"for a hermitian quadric",
	[IsClassicalPolarSpace and IsClassicalPolarSpaceRep and IsHermitianPolarSpace],
	function( p )
		return Concatenation("H(",String(p!.dimension),", ",String(Sqrt(Size(p!.basefield))),"^2): ",
			EquationForPolarSpace(p),"=0");
	end );

InstallMethod( PrintObj, [ IsClassicalPolarSpace and IsClassicalPolarSpaceRep ],
  function( p )
    Print("PolarSpace(\n");
    if HasQuadraticForm(p) then
       Print(QuadraticForm(p));
    else
       Print(SesquilinearForm(p));
    fi;
    Print(", ", p!.basefield, " )");
  end );

InstallMethod( Display, [ IsClassicalPolarSpace and IsClassicalPolarSpaceRep ],
# fixed the output for rank (11/11/14 ml)
  function( p )
    Print("<polar space of rank ",RankAttr(p)," in PG(", p!.dimension, ", ", Size(p!.basefield), ")",">\n");
    if HasQuadraticForm(p) then
       Display(QuadraticForm(p));
    fi;
    Display(SesquilinearForm(p));
    #if HasDiagramOfGeometry( p ) then
    #   Display( DiagramOfGeometry( p ) );
    #fi;
  end );

InstallMethod( PrintObj,
  [ IsClassicalPolarSpace and IsClassicalPolarSpaceRep and IsEllipticQuadric ],
        function( p )
          Print("EllipticQuadric(",p!.dimension,",",p!.basefield,"): ",EquationForPolarSpace(p),"=0");
        end );

InstallMethod( Display, 
  [ IsClassicalPolarSpace and IsClassicalPolarSpaceRep and IsEllipticQuadric ],
  function( p )
    Print("Q-(",p!.dimension,", ",Size(p!.basefield),")\n");
    if HasQuadraticForm(p) then
       Display(QuadraticForm(p));
    fi;
    Display(SesquilinearForm(p));
    #if HasDiagramOfGeometry( p ) then
    #   Display( DiagramOfGeometry( p ) );
    #fi;
  end );

InstallMethod( PrintObj,
  [ IsClassicalPolarSpace and IsClassicalPolarSpaceRep and IsSymplecticSpace ],
        function( p )
          Print("SymplecticSpace(",p!.dimension,",",p!.basefield,"): ",EquationForPolarSpace(p),"=0");
  end);

InstallMethod( Display, 
  [ IsClassicalPolarSpace and IsClassicalPolarSpaceRep and IsSymplecticSpace ],
  function( p )
    Print("W(",p!.dimension,", ",Size(p!.basefield),")\n");
    Display(SesquilinearForm(p));
    #if HasDiagramOfGeometry( p ) then
    #   Display( DiagramOfGeometry( p ) );
    #fi;
  end );

InstallMethod( PrintObj,
  [ IsClassicalPolarSpace and IsClassicalPolarSpaceRep and IsParabolicQuadric ],
        function( p )
          Print("ParabolicQuadric(",p!.dimension,",",p!.basefield,"): ",EquationForPolarSpace(p),"=0");
  end);

InstallMethod( Display, 
  [ IsClassicalPolarSpace and IsClassicalPolarSpaceRep and IsParabolicQuadric ],
  function( p )
    Print("Q(",p!.dimension,", ",Size(p!.basefield),")\n");
    if HasQuadraticForm(p) then
       Display(QuadraticForm(p));
    fi;
    Display(SesquilinearForm(p));
    #if HasDiagramOfGeometry( p ) then
    #   Display( DiagramOfGeometry( p ) );
    #fi;
  end );

InstallMethod( PrintObj,
  [ IsClassicalPolarSpace and IsClassicalPolarSpaceRep and IsHyperbolicQuadric ],
        function( p )
          Print("HyperbolicQuadric(",p!.dimension,",",p!.basefield,"): ",EquationForPolarSpace(p),"=0");
        end);

InstallMethod( Display, 
  [ IsClassicalPolarSpace and IsClassicalPolarSpaceRep and IsHyperbolicQuadric ],
  function( p )
    Print("Q+(",p!.dimension,", ",Size(p!.basefield),")\n");
    if HasQuadraticForm(p) then
       Display(QuadraticForm(p));
    fi;
    Display(SesquilinearForm(p));
    #if HasDiagramOfGeometry( p ) then
    #   Display( DiagramOfGeometry( p ) );
    #fi;
  end );

InstallMethod( PrintObj,
  [IsClassicalPolarSpace and IsClassicalPolarSpaceRep and IsHermitianPolarSpace ],
        function( p )
          Print("HermitianPolarSpace(",p!.dimension,",",p!.basefield,"): ",EquationForPolarSpace(p),"=0");
        end);

InstallMethod( Display, 
  [ IsClassicalPolarSpace and IsClassicalPolarSpaceRep and IsHermitianPolarSpace ],
  function( p )
    Print("H(",p!.dimension,", ",Size(p!.basefield),")\n");
    Display(SesquilinearForm(p));
    #if HasDiagramOfGeometry( p ) then
    #   Display( DiagramOfGeometry( p ) );
    #fi;
  end );

#############################################################################
## The following methods are needed for "naked" polar spaces; those
## that on conception are devoid of many of the attributes that
## the "canonical" polar spaces exhibit (see the code for the
## "SymplecticSpace" as an example).
#############################################################################

# CHECKED 21/09/11 jdb
#############################################################################
#O  IsomorphismCanonicalPolarSpace( <ps> )
# This method returns a geometry morphism that is in fact the coordinate transformation
# going FROM the standard polar space TO the given polar space <ps>
##
InstallMethod( IsomorphismCanonicalPolarSpace, 
	"for a polar space",
	[ IsClassicalPolarSpace and IsClassicalPolarSpaceRep ],
	function( ps )
		local d, f, type, canonical, iso, info;
		d := ps!.dimension;
		f := ps!.basefield;
		type := PolarSpaceType( ps );
		if type = "hermitian" then
			canonical := HermitianPolarSpace(d, f);               
			SetIsHermitianPolarSpace(ps, true);            
		elif type = "symplectic" then
			canonical := SymplecticSpace(d, f);         
			SetIsSymplecticSpace(ps, true);
		elif type = "elliptic" then 
			canonical := EllipticQuadric(d, f);         
			SetIsEllipticQuadric(ps, true);
		elif type = "parabolic" then
			canonical := ParabolicQuadric(d, f);         
			SetIsParabolicQuadric(ps, true);
		elif type = "hyperbolic" then
			canonical := HyperbolicQuadric(d, f);         
			SetIsHyperbolicQuadric(ps, true);
		fi;
		iso := IsomorphismPolarSpacesNC(canonical, ps, false);      ## jb: no longer computes nice monomorphism here
		info := ClassicalGroupInfo( canonical );
		SetClassicalGroupInfo( ps, rec( degree := info!.degree ) );
		return iso;
	end );

#############################################################################
#O  IsomorphismCanonicalPolarSpaceWithIntertwiner( <ps> )
# This method returns a geometry morphism that is in fact the coordinate transformation
# going FROM the standard polar space TO the given polar space <ps>, with intertwiner
##
InstallMethod( IsomorphismCanonicalPolarSpaceWithIntertwiner, 
	"for a polar space",
	[ IsClassicalPolarSpace and IsClassicalPolarSpaceRep ],
	function( ps )
		local d, f, type, canonical, iso, info;
		d := ps!.dimension;
		f := ps!.basefield;
		type := PolarSpaceType( ps );
		if type = "hermitian" then
			canonical := HermitianPolarSpace(d, f);               
			SetIsHermitianPolarSpace(ps, true);            
		elif type = "symplectic" then
			canonical := SymplecticSpace(d, f);         
			SetIsSymplecticSpace(ps, true);
		elif type = "elliptic" then 
			canonical := EllipticQuadric(d, f);         
			SetIsEllipticQuadric(ps, true);
		elif type = "parabolic" then
			canonical := ParabolicQuadric(d, f);         
			SetIsParabolicQuadric(ps, true);
		elif type = "hyperbolic" then
			canonical := HyperbolicQuadric(d, f);         
			SetIsHyperbolicQuadric(ps, true);
		fi;
		iso := IsomorphismPolarSpacesNC(canonical, ps, true); 
		info := ClassicalGroupInfo( canonical );
		SetClassicalGroupInfo( ps, rec( degree := info!.degree ) );
		return iso;
	end );

#############################################################################
#More attributes...
#############################################################################

# CHECKED 21/09/11 jdb
#############################################################################
#O  RankAttr( <ps> )
# returns the rank of the polar space <ps>, which is the Witt index of the
# defining form.
##
InstallMethod( RankAttr,
	"for a polar space",
	[ IsClassicalPolarSpace and IsClassicalPolarSpaceRep ],
	function( ps )
		local iso;
		iso := IsomorphismCanonicalPolarSpace( ps );
		return RankAttr(Source(iso)!.geometry);
  end );

# CHECKED 21/09/11 jdb
#############################################################################
#O  TypesOfElementsOfIncidenceStructure( <ps> )
# Well known method that returns a list with the names of the elements of 
# the polar space <ps>
##
InstallMethod( TypesOfElementsOfIncidenceStructure, 
	"for a polar space ",
	[ IsClassicalPolarSpace and IsClassicalPolarSpaceRep ],
	function( ps )
		local iso;
		iso := IsomorphismCanonicalPolarSpace( ps );
		return TypesOfElementsOfIncidenceStructure(Source(iso)!.geometry);
	end );

#The next two methods are wrong. THe TypesOfElementsOfIncidenceStructure are determined by the
#rank of the polar space, not the projective dimenion.
#InstallMethod( TypesOfElementsOfIncidenceStructure, "for a polar space", 
#  [IsClassicalPolarSpace],
#  function( ps )
#    local d,i,types;
#    types := ["point"];
#    d := ProjectiveDimension(ps);
#    if d >= 2 then Add(types,"line"); fi;
#    if d >= 3 then Add(types,"plane"); fi;
#    if d >= 4 then Add(types,"solid"); fi;
#    for i in [5..d] do
#        Add(types,Concatenation("proj. subspace of dim. ",String(i-1)));
#    od;
#    return types;
#  end );

#InstallMethod( TypesOfElementsOfIncidenceStructurePlural, "for a polar space",
#  [IsClassicalPolarSpace],
#  function( ps )
#    local d,i,types;
#    types := ["points"];
#    d := ProjectiveDimension(ps);
#    if d >= 2 then Add(types,"lines"); fi;
#    if d >= 3 then Add(types,"planes"); fi;
#    if d >= 4 then Add(types,"solids"); fi;
#    for i in [5..d] do
#        Add(types,Concatenation("proj. subspaces of dim. ",String(i-1)));
#    od;
#    return types;
#  end );

# CHECKED 22/09/11 jdb
#############################################################################
#O  TypesOfElementsOfIncidenceStructurePlural( <ps> )
# Well known method that returns a list with the names of the elements of 
# the polar space <ps>
##
InstallMethod( TypesOfElementsOfIncidenceStructurePlural, 
	"for a polar space",  
	[IsClassicalPolarSpace],
	function( ps )
		local d,i,types;
		types := ["points"];
		d := Rank(ps);
		if d >= 2 then Add(types,"lines"); fi;
		if d >= 3 then Add(types,"planes"); fi;
		if d >= 4 then Add(types,"solids"); fi;
		for i in [5..d] do
			Add(types,Concatenation("proj. subspaces of dim. ",String(i-1)));
		od;
		return types;
	end );

# CHECKED 21/09/11 jdb
#############################################################################
#O  Order( <ps> )
# If <ps> has rank two, then it is a GQ, and we can ask its order.
##
InstallOtherMethod( Order, 
	"for a polar space",
	[ IsClassicalPolarSpace and IsClassicalPolarSpaceRep ],
	function( ps )
		if RankAttr(ps) = 2 then
			return Order(Source(IsomorphismCanonicalPolarSpace(ps) )!.geometry );
		else
			Error("Rank is not 2");
		fi;
	end );
  
# CHECKED 21/09/11 jdb
#############################################################################
#O  RepresentativesOfElements( <ps> )
# returns an element of each type of the polar space <ps>
##
InstallMethod( RepresentativesOfElements, 
	"for a polar space",
	[ IsClassicalPolarSpace and IsClassicalPolarSpaceRep ],
	function( ps )
		local iso, reps;
		iso := IsomorphismCanonicalPolarSpace( ps );
		reps := RepresentativesOfElements( Source( iso )!.geometry );
		return ImagesSet(iso, reps);
	end );

#############################################################################
# one more operation...
#############################################################################

# CHECKED 21/09/11 jdb
#############################################################################
#O  QUO( <ps>, <v> )
# returns the quotien space of the element <v>, which must be an element of the
# polar space <ps>
##
InstallOtherMethod(QUO,
	"for a polar space and an element of a polar space",
	[ IsClassicalPolarSpace and IsClassicalPolarSpaceRep, IsSubspaceOfClassicalPolarSpace],
	function( ps, v )
		if not v in ps then 
			Error( "Subspace does not belong to the polar space" );
		fi;
		return Range( NaturalProjectionBySubspace( ps, v ) )!.geometry;
	end );

#############################################################################
# Counting the number of elements of the polar spaces
#############################################################################

# CHECKED 21/09/11 jdb
# question: can we not use the NumberOfTotallySingularSubspaces for this?
#############################################################################
#O  Size( <vs> )
# returns the number of elements on <vs>, a collection of elements of a polar
# space of a given type.
##
InstallMethod( Size, 
	"for a collection of elements of a polar space",
	[IsSubspacesOfClassicalPolarSpace],
	function( vs )
		local geom, q, d, m, ovnum, ordominus, numti, gauss, flavour;
		geom := vs!.geometry; 
		q := Size(vs!.geometry!.basefield);
		d := vs!.geometry!.dimension + 1;
		m := vs!.type;
		flavour := PolarSpaceType(geom);
        if flavour = "symplectic" then
			return Product(List([0..(m-1)],i->(q^(d-2*i)-1)/(q^(i+1)-1)));
		fi;
		if flavour = "elliptic" then
			gauss:=Product(List([0..(m-1)],i->(q^(d/2-1)-q^i)/(q^m-q^i)));
			numti:=gauss * Product(List([0..(m-1)],j->q^(d/2-j)+1));
			return numti;
		fi;
		if flavour = "hyperbolic" then
			gauss:=Product(List([0..(m-1)],i->(q^(d/2)-q^i)/(q^m-q^i)));
			numti:=gauss * Product(List([0..(m-1)],j->q^(d/2-1-j)+1));
			return numti;
		fi;
		if flavour = "parabolic" then
			gauss:=Product(List([0..(m-1)],i->(q^((d-1)/2)-q^i)/(q^m-q^i)));
			numti:=gauss * Product(List([0..(m-1)],j->q^( (d+1)/2-1-j)+1));
			return numti;
		fi;
		if flavour = "hermitian" then
			return Product(List([(d+1-2*m)..d],i->(Sqrt(q)^i-(-1)^i)))/
			Product(List([1..m],j->q^j-1 ));
		fi;
		Error("Unknown polar space type!");
	end );

#############################################################################
# Elements -- Subspaces
#############################################################################

## The following needs more work: I think it's ok now. jdb
## jb 19/02/2009: Just changed the compressed matrix methods
##               so that length 1 matrices are allowed.

###cmat methods need to be added here. VectorSpaceToElement checks wheter 
# a vectorspace really makes an element of a polar space by checking 
# whether it is totally isotropic. Therefore we need some forms functionality.
# Since forms is not working with cmat/cvecs, and this will not change, we have
# to unpack a cmat before piping it to forms. Hence it makes sens to create VectorSpaceToElement
# methods the lazy way: just unpack and pipe to an existing VectorSpaceToElement method.
###

# Added 20/3/14 jdb
#############################################################################
#O  VectorSpaceToElement( <geom>, <v> ) returns the elements in <geom> determined
# by the vectorspace <v>. 
##
InstallMethod( VectorSpaceToElement, 
	"for a polar space and a CMat",
	[IsClassicalPolarSpace, IsCMatRep],
	function( geom, v )
		return VectorSpaceToElement(geom,Unpack(v));
	end );
	
# Added 20/3/14 jdb
#############################################################################
#O  VectorSpaceToElement( <geom>, <v> ) returns the elements in <geom> determined
# by the vectorspace <v>. 
##
InstallMethod( VectorSpaceToElement, 
	"for a polar space and a cvec",
	[IsClassicalPolarSpace, IsCVecRep],
	function( geom, v )
		return VectorSpaceToElement(geom,Unpack(v));
	end );

# CHECKED 21/09/11 jdb
# cmat change 20/3/14.
# changed 19/01/16 (jdb): by a change of IsPlistRep, this method gets also
# called when using a row vector, causing a problem with TriangulizeMat.
# a solution was to add IsMatrix.
#############################################################################
#O  VectorSpaceToElement( <geom>, <v> ) returns the elements in <geom> determined
# by the vectorspace <v>. Several checks are built in. 
##
# JB: Fixed a strange error in here
# I had Q+(11,3) (with a different form than usual) and tried to wrap
# [ [ 0*Z(3), 0*Z(3), Z(3)^0, 0*Z(3), 0*Z(3), 0*Z(3), Z(3), 0*Z(3), 0*Z(3), 0*Z(3), 0*Z(3), 0*Z(3) ] ]
# It kept on returning 0*Z(3).
# JDB: I kept John's historical note, but the code where the bug was, has disappeared with the cmat adaptation :-)
# Information on NewMatrix us here can be found in projectivespaces.gi, method for VectorSpaceToElement.
#
InstallMethod( VectorSpaceToElement, 
	"for a polar space and a Plist",
	[IsClassicalPolarSpace, IsPlistRep and IsMatrix],
	function( geom, v )
		local  x, n, i, y;
		## when v is empty... 
		if IsEmpty(v) then
			Error("<v> does not represent any element");
		fi;
        if not IsMatrix(v) then #next 3 lines: patch of Max Horn (16/2/16)
            TryNextMethod();
        fi;

		x := MutableCopyMat(v);
		TriangulizeMat(x);
		## dimension should be correct
		if Length(v[1]) <> geom!.dimension + 1 then
			Error("Dimensions are incompatible");
		fi;
		## Remove zero rows. It is possible the the user
		## has inputted a matrix which does not have full rank
        n := Length(x);
		i := 0;
		while i < n and ForAll(x[n-i], IsZero) do
			i := i+1; 
		od;
		if i = n then
			return EmptySubspace(geom);
		fi;
		x := x{[1..n-i]};
		#if we check later on the the subspace is an element of the polar space, then
		#the user cannot create the whole polar space like this, so I commented out the
		# next three lines.
		#if Length(x)=ProjectiveDimension(geom)+1 then
		#	return geom;
		#fi;
        ## check here that it is in the polar space. 
		if HasQuadraticForm(geom) then
			if not IsTotallySingularSubspace(QuadraticForm(geom),x) then
				Error("<x> does not generate an element of <geom>");
			fi;
		else
			if not IsTotallyIsotropicSubspace(SesquilinearForm(geom),x) then
				Error("<x> does not generate an element of <geom>");
			fi;
		fi;
		y := NewMatrix(IsCMatRep,geom!.basefield,Length(x[1]),x);
		if Length(y) = 1 then
			return Wrap(geom, 1, y[1]);
		else
			return Wrap(geom, Length(y), y);
		fi;
	end );

# CHECKED 21/09/11
# cmat changed 20/3/14.
#############################################################################
#O  VectorSpaceToElement( <geom>, <v> ) returns the elements in <geom> determined
# by the vectorspace <v>. Several checks are built in. 
##
InstallMethod( VectorSpaceToElement,	
	"for a compressed GF(2)-matrix",
	[IsClassicalPolarSpace, IsGF2MatrixRep],
	function( geom, v )
		local  x, n, i, y;
		if IsEmpty(v) then
			Error("<v> does not represent any element");
		fi;
		x := MutableCopyMat(v);
		TriangulizeMat(x);
		## remove zero rows
		n := Length(x);
		i := 0;
		while i < n and ForAll(x[n-i], IsZero) do
			i := i+1; 
		od;
		if i = n then
			return EmptySubspace(geom);
		fi;
		x := x{[1..n-i]};
		#x := x{[1..n-i]};
		#if we check later on the the subspace is an element of the polar space, then
		#the user cannot create the whole polar space like this, so I commented out the
		# next three lines.
		#if Length(x)=ProjectiveDimension(geom)+1 then
		#	return geom;
		#fi;
        ## check here that it is in the polar space. 
		if HasQuadraticForm(geom) then
			if not IsTotallySingularSubspace(QuadraticForm(geom),x) then
				Error("<x> does not generate an element of <geom>");
			fi;
		else
			if not IsTotallyIsotropicSubspace(SesquilinearForm(geom),x) then
				Error("<x> does not generate an element of <geom>");
			fi;
		fi;
		y := NewMatrix(IsCMatRep,geom!.basefield,Length(x[1]),x);
		if Length(y) = 1 then
			return Wrap(geom, 1, y[1]);
		else
			return Wrap(geom, Length(y), y);
		fi;
  end );
  
# CHECKED 21/09/11
# cmat changed 20/3/14.
#############################################################################
#O  VectorSpaceToElement( <geom>, <v> ) returns the elements in <geom> determined
# by the vectorspace <v>. Several checks are built in. 
##
InstallMethod( VectorSpaceToElement, 
	"for a compressed basis of a vector subspace",
	[IsClassicalPolarSpace, Is8BitMatrixRep],
	function( geom, v )
		local  x, n, i,y; 
		if IsEmpty(v) then
			Error("<v> does not represent any element");
		fi;
		x := MutableCopyMat(v);
		TriangulizeMat(x);
	## dimension should be correct
		if Length(v[1]) <> geom!.dimension + 1 then
			Error("Dimensions are incompatible");
		fi;	
	## remove zero rows
		n := Length(x);
		i := 0;
		while i < n and ForAll(x[n-i], IsZero) do
			i := i+1; 
		od;
		if i = n then
			return EmptySubspace(geom);
		fi;
		x := x{[1..n-i]};
		#if we check later on the the subspace is an element of the polar space, then
		#the user cannot create the whole polar space like this, so I commented out the
		# next three lines.
		#if Length(x)=ProjectiveDimension(geom)+1 then
		#	return geom;
		#fi;
      
		## check here that it is in the polar space. 
		if HasQuadraticForm(geom) then
			if not IsTotallySingularSubspace(QuadraticForm(geom),x) then
				Error("<x> does not generate an element of <geom>");
			fi;
		else
			if not IsTotallyIsotropicSubspace(SesquilinearForm(geom),x) then
				Error("<x> does not generate an element of <geom>");
			fi;
		fi;
		y := NewMatrix(IsCMatRep,geom!.basefield,Length(x[1]),x);
		if Length(y) = 1 then
			return Wrap(geom, 1, y[1]);
		else
			return Wrap(geom, Length(y), y);
		fi;
end );

# CHECKED 21/09/11 jdb
# cvec version 20/3/14
#############################################################################
#O  VectorSpaceToElement( <geom>, <v> ) returns the elements in <geom> determined
# by the rowvector <v>. Several checks are built in.
##
InstallMethod( VectorSpaceToElement, 
	"for a row vector",
	[IsClassicalPolarSpace, IsRowVector],
	function( geom, v )
		local  x,y;
		## dimension should be correct
		if Length(v) <> geom!.dimension + 1 then
			Error("Dimensions are incompatible");
		fi;
		x := ShallowCopy(v);
		if IsZero(x) then
			return EmptySubspace(geom);
		else
			if HasQuadraticForm(geom) then
				if not IsSingularVector(QuadraticForm(geom),x) then
					Error("<v> does not generate an element of <geom>");
				fi;
			else
				if not IsIsotropicVector(SesquilinearForm(geom),x) then
					Error("<v> does not generate an element of <geom>");
				fi;
			fi;
			MultRowVector(x,Inverse( x[PositionNonZero(x)] ));
            y := NewMatrix(IsCMatRep,geom!.basefield,Length(x),[x]);
			return Wrap(geom, 1, y[1]);
		fi;
  end );

# CHECKED 22/09/11 jdb 
# cvec version 20/3/14
#############################################################################
#O  VectorSpaceToElement( <geom>, <v> ) returns the elements in <geom> determined
# by the rowvector <v>. Several checks are built in.
##
InstallMethod( VectorSpaceToElement, 
	"for a polar space and an 8-bit vector",
	[IsClassicalPolarSpace, Is8BitVectorRep],
	function( geom, v )
		local  x, n, i,y;
		## when v is empty...
		if IsEmpty(v) then
			return EmptySubspace(geom);
		fi;
		x := ShallowCopy(v);
		## dimension should be correct
		if Length(v) <> geom!.dimension + 1 then
			Error("Dimensions are incompatible");
		fi;
		## We must also compress our vector.
		#ConvertToVectorRep(x, geom!.basefield);
		## bad characters, such as jdb, checked this with input zero vector...
		if IsZero(x) then
			return EmptySubspace(geom);
		else
			if HasQuadraticForm(geom) then
				if not IsSingularVector(QuadraticForm(geom),x) then
					Error("<v> does not generate an element of <geom>");
				fi;
			else
				if not IsIsotropicVector(SesquilinearForm(geom),x) then
					Error("<v> does not generate an element of <geom>");
				fi;
			fi;			
			MultRowVector(x,Inverse( x[PositionNonZero(x)] ));
			y := NewMatrix(IsCMatRep,geom!.basefield,Length(x),[x]);
			return Wrap(geom, 1, y[1]);
		fi;
	end );

# CHECKED 22/09/11 jdb  
# Changed 28/11/11 jdb + pc, according to remark in tiny optimalisations.
# added a comment 30/11/11
# cmat version: Unpack before piping to forms. (20/3/14)
#############################################################################
#O  \in( <w>, <ps> ) true if the element <w> is contained in <ps>
# remarks: should we change this? I mean: this method makes a projective subspace
# suddenly into a polar space subspace, if this thest is true. Can cause weird thing 
# when displaying objects. We changed it, see commented out stuff below.
# 30/11/11: caveat: just doing w!.geo := ps; changes the ambient geometry of x,
# but it does not change the categories of x. So maybe x belongs to the polar space then
# but not to the category IsSubspaceOfClassicalPolarSpace. So some operations might stay
# unapplicable anyway. So changing w!.geo has no advantages. 
##
#I changed this, I now use the built in functions of forms to perform the test.
#jdb 6/1/8
InstallMethod( \in, 
	"for an element of a polar space and a polar space",
	[IsElementOfIncidenceStructure, IsClassicalPolarSpace],
	function( w, ps )
		local form, r, ti;
    # if the vector spaces don't agree we can't go any further
		if w!.geo!.dimension <> ps!.dimension or
			not IsSubset(ps!.basefield, w!.geo!.basefield) then
			return false;
		fi;
		r := Unpack(w!.obj); #here is the cmat change.
		if w!.type = 1 then r := [r]; fi;
    # check if the subspace is totally isotropic/singular
		if HasQuadraticForm(ps) then
			form := QuadraticForm(ps);
			ti := IsTotallySingularSubspace(form,r);
		else
			form := SesquilinearForm(ps);
			ti:= IsTotallyIsotropicSubspace(form,r); 
		fi;
    # if yes, make it an element of the polar space.
	#	if not IsSubspaceOfClassicalPolarSpace(w) and ti then
	#		w!.geo := ps;
	#	fi;
		return ti;
	end );

#ADDED 30/11/2011 jdb.
#############################################################################
#O  Span( <x>, <y>, <b> )
# returns <x,y>, <x> and <y> two subspaces of a polar space and a boolean
##
InstallMethod( Span, 
	"for two subspaces of a polar space",
	[IsSubspaceOfProjectiveSpace, IsSubspaceOfProjectiveSpace, IsBool],
	function( x, y, b )
	## This method is quicker than the old one
	local ux, uy, typx, typy, span, vec, spanel;
	if not b then
	  return Span(x,y);
	fi;
	typx := x!.type;
	typy := y!.type;
	vec := x!.geo!.vectorspace;
	if vec = y!.geo!.vectorspace then
		ux := Unwrap(x);
		uy := Unwrap(y);
		if typx = 1 then ux := [ux]; fi;
		if typy = 1 then uy := [uy]; fi;
		span := SumIntersectionMat(ux, uy)[1];	
		if Length(span) < vec!.DimensionOfVectors then
			spanel := VectorSpaceToElement( AmbientSpace(x!.geo), span);
			if IsIdenticalObj(x!.geo,y!.geo) then
				if spanel in x!.geo then
				  return VectorSpaceToElement( x!.geo, span);
				else
				  return spanel;
				fi;
			else
				return spanel;
			fi;
		else
			return AmbientSpace(x!.geo);
		fi;
	else
		Error("Subspaces belong to different ambient spaces");
	fi;
	end );
	


#ADDED 30/11/2011 jdb.
# cvec note (20/3/14): SumIntersectionMat seems to be incompatible with
# cvecs. So I will Unpack, do what I have to do, and get what I want
# since VectorSpaceToElement is helping here.
#############################################################################
#O  Meet( <x>, <y> )
# returns the intersection of <x> and <y>, two subspaces of a polar space.
##
InstallMethod( Meet,
	"for two subspaces of a polar space",
	[IsSubspaceOfClassicalPolarSpace, IsSubspaceOfClassicalPolarSpace],
	function( x, y )
		local ux, uy, typx, typy, int, f, rk;
		typx := x!.type;
		typy := y!.type;
		if x!.geo!.vectorspace = y!.geo!.vectorspace then 
			ux := Unpack(Unwrap(x)); 
			uy := Unpack(Unwrap(y));
			if typx = 1 then ux := [ux]; fi;
			if typy = 1 then uy := [uy]; fi;
			f := x!.geo!.basefield; 
			int := SumIntersectionMat(ux, uy)[2];
			if not int=[] then 
				if IsIdenticalObj(x!.geo,y!.geo) then
					return VectorSpaceToElement(x!.geo,int);
				else
					return VectorSpaceToElement( AmbientSpace(x), int);
				fi;
			else 
				return EmptySubspace(AmbientSpace(x));
			fi;
		else
			Error("Subspaces belong to different ambient spaces");
		fi;
  end );

#############################################################################
# Collection of elements (of given type)
#############################################################################

# CHECKED 22/09/11 jdb
#############################################################################
#O  ElementsOfIncidenceStructure( <ps>, <j> )
# returns the elements of the polar space <ps> of type <j>
## 
InstallMethod( ElementsOfIncidenceStructure, 
	"for a polar space and an integer",
	[IsClassicalPolarSpace and IsClassicalPolarSpaceRep, IsPosInt],
	function( ps, j )
		local r;
		r := Rank(ps);
		if j > r then
			Error("<geo> has no elements of type <j>");
		else
			#Print("grapje\n");
			return Objectify(
			NewType( ElementsCollFamily, IsSubspacesOfClassicalPolarSpace and
                                     IsSubspacesOfClassicalPolarSpaceRep ),
			rec(
				geometry := ps,
				type := j,
				size := NumberOfTotallySingularSubspaces(ps, j)
				)
			);
		fi;
	end);

# CHECKED 22/09/11 jdb
#############################################################################
#O  ElementsOfIncidenceStructure( <ps> )
# returns all the elements of the polar space <ps> 
## 
InstallMethod( ElementsOfIncidenceStructure, 
	"for a polar space",
	[IsClassicalPolarSpace and IsClassicalPolarSpaceRep],
	function( ps )
		return Objectify(
			NewType( ElementsCollFamily, IsAllElementsOfIncidenceStructure ),
			rec( geometry := ps )
				);
	end);

#the next four operations are not necessary any more, the generic methods for them
# in Lie geometry are used.

#InstallMethod( Points, [IsClassicalPolarSpace],
#  function( ps )
#    return ElementsOfIncidenceStructure(ps, 1);
#  end);

#InstallMethod( Lines, [IsClassicalPolarSpace],
#  function( ps )
#    return ElementsOfIncidenceStructure(ps, 2);
#  end);

#InstallMethod( Planes, [IsClassicalPolarSpace],
#  function( ps )
#    return ElementsOfIncidenceStructure(ps, 3);
#  end);

#InstallMethod( Solids, [IsClassicalPolarSpace],
#  function( ps )
#    return ElementsOfIncidenceStructure(ps, 4);
#  end);


# CHECKED 22/09/11 jdb
# question: can we use this function for Size?
#############################################################################
#O  NumberOfTotallySingularSubspaces( <ps>, <j> )
# returns the number of elements of type <j> of the polar space <ps>
## 
InstallMethod( NumberOfTotallySingularSubspaces, 
	"for a polar space and an integer",
	[IsClassicalPolarSpace, IsPosInt],
	function(ps, j)

  ## In a classical finite polar space of rank r with parameters (q,q^e), 
  ## the number of subspaces of algebraic dimension j is given by
  ## [d, n] Prod_{i=1}^{n} (q^{r+e-i}+1)

		local r, d, type, e, q, qe;
		r := RankAttr(ps);
		d := ps!.dimension;
		type := PolarSpaceType(ps);
		q := Size(ps!.basefield); 
		if type = "elliptic" then e := 2; qe := q^e;
		elif type = "hyperbolic" then e:= 0; qe := q^e;
		elif type = "parabolic" or type = "symplectic" then e:=1; qe := q^e;
		elif type = "hermitian" and IsEvenInt(ps!.dimension) then e:=3; 
			qe := RootInt(q,2)^e;
		elif type = "hermitian" and IsOddInt(ps!.dimension) then e:=1; 
			qe := RootInt(q,2)^e;
		else Error("Polar space doesn't know its type!");
		fi;

		return Size(Subspaces(GF(q)^r, j)) * Product(List([1..j], i -> q^(r-i) * qe +1));
	end);

# CHECKED 17/03/14 jdb
# cmat version. Same consideration as elsewhere: forms does not use cmats, so unpack before piping to form.
#############################################################################
#O  TypeOfSubspace( <ps>, <w> )
# returns the type of the polar space induced by <ps> in the projective subspace
# <w>
## 
InstallMethod( TypeOfSubspace,
	"for a polar space and a subspace of a projective geometry",
     [ IsClassicalPolarSpace, IsSubspaceOfProjectiveSpace ],
	function( ps, w )
  
    ## At the moment, this is a helper operation, but perhaps
    ## it could be used by the user. This operation returns
    ## "degenerate", "hermitian", "symplectic", "elliptic",
    ## "hyperbolic", or "parabolic".
    
		local pstype, dim, type, mat, gf, q, form, newform,uw;
		pstype := PolarSpaceType( ps );
		gf := ps!.basefield;             
		q := Size(gf);
		dim := w!.type;

		if pstype in ["elliptic", "parabolic", "hyperbolic"] and IsEvenInt(q) then
			form := QuadraticForm( ps );
			uw := Unpack(w!.obj); #here is an unpack cmat change
			mat := uw * form!.matrix * TransposedMat(uw);
			newform := QuadraticFormByMatrix( mat, gf );

#  jdb makes a change here. it was 'IsDegenerateForm( newform )' but it is clear that we want to test whether
#  the quadratic form is *singular*, according to the definition in forms about degenerecy and singularity

			if IsSingularForm( newform ) then
				return "degenerate";    
			elif IsOddInt(dim) then
				return "parabolic";
			else
				if IsHyperbolicForm( newform ) then
					return "hyperbolic";
				else 
					return "elliptic";
				fi;
			fi;
		else
			form := SesquilinearForm( ps );
			#mat := w!.obj * form!.matrix * TransposedMat(w!.obj); #I will use something more advanced to include hermitian forms.
			uw := Unpack(w!.obj); #here is an unpack cmat change
			mat := [uw,uw]^form; 
			if pstype = "symplectic" then
				newform := BilinearFormByMatrix( mat, gf );
				if IsDegenerateForm( newform ) then
					return "degenerate";
				else
					return "symplectic";
				fi;
			elif pstype = "hermitian" then
				#if IsHermitianMatrix( mat, gf ) then #I think this is a mistake
				newform := HermitianFormByMatrix(mat,gf);
				if IsDegenerateForm( newform ) then
					return "degenerate";
				else
					return "hermitian";
				fi;
			elif pstype in ["elliptic", "parabolic", "hyperbolic"] then
				newform := BilinearFormByMatrix( mat, gf );
				if IsDegenerateForm( newform ) then
					return "degenerate";
				else
					if IsEllipticForm(newform) then
						return "elliptic";
					elif IsHyperbolicForm(newform) then
						return "hyperbolic";
					else
						return "parabolic";
					fi;             
				fi;
			fi;
		fi;
    
		Print("Polar space does not have a recognisable type\n");
		return;
	end );

#############################################################################
# Methods to create flags.
#############################################################################

# ADDED 31/3/2014 jdb
# checks added 17/12/14 jdb
#############################################################################
#O  FlagOfIncidenceStructure( <ps>, <els> )
# returns the flag of the projective space <ps> with elements in <els>.
# the method checks whether the input really determines a flag.
# Compare the checks with the same method for projective space and a list of elements.
# I can't get CategoryCollections work for cps, but on the other hand, it is
# useful to allow els to be a list of subspaces of the ambient space of <ps>.
##
InstallMethod( FlagOfIncidenceStructure,
	"for a projective space and list of subspaces of the projective space",
	[ IsClassicalPolarSpace, IsSubspaceOfProjectiveSpaceCollection ],
	function(ps,els)
		local list,i,test,type,flag;
		list := Set(ShallowCopy(els));
		if Length(list) > Rank(ps) then
		  Error("A flag can contain at most Rank(<ps>) elements");
		fi;
        test := List(list,x->AmbientSpace(x));
        if not ForAll(test,x->x=AmbientSpace(ps)) then
            Error("not all elements have the ambient space of <ps> as ambient space");
        fi;
		test := Set(List(list,x->x in ps));
		if (test <> [ true ] and test <> []) then
		  Error("not all elements in <els> belong to <ps>");
		fi;
		test := Set(List([1..Length(list)-1],i -> IsIncident(list[i],list[i+1])));
		if (test <> [ true ] and test <> []) then
		  Error("<els> does not determine a flag>");
		fi;
		flag := rec(geo := ps, types := List(list,x->x!.type), els := list, vectorspace := ps!.vectorspace );
		ObjectifyWithAttributes(flag, IsFlagOfCPSType, IsEmptyFlag, false, RankAttr, Size(list) );
		return flag;
	end);

# ADDED 31/3/2014 jdb
#############################################################################
#O  FlagOfIncidenceStructure( <ps>, <els> )
# returns the empty flag of the projective space <ps>.
##
InstallMethod( FlagOfIncidenceStructure,
	"for a projective space and an empty list",
	[ IsClassicalPolarSpace, IsList and IsEmpty ],
	function(ps,els)
		local flag;
		flag := rec(geo := ps, types := [], els := [], vectorspace := ps!.vectorspace );
		ObjectifyWithAttributes(flag, IsFlagOfCPSType, IsEmptyFlag, true, RankAttr, 0 );
		return flag;
	end);


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

InstallMethod( ViewObj, "for a flag of a polar space",
	[ IsFlagOfClassicalPolarSpace and IsFlagOfIncidenceStructureRep ],
	function( flag )
		Print("<a flag of ",ViewString(flag!.geo)," >");
	end );

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

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


############################################################################
## Methods for random stuff
## Since it is quick to find a pseudo-random element
## of a group (a random subproduct of the generators),
## we just find a random collineation and take the image
## of the associated element representative (see RepresentativesOfElements).
#############################################################################


# CHECKED 22/09/2011 jdb.
# CAHNGED 20/08/2014 jdb.
#############################################################################
#O  RandomSubspace( <ps>, <d> )
# returns a random subspace of projective dimension <d> in the polar space <ps>
##
InstallMethod( RandomSubspace, 
	"for a polar space and a projective dimension",
	[ IsClassicalPolarSpace, IsPosInt ],                                             
	function( ps, d )
		local x, rep, enum;
		if HasCollineationGroup(ps) then
            x := PseudoRandom( CollineationGroup(ps) );
            rep := RepresentativesOfElements(ps)[d+1];
            return OnProjSubspaces(rep, x);
        else
            enum := Enumerator(ElementsOfIncidenceStructure(ps,d));
            return Random(enum);
        fi;
  end );

# CHECKED 22/09/2011 jdb 
#############################################################################
#O  Random( <subs> )
# returns a random subspace of projective dimension <d> in the polar space <ps>
##
InstallMethod( Random, 
	"for a collection of subspaces of a polar space",
    [ IsSubspacesOfClassicalPolarSpace ],
	function( subs )
		local ps, x, rep, enum;
		ps := subs!.geometry;
		if HasCollineationGroup(ps) then
            x := PseudoRandom( CollineationGroup(ps) );
            rep := RepresentativesOfElements(ps)[subs!.type];
            return OnProjSubspaces(rep, x);
        else
            enum := Enumerator(subs);
            return Random(enum);
        fi;
	end );
  
# CHECKED 16/12/2011 jdb + ml CHECKED and CORRECTED 
#############################################################################
#O just return IteratorList(Enumerator(vs));
InstallMethod(Iterator,  
	"for subspaces of a polar space",
	[IsSubspacesOfClassicalPolarSpace],
	vs -> IteratorList(Enumerator(vs)) );


#############################################################################
#
#   Shadows of elements and flags
#
#############################################################################

# CHECKED 22/09/11 jdb
# cmat notice. in this case just the ConvertToMatrixRep causes trouble.
#############################################################################
#O ShadowOfElement(<ps>, <v>, <j> ). Recall that for every particular Lie 
# geometry a method for ShadowOfElement  must be installed. 
##
InstallMethod( ShadowOfElement,
	"for a polar space, a subspace of a projective space, and an integer",
	[IsClassicalPolarSpace, IsSubspaceOfProjectiveSpace, IsPosInt],
	function( ps, v, j )
		local localinner, localouter, localfactorspace, pstype, psdim, f, vdim, sz;
		pstype := PolarSpaceType(ps);
		psdim := ps!.dimension;
		f := ps!.basefield;
		vdim := v!.type;  
        if not AmbientSpace(ps) = AmbientSpace(v) then
            Error("<v> is not a subspace of (the ambient space) of <ps>");
        elif not v in ps then
            Error("<v> is not a subspace contained in <ps>");
        fi;
        if j > Rank(ps) then
            Error("<ps> has no elements of type <j>");
		elif j < vdim then
			localinner := [];
			localouter := Unpack(v!.obj);
			if IsVector(localouter) and not IsMatrix(localouter) then
				localouter := [localouter]; 
			fi;
			#ConvertToMatrixRep( localouter, f );
			localfactorspace := Subspace(ps!.vectorspace, localouter);
			sz := Size(Subspaces(localfactorspace, j));
		elif j = vdim then
			localinner := Unpack(v!.obj);
			if IsVector(localinner) and not IsMatrix(localinner) then
				localinner := [localinner]; 
			fi;
			localouter := localinner;
			localfactorspace := TrivialSubspace(ps!.vectorspace);
			sz := 1;
		else  
			localinner := Unpack(v!.obj);
			localouter := Unpack(PolarMap(ps)(v)!.obj); #actually, this is not a polarity when q is even and ps is parabolic.
			#localouter := UnderlyingObject(v^PolarityOfProjectiveSpace(ps));
			if pstype = "symplectic" then
				localfactorspace := SymplecticSpace( psdim- 2*vdim, f );
			elif pstype = "hermitian" then
				localfactorspace := HermitianPolarSpace( psdim-2*vdim, f );
			elif pstype = "elliptic" then 
				localfactorspace := EllipticQuadric( psdim-2*vdim, f );
			elif pstype = "parabolic" then 
				localfactorspace := ParabolicQuadric( psdim-2*vdim, f );
			elif pstype = "hyperbolic" then 
				localfactorspace := HyperbolicQuadric( psdim-2*vdim, f );
			fi;    
			sz := Size(ElementsOfIncidenceStructure(localfactorspace, j - vdim)); 
		fi;
        return Objectify(
		NewType( ElementsCollFamily, IsElementsOfIncidenceStructure and
                                IsShadowSubspacesOfClassicalPolarSpace and
                                IsShadowSubspacesOfClassicalPolarSpaceRep),
				rec( geometry := ps,
					type := j,
					inner := localinner,
					outer := localouter,
					factorspace := localfactorspace,
					size := sz
					)
				);
	end );

# added 3/9/14. This was necessary since there is a generic method now
# for Iterator of shadow elements of a generic incidence structure which 
# is too generic for particular incidence geometries. The method here 
# is based on the standard GAP method that was used before we introduce the
# generic method.
#############################################################################
#O just return IteratorList(Enumerator(vs));
InstallMethod(Iterator,  
	"for shadow subspaces of a polar space",
	[ IsShadowSubspacesOfClassicalPolarSpace ],
	vs -> IteratorList(Enumerator(vs)) );


# CHECKED 22/09/11 jdb
#############################################################################
#O Size( <vs> ) returns the number of elements in a shadow collection
##
InstallMethod( Size,
	"for a collection of shadow elements of a polar space",
	[IsShadowSubspacesOfClassicalPolarSpace and	IsShadowSubspacesOfClassicalPolarSpaceRep ],
	function( vs )
		return vs!.size;
	end);

#############################################################################
#O IsCollinear( <ps>, <a>, <b> ) returns true if <a> and <b> are collinear in 
# <ps>
##
#InstallMethod( IsCollinear, 
#	"for points of a polar space", 
#	[IsClassicalPolarSpace and IsClassicalPolarSpaceRep, 
#		IsSubspaceOfProjectiveSpace, IsSubspaceOfProjectiveSpace],
#	function( ps, a, b )
#    local m;
#    m := SesquilinearForm(ps)!.matrix;
#    return Unwrap(a)*m*Unwrap(b)=Zero(ps!.basefield);
#
   # JB: There was a bug here for Hermitian polar spaces
   # We may want to copy the code from \^ "for sesquilinear forms" to here

#    return IsZero( [Unwrap(a),Unwrap(b)]^SesquilinearForm(ps) );   
#  end);

# above method was wrong
#############################################################################
#O IsCollinear( <ps>, <a>, <b> ) returns true if <a> and <b> are collinear in 
# <ps>
##
InstallMethod( IsCollinear, 
	"for a polar space and two points of its ambient space", 
	[IsClassicalPolarSpace and IsClassicalPolarSpaceRep, 
		IsSubspaceOfProjectiveSpace, IsSubspaceOfProjectiveSpace],
	function( ps, a, b )
		if not a!.type=1 and b!.type=1 then
			Error("<a> and <b> should be points");
		elif not AmbientSpace(a) = AmbientSpace(ps) and AmbientSpace(b) = AmbientSpace(ps) then
			Error("<a> and <b> should belong to the ambientspace of <ps>");
		else
			return (a in ps) and (b in ps) and IsZero( [Unwrap(a),Unwrap(b)]^SesquilinearForm(ps) );   
		fi;
	end );

#############################################################################
# Polarities and polar spaces. 
#############################################################################

# CHECKED 22/09/11 jdb
#############################################################################
#O PolarityOfProjectiveSpace( <ps> )
#the next method returns the polarity associated to a polar space.
#recall that polarspaces and associated polarities are equivalent, except
#when q is even and the polar space is orthogonal, in this case, the associated 
#symplectic polarity is returned, so usable to compute tangent hyperplanes etc.
#When q is even and the projective dimension is even, this associated sesquilinear form
#is degenerate, so not usable to construct a polarity, because we ask a non-degenerate form
#So, the above condition is, due to definitions in Fining, and the definition of
#the "associated sesquilinear form of a polar space, equivalent with cheking
#whether the form returned by SesquilinearForm(ps) is degenerate. If not, we go!
#all necessary algebraic stuff is in the forms package.
##
InstallMethod(PolarityOfProjectiveSpace,
  "for a polar space",
  [IsClassicalPolarSpace],
  function(ps)
  local form;
  form := SesquilinearForm(ps);
  if IsDegenerateForm(form) then
    Error("no polarity of the ambient projective space can be associated to <ps>");
  else return PolarityOfProjectiveSpace(form);
  fi;
end );

# CHECKED 22/09/11 jdb
#############################################################################
#O PolarSpace( <polarity> ) returns the polar space associated to the polarity.
# returns an error if <polarity> is pseudo. Of course, orthogonal polar spaces
# in even characteristic cannot be reached with this method.
##
InstallMethod( PolarSpace, 
	"from a polarity of a projective space",
	[ IsPolarityOfProjectiveSpace ],
	function( polarity )
		local form, ps;
		form := SesquilinearForm(polarity);
		if not IsPseudoForm(form) then
			ps := PolarSpace( form );
		else
			Error("<polarity> is pseudo and does not induce a polar space");
		fi;
		return ps;
	end );

# CHECKED 22/09/11 jdb
#############################################################################
#O GeometryOfAbsolutePoints( <polarity> ) returns the geometry of absolute 
# points of <polarity>. This is in most cases a polar space, which explains why this
# method is found here.
##
InstallMethod( GeometryOfAbsolutePoints, 
	"for a polarity of a projective space",
	[ IsPolarityOfProjectiveSpace ],
	function( polarity )
		local form, geom, ps, vect, mat, n, sub;
		form := SesquilinearForm(polarity);
		if IsPseudoForm(form) then
			mat := polarity!.mat;
			n := Length(mat);
			vect := List([1..n],i->mat[i][i]);
			sub := NullspaceMat(TransposedMat([vect]));
			ps := ProjectiveSpace(n-1,polarity!.fld);
			return VectorSpaceToElement(ps,sub);
		else
			return PolarSpace(form);
		fi;
		return ps;
	end );

# CHECKED 22/09/11 jdb
#############################################################################
#O AbsolutePoints( <polarity> ) returns the collection of points of the 
# geometry of absolute points of <polarity>
##
InstallMethod( AbsolutePoints,
	"for a polarity of a projective space",
	[ IsPolarityOfProjectiveSpace ],
	function( polarity )
		return Points(GeometryOfAbsolutePoints(polarity));
	end );

# added 31/07/2014 jdb
#############################################################################
#O TangentSpace( <polarspace>, <el> ) returns the collection of points of the 
# geometry of absolute points of <polarity>
##
InstallMethod( AbsolutePoints,
	"for a polarity of a projective space",
	[ IsPolarityOfProjectiveSpace ],
	function( polarity )
		return Points(GeometryOfAbsolutePoints(polarity));
	end );



#I think the next method is obsolete. We have a method \in for a subspace of a projective space
# and a polar space, and this does the job. I leave as is, comment out, and if there are no complains, 
# it can be deleted.

#InstallMethod( IsTotallySingular, 
#	"for a elementen variety w.r.t a polarity",  
#              [ IsClassicalPolarSpace and IsClassicalPolarSpaceRep, IsSubspaceOfProjectiveSpace ],
#  function( ps, v )
#    local uv, bi, f, ty, localaut, m, form, 
#          flag, bsum, count1, count2;

## Suppose the polar space is defined by a quadratic form Q. Then
## a subspace of the ambient projective space is totally singular if
## it computes zero under Q. If we have only a sesquilinear form b, 
## then let the map b(v,v) take the role of Q. 
#
#    form := SesquilinearForm(ps);
#    m := form!.matrix;
#    f := form!.basefield;
#    ty := form!.type;
#    uv := v!.obj;
#    if v!.type = 1 then uv := [uv]; fi; 
#    if ty = "hermitian" then 
#       localaut := CompanionAutomorphism(ps);
#       return IsZero( uv * m * (TransposedMat(uv)^localaut) );
#    else

## A subspace with basis B is totally singular w.r.t the quadratic form 
## Q(v) = vMv^T if for all pairs b_i and b_j in B we have Q(b_i+b_j) = 0.

       # check first that everything in uv is t.s.
       
#       flag := ForAll(uv, x -> IsZero(x * m * x));
#       if v!.type > 1 and flag then

         ## just a shorter way to go through combinations
#         count1 := 1; count2 := 2;
#         repeat
#           repeat 
#             bsum := uv[count1]+uv[count2];
#             flag := IsZero(bsum * m * bsum);
#             count2 := count2 + 1;
#           until flag or count2 > Length(uv);
#           count1 := count1 + 1;
#           count2 := count1 + 1;
#         until flag or count1 >= Length(uv);
#       fi;
#       return flag;    
#    fi;
#  end );

# this is obsolete now
#InstallMethod( IsTotallyIsotropic, "for a projective variety w.r.t a polarity", 
#             [ IsClassicalPolarSpace and IsClassicalPolarSpaceRep, IsSubspaceOfProjectiveSpace ],
#  function( ps, v )
#    local perp, perpv;
#    perp := Polarity(ps);
#    perpv := perp(v);
#    return perpv!.type >= v!.type and v in perp(v);
#  end );

#CHECKED 20/3/14
# cmat notice: for some computations it seems necessary to Unpack the cmats.
#############################################################################
#O PolarMap( <ps> ) returns the polar map associated to the polar space <ps>.
# in most cases this polar map is just the polarity. Actually, this operation is
# only for internal use.
InstallMethod( PolarMap,  
	"for a polar space",
	[IsClassicalPolarSpace],
	function( ps )
		local perp, m, ty, f, form, bi;

## If we have only a quadratic form Q, then we must remember
## to use let the map b(v,w) := Q(v+w)-Q(v)-Q(w) be the polarisation of Q,
## and consider the polarity induced from this map. This can be computed using AssociatedBilinearForm (from package forms).

    form := SesquilinearForm(ps);
    m := form!.matrix;
    f := form!.basefield;
    ty := form!.type;

    if ty = "hermitian" then
       perp := function(v)
         local perpv, uv, rk, aut;
         aut := CompanionAutomorphism(ps);
         uv := Unpack(v!.obj); #cmat unpack.
         if v!.type = 1 then uv := [uv]; fi; 
         perpv := MutableCopyMat(TriangulizedNullspaceMat( m * TransposedMat(uv)^aut )); 
         #rk := Rank(perpv);   
         # if rk = 1 then perpv := perpv[1]; fi;
         return VectorSpaceToElement(AmbientSpace(ps), perpv); #cmat notice: now Wrap cannot be used anymore, rk not needed.
       end;
    else
       if IsEvenInt(Size(f)) and 
          (ty = "elliptic" or ty = "hyperbolic" or ty = "parabolic") then
          ## recover bilinear form from quadratic form
          bi := m + TransposedMat(m);
       else 
          bi := m;
       fi;
       
       perp := function(v)
         local perpv, uv, rk;
         uv := Unpack(v!.obj); #cmat unpack.
         if v!.type = 1 then uv := [uv]; fi; 
         perpv := MutableCopyMat(TriangulizedNullspaceMat( bi * TransposedMat(uv) )); 
         #rk := Rank(perpv);   
         #if rk = 1 then
         #   perpv := perpv[1]; 
            #ConvertToVectorRep( perpv, f );
         #else
			#ConvertToMatrixRep( perpv, f );
         #fi;
         return VectorSpaceToElement(AmbientSpace(ps), perpv); #cmat notice: now Wrap cannot be used anymore, rk not needed anymore.
       end;
    fi;
    return perp; ## should we return a correlation here? No.
  end );

#InstallMethod( DefiningPolarity, "for a polar space, if it is defined by a polarity",
#    [ IsClassicalPolarSpace ],
#  function( ps )
#    local f, q, type, form, polarity;
#    f := ps!.basefield;
#    q := Size(f);
#    type := PolarSpaceType( ps );
#    if type in ["elliptic", "hyperbolic", "parabolic"] and IsEvenInt(q) then
#        Error("This polar space is not defined by a polarity");
#    else
#       form := SesquilinearForm( ps );
#    fi;
#    polarity := PolarityOfProjectiveSpace( form );
#    return polarity;
#  end );
#

#InstallMethod( Polarisation, "for a quadratic form",
#  [ IsQuadraticForm ],
#  function( form )
#
#  ## This function returns the bilinear form obtained from a quadratic
#  ## form Q; that is, <u ,v> = Q(u + v) - Q(u) - Q(v).
#  ## This method will be replace by the new operation in "Forms"
#  # jdb: I commented out this function now, we can use AssociatedBilinearForm.
#  local gram, f;
#    gram := form!.matrix;
#    f := form!.basefield;
#    return BilinearFormByMatrix( gram + TransposedMat(gram), f);
#  end);
 
#InstallMethod( \in, "for a variety and a polar space",
#  [IsElementOfIncidenceStructure, IsClassicalPolarSpace],
#  function( w, ps )
#    local form, r, tsingular, mat, aut, polar;
#    # if the vector spaces don't agree we can't go any further
#    if w!.geo!.vectorspace <> ps!.vectorspace then
#      return false;
#    fi;
#
#    r := w!.obj;
#    if w!.type = 1 then r := [r]; fi;
#
#    # check if the subspace is totally isotropic/singular
#
#    if HasQuadraticForm(ps) then
#       form := QuadraticForm(ps);
#       
#       ## check that each basis element is singular
#       if not ForAll( r, i -> IsZero( i^form ) ) then
#          return false;  
#       fi;
#       
#       ## now look at all pairs of basis elements
#       ## under the associated bilinear form
#
#       polar := AssociatedBilinearForm( form );
#       tsingular := ForAll([1..Size(r)-1], i ->
#                      ForAll([i+1..Size(r)], j ->
#                        IsZero( [r[i],r[j]]^polar ) ) );
#    else
#       form := SesquilinearForm(ps);
#       mat := form!.matrix;
#       if form!.type = "hermitian" then
#          aut := CompanionAutomorphism( ps );
#          tsingular := IsZero( (r^aut) * mat * TransposedMat(r) );
#       else 
#          tsingular := IsZero( r * mat * TransposedMat(r) ); 
#       fi;
#    fi;
#   
#    if not IsSubspaceOfClassicalPolarSpace(w) and tsingular then
#       w!.geo := ps;
#    fi;
#    return tsingular;
#  end );

#############################################################################
#
#  Groups: (special) isometry groups and similarity group of finite classical
#           polar spaces
#
#############################################################################

#############################################################################
#O CollineationGroup( <ps> ) returns collineation group of <ps>
##
InstallMethod( CollineationGroup,
	"for a polar space",
	[ IsClassicalPolarSpace and IsClassicalPolarSpaceRep ],
	function( ps )
		local iso, twiner, g, info, x, points, hom, d, f, coll, type, act;
		d := ps!.dimension + 1; 
		f := ps!.basefield;
		if HasIsCanonicalPolarSpace(ps) and IsCanonicalPolarSpace(ps) then
			type := PolarSpaceType( ps );
			info := ClassicalGroupInfo( ps );
			if type = "symplectic" then
				g := GammaSp(d,f);
			elif type = "elliptic" then
				g := GammaOminus(d,f);  
			elif type = "hyperbolic" then
				g := GammaOplus(d,f);
			elif type = "parabolic" then
				g := GammaO(d,f);
			elif type = "hermitian" then
   			   g := GammaU(d, f);
			fi;
		else    
			iso := IsomorphismCanonicalPolarSpaceWithIntertwiner( ps );
			Info(InfoFinInG, 1, "Computing collineation group of canonical polar space...");
			coll := CollineationGroup( Source(iso)!.geometry );
			info := ClassicalGroupInfo( ps );
			twiner := Intertwiner( iso );           
			g := Image(twiner, coll);
		fi;
        ## Setting up the NiceMonomorphism
		x := RepresentativesOfElements( ps );  
		
		# We need to know in the case that d = 2 whether we have a
		# hermitian polar space. If it is hermitian, then for 2.PGammaU(d, q^2), 
		# the NiceMonomorphism will just use that for PGammaL(d, q)
		 
 	   type := PolarSpaceType( ps );
       if type ="hermitian" and d = 2 and not IsPrime(Sqrt(Size(f)))  then
          Info(InfoFinInG, 1, "No nice monomorphism computed.");
          Info(InfoFinInG, 2, "This really pisses me off, John!");
 	   else
	      if not IsEmpty(x) then
            if type = "hermitian" and d=4 then
                x := x[2];
                act := OnProjSubspacesWithFrob;
            else
                x := x[1];
                act := OnProjPointsWithFrob;
            fi;
		     if FINING.Fast then
	               hom := NiceMonomorphismByOrbit( g, x!.obj, act, info!.degree);
			 else 
		           points := Orbit(g, x, OnProjSubspaces);
		           hom := ActionHomomorphism(g, points, OnProjSubspaces, "surjective");    
		           SetIsBijective(hom, true);
		           SetNiceObject(g, Image(hom) );
	        fi;
            SetNiceMonomorphism(g, hom );
	      fi;
 	   fi;	
        
        # only for making generalised polygons section more generic:
        if IsClassicalGQ(ps) then
            SetCollineationAction(g,OnProjSubspaces);
        fi;
        return g;
	end );

#############################################################################
#O SpecialIsometryGroup( <ps> ) returns the special isometry group of <ps>
##
InstallMethod( SpecialIsometryGroup,
	"for a classical polar space",
	[ IsClassicalPolarSpace and IsClassicalPolarSpaceRep ],
	function( ps )
    local iso, twiner, g, info, coll, d, f, type;

    if HasIsCanonicalPolarSpace(ps) and IsCanonicalPolarSpace(ps) then
       type := PolarSpaceType( ps );
       info := ClassicalGroupInfo( ps );
       coll := CollineationGroup( ps );
       d := ps!.dimension + 1; 
       f := ps!.basefield;
       
       if type = "symplectic" then
          g := Spdesargues(d,f);
       elif type = "elliptic" then
          g := SOdesargues(-1,d,f);
       elif type = "hyperbolic" then
          g := SOdesargues(1,d,f);
       elif type = "parabolic" then
          g := SOdesargues(0,d,f);
       elif type = "hermitian" then
          g := SUdesargues(d, f);
       fi;
      
       SetParent(g, coll);
    else
       iso := IsomorphismCanonicalPolarSpaceWithIntertwiner( ps );
       twiner := Intertwiner( iso );
       g := Image(twiner, SpecialIsometryGroup(Source(iso)!.geometry) );
       SetParent(g, CollineationGroup(ps));
    fi;
    return g;
  end );

#############################################################################
#O IsometryGroup( <ps> ) returns the isometry group of <ps>
##
InstallMethod( IsometryGroup, 
	"for a classical polar space",
	[ IsClassicalPolarSpace and IsClassicalPolarSpaceRep ],
  function( ps )
    local iso, twiner, g, info, coll, d, f, type;

    if HasIsCanonicalPolarSpace(ps) and IsCanonicalPolarSpace(ps) then
       #Print("yes!");
	   type := PolarSpaceType( ps );
       info := ClassicalGroupInfo( ps );
       coll := CollineationGroup( ps );
       d := ps!.dimension + 1; 
       f := ps!.basefield;
       
       if type = "symplectic" then
          g := Spdesargues(d,f);
       elif type = "elliptic" then
          g := GOdesargues(-1,d,f);
       elif type = "hyperbolic" then
          g := GOdesargues(1,d,f);
       elif type = "parabolic" then
          g := GOdesargues(0,d,f);
       elif type = "hermitian" then
          g := GUdesargues(d, f);
       fi;
      
       SetParent(g, coll);
    else
       
       iso := IsomorphismCanonicalPolarSpaceWithIntertwiner( ps );

       twiner := Intertwiner( iso );
       g := Image(twiner, IsometryGroup(Source(iso)!.geometry) );
       SetParent(g, CollineationGroup(ps));
    fi;
    return g;
  end );

#############################################################################
#O SimilarityGroup( <ps> ) returns the similarity group of <ps>
##
InstallMethod( SimilarityGroup, [ IsClassicalPolarSpace and IsClassicalPolarSpaceRep ],
  function( ps )
    local iso, twiner, g, info, coll, d, f, type;
    if HasIsCanonicalPolarSpace(ps) and IsCanonicalPolarSpace(ps) then
       type := PolarSpaceType( ps );
       info := ClassicalGroupInfo( ps );
       coll := CollineationGroup( ps );
       d := ps!.dimension + 1; 
       f := ps!.basefield;
       
       if type = "symplectic" then
          g := GSpdesargues(d,f);
       elif type = "elliptic" then
          g := DeltaOminus(d,f);
       elif type = "hyperbolic" then
          g := DeltaOplus(d,f);
       elif type = "parabolic" then
          g := GOdesargues(0,d,f);
       elif type = "hermitian" then
          g := GUdesargues(d, f);
       fi;
      
       SetParent(g, coll);
    else
      iso := IsomorphismCanonicalPolarSpaceWithIntertwiner( ps );
      twiner := Intertwiner( iso );
      g := Image(twiner, SimilarityGroup(Source(iso)!.geometry) );
      SetParent(g, CollineationGroup(ps));
    fi;
    return g;
  end );

#############################################################################
		
#############################################################################
#P  IsParabolicQuadric( <ps> )
# returns true if <ps> is a parabolic quadric
##
InstallMethod( IsParabolicQuadric,
	"for a polar space having HasQuadraticForm",
	[IsClassicalPolarSpace],
	1,
	function( ps )
		if HasQuadraticForm(ps) then
			return IsParabolicForm(QuadraticForm(ps));
		else
			TryNextMethod();
		fi;
	end);
	
#############################################################################
#P  IsParabolicQuadric( <ps> )
# returns true if <ps> is a parabolic quadric
##
InstallMethod( IsParabolicQuadric, 
	"for a polar space",
	[IsClassicalPolarSpace],
	function( ps )
		return IsParabolicForm(SesquilinearForm(ps));
	end);

#############################################################################
#P  IsHyperbolicQuadric( <ps> )
# returns true if <ps> is a hyperbolic quadric
##
InstallMethod( IsHyperbolicQuadric, 
	"for a polar space having HasQuadraticForm",
	[IsClassicalPolarSpace],
	1,
	function( ps )
		if HasQuadraticForm(ps) then
			return IsHyperbolicForm(QuadraticForm(ps));
		else
			TryNextMethod();
		fi;
	end);
	
#############################################################################
#P  IsHyperbolicQuadric( <ps> )
# returns true if <ps> is a hyperbolic quadric
##
InstallMethod( IsHyperbolicQuadric, 
	"for a polar space",
	[IsClassicalPolarSpace],
	function( ps )
		return IsHyperbolicForm(SesquilinearForm(ps));
	end);


#############################################################################
#P  IsEllipticQuadric( <ps> )
# returns true if <ps> is an elliptic quadric
##
InstallMethod( IsEllipticQuadric, 
	"for a polar space having HasQuadraticForm",
	[IsClassicalPolarSpace],
	1,
	function( ps )
		if HasQuadraticForm(ps) then
			return IsEllipticForm(QuadraticForm(ps));
		else
			TryNextMethod();
		fi;
	end);
	
#############################################################################
#P  IsEllipticQuadric( <ps> )
# returns true if <ps> is an elliptic quadric
##
InstallMethod( IsEllipticQuadric, 
	"for a polar space",
	[IsClassicalPolarSpace],
	function( ps )
		return IsEllipticForm(SesquilinearForm(ps));
	end);


#############################################################################
#P  IsSymplecticSpace( <ps> )
# returns true if <ps> is an elliptic quadric
##
InstallMethod( IsSymplecticSpace,
	"for a polar space",
	[IsClassicalPolarSpace],
	function( ps )
		return PolarSpaceType(ps)="symplectic";
	end);
    
#############################################################################
#P  IsHermitianPolarSpace( <ps> )
# returns true if <ps> is an elliptic quadric
##
InstallMethod( IsHermitianPolarSpace,
	"for a polar space",
	[IsClassicalPolarSpace],
	function( ps )
		return PolarSpaceType(ps)="hermitian";
	end);

#############################################################################
# A litte extra since we put some quadrics and hermitian varieties in 
# IsProjectiveVariety.
#############################################################################


#############################################################################
#P  DefiningListOfPolynomials( <ps> )
# returns all the elements of the polar space <ps> 
## 
InstallMethod( DefiningListOfPolynomials, 
	"for a polar space",
	[IsProjectiveVariety and IsClassicalPolarSpace and IsClassicalPolarSpaceRep],
	function( ps )
		return [EquationForPolarSpace(ps)];
	end);

#############################################################################
# Elementary analytic geometry.
#############################################################################


#added 26/4/2014 jdb
#############################################################################
#A  NucleusOfParabolicQuadric( <ps> )
# returns the nuclues of a parabolic quadric (even characteristic).
## 
InstallMethod( NucleusOfParabolicQuadric, 
	"for a polar space",
	[ IsClassicalPolarSpace ],
	function( ps )
	if not IsParabolicQuadric(ps) and IsEvenInt(Size(BaseField(ps))) then
		Error(" <ps> has no nucleus" );
	else
		return VectorSpaceToElement( AmbientSpace(ps), RadicalOfFormBaseMat( SesquilinearForm(ps) ) );
	fi;
end);


#added 01/08/2014 jdb
#############################################################################
#A  TangentSpace( <el> )
# returns the tangent space at <el>, <el> must be a subspace of a polar space.
## 
InstallMethod( TangentSpace, 
	"for a subspace of a polar space",
	[ IsSubspaceOfClassicalPolarSpace ],
	function( el )
		local form, vec;
		form := SesquilinearForm(el!.geo);
		if not IsDegenerateForm(form) then
			return el^PolarityOfProjectiveSpace(form);
		else
			vec := Unpack(el!.obj)*form!.matrix;
			if el!.type = 1 then
				return HyperplaneByDualCoordinates(AmbientSpace(el),vec);
			else
				return VectorSpaceToElement(AmbientSpace(el), NullspaceMat(TransposedMat(vec)));
			fi;
		fi;
end);

#added 01/08/2014 jdb
#############################################################################
#A  TangentSpace( <el> )
# returns the tangent space at <el>, <el> must be a subspace of a polar space.
## 
InstallMethod( TangentSpace, 
	"for a subspace of a polar space",
	[ IsClassicalPolarSpace, IsSubspaceOfProjectiveSpace ],
	function( ps, el )
		local form, vec;
		if not el in ps then
			Error("<el> should lie in <ps>");
		fi;
		form := SesquilinearForm(ps);
		if not IsDegenerateForm(form) then
			return el^PolarityOfProjectiveSpace(form);
		else
			vec := Unpack(el!.obj)*form!.matrix;
			if el!.type = 1 then
				return HyperplaneByDualCoordinates(AmbientSpace(el),vec);
			else
				return VectorSpaceToElement(AmbientSpace(el), NullspaceMat(TransposedMat(vec)));
			fi;
		fi;
end);

#added 01/08/2014 jdb
#############################################################################
#A  Pole(<ps>, <el> )
# returns the pole of <el> with respect to <ps>.
## 
InstallMethod( Pole, 
	"for a subspace of a polar space",
	[ IsClassicalPolarSpace, IsSubspaceOfProjectiveSpace ],
	function( ps, el )
		local form, vec;
		if not el in AmbientSpace(ps) then
			Error("<el> should lie in the ambient space of <ps>");
		fi;
		form := SesquilinearForm(ps);
		if not IsDegenerateForm(form) then
			return el^PolarityOfProjectiveSpace(form);
		else
			vec := Unpack(el!.obj)*form!.matrix;
			if el!.type = 1 then
				return HyperplaneByDualCoordinates(AmbientSpace(el),vec);
			else
				return VectorSpaceToElement(AmbientSpace(el), NullspaceMat(TransposedMat(vec)));
			fi;
		fi;
end);

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