diff --git a/lib/dicthf.gi b/lib/dicthf.gi index 77f5de7bcb..d35cc50c85 100644 --- a/lib/dicthf.gi +++ b/lib/dicthf.gi @@ -110,14 +110,21 @@ local f,n,bytelen,data,qq,i; return function(v) local x,sy,p; sy := 0; - for x in v do - p := Position(f, x); + if IsZmodnZVectorRep(v) then + for x in v![ELSPOS] do + sy := n*sy + (x-1); + od; + else + for x in v do + p := Position(f, x); # want to be quick: Assume no failures -# if p = fail then -# Error("NumberFFVector: Vector not over specified field"); -# fi; - sy := n*sy + (p-1); - od; +# if p = fail then +# Error("NumberFFVector: Vector not over specified field"); +# fi; + sy := n*sy + (p-1); + od; + fi; + return sy; end; fi; diff --git a/lib/grpffmat.gi b/lib/grpffmat.gi index 3e308c21a5..7f72d525fe 100644 --- a/lib/grpffmat.gi +++ b/lib/grpffmat.gi @@ -39,8 +39,14 @@ end ); InstallMethod(FieldOfMatrixList,"finite field matrices",true, [IsListOrCollection and IsFFECollCollColl],0, function(l) -local deg, i, j, char; +local deg, i, j, char,B; if Length(l)=0 then Error("list must be nonempty");fi; + if ForAll( l, HasBaseDomain ) then + B:= BaseDomain( l[1] ); + if ForAll( l, x -> B = BaseDomain( x ) ) then + return B; + fi; + fi; deg := 1; for i in l do for j in i do @@ -58,8 +64,15 @@ end); InstallMethod(DefaultScalarDomainOfMatrixList,"finite field matrices",true, [IsListOrCollection and IsFFECollCollColl],0, function(l) -local deg, i, j, char,m; -if Length(l)=0 then Error("list must be nonempty");fi; +local deg, i, j, char,m,B; + if Length(l)=0 then Error("list must be nonempty");fi; + if ForAll( l, HasBaseDomain ) then + B:= BaseDomain( l[1] ); + if ForAll( l, x -> B = BaseDomain( x ) ) then + return B; + fi; + fi; + deg := 1; for i in l do # treat compact matrices quickly diff --git a/lib/grpmat.gi b/lib/grpmat.gi index f876e65a1c..775ec29037 100644 --- a/lib/grpmat.gi +++ b/lib/grpmat.gi @@ -240,6 +240,7 @@ local field, dict, acts, start, j, zerov, zero, dim, base, partbas, heads, else Error("illegal action"); fi; + start:=List(start,x->ImmutableVector(field,x)); zerov:=Zero(start[1]); zero:=zerov[1]; @@ -355,7 +356,14 @@ local field, dict, acts, start, j, zerov, zero, dim, base, partbas, heads, if v<>zerov then Add(base,orb[i]); Add(partbas,ShallowCopy(orb[i])); + # filter for vector objects, not compressed FF vectors + if ForAny(partbas,x->IsVectorObj(x) and not IsDataObjectRep(x)) then + partbas:=Matrix(BaseDomain(partbas[1]),partbas); + fi; TriangulizeMat(partbas); + if IsMatrixObj(partbas) then + partbas:=ShallowCopy(RowsOfMatrix(partbas)); + fi; heads:=List(partbas,PositionNonZero); if Length(partbas)>=dim then # full dimension reached @@ -972,6 +980,81 @@ local G,fam,typ,f; return G; end ); +# ditto for nonprime ZmodnZ +InstallMethod( GroupWithGenerators, "list of zmodnz matrices", + [ IsZmodnZObjNonprimeCollCollColl ], +#T ??? +function( gens ) +local G,typ,f; + + if not IsFinite(gens) then TryNextMethod(); fi; + typ:=MakeGroupyType(FamilyObj(gens), + IsGroup and IsAttributeStoringRep + and HasGeneratorsOfMagmaWithInverses + and IsFinitelyGeneratedGroup and HasIsEmpty and IsFinite, + gens,false,true); + + f:=DefaultScalarDomainOfMatrixList(gens); + gens:=List(Immutable(gens),i->ImmutableMatrix(f,i)); + + G:=rec(); + ObjectifyWithAttributes(G,typ,GeneratorsOfMagmaWithInverses,AsList(gens)); + + if IsField(f) then SetDefaultFieldOfMatrixGroup(G,f);fi; + + return G; +end ); + +InstallMethod( GroupWithGenerators, + "list of zmodnz matrices with identity", IsCollsElms, + [ IsZmodnZObjNonprimeCollCollColl,IsMultiplicativeElementWithInverse and + IsZmodnZObjNonprimeCollColl], +function( gens, id ) +local G,typ,f; + + if not IsFinite(gens) then TryNextMethod(); fi; + typ:=MakeGroupyType(FamilyObj(gens), IsGroup and IsAttributeStoringRep + and HasGeneratorsOfMagmaWithInverses and IsFinitelyGeneratedGroup + and HasIsEmpty and IsFinite and HasOne, + gens,id,true); + + f:=DefaultScalarDomainOfMatrixList(gens); + gens:=List(Immutable(gens),i->ImmutableMatrix(f,i)); + id:=ImmutableMatrix(f,id); + + G:=rec(); + ObjectifyWithAttributes(G,typ,GeneratorsOfMagmaWithInverses,AsList(gens), + One,id); + + if IsField(f) then SetDefaultFieldOfMatrixGroup(G,f);fi; + + return G; +end ); + +InstallMethod( GroupWithGenerators, + "empty list of zmodnz matrices with identity", true, + [ IsList and IsEmpty, + IsMultiplicativeElementWithInverse and IsZmodnZObjNonprimeCollColl], +function( gens, id ) +local G,fam,typ,f; + + if not IsFinite(gens) then TryNextMethod(); fi; + typ:=MakeGroupyType(FamilyObj([id]), IsGroup and IsAttributeStoringRep + and HasGeneratorsOfMagmaWithInverses and HasOne and IsTrivial, + gens,id,true); + + f:=DefaultScalarDomainOfMatrixList([id]); + id:=ImmutableMatrix(f,id); + + G:=rec(); + ObjectifyWithAttributes(G,typ,GeneratorsOfMagmaWithInverses,AsList(gens), + One,id); + + if IsField(f) then SetDefaultFieldOfMatrixGroup(G,f);fi; + + return G; +end ); + ############################################################################# ## diff --git a/lib/matobjnz.gi b/lib/matobjnz.gi index 227154fe8a..3224e01c32 100644 --- a/lib/matobjnz.gi +++ b/lib/matobjnz.gi @@ -69,13 +69,20 @@ InstallMethod( NewZeroVector, "for IsZmodnZVectorRep, a ring, and an int", end ); InstallMethod( ViewObj, "for a zmodnz vector", [ IsZmodnZVectorRep ], - function( v ) +function( v ) +local l; if not IsMutable(v) then Print(""); + Print("vector mod ",Size(v![BDPOS])); + l:=Length(v![ELSPOS]); + if 0"); + else + Print(" of length ",Length(v![ELSPOS]),">"); + fi; end ); InstallMethod( PrintObj, "for a zmodnz vector", [ IsZmodnZVectorRep ], @@ -395,6 +402,25 @@ local i,m; fi; end ); +InstallOtherMethod( AddRowVector, "for plist, zmodnz vector, and a scalar", + [ IsPlistRep and IsMutable, IsZmodnZVectorRep, IsObject ], +function( a, b, s ) +local i,m; + if not ForAll(a,IsModulusRep) then TryNextMethod();fi; + for i in [1..Length(a)] do + a[i]:=a[i]+b[i]*s; + od; +end); + +InstallOtherMethod( AddRowVector, "for plist, plist vector, and a scalar", + [ IsPlistRep and IsMutable, IsPlistVectorRep, IsObject ], +function( a, b, s ) +local i,m; + for i in [1..Length(a)] do + a[i]:=a[i]+b[i]*s; + od; +end); + InstallMethod( AddRowVector, "for two zmodnz vectors, a scalar, and two positions", [ IsZmodnZVectorRep and IsMutable, IsZmodnZVectorRep, @@ -731,9 +757,16 @@ InstallMethod( Matrix, "for a list and a zmodnz matrix", InstallMethod( ViewObj, "for a zmodnz matrix", [ IsZmodnZMatrixRep ], function( m ) + local l; Print("<"); if not IsMutable(m) then Print("immutable "); fi; - Print(Length(m![ROWSPOS]),"x",m![RLPOS],"-matrix over ",m![BDPOS],">"); + l:=[Length(m![ROWSPOS]),m![RLPOS]]; + if Product(l)<=9 and Product(l)<>0 then + Print("matrix mod ",Size(m![BDPOS]),": ", + List(m![ROWSPOS],x->x![ELSPOS]),">"); + else + Print(l[1],"x",l[2],"-matrix mod ",Size(m![BDPOS]),">"); + fi; end ); InstallMethod( PrintObj, "for a zmodnz matrix", [ IsZmodnZMatrixRep ], @@ -1046,12 +1079,32 @@ InstallMethod( \*, "for two zmodnz matrices",IsIdenticalObj, return Objectify( ty, [a![BDPOS],a![EMPOS],b![RLPOS],l] ); end ); +InstallMethod(\*,"for zmodnz matrix and ordinary matrix",IsIdenticalObj, + [IsZmodnZMatrixRep,IsMatrix], +function(a,b) + return Matrix(BaseDomain(a),List(RowsOfMatrix(a),x->x*b)); +end); + + InstallMethod( \=, "for two zmodnz matrices",IsIdenticalObj, [ IsZmodnZMatrixRep, IsZmodnZMatrixRep ], function( a, b ) return EQ_LIST_LIST_DEFAULT(a![ROWSPOS],b![ROWSPOS]); end ); +InstallMethod( \=, "for zmodnz matrix and matrix",IsIdenticalObj, + [ IsZmodnZMatrixRep, IsMatrix ], + function( a, b ) + return Unpack(a)=b; + end ); + +InstallMethod( \=, "for matrix and zmodnz matrix",IsIdenticalObj, + [ IsMatrix, IsZmodnZMatrixRep ], + function( a, b ) + return a=Unpack(b); + end ); + + InstallMethod( \<, "for two zmodnz matrices",IsIdenticalObj, [ IsZmodnZMatrixRep, IsZmodnZMatrixRep ], function( a, b ) @@ -1302,9 +1355,7 @@ InstallMethod( TransposedMatImmutable, "for a zmodnz matrix", return n; end ); -InstallMethod( \*, "for a zmodnz vector and a zmodnz matrix", - IsElmsColls, [ IsZmodnZVectorRep, IsZmodnZMatrixRep ], - function( v, m ) +ZMZVECMAT:=function( v, m ) local i,res,s,r; r:=BaseDomain(v); # do arithmetic over Z first so that we reduce only once @@ -1325,11 +1376,17 @@ InstallMethod( \*, "for a zmodnz vector and a zmodnz matrix", MakeImmutable(res); fi; return res; - end ); + end; -InstallOtherMethod( \*, "for a plist vector and a zmodnz matrix", - IsElmsColls, [ IsList, IsZmodnZMatrixRep ], - function( v, m ) +InstallMethod( \*, "for a zmodnz vector and a zmodnz matrix", + IsElmsColls, [ IsZmodnZVectorRep, IsZmodnZMatrixRep ], + ZMZVECMAT); + +InstallOtherMethod( \^, "for a zmodnz vector and a zmodnz matrix", + IsElmsColls, [ IsZmodnZVectorRep, IsZmodnZMatrixRep ], + ZMZVECMAT); + +PLISTVECZMZMAT:=function( v, m ) local i,res,s,r; r:=BaseDomain(m); # do arithmetic over Z first so that we reduce only once @@ -1350,7 +1407,42 @@ InstallOtherMethod( \*, "for a plist vector and a zmodnz matrix", MakeImmutable(res); fi; return res; - end ); + end; + +InstallOtherMethod( \*, "for a plist vector and a zmodnz matrix", + IsElmsColls, [ IsList, IsZmodnZMatrixRep ], + PLISTVECZMZMAT); + +InstallOtherMethod( \^, "for a plist vector and a zmodnz matrix", + IsElmsColls, [ IsList, IsZmodnZMatrixRep ], + PLISTVECZMZMAT); + +ZMZVECTIMESPLISTMAT:=function( v, m ) + local i,res,s,r; + r:=BaseDomain(v); + # do arithmetic over Z first so that we reduce only once + res:=ListWithIdenticalEntries(Length(m[1]),Zero(r)); + for i in [1..Length(v)] do + s := v[i]; + if not IsZero(s) then + AddRowVector(res,m[i],s); + fi; + od; + res:=Vector(r,res); + + if not IsMutable(v) and not IsMutable(m) then + MakeImmutable(res); + fi; + return res; + end; + +InstallOtherMethod( \*, "for a zmodnz vector and plist matrix", + IsElmsColls, [ IsZmodnZVectorRep, IsMatrix ], + ZMZVECTIMESPLISTMAT); + +InstallOtherMethod( \^, "for a zmodnz vector and plist matrix", + IsElmsColls, [ IsZmodnZVectorRep, IsMatrix ], + ZMZVECTIMESPLISTMAT); InstallMethod( CompatibleVector, "for a zmodnz matrix", [ IsZmodnZMatrixRep ], diff --git a/lib/matrix.gd b/lib/matrix.gd index ff8051a2b3..e5114bea05 100644 --- a/lib/matrix.gd +++ b/lib/matrix.gd @@ -625,7 +625,7 @@ DeclareOperation( "TriangulizedNullspaceMatDestructive", [ IsMatrix and IsMutabl ## ## <#/GAPDoc> ## -DeclareOperation( "GeneralisedEigenvalues", [ IsRing, IsMatrix ] ); +DeclareOperation( "GeneralisedEigenvalues", [ IsRing, IsMatrixOrMatrixObj ] ); DeclareSynonym( "GeneralizedEigenvalues", GeneralisedEigenvalues ); ############################################################################# @@ -644,7 +644,7 @@ DeclareSynonym( "GeneralizedEigenvalues", GeneralisedEigenvalues ); ## ## <#/GAPDoc> ## -DeclareOperation( "GeneralisedEigenspaces", [ IsRing, IsMatrix ] ); +DeclareOperation( "GeneralisedEigenspaces", [ IsRing, IsMatrixOrMatrixObj ] ); DeclareSynonym( "GeneralizedEigenspaces", GeneralisedEigenspaces ); diff --git a/lib/matrix.gi b/lib/matrix.gi index 4aef74f6e0..1df6c180bd 100644 --- a/lib/matrix.gi +++ b/lib/matrix.gi @@ -2174,17 +2174,22 @@ InstallMethod( NullspaceMatDestructive, [ IsOrdinaryMatrix and IsMutable], mat -> SemiEchelonMatTransformationDestructive(mat).relations ); -InstallMethod( TriangulizedNullspaceMat, - "generic method for ordinary matrices", - [ IsOrdinaryMatrix ], +InstallOtherMethod( TriangulizedNullspaceMat, + "generic method for matrices", + [ IsMatrixOrMatrixObj ], mat -> TriangulizedNullspaceMatDestructive( MutableCopyMatrix( mat ) ) ); -InstallMethod( TriangulizedNullspaceMatDestructive, - "generic method for ordinary matrices", - [ IsOrdinaryMatrix and IsMutable], +InstallOtherMethod( TriangulizedNullspaceMatDestructive, + "generic method for matrices", + [ IsMatrixOrMatrixObj and IsMutable], function( mat ) local ns; ns := SemiEchelonMatTransformationDestructive(mat).relations; + if IsPlistRep(ns) and Length(ns)>0 + # filter for vector objects, not compressed FF vectors + and ForAll(ns,x->IsVectorObj(x) and not IsDataObjectRep(x)) then + ns:=Matrix(BaseDomain(mat),ns); + fi; TriangulizeMat(ns); return ns; end ); @@ -2258,8 +2263,8 @@ end ); #M GeneralisedEigenvalues( , ) ## InstallMethod( GeneralisedEigenvalues, - "for a matrix", - [ IsField, IsMatrix ], + "for a matrix or matrix obj", + [ IsField, IsMatrixOrMatrixObj ], function( F, A ) return Set( Factors( UnivariatePolynomialRing(F), MinimalPolynomial(F, A,1) ) ); end ); @@ -2269,11 +2274,18 @@ InstallMethod( GeneralisedEigenvalues, #M GeneralisedEigenspaces( , ) ## InstallMethod( GeneralisedEigenspaces, - "for a matrix", - [ IsField, IsMatrix ], + "for a matrix or matrix obj", + [ IsField, IsMatrixOrMatrixObj ], function( F, A ) - return List( GeneralisedEigenvalues( F, A ), eval -> - VectorSpace( F, TriangulizedNullspaceMat( Value( eval, A ) ) ) ); + local M,S,eval; + S:=[]; + for eval in GeneralisedEigenvalues(F,A) do + M:=TriangulizedNullspaceMat( Value( eval, A ) ); + if IsMatrixObj(M) and not IsMatrix(M) then + M:=RowsOfMatrix(M); fi; + Add(S,VectorSpace(F,M)); + od; + return S; end ); ############################################################################# @@ -2436,6 +2448,27 @@ InstallMethod( SemiEchelonMat, return SemiEchelonMatDestructive( copymat ); end ); +InstallOtherMethod( SemiEchelonMat, + "generic method for list of vector objects", + [ IsList ], + function( mat ) + local copymat, v, vc, f; + # filter for vector objects, not compressed FF vectors + if not (ForAll(mat,x->IsVectorObj(x) and not IsDataObjectRep(x)) + and Length(mat)>0) then + TryNextMethod(); + fi; + copymat := []; + if Length(mat)>0 then + f := BaseDomain(mat[1]); + for v in mat do + vc := ShallowCopy(v); + Add(copymat, vc); + od; + fi; + return SemiEchelonMatDestructive(Matrix(f, copymat )); +end ); + ############################################################################# ## @@ -2488,6 +2521,9 @@ BindGlobal("DoSemiEchelonMatTransformationDestructive", function(f, mat ) vectors := []; T := IdentityMat( nrows, f ); + if IsMatrixObj(mat) then + T:=Matrix(f,T); + fi; coeffs := []; relations := []; @@ -3020,7 +3056,7 @@ function( M1, M2 ) return [ sum, int ]; end ); -InstallOtherMethod( SumIntersectionMat,"MatricObject", IsIdenticalObj, +InstallOtherMethod( SumIntersectionMat,"MatrixObject", IsIdenticalObj, [ IsMatrixObj, IsMatrixObj ], function( M1, M2 ) local n, # number of columns @@ -3261,6 +3297,17 @@ local m; return m; end); +InstallOtherMethod( TriangulizedMat,"list of vectore", [IsList], +function ( mat ) +local m; + m:=MutableCopyMatrix(mat); + TriangulizeMat(m); + return m; +end); + + + + InstallOtherMethod( TriangulizedMat, "for an empty list", [ IsList and IsEmpty ], mat -> []); @@ -4043,15 +4090,40 @@ InstallGlobalFunction( DirectSumMat, function (arg) m:= arg[r]; r:=r+1; od; if m <> [ ] then - F:= DefaultField( m[1,1] ); + r:=[]; + for c in arg do + if HasBaseDomain(c) then + Add(r,BaseDomain(c)); + else + Add(r,DefaultFieldOfMatrix(c)); + fi; + od; + F:=r[1]; + for c in r{[2..Length(r)]} do + if not IsSubset(F,c) then + if IsSubset(c,F) then + F:=c; + else + Error("build field/ring"); + fi; + fi; + od; + else F:= Rationals; fi; - res:=List(NullMat(Sum(arg,Length),Sum(arg,f),F),ShallowCopy); + res:=[Sum(arg,Length),Sum(arg,f)]; + if ForAny(arg,IsMatrixObj) then + res:=ZeroMatrix(F,res[1],res[2]); + else + res:=List(NullMat(res[1],res[2],F),ShallowCopy); + fi; r:=0; c:=0; for m in arg do - res{r+[1..Length(m)]}{c+[1..f(m)]}:=m; + #res{r+[1..Length(m)]}{c+[1..f(m)]}:=m; + CopySubMatrix(m,res,[1..NrRows(m)],r+[1..Length(m)], + [1..NrCols(m)], c+[1..f(m)]); r:=r+Length(m); c:=c+f(m); od; diff --git a/lib/meataxe.gi b/lib/meataxe.gi index 5248325b81..7fabdcf154 100644 --- a/lib/meataxe.gi +++ b/lib/meataxe.gi @@ -381,21 +381,36 @@ SMTX_SpinnedBasis:=function( arg ) ngens:=Length(matrices); fi; ans:=[]; - if Length(v)=0 then - return []; - fi; - if not IsList(v[1]) then - v:=[v]; - fi; zero:=Zero(matrices[1][1][1]); - ans:=ShallowCopy(Basis(VectorSpace(F,v))); - if Length(ans)=0 then - return ans; + if IsList(v) and Length(v)=0 then + return []; + elif IsMatrix(v) then + TriangulizeMat(v); + ans:=Filtered(v,x->not IsZero(x)); + elif IsList(v) and IsVectorObj(v[1]) then + v:=TriangulizedMat(Matrix(F,v)); + ans:=Filtered(List(v),x->not IsZero(x)); + else + # single vector (as vector or list) + ans:=[v]; fi; + + + #ans:=ShallowCopy(Basis(VectorSpace(F,v))); + + ans:=Filtered(ans,x->not IsZero(x)); + if Length(ans)=0 then return ans; fi; ans:=List(ans, v -> ImmutableVector(F,v)); dim:=Length(ans[1]); subdim:=Length(ans); leadpos:=SubGModLeadPos(ans,dim,subdim,zero); + for i in [1..Length(ans)] do + w:=ans[i]; + j:=w[PositionNonZero(w)]; + if not IsOne(j) then + ans[i]:=j^-1*ans[i]; + fi; + od; i:=1; while i <= subdim do @@ -508,8 +523,8 @@ local s, c, q, leadpos, zero, zerov, smatrices, newg, im, newim, k, subi, # merit a basechange # first extend the basis - sub:=ShallowCopy(sub); - Append(sub,One(matrices[1]){Difference([1..dim],leadpos)}); + sub:=List(sub); + Append(sub,List(One(matrices[1])){Difference([1..dim],leadpos)}); sub:=ImmutableMatrix(F,sub); subi:=sub^-1; qmats:=[]; @@ -519,12 +534,12 @@ local s, c, q, leadpos, zero, zerov, smatrices, newg, im, newim, k, subi, for g in matrices do g:=sub*g*subi; if s then - h:=g{sr}{sr}; + h:=ExtractSubMatrix(g,sr,sr); h:=ImmutableMatrix(F,h); Add(smats,h); fi; if q then - h:=g{qr}{qr}; + h:=ExtractSubMatrix(g,qr,qr); h:=ImmutableMatrix(F,h); Add(qmats,h); fi; @@ -909,7 +924,7 @@ local g1,g2,coefflist,M,pol; fi; Add(coefflist, g2); od; - Info(InfoMeatAxe,2,"Evaluated random element in algebra."); + Info(InfoMeatAxe,3,"Evaluated random element in algebra."); pol:=CharacteristicPolynomialMatrixNC(F,M,1); return [M,coefflist,pol]; end; @@ -1031,7 +1046,7 @@ SMTX_IrreducibilityTest:=function( module ) # To choose random element, first add on a new generator as a product of # two randomly chosen unequal existing generators # Record the product in newgenlist. - Info(InfoMeatAxe,1,"Choosing random element number ",count); + Info(InfoMeatAxe,3,"Choosing random element number ",count); M:=SMTX.SMCoRaEl(matrices,ngens,newgenlist,dim,F); @@ -1043,7 +1058,7 @@ SMTX_IrreducibilityTest:=function( module ) idmat:=M^0; orig_pol:=pol; - Info(InfoMeatAxe,2,"Evaluated characteristic polynomial. Time = ", + Info(InfoMeatAxe,3,"Evaluated characteristic polynomial. Time = ", Runtime()-rt0,"."); # Now we extract the irreducible factors of pol starting with those # of low degree @@ -1058,7 +1073,7 @@ SMTX_IrreducibilityTest:=function( module ) else fac:=Factors(R, pol: factoroptions:=rec(onlydegs:=[deg])); fac:=Filtered(fac,i->DegreeOfLaurentPolynomial(i)=deg); - Info(InfoMeatAxe,2,Length(fac)," factors of degree ",deg, + Info(InfoMeatAxe,3,Length(fac)," factors of degree ",deg, ", Time = ",Runtime()-rt0,"."); fi; until fac <> [] or deg = maxdeg; @@ -1374,7 +1389,7 @@ local matrices, ngens, M, mat, N, newgenlist, coefflist, orig_ngens, " random elements and failed ", "to find a good one. Type return to keep trying."); fi; - Info(InfoMeatAxe,2,"Choosing random element number ",count,"."); + Info(InfoMeatAxe,3,"Choosing random element number ",count,"."); M:=SMTX.SMCoRaEl(matrices,ngens,newgenlist,dim,F); ngens:=Length(matrices); @@ -1384,7 +1399,7 @@ local matrices, ngens, M, mat, N, newgenlist, coefflist, orig_ngens, M:=M[1]; idmat:=M^0; - Info(InfoMeatAxe,2,"Evaluated characteristic polynomial. Time = ", + Info(InfoMeatAxe,3,"Evaluated characteristic polynomial. Time = ", Runtime()-rt0,"."); # That is necessary in case p is defined over a smaller field that F. oldpol:=pol; @@ -1400,7 +1415,7 @@ local matrices, ngens, M, mat, N, newgenlist, coefflist, orig_ngens, else fac:=Factors(R, pol: factoroptions:=rec(onlydegs:=[deg])); fac:=Filtered(fac,i->DegreeOfLaurentPolynomial(i)<=deg); - Info(InfoMeatAxe,2,Length(fac)," factors of degree ",deg, + Info(InfoMeatAxe,3,Length(fac)," factors of degree ",deg, ", Time = ",Runtime()-rt0,"."); sfac:=Set(fac); fi; @@ -1459,24 +1474,25 @@ SMTX.GoodElementGModule:=SMTX_GoodElementGModule; ## SMTX_FrobeniusAction:=function( arg ) local L, d, p, M, one, zero, R, h, v, w, i, j, nd, ans, - A, basis; + A, basis,fld; - if Length(arg) = 2 then - A:=arg[1]; - v:=arg[2]; + fld:=arg[1]; + A:=arg[2]; + v:=arg[3]; + if Length(arg) = 3 then basis:=0; - elif Length(arg) = 3 then - A:=arg[1]; - v:=arg[2]; - basis:=arg[3]; + elif Length(arg) = 4 then + basis:=arg[4]; else - return Error("usage: SMTX.FrobeniusAction( , , [, ] )"); + return Error("usage: SMTX.FrobeniusAction(, , , [, ] )"); fi; one :=One(A[1][1]); zero:=Zero(one); d:=Length( A ); M:=ListWithIdenticalEntries(Length(A[1]) + 1,zero); - ConvertToVectorRep(M,DefaultField(v)); + M:=ImmutableVector(fld,M); + v:=ImmutableVector(fld,v); + A:=ImmutableMatrix(fld,A); # L[i] (length d) will contain a vector with head entry 1 at position i, # which is in the current block. @@ -1515,6 +1531,7 @@ local L, d, p, M, one, zero, R, h, v, w, i, j, nd, ans, if h <= d then #AH replaced Copy by ShallowCopy as only vector is used if basis <> 0 then basis[j]:=ShallowCopy(v); fi; + R[h]:=p * w[h]^-1; L[h]:=w * w[h]^-1; j:=j + 1; @@ -1708,7 +1725,7 @@ local dim, ndim, gcd, div, e, ct, F, q, ok, basisN:=[]; Info(InfoMeatAxe,2, "Calc. Frobenius action of element from group algebra on nullspace."); - M0:=SMTX.FrobeniusAction(M,v,basisN); + M0:=SMTX.FrobeniusAction(F,M,v,basisN); zero:=Zero(F); one:= One(F); @@ -1757,15 +1774,18 @@ local dim, ndim, gcd, div, e, ct, F, q, ok, # A matrix product gives us the basis for B in terms of the # original basis for the module. basisBN:=[]; - C0:=SMTX.FrobeniusAction(C^pow,v0,basisBN); + C0:=SMTX.FrobeniusAction(F,C^pow,v0,basisBN); C:=[]; until Length(C0) = e; Info(InfoMeatAxe,2,"Found one."); - basisB:=ShallowCopy(basisBN * basisN); + basisB:=List( + ImmutableMatrix(F,basisBN) * + ImmutableMatrix(F,basisN)); else C0:=M0; basisB:=ShallowCopy(basisN); fi; + C0:=ImmutableMatrix(F,C0); # Now try to extend basisB to a basis for the whole module, by # translating it by the generating matrices. Info(InfoMeatAxe,2,"Trying to extend basis to whole module."); @@ -1807,7 +1827,7 @@ local dim, ndim, gcd, div, e, ct, F, q, ok, SMTX.SetCentMat(module, Pinv * centmat * P); # get the base right # We will also record the minimal polynomial of C0 (and hence of # centmat) in case we need it at some future date. - SMTX.SetCentMatMinPoly(module, MinimalPolynomialMatrixNC(F,C0,1)); + SMTX.SetCentMatMinPoly(module, MinimalPolynomial(F,C0,1)); return false; fi; Info(InfoMeatAxe,2,"But it didn't."); @@ -1986,7 +2006,7 @@ SMTX_CollectedFactors:= function( module ) # build the submodule formed by their images mat:=Concatenation(homs); TriangulizeMat(mat); - mat:=Filtered(mat,i->not IsZero(i)); + mat:=Filtered(List(mat),i->not IsZero(i)); mat:=ImmutableMatrix(field,mat); if Length(mat) mat[j] * gens2[i] then - Print(i,j,"\n"); Error("matrix does not define a homomorphism"); fi; od; @@ -3422,7 +3443,7 @@ SMTX_InvariantQuadraticForm:=function( module ) # Matrix must be symplectic - perhaps it must be? for i in [1..dim] do if ciso[i][i] <> z then - Print("Non-symplectic failure!\n"); + #Print("Non-symplectic failure!\n"); return fail; fi; od; diff --git a/lib/overload.g b/lib/overload.g index fa4ba342dd..2a773ab935 100644 --- a/lib/overload.g +++ b/lib/overload.g @@ -121,7 +121,7 @@ InstallMethod( DerivedSeries, [ IsGroup ], DerivedSeriesOfGroup ); ## DeclareOperation( "Determinant", [ IsObject ] ); -InstallMethod( Determinant, [ IsMatrix ], DeterminantMat ); +InstallMethod( Determinant, [ IsMatrixOrMatrixObj ], DeterminantMat ); InstallMethod( Determinant, [ IsClassFunction ], DeterminantOfCharacter ); diff --git a/lib/polyfinf.gi b/lib/polyfinf.gi index 04acdb184b..289eb2c4d8 100644 --- a/lib/polyfinf.gi +++ b/lib/polyfinf.gi @@ -104,7 +104,11 @@ local br, ind, c, facs, deg, px, pow, cyc, gcd,d,powc,fc,fam; #px := LaurentPolynomialByExtRepNC( # FamilyObj(f), [One(br)],1, ind ); # pow := px; - px:=ImmutableVector(br, [Zero(br),-One(br)]); + if IsFinite(br) and IsField(br) and Size(br)>MAXSIZE_GF_INTERNAL then + px:=Immutable([Zero(br),-One(br)]); + else + px:=ImmutableVector(br, [Zero(br),-One(br)]); + fi; powc:=-px; fc:=CoefficientsOfLaurentPolynomial(f)[1]; fam:=FamilyObj(One(br)); diff --git a/lib/ratfun1.gi b/lib/ratfun1.gi index 1d7ca3d9dd..234a171672 100644 --- a/lib/ratfun1.gi +++ b/lib/ratfun1.gi @@ -42,7 +42,13 @@ local f,typ,lc; # slightly better to do this after the Length has been determined if IsFFECollection(coeff) and IS_PLIST_REP(coeff) then - coeff := ImmutableVector(DefaultRing(coeff), coeff); + f:=DefaultRing(coeff); + if IsFinite(f) and Size(f)>MAXSIZE_GF_INTERNAL then + # do not pack Zmodnz objects into vectors + coeff := Immutable(coeff); + else + coeff := ImmutableVector(f, coeff); + fi; fi; diff --git a/lib/vecmat.gi b/lib/vecmat.gi index 9585cb6ce9..9d3cb4f778 100644 --- a/lib/vecmat.gi +++ b/lib/vecmat.gi @@ -1560,8 +1560,14 @@ end); #F ImmutableMatrix( , [,] ) ## BindGlobal("DoImmutableMatrix", function(field,matrix,change) -local sf, rep, ind, ind2, row, i,big,l; - if not (IsPlistRep(matrix) or IsGF2MatrixRep(matrix) or +local sf, rep, ind, ind2, row, i,big,l,nr; + if IsMatrixObj(matrix) then + if field=BaseDomain(matrix) then + return Immutable(matrix); + else + return ImmutableMatrix(field,Unpack(matrix)); + fi; + elif not (IsPlistRep(matrix) or IsGF2MatrixRep(matrix) or Is8BitMatrixRep(matrix)) then # if empty or not list based, simply return `Immutable'. return Immutable(matrix); @@ -1570,6 +1576,9 @@ local sf, rep, ind, ind2, row, i,big,l; sf:=field; elif IsField(field) then sf:=Size(field); + elif IsZmodnZObjNonprimeCollection(field) then + # slight abuse of ``field'' variable name + sf:=Size(field); else # not a field return Immutable(matrix); @@ -1586,10 +1595,17 @@ local sf, rep, ind, ind2, row, i,big,l; rep:=IsPlistRep; fi; + # cannot use NrRows consistently, as input might be mixed format + if IsList(matrix) then + nr:=Length(matrix); + else + nr:=NrRows(matrix); + fi; + # get the indices of the rows that need changing the representation. ind:=[]; # rows to convert ind2:=[]; # rows to rebuild - for i in [1..NrRows(matrix)] do + for i in [1..nr] do if not rep(matrix[i]) then if big or IsLockedRepresentationVector(matrix[i]) or (IsMutable(matrix[i]) and not change) then @@ -1619,10 +1635,20 @@ local sf, rep, ind, ind2, row, i,big,l; fi; # rebuild some rows - if big then - for i in ind2 do - matrix[i]:=List(matrix[i],j->j); # plist conversion - od; + if IsZmodnZObjNonprimeCollection(field) then + matrix:=Matrix(field,matrix); + big:=true; + elif big then + if sf<>infinity and IsPrimeInt(sf) and sf>MAXSIZE_GF_INTERNAL then + if not (IsMatrixObj(matrix) and not IsMutable(matrix)) then + if field=sf then field:=Integers mod sf;fi; + matrix:=Matrix(field,matrix); + fi; + else + for i in ind2 do + matrix[i]:=List(matrix[i],j->j); # plist conversion + od; + fi; else for i in ind2 do row := ShallowCopy(matrix[i]); @@ -1643,12 +1669,20 @@ local sf, rep, ind, ind2, row, i,big,l; return matrix; end); -InstallMethod( ImmutableMatrix,"general,2",[IsObject,IsMatrix],0, +InstallOtherMethod( ImmutableMatrix,"general,2", +[IsObject,IsMatrixOrMatrixObj],0, function(f,m) return DoImmutableMatrix(f,m,false); end); -InstallOtherMethod( ImmutableMatrix,"general,3",[IsObject,IsMatrix,IsBool],0, +InstallOtherMethod( ImmutableMatrix,"List of vectors", +[IsObject,IsList],0, +function(f,m) + if not ForAll(m,x->IsList(x) or IsVector(x)) then TryNextMethod();fi; + return DoImmutableMatrix(f,m,false); +end); + +InstallOtherMethod(ImmutableMatrix,"general,3",[IsObject,IsMatrixOrMatrixObj,IsBool],0, DoImmutableMatrix); InstallOtherMethod( ImmutableMatrix,"field,8bit",[IsField,Is8BitMatrixRep],0, @@ -1703,10 +1737,28 @@ end); ## InstallMethod( ImmutableVector,"general,2",[IsObject,IsRowVector],0, function(f,v) +local sf; + if (IsInt(f) and f>MAXSIZE_GF_INTERNAL) or + (IsField(f) and Size(f)>MAXSIZE_GF_INTERNAL) then + if IsInt(f) then + sf:=f; + else + sf:=Size(f); + fi; + if sf<>infinity and IsPrimeInt(sf) then + if f=sf then f:=Integers mod sf;fi; + return Immutable(Vector(f,v)); + fi; + fi; ConvertToVectorRepNC(v,f); return Immutable(v); end); +InstallOtherMethod( ImmutableVector,"vectorObj,2",[IsObject,IsVectorObj],0, +function(f,v) + return Immutable(v); +end); + InstallOtherMethod( ImmutableVector,"general,3",[IsObject,IsRowVector,IsBool],0, function(f,v,change) ConvertToVectorRepNC(v,f); diff --git a/lib/vspcrow.gi b/lib/vspcrow.gi index c60f074358..1b40df4f80 100644 --- a/lib/vspcrow.gi +++ b/lib/vspcrow.gi @@ -155,6 +155,48 @@ InstallMethod( LeftModuleByGenerators, return V; end ); +InstallOtherMethod( LeftModuleByGenerators, + "for division ring and list of vector objects", + IsElmsColls, + [ IsDivisionRing, IsList ], + # ensure it ranks above the generic method + function( F, mat ) + local V,typ; + + # filter for vector objects, not compressed FF vectors + if not ForAll(mat,x->IsVectorObj(x) and not IsDataObjectRep(x)) then + TryNextMethod(); + fi; + typ:=IsAttributeStoringRep and HasIsEmpty and IsFiniteDimensional; + if ForAll( mat, row -> IsSubset( F, BaseDomain(row) ) ) then + typ:=typ and IsGaussianRowSpace; + else + typ:=typ and IsVectorSpace and IsRowModule and IsNonGaussianRowSpace; + fi; + + if Length(mat)>0 and ForAny(mat,x->not IsZero(x)) then + typ:=typ and IsNonTrivial; + else + typ:=typ and IsTrivial; + fi; + + if HasIsFinite(F) then + if IsFinite(F) then + typ:=typ and IsFinite; + else + typ:=typ and HasIsFinite; # i.e. not finite + fi; + fi; + + V:= Objectify( NewType( FamilyObj( mat ), typ), rec() ); + + SetLeftActingDomain( V, F ); + SetGeneratorsOfLeftModule( V, AsList( mat ) ); + SetDimensionOfVectors( V, Length( mat[1] ) ); + + return V; + end ); + ############################################################################# ## @@ -289,11 +331,8 @@ InstallMethod( LinearCombination, IsCollsElms, ## #M Coefficients( , ) . method for semi-ech. basis of Gaussian space ## -InstallMethod( Coefficients, - "for semi-ech. basis of a Gaussian row space, and a row vector", - IsCollsElms, - [ IsBasis and IsSemiEchelonBasisOfGaussianRowSpaceRep, IsRowVector ], - function( B, v ) + +COEFFS_SEMI_ECH_BASIS:=function( B, v ) local vectors, # basis vectors of `B' heads, # heads info of `B' len, # length of `v' @@ -333,8 +372,19 @@ InstallMethod( Coefficients, # Return the coefficients. return coeff; - end ); + end; + +InstallMethod( Coefficients, + "for semi-ech. basis of a Gaussian row space, and a row vector", + IsCollsElms, + [ IsBasis and IsSemiEchelonBasisOfGaussianRowSpaceRep, IsRowVector ], + COEFFS_SEMI_ECH_BASIS); +InstallMethod( Coefficients, + "for semi-ech. basis of a Gaussian row space, and vector object", + IsCollsElms, + [ IsBasis and IsSemiEchelonBasisOfGaussianRowSpaceRep, IsVectorObj ], + COEFFS_SEMI_ECH_BASIS); ############################################################################# ## @@ -640,16 +690,24 @@ InstallMethod( SemiEchelonBasis, return B; end ); -InstallMethod( SemiEchelonBasis, - "for Gaussian row space and matrix", +InstallOtherMethod( SemiEchelonBasis, + "for Gaussian row space and list of vector objects", IsIdenticalObj, - [ IsGaussianRowSpace, IsMatrix ], + [ IsGaussianRowSpace, IsList ], function( V, gens ) local heads, # heads info for the basis B, # the basis, result gensi, # immutable copy + flag, v; # loop over vector space generators + flag:=false; + if ForAll(gens,x->IsVectorObj(x) and not IsDataObjectRep(x)) then + flag:=true; + elif not IsMatrix(gens) then + TryNextMethod(); + fi; + # Check that the vectors form a semi-echelonized basis. heads:= HeadsInfoOfSemiEchelonizedMat( gens, DimensionOfVectors( V ) ); if heads = fail then @@ -669,6 +727,7 @@ InstallMethod( SemiEchelonBasis, fi; SetUnderlyingLeftModule( B, V ); gensi := ImmutableMatrix(LeftActingDomain(V), gens); + if flag then gensi:=RowsOfMatrix(gensi);fi; SetBasisVectors( B, gensi ); B!.heads:= heads; @@ -713,6 +772,40 @@ InstallMethod( SemiEchelonBasisNC, return B; end ); +InstallOtherMethod( SemiEchelonBasisNC, + "for Gaussian row space and list of vector objects", + IsIdenticalObj, + [ IsGaussianRowSpace, IsList ], + function( V, gens ) + local B, # the basis, result + gensi; # immutable copy + + # filter for vector objects, not compressed FF vectors + if not ForAll(gens,x->IsVectorObj(x) and not IsDataObjectRep(x)) then + TryNextMethod(); + fi; + + B:= Objectify( NewType( FamilyObj( gens ), + IsFiniteBasisDefault + and IsSemiEchelonized + and IsSemiEchelonBasisOfGaussianRowSpaceRep ), + rec() ); + if IsEmpty( gens ) then + SetIsEmpty( B, true ); + else + SetIsRectangularTable( B, true ); + fi; + SetUnderlyingLeftModule( B, V ); + gensi := RowsOfMatrix(ImmutableMatrix(LeftActingDomain(V), gens)); + SetBasisVectors( B, gens ); + + # Provide the `heads' information. + B!.heads:= HeadsInfoOfSemiEchelonizedMat( gens, DimensionOfVectors( V ) ); + + # Return the basis. + return B; + end ); + ############################################################################# ## @@ -881,9 +974,18 @@ InstallMethod( Intersection2, S:= Intersection2( AsVectorSpace( S, V ), AsVectorSpace( S, W ) ); else - # Compute the intersection of two spaces over the same field. + # Compute the intersection of two spaces over the same field. + if ForAll(GeneratorsOfLeftModule(V), + x->IsVectorObj(x) and not IsDataObjectRep(x)) + and ForAll(GeneratorsOfLeftModule(W), + x->IsVectorObj(x) and not IsDataObjectRep(x)) then + mat:= SumIntersectionMat( Matrix(LeftActingDomain(V), + GeneratorsOfLeftModule( V )),Matrix(LeftActingDomain(W), + GeneratorsOfLeftModule( W ) ))[2]; + else mat:= SumIntersectionMat( BasisVectors(SemiEchelonBasis( V )), BasisVectors(SemiEchelonBasis( W )) )[2]; + fi; #T why not just the generators if no basis is known yet? if IsEmpty( mat ) then S:= TrivialSubspace( V ); @@ -1066,6 +1168,10 @@ InstallMethod( CanonicalBasis, # Make a copy to avoid changing the original argument. B:= List( GeneratorsOfLeftModule( V ), ShallowCopy ); + # filter for vector objects, not compressed FF vectors + if ForAny(B,x->IsVectorObj(x) and not IsDataObjectRep(x)) then + B:=List(B,Unpack); + fi; m:= Length( B ); n:= Length( B[1] ); diff --git a/tst/testinstall/MatrixObj/IdentityMatrix.tst b/tst/testinstall/MatrixObj/IdentityMatrix.tst index fa7b07e327..b9a0d48890 100644 --- a/tst/testinstall/MatrixObj/IdentityMatrix.tst +++ b/tst/testinstall/MatrixObj/IdentityMatrix.tst @@ -88,9 +88,9 @@ Error, IdentityMatrix: the dimension must be non-negative # gap> IdentityMatrix(Integers mod 4, 2); -<2x2-matrix over (Integers mod 4)> + gap> IdentityMatrix(Integers mod 4, 0); -<0x0-matrix over (Integers mod 4)> +<0x0-matrix mod 4> gap> IdentityMatrix(Integers mod 4, -1); Error, IdentityMatrix: the dimension must be non-negative diff --git a/tst/testinstall/MatrixObj/ZeroMatrix.tst b/tst/testinstall/MatrixObj/ZeroMatrix.tst index 2affaa554b..495fe9b55a 100644 --- a/tst/testinstall/MatrixObj/ZeroMatrix.tst +++ b/tst/testinstall/MatrixObj/ZeroMatrix.tst @@ -94,11 +94,11 @@ gap> ZeroMatrix(Integers, 2, 0); # gap> ZeroMatrix(Integers mod 4, 2, 3); -<2x3-matrix over (Integers mod 4)> + gap> ZeroMatrix(Integers mod 4, 0, 3); -<0x3-matrix over (Integers mod 4)> +<0x3-matrix mod 4> gap> ZeroMatrix(Integers mod 4, 2, 0); -<2x0-matrix over (Integers mod 4)> +<2x0-matrix mod 4> # gap> ZeroMatrix(GF(2), 2, 3); diff --git a/tst/testinstall/MatrixObj/ZeroVector.tst b/tst/testinstall/MatrixObj/ZeroVector.tst index de4aa2b3d1..6d435d617c 100644 --- a/tst/testinstall/MatrixObj/ZeroVector.tst +++ b/tst/testinstall/MatrixObj/ZeroVector.tst @@ -82,9 +82,9 @@ Error, ZeroVector: length must be non-negative # gap> ZeroVector(Integers mod 4, 2); - + gap> ZeroVector(Integers mod 4, 0); - + gap> ZeroVector(Integers mod 4, -1); Error, ZeroVector: length must be non-negative diff --git a/tst/testinstall/vecmat.tst b/tst/testinstall/vecmat.tst index c5037977b3..cc7d5616c0 100644 --- a/tst/testinstall/vecmat.tst +++ b/tst/testinstall/vecmat.tst @@ -226,18 +226,12 @@ gap> F := Integers mod 6;; m := IdentityMat( 3, F ); [ [ ZmodnZObj( 1, 6 ), ZmodnZObj( 0, 6 ), ZmodnZObj( 0, 6 ) ], [ ZmodnZObj( 0, 6 ), ZmodnZObj( 1, 6 ), ZmodnZObj( 0, 6 ) ], [ ZmodnZObj( 0, 6 ), ZmodnZObj( 0, 6 ), ZmodnZObj( 1, 6 ) ] ] -gap> w := ImmutableMatrix( F, m ); -[ [ ZmodnZObj( 1, 6 ), ZmodnZObj( 0, 6 ), ZmodnZObj( 0, 6 ) ], - [ ZmodnZObj( 0, 6 ), ZmodnZObj( 1, 6 ), ZmodnZObj( 0, 6 ) ], - [ ZmodnZObj( 0, 6 ), ZmodnZObj( 0, 6 ), ZmodnZObj( 1, 6 ) ] ] +gap> w := ImmutableMatrix( F, m );; gap> m = w; true gap> IsMutable(w); false -gap> w := ImmutableMatrix( F, m, true ); -[ [ ZmodnZObj( 1, 6 ), ZmodnZObj( 0, 6 ), ZmodnZObj( 0, 6 ) ], - [ ZmodnZObj( 0, 6 ), ZmodnZObj( 1, 6 ), ZmodnZObj( 0, 6 ) ], - [ ZmodnZObj( 0, 6 ), ZmodnZObj( 0, 6 ), ZmodnZObj( 1, 6 ) ] ] +gap> w := ImmutableMatrix( F, m, true );; gap> m = w; true gap> IsMutable(w); diff --git a/tst/testinstall/zmodnz.tst b/tst/testinstall/zmodnz.tst index 2ea1b9461f..16e8879b91 100644 --- a/tst/testinstall/zmodnz.tst +++ b/tst/testinstall/zmodnz.tst @@ -419,9 +419,7 @@ gap> one:= One( A ); [ [ ZmodnZObj( 1, 6 ), ZmodnZObj( 0, 6 ) ], [ ZmodnZObj( 0, 6 ), ZmodnZObj( 1, 6 ) ] ] gap> G:= GroupWithGenerators( [ one ] );; -gap> One( G ); -[ [ ZmodnZObj( 1, 6 ), ZmodnZObj( 0, 6 ) ], - [ ZmodnZObj( 0, 6 ), ZmodnZObj( 1, 6 ) ] ] +gap> One( G );; gap> m:=[[4,1],[1,5]] * ZmodnZObj(1,6);; gap> m in A; m in G; true diff --git a/tst/teststandard/opers/Matobjnz.tst b/tst/teststandard/opers/Matobjnz.tst new file mode 100644 index 0000000000..8274552c51 --- /dev/null +++ b/tst/teststandard/opers/Matobjnz.tst @@ -0,0 +1,17 @@ +gap> START_TEST("Matobjnz.tst"); + +# +gap> p:=NextPrimeInt(MAXSIZE_GF_INTERNAL);; +gap> g:=AlternatingGroup(5);; +gap> mo:=IrreducibleModules(g,GF(p));; +gap> Set(List(mo[2],x->x.dimension)); +[ 1, 4, 5, 6 ] +gap> h:=Group(mo[2][2].generators); + +gap> Size(h); +60 +gap> Length(ConjugacyClassesSubgroups(h)); +9 + +# +gap> STOP_TEST("Matobjnz.tst",1);