|
#############################################################################
##
#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" );
--> --------------------
--> maximum size reached
--> --------------------
[ 0.70Quellennavigators
Projekt
]
|