Spracherkennung für: .gi vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]
#############################################################################
##
#W ctadmin.gi GAP 4 package CTblLib Thomas Breuer
#W Ute Schiffer
##
## This file contains the implementation part of the data of the GAP
## character table library that is not automatically produced from the
## library files.
##
## 1. Representations of library tables
## 2. Functions used in the library files
## 3. Functions to construct library tables
## 4. Functions used as `construction' component of library tables
## 5. Selection functions for the table library
## 6. Functions to produce tables in library format
##
## Note that in all construction functions, the table under construction is
## a plain record, *not* a table object.
##
#############################################################################
##
#M InfoText( <libtbl> )
##
## <#GAPDoc Label="InfoText_libtable">
## <ManSection>
## <Meth Name="InfoText" Arg="tbl"/>
##
## <Description>
## This method for library character tables returns an empty string
## if no <Ref Attr="InfoText"/> value is stored on the table <A>tbl</A>.
## <P/>
## Without this method, it would be impossible to use <Ref Attr="InfoText"/>
## in calls to <Ref Func="AllCharacterTableNames"/>,
## as in the following example.
## <P/>
## <Example><![CDATA[
## gap> AllCharacterTableNames( InfoText,
## > s -> PositionSublist( s, "tests:" ) <> fail );;
## ]]></Example>
## </Description>
## </ManSection>
## <#/GAPDoc>
##
## We cannot use 'InstallMethod' since there are two declarations for the
## attribute 'InfoText', and the method matches both of them.
##
## (Apparently a more general declaration has been added recently.
## A clean solution would be to change 'DeclareAttributeSuppCT'
## such that no declaration happens if the operation exists already.)
##
InstallOtherMethod( InfoText,
"for character tables in the library, the default is an empty string",
[ "IsCharacterTable and IsLibraryCharacterTableRep" ],
tbl -> "" );
#############################################################################
##
#F PParseBackwards( <string>, <format> )
##
BindGlobal( "PParseBackwards", function( string, format )
#T Remove this as soon as `gpisotyp' is available!
local result, pos, j, pos2;
# Scan the string backwards.
result:= [];
pos:= Length( string );
for j in Reversed( format ) do
if IsString( j ) then
pos2:= pos - Length( j );
if pos2 < 0 or string{ [ pos2+1 .. pos ] } <> j then
return fail;
fi;
else
pos2:= pos;
while 0 < pos2 and j( string[ pos2 ] ) do
pos2:= pos2-1;
od;
fi;
if j = IsDigitChar then
Add( result, Int( string{ [ pos2+1 .. pos ] } ) );
else
Add( result, string{ [ pos2+1 .. pos ] } );
fi;
pos:= pos2;
od;
if 0 < pos then
return fail;
fi;
return Reversed( result );
end );
#############################################################################
##
#F CTblLib.ReadTbl( [<filename>, ]<id> ) . . . . read character tables files
##
## This function is used to read data files of the character table library.
## If the initial part of <filename> is one of `~/', `/' or `./' then we
## interpret the name as *absolute*, and try to read the file analogous to
## `Read'; otherwise we interpret the name as *relative* to the `data'
## directory of the CTblLib package.
##
CTblLib.ReadTbl:= function( arg )
local name, id, var, readIndent, found;
if Length( arg ) = 1 then
id:= arg[1];
name:= Concatenation( id, ".tbl" );
else
name:= arg[1];
id:= arg[2];
fi;
# `LIBTABLE.TABLEFILENAME' is used in `MOT', `ALF', `ALN'.
LIBTABLE.TABLEFILENAME:= id;
if not IsBound( LIBTABLE.( id ) ) then
LIBTABLE.( id ):= rec();
fi;
# Make some variables available that are used in data files.
# Save perhaps available user variables with these names.
if 2 <= Length( name )
and ( name[1] = '/' or name{ [ 1, 2 ] } in [ "~/", "./" ] ) then
# `name' is an absolute filename.
CTblLib.ALN:= NotifyNameOfCharacterTable;
else
# The names in ``official'' table files are already stored.
CTblLib.ALN:= Ignore;
fi;
for var in [ "ACM", "ALF", "ALN", "ARC", "MBT", "MOT" ] do
if IsBoundGlobal( var ) then
if IsReadOnlyGlobal( var ) then
MakeReadWriteGlobal( var );
CTblLib.( Concatenation( "GlobalR_", var ) ):= ValueGlobal( var );
else
CTblLib.( Concatenation( "GlobalW_", var ) ):= ValueGlobal( var );
fi;
UnbindGlobal( var );
fi;
ASS_GVAR( var, CTblLib.( var ) );
od;
if 2 <= Length( name )
and ( name[1] = '/' or name{ [ 1, 2 ] } in [ "~/", "./" ] ) then
name:= UserHomeExpand( name );
if GAPInfo.CommandLineOptions.D then
readIndent:= SHALLOW_COPY_OBJ( READ_INDENT );
APPEND_LIST_INTR( READ_INDENT, " " );
Print( "#I", READ_INDENT, "Read( \"", name, "\" )\n" );
fi;
found:= IsReadableFile( name ) = true and READ( name );
if GAPInfo.CommandLineOptions.D then
READ_INDENT:= readIndent;
if found and READ_INDENT = "" then
Print( "#I Read( \"", name, "\" ) done\n" );
fi;
fi;
else
found:= RereadPackage( "ctbllib", Concatenation( "data/", name ) );
fi;
# Unbind the variables again that have been assigned above.
for var in [ "ACM", "ALF", "ALN", "ARC", "MBT", "MOT" ] do
UnbindGlobal( var );
if IsBound( CTblLib.( Concatenation( "GlobalR_", var ) ) ) then
BindGlobal( var, CTblLib.( Concatenation( "GlobalR_", var ) ) );
Unbind( CTblLib.( Concatenation( "GlobalR_", var ) ) );
elif IsBound( CTblLib.( Concatenation( "GlobalW_", var ) ) ) then
ASS_GVAR( var, CTblLib.( Concatenation( "GlobalW_", var ) ) );
Unbind( CTblLib.( Concatenation( "GlobalW_", var ) ) );
fi;
od;
return found;
end;
#############################################################################
##
#V LIBTABLE
##
InstallFlushableValue( LIBTABLE, rec(
LOADSTATUS := rec(),
TABLEFILENAME := "",
clmelab := [],
clmexsp := [],
) );
#############################################################################
##
#V LIBLIST
##
## <#GAPDoc Label="LIBLIST">
## <ManSection>
## <Var Name="LIBLIST"/>
##
## <Description>
## &GAP;'s knowledge about the ordinary character tables in the
## &GAP; Character Table Library is given by several JSON format files
## that get evaluated when the file <F>gap4/ctprimar.g</F>
## (the <Q>primary file</Q> of the character table library) is read.
## These files can be produced from the data files,
## see Section <Ref Subsect="subsec:CTblLib data files"/>.
## <P/>
## The information is stored in the global variable <Ref Var="LIBLIST"/>,
## which is a record with the following components.
## <P/>
## <List>
## <Mark><C>firstnames</C></Mark>
## <Item>
## the list of
## <Ref Func="Identifier" Label="for character tables" BookName="ref"/>
## values of the ordinary tables,
## </Item>
## <Mark><C>files</C></Mark>
## <Item>
## the list of filenames containing the data of ordinary tables,
## </Item>
## <Mark><C>filenames</C></Mark>
## <Item>
## a list of positive integers, value <M>j</M> at position <M>i</M> means
## that the table whose identifier is the <M>i</M>-th in the
## <C>firstnames</C> list is contained in the <M>j</M>-th file of the
## <C>files</C> component,
## </Item>
## <Mark><C>fusionsource</C></Mark>
## <Item>
## a list containing at position <M>i</M> the list of names of tables that
## store a fusion into the table whose identifier is the <M>i</M>-th in
## the <C>firstnames</C> list,
## </Item>
## <Mark><C>allnames</C></Mark>
## <Item>
## a list of all admissible names of ordinary library tables,
## </Item>
## <Mark><C>position</C></Mark>
## <Item>
## a list that stores at position <M>i</M> the position in
## <C>firstnames</C> of the identifier of the table with the <M>i</M>-th
## admissible name in <C>allnames</C>,
## </Item>
## <Mark><C>simpleinfo</C></Mark>
## <Item>
## a list of triples <M>[ m, name, a ]</M> describing
## the tables of simple groups in the library;
## <M>name</M> is the identifier of the table,
## <M>m</M><C>.</C><M>name</M> and <M>name</M><C>.</C><M>a</M> are
## admissible names for its Schur multiplier and automorphism group,
## respectively, if these tables are available at all,
## </Item>
## <Mark><C>sporadicSimple</C></Mark>
## <Item>
## a list of identifiers of the tables of the <M>26</M> sporadic simple
## groups, and
## </Item>
## <Mark><C>GENERIC</C></Mark>
## <Item>
## a record with information about generic tables
## (see Section <Ref Sect="sec:generictables"/>).
## </Item>
## </List>
## </Description>
## </ManSection>
## <#/GAPDoc>
##
BindGlobal( "LIBLIST", rec() );
# The information about TomLib tables will be read as soon as
# this package is available.
# We initialize the interface here because rereading 'gap4/ctprimar.g'
# shall not overwrite known values.
LIBLIST.TOM_TBL_INFO_VERSION:= "no information about tables of marks";
LIBLIST.TOM_TBL_INFO:= [ [], [] ];
ReadPackage( "ctbllib", "gap4/ctprimar.g" );
#############################################################################
##
#F GALOIS( <chars>, <list> )
#F TENSOR( <chars>, <list> )
#F EvalChars( <chars> )
##
InstallGlobalFunction( GALOIS, function( chars, li )
return List( chars[ li[1] ], x -> GaloisCyc( x, li[2] ) );
end );
InstallGlobalFunction( TENSOR, function( chars, list )
local i, chi, psi, result;
chi:= chars[ list[1] ];
psi:= chars[ list[2] ];
result:= [];
for i in [ 1 .. Length( chi ) ] do result[i]:= chi[i] * psi[i]; od;
return result;
end );
InstallGlobalFunction( EvalChars, function( chars )
local i;
for i in [ 1 .. Length( chars ) ] do
if IsFunction( chars[i][1] ) then
chars[i]:= chars[i][1]( chars, chars[i][2] );
fi;
od;
return chars;
end );
#############################################################################
##
#F CTblLib.ALF( <from>, <to>, <map>[, <text>, <spec>] ) add library fusions
##
CTblLib.ALF:= function( arg )
local pos, fus, text;
if CTblLib.ALN <> Ignore then
# A file is read that does not belong to the official library.
# Check that the names are valid.
if not arg[1] in RecNames( LIBTABLE.( LIBTABLE.TABLEFILENAME ) ) then
Error( "source `", arg[1], "' is not stored in `LIBTABLE.",
LIBTABLE.TABLEFILENAME, "'" );
fi;
pos:= Position( LIBLIST.firstnames, arg[2] );
#T this is not sorted! (take LIBLIST.allnames instead, and use indirection)
if pos = fail then
Info( InfoWarning, 1,
"destination `", arg[2], "' is not a valid first name" );
else
# Check whether there was already such a fusion.
if not arg[1] in LIBLIST.fusionsource[ pos ] then
# Store the fusion source.
LIBLIST.fusionsource:= ShallowCopy( LIBLIST.fusionsource );
LIBLIST.fusionsource[ pos ]:= MakeImmutable( Concatenation(
LIBLIST.fusionsource[ pos ], [ arg[1] ] ) );
MakeImmutable( LIBLIST.fusionsource );
fi;
fi;
fi;
fus:= rec( name:= arg[2], map:= arg[3] );
if 4 <= Length( arg ) then
text:= Concatenation( arg[4] );
ConvertToStringRep( text );
fus.text:= text;
fi;
if Length( arg ) = 5 then
text:= arg[5];
ConvertToStringRep( text );
fus.specification:= text;
fi;
Add( LIBTABLE.( LIBTABLE.TABLEFILENAME ).( arg[1] ).ComputedClassFusions,
fus );
end;
#############################################################################
##
#F CTblLib.ACM( <spec>, <dim>, <val> ) . . . . . . . . . add Clifford matrix
##
CTblLib.ACM:= function( spec, dim, val )
spec:= LIBTABLE.( Concatenation( "clm", spec ) );
if not IsBound( spec[ dim ] ) then
spec[ dim ]:= [];
fi;
Add( spec[ dim ], val );
end;
#############################################################################
##
#F CTblLib.ARC( <name>, <comp>, <val> ) . . . add component of library table
##
CTblLib.ARC:= function( name, comp, val )
LIBTABLE.( LIBTABLE.TABLEFILENAME ).( name ).( comp ):= val;
end;
#############################################################################
##
#F NotifyGroupInfoForCharacterTable( <id>, <value> )
##
## This function shall be available also if the Browse package is not
## available, and then do nothing.
##
BindGlobal( "NotifyGroupInfoForCharacterTable", function( id, value )
local enum, r, list, pos, i;
if id in CTblLib.Data.IdEnumerator.identifiers then
enum:= CTblLib.Data.IdEnumerator;
elif id in CTblLib.Data.IdEnumeratorExt.identifiers then
enum:= CTblLib.Data.IdEnumeratorExt;
else
return false;
fi;
if not IsBound( enum.attributes.indiv ) then
return false;
fi;
r:= enum.attributes.indiv;
if not IsBound( r.data ) then
r.data:= rec( automatic:= [], nonautomatic:= [] );
fi;
list:= r.data.nonautomatic;
pos:= PositionSorted( list, [ id ] );
if IsBound( list[ pos ] ) then
if list[ pos ][1] = id then
Add( list[ pos ][2], value );
else
for i in [ Length( list ), Length( list ) - 1 .. pos ] do
list[ i+1 ]:= list[i];
od;
list[ pos ]:= [ id, [ value ] ];
fi;
else
Add( list, [ id, [ value ] ] );
fi;
return true;
end );
#############################################################################
##
#F CTblLib.NotifyNameOfCharacterTable( <firstname>, <newnames>, <pos> )
##
## This code does not perform any argument check,
## and does not clean up components in 'LIBLIST'.
##
CTblLib.NotifyNameOfCharacterTable:= function( firstname, newnames, pos,
allnames, position )
local lower, pos2, name, j;
lower:= List( newnames, LowercaseString );
if ForAny( lower, x -> x in allnames ) then
Error( "<newnames> must contain only new names" );
fi;
Append( allnames, lower );
Append( position, List( lower, x -> pos ) );
end;
#############################################################################
##
#F NotifyNameOfCharacterTable( <firstname>, <newnames> )
##
## notifies the new names in the list <newnames> for the library table with
## first name <firstname>, if there is no other table yet for that some of
## these names are admissible.
##
InstallGlobalFunction( NotifyNameOfCharacterTable,
function( firstname, newnames )
local pos, allnames, position;
if not ( IsString( firstname )
and IsList( newnames ) and ForAll( newnames, IsString ) ) then
Error( "<firstname> and entries in list <newnames> must be strings" );
elif ForAny( [ 1 .. Length( firstname ) - 2 ],
x -> firstname{ [ x .. x+2 ] } = "mod" ) then
Error( "Brauer tables must not have explicitly given `othernames'" );
fi;
pos:= Position( LIBLIST.firstnames, firstname );
#T this is not sorted! (take LIBLIST.allnames instead, and use indirection)
if pos = fail then
Error( "no GAP library table with first name `", firstname, "'" );
fi;
# Change `LIBLIST'.
allnames:= ShallowCopy( LIBLIST.allnames );
position:= ShallowCopy( LIBLIST.position );
CTblLib.NotifyNameOfCharacterTable( firstname, newnames, pos,
allnames, position );
SortParallel( allnames, position );
LIBLIST.allnames:= MakeImmutable( allnames );
LIBLIST.position:= MakeImmutable( position );
end );
#############################################################################
##
#F NotifyCharacterTables( <list> )
##
InstallGlobalFunction( NotifyCharacterTables,
function( list )
local firstnames, filenames, files, fusionsource, allnames, position,
triple, firstname, filename, othernames, len;
if not IsList( list ) then
Error( "<list> must be a list of triples" );
fi;
firstnames:= ShallowCopy( LIBLIST.firstnames );
filenames:= ShallowCopy( LIBLIST.filenames );
files:= ShallowCopy( LIBLIST.files );
fusionsource:= ShallowCopy( LIBLIST.fusionsource );
allnames:= ShallowCopy( LIBLIST.allnames );
position:= ShallowCopy( LIBLIST.position );
for triple in list do
if Length( triple ) <> 3 then
Error( "<list> must be a list of triples" );
fi;
firstname:= triple[1];
filename:= triple[2];
othernames:= triple[3];
if not ( IsString( firstname ) and IsString( filename ) and
IsList( othernames ) and ForAll( othernames, IsString ) ) then
Error( "<firstname>, <filename> must be strings, ",
"<othernames> must be a list of strings" );
elif LowercaseString( firstname ) in LIBLIST.allnames then
Error( "<firstname> is already a valid name" );
fi;
if not filename in files then
Add( files, filename );
fi;
len:= Length( firstnames ) + 1;
firstnames[ len ]:= firstname;
filenames[ len ]:= Position( files, filename );
fusionsource[ len ]:= [];
CTblLib.NotifyNameOfCharacterTable( firstname, [ firstname ], len,
allnames, position );
CTblLib.NotifyNameOfCharacterTable( firstname, othernames, len,
allnames, position );
if IsBound( CTblLib.Data.IdEnumeratorExt.identifiers ) then
Add( CTblLib.Data.IdEnumeratorExt.identifiers, firstname );
# This is an ugly hack in order to achieve a better
# integration of the SpinSym package.
# It would be better (and cheaper) if the attributes would be set
# inside the SpinSym package.
# Also, we *want* those SpinSym tables that are available also in the
# main library to be regarded as duplicates, although they are
# formally not declared as duplicates, i. e., as permuted tables
# of library tables.
if IsBound( GAPInfo.PackageCurrent ) and
IsBound( GAPInfo.PackageCurrent.PackageName ) and
GAPInfo.PackageCurrent.PackageName = "SpinSym" then
Add( CTblLib.SpinSymNames, firstname );
if not IsBound( GAPInfo.PackageExtensionsLoaded ) then
# We are in GAP up to version 4.12.
# The SpinSym package notifies its tables
# *after* the CTblLib package has been loaded.
# Thus we have to set the attributes here.
CTblLib.SetAttributesForSpinSymTable( firstname );
fi;
fi;
fi;
od;
LIBLIST.firstnames:= MakeImmutable( firstnames );
LIBLIST.filenames:= MakeImmutable( filenames );
LIBLIST.files:= MakeImmutable( files );
LIBLIST.fusionsource:= MakeImmutable( fusionsource );
SortParallel( allnames, position );
LIBLIST.allnames:= MakeImmutable( allnames );
LIBLIST.position:= MakeImmutable( position );
end );
#############################################################################
##
#F NotifyCharacterTable( <firstname>, <filename>, <othernames> )
##
InstallGlobalFunction( NotifyCharacterTable,
function( firstname, filename, othernames )
NotifyCharacterTables( [ [ firstname, filename, othernames ] ] );
end );
#############################################################################
##
#F NotifyBrauerTable( <firstordname>, <p>, <filename> )
#F NotifyBrauerTables( <list> )
##
BindGlobal( "NotifyBrauerTables", function( list )
local firstmodnames, filenames, triple, firstordname, p, filename,
lowername, pos;
if not IsList( list ) then
Error( "<list> must be a list of triples" );
fi;
firstmodnames:= ShallowCopy( LIBLIST.PrivateBrauerTables[1] );
filenames:= ShallowCopy( LIBLIST.PrivateBrauerTables[2] );
for triple in list do
if Length( triple ) <> 3 then
Error( "<list> must be a list of triples" );
fi;
firstordname:= triple[1];
p:= triple[2];
filename:= triple[3];
if not ( IsString( firstordname ) and IsString( filename ) and
IsPrimeInt( p ) ) then
Error( "<firstordname>, <filename> must be strings, ",
"<p> must be a prime integer" );
fi;
lowername:= MakeImmutable(
Concatenation( LowercaseString( firstordname ),
"mod", String( p ) ) );
pos:= Position( firstmodnames, lowername );
if pos <> fail then
if filenames[ pos ] <>filename then
Error( "<firstordname> is already notified with a different file" );
fi;
else
Add( firstmodnames, lowername );
Add( filenames, Immutable( filename ) );
fi;
od;
LIBLIST.PrivateBrauerTables[1]:= firstmodnames;
LIBLIST.PrivateBrauerTables[2]:= filenames;
SortParallel( firstmodnames, filenames );
end );
BindGlobal( "NotifyBrauerTable", function( firstordname, p, filename )
NotifyBrauerTables( [ [ firstordname, p, filename ] ] );
end );
#############################################################################
##
#F CTblLib.MBT( <arg> )
##
CTblLib.MBT:= function( arg )
local i, record;
record:= rec(
InfoText := arg[ 3],
UnderlyingCharacteristic := arg[ 2],
block := arg[ 4],
defect := arg[ 5],
basicset := arg[ 6],
brauertree := arg[ 7],
decinv := arg[ 8],
factorblocks := arg[ 9],
AutomorphismsOfTable := arg[10],
indicator := arg[11]
);
for i in RecNames( record ) do
if record.(i) = 0 then
Unbind( record.(i) );
fi;
od;
if Length( arg ) = 12 then
for i in RecNames( arg[12] ) do
record.(i):= arg[12].(i);
od;
fi;
LIBTABLE.( LIBTABLE.TABLEFILENAME ).(
Concatenation( arg[1], "mod", String( arg[2] ) ) ):= record;
end;
#############################################################################
##
#F CTblLib.MOT( <arg> )
##
CTblLib.MOT:= function( arg )
local record, i, len;
# Construct the record.
record:= rec(
Identifier := arg[1],
InfoText := arg[2],
UnderlyingCharacteristic := 0,
SizesCentralizers := arg[3],
ComputedPowerMaps := arg[4],
ComputedClassFusions := [],
Irr := arg[5],
AutomorphismsOfTable := arg[6]
);
for i in [ "InfoText", "SizesCentralizers", "ComputedPowerMaps",
"ComputedClassFusions", "Irr", "AutomorphismsOfTable" ] do
if record.(i) = 0 then
Unbind( record.(i) );
fi;
od;
if IsBound( arg[7] ) and IsList( arg[7] ) then
record.ConstructionInfoCharacterTable:= arg[7];
fi;
len:= Length( arg );
if IsRecord( arg[ len ] ) then
for i in RecNames( arg[ len ] ) do
record.(i):= arg[ len ].(i);
od;
fi;
# Store the table record.
LIBTABLE.( LIBTABLE.TABLEFILENAME ).( arg[1] ):= record;
end;
#############################################################################
##
#V GEN_Q_P
##
#F PrimeBase( <q> )
##
InstallFlushableValue( GEN_Q_P, [] );
InstallGlobalFunction( PrimeBase, function( q )
if not IsBound( GEN_Q_P[q] ) then
GEN_Q_P[q]:= FactorsInt( q )[1];
fi;
return GEN_Q_P[q];
end );
#############################################################################
##
#F LibInfoCharacterTable( <tblname> )
##
InstallGlobalFunction( LibInfoCharacterTable, function( tblname )
local i, ordinfo, str, pos, filename;
if IsCharacterTable( tblname ) then
tblname:= Identifier( tblname );
fi;
# Is `tblname' the name of a Brauer table,
# i.e., does it have the structure `<ordname>mod<prime>' ?
# If so, return `<firstordname>mod<prime>' where
# `<firstordname> = LibInfoCharacterTable( <ordname> ).firstName'.
tblname:= LowercaseString( tblname );
for i in [ 1 .. Length( tblname ) - 2 ] do
if tblname{ [ i .. i+2 ] } = "mod" then
ordinfo:= LibInfoCharacterTable( tblname{ [ 1 .. i-1 ] } );
if ordinfo <> fail then
ordinfo.firstName:= Concatenation( ordinfo.firstName,
tblname{ [ i .. Length( tblname ) ] } );
ConvertToStringRep( ordinfo.firstName );
str:= ordinfo.fileName;
pos:= Position( LIBLIST.PrivateBrauerTables[1],
LowercaseString( ordinfo.firstName ) );
if pos <> fail then
ordinfo.fileName:= LIBLIST.PrivateBrauerTables[2][ pos ];
elif '/' in str then
pos:= Maximum( Filtered( [ 1 .. Length( str ) ],
i -> str[i] = '/' ) );
filename:= str{ [ pos+1 .. Length( str ) ] };
if 3 <= Length( filename ) and filename{ [ 1 .. 3 ] } = "cto" then
filename[3]:= 'b';
fi;
ordinfo.fileName:= Concatenation( str{ [ 1 .. pos ] }, filename );
else
ordinfo.fileName:= ShallowCopy( str );
if 3 <= Length( str ) and str{ [ 1 .. 3 ] } = "cto" then
ordinfo.fileName[3]:= 'b';
fi;
fi;
ConvertToStringRep( ordinfo.fileName );
fi;
return ordinfo;
fi;
od;
# The name might belong to an ordinary table.
pos:= Position( LIBLIST.allnames, tblname );
if pos <> fail then
pos:= LIBLIST.position[ pos ];
if pos <> fail then
return rec( firstName := LIBLIST.firstnames[ pos ],
fileName := LIBLIST.files[
LIBLIST.filenames[ pos ] ] );
fi;
return fail;
fi;
# The name might belong to a generic table.
if tblname in LIBLIST.GENERIC.allnames then
return rec( firstName := LIBLIST.GENERIC.firstnames[
Position( LIBLIST.GENERIC.allnames,
tblname ) ],
fileName := "ctgeneri" );
fi;
return fail;
end );
#############################################################################
##
#F LibraryTables( <filename> )
##
InstallGlobalFunction( LibraryTables, function( filename )
local suffix, file, pos;
# Omit the initial path for the name of the component in `LIBTABLE'.
suffix:= filename;
pos:= Position( suffix, '/' );
while pos <> fail do
suffix:= suffix{ [ pos+1 .. Length( suffix ) ] };
pos:= Position( suffix, '/' );
od;
if not IsBound( LIBTABLE.LOADSTATUS.( suffix ) )
or LIBTABLE.LOADSTATUS.( suffix ) = "unloaded" then
# It is necessary to read a library file.
# First unload all files which are not `"userloaded"', except that
# with the ordinary resp. Brauer tables corresponding to those in
# the file `filename'
if UserPreference( "CTblLib", "UnloadCTblLibFiles" ) then
for file in RecNames( LIBTABLE.LOADSTATUS ) do
if LIBTABLE.LOADSTATUS.( file ) <> "userloaded" and
suffix{ [ 4 .. Length( suffix ) ] }
<> file{ [ 4 .. Length( file ) ] } then
LIBTABLE.( file ):= rec();
LIBTABLE.LOADSTATUS.( file ):= "unloaded";
fi;
od;
fi;
LIBTABLE.( suffix ):= rec();
# Try to read the file.
if not CTblLib.ReadTbl( Concatenation( filename, ".tbl" ), suffix ) then
Info( InfoCharacterTable, 1,
"no file `", filename,
".tbl' in the GAP Character Table Library" );
return fail;
fi;
# Reset the load status from `"userloaded"' to `"loaded"'.
LIBTABLE.LOADSTATUS.( suffix ):= "loaded";
fi;
return LIBTABLE.( suffix );
end );
############################################################################
##
#V CTblLib.SupportedGenericIdentifiers
##
## Make generic identifiers admissible.
##
CTblLib.SupportedGenericIdentifiers:= [
[ PParseBackwards, [ "c", IsDigitChar ],
"Cyclic", [ 2 ] ],
[ PParseBackwards, [ "alt(", IsDigitChar, ")" ],
"Alternating", [ 2 ] ],
[ PParseBackwards, [ "sym(", IsDigitChar, ")" ],
"Symmetric", [ 2 ] ],
[ PParseBackwards, [ "dihedral(", IsDigitChar, ")" ],
"Dihedral", [ 2 ] ],
[ PParseBackwards, [ "2.sym(", IsDigitChar, ")" ],
"DoubleCoverSymmetric", [ 2 ] ],
[ PParseBackwards, [ "2.alt(", IsDigitChar, ")" ],
"DoubleCoverAlternating", [ 2 ] ],
];
#############################################################################
##
#F CTblLib.TrySpecialization( <tblname> )
##
CTblLib.TrySpecialization:= function( tblname )
local entry, scan;
tblname:= LowercaseString( tblname );
for entry in CTblLib.SupportedGenericIdentifiers do
scan:= entry[1]( tblname, entry[2] );
if scan <> fail then
return CallFuncList( CharacterTableFromLibrary,
Concatenation( [ entry[3] ], scan{ entry[4] } ) );
fi;
od;
return fail;
end;
#############################################################################
##
#F CharacterTableFromLibrary( <tblname> )
#F CharacterTableFromLibrary( <series>, <param1>[, <param2>] )
##
InstallGlobalFunction( CharacterTableFromLibrary, function( arg )
local tblname, firstname, filename, suffix, pos, librarytables, name,
libtbl, fld, i, fus;
if IsEmpty( arg ) or not IsString( arg[1] ) then
Error( "usage: CharacterTableFromLibrary( <tblname> )\n",
" resp. CharacterTableFromLibrary( <series>, <parameters> )" );
elif Length( arg ) = 1 then
# `CharacterTableFromLibrary( tblname )'
tblname:= arg[1];
firstname:= LibInfoCharacterTable( tblname );
if firstname = fail then
# Perhaps it is the identifier of a generic table.
libtbl:= CTblLib.TrySpecialization( tblname );
if libtbl <> fail then
return libtbl;
fi;
Info( InfoCharacterTable, 1,
"No library table with name `", tblname, "'" );
return fail;
fi;
filename := firstname.fileName;
firstname := firstname.firstName;
suffix:= filename;
pos:= Position( suffix, '/' );
while pos <> fail do
suffix:= suffix{ [ pos+1 .. Length( suffix ) ] };
pos:= Position( suffix, '/' );
od;
if 3 < Length( suffix ) and suffix{ [ 1 .. 3 ] } = "ctb" then
# Brauer table, call `BrauerTable'
# (First get the ordinary table.)
name:= PartsBrauerTableName( firstname );
return BrauerTable(
CharacterTableFromLibrary( name.ordname ),
name.prime );
fi;
# ordinary or generic table
librarytables:= LibraryTables( filename );
if librarytables = fail
or not IsBound( librarytables.( firstname ) ) then
Info( InfoCharacterTable, 1,
"No library table with name `", tblname, "'" );
return fail;
fi;
libtbl:= librarytables.( firstname );
# If the table has not yet been converted to an object,
# we must do this now.
if IsRecord( libtbl ) then
# If the table is a generic table then simply return it.
if IsBound( libtbl.isGenericTable )
and libtbl.isGenericTable = true then
return libtbl;
fi;
# Concatenate the lines of the `InfoText' component.
if IsBound( libtbl.InfoText ) then
libtbl.InfoText:= Concatenation( libtbl.InfoText );
ConvertToStringRep( libtbl.InfoText );
fi;
# Store the fusion sources.
pos:= Position( LIBLIST.firstnames, firstname );
#T this is not sorted! (take LIBLIST.allnames instead, and use indirection)
libtbl.NamesOfFusionSources:=
ShallowCopy( LIBLIST.fusionsource[ pos ] );
# Evaluate characters encoded as `[GALOIS,[i,j]]'
# or `[TENSOR,[i,j]]'.
if IsBound( libtbl.projectives ) then
fld:= libtbl.projectives;
libtbl.ProjectivesInfo:= [];
Unbind( libtbl.projectives );
for i in [ 1, 3 .. Length( fld ) - 1 ] do
Add( libtbl.ProjectivesInfo,
rec( name:= fld[i], chars:= EvalChars( fld[i+1] ) ) );
od;
fi;
# Obey the construction component.
if IsBound( libtbl.ConstructionInfoCharacterTable ) then
if IsFunction( libtbl.ConstructionInfoCharacterTable ) then
#T for backwards compatibility, supported in Version 1.1.
libtbl.ConstructionInfoCharacterTable( libtbl );
else
CallFuncList(
ValueGlobal( libtbl.ConstructionInfoCharacterTable[1] ),
Concatenation( [ libtbl ],
libtbl.ConstructionInfoCharacterTable{ [ 2 .. Length(
libtbl.ConstructionInfoCharacterTable ) ] } ) );
fi;
fi;
# initialize some components
if IsBound( libtbl.ComputedPowerMaps )
and not IsEmpty( libtbl.ComputedPowerMaps )
and not IsBound( libtbl.OrdersClassRepresentatives ) then
libtbl.OrdersClassRepresentatives:=
ElementOrdersPowerMap( libtbl.ComputedPowerMaps );
if not ForAll( libtbl.OrdersClassRepresentatives, IsPosInt ) then
Info( InfoWarning, 1,
"representative orders of library table ", tblname,
" not uniquely determined" );
Unbind( libtbl.OrdersClassRepresentatives );
fi;
fi;
if IsBound( libtbl.AutomorphismsOfTable ) and
IsList( libtbl.AutomorphismsOfTable ) then
libtbl.AutomorphismsOfTable:= GroupByGenerators(
libtbl.AutomorphismsOfTable, () );
fi;
if IsBound( libtbl.maxes ) then
libtbl.Maxes:= libtbl.maxes;
Unbind( libtbl.maxes );
fi;
if IsBound( libtbl.tomfusion ) then
if IsBound( libtbl.tomfusion.text ) then
libtbl.tomfusion.text:= Concatenation( libtbl.tomfusion.text );
ConvertToStringRep( libtbl.tomfusion.text );
fi;
libtbl.FusionToTom:= libtbl.tomfusion;
# For backwards compatibility, do not unbind the component.
libtbl.tomidentifier:= libtbl.tomfusion.name;
fi;
if IsBound( libtbl.isSimple ) then
libtbl.IsSimpleCharacterTable:= libtbl.isSimple;
Unbind( libtbl.isSimple );
fi;
if IsBound( libtbl.extInfo ) then
libtbl.ExtensionInfoCharacterTable:= libtbl.extInfo;
Unbind( libtbl.extInfo );
fi;
if IsBound( libtbl.CAS ) then
libtbl.CASInfo:= libtbl.CAS;
Unbind( libtbl.CAS );
fi;
if IsBound( libtbl.CASInfo ) then
# For tables constructed from others,
# the value may be copied from an attribute value
# and hence may be immutable.
#T mutability problem:
#T if the following comment signs are removed then GAP runs into an error!
# if not IsMutable( libtbl.CASInfo ) then
libtbl.CASInfo:= List( libtbl.CASInfo, ShallowCopy );
# fi;
for i in libtbl.CASInfo do
if IsBound( i.text ) and ForAll( i.text, IsString ) then
i.text:= Concatenation( i.text );
ConvertToStringRep( i.text );
fi;
od;
fi;
# Evaluate characters encoded as `[GALOIS,[i,j]]', `[TENSOR,[i,j]]'.
EvalChars( libtbl.Irr );
# Make the table object, and store it for the next call.
ConvertToLibraryCharacterTableNC( libtbl );
librarytables.( firstname ):= libtbl;
fi;
# Return the library table.
return libtbl;
else
if arg[1] = "Quaternionic" and Length( arg ) = 2
and IsInt( arg[2] ) then
return CharacterTableQuaternionic( arg[2] );
elif arg[1] = "GL" and Length( arg ) = 3
and IsInt( arg[2] ) and IsInt( arg[3] ) then
# `CharacterTable( GL, 2, q )'
if arg[2] = 2 then
return CharacterTableSpecialized(
CharacterTableFromLibrary( "GL2" ), arg[3] );
else
Info( InfoCharacterTable, 1,
"Table of GL(", arg[2], ",q) not yet implemented" );
return fail;
fi;
elif arg[1] = "SL" and Length( arg ) = 3
and IsInt( arg[2] ) and IsInt( arg[3] ) then
# CharacterTable( SL, 2, q )
if arg[2] = 2 then
if arg[3] mod 2 = 0 then
return CharacterTableSpecialized(
CharacterTableFromLibrary( "SL2even" ),
arg[3] );
else
return CharacterTableSpecialized(
CharacterTableFromLibrary( "SL2odd" ),
arg[3] );
fi;
else
Info( InfoCharacterTable, 1,
"Table of SL(", arg[2], ",q) not yet implemented" );
return fail;
fi;
elif arg[1] = "PSL" and Length( arg ) = 3
and IsInt( arg[2] ) and IsInt( arg[3] ) then
# PSL( 2, q )
if arg[2] = 2 then
if arg[3] mod 2 = 0 then
return CharacterTableSpecialized(
CharacterTableFromLibrary( "SL2even" ),
arg[3] );
elif ( arg[3] - 1 ) mod 4 = 0 then
return CharacterTableSpecialized(
CharacterTableFromLibrary( "PSL2even" ),
arg[3] );
else
return CharacterTableSpecialized(
CharacterTableFromLibrary( "PSL2odd" ),
arg[3] );
fi;
else
Info( InfoCharacterTable, 1,
"Table of PSL(", arg[2], ",q) not yet implemented" );
return fail;
fi;
elif arg[1] = "GU" and Length( arg ) = 3
and IsInt( arg[2] ) and IsInt( arg[3] ) then
# GU( 3, q )
if arg[2] = 3 then
return CharacterTableSpecialized(
CharacterTableFromLibrary( "GU3" ), arg[3] );
else
Info( InfoCharacterTable, 1,
"Table of GU(", arg[2], ",q) not yet implemented" );
return fail;
fi;
elif arg[1] = "SU" and Length( arg ) = 3
and IsInt( arg[2] ) and IsInt( arg[3] ) then
# SU( 3, q )
if arg[2] = 3 then
return CharacterTableSpecialized(
CharacterTableFromLibrary( "SU3" ),
arg[3] );
else
Info( InfoCharacterTable, 1,
"Table of SU(", arg[2], ",q) not yet implemented" );
return fail;
fi;
elif arg[1] = "Suzuki" and Length( arg ) = 2
and IsInt( arg[2] ) then
if not PrimeDivisors( arg[2] ) = [ 2 ] then
Info( InfoCharacterTable, 1,
"CharacterTable(\"Suzuki\",q): q must be a power of 2");
return fail;
fi;
return CharacterTableSpecialized(
CharacterTableFromLibrary( "Suzuki" ),
[ arg[2],
2^((Length(FactorsInt(arg[2]))+1)/2) ] );
else
return CharacterTableSpecialized(
CharacterTableFromLibrary( arg[1] ), arg[2] );
fi;
fi;
end );
#############################################################################
##
#F PartsBrauerTableName( <modname> )
##
InstallGlobalFunction( PartsBrauerTableName, function( modname )
local i, primestring, ordname, prime, digits;
primestring:= 0;
for i in [ 1 .. Length( modname ) - 2 ] do
if modname{ [ i .. i + 2 ] } = "mod" then
primestring:= modname{ [ i + 3 .. Length( modname ) ] };
ordname:= modname{ [ 1 .. i-1 ] };
fi;
od;
if primestring = 0 then
Print( "#I PartsBrauerTableName: ", modname,
" is no valid name\n",
"#I for a Brauer table\n" );
return fail;
fi;
# Convert the string back to a number.
digits:= "0123456789";
primestring:= List( primestring, x -> Position( digits, x ) );
if fail in primestring then
Print( "#I PartsBrauerTableName: ", modname,
" is no valid name\n",
"#I for a Brauer table\n" );
return fail;
fi;
prime:= 0;
for i in [ 1 .. Length( primestring ) ] do
prime:= 10 * prime + ( primestring[i] - 1 );
od;
return rec( ordname:= ordname, prime:= prime );
end );
#############################################################################
##
#F BasicSetBrauerTree( <brauertree> )
##
InstallGlobalFunction( BasicSetBrauerTree, function( brauertree )
local i, degrees, basicset, edge, elm;
brauertree:= Set( brauertree );
basicset:= [];
# degrees of the vertices
degrees:= [];
for edge in brauertree do
for i in edge do
if not IsBound( degrees[i] ) then
degrees[i]:= 1;
else
degrees[i]:= degrees[i] + 1;
fi;
od;
od;
while brauertree <> [] do
# take a vertex of degree 1, remove its edge, adjust `degrees'
elm:= Position( degrees, 1 );
AddSet( basicset, elm );
edge:= First( brauertree, x -> elm in x );
RemoveSet( brauertree, edge );
for i in edge do
degrees[i]:= degrees[i] - 1;
od;
od;
return basicset;
end );
#############################################################################
##
#F DecMatBrauerTree( <brauertree> )
##
InstallGlobalFunction( DecMatBrauerTree, function( brauertree )
local i, j, max, decmat;
max:= 1;
for i in brauertree do
max:= Maximum( max, Maximum(i) );
od;
decmat:= NullMat( max, Length( brauertree ) );
for i in [ 1 .. Length( brauertree ) ] do
for j in brauertree[i] do
decmat[j][i]:= 1;
od;
od;
return decmat;
end );
#############################################################################
##
#F BrauerTree( <decmat> )
##
InstallGlobalFunction( BrauerTree, function( decmat )
local i, j, brauertree, edge, len;
if not ( IsMatrix( decmat )
and ForAll( decmat, x -> ForAll( x, y -> y=0 or y=1 ) ) ) then
Print( "#I BrauerTree: <decmat> is not decomposition matrix\n",
"#I of a block of cyclic defect\n");
return fail;
fi;
if decmat = [ [ 1 ] ] then return []; fi;
brauertree:= [];
for i in [ 1 .. Length( decmat[1] ) ] do
# find the entries 1 in column `i'
edge:= [];
for j in [ 1 .. Length( decmat ) ] do
if decmat[j][i] = 1 then Add( edge, j ); fi;
od;
len:= Length( edge );
# If `len = 2', we have an ordinary edge of the tree; else this may
# concern an exceptional character.
if len = 2 then
Add( brauertree, edge );
else
if Length( Set( decmat{ edge } ) ) <= 2 then
# all or all but one ordinary irreducibles restrict identically
Add( brauertree, edge );
else
Print( "#I BrauerTree: <decmat> is not decomposition",
" matrix\n",
"#I of a block of cyclic defect\n");
return fail;
fi;
fi;
od;
return brauertree;
end );
#############################################################################
##
#F BrauerTableFromLibrary( <ordtbl>, <prime> )
##
InstallGlobalFunction( BrauerTableFromLibrary, function( ordtbl, prime )
local filename, # name of the file containing the Brauer table
fld, # library tables of the whole library file
libtbl, # record with data of the desired table
reg, # Brauer table, result
op, # largest normal $p$-subgroup
orders, # representative orders in `ordtbl'
nccl, # no. of classes in `ordtbl'
entry, # loop over stored fusions
fusion, # one fusion map
result_blocks,
i, j,
ord,
pow,
ordblocks,
modblocks,
defect,
name,
irreducibles,
restricted,
block,
basicset,
class,
images,
chi,
gal,
newimages,
pos,
im,
decmat,
brauertree,
facttbl,
mfacttbl,
pbl,
info,
factinfo,
ordchars,
offset,
decinv,
suffix;
# Get the library file of the Brauer table if possible.
name:= Concatenation( Identifier( ordtbl ), "mod", String( prime ) );
filename:= LibInfoCharacterTable( name );
if IsRecord( filename ) then
filename:= filename.fileName;
fld:= LibraryTables( filename );
else
fld:= fail;
fi;
if fld = fail or not IsBound( fld.( name ) ) then
# For p-solvable tables, prefer the generic method.
if IsPSolvableCharacterTable( ordtbl, prime ) then
return fail;
fi;
# Maybe we have to factor out a normal $p$-subgroup before
# we find the table (name) in the library.
op:= ClassPositionsOfPCore( ordtbl, prime );
if Length( op ) = 1 then
Info( InfoCharacterTable, 1,
"No library table with name `", name, "'" );
return fail;
fi;
orders:= OrdersClassRepresentatives( ordtbl );
nccl:= NrConjugacyClasses( ordtbl );
for entry in ComputedClassFusions( ordtbl ) do
fusion:= entry.map;
if Positions( fusion, 1 ) = op then
# We found the ordinary factor for which the Brauer characters
# are equal to the ones we need.
facttbl:= CharacterTableFromLibrary( entry.name );
if facttbl = fail then
return fail;
fi;
mfacttbl:= BrauerTable( facttbl, prime );
if mfacttbl = fail then
return fail;
fi;
# Now we set up a *new* Brauer table since the ordinary table
# as well as the blocks information for the factor group is
# different from the one for the extension.
reg:= CharacterTableRegular( ordtbl, prime );
SetFilterObj( reg, IsLibraryCharacterTableRep );
# Set the irreducibles.
# Note that the ordering of classes is in general *not* the same,
# so we must translate with the help of fusion maps.
fusion:= CompositionMaps(
InverseMap( GetFusionMap( mfacttbl, facttbl ) ),
CompositionMaps( GetFusionMap( ordtbl, facttbl ),
GetFusionMap( reg, ordtbl ) ) );
SetIrr( reg, List( Irr( mfacttbl ),
chi -> Character( reg,
ValuesOfClassFunction( chi ){ fusion } ) ) );
# Set known attribute values that can be copied from `mfacttbl'.
if HasAutomorphismsOfTable( mfacttbl ) then
SetAutomorphismsOfTable( reg, AutomorphismsOfTable( mfacttbl )
^ Inverse( PermList( fusion ) ) );
fi;
if HasInfoText( mfacttbl ) then
SetInfoText( reg, InfoText( mfacttbl ) );
fi;
if HasComputedIndicators( mfacttbl ) then
SetComputedIndicators( reg, ComputedIndicators( mfacttbl ) );
fi;
# Return the table.
return reg;
fi;
od;
Info( InfoCharacterTable, 1,
"No library table of the factor by O_p" );
return fail;
fi;
libtbl:= fld.( name );
# If the table was already constructed simply return it.
if IsBrauerTable( libtbl ) then
return libtbl;
fi;
# Otherwise we have to work.
reg:= CharacterTableRegular( ordtbl, prime );
SetFilterObj( reg, IsLibraryCharacterTableRep );
#T just a hack ...
reg!.defect:= libtbl.defect;
reg!.block:= libtbl.block;
if IsBound( libtbl.decinv ) then
reg!.decinv:= libtbl.decinv;
fi;
if IsBound( libtbl.basicset ) then
reg!.basicset:= libtbl.basicset;
fi;
if IsBound( libtbl.brauertree ) then
reg!.brauertree:= libtbl.brauertree;
fi;
#T end of the hack ...
# Concatenate the lines of the `InfoText' component if necessary.
if not IsBound( libtbl.InfoText ) then
SetInfoText( reg, "(no info text)" );
elif IsString( libtbl.InfoText ) then
SetInfoText( reg, libtbl.InfoText );
else
SetInfoText( reg, Concatenation( libtbl.InfoText ) );
fi;
# If automorphisms are known (list of generators), convert to a group.
if IsBound( libtbl.AutomorphismsOfTable ) then
SetAutomorphismsOfTable( reg,
GroupByGenerators( libtbl.AutomorphismsOfTable, () ) );
fi;
# Initialize some components.
if not IsBound( libtbl.decinv ) then
libtbl.decinv:= [];
fi;
block:= [];
defect:= [];
basicset:= [];
brauertree:= [];
decinv:= [];
# If the distribution to blocks is stored on the table
# then use it, otherwise compute it.
ordblocks:= InverseMap( PrimeBlocks( ordtbl, prime ).block );
# Get the blocks of factor groups if necessary;
# `factorblocks' is a list of pairs containing the names of the
# tables that hold the blocks and the offset of basic set characters.
if IsBound( libtbl.factorblocks ) then
suffix:= filename;
pos:= Position( suffix, '/' );
while pos <> fail do
suffix:= suffix{ [ pos+1 .. Length( suffix ) ] };
pos:= Position( suffix, '/' );
od;
for i in libtbl.factorblocks do
facttbl:= Concatenation( i[1], "mod", String( prime ) );
if IsBound( LIBTABLE.( suffix ).( facttbl ) ) then
facttbl:= LIBTABLE.( suffix ).( facttbl );
else
# The factor table is in another file (hopefully a rare case),
# or it is obtained from a construction.
facttbl:= CharacterTableFromLibrary( i[1] ) mod prime;
fi;
if block = [] then
offset:= 0;
else
offset:= Maximum( block ) + 1 - Minimum( facttbl!.block );
fi;
pos:= Length( defect );
Append( defect, facttbl!.defect );
Append( block, offset + facttbl!.block );
for j in [ 1 .. Length( facttbl!.defect ) ] do
if facttbl!.defect[j] <> 0 then
if IsBound( facttbl!.decinv ) and
IsBound( facttbl!.decinv[j] ) then
if IsInt( facttbl!.decinv[j] ) then
decinv[ pos + j ]:= facttbl!.decinv[ facttbl!.decinv[j] ];
else
decinv[ pos + j ]:= facttbl!.decinv[j];
fi;
brauertree[ pos + j ]:= fail;
basicset[ pos + j ]:= i[2] + facttbl!.basicset[j];
else
if IsInt( facttbl!.brauertree[j] ) then
brauertree[ pos + j ]:=
facttbl!.brauertree[ facttbl!.brauertree[j] ];
else
brauertree[ pos + j ]:= facttbl!.brauertree[j];
fi;
basicset[ pos + j ]:= ordblocks[ pos + j ]{
BasicSetBrauerTree( brauertree[ pos + j ] ) };
fi;
fi;
od;
od;
reg!.factorblocks:= libtbl.factorblocks;
#T a hack? (make the stored component evaluable)
fi;
pos:= Length( defect );
Append( defect, libtbl.defect );
Append( block, libtbl.block );
for j in [ 1 .. Length( libtbl.defect ) ] do
if libtbl.defect[j] <> 0 then
if IsBound( libtbl.decinv[j] ) then
if IsInt( libtbl.decinv[j] ) then
decinv[ pos + j ]:= libtbl.decinv[ libtbl.decinv[j] ];
else
decinv[ pos + j ]:= libtbl.decinv[j];
fi;
brauertree[ pos + j ]:= fail;
basicset[ pos + j ]:= libtbl.basicset[j];
else
if IsInt( libtbl.brauertree[j] ) then
brauertree[ pos + j ]:=
libtbl.brauertree[ libtbl.brauertree[j] ];
else
brauertree[ pos + j ]:= libtbl.brauertree[j];
fi;
basicset[ pos + j ]:= ordblocks[ pos + j ]{
BasicSetBrauerTree( brauertree[ pos + j ] ) };
fi;
fi;
od;
# compute the blocks and the irreducibles of each block,
# and assign them to the right positions;
# assign the known decomposition matrices and Brauer trees;
# ignore defect 0 blocks
irreducibles:= [];
restricted:= RestrictedClassFunctions( Irr( ordtbl ), reg );
modblocks := InverseMap( block );
result_blocks:= [];
for i in [ 1 .. Length( ordblocks ) ] do
if IsInt( ordblocks[i] ) then ordblocks[i]:= [ ordblocks[i] ]; fi;
if IsInt( modblocks[i] ) then modblocks[i]:= [ modblocks[i] ]; fi;
if defect[i] = 0 then
irreducibles[ modblocks[i][1] ]:= restricted[ ordblocks[i][1] ];
decinv[i]:= [ [1] ];
basicset[i]:= ordblocks[i];
else
if IsBound( basicset[i] ) then
if IsBound( brauertree[i] ) and brauertree[i] <> fail then
decinv[i]:= DecMatBrauerTree( brauertree[i]){
Filtered( [ 1 .. Length( ordblocks[i] ) ],
x -> ordblocks[i][x] in basicset[i] )
}^(-1) ;
fi;
if IsBound( decinv[i] ) then
irreducibles{ modblocks[i] }:=
List( decinv[i] * List( restricted{ basicset[i] },
ValuesOfClassFunction ),
vals -> Character( reg, vals ) );
else
Error( "at least one of the components <decinv>, <brauertree> ",
"must be bound at pos. ", i );
fi;
else
Print( "#E BrauerTable: no basic set for block ", i, "\n" );
fi;
fi;
result_blocks[i]:= rec( defect := defect[i],
ordchars := ordblocks[i],
modchars := modblocks[i],
decinv := decinv[i],
basicset := basicset[i] );
if IsBound( brauertree[i] ) and brauertree[i] <> fail then
result_blocks[i].brauertree:= brauertree[i];
fi;
od;
# instead of calling `Immutable' for the entries in the loop ...
MakeImmutable( ordblocks );
MakeImmutable( modblocks );
MakeImmutable( decinv );
MakeImmutable( basicset );
MakeImmutable( brauertree );
SetBlocksInfo( reg, result_blocks );
SetIrr( reg, irreducibles );
if IsBound( libtbl.CharacterParameters ) then
SetCharacterParameters( reg, libtbl.CharacterParameters );
fi;
# decode the `IrredInfo' value
# (contains 2nd indicator if the prime is 2, else nothing)
if IsBound( libtbl.indicator ) then
SetComputedIndicators( reg, [ , libtbl.indicator ] );
fi;
#T BAD HACK until incomplete tables disappeared ...
#T only file ctborth2 ...
if IsBound( libtbl.warning ) then
Print( "#W warning for table of `", Identifier( reg ), "':\n",
libtbl.warning, "\n" );
fi;
# Store additional information.
# for the moment just as components.
for entry in [ "version", "date" ] do
if IsBound( libtbl.( entry ) ) then
reg!.( entry ):= libtbl.( entry );
fi;
od;
for entry in [ "ClassInfo", "RootDatumInfo" ] do
if IsBound( libtbl.( entry ) ) then
Setter( ValueGlobal( entry ) )( reg, libtbl.( entry ) );
fi;
od;
# Store the Brauer table for the next call.
fld.( name ):= reg;
# Return the Brauer table.
return reg;
end );
#############################################################################
##
#M BrauerTableOp( <tbl>, <p> ) . . . . . . . . . . <p>-modular library table
##
## Let <tbl> be an ordinary character table from the GAP table library,
## and <p> be a prime integer.
## - If the <p>-modular Brauer table of <tbl> is stored in a library file
## then this table is returned.
## - If <tbl> is <p>-solvable then we delegate to the library method
## that is based on the Fong-Swan theorem.
## - If the construction information stored on <tbl> admits the construction
## of the <p>-modular Brauer table then the result of this construction
## is returned.
## - Otherwise fall back to the generic method.
##
InstallMethod( BrauerTableOp,
[ "IsOrdinaryTable and IsLibraryCharacterTableRep", "IsPosInt" ], SUM_FLAGS,
function( tbl, p )
local result, modtbl, info, t, orig, fus, rest, modtblMG, modtblGA,
tblG2, tblG3, found, cand, tblG, modtblG, modtblG2, modtblG3,
modtblH1, modtblG1, modtblH2, perm,
ordfacttbls, modfacttbls, modfacttbl, ordtbl, proj, inv, ker, irr;
result:= fail;
modtbl:= BrauerTableFromLibrary( tbl, p );
if modtbl <> fail then
result:= modtbl;
elif IsPSolvableCharacterTable( tbl, p ) then
# The generic method is preferred to table constructions.
TryNextMethod();
elif HasConstructionInfoCharacterTable( tbl ) then
info:= ConstructionInfoCharacterTable( tbl );
if IsList( info ) and info[1] = "ConstructPermuted" then
t:= CallFuncList( CharacterTableFromLibrary, info[2] );
orig:= t mod p;
if orig <> fail then
result:= CharacterTableRegular( tbl, p );
# Restrict the permutation (if given) to the `p'-regular classes.
fus:= GetFusionMap( result, tbl );
if IsBound( info[3] ) then
fus:= OnTuples( fus, Inverse( info[3] ) );
fi;
rest:= CompositionMaps( InverseMap( GetFusionMap( orig, t ) ),
fus );
SetIrr( result, List( Irr( orig ),
chi -> Character( result,
ValuesOfClassFunction( chi ){ rest } ) ) );
# Transfer 2-modular indicators if they are stored.
if p = 2 and HasComputedIndicators( orig ) and
IsBound( ComputedIndicators( orig )[2] ) then
ComputedIndicators( result )[2]:= ComputedIndicators( orig )[2];
fi;
# Transfer character parameters if they are stored.
if HasCharacterParameters( orig ) then
SetCharacterParameters( result, CharacterParameters( orig ) );
fi;
fi;
elif IsList( info ) and info[1] = "ConstructMGA" then
modtblMG:= CharacterTable( info[2] ) mod p;
modtblGA:= CharacterTable( info[3] ) mod p;
if ForAll( [ modtblMG, modtblGA ], IsCharacterTable ) then
result:= BrauerTableOfTypeMGA( modtblMG, modtblGA, tbl );
if result <> fail then
result:= result.table;
fi;
fi;
elif IsList( info ) and info[1] = "ConstructGS3" then
# The identifier of the table of the normal subgroup 'G'
# does not occur in the construction parameters.
# We know that the factor group modulo 'G' is a Frobenius group
# such that 'info[2]' modulo 'G' is a cyclic Frobenius complement.
tblG2:= CharacterTable( info[2] );
tblG3:= CharacterTable( info[3] );
if ForAll( [ tblG2, tblG3 ], IsCharacterTable ) then
found:= false;
for cand in Intersection( NamesOfFusionSources( tblG2 ),
NamesOfFusionSources( tblG3 ) ) do
tblG:= CharacterTable( cand );
if IsPrimeInt( Size( tblG2 ) / Size( tblG ) ) then
found:= true;
break;
fi;
od;
if found then
modtblG:= tblG mod p;
modtblG2:= tblG2 mod p;
modtblG3:= tblG3 mod p;
if ForAll( [ modtblG, modtblG2, modtblG3 ],
IsCharacterTable ) then
result:= CharacterTableOfTypeGS3( modtblG, modtblG2, modtblG3,
tbl,
Concatenation( Identifier( tbl ), "mod", String( p ) ) );
if Length( Irr( result.table ) ) =
Length( Irr( result.table )[1] ) then
result:= result.table;
else
# Not all irreducibles have been determined.
result:= fail;
fi;
fi;
fi;
fi;
elif IsList( info ) and info[1] = "ConstructIndexTwoSubdirectProduct"
then
modtblH1:= CharacterTable( info[2] ) mod p;
modtblG1:= CharacterTable( info[3] ) mod p;
modtblH2:= CharacterTable( info[4] ) mod p;
modtblG2:= CharacterTable( info[5] ) mod p;
if not fail in [ modtblH1, modtblG1, modtblH2, modtblG2 ] then
perm:= info[7];
if HasClassPermutation( tbl ) then
perm:= perm * ClassPermutation( tbl );
fi;
result:= CharacterTableOfIndexTwoSubdirectProduct(
modtblH1, modtblG1, modtblH2, modtblG2,
[ tbl, perm ] );
if result <> fail then
result:= result.table;
fi;
fi;
elif IsList( info ) and info[1] = "ConstructV4G" then
result:= fail;
if Length( info ) = 2 then
ordfacttbls:= List( info[2], CharacterTableFromLibrary );
modfacttbls:= List( ordfacttbls, x -> x mod p );
if not fail in modfacttbls then
result:= BrauerTableOfTypeV4G( tbl, modfacttbls );
fi;
else
modfacttbl:= CharacterTable( info[2] ) mod p;
if modfacttbl <> fail then
result:= CallFuncList( BrauerTableOfTypeV4G,
Concatenation( [ tbl, modfacttbl ],
info{ [ 3 .. Length( info ) ] } ) );
fi;
fi;
elif IsList( info ) and info[1] = "ConstructFactor" then
# If the kernel in question is the p-core then
# we would run into an infinite recursion
# if we try to compute the Brauer table for the big table,
# because the big table would delegate to the factor modulo
# its p-core.
ordtbl:= CallFuncList( CharacterTableFromLibrary, info[2] );
if ClassPositionsOfPCore( ordtbl, p ) = info[3] then
modtbl:= fail;
else
modtbl:= ordtbl mod p;
fi;
if modtbl <> fail then
result:= CharacterTableRegular( tbl, p );
proj:= GetFusionMap( modtbl, result );
if proj = fail then
result:= fail;
else
# It may happen that the kernel contains a nontrivial p-part.
proj:= ProjectionMap( proj );
inv:= InverseMap( GetFusionMap( modtbl, ordtbl ) );
ker:= Filtered( info[3], i -> IsBound( inv[i] ) );
ker:= inv{ ker };
irr:= List( Filtered( Irr( modtbl ),
x -> IsSubset( ClassPositionsOfKernel( x ),
ker ) ),
x -> Character( result, x{ proj } ) );
SetIrr( result, irr );
fi;
fi;
fi;
fi;
if result <> fail then
SetFilterObj( result, IsLibraryCharacterTableRep );
if HasClassParameters( tbl ) then
SetClassParameters( result,
ClassParameters( tbl ){ GetFusionMap( result, tbl ) } );
fi;
return result;
fi;
TryNextMethod();
end );
#############################################################################
##
#M BrauerTable( <tblname>, <p> )
##
InstallMethod( BrauerTable,
"for a string (the name of the table), and a prime",
[ "IsString", "IsInt" ],
function( tblname, p )
local tbl;
if not IsPrimeInt( p ) then
Error( "<p> must be a prime integer" );
fi;
tbl:= CharacterTable( tblname );
if tbl <> fail then
tbl:= BrauerTable( tbl, p );
fi;
return tbl;
end );
#############################################################################
##
#F CharacterTableSpecialized( <generic_table>, <q> ) . . . . specialise <q>
##
InstallGlobalFunction( CharacterTableSpecialized, function( gtab, q )
local taf, # record of the specialized table, result
genclass, #
classparam, #
genchar, #
charparam, #
parm, #
i, k, #
class; #
# Check if the argument is valid.
if not ( IsRecord( gtab ) and IsBound( gtab.isGenericTable ) ) then
Error( "this is not a generic character table" );
elif IsBound( gtab!.domain ) and not gtab!.domain( q ) then
Error( q, " is not a valid parameter for this generic table" );
fi;
# A generic character table must contain at least functions to compute
# the parametrisation of classes and characters.
if IsBound( gtab!.wholetable ) then
# If the generic table has a component `wholetable'
# (a function which takes the generic table and `q' as arguments),
# use this function to construct the whole table.
taf:= gtab!.wholetable( gtab, q );
else
taf := rec();
# Get the parametrisation of classes and characters.
# `genclass' stores for each class of the specialized character table
# the number of the class of the generic table it stems from.
# `classparam' stores the parameter of the special class.
# `genchar' and `charparam' do the same for characters.
if not IsBound( gtab!.classparam )
or not IsBound( gtab!.charparam ) then
Error( "components `classparam' and `charparam' are missing" );
fi;
genclass := [];
classparam := [];
for i in [ 1 .. Length( gtab!.classparam ) ] do
parm := gtab!.classparam[i](q);
Append( classparam, parm );
Append( genclass, List( parm, j -> i ) );
od;
genchar := [];
charparam := [];
for i in [ 1 .. Length( gtab!.charparam ) ] do
parm := gtab!.charparam[i](q);
Append( charparam, parm );
Append( genchar, List( parm, j -> i ) );
od;
# Compute the name of the table.
if IsBound( gtab!.specializedname ) then
taf.Identifier:= gtab!.specializedname( q );
ConvertToStringRep( taf.Identifier );
fi;
# Compute the group order.
if IsBound( gtab!.size ) then
taf.Size := gtab!.size(q);
fi;
# Compute centralizer and representative orders.
if IsBound( gtab!.centralizers ) then
taf.SizesCentralizers := List( [ 1 .. Length( classparam ) ],
j -> gtab!.centralizers[ genclass[j] ]( q, classparam[j] ) );
fi;
if IsBound( gtab!.orders ) then
taf.OrdersClassRepresentatives :=
List( [ 1 .. Length( classparam ) ],
j -> gtab!.orders[ genclass[j] ]( q, classparam[j] ) );
fi;
# Compute the power maps.
taf.ComputedPowerMaps := [];
if IsBound( gtab!.powermap ) and IsBound( taf.Size ) then
for i in Reversed( PrimeDivisors( taf.Size ) ) do
taf.ComputedPowerMaps[i] := [];
for class in Reversed( [1 .. Length( classparam ) ] ) do
parm := gtab!.powermap[genclass[class]](q, classparam[class],i);
k := 1;
while genclass[k] <> parm[1] or classparam[k] <> parm[2] do
k := k+1;
od;
taf.ComputedPowerMaps[i][class] := k;
od;
od;
fi;
# Perform some initialisations, if the necessary data are present.
if IsBound( gtab!.classtext ) then
taf.classtext := List( [ 1 .. Length( classparam ) ],
j -> gtab!.classtext[ genclass[j] ]( q, classparam[j] ) );
fi;
# Compute the character values.
if IsBound( gtab!.matrix ) then
taf.Irr := gtab!.matrix( q );
elif IsBound( gtab!.irreducibles ) then
taf.Irr := List( [ 1 .. Length( charparam ) ],
i -> List( [1..Length(classparam)],
j -> gtab!.irreducibles[genchar[i]][genclass[j]]
( q, charparam[i], classparam[j] ) ) );
fi;
taf.ClassParameters:= List( [ 1 .. Length( classparam ) ],
i -> [ genclass[i], classparam[i] ] );
taf.CharacterParameters:= List( [ 1 .. Length( charparam ) ],
i -> [ genchar[i], charparam[i] ] );
if IsBound( gtab!.text ) then
taf.InfoText:= Concatenation( "computed using ", gtab!.text );
fi;
if IsBound( gtab!.UnderlyingCharacteristic ) then
taf.UnderlyingCharacteristic:= gtab!.UnderlyingCharacteristic;
else
taf.UnderlyingCharacteristic:= 0;
fi;
fi;
# Objectify and return the table.
ConvertToLibraryCharacterTableNC( taf );
return taf;
end );
#############################################################################
##
#F TransferComponentsToLibraryTableRecord( <t>, <tbl> )
##
InstallGlobalFunction( TransferComponentsToLibraryTableRecord,
function( t, tbl )
local names, i, fld;
names:= ShallowCopy( RecNames( tbl ) );
Add( names, "Irr" );
# Set the supported attribute values.
for i in [ 1, 4 .. Length( SupportedCharacterTableInfo ) - 2 ] do
if not SupportedCharacterTableInfo[ i+1 ] in names
and Tester( SupportedCharacterTableInfo[i] )( t ) then
tbl.( SupportedCharacterTableInfo[ i+1 ] ):=
SupportedCharacterTableInfo[i]( t );
fi;
od;
# Set the supported library table components.
for fld in Difference( SupportedLibraryTableComponents, names ) do
if IsBound( t!.( fld ) ) then
tbl.( fld ):= t!.( fld );
fi;
od;
# Set the irreducibles if necessary.
if HasIrr( t ) and not IsBound( tbl!.Irr ) then
tbl.Irr:= List( Irr( t ), ValuesOfClassFunction );
fi;
end );
#############################################################################
##
#F InducedLibraryCharacters( <subtbl>, <tblrec>, <chars>, <fusionmap> )
##
## is the list of class function values lists
##
InstallGlobalFunction( InducedLibraryCharacters,
function( subtbl, tblrec, chars, fusion )
local j, # loop variables
centralizers, # centralizer orders in hte supergroup
nccl, # number of conjugacy classes of the group
subnccl, # number of conjugacy classes of the subgroup
suborder, # order of the subgroup
subclasses, # class lengths in the subgroup
induced, # list of induced characters, result
singleinduced, # one induced character
char; # one character to be induced
centralizers:= tblrec.SizesCentralizers;
nccl:= Length( centralizers );
suborder:= Size( subtbl );
subclasses:= SizesConjugacyClasses( subtbl );
subnccl:= Length( subclasses );
induced:= [];
for char in chars do
# preset the character with zeros
singleinduced:= ListWithIdenticalEntries( nccl, 0 );
# add the contribution of each class of the subgroup
for j in [ 1 .. subnccl ] do
if char[j] <> 0 then
singleinduced[ fusion[j] ]:= singleinduced[ fusion[j] ]
+ char[j] * subclasses[j];
fi;
od;
# adjust the values by multiplication
for j in [ 1 .. nccl ] do
singleinduced[j]:= singleinduced[j] * centralizers[j] / suborder;
od;
Add( induced, singleinduced );
od;
# Return the list of values lists.
return induced;
end );
#############################################################################
##
#F UnpackedCll( <cll> )
##
InstallGlobalFunction( UnpackedCll, function( cll )
local l, clmlist, # library list of the possible matrices
clf, # Clifford matrix record, result
pi; # permutation to sort library matrices
# Initialize the Clifford matrix record.
clf:= rec(
inertiagrps := cll[1],
fusionclasses := cll[2]
);
if Length( cll[2] ) = 1 then
clf.mat:= [ [ 1 ] ];
elif IsMatrix( cll[3] ) then
# is already unpacked, for example dimension 2
clf.mat:= cll[3];
else
# Fetch the matrix from the library.
cll:= cll[3];
clf.libname:= cll;
l:= cll[2];
clmlist:= LibraryTables( Concatenation( "clm", cll[1] ) );
if clmlist = fail or not IsBound( clmlist[l] ) then
Error( "sorry, component <mat> not found in the library" );
fi;
clf.mat:= List( clmlist[l][ cll[3] ], ShallowCopy );
# Sort the rows and columns of the Clifford matrix
# w.r.t. the explicitly given permutations.
if IsBound( cll[4] ) then
clf.mat:= Permuted( clf.mat, cll[4] );
fi;
if IsBound( cll[5] ) then
pi:= cll[5];
clf.mat:= List( clf.mat, x -> Permuted( x, pi ) );
fi;
fi;
return clf;
end );
#############################################################################
##
#F CllToClf( <tbl>, <cll> )
##
InstallGlobalFunction( CllToClf, function( tbl, cll )
local Ti, #
factor, # character table of the factor group G/N
classnames,
i, nr,
dim, # dimension of the matrix
clf, # expanded record
pos,
map;
Ti:= tbl!.cliffordTable.Ti;
factor:= Ti.tables[1];
classnames:= ClassNames( factor );
nr:= cll[2][1];
dim:= Length( cll[2] );
# Decode `cll'.
clf:= UnpackedCll( cll );
# Fill the Clifford matrix record.
clf.nr := nr;
clf.size := dim;
clf.order := factor.orders[nr];
clf.orders := [ factor.orders[nr] ];
clf.elname := classnames[nr];
clf.full := true;
# Compute the row weights $b_a = |C_{T_m/N}(gN)|$.
clf.roww:= List( [ 1 .. dim ],
i -> SizesCentralizers( Ti.tables[ cll[1][i] ] )[ cll[2][i] ] );
# Compute the column weights $m_k = |Cl_{G/N}(gN)| / |Cl_G(g_k)|$.
pos:= 0;
for map in Ti.fusions do
pos:= pos + Number( map, x -> x < nr );
od;
clf.colw:= List( [ 1 .. dim ],
i -> SizesConjugacyClasses( tbl )[ pos+i ] /
SizesConjugacyClasses( factor )[nr] );
#T !!
# if dim = 1 then
# if IsBound( cll[4] ) then
# clf.colw := [cll[4][2]];
# else
# clf.colw := [1];
# #T ??
# fi;
# elif dim = 2 then
#
# factor:= Ti.tables[ clf.inertiagrps[2] ];
# if not IsCharacterTable( factor ) then
# factor:= CallFuncList( CharacterTableFromLibrary, factor );
# fi;
#
# if IsBound( cll[4] ) then
# if cll[4][1] = 0 then #not really splitted
# clf.colw := cll[4][2]*[1, clf.roww[1]/clf.roww[2]];
# clf.mat:= [[1,1],[clf.roww[1]/clf.roww[2],-1]];
# else
# clf.colw := [ 1, cll[4][2]-1 ];
# clf.mat:= [[1,1],[cll[4][4]*clf.colw[2],-cll[4][4]]];
# fi;
# else
# clf.colw := [1, clf.roww[1]/clf.roww[2]];
# clf.mat:= [[1,1],[clf.colw[2],-1]];
# #T but this holds only for split cosets!
# fi;
# fi;
# Handle the special case of extraspecial groups.
if Length( cll ) = 4 then
clf.splitinfos:= rec( classindex := cll[4][1],
p := cll[4][2] );
if IsBound( cll[4][3] ) then
clf.splitinfos.numclasses:= cll[4][3];
fi;
if IsBound( cll[4][4] ) then
clf.splitinfos.root:= cll[4][4];
fi;
fi;
return clf;
end );
#############################################################################
##
#F CompleteGroup()
##
InstallGlobalFunction( CompleteGroup,
function() Error( "this is just a dummy function" ); end );
#############################################################################
##
#F OfThose()
##
InstallGlobalFunction( OfThose,
function() Error( "this is just a dummy function" ); end );
#############################################################################
##
#F CTblLib.GetAccessFunctionAttrs()
##
CTblLib.AccessFunctionAttrs:= fail;
CTblLib.GetAccessFunctionAttrs:= function()
local attrs, attr, name;
if CTblLib.AccessFunctionAttrs = fail then
attrs:= [ [ Identifier ], [ "dummy" ], [ "dummy" ] ];
for name in RecNames( CTblLib.Data.IdEnumerator.attributes ) do
attr:= CTblLib.Data.IdEnumerator.attributes.( name );
if IsBound( attr.name ) then
Add( attrs[1], ValueGlobal( attr.name ) );
Add( attrs[2], attr );
Add( attrs[3], CTblLib.Data.IdEnumeratorExt.attributes.( name ) );
fi;
od;
CTblLib.AccessFunctionAttrs:= attrs;
fi;
return CTblLib.AccessFunctionAttrs;
end;
#############################################################################
##
#F CTblLib.ValueOfFunction( <func>, <nam> )
##
CTblLib.ValueOfFunction:= function( func, nam )
local attrs, apos, val, attr, result;
# Initialize the attribute information if necessary.
attrs:= CTblLib.GetAccessFunctionAttrs();
apos:= Position( attrs[1], func );
val:= nam;
if apos = 1 then
# Applying the function yields the identifier of the table.
if IsCharacterTable( nam ) then
nam:= Identifier( nam );
fi;
return [ nam, val ];
elif apos = fail then
# Use the character table for computing this invariant.
if not IsCharacterTable( val ) then
val:= CharacterTable( nam );
fi;
return [ func( val ), val ];
else
# We fetch the known attribute value,
# or compute it and amend the cache.
if IsCharacterTable( nam ) then
nam:= Identifier( nam );
fi;
if nam in CTblLib.Data.IdEnumerator.identifiers then
# The table is not private, the value is stored.
attr:= attrs[2][ apos ];
return [ attr.attributeValue( attr, nam ), val ];
else
#T what about CTblLib.Data.IdEnumeratorExt?
# The table is private, the value may be missing.
#T TODO:
#T -> if val is the table then use it & set the value, only otherwise compute!
attr:= attrs[3][ apos ];
return [ attr.attributeValue( attr, nam ), val ];
fi;
fi;
end;
#############################################################################
##
#P IsQuasisimple( <tbl> )
##
if not IsBound( IsQuasisimple ) then
# Apparently the GAP library does not install a method for character tables.
DeclareProperty( "IsQuasisimple", IsObject );
DeclarePropertySuppCT( "IsQuasisimpleCharacterTable",
IsNearlyCharacterTable );
InstallMethod( IsQuasisimpleCharacterTable,
[ "IsOrdinaryTable" ],
tbl -> IsPerfect( tbl ) and
IsSimple( tbl / ClassPositionsOfCentre( tbl ) ) );
InstallMethod( IsQuasisimple, [ "IsOrdinaryTable" ],
IsQuasisimpleCharacterTable );
fi;
#############################################################################
##
#F CTblLib.AccessFunction( [{<func>, <val>}], <mode>
#F [: OrderedBy:= <func>] )
#F CTblLib.AccessFunction( [<func>, <val>,{ OfThose, <func>,}] <mode>
#F [: OrderedBy:= <func>] )
##
## <mode> must be one of "one", "all".
##
## If <mode> has the value "all" then we run over all library tables,
## and in the end we sort the result either according to the function
## stored in the global option "OrderedBy" or (the default) alphabetically.
##
## If <mode> has the value "one" there are several cases:
## - If no sorting is prescribed by the global option "OrderedBy" then take
## the first table that fits to the conditions in <args>.
## - If "OrderedBy" is one of the supported (cheap) attributes then run over
## the library tables in this order and return the first table that fits
## to <args>.
## - If "OrderedBy" is not one of the supported (cheap) attributes then
## anyhow we have to construct all library tables that fit to <args>.
## Thus we run over all library tables and keep always only the currently
## smallest one that fits, according to "OrderedBy".
##
CTblLib.AccessFunction:= function( args, mode )
local names, orderedby, attrs, pos, cheapconditions, otherconditions, i,
values, multinfo, simpinfo, autoinfo, result, key, name, vals,
newvals, val, ident, pp, ext, resul, newkey;
names:= LIBLIST.firstnames;
# Fetch the option value.
orderedby:= ValueOption( "OrderedBy" );
if orderedby = fail then
if IsEmpty( args ) then
if mode = "all" then
# all table names in the library
return ShallowCopy( names );
else
# one table name in the library
return names[1];
fi;
fi;
elif not IsFunction( orderedby ) then
Error( "global option OrderedBy, if given, must be a function" );
fi;
if mode = "one" or not IsEmpty( args ) then
# Rearrange the conditions until the first occurrence of 'OfThose'
# such that the cheap ones come first.
# For that, initialize the attribute information if necessary.
attrs:= CTblLib.GetAccessFunctionAttrs();
pos:= Position( args, OfThose );
if pos = fail then
pos:= Length( args ) + 1;
fi;
cheapconditions:= [];
otherconditions:= [];
for i in [ 1, 3 .. pos-2 ] do
if args[i] in attrs[1] then
Append( cheapconditions, args{ [ i, i+1 ] } );
else
Append( otherconditions, args{ [ i, i+1 ] } );
fi;
od;
Append( cheapconditions, otherconditions );
Append( cheapconditions, args{ [ pos .. Length( args ) ] } );
args:= cheapconditions;
fi;
if mode = "one" and orderedby in attrs[1] then
# Sort the library table names by the required ordering,
# and then forget about the global option.
names:= ShallowCopy( names );
values:= List( names,
nam -> CTblLib.ValueOfFunction( orderedby, nam )[1] );
StableSortParallel( values, names );
orderedby:= fail;
fi;
# table names of sporadic simple groups
# (sorted according to size)
multinfo:= List( LIBLIST.simpleInfo, x -> x[1] );
simpinfo:= List( LIBLIST.simpleInfo, x -> x[2] );
autoinfo:= List( LIBLIST.simpleInfo, x -> x[3] );
#T access gpisotyp data!
# Initialize the result.
if mode = "all" then
result:= [];
key:= [];
else
key:= fail;
fi;
# Loop over the names.
for name in names do
# Now there are two possibilities:
# Either one filters the list `list',
# or we reach an `OfThose', so we replace the current entry of `list'
# by the image under the mapping instruction after `OfThose'.
vals:= [ name ];
pos:= 1;
while pos <= Length( args ) do
newvals:= [];
for val in vals do
if args[ pos ] = OfThose then
if args[ pos + 1 ] in
[ SchurCover, AutomorphismGroup, CompleteGroup ] then
# These cases are supported via `ExtensionInfoCharacterTable'.
ident:= val;
if IsCharacterTable( ident ) then
ident:= Identifier( ident );
fi;
pp:= Position( simpinfo, ident );
if pp = fail then
if not IsCharacterTable( val ) then
val:= CharacterTable( val );
fi;
if HasExtensionInfoCharacterTable( val ) then
ext:= ExtensionInfoCharacterTable( val );
else
Error( "no info about multiplier and autom. group of `",
val, "' stored" );
fi;
else
ext:= [ multinfo[ pp ], autoinfo[ pp ] ];
fi;
if args[ pos + 1 ] <> AutomorphismGroup then
if ext[1] <> "" then
ident:= Concatenation( ext[1], ".", ident );
fi;
fi;
if args[ pos + 1 ] <> SchurCover then
if ext[2] <> "" then
ident:= Concatenation( ident, ".", ext[2] );
fi;
fi;
Add( newvals, ident );
#T in these cases,
#T better use `val' itself if the image is the simple group?
#T or avoid storing a possibly large number of tables?
#T or admit switching the mode?
else
# If a database attribute is available for the mapping
# then use it (`Maxes', `NamesOfFusionSources').
resul:= CTblLib.ValueOfFunction( args[ pos + 1 ], val );
val:= resul[2];
resul:= resul[1];
if IsString( resul ) then
Add( newvals, resul );
elif ForAll( resul, IsString ) then
UniteSet( newvals, resul );
else
Error( "<args>[", pos+1, "] must return a (list of) strings" );
fi;
fi;
else
# Check one condition.
resul:= CTblLib.ValueOfFunction( args[ pos ], val );
val:= resul[2];
resul:= resul[1];
if resul = args[ pos+1 ] or
( IsList( args[ pos+1 ] ) and resul in args[ pos+1 ] ) or
( IsFunction( args[ pos+1 ] ) and args[ pos+1 ]( resul ) )
then
Add( newvals, val );
fi;
fi;
od;
pos:= pos + 2;
vals:= newvals;
od;
# All conditions have been checked for `name'.
for val in vals do
if orderedby = fail then
if not IsString( val ) then
val:= Identifier( val );
fi;
if mode = "all" then
Add( result, val );
else
return val;
fi;
else
newkey:= CTblLib.ValueOfFunction( orderedby, val )[1];
if not IsString( val ) then
val:= Identifier( val );
fi;
if mode = "all" then
Add( key, newkey );
Add( result, val );
elif key = fail or newkey < key then
key:= newkey;
result:= val;
fi;
fi;
od;
od;
# Return the result.
if mode = "one" then
if key = fail then
return fail;
fi;
return result;
elif orderedby = fail then
return Set( result );
else
SortParallel( key, result );
return result;
fi;
end;
#############################################################################
##
#F AllCharacterTableNames( {<func>, <val>} )
#F AllCharacterTableNames( <func>, <val>, ...{, OfThose, <func>} )
##
InstallGlobalFunction( AllCharacterTableNames, function( arg )
return CTblLib.AccessFunction( arg, "all" );
end );
#############################################################################
##
#F OneCharacterTableName( {<func>, <val>} )
#F OneCharacterTableName( <func>, <val>, ...{, OfThose, <func>} )
##
InstallGlobalFunction( OneCharacterTableName, function( arg )
return CTblLib.AccessFunction( arg, "one" );
end );
#############################################################################
##
#F NameOfEquivalentLibraryCharacterTable( <ordtbl> )
#F NamesOfEquivalentLibraryCharacterTables( <ordtbl> )
##
CTblLib.AccessNamesOfEquivalentLibraryCharacterTables:=
function( ordtbl, mode )
local init, result, name, trans;
# Test cheap attributes first, and exclude duplicates.
init:= AllCharacterTableNames( Size, Size( ordtbl ),
NrConjugacyClasses, NrConjugacyClasses( ordtbl ),
IsDuplicateTable, false );
#T why not more invariants?
#T if ordtbl is itself a library table then make sure that the test is cheap!
# Do the hard test.
result:= [];
for name in init do
#T careful:
#T TransformingPermutationsCharacterTables computes table automorphisms
#T if necessary, but here we do not need them!
trans:= TransformingPermutationsCharacterTables(
CharacterTable( name ), ordtbl );
if trans <> fail then
if mode = "all" then
AddSet( result, name );
else
return name;
fi;
fi;
od;
# Return the result.
if mode <> "all" then
return fail;
else
# Add the names of duplicates.
for name in ShallowCopy( result ) do
UniteSet( result, IdentifiersOfDuplicateTables( name ) );
od;
return result;
fi;
end;
InstallGlobalFunction( NameOfEquivalentLibraryCharacterTable,
function( ordtbl )
if not IsOrdinaryTable( ordtbl ) then
Error( "usage: NameOfEquivalentLibraryCharacterTable( <ordtbl> )" );
fi;
return CTblLib.AccessNamesOfEquivalentLibraryCharacterTables( ordtbl,
"one" );
end );
InstallGlobalFunction( NamesOfEquivalentLibraryCharacterTables,
function( ordtbl )
if not IsOrdinaryTable( ordtbl ) then
Error( "usage: NamesOfEquivalentLibraryCharacterTables( <ordtbl> )" );
fi;
return CTblLib.AccessNamesOfEquivalentLibraryCharacterTables( ordtbl,
"all" );
end );
#############################################################################
##
#F UnloadCharacterTableData()
##
## This is equivalent to flushing the cache.
##
InstallGlobalFunction( UnloadCharacterTableData, function()
local name;
for name in RecNames( LIBTABLE.LOADSTATUS ) do
Unbind( LIBTABLE.( name ) );
od;
LIBTABLE.LOADSTATUS := rec();
LIBTABLE.TABLEFILENAME := "";
LIBTABLE.clmelab := [];
LIBTABLE.clmexsp := [];
end );
#T #############################################################################
#T ##
#T #F ShrinkClifford( <tbl> )
#T ##
#T InstallGlobalFunction( ShrinkClifford, function( tbl )
#T
#T local i, flds, cltbl;
#T
#T cltbl:= tbl!.cliffordTable;
#T cltbl.Ti.tables := cltbl.Ti.ident;
#T
#T cltbl.cliffordrecords:= [];
#T
#T for i in [1..cltbl.size] do
#T
#T cltbl.cliffordrecords[i]:= ClfToCll( cltbl.(i) );
#T Unbind( cltbl.(i) );
#T
#T od;
#T
#T Unbind( tbl.irreducibles);
#T #T how to remove attributes ??
#T Unbind( cltbl.Ti.ident );
#T Unbind( cltbl.Ti.expN );
#T
#T for flds in [ "name", "grpname", "elements", "isDomain", "operations",
#T "charTable", "size", "expN" ] do
#T Unbind( cltbl.(flds) );
#T od;
#T end );
#############################################################################
##
#F TextString( <text> )
##
InstallGlobalFunction( TextString, function( text )
local str, start, stop, line, len, pos;
str:= "[\n\"";
stop:= 1;
len:= Length( text );
while stop <= len do
start:= stop;
while stop <= len and text[stop] <> '\n' do
stop:= stop + 1;
od;
line:= text{ [ start .. stop-1 ] };
pos:= Position( line, '\"' );
while pos <> fail do
line:= Concatenation( line{ [ 1 .. pos-1 ] },
"\\\"", line{ [ pos+1 .. Length( line ) ] } );
pos:= Position( line, '\"', pos + 1 );
od;
Append( str, line );
if stop <= len then
Append( str, "\\n\",\n\"" );
stop:= stop+1; # skip the '\n'
fi;
od;
Append( str, "\"\n]" );
return str;
end );
#############################################################################
##
#F ShrinkChars( <chars> )
##
InstallGlobalFunction( ShrinkChars, function( chars )
local i, j, k, N, oldchars, linear, chi, fams, pos, ppos;
linear:= Filtered( chars, x -> x[1] = 1 );
fams:= GaloisMat( chars ).galoisfams;
chars:= ShallowCopy( chars );
oldchars:= ShallowCopy( chars );
if Length( linear ) > 1 then
ppos:= List( linear, x -> Position( chars, x ) );
for i in [ 1 .. Length( chars ) ] do
chi:= chars[i];
if not IsString( chi ) then
for j in [ 1 .. Length( linear ) ] do
pos:= Position( chars, Tensored( [ linear[j] ],[ chi ] )[1] );
if pos <> fail and pos > i and pos > ppos[j] then
chars[ pos ]:= Concatenation( "\n[TENSOR,[",
String(i),",",String( ppos[j] ),"]]");
fi;
od;
fi;
od;
fi;
for i in [ 1 .. Length( chars ) ] do
if IsList( fams[i] ) then
for j in [ 2 .. Length( fams[i][1] ) ] do
if fams[i][1][j] <= Length( chars ) then
chi:= chars[ fams[i][1][j] ];
if IsClassFunction( chi ) then
chi:= ValuesOfClassFunction( chi );
fi;
if not IsString( chi ) then
N:= Conductor( chi );
k:= First( [ 2..N ], x -> chi = List( oldchars[i],
y -> GaloisCyc(y,x) ) );
chars[ fams[i][1][j] ]:= Concatenation("\n[GALOIS,[",
String(i),",",String(k),"]]");
fi;
fi;
od;
fi;
od;
return chars;
end );
#T #############################################################################
#T ##
#T #F ClfToCll( <clf> )
#T ##
#T InstallGlobalFunction( ClfToCll, function( clf )
#T local p, # position of the Clifford matrix clm in CLM[*]
#T cll, # compressed record
#T clm, # the pure Clifford matrix consisting of "mat" and "colw"
#T clmlist, # list of stored cliffordrecords
#T l,
#T lname, # name of item in the library
#T list, #
#T tr;
#T
#T # Check the input.
#T if not IsRecord( clf ) or
#T not IsBound( clf.inertiagrps ) or
#T not IsBound( clf.fusionclasses ) or
#T not IsBound( clf.mat ) then
#T Error( "<clf> must be record with components `inertiagrps', `mat' ",
#T "and `fusionclasses'" );
#T fi;
#T
#T l:= Length( clf.mat[1] );
#T cll:= [ clf.inertiagrps, clf.fusionclasses ];
#T
#T if IsBound( clf.splitinfos ) then
#T lname := "exsp";
#T cll[4]:= [ clf.splitinfos.classindex, clf.splitinfos.p ];
#T if IsBound( clf.splitinfos.numclasses ) then
#T cll[4][3]:= clf.splitinfos.numclasses;
#T fi;
#T if IsBound( clf.splitinfos.root ) then
#T cll[4][4]:= clf.splitinfos.root;
#T fi;
#T else
#T lname := "elab";
#T fi;
#T
#T if l = 2 then
#T
#T # Store the full matrix.
#T cll[3]:= clf.mat;
#T
#T elif 2 < l then
#T
#T clm:= clf.mat;
#T cll[3]:= clm;
#T
#T # Try to find the matrix in the library of Clifford matrices.
#T clmlist := LibraryTables( Concatenation( "clm", lname ) );
#T if not IsList( clmlist ) then
#T Error( "#E ClfToCll: can't find library of Clifford matrices.\n" );
#T fi;
#T
#T if IsBound( clmlist[l] ) then
#T
#T list:= clmlist[l];
#T p:= Position( list, clm );
#T if p <> fail then
#T
#T # Just store the library code.
#T cll[3]:= [ lname, l, p ];
#T return cll;
#T
#T else
#T
#T # The matrix itself is not in the library.
#T # Perhaps it is contained up to permutations of rows/columns,
#T # in this case print an appropriate message.
#T for p in [ 1 .. Length( list ) ] do
#T
#T tr:= TransformingPermutations( clm, list[p] );
#T if tr <> fail then
#T
#T # The matrix can be permuted to a library matrix.
#T cll[3]:= [ lname, l, p ];
#T if tr.rows <> () then
#T cll[3][4]:= tr.rows^-1;
#T fi;
#T if tr.columns <> () then
#T cll[3][5]:= tr.columns^-1;
#T fi;
#T return cll;
#T
#T fi;
#T
#T od;
#T
#T Print( "#I Clifford matrix not found in the library\n" );
#T
#T # `clm' not found in library, either because given libname is wrong or
#T # the matrix must be added first by an authorized person.
#T # The order would be:
#T # PrintClmsToLib( <file>, [clf] );
#T
#T fi;
#T fi;
#T fi;
#T
#T return cll;
#T end );
#############################################################################
##
#F LibraryFusion( <name>, <fus> )
#F LibraryFusion( <s>, <t> )
##
InstallGlobalFunction( LibraryFusion, function( name, fus )
local poss, reps, source, target, string, linelen, i, str, spec;
if IsCharacterTable( name ) and IsCharacterTable( fus ) then
# Try to compute the information in question.
poss:= PossibleClassFusions( name, fus );
reps:= RepresentativesFusions( name, poss, fus );
source:= Identifier( name );
if Length( poss ) = 0 then
return fail;
elif Length( poss ) = 1 then
fus:= rec( name:= fus, map:= poss[1],
text:= "fusion map is unique" );
elif Length( reps ) = 1 then
fus:= rec( name:= fus, map:= poss[1],
text:= "fusion map is unique up to table aut." );
else
return fail;
fi;
elif IsCharacterTable( name ) then
source:= Identifier( name );
else
source:= name;
fi;
target:= fus.name;
if IsCharacterTable( target ) then
if HasClassPermutation( target )
and not IsOne( ClassPermutation( target ) ) then
Print( "#W target of the fusion map stores a class permutation\n" );
fi;
target:= Identifier( target );
fi;
# Initialize the result string.
string:= "";
# Print the source and destination.
Append( string, "ALF(\"" );
Append( string, source );
Append( string, "\",\"" );
Append( string, target );
# Initialize the current position in the line.
linelen:= Length( source ) + Length( target ) + 11;
# Add the values of the fusion map.
Append( string, "\",[" );
for i in fus.map do
str:= String( i );
if linelen + Length( str ) + 1 < 75 then
linelen:= linelen + Length( str ) + 1;
else
Append( string, "\n" );
linelen:= Length( str ) + 1;
fi;
Append( string, str );
Append( string, "," );
od;
string[ Length( string ) ]:= ']';
# If a text is bound, add it.
if IsBound( fus.text ) then
Append( string, "," );
Append( string, TextString( fus.text ) );
# If a specification is bound, add it.
if IsBound( fus.specification ) then
spec:= fus.specification;
if IsString( spec ) then
spec:= Concatenation( "\"", spec, "\"" );
fi;
Append( string, "," );
Append( string, spec );
fi;
fi;
Append( string, ");\n" );
return string;
end );
#############################################################################
##
#F LibraryFusionTblToTom( <tblname>, <fus> )
##
InstallGlobalFunction( LibraryFusionTblToTom, function( name, fus )
local string, target, linelen, i, str;
if IsCharacterTable( name ) then
name:= Identifier( name );
fi;
# Initialize the result string.
string:= "";
target:= fus.name;
if IsTableOfMarks( target ) then
target:= Identifier( target );
fi;
# Print the source and destination.
Append( string, "ARC(\"" );
Append( string, name );
Append( string, "\",\"tomfusion\",rec(name:=\"" );
Append( string, target );
Append( string, "\",map:=[" );
# Initialize the current position in the line.
linelen:= Length( string );
# Add the values of the fusion map.
for i in fus.map do
str:= String( i );
if linelen + Length( str ) >= 74 then
Add( string, '\n' );
linelen:= 0;
fi;
linelen:= linelen + Length( str ) + 1;
Append( string, str );
Add( string, ',' );
od;
string[ Length( string ) ]:= ']';
# If a text is bound, add it.
if IsBound( fus.text ) then
Add( string, ',' );
if linelen >= 71 then
Add( string, '\n' );
fi;
Append( string, "text:=" );
Append( string, TextString( fus.text ) );
fi;
# If a permutation of maxes is bound then add it.
if IsBound( fus.perm ) then
Add( string, ',' );
if linelen >= 71 then
Add( string, '\n' );
fi;
Append( string, "perm:=" );
Append( string, String( fus.perm ) );
fi;
Append( string, "));\n" );
return string;
end );
#############################################################################
##
#F CTblLib.ConsiderFactorBlocks( <tbl> )
##
## (This is a utility for `CTblLib.StringBrauer'.)
##
## Let <A>tbl</A> be the Brauer character table of a group <M>G</M>, say.
## If the &GAP; Character Table Library contains the tables of factor groups
## of <M>G</M> by central subgroups
## such that the irreducible characters of these factors precede the other
## characters in <A>tbl</A> then the blocks of the factors can be omitted
## in the library version of <A>tbl</A>,
## and references to the factors are stored instead.
## <P/>
## <Ref Func="CTblLib.ConsiderFactorBlocks"/> returns an empty record
## if no such replacement is possible because no table of a relevant
## factor group was found.
## If some factor fusion is missing or if the irreducibles of a factor table
## are sorted incompatibly then a record with the component <C>error</C>
## is returned, whose value is a string.
## Otherwise a record with the components <C>offset</C> and <C>info</C> is
## returned,
## the former being the number of those irreducible characters of <A>tbl</A>
## that are not covered by the factor groups,
## and the latter being the list that can be inserted as value of the
## <C>factorblocks</C> component in the library version of <A>tbl</A>;
## this is a list of pairs whose first entries are the names of the factor
## tables, and the second entries are the offsets of numbers of basic set
## characters.
## <P/>
## Currently this special treatment of factor groups is supported only for
## Brauer tables in the &ATLAS; of Finite Groups.
## This means that the central subgroups in question must have order
## divisible by <M>12</M>, and the ordering of characters in the tables of
## the factor groups must coincide with the ordering for the table given.
##
CTblLib.ConsiderFactorBlocks:= function( tbl )
local p, ordinary, factfus, classes, nsg, pos, mult, sizes, kernels, i,
kernel, size, name, facttbl, map, modfacttbl,
info,
nonfaith1,
nonfaith,
1faith,
ppos,
2faith,
nonfaith2,
nonfaith4,
4faith;
# Get the factor fusions stored on the ordinary table.
p:= UnderlyingCharacteristic( tbl );
ordinary:= OrdinaryCharacterTable( tbl );
factfus:= Filtered( ComputedClassFusions( ordinary ),
x -> Length( ClassPositionsOfKernel( x.map ) ) <> 1 );
# We must make sure that the fusions to all relevant factor tables
# are available. (Otherwise some blocks could be missing.)
#T (example: "2^2.L3(4).2_3" with fusion only to "2.L3(4).2_3"
#T but not to "L3(4).2_3")
classes:= SizesConjugacyClasses( ordinary );
nsg:= Filtered( ClassPositionsOfNormalSubgroups( ordinary ),
n -> 12 mod Sum( classes{ n } ) = 0 );
nsg:= Difference( nsg, [ [ 1 ] ] );
# Get the maximal normal subgroup of order dividing $12$.
pos:= 0;
mult:= 0;
sizes:= List( factfus, x -> 0 );
kernels:= [];
for i in [ 1 .. Length( factfus ) ] do
kernel:= ClassPositionsOfKernel( factfus[i].map );
size:= Sum( classes{ kernel } );
if 12 mod size = 0 then
RemoveSet( nsg, kernel );
sizes[i]:= size;
kernels[i]:= kernel;
if mult < size then
pos:= i;
mult:= size;
fi;
fi;
od;
if pos = 0 or ( mult mod p = 0 ) then
# No ordinary factor table was found,
# or the Brauer table can be constructed itself from that of a factor.
return rec();
elif not IsEmpty( nsg ) and
# If `kernels[ pos ]' describes a cyclic group then some fusions are
# missing; if the normal subgroup has the type 2^2 or 2^2x3 then only
# one factor fusion for kernels of order 2 or 6 is needed.
not ( sizes[ pos ] = 4 and Length( nsg ) = 2 and
List( nsg, n -> Sum( classes{ n } ) ) = [ 2, 2 ] ) and
not ( sizes[ pos ] = 12 and Length( nsg ) = 4 and
SortedList( List( nsg, n -> Sum( classes{ n } ) ) ) = [ 2, 2, 6, 6 ] ) then
#T and what if 2^2.G really comes with three nonisom. factors 2.G?
return rec( error:= "some fusion missing" );
fi;
# Get the table of the smallest factor group.
name:= factfus[ pos ].name;
facttbl:= CharacterTable( name );
# Check that the irreducibles fit together.
map:= factfus[ pos ].map;
if List( Irr( facttbl ), x -> x{ map } )
<> Irr( ordinary ){ [ 1 .. NrConjugacyClasses( facttbl ) ] } then
return rec( error:= "incompatible irred. for 1.G" );
fi;
modfacttbl:= facttbl mod p;
if modfacttbl = fail or not IsBound( modfacttbl!.defect ) then
return rec( error:= "missing components in 1.G" );
fi;
# Initialize the `factorblocks' info.
info:= [ [ name, 0 ] ];
nonfaith1:= Length( PrimeBlocks( facttbl, p ).defect );
nonfaith:= nonfaith1;
# If `mult' is a prime then we are done.
if mult <> 3 and mult <> 2 then
# Get the number of ordinary characters of `1.G'.
1faith:= Maximum( map );
# Get the number of faithful ordinary characters of `2.G'.
# (Note that there is no group `2.G' if `2^2.G' occurs and the three
# subgroups of order two inside the `2^2' are conjugate.)
ppos:= First( [ 1 .. Length( factfus ) ],
i -> sizes[i] = mult / 2
and IsSubset( kernels[ pos ], kernels[i] ) );
if ppos <> fail then
map:= factfus[ ppos ].map;
2faith:= Maximum( map ) - 1faith;
Add( info, [ factfus[ ppos ].name, 0 ] );
facttbl:= CharacterTable( factfus[ ppos ].name );
nonfaith2:= Length( PrimeBlocks( facttbl, p ).defect );
nonfaith:= nonfaith2;
# Check that the irreducibles fit together.
if List( Irr( facttbl ), x -> x{ map } )
<> Irr( ordinary ){ [ 1 .. NrConjugacyClasses( facttbl ) ] } then
return rec( error:= "incompatible irred. for 2.G" );
fi;
else
2faith:= 0;
fi;
# If `mult = 4' then we are done.
if mult = 6 then
# Consider `3.G'
# (offset is the number of ordinary faithful characters of `2.G').
ppos:= First( [ 1 .. Length( factfus ) ],
i -> sizes[i] = 2
and IsSubset( kernels[ pos ], kernels[i] ) );
Add( info, [ factfus[ ppos ].name, 2faith ] );
facttbl:= CharacterTable( factfus[ ppos ].name );
# Check that the irreducibles fit together.
map:= factfus[ ppos ].map;
if List( Irr( facttbl ), x -> x{ map } )
<> Irr( ordinary ){
Concatenation( [ 1 .. 1faith ], 1faith + 2faith +
[ 1 .. Maximum( map ) - 1faith ] ) } then
return rec( error:= "incompatible irred. for 3.G" );
fi;
nonfaith:= Length( PrimeBlocks( facttbl, p ).defect )
+ nonfaith2 - nonfaith1;
elif mult = 12 then
# If the normal subgroup of order 12 is cyclic then the proper factor
# groups have orders 1, 2, 4, 3, 6 (in this order).
# If the structure is 2^2x3 then the proper factor groups have orders
# 1, 2, 4, 3, 6 or 1, 4, 3 (in this order).
# Consider `4.G' or `2^2.G' (no offset).
ppos:= First( [ 1 .. Length( factfus ) ],
i -> sizes[i] = 3
and IsSubset( kernels[ pos ], kernels[i] ) );
Add( info, [ factfus[ ppos ].name, 0 ] );
facttbl:= CharacterTable( factfus[ ppos ].name );
nonfaith4:= Length( PrimeBlocks( facttbl, p ).defect );
# Check that the irreducibles fit together.
map:= factfus[ ppos ].map;
if List( Irr( facttbl ), x -> x{ map } )
<> Irr( ordinary ){ [ 1 .. NrConjugacyClasses( facttbl ) ] } then
return rec( error:= "incompatible irred. for [4].G" );
fi;
# Get the number of ordinary characters of `4.G' or `2^2.G' that
# are not characters of `2.G'.
4faith:= Maximum( map ) - 1faith - 2faith;
# Consider `3.G' (offset is the number of
# ordinary faithful characters of `2.G' and `4.G').
ppos:= First( [ 1 .. Length( factfus ) ],
i -> sizes[i] = 4
and IsSubset( kernels[ pos ], kernels[i] ) );
Add( info, [ factfus[ ppos ].name, 2faith + 4faith ] );
map:= factfus[ ppos ].map;
facttbl:= CharacterTable( factfus[ ppos ].name );
# Check that the irreducibles fit together.
if List( Irr( facttbl ), x -> x{ map } )
<> Irr( ordinary ){
Concatenation( [ 1 .. 1faith ], 1faith + 2faith + 4faith +
[ 1 .. Maximum( map ) - 1faith ] ) } then
return rec( error:= "incompatible irred. for 3.G" );
fi;
# Consider `6.G' (offset is the number of
# ordinary faithful characters of `4.G').
ppos:= First( [ 1 .. Length( factfus ) ],
i -> sizes[i] = 2
and IsSubset( kernels[ pos ], kernels[i] ) );
if ppos <> fail then
map:= factfus[ ppos ].map;
Add( info, [ factfus[ ppos ].name, 4faith ] );
facttbl:= CharacterTable( factfus[ ppos ].name );
nonfaith:= Length( PrimeBlocks( facttbl, p ).defect )
+ nonfaith4 - nonfaith2;
# Check that the irreducibles fit together.
if List( Irr( facttbl ), x -> x{ map } )
<> Irr( ordinary ){
Concatenation( [ 1 .. 1faith + 2faith ], 1faith + 2faith + 4faith +
[ 1 .. Maximum( map ) - 1faith - 2faith ] ) } then
return rec( error:= "incompatible irred. for 6.G" );
fi;
else
# The normal subgroup has the structure 2^2x3, and no invariant 2.
# So we have to take the table of 3.G as the missing factor table
# for computing the number of non-faithful characters..
#T ???
nonfaith:= Length( PrimeBlocks( facttbl, p ).defect )
+ nonfaith4 - nonfaith1;
fi;
fi;
fi;
# Return the ``shrink'' information.
return rec( offset := nonfaith,
info := info );
end;
#############################################################################
##
#F BlanklessPrintTo( <stream>, <obj>, <ncols>, <used>, <quote> )
##
InstallGlobalFunction( BlanklessPrintTo,
function( stream, obj, ncols, used, quote )
local PrintToEx, i, names, newstring, newstream, len, j;
# Print the arguments without separators and line breaks.
PrintToEx := function( arg )
local len, entry;
len:= Sum( arg, Length );
if ncols < used + len then
PrintTo( stream, "\n" );
used:= 0;
fi;
for entry in arg do
PrintTo( stream, entry );
od;
used:= used + len;
end;
if not IsEmptyString( obj ) and IsList( obj ) and IsEmpty( obj ) then
if used + 2 > ncols then
PrintTo( stream, "\n" );
used:= 0;
fi;
PrintTo( stream, "[]" );
used:= used + 2;
elif IsString( obj ) then
if quote then
if '\n' in obj then
PrintTo( stream, TextString( obj ) );
used:= 1;
else
PrintToEx( "\"", obj, "\"" );
fi;
else
PrintToEx( obj );
fi;
elif IsRange( obj ) and 2 < Length( obj ) then
PrintToEx( "[" );
used:= BlanklessPrintTo( stream, obj[1], ncols, used, true );
if obj[2] <> obj[1] + 1 then
PrintToEx( "," );
used:= BlanklessPrintTo( stream, obj[2], ncols, used, true );
fi;
PrintToEx( ".." );
used:= BlanklessPrintTo( stream, obj[ Length( obj ) ], ncols, used,
true );
PrintToEx( "]" );
elif IsList( obj ) then
PrintToEx( "[" );
for i in [ 1 .. Length( obj ) - 1 ] do
if IsBound( obj[i] ) then
used:= BlanklessPrintTo( stream, obj[i], ncols, used, true );
fi;
PrintToEx( "," );
od;
if not IsEmpty( obj ) then
used:= BlanklessPrintTo( stream, obj[ Length( obj ) ], ncols, used,
true );
fi;
PrintToEx( "]" );
elif IsRecord( obj ) then
PrintToEx( "rec(" );
names:= RecNames( obj );
for i in [ 1 .. Length( names ) - 1 ] do
PrintToEx( names[i], ":=" );
used:= BlanklessPrintTo( stream, obj.( names[i] ), ncols, used,
true );
PrintToEx( ",\n" );
used:= 0;
od;
if not IsEmpty( names ) then
i:= Length( names );
PrintToEx( names[i], ":=" );
used:= BlanklessPrintTo( stream, obj.( names[i] ), ncols, used,
true );
fi;
PrintToEx( ")" );
elif IsChar( obj ) then
PrintToEx( [ '\'', obj, '\'' ] );
elif IsPerm( obj ) or IsCyc( obj ) then
newstring:= "";
newstream:= OutputTextString( newstring, true );
SetPrintFormattingStatus( newstream, false );
PrintTo( newstream, obj );
CloseStream( newstream );
newstring:= ReplacedString( newstring, " ", "" );
i:= 1;
while i <= Length( newstring ) do
if not IsDigitChar( newstring[i] ) then
# This implies that a leading '-' character is preferably left
# at the end of lines; this is perhaps not a good idea.
PrintToEx( [ newstring[i] ] );
i:= i + 1;
else
j:= i + 1;
while j <= Length( newstring ) and IsDigitChar( newstring[j] ) do
j:= j+1;
od;
used:= BlanklessPrintTo( stream, newstring{ [ i .. j-1 ] }, ncols,
used, false );
i:= j;
fi;
od;
else
newstring:= "";
newstream:= OutputTextString( newstring, true );
PrintTo( newstream, obj );
CloseStream( newstream );
len:= Length( newstring );
if ncols < used + len then
PrintTo( stream, "\n" );
used:= 0;
fi;
PrintTo( stream, newstring );
used:= used + len;
fi;
return used;
end );
#############################################################################
##
#F CTblLib.BasicSet( <decmat> )
##
CTblLib.BasicSet:= function( decmat )
local lic, other, k, comb, old, diff, new, bs;
lic:= LinearIndependentColumns( TransposedMat( decmat ) );
if DeterminantMat( decmat{ lic } ) in [ 1, -1 ] then
return lic;
fi;
# The first choice was not successful.
# Exchange `k' members, starting with `k = 1'.
other:= Difference( [ 1 .. Length( decmat ) ], lic );
for k in [ 1 .. Minimum( Length( lic ), Length( other ) ) ] do
Info( InfoCharacterTable, 2,
"CTblLib.BasicSet: exchange ", k, " members" );
comb:= Combinations( other, k );
for old in Combinations( lic, k ) do
diff:= Difference( lic, old );
for new in comb do
bs:= Concatenation( diff, new );
if DeterminantMat( decmat{ bs } ) in [ 1, -1 ] then
return Set( bs );
fi;
od;
od;
od;
# There is no basic set that consists of ordinary irreducibles.
Info( InfoWarning, 1, "no basic set of irreducibles found" );
return fail;
end;
#############################################################################
##
#F CTblLib.AppendSpecial( <stream>, <ncols>, <chars>, <used> )
##
## Special cases are `Irr( tbl )' and `ProjectivesInfo( tbl )' since
## after the call of `ShrinkChars' they may
## contain strings, which shall be printed without `"'
##
CTblLib.AppendSpecial:= function( stream, ncols, chars, used )
local j;
used:= BlanklessPrintTo( stream, "[", ncols, used, false );
for j in [ 1 .. Length( chars ) ] do
if j <> 1 then
used:= BlanklessPrintTo( stream, ",", ncols, used, false );
fi;
if IsBound( chars[j] ) then
if IsString( chars[j] ) then
PrintTo( stream, chars[j] ); # strip the `"'
used:= Length( chars[j] );
else
used:= BlanklessPrintTo( stream, chars[j], ncols, used, false );
fi;
fi;
od;
return BlanklessPrintTo( stream, "]", ncols, used, false );
end;
#############################################################################
##
#F CTblLib.StringOfProjectivesInfo( <projinfo>, <name> )
##
CTblLib.StringOfProjectivesInfo:= function( projinfo, name )
local ncols, string, stream, used, j;
ncols:= 78;
string:= "";
stream:= OutputTextString( string, true );
SetPrintFormattingStatus( stream, false );
used:= BlanklessPrintTo( stream,
Concatenation( "ARC(\"", name,
"\",\"projectives\",[" ),
ncols, 0, false );
for j in projinfo do
used:= BlanklessPrintTo( stream,
Concatenation( "\"", j.name, "\"," ), ncols, used, false );
used:= CTblLib.AppendSpecial( stream, ncols,
ShrinkChars( EvalChars( j.chars ) ), used );
used:= BlanklessPrintTo( stream, ",", ncols, used, false );
od;
BlanklessPrintTo( stream, "]);", ncols, used, false );
PrintTo( stream, "\n" );
# Return the string.
CloseStream( stream );
return string;
end;
#############################################################################
##
#F CTblLib.StringOrdinary( <tbl> )
#F CTblLib.StringBrauer( <tbl> )
#F PrintToLib( <file>, <tbl> )
##
CTblLib.StringOrdinary:= function( tbl )
local ncols,
used,
string,
stream,
i, j,
name,
powermap,
primes,
tblinfo,
chars,
fld,
info,
fus,
names,
linelen,
done,
val,
libinfo,
maxes,
pos;
ncols:= 78;
string:= "";
stream:= OutputTextString( string, true );
SetPrintFormattingStatus( stream, false );
name:= Identifier( tbl );
# Step 1: Do the preparatory work, i.e.,
# shrink the Clifford records and remove the irreducibles,
# compute missing irreducibles, power maps, and table
# automorphisms if an underlying group is stored.
if IsLibraryCharacterTableRep( tbl )
and HasConstructionInfoCharacterTable( tbl )
and IsList( ConstructionInfoCharacterTable( tbl ) )
and ConstructionInfoCharacterTable( tbl )[1] = "ConstructClifford" then
if HasIrr( tbl ) then
Error( "handling of Clifford tables not yet installed!" );
ShrinkClifford( tbl );
fi;
elif HasUnderlyingGroup( tbl ) then
Irr( tbl );
for i in PrimeDivisors( Size( tbl ) ) do
PowerMap( tbl, i );
od;
AutomorphismsOfTable( tbl );
fi;
# Step 2: Print the compulsory components.
PrintTo( stream, Concatenation( "MOT(\"", name, "\",\n" ) );
if HasInfoText( tbl ) then
PrintTo( stream, TextString( InfoText( tbl ) ), ",\n" );
else
PrintTo( stream, "0,\n" );
fi;
if HasSizesCentralizers( tbl ) or HasSizesConjugacyClasses( tbl ) then
used:= BlanklessPrintTo( stream, SizesCentralizers( tbl ), ncols, 0,
false );
BlanklessPrintTo( stream, ",", ncols, used, false );
PrintTo( stream, "\n" );
else
PrintTo( stream, "0,\n" );
fi;
if HasComputedPowerMaps( tbl )
and 1 < Length( ComputedPowerMaps( tbl ) ) then
powermap:= ShallowCopy( ComputedPowerMaps( tbl ) );
if IsBound( powermap[1] ) then
Unbind( powermap[1] );
fi;
if HasIrr( tbl ) then
primes:= PrimeDivisors( Size( tbl ) );
for i in [ 2 .. Length( powermap ) ] do
if IsBound( powermap[i] ) and not i in primes then
Unbind( powermap[i] );
fi;
od;
fi;
used:= BlanklessPrintTo( stream, powermap, ncols, 0, false );
BlanklessPrintTo( stream, ",", ncols, used, false );
PrintTo( stream, "\n" );
else
PrintTo( stream, "0,\n" );
fi;
if HasIrr( tbl ) then
used:= CTblLib.AppendSpecial( stream, ncols, ShrinkChars( Irr( tbl ) ), 0 );
BlanklessPrintTo( stream, ",", ncols, used, false );
PrintTo( stream, "\n" );
else
PrintTo( stream, "0,\n" );
fi;
if HasAutomorphismsOfTable( tbl ) then
used:= BlanklessPrintTo( stream,
Filtered( GeneratorsOfGroup( AutomorphismsOfTable( tbl ) ),
p -> not IsOne( p ) ),
ncols, 0, false );
else
PrintTo( stream, "0" );
used:= 1;
fi;
if IsLibraryCharacterTableRep( tbl )
and HasConstructionInfoCharacterTable( tbl ) then
BlanklessPrintTo( stream, ",", ncols, used, false );
PrintTo( stream, "\n" );
if IsFunction( ConstructionInfoCharacterTable( tbl ) ) then
used:= BlanklessPrintTo( stream,
ConstructionInfoCharacterTable( tbl ), ncols, 0,
false );
else
used:= BlanklessPrintTo( stream,
ConstructionInfoCharacterTable( tbl ), ncols, 0,
false );
#T is this meaningful?
fi;
fi;
BlanklessPrintTo( stream, ");", ncols, used, false );
PrintTo( stream, "\n" );
# Step 3: Print the optional components.
# Print the representative orders only if they are not redundant.
if HasOrdersClassRepresentatives( tbl ) and
( IsEmpty( ComputedPowerMaps( tbl ) )
or OrdersClassRepresentatives( tbl )
<> ElementOrdersPowerMap( ComputedPowerMaps( tbl ) ) ) then
used:= BlanklessPrintTo( stream,
Concatenation( "ARC(\"", name,
"\",\"OrdersClassRepresentatives\"," ),
ncols, 0, false );
used:= BlanklessPrintTo( stream,
OrdersClassRepresentatives( tbl ), ncols, used, false );
BlanklessPrintTo( stream, ");\n", ncols, used, false );
fi;
# Shrink and print the projectives.
if HasProjectivesInfo( tbl ) then
PrintTo( stream,
CTblLib.StringOfProjectivesInfo( ProjectivesInfo( tbl ), name ) );
fi;
# Print remaining supported components of library tables.
for fld in SupportedLibraryTableComponents do
#T CTblLib.StringOrdinary also for ``tables with group'' !!
if IsBound( tbl!.( fld ) ) then
used:= BlanklessPrintTo( stream,
Concatenation( "ARC(\"", name, "\",\"", fld, "\"," ),
ncols, 0, false );
used:= BlanklessPrintTo( stream, tbl!.( fld ), ncols, used, true );
BlanklessPrintTo( stream, ");" , ncols, used, false );
PrintTo( stream, "\n" );
fi;
od;
# Print remaining supported attributes of ordinary tables.
done:= [
"AutomorphismsOfTable",
"ComputedClassFusions",
"ComputedPowerMaps",
"ConjugacyClasses",
"ConstructionInfoCharacterTable",
"ExtensionInfoCharacterTable",
"FusionToTom",
"IdentificationOfConjugacyClasses",
"Identifier",
"InfoText",
"Irr",
"IsPerfectCharacterTable",
"IsSimpleCharacterTable",
"IsSolvableCharacterTable",
"NamesOfFusionSources",
"NrConjugacyClasses",
"OrdersClassRepresentatives",
"ProjectivesInfo",
"SizesCentralizers",
"SizesConjugacyClasses",
"UnderlyingCharacteristic",
"UnderlyingGroup",
];
if HasSizesCentralizers( tbl ) then
Add( done, "Size" );
fi;
for i in [ 3, 6 .. Length( SupportedCharacterTableInfo ) ] do
fld:= SupportedCharacterTableInfo[ i-1 ];
if not fld in done
and Tester( SupportedCharacterTableInfo[ i-2 ] )( tbl ) then
val:= SupportedCharacterTableInfo[ i-2 ]( tbl );
# Omit empty lists, for example in the case `ComputedPrimeBlockss'.
if not IsList( val ) or not IsEmpty( val ) then
used:= BlanklessPrintTo( stream,
Concatenation( "ARC(\"", name, "\",\"", fld, "\"," ),
ncols, 0, false );
used:= BlanklessPrintTo( stream,
val,
ncols, used, true );
BlanklessPrintTo( stream, ");" , ncols, used, false );
PrintTo( stream, "\n" );
fi;
fi;
od;
if HasIsSimpleCharacterTable( tbl )
and IsSimpleCharacterTable( tbl ) then
BlanklessPrintTo( stream,
Concatenation( "ARC(\"", name, "\",\"isSimple\",true);\n" ),
ncols, 0, false );
fi;
if HasExtensionInfoCharacterTable( tbl ) then
BlanklessPrintTo( stream,
Concatenation( "ARC(\"", name, "\",\"extInfo\",[\"",
String( ExtensionInfoCharacterTable( tbl )[1] ),
"\",\"",
String( ExtensionInfoCharacterTable( tbl )[2] ),
"\"]);\n" ),
ncols, 0, false );
fi;
if HasFusionToTom( tbl ) then
PrintTo( stream, LibraryFusionTblToTom( tbl, FusionToTom( tbl ) ) );
fi;
# Add the fusion assignments
# (first the factor fusions, then the subgroup fusions).
for fus in Filtered( ComputedClassFusions( tbl ),
r -> Length( ClassPositionsOfKernel( r.map ) ) > 1 ) do
PrintTo( stream, LibraryFusion( name, fus ) );
od;
for fus in Filtered( ComputedClassFusions( tbl ),
r -> Length( ClassPositionsOfKernel( r.map ) ) = 1 ) do
PrintTo( stream, LibraryFusion( name, fus ) );
od;
# Write the names information to the file.
libinfo:= LibInfoCharacterTable( name );
if libinfo <> fail then
names:= [];
#T if IsBound( libinfo.othernames ) then
#T Append( names, libinfo.othernames );
#T fi;
#T if IsBound( libinfo.CASnames ) then
#T Append( names, libinfo.CASnames );
#T fi;
#T get the other names from somewhere ...
if not IsEmpty( names ) then
used:= BlanklessPrintTo( stream,
Concatenation( "ALN(\"", name, "\",[" ), ncols, 0, false );
for i in [ 1 .. Length( names )-1 ] do
used:= BlanklessPrintTo( stream,
Concatenation( "\"", names[i], "\"," ), ncols, used, false );
od;
BlanklessPrintTo( stream,
Concatenation( "\"", names[ Length( names ) ], "\"]);" ),
ncols, used, false );
PrintTo( stream, "\n" );
fi;
fi;
PrintTo( stream, "\n" );
# Return the string.
CloseStream( stream );
return string;
end;
CTblLib.StringBrauer:= function( tbl )
local ordtbl,
ordname,
prime,
info,
factorblocks,
block,
offset,
defect,
basicset,
brauertree,
decinv,
i,
decmat,
lic,
pos,
automorphisms,
indicator,
ncols,
string,
stream,
used,
additional;
ordtbl:= OrdinaryCharacterTable( tbl );
ordname:= Identifier( ordtbl );
prime:= UnderlyingCharacteristic( tbl );
# Fetch the blocks info.
info:= BlocksInfo( tbl );
# Check whether tables of factors groups are available
# that allow one to omit some of the blocks.
factorblocks:= CTblLib.ConsiderFactorBlocks( tbl );
# `block' component (position 4)
block:= InverseMap( List( info, r -> r.modchars ) );
# factor blocks (pos. 9)
offset:= 0;
if IsBound( factorblocks.info ) then
offset:= factorblocks.offset;
info:= info{ [ offset + 1 .. Length( info ) ] };
block:= Filtered( block, x -> offset < x );
factorblocks:= factorblocks.info;
else
factorblocks:= 0;
fi;
# `defect' component (position 5)
defect:= List( info, r -> r.defect );
# `basicset' component (position 6)
# `brauertree' component (position 7)
# `decinv' component (position 8)
basicset:= [];
brauertree:= [];
decinv:= [];
for i in [ 1 .. Length( info ) ] do
if info[i].defect = 1 then
brauertree[i]:= BrauerTree( DecompositionMatrix( tbl, i + offset ) );
# Replace multiple occurrences of a tree by an index.
pos:= Position( brauertree, brauertree[i] );
if pos < i then
brauertree[i]:= pos;
fi;
elif info[i].defect > 1 then
if IsBound( info[i].basicset ) and IsBound( info[i].decinv ) then
basicset[i]:= info[i].basicset;
decinv[i]:= info[i].decinv;
else
decmat:= DecompositionMatrix( tbl, i + offset );
lic:= CTblLib.BasicSet( decmat );
basicset[i]:= info[i].ordchars{ lic };
decinv[i]:= Inverse( decmat{ lic } );
fi;
# Replace multiple occurrences of a matrix by an index.
pos:= Position( decinv, decinv[i] );
if pos < i then
decinv[i]:= pos;
fi;
fi;
od;
# automorphisms (pos. 10)
if HasAutomorphismsOfTable( tbl ) then
automorphisms:= GeneratorsOfGroup( AutomorphismsOfTable( tbl ) );
else
automorphisms:= 0;
fi;
# indicator (pos. 11)
if prime = 2 and IsBound( ComputedIndicators( tbl )[2] ) then
indicator:= ComputedIndicators( tbl )[2];
else
indicator:= 0;
fi;
# Create the output stream.
ncols:= 78;
string:= "";
stream:= OutputTextString( string, true );
SetPrintFormattingStatus( stream, false );
# Print the header (positions 1, 2).
PrintTo( stream, "MBT(\"", ordname, "\",", prime, ",\n" );
# `InfoText' (pos. 3)
if not HasInfoText( tbl ) then
PrintTo( stream, "\"(no info text)\"" );
elif InfoText( tbl ) =
"origin: modular ATLAS of finite groups, tests: DEC, TENS" then
PrintTo( stream, "TEXT1" );
else
PrintTo( stream, TextString( InfoText( tbl ) ) );
fi;
PrintTo( stream, ",\n" );
# Print the other components.
for i in [ block, defect, basicset, brauertree, decinv,
factorblocks, automorphisms ] do
used:= BlanklessPrintTo( stream, i, ncols, 0, false );
BlanklessPrintTo( stream, ",", ncols, used, false );
PrintTo( stream, "\n" );
od;
used:= BlanklessPrintTo( stream, indicator, ncols, 0, false );
additional:= [ "version", "date", "ClassInfo", "RootDatumInfo" ];
if ForAny( additional, nam -> IsBound( tbl!.( nam ) ) ) then
BlanklessPrintTo( stream, ",", ncols, used, false );
PrintTo( stream, "\n" );
used:= BlanklessPrintTo( stream, "rec(", ncols, 0, false );
for i in additional do
if IsBound( tbl!.( i ) ) then
used:= BlanklessPrintTo( stream, i, ncols, used, false );
used:= BlanklessPrintTo( stream, ":=", ncols, used, false );
used:= BlanklessPrintTo( stream, tbl!.( i ), ncols, used, false );
BlanklessPrintTo( stream, ",", ncols, used, false );
PrintTo( stream, "\n" );
used:= 0;
fi;
od;
PrintTo( stream, ")" );
used:= 1;
fi;
# Complete the function call.
BlanklessPrintTo( stream, ");", ncols, used, false );
PrintTo( stream, "\n\n" );
# Close the stream.
CloseStream( stream );
return string;
end;
InstallGlobalFunction( PrintToLib, function( file, tbl )
local string, oldsize;
if not ( IsString( file ) and IsCharacterTable( tbl ) ) then
Error( "usage: PrintToLib( <file>, <tbl> ) for string <file>\n",
"and char. table <tbl>" );
fi;
if Length( file ) <= 3
or file{ [ Length( file ) - 3 .. Length( file ) ] } <> ".tbl" then
file:= Concatenation( file, ".tbl" );
fi;
if IsOrdinaryTable( tbl ) then
string:= CTblLib.StringOrdinary( tbl );
elif IsBrauerTable( tbl ) then
string:= CTblLib.StringBrauer( tbl );
fi;
oldsize:= SizeScreen();
SizeScreen( [ 80 ] );
AppendTo( file, string );
SizeScreen( oldsize );
end );
#T #############################################################################
#T ##
#T #F PrintClmsToLib( <file>, <clms> )
#T ##
#T InstallGlobalFunction( PrintClmsToLib, function( filename, clms )
#T
#T local ind, i, il, lclms, clm, size,
#T l, # clmname
#T clmlist, # list of cliffordmatrices in the library
#T lname, # name of the file in the library
#T ir, # the internal record used here of the library
#T found; # whether the clm is already in the library
#T
#T if not( IsCliffordTable( clms ) or
#T IsList( clms ) and ForAll( clms, x-> IsBound( x.mat ) and
#T IsBound( x.colw ) ) ) then
#T Error( "usage: PrintClmsToLib( <file>, <clms> ) for a list ",
#T "of cliffordrecords or a cliffordtable " );
#T fi;
#T
#T if IsList( clms ) then lclms := Length( clms );
#T else lclms := clms.size;
#T fi;
#T
#T ir := [];
#T for ind in [1..lclms] do
#T if IsList( clms ) then clm := clms[ind];
#T else clm := clms.(ind);
#T fi;
#T
#T size := 0;
#T if IsBound( clm.mat ) then size := Length( clm.mat[1] ); fi;
#T
#T if size = 0 then
#T Print("#I PrintClmsToLib: no <mat> and <colw>. Nothing done.\n");
#T elif size > 2 then
#T if IsBound( clm.splitinfos ) then
#T lname := "exsp";
#T else
#T lname := "elab";
#T fi;
#T l := Concatenation( lname, String( size ));
#T
#T clmlist := LibraryTables( Concatenation( "clm", lname ) );
#T found := false;
#T if IsBound( clmlist.(l) ) then
#T i := 0;
#T il := Length( clmlist.(l) );
#T while ( not found and i < il ) do
#T i := i+1;
#T found := clmlist.(l)[i][1] = clm.mat
#T and clmlist.(l)[i][2] = clm.colw;
#T od;
#T fi;
#T if not found and IsBound( ir[size] ) then
#T i := 0;
#T il := Length( ir[size] );
#T while ( not found and i < il ) do
#T i := i+1;
#T found := ir[size][i][1] = clm.mat
#T and ir[size][i][2] = clm.colw;
#T od;
#T fi;
#T
#T if not found then
#T if IsBound( ir[size] ) then
#T ir[size][Length( ir[size] )+1] :=
#T [clm.mat, clm.colw];
#T else
#T ir[size] := [ [clm.mat, clm.colw] ];
#T fi;
#T else
#T Print( "#I PrintClmsToLib: Matrix ", ind,
#T " already in library or in ", filename, ".\n" );
#T fi;
#T fi;
#T od;
#T
#T PrintTo( filename, ir, "\n" );
#T
#T return;
#T end );
#############################################################################
##
#F OrbitsResidueClass( <pq>, <set> )
##
InstallGlobalFunction( OrbitsResidueClass, function( pq, set )
local gen, orbs, pnt, orb, i;
if Length( pq ) = 2 then
# `pq' is a pair `[ <p>, <q> ]' where <p> is an odd prime power;
# take a residue class mod <p> of order <q>.
gen:= PowerModInt( PrimitiveRootMod( pq[1] ), Phi( pq[1] ) / pq[2],
pq[1] );
else
# We know that <k> has order <q> modulo <p>.
# `pq' is a triple `[ <p>, <q>, <k> ]' where <k> has order <q> modulo
# <p> and acts semiregularly on the nonidentity residue classes mod <p>.
gen:= pq[3];
fi;
orbs:= [];
while not IsEmpty( set ) do
pnt:= set[1];
orb:= [];
for i in [ 1 .. pq[2] ] do
orb[i]:= pnt;
pnt:= ( pnt * gen ) mod pq[1];
od;
Add( orbs, orb );
SubtractSet( set, orb );
od;
return orbs;
end );
#############################################################################
##
#M GroupInfoForCharacterTable( <tbl> )
##
InstallMethod( GroupInfoForCharacterTable,
[ "IsOrdinaryTable and IsLibraryCharacterTableRep" ],
tbl -> GroupInfoForCharacterTable( Identifier( tbl ) ) );
InstallOtherMethod( GroupInfoForCharacterTable,
[ "IsString" ],
function( id )
local result, name, attr, val, generic, i, bad, good, other;
if Length( CTblLib.Data.IdEnumerator.identifiers ) = 0 then
Info( InfoWarning, 1,
"the data for 'GroupInfoForCharacterTable' are not available" );
return [];
fi;
# Replace the name by the identifier if applicable.
id:= LibInfoCharacterTable( id );
if id = fail then
return [];
fi;
id:= id.firstName;
result:= [];
for name in CTblLib.Data.attributesRelevantForGroupInfoForCharacterTable do
if id in CTblLib.Data.IdEnumerator.identifiers then
attr:= CTblLib.Data.IdEnumerator.attributes.( name );
else
attr:= CTblLib.Data.IdEnumeratorExt.attributes.( name );
fi;
val:= attr.attributeValue( attr, id );
if name = "factorsOfDirectProduct" then
# Add only pairs of those factors that know group info.
generic:= [ "Cyclic", "Alternating", "Symmetric", "Dihedral" ];
val:= Filtered( val,
info -> info[2] <> "fail" and
ForAll( info[2], l -> l[1] in generic or
( Length( l ) = 1 and not IsEmpty(
GroupInfoForCharacterTable( l[1] ) ) ) ) );
elif name = "indiv" then
val:= ShallowCopy( val );
for i in [ 1 .. Length( val ) ] do
if val[i][1] in [ "IndividualGroupConstruction",
"FactorGroupOfPerfectSpaceGroup" ] then
# Show only what is needed to find the construction;
# the function in question will fetch the data.
val[i]:= [ Concatenation( "CTblLib.", val[i][1] ), [ id ] ];
fi;
od;
fi;
Append( result, val );
od;
Sort( result );
if ValueOption( "sort" ) <> fail then
# Use a heuristic to get reasonable constructions first.
# Note that for example "47:23" has '[ "AtlasSubgroup", [ "B", 30 ] ]'
# alphabetically first.
# Thus move 'AtlasSubgroup' to the end, and 'SmallGroup' to the start.
bad:= Filtered( result, x -> x[1] = "AtlasSubgroup" );
good:= Filtered( result, x -> x[1] = "SmallGroup" );
other:= Filtered( result, x -> not ( x in bad or x in good ) );
result:= Concatenation( good, other, bad );
fi;
return result;
end );
#############################################################################
##
#M KnowsSomeGroupInfo( <tbl> )
##
InstallMethod( KnowsSomeGroupInfo,
[ "IsOrdinaryTable" ],
tbl -> not IsEmpty( GroupInfoForCharacterTable( tbl ) ) );
#############################################################################
##
#F CharacterTableForGroupInfo( <info> )
##
InstallGlobalFunction( CharacterTableForGroupInfo, function( info )
local name, attr, value;
if not ( IsDenseList( info ) and Length( info ) = 2 ) then
Error( "<info> must be a list of length two" );
elif not IsString( info[1] ) then
Error( "<info>[1] must be a string" );
elif not IsList( info[2] ) then
Error( "<info>[2] must be the list of arguments of <info>[1]" );
fi;
for name in RecNames( CTblLib.Data.IdEnumerator.attributes ) do
attr:= CTblLib.Data.IdEnumerator.attributes.( name );
if attr.identifier = "indiv" and info[1] in [
"CTblLib.IndividualGroupConstruction",
"CTblLib.FactorGroupOfPerfectSpaceGroup" ] then
return CharacterTable( info[2][1] );
elif attr.identifier in
CTblLib.Data.attributesRelevantForGroupInfoForCharacterTable
and IsBound( attr.reverseEval ) then
value:= attr.reverseEval( attr, info );
if value <> fail then
return CharacterTable( value );
fi;
fi;
od;
return fail;
end );
#############################################################################
##
#F GroupForGroupInfo( <info> )
##
InstallGlobalFunction( GroupForGroupInfo, function( info )
local factors, genericnam, genericfun, entry, pos, grp, ginfo, func;
if not ( IsDenseList( info )
and Length( info ) = 2 and IsString( info[1] )
and IsList( info[2] ) and not IsEmpty( info[2] ) ) then
return fail;
elif info[1] = "DirectProductByNames" then
# Delegate to the factors.
factors:= [];
genericnam:= [ "Cyclic", "Alternating", "Symmetric", "Dihedral" ];
genericfun:= [ CyclicGroup, AlternatingGroup, SymmetricGroup,
DihedralGroup ];
for entry in info[2] do
if Length( entry ) = 2 and IsPosInt( entry[2] ) then
pos:= Position( genericnam, entry[1] );
if pos = fail then
return fail;
fi;
Add( factors, genericfun[ pos ]( entry[2] ) );
elif Length( entry ) = 1 then
grp:= fail;
for ginfo in GroupInfoForCharacterTable( entry[1] : sort:= true ) do
grp:= GroupForGroupInfo( ginfo );
if IsGroup( grp ) then
Add( factors, grp );
break;
fi;
od;
if grp = fail then
return fail;
fi;
else
return fail;
fi;
od;
if Length( factors ) < 2 then
return fail;
fi;
# Choose factors that fit together.
if IsPermGroup( factors[1] ) and IsPcGroup( factors[2] ) then
factors[2]:= Image( IsomorphismPermGroup( factors[2] ) );
elif IsPcGroup( factors[1] ) and IsPermGroup( factors[2] ) then
factors[1]:= Image( IsomorphismPermGroup( factors[1] ) );
fi;
return CallFuncList( DirectProduct, factors );
elif info[1] = "PerfectGroup" then
# Create a permutation group not a f.p. group.
return PerfectGroup( IsPermGroup, info[2][1], info[2][2] );
elif info[1] = "Aut" then
# Create the automorphism group of another group.
for entry in GroupInfoForCharacterTable( info[2][1] : sort:= true ) do
grp:= GroupForGroupInfo( entry );
if grp <> fail then
return AutomorphismGroup( grp );
fi;
od;
return fail;
elif info[1] = "AGL" then
# There is no function 'AGL' in GAP (yet).
info:= ShallowCopy( info );
info[1]:= "CTblLib.AGL";
fi;
pos:= Position( info[1], '.' );
if pos = fail then
func:= info[1];
if not IsBoundGlobal( func ) then
return fail;
fi;
func:= ValueGlobal( func );
else
func:= info[1]{ [ 1 .. pos - 1 ] };
if not IsBoundGlobal( func ) then
return fail;
fi;
func:= ValueGlobal( func );
if not IsRecord( func ) then
return fail;
fi;
func:= func.( info[1]{ [ pos + 1 .. Length( info[1] ) ] } );
fi;
if not IsFunction( func ) then
return fail;
fi;
return CallFuncList( func, info[2] );
end );
#############################################################################
##
#F GroupForTom( <tomidentifier>[, <repnr>] )
##
InstallGlobalFunction( GroupForTom, function( arg )
local tom;
if Length( arg ) = 1 and IsString( arg[1] ) then
tom:= TableOfMarks( arg[1] );
if tom <> fail and HasUnderlyingGroup( tom ) then
return UnderlyingGroup( tom );
fi;
elif Length( arg ) = 2 and IsString( arg[1] ) and IsPosInt( arg[2] ) then
tom:= TableOfMarks( arg[1] );
if tom <> fail and IsTableOfMarksWithGens( tom ) then
return RepresentativeTom( tom, arg[2] );
fi;
fi;
return fail;
end );
#############################################################################
##
#F AtlasStabilizer( <gapname>, <repname> )
##
InstallGlobalFunction( AtlasStabilizer, function( gapname, repname )
local fun, info, g;
if not IsBoundGlobal( "AllAtlasGeneratingSetInfos" ) then
return fail;
fi;
fun:= ValueGlobal( "AllAtlasGeneratingSetInfos" );
info:= First( fun( gapname, IsTransitive, true ),
r -> r.repname = repname );
if info = fail then
return fail;
fi;
g:= ValueGlobal( "AtlasGroup" )( info );
if g = fail then
return fail;
fi;
return Stabilizer( g, info.p );
end );
#############################################################################
##
#M IsAtlasCharacterTable( <tbl> )
##
InstallMethod( IsAtlasCharacterTable,
[ "IsOrdinaryTable" ],
tbl -> PositionSublist( InfoText( tbl ),
"origin: ATLAS of finite groups" ) <> fail );
InstallMethod( IsAtlasCharacterTable,
[ "IsBrauerTable" ],
tbl -> IsAtlasCharacterTable( OrdinaryCharacterTable( tbl ) ) );
#############################################################################
##
#M IsNontrivialDirectProduct( <tbl> )
##
InstallMethod( IsNontrivialDirectProduct,
[ "IsOrdinaryTable" ],
tbl -> not IsEmpty( ClassPositionsOfDirectProductDecompositions(
tbl ) ) );
#############################################################################
##
#M KnowsDeligneLusztigNames( <tbl> )
##
InstallMethod( KnowsDeligneLusztigNames,
[ "IsOrdinaryTable" ],
tbl -> DeltigLibGetRecord( Identifier( tbl ) ) <> fail );
#############################################################################
##
#M IsDuplicateTable( <tbl> )
##
## Perhaps it is not a good idea to force duplicate tables to be constructed
## via `ConstructPermuted',
## in the sense that there might be faster constructions.
##
InstallMethod( IsDuplicateTable,
[ "IsOrdinaryTable" ],
tbl -> IdentifierOfMainTable( tbl ) <> fail );
#############################################################################
##
#M IdentifierOfMainTable( <tbl> )
##
InstallMethod( IdentifierOfMainTable,
[ "IsOrdinaryTable" ],
function( tbl )
local result, info, n;
result:= Identifier( tbl );
if HasConstructionInfoCharacterTable( tbl ) then
info:= ConstructionInfoCharacterTable( tbl );
if IsList( info ) and info[1] = "ConstructPermuted" then
info:= info[2];
if Length( info ) = 1 then
# permuted library table
result:= info[1];
elif Length( info ) = 2 then
# perhaps one of the spinsym tables;
# this code is needed because these tables are not declared as
# permuted tables of library tables, and we *want* to regard them
# as duplicates
n:= info[2];
if n in [ 2 .. 19 ] then
if info[1] = "Alternating" then
if n = 3 then
result:= "C3";
elif n = 4 then
result:= "a4";
elif n <> 2 then
result:= Concatenation( "A", String( n ) );
fi;
elif info[1] = "Symmetric" then
if n = 2 then
result:= "C2";
elif n = 3 then
result:= "S3";
elif n = 4 then
result:= "s4";
elif n = 6 then
result:= "A6.2_1";
else
result:= Concatenation( "A", String( n ), ".2" );
fi;
elif info[1] = "DoubleCoverAlternating" then
if n = 2 then
result:= "C2";
elif n = 3 then
result:= "C6";
elif n = 4 then
result:= "2.L2(3)";
elif 2 < n and n < 14 then
result:= Concatenation( "2.A", String( n ) );
fi;
elif info[1] = "DoubleCoverSymmetric" then
if n = 2 then
result:= "C4";
elif n = 3 then
result:= "2.S3";
elif n = 4 then
result:= "2.Symm(4)";
elif n = 6 then
result:= "2.A6.2_1";
elif n < 15 then
result:= Concatenation( "Isoclinic(2.A", String( n ), ".2)" );
fi;
fi;
fi;
fi;
fi;
fi;
if result = Identifier( tbl ) then
return fail;
else
return result;
fi;
end );
#############################################################################
##
#M IdentifiersOfDuplicateTables( <tbl> )
##
InstallMethod( IdentifiersOfDuplicateTables,
[ "IsOrdinaryTable" ],
tbl -> AllCharacterTableNames( IdentifierOfMainTable,
Identifier( tbl ) ) );
InstallOtherMethod( IdentifiersOfDuplicateTables,
[ "IsString" ],
id -> AllCharacterTableNames( IdentifierOfMainTable, id ) );
#############################################################################
##
#V CTblLib.NameReplacements
##
CTblLib.NameReplacements:= CallFuncList( function()
local file, str;
file:= Filename( DirectoriesPackageLibrary( "ctbllib", "data" )[1],
"namerepl.json" );
str:= StringFile( file );
if str = fail then
Error( "the data file '", file, "' is not available" );
fi;
return TransposedMat( EvalString( str ) );
end, [] );
#############################################################################
##
#F StructureDescriptionCharacterTableName( <name> )
##
InstallGlobalFunction( StructureDescriptionCharacterTableName,
function( name )
local parts, tbl, pos;
parts:= PParseBackwards( name, [ IsChar, "M", IsDigitChar ] );
if parts <> fail then
# If the table has a `ConstructPermuted' description then
# replace the name.
tbl:= CharacterTable( name );
if HasConstructionInfoCharacterTable( tbl ) and
IsList( ConstructionInfoCharacterTable( tbl ) ) and
ConstructionInfoCharacterTable( tbl )[1] = "ConstructPermuted" and
Length( ConstructionInfoCharacterTable( tbl )[2] ) = 1 then
name:= ConstructionInfoCharacterTable( tbl )[2][1];
fi;
fi;
# Replace {} by ().
name:= ReplacedString( name, "{", "(" );
name:= ReplacedString( name, "}", ")" );
# Replace individual values.
pos:= Position( CTblLib.NameReplacements[1], name );
if pos <> fail then
name:= CTblLib.NameReplacements[2][ pos ];
fi;
return name;
end );
#############################################################################
##
#F GaloisPartnersOfIrreducibles( <tbl>, <characters>, <n> )
##
## Compute the Galois automorphism(s) carrying to the partner(s).
##
InstallGlobalFunction( GaloisPartnersOfIrreducibles,
function( tbl, characters, n )
local partners, # list of partners, result
chi, # loop over `characters'
N, # conductor of `chi'
k, # list of values representing the partner cohorts
facts, # collected factors of `N'
primes, # prime factors of `N'
2part, # $2$-part of `N'
3part, # $3$-part of `N'
NN, # part of `N' that is coprime to $2$ and $3$
kk, # list with possible prime residues for each in `k'
new, # admissible subsets of `kk'
p; # characteristic in the case of a Brauer table
if n < 3 then
Error( "only for n >=3" );
fi;
partners:= [];
for chi in characters do
N:= Conductor( chi );
# The rules of the ordinary Atlas apply for ordinary tables
# and for Brauer characters for which all algebraic conjugates
# are Brauer characters.
if IsOrdinaryTable( tbl )
or ( IsBrauerTable( tbl )
and ( n <> 12 or Identifier( tbl ) <> "12.M22mod11" )
#T is this reasonable?
and ForAll( PrimeResidues( N ),
k -> List( chi,
x -> GaloisCyc( x, k ) ) in characters ) ) then
if n <> 12 then
k:= [ n - 1 ];
else
k:= [ 5, 7, 11 ];
fi;
facts:= Collected( Factors( N ) );
primes:= List( facts, x -> x[1] );
if 2 in primes then
2part:= 2^facts[ Position( primes, 2 ) ][2];
else
2part:= 1;
fi;
if 3 in primes then
3part:= 3^facts[ Position( primes, 3 ) ][2];
else
3part:= 1;
fi;
NN:= N / ( 2part * 3part );
# The automorphism $*k^\prime$ that carries to the
# partner in the cohort given by $*k$
# is determined by the conditions that
# $k^\prime \equiv k \pmod{n}$,
# $k^\prime \equiv 1$ modulo each divisor of $N$ coprime to $n$,
# $k^\prime \equiv \pm 1$ modulo powers of $2$ and $3$
# dividing $n$,
# where $+1$ is preferred if there is a choice.
# (As an example, consider $N = 60$, $n = 3$, and $k = 2$
# where $11$ and $41$ are possible solutions,
# and the action on $20$-th roots of unity is different;
# this occurs for the group $3.U_3(11)$.)
# Note that we may have to replace $N$ by the l.c.m. of $N$
# and $n$, for example if $n = 6$ and $N$ is odd.
kk:= List( k, y -> Filtered( PrimeResidues( LcmInt( N, n ) ),
x -> x mod NN in [ 0, 1 ]
and x mod n = y
and x mod 2part in [ 2part-1, 1 ]
and x mod 3part in [ 3part-1, 1 ] ) );
for k in [ 1 .. Length( kk ) ] do
if Length( kk[k] ) = 1 then
kk[k]:= kk[k][1];
else
if 2part <> 1 then
new:= Filtered( kk[k], x -> x mod 2part = 1 );
if Length( new ) <> 0 then
kk[k]:= new;
fi;
fi;
if Length( kk[k] ) > 1 and 3part <> 1 then
new:= Filtered( kk[k], x -> x mod 3part = 1 );
if Length( new ) <> 0 then
kk[k]:= new;
fi;
fi;
kk[k]:= kk[k][1];
fi;
od;
else
p:= UnderlyingCharacteristic( tbl );
if n <> 12 then
if ( p-1 ) mod n <> 0 then
kk:= [ p ];
else
kk:= [ -1 ];
fi;
elif Identifier( tbl ) = "12.M22mod11" then
kk:= [ -1 ];
else
kk:= [ p, -1, -p ];
fi;
fi;
Add( partners, kk );
od;
return partners;
end );
#############################################################################
##
#F AtlasLabelsOfIrreducibles( <tbl>[, <short>] )
##
InstallGlobalFunction( AtlasLabelsOfIrreducibles, function( arg )
local tbl, # first argument, an ATLAS character table
short, # optional second argument, choice of short labels
ordtbl, # ordinary table of `tbl'
out, # index of the derived subgroup
centre, # centre of the derived subgroup
mult, # order of `centre'
nccl, # no. of conjugacy classes of `tbl'
charname, # string "\\chi" or "\\varphi"
der, # table of the derived subgroup corresp. to `tbl'
ordder, # table of the derived subgroup corresp. to `ordtbl'
pos, # position in a list
irr, # list of irreducibles
portions, # list that separates characters with different kernel
divisors, # list of divisors of the multiplier
i, j, k, l, # loop variables
labels, # list of labels, result
portionlbs, # list of labels for one portion
max, # current maximal offset
partners, # output of `GaloisPartnersOfIrreducibles'
special, # list of special cases requiring offsets
offsets, # current offsets in special case
fus, # various fusion maps
rest, # list of restricted characters
distrib, # positions of restrictions in `rest'
restchi, # one restriction to the derived subgroup
dec, # decomposition matrix of restrictions
derlabels, # list of labels of the derived subgroup
inv, # inverse map of `distrib'
dl, # list of nonzero coefficients in a decomposition
n, # length of `dl'
lb, # one label
intermed, # list of intermediate tables
index, # index of an inertia subgroup
fus1, # fusion from derived subgroup to intermediate group
fus2, # fusion from intermediate group to group
ext, # positions of extensions of a character
interirr; # irreducibles of an intermediate table
# Get the arguments.
tbl:= arg[1];
short:= Length( arg ) = 2 and ( arg[2] = "short" or arg[2] = true );
# `tbl' is assumed to be the table of a bicyclic extension
# of a simple group.
# Get the index `out' of the derived subgroup
# and the order `mult' of the centre of the derived subgroup.
if IsBrauerTable( tbl ) then
ordtbl:= OrdinaryCharacterTable( tbl );
else
ordtbl:= tbl;
fi;
out:= AbelianInvariants( ordtbl );
if not IsSSortedList( out ) then
Error( "<tbl> is not a bicyclic extension of a simple table" );
fi;
out:= Product( out, 1 );
centre:= ClassPositionsOfFittingSubgroup( ordtbl );
mult:= Sum( SizesConjugacyClasses( ordtbl ){ centre }, 0 );
if 12 mod mult <> 0 then
Error( "<tbl> is not a bicyclic extension of an ATLAS table" );
fi;
nccl:= NrConjugacyClasses( tbl );
# Initializations for the final labels.
if IsOrdinaryTable( tbl ) then
charname:= "\\chi_{";
else
charname:= "\\varphi_{";
fi;
# Compute label descriptions for the derived subgroup.
# (For tables of non-perfect groups,
# the labels are formed relative to the labels of the
# table of the derived subgroup,
# so we need this table and its label descriptions.)
if out = 1 then
der:= tbl;
ordder:= ordtbl;
else
ordder:= Identifier( ordtbl );
pos:= Length( ordder );
while IsDigitChar( ordder[ pos ] ) or ordder[ pos ] = '_' do
pos:= pos - 1;
od;
if pos = Length( ordder ) or ordder[ pos ] <> '.' then
Error( "derived subgroup table not given by identifier of <tbl>" );
fi;
ordder:= CharacterTable( ordder{ [ 1 .. pos-1 ] } );
if ordder = fail then
Info( InfoCharacterTable, 1 ,
"no derived character table of ", Identifier( ordtbl ),
" available" );
return fail;
fi;
if IsBrauerTable( tbl ) then
der:= BrauerTable( ordder, UnderlyingCharacteristic( tbl ) );
if der = fail then
Info( InfoCharacterTable, 1 ,
"no ", UnderlyingCharacteristic( tbl ), "-modular ",
"derived character table of ", Identifier( ordtbl ),
" available" );
return fail;
fi;
else
der:= ordder;
fi;
fi;
# `der' is the table of a central extension of a simple group.
# Each label is encoded by an integer (the subscript)
# or a list of length two (the pair of subscript and a Galois
# automorphism).
# Distribute the characters to different portions.
irr:= List( Irr( der ), ValuesOfClassFunction );
if mult <> 12 then
divisors:= DivisorsInt( mult );
else
divisors:= [ 1, 2, 4, 3, 6, 12 ];
fi;
# The `i'-th portion consists of those characters with
# conductor of the restriction to `centre' equal to `divisors[i]'.
if IsBrauerTable( der ) then
fus:= GetFusionMap( der, ordder );
centre:= Filtered( [ 1 .. Length( fus ) ], i -> fus[i] in centre );
fi;
portions:= List( divisors,
i -> Filtered( [ 1 .. NrConjugacyClasses( der ) ],
j -> Conductor( irr[j]{ centre } ) = i ) );
labels:= [];
max:= 0;
for i in [ 1 .. Length( portions ) ] do
if divisors[i] < 3 then
# The $i$-th label has index $i$.
Append( labels, [ 1 .. Length( portions[i] ) ] + max );
max:= Maximum( labels );
else
# Some of the characters are not printed in the {\ATLAS},
# therefore the labels are more complicated.
portionlbs:= [];
# Compute the Galois automorphisms mapping the printed
# characters to their partner(s).
partners:= GaloisPartnersOfIrreducibles( der, irr{ portions[i] },
divisors[i] );
for k in [ 1 .. Length( portions[i] ) ] do
j:= portions[i][k];
# Get the position(s) of the partner(s).
pos:= List( partners[k],
x -> Position( irr,
List( irr[j],
y -> GaloisCyc( y, x ) ) ) );
# Construct labels relative to the *first* character
# of each set with the same proxy.
if j < Minimum( pos ) then
max:= max + 1;
portionlbs[k]:= max;
for l in [ 1 .. Length( pos ) ] do
portionlbs[ Position( portions[i], pos[l] ) ]:=
[ max, partners[k][l] ];
od;
fi;
od;
Append( labels, portionlbs );
fi;
od;
# Adjust the labels in the special cases
# $A_6$, $A_7$, $L_3(4)$, $M_{22}$, $U_4(3)$, $O_7(3)$,
# $U_6(2)$, $Suz$, $Fi_{22}$, and ${}^2E_6(2)$.
# For these groups, $6$ divides the multiplier,
# so at least $3.G$ needs an offset.
special:= [
"3.A6", [ 7, 6 ],
"3.A6mod5", [ 5, 4 ],
"3.A7", [ 9, 7 ],
"3.A7mod5", [ 8, 6 ],
"3.A7mod7", [ 7, 5 ],
"3.L3(4)", [ 10, 21 ],
"4_2.L3(4)", [ 18, 6 ],
"6.L3(4)", [ 18, 13 ],
"12_1.L3(4)", [ 24, 7 ],
"12_2.L3(4)", [ 41, 11, 18, 6 ],
"4_2.L3(4)mod3", [ 16, 5 ],
"12_2.L3(4)mod3", [ 16, 5 ],
"3.L3(4)mod5", [ 8, 15 ],
"6.L3(4)mod5", [ 14, 9 ],
"12_1.L3(4)mod5", [ 18, 5 ],
"12_2.L3(4)mod5", [ 31, 7, 14, 4 ],
"3.L3(4)mod7", [ 8, 15 ],
"6.L3(4)mod7", [ 14, 9 ],
"12_1.L3(4)mod7", [ 18, 5 ],
"12_2.L3(4)mod7", [ 31, 7, 14, 4 ],
"3.M22", [ 12, 19 ],
"6.M22", [ 23, 8 ],
"3.M22mod5", [ 11, 10 ],
"6.M22mod5", [ 21, 7 ],
"3.M22mod7", [ 10, 9 ],
"6.M22mod7", [ 19, 6 ],
"3_1.U4(3)", [ 20, 35 ],
"3_2.U4(3)", [ 20, 78 ],
"6_1.U4(3)", [ 39, 16 ],
"6_2.U4(3)", [ 39, 59 ],
"12_2.U4(3)", [ 55, 43 ],
"3_2.U4(3)mod2", [ 12, 8 ],
"6_2.U4(3)mod2", [ 12, 8 ],
"12_2.U4(3)mod2", [ 12, 8 ],
"3_2.U4(3)mod5", [ 19, 73 ],
"6_2.U4(3)mod5", [ 37, 55 ],
"12_2.U4(3)mod5", [ 52, 40 ],
"3_2.U4(3)mod7", [ 18, 68 ],
"6_2.U4(3)mod7", [ 35, 51 ],
"12_2.U4(3)mod7", [ 49, 37 ],
"3.O7(3)", [ 58, 30 ],
"3.O7(3)mod5", [ 53, 27 ],
"3.O7(3)mod7", [ 56, 28 ],
"3.O7(3)mod13", [ 56, 28 ],
"3.Suz", [ 43, 33 ],
"3.Suzmod5", [ 35, 26 ],
"3.Suzmod7", [ 39, 30 ],
"3.Suzmod11", [ 42, 32 ],
"3.Suzmod13", [ 41, 31 ],
"3.Fi22", [ 65, 49 ],
"3.Fi22mod5", [ 59, 43 ],
"3.Fi22mod7", [ 62, 46 ],
"3.Fi22mod11", [ 61, 45 ],
"3.Fi22mod13", [ 63, 47 ],
"3.2E6(2)", [ "?", "?" ],
"3.2E6(2)mod5", [ "?", "?" ],
"3.2E6(2)mod7", [ "?", "?" ],
"3.2E6(2)mod11", [ "?", "?" ],
"3.2E6(2)mod13", [ "?", "?" ],
"3.2E6(2)mod17", [ "?", "?" ],
"3.2E6(2)mod19", [ "?", "?" ],
];
pos:= Position( special, Identifier( der ) );
if pos <> fail then
offsets:= special[ pos+1 ];
for i in [ 1 .. Length( labels ) ] do
if IsInt( labels[i] ) and labels[i] > offsets[1] then
labels[i]:= labels[i] + offsets[2];
elif IsList( labels[i] ) and labels[i][1] > offsets[1] then
labels[i][1]:= labels[i][1] + offsets[2];
elif Length( offsets ) > 2 then
if IsInt( labels[i] ) and labels[i] > offsets[3] then
labels[i]:= labels[i] + offsets[4];
elif IsList( labels[i] ) and labels[i][1] > offsets[3] then
labels[i][1]:= labels[i][1] + offsets[4];
fi;
fi;
od;
fi;
if out = 1 then
# Build the final labels from the subscripts.
for i in [ 1 .. Length( labels ) ] do
if IsInt( labels[i] ) then
lb:= Concatenation( charname, String( labels[i] ), "}" );
else
lb:= Concatenation( charname, String( labels[i][1] ), "}^{\\ast" );
k:= labels[i][2];
if k < 0 then
Append( lb, "\\ast" );
k:= -k;
fi;
if k <> 1 then
Append( lb, " " );
Append( lb, String( k ) );
fi;
Append( lb, "}" );
fi;
labels[i]:= lb;
od;
else
# Start with the labels for `der'.
# They are of the form `i' (for $\chi_i$)
# or `[ i, j ]' (for $\chi_i^{*j}$).
derlabels:= labels;
labels:= [];
fus:= GetFusionMap( der, tbl );
if fus = fail then
Info( InfoCharacterTable, 1 ,
"no fusion from ", der, " to ", tbl, " available" );
return fail;
fi;
# `rest' is the list of the different restrictions of `irr'
# to the derived subgroup.
# `distrib[i]' is the position of the restriction of `irr[i]'
# in `rest'.
rest:= [];
distrib:= [];
irr:= List( Irr( tbl ), ValuesOfClassFunction );
for i in [ 1 .. Length( irr ) ] do
restchi:= irr[i]{ fus };
pos:= Position( rest, restchi );
if pos = fail then
Add( rest, restchi );
pos:= Length( rest );
fi;
distrib[i]:= pos;
od;
# Compute the decompositions of `rest' into irreducibles of `der'.
dec:= Decomposition( List( Irr( der ), ValuesOfClassFunction ),
rest, "nonnegative" );
# Store tables of intermediate groups if `out' is nonprime.
intermed:= [];
# Compute the labels of `irr'.
# The number $n$ of different irreducibles of `der' contained in
# a restriction divides `out'.
# Let `outprime' be the $p^\prime$ part of `out'.
# The following labels occur.
# - If $n = 1$ then the labels are of the form
# $\chi_{i,k}$ and $\chi_{i,k}^{*j}$, respectively,
# where $0 \leq k < `outprime'$.
# - If $n$ equals `out' then the labels are of the form
# $\chi_{i1+i2+...+iout}$ or
# $\chi_{i1* j1 + i2* j2 +...+ iout* jout}$ .
# - In all other cases, `outprime' is either $4$ or $6$,
# and we need the intermediate tables.
inv:= InverseMap( distrib );
for i in [ 1 .. Length( rest ) ] do
if IsInt( inv[i] ) then
inv[i]:= [ inv[i] ];
fi;
dl:= Filtered( [ 1 .. Length( dec[i] ) ], j -> dec[i][j] <> 0 );
n:= Length( dl );
if n = 1 then
# extension case
for j in [ 1 .. Length( inv[i] ) ] do
if IsInt( derlabels[ dl[1] ] ) then
# extension
labels[ inv[i][j] ]:= Concatenation( charname,
String( derlabels[ dl[1] ] ), ",",
String( j-1 ), "}" );
else
# conjugate of an extension
lb:= Concatenation( charname, String( derlabels[ dl[1] ][1] ),
",", String( j-1 ), "}^{\\ast" );
k:= derlabels[ dl[1] ][2];
if k < 0 then
Append( lb, "\\ast" );
k:= -k;
fi;
if k <> 1 then
Append( lb, " " );
Append( lb, String( k ) );
fi;
Append( lb, "}" );
labels[ inv[i][j] ]:= lb;
fi;
od;
elif n = out then
# case of fusion
if short then
if ForAny( derlabels{ dl }, IsInt ) then
lb:= Concatenation( charname,
String( First( derlabels{ dl }, IsInt ) ),
"+}" );
else
j:= derlabels[ dl[1] ];
lb:= Concatenation( charname,
String( j[1] ),
"\\ast" );
k:= j[2];
if k < 0 then
Append( lb, "\\ast" );
k:= - k;
fi;
Append( lb, " " );
if k <> 1 then
Append( lb, String( k ) );
fi;
Append( lb, "+}" );
fi;
else
lb:= ShallowCopy( charname );
for j in derlabels{ dl } do
if IsInt( j ) then
Append( lb, Concatenation( String( j ), "+" ) );
else
Append( lb, String( j[1] ) );
Append( lb, "\\ast" );
k:= j[2];
if k < 0 then
Append( lb, "\\ast" );
k:= -k;
fi;
Append( lb, " " );
if k <> 1 then
Append( lb, String( k ) );
fi;
Append( lb, "+" );
fi;
od;
lb[ Length( lb ) ]:= '}';
fi;
labels[ inv[i][1] ]:= lb;
else
# We have an intermediate group $U$ such that the characters
# of the simple group extend to $U$ and the extensions of
# different characters fuse in the full extension.
# (In our cases, this intermediate group is uniquely determined
# because `out' is either $4$ or $6$.)
# The inertia subgroup has index `index'.
index:= Length( dl );
if not IsBound( intermed[ index ] ) then
intermed[ index ]:= List( Filtered(
ComputedClassFusions( ordder ),
x -> x.name in NamesOfFusionSources( ordtbl ) ),
y -> CharacterTable( y.name ) );
intermed[ index ]:= Filtered( intermed[ index ],
x -> Size( tbl ) / Size( x ) = index );
if Length( intermed[ index ] ) > 1 then
Error( "indermediate table of index ", index, " in ", ordtbl,
" not unique" );
fi;
intermed[ index ]:= intermed[ index ][1];
if IsBrauerTable( der ) then
intermed[ index ]:= BrauerTable( intermed[ index ],
UnderlyingCharacteristic( der ) );
fi;
fi;
interirr:= List( Irr( intermed[ index ] ),
ValuesOfClassFunction );
fus1:= GetFusionMap( der, intermed[ index ] );
fus2:= GetFusionMap( intermed[ index ], tbl );
for j in inv[i] do
# Construct the label string.
if short then
if ForAny( dl, x -> IsInt( derlabels[x] ) ) then
l:= First( dl, x -> IsInt( derlabels[x] ) );
# Get the extensions from `der' to the intermediate group.
ext:= ValuesOfClassFunction( Irr( der )[l] );
ext:= Filtered( [ 1 .. Length( interirr ) ],
x -> Irr( intermed[ index ] )[x]{ fus1 } = ext );
# Count which extension occurs in the restriction from `tbl'
# to the intermediate group.
k:= Decomposition( interirr, [ irr[j]{ fus2 } ],
"nonnegative" );
k:= First( [ 1 .. Length( ext ) ],
x -> k[1][ ext[x] ] <> 0 );
lb:= Concatenation( charname,
String( derlabels[l] ),
",",
String( k-1 ),
"+}" );
else
l:= dl[1];
# Get the extensions from `der' to the intermediate group.
ext:= ValuesOfClassFunction( Irr( der )[l] );
ext:= Filtered( [ 1 .. Length( interirr ) ],
x -> Irr( intermed[ index ] )[x]{ fus1 } = ext );
# Count which extension occurs in the restriction from `tbl'
# to the intermediate group.
k:= Decomposition( interirr, [ irr[j]{ fus2 } ],
"nonnegative" );
k:= First( [ 1 .. Length( ext ) ],
x -> k[1][ ext[x] ] <> 0 );
lb:= Concatenation( charname,
String( derlabels[l][1] ),
",",
String( k-1 ),
"\\ast" );
if derlabels[l][2] < 0 then
Append( lb, "\\ast" );
derlabels[l][2]:= -derlabels[l][2];
fi;
if derlabels[l][2] <> 1 then
Append( lb, " " );
Append( lb, String( derlabels[l][2] ) );
fi;
Append( lb, "+}" );
fi;
else
lb:= ShallowCopy( charname );
for l in dl do
# Get the extensions from `der' to the intermediate group.
ext:= ValuesOfClassFunction( Irr( der )[l] );
ext:= Filtered( [ 1 .. Length( interirr ) ],
x -> Irr( intermed[ index ] )[x]{ fus1 } = ext );
# Count which extension occurs in the restriction from `tbl'
# to the intermediate group.
k:= Decomposition( interirr, [ irr[j]{ fus2 } ],
"nonnegative" );
k:= First( [ 1 .. Length( ext ) ],
x -> k[1][ ext[x] ] <> 0 );
if IsInt( derlabels[l] ) then
Append( lb, String( derlabels[l] ) );
Append( lb, "," );
Append( lb, String( k-1 ) );
Append( lb, "+" );
else
Append( lb, String( derlabels[l][1] ) );
Append( lb, "," );
Append( lb, String( k-1 ) );
Append( lb, "\\ast" );
if derlabels[l][2] < 0 then
Append( lb, "\\ast" );
derlabels[l][2]:= -derlabels[l][2];
fi;
if derlabels[l][2] <> 1 then
Append( lb, String( derlabels[l][2] ) );
fi;
Append( lb, "+" );
fi;
od;
lb[ Length( lb ) ]:= '}';
fi;
labels[j]:= lb;
od;
fi;
od;
fi;
# Return the labels.
return labels;
end );
#############################################################################
##
## The rest of this file contains the interface between the GAP libraries
## of character tables and of tables of marks.
##
## The interface consists of methods for the operations `CharacterTable' and
## `TableOfMarks', with argument a table of marks and a character table,
## respectively.
## These methods try to get the corresponding character table resp.~table of
## marks from the library.
##
## If the required information is not found in the respective library,
## and if no group is available from which this information can be computed
## then `fail' returned.
##
## The availability of the required information is looked up in
## `LIBLIST.TOM_TBL_INFO'.
## This is a list of length two,
## the first entry being the list of identifiers of tables of marks,
## the second entry being the list of corresponding identifiers of library
## character tables.
##
## (If not both the library of character tables and the library of tables of
## marks are installed then the entries in `LIBLIST.TOM_TBL_INFO' are empty
## lists, hence the methods mentioned above become trivial.)
##
#############################################################################
##
#M TableOfMarks( <tbl> ) . . . . . . . . . . . . . . . for a character table
#M TableOfMarks( <G> )
##
## We delegate from <tbl> to the underlying group in the general case.
##
## If the argument is a group, we can use the known table of marks of the
## known ordinary character table.
##
InstallOtherMethod( TableOfMarks,
[ "IsCharacterTable and HasUnderlyingGroup" ],
tbl -> TableOfMarks( UnderlyingGroup( tbl ) ) );
InstallOtherMethod( TableOfMarks,
[ "IsGroup and HasOrdinaryCharacterTable" ], 100,
function( G )
local tbl;
tbl:= OrdinaryCharacterTable( G );
if HasTableOfMarks( tbl ) then
return TableOfMarks( tbl );
else
TryNextMethod();
fi;
end );
#############################################################################
##
#M TableOfMarks( <tbl> ) . . . . . . . . . . . for a library character table
##
## <#GAPDoc Label="TableOfMarks">
## <ManSection>
## <Meth Name="TableOfMarks" Arg="tbl"
## Label="for a character table from the library"/>
##
## <Description>
## Let <A>tbl</A> be an ordinary character table from the
## &GAP; Character Table Library,
## for the group <M>G</M>, say.
## If the <Package>TomLib</Package> package is loaded and contains the
## table of marks of <M>G</M> then there is a method based on
## <Ref Oper="TableOfMarks" Label="for a string" BookName="ref"/>
## that returns this table of marks.
## If there is no such table of marks but <A>tbl</A> knows its underlying
## group then this method delegates to the group.
## Otherwise <K>fail</K> is returned.
## <P/>
## <Example>
## gap> TableOfMarks( CharacterTable( "A5" ) );
## TableOfMarks( "A5" )
## gap> TableOfMarks( CharacterTable( "M" ) );
## fail
## </Example>
## </Description>
## </ManSection>
## <#/GAPDoc>
##
InstallOtherMethod( TableOfMarks,
[ "IsOrdinaryTable and IsLibraryCharacterTableRep" ],
function( tbl )
local pos;
pos:= Position( LIBLIST.TOM_TBL_INFO[2],
LowercaseString( Identifier( tbl ) ) );
if pos <> fail then
return TableOfMarks( LIBLIST.TOM_TBL_INFO[1][ pos ] );
elif HasUnderlyingGroup( tbl ) then
TryNextMethod();
fi;
return fail;
end );
#############################################################################
##
#M CharacterTable( <G> )
##
## If the argument is a group, we can use the known character table of the
## known table of marks.
##
InstallOtherMethod( CharacterTable,
[ "IsGroup and HasTableOfMarks" ], 100,
function( G )
local tom;
tom:= TableOfMarks( G );
if HasOrdinaryCharacterTable( tom ) then
return OrdinaryCharacterTable( tom );
else
TryNextMethod();
fi;
end );
#############################################################################
##
#F NameOfLibraryCharacterTable( <tomname> )
##
InstallGlobalFunction( NameOfLibraryCharacterTable, function( tomname )
local pos;
pos:= Position( LIBLIST.TOM_TBL_INFO[1], LowercaseString( tomname ) );
if pos <> fail then
pos:= LibInfoCharacterTable( LIBLIST.TOM_TBL_INFO[2][ pos ] );
if IsRecord( pos ) then
return pos.firstName;
fi;
fi;
return fail;
end );
#############################################################################
##
#F CTblLib.SetAttributesForSpinSymTable( <firstname> )
##
## The character tables with the identifiers '"Alt(<n>)"', '"2.Alt(<n>)"',
## '"Sym(<n>)"', and '"2.Sym(<n>)"', for $1 \leq <n> \leq 19$,
## are provided by the SpinSym package.
## If the values of the attributes supported by 'CTblLib.Data.IdEnumerator'
## are not set then calls of 'AllCharacterTableNames' will have to compute
## these values and thus will be slow.
## Moreover, several SpinSym tables are duplicates of tables from CTblLib,
## so we have to notify at least this information.
##
CTblLib.SetAttributesForSpinSymTable:= function( spinsymname )
local store_data, enum, attrs, enumext, attrsext, posext, n,
An, Sn, 2An, 2Sn, libname, attr, pos, repl, str, values;
# auxiliary function that adds data for `libname` either directly
# to the attribute `dataname` or to a global variable tat gets evaluated
# as soon as the underlying GAP package becomes available
store_data:= function( libname, dataname )
local nam, str, data, values;
if libname = fail then
return;
fi;
nam:= Concatenation( "Source_data_", dataname );
if not IsBound( CTblLib.Data.( nam ) ) then
str:= StringFile( Filename( DirectoriesPackageLibrary( "CTblLib",
"data" ),
Concatenation( "grp_", dataname, ".json" ) ) );
CTblLib.Data.( nam ):= AGR.GapObjectOfJsonText( str ).value;
fi;
data:= CTblLib.Data.( nam ).automatic;
values:= PositionSorted( data, [ libname ] );
if not IsBound( data[ values ] ) then
return;
fi;
values:= data[ values ];
if values[1] <> libname then
return;
elif IsBound( attrsext.( dataname ) ) then
Add( attrsext.( dataname ).data.automatic,
[ spinsymname, values[2] ] );
else
# The data will be inserted in an extension file.
nam:= Concatenation( "IdEnumeratorExt_attributes_", dataname,
"_data_automatic" );
if not IsBound( CTblLib.( nam ) ) then
CTblLib.( nam ):= [];
fi;
Add( CTblLib.( nam ), [ spinsymname, values[2] ] );
fi;
end;
enum:= CTblLib.Data.IdEnumerator;
if enum.identifiers = [] then
# No attributes are available.
return;
fi;
attrs:= enum.attributes;
enumext:= CTblLib.Data.IdEnumeratorExt;
attrsext:= enumext.attributes;
posext:= Position( enumext.identifiers, spinsymname );
n:= Int( spinsymname{ [ Position( spinsymname, '(' ) + 1
.. Length( spinsymname ) - 1 ] } );
if n < 2 or 19 < n then
return;
fi;
An:= ( spinsymname[1] = 'A' );
Sn:= ( spinsymname[1] = 'S' );
2An:= ( spinsymname[3] = 'A' );
2Sn:= ( spinsymname[3] = 'S' );
libname:= fail;
if An then
if n = 3 then
libname:= "C3";
elif n = 4 then
libname:= "a4";
elif n <> 2 then
libname:= Concatenation( "A", String( n ) );
fi;
elif Sn then
if n = 2 then
libname:= "C2";
elif n = 3 then
libname:= "S3";
elif n = 4 then
libname:= "s4";
elif n = 6 then
libname:= "A6.2_1";
else
libname:= Concatenation( "A", String( n ), ".2" );
fi;
elif 2An then
if n = 2 then
libname:= "C2";
elif n = 3 then
libname:= "C6";
elif n = 4 then
libname:= "2.L2(3)";
else
libname:= Concatenation( "2.A", String( n ) );
fi;
elif 2Sn then
if n = 2 then
libname:= "C4";
elif n = 3 then
libname:= "2.S3";
elif n = 4 then
libname:= "2.Symm(4)";
elif n = 6 then
libname:= "2.A6.2_1";
else
# Note that the library tables with identifier "2.A<n>.2"
# are not the tables 'CharacterTable( "DoubleCoverSymmetric", <n> )'.
libname:= Concatenation( "Isoclinic(2.A", String( n ), ".2)" );
fi;
fi;
if libname <> fail and
Position( LIBLIST.allnames, LowercaseString( libname ) ) = fail then
libname:= fail;
fi;
# AbelianInvariants:
attr:= attrsext.AbelianInvariants;
if spinsymname in [ "2.Sym(2)", "2.Sym(3)" ] then
attr.data[ posext ]:= [ 4 ];
elif Sn or 2Sn then
attr.data[ posext ]:= [ 2 ];
elif 5 <= n then
attr.data[ posext ]:= [];
else
pos:= PositionSorted( [ "2.Alt(2)", "2.Alt(3)", "2.Alt(4)", "Alt(2)",
"Alt(3)", "Alt(4)" ], spinsymname );
repl:= [ [ 2 ], [ 2, 3 ], [ 3 ], [], [ 3 ], [ 3 ] ];
attr.data[ posext ]:= repl[ pos ];
fi;
# almostSimpleInfo:
if 5 <= n then
if An then
Add( attrsext.almostSimpleInfo.data.automatic,
[ spinsymname, [ libname, [ 1, 1 ] ] ] );
elif Sn then
Add( attrsext.almostSimpleInfo.data.automatic,
[ spinsymname, [ Concatenation( "A", String( n ) ), [ 2, 1 ] ] ] );
fi;
fi;
# atlas:
# If the AtlasRep package is not (yet) loaded at runtime,
# we add the information to a global variable that gets evaluated
# as soon as AtlasRep is available.
# In order to copy the known values from a library table,
# we evaluate the source data of the 'atlas' attribute.
# (Note that we can do this because the 'eval' function in
# 'CTblLib.Data.IdEnumerator' and in 'CTblLib.Data.IdEnumeratorExt'
# are the same.)
store_data( libname, "atlas" );
# basic:
if An then
Add( attrsext.basic.data.automatic,
[ spinsymname, [ [ "AlternatingGroup", [ n ] ] ] ] );
elif Sn then
Add( attrsext.basic.data.automatic,
[ spinsymname, [ [ "SymmetricGroup", [ n ] ] ] ] );
elif 2An and 4 <= n then
Add( attrsext.basic.data.automatic,
[ spinsymname, [ [ "DoubleCoverOfAlternatingGroup", [ n ] ] ] ] );
elif 2Sn and 4 <= n then
Add( attrsext.basic.data.automatic,
[ spinsymname, [ [ "SchurCoverOfSymmetricGroup", [ n ] ] ] ] );
fi;
# FusionsTo:
# 'Alt(<n>)' -> 'Sym(<n>)',
# '2.Alt(<n>)' -> 'Alt(<n>)',
# '2.Alt(<n>)' -> '2.Sym(<n>)',
# '2.Sym(<n>)' -> 'Sym(<n>)'.
attr:= attrsext.FusionsTo;
values:= [];
if 2An or 2Sn then
Add( values, ReplacedString( spinsymname, "2.", "" ) );
fi;
if An or 2An then
Add( values, ReplacedString( spinsymname, "Alt", "Sym" ) );
fi;
Add( attr.data.automatic, [ spinsymname, values ] );
# IdentifierOfMainTable:
attrsext.IdentifierOfMainTable.data[ posext ]:= libname;
# IsDuplicateTable:
attrsext.IsDuplicateTable.data[ posext ]:= ( libname <> fail );
# InfoText:
if An then
attrsext.InfoText.data[ posext ]:=
"computed using generic character table for alternating groups";
elif Sn then
attrsext.InfoText.data[ posext ]:=
"computed using generic character table for symmetric groups";
else
attrsext.InfoText.data[ posext ]:= "";
fi;
# IsAbelian:
attrsext.IsAbelian.data[ posext ]:=
( n <= 2 or ( n = 3 and ( An or 2An ) ) );
# IsAlmostSimple:
attrsext.IsAlmostSimple.data[ posext ]:= ( 5 <= n and ( An or Sn ) );
# IsNontrivialDirectProduct, factorsOfDirectProduct:
if spinsymname = "2.Alt(3)" then
attrsext.IsNontrivialDirectProduct.data[ posext ]:= true;
Add( attrsext.factorsOfDirectProduct.data.automatic,
[ spinsymname, [ [ "DirectProductByNames", "fail" ] ] ] );
else
attrsext.IsNontrivialDirectProduct.data[ posext ]:= false;
fi;
# IsPerfect:
attrsext.IsPerfect.data[ posext ]:= ( 5 <= n and ( An or 2An ) ) or
( spinsymname = "Alt(2)" );
# IsSimple:
attrsext.IsSimple.data[ posext ]:= ( An and 5 <= n ) or
( spinsymname in [ "2.Alt(2)", "Sym(2)", "Alt(3)" ] );
# IsSporadicSimple:
attrsext.IsSporadicSimple.data[ posext ]:= false;
# NamesOfFusionSources:
values:= [];
if Sn or 2Sn then
Add( values, ReplacedString( spinsymname, "Sym", "Alt" ) );
fi;
if An or Sn then
Add( values, Concatenation( "2.", spinsymname ) );
fi;
attrsext.NamesOfFusionSources.data[ posext ]:= values;
# NrConjugacyClasses:
if An then
attrsext.NrConjugacyClasses.data[ posext ]:=
Length( CharTableAlternating.classparam[1]( n ) );
elif Sn then
attrsext.NrConjugacyClasses.data[ posext ]:= NrPartitions( n );
elif libname <> fail then
attr:= attrs.NrConjugacyClasses;
attrsext.NrConjugacyClasses.data[ posext ]:=
attr.attributeValue( attr, libname );
elif 2An then
if n = 2 then
attrsext.NrConjugacyClasses.data[ posext ]:= 2;
else
values:= [ 105, 135, 171, 213, 269, 335 ];
attrsext.NrConjugacyClasses.data[ posext ]:= values[ n - 13 ];
fi;
else
values:= [ 216, 279, 354, 454, 571 ];
attrsext.NrConjugacyClasses.data[ posext ]:= values[ n - 14 ];
fi;
# "perf":
if ( An or 2An ) and 5 <= n and n <= 9 then
attr:= attrs.perf;
values:= attr.attributeValue( attr, libname );
if not IsEmpty( values ) then
Add( attrsext.perf.data.automatic, [ spinsymname, values ] );
fi;
fi;
# "prim":
# Take the known values for alternating and symmetric groups.
if ( An or Sn ) and libname <> fail then
attr:= attrs.prim;
values:= attr.attributeValue( attr, libname );
if not IsEmpty( values ) then
Add( attrsext.prim.data.automatic, [ spinsymname, values ] );
fi;
fi;
# Size:
if An then
attrsext.Size.data[ posext ]:= Factorial( n ) / 2;
elif 2An or Sn then
attrsext.Size.data[ posext ]:= Factorial( n );
elif 2Sn then
attrsext.Size.data[ posext ]:= 2 * Factorial( n );
fi;
# small:
if libname <> fail then
attr:= attrs.small;
values:= attr.attributeValue( attr, libname );
if not IsEmpty( values ) then
Add( attrsext.small.data.automatic, [ spinsymname, values ] );
fi;
fi;
# tom:
# If the TomLib package is not (yet) loaded at runtime,
# we add the information to a global variable that gets evaluated
# as soon as TomLib is available.
# In order to copy the known values from a library table,
# we evaluate the source data of the 'tom' attribute.
# (Note that we can do this because the 'eval' function in
# 'CTblLib.Data.IdEnumerator' and in 'CTblLib.Data.IdEnumeratorExt'
# are the same.)
store_data( libname, "tom" );
# trans:
if libname <> fail then
attr:= attrs.trans;
values:= attr.attributeValue( attr, libname );
if not IsEmpty( values ) then
Add( attrsext.trans.data.automatic, [ spinsymname, values ] );
fi;
fi;
# IdentifiersOfDuplicateTables
attrsext.IdentifiersOfDuplicateTables.data[ posext ]:= [];
end;
#############################################################################
##
#F CTblLib.AGL( <d>, <q> )
##
CTblLib.AGL:= function( d, q )
local gl, F, gens, mats, i;
gl:= GL( d, q );
F:= FieldOfMatrixGroup( gl );
gens:= GeneratorsOfGroup( gl );
mats:= List( [ 1 .. Length( gens ) + 1 ], i -> IdentityMat( d+1, F ) );
for i in [ 1 .. Length( gens ) ] do
mats[i]{ [ 1 .. d ] }{ [ 1 .. d ] }:= gens[i];
od;
mats[ Length( mats ) ][ d+1, 1 ]:= One( F );
return GroupByGenerators( mats );
end;
#############################################################################
##
#E