|
#############################################################################
##
#W test.g GAP 4 package AtlasRep Thomas Breuer
##
## This file contains functions to test the data available in the
## ATLAS of Group Representations or in private extensions.
##
#############################################################################
##
## <#GAPDoc Label="tests">
## The file <F>tst/testall.g</F> of the package
## contains <Ref Func="Test" BookName="ref"/> statements
## for checking whether the &AtlasRep; functions behave as documented.
## One can run these tests by calling
## <C>ReadPackage( "AtlasRep", "tst/testall.g" )</C>.
## The examples in the package manual form a part of the tests,
## they are collected in the file <F>tst/docxpl.tst</F> of the package.
## <P/>
## The remainder of this section deals with consistency checks of the data.
## The tests described in Section
## <Ref Subsect="subsect:AGR sanity checks by toc"/> can be used
## for data from any extension of the database
## (see Chapter <Ref Chap="chap:Private Extensions"/>),
## Section <Ref Subsect="subsect:AGR other sanity checks"/> lists tests
## which apply only to the core part of the database.
## <P/>
## All these tests apply only to <E>locally</E> available files
## (see Section <Ref Sect="sect:The Tables of Contents of the AGR"/>),
## no files are downloaded during the tests.
## Thus the required space and time for running these tests
## depend on the amount of locally available data.
## <P/>
## Some of the tests compute and verify additional data,
## such as information about point stabilizers of permutation
## representations.
## In these cases, output lines starting with <C>#E</C> are error messages
## that point to inconsistencies,
## whereas output lines starting with <C>#I</C> inform about data that have
## been computed and were not yet stored,
## or about stored data that were not verified.
## These tests are experimental in the sense that they involve several
## heuristics. Depending on the data to which they are applied,
## it may happen that the tests run out of space or do not finish in
## acceptable time. Please inform the package maintainer if you run into
## such problems.
##
## <Subsection Label="subsect:AGR sanity checks by toc">
## <Heading>Sanity Checks for a Table of Contents</Heading>
##
## The following tests can be used to check the data that belong to a given
## part of the database (core data or extension).
## Each of these tests is given by a function with optional argument
## <M>tocid</M>, the identifying string that had been entered as the second
## argument of
## <Ref Func="AtlasOfGroupRepresentationsNotifyData"
## Label="for a local file describing private data"/>.
## The contents of the core part can be checked by entering <C>"core"</C>,
## which is also the default for <M>tocid</M>.
## The function returns <K>false</K> if an error occurs,
## otherwise <K>true</K>.
## Currently the following tests of this kind are available.
## (For some of them, the global option <C>TryToExtendData</C> can be
## entered in order to try the computation of not yet stored data.)
## <P/>
## <List>
## <#Include Label="test:AGR.Test.GroupOrders">
## <!-- <Include Label="test:AGR.Test.GroupFlags"> -->
## <#Include Label="test:AGR.Test.Words">
## <#Include Label="test:AGR.Test.ClassScripts">
## <#Include Label="test:AGR.Test.CycToCcls">
## <#Include Label="test:AGR.Test.FileHeaders">
## <#Include Label="test:AGR.Test.Files">
## <#Include Label="test:AGR.Test.BinaryFormat">
## <#Include Label="test:AGR.Test.Primitivity">
## <#Include Label="test:AGR.Test.Characters">
## <#Include Label="test:AGR.Test.StdCompatibility">
## <#Include Label="test:AGR.Test.KernelGenerators">
## <#Include Label="test:AGR.Test.MaxesOrders">
## <#Include Label="test:AGR.Test.MaxesStructure">
## <#Include Label="test:AGR.Test.MaxesStandardization">
## <#Include Label="test:AGR.Test.CompatibleMaxes">
## </List>
##
## </Subsection>
##
## <Subsection Label="subsect:AGR other sanity checks">
## <Heading>Other Sanity Checks</Heading>
##
## The tests described in this section are intended for checking data
## that do not belong to a particular part of the &AtlasRep; database.
## Therefore <E>all</E> locally available data are used in these tests.
## Each of the tests is given by a function without arguments that
## returns <K>false</K> if a contradiction was found during the test,
## and <K>true</K> otherwise.
## Additionally, certain messages are printed
## when contradictions between stored and computed data are found,
## when stored data cannot be verified computationally,
## or when the computations yield improvements of the stored data.
## Currently the following tests of this kind are available.
## <P/>
## <List>
## <#Include Label="test:AGR.Test.Standardization">
## <#Include Label="test:AGR.Test.StdTomLib">
## <#Include Label="test:AGR.Test.MinimalDegrees">
## </List>
##
## </Subsection>
## <#/GAPDoc>
##
if not IsPackageMarkedForLoading( "TomLib", "" ) then
IsStandardGeneratorsOfGroup:= "dummy";
LIBTOMKNOWN:= "dummy";
fi;
if not IsPackageMarkedForLoading( "CTblLib", "" ) then
ConstructionInfoCharacterTable:= "dummy";
HasConstructionInfoCharacterTable:= "dummy";
LibInfoCharacterTable:= "dummy";
StructureDescriptionCharacterTableName:= "dummy";
fi;
if not IsPackageMarkedForLoading( "Recog", "" ) then
InfoRecog:= "dummy";
RecogniseGroup:= "dummy";
SLPforElement:= "dummy";
NiceGens:= "dummy";
fi;
#############################################################################
##
AGR.FillHoles:= function( list, default )
local i;
for i in [ 1 .. Length( list ) ] do
if not IsBound( list[i] ) then
list[i]:= default;
fi;
od;
return list;
end;
AGR.TOCLine:= function( tag, name, values, default )
return Filtered( String( [ tag,
[ name, AGR.FillHoles( values, default ) ] ] ),
x -> x <> ' ' );
end;
#############################################################################
##
#V AGR.Test
#V AGR.Test.HardCases
#V AGR.Test.HardCases.MaxNumberMaxes
#V AGR.Test.HardCases.MaxNumberStd
#V AGR.Test.HardCases.MaxNumberVersions
#V AGR.Test.MaxTestDegree
##
## 'AGR.Test' is a record whose components belong to the various tests,
## and list data which shall be omitted from the tests
## because they would be too space or time consuming.
##
## In the test loops, we assume upper bounds on the numbers of available
## maximal subgroups and standardizations,
## and we perform some tests only if a sufficiently small permutation
## representation is available.
##
AGR.Test:= rec();
AGR.Test.HardCases:= rec();
AGR.Test.HardCases.MaxNumberMaxes:= 50;
AGR.Test.HardCases.MaxNumberStd:= 2;
AGR.Test.HardCases.MaxNumberVersions:= 3;
AGR.Test.MaxTestDegree:= 10^5;
#T 6.Suz.2 needs 200000 ...
#T 6.Fi22.2 needs ...
#############################################################################
##
#F AGR.Test.Words( [<tocid>[, <gapname>]][,][<verbose>] )
##
## <#GAPDoc Label="test:AGR.Test.Words">
## <Mark><C>AGR.Test.Words( [</C><M>tocid</M><C>] )</C></Mark>
## <Item>
## processes the straight line programs that belong to <M>tocid</M>,
## using the function stored in the <C>TestWords</C> component of the
## data type in question.
## <P/>
## The straight line programs for the cases listed in
## <C>AGR.Test.HardCases.TestWords</C> are omitted.
## </Item>
## <#/GAPDoc>
##
AGR.Test.HardCases.TestWords:= [
[ "find", [ "B", "HN", "S417", "F24d2" ] ],
[ "check", [ "B" ] ],
[ "maxes", [ "Co1" ] ],
#T doable with recog?
];
AGR.Test.Words:= function( arg )
local result, maxdeg, tocid, verbose, types, toc, name, r, type, omit,
entry, prg, gens, grp, size;
# Initialize the result.
result:= true;
maxdeg:= AGR.Test.MaxTestDegree;
if Length( arg ) = 0 then
return AGR.Test.Words( "core", false );
elif Length( arg ) = 1 and IsBool( arg[1] ) then
return AGR.Test.Words( "core", arg[1] );
elif Length( arg ) = 1 and IsString( arg[1] ) then
return AGR.Test.Words( arg[1], false );
elif Length( arg ) = 2 and IsString( arg[1] ) and IsString( arg[2] ) then
return AGR.Test.Words( arg[1], arg[2], false );
elif Length( arg ) = 2 and IsString( arg[1] ) and IsBool( arg[2] ) then
for name in AtlasOfGroupRepresentationsInfo.GAPnames do
result:= AGR.Test.Words( arg[1],
name[1], arg[2] ) and result;
od;
return result;
elif not ( Length( arg ) = 3 and IsString( arg[1] )
and IsString( arg[2] )
and IsBool( arg[3] ) ) then
Error( "usage: AGR.Test.Words( [<tocid>[, ",
"<gapname>]][,][<verbose>] )" );
fi;
tocid:= arg[1];
verbose:= arg[3];
# Check only straight line programs.
types:= AGR.DataTypes( "prg" );
name:= First( AtlasOfGroupRepresentationsInfo.GAPnames,
x -> x[1] = arg[2] );
for toc in AGR.TablesOfContents( [ tocid, "local" ] ) do
if IsBound( toc.( name[2] ) ) then
r:= toc.( name[2] );
# Note that the ordering in the 'and' statement must not be
# changed, in order to execute all tests!
for type in types do
omit:= First( AGR.Test.HardCases.TestWords,
pair -> pair[1] = type[1] );
if IsBound( r.( type[1] ) ) then
if IsList( omit ) and name[2] in omit[2] then
if verbose then
Print( "#I AGR.Test.Words:\n",
"#I omit TestWords for ", type[1], " and ", name[2],
"\n" );
fi;
else
for entry in r.( type[1] ) do
result:= type[2].TestWords( tocid, name[2],
entry[ Length( entry ) ], type, verbose )
and result;
od;
fi;
fi;
od;
# Check also those 'maxext' scripts (which do not form a data type)
# that belong to the given t.o.c.
r:= name[3];
if IsBound( r.maxext ) then
for entry in Filtered( r.maxext, l -> l[4] = tocid ) do
prg:= AtlasProgram( name[1], entry[1], "maxes", entry[2] );
if prg = fail then
if verbose then
Print( "#E AGR.Test.Words:\n",
"#E cannot verify 'maxext' entry '", entry[3], "'\n" );
result:= false;
fi;
elif not IsInternallyConsistent( prg.program ) then
Print( "#E AGR.Test.Words:\n",
"#E program '", entry[3],
"' not internally consistent\n" );
result:= false;
else
# Get a representation if available, and map the generators.
gens:= OneAtlasGeneratingSetInfo( prg.groupname,
prg.standardization,
NrMovedPoints, [ 2 .. maxdeg ],
"contents", [ tocid, "local" ] );
if gens = fail then
if verbose then
Print( "#I AGR.Test.Words:\n",
"#I no perm. repres. for '", prg.groupname,
"', no check for '", entry[3], "'\n" );
fi;
else
gens:= AtlasGenerators( gens );
grp:= Group( gens.generators );
if IsBound( gens.size ) then
SetSize( grp, gens.size );
fi;
gens:= ResultOfStraightLineProgram( prg.program,
gens.generators );
size:= Size( SubgroupNC( grp, gens ) );
#T use the recog package for larger cases!
if IsBound( prg.size ) then
if size <> prg.size then
Print( "#E AGR.Test.Words:\n",
"#E program '", entry[3], "' for group of order ",
size, " not ", prg.size, "\n" );
result:= false;
fi;
else
Print( "#I AGR.Test.Words:\n",
"#I add size ", size, " for program '", entry[3],
"'\n" );
fi;
fi;
fi;
od;
fi;
fi;
od;
# Return the result.
return result;
end;
#############################################################################
##
#F AGR.Test.FileHeaders( [<tocid>[,<gapname>]] )
##
## <#GAPDoc Label="test:AGR.Test.FileHeaders">
## <Mark><C>AGR.Test.FileHeaders( [</C><M>tocid</M><C>] )</C></Mark>
## <Item>
## checks whether the &MeatAxe; text files that belong to <M>tocid</M>
## have a header line that is consistent with the filename,
## and whether the contents of all &GAP; format data files that belong to
## <M>tocid</M> is consistent with the filename.
## </Item>
## <#/GAPDoc>
##
AGR.Test.FileHeaders:= function( arg )
local result, name, toc, record, type, entry, test;
# Initialize the result.
result:= true;
if Length( arg ) = 2 then
name:= First( AtlasOfGroupRepresentationsInfo.GAPnames,
x -> x[1] = arg[2] );
for toc in AGR.TablesOfContents( [ arg[1], "local" ] ) do
if IsBound( toc.( name[2] ) ) then
record:= toc.( name[2] );
for type in AGR.DataTypes( "rep" ) do
if IsBound( record.( type[1] ) ) then
for entry in record.( type[1] ) do
test:= type[2].TestFileHeaders( arg[1], arg[2], entry, type );
if not IsBool( test ) then
Print( "#E AGR.Test.FileHeaders:\n",
"#E ", test, " for ", entry[ Length( entry ) ],
"\n" );
test:= false;
fi;
result:= test and result;
od;
fi;
od;
fi;
od;
elif Length( arg ) = 1 then
for entry in AtlasOfGroupRepresentationsInfo.GAPnames do
result:= AGR.Test.FileHeaders( arg[1], entry[1] ) and result;
od;
elif Length( arg ) = 0 then
result:= AGR.Test.FileHeaders( "core" );
fi;
# Return the result.
return result;
end;
#############################################################################
##
#F AGR.Test.BinaryFormat( [<tocid>] )
##
## <#GAPDoc Label="test:AGR.Test.BinaryFormat">
## <Mark><C>AGR.Test.BinaryFormat( [</C><M>tocid</M><C>] )</C></Mark>
## <Item>
## checks whether all &MeatAxe; text files that belong to <M>tocid</M>
## satisfy that applying first <Ref Func="CMtxBinaryFFMatOrPerm"/> and
## then <Ref Func="FFMatOrPermCMtxBinary"/> yields the same object.
## </Item>
## <#/GAPDoc>
##
AGR.Test.BinaryFormat:= function( arg )
local tmpfile, tocid, result, r, gens, gen, test, cnv;
# Create one temporary file.
tmpfile:= Filename( DirectoryTemporary(), "testfile" );
# Get the data directory.
if IsEmpty( arg ) then
tocid:= [ "core", "local" ];
else
tocid:= arg[1];
fi;
result:= true;
for r in Concatenation( AllAtlasGeneratingSetInfos( "contents", tocid,
IsPermGroup, true ),
AllAtlasGeneratingSetInfos( "contents", tocid,
Characteristic, IsPosInt ) ) do
gens:= AtlasGenerators( r );
if gens <> fail then
gens:= gens.generators;
for gen in gens do
test:= false;
if IsPerm( gen ) then
CMtxBinaryFFMatOrPerm( gen, LargestMovedPoint( gen ), tmpfile );
test:= true;
elif IsMatrix( gen ) then
cnv:= ConvertToMatrixRep( gen );
if IsInt( cnv ) then
CMtxBinaryFFMatOrPerm( gen, cnv, tmpfile );
test:= true;
fi;
else
Print( "#E AGR.Test.BinaryFormat:\n",
"#E not permutation or matrix for '", r, "'\n" );
result:= false;
fi;
if test and gen <> FFMatOrPermCMtxBinary( tmpfile ) then
Print( "#E AGR.Test.BinaryFormat:\n",
"#E differences for '", r, "'\n" );
result:= false;
fi;
od;
fi;
od;
# Remove the temporary file.
RemoveFile( tmpfile );
# Return the result.
return result;
end;
#############################################################################
##
#F AGR.Test.Standardization( [<gapname>] )
##
## <#GAPDoc Label="test:AGR.Test.Standardization">
## <Mark><C>AGR.Test.Standardization()</C></Mark>
## <Item>
## checks whether all generating sets corresponding to the same set of
## standard generators have the same element orders; for the case that
## straight line programs for computing certain class representatives are
## available, also the orders of these representatives are checked
## w. r. t. all generating sets.
## </Item>
## <#/GAPDoc>
##
AGR.Test.Standardization:= function( arg )
local result, name, gapname, gensorders, cclorders, cycorders, tbl, info,
gens, std, ords, pair, prg, names, choice;
# Initialize the result.
result:= true;
if Length( arg ) = 0 then
for name in AtlasOfGroupRepresentationsInfo.GAPnames do
result:= AGR.Test.Standardization( name[1] ) and result;
od;
elif Length( arg ) = 1 and IsString( arg[1] ) then
gapname:= arg[1];
if AGR.InfoForName( gapname ) = fail then
Print( "#E AGR.Test.Standardization:\n",
"#E no group with GAP name '", gapname, "'\n" );
return false;
fi;
gensorders:= [];
cclorders:= [];
cycorders:= [];
tbl:= CharacterTable( gapname );
# Loop over the relevant representations.
for info in AllAtlasGeneratingSetInfos( gapname, "contents", "local" ) do
gens:= AtlasGenerators( info.identifier );
std:= gens.standardization;
# Check that the generators are invertible,
# and that the orders are equal in all representations.
if ForAll( gens.generators, x -> Inverse( x ) <> fail ) then
ords:= List( gens.generators, Order );
else
ords:= [ fail ];
fi;
if not ForAll( ords, IsInt ) then
Print( "#E AGR.Test.Standardization:\n",
"#E representation '", gens.identifier[2],
"': non-finite order\n" );
result:= false;
elif IsBound( gensorders[ std+1 ] ) then
if gensorders[ std+1 ] <> ords then
Print( "#E AGR.Test.Standardization:\n",
"#E '", gapname, "': representation '",
gens.identifier[2], "':\n",
"#E incompatible generator orders ",
ords, " and ", gensorders[ std+1 ], "\n" );
result:= false;
fi;
else
gensorders[ std+1 ]:= ords;
fi;
# If scripts for computing representatives of cyclic subgroups
# or representatives of conjugacy classes are available
# then check that their orders are equal in all representations.
for pair in [ [ cclorders, "classes" ], [ cycorders, "cyclic" ] ] do
if not IsBound( pair[1][ std+1 ] ) then
prg:= AtlasProgram( gapname, std, pair[2] );
if prg = fail then
pair[1][ std+1 ]:= fail;
else
pair[1][ std+1 ]:= [ prg.program,
List( ResultOfStraightLineProgram(
prg.program, gens.generators ), Order ) ];
if tbl <> fail then
names:= AtlasClassNames( tbl );
if IsBound( prg.outputs ) then
choice:= List( prg.outputs, x -> Position( names, x ) );
if ( not fail in choice ) and pair[1][ std+1 ][2]
<> OrdersClassRepresentatives( tbl ){ choice } then
Print( "#E AGR.Test.Standardization:\n",
"#E '", gapname, "': representation '",
gens.identifier[2], "':\n",
"#E ", pair[2],
" orders differ from character table\n" );
result:= false;
fi;
else
Print( "#E no component 'outputs' in '", pair[2],
"' for '", gapname, "'\n" );
fi;
fi;
fi;
elif pair[1][ std+1 ] <> fail then
if pair[1][ std+1 ][2] <> List( ResultOfStraightLineProgram(
pair[1][ std+1 ][1], gens.generators ), Order ) then
Print( "#E AGR.Test.Standardization:\n",
"#E '", gapname, "': representation '",
gens.identifier[2], "':\n",
"#E incompatible ", pair[2], " orders\n" );
result:= false;
fi;
fi;
od;
od;
fi;
# Return the result.
return result;
end;
#############################################################################
##
#F AGR.Test.StdTomLib( [<gapname>] )
##
## <#GAPDoc Label="test:AGR.Test.StdTomLib">
## <Mark><C>AGR.Test.StdTomLib()</C></Mark>
## <Item>
## checks whether the standard generators are compatible with those that
## occur in the <Package>TomLib</Package> package.
## </Item>
## <#/GAPDoc>
##
AGR.Test.StdTomLib:= function( arg )
local result, name, tomnames, tbl, tom, gapname, info, allgens, stdavail,
verified, falsified, G, i, iinfo, type, prg, res, gens, G2,
fitstotom, fitstohom;
if not IsPackageMarkedForLoading( "TomLib", "1.0" ) then
Print( "#E AGR.Test.StdTomLib:\n",
"#E TomLib not loaded, cannot verify ATLAS standardizations\n" );
return false;
fi;
# Initialize the result.
result:= true;
if Length( arg ) = 0 then
for name in AtlasOfGroupRepresentationsInfo.GAPnames do
result:= AGR.Test.StdTomLib( name[1] ) and result;
od;
# Check also that all tables of marks which provide standardization
# information really belong to ATLAS groups.
tomnames:= Set( List( Filtered( LIBTOMKNOWN.STDGEN, x -> x[2] <> "N" ),
x -> x[1] ) );
for name in AtlasOfGroupRepresentationsInfo.GAPnames do
tbl:= CharacterTable( name[1] );
if tbl <> fail then
tom:= TableOfMarks( tbl );
if tom <> fail then
RemoveSet( tomnames, Identifier( tom ) );
fi;
fi;
od;
if not IsEmpty( tomnames ) then
Print( "#E AGR.Test.StdTomLib:\n",
"#E cannot verify ATLAS standardizations for tables of ",
"marks in\n",
"#E ", tomnames, "\n" );
result:= false;
fi;
elif Length( arg ) = 1 and IsString( arg[1] ) then
gapname:= arg[1];
if AGR.InfoForName( gapname ) = fail then
Print( "#E AGR.Test.StdTomLib:\n",
"#E no group with GAP name '", gapname, "'\n" );
return false;
fi;
tbl:= CharacterTable( gapname );
# Check the ATLAS standardization against the TomLib standardization.
# (We consider only ATLAS permutation representations.)
if tbl = fail then
tom:= fail;
else
tom:= TableOfMarks( tbl );
fi;
if tom <> fail then
# The table of marks is available,
# which implies that the TomLib package is loaded.
# Thus '(Has)StandardGeneratorsInfo' is bound.
# (But avoid a syntax error message when reading the file
# in the case that TomLib is not available.)
if ValueGlobal( "HasStandardGeneratorsInfo" )( tom ) then
info:= ValueGlobal( "StandardGeneratorsInfo" )( tom );
else
info:= [];
fi;
allgens:= AllAtlasGeneratingSetInfos( gapname, IsPermGroup, true,
"contents", "local" );
stdavail:= Set( List( allgens, x -> x.standardization ) );
if not IsSubset( stdavail,
Set( List( info, r -> r.standardization ) ) ) then
Print( "#E AGR.Test.StdTomLib:\n",
"#E strange standardization info ",
" for table of marks of ", gapname, "\n" );
result:= false;
fi;
if not IsSubset( Set( List( info, r -> r.standardization ) ),
stdavail ) then
Print( "#I AGR.Test.StdTomLib:\n",
"#I extend STDGEN info for ", gapname, "\n" );
fi;
allgens:= List( stdavail,
i -> First( allgens, x -> x.standardization = i ) );
verified:= [];
falsified:= [];
G:= UnderlyingGroup( tom );
for i in Union( stdavail, List( info, r -> r.standardization ) ) do
# 1. Apply AtlasRep checks (using 'pres' and 'check' scripts)
# to the TomLib generators.
iinfo:= First( info, r -> IsBound( r.standardization ) and
r.standardization = i );
if i in stdavail then
for type in [ "pres", "check" ] do
prg:= AtlasProgram( gapname, i, type );
if prg <> fail then
res:= ResultOfStraightLineDecision( prg.program,
GeneratorsOfGroup( G ) );
if res = true then
AddSet( verified, i );
if iinfo = fail then
Print( "#I AGR.Test.StdTomLib:\n",
"#I ", gapname,
": extend TomLib standardization info, ",
"we have standardization = ", i, "\n" );
elif ForAny( info, r -> IsBound( r.standardization ) and
r.standardization <> i ) then
Print( "#E AGR.Test.StdTomLib:\n",
"#E ", gapname,
": different TomLib standardizations (", i,
" verified)?\n" );
result:= false;
fi;
else
AddSet( falsified, i );
if iinfo <> fail then
Print( "#E AGR.Test.StdTomLib:\n",
"#E ", gapname,
": TomLib standardization info is not ", i, "\n" );
result:= false;
fi;
fi;
fi;
od;
fi;
# 2. Apply TomLib checks to the Atlas generators
# (permutations only).
if iinfo.script = fail then
Print( "#E AGR.Test.StdTomLib:\n",
"#E ", gapname,
": script component 'fail' in TomLib standardization\n" );
else
# Compare the available ATLAS generators
# with this TomLib standardization.
for gens in allgens do
gens:= AtlasGenerators( gens.identifier );
G2:= Group( gens.generators );
fitstotom:= IsStandardGeneratorsOfGroup( info, G2,
gens.generators );
fitstohom:= GroupHomomorphismByImages( G, G2,
GeneratorsOfGroup( G ), gens.generators )
<> fail;
if fitstotom <> fitstohom then
Print( "#E AGR.Test.StdTomLib:\n",
"#E ", gapname,
": IsStandardGeneratorsOfGroup and ",
"homom. construction for standardization ",
gens.standardization, " inconsistent\n" );
fi;
if fitstotom then
AddSet( verified, gens.standardization );
if IsBound( info.standardization ) then
if info.standardization <> gens.standardization then
Print( "#I AGR.Test.StdTomLib:\n",
"#I ", gapname,
": TomLib standardization is ",
gens.standardization, " not ",
info.standardization, "\n" );
result:= false;
fi;
else
Print( "#I AGR.Test.StdTomLib:\n",
"#I ", gapname,
": TomLib standardization is ",
gens.standardization, "\n" );
fi;
else
AddSet( falsified, gens.standardization );
if IsBound( info.standardization ) and
info.standardization = gens.standardization then
Print( "#E AGR.Test.StdTomLib:\n",
"#E ", gapname,
": TomLib standardization is not ",
info.standardization, "\n" );
fi;
fi;
od;
fi;
od;
# Now 'verified' and 'falsified' are the lists of standardizations
# that hold or do not hold, respectively, for the generators of 'G'.
if IsEmpty( info ) then
Print( "#I AGR.Test.StdTomLib:\n",
"#I ", gapname, ": add TomLib info!\n" );
fi;
if IsSubset( falsified, stdavail ) and
ForAny( info, r -> r.ATLAS <> false ) then
Print( "#E AGR.Test.StdTomLib:\n",
"#E ", gapname,
": TomLib standardization info must be ATLAS = \"N\"\n" );
fi;
if ( not IsSubset( falsified, stdavail ) ) and
ForAny( info, r -> r.ATLAS = false ) then
Print( "#E AGR.Test.StdTomLib:\n",
"#E ", gapname,
": cannot verify TomLib info ATLAS = \"N\"\n" );
fi;
fi;
fi;
# Return the result.
return result;
end;
#############################################################################
##
#F AGR.Test.Files( [<tocid>[, <gapname>]] )
##
## <#GAPDoc Label="test:AGR.Test.Files">
## <Mark><C>AGR.Test.Files( [</C><M>tocid</M><C>] )</C></Mark>
## <Item>
## checks whether the &MeatAxe; text files that belong to <M>tocid</M>
## can be read with <Ref Func="ScanMeatAxeFile"/> such that the result
## is not <K>fail</K>.
## The function does not check whether the first line of a &MeatAxe; text
## file is consistent with the filename, since this can be tested with
## <C>AGR.Test.FileHeaders</C>.
## </Item>
## <#/GAPDoc>
##
AGR.Test.Files:= function( arg )
local result, entry, name, toc, record, type;
# Initialize the result.
result:= true;
if IsEmpty( arg ) then
result:= AGR.Test.Files( "core" );
elif Length( arg ) = 1 then
for entry in AtlasOfGroupRepresentationsInfo.GAPnames do
result:= AGR.Test.Files( arg[1], entry[1] ) and result;
od;
elif Length( arg ) = 2 then
name:= First( AtlasOfGroupRepresentationsInfo.GAPnames,
x -> x[1] = arg[2] );
if name = fail then
return result;
fi;
name:= name[2];
for toc in AGR.TablesOfContents( [ arg[1], "local" ] ) do
if IsBound( toc.( name ) ) then
record:= toc.( name );
for type in AGR.DataTypes( "rep" ) do
if IsBound( record.( type[1] ) ) then
for entry in record.( type[1] ) do
result:= type[2].TestFiles( arg[1], name, entry, type )
and result;
od;
fi;
od;
fi;
od;
fi;
# Return the result.
return result;
end;
#############################################################################
##
#F AGR.Test.ClassScripts( [<tocid>[, <gapname>]] )
##
## <#GAPDoc Label="test:AGR.Test.ClassScripts">
## <Mark><C>AGR.Test.ClassScripts( [</C><M>tocid</M><C>] )</C></Mark>
## <Item>
## checks whether the straight line programs that belong to <M>tocid</M>
## and that compute representatives of certain conjugacy classes
## are consistent with information stored on the &GAP; character table
## of the group in question, in the sense that
## the given class names really occur in the character table and that
## the element orders and centralizer orders for the classes are correct.
## </Item>
## <#/GAPDoc>
##
AGR.Test.ClassScripts:= function( arg )
local result, maxdeg, entry, tocid, gapname, groupname, toc, record,
std, name, prg, tbl, outputs, ident, classnames, map, gens, roots,
grp, reps, orders1, orders2, cents1, cents2, cycscript;
# Initialize the result.
result:= true;
maxdeg:= AGR.Test.MaxTestDegree;
if IsEmpty( arg ) then
return AGR.Test.ClassScripts( "core" );
elif Length( arg ) = 1 and IsString( arg[1] ) then
# The argument is an identifier of an extension.
for entry in AtlasOfGroupRepresentationsInfo.GAPnames do
result:= AGR.Test.ClassScripts( arg[1], entry[1] ) and result;
od;
return result;
elif Length( arg ) = 2 and IsString( arg[1] ) and IsString( arg[2] ) then
# The arguments are an identifier and a group name.
tocid:= arg[1];
gapname:= arg[2];
else
Error( "usage: AGR.Test.ClassScripts( [<tocid>[, <groupname>]] )" );
fi;
groupname:= First( AtlasOfGroupRepresentationsInfo.GAPnames,
pair -> pair[1] = gapname );
if groupname = fail then
Print( "#E AGR.Test.ClassScripts:\n",
"#E no group with name '", gapname, "'\n" );
return false;
fi;
groupname:= groupname[2];
for toc in AGR.TablesOfContents( [ tocid, "local" ] ) do
if IsBound( toc.( groupname ) ) then
record:= toc.( groupname );
for name in [ "cyclic", "classes", "cyc2ccl" ] do
if IsBound( record.( name ) ) then
for std in Set( List( record.( name ), x -> x[1] ) ) do
prg:= AtlasProgram( gapname, std, name );
if prg = fail then
Print( "#E AGR.Test.ClassScripts:\n",
"#E inconsistent program '", name, "' for '",
gapname, "'\n" );
result:= false;
else
# Fetch the character table of the group.
# (No further tests are possible if it is not available.)
tbl:= CharacterTable( gapname );
if tbl <> fail then
ident:= prg.identifier[2];
classnames:= AtlasClassNames( tbl );
if classnames <> fail then
if IsBound( prg.outputs ) then
outputs:= prg.outputs;
map:= List( outputs, x -> Position( classnames, x ) );
else
Print( "#E AGR.Test.ClassScripts:\n",
"#E no component 'outputs' in '", name,
"' for '", gapname, "'\n" );
result:= false;
outputs:= [ "-" ];
map:= [ fail ];
fi;
prg:= prg.program;
# (If '-' signs occur then we cannot test the names,
# but the number of outputs can be checked.)
roots:= ClassRoots( tbl );
roots:= Filtered( [ 1 .. Length( roots ) ],
i -> IsEmpty( roots[i] ) );
roots:= Set( List( roots, x -> ClassOrbit( tbl, x ) ) );
if ForAll( outputs, x -> not '-' in x ) then
# Check the class names.
if fail in map then
Print( "#E AGR.Test.ClassScripts:\n",
"#E strange class names ",
Difference( outputs, classnames ),
" for program ", ident, "\n" );
result:= false;
fi;
if name in [ "classes", "cyc2ccl" ]
and Set( classnames ) <> Set( outputs ) then
Print( "#E AGR.Test.ClassScripts:\n",
"#E class names ",
Difference( classnames, outputs ),
" not hit for program ", ident, "\n" );
result:= false;
fi;
if name = "cyclic" then
# Check whether all maximally cyclic subgroups
# are covered.
roots:= Filtered( roots,
list -> IsEmpty( Intersection( outputs,
classnames{ list } ) ) );
if not IsEmpty( roots ) then
Print( "#E AGR.Test.ClassScripts:\n",
"#E maximally cyclic subgroups ",
List( roots, x -> classnames{ x } ),
" not hit for program ", ident, "\n" );
result:= false;
fi;
fi;
elif name = "cyclic" and
Length( outputs ) <> Length( roots ) and
not ForAny( outputs, x -> '-' in x ) then
# The programs 'F23G1-cycW1' and 'F24G1-cycW1'
# specify some elements only up to Galois conjugacy.
Print( "#E AGR.Test.ClassScripts:\n",
"#E no. of outputs and cyclic subgroups differ",
" for program '", ident, "'\n" );
result:= false;
fi;
if not fail in map then
# Compute the representatives in a representation.
# (No further tests are possible if none is available.)
gens:= OneAtlasGeneratingSetInfo( gapname, std,
NrMovedPoints, [ 2 .. maxdeg ],
"contents", [ tocid, "local" ] );
if gens <> fail then
gens:= AtlasGenerators( gens.identifier );
if gens <> fail then
gens:= gens.generators;
fi;
if fail in gens then
gens:= fail;
fi;
if not name in [ "cyclic", "classes" ] then
# The input consists of the images of the standard
# generators under the 'cyc' script (which may belong
# to a different t.o.c.).
cycscript:= AtlasProgram( gapname, std, "cyclic",
"version", AGR.VersionOfSLP( ident )[1],
"contents", [ tocid, "local" ] );
if cycscript = fail then
gens:= fail;
Print( "#E AGR.Test.ClassScripts:\n",
"#E no script for computing the 'cyc' ",
"part of '", ident, "' available\n" );
result:= false;
elif gens <> fail then
gens:= ResultOfStraightLineProgram(
cycscript.program, gens );
fi;
fi;
fi;
if gens <> fail then
grp:= Group( gens );
reps:= ResultOfStraightLineProgram( prg, gens );
if Length( reps ) <> Length( outputs ) then
Print( "#E AGR.Test.ClassScripts:\n",
"#E inconsistent output numbers for ",
"program ", ident, "\n" );
result:= false;
else
# Check element orders and centralizer orders.
orders1:= OrdersClassRepresentatives( tbl ){ map };
orders2:= List( reps, Order );
if orders1 <> orders2 then
Print( "#E AGR.Test.ClassScripts:\n",
"#E element orders of ",
outputs{ Filtered( [ 1 .. Length( outputs ) ],
i -> orders1[i] <> orders2[i] ) },
" differ for program ", ident, "\n" );
result:= false;
fi;
cents1:= SizesCentralizers( tbl ){ map };
cents2:= List( reps, x -> Size( Centralizer(grp,x) ) );
if cents1 <> cents2 then
Print( "#E AGR.Test.ClassScripts:\n",
"#E centralizer orders of ",
outputs{ Filtered( [ 1 .. Length( outputs ) ],
i -> cents1[i] <> cents2[i] ) },
" differ for program ", ident, "\n" );
result:= false;
fi;
fi;
fi;
fi;
fi;
fi;
fi;
od;
fi;
od;
fi;
od;
# Return the result.
return result;
end;
#############################################################################
##
#F AGR.Test.CycToCcls( [<tocid>[, <gapname>]] )
##
## <#GAPDoc Label="test:AGR.Test.CycToCcls">
## <Mark><C>AGR.Test.CycToCcls( [</C><M>tocid</M><C>][:TryToExtendData] )</C></Mark>
## <Item>
## checks whether all straight line programs that belong to <M>tocid</M>
## and that compute class representatives from representatives of cyclic
## subgroups possess a corresponding straight line program
## (<E>anywhere</E> in the database)
## for computing representatives of cyclic subgroups.
## </Item>
## <#/GAPDoc>
##
## if the extend option is set then:
## checks whether some straight line program that computes representatives
## of conjugacy classes of a group can be computed from the ordinary
## &GAP; character table of that group and a straight line program in
## <M>tocid</M> that computes representatives of cyclic subgroups.
## In this case the missing scripts are printed.
##
AGR.Test.CycToCcls:= function( arg )
local result, triple, tocid, groupname, gapname, toc, record, entry,
versions, prg, tbl, version, str;
# Initialize the result.
result:= true;
if IsEmpty( arg ) then
return AGR.Test.CycToCcls( "core" );
elif Length( arg ) = 1 and IsString( arg[1] ) then
# The argument is an identifier of an extension.
for entry in AtlasOfGroupRepresentationsInfo.GAPnames do
result:= AGR.Test.CycToCcls( arg[1], entry[1] ) and result;
od;
return result;
elif Length( arg ) = 2 and IsString( arg[1] ) and IsString( arg[2] ) then
# The arguments are an identifier and a group name.
tocid:= arg[1];
gapname:= arg[2];
else
Error( "usage: AGR.Test.CycToCcls( [<tocid>[, <gapname>]] )" );
fi;
groupname:= First( AtlasOfGroupRepresentationsInfo.GAPnames,
pair -> pair[1] = gapname );
if groupname = fail then
Print( "#E AGR.Test.CycToCcls:\n",
"#E no group with name '", gapname, "'\n" );
return false;
fi;
groupname:= groupname[2];
for toc in AGR.TablesOfContents( tocid ) do
if not IsBound( toc.( groupname ) ) then
return true;
fi;
record:= toc.( groupname );
# Run over the 'cyc2ccl' scripts that are available in this t.o.c,
# and check whether *some t.o.c.* provides the corresponding 'cyc' script.
if IsBound( record.cyc2ccl ) then
for entry in record.cyc2ccl do
versions:= AGR.VersionOfSLP( entry[2] );
prg:= AtlasProgramInfo( gapname, "cyclic", "version", versions[1] );
if prg = fail then
Print( "#E AGR.Test.CycToCcls:\n",
"#E no program '\"",
ReplacedString( entry[2]{
[ 1 .. Position( entry[2], '-' )-1 ] },
"cyc", "-cyc" ), "\"' available\n" );
result:= false;
fi;
od;
fi;
if ValueOption( "TryToExtendData" ) <> true then
return result;
fi;
# Check whether the current t.o.c. contains a 'cyc' script for which
# no 'cyc2ccl' script is available in *any t.o.c.* but for which such a
# script can be computed.
# (This is possible only if we have the character table of the group.)
tbl:= CharacterTable( gapname );
if tbl <> fail and IsBound( record.cyclic ) then
for entry in record.cyclic do
version:= AGR.VersionOfSLP( entry[2] );
prg:= AtlasProgram( gapname, "cyc2ccl", version,
"contents", "local" );
if prg = fail then
# There is no 'cyc2ccl' script but perhaps we can create it.
prg:= AtlasProgram( gapname, "cyclic", version,
"contents", "local" );
if prg = fail then
Print( "#E AGR.Test.CycToCcls:\n",
"#E cannot access program '\"", entry[2], "\"\n" );
result:= false;
else
str:= StringOfAtlasProgramCycToCcls(
AtlasStringOfProgram( prg.program, prg.outputs ),
tbl, "names" );
if str <> fail then
prg:= ScanStraightLineProgram( str, "string" );
if prg = fail then
Print( "#E AGR.Test.CycToCcls:\n",
"#E automatically created cyc2ccl script for '",
entry[2], "' would be incorrect" );
result:= false;
else
prg:= prg.program;
Print( "#I AGR.Test.CycToCcls:\n",
"#I add the following script, in the new file '",
ReplacedString( entry[2], "-", "" ), "-cclsW1':\n",
str, "\n" );
fi;
fi;
fi;
fi;
od;
fi;
od;
# Return the result.
return result;
end;
#############################################################################
##
#F AGR.Test.GroupOrders( [true] )
##
## <#GAPDoc Label="test:AGR.Test.GroupOrders">
## <Mark><C>AGR.Test.GroupOrders()</C></Mark>
## <Item>
## checks whether the group orders stored in the <C>GAPnames</C> component
## of <Ref Var="AtlasOfGroupRepresentationsInfo"/> coincide with the
## group orders computed from an &ATLAS; permutation representation of
## degree up to <C>AGR.Test.MaxTestDegree</C>,
## from the available character table or table of marks with the given name,
## or from the structure of the name,
## in the sense that splitting the name at the first dot (<C>.</C>) or
## colon (<C>:</C>) and applying the same criteria to derive the group
## order from the two parts may yield enough information.
## </Item>
## <#/GAPDoc>
##
AGR.SizeExceptional2E6:= q -> q^36*(q^12-1)*(q^9+1)*(q^8-1)*(q^6-1)*
(q^5+1)*(q^2-1) / Gcd( 3, q+1 );
AGR.SizeExceptionalE:= function( n, q )
local data;
if n = 6 then
data:= [ 36, [ 12, 9, 8, 6, 5, 2 ], Gcd( 3, q-1 ) ];
elif n = 7 then
data:= [ 63, [ 18, 14, 12, 10, 8, 6, 2 ], Gcd( 2, q-1 ) ];
elif n = 8 then
data:= [ 120, [ 30, 24, 20, 18, 14, 12, 8, 2 ], 1 ];
else
Error( "<n> must be one of 6, 7, 8" );
fi;
return q^data[1] * Product( List( data[2], i -> q^i - 1 ) ) / data[3];
end;
AGR.Test.GroupOrders:= function( arg )
local verbose, formats, maxdeg, HasRemovableOuterBrackets, SizesFromName,
result, entry, size;
verbose:= ( Length( arg ) <> 0 and arg[1] = true );
formats:= [
[ [ "L", IsDigitChar, "(", IsDigitChar, ")" ],
l -> Size( PSL( l[2], l[4] ) ) ],
[ [ "2.L", IsDigitChar, "(", IsDigitChar, ")" ],
l -> 2 * Size( PSL( l[2], l[4] ) ) ],
[ [ "S", IsDigitChar, "(", IsDigitChar, ")" ],
l -> Size( PSp( l[2], l[4] ) ) ],
[ [ "2.S", IsDigitChar, "(", IsDigitChar, ")" ],
l -> 2 * Size( PSp( l[2], l[4] ) ) ],
[ [ "U", IsDigitChar, "(", IsDigitChar, ")" ],
l -> Size( PSU( l[2], l[4] ) ) ],
[ [ "E", IsDigitChar, "(", IsDigitChar, ")" ],
l -> AGR.SizeExceptionalE( l[2], l[4] ) ],
[ [ "2E6(", IsDigitChar, ")" ],
l -> AGR.SizeExceptional2E6( l[2] ) ],
];
maxdeg:= AGR.Test.MaxTestDegree;
HasRemovableOuterBrackets:= function( name )
local len, open, i;
len:= Length( name );
if Length( name ) < 2 or name[1] <> '(' or name[ len ] <> ')' then
return false;
fi;
open:= 0;
for i in [ 1 .. len-1 ] do
if name[i] = '(' then
open:= open + 1;
elif name[i] = ')' then
open:= open - 1;
fi;
if open = 0 then
return false;
fi;
od;
return true;
end;
SizesFromName:= function( name )
local result, pair, parse, tbl, tom, data, splitchar, pos,
name1, name2, size1, size2;
result:= [];
# Strip outer brackets.
while HasRemovableOuterBrackets( name ) do
name:= name{ [ 2 .. Length( name ) - 1 ] };
od;
# Deal with the case of integers.
if ForAll( name, x -> IsDigitChar( x ) or x in "^()" ) then
# No other criterion matches with this format, so we return.
return [ EvalString( name ) ];
fi;
#T perhaps improve: admit also '+' and '-'
#T if ForAll( name, x -> IsDigitChar( x ) or x in "^()+-" ) then
#T Print( "name not yet handled: ", name, "\n" );
#T fi;
for pair in formats do
parse:= ParseBackwards( name, pair[1] );
if parse <> fail then
AddSet( result, pair[2]( parse ) );
fi;
od;
# Try to use the character table information.
tbl:= CharacterTable( name );
if tbl <> fail then
AddSet( result, Size( tbl ) );
fi;
# Try to use the table of marks information.
tom:= TableOfMarks( name );
if tom <> fail then
AddSet( result, Size( UnderlyingGroup( tom ) ) );
fi;
# Try to use the (locally available) database,
# but only permutation representations up to degree 'maxdeg'.
data:= OneAtlasGeneratingSetInfo( name,
NrMovedPoints, [ 1 .. maxdeg ],
"contents", "local" );
if data <> fail then
data:= AtlasGenerators( data );
if data <> fail then
AddSet( result, Size( Group( data.generators ) ) );
fi;
fi;
# Try to evaluate the name structure.
for splitchar in ".:" do
pos:= Position( name, splitchar );
while pos <> fail do
name1:= name{ [ 1 .. pos-1 ] };
name2:= name{ [ pos+1 .. Length( name ) ] };
if Length( Positions( name1, '(' ) )
= Length( Positions( name1, ')' ) ) then
size1:= SizesFromName( name1 );
size2:= SizesFromName( name2 );
if Length( size1 ) = 1 and Length( size2 ) = 1 then
AddSet( result, size1[1] * size2[1] );
elif Length( size1 ) > 1 or Length( size2 ) > 1 then
Print( "#E AGR.Test.GroupOrders:\n",
"#E group orders: problem with '", name, "'\n" );
UniteSet( result,
Concatenation( List( size1, x -> x * size2 ) ) );
fi;
fi;
pos:= Position( name, splitchar, pos );
od;
od;
return result;
end;
result:= true;
for entry in AtlasOfGroupRepresentationsInfo.GAPnames do
size:= SizesFromName( entry[1] );
if 1 < Length( size ) then
Print( "#E AGR.Test.GroupOrders:\n",
"#E several group orders for '",
entry[1], "':\n#E ", size, "\n" );
result:= false;
elif not IsBound( entry[3].size ) then
if Length( size ) = 0 then
if verbose then
Print( "#I AGR.Test.GroupOrders:\n",
"#I group order for '", entry[1], "' unknown\n" );
fi;
else
entry[3].size:= size[1];
Print( "#I AGR.Test.GroupOrders:\n",
"#I set group order for '", entry[1], "'\n",
"[\"GRS\",[\"", entry[1], "\",", size[1], "]],\n" );
fi;
elif Length( size ) = 0 then
if verbose then
Print( "#I AGR.Test.GroupOrders:\n",
"#I cannot verify group order for '", entry[1], "'\n" );
fi;
elif size[1] <> entry[3].size then
Print( "#E AGR.Test.GroupOrders:\n",
"#E wrong group order for '", entry[1], "'\n" );
result:= false;
fi;
od;
return result;
end;
#############################################################################
##
#F AGR.IsFactorFusionWhoseImageHasSameMaxes( <tbl>, <factfus> )
##
## Let <A>tbl</A> be the character table of a group <M>G</M>, say,
## and <A>factfus</A> be a factor fusion record from <A>tbl</A>.
## Let <M>F</M> denote the factor group of <M>G</M> whose character table
## is given by <A>factfus</A><C>.name</C>.
## If we can show that the maximal subgroups of <M>F</M> are exactly the
## images of the maximal subgroups of <M>G</M> under the epimorphism from
## <M>G</M> to <M>F</M> then this function returns <K>true</K>,
## otherwise <K>fail</K>.
## <P/>
## The function is used to deduce the orders of maximal subgroups from those
## of suitable factor groups.
## <P/>
## The following idea is applied:
## If <M>K</M> is a normal subgroup in <M>G</M> such that <M>K</M>
## is contained in the Frattini subgroup <M>\Phi(G)</M> of <M>G</M>
## (i. e., contained in <E>any</E> maximal subgroup of <M>G</M>)
## then the maximal subgroups of <M>G</M> are exactly the preimages of the
## maximal subgroups of <M>G/K</M> under the natural epimorphism.
## <P/>
## This situation occurs in the following cases.
## <List>
## <Item>
## If <M>G</M> is perfect then <M>Z(G)</M> is contained in <M>\Phi(G)</M>
## because <M>G' \cap Z(G) \leq \Phi(G)</M> holds,
## by <Cite Key="Hup67" SubKey="Kap. III, §3, Satz 3.12)"/>.
## For example, the orders of the maximal subgroups of <M>3.A_6</M> are
## the orders of the maximal subgroups of <M>A_6</M>,
## multiplied by the factor three.
## </Item>
## <Item>
## If <M>G</M> is an upward extension of a perfect group <M>N</M>
## then <M>Z(N)</M> is contained in <M>\Phi(N)</M>,
## and since <M>\Phi(N) \leq \Phi(G)</M> holds for any normal subgroup
## <M>N</M> of <M>G</M>
## (see <Cite Key="Hup67" SubKey="Kap. III, §3, Hilfssatz 3.3 b)"/>),
## we get that <M>Z(N)</M> is contained in <M>\Phi(G)</M>.
## For example the orders of the maximal subgroups of <M>3.A_6.2_1</M> are
## the orders of the maximal subgroups of <M>A_6.2_1</M>,
## multiplied by the factor three.
## </Item>
## </List>
##
AGR.IsFactorFusionWhoseImageHasSameMaxes:= function( tbl, factfus )
local ker, nam, subtbl, subfus, subker;
# Compute the kernel <M>K</M> of the epimorphism.
ker:= ClassPositionsOfKernel( factfus.map );
if Length( ker ) = 1 then
# This is not a factor fusion.
return fail;
elif not IsSubset( ClassPositionsOfDerivedSubgroup( tbl ), ker ) then
# We have no criterion for this case.
return fail;
elif IsSubset( ClassPositionsOfCentre( tbl ), ker ) then
# We have <M>K \leq G' \cap Z(G)</M>,
# so the maximal subgroups are exactly the preimages of the
# maximal subgroups in the factor group.
return true;
fi;
# Look for a suitable normal subgroup <M>N</M> of <M>G</M>.
for nam in NamesOfFusionSources( tbl ) do
subtbl:= CharacterTable( nam );
if subtbl <> fail then
subfus:= GetFusionMap( subtbl, tbl );
if Size( subtbl ) = Sum( SizesConjugacyClasses( tbl ){
Set( subfus ) } ) and
IsSubset( subfus, ker ) then
# <M>N</M> is normal in <M>G</M>, with <M>K \leq N</M>
subker:= Filtered( [ 1 .. Length( subfus ) ],
i -> subfus[i] in ker );
if IsSubset( ClassPositionsOfDerivedSubgroup( subtbl ),
subker ) and
IsSubset( ClassPositionsOfCentre( subtbl ), subker ) then
# We have <M>K \leq N' \cap Z(N)</M>.
return true;
fi;
fi;
fi;
od;
return fail;
end;
#############################################################################
##
#F AGR.Test.MaxesOrders( [<tocid>,[ <entry>][,][<verbose>] )
##
## <#GAPDoc Label="test:AGR.Test.MaxesOrders">
## <Mark><C>AGR.Test.MaxesOrders( [</C><M>tocid</M><C>] )</C></Mark>
## <Item>
## checks whether the orders of maximal subgroups stored in the component
## <C>GAPnames</C> of <Ref Var="AtlasOfGroupRepresentationsInfo"/>
## coincide with the orders computed from the restriction of an &ATLAS;
## permutation representation of degree up to
## <C>AGR.Test.MaxTestDegree</C>
## (using a straight line program that belongs to <M>tocid</M>),
## from the character table, or the table of marks with the given name,
## or from the information about maximal subgroups of the factor group
## modulo a normal subgroup that is contained in the Frattini subgroup.
## </Item>
## <#/GAPDoc>
##
AGR.Test.MaxesOrders:= function( arg )
local verbose, tocid, result, toc, extend, maxdeg, maxmax,
MaxesInfoForName, entry, info, size, filt;
verbose:= ( Length( arg ) <> 0 and arg[ Length( arg ) ] = true );
tocid:= First( arg, IsString );
if tocid = fail then
tocid:= "core";
fi;
result:= true;
toc:= AGR.TablesOfContents( [ tocid, "local" ] );
if toc = fail then
return result;
fi;
toc:= toc[1];
extend:= ( ValueOption( "TryToExtendData" ) = true );
maxdeg:= AGR.Test.MaxTestDegree;
maxmax:= AGR.Test.HardCases.MaxNumberMaxes;
MaxesInfoForName:= function( name )
local result, nrmaxes, tbl, oneresult, i,
subtbl, tom, std, g, prg, gens, factfus, recurs, good;
result:= [];
nrmaxes:= [];
# Try to use the character table information.
tbl:= CharacterTable( name );
if tbl <> fail then
if HasMaxes( tbl ) then
AddSet( nrmaxes, Length( Maxes( tbl ) ) );
AddSet( result, List( Maxes( tbl ),
nam -> Size( CharacterTable( nam ) ) ) );
else
# Try whether individual maxes are supported.
oneresult:= [];
if tbl <> fail then
for i in [ 1 .. maxmax ] do
subtbl:= CharacterTable( Concatenation( Identifier( tbl ), "M",
String( i ) ) );
if subtbl <> fail then
oneresult[i]:= Size( subtbl );
fi;
od;
fi;
if not IsEmpty( oneresult ) then
AddSet( result, oneresult );
fi;
fi;
fi;
# Try to use the table of marks information.
# more tests: how to identify FusionsToLibTom( tom )?
tom:= TableOfMarks( name );
if tom <> fail then
AddSet( nrmaxes, Length( MaximalSubgroupsTom( tom )[1] ) );
AddSet( result, Reversed( SortedList( OrdersTom( tom ){
MaximalSubgroupsTom( tom )[1] } ) ) );
fi;
# Try to use the AtlasRep database.
for std in [ 1 .. AGR.Test.HardCases.MaxNumberStd ] do
g:= AtlasGroup( name, std, "contents", "local" );
if ( g <> fail ) and
( ( HasSize( g ) and Size( g ) < 10^7 ) or
( IsPermGroup( g ) and NrMovedPoints( g ) <= maxdeg ) ) then
oneresult:= [];
for i in [ 1 .. maxmax ] do
if extend then
prg:= AtlasProgram( name, std, "maxes", i, "contents", "local" );
else
prg:= AtlasProgram( name, std, "maxes", i,
"contents", [ tocid, "local" ] );
fi;
if prg <> fail then
gens:= ResultOfStraightLineProgram( prg.program,
GeneratorsOfGroup( g ) );
if verbose then
Print( "#I AGR.Test.MaxesOrders:\n",
"#I computing max. ", i, " for ", name, "\n" );
fi;
oneresult[i]:= Size( SubgroupNC( g, gens ) );
fi;
od;
if not IsEmpty( oneresult ) then
AddSet( result, oneresult );
fi;
fi;
od;
# Try to deduce the orders of maximal subgroups from those of factors.
if tbl <> fail then
for factfus in ComputedClassFusions( tbl ) do
if AGR.IsFactorFusionWhoseImageHasSameMaxes( tbl, factfus ) = true
then
recurs:= MaxesInfoForName( factfus.name );
UniteSet( nrmaxes, recurs.nrmaxes );
UniteSet( result,
recurs.maxesorders * Sum( SizesConjugacyClasses( tbl ){
ClassPositionsOfKernel( factfus.map ) } ) );
fi;
od;
fi;
# Compact the partial results.
good:= true;
for oneresult in result{ [ 2 .. Length( result ) ] } do
for i in [ 1 .. Length( oneresult ) ] do
if IsBound( result[1][i] ) then
if IsBound( oneresult[i] ) then
if result[1][i] <> oneresult[i] then
good:= false;
fi;
fi;
elif IsBound( oneresult[i] ) then
result[1][i]:= oneresult[i];
fi;
od;
od;
if good and not IsEmpty( result ) then
result:= [ result[1] ];
fi;
return rec( maxesorders:= result,
nrmaxes:= Set( nrmaxes ) );
end;
if Length( arg ) = 0 or
ForAll( arg, x -> IsString( x ) or IsBool( x ) ) then
for entry in AtlasOfGroupRepresentationsInfo.GAPnames do
result:= AGR.Test.MaxesOrders( tocid, entry, verbose ) and result;
od;
else
entry:= First( arg, x -> not IsBool( x ) and not IsString( x ) );
info:= MaxesInfoForName( entry[1] );
if not IsBound( entry[3].nrMaxes ) then
if Length( info.nrmaxes ) = 1 then
Print( "#I AGR.Test.MaxesOrders:\n",
"#I set maxes number for '", entry[1], "':\n",
"[\"MXN\",[\"", entry[1], "\",", info.nrmaxes[1], "]],\n" );
fi;
elif Length( info.nrmaxes ) <> 1 then
if verbose then
Print( "#I AGR.Test.MaxesOrders:\n",
"#I cannot verify stored maxes number for '", entry[1],
"'\n" );
fi;
fi;
size:= info.maxesorders;
if 1 < Length( size ) then
Print( "#E AGR.Test.MaxesOrders:\n",
"#E several maxes orders for '", entry[1], "':\n",
"#E ", size, "\n" );
result:= false;
elif not IsBound( entry[3].sizesMaxes )
or IsEmpty( entry[3].sizesMaxes ) then
# No maxes orders are stored yet.
if Length( size ) = 0 then
if IsBound( toc.( entry[2] ) ) and
IsBound( toc.( entry[2] ).maxes ) and
not IsEmpty( toc.( entry[2] ).maxes ) then
# We have at least one straight line program but no repres.
Print( "#I AGR.Test.MaxesOrders:\n",
"#I maxes orders for '", entry[1],
"' unknown (but slps available)\n" );
elif verbose then
# We have no information about maximal subgroups.
Print( "#I AGR.Test.MaxesOrders:\n",
"#I maxes orders for '", entry[1], "' unknown\n" );
fi;
else
if IsBound( entry[3].size ) then
if entry[3].size in size[1] then
Print( "#E AGR.Test.MaxesOrders:\n",
"#E group order in maxes orders list for '", entry[1],
"'\n" );
result:= false;
--> --------------------
--> maximum size reached
--> --------------------
[ Dauer der Verarbeitung: 0.73 Sekunden
(vorverarbeitet)
]
|