Spracherkennung für: .g vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]
#############################################################################
##
#W test.g GAP 4 package CTblLib Thomas Breuer
##
## This file contains functions to test the data available in the
## GAP Character Table Library.
##
#############################################################################
##
## <#GAPDoc Label="tests">
## The fact that the &GAP; Character Table Library is designed as an
## open database
## (see Chapter <Ref Chap="ch:introduction"/>)
## makes it especially desirable to have consistency checks available
## which can be run automatically whenever new data get added.
## <P/>
## The file <F>tst/testall.g</F> of the package
## contains <Ref Func="Test" BookName="ref"/> statements
## for executing a collection of such sanity checks;
## one can run them by calling
## <C>ReadPackage( "CTblLib", "tst/testall.g" )</C>.
## If no problem occurs then &GAP; prints only lines starting with one of
## the following.
## <P/>
## <Log><![CDATA[
## + Input file:
## + GAP4stones:
## ]]></Log>
## <P/>
## 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 following tests concern only <E>ordinary</E> character tables.
## In all cases,
## let <M>tbl</M> be the ordinary character table of a group <M>G</M>, say.
## The return value is <K>false</K> if an error occurred,
## and <K>true</K> otherwise.
## <P/>
## <List>
## <#Include Label="test:CTblLib.Test.InfoText">
## <#Include Label="test:CTblLib.Test.RelativeNames">
## <#Include Label="test:CTblLib.Test.FindRelativeNames">
## <#Include Label="test:CTblLib.Test.PowerMaps">
## <#Include Label="test:CTblLib.Test.TableAutomorphisms">
## <#Include Label="test:CTblLib.Test.CompatibleFactorFusions">
## <#Include Label="test:CTblLib.Test.FactorsModPCore">
## <#Include Label="test:CTblLib.Test.Fusions">
## <#Include Label="test:CTblLib.Test.Maxes">
## <#Include Label="test:CTblLib.Test.ClassParameters">
## <#Include Label="test:CTblLib.Test.TablesOfSymmetricGroup">
## <#Include Label="test:CTblLib.Test.Constructions">
## <#Include Label="test:CTblLib.Test.ExtensionInfo">
## <#Include Label="test:CTblLib.Test.GroupForGroupInfo">
## </List>
## <P/>
## The following tests concern only <E>modular</E> character tables.
## In all cases,
## let <M>modtbl</M> be a Brauer character table of a group <M>G</M>, say.
## <P/>
## <List>
## <#Include Label="test:CTblLib.Test.BlocksInfo">
## <#Include Label="test:CTblLib.Test.TensorDecomposition">
## <#Include Label="test:CTblLib.Test.Indicators">
## <#Include Label="test:CTblLib.Test.FactorBlocks">
## </List>
## <#/GAPDoc>
##
#############################################################################
##
## 1. General tools for checking character tables
##
CTblLib.BlanklessString:= function( obj, ncols )
local result, stream;
result:= "";
stream:= OutputTextString( result, true );
SetPrintFormattingStatus( stream, true );
BlanklessPrintTo( stream, obj, ncols, 0, false );
CloseStream( stream );
return result;
end;
CTblLib.Test.BracketsString:= function( string )
local pos, open, i, partner;
pos:= 1;
open:= [];
for i in [ 1 .. Length( string ) ] do
if string[i] in "({[" then
Add( open, string[i] );
elif string[i] in ")}]" then
if Length( open ) = 0 then
return false;
fi;
partner:= open[ Length( open ) ];
if ( string[i] = ')' and partner = '(' ) or
( string[i] = '}' and partner = '{' ) or
( string[i] = ']' and partner = '[' ) then
Unbind( open[ Length( open ) ] );
else
return false;
fi;
fi;
od;
return IsEmpty( open );
end;
#############################################################################
##
#F CTblLib.PrintTestLog( <type>, <functionname>, <tblname>, <texts1>,
#F <texts2>, ... )
#F CTblLib.PrintTestLog( <type>, <functionname>, <tblname>, <textlist> )
##
## This function is used in the test functions in this file.
## <type> should be one of the strings "E", "I";
##
CTblLib.PrintTestLog:= function( arg )
local type, functionname, info, pos, tblname, texts, libinfo, text, entry;
type:= arg[1]; # either "E" or "I"
functionname:= arg[2];
info:= arg[3];
pos:= PositionSublist( info, " -> " );
if pos = fail then
tblname:= info;
else
tblname:= info{ [ 1 .. pos-1 ] };
fi;
texts:= arg{ [ 4 .. Length( arg ) ] };
if Length( texts ) = 1 and IsList( texts[1] )
and not IsString( texts[1] ) then
texts:= texts[1];
fi;
libinfo:= LibInfoCharacterTable( tblname );
if libinfo = fail then
libinfo:= "no library file";
else
libinfo:= libinfo.fileName;
fi;
Print( "#", type, " ", functionname, ": (at time ", Runtime(), ")\n",
"#", type, " for " );
if tblname = info then
Print( "table ", tblname, " (in ", libinfo, ")\n" );
else
Print( info, " (in ", libinfo, ")\n" );
fi;
for text in texts do
Print( "#", type, " " );
if IsString( text ) or not IsList( text ) then
Print( text );
else
for entry in text do
Print( entry );
od;
fi;
Print( "\n" );
od;
end;
#############################################################################
##
#F CTblLib.AdmissibleNames( <tblname> )
##
CTblLib.AdmissibleNames:= function( tblname )
local pos;
pos:= Position( LIBLIST.allnames, LowercaseString( tblname ) );
if pos = fail then
# The table does not belong to the library.
return [ tblname ];
#T not clean but needed in the functions below,
#T in order to deal with new tables before adding them?
else
pos:= LIBLIST.position[ pos ];
return LIBLIST.allnames{ Filtered( [ 1 .. Length( LIBLIST.position ) ],
i -> LIBLIST.position[i] = pos ) };
fi;
end;
#############################################################################
##
#F CTblLib.Test.RelativeNames( <tbl>[, <tblname>] )
##
## <#GAPDoc Label="test:CTblLib.Test.RelativeNames">
## <Mark><C>CTblLib.Test.RelativeNames( </C><M>tbl</M><C>[, </C><M>tblname</M><C>] )</C></Mark>
## <Item>
## checks some properties of those admissible names for <M>tbl</M>
## that refer to a related group <M>H</M>, say.
## Let <M>name</M> be an admissible name for the character table of
## <M>H</M>. (In particular, <M>name</M> is not an empty string.)
## Then the following relative names are considered.
## <P/>
## <List>
## <Mark><M>name</M><C>M</C><M>n</M></Mark>
## <Item>
## <M>G</M> is isomorphic with the groups in the <M>n</M>-th class of
## maximal subgroups of <M>H</M>.
## An example is <C>"M12M1"</C> for the Mathieu group <M>M_{11}</M>.
## We consider only cases where <M>name</M> does <E>not</E> contain
## the letter <C>x</C>.
## For example, <C>2xM12</C> denotes the direct product of a cyclic group
## of order two and the Mathieu group <M>M_{12}</M>
## but <E>not</E> a maximal subgroup of <Q><C>2x</C></Q>.
## Similarly, <C>3x2.M22M5</C> denotes the direct product of a cyclic
## group of order three and a group in the fifth class of maximal
## subgroups of <M>2.M_{22}</M>
## but <E>not</E> a maximal subgroup of <Q><C>3x2.M22</C></Q>.
## </Item>
## <Mark><M>name</M><C>N</C><M>p</M></Mark>
## <Item>
## <M>G</M> is isomorphic with the normalizers of the
## Sylow <M>p</M>-subgroups of <M>H</M>.
## An example is <C>"M24N2"</C> for the (self-normalizing)
## Sylow <M>2</M>-subgroup in the Mathieu group <M>M_{24}</M>.
## </Item>
## <Mark><M>name</M><C>N</C><M>cnam</M></Mark>
## <Item>
## <M>G</M> is isomorphic with the normalizers of the
## cyclic subgroups generated by the elements in the class with the name
## <M>cnam</M> of <M>H</M>.
## An example is <C>"O7(3)N3A"</C> for the normalizer of an element
## in the class <C>3A</C> of the simple group <M>O_7(3)</M>.
## </Item>
## <Mark><M>name</M><C>C</C><M>cnam</M></Mark>
## <Item>
## <M>G</M> is isomorphic with the groups in the centralizers of the
## elements in the class with the name <M>cnam</M> of <M>H</M>.
## An example is <C>"M24C2A"</C> for the centralizer of an element in the
## class <C>2A</C> in the Mathieu group <M>M_{24}</M>.
## </Item>
## </List>
## <P/>
## In these cases, <C>CTblLib.Test.RelativeNames</C> checks whether a
## library table with the admissible name <M>name</M> exists and a class
## fusion to <M>tbl</M> is stored on this table.
## <P/>
## In the case of Sylow <M>p</M>-normalizers,
## it is also checked whether <M>G</M> contains a normal
## Sylow <M>p</M>-subgroup of the same order as the
## Sylow <M>p</M>-subgroups in <M>H</M>.
## If the normal Sylow <M>p</M>-subgroup of <M>G</M> is cyclic then it is
## also checked whether <M>G</M> is the full Sylow <M>p</M>-normalizer in
## <M>H</M>.
## (In general this information cannot be read off
## from the character table of <M>H</M>).
## <P/>
## In the case of normalizers (centralizers) of cyclic subgroups,
## it is also checked whether <M>H</M> really normalizes (centralizes) a
## subgroup of the given order,
## and whether the class fusion from <M>tbl</M> to the table of <M>H</M>
## is compatible with the relative name.
## <P/>
## If the optional argument <M>tblname</M> is given then only this name
## is tested.
## If there is only one argument then all admissible names for <M>tbl</M>
## are tested.
## </Item>
## <#/GAPDoc>
##
CTblLib.Test.RelativeNames:= function( arg )
local result, tbl, name, tocheck, tblname, parse, filt, p, size, classes,
supertbl, orders, centralizers, cand, cen, fus, classname, cname,
orbits, supname;
result:= true;
tbl:= arg[1];
if Length( arg ) = 1 then
for name in CTblLib.AdmissibleNames( Identifier( tbl ) ) do
result:= CTblLib.Test.RelativeNames( tbl, name ) and result;
od;
else
tocheck:= [];
tblname:= Identifier( tbl );
name:= LowercaseString( arg[2] );
# The names of ordinary tables must not involve the substring `mod'.
if PositionSublist( tblname, "mod" ) <> fail then
CTblLib.PrintTestLog( "E", "CTblLib.Test.RelativeNames", tblname,
[ [ "`", name, "' contains substring `mod'" ] ] );
result:= false;
fi;
# Brackets in the name must occur in pairs.
if not CTblLib.Test.BracketsString( name ) then
CTblLib.PrintTestLog( "E", "CTblLib.Test.RelativeNames", tblname,
[ [ "inconsistent brackets in `", name, "'" ] ] );
result:= false;
fi;
# Check names of the form <grpname>M<n>.
# (We are not interested in names such as "3xM12" or "3x2.M22M5".)
parse:= PParseBackwards( name, [ IsChar, "m", IsDigitChar ] );
if parse <> fail and parse[3] <> 0 and not 'x' in parse[1] then
Add( tocheck, parse[1] );
fi;
# Check names of the form <grpname>N<p>.
parse:= PParseBackwards( name, [ IsChar, "n", IsDigitChar ] );
if parse <> fail and parse[3] <> 0 then
p:= parse[3];
if not IsPrimeInt( p ) then
CTblLib.PrintTestLog( "E", "CTblLib.Test.RelativeNames", tblname,
[ [ "`", p, "' is not a prime" ] ] );
result:= false;
else
Add( tocheck, parse[1] );
# Check whether the Sylow `p' subgroup is normal.
size:= p ^ Number( Factors( Size( tbl ) ), x -> x = p );
classes:= SizesConjugacyClasses( tbl );
if ForAll( ClassPositionsOfNormalSubgroups( tbl ),
l -> Sum( classes{ l } ) <> size ) then
CTblLib.PrintTestLog( "E", "CTblLib.Test.RelativeNames", tblname,
[ [ "Sylow ", p, " subgroup is not normal" ] ] );
result:= false;
fi;
# Check whether the two Sylow `p' subgroups have the same order.
supertbl:= CharacterTable( parse[1] );
if supertbl <> fail then
if size
<> p ^ Number( Factors( Size( supertbl ) ), x -> x = p ) then
CTblLib.PrintTestLog( "E", "CTblLib.Test.RelativeNames", tblname,
[ [ "the Sylow ", p, " subgroup of `", parse[1],
"' has different order" ] ] );
result:= false;
fi;
# If the Sylow `p' subgroup is cyclic then check the order of the
# normalizer.
orders:= OrdersClassRepresentatives( supertbl );
if size in orders and
Size( tbl )
<> SizesCentralizers( supertbl )[ Position( orders, size ) ]
* Phi( size ) / Number( orders, x -> x = size ) then
CTblLib.PrintTestLog( "E", "CTblLib.Test.RelativeNames", tblname,
[ [ "order is not that of the Sylow ", p,
" normalizer of `", parse[1], "'" ] ] );
result:= false;
fi;
fi;
fi;
fi;
# Check names of the form <grpname>C<nam>.
parse:= PParseBackwards( name,
[ IsChar, "c", IsDigitChar, IsAlphaChar ] );
if parse <> fail and parse[3] <> 0 and not IsEmpty( parse[4] ) then
Add( tocheck, parse[1] );
supertbl:= CharacterTable( parse[1] );
if supertbl <> fail then
# Check whether a class in the big group has a centralizer order
# equal to the order of `tbl',
# such that the class fusion is compatible with that.
centralizers:= SizesCentralizers( supertbl );
orders:= OrdersClassRepresentatives( supertbl );
cand:= Filtered( [ 1 .. Length( centralizers ) ],
i -> centralizers[i] = Size( tbl ) and
orders[i] = parse[3] );
if IsEmpty( cand ) then
CTblLib.PrintTestLog( "E", "CTblLib.Test.RelativeNames", tblname,
[ [ "not the centralizer of an el. of order ", parse[3],
" in `", parse[1], "'" ] ] );
result:= false;
else
orders:= OrdersClassRepresentatives( tbl );
cen:= Filtered( ClassPositionsOfCentre( tbl ),
i -> orders[i] = parse[3] );
fus:= GetFusionMap( tbl, supertbl );
if fus <> fail then
cen:= Filtered( cen, i -> fus[i] in cand );
fi;
if IsEmpty( cen ) then
CTblLib.PrintTestLog( "E", "CTblLib.Test.RelativeNames", tblname,
[ [ "not the centralizer of an el. of order ", parse[3],
" in `", parse[1], "'" ] ] );
result:= false;
elif fus <> fail then
classname:= LowercaseString( Concatenation(
String( parse[3] ), parse[4] ) );
cname:= List( ClassNames( supertbl ){ fus{ cen } },
LowercaseString );
if not classname in cname then
CTblLib.PrintTestLog( "E", "CTblLib.Test.RelativeNames", tblname,
[ "centralizer in `", parse[1], "' of a class in `" ],
[ String( cname ), "' not of `", classname, "'" ] );
result:= false;
fi;
fi;
fi;
fi;
fi;
# Check names of the form <grpname>N<nam>.
parse:= PParseBackwards( name,
[ IsChar, "n", IsDigitChar, IsAlphaChar ] );
if parse <> fail and parse[3] <> 0 and not IsEmpty( parse[4] ) then
Add( tocheck, parse[1] );
supertbl:= CharacterTable( parse[1] );
if supertbl <> fail then
# Check whether a class in the big group has a normalizer order
# equal to the order of `tbl',
# such that the class fusion is compatible with that.
centralizers:= SizesCentralizers( supertbl );
orders:= OrdersClassRepresentatives( supertbl );
orbits:= List( [ 1 .. NrConjugacyClasses( supertbl ) ],
i -> Length( ClassOrbit( supertbl, i ) ) );
cand:= Filtered( [ 1 .. Length( centralizers ) ],
i -> centralizers[i] * Phi( orders[i] )
/ orbits[i] = Size( tbl ) and
orders[i] = parse[3] );
if IsEmpty( cand ) then
CTblLib.PrintTestLog( "E", "CTblLib.Test.RelativeNames", tblname,
[ [ "not the centralizer of an el. of order ", parse[3],
" in `", parse[1], "'" ] ] );
result:= false;
else
orders:= OrdersClassRepresentatives( tbl );
classes:= SizesConjugacyClasses( tbl );
cen:= Filtered( [ 1 .. Length( orders ) ],
i -> orders[i] = parse[3] and
orders[i] = Sum(
classes{ ClassPositionsOfNormalClosure( tbl, [ i ] ) } ) );
fus:= GetFusionMap( tbl, supertbl );
if fus <> fail then
cen:= Filtered( cen, i -> fus[i] in cand );
fi;
if IsEmpty( cen ) then
CTblLib.PrintTestLog( "E", "CTblLib.Test.RelativeNames", tblname,
[ [ "not the normalizer of an el. of order ", parse[3],
" in `", parse[1], "'" ] ] );
result:= false;
elif fus <> fail then
classname:= LowercaseString( Concatenation(
String( parse[3] ), parse[4] ) );
cname:= List( ClassNames( supertbl ){ fus{ cen } },
LowercaseString );
if not classname in cname then
CTblLib.PrintTestLog( "E", "CTblLib.Test.RelativeNames", tblname,
[ "normalizer in `", parse[1], "' of a class in `" ],
[ String( cname ), "' not of `", classname, "'" ] );
result:= false;
fi;
fi;
fi;
fi;
fi;
for supname in tocheck do
supertbl:= CharacterTable( supname );
if supertbl = fail then
CTblLib.PrintTestLog( "E", "CTblLib.Test.RelativeNames", tblname,
[ [ "no character table with name `", supname, "'" ] ] );
result:= false;
elif GetFusionMap( tbl, supertbl ) = fail then
# Check that a class fusion is stored.
CTblLib.PrintTestLog( "I", "CTblLib.Test.RelativeNames", tblname,
[ [ "no fusion to `", Identifier( supertbl ), "' stored" ] ] );
fus:= CTblLib.Test.SubgroupFusion( tbl, supertbl );
if IsRecord( fus ) then
CTblLib.PrintTestLog( "I", "CTblLib.Test.RelativeNames", tblname,
"store the following fusion" );
fus:= ShallowCopy( fus );
fus.name:= supertbl;
Print( LibraryFusion( tbl, fus ) );
fi;
result:= false;
fi;
od;
fi;
return result;
end;
#############################################################################
##
#F CTblLib.Test.FindRelativeNames( <tbl> )
##
## <#GAPDoc Label="test:CTblLib.Test.FindRelativeNames">
## <Mark><C>CTblLib.Test.FindRelativeNames( </C><M>tbl</M><C> )</C></Mark>
## <Item>
## runs over the class fusions stored on <M>tbl</M>.
## If <M>tbl</M> is the full centralizer/normalizer of a cyclic subgroup
## in the table to which the class fusion points
## then the function proposes to make the corresponding relative name
## an admissible name for <M>tbl</M>.
## </Item>
## <#/GAPDoc>
##
CTblLib.Test.FindRelativeNames:= function( tbl )
local orders, classes, cen, nor, result, tocheck, record, fus, supertbl,
centralizers, superclasses, i, orbits, j, name, info;
orders:= OrdersClassRepresentatives( tbl );
classes:= SizesConjugacyClasses( tbl );
cen:= ClassPositionsOfCentre( tbl );
nor:= Filtered( [ 1 .. NrConjugacyClasses( tbl ) ],
i -> orders[i] = Sum( classes{
ClassPositionsOfNormalClosure( tbl, [ i ] ) } ) );
result:= true;
tocheck:= [];
for record in ComputedClassFusions( tbl ) do
fus:= record.map;
if Length( ClassPositionsOfKernel( fus ) ) = 1 then
supertbl:= CharacterTable( record.name );
if supertbl <> fail then
centralizers:= SizesCentralizers( supertbl );
superclasses:= SizesConjugacyClasses( supertbl );
orders:= OrdersClassRepresentatives( supertbl );
# Is `tbl' is an element centralizer in a bigger table?
if 1 < Length( cen ) then
for i in [ 2 .. Length( cen ) ] do
if centralizers[ fus[ cen[i] ] ] = Size( tbl ) and
not orders[ fus[ cen[i] ] ]
= Sum( superclasses{ ClassPositionsOfNormalClosure(
supertbl, [ fus[ cen[i] ] ] ) } ) and
not orders[ fus[ cen[i] ] ] = 2 then
Add( tocheck, Concatenation( Identifier( supertbl ), "C",
ClassNames( supertbl )[ fus[ cen[i] ] ] ) );
fi;
od;
fi;
# Is `tbl' is an element normalizer in a bigger table?
if 1 < Length( nor ) then
orbits:= List( [ 1 .. NrConjugacyClasses( supertbl ) ],
i -> Length( ClassOrbit( supertbl, i ) ) );
for i in [ 2 .. Length( nor ) ] do
j:= fus[ nor[i] ];
if centralizers[j] * Phi( orders[j] ) / orbits[j]
= Size( tbl ) and
not orders[ fus[ nor[i] ] ]
= Sum( superclasses{ ClassPositionsOfNormalClosure(
supertbl, [ fus[ nor[i] ] ] ) } ) then
if IsPrimePowerInt( orders[j] ) and
Gcd( orders[j], Size( supertbl ) / orders[j] ) = 1 then
# Prefer the Sylow normalizer name.
Add( tocheck, Concatenation( Identifier( supertbl ), "N",
String( Factors( orders[j] )[1] ) ) );
else
# Choose the name of the normalizer of a cyclic subgroup.
Add( tocheck, Concatenation( Identifier( supertbl ), "N",
ClassNames( supertbl )[ fus[ nor[i] ] ] ) );
fi;
fi;
od;
fi;
fi;
fi;
od;
for name in Set( tocheck ) do
info:= LibInfoCharacterTable( name );
if info = fail then
CTblLib.PrintTestLog( "I", "CTblLib.Test.FindRelativeNames",
Identifier( tbl ),
[ [ "add the new name `", name, "'" ] ] );
elif info.firstName <> Identifier( tbl ) then
CTblLib.PrintTestLog( "E", "CTblLib.Test.FindRelativeNames",
Identifier( tbl ),
[ [ "`", name, "' should be a name for `",
Identifier( tbl ), "' not `", info.firstName, "'" ] ] );
result:= false;
fi;
od;
return result;
end;
#############################################################################
##
#F CTblLib.Test.Decompositions( <sub>, <fuslist>, <tbl> )
##
## Let <sub> and <tbl> be ordinary character tables, and <fuslist> a list of
## possible class fusions from <sub> to <tbl>.
##
## `CTblLibTestDecompositions' returns the set of all those entries in
## <fuslist> such that for all available $p$-modular Brauer tables of <sub>
## and <tbl>, the $p$-modular Brauer characters of <tbl> decompose into
## $p$-modular Brauer characters of <sub>.
##
CTblLib.Test.Decompositions:= function( sub, fuslist, tbl )
local bad, p, modtbl, modsub, modfuslist, modfus;
if IsEmpty( fuslist ) then
return [];
fi;
bad:= [];
for p in PrimeDivisors( Size( tbl ) ) do
modtbl:= tbl mod p;
if modtbl <> fail then
modsub:= sub mod p;
if modsub <> fail then
modfuslist:= List( fuslist, fus ->
CompositionMaps( InverseMap( GetFusionMap( modtbl, tbl ) ),
CompositionMaps( fus,
GetFusionMap( modsub, sub ) ) ) );
for modfus in Set( modfuslist ) do
if fail in Decomposition( Irr( modsub ),
List( Irr( modtbl ), chi -> chi{ modfus } ),
"nonnegative" ) then
UniteSet( bad,
fuslist{ Filtered( [ 1 .. Length( fuslist ) ],
i -> modfuslist[i] = modfus ) } );
fi;
od;
fi;
fi;
od;
return Difference( fuslist, bad );
end;
#T Jon Thackray says: LinearIndependentColumns runs forever in
#T some computation with Ly ...
#############################################################################
##
#V CTblLib.IgnoreFactorFusionsCompatibility
#F CTblLib.PermutationInducedOnFactor( <factfus>, <perm> )
#F CTblLib.Test.CompatibleFactorFusions( <tbl> )
##
## <#GAPDoc Label="test:CTblLib.Test.CompatibleFactorFusions">
## <Mark><C>CTblLib.Test.CompatibleFactorFusions( </C><M>tbl</M><C> )</C></Mark>
## <Item>
## checks whether triangles and quadrangles of factor fusions from
## <M>tbl</M> to other library tables commute
## (where the entries in the list
## <C>CTblLib.IgnoreFactorFusionsCompatibility</C> are excluded from the
## tests),
## and whether the factor fusions commute with the actions of
## corresponding outer automorphisms.
## </Item>
## <#/GAPDoc>
##
CTblLib.IgnoreFactorFusionsCompatibility:= [
[ "2x2^3:L3(2)x2", "2x2^3:L3(2)", "C2" ],
[ "2.A5xA5", "2.A5", "A5" ],
[ "2.A5xA5", "A5xA5", "A5" ],
[ "2.A5xA5", Set( [ "A5xA5", "2.A5" ] ), "A5" ],
];
CTblLib.PermutationInducedOnFactor:= function( factfus, perm )
local ind, i, pre, img;
ind:= [];
for i in [ 1 .. Length( factfus ) ] do
pre:= factfus[i];
img:= factfus[ i^perm ];
if IsBound( ind[ pre ] ) then
if ind[ pre ] <> img then
return fail;
fi;
else
ind[ pre ]:= img;
fi;
od;
return PermList( ind );
end;
CTblLib.Test.CompatibleFactorFusions:= function( tbl )
local result, tbls, ids, incid, t, factfus, facttbl, comp, triple1,
triple2, i, j, auts, fact, ker, triple, factauts, ind, kerimg,
supfact, facttriple;
result:= true;
# Collect factor fusions from `tbl' to other tables,
# and from these tables to other tables.
tbls:= [ tbl ];
ids:= [ Identifier( tbl ) ];
incid:= [];
for t in tbls do
for factfus in Filtered( ComputedClassFusions( t ),
r -> 1 < Length( ClassPositionsOfKernel( r.map ) ) ) do
if not factfus.name in ids then
facttbl:= CharacterTable( factfus.name );
if facttbl <> fail then
Add( ids, factfus.name );
Add( tbls, facttbl );
fi;
fi;
Add( incid, [ Identifier( t ), factfus.name, factfus.map ] );
od;
od;
# Check triangles and quadrangles in the directed graph.
comp:= [];
for triple1 in incid do
for triple2 in incid do
if triple1[2] = triple2[1] then
# for t1 -> t2 and t2 -> t3, store [ t1, t3, map, t2 ]
Add( comp, [ triple1[1], triple2[2],
CompositionMaps( triple2[3], triple1[3] ), triple1[2] ] );
fi;
od;
od;
for i in [ 1 .. Length( comp ) ] do
triple1:= comp[i];
# triangles
for triple2 in incid do
if triple1[1] = triple2[1] and triple1[2] = triple2[2] and
triple1[3] <> triple2[3] and
not [ triple1[1], triple1[4], triple1[2] ] in
CTblLib.IgnoreFactorFusionsCompatibility then
CTblLib.PrintTestLog( "E", "CTblLib.Test.CompatibleFactorFusions",
Identifier( tbl ),
[ [ "inconsistent triangle: ", triple1[1], " ->> ", triple1[4],
" ->> ", triple1[2] ] ] );
result:= false;
fi;
od;
# quadrangles
for j in [ 1 .. i-1 ] do
triple2:= comp[j];
if triple1[1] = triple2[1] and triple1[2] = triple2[2] and
triple1[3] <> triple2[3] and
not [ triple1[1], Set( [ triple1[4], triple2[4] ] ), triple1[2] ] in
CTblLib.IgnoreFactorFusionsCompatibility then
CTblLib.PrintTestLog( "E", "CTblLib.Test.CompatibleFactorFusions",
Identifier( tbl ),
[ [ "inconsistent quadrangle: ",
triple1[1], " ->> (", triple1[4], " or ",
triple2[4], ") ->> ", triple1[2] ] ] );
result:= false;
fi;
od;
od;
# Check compatibility of factor fusions with outer automorphims.
auts:= CTblLib.PermutationsInducedByOuterAutomorphisms( tbl );
for factfus in Filtered( ComputedClassFusions( t ),
r -> 1 < Length( ClassPositionsOfKernel( r.map ) ) ) do
fact:= CharacterTable( factfus.name );
if fact <> fail then
ker:= ClassPositionsOfKernel( factfus.map );
for triple in auts do
if Set( ker ) = Set( OnTuples( ker, triple[1] ) ) then
# The outer automorphism acts on the factor group.
factauts:= CTblLib.PermutationsInducedByOuterAutomorphisms( fact );
ind:= CTblLib.PermutationInducedOnFactor( factfus.map, triple[1] );
if ind <> fail then
kerimg:= Set( triple[3]{ ker } );
supfact:= First( ComputedClassFusions( triple[2] ),
r -> ClassPositionsOfKernel( r.map ) = kerimg );
if supfact <> fail then
facttriple:= First( factauts,
x -> Identifier( x[2] ) = supfact.name );
if facttriple <> fail then
if ind <> facttriple[1] then
# The permutations do not fit together.
result:= false;
CTblLib.PrintTestLog( "E",
"CTblLib.Test.CompatibleFactorFusions",
Identifier( tbl ),
[ [ "autom. induced by ", Identifier( triple[2] ),
" incompatible with factor ", factfus.name ] ] );
fi;
fi;
fi;
fi;
fi;
od;
fi;
od;
return result;
end;
#############################################################################
##
#V CTblLib.HardFusions
##
## `CTblLib.HardFusions' is a list of pairs `[ <subname>, <tblname> ]'
## where <subname> and <tblname> are `Identifier' values of character
## tables such that `CTblLib.Test.SubgroupFusion' shall omit the compatibility
## check for the class fusion between these tables.
##
CTblLib.HardFusions:= [];
Add( CTblLib.HardFusions, [ "Co1N3", "Co1" ] );
Add( CTblLib.HardFusions, [ "Co1N2", "Co1" ] );
Add( CTblLib.HardFusions, [ "Co2N2", "Co2" ] );
# computed using the groups, fusion status unclear
Add( CTblLib.HardFusions, [ "2^10.2^3.2^3.S3", "Co2" ] );
# computed using the groups, fusion status unclear
Add( CTblLib.HardFusions, [ "2^10.2^3.2^3.S3", "2^10:m22:2" ] );
# computed using the groups, fusion status unclear
Add( CTblLib.HardFusions, [ "Fi22N3", "Fi22" ] );
# computed via factorization through 3^(1+6):2^(3+4):3^2:2
Add( CTblLib.HardFusions, [ "M24N2", "M24" ] );
# computed from the groups, time 227180 msec, incl. tables comput.
# (25-11-2002)
Add( CTblLib.HardFusions, [ "M24N2", "He" ] );
# computed from the groups, time 12451360 msec, incl. tables comput.
# (26-11-2002)
Add( CTblLib.HardFusions, [ "O8+(3)M14", "O8+(3)" ] );
# 1 orbit, 648 sol., time 154539590 msec on regulus (22-11-2002)
Add( CTblLib.HardFusions, [ "L3(3)", "B" ] );
# 1 orbit, 36 sol., harmless if one forbids decomposition
Add( CTblLib.HardFusions, [ "2^2xF4(2)", "2.2E6(2).2" ] );
Add( CTblLib.HardFusions, [ "(3^2:D8xU4(3).2^2).2", "B" ] );
Add( CTblLib.HardFusions, [ "[2^35].(S5xL3(2))", "B" ] );
# unique, takes 34621084 msec (2009-04-20)
Add( CTblLib.HardFusions, [ "2^2x3xS3xU4(2)", "(2^2x3).U6(2)" ] );
# takes a long time ... (February 2010)
Add( CTblLib.HardFusions, [ "2xM11", "2.B" ] );
# takes a long time, needs a lot of space ... (February 2010)
Add( CTblLib.HardFusions, [ "(2^2x3).2E6(2)", "(2^2x3).2E6(2).2" ] );
# not ready after 48 h ... (February 2010)
Add( CTblLib.HardFusions, [ "2xCo1N3", "2.Co1" ] );
# use the fusion Co1N3 -> Co1, and set "decompose" to `false'
# (with the latter, the computations are very quick;
# the decomposition test takes at least hours)
Add( CTblLib.HardFusions, [ "3.2E6(2)M8", "3.2E6(2)" ] );
Add( CTblLib.HardFusions, [ "3.2E6(2)M9", "3.2E6(2)" ] );
# the test says ``text should not mention that fusion is relative''
# in these two cases; but the fusions ARE relative,
# just the reference table of 3.2E6(2).3 is missing
# (so the pairs can be removed from `CTblLib.HardFusions' as soon as
# this table will be available)
#############################################################################
##
#F CTblLib.InitFusionsStatistics( <statfile> )
#F CTblLib.AmendFusionsStatistics( <entry> )
#F CTblLib.FinalizeFusionsStatistics()
##
## Create a file with information about all subgroup fusions stored in the
## GAP Character Table Library.
## For the fusion from the table with identifier <subtbl> into that with
## identifier <tbl>, a list entry of the following form is printed.
##
## `[<subtbl>,<tbl>,<nrfus>,<nrorbs>,<nrcomp>,<nrcorbs>,<normtime>],'
##
## Here <nrfus> is the number of fusions,
## <nrorbs> is the number of orbits on the maps under table automorphisms,
## <nrcomp> is the number of those fusions that are compatible with the
## Brauer tables available for <subtbl> and <tbl>,
## <nrcorbs> is the number of orbits on the compatible maps under table
## automorphisms, and
## <normtime> is the time needed to compute the fusions,
## divided by ... (so this value is expected to be more or less independent
## of the machine used).
##
## Thus the fusion is unique if <nrfus> is $1$,
## it is unique up to table automorphisms if <nrorbs> is $1$;
## otherwise the fusion is ambiguous.
## If <nrcomp> is smaller than <nrfus> then the Brauer tables impose
## extra conditions on the fusions, and if <nrcorbs> is smaller than
## <nrorbs> then the Brauer tables reduce the ambiguity.
##
CTblLib.InitFusionsStatistics:= function( statfile )
local time, l, i, j;
# Measure the time for some typical computations.
time:= Runtime();
l:= [];
for i in [ 1 .. 1000 ] do
for j in [ 1 .. 1000 ] do
l[j]:= j;
od;
od;
time:= ( Runtime() - time );
# Create the file.
PrintTo( statfile, "[\n" );
LIBTABLE.FusionsStatistics:= rec( statfile:= statfile, time:= time );
end;
CTblLib.AmendFusionsStatistics:= function( entry )
if IsBound( LIBTABLE.FusionsStatistics ) then
AppendTo( LIBTABLE.FusionsStatistics.statfile, Concatenation( "[\"",
Identifier( entry[1] ),
"\",\"",
Identifier( entry[2] ),
"\",",
String( Length( entry[3] ) ),
",",
String( Length( entry[4] ) ),
",",
String( Length( entry[5] ) ),
",",
String( Length( entry[6] ) ),
",",
String( Int( entry[7] / LIBTABLE.FusionsStatistics.time ) ),
"],\n" ) );
fi;
end;
CTblLib.FinalizeFusionsStatistics:= function()
AppendTo( LIBTABLE.FusionsStatistics.statfile, "\n];\n" );
end;
#############################################################################
##
#V CTblLib.ExcludedFromFusionCompatibility
##
## a list of those quadruples [ <sub>, <tbl>, <subfact>, <tblfact> ]
## or [ <sub>, <tbl>, <subext>, <tblext> ]
## for which no commutative diagram of stored fusions is guaranteed
##
CTblLib.ExcludedFromFusionCompatibility:= [
# quadruples involving two subgroup fusions and two factor fusions
[ "2.O7(3)M5", "2.O7(3)", "G2(3)", "O7(3)" ],
[ "3.O7(3)M8", "3.O7(3)", "S6(2)", "O7(3)" ],
[ "3.O7(3)M11", "3.O7(3)", "A9.2", "O7(3)" ],
[ "2.S6(2)", "2.O8+(2)", "S6(2)", "O8+(2)" ],
[ "2^(1+6)_+.A8", "2.O8+(2)", "2^6:A8", "O8+(2)" ],
[ "2.A9", "2.O8+(2)", "A9", "O8+(2)" ],
[ "2.U4(3).2_2", "2.U6(2)", "U4(3).2_2", "U6(2)" ],
# since 2.U4(3).2_2 = 2.U6(2)M5, U4(3).2_2 = U6(2)M4
[ "6_1.U4(3).2_2", "6.U6(2)", "U4(3).2_2", "U6(2)" ],
# since 6_1.U4(3).2_2 = 6.U6(2)M5, U4(3).2_2 = U6(2)M4
[ "6_1.U4(3).2_2", "6.U6(2)", "3_1.U4(3).2_2", "3.U6(2)" ],
# since 6_1.U4(3).2_2 = 6.U6(2)M5, 3_1.U4(3).2_2 = U6(2)M4
[ "2.M22", "2.U6(2)", "M22", "U6(2)" ],
# since 2.M22 = 2.U6(2)M12, M22 = U6(2)M11
[ "6.M22", "6.U6(2)", "M22", "U6(2)" ],
# since 6.M22 = 6.U6(2)M12, M22 = U6(2)M11
[ "6.M22", "6.U6(2)", "3.M22", "3.U6(2)" ],
# since 6.M22 = 6.U6(2)M12, 3.M22 = 3.U6(2)M11
[ "2.Fi22", "2.2E6(2)", "Fi22", "2E6(2)" ],
# since 2.Fi22 = 2.2E6(2)M8, Fi22 = 2E6(2)M7
[ "6.Fi22", "6.2E6(2)", "Fi22", "2E6(2)" ],
# since 6.Fi22 = 6.2E6(2)M8, Fi22 = 2E6(2)M7
[ "6.Fi22", "6.2E6(2)", "3.Fi22", "3.2E6(2)" ],
# since 6.Fi22 = 6.2E6(2)M8, 3.Fi22 = 3.2E6(2)M7
[ "2.F4(2)", "2.2E6(2)", "F4(2)", "2E6(2)" ],
# since 2.F4(2) = 2.2E6(2)M4, "F4(2) = 2E6(2)M3
[ "6.M22", "6.U6(2)", "2.M22", "2.U6(2)" ],
# why?
[ "2xM22", "2.U6(2)", "M22", "U6(2)" ],
#T why?
# [ "2^2.U6(2)M12","2^2.U6(2)", "2.M22", "2.U6(2)" ],
#T no! excluding 6.M22 -> 3.M22 and 2.M22 -> M22 should suffice!
# quadruples involving four subgroup fusions
[ "L2(11)", "M12", "L2(11).2", "M12.2" ],
# since L2(11).2 occurs as both the novelty M12.2M2 (cont. 2B elements)
# and as the extension M12.2M3 (cont. 2A elements) in M12.2;
# in AtlasRep and MFER, the novelty occurs first
];
#############################################################################
##
#F CTblLib.AddIncompatibleFusionsOfFactors( <sub>, <tbl>, <fus>, <incompat> )
##
CTblLib.AddIncompatibleFusionsOfFactors:= function( sub, tbl, fus, incompat )
local tblfactfus, record, ker, kersize, pair, subfact, tblfact,
storedfus, ffus;
# Compute a list of pairs [ <kernelsize>, <factfusrec> ] for `tbl'.
tblfactfus:= Filtered( List( ComputedClassFusions( tbl ),
r -> [ Sum( SizesConjugacyClasses( tbl ){ ClassPositionsOfKernel(
r.map ) } ), r ] ),
pair -> pair[1] <> 1 );
# Collect commutative diagrams of factor fusions and subgroup fusions.
# We consider only those diagrams where a subgroup fusion between the
# factor tables is already stored.
for record in ComputedClassFusions( sub ) do
ker:= ClassPositionsOfKernel( record.map );
if ker <> [ 1 ] then
kersize:= Sum( SizesConjugacyClasses( sub ){ ker } );
for pair in Filtered( tblfactfus, x -> x[1] = kersize ) do
subfact:= CharacterTable( record.name );
tblfact:= CharacterTable( pair[2].name );
if subfact <> fail and tblfact <> fail then
if List( [ sub, tbl, subfact, tblfact ], Identifier )
in CTblLib.ExcludedFromFusionCompatibility then
incompat.someexcluded:= true;
else
storedfus:= GetFusionMap( subfact, tblfact );
if storedfus <> fail and
Set( fus{ ker } ) = ClassPositionsOfKernel( pair[2].map ) then
# Compute the induced fusion between the factors and compare.
ffus:= CompositionMaps( pair[2].map,
CompositionMaps( fus, InverseMap( record.map ) ) );
if ffus <> storedfus then
Add( incompat.badfusions, [ sub, tbl, subfact, tblfact ] );
fi;
CTblLib.AddIncompatibleFusionsOfFactors( subfact, tblfact,
storedfus, incompat );
fi;
fi;
fi;
od;
fi;
od;
end;
#############################################################################
##
#F CTblLib.Test.SubgroupFusionOfMaximalSubgroup( <sub>, <tbl> )
##
## returns
## `false' if was not applicable,
## `true' if no further treatment of the fusion is needed,
## because it is interpreted relative to another fusion
## (also if inconsistent!).
##
CTblLib.MaxesNames:= function( tbl )
local result, tblname, pos, name, index;
if HasMaxes( tbl ) then
return Maxes( tbl );
fi;
result:= [];
tblname:= Concatenation( LowercaseString( Identifier( tbl ) ), "m" );
pos:= PositionSorted( LIBLIST.allnames, tblname );
while pos <= Length( LIBLIST.allnames ) do
name:= LIBLIST.allnames[ pos ];
if Length( name ) <= Length( tblname ) then
break;
fi;
index:= name{ [ Length( tblname ) + 1 .. Length( name ) ] };
if ForAll( index, IsDigitChar ) then
Add( result,
[ Int( index ), LibInfoCharacterTable( name ).firstName ] );
fi;
pos:= pos + 1;
od;
Sort( result );
return List( result, x -> x[2] );
end;
#T this function strikes for example for 2^2.L3(4).2_1 -> 2^2.L3(4).2^2
#T (w.r.t. Brauer tables for p = 5 or 7)
#T but why??
CTblLib.PermutationsInducedByOuterAutomorphisms:= function( tbl )
local result, r, suptbl, fus, order, img, inv, lists, stab, cand, p,
modtbl, modfus, range;
result:= [];
for r in ComputedClassFusions( tbl ) do
suptbl:= CharacterTable( r.name );
if suptbl <> fail then
fus:= r.map;
order:= Size( suptbl ) / Size( tbl );
img:= Set( fus );
if img in ClassPositionsOfNormalSubgroups( suptbl ) and
Sum( SizesConjugacyClasses( suptbl ){ img } ) = Size( tbl ) and
IsCyclic( suptbl / img ) then
# `tbl' is a normal subgroup of index `order' in `suptbl'.
inv:= InverseMap( fus );
lists:= Filtered( inv, IsList );
if not IsEmpty( lists ) then
stab:= Stabilizer( AutomorphismsOfTable( tbl ),
Filtered( inv, IsInt ), OnTuples );
stab:= Stabilizer( stab, lists, OnTuplesSets );
cand:= Filtered( stab,
x -> Order( x ) = order and
ForAll( lists, l -> l <> OnTuples( l, x ) ) );
cand:= Set( List( cand, SmallestGeneratorPerm ) );
# Check whether the restriction to p-regular classes defines
# a table automorphism.
for p in PrimeDivisors( Size( tbl ) ) do
modtbl:= tbl mod p;
if modtbl <> fail then
modfus:= GetFusionMap( modtbl, tbl );
range:= [ 1 .. NrConjugacyClasses( tbl ) ];
cand:= Filtered( cand, pi -> PermList( CompositionMaps(
InverseMap( modfus ),
CompositionMaps( OnTuples( range, pi ), modfus ) ) ) in
AutomorphismsOfTable( modtbl ) );
fi;
od;
if Length( cand ) = 0 then
CTblLib.PrintTestLog( "E",
"CTblLib.PermutationsInducedByOuterAutomorphisms",
Identifier( tbl ),
[ [ "no table autom. induced by fusion to ",
Identifier( suptbl ), "?" ] ] );
elif Length( cand ) = 1 then
Add( result, [ cand[1], suptbl, fus ] );
elif Length( cand ) = 2 and Identifier( tbl ) = "U3(8)"
and Identifier( suptbl ) = "U3(8).3_3" then
# There are two groups U3(8).3_3 and U3(8).3_3',
# to which the two candidates belong.
# Note that the automorphisms 3_3 and 3_3' arise as 3_1 * 3_2
# and 3_1 / 3_2, respectively; we choose the smaller of the two
# possibilities as 3_3.
Add( result, [ (6,7,8)(11,12,13)(14,15,16)(17,19,21)(18,20,22)*
(23,25,27)(24,26,28), suptbl, fus ] );
else
CTblLib.PrintTestLog( "I",
"CTblLib.PermutationsInducedByOuterAutomorphisms",
Identifier( tbl ),
[ [ "cannot identify table autom. induced by action of ",
Identifier( suptbl ) ] ] );
#T maybe we need just the orbits not the action itself
#T (if the classes in question are not hit, for example ...)
fi;
fi;
fi;
fi;
od;
return result;
end;
CTblLib.Test.SubgroupFusionOfMaximalSubgroup:= function( sub, tbl )
#T this function did not detect that the stored fusion J3M3 -> J3
#T was not the one from J3M2 permuted by the group aut.;
#T the stored fusion did not admit decomposition of the restriction
#T of the 19-modular 110
local maxesnames, pos, fusid, mx, tr, choice, fusions, auts, triple,
cand, suptbl,
fus, invariant, extcand, extname, ext, extfus, orb, orbreps,
i, map, pos2, done, rep, fusrec, relative, source, text, incompat,
incompatnames;
maxesnames:= CTblLib.MaxesNames( tbl );
pos:= PositionsProperty( maxesnames, x -> Identifier( sub ) = x );
if IsEmpty( pos ) then
# The subgroup is not known to be maximal,
# so this function is not useful for this fusion.
return false;
fi;
fusid:= Concatenation( Identifier( sub ), " -> ", Identifier( tbl ) );
# Collect maxes with tables equivalent to that of `sub'.
mx:= List( maxesnames, CharacterTable );
tr:= List( [ 1 .. Length( mx ) ],
i -> TransformingPermutationsCharacterTables( sub, mx[i] ) );
choice:= PositionsProperty( tr, x -> x <> fail );
fusions:= List( choice, i -> GetFusionMap( mx[i], tbl ) );
tr:= tr{ choice };
if fail in fusions then
CTblLib.PrintTestLog( "E", "CTblLib.Test.SubgroupFusionOfMaximalSubgroup",
fusid,
[ [ "missing fusion from some maxes of ", Identifier( tbl ) ] ] );
return false;
fi;
# Compute the action of outer automorphisms of `tbl'.
auts:= [];
for triple in CTblLib.PermutationsInducedByOuterAutomorphisms( tbl ) do
cand:= triple[1];
suptbl:= triple[2];
fus:= GetFusionMap( sub, tbl );
Add( auts, cand );
invariant:= RepresentativeAction( AutomorphismsOfTable( sub ),
fus, OnTuples( fus, cand ), Permuted ) <> fail;
extcand:= Intersection( CTblLib.MaxesNames( suptbl ),
List( ComputedClassFusions( sub ), x -> x.name ) );
RemoveSet( extcand, Identifier( tbl ) );
for extname in extcand do
ext:= CharacterTable( extname );
if ext <> fail and
Size( ext ) / Size( sub ) = Size( suptbl ) / Size( tbl ) then
extfus:= GetFusionMap( ext, suptbl );
if extfus <> fail then
if CompositionMaps( extfus, GetFusionMap( sub, ext ) ) =
CompositionMaps( triple[3], fus ) then
if not invariant then
# The subgroup behaves as if it would extend to the
# autom. extension but it cannot extend.
CTblLib.PrintTestLog( "E",
"CTblLib.Test.SubgroupFusionOfMaximalSubgroup", fusid,
"strange/invalid commutative diagram",
[ Identifier( sub ), " -> ", Identifier( suptbl ) ],
[ "via ", Identifier( ext ), " and ", Identifier( tbl ) ] );
fi;
elif invariant
and not List( [ sub, tbl, ext, suptbl ], Identifier )
in CTblLib.ExcludedFromFusionCompatibility then
# The diagram shold commute.
CTblLib.PrintTestLog( "E",
"CTblLib.Test.SubgroupFusionOfMaximalSubgroup", fusid,
"diagram should commute:",
[ Identifier( sub ), " -> ", Identifier( suptbl ) ],
[ "via ", Identifier( ext ), " and ", Identifier( tbl ) ] );
fi;
fi;
fi;
od;
od;
if IsEmpty( auts ) then
# We cannot derive conditions from outer automorphisms.
# so this function is not useful for this fusion.
return false;
fi;
# Compute the orbit of the given fusion under `auts',
# and representatives under table automorphisms of `sub'.
fusions:= List( [ 1 .. Length( tr ) ],
i -> Permuted( fusions[i], tr[i].columns ) );
fus:= fusions[ Position( choice, pos[1] ) ];
orb:= Orbit( Group( auts ), fus, OnTuples );
orbreps:= [ fus ];
for i in [ 2 .. Length( orb ) ] do
map:= orb[i];
pos2:= PositionProperty( orbreps, rep -> RepresentativeAction(
AutomorphismsOfTable( sub ), map, rep, Permuted ) <> fail );
if pos2 = fail then
Add( orbreps, map );
fi;
od;
# Each of the essentially different fusions must occur
# among the maxes fusions (with the same multiplicity).
done:= [];
for rep in orbreps do
pos2:= PositionsProperty( fusions,
map -> RepresentativeAction( AutomorphismsOfTable( sub ),
map, rep, Permuted ) <> fail );
UniteSet( done, choice{ pos2 } );
if Length( pos2 ) = 0 then
#T better check that always the same nonzero length ...
CTblLib.PrintTestLog( "E",
"CTblLib.Test.SubgroupFusionOfMaximalSubgroup", fusid,
"multiplicity of same fusion among maxes does not fit" );
fi;
od;
# The fusion of `sub' can be interpreted relative to another fusion.
fusrec:= First( ComputedClassFusions( sub ),
r -> r.name = Identifier( tbl ) );
relative:= false;
if IsBound( fusrec.text ) then
source:= fail;
text:= ReplacedString( fusrec.text, "\n", " " );
pos2:= PositionSublist( text, ", mapped under" );
if pos2 <> fail then
relative:= true;
text:= text{ [ 1 .. pos2 - 1 ] };
pos2:= PositionSublist( text, " from " );
if pos2 <> fail then
source:= CharacterTable( text{ [ pos2 + 6 .. Length( text ) ] } );
fi;
fi;
if source <> fail then
if Identifier( source ) <> maxesnames[ Minimum( done ) ] and
not ( relative and pos[1] = Minimum( done ) ) then
CTblLib.PrintTestLog( "E",
"CTblLib.Test.SubgroupFusionOfMaximalSubgroup", fusid,
[ [ "text should mention that fusion is relative to ",
Concatenation( maxesnames[ Minimum( done ) ],
" not ", Identifier( source ) ) ] ] );
fi;
# Check that the fusions lie in the same orbit.
if TransformingPermutationsCharacterTables( source, sub ) = fail then
CTblLib.PrintTestLog( "E",
"CTblLib.Test.SubgroupFusionOfMaximalSubgroup", fusid,
"wrong source stored for relative fusion" );
fi;
elif relative then
CTblLib.PrintTestLog( "E",
"CTblLib.Test.SubgroupFusionOfMaximalSubgroup", fusid,
"missing source information in relative fusion" );
fi;
fi;
if pos[1] = Minimum( done ) then
# `sub' is the first table among the maxes in its orbit,
# it should not be a relative fusion.
if relative then
CTblLib.PrintTestLog( "E",
"CTblLib.Test.SubgroupFusionOfMaximalSubgroup", fusid,
"text should not mention that fusion is relative" );
fi;
# Return `false' in order to check the fusion.
return false;
fi;
# `sub' is not the first table among the maxes in its orbit,
# so the fusion should be a relative fusion.
if not relative then
CTblLib.PrintTestLog( "E",
"CTblLib.Test.SubgroupFusionOfMaximalSubgroup", fusid,
[ [ "text should mention that fusion is relative to ",
maxesnames[ Minimum( done ) ] ] ] );
fi;
# Check that the stored fusion is compatible with factors.
incompat:= rec( badfusions:= [], someexcluded:= false );
CTblLib.AddIncompatibleFusionsOfFactors( sub, tbl, fus, incompat );
if not IsEmpty( incompat.badfusions ) then
incompatnames:= List( incompat.badfusions,
entry -> Concatenation( [ " ", Identifier( entry[3] ),
" -> ", Identifier( entry[4] ),
" (w.r.t. ", Identifier( entry[1] ),
" -> ", Identifier( entry[2] ), ")" ] ) );
CTblLib.PrintTestLog( "E",
"CTblLib.Test.SubgroupFusionOfMaximalSubgroup", fusid,
Concatenation(
[ "no fusion (compatible with Brauer tables if applicable and)",
" consistent with" ], incompatnames ) );
fi;
return true;
end;
#############################################################################
##
#F CTblLib.Test.SubgroupFusion( <sub>, <tbl> )
##
## If no class fusion from <sub> to <tbl> is possible or if the possible
## class fusions contradict the stored fusion then `false' is returned.
## If a fusion is stored and is compatible with the possible fusions,
## and the fusion is not unique up to table automorphisms and if the stored
## fusion has no `text' component then `fail' is returned.
## Otherwise the fusion record is returned.
##
## If the pair of identifiers of <sub> and <tbl> occurs in the global list
## `CTblLib.HardFusions' amd if a fusion is stored then the fusion record is
## returned without tests, and a message is printed.
##
CTblLib.Test.SubgroupFusion:= function( sub, tbl )
local fusrec, fusid, spec, tom, pos, perms, pi, storedfus, time, fus,
filt, fusreps, filtreps, fus_c, reducedby, someexcluded, map,
incompat, entry, pair, pairstring, fusreps_c, filt_c, filtreps_c,
comp, libinfo, changedtext, reducedbyBrauer, reducedbyfactors,
result;
fusrec:= First( ComputedClassFusions( sub ),
r -> r.name = Identifier( tbl ) );
fusid:= Concatenation( Identifier( sub ), " -> ", Identifier( tbl ) );
# Verify a specification of the kind 'tom:<n>'.
if fusrec <> fail and IsBound( fusrec.specification ) then
spec:= fusrec.specification;
if IsString( spec ) and 4 < Length( spec )
and spec{ [ 1 .. 4 ] } = "tom:" then
tom:= TableOfMarks( tbl );
pos:= Int( spec{ [ 5 .. Length( spec ) ] } );
if tom = fail then
CTblLib.PrintTestLog( "E", "CTblLib.Test.SubgroupFusion", fusid,
[ [ "specification ", spec, " but no table of marks?" ] ] );
elif not IsInt( pos ) then
CTblLib.PrintTestLog( "E", "CTblLib.Test.SubgroupFusion", fusid,
[ [ "strange specification ", spec ] ] );
elif not HasFusionToTom( tbl ) then
CTblLib.PrintTestLog( "E", "CTblLib.Test.SubgroupFusion", fusid,
"no fusion to tom" );
else
perms:= PermCharsTom( tbl, tom );
if Length( perms ) < pos then
CTblLib.PrintTestLog( "E", "CTblLib.Test.SubgroupFusion", fusid,
[ [ "specification ", spec, ": too few trans. perm. char." ] ] );
else
pi:= InducedClassFunctionsByFusionMap( sub, tbl,
[ TrivialCharacter( sub ) ], fusrec.map )[1];
if not pi in perms then
CTblLib.PrintTestLog( "E", "CTblLib.Test.SubgroupFusion", fusid,
"stored fusion does not fit to any trans. perm. char." );
elif pi <> perms[ pos ] then
CTblLib.PrintTestLog( "E", "CTblLib.Test.SubgroupFusion", fusid,
[ [ "specification ", spec, " is wrong, store `tom:",
Position( perms, pi ), "' instead" ] ] );
#T might be another position ...
fi;
fi;
fi;
fi;
fi;
#T propose a specification tom:<n> when the image knows its table of marks!
# Shall the test be omitted?
if [ Identifier( sub ), Identifier( tbl ) ] in CTblLib.HardFusions then
if fusrec = fail then
CTblLib.PrintTestLog( "E", "CTblLib.Test.SubgroupFusion", fusid,
"omitting fusion check (no map stored)" );
else
# At least test the existing map for consistency.
fus:= PossibleClassFusions( sub, tbl,
rec( fusionmap:= fusrec.map ) );
if IsEmpty( fus ) then
CTblLib.PrintTestLog( "E", "CTblLib.Test.SubgroupFusion", fusid,
"stored fusion is wrong" );
else
CTblLib.PrintTestLog( "E", "CTblLib.Test.SubgroupFusion", fusid,
"omitting fusion check" );
fi;
fi;
return fusrec;
fi;
# If the fusion must be interpreted relative to another one
# then do not test anything else.
if CTblLib.Test.SubgroupFusionOfMaximalSubgroup( sub, tbl ) then
return fusrec;
fi;
if fusrec = fail then
fusrec:= rec();
storedfus:= fail;
else
storedfus:= fusrec.map;
fi;
# Recompute the possible class fusions.
time:= Runtime();
fus:= PossibleClassFusions( sub, tbl );
time:= Runtime() - time;
fusreps:= RepresentativesFusions( sub, fus, tbl );
filt:= CTblLib.Test.Decompositions( sub, fus, tbl );
filtreps:= RepresentativesFusions( sub, filt, tbl );
# Amend the statistics if wanted.
CTblLib.AmendFusionsStatistics(
[ sub, tbl, fus, fusreps, filt, filtreps, time ] );
# We may have no fusion at all.
if IsEmpty( fus ) then
CTblLib.PrintTestLog( "E", "CTblLib.Test.SubgroupFusion", fusid,
"no fusion possible" );
return false;
elif IsEmpty( filt ) then
CTblLib.PrintTestLog( "E", "CTblLib.Test.SubgroupFusion", fusid,
"no fusion compatible with Brauer tables" );
return false;
fi;
# Consider factor fusions.
# We use the information about factors for preferably choosing a map
# from the set of compatible fusions,
# and for printing warnings in case of inconsistencies,
# but stored incompatible fusions are not automatically replaced.
fus_c:= [];
reducedby:= [ [], [] ];
someexcluded:= false;
for map in fus do
incompat:= rec( badfusions:= [], someexcluded:= false );
CTblLib.AddIncompatibleFusionsOfFactors( sub, tbl, map, incompat );
someexcluded:= someexcluded or incompat.someexcluded;
if IsEmpty( incompat.badfusions ) then
Add( fus_c, map );
else
for entry in incompat.badfusions do
pairstring:= Concatenation( [ " ", Identifier( entry[3] ),
" -> ", Identifier( entry[4] ),
" (w.r.t. ", Identifier( entry[1] ),
" -> ", Identifier( entry[2] ), ")" ] );
if not pairstring in reducedby[1] then
Add( reducedby[1], pairstring );
Add( reducedby[2], entry{ [ 3, 4 ] } );
fi;
od;
SortParallel( reducedby[1], reducedby[2] );
fi;
od;
fusreps_c:= RepresentativesFusions( sub, fus_c, tbl );
filt_c:= Filtered( fus_c, x -> x in filt );
filtreps_c:= RepresentativesFusions( sub, filt_c, tbl );
# We may have no consistent fusion.
if IsEmpty( fus_c ) then
CTblLib.PrintTestLog( "E", "CTblLib.Test.SubgroupFusion", fusid,
Concatenation( [ "no fusion consistent with" ], reducedby[1] ) );
# Propose changing the fusion between factors.
for pair in reducedby[2] do
if GetFusionMap( sub, pair[1] ) <> fail and
GetFusionMap( tbl, pair[2] ) <> fail then
comp:= Set( List( filt, map -> CompositionMaps(
GetFusionMap( tbl, pair[2] ),
CompositionMaps( map, InverseMap( GetFusionMap(
sub, pair[1] ) ) ) ) ) );
# Consider only those fusions that map the kernel compatibly.
comp:= Filtered( comp, x -> ForAll( x, IsInt ) );
libinfo:= LibInfoCharacterTable( pair[1] );
if libinfo = fail then
libinfo:= "no library file";
else
libinfo:= libinfo.fileName;
fi;
if Length( comp ) = 1 then
CTblLib.PrintTestLog( "I", "CTblLib.Test.SubgroupFusion", fusid,
"replace fusion of factors by the following unique fusion",
Concatenation( "(in ", libinfo, ")" ) );
Print( LibraryFusion( pair[1], rec( name:= pair[2], map:= comp[1],
text:= Concatenation(
"unique map that is compatible with ",
Identifier( sub ), " -> ", Identifier( tbl ) ) ) ) );
elif Length( RepresentativesFusions( pair[1], comp, pair[2] ) )
= 1 then
CTblLib.PrintTestLog( "I", "CTblLib.Test.SubgroupFusion", fusid,
"perhaps replace fusion of factors by the following (u.t.a.)",
Concatenation( "(in ", libinfo, ")" ) );
Print( LibraryFusion( pair[1], rec( name:= pair[2], map:= comp[1],
text:= Concatenation(
"compatible with ",
Identifier( sub ), " -> ", Identifier( tbl ) ) ) ) );
else
CTblLib.PrintTestLog( "I", "CTblLib.Test.SubgroupFusion", fusid,
"perhaps replace fusion of factors by the following (ambig.)",
Concatenation( "(in ", libinfo, ")" ) );
Print( LibraryFusion( pair[1], rec( name:= pair[2], map:= comp[1]
) ) );
fi;
fi;
od;
return false;
elif IsEmpty( filt_c ) then
CTblLib.PrintTestLog( "E", "CTblLib.Test.SubgroupFusion", fusid,
Concatenation( [ "no fusion compatible with Brauer tables ",
"and consistent with" ], reducedby[1] ) );
return false;
fi;
# Now we have consistent candidates.
# Check whether the stored text mentions (only) the reductions used.
# (The words ``factorization'' and ``factors through'' are allowed.)
changedtext:= false;
reducedbyBrauer:= IsBound( fusrec.text ) and
PositionSublist( fusrec.text, "Brauer" ) <> fail;
reducedbyfactors:= IsBound( fusrec.text ) and
PositionSublist( ReplacedString( ReplacedString( fusrec.text,
"factori", "" ), "factors through", "" ), "factor" ) <> fail;
if Length( filt ) < Length( fus ) then
# We have to choose a fusion that is compatible with Brauer tables,
# perhaps the Brauer tables resolve ambiguities.
if not reducedbyBrauer then
CTblLib.PrintTestLog( "E", "CTblLib.Test.SubgroupFusion", fusid,
"text should mention reduction by Brauer tables" );
changedtext:= true;
fi;
elif reducedbyBrauer then
CTblLib.PrintTestLog( "E", "CTblLib.Test.SubgroupFusion", fusid,
"text needs not mention reduction by Brauer tables" );
changedtext:= true;
fi;
if Length( filt_c ) < Length( filt ) then
# In addition to the compatibility with Brauer tables,
# we have to choose a fusion that is compatible with factor tables,
# perhaps the factor tables resolve ambiguities.
if not reducedbyfactors then
CTblLib.PrintTestLog( "E", "CTblLib.Test.SubgroupFusion", fusid,
"text should mention reduction by fusions of factors" );
changedtext:= true;
fi;
elif reducedbyfactors then
CTblLib.PrintTestLog( "E", "CTblLib.Test.SubgroupFusion", fusid,
"text needs not mention reduction by fusions of factors" );
changedtext:= true;
fi;
# Check whether the stored fusion is one of the candidates.
if storedfus <> fail then
if not storedfus in filt then
CTblLib.PrintTestLog( "E", "CTblLib.Test.SubgroupFusion", fusid,
"stored fusion is wrong" );
elif not storedfus in filt_c then
CTblLib.PrintTestLog( "E", "CTblLib.Test.SubgroupFusion", fusid,
Concatenation( [
"stored fusion not compatible with factor embeddings" ],
reducedby[1] ) );
fi;
fi;
# Check the uniqueness status, which should be mentioned in the text.
if Length( fus ) = 1 then
# The fusion is unique.
if IsBound( fusrec.text )
and fusrec.text <> "fusion map is unique"
and ( Length( fusrec.text ) < 21 or
fusrec.text{ [ 1 .. 21 ] } <> "fusion map is unique," )
and ( Length( fusrec.text ) < 22 or
fusrec.text{ [ 1 .. 22 ] } <> "fusion map is unique (" ) then
CTblLib.PrintTestLog( "E", "CTblLib.Test.SubgroupFusion", fusid,
"text for stored fusion is wrong (map is unique!)" );
changedtext:= true;
fi;
result:= rec( name := Identifier( tbl ),
map := fus[1],
text := "fusion map is unique" );
elif 1 < Length( filtreps_c ) and 1 < Length( fusreps ) then
# The fusion is ambiguous.
if storedfus = fail then
CTblLib.PrintTestLog( "E", "CTblLib.Test.SubgroupFusion", fusid,
"ambiguous fusion, no map stored" );
result:= fail;
elif not IsBound( fusrec.text ) then
# The ambiguity of the fusion is not mentioned in the stored fusion.
CTblLib.PrintTestLog( "E", "CTblLib.Test.SubgroupFusion", fusid,
"ambiguous fusion, no text stored" );
result:= fail;
elif PositionSublist( fusrec.text, "together" ) = fail
and PositionSublist( fusrec.text, "determined" ) = fail then
# The ambiguity of the fusion is not mentioned in the stored fusion.
CTblLib.PrintTestLog( "E", "CTblLib.Test.SubgroupFusion", fusid,
"ambiguous fusion, no \"together\" or \"determined\" in text" );
result:= fail;
else
# Keep the map; the text explains why it was chosen.
result:= fusrec;
fi;
###########################
if result = fail then
# Print the information we have about the ambiguous fusion.
CTblLib.PrintTestLog( "E", "CTblLib.Test.SubgroupFusion", fusid,
"data for the ambiguous fusion (fus, filt, filt_c, ...):",
JoinStringsWithSeparator(
List( [ fus, filt, filt_c, fusreps, filtreps, filtreps_c ],
x -> String( Length( x ) ) ), "/" ) );
Print( filtreps_c, "\n" );
fi;
###########################
elif 1 < Length( filtreps ) then
# The compatibility conditions determine the fusion up to table autom..
# Keep the stored fusion if it exists and is admissible.
result:= rec( name := Identifier( tbl ) );
if Length( filt_c ) < Length( filt ) then
result.text:= Concatenation(
"fusion map determined up to table aut. by compatibility\n",
"with factors" );
if Length( filt ) < Length( fus ) then
Append( result.text, " and Brauer tables" );
fi;
elif Length( filt ) < Length( fus ) then
result.text:= Concatenation(
"fusion map determined up to table aut. by compatibility\n",
"with Brauer tables" );
else
result.text:= "fusion map is unique up to table autom.";
fi;
if storedfus in filt_c then
result.map:= storedfus;
else
result.map:= filt_c[1];
fi;
if someexcluded then
result.text:= ReplacedString( result.text, "factors",
"relevant factors" );
fi;
elif Length( filt ) = Length( fus ) then
# The fusion is unique up to table automorphisms,
# and no Brauer table imposes a condition.
if IsBound( fusrec.text ) and
PositionSublist( fusrec.text, "unique up to table " ) = fail then
CTblLib.PrintTestLog( "E", "CTblLib.Test.SubgroupFusion", fusid,
[ [ "text for stored fusion is wrong ",
"(map is unique up to table automorphisms!)" ] ] );
fi;
result:= rec( name := Identifier( tbl ) );
if changedtext or not IsBound( fusrec.text ) then
result.text:= "fusion map is unique up to table autom.";
else
result.text:= fusrec.text;
fi;
# Keep the stored fusion if it exists and is admissible.
if storedfus in filt_c then
result.map:= storedfus;
else
result.map:= filt_c[1];
fi;
if Length( filt_c ) < Length( filt ) then
# Mention compatibility with fusions between factors.
Append( result.text, ",\nrepresentative compatible with factors" );
if someexcluded then
result.text:= ReplacedString( result.text, "factors",
"relevant factors" );
fi;
fi;
else
# We have 1 < Length( fus ), Length( filtreps ) = 1,
# Length( filtreps_c ) = 1, Length( filt ) < Length( fus ).
# So the Brauer tables impose additional conditions;
# together with this and perhaps with factor consistencies,
# the fusion is unique at least up to table automorphisms.
# Keep the stored fusion if it exists and is admissible.
result:= rec( name := Identifier( tbl ),
map := fus );
if storedfus in filt_c then
result.map:= storedfus;
else
result.map:= filt_c[1];
fi;
if Length( fusreps ) = 1 then
if Length( filt ) = 1 then
# The fusion is unique.
result.text:= Concatenation(
"fusion map is unique up to table autom.,\n",
"unique map that is compatible with Brauer tables" );
elif Length( filt_c ) < Length( filt ) then
# The fusion is unique up to table automorphisms
# and compatible with factors.
result.text:= Concatenation(
"fusion map is unique up to table autom.,\n",
"compatible with Brauer tables and factors" );
if someexcluded then
result.text:= ReplacedString( result.text, "factors",
"relevant factors" );
fi;
else
# The fusion is unique up to table automorphisms.
result.text:= Concatenation(
"fusion map is unique up to table autom.,\n",
"compatible with Brauer tables" );
fi;
elif Length( filt ) = 1 then
result.text:= "fusion map uniquely determined by Brauer tables";
elif Length( filt_c ) < Length( filt ) then
# The fusion is unique up to table automorphisms
# and compatible with factors.
result.text:= Concatenation(
"fusion map determined up to table autom.\n",
"by Brauer tables and factors" );
if someexcluded then
result.text:= ReplacedString( result.text, "factors",
"relevant factors" );
fi;
else
result.text:=
"fusion map determined up to table autom. by Brauer tables";
fi;
fi;
if changedtext and IsRecord( result ) then
result.replace:= true;
fi;
if IsBound( fusrec.specification ) then
result.specification:= fusrec.specification;
fi;
# Return the result.
return result;
end;
#############################################################################
##
#F CTblLib.Test.FactorFusion( <tbl>, <fact> )
##
#T changed function!!
## If no class fusion from <tbl> onto <fact> is possible or if the possible
## factor fusions contradict the stored fusion then `false' is returned.
## If a fusion is stored and is compatible with the possible fusions,
## and the fusion is not unique up to table automorphisms and if the stored
## fusion has no `text' component then `fail' is returned.
## Otherwise the fusion record is returned.
##
CTblLib.Test.FactorFusion:= function( tbl, fact )
local result, storedfus, fusid, ker, f, tr, map1, map2, quot,
classes, kernels, factors, trans, pos, auts, triple, factauts, ind,
kerimg, supfact, facttriple;
result:= true;
storedfus:= GetFusionMap( tbl, fact );
fusid:= Concatenation( Identifier( tbl ), " -> ", Identifier( fact ) );
if storedfus <> fail then
if Maximum( storedfus ) > Length( Irr( fact ) ) or
not IsSubset( Irr( tbl ),
List( Irr( fact ), x -> x{ storedfus } ) ) then
result:= false;
else
# If the stored fusion fits then keep it.
ker:= ClassPositionsOfKernel( storedfus );
f:= CharacterTableFactorGroup( tbl, ker );
tr:= TransformingPermutationsCharacterTables( fact, f );
if tr = fail then
result:= false;
else
map1:= OnTuples( storedfus, tr.columns );
map2:= GetFusionMap( tbl, f );
if ElementProperty( tr.group, pi -> map1 = OnTuples( map2, pi ) )
= fail then
result:= false;
fi;
fi;
fi;
else
result:= false;
fi;
if result = false then
# The stored fusion does not fit, or no fusion is stored.
quot:= Size( tbl ) / Size( fact );
classes:= SizesConjugacyClasses( tbl );
kernels:= Filtered( ClassPositionsOfNormalSubgroups( tbl ),
list -> Sum( classes{ list } ) = quot );
factors:= List( kernels, n -> tbl / n );
trans:= List( factors,
f -> TransformingPermutationsCharacterTables( f,
fact ) );
if ForAll( trans, x -> x = fail ) then
CTblLib.PrintTestLog( "E", "CTblLib.Test.FactorFusion", fusid,
"no factor fusion is possible" );
storedfus:= fail;
elif storedfus = fail then
# Choose a fusion.
CTblLib.PrintTestLog( "I", "CTblLib.Test.FactorFusion", fusid,
"add missing fusion" );
pos:= PositionProperty( trans, IsRecord );
storedfus:= OnTuples( GetFusionMap( tbl, factors[ pos ] ),
trans[ pos ].columns );
Print( LibraryFusion( tbl, rec( name:= fact,
map:= storedfus) ) );
else
# Replace the stored fusion.
CTblLib.PrintTestLog( "E", "CTblLib.Test.FactorFusion", fusid,
"replace wrong fusion" );
pos:= Position( kernels, ClassPositionsOfKernel( storedfus ) );
if trans[ pos ] = fail then
pos:= PositionProperty( trans, IsRecord );
fi;
storedfus:= OnTuples( GetFusionMap( tbl, factors[ pos ] ),
trans[ pos ].columns );
Print( LibraryFusion( tbl, rec( name:= fact,
map:= storedfus ) ) );
fi;
fi;
if storedfus <> fail then
# Check whether the fusion is compatible with outer automorphisms.
#T Give an example where this helps.
auts:= CTblLib.PermutationsInducedByOuterAutomorphisms( tbl );
factauts:= CTblLib.PermutationsInducedByOuterAutomorphisms( fact );
ker:= ClassPositionsOfKernel( storedfus );
for triple in auts do
if Set( ker ) = Set( OnTuples( ker, triple[1] ) ) then
# The outer automorphism acts on the factor group.
ind:= CTblLib.PermutationInducedOnFactor( storedfus, triple[1] );
if ind <> fail then
kerimg:= Set( triple[3]{ ker } );
supfact:= First( ComputedClassFusions( triple[2] ),
r -> ClassPositionsOfKernel( r.map ) = kerimg );
if supfact <> fail then
facttriple:= First( factauts,
x -> Identifier( x[2] ) = supfact.name );
if facttriple <> fail then
if ind <> facttriple[1] then
# The permutations do not fit together.
result:= false;
CTblLib.PrintTestLog( "I", "CTblLib.Test.FactorFusion",
fusid,
[ [ "incompatible autom. induced by ",
Identifier( triple[2] ) ] ] );
#T So it is reasonable to define extensions from subgroups;
#T Expl: The first M22 < U6(2) extends to M22.2, the others do not;
#T the same happens in preimages in (2^2x3).U6(2),
#T provided that the automorphism of order three acts at all.
fi;
fi;
fi;
fi;
fi;
od;
fi;
return result;
end;
#############################################################################
##
#F CTblLib.Test.FusionToTom( <tbl> )
##
CTblLib.Test.FusionToTom:= function( tbl )
local tom, result, fustotom, tommaxes, tblmaxes, orders, nam, pos, fus,
filt, func, g, gens, stdinfo, std, prg, cyc, cyctompos, cyctom,
names, fusionfromreps, i, compat1, compat2, primperm, t, map,
tomprimperm, fusrec, fusreps, computed, repl, tblfustom, pair,
supertom, tomfus, supertblname, supertbl, tblfus,
supertblfussupertom, r, altern;
if not IsPackageMarkedForLoading( "TomLib", "" ) then
# The package is not available, or someone has decided not to load it.
return true;
fi;
tom:= TableOfMarks( tbl );
if tom = fail then
if HasFusionToTom( tbl ) then
CTblLib.PrintTestLog( "E", "CTblLib.Test.FusionToTom",
Identifier( tbl ), "no table of marks but HasFusionToTom?" );
return false;
fi;
return true;
fi;
result:= true;
# Compare the compatibility of the maximal subgroup info.
fustotom:= ValueGlobal( "NotifiedFusionsToLibTom" )( Identifier( tom ) );
tommaxes:= MaximalSubgroupsTom( tom )[1];
if HasMaxes( tbl ) then
tblmaxes:= List( Maxes( tbl ), CharacterTable );
orders:= OrdersTom( tom ){ tommaxes };
if Length( orders ) <> Length( tblmaxes ) then
CTblLib.PrintTestLog( "E", "CTblLib.Test.FusionToTom",
Identifier( tbl ),
[ [ "different number of maxes in char. table (",
Length( tblmaxes ), ") and table of marks (",
Length( tommaxes ), ")" ] ] );
result:= false;
elif SortedList( orders ) <> SortedList( List( tblmaxes, Size ) ) then
CTblLib.PrintTestLog( "E", "CTblLib.Test.FusionToTom",
Identifier( tbl ),
[ "table of marks for `", Identifier( tom ),
"' has maxes of orders" ],
String( orders ),
"but the character table has ",
String( List( tblmaxes, Size ) ) );
result:= false;
fi;
elif IsSubset( List( fustotom, x -> x[2] ), tommaxes ) then
# The maxes of the character table should be added.
tommaxes:= List( tommaxes,
x -> First( fustotom, y -> y[2] = x )[1] );
tblmaxes:= [];
for nam in tommaxes do
pos:= Position( LIBLIST.TOM_TBL_INFO[1],
LowercaseString( Identifier( TableOfMarks( nam ) ) ) );
if pos = fail then
Add( tblmaxes, fail );
else
Add( tblmaxes, LibInfoCharacterTable( LIBLIST.TOM_TBL_INFO[2][ pos ]
).firstName );
fi;
od;
CTblLib.PrintTestLog( "I", "CTblLib.Test.FusionToTom",
Identifier( tbl ),
"add `maxes' for the character table,",
"for the table of marks these are",
String( tommaxes ),
"the character table names are",
String( tblmaxes ) );
fi;
# Compute all possible fusions.
fus:= PossibleFusionsCharTableTom( tbl, tom );
if IsEmpty( fus ) then
CTblLib.PrintTestLog( "E", "CTblLib.Test.FusionToTom",
Identifier( tbl ),
[ [ "no fusion to tom `", Identifier( tom ), "' possible" ] ] );
return false;
fi;
# If a classes script is available for the group then we want
# that it is conistent with the fusion to the table of marks:
# The script maps some class names in the character table to group
# elements, and we can identify the position of the corresponding classes
# of cyclic subgroups in the table of marks.
filt:= fus;
if IsPackageMarkedForLoading( "AtlasRep", "" ) then
if ValueGlobal( "HasStandardGeneratorsInfo" )( tom ) then
func:= ValueGlobal( "AtlasProgram" );
g:= UnderlyingGroup( tom );
gens:= GeneratorsOfGroup( g );
for stdinfo in ValueGlobal( "StandardGeneratorsInfo" )( tom ) do
if stdinfo.ATLAS = true then
std:= stdinfo.standardization;
elif IsBound( stdinfo.ATLASFromTom ) then
gens:= ResultOfStraightLineProgram( stdinfo.ATLASFromTom, gens );
std:= 1;
else
std:= fail;
fi;
if std <> fail then
prg:= func( Identifier( tbl ), std, "classes" );
if prg = fail then
prg:= func( Identifier( tbl ), std, "cyclic" );
fi;
if prg <> fail then
cyc:= List( ResultOfStraightLineProgram( prg.program, gens ),
x -> SubgroupNC( g, [ x ] ) );
cyctompos:= Filtered( [ 1 .. Length( OrdersTom( tom ) ) ],
i -> IsCyclicTom( tom, i ) );
cyctom:= List( cyctompos, i -> RepresentativeTom( tom, i ) );
names:= ValueGlobal( "AtlasClassNames" )( tbl );
fusionfromreps:= [];
for i in [ 1 .. Length( prg.outputs ) ] do
pos:= Position( names, prg.outputs[i] );
if pos = fail then
CTblLib.PrintTestLog( "E", "CTblLib.Test.FusionToTom",
Identifier( tbl ),
[ [ "no class name `", prg.outputs[i],
"' (occurs in AtlasRep slp)" ] ] );
return false;
else
fusionfromreps[ pos ]:= cyctompos[
PositionProperty( cyctom,
x -> IsConjugate( g, x, cyc[i] ) ) ];
fi;
od;
filt:= Filtered( fus,
map -> ForAll( [ 1 .. Length( fusionfromreps ) ],
i -> not IsBound( fusionfromreps[i] ) or
fusionfromreps[i] = map[i] ) );
break;
fi;
fi;
od;
fi;
fi;
if IsEmpty( filt ) then
CTblLib.PrintTestLog( "E", "CTblLib.Test.FusionToTom",
Identifier( tbl ),
[ [ "no fusion to tom `", Identifier( tom ),
"' compatible with AtlasRep slp" ] ] );
return false;
fi;
# If the `Maxes' value of `tbl' is known then we want to choose a fusion
# that is compatible with the primitive perm. characters.
# If necessary then we allow for a permutation of lists of maxes.
compat1:= filt;
compat2:= filt;
if HasMaxes( tbl ) then
primperm:= [];
for t in tblmaxes do
if GetFusionMap( t, tbl ) <> fail then
Add( primperm, TrivialCharacter( t )^tbl );
else
Add( primperm, fail );
fi;
od;
if fail in primperm then
CTblLib.PrintTestLog( "E", "CTblLib.Test.FusionToTom",
Identifier( tbl ),
"missing maxes fusions from character tables in ",
String( Maxes( tbl ){ Filtered( [ 1 .. Length( tblmaxes ) ],
i -> primperm[i] = fail ) } ) );
result:= false;
else
compat1:= [];
compat2:= [];
for map in filt do
tomprimperm:= PermCharsTom( map, tom ){ tommaxes };
if tomprimperm = primperm then
Add( compat1, map );
Add( compat2, map );
elif SortedList( tomprimperm ) = SortedList( primperm ) then
Add( compat1, map );
fi;
od;
fi;
fi;
if IsEmpty( compat1 ) then
if fus = filt then
CTblLib.PrintTestLog( "E", "CTblLib.Test.FusionToTom",
Identifier( tbl ),
[ [ "no fusion to tom `", Identifier( tom ),
"' compatible with `Maxes'" ] ] );
else
CTblLib.PrintTestLog( "E", "CTblLib.Test.FusionToTom",
Identifier( tbl ),
[ [ "no fusion to tom `", Identifier( tom ),
"' compatible with both AtlasRep slp and `Maxes'" ] ] );
fi;
return false;
fi;
if HasFusionToTom( tbl ) then
fusrec:= FusionToTom( tbl );
else
fusrec:= fail;
fi;
# Compute representatives under table automorphisms.
# (We do *not* use automorphisms of `tom' because
# the ambiguities can be resolved using the group stored in `tom'.)
fusreps:= RepresentativesFusions( AutomorphismsOfTable( tbl ), fus,
Group( () ) );
# Compose the fusion record which we would like to store.
computed:= rec( name:= Identifier( tom ) );
if Length( fus ) = 1 then
# The fusion is unique.
computed.map:= fus[1];
computed.text:= "fusion map is unique";
elif Length( filt ) = 1 then
# Take the unique map that is compatible with AtlasRep.
computed.map:= filt[1];
computed.text:= "unique fusion map compatible with AtlasRep";
elif Length( fusreps ) = 1 then
# There is no AtlasRep script,
# and the fusion is unique up to table automorphisms.
computed.text:= "fusion map is unique up to table autom.";
if compat1 <> filt then
# The maximal subgroups really impose a condition.
# Note that the choice according to a permutation
# (if 'compat2' is nonempty) is not of this type.
Append( computed.text, ", compatible with `Maxes'" );
fi;
if compat2 <> [] then
computed.map:= compat2[1];
else
computed.map:= compat1[1];
fi;
else
# The fusion is ambiguous.
if fusrec = fail then
CTblLib.PrintTestLog( "E", "CTblLib.Test.FusionToTom",
Identifier( tbl ),
[ [ "ambiguous fusion to tom `", Identifier( tom ),
"', no map stored" ] ] );
result:= fail;
elif not IsBound( fusrec.text ) then
# The ambiguity of the fusion is not mentioned in the stored fusion.
CTblLib.PrintTestLog( "E", "CTblLib.Test.FusionToTom",
Identifier( tbl ),
[ [ "ambiguous fusion to tom `", Identifier( tom ),
"', no text stored" ] ] );
result:= fail;
elif PositionSublist( fusrec.text, "together" ) = fail
and PositionSublist( fusrec.text, "determined" ) = fail then
# The ambiguity of the fusion is not mentioned in the stored fusion.
CTblLib.PrintTestLog( "E", "CTblLib.Test.FusionToTom",
Identifier( tbl ),
[ "ambiguous fusion to tom `", Identifier( tom ), "'," ],
"without \"together\" or \"determined\" in text" );
result:= fail;
elif not fusrec.map in compat1 then
# The stored fusion does not fit but the text supports it.
CTblLib.PrintTestLog( "E", "CTblLib.Test.FusionToTom",
Identifier( tbl ),
[ "ambiguous fusion to tom `", Identifier( tom ), "'," ],
"with text but map is incompatible" );
result:= fail;
elif ( not fusrec.map in compat2 ) and not IsBound( fusrec.perm ) then
# The maxes don't fit but the text supports the stored map.
CTblLib.PrintTestLog( "E", "CTblLib.Test.FusionToTom",
Identifier( tbl ),
[ "ambiguous fusion to tom `", Identifier( tom ), "'," ],
"with text, but map requires permutation of maxes" );
result:= fail;
else
# The text explains how the ambiguity was solved, and the maxes fit.
fi;
if result = fail then
# Print the information we have about the ambiguous fusion.
CTblLib.PrintTestLog( "E", "CTblLib.Test.FusionToTom",
Identifier( tbl ),
"data for the ambiguous fusion (fus, fusreps):",
JoinStringsWithSeparator(
List( [ fus, fusreps ],
x -> String( Length( x ) ) ), "/" ) );
Print( fusreps, "\n" );
result:= false;
fi;
fi;
# Compute the required permutation of maxes.
if HasMaxes( tbl ) then
computed.perm:= SortingPerm( PermCharsTom( computed.map, tom ){
tommaxes } ) / SortingPerm( primperm );
if IsOne( computed.perm ) then
Unbind( computed.perm );
fi;
fi;
if fusrec <> fail then
# Check the compatibility of a stored fusion.
# Check the name.
if fusrec.name <> computed.name then
CTblLib.PrintTestLog( "E", "CTblLib.Test.FusionToTom",
Identifier( tbl ),
[ [ "`name' of `FusionToTom' should equal `Identifier' of `",
Identifier( tom ) ] ] );
result:= false;
fi;
# Check the map and the text of not ambiguous fusions.
if Length( fus ) = 1 or Length( filt ) = 1 or Length( fusreps ) = 1 then
if fusrec.map <> computed.map and not
( Length( filt ) > 1 and fusrec.map in compat1 ) then
CTblLib.PrintTestLog( "E", "CTblLib.Test.FusionToTom",
Identifier( tbl ),
"`map' of `FusionToTom' is wrong, replace it as follows" );
result:= fail;
fi;
repl:= ReplacedString( fusrec.text, "\n", " " );
repl:= ReplacedString( repl, "table automorphisms", "table autom." );
if Length( repl ) < Length( computed.text )
or repl{ [ 1 .. Length( computed.text ) ] } <> computed.text then
CTblLib.PrintTestLog( "E", "CTblLib.Test.FusionToTom",
Identifier( tbl ),
[ [ "`text' of `FusionToTom'" ],
[ "(", fusrec.text, ")" ],
[ "is wrong, replace it as follows" ] ] );
result:= fail;
fi;
# Check the perm component.
if IsBound( fusrec.perm ) then
if IsBound( computed.perm ) then
if fusrec.perm <> computed.perm then
CTblLib.PrintTestLog( "E", "CTblLib.Test.FusionToTom",
Identifier( tbl ),
"replace the permutation in the fusion to tom" );
result:= fail;
fi;
else
CTblLib.PrintTestLog( "E", "CTblLib.Test.FusionToTom",
Identifier( tbl ),
"replace the stored fusion to tom by one without perm." );
result:= fail;
fi;
elif IsBound( computed.perm ) then
CTblLib.PrintTestLog( "E", "CTblLib.Test.FusionToTom",
Identifier( tbl ),
"fusion to tom needs the following perm. of maxes" );
result:= fail;
fi;
fi;
if result = fail then
result:= false;
Print( LibraryFusionTblToTom( tbl, computed ) );
fi;
elif Length( filt ) = 1 or Length( fusreps ) = 1 then
# Propose a fusion if it is not ambiguous.
fusrec:= computed;
CTblLib.PrintTestLog( "I", "CTblLib.Test.FusionToTom",
Identifier( tbl ),
"add `FusionToTom' value" );
Print( LibraryFusionTblToTom( tbl, fusrec ) );
fi;
# Check that the permutation does what it shall do.
if IsBound( fusrec.perm ) and
Permuted( PermCharsTom( fusrec.map, tom ){ tommaxes }, fusrec.perm )
<> primperm then
CTblLib.PrintTestLog( "E", "CTblLib.Test.FusionToTom",
Identifier( tbl ),
"stored permutation of maxes in fusion to tom is wrong" );
result:= false;
fi;
# Test whether the fusions between tables of marks are compatible with
# the fusions between character tables.
if HasFusionToTom( tbl ) then
tblfustom:= FusionToTom( tbl );
for pair in ValueGlobal( "FusionsOfLibTom" )( tom ) do
supertom:= TableOfMarks( pair[1] );
tomfus:= ShallowCopy( pair[2] );
if supertom = fail then
CTblLib.PrintTestLog( "E", "CTblLib.Test.FusionToTom",
Identifier( tbl ),
"table of marks for `", pair[1], "' is not available" );
result:= false;
else
supertblname:= NameOfLibraryCharacterTable( pair[1] );
if supertblname <> fail then
supertbl:= CharacterTable( supertblname );
# Check the consistency.
# (Note that several fusions may be available between two
# tables of marks, but character tables do not support this.)
tblfus:= Filtered( ComputedClassFusions( tbl ),
x -> x.name = supertblname );
if IsEmpty( tblfus ) then
CTblLib.PrintTestLog( "I", "CTblLib.Test.FusionToTom",
Identifier( tbl ),
[ [ "character table fusion `", Identifier( tbl ),
"' -> `", Identifier( supertbl ), "' missing" ] ] );
tblfus:= CTblLib.Test.SubgroupFusion( tbl, supertbl );
if IsRecord( tblfus ) then
Print( "#I store the following one:\n",
LibraryFusion( Identifier( tbl ), tblfus ) );
tblfus:= [ tblfus ];
else
tblfus:= [];
fi;
fi;
supertblfussupertom:= FusionToTom( supertbl );
for r in tblfus do
if not IsBound( r.specification ) or
r.specification = Concatenation( "tom:",
String( tomfus[ Length( tomfus ) ] ) ) then
if IsRecord( tblfustom )
and IsRecord( supertblfussupertom )
and CompositionMaps( tomfus, tblfustom.map ) <>
CompositionMaps( supertblfussupertom.map, r.map ) then
CTblLib.PrintTestLog( "E", "CTblLib.Test.FusionToTom",
Identifier( tbl ),
Concatenation( "fusion `", Identifier( tom ), "' -> `",
Identifier( supertom ),
"' incompatible with char. tables" ) );
altern:= ValueGlobal( "NotifiedFusionsOfLibTom" );
altern:= Filtered( altern( tom ),
p -> p[1] = pair[1]
and p[2] <> tomfus[ Length( tomfus ) ] );
if not IsEmpty( altern ) then
Print( "#E (try `tom:<n>' specif. for <n> in ",
List( altern, x -> x[2] ), ")\n" );
fi;
supertblfussupertom:= fail;
result:= false;
fi;
fi;
od;
# Add the fusion to `tom' if necessary.
if IsRecord( tblfustom ) and not HasFusionToTom( tbl ) then
CTblLib.PrintTestLog( "I", "CTblLib.Test.FusionToTom",
Identifier( tbl ),
"store the following tomfusion `",
Identifier( tbl ), "' -> `", Identifier( tom ), "':" );
Print( LibraryFusionTblToTom( tbl, tblfustom ), "\n" );
fi;
# Add the fusion to `supertom' if necessary.
if IsRecord( supertblfussupertom ) and
not HasFusionToTom( supertbl ) then
CTblLib.PrintTestLog( "I", "CTblLib.Test.FusionToTom",
Identifier( tbl ),
"store the following tomfusion `",
Identifier( supertbl ),
"' -> `", Identifier( supertom ), "':" );
Print( LibraryFusionTblToTom( supertbl, supertblfussupertom ),
"\n" );
fi;
fi;
fi;
od;
fi;
# Return the result.
return result;
end;
#############################################################################
##
#F CTblLib.Test.Fusions( <tbl> )
##
## <#GAPDoc Label="test:CTblLib.Test.Fusions">
## <Mark><C>CTblLib.Test.Fusions( </C><M>tbl</M><C> )</C></Mark>
## <Item>
## checks the class fusions that are stored on the table <M>tbl</M>:
## No duplicates shall occur, each subgroup fusion or factor fusion is
## tested using <C>CTblLib.Test.SubgroupFusion</C> or
## <C>CTblLib.Test.FactorFusion</C>, respectively,
## and a fusion to the table of marks for <M>tbl</M> is tested using
## <C>CTblLib.Test.FusionToTom</C>.
## </Item>
## <#/GAPDoc>
##
CTblLib.Test.Fusions:= function( sub )
local result, destnames, dupl, tbl, record, fus, name;
# Initialize the result.
result:= true;
# Check that there are no duplicate fusions.
# (Duplicates distinguished by specifications are allowed,
# they arise for example in direct product constructions.)
destnames:= SortedList( List( ComputedClassFusions( sub ),
r -> r.name ) );
dupl:= destnames{ Filtered( [ 1 .. Length( destnames ) ],
i -> PositionSorted( destnames, destnames[i] ) <> i ) };
dupl:= Filtered( ComputedClassFusions( sub ),
r -> r.name in dupl and not IsBound( r.specification ) );
if not IsEmpty( dupl ) then
dupl:= List( dupl, r -> Concatenation( "ALF(\"", Identifier( sub ),
"\",\"", r.name, "\"..." ) );
CTblLib.PrintTestLog( "E", "CTblLib.Test.Fusions", Identifier( sub ),
Concatenation( [ "remove duplicate fusions" ], dupl ) );
result:= false;
fi;
for record in ShallowCopy( ComputedClassFusions( sub ) ) do
tbl:= CharacterTable( record.name );
# Do not report a problem if `fail' is returned,
# since direct products involving `sub' may have been
# constructed before this test started.
if tbl <> fail then
if Size( sub ) <= Size( tbl ) then
fus:= CTblLib.Test.SubgroupFusion( sub, tbl );
if not IsRecord( fus ) then
result:= false;
elif record.map <> fus.map then
CTblLib.PrintTestLog( "E", "CTblLib.Test.Fusions",
Identifier( sub ),
Concatenation( Identifier( sub ), " -> ", Identifier( tbl ) ),
"replace the stored fusion by the following one" );
fus:= ShallowCopy( fus );
fus.name:= tbl;
Print( LibraryFusion( sub, fus ) );
elif IsBound( fus.replace ) and fus.replace = true then
if IsBound( record.text ) and IsBound( fus.text ) and
record.text = fus.text then
CTblLib.PrintTestLog( "E", "CTblLib.Test.Fusions",
Identifier( sub ),
Concatenation( Identifier( sub ), " -> ", Identifier( tbl ) ),
"strange fusion, perhaps ambiguous in spite of text?" );
#T check that the texts of ambiguous fusions do not lie!
else
CTblLib.PrintTestLog( "E", "CTblLib.Test.Fusions",
Identifier( sub ),
Concatenation( Identifier( sub ), " -> ", Identifier( tbl ) ),
"replace the text of the stored fusion by the following one" );
fi;
fus:= ShallowCopy( fus );
fus.name:= tbl;
Print( LibraryFusion( sub, fus ) );
fi;
else
result:= CTblLib.Test.FactorFusion( sub, tbl ) and result;
fi;
fi;
od;
result:= CTblLib.Test.FusionToTom( sub ) and result;
# Return the result.
return result;
end;
#############################################################################
##
#V CTblLib.HardPowerMaps
##
## `CTblLib.HardPowerMaps' is a list of pairs `[ <tblname>, <p> ]'
## where <tblname> is a `Identifier' value of a character
## table such that `CTblLib.Test.PowerMaps' shall omit the compatibility
## check for the <p>-th power map.
##
CTblLib.HardPowerMaps:= [];
Add( CTblLib.HardPowerMaps, [ "2^12:Sz(8)", 2 ] );
# 5040 possibilities!
#############################################################################
##
#F CTblLib.Test.PowerMaps( <tbl> )
##
## <#GAPDoc Label="test:CTblLib.Test.PowerMaps">
## <Mark><C>CTblLib.Test.PowerMaps( </C><M>tbl</M><C> )</C></Mark>
## <Item>
## checks whether all <M>p</M>-th power maps are stored on <M>tbl</M>,
## for prime divisors <M>p</M> of the order of <M>G</M>,
## and whether they are correct.
## (This includes the information about uniqueness of the power maps.)
## </Item>
## <#/GAPDoc>
##
CTblLib.Test.PowerMaps:= function( tbl )
local result, powermaps, info, p, pow, reps, storedmap, name;
# Initialize the result.
result:= true;
name:= Identifier( tbl );
powermaps:= ComputedPowerMaps( tbl );
if HasInfoText( tbl ) then
info:= InfoText( tbl );
else
info:= "";
fi;
for p in PrimeDivisors( Size( tbl ) ) do
# Shall the test be omitted?
if [ name, p ] in CTblLib.HardPowerMaps then
if not IsBound( powermaps[p] ) then
CTblLib.PrintTestLog( "E", "CTblLib.Test.PowerMaps", name,
[ "omitting check of ", Ordinal( p ),
" power map (no map stored)" ] );
result:= false;
else
# At least test the existing map for consistency.
pow:= PossiblePowerMaps( tbl, rec( powermap:= powermaps[p] ) );
if IsEmpty( pow ) then
CTblLib.PrintTestLog( "E", "CTblLib.Test.PowerMaps", name,
[ "stored ", Ordinal( p ), " power map is wrong" ] );
result:= false;
else
CTblLib.PrintTestLog( "E", "CTblLib.Test.PowerMaps", name,
[ "omitting check of ", Ordinal( p ), " power map" ] );
fi;
fi;
return result;
fi;
pow:= PossiblePowerMaps( tbl, p );
reps:= RepresentativesPowerMaps( pow,
MatrixAutomorphisms( Irr( tbl ) ) );
if not IsBound( powermaps[p] ) then
CTblLib.PrintTestLog( "I", "CTblLib.Test.PowerMaps", name,
[ [ "no ", Ordinal( p ), " power map stored" ] ] );
storedmap:= fail;
else
storedmap:= powermaps[p];
fi;
if IsEmpty( pow ) then
CTblLib.PrintTestLog( "E", "CTblLib.Test.PowerMaps", name,
[ [ "no ", Ordinal( p ), " power map possible" ] ] );
result:= false;
elif storedmap <> fail and not storedmap in pow then
CTblLib.PrintTestLog( "E", "CTblLib.Test.PowerMaps", name,
[ [ "stored ", Ordinal( p ), " power map is wrong" ] ] );
result:= false;
elif Length( reps ) <> 1 then
if PositionSublist( info, Concatenation( Ordinal( p ),
" power map determined" ) ) = fail then
CTblLib.PrintTestLog( "E", "CTblLib.Test.PowerMaps", name,
[ [ "ambiguous ", Ordinal( p ), " power map" ] ] );
result:= false;
fi;
elif Length( pow ) <> 1 then
if PositionSublist( info, Concatenation( Ordinal( p ),
" power map determined only up to matrix automorphism" ) )
<> fail then
CTblLib.PrintTestLog( "E", "CTblLib.Test.PowerMaps", name,
[ [ Ordinal( p ), " power map is det. only up to mat. aut." ] ] );
result:= false;
fi;
elif PositionSublist( info, Concatenation( Ordinal( p ),
" power map determined" ) ) <> fail then
CTblLib.PrintTestLog( "E", "CTblLib.Test.PowerMaps", name,
[ [ "unnecessary statement about ", Ordinal( p ), " power map" ] ] );
result:= false;
fi;
if storedmap = fail then
if Length( pow ) = 1 then
CTblLib.PrintTestLog( "I", "CTblLib.Test.PowerMaps", name,
[ [ "store the following unique ", Ordinal( p ), " power map" ] ] );
Print( pow[1], "\n" );
elif Length( reps ) = 1 then
CTblLib.PrintTestLog( "I", "CTblLib.Test.PowerMaps", name,
[ [ "store the following ", Ordinal( p ),
" power map (unique up to matrix automorphisms)" ] ] );
Print( reps[1], "\n" );
fi;
fi;
od;
# Return the result.
return result;
end;
#############################################################################
##
#F CTblLib.Test.BlocksInfo( <modtbl> )
##
## <#GAPDoc Label="test:CTblLib.Test.BlocksInfo">
## <Mark><C>CTblLib.Test.BlocksInfo( </C><M>modtbl</M><C> )</C></Mark>
## <Item>
## checks whether the decomposition matrices of all blocks of the Brauer
## table <M>modtbl</M> are integral, as well as the inverses of their
## restrictions to basic sets.
## </Item>
## <#/GAPDoc>
##
CTblLib.Test.BlocksInfo:= function( modtbl )
local info, name, i;
info:= BlocksInfo( modtbl );
name:= Identifier( modtbl );
for i in [ 1 .. Length( info ) ] do
if IsBound( info[i].decinv )
and not ForAll( Concatenation( info[i].decinv ), IsInt ) then
CTblLib.PrintTestLog( "E", "CTblLib.Test.BlocksInfo", name,
[ [ "nonintegral entry in ", Ordinal( i ), " `decinv'" ] ] );
fi;
if not ForAll( Concatenation( DecompositionMatrix( modtbl, i ) ),
IsInt ) then
CTblLib.PrintTestLog( "E", "CTblLib.Test.BlocksInfo", name,
[ [ "nonintegral entry in ", Ordinal( i ), " dec. mat." ] ] );
fi;
od;
return true;
end;
#############################################################################
##
#F CTblLib.Test.TensorDecomposition( <modtbl>[, <verbose>] )
##
## <#GAPDoc Label="test:CTblLib.Test.TensorDecomposition">
## <Mark><C>CTblLib.Test.TensorDecomposition( </C><M>modtbl</M><C> )</C></Mark>
## <Item>
## checks whether the tensor products of irreducible Brauer characters of
## the Brauer table <M>modtbl</M> decompose into Brauer characters.
## </Item>
## <#/GAPDoc>
##
CTblLib.Test.TensorDecomposition:= function( arg )
local modtbl, verbose, ibr, name, i, tens;
modtbl:= arg[1];
verbose:= Length( arg ) = 2 and arg[2] = true;
ibr:= IBr( modtbl );
name:= Identifier( modtbl );
for i in [ 1 .. Length( ibr ) ] do
tens:= Set( Tensored( [ ibr[i] ], ibr{ [ 1 .. i ] } ) );
if not ForAll( Decomposition( ibr, tens, "nonnegative" ), IsList ) then
if verbose then
CTblLib.PrintTestLog( "E", "CTblLib.Test.TensorDecomposition",
name, [ [ "failed for products with X[", i, "]" ] ] );
fi;
return false;
fi;
od;
if HasInfoText( modtbl ) and
PositionSublist( InfoText( modtbl ), "TENS" ) = fail then
if verbose then
CTblLib.PrintTestLog( "I", "CTblLib.Test.TensorDecomposition", name,
"add \"TENS\" to `InfoText'" );
fi;
fi;
return true;
end;
#############################################################################
##
#F CTblLib.Test.Indicators( <modtbl> )
##
## <#GAPDoc Label="test:CTblLib.Test.Indicators">
## <Mark><C>CTblLib.Test.Indicators( </C><M>modtbl</M><C> )</C></Mark>
## <Item>
## checks the <M>2</M>-nd indicators of the Brauer table <M>modtbl</M>:
## The indicator of a Brauer character is zero iff it has at least one
## nonreal value.
## In odd characteristic, the indicator of an irreducible Brauer character
## is equal to the indicator of any ordinary irreducible character that
## contains it as a constituent, with odd multiplicity.
## In characteristic two, we test that all nontrivial real irreducible
## Brauer characters have even degree,
## and that irreducible Brauer characters with indicator <M>-1</M> lie in
## the principal block.
## </Item>
## <#/GAPDoc>
##
CTblLib.Test.Indicators:= function( modtbl )
local name, ind, modind, unknown, irr, result, i, info, decmat, j, chi,
odd;
name:= Identifier( modtbl );
if not IsBrauerTable( modtbl ) then
Error( "<modtbl> must be a Brauer table" );
elif UnderlyingCharacteristic( modtbl ) = 2
and not IsBound( ComputedIndicators( modtbl )[2] ) then
CTblLib.PrintTestLog( "I", "CTblLib.Test.Indicators", name,
"2nd indicator is not stored" );
return true;
fi;
ind:= Indicator( OrdinaryCharacterTable( modtbl ), 2 );
modind:= Indicator( modtbl, 2 );
unknown:= Filtered( [ 1 .. Length( modind ) ],
i -> IsUnknown( modind[i] ) );
if not IsEmpty( unknown ) then
CTblLib.PrintTestLog( "I", "CTblLib.Test.Indicators", name,
[ [ Length( unknown ), " unknown indicators" ] ] );
fi;
irr:= Irr( modtbl );
result:= true;
for i in [ 1 .. Length( BlocksInfo( modtbl ) ) ] do
info:= BlocksInfo( modtbl )[i];
decmat:= DecompositionMatrix( modtbl, i );
for j in [ 1 .. Length( info.modchars ) ] do
chi:= irr[ info.modchars[j] ];
if ForAny( chi, x -> GaloisCyc( x, -1 ) <> x ) then
# The indicator of a Brauer character is zero iff it has
# at least one nonreal value.
if modind[ info.modchars[j] ] <> 0 then
CTblLib.PrintTestLog( "E", "CTblLib.Test.Indicators", name,
[ [ "indicator of X[", info.modchars[j], "] (degree ", chi[1],
") must be 0, not ", modind[ info.modchars[j] ] ] ] );
result:= false;
fi;
elif UnderlyingCharacteristic( modtbl ) = 2 then
# In characteristic two, irreducible Brauer characters with
# indicator <M>-1</M> lie in the principal block.
if modind[ info.modchars[j] ] = -1 and i <> 1 then
CTblLib.PrintTestLog( "E", "CTblLib.Test.Indicators", name,
[ [ "X[", info.modchars[j], "] (degree ", chi[1],
") has indicator -1 but is in block ", i ] ] );
result:= false;
fi;
# All nontrivial irreducible real Brauer characters
# in characteristic two have even degree.
if not ForAll( chi, x -> x = 1 ) and chi[1] mod 2 <> 0 then
CTblLib.PrintTestLog( "E", "CTblLib.Test.Indicators", name,
[ [ "degree X[", info.modchars[j], "][1] = ", chi[1],
" but should be even" ] ] );
result:= false;
fi;
else
# In odd characteristic, the indicator is equal to the indicator
# of an ordinary character that contains it as a constituent,
# with odd multiplicity.
odd:= Filtered( [ 1 .. Length( decmat ) ],
x -> decmat[x][j] mod 2 <> 0 );
if IsEmpty( odd ) then
CTblLib.PrintTestLog( "E", "CTblLib.Test.Indicators", name,
[ [ "no odd constituent for X[", info.modchars[j],
"] (degree ", chi[1], ")" ] ] );
result:= false;
else
odd:= List( odd, x -> ind[ info.ordchars[x] ] );
if ForAny( odd,
x -> x <> 0 and x <> modind[ info.modchars[j] ] ) then
if 1 < Length( Set( odd ) ) then
CTblLib.PrintTestLog( "E", "CTblLib.Test.Indicators", name,
[ [ "ind. of odd const. not unique for X[",
info.modchars[j], "] (degree ", chi[1], ")" ] ] );
else
CTblLib.PrintTestLog( "E", "CTblLib.Test.Indicators", name,
[ [ "indicator of X[", i.modchars[j], "] (degree ",
chi[1], ") must be ", odd[1], ", not ",
modind[ info.modchars[j] ] ] ] );
fi;
result:= false;
fi;
fi;
fi;
od;
od;
# Return the result.
return result;
end;
#############################################################################
##
#F CTblLib.Test.FactorBlocks( <modtbl> )
##
## <#GAPDoc Label="test:CTblLib.Test.FactorBlocks">
## <Mark><C>CTblLib.Test.FactorBlocks( </C><M>modtbl</M><C> )</C></Mark>
## <Item>
## If the Brauer table <M>modtbl</M> is encoded using references to tables
## of factor groups then we must make sure that the irreducible characters
## of the underlying ordinary table and the factors in question are sorted
## compatibly.
## (Note that we simply take over the block information about the factors,
## without applying an explicit mapping.)
## </Item>
## <#/GAPDoc>
##
CTblLib.Test.FactorBlocks:= function( modtbl )
local name, test;
if IsBound( modtbl!.factorblocks ) then
name:= Identifier( modtbl );
test:= CTblLib.ConsiderFactorBlocks( modtbl );
if test = rec() then
CTblLib.PrintTestLog( "E", "CTblLib.Test.FactorBlocks", name,
"no factor table found" );
return false;
elif IsBound( test.error ) then
CTblLib.PrintTestLog( "E", "CTblLib.Test.FactorBlocks", name,
test.error );
return false;
elif test.info <> modtbl!.factorblocks then
CTblLib.PrintTestLog( "E", "CTblLib.Test.FactorBlocks", name,
"inconsistent results" );
return false;
fi;
fi;
return true;
end;
#############################################################################
##
#F CTblLib.Test.InfoText( <tbl> )
##
## <#GAPDoc Label="test:CTblLib.Test.InfoText">
## <Mark><C>CTblLib.Test.InfoText( </C><M>tbl</M><C> )</C></Mark>
## <Item>
## checks some properties of the <Ref Attr="InfoText" BookName="ref"/>
## value of <M>tbl</M>, if available.
## Currently it is not recommended to use this value programmatically.
## However, one can rely on the following structure of this value
## for tables in the &GAP; Character Table Library.
## <P/>
## <List>
## <Item>
## The value is a string that consists of <C>\n</C> separated lines.
## </Item>
## <Item>
## If a line of the form <Q>maximal subgroup of <M>grpname</M></Q>
## occurs, where <M>grpname</M> is the name of a character table,
## then a class fusion from the table in question to that with name
## <M>grpname</M> is stored.
## </Item>
## <Item>
## If a line of the form
## <Q><M>n</M>th maximal subgroup of <M>grpname</M></Q> occurs
## then additionally the name <M>grpname</M><C>M</C><M>n</M> is admissible
## for <M>tbl</M>.
## Furthermore, if the table with name <M>grpname</M> has a
## <Ref Func="Maxes"/> value then <M>tbl</M> is referenced in position
## <M>n</M> of this list.
## </Item>
## </List>
## </Item>
## <#/GAPDoc>
#T check also the cases <n>st, <n>nd, <n>rd!
#T check also lines containing ``<n>th and <m>th''!
#T check also lines ``<n>th maximal subgroup of ... group <grpname>...''!
##
CTblLib.Test.InfoText:= function( tbl )
local name, result, info, pos, indices, pos3, groupname, info2, index,
relname, reltbl, suptbl, maxes;
name:= Identifier( tbl );
result:= true;
if not HasInfoText( tbl ) then
# Nothing is to do.
return true;
elif not IsString( InfoText( tbl ) ) then
CTblLib.PrintTestLog( "E", "CTblLib.Test.InfoText", name,
"`InfoText' value is not a string" );
return false;
fi;
for info in SplitString( InfoText( tbl ), "\n" ) do
# Filter out phrases of the form `maximal subgroup of <grpname>'.
pos:= PositionSublist( info, "maximal subgroup of " );
if pos <> fail then
# Get the indices if they are given.
indices:= SplitString( info{ [ 1 .. pos-1 ] }, " ", " " );
indices:= Filtered( List( indices, x -> Filtered( x, IsDigitChar ) ),
x -> x <> "" );
indices:= SortedList( List( indices, Int ) );
# Get the name of the overgroup.
pos3:= PositionSublist( info, ",", pos + 20 );
if pos3 = fail then
pos3:= Length( info ) + 1;
fi;
groupname:= info{ [ pos + 20 .. pos3 - 1 ] };
# Check that the line is what we expect it to be.
info2:= Concatenation(
JoinStringsWithSeparator( List( indices, Ordinal ),
" and " ), " maximal subgroup of ", groupname );
if info <> info2 and info <> Concatenation( info2, "," ) then
result:= false;
CTblLib.PrintTestLog( "E", "CTblLib.Test.InfoText", name,
"confusing line", info );
fi;
# Check the admissibility of the relative names.
for index in indices do
relname:= Concatenation( groupname, "M", String( index ) );
reltbl:= CharacterTable( relname );
if reltbl = fail then
result:= false;
CTblLib.PrintTestLog( "E", "CTblLib.Test.InfoText", name,
[ [ "no table `", relname, "'" ] ] );
elif Identifier( reltbl ) <> name then
result:= false;
CTblLib.PrintTestLog( "E", "CTblLib.Test.InfoText", name,
[ [ "tables `", name, "' and `", relname, "' differ" ] ] );
fi;
od;
# Get the character table of the overgroup.
suptbl:= CharacterTable( groupname );
if suptbl = fail then
CTblLib.PrintTestLog( "E", "CTblLib.Test.InfoText", name,
[ [ "substring `", info{ [ pos .. pos3 - 1 ] }, "'",
"but no table of `", groupname, "'" ] ] );
result:= false;
elif GetFusionMap( tbl, suptbl ) = fail then
CTblLib.PrintTestLog( "E", "CTblLib.Test.InfoText", name,
[ [ "missing fusion ", name, " -> ",
Identifier( suptbl ), " in spite of `InfoText'\n" ] ] );
result:= false;
# (We do not recompute the fusion here,
# this is done in ...)
fi;
if suptbl <> fail and HasMaxes( suptbl ) and indices <> [] then
# Compare the two values.
maxes:= Maxes( suptbl );
if Length( maxes ) < Maximum( indices ) then
CTblLib.PrintTestLog( "E", "CTblLib.Test.InfoText", name,
[ [ "name `", groupname, "M", Maximum( indices ),
"' but Maxes value of length ", Length( maxes ) ] ] );
result:= false;
elif name <> maxes[ indices[1] ] or
ForAny( indices, i -> maxes[i] <> name and
maxes[i] <> Concatenation( groupname, "M", String( i ) ) ) then
CTblLib.PrintTestLog( "E", "CTblLib.Test.InfoText", name,
[ [ "`", JoinStringsWithSeparator( Maxes{ indices }, ", " ),
"'?" ] ] );
result:= false;
fi;
fi;
fi;
od;
return result;
end;
#############################################################################
##
#V CTblLib.HardTableAutomorphisms
##
## `CTblLib.HardTableAutomorphisms' is a list of `Identifier' values of
## (ordinary or Brauer) character tables such that
## `CTblLib.Test.TableAutomorphisms' shall omit the recomputation of the
## table automorphisms for these tables.
##
CTblLib.HardTableAutomorphisms:= [];
Add( CTblLib.HardTableAutomorphisms, "O8+(3)M14" );
Add( CTblLib.HardTableAutomorphisms, "3.U6(2).3" );
Add( CTblLib.HardTableAutomorphisms, "3.U6(2).3mod5" );
Add( CTblLib.HardTableAutomorphisms, "3.U6(2).3mod7" );
Add( CTblLib.HardTableAutomorphisms, "3.U6(2).3mod11" );
Add( CTblLib.HardTableAutomorphisms, "2^2.(2^(1+8)_+:(S3xS3xS3))" );
Add( CTblLib.HardTableAutomorphisms, "3x2^2.2^(4+8):(S3xA5)" );
Add( CTblLib.HardTableAutomorphisms, "2^2x3xS3xU4(2)" );
Add( CTblLib.HardTableAutomorphisms, "(2^2x3).(3^(1+4).(2^7.3))" );
Add( CTblLib.HardTableAutomorphisms, "6x2.F4(2)" );
Add( CTblLib.HardTableAutomorphisms, "(2^2x3).2E6(2)" );
Add( CTblLib.HardTableAutomorphisms, "O8+(7)" );
Add( CTblLib.HardTableAutomorphisms, "2.O8+(7)" ); # 4647 sec
Add( CTblLib.HardTableAutomorphisms, "O12-(3)" ); # 81031 msec
Add( CTblLib.HardTableAutomorphisms, "O12+(3)" );
Add( CTblLib.HardTableAutomorphisms, "2_1.O12+(3)" );
Add( CTblLib.HardTableAutomorphisms, "U6(4)" );
#############################################################################
##
#F CTblLib.Test.TableAutomorphisms( <tbl> )
##
## <#GAPDoc Label="test:CTblLib.Test.TableAutomorphisms">
## <Mark><C>CTblLib.Test.TableAutomorphisms( </C><M>tbl</M><C> )</C></Mark>
## <Item>
## checks whether the table automorphisms are stored on <M>tbl</M>,
## and whether they are correct.
## Also all available Brauer tables of <M>tbl</M> are checked.
## </Item>
## <#/GAPDoc>
##
CTblLib.Test.TableAutomorphisms:= function( tbl )
local result, libinfo, p, ordtbl, info, name, aut, irr, irrset, powermap,
nccl, stored, modtbl;
# Initialize the result.
result:= true;
#Print( "#I CTblLib.Test.TableAutomorphisms: testing ", tbl, "\n" );
libinfo:= LibInfoCharacterTable( Identifier( tbl ) );
# Exclude tables which do not need stored table automorphisms.
if libinfo = fail then
# The table may be a Brauer table that can be constructed from the
# construction info of its ordinary table.
return true;
elif HasOrdinaryCharacterTable( tbl ) then
p:= UnderlyingCharacteristic( tbl );
ordtbl:= OrdinaryCharacterTable( tbl );
if 1 < Length( ClassPositionsOfPCore( ordtbl, p ) ) then
# The Brauer table belongs to a proper factor group.
# It will be tested when the ordinary table of this factor is tested.
return true;
elif IsPSolvableCharacterTable( ordtbl, p ) then
# No modular library table is needed.
# (However, if table automorphisms are stored then test them.)
if not HasAutomorphismsOfTable( tbl ) then
return true;
fi;
elif HasConstructionInfoCharacterTable( ordtbl ) then
info:= ConstructionInfoCharacterTable( ordtbl );
if IsList( info ) and info[1] in [ "ConstructDirectProduct",
"ConstructIndexTwoSubdirectProduct", "ConstructIsoclinic",
"ConstructMGA", "ConstructPermuted", "ConstructV4G" ] then
# The ordinary table was constructed from other ordinary tables,
# see the corresponding methods for `BrauerTableOp'
# (one in the GAP library and one in CTblLib).
# No modular library table is needed.
# (However, if table automorphisms are stored then test them.)
if not HasAutomorphismsOfTable( tbl ) then
return true;
fi;
fi;
fi;
fi;
name:= Identifier( tbl );
if name in CTblLib.HardTableAutomorphisms then
# The test shall be omitted?
CTblLib.PrintTestLog( "I", "CTblLib.Test.TableAutomorphisms", name,
"omitting table automorphisms check" );
result:= true;
elif not HasAutomorphismsOfTable( tbl ) then
if not ( HasConstructionInfoCharacterTable( tbl ) and
ConstructionInfoCharacterTable( tbl )[1] = "ConstructPermuted" and
Length( ConstructionInfoCharacterTable( tbl )[2] ) = 1 ) then
if IsOrdinaryTable( tbl ) then
aut:= TableAutomorphisms( tbl, Irr( tbl ), "closed" );
else
aut:= TableAutomorphisms( tbl, Irr( tbl ) );
fi;
CTblLib.PrintTestLog( "I", "CTblLib.Test.TableAutomorphisms", name,
"table automorphisms missing, add" );
Print( CTblLib.BlanklessString( GeneratorsOfGroup( aut ), 78 ), "\n" );
result:= false;
fi;
else
# Check that the stored automorphisms are automorphisms,
# and that there are not more automorphisms than the stored ones.
irr:= Irr( tbl );
irrset:= Set( irr );
powermap:= ComputedPowerMaps( tbl );
nccl:= NrConjugacyClasses( tbl );
stored:= AutomorphismsOfTable( tbl );
aut:= Filtered( GeneratorsOfGroup( stored ),
gen -> ForAll( irr, chi -> Permuted( chi, gen ) in irrset )
and ForAll( powermap,
x -> ForAll( [ 1 .. nccl ],
y -> x[ y^gen ] = x[y]^gen ) ) );
aut:= SubgroupNC( stored, aut );
if aut <> stored then
CTblLib.PrintTestLog( "E", "CTblLib.Test.TableAutomorphisms", name,
"wrong automorphisms stored" );
fi;
aut:= TableAutomorphisms( tbl, Irr( tbl ), aut );
if aut <> stored then
CTblLib.PrintTestLog( "E", "CTblLib.Test.TableAutomorphisms", name,
"replace wrong automorphisms by" );
Print( CTblLib.BlanklessString( GeneratorsOfGroup( aut ), 78 ), "\n" );
result:= false;
fi;
fi;
# Check also the available Brauer tables.
if IsOrdinaryTable( tbl ) then
for p in PrimeDivisors( Size( tbl ) ) do
modtbl:= tbl mod p;
if IsCharacterTable( modtbl ) then
result:= CTblLib.Test.TableAutomorphisms( modtbl ) and result;
fi;
od;
fi;
# Return the result.
return result;
end;
#############################################################################
##
## 2. Check ``construction tables''
##
#############################################################################
##
#F CTblLib.Test.PermutedConstruction( <tbl> )
##
## Check that the tables are really equivalent, that is, no defining
## components differ.
## Note that `ConstructPermuted' must not change the isomorphism type of
## the table; if one wants to achieve this then one should use
## `ConstructAdjusted'.
##
## Check that the source table is referenced by its identifier.
##
CTblLib.Test.PermutedConstruction:= function( tbl )
local info, orig, filt;
info:= ConstructionInfoCharacterTable( tbl );
orig:= CallFuncList( CharacterTableFromLibrary, info[2] );
if orig = fail then
CTblLib.PrintTestLog( "E", "CTblLib.Test.PermutedConstruction",
Identifier( tbl ),
"table of `", info[2] , "' is not available" );
return false;
fi;
if IsBound( info[3] ) then
orig:= CharacterTableWithSortedClasses( orig, info[3] );
fi;
if IsBound( info[4] ) then
orig:= CharacterTableWithSortedCharacters( orig, info[4] );
fi;
if Irr( orig ) <> Irr( tbl ) then
CTblLib.PrintTestLog( "E", "CTblLib.Test.PermutedConstruction",
Identifier( tbl ),
"irreducibles do not fit" );
return false;
elif OrdersClassRepresentatives( orig ) <>
OrdersClassRepresentatives( tbl ) then
CTblLib.PrintTestLog( "E", "CTblLib.Test.PermutedConstruction",
Identifier( tbl ),
"element orders do not fit" );
return false;
else
filt:= Filtered( [ 1 .. Length( ComputedPowerMaps( tbl ) ) ],
i -> IsBound( ComputedPowerMaps( tbl )[i] ) and
IsBound( ComputedPowerMaps( orig )[i] ) );
if ForAny( filt, i -> ComputedPowerMaps( tbl )[i] <>
ComputedPowerMaps( orig )[i] ) then
CTblLib.PrintTestLog( "E", "CTblLib.Test.PermutedConstruction",
Identifier( tbl ),
"power maps do not fit" );
return false;
fi;
fi;
if Length( info[2] ) = 1 and Identifier( orig ) <> info[2][1] then
CTblLib.PrintTestLog( "E", "CTblLib.Test.PermutedConstruction",
Identifier( tbl ),
Concatenation( "construction should refer to ",
Identifier( orig ), " not ", info[2][1] ) );
return false;
fi;
return true;
end;
#############################################################################
##
#F CTblLib.Test.DirectProductConstruction( <tbl> )
##
## Check that there are at least two factors.
## Check that the names of the factors are `Identifier' values.
##
CTblLib.Test.DirectProductConstruction:= function( tbl )
local info;
info:= ConstructionInfoCharacterTable( tbl );
if Length( info[2] ) < 2 then
CTblLib.PrintTestLog( "I", "CTblLib.Test.DirectProductConstruction",
Identifier( tbl ),
"use `ConstructPermuted' not `ConstructDirectProduct'" );
return false;
fi;
info:= Filtered( info[2], l -> Length( l ) = 1 and
LibInfoCharacterTable( l[1] ).firstName <> l[1] );
if not IsEmpty( info ) then
info:= List( info, l -> l[1] );
CTblLib.PrintTestLog( "I", "CTblLib.Test.DirectProductConstruction",
Identifier( tbl ),
"replace stored factor(s) ", info, " by ",
List( info, l -> LibInfoCharacterTable( l ).firstName ) );
return false;
fi;
return true;
end;
#############################################################################
##
#F CTblLib.Test.GS3Construction( <tbl> )
##
## Assume that <tbl> is an ordinary character table such that the first
## entry of `ConstructionInfoCharacterTable( <tbl> )' is `"ConstructGS3"'.
## `CTblLib.Test.GS3Construction' checks
## whether the action on the classes of the index two subgroup is correct,
## that the construction with `CharacterTableOfTypeGS3' yields the same
## irreducibles as those of <tbl>,
## that the available Brauer tables coincide with the automatically
## constructed ones,
## and that all Brauer tables are available that can be constructed this
## way.
##
CTblLib.Test.GS3Construction:= function( tbl )
local result, name, info, t2, t3, tnames, t, t3fustbl, aut, poss, ts3,
p, tmodp, t2modp, t3modp, tblmodp, ts3modp, nsg;
result:= true;
name:= Identifier( tbl );
info:= ConstructionInfoCharacterTable( tbl );
t2:= CharacterTable( info[2] );
t3:= CharacterTable( info[3] );
tnames:= Intersection( NamesOfFusionSources( t2 ),
NamesOfFusionSources( t3 ) );
t:= Filtered( List( tnames, CharacterTable ),
ttbl -> 6 * Size( ttbl ) = Size( tbl ) );
if Length( t ) <> 1 then
CTblLib.PrintTestLog( "E", "CTblLib.Test.GS3Construction", name,
"table of the kernel of S3 not identified" );
return false;
fi;
t:= t[1];
# Get the action of `tbl' on the classes of `t3'.
t3fustbl:= GetFusionMap( t3, tbl );
aut:= Product( List( Filtered( InverseMap( t3fustbl ), IsList ),
x -> ( x[1], x[2] ) ), () );
poss:= PossibleActionsForTypeGS3( t, t2, t3 );
if not aut in poss then
CTblLib.PrintTestLog( "E", "CTblLib.Test.GS3Construction", name,
"the action of G.S3 on G.3 is not possible" );
result:= false;
elif Length( poss ) <> 1 then
CTblLib.PrintTestLog( "E", "CTblLib.Test.GS3Construction", name,
"the action of G.S3 on G.3 is not unique" );
result:= false;
fi;
# Check that the two constructions (from the tables of subgroups
# and from the info stored on `tbl') yield the same result.
ts3:= CharacterTableOfTypeGS3( t, t2, t3, aut, "test" );
if SortedList( Irr( ts3.table ) ) <> SortedList( Irr( tbl ) ) then
CTblLib.PrintTestLog( "E", "CTblLib.Test.GS3Construction", name,
"characters in constructed and library table differ" );
result:= false;
elif Irr( ts3.table ) <> Irr( tbl ) then
CTblLib.PrintTestLog( "I", "CTblLib.Test.GS3Construction", name,
"characters in constructed and library table sorted incompatibly" );
fi;
# Check that also the Brauer tables are available.
for p in PrimeDivisors( Size( tbl ) ) do
tmodp:= t mod p;
t2modp:= t2 mod p;
t3modp:= t3 mod p;
if tmodp <> fail and t2modp <> fail and t3modp <> fail then
tblmodp:= tbl mod p;
ts3modp:= CharacterTableOfTypeGS3( tmodp, t2modp, t3modp, tbl,
Concatenation( Identifier( tbl ), "mod", String( p ) ) );
if tblmodp = fail then
# Add the table to the library if it has trivial $O_p(G)$.
nsg:= List( ClassPositionsOfNormalSubgroups( tbl ),
x -> Sum( SizesConjugacyClasses( tbl ){ x } ) );
if not ForAny( nsg, n -> IsPrimePowerInt( n ) and n mod p = 0 ) then
AutomorphismsOfTable( ts3modp.table );
# Perform all checks for new Brauer tables.
if CTblLib.Test.OneBrauerCharacterTable( ts3modp.table ) then
CTblLib.PrintTestLog( "I", "CTblLib.Test.GS3Construction", name,
[ [ "add the following ", p, "-modular table" ] ] );
Print( CTblLib.StringBrauer( ts3modp.table) );
else
CTblLib.PrintTestLog( "E", "CTblLib.Test.GS3Construction", name,
[ [ "proposed new ", p, "-modular table corrupted" ] ] );
fi;
fi;
elif SortedList( Irr( ts3modp.table ) )
<> SortedList( Irr( tblmodp ) ) then
CTblLib.PrintTestLog( "E", "CTblLib.Test.GS3Construction", name,
[ [ "characters in constructed and library table mod ", p,
" differ" ] ] );
result:= false;
elif Irr( ts3modp.table ) <> Irr( tblmodp ) then
CTblLib.PrintTestLog( "I", "CTblLib.Test.GS3Construction", name,
[ [ "characters in constructed and library table mod ", p,
" sorted incompatibly" ] ] );
fi;
fi;
od;
return result;
end;
#############################################################################
##
#F CTblLib.Test.MGAConstruction( <tbl> )
##
## Check that the Brauer tables constructed from ordinary MGA tables are
## consistent, and that a perhaps explicitly stored table coincides with a
## table constructed from the compound Brauer tables.
##
CTblLib.Test.MGAConstruction:= function( tbl )
local info, result, p, m1, modtblMG, modtblGA, m2;
info:= ConstructionInfoCharacterTable( tbl );
result:= true;
for p in PrimeDivisors( Size( tbl ) ) do
m1:= BrauerTableFromLibrary( tbl, p );
modtblMG:= CharacterTable( info[2] ) mod p;
modtblGA:= CharacterTable( info[3] ) mod p;
m2:= fail;
if ForAll( [ modtblMG, modtblGA ], IsCharacterTable ) then
m2:= BrauerTableOfTypeMGA( modtblMG, modtblGA, tbl );
if m2 <> fail then
m2:= m2.table;
fi;
fi;
if m1 <> fail and m2 <> fail and
Set( Irr( m1 ) ) <> Set( Irr( m2 ) ) then
CTblLib.PrintTestLog( "E", "CTblLib.Test.MGAConstruction",
Identifier( m1 ),
"different irreducibles in the two constructions" );
result:= false;
fi;
if m2 <> fail and not ForAll( Flat( Irr( m2 ) ), IsCycInt ) then
CTblLib.PrintTestLog( "E", "CTblLib.Test.MGAConstruction",
Identifier( m2 ),
"wrong parameters in the MGA construction" );
result:= false;
fi;
od;
return result;
end;
#############################################################################
##
#V CTblLib.Test.ConstructionsFunctions
##
CTblLib.Test.ConstructionsFunctions:= [
"ConstructGS3", CTblLib.Test.GS3Construction,
"ConstructDirectProduct", CTblLib.Test.DirectProductConstruction,
"ConstructPermuted", CTblLib.Test.PermutedConstruction,
"ConstructMGA", CTblLib.Test.MGAConstruction,
];
#############################################################################
##
#F CTblLib.Test.Constructions( <tbl> )
##
## <#GAPDoc Label="test:CTblLib.Test.Constructions">
## <Mark><C>CTblLib.Test.Constructions( </C><M>tbl</M><C> )</C></Mark>
## <Item>
## checks the <Ref Func="ConstructionInfoCharacterTable"/> status for
## the table <M>tbl</M>:
## If this attribute value is set then tests depending on this value are
## executed;
## if this attribute is not set then it is checked whether a description
## of <M>tbl</M> via a construction would be appropriate.
## </Item>
## <#/GAPDoc>
##
CTblLib.Test.Constructions:= function( tbl )
local constr, pos, dp, kernels, fusions, r, ker, result, pair, poss;
if HasConstructionInfoCharacterTable( tbl ) then
constr:= ConstructionInfoCharacterTable( tbl );
if IsFunction( constr ) then
CTblLib.PrintTestLog( "I", "CTblLib.Test.Constructions",
Identifier( tbl ),
"construction via function (allowed but not recommended)" );
else
# Apply tests depending on the construction type of the table.
pos:= Position( CTblLib.Test.ConstructionsFunctions, constr[1] );
if pos <> fail then
return CTblLib.Test.ConstructionsFunctions[ pos + 1 ]( tbl );
fi;
fi;
else
# Check that tables of direct products are stored as such.
dp:= ClassPositionsOfDirectProductDecompositions( tbl );
if not IsEmpty( dp ) then
# Check for stored factor fusions whose kernels are direct factors.
kernels:= [];
fusions:= [];
for r in ComputedClassFusions( tbl ) do
ker:= ClassPositionsOfKernel( r.map );
if 1 < Length( ker ) then
Add( kernels, ker );
Add( fusions, r );
fi;
od;
SortParallel( kernels, fusions );
result:= [];
for pair in dp do
poss:= List( pair, l -> PositionSet( kernels, l ) );
if not fail in poss then
Add( result, List( fusions{ poss }, r -> [ r.name ] ) );
fi;
od;
if result = [] then
# The table belongs to a direct product but we do not know factors.
CTblLib.PrintTestLog( "I", "CTblLib.Test.Constructions",
Identifier( tbl ),
"direct product but not stored as such" );
else
# We know at least one factorization.
CTblLib.PrintTestLog( "I", "CTblLib.Test.Constructions",
Identifier( tbl ),
Concatenation( "direct product of ", String( result ),
", store this" ) );
fi;
return false;
fi;
fi;
return true;
end;
#############################################################################
##
#F CTblLib.Test.ExtensionInfo( <tbl> )
##
## <#GAPDoc Label="test:CTblLib.Test.ExtensionInfo">
## <Mark><C>CTblLib.Test.ExtensionInfo( </C><M>tbl</M><C> )</C></Mark>
## <Item>
## checks whether the attribute <Ref Attr="ExtensionInfoCharacterTable"/>
## is known for all nonabelian simple character tables
## that are not duplicates.
## </Item>
## <#/GAPDoc>
##
CTblLib.Test.ExtensionInfo := function( tbl )
if IsSimple( tbl ) and not IsAbelian( tbl )
and not IsDuplicateTable( tbl )
and not HasExtensionInfoCharacterTable( tbl ) then
CTblLib.PrintTestLog( "I", "CTblLib.Test.ExtensionInfo",
Identifier( tbl ),
"missing extension info" );
return false;
fi;
return true;
end;
#############################################################################
##
#F CTblLib.Test.FactorsModPCore( <tbl> )
##
## <#GAPDoc Label="test:CTblLib.Test.FactorsModPCore">
## <Mark><C>CTblLib.Test.FactorsModPCore( </C><M>tbl</M><C> )</C></Mark>
## <Item>
## checks, for all those prime divisors <M>p</M> of the order of <M>G</M>
## such that <M>G</M> is not <M>p</M>-solvable,
## whether the factor fusion to the character table of <M>G/O_p(G)</M>
## is stored on <M>tbl</M>.
## <P/>
## Note that if <M>G</M> is not <M>p</M>-solvable
## and <M>O_p(G)</M> is nontrivial
## then we can compute the <M>p</M>-modular Brauer table of <M>G</M>
## if that of the factor group <M>G/O_p(G)</M> is available.
## The availability of this table is indicated via the availability
## of the factor fusion from <M>tbl</M>.
## </Item>
## <#/GAPDoc>
##
CTblLib.Test.FactorsModPCore:= function( tbl )
local name, classes, p, op, fact, trans, cand;
name:= Identifier( tbl );
classes:= SizesConjugacyClasses( tbl );
for p in Filtered( PrimeDivisors( Size( tbl ) ),
p -> BrauerTable( tbl, p ) = fail ) do
op:= ClassPositionsOfPCore( tbl, p );
if op <> [ 1 ] and
ForAll( ComputedClassFusions( tbl ),
fus -> ClassPositionsOfKernel( fus.map ) <> op ) then
CTblLib.PrintTestLog( "I", "CTblLib.Test.FactorsModPCore", name,
[ [ "no stored factor fusion to factor mod O_", p ] ] );
# Try to find the table of the factor group in the library.
fact:= CharacterTableFactorGroup( tbl, op );
cand:= NameOfEquivalentLibraryCharacterTable( fact );
if cand <> fail then
cand:= CharacterTable( cand );
trans:= TransformingPermutationsCharacterTables( fact, cand );
CTblLib.PrintTestLog( "I", "CTblLib.Test.FactorsModPCore", name,
"store the following fusion" );
Print( LibraryFusion( Identifier( tbl ),
rec( name := cand,
map := OnTuples( GetFusionMap( tbl, fact ),
trans.columns ) ) ) );
else
CTblLib.PrintTestLog( "I", "CTblLib.Test.FactorsModPCore", name,
"(the factor table is currently missing)" );
fi;
fi;
od;
return true;
end;
#############################################################################
##
#F CTblLib.Test.Maxes( <tbl> )
##
## <#GAPDoc Label="test:CTblLib.Test.Maxes">
## <Mark><C>CTblLib.Test.Maxes( </C><M>tbl</M><C> )</C></Mark>
## <Item>
## checks for those character tables <M>tbl</M> that have the
## <Ref Func="Maxes"/> set whether the character tables
## with the given names are really available,
## that they are ordered w.r.t. non-increasing group order,
## and that the fusions into <M>tbl</M> are stored.
## </Item>
## <#/GAPDoc>
##
CTblLib.Test.Maxes:= function( tbl )
local result, name, maxestbls, maxorders, i;
result:= true;
if HasMaxes( tbl ) then
name:= Identifier( tbl );
maxestbls:= List( Maxes( tbl ), CharacterTable );
maxorders:= [];
for i in [ 1 .. Length( maxestbls ) ] do
if maxestbls[i] = fail then
CTblLib.PrintTestLog( "E", "CTblLib.Test.Maxes", name,
[ [ "no table of ", Ordinal( i ), " max. subgroup" ] ] );
result:= false;
else
Add( maxorders, Size( maxestbls[i] ) );
if GetFusionMap( maxestbls[i], tbl ) = fail then
CTblLib.PrintTestLog( "E", "CTblLib.Test.Maxes", name,
[ [ "no fusion from ", Ordinal( i ), " max. subgroup" ] ] );
result:= false;
fi;
fi;
od;
if not IsSortedList( - maxorders ) then
CTblLib.PrintTestLog( "E", "CTblLib.Test.Maxes", name,
"orders of max. subgroups are not non-increasing" );
result:= false;
fi;
fi;
return result;
end;
#############################################################################
##
#F CTblLib.Test.ClassParameters( <tbl> )
##
## <#GAPDoc Label="test:CTblLib.Test.ClassParameters">
## <Mark><C>CTblLib.Test.ClassParameters( </C><M>tbl</M><C> )</C></Mark>
## <Item>
## checks the compatibility of class parameters of alternating and
## symmetric groups (partitions describing cycle structures),
## using the underlying group stored in the corresponding table of marks.
## </Item>
## <#/GAPDoc>
##
CTblLib.Test.ClassParameters:= function( tbl )
local result, name, pos, paras, fus, tom, i, g, cyc, part, j;
result:= true;
if HasClassParameters( tbl ) and HasFusionToTom( tbl ) then
name:= Identifier( tbl );
pos:= Position( name, '.' );
if pos = fail then
pos:= Length( name ) + 1;
fi;
if name[1] = 'A' and ForAll( name{ [ 2 .. pos-1 ] }, IsDigitChar ) then
paras:= ClassParameters( tbl );
fus:= FusionToTom( tbl );
tom:= TableOfMarks( tbl );
for i in [ 1 .. Length( fus.map ) ] do
g:= RepresentativeTom( tom, fus.map[i] );
if not IsCyclic( g ) then
CTblLib.PrintTestLog( "E", "CTblLib.Test.ClassParameters", name,
[ [ Ordinal( i ), " subgroup is not cyclic" ] ] );
result:= false;
else
cyc:= [];
part:= paras[i][2];
if IsList( part[1] ) then
part:= part[1];
fi;
for j in part do
if j <> 1 then
if IsBound( cyc[ j-1 ] ) then
cyc[ j-1 ]:= cyc[ j-1 ] + 1;
else
cyc[ j-1 ]:= 1;
fi;
fi;
od;
if cyc <> CycleStructurePerm( MinimalGeneratingSet( g )[1] ) then
CTblLib.PrintTestLog( "E", "CTblLib.Test.ClassParameters", name,
[ [ Ordinal( i ), " class parameter is wrong" ] ] );
result:= false;
fi;
fi;
od;
fi;
fi;
return result;
end;
#############################################################################
##
## <#GAPDoc Label="test:CTblLib.Test.TablesOfSymmetricGroup">
## <Mark><C>CTblLib.Test.TablesOfSymmetricGroup( </C><M>n</M><C> )</C></Mark>
## <Item>
## checks that the class parameters and character parameters of the
## two ordinary character tables of the symmetric group of degree <M>n</M>
## (from the &ATLAS; and from the <Package>SpinSym</Package> package)
## are consistent, and that the parameters for the ordinary tables
## are consistent with those for the Brauer tables.
## The interesting values of <A>n</A> are in <C>[ 5 .. 19 ]</C>.
## </Item>
## <#/GAPDoc>
##
## CTblLib.Test.ComputedCharacterParameters( <modtbl> )
##
## auxiliary function:
##
## Assume that <modtbl> is the p-modular Brauer table of a symmetric group
## such that the 'CharacterParameters' value of its underlying ordinary
## table is stored.
## Compute the character parameters of <modtbl> as described in the book
## by James/Kerber, that is,
## - take the ordinary characters corresponding to p-regular partitions,
## ordered according to reverse lexicographical ordering (that is, the
## trivial character appears first),
## - take the corresponding decomposition matrix and permute its columns
## such that the matrix has triangular shape,
## - now the rows and columns of this matrix belong to the same parameters.
##
## (For the symmetric group on n points, the parameter of trivial character
## is [n], and the parameter of the sign character is the p-regular part of
## [1^n].)
##
CTblLib.Test.ComputedCharacterParameters:= function( modtbl )
local IsPRegularPartition, p, n, ordtbl, pos, decmat, ordparas, tr,
modindices;
IsPRegularPartition:= function( pi, p )
return Maximum( List( Collected( pi ), x -> x[2] ) ) < p;
end;
p:= UnderlyingCharacteristic( modtbl );
n:= First( [ 1 .. 20 ], i -> Size( modtbl ) = Factorial( i ) );
ordtbl:= OrdinaryCharacterTable( modtbl );
pos:= PositionsProperty( CharacterParameters( ordtbl ),
para -> IsPRegularPartition( para[2], p ) );
decmat:= ShallowCopy( DecompositionMatrix( modtbl ){ pos } );
ordparas:= - CharacterParameters( ordtbl ){ pos };
SortParallel( ordparas, decmat );
tr:= - MutableTransposedMat( decmat );
modindices:= [ 1 .. Length( tr ) ];
SortParallel( tr, modindices );
return Permuted( -ordparas, PermList( modindices ) );
end;
CTblLib.Test.TablesOfSymmetricGroup:= function( n )
local result, t, tt, trans, classperm, pi, charperm, p, tmod, ttmod,
pclassperm, prowperm;
result:= true;
# ordinary tables
t:= CharacterTable( Concatenation( "S", String(n) ) );
tt:= CharacterTable( Concatenation( "Sym(", String(n), ")" ) );
if t = fail or tt = fail then
Print( "#E tables for symm. group of degree '", n,
"' not available\n" );
result:= false;
fi;
trans:= TransformingPermutationsCharacterTables( t, tt );
classperm:= trans.columns;
# n = 6 admits other table automorphisms ...
for pi in trans.group do
if Permuted( ClassParameters( t ), classperm * pi ) =
ClassParameters( tt ) then
classperm:= classperm * pi;
break;
fi;
od;
if Permuted( ClassParameters( t ), classperm ) <>
ClassParameters( tt ) then
Print( "#E n = ", n, ": problem with class parameters\n" );
result:= false;
fi;
charperm:= SortingPerm( List( Irr( t ), x -> Permuted( x, classperm ) ) )
/ SortingPerm( Irr( tt ) );
if Permuted( CharacterParameters( t ), charperm ) <>
CharacterParameters( tt ) then
Print( "#E n = ", n, ": problem with ordinary character parameters\n" );
result:= false;
fi;
# modular tables
for p in PrimeDivisors( Size( t ) ) do
tmod:= t mod p;
ttmod:= tt mod p;
if ttmod <> fail then
# Test the induced class permutation.
pclassperm:= PermList( CompositionMaps(
InverseMap( GetFusionMap( ttmod, tt ) ),
CompositionMaps( ListPerm( classperm,
NrConjugacyClasses( t ) ),
GetFusionMap( tmod, t ) ) ) );
if Permuted( ClassParameters( tmod ), pclassperm ) <>
ClassParameters( ttmod ) then
Print( "#E n = ", n, ", p = ", p,
": problem with class parameters\n" );
result:= false;
fi;
prowperm:= SortingPerm( List( Irr( tmod ),
x -> Permuted( x, pclassperm ) ) ) / SortingPerm( Irr( ttmod ) );
if Permuted( List( Irr( tmod ),
x -> Permuted( x, pclassperm ) ), prowperm ) <> Irr( ttmod ) then
result:= false;
fi;
# Check the consistency of ordinary and modular character parameters
# for the library table, independent of the SpinSym table.
if not HasCharacterParameters( tmod ) then
Print( "#I character parameters missing in ", tmod, ",\n",
"#I set the value:\n",
"rec(CharacterParameters:=", ReplacedString( String(
CTblLib.Test.ComputedCharacterParameters( tmod ) ),
" ", "" ), ")\n" );
elif CharacterParameters( tmod )
<> CTblLib.Test.ComputedCharacterParameters( tmod ) then
Print( "#E n = ", n, ", p = ", p,
": stored modular character parameters differ from\n",
"#E the ones computed from the ordinary ones\n" );
result:= false;
fi;
fi;
od;
return result;
end;
#############################################################################
##
## <#GAPDoc Label="test:CTblLib.Test.GroupForGroupInfo">
## <Mark><C>CTblLib.Test.GroupForGroupInfo( </C><M>tbl</M><C> )</C></Mark>
## <Item>
## checks that the entries in the list returned by
## <Ref Func="GroupInfoForCharacterTable"/> fit to the character table
## <M>tbl</M>.
## </Item>
## <#/GAPDoc>
##
CTblLib.Test.GroupForGroupInfo:= function( tbl )
local result, size, name, info, G;
result:= true;
size:= Size( tbl );
name:= Identifier( tbl );
for info in GroupInfoForCharacterTable( tbl ) do
G:= GroupForGroupInfo( info );
if G = fail or not IsGroup( G ) then
CTblLib.PrintTestLog( "E", "CTblLib.Test.GroupForGroupInfo", name,
[ [ "not admissible info ", String( info ) ] ] );
result:= false;
elif Size( G ) <> size then
CTblLib.PrintTestLog( "E", "CTblLib.Test.GroupForGroupInfo", name,
[ [ "wrong order for ", String( info ) ] ] );
result:= false;
fi;
od;
return result;
end;
#############################################################################
##
#V CTblLib.TestsForOrdinaryTables
##
CTblLib.TestsForOrdinaryTables:= [
"InfoText",
"RelativeNames",
"FindRelativeNames",
"PowerMaps",
"TableAutomorphisms",
"CompatibleFactorFusions",
"FactorsModPCore",
"Fusions",
"Maxes",
"ClassParameters",
"Constructions",
"ExtensionInfo",
"GroupForGroupInfo",
];
#############################################################################
##
#V CTblLib.TestsForBrauerTables
##
CTblLib.TestsForBrauerTables:= [
"BlocksInfo",
"TensorDecomposition",
"Indicators",
"FactorBlocks",
];
#T what about decomposition matrix?
#T (there were cases where the constructed table was o.k.
#T but did not fit to the ordinary table!)
#############################################################################
##
#F CTblLib.TestOneOrdinaryCharacterTable( <tbl> )
##
CTblLib.TestOneOrdinaryCharacterTable:= function( tbl )
local result, entry;
result:= true;
for entry in CTblLib.TestsForOrdinaryTables do
#Print( "#I call CTblLib.Test.", entry, " for ", Identifier( tbl ), "\n" );
if not CallFuncList( CTblLib.Test.( entry ), [ tbl ] ) then
Print( "#E problems with `CTblLib.Test.", entry, "' for `",
Identifier( tbl ), "'\n" );
result:= false;
fi;
od;
return result;
end;
#############################################################################
##
#F CTblLib.TestOneBrauerCharacterTable( <modtbl> )
##
CTblLib.TestOneBrauerCharacterTable:= function( modtbl )
local result, entry;
result:= true;
for entry in CTblLib.TestsForBrauerTables do
#Print( "#I call CTblLib.Test.", entry, " for ", Identifier( modtbl ), "\n" );
if not CallFuncList( CTblLib.Test.( entry ), [ modtbl ] ) then
Print( "#E problems with `CTblLib.Test.", entry, "' for `",
Identifier( modtbl ), "'\n" );
result:= false;
fi;
od;
return result;
end;
#############################################################################
##
#F CTblLib.TestOneCharacterTable( <tbl> )
##
## Apply the tests introduced above to the ordinary table <tbl>.
## Apply the tests introduced above to all available Brauer tables
## of this table (including those Brauer tables that can be constructed by
## GAP).
##
CTblLib.TestOneCharacterTable:= function( tbl )
local result, p, modtbl;
if not IsOrdinaryTable( tbl ) then
Print( "#E `", tbl, "' is not an ordinary character table\n" );
return false;
fi;
result:= CTblLib.TestOneOrdinaryCharacterTable( tbl );
for p in PrimeDivisors( Size( tbl ) ) do
modtbl:= tbl mod p;
if modtbl <> fail then
result:= CTblLib.TestOneBrauerCharacterTable( modtbl ) and result;
fi;
od;
return result;
end;
#############################################################################
##
#E