|
#############################################################################
##
#W ctdbattr.g GAP 4 package CTblLib Thomas Breuer
##
## This file contains the declarations for the database id enumerators
## `CTblLib.Data.IdEnumerator' and `CTblLib.Data.IdEnumeratorExt`,
## and their database attributes.
## See the GAP package `Browse' for technical details.
## Among others, the enumerators provide the data for the GAP attribute
## `GroupInfoForCharacterTable'.
##
## The component `reverseEval' is used only by the function
## `CharacterTableForGroupInfo',
## it is not needed by the database attribute handling mechanism.
##
## (Some attributes are available only if additional GAP packages are
## available, for example the TomLib package.
## Their data are read via suitable package extensions that are listed
## in CTblLib's 'PackageInfo.g' file.)
##
#############################################################################
##
#F IsDihedralCharacterTable( <tbl> )
##
## Let <A>tbl</A> be the character table of a group <M>G</M>, say.
## Then <M>G</M> is a dihedral group if and only if
## the order of <M>G</M> is an even integer <M>n</M>,
## <M>G</M> contains a cyclic normal subgroup <M>N</M> of index two,
## <M>G \setminus N</M> contains involutions,
## and the table of <M>G</M> is real.
##
BindGlobal( "IsDihedralCharacterTable", function( tbl )
local size, orders, nsg, ccl;
size:= Size( tbl );
if size mod 2 = 1 then
return false;
fi;
orders:= OrdersClassRepresentatives( tbl );
nsg:= Filtered( ClassPositionsOfNormalSubgroups( tbl ),
n -> size / 2 in orders{ n } );
ccl:= [ 1 .. NrConjugacyClasses( tbl ) ];
return ForAny( nsg, n -> 2 in orders{ Difference( ccl, n ) } )
and PowerMap( tbl, -1 ) = ccl;
end );
#############################################################################
##
#F CTblLib.Data.CharacterTablesOfNormalSubgroupWithGivenImage( <tbl>,
#F <classes> )
##
## Let <tbl> be an ordinary character table, and <classes> be a list of
## class positions for <tbl>.
## `CTblLib.Data.CharacterTablesOfNormalSubgroupWithGivenImage' returns the
## list of those library tables that store a class fusion to <tbl> having
## image exactly <classes> and whose size equals the union of the
## class lengths for <classes>.
##
CTblLib.Data.CharacterTablesOfNormalSubgroupWithGivenImage:=
function( tbl, classes )
local n, result, ids, name, subtbl, fus, next;
n:= Sum( SizesConjugacyClasses( tbl ){ classes } );
result:= [];
ids:= [];
for name in NamesOfFusionSources( tbl ) do
subtbl:= CharacterTable( name );
if subtbl <> fail and Size( subtbl ) mod n = 0 then
fus:= GetFusionMap( subtbl, tbl );
if Set( fus ) = classes and Size( subtbl ) = n then
# The table itself satisfies the conditions.
if not Identifier( subtbl ) in ids then
Add( result, [ subtbl, fus ] );
AddSet( ids, Identifier( subtbl ) );
fi;
elif ClassPositionsOfKernel( fus ) = [ 1 ]
and IsSubset( fus, classes ) then
# Maybe a subgroup fits, so enter a recursion.
for next in
CTblLib.Data.CharacterTablesOfNormalSubgroupWithGivenImage( subtbl,
Filtered( [ 1 .. Length( fus ) ], i -> fus[i] in classes ) ) do
if not Identifier( next[1] ) in ids then
Add( result, [ next[1], fus{ next[2] } ] );
AddSet( ids, Identifier( next[1] ) );
fi;
od;
fi;
fi;
od;
return result;
end;
#############################################################################
##
#F CTblLib.FindTableForGroup( <G>, <tbls>, <pos> )
##
## Let <G> be a group, <tbls> be a list of ordinary character tables,
## and <pos> be a position in this list such that <G> belongs to (at least)
## one of these tables.
## `CTblLib.FindTableForGroup' tries to decide whether <G> belongs to the
## <pos>-th entry in <tbls>.
## If this is possible then `true' or `false' is returned,
## otherwise `fail' is returned.
##
CTblLib.FindTableForGroup:= function( G, tbls, pos )
local funs, invs, rng, bound, k, g, i, val;
# Use element orders and centralizer orders.
funs:= [ Order,
g -> Size( Centralizer( G, g ) ) ];
invs:= List( tbls, t -> TransposedMat(
[ OrdersClassRepresentatives( t ),
SizesCentralizers( t ) ] ) );
# Can we decide the question, or is there a chance to fail?
rng:= [ 1 .. Length( tbls ) ];
if ForAll( rng, i -> i = pos or
not ( IsEmpty( Difference( invs[i], invs[ pos ] ) ) or
IsEmpty( Difference( invs[i], invs[ pos ] ) ) ) ) then
bound:= infinity;
else
bound:= 100;
fi;
k:= 1;
while k <= bound do
g:= PseudoRandom( G );
for i in [ 1 .. Length( funs ) ] do
val:= funs[i]( g );
rng:= Filtered( rng,
j -> ForAny( invs[j], tuple -> val = tuple[i] ) );
if rng = [ pos ] then
return true;
elif not pos in rng then
return false;
fi;
od;
k:= k + 1;
od;
return fail;
end;
#############################################################################
##
## Provide utilities for the computation of database attribute values.
## They allow one to access table data without actually creating the tables.
##
#############################################################################
##
#F CTblLib.AttrDataString( <entry>, <default>, <withcomment> )
##
## This is the default value for the `string' component of database
## attributes,
## which is used for storing the attriute values in files.
##
CTblLib.AttrDataString:= function( entry, default, withcomment )
local encode, result, comment, data, l, x;
encode:= function( value )
if IsString( value ) and
( IsStringRep( value ) or not IsEmpty( value ) ) then
value:= ReplacedString( value, "\"", "\\\"" );
value:= ReplacedString( value, "\n", "\\n" );
value:= Concatenation( "\"", value, "\"" );
elif IsList( value ) then
value:= Concatenation( "[",
JoinStringsWithSeparator( List( value, encode ), "," ),
"]" );
else
value:= String( value );
value:= ReplacedString( value, "\"", "\\\"" );
value:= ReplacedString( value, "\n", "\\n" );
fi;
return value;
end;
# Default values need not be stored.
if IsList( entry[2] ) and entry[2] = default then
return "";
fi;
# The first entry is the identifier.
result:= Concatenation( "[", encode( entry[1] ), "," );
if withcomment then
# A comment may be stored at the second position.
comment:= entry[2][1];
data:= entry[2][2];
Append( result, Concatenation( "[", encode( comment ), "," ) );
else
data:= entry[2];
fi;
Append( result, encode( data ) );
if withcomment then
Append( result, "]" );
fi;
Append( result, "],\n" );
return result;
end;
#############################################################################
##
#F CTblLib.Data.InvariantByRecursion( <list>, <funcs> )
##
## ... is used only for computing the `Size' and `NrConjugacyClasses' values
##
## <funcs> is a record with the components
## `gentablefunc'
## a function that takes two arguments,
## a generic character table and ...
## `wreathsymmfunc'
## a function ...
## `cheaptest'
## a function ...
##
CTblLib.Data.InvariantByRecursion:= function( list, funcs )
local id, info, r, res, cen;
id:= list[1];
info:= LibInfoCharacterTable( id );
id:= info.firstName;
if info.fileName = "ctgeneri" then
# Evaluate only part of the generic table.
return funcs.gentablefunc( CharacterTable( info.firstName ), list );
elif Length( list ) = 1
and IsBound( CTblLib.Data.CharacterTableInfo.( id ) ) then
r:= CTblLib.Data.CharacterTableInfo.( id );
res:= funcs.cheaptest( r );
if res <> fail then
return res;
elif IsBound( r.ConstructionInfoCharacterTable )
and IsList( r.ConstructionInfoCharacterTable ) then
# Determine the size/nccl from the sizes/nccls of the input tables.
if r.ConstructionInfoCharacterTable[1] in
[ "ConstructDirectProduct", "ConstructIsoclinic" ] then
r:= List( r.ConstructionInfoCharacterTable[2],
l -> CTblLib.Data.InvariantByRecursion( l, funcs ) );
if fail in r then
return fail;
fi;
return Product( r, 1 );
elif r.ConstructionInfoCharacterTable[1] in
[ "ConstructPermuted", "ConstructAdjusted" ] then
r:= CTblLib.Data.InvariantByRecursion(
r.ConstructionInfoCharacterTable[2], funcs );
if r = fail then
return fail;
fi;
return r;
elif r.ConstructionInfoCharacterTable[1]
= "ConstructWreathSymmetric" then
id:= r.ConstructionInfoCharacterTable[3];
r:= CTblLib.Data.InvariantByRecursion(
r.ConstructionInfoCharacterTable[2], funcs );
if r = fail then
return fail;
fi;
return funcs.wreathsymmfunc( r, id );
elif r.ConstructionInfoCharacterTable[1]
= "ConstructCentralProduct" then
cen:= r.ConstructionInfoCharacterTable[3];
r:= List( r.ConstructionInfoCharacterTable[2],
l -> CTblLib.Data.InvariantByRecursion( l, funcs ) );
if fail in r then
return fail;
fi;
return Product( r, 1 ) / Length( cen );
fi;
fi;
fi;
return fail;
end;
CTblLib.Data.prepare:= function( attr )
CTblLib.Data.unload:= UserPreference( "CTblLib", "UnloadCTblLibFiles" );
SetUserPreference( "CTblLib", "UnloadCTblLibFiles", false );
end;
CTblLib.Data.cleanup:= function( attr )
SetUserPreference( "CTblLib", "UnloadCTblLibFiles", CTblLib.Data.unload );
Unbind( CTblLib.Data.unload );
end;
# a function that does nothing
CTblLib.Data.MyIdFunc:= function( arg ); end;
CTblLib.Data.TABLE_ACCESS_FUNCTIONS:= [
rec(),
rec(
LIBTABLE := rec( LOADSTATUS := rec(), clmelab := [], clmexsp := [] ),
# These functions are used in the data files.
GALOIS := CTblLib.Data.MyIdFunc,
TENSOR := CTblLib.Data.MyIdFunc,
ALF := CTblLib.Data.MyIdFunc,
ACM := CTblLib.Data.MyIdFunc,
ARC := CTblLib.Data.MyIdFunc,
ALN := CTblLib.Data.MyIdFunc,
MBT := CTblLib.Data.MyIdFunc,
MOT := CTblLib.Data.MyIdFunc,
) ];
CTblLib.Data.SaveTableAccessFunctions := function()
local name;
if CTblLib.Data.TABLE_ACCESS_FUNCTIONS[1] <> rec() then
Info( InfoDatabaseAttributeX, 1, "functions were already saved" );
return;
fi;
Info( InfoDatabaseAttributeX, 1, "before saving global variables" );
for name in RecNames( CTblLib.Data.TABLE_ACCESS_FUNCTIONS[2] ) do
if IsBoundGlobal( name ) then
if IsReadOnlyGlobal( name ) then
MakeReadWriteGlobal( name );
CTblLib.Data.TABLE_ACCESS_FUNCTIONS[1].( name ):=
[ ValueGlobal( name ), "readonly" ];
else
CTblLib.Data.TABLE_ACCESS_FUNCTIONS[1].( name ):=
[ ValueGlobal( name ) ];
fi;
UnbindGlobal( name );
fi;
ASS_GVAR( name, CTblLib.Data.TABLE_ACCESS_FUNCTIONS[2].( name ) );
od;
end;
CTblLib.Data.RestoreTableAccessFunctions := function()
local name;
if CTblLib.Data.TABLE_ACCESS_FUNCTIONS[1] = rec() then
Info( InfoDatabaseAttributeX, 1, "cannot restore without saving" );
return;
fi;
for name in RecNames( CTblLib.Data.TABLE_ACCESS_FUNCTIONS[2] ) do
UnbindGlobal( name );
if IsBound( CTblLib.Data.TABLE_ACCESS_FUNCTIONS[1].( name ) ) then
ASS_GVAR( name, CTblLib.Data.TABLE_ACCESS_FUNCTIONS[1].( name )[1] );
if Length( CTblLib.Data.TABLE_ACCESS_FUNCTIONS[1].( name ) ) = 2 then
MakeReadOnlyGlobal( name );
fi;
Unbind( CTblLib.Data.TABLE_ACCESS_FUNCTIONS[1].( name ) );
fi;
od;
Info( InfoDatabaseAttributeX, 1, "after restoring global variables" );
end;
#############################################################################
##
#F CTblLib.Data.ComputeCharacterTableInfoByScanningLibraryFiles(
#F <neededcomponents>, <providedcomponents>, <pairs> )
##
## If `CTblLib.Data.knownComponents' contains all entries of the list
## <neededcomponents> then nothing is done.
##
## Otherwise, ...
##
## The argument <A>pairs</A> must be a list of pairs
## <C>[ <A>nam</A>, <A>fun</A> ]</C>
## where <A>nam</A> is the name of a function to be reassigned during the
## reread process (such as <C>"MOT"</C>, <C>"ARC"</C>),
## and <A>fun</A> is the corresponding value.
##
CTblLib.Data.ComputeCharacterTableInfoByScanningLibraryFiles :=
function( neededcomponents, providedcomponents, pairs )
local filenames, pair, name;
# Check if we have to do something.
if IsBound( CTblLib.Data.knownComponents ) and
IsSubset( CTblLib.Data.knownComponents, neededcomponents ) then
return;
fi;
# Remember the names of all character table library files.
filenames:= LIBLIST.files;
# Disable the table library access.
CTblLib.Data.SaveTableAccessFunctions();
# Define appropriate access functions.
for pair in pairs do
ASS_GVAR( pair[1], pair[2] );
od;
# Clear the cache.
CTblLib.Data.CharacterTableInfo:= rec();
# Loop over the library files.
for name in filenames do
if name[1] = '/' then
# private extension
Read( Concatenation( name, ".tbl" ) );
else
ReadPackage( "ctbllib", Concatenation( "data/", name, ".tbl" ) );
fi;
od;
# Restore the ordinary table library access.
CTblLib.Data.RestoreTableAccessFunctions();
# Replace the component list for the next call.
CTblLib.Data.knownComponents:= providedcomponents;
end;
#############################################################################
##
## Create the database id enumerator to which the database attributes refer.
##
CTblLib.Data.IdEnumerator:= DatabaseIdEnumeratorX( rec(
identifiers:= Set( LIBLIST.firstnames_orig ),
isSorted:= true,
entry:= function( idenum, id ) return CharacterTable( id ); end,
version:= LIBLIST.lastupdated,
update:= function( idenum )
idenum.identifiers:= Set( AllCharacterTableNames() );
idenum.version:= LIBLIST.lastupdated;
return true;
end,
# isUpToDate:= idenum -> idenum.version = LIBLIST.lastupdated,
#T note: notifying a new table should then change the lastupdated field!
viewSort:= CTblLib.CompareAsNumbersAndNonnumbers,
align:= "lt",
) );
#############################################################################
##
#V CTblLib.Data.IdEnumerator.attributes.Size
##
AddSet( CTblLib.SupportedAttributes, "Size" );
DatabaseAttributeAddX( CTblLib.Data.IdEnumerator, rec(
identifier:= "Size",
description:= "sizes of GAP library character tables",
type:= "pairs",
name:= "Size",
datafile:= Filename( DirectoriesPackageLibrary( "ctbllib", "data" )[1],
"attr_size.json" ),
dataDefault:= fail,
isSorted:= true,
neededAttributes:= [],
prepareAttributeComputation:= function( attr )
CTblLib.Data.prepare( attr );
CTblLib.Data.ComputeCharacterTableInfoByScanningLibraryFiles(
[ "ConstructionInfoCharacterTable", "SizesCentralizers" ],
[ "ConstructionInfoCharacterTable", "InfoText", "Maxes",
"SizesCentralizers" ],
[ [ "MOT", function( arg )
local record;
record:= rec( InfoText:= arg[2],
SizesCentralizers:= arg[3] );
if IsBound( arg[7] ) then
record.ConstructionInfoCharacterTable:= arg[7];
fi;
CTblLib.Data.CharacterTableInfo.( arg[1] ):= record;
end ],
[ "ARC", function( arg )
if arg[2] = "maxes" then
CTblLib.Data.CharacterTableInfo.( arg[1] ).Maxes:= arg[3];
fi;
end ] ] );
end,
cleanupAfterAttributeComputation:= CTblLib.Data.cleanup,
create:= function( attr, id )
local r, other;
if IsBound( CTblLib.Data.CharacterTableInfo ) and
IsBound( CTblLib.Data.CharacterTableInfo.( id ) ) then
r:= CTblLib.Data.InvariantByRecursion( [ id ],
rec( gentablefunc:= function( gentable, list)
return gentable.size( list[2] );
end,
cheaptest:= function( r )
if IsList( r.SizesCentralizers ) then
return r.SizesCentralizers[1];
fi;
return fail;
end,
wreathsymmfunc:= function( subval, n )
return subval^n * Factorial( n );
end,
) );
else
r:= fail;
fi;
if r = fail then
Info( InfoDatabaseAttributeX, 1,
"hard test for Size computation of ", id );
return Size( CharacterTable( id ) );
else
return r;
fi;
end,
string:= entry -> CTblLib.AttrDataString( entry, fail, false ),
check:= ReturnTrue,
align:= "t",
viewLabel:= "size",
viewSort:= CTblLib.CompareLenLex,
categoryValue:= res -> Concatenation( "size = ", String( res ) ),
sortParameters:= [ "add counter on categorizing", "yes" ],
widthCol:= 25,
) );
#############################################################################
##
#V CTblLib.Data.IdEnumerator.attributes.IdentifierOfMainTable
##
DatabaseAttributeAddX( CTblLib.Data.IdEnumerator, rec(
identifier:= "IdentifierOfMainTable",
description:= "identifier of the table for which the current table is a duplicate",
type:= "pairs",
name:= "IdentifierOfMainTable",
datafile:= Filename( DirectoriesPackageLibrary( "ctbllib", "data" )[1],
"attr_main.json" ),
dataDefault:= fail,
isSorted:= true,
neededAttributes:= [],
prepareAttributeComputation:= function( attr )
CTblLib.Data.prepare( attr );
CTblLib.Data.ComputeCharacterTableInfoByScanningLibraryFiles(
[ "ConstructionInfoCharacterTable" ],
[ "ConstructionInfoCharacterTable", "InfoText", "Maxes",
"SizesCentralizers" ],
[ [ "MOT", function( arg )
local record;
record:= rec( InfoText:= arg[2],
SizesCentralizers:= arg[3] );
if IsBound( arg[7] ) then
record.ConstructionInfoCharacterTable:= arg[7];
fi;
CTblLib.Data.CharacterTableInfo.( arg[1] ):= record;
end ],
[ "ARC", function( arg )
if arg[2] = "maxes" then
CTblLib.Data.CharacterTableInfo.( arg[1] ).Maxes:= arg[3];
fi;
end ] ] );
end,
cleanupAfterAttributeComputation:= CTblLib.Data.cleanup,
create:= function( attr, id )
local t, info;
if IsBound( CTblLib.Data.CharacterTableInfo ) and
IsBound( CTblLib.Data.CharacterTableInfo.( id ) ) then
t:= CTblLib.Data.CharacterTableInfo.( id );
if IsBound( t.ConstructionInfoCharacterTable ) then
info:= t.ConstructionInfoCharacterTable;
fi;
else
t:= CharacterTable( id );
if HasConstructionInfoCharacterTable( t ) then
info:= ConstructionInfoCharacterTable( t );
fi;
fi;
if IsBound( info ) and IsList( info )
and info[1] = "ConstructPermuted" then
info:= info[2];
if Length( info ) = 1 then
# permuted library table
return info[1];
fi;
fi;
return fail;
end,
string:= entry -> CTblLib.AttrDataString( entry, fail, false ),
check:= ReturnTrue,
align:= "t",
viewLabel:= "main",
categoryValue:= res -> Concatenation( "main table = ", String( res ) ),
sortParameters:= [ "add counter on categorizing", "yes" ],
widthCol:= 12,
) );
#############################################################################
##
#V CTblLib.Data.IdEnumerator.attributes.IsDuplicateTable
##
AddSet( CTblLib.SupportedAttributes, "IsDuplicateTable" );
DatabaseAttributeAddX( CTblLib.Data.IdEnumerator, rec(
identifier:= "IsDuplicateTable",
description:= "are GAP library character tables duplicates of others",
type:= "values",
name:= "IsDuplicateTable",
align:= "c",
categoryValue:= function( val )
if val then
return "duplicate";
else
return "not duplicate";
fi;
end,
neededAttributes:= [ "IdentifierOfMainTable" ],
create:= function( attr, id )
local main;
main:= attr.idenumerator.attributes.IdentifierOfMainTable;
return IsString( main.attributeValue( main, id ) );
end,
viewValue:= x -> CTblLib.ReplacedEntry( x, [ false, true ],
[ "-", "+" ] ),
viewLabel:= "duplicate?",
sortParameters:= [ "add counter on categorizing", "yes" ],
) );
#############################################################################
##
#V CTblLib.Data.IdEnumerator.attributes.IdentifiersOfDuplicateTables
##
AddSet( CTblLib.SupportedAttributes, "IdentifiersOfDuplicateTables" );
DatabaseAttributeAddX( CTblLib.Data.IdEnumerator, rec(
identifier:= "IdentifiersOfDuplicateTables",
description:= "identifiers of GAP library character tables that are duplicates",
type:= "pairs",
name:= "IdentifiersOfDuplicateTables",
dataDefault:= [],
isSorted:= true,
neededAttributes:= [ "IdentifierOfMainTable" ],
prepareAttributeComputation:= function( attr )
local main, id, mainid;
main:= attr.idenumerator.attributes.IdentifierOfMainTable;
CTblLib.IdentifiersOfDuplicateTables:= rec();
for id in attr.idenumerator.identifiers do
mainid:= main.attributeValue( main, id );
if mainid <> fail then
if not IsBound( CTblLib.IdentifiersOfDuplicateTables.( mainid ) ) then
CTblLib.IdentifiersOfDuplicateTables.( mainid ):= [ id ];
else
AddSet( CTblLib.IdentifiersOfDuplicateTables.( mainid ), id );
fi;
fi;
od;
end,
cleanupAfterAttributeComputation:= function( attr )
Unbind( CTblLib.IdentifiersOfDuplicateTables );
end,
create:= function( attr, id )
if IsBound( CTblLib.IdentifiersOfDuplicateTables.( id ) ) then
return CTblLib.IdentifiersOfDuplicateTables.( id );
else
return [];
fi;
end,
viewLabel:= "duplicates",
) );
#############################################################################
##
#V CTblLib.Data.IdEnumerator.attributes.NrConjugacyClasses
##
AddSet( CTblLib.SupportedAttributes, "NrConjugacyClasses" );
DatabaseAttributeAddX( CTblLib.Data.IdEnumerator, rec(
identifier:= "NrConjugacyClasses",
description:= "class numbers of GAP library character tables",
type:= "pairs",
name:= "NrConjugacyClasses",
datafile:= Filename( DirectoriesPackageLibrary( "ctbllib", "data" )[1],
"attr_nccl.json" ),
dataDefault:= fail,
isSorted:= true,
neededAttributes:= [],
prepareAttributeComputation:= function( attr )
CTblLib.Data.prepare( attr );
CTblLib.Data.ComputeCharacterTableInfoByScanningLibraryFiles(
[ "ConstructionInfoCharacterTable", "SizesCentralizers" ],
[ "ConstructionInfoCharacterTable", "InfoText", "Maxes",
"SizesCentralizers" ],
[ [ "MOT", function( arg )
local record;
record:= rec( InfoText:= arg[2],
SizesCentralizers:= arg[3] );
if IsBound( arg[7] ) then
record.ConstructionInfoCharacterTable:= arg[7];
fi;
CTblLib.Data.CharacterTableInfo.( arg[1] ):= record;
end ],
[ "ARC", function( arg )
if arg[2] = "maxes" then
CTblLib.Data.CharacterTableInfo.( arg[1] ).Maxes:= arg[3];
fi;
end ] ] );
end,
cleanupAfterAttributeComputation:= CTblLib.Data.cleanup,
create:= function( attr, id )
local r, other;
if IsBound( CTblLib.Data.CharacterTableInfo ) and
IsBound( CTblLib.Data.CharacterTableInfo.( id ) ) then
r:= CTblLib.Data.InvariantByRecursion( [ id ],
rec( gentablefunc:= function( gentable, list )
return Sum( List( gentable.classparam,
p -> Length( p( list[2] ) ) ), 0 );
end,
cheaptest:= function( r )
if IsList( r.SizesCentralizers ) then
return Length( r.SizesCentralizers );
fi;
return fail;
end,
wreathsymmfunc:= function( subval, n )
return NrPartitionTuples( n, subval );
end,
) );
else
r:= fail;
fi;
if r = fail then
return NrConjugacyClasses( CharacterTable( id ) );
else
return r;
fi;
end,
string:= entry -> CTblLib.AttrDataString( entry, fail, false ),
check:= ReturnTrue,
align:= "t",
viewLabel:= "nccl",
viewSort:= CTblLib.CompareLenLex,
categoryValue:= res -> Concatenation( "nccl = ", String( res ) ),
sortParameters:= [ "add counter on categorizing", "yes" ],
widthCol:= 4,
) );
#############################################################################
##
#V CTblLib.Data.IdEnumerator.attributes.InfoText
##
## This is used for creating the group overviews
## (plain strings or Browse tables or HTML files).
##
AddSet( CTblLib.SupportedAttributes, "InfoText" );
DatabaseAttributeAddX( CTblLib.Data.IdEnumerator, rec(
identifier:= "InfoText",
description:= "info text of GAP library character tables",
type:= "pairs",
name:= "InfoText",
datafile:= Filename( DirectoriesPackageLibrary( "ctbllib", "data" )[1],
"attr_text.json" ),
dataDefault:= "",
isSorted:= true,
neededAttributes:= [],
prepareAttributeComputation:= function( attr )
CTblLib.Data.prepare( attr );
CTblLib.Data.ComputeCharacterTableInfoByScanningLibraryFiles(
[ "ConstructionInfoCharacterTable", "InfoText" ],
[ "ConstructionInfoCharacterTable", "InfoText", "Maxes",
"SizesCentralizers" ],
[ [ "MOT", function( arg )
local record;
record:= rec( InfoText:= arg[2],
SizesCentralizers:= arg[3] );
if IsBound( arg[7] ) then
record.ConstructionInfoCharacterTable:= arg[7];
fi;
CTblLib.Data.CharacterTableInfo.( arg[1] ):= record;
end ],
[ "ARC", function( arg )
if arg[2] = "maxes" then
CTblLib.Data.CharacterTableInfo.( arg[1] ).Maxes:= arg[3];
fi;
end ] ] );
end,
cleanupAfterAttributeComputation:= CTblLib.Data.cleanup,
create:= function( attr, id )
local info, r;
if IsBound( CTblLib.Data.CharacterTableInfo ) and
IsBound( CTblLib.Data.CharacterTableInfo.( id ) ) then
info:= LibInfoCharacterTable( id );
id:= info.firstName;
if info.fileName = "ctgeneri" then
# Evaluate only part of the generic table.
return CharacterTable( info.firstName ).text;
else
r:= CTblLib.Data.CharacterTableInfo.( id );
if IsList( r.InfoText ) then
return Concatenation( r.InfoText );
fi;
return "";
fi;
else
r:= CharacterTable( id );
if HasInfoText( r ) then
return InfoText( r );
else
return "";
fi;
fi;
end,
string:= entry -> CTblLib.AttrDataString( entry, "", false ),
check:= ReturnTrue,
align:= "t",
viewLabel:= "info",
widthCol:= 20,
) );
#############################################################################
##
#V CTblLib.Data.IdEnumerator.attributes.Maxes
##
AddSet( CTblLib.SupportedAttributes, "Maxes" );
DatabaseAttributeAddX( CTblLib.Data.IdEnumerator, rec(
identifier:= "Maxes",
description:= "maxes lists of GAP library character tables",
type:= "pairs",
name:= "Maxes",
datafile:= Filename( DirectoriesPackageLibrary( "ctbllib", "data" )[1],
"attr_maxes.json" ),
dataDefault:= fail,
isSorted:= true,
neededAttributes:= [],
prepareAttributeComputation:= function( attr )
CTblLib.Data.prepare( attr );
CTblLib.Data.ComputeCharacterTableInfoByScanningLibraryFiles(
[ "Maxes" ],
[ "ConstructionInfoCharacterTable", "InfoText", "Maxes",
"SizesCentralizers" ],
[ [ "MOT", function( arg )
local record;
record:= rec( InfoText:= arg[2],
SizesCentralizers:= arg[3] );
if IsBound( arg[7] ) then
record.ConstructionInfoCharacterTable:= arg[7];
fi;
CTblLib.Data.CharacterTableInfo.( arg[1] ):= record;
end ],
[ "ARC", function( arg )
if arg[2] = "maxes" then
CTblLib.Data.CharacterTableInfo.( arg[1] ).Maxes:= arg[3];
fi;
end ] ] );
end,
cleanupAfterAttributeComputation:= CTblLib.Data.cleanup,
create:= function( attr, id )
if IsBound( CTblLib.Data.CharacterTableInfo ) and
IsBound( CTblLib.Data.CharacterTableInfo.( id ) ) and
IsBound( CTblLib.Data.CharacterTableInfo.( id ).Maxes ) then
return CTblLib.Data.CharacterTableInfo.( id ).Maxes;
else
return fail;
fi;
end,
string:= entry -> CTblLib.AttrDataString( entry, fail, false ),
check:= ReturnTrue,
align:= "t",
viewLabel:= "maxes",
widthCol:= 25,
) );
#############################################################################
##
#V CTblLib.Data.IdEnumerator.attributes.NamesOfFusionSources
##
AddSet( CTblLib.SupportedAttributes, "NamesOfFusionSources" );
DatabaseAttributeAddX( CTblLib.Data.IdEnumerator, rec(
identifier:= "NamesOfFusionSources",
description:= "fusions from other character tables to the given one",
type:= "values",
name:= "NamesOfFusionSources",
align:= "tl",
create:= function( attr, id )
local pos;
pos:= Position( LIBLIST.firstnames, id );
#T firstnames is not sorted, better use allnames & position?
if pos <> fail then
return LIBLIST.fusionsource[ pos ];
fi;
return [];
end,
version:= CTblLib.Data.IdEnumerator.version,
viewValue:= x -> rec( rows:= x, align:= "tl" ),
viewLabel:= "fusions -> G",
categoryValue:= function( val )
if IsEmpty( val ) then
return "(no fusions to these tables)";
fi;
return List( val, x -> Concatenation( "fusions from ", x ) );
end,
sortParameters:= [ "add counter on categorizing", "yes",
"split rows on categorizing", "yes" ],
) );
#############################################################################
##
#V CTblLib.Data.IdEnumerator.attributes.FusionsTo
##
DatabaseAttributeAddX( CTblLib.Data.IdEnumerator, rec(
identifier:= "FusionsTo",
description:= "fusions from the given character table to other ones",
type:= "values",
align:= "tl",
categoryValue:= function( val )
if IsEmpty( val ) then
return "(no fusions from these tables)";
fi;
return List( val, x -> Concatenation( "fusions to ", x ) );
end,
prepareAttributeComputation:= function( attr )
local i, nam, src;
CTblLib.Data.sortedfirstnames:= ShallowCopy( LIBLIST.firstnames );
CTblLib.Data.position:= [ 1 .. Length( LIBLIST.firstnames ) ];
SortParallel( CTblLib.Data.sortedfirstnames, CTblLib.Data.position );
CTblLib.Data.FusionInfo:= List( [ 1 .. Length( LIBLIST.firstnames ) ],
i -> [] );
for i in [ 1 .. Length( LIBLIST.firstnames ) ] do
nam:= LIBLIST.firstnames[i];
for src in LIBLIST.fusionsource[i] do
Add( CTblLib.Data.FusionInfo[ PositionSet(
CTblLib.Data.sortedfirstnames, src ) ], nam );
od;
od;
end,
create:= function( attr, id )
local pos;
pos:= PositionSet( CTblLib.Data.sortedfirstnames, id );
if pos <> fail then
return CTblLib.Data.FusionInfo[ pos ];
fi;
return [];
end,
viewValue:= x -> rec( rows:= x, align:= "tl" ),
viewLabel:= "fusions G ->",
sortParameters:= [ "add counter on categorizing", "yes",
"split rows on categorizing", "yes" ],
) );
#############################################################################
##
#V CTblLib.Data.IdEnumerator.attributes.basic
##
Add( CTblLib.Data.attributesRelevantForGroupInfoForCharacterTable, "basic" );
DatabaseAttributeAddX( CTblLib.Data.IdEnumerator, rec(
identifier:= "basic",
description:= "mapping between CTblLib and GAP's basic groups libraries",
type:= "pairs",
datafile:= Filename( DirectoriesPackageLibrary( "ctbllib", "data" )[1],
"grp_basic.json" ),
dataDefault:= [],
isSorted:= true,
reverseEval:= function( attr, info )
local data, entry;
if info[1] in [ "AlternatingGroup", "CyclicGroup",
"DihedralGroup", "MathieuGroup", "POmega", "PSL",
"PSU", "PSp", "ReeGroup", "SuzukiGroup",
"SymmetricGroup" ] then
if not IsBound( attr.data ) then
Read( attr.datafile );
fi;
for data in [ attr.data.automatic, attr.data.nonautomatic ] do
for entry in data do
if info in entry[2] then
return entry[1];
fi;
od;
od;
fi;
return fail;
end,
neededAttributes:= [ "IsDuplicateTable", "IdentifiersOfDuplicateTables" ],
prepareAttributeComputation:= function( attr )
local i;
CTblLib.Data.invposition:= InverseMap( LIBLIST.position );
for i in [ 1 .. Length( CTblLib.Data.invposition ) ] do
if IsInt( CTblLib.Data.invposition[i] ) then
CTblLib.Data.invposition[i]:= [ CTblLib.Data.invposition[i] ];
fi;
od;
CTblLib.Data.attrvalues_basic:= rec();
CTblLib.Data.prepare( attr );
end,
cleanupAfterAttributeComputation:= function( attr )
Unbind( CTblLib.Data.invposition );
Unbind( CTblLib.Data.attrvalues_basic );
CTblLib.Data.cleanup( attr );
end,
create:= function( attr, id )
local main, mainid, result, tbl, type, r, nsg, simp, cen, ext, explicit,
entry, name;
# For duplicate tables, take (and cache) the result for the main table.
main:= attr.idenumerator.attributes.IdentifierOfMainTable;
mainid:= main.attributeValue( main, id );
if mainid <> fail then
id:= mainid;
fi;
if IsBound( CTblLib.Data.attrvalues_basic ) and
IsBound( CTblLib.Data.attrvalues_basic.( id ) ) then
return CTblLib.Data.attrvalues_basic.( id );
fi;
# Now we know that we have to work.
result:= [];
tbl:= CharacterTable( id );
if IsSimpleCharacterTable( tbl ) then
type:= IsomorphismTypeInfoFiniteSimpleGroup( tbl );
if type.series = "A" then
Add( result, [ "AlternatingGroup", [ type.parameter ] ] );
if type.parameter = 5 then
Add( result, [ "PSL", [ 2, 4 ] ] );
Add( result, [ "PSL", [ 2, 5 ] ] );
elif type.parameter = 6 then
Add( result, [ "PSL", [ 2, 9 ] ] );
elif type.parameter = 8 then
Add( result, [ "PSL", [ 4, 2 ] ] );
fi;
elif type.series = "B" then
Add( result,
[ "POmega", [ 2 * type.parameter[1] + 1, type.parameter[2] ] ] );
if IsEvenInt( type.parameter[2] ) or type.parameter[1] = 2 then
Add( result,
[ "PSp", [ 2 * type.parameter[1], type.parameter[2] ] ] );
fi;
if type.parameter = [ 2, 3 ] then
Add( result, [ "PSU", [ 4, 2 ] ] );
Add( result, [ "POmega", [ -1, 6, 2 ] ] );
fi;
elif type.series = "C" then
Add( result,
[ "PSp", [ 2 * type.parameter[1], type.parameter[2] ] ] );
if type.parameter[1] = 2 then
Add( result, [ "POmega", [ 5, type.parameter[2] ] ] );
fi;
elif type.series = "D" then
Add( result,
[ "POmega", [ 1, 2 * type.parameter[1], type.parameter[2] ] ] );
elif type.series = "L" then
Add( result, [ "PSL", type.parameter ] );
if type.parameter[1] = 2 then
Add( result, [ "POmega", [ 3, type.parameter[2] ] ] );
Add( result, [ "PSp", type.parameter ] );
Add( result, [ "PSU", type.parameter ] );
r:= RootInt( type.parameter[2] );
if r^2 = type.parameter[2] then
Add( result, [ "POmega", [ -1, 4, r ] ] );
fi;
if type.parameter[2] = 7 then
Add( result, [ "PSL", [ 3, 2 ] ] );
fi;
elif type.parameter[1] = 4 then
Add( result, [ "POmega", [ 1, 6, type.parameter[2] ] ] );
fi;
elif type.series = "G" then
Add( result, [ "SimpleGroup", [ "G", 2, type.parameter ] ] );
elif type.series = "2A" then
Add( result,
[ "PSU", [ type.parameter[1] + 1, type.parameter[2] ] ] );
if type.parameter[1] = 3 then
Add( result, [ "POmega", [ -1, 6, type.parameter[2] ] ] );
fi;
elif type.series = "2B" then
Add( result, [ "SuzukiGroup", [ type.parameter ] ] );
elif type.series = "2D" then
Add( result,
[ "POmega", [ -1, 2 * type.parameter[1], type.parameter[2] ] ] );
elif type.series = "2G" then
Add( result, [ "ReeGroup", [ type.parameter ] ] );
elif type.series = "3D" then
Add( result, [ "SimpleGroup", [ "3D", 4, type.parameter ] ] );
elif type.series = "Spor" then
if type.name = "M(11)" then
Add( result, [ "MathieuGroup", [ 11 ] ] );
elif type.name = "M(12)" then
Add( result, [ "MathieuGroup", [ 12 ] ] );
elif type.name = "M(22)" then
Add( result, [ "MathieuGroup", [ 22 ] ] );
elif type.name = "M(23)" then
Add( result, [ "MathieuGroup", [ 23 ] ] );
elif type.name = "M(24)" then
Add( result, [ "MathieuGroup", [ 24 ] ] );
fi;
#T more series of simple groups?
fi;
elif IsPerfectCharacterTable( tbl ) then
# Detect nonsolvable groups SL(2,q), for odd q.
cen:= ClassPositionsOfCentre( tbl );
if Size( cen ) = 2 then
simp:= tbl / cen;
if IsSimpleCharacterTable( simp ) then
type:= IsomorphismTypeInfoFiniteSimpleGroup( simp );
if type.series = "L" and type.parameter[1] = 2 then
Add( result, [ "SL", type.parameter ] );
fi;
fi;
fi;
#T detect DoubleCoverOfAlternatingGroup:
#T modulo central inv., a simple group of type An, except in small cases
fi;
# Detect nonsolvable symmetric groups
#T Can this be done without the character table of the socle?
# and automorphism groups of simple groups.
if IsAlmostSimpleCharacterTable( tbl ) and
not IsSimpleCharacterTable( tbl ) then
# There is a unique minimal normal subgroup.
nsg:= ClassPositionsOfMinimalNormalSubgroups( tbl )[1];
simp:= CTblLib.Data.CharacterTablesOfNormalSubgroupWithGivenImage(
tbl, nsg );
if not IsEmpty( simp ) then
simp:= simp[1][1];
type:= IsomorphismTypeInfoFiniteSimpleGroup( simp );
if type.series = "A" then
if type.parameter <> 6 then
Add( result, [ "SymmetricGroup", [ type.parameter ] ] );
elif not 8 in OrdersClassRepresentatives( tbl ) then
# 'A6.2_1'
Add( result, [ "SymmetricGroup", [ type.parameter ] ] );
Add( result, [ "PSp", [ 4, 2 ] ] );
Add( result, [ "POmega", [ 5, 2 ] ] );
elif not 10 in OrdersClassRepresentatives( tbl ) then
# 'A6.2_3'
Add( result, [ "MathieuGroup", [ 10 ] ] );
elif NrConjugacyClasses( tbl ) = 11 then
# 'A6.2_2'
Add( result, [ "PGL", [ 2, 9 ] ] );
Add( result, [ "PGU", [ 2, 9 ] ] );
else
# 'A6.2^2'
Add( result, [ "PGammaL", [ 2, 9 ] ] );
fi;
fi;
if HasExtensionInfoCharacterTable( simp ) then
ext:= ExtensionInfoCharacterTable( simp );
if ext[2] <> "" then
ext:= CharacterTable( Concatenation( Identifier( simp ),
".", ext[2] ) );
if ext <> fail and Size( ext ) = Size( tbl ) then
Add( result, [ "Aut", [ Identifier( simp ) ] ] );
fi;
fi;
fi;
# Detect some projective and semilinear groups.
# (Note that we give no abstract description.)
explicit:= [
["L2(16).4",[["PGammaL",[2,16]],["SigmaL",[2,16]],["PSigmaL",[2,16]]]],
["L2(25).2^2",[["PGammaL",[2,25]]]],
["L2(25).2_1",[["PGL",[2,25]]]],
["L2(25).2_2",[["PSigmaL",[2,25]]]],
["L2(27).2",[["PGL",[2,27]]]],
["L2(27).3",[["PSigmaL",[2,27]]]],
["L2(27).6",[["PGammaL",[2,27]]]],
["L2(32).5",[["PGammaL",[2,32]],["SigmaL",[2,32]],["PSigmaL",[2,32]]]],
["L2(49).2^2",[["PGammaL",[2,49]]]],
["L2(49).2_1",[["PGL",[2,49]]]],
["L2(49).2_2",[["PSigmaL",[2,49]]]],
["L2(64).6",[["PGammaL",[2,64]]]],
["L2(81).(2x4)",[["PGammaL",[2,81]]]],
["L2(81).2_2",[["PGL",[2,81]]]],
["L2(81).4_1",[["PSigmaL",[2,81]]]],
["L3(7).3",[["PGL",[3,7]]]],
["L3(8).3",[["PGammaL",[3,8]],["SigmaL",[3,8]],["PSigmaL",[3,8]]]],
["L3(9).2_2",[["PGammaL",[3,9]],["SigmaL",[3,9]],["PSigmaL",[3,9]]]],
["L4(3).2_1",[["PGL",[4,3]]]],
["L4(3).2_2",[["PGO",[+1,6,3]]]],
["L4(4).2_1",[["PSigmaL",[4,4]]]],
["O8+(3).2_1",[["PSO",[+1,8,3]]]],
["O8+(3).(2^2)_{122}",[["PGO",[+1,8,3]]]],
["O8-(3).2_1",[["PGO",[-1,8,3]]]],
["S4(5).2",[["SO",[5,5]]]],
["U3(11).3",[["PGU",[3,11]]]],
["U3(5).3",[["PGU",[3,5]]]],
["U3(8).3^2",[["PGammaU",[3,8]]]],
["U3(8).3_1",[["PSigmaU",[3,8]]]],
["U3(8).3_2",[["PGU",[3,8]]]],
["U4(2).2",[["PSO",[5,3]]]],
["U4(3).2_1",[["PSO",[-1,6,3]]]],
["U4(5).2_2",[["PGU",[4,5]]]],
];
entry:= First( explicit, x -> Identifier( tbl ) = x[1] );
if entry <> fail then
Append( result, entry[2] );
fi;
fi;
fi;
# Detect some AGLs.
# (Note that we give no abstract description.)
explicit:= [
["S3",[["AGL",[1,3]]]],
["a4",[["AGL",[1,4]]]],
["5:4",[["AGL",[1,5]]]],
["7:6",[["AGL",[1,7]]]],
["2^3:7",[["AGL",[1,8]]]],
["11:10",[["AGL",[1,11]]]],
["13:12",[["AGL",[1,13]]]],
["17:16",[["AGL",[1,17]]]],
["19:18",[["AGL",[1,19]]]],
["23:22",[["AGL",[1,23]]]],
["frob",[["AGL",[1,29]]]],
["31:30",[["AGL",[1,31]]]],
["41:40",[["AGL",[1,41]]]],
["s4",[["AGL",[2,2]]]],
["3^2.2.S4",[["AGL",[2,3]]]],
["j3m4",[["AGL",[2,4]]]],
["5^2:4s5",[["AGL",[2,5]]]],
["2^6:(7xL2(8))",[["AGL",[2,8]]]],
["3^4:GL2(9)",[["AGL",[2,9]]]],
["11^2:(5x2L2(11).2)",[["AGL",[2,11]]]],
["2^3:sl(3,2)",[["AGL",[3,2]]]],
["2^4:a8",[["AGL",[4,2]]]],
["2^5:L5(2)",[["AGL",[5,2]]]],
["2^6:L6(2)",[["AGL",[6,2]]]],
["P1L82",[["AGL",[7,2]]]],
];
entry:= First( explicit, x -> Identifier( tbl ) = x[1] );
if entry <> fail then
Append( result, entry[2] );
fi;
# Detect some solvable groups.
if Size( tbl ) = 12 and NrConjugacyClasses( tbl ) = 4 then
Add( result, [ "AlternatingGroup", [ 4 ] ] );
Add( result, [ "PSL", [ 2, 3 ] ] );
Add( result, [ "PSU", [ 2, 3 ] ] );
Add( result, [ "PSp", [ 2, 3 ] ] );
elif Size( tbl ) = 20 and NrConjugacyClasses( tbl ) = 5 then
Add( result, [ "SuzukiGroup", [ 2 ] ] );
elif Size( tbl ) = 24 and NrConjugacyClasses( tbl ) = 5 then
Add( result, [ "SymmetricGroup", [ 4 ] ] );
elif Size( tbl ) = 72 and NrConjugacyClasses( tbl ) = 6 then
Add( result, [ "MathieuGroup", [ 9 ] ] );
Add( result, [ "PSU", [ 3, 2 ] ] );
fi;
if IsCyclic( tbl ) then
Add( result, [ "CyclicGroup", [ Size( tbl ) ] ] );
if Size( tbl ) = 2 then
Add( result, [ "SymmetricGroup", [ 2 ] ] );
elif Size( tbl ) = 3 then
Add( result, [ "AlternatingGroup", [ 3 ] ] );
fi;
fi;
if IsDihedralCharacterTable( tbl ) then
Add( result, [ "DihedralGroup", [ Size( tbl ) ] ] );
if Size( tbl ) = 6 then
Add( result, [ "SymmetricGroup", [ 3 ] ] );
Add( result, [ "PSL", [ 2, 2 ] ] );
Add( result, [ "PSU", [ 2, 2 ] ] );
Add( result, [ "PSp", [ 2, 2 ] ] );
fi;
fi;
if IsEmpty( result ) then
result:= attr.dataDefault;
else
result:= Set( result );
fi;
# Cache the result.
CTblLib.Data.attrvalues_basic.( id ):= result;
return result;
end,
string:= entry -> CTblLib.AttrDataString( entry, [], false ),
check:= ReturnTrue,
) );
#############################################################################
##
#V CTblLib.Data.IdEnumerator.attributes.perf
##
Add( CTblLib.Data.attributesRelevantForGroupInfoForCharacterTable, "perf" );
DatabaseAttributeAddX( CTblLib.Data.IdEnumerator, rec(
identifier:= "perf",
description:= "mapping between CTblLib and GAP's library of perfect groups",
type:= "pairs",
datafile:= Filename( DirectoriesPackageLibrary( "ctbllib", "data" )[1],
"grp_perf.json" ),
dataDefault:= [],
isSorted:= true,
eval:= function( attr, l )
return List( l, val -> [ "PerfectGroup", val ] );
end,
reverseEval:= function( attr, info )
local data, entry;
if info[1] = "PerfectGroup" then
if not IsBound( attr.data ) then
Read( attr.datafile );
fi;
for data in [ attr.data.automatic, attr.data.nonautomatic ] do
for entry in data do
if info[2] in entry[2] then
return entry[1];
fi;
od;
od;
fi;
return fail;
end,
neededAttributes:= [ "IsDuplicateTable", "IdentifiersOfDuplicateTables" ],
prepareAttributeComputation:= function( attr )
local i;
CTblLib.Data.invposition:= InverseMap( LIBLIST.position );
for i in [ 1 .. Length( CTblLib.Data.invposition ) ] do
if IsInt( CTblLib.Data.invposition[i] ) then
CTblLib.Data.invposition[i]:= [ CTblLib.Data.invposition[i] ];
fi;
od;
CTblLib.Data.attrvalues_perf:= rec();
end,
cleanupAfterAttributeComputation:= function( attr )
Unbind( CTblLib.Data.invposition );
Unbind( CTblLib.Data.attrvalues_perf );
end,
create:= function( attr, id )
local main, mainid, tbl, result, n, nr, pos, type, i, G;
# For duplicate tables, take (and cache) the result for the main table.
main:= attr.idenumerator.attributes.IdentifierOfMainTable;
mainid:= main.attributeValue( main, id );
if mainid <> fail then
id:= mainid;
fi;
if IsBound( CTblLib.Data.attrvalues_perf ) and
IsBound( CTblLib.Data.attrvalues_perf.( id ) ) then
return CTblLib.Data.attrvalues_perf.( id );
fi;
# Now we know that we have to work.
tbl:= CharacterTable( id );
result:= [];
if IsPerfectCharacterTable( tbl ) then
n:= Size( tbl );
nr:= NumberPerfectLibraryGroups( n );
if IsPosInt( nr ) then
if NumberPerfectGroups( n ) = 1 then
# If there is only one perfect group of this order
# (and we believe this) then we assign the table name to it.
pos:= 1;
elif IsSimpleCharacterTable( tbl ) then
# If the table is simple then compare isomorphism types.
type:= IsomorphismTypeInfoFiniteSimpleGroup( tbl );
for i in [ 1 .. nr ] do
G:= PerfectGroup( IsPermGroup, n, i );
if IsSimpleGroup( G ) and
IsomorphismTypeInfoFiniteSimpleGroup( G ).name = type.name then
pos:= i;
break;
fi;
od;
else
# Do the hard test.
#T perhaps compare the lattice of normal subgroups first?
for i in [ 1 .. nr ] do
G:= PerfectGroup( IsPermGroup, n, i );
if NrConjugacyClasses( G ) = NrConjugacyClasses( tbl ) and
IsRecord( TransformingPermutationsCharacterTables(
CharacterTable( G ), tbl ) ) then
pos:= i;
break;
fi;
od;
fi;
Add( result, [ n, pos ] );
fi;
fi;
if IsEmpty( result ) then
result:= attr.dataDefault;
else
result:= Set( result );
fi;
# Cache the result.
CTblLib.Data.attrvalues_perf.( id ):= result;
return result;
end,
string:= entry -> CTblLib.AttrDataString( entry, [], false ),
check:= ReturnTrue,
) );
#############################################################################
##
#V CTblLib.Data.IdEnumerator.attributes.prim
##
Add( CTblLib.Data.attributesRelevantForGroupInfoForCharacterTable, "prim" );
# The library of primitive groups may be not (yet) loaded.
if IsBound( PRIMRANGE ) then
CTblLib.MAXPRIMRANGE:= PRIMRANGE[ Length( PRIMRANGE ) ];
else
CTblLib.MAXPRIMRANGE:= 2499;
fi;
DatabaseAttributeAddX( CTblLib.Data.IdEnumerator, rec(
identifier:= "prim",
description:= "mapping between CTblLib and GAP's library of prim. groups",
type:= "pairs",
datafile:= Filename( DirectoriesPackageLibrary( "ctbllib", "data" )[1],
"grp_prim.json" ),
dataDefault:= [ "no information", [] ],
isSorted:= true,
eval:= function( attr, l )
return List( l[2], val -> [ "PrimitiveGroup", val ] );
end,
reverseEval:= function( attr, info )
local data, entry;
if info[1] = "PrimitiveGroup" then
if not IsBound( attr.data ) then
Read( attr.datafile );
fi;
for data in [ attr.data.automatic, attr.data.nonautomatic ] do
for entry in data do
if info[2] in entry[2][2] then
return entry[1];
fi;
od;
od;
fi;
return fail;
end,
neededAttributes:= [ "IsDuplicateTable", "IdentifiersOfDuplicateTables" ],
prepareAttributeComputation:= function( attr )
local result, i;
CTblLib.Data.prepare( attr );
CTblLib.Data.invposition:= InverseMap( LIBLIST.position );
for i in [ 1 .. Length( CTblLib.Data.invposition ) ] do
if IsInt( CTblLib.Data.invposition[i] ) then
CTblLib.Data.invposition[i]:= [ CTblLib.Data.invposition[i] ];
fi;
od;
CTblLib.Data.attrvalues_prim:= rec();
end,
cleanupAfterAttributeComputation:= function( attr )
CTblLib.Data.cleanup( attr );
Unbind( CTblLib.Data.invposition );
Unbind( CTblLib.Data.attrvalues_prim );
end,
create:= function( attr, id )
local main, mainid, tbl, nsg, nsgsizes, n, solvmin, deg, cand, result,
type, G, gcd, dupl, names, name, simp, outinfo, info, facttbl, der,
socle, soclefact, tbls, tblpos, cand2, try, mustbesplit, fuscand,
s, pos;
# For duplicate tables, take (and cache) the result for the main table.
main:= attr.idenumerator.attributes.IdentifierOfMainTable;
mainid:= main.attributeValue( main, id );
if mainid <> fail then
id:= mainid;
fi;
if IsBound( CTblLib.Data.attrvalues_prim ) and
IsBound( CTblLib.Data.attrvalues_prim.( id ) ) then
return CTblLib.Data.attrvalues_prim.( id );
fi;
# Now we know that we have to work.
tbl:= CharacterTable( id );
# Let $G$ be a primitive permutation group of degree $n$
# that contains a *solvable* minimal normal subgroup $N$.
# Then we have $|N| = n$, $|G|$ divides $|N|!$, $G$ is centerless
# (Any point stabilizer $M$ is maximal in $G$, and $M \cap Z(G)$ is
# contained in $Core_G(M)$, which is trivial; if $Z(G)$ is nontrivial
# then $G = \langle M, Z(G) \rangle \cong M \times Z(G)$, contradiction.),
# $N$ is the unique minimal normal subgroup of $G$,
# and $G$ is a split extension of $N$.
# (Proof:
# Let $M$ be a core-free maximal subgroup of index $n$ in $G$.
# Then $M \cap N$ is invariant under $M$ and (since $N$ is abelian)
# under $N$, and because $N$ is not contained in $M$, we have $G = M N$,
# so $M \cap N$ is normal in $G$ and hence $|M \cap N| = 1$.
# This implies $n = [G:M] = |N|$, and clearly $G$ embeds into $Sym(n)$.
# If $G$ would contain a nontrivial central subgroup $Z$ of prime order
# then $|M \cap Z| = 1$ holds, which implies that $M$ is normal in $G$,
# a contradiction.
# Obviously $G$ cannot contain another *solvable* minimal normal
# subgroup. Suppose there is a nonsolvable minimal normal subgroup $T$,
# say. Then $T$ commutes with $N$, so $T \cap M$ is normal in $M$ and
# commutes with $N$, hence is normal in $G$ and thus trivial --but this
# implies that $G$ is a split extension of $T$ with $M$, hence the order
# of $T$ is the prime power $n$, a contradiction.)
# So we can immediately exclude tables with nontrivial centre,
# as well as tables with a minimal normal subgroup $N$ of prime power
# order that is either *larger* than the largest degree in the library
# of primitive groups
# or *too small* in the sense that the group order does not divide the
# factorial of $|N|$.
# Also note that for tables with a minimal normal subgroup $N$
# of prime power order, the only possible degree is $|N|$,
# and the table must admit a class fusion from the factor modulo $N$,
# corresponding to the embedding of the point stabilizer
# (so nonsplit extensions may be excluded using the character table).
if 1 < Length( ClassPositionsOfCentre( tbl ) ) then
if IsBound( CTblLib.Data.attrvalues_prim ) then
CTblLib.Data.attrvalues_prim.( id ):= attr.dataDefault;
fi;
return attr.dataDefault;
fi;
nsg:= ClassPositionsOfMinimalNormalSubgroups( tbl );
nsgsizes:= List( nsg, x -> Sum( SizesConjugacyClasses( tbl ){ x } ) );
n:= Size( tbl );
solvmin:= Filtered( nsgsizes, IsPrimePowerInt );
if Length( solvmin ) >= 1 and Length( nsg ) > 1 then
# A primitive group containing a solvable minimal subgroup cannot
# contain another minimal normal subgroup.
if IsBound( CTblLib.Data.attrvalues_prim ) then
CTblLib.Data.attrvalues_prim.( id ):= attr.dataDefault;
fi;
return attr.dataDefault;
elif Length( solvmin ) = 1 then
# We know the possible degree.
deg:= solvmin[1];
if deg > CTblLib.MAXPRIMRANGE or Factorial( deg ) mod n <> 0 then
#T the factorial value is no longer cached, thus we can do better
#T (n is much smaller than it):
#T some prime divisor of n is larger than deg,
#T or some prime power dividing n does not divide the factorial
#T (compute this condition)
if IsBound( CTblLib.Data.attrvalues_prim ) then
CTblLib.Data.attrvalues_prim.( id ):= attr.dataDefault;
fi;
return attr.dataDefault;
fi;
# Use only those invariants that are already stored for the groups
# in the GAP library of primitive groups;
# for example, do not force computing the number of conjugacy classes.
cand:= AllPrimitiveGroups(
NrMovedPoints, deg,
Size, n,
IsSimple, IsSimple( tbl ),
IsSolvable, IsSolvable( tbl ),
IsPerfect, IsPerfect( tbl ) );
else
# Use only those invariants that are already stored for the groups
# in the GAP library of primitive groups;
# for example, do not force computing the number of conjugacy classes.
cand:= AllPrimitiveGroups(
# avoid the annoying ``Degree restricted to ...'' message.
NrMovedPoints, [ 1 .. CTblLib.MAXPRIMRANGE ],
Size, n,
IsSimple, IsSimple( tbl ),
IsSolvable, IsSolvable( tbl ),
IsPerfect, IsPerfect( tbl ) );
fi;
if cand = [] then
if IsBound( CTblLib.Data.attrvalues_prim ) then
CTblLib.Data.attrvalues_prim.( id ):= attr.dataDefault;
fi;
return attr.dataDefault;
fi;
result:= [];
if IsSimple( tbl ) then
# The isomorphism type of simple tables can be determined.
# Simply assign the name to the simple group.
type:= IsomorphismTypeInfoFiniteSimpleGroup( tbl );
for G in cand do
if IsomorphismTypeInfoFiniteSimpleGroup( G ).name = type.name then
Add( result, [ NrMovedPoints( G ), PrimitiveIdentification( G ) ] );
fi;
od;
result:= [ "simple group", result ];
if IsBound( CTblLib.Data.attrvalues_prim ) then
CTblLib.Data.attrvalues_prim.( id ):= result;
fi;
return result;
elif IsPerfect( tbl ) and NumberPerfectGroups( n ) = 1 then
# If there is a unique perfect group of this order then we are done.
for G in cand do
Add( result, [ NrMovedPoints( G ), PrimitiveIdentification( G ) ] );
od;
result:= [ "unique perfect group of its order", result ];
if IsBound( CTblLib.Data.attrvalues_prim ) then
CTblLib.Data.attrvalues_prim.( id ):= result;
fi;
return result;
fi;
# For any minimal normal subgroup $N$ (not necessarily abelian)
# in a primitive group, the degree of the action divides $|N|$ because
# $N$ acts transitively.
# So we can exclude all candidates that do not satisfy this condition.
gcd:= Gcd( nsgsizes );
cand:= Filtered( cand, G -> gcd mod NrMovedPoints( G ) = 0 );
dupl:= attr.idenumerator.attributes.IdentifiersOfDuplicateTables;
names:= Concatenation( [ id ], dupl.attributeValue( dupl, id ) );
if IsAlmostSimpleCharacterTable( tbl ) then
for name in names do
# Determine the isomorphism type of the socle.
# If the character table library provides enough information about
# the automorphic extensions of this group then try to determine the
# isomorphism type of the almost simple group.
tbl:= CharacterTable( name );
nsg:= ClassPositionsOfMinimalNormalSubgroups( tbl );
simp:= CTblLib.Data.CharacterTablesOfNormalSubgroupWithGivenImage(
tbl, nsg[1] );
if not IsEmpty( simp )
and HasExtensionInfoCharacterTable( simp[1][1] ) then
simp:= simp[1][1];
type:= IsomorphismTypeInfoFiniteSimpleGroup( simp );
# The following list contains pairs `[ <nam>, <indices> ]'
# where <nam> runs over the suffixes of the names of
# full automorphism groups of simple groups that occur in the
# character table library, and <indices> is a list of orders of
# socle factors for which the isomorphism type of the extension
# is uniquely determined by these orders.
outinfo:= [
[ "2", [ 2 ] ],
[ "3", [ 3 ] ],
[ "4", [ 2, 4 ] ],
[ "2^2", [ 4 ] ],
[ "5", [ 5 ] ],
[ "6", [ 2, 3, 6 ] ],
[ "3.2", [ 2, 3, 6 ] ],
[ "(2x4)", [ 8 ] ],
[ "D8", [ 8 ] ],
[ "D12", [ 3, 4, 12 ] ],
[ "(2xD8)", [ 16 ] ],
[ "(S3x3)", [ 2, 9, 18 ] ],
[ "5:4", [ 2, 4, 5, 10, 20 ] ],
[ "S4", [ 3, 6, 8, 12, 24 ] ],
];
info:= ExtensionInfoCharacterTable( simp )[2];
info:= First( outinfo, x -> x[1] = info );
facttbl:= tbl / nsg[1];
if info = fail then
Print( "#E problem: is the table of ", id,
" really almost simple?\n ");
elif Size( tbl ) / Size( simp ) in info[2]
or ( info[1] = "(2x4)" and Size( tbl ) / Size( simp ) = 4
and not IsCyclic( facttbl ) )
or ( info[1] = "D8" and Size( tbl ) / Size( simp ) = 4
and IsCyclic( facttbl ) )
or ( info[1] = "D12" and Size( tbl ) / Size( simp ) = 6
and IsCyclic( facttbl ) )
or ( info[1] = "(S3x3)" and Size( tbl ) / Size( simp ) = 6 )
or ( info[1] = "S4" and Size( tbl ) / Size( simp ) = 4
and IsCyclic( facttbl ) ) then
# We can identify the group.
for G in cand do
if IsAlmostSimpleGroup( G ) then
der:= DerivedSeriesOfGroup( G );
socle:= der[ Length( der ) ];
soclefact:= G / socle;
if type.name = IsomorphismTypeInfoFiniteSimpleGroup( socle ).name and
( Size( tbl ) / Size( simp ) in info[2] or
( info[1] = "(2x4)" and not IsCyclic( soclefact ) ) or
( info[1] = "D8" and IsCyclic( soclefact ) ) or
( info[1] = "D12" and IsCyclic( soclefact ) ) or
( info[1] = "(S3x3)" and IsCyclic( soclefact )
and IsCyclic( facttbl ) ) or
( info[1] = "(S3x3)" and not IsCyclic( soclefact )
and not IsCyclic( facttbl ) ) or
( info[1] = "S4" and IsCyclic( soclefact ) ) ) then
Add( result, [ NrMovedPoints( G ),
PrimitiveIdentification( G ) ] );
fi;
fi;
od;
result:= [ Concatenation( "unique almost simple group with the ",
"given socle and socle factor" ),
result ];
if IsBound( CTblLib.Data.attrvalues_prim ) then
CTblLib.Data.attrvalues_prim.( id ):= result;
fi;
return result;
else
# Try to identify the extension of the socle by excluding
# all but one of the possibilities
# if we have all tables for these possibilities.
outinfo:= [
[ "2^2", [ [ 2, [ "2_1", "2_2", "2_3" ] ] ] ],
[ "(2x4)", [ [ 2, [ "2_1", "2_2", "2_3" ] ],
[ 4, [ "2^2", "4_1", "4_2" ] ] ] ],
[ "D8", [ [ 2, [ "2_1", "2_2", "2_3" ] ],
[ 4, [ "4", "(2^2)_{122}",
"(2^2)_{133}" ] ] ] ],
[ "D12", [ [ 2, [ "2_1", "2_2", "2_3" ] ],
[ 6, [ "6", "3.2_2", "3.2_3" ] ] ] ],
[ "(S3x3)", [ [ 3, [ "3_1", "3_2", "3_3" ] ] ] ],
[ "S4", [ [ 2, [ "2_1", "2_2" ] ],
[ 4, [ "4", "(2^2)_{111}",
"(2^2)_{122}" ] ] ] ],
];
info:= First( outinfo, x -> x[1] = info[1] );
if info <> fail then
info:= First( info[2],
x -> x[1] = Size( tbl ) / Size( simp ) );
if info <> fail then
tbls:= List( info[2],
s -> CharacterTable( Concatenation(
Identifier( simp ), ".", s ) ) );
if ForAll( tbls, IsCharacterTable ) then
tblpos:= First( [ 1 .. Length( tbls ) ],
i -> TransformingPermutationsCharacterTables(
tbl, tbls[i] ) <> fail );
cand2:= [];
for G in cand do
if IsAlmostSimpleGroup( G ) then
der:= DerivedSeriesOfGroup( G );
socle:= der[ Length( der ) ];
if type.name = IsomorphismTypeInfoFiniteSimpleGroup( socle ).name
then
try:= CTblLib.FindTableForGroup( G, tbls, tblpos );
if try = true then
Add( result, [ NrMovedPoints( G ),
PrimitiveIdentification( G ) ] );
elif try = fail then
Add( cand2, G );
fi;
fi;
fi;
od;
if cand2 = [] then
result:= [ Concatenation( "almost simple group with ",
"the given socle and socle factor that fits" ),
result ];
if IsBound( CTblLib.Data.attrvalues_prim ) then
--> --------------------
--> maximum size reached
--> --------------------
[ zur Elbe Produktseite wechseln0.60Quellennavigators
Analyse erneut starten
]
|