GAP 4.8.9 installation with standard packages -- copy to your CoCalc project to get it
#############################################################################
##
#W access.gi GAP 4 package AtlasRep Thomas Breuer
##
#Y Copyright (C) 2001, Lehrstuhl D fuer Mathematik, RWTH Aachen, Germany
##
## This file contains functions for accessing data from the ATLAS of Group
## Representations.
##
#############################################################################
##
#F AGR.StringFile( <filename> )
##
## In unfortunate cases, files may contain line breaks of the form "\r\n"
## instead of "\n".
## 'Read' would recognize this situation, and would silently replace these
## line breaks, but 'StringFile' keeps the file contents.
## Therefore we remove the '\r' characters.
##
AGR.StringFile:= function( filename )
local str;
str:= StringFile( filename );
if IsString( str ) then
str:= ReplacedString( str, "\r", "" );
fi;
return str;
end;
#############################################################################
##
#V AGR.ExtensionInfoCharacterTable
#V AGR.HasExtensionInfoCharacterTable
#V AGR.LibInfoCharacterTable
##
if IsBound( ExtensionInfoCharacterTable ) then
AGR.ExtensionInfoCharacterTable:= ExtensionInfoCharacterTable;
AGR.HasExtensionInfoCharacterTable:= HasExtensionInfoCharacterTable;
AGR.LibInfoCharacterTable:= LibInfoCharacterTable;
fi;
#############################################################################
##
#F AGR.IsLowerAlphaOrDigitChar( <char> )
##
AGR.IsLowerAlphaOrDigitChar:=
char -> IsLowerAlphaChar( char ) or IsDigitChar( char );
#############################################################################
##
## If the IO package is not installed then error messages are avoided
## via the following assignments.
##
if not IsBound( SingleHTTPRequest ) then
SingleHTTPRequest:= "dummy";
fi;
if not IsBound( IO_stat ) then
IO_stat:= "dummy";
fi;
#############################################################################
##
#F AtlasOfGroupRepresentationsTransferFile( <server>, <srvfile>, <dstfile> )
##
## This function encapsulates the access to a file either via <C>wget</C>
## or via the <Package>IO</Package> package
## <Cite Key="IO"/><Index>IO package</Index>.
## <P/>
## The source file is described by the server name <A>server</A> and the
## path <A>srvfile</A>.
## The file is written to the filename <A>dstfile</A>.
##
## <#GAPDoc Label="IO_or_wget">
## When access to remote data is enabled
## (see Section <Ref Subsect="subsect:Local or remote access"/>)
## then one needs either the &GAP; package <Package>IO</Package>
## <Cite Key="IO"/><Index>IO package</Index>
## or the external program <F>wget</F><Index Key="wget"><F>wget</F></Index>
## for accessing data files.
## <P/>
## The chosen alternative is given by the value of the <C>wget</C> component
## of the global variable <Ref Var="AtlasOfGroupRepresentationsInfo"/>.
## <P/>
## If this component has the value <K>true</K> then only <F>wget</F>
## is tried,
## if the value is <K>false</K> then only the <Package>IO</Package> package
## is used.
## If this component is not bound or bound to another value than <K>true</K>
## or <K>false</K> (this is also the default)
## then the <Package>IO</Package> package <Index>IO package</Index>
## is preferred to <F>wget</F><Index Key="wget"><F>wget</F></Index>
## if this package is available, and otherwise <F>wget</F> is tried.
## <P/>
## Note that the system program <F>wget</F> may be not available,
## and that it may require some work to install it;
## hints for that can be found on the home page of the
## <Package>AtlasRep</Package> package (see
## Section <Ref Sect="sect:Web Services for the AtlasRep Package"/>).
## <#/GAPDoc>
##
## If the access worked then <K>true</K> is returned,
## otherwise <K>false</K>.
##
BindGlobal( "AtlasOfGroupRepresentationsTransferFile",
function( server, srvfile, dstfile )
local pos, dstdir, wget, io, result;
# Check whether 'dstfile' can be written.
pos:= Positions( dstfile, '/' );
if pos = [] then
dstdir:= ".";
else
dstdir:= dstfile{ [ 1 .. pos[ Length( pos ) ] - 1 ] };
fi;
if not IsWritableFile( dstdir ) then
Info( InfoWarning, 1,
"Package AtlasRep:\n",
"#I cannot write to the directory '", dstdir, "',\n",
"#I perhaps change the AtlasRep data directories using\n",
"#I 'SetUserPreference( \"AtlasRep\",",
" \"AtlasRepDataDirectory\", ... )'" );
return false;
fi;
# Determine admissible alternatives.
wget:= true;
io:= true;
if IsBound( AtlasOfGroupRepresentationsInfo.wget ) then
if AtlasOfGroupRepresentationsInfo.wget = true then
io:= false;
elif AtlasOfGroupRepresentationsInfo.wget = false then
wget:= false;
fi;
fi;
srvfile:= Concatenation( "/", srvfile);
# Try the IO package if it is admissible.
if io and LoadPackage( "io" ) = true then
Info( InfoAtlasRep, 2,
"calling SingleHTTPRequest to get ", server, srvfile );
result:= SingleHTTPRequest( server, 80, "GET",
srvfile,
rec(), false, dstfile );
if result.statuscode <> 200 then
Info( InfoAtlasRep, 2,
"SingleHTTPRequest failed with status\n#I ", result.status );
else
return true;
fi;
fi;
# Try wget if it is admissible.
if wget then
wget:= Filename( DirectoriesSystemPrograms(), "wget" );
if wget = fail then
Info( InfoAtlasRep, 1, "no `wget' executable found" );
else
Info( InfoAtlasRep, 2,
"calling `wget' to get `", server, srvfile, "'" );
result:= Process( DirectoryCurrent(), wget,
InputTextNone(), OutputTextNone(),
[ "-q", "-O", dstfile,
Concatenation( "http://", server, srvfile ) ] );
if result <> 0 then
Info( InfoAtlasRep, 2,
"`wget' failed to fetch `",
Concatenation( "http://", server, srvfile ), "'" );
RemoveFile( dstfile );
else
return true;
fi;
fi;
fi;
# No admissible alternative was successful.
return false;
end );
#############################################################################
##
#V AtlasOfGroupRepresentationsAccessFunctionsDefault
##
## several functions may be provided; return value `fail' means that
## the next function is tried, otherwise the result counts
##
InstallValue( AtlasOfGroupRepresentationsAccessFunctionsDefault, [
rec(
description:= "default functions (read text files)",
active:= true,
location:= function( filename, groupname, dirname, type )
local pref, datadirs, info, name, namegz, names, fname;
pref:= UserPreference( "AtlasRep", "AtlasRepDataDirectory" );
if dirname in [ "datagens", "dataword" ] then
datadirs:= [ Directory( Concatenation( pref, dirname ) ) ];
else
for info in AtlasOfGroupRepresentationsInfo.private do
if dirname = info[2] then
datadirs:= [ Directory( info[1] ) ];
break;
fi;
od;
if not IsBound( datadirs ) then
Error( "no private directory with identifier `", dirname, "'" );
fi;
fi;
# There may be an uncompressed or a compressed version.
# If both are available then prefer the uncompressed version.
if IsString( filename ) then
name:= Filename( datadirs, filename );
if name = fail or not IsExistingFile( name ) then
namegz:= Filename( datadirs, Concatenation( filename, ".gz" ) );
if namegz = fail then
# No version is available yet.
return Filename( datadirs[1], filename );
else
return namegz;
fi;
else
return name;
fi;
else
# Treat the list entries separately.
names:= [];
for fname in filename do
name:= Filename( datadirs, fname );
if name = fail or not IsExistingFile( name ) then
namegz:= Filename( datadirs, Concatenation( fname, ".gz" ) );
if namegz = fail then
# No version is available yet.
Add( names, Filename( datadirs[1], fname ) );
else
Add( names, namegz );
fi;
else
Add( names, name );
fi;
od;
return names;
fi;
end,
fetch:= function( filepath, filename, groupname, dirname, type )
local triple, info, dirnam, result, gzip;
# Get the group name info.
triple:= First( AtlasOfGroupRepresentationsInfo.groupnames,
x -> x[3] = groupname );
if triple = fail then
Error( "illegal value of <groupname>" );
fi;
# Try to fetch the remote file.
result:= fail;
for info in AtlasOfGroupRepresentationsInfo.servers do
dirnam:= Concatenation( info[2], triple[1], "/", triple[2] );
# Compose the name of the directory on the server.
if dirname = "dataword" then
Append( dirnam, "/words/" );
elif filename[ Length( filename ) ] = 'g' then
Append( dirnam, "/gap0/" );
else
Append( dirnam, "/mtx/" );
fi;
# Fetch the file if possible.
result:= AtlasOfGroupRepresentationsTransferFile( info[1],
Concatenation( dirnam, filename ),
filepath );
if result = false then
Info( InfoAtlasRep, 2,
"cannot transfer file from '", info[1], "'\n",
"#I to '", filepath, "'" );
else
break;
fi;
od;
if result = false then
Info( InfoAtlasRep, 1,
"file '", filename, "' not fetched" );
return false;
fi;
# The file has just been fetched, perform postprocessing.
# (For MeatAxe format only: If wanted then compress the new file.)
if AtlasOfGroupRepresentationsInfo.compress = true
and dirnam[ Length( dirnam ) - 1 ] = 'x' then
gzip:= Filename( DirectoriesSystemPrograms(), "gzip" );
if gzip = fail or not IsExecutableFile( gzip ) then
Info( InfoAtlasRep, 1, "no `gzip' executable found" );
else
result:= Process( DirectoryCurrent(), gzip,
InputTextNone(), OutputTextNone(), [ filepath ] );
if result = fail then
Info( InfoAtlasRep, 2,
"impossible to compress file `", filepath, "'" );
fi;
fi;
fi;
return true;
end,
contents:= function( filepath, filename, groupname, dirname, type )
local len, i;
if IsString( filepath ) then
len:= Length( filepath );
if 3 < len and filepath{ [ len-2 .. len ] } = ".gz" then
filepath:= filepath{ [ 1 .. len-3 ] };
fi;
else
filepath:= ShallowCopy( filepath );
for i in [ 1 .. Length( filepath ) ] do
len:= Length( filepath[i] );
if 3 < len and filepath[i]{ [ len-2 .. len ] } = ".gz" then
filepath[i]:= filepath[i]{ [ 1 .. len-3 ] };
fi;
od;
fi;
return type[2].ReadAndInterpretDefault( filepath );
end,
),
rec(
description:= "read MeatAxe binary not text format",
active:= false,
location:= function( filename, groupname, dirname, type )
local pref, datadirs, info, names, fname, name;
if not type[1] in [ "perm", "matff" ] then
return fail;
fi;
pref:= UserPreference( "AtlasRep", "AtlasRepDataDirectory" );
if dirname = "datagens" then
datadirs:= [ Directory( Concatenation( pref, dirname ) ) ];
else
for info in AtlasOfGroupRepresentationsInfo.private do
if dirname = info[2] then
datadirs:= [ Directory( info[1] ) ];
break;
fi;
od;
if not IsBound( datadirs ) then
Error( "no private directory with identifier `", dirname, "'" );
fi;
fi;
# A list of file names is given, and the files are not compressed.
# Replace the text format names by binary format names.
filename:= List( filename, nam -> ReplacedString( nam, ".m", ".b" ) );
names:= [];
for fname in filename do
name:= Filename( datadirs, fname );
if name = fail then
# No version is available yet.
Add( names, Filename( datadirs[1], fname ) );
else
Add( names, name );
fi;
od;
return names;
end,
fetch:= function( filepath, filename, groupname, dirname, type )
local triple, info, dirnam, result;
# Get the group name info.
triple:= First( AtlasOfGroupRepresentationsInfo.groupnames,
x -> x[3] = groupname );
if triple = fail then
Error( "illegal value of <groupname>" );
fi;
# Try to fetch the remote file.
result:= fail;
filename:= ReplacedString( filename, ".m", ".b" );
for info in AtlasOfGroupRepresentationsInfo.servers do
# Fetch the file if possible.
result:= AtlasOfGroupRepresentationsTransferFile( info[1],
Concatenation( info[2], triple[1], "/", triple[2],
"/bin/", filename ),
filepath );
if result = false then
Info( InfoAtlasRep, 2,
"no connection to AtlasRep server ", info[1] );
else
break;
fi;
od;
if result = false then
Info( InfoAtlasRep, 1,
"no file `", filename, "' found on the servers" );
return false;
fi;
# (Do not compress the new file, it is in binary format.)
return true;
end,
contents:= function( filepath, filename, groupname, dirname, type )
# This function is called only for the types "perm" and "matff",
# and binary format files are *not* compressed.
return List( filepath, FFMatOrPermCMtxBinary );
end,
),
#T The following is currently useless because of an unlucky files format.
# rec(
# description:= "read GAP format not MeatAxe format",
#
# active:= false,
#
# location:= function( filename, groupname, dirname, type )
# local datadirs, info, names, fname, name;
#
# # (Does the same as the `location' function for MeatAxe binary format,
# # except that we replace the suffix of the filename by `.g' not `.b'.)
# if not type[1] in [ "perm", "matff" ] then
# return fail;
# fi;
# if dirname = "datagens" then
# datadirs:= DirectoriesPackageLibrary( "atlasrep", dirname );
# else
# for info in AtlasOfGroupRepresentationsInfo.private do
# if dirname = info[2] then
# datadirs:= [ Directory( info[1] ) ];
# break;
# fi;
# od;
# if not IsBound( datadirs ) then
# Error( "no private directory with identifier `", dirname, "'" );
# fi;
# fi;
#
# # A list of file names is given, and the files are not compressed.
# # Replace the text format names by binary format names.
# filename:= List( filename, nam -> ReplacedString( nam, ".m", ".g" ) );
# names:= [];
# for fname in filename do
# name:= Filename( datadirs, fname );
# if name = fail then
# # No version is available yet.
# Add( names, Filename( datadirs[1], fname ) );
# else
# Add( names, name );
# fi;
# od;
# return names;
## alternative for ONE file with SEVERAL generators:
## # Replace the list of text format names by one GAP format name.
## filename:= ReplacedString( filename[1], ".m1", ".g" );
## name:= Filename( datadirs, filename );
## if name = fail then
## # No version is available yet.
## return Filename( datadirs[1], filename );
## else
## return name;
## fi;
# end,
#
# fetch:= function( filepath, filename, groupname, dirname, type )
# local triple, info, dirnam, result;
#
# # (Does the same as the `fetch' function for MeatAxe binary format,
# # except that the source file is expected in `gap' not `bin'.)
# # Get the group name info.
# triple:= First( AtlasOfGroupRepresentationsInfo.groupnames,
# x -> x[3] = groupname );
# if triple = fail then
# Error( "illegal value of <groupname>" );
# fi;
#
# # Try to fetch the remote file.
# result:= fail;
# filename:= ReplacedString( filename, ".m", ".g" );
# for info in AtlasOfGroupRepresentationsInfo.servers do
#
# # Fetch the file if possible.
# result:= AtlasOfGroupRepresentationsTransferFile( info[1],
# Concatenation( info[2], triple[1], "/", triple[2],
# "/gap/", filename ),
# filepath );
# if result = false then
# Info( InfoAtlasRep, 2,
# "no connection to AtlasRep server ", info[1] );
# else
# break;
# fi;
#
# od;
# if result = false then
# Info( InfoAtlasRep, 1,
# "no file `", filename, "' found on the servers" );
# return false;
# fi;
#
# # (Do not compress the new file, it is not in MeatAxe text format.)
# return true;
# end,
#
# contents:= function( filepath, filename, groupname, dirname, type )
# # This function is called only for the types "perm" and "matff",
# # and GAP format files are *not* compressed.
# return List( filepath, AtlasDataGAPFormatFile );
## alternative for ONE file with SEVERAL generators:
## return AtlasDataGAPFormatFile( filepath );
# end,
# ),
rec(
description:= "direct access to a local server",
active:= false,
location:= function( filename, groupname, dirname, type )
local triple, dirnam, name, names, fname;
# This is meaningful only for official data
# and if there is a local server.
if not ( dirname in [ "datagens", "dataword" ] and
IsBound( AtlasOfGroupRepresentationsInfo.localserver ) ) then
return fail;
fi;
# Get the group name info.
triple:= First( AtlasOfGroupRepresentationsInfo.groupnames,
x -> x[3] = groupname );
if triple = fail then
Error( "illegal value of <groupname>" );
fi;
# Compose the name of the directory on the server.
dirnam:= Concatenation( AtlasOfGroupRepresentationsInfo.localserver,
triple[1], "/", triple[2] );
if dirname = "dataword" then
Append( dirnam, "/words/" );
elif filename[ Length( filename ) ] = 'g' then
Append( dirnam, "/gap0/" );
else
Append( dirnam, "/mtx/" );
fi;
# Check whether the file(s) exist(s).
if IsString( filename ) then
name:= Concatenation( dirnam, filename );
if IsExistingFile( name ) then
return name;
fi;
return fail;
else
names:= [];
for fname in filename do
name:= Concatenation( dirnam, fname );
if IsExistingFile( name ) then
Add( names, name );
else
return fail;
fi;
od;
return names;
fi;
end,
fetch:= function( filepath, filename, groupname, dirname, type )
# The `location' function has checked that the file exists.
return true;
end,
contents:= function( filepath, filename, groupname, dirname, type )
# We need not care about compressed files.
return type[2].ReadAndInterpretDefault( filepath );
end,
),
] );
#############################################################################
##
#F AGR.CrcFileFits( <filename>, <path> )
##
AGR.CrcFileFits:= function( filename, path )
local crc, len;
crc:= First( AtlasOfGroupRepresentationsInfo.filenames,
p -> p[1] = filename );
if crc = fail then
return false;
fi;
len:= Length( path );
if path{ [ len - 2 .. len ] } = ".gz" then
path:= path{ [ 1 .. len - 3 ] };
fi;
if crc[2] = CrcFile( path ) then
return true;
else
Info( InfoWarning, 1,
"CrcFile value of\n",
"#I '", path, "'\n",
"#I does not match, ignoring this file" );
return false;
fi;
end;
#############################################################################
##
#F AtlasOfGroupRepresentationsLocalFilename( <dirname>, <groupname>,
#F <filename>, <type> )
##
InstallGlobalFunction( AtlasOfGroupRepresentationsLocalFilename,
function( dirname, groupname, filename, type )
local cand, r, path, i;
cand:= [];
for r in Reversed( AtlasOfGroupRepresentationsInfo.accessFunctions ) do
if r.active = true then
path:= r.location( filename, groupname, dirname, type );
if path <> fail then
# Check whether the CRC values fit.
if IsString( path ) then
path:= [ path ];
filename:= [ filename ];
fi;
for i in [ 1 .. Length( filename ) ] do
path[i]:= [ path[i], IsExistingFile( path[i] ) ];
od;
if ForAll( path, x -> x[2] ) then
# This has priority, do not consider other sources.
cand:= [ [ r, path ] ];
break;
else
Add( cand, [ r, path ] );
fi;
fi;
fi;
od;
return cand;
end );
#############################################################################
##
#F AtlasOfGroupRepresentationsLocalFilenameTransfer( <dirname>, <groupname>,
#F <filename>, <type> )
##
InstallGlobalFunction( AtlasOfGroupRepresentationsLocalFilenameTransfer,
function( dirname, groupname, filename, type )
local cand, list, filenamex, result, ok, i;
# Determine the local directory where to look for the file,
# and the functions that claim to be applicable.
cand:= AtlasOfGroupRepresentationsLocalFilename( dirname, groupname,
filename, type );
if IsString( filename ) then
filenamex:= [ filename ];
else
filenamex:= filename;
fi;
for list in cand do
if Length( list[2] ) = Length( filenamex ) then
# This is the situation we can handle.
if IsString( filename ) then
result:= [ list[2][1][1], list[1] ];
else
result:= [ List( list[2], x -> x[1] ), list[1] ];
fi;
ok:= true;
for i in [ 1 .. Length( list[2] ) ] do
if list[2][i][2] then
# This file is already available.
if dirname in [ "datagens", "dataword" ] then
# Check its crc value.
if not AGR.CrcFileFits( filenamex[i], list[2][i][1] ) then
return fail;
fi;
fi;
elif AtlasOfGroupRepresentationsInfo.remote = true and
dirname in [ "datagens", "dataword" ] and
list[1].fetch( list[2][i][1],
filenamex[i], groupname, dirname, type ) then
# We have created a new local file.
# Check its crc value,
if not AGR.CrcFileFits( filenamex[i], list[2][i][1] ) then
return fail;
fi;
else
# We cannot fetch the file.
ok:= false;
break;
fi;
od;
if ok then
# Return path(s) and access functions.
return result;
fi;
fi;
od;
# Not all files can be made available, or not all crc values fit.
Info( InfoAtlasRep, 1,
"no file(s) `", filename, "' found in the local directories" );
return fail;
end );
#############################################################################
##
#F AtlasOfGroupRepresentationsTestTableOfContentsRemoteUpdates()
##
InstallGlobalFunction(
AtlasOfGroupRepresentationsTestTableOfContentsRemoteUpdates, function()
local version, inforec, home, server, path, dstfilename, result, lines,
pref, datadirs, line, pos, pos2, filename, filenames, localfile,
servdate, stat;
if LoadPackage( "io" ) <> true then
Info( InfoAtlasRep, 1, "the package IO is not available" );
return fail;
fi;
# Download the file that lists the changes.
version:= InstalledPackageVersion( "atlasrep" );
inforec:= First( PackageInfo( "atlasrep" ), r -> r.Version = version );
home:= inforec.PackageWWWHome;
if home{ [ 1 .. 7 ] } = "http://" then
home:= home{ [ 8 .. Length( home ) ] };
fi;
server:= home{ [ 1 .. Position( home, '/' ) - 1 ] };
path:= home{ [ Position( home, '/' ) + 1 .. Length( home ) ] };
dstfilename:= Filename( DirectoryTemporary(), "changes.htm" );
result:= [];
if AtlasOfGroupRepresentationsTransferFile( server,
Concatenation( path, "/htm/data/changes.htm" ),
dstfilename ) then
lines:= SplitString( AGR.StringFile( dstfilename ), "\n" );
lines:= Filtered( lines,
x -> 20 < Length( x ) and x{ [ 1 .. 4 ] } = "<tr>"
and x{ [ -3 .. 0 ] + Length( x ) } = " -->" );
pref:= UserPreference( "AtlasRep", "AtlasRepDataDirectory" );
datadirs:= [ Directory( Concatenation( pref, "datagens" ) ),
Directory( Concatenation( pref, "dataword" ) ) ];
for line in lines do
pos:= PositionSublist( line, "</td><td>" );
if pos <> fail then
pos2:= PositionSublist( line, "</td><td>", pos );
filename:= line{ [ pos+9 .. pos2-1 ] };
if PositionSublist( filename, "<it>i</it>" ) <> fail then
filenames:= List( [ "1", "2" ],
i -> ReplacedString( filename, "<it>i</it>", i ) );
else
filenames:= [ filename ];
fi;
for filename in filenames do
localfile:= Filename( datadirs, filename );
if localfile <> fail then
if not IsExistingFile( localfile ) then
localfile:= Concatenation( localfile, ".gz" );
fi;
if IsExistingFile( localfile ) then
# There is something to compare.
pos:= PositionSublist( line, "<!-- " );
if pos <> fail then
servdate:= Int( line{ [ pos+5 .. Length( line )-4 ] } );
stat:= IO_stat( localfile );
if stat <> fail then
if stat.mtime < servdate then
Add( result, localfile );
fi;
fi;
fi;
fi;
fi;
od;
fi;
od;
fi;
return result;
end );
#############################################################################
##
#F AGR.FileContents( <dirname>, <groupname>, <filename>, <type> )
##
AGR.FileContents:= function( dirname, groupname, filename, type )
local result;
if not ( IsString( filename ) or
( IsList( filename ) and ForAll( filename, IsString ) ) ) then
Error( "<file> must be a string or a list of strings" );
fi;
result:= AtlasOfGroupRepresentationsLocalFilenameTransfer( dirname,
groupname, filename, type );
if result = fail then
return fail;
fi;
# 3. We have the local file(s). Try to extract the contents.
return result[2].contents( result[1], filename, groupname, dirname, type );
end;
#############################################################################
##
#F FilenameAtlas( <dirname>, <groupname>, <filename> )
##
InstallGlobalFunction( FilenameAtlas,
function( dirname, groupname, filename )
local type;
if not IsBound( AtlasOfGroupRepresentationsInfo.WarnedFilenameAtlas ) then
AtlasOfGroupRepresentationsInfo.WarnedFilenameAtlas:= true;
Info( InfoWarning, 1,
"FilenameAtlas is deprecated,\n#I use ",
"`AtlasOfGroupRepresentationsLocalFilenameTransfer' instead" );
fi;
for type in AGR.DataTypes( "rep", "prg" ) do
if AGR.ParseFilenameFormat( filename, type[2].FilenameFormat )
<> fail then
return AtlasOfGroupRepresentationsLocalFilenameTransfer( dirname,
groupname, filename, type )[1];
fi;
od;
return fail;
end );
#############################################################################
##
#F AGR.InfoForName( <gapname> )
##
AGR.InfoForName:= function( gapname )
local pos;
gapname:= AGR.GAPName( gapname );
pos:= PositionSorted( AtlasOfGroupRepresentationsInfo.GAPnames,
[ gapname ] );
if pos <= Length( AtlasOfGroupRepresentationsInfo.GAPnames ) and
AtlasOfGroupRepresentationsInfo.GAPnames[ pos ][1] = gapname then
return AtlasOfGroupRepresentationsInfo.GAPnames[ pos ];
else
return fail;
fi;
end;
#############################################################################
##
## auxiliary function
##
AGR.TST:= function( gapname, value, compname, testfun, msg )
if not IsBound( AGR.GAPnamesRec.( gapname ) ) then
Error( "AGR.GAPnamesRec.( \"", gapname, "\" ) is not bound" );
elif not IsBound( AGR.GAPnamesRec.( gapname )[3] ) then
Error( "AGR.GAPnamesRec.( \"", gapname, "\" )[3] is not bound" );
elif IsBound( AGR.GAPnamesRec.( gapname )[3].( compname ) ) then
Error( "AGR.GAPnamesRec.( \"", gapname, "\" )[3].", compname,
" is bound" );
elif not testfun( value ) then
Error( "<", compname, "> must be a ", msg );
fi;
end;
#############################################################################
##
#F AGR.IsRepNameAvailable( <repname> )
##
## If `AtlasOfGroupRepresentationsInfo.checkData' is bound then this
## function is called when additional data are added that refer to the
## representation <repname>.
##
AGR.IsRepNameAvailable:= function( repname )
local filenames, type, parsed, groupname, gapname;
filenames:= [ Concatenation( repname, ".m1" ),
Concatenation( repname, ".g" ) ];
for type in AGR.DataTypes( "rep" ) do
parsed:= List( filenames,
x -> AGR.ParseFilenameFormat( x, type[2].FilenameFormat ) );
if ForAny( parsed, IsList ) then
break;
fi;
od;
if ForAll( parsed, IsBool ) then
Print( "#E wrong format of `", repname, "'\n" );
return false;
fi;
groupname:= First( parsed, IsList )[1];
gapname:= First( AtlasOfGroupRepresentationsInfo.GAPnames,
pair -> pair[2] = groupname );
if gapname = fail then
Print( "#E no group name `", groupname, "' for `", repname, "'\n" );
return false;
elif ForAll( AllAtlasGeneratingSetInfos( gapname[1] ),
x -> x.repname <> repname ) then
Print( "#E no representation `", repname, "' available\n" );
return false;
fi;
return true;
end;
#############################################################################
##
#F AGR.IsPrgNameAvailable( <prgname> )
##
## If `AtlasOfGroupRepresentationsInfo.checkData' is bound then this
## function is called when additional data are added that refer to the
## program <prgname>.
##
AGR.IsPrgNameAvailable:= function( prgname )
local type, parsed, groupname;
for type in AGR.DataTypes( "prg" ) do
parsed:= AGR.ParseFilenameFormat( prgname, type[2].FilenameFormat );
if IsList( parsed ) then
break;
fi;
od;
if parsed = fail then
Print( "#E wrong format of `", prgname, "'\n" );
return false;
fi;
groupname:= parsed[1];
if ForAny( AGR.TablesOfContents( "all" ),
toc -> IsBound( toc.( groupname ) ) and
ForAny( RecNames( toc.( groupname ) ),
nam -> ForAny( toc.( groupname ).( nam ),
l -> l[ Length( l ) ] = prgname ) ) ) then
return true;
else
Print( "#E no program `", prgname, "' available\n" );
return false;
fi;
end;
#############################################################################
##
#V AGR.MapNameToGAPName
#F AGR.GAPName( <name> )
##
## Let <name> be a string.
## If `LowercaseString( <name> )' is the lower case version of the GAP name
## of an ATLAS group then `AGR.GAPName' returns this GAP name.
## If <name> is an admissible name of a GAP character table with identifier
## <id> (this condition is already case insensitive) then `AGR.GAPName'
## returns `AGR.GAPName( <id> )'.
##
## These two conditions are forced to be consistent, as follows.
## Whenever a GAP name <nam>, say, of an ATLAS group is notified with
## `AGR.GNAN', we compute `LibInfoCharacterTable( <nam> )'.
## If this is `fail' then there is no danger of an inconsistency,
## and if the result is a record <r> then we have the condition
## `AGR.GAPName( <r>.firstName ) = <nam>'.
##
## So a case insensitive partial mapping from character table identifiers
## to GAP names of ATLAS groups is built in `AGR.GNAN',
## and is used in `AGR.GAPName'
##
## Examples of different names for a group are `"F3+"' vs. `"Fi24'"'
## and `"S6"' vs. `"A6.2_1"'.
##
AGR.MapNameToGAPName:= [ [], [] ];
AGR.GAPName:= function( name )
local r, nname, pos;
# Make sure that the file `gap/types.g' is alreay loaded.
IsRecord( AtlasOfGroupRepresentationsInfo );
if IsBound( AGR.LibInfoCharacterTable ) then
r:= AGR.LibInfoCharacterTable( name );
else
r:= fail;
fi;
if r = fail then
nname:= LowercaseString( name );
else
nname:= r.firstName;
fi;
pos:= Position( AGR.MapNameToGAPName[1], nname );
if pos = fail then
return name;
fi;
return AGR.MapNameToGAPName[2][ pos ];
end;
#############################################################################
##
#F AGR.GNAN( <gapname>, <atlasname> )
##
## <#GAPDoc Label="AGR.GNAN">
## <Mark><C>AGR.GNAN( <A>gapname</A>, <A>atlasname</A> )</C></Mark>
## <Item>
## Called with two strings <A>gapname</A> (the &GAP; name of the group)
## and <A>atlasname</A> (the &ATLAS; name of the group),
## <C>AGR.GNAN</C> stores the information in the list
## <C>AtlasOfGroupRepresentationsInfo.GAPnames</C>,
## which defines the name mapping between the <Package>ATLAS</Package>
## names and &GAP; names of the groups.
## <P/>
## This function may be used also for private extensions of the database.
## <P/>
## An example of a valid call is
## <C>AGR.GNAN("A5.2","S5")</C>.
## </Item>
## <#/GAPDoc>
##
AGR.GNAN:= function( gapname, atlasname )
local value, r, pos;
if IsBound( AtlasOfGroupRepresentationsInfo.checkData ) then
if ForAny( AtlasOfGroupRepresentationsInfo.GAPnames,
pair -> gapname = pair[1] ) then
Error( "cannot notify `", gapname, "' more than once" );
elif ForAny( AtlasOfGroupRepresentationsInfo.GAPnames,
pair -> atlasname = pair[2] ) then
Error( "ambiguous GAP names for ATLAS name `", atlasname, "'" );
fi;
fi;
# Make the character table names admissible.
if IsBound( AGR.LibInfoCharacterTable ) then
r:= AGR.LibInfoCharacterTable( gapname );
else
r:= fail;
fi;
if r = fail then
# Store the lowercase name.
Add( AGR.MapNameToGAPName[1], LowercaseString( gapname ) );
Add( AGR.MapNameToGAPName[2], gapname );
elif not r.firstName in AGR.MapNameToGAPName[1] then
Add( AGR.MapNameToGAPName[1], r.firstName );
Add( AGR.MapNameToGAPName[2], gapname );
else
Error( "<gapname> is not compatible with CTblLib" );
fi;
value:= [ gapname, atlasname, rec() ];
AddSet( AtlasOfGroupRepresentationsInfo.GAPnames, value );
AGR.GAPnamesRec.( gapname ):= value;
end;
#############################################################################
##
#F AGR.GRP( <dirname>, <simpname>, <groupname> )
##
## <#GAPDoc Label="AGR.GRP">
## <Mark><C>AGR.GRP( <A>dirname</A>, <A>simpname</A>, <A>groupname</A>)</C></Mark>
## <Item>
## Called with three strings, <C>AGR.GRP</C> stores in the
## <C>groupname</C> component of
## <Ref Var="AtlasOfGroupRepresentationsInfo"/> in which path on the
## servers the data about the group with &ATLAS; name <A>groupname</A>
## can be found.
## <P/>
## This function is <E>not</E> intended for private extensions of the
## database.
## <P/>
## An example of a valid call is
## <C>AGR.GRP("alt","A5","S5")</C>.
## </Item>
## <#/GAPDoc>
##
AGR.GRP:= function( dirname, simpname, groupname )
local entry;
if ForAll( AtlasOfGroupRepresentationsInfo.GAPnames,
pair -> pair[2] <> groupname ) then
# There is no corresponding GAP name.
AddSet( AtlasOfGroupRepresentationsInfo.GAPnames,
Immutable( [ groupname, groupname ] ) );
AGR.SetGAPnamesSortDisp();
Info( InfoAtlasRep, 1,
"no GAP name known for `", groupname, "'" );
fi;
if IsBound( AtlasOfGroupRepresentationsInfo.checkData ) then
entry:= First( AtlasOfGroupRepresentationsInfo.groupnames,
l -> l[3] = groupname );
if entry <> fail then
# Check whether the group is already notified.
if entry[1] = dirname then
Info( InfoAtlasRep, 1,
"group with Atlas name `", groupname, "' already notified" );
else
Error( "group with Atlas name `", groupname,
"' notified for different directories!" );
fi;
if entry[2] <> simpname then
Error( "group with Atlas name `", groupname,
"' notified for different simple groups!" );
fi;
return;
fi;
fi;
# Notify the group.
Add( AtlasOfGroupRepresentationsInfo.groupnames,
[ dirname, simpname, groupname ] );
end;
#############################################################################
##
#F AGR.TOC( <typename>, <filename>, <crc> )
##
## <#GAPDoc Label="AGR.TOC">
## <Mark><C>AGR.TOC( <A>typename</A>, <A>filename</A>, <A>crcfile</A> )</C></Mark>
## <Item>
## Called with two strings <A>typename</A> and <A>filename</A>,
## and a list <A>crc</A> of integers,
## <C>AGR.TOC</C> notifies an entry to the <C>TableOfContents.remote</C>
## component of <Ref Var="AtlasOfGroupRepresentationsInfo"/>,
## where <A>typename</A> must be the name of the data type to which
## the entry belongs,
## <A>filename</A> must be the prefix of the data file(s),
## and <A>crc</A> must be the list of <Ref BookName="ref" Func="CrcFile"/>
## values of the file(s).
## <P/>
## This function is <E>not</E> intended for private extensions of the
## database.
## <P/>
## An example of a valid call is
## <C>AGR.TOC("perm","S5G1-p5B0.m",[-3581724,115937465])</C>.
## </Item>
## <#/GAPDoc>
##
AGR.TOC:= function( type, string, crc )
local n, t, stringx, record, entry, groupname, added, j;
n:= Length( crc );
# Parse the filename with the given format info.
# type:= First( AGR.DataTypes( "rep", "prg" ), x -> x[1] = type );
for t in AGR.DataTypes( "rep", "prg" ) do
if t[1] = type then
type:= t;
break;
fi;
od;
if 1 < n then
stringx:= Concatenation( string, "1" );
else
stringx:= string;
fi;
record:= AtlasTableOfContents( "remote" ).TableOfContents;
entry:= AGR.ParseFilenameFormat( stringx, type[2].FilenameFormat );
if entry = fail then
Info( InfoAtlasRep, 1, "`", [ type, string, crc ],
"' is not a valid t.o.c. entry" );
return;
fi;
# Get the list for the data in the record for the group name.
groupname:= entry[1];
if IsBound( AtlasOfGroupRepresentationsInfo.checkData ) and
ForAll( AtlasOfGroupRepresentationsInfo.groupnames,
x -> x[3] <> groupname ) then
Error( "`", groupname, "' is not a valid group name" );
fi;
if not IsBound( record.( groupname ) ) then
record.( groupname ):= rec();
fi;
record:= record.( groupname );
if not IsBound( record.( type[1] ) ) then
record.( type[1] ):= [];
fi;
# Add the first filename.
added:= type[2].AddFileInfo( record.( type[1] ), entry, stringx );
Add( AtlasOfGroupRepresentationsInfo.filenames,
Immutable( [ stringx, crc[1] ] ) );
# Add the other filenames if necessary.
if added then
for j in [ 2 .. n ] do
entry[ Length( entry ) ]:= j;
stringx:= Concatenation( string, String( j ) );
added:= type[2].AddFileInfo( record.( type[1] ), entry, stringx )
and added;
Add( AtlasOfGroupRepresentationsInfo.filenames,
Immutable( [ stringx, crc[j] ] ) );
od;
fi;
if not added then
Info( InfoAtlasRep, 1, "`", [ type, string, crc ],
"' is not a valid t.o.c. entry" );
fi;
end;
#############################################################################
##
#F AGR.GRS( <gapname>, <size> )
##
## <#GAPDoc Label="AGR.GRS">
## <Mark><C>AGR.GRS( <A>gapname</A>, <A>size</A> )</C></Mark>
## <Item>
## Called with the string <A>gapname</A> (the &GAP; name of the group)
## and the integer <A>size</A> (the order of the group),
## <C>AGR.GRS</C> stores this information in
## <C>AtlasOfGroupRepresentationsInfo.GAPnames</C>.
## <P/>
## An example of a valid call is
## <C>AGR.GRS("A5.2",120)</C>.
## </Item>
## <#/GAPDoc>
##
AGR.GRS:= function( gapname, size )
if IsBound( AtlasOfGroupRepresentationsInfo.checkData ) then
AGR.TST( gapname, size, "size", IsPosInt, "positive integer" );
fi;
AGR.GAPnamesRec.( gapname )[3].size:= size;
end;
#############################################################################
##
#F AGR.MXN( <gapname>, <nrMaxes> )
##
## <#GAPDoc Label="AGR.MXN">
## <Mark><C>AGR.MXN( <A>gapname</A>, <A>nrMaxes</A> )</C></Mark>
## <Item>
## Called with the string <A>gapname</A> (the &GAP; name of the group)
## and the integer <A>nrMaxes</A> (the number of classes of maximal
## subgroups of the group),
## <C>AGR.MXN</C> stores the information in
## <C>AtlasOfGroupRepresentationsInfo.GAPnames</C>.
## <P/>
## An example of a valid call is
## <C>AGR.MXN("A5.2",4)</C>.
## </Item>
## <#/GAPDoc>
##
AGR.MXN:= function( gapname, nrMaxes )
if IsBound( AtlasOfGroupRepresentationsInfo.checkData ) then
AGR.TST( gapname, nrMaxes, "nrMaxes", IsPosInt, "positive integer" );
fi;
AGR.GAPnamesRec.( gapname )[3].nrMaxes:= nrMaxes;
end;
#############################################################################
##
#F AGR.MXO( <gapname>, <sizesMaxes> )
##
## <#GAPDoc Label="AGR.MXO">
## <Mark><C>AGR.MXO( <A>gapname</A>, <A>sizesMaxes</A> )</C></Mark>
## <Item>
## Called with the string <A>gapname</A> (the &GAP; name of the group)
## and the list <A>sizesMaxes</A> (of subgroup orders of the classes of
## maximal subgroups of the group, not necessarily dense,
## in non-increasing order),
## <C>AGR.MXO</C> stores the information in
## <C>AtlasOfGroupRepresentationsInfo.GAPnames</C>.
## <P/>
## An example of a valid call is
## <C>AGR.MXO("A5.2",[60,24,20,12])</C>.
## </Item>
## <#/GAPDoc>
##
AGR.MXO:= function( gapname, sizesMaxes )
if IsBound( AtlasOfGroupRepresentationsInfo.checkData ) then
AGR.TST( gapname, sizesMaxes, "sizesMaxes",
x -> IsList( x ) and ForAll( x, IsPosInt )
and IsSortedList( Reversed( Compacted( x ) ) ),
"list of non-increasing pos. integers" );
fi;
AGR.GAPnamesRec.( gapname )[3].sizesMaxes:= sizesMaxes;
end;
#############################################################################
##
#F AGR.MXS( <gapname>, <structureMaxes> )
##
## <#GAPDoc Label="AGR.MXS">
## <Mark><C>AGR.MXS( <A>gapname</A>, <A>structureMaxes</A> )</C></Mark>
## <Item>
## Called with the string <A>gapname</A> (the &GAP; name of the group)
## and the list <A>structureMaxes</A> (of strings describing the
## structures of the maximal subgroups of the group, not necessarily dense),
## <C>AGR.MXS</C> stores the information in
## <C>AtlasOfGroupRepresentationsInfo.GAPnames</C>.
## <P/>
## An example of a valid call is
## <C>AGR.MXS("A5.2",["A5","S4","5:4","S3x2"])</C>.
## </Item>
## <#/GAPDoc>
##
AGR.MXS:= function( gapname, structureMaxes )
if IsBound( AtlasOfGroupRepresentationsInfo.checkData ) then
AGR.TST( gapname, structureMaxes, "structureMaxes",
x -> IsList( x ) and ForAll( x, IsString ),
"list of strings" );
fi;
AGR.GAPnamesRec.( gapname )[3].structureMaxes:= structureMaxes;
end;
#############################################################################
##
## AGR.KERPRG( <gapname>, <kernelProgram> )
##
## <#GAPDoc Label="AGR.KERPRG">
## <Mark><C>AGR.KERPRG( <A>gapname</A>, <A>kernelProgram</A> )</C></Mark>
## <Item>
## Called with the string <A>gapname</A> (the &GAP; name of the group)
## and the list <A>kernelProgram</A> (with entries the standardization of
## the group, the &GAP; name of a factor group, and the list of lines of a
## straight line program that computes generators of the kernel of the
## epimorphism from the group to the factor group),
## <C>AGR.KERPRG</C> stores the information in
## <C>AtlasOfGroupRepresentationsInfo.GAPnames</C>.
## <P/>
## An example of a valid call is
## <C>AGR.KERPRG("2.J2",[1,"J2",[[[1,2]]]])</C>.
## </Item>
## <#/GAPDoc>
##
AGR.KERPRG:= function( gapname, kernelProgram )
if IsBound( AtlasOfGroupRepresentationsInfo.checkData ) and
not ( IsList( kernelProgram ) and Length( kernelProgram ) = 3 and
IsPosInt( kernelProgram[1] ) and
IsString( kernelProgram[2] ) and
IsDenseList( kernelProgram[3] ) ) then
Error( "<kernelProgram> must be a suitable list" );
fi;
if not IsBound( AGR.GAPnamesRec.( gapname )[3].kernelPrograms ) then
AGR.GAPnamesRec.( gapname )[3].kernelPrograms:= [];
fi;
Add( AGR.GAPnamesRec.( gapname )[3].kernelPrograms, kernelProgram );
end;
#############################################################################
##
#F AGR.STDCOMP( <gapname>, <factorCompatibility> )
##
## <#GAPDoc Label="AGR.STDCOMP">
## <Mark><C>AGR.STDCOMP</C></Mark>
## <Item>
## Called with the string <A>gapname</A> (the &GAP; name of the group)
## and the list <A>factorCompatibility</A> (with entries
## the standardization of the group, the &GAP; name of a factor group,
## the standardization of this factor group, and
## <K>true</K> or <K>false</K>, indicating whether mapping the standard
## generators for <A>gapname</A> to those of <A>factgapname</A> defines an
## epimorphism),
## <C>AGR.STDCOMP</C> stores the information in
## <C>AtlasOfGroupRepresentationsInfo.GAPnames</C>.
## <P/>
## An example of a valid call is
## <C>AGR.STDCOMP("2.A5.2",[1,"A5.2",1,true])</C>.
## </Item>
## <#/GAPDoc>
##
AGR.STDCOMP:= function( gapname, factorCompatibility )
if IsBound( AtlasOfGroupRepresentationsInfo.checkData ) and
not ( IsList( factorCompatibility ) and
Length( factorCompatibility ) = 4 and
IsPosInt( factorCompatibility[1] ) and
IsString( factorCompatibility[2] ) and
IsPosInt( factorCompatibility[3] ) and
IsBool( factorCompatibility[4] ) ) then
Error( "<factorCompatibility> must be a suitable list" );
fi;
if not IsBound( AGR.GAPnamesRec.( gapname )[3].factorCompatibility ) then
AGR.GAPnamesRec.( gapname )[3].factorCompatibility:= [];
fi;
Add( AGR.GAPnamesRec.( gapname )[3].factorCompatibility,
factorCompatibility );
end;
#############################################################################
##
#F AGR.RNG( <repname>, <descr> )
##
## <#GAPDoc Label="AGR.RNG">
## <Mark><C>AGR.RNG( <A>repname</A>, <A>descr</A> )</C></Mark>
## <Item>
## Called with two strings <A>repname</A> (denoting the name
## of a file containing the generators of a matrix representation over a
## ring that is not determined by the filename)
## and <A>descr</A> (describing this ring <M>R</M>, say),
## <C>AGR.RNG</C> adds the triple
## <M>[ <A>repname</A>, <A>descr</A>, R ]</M>
## to the list stored in the <C>ringinfo</C> component of
## <Ref Var="AtlasOfGroupRepresentationsInfo"/>.
## <P/>
## An example of a valid call is
## <C>AGR.RNG("A5G1-Ar3aB0","Field([Sqrt(5)])")</C>.
## </Item>
## <#/GAPDoc>
##
AGR.RNG:= function( repname, descr )
local triple;
if IsBound( AtlasOfGroupRepresentationsInfo.checkData ) then
# Check that this representation really exists.
if not AGR.IsRepNameAvailable( repname ) then
return;
fi;
fi;
triple:= [ repname, descr, EvalString( descr ) ];
if triple in AtlasOfGroupRepresentationsInfo.ringinfo then
Info( InfoAtlasRep, 1,
"triple `", triple, "' cannot be notified more than once" );
else
Add( AtlasOfGroupRepresentationsInfo.ringinfo, triple );
fi;
end;
#############################################################################
##
#F AGR.TOCEXT( <atlasname>, <std>, <maxnr>, <files> )
##
## <#GAPDoc Label="AGR.TOCEXT">
## <Mark><C>AGR.TOCEXT( <A>atlasname</A>, <A>std</A>, <A>maxnr</A>, <A>files</A> )</C></Mark>
## <Item>
## Called with the string <A>atlasname</A> (the &ATLAS; name of the
## group), the positive integers <A>std</A> (the standardization) and
## <A>maxnr</A> (the number of the class of maximal subgroups), and
## the list <A>files</A> (of filenames of straight line programs for
## computing generators of the <A>maxnr</A>-th maximal subgroup, using
## a straight line program for a factor group plus perhaps some straight
## line program for computing kernel generators),
## <C>AGR.TOCEXT</C> stores the information in the <C>maxext</C> component
## of the <A>atlasname</A> component of the <C>"remote"</C>
## table of contents.
## <P/>
## An example of a valid call is
## <C>AGR.TOCEXT("2A5",1,3,["A5G1-max3W1"])</C>.
## </Item>
## <#/GAPDoc>
##
AGR.TOCEXT:= function( atlasname, std, maxnr, files )
local r, info;
if IsBound( AtlasOfGroupRepresentationsInfo.checkData ) then
if not ( IsString( atlasname ) and IsPosInt( std )
and IsPosInt( maxnr )
and IsList( files )
and ForAll( files, IsString ) ) then
Error( "not a valid t.o.c.ext entry" );
elif ForAll( AtlasOfGroupRepresentationsInfo.GAPnames,
x -> x[2] <> atlasname ) then
Error( "`", atlasname, "' is not a valid group name" );
fi;
# Check that the required programs really exist.
if not AGR.IsPrgNameAvailable( files[1] ) then
# The program for the max. subgroup of the factor is not available.
return;
elif IsBound( files[2] ) then
# Check whether the required program for computing kernel generators
# is available.
info:= First( AtlasOfGroupRepresentationsInfo.GAPnames,
x -> x[2] = atlasname )[3];
if not ( IsBound( info.kernelPrograms ) and
ForAny( info.kernelPrograms, x -> x[2] = files[2] ) ) then
Print( "#E kernel program required by `", atlasname, "' and `",
files, "' not available\n" );
return;
fi;
fi;
fi;
r:= AtlasTableOfContents( "remote" ).TableOfContents;
if not IsBound( r.( atlasname ) ) then
r.( atlasname ):= rec();
fi;
r:= r.( atlasname );
if not IsBound( r.maxext ) then
r.maxext:= [];
fi;
Add( r.maxext, [ std, maxnr, files ] );
end;
#############################################################################
##
#F AGR.API( <repname>, <info> )
##
## <#GAPDoc Label="AGR.API">
## <Mark><C>AGR.API( <A>repname</A>, <A>info</A> )</C></Mark>
## <Item>
## Called with the string <A>repname</A> (denoting the name of a
## permutation representation)
## and the list <A>info</A> (describing the point stabilizer of this
## representation),
## <C>AGR.API</C> binds the component <A>repname</A> of the record
## <C>AtlasOfGroupRepresentationsInfo.permrepinfo</C> to <A>info</A>.
## <P/>
## <A>info</A> has the following entries.
## <List>
## <Item>
## At position <M>1</M>, the transitivity is stored.
## </Item>
## <Item>
## If the transitivity is zero then the second entry is the list of
## orbit lengths.
## </Item>
## <Item>
## If the transitivity is positive then the second entry is the rank
## of the action.
## </Item>
## <Item>
## If the transitivity is positive then the third entry is one of the
## strings <C>"prim"</C>, <C>"imprim"</C>, denoting primitivity or not.
## </Item>
## <Item>
## If the transitivity is positive then the fourth entry is a string
## describing the structure of the point stabilizer.
## If the third entry is <C>"imprim"</C> then this description consists
## of a subgroup part and a maximal subgroup part, separated by
## <C>" < "</C>.
## </Item>
## <Item>
## If the third entry is <C>"prim"</C> then the fifth entry is either
## <C>"???"</C>
## or it denotes the number of the class of maximal subgroups
## that are the point stabilizers.
## </Item>
## </List>
## <P/>
## An example of a valid call is
## <C>AGR.API("A5G1-p5B0",[3,2,"prim","A4",1])</C>.
## </Item>
## <#/GAPDoc>
##
AGR.API:= function( repname, info )
local r;
if IsBound( AtlasOfGroupRepresentationsInfo.checkData ) then
if IsBound( AtlasOfGroupRepresentationsInfo.permrepinfo.( repname ) ) then
Error( "cannot notify `", repname, "' more than once" );
fi;
# Check that this representation really exists.
if not AGR.IsRepNameAvailable( repname ) then
return;
fi;
fi;
r:= rec( transitivity:= info[1] );
if info[1] = 0 then
r.orbits:= info[2];
else
r.rankAction:= info[2];
r.isPrimitive:= ( info[3] = "prim" );
r.stabilizer:= info[4];
if r.isPrimitive then
r.maxnr:= info[5];
fi;
fi;
AtlasOfGroupRepresentationsInfo.permrepinfo.( repname ):= r;
end;
#############################################################################
##
#F AGR.CHAR( <groupname>, <repname>, <char>, <pos>[, <charname>] )
##
## <#GAPDoc Label="AGR.CHAR">
## <Mark><C>AGR.CHAR( <A>groupname</A>, <A>repname</A>, <A>char</A>, <A>pos</A>[, <A>charname</A>] )</C></Mark>
## <Item>
## Called with the strings <A>groupname</A> (the &GAP; name of the group)
## and <A>repname</A> (denoting the name of the representation),
## the integer <A>char</A> (the characteristic of the representation),
## and <A>pos</A> (the position or list of positions of the irreducible
## constituent(s)),
## <C>AGR.CHAR</C> stores the information in
## <C>AtlasOfGroupRepresentationsInfo.characterinfo</C>.
## A string describing the character can be entered as <A>charname</A>.
## <P/>
## An example of a valid call is
## <C>AGR.CHAR("M11","M11G1-p11B0",0,[1,2],"1a+10a")</C>.
## </Item>
## <#/GAPDoc>
##
AGR.CHAR:= function( arg )
local map, groupname, repname, char, pos;
map:= AtlasOfGroupRepresentationsInfo.characterinfo;
groupname:= arg[1];
repname:= arg[2];
char:= arg[3];
pos:= arg[4];
if not IsBound( map.( groupname ) ) then
map.( groupname ):= [];
fi;
map:= map.( groupname );
if char = 0 then
char:= 1;
fi;
if not IsBound( map[ char ] ) then
map[ char ]:= [ [], [], [] ];
fi;
map:= map[ char ];
if IsBound( AtlasOfGroupRepresentationsInfo.checkData ) then
# Check whether we have already a character for this representation.
# (Two different representations with the same character are allowed.)
if arg[2] in map[2] and map[1][ Position( map[2], repname ) ] <> pos then
Error( "attempt to enter two different characters for ", arg[2] );
fi;
# Check that this representation really exists.
if not AGR.IsRepNameAvailable( repname ) then
return;
fi;
fi;
Add( map[1], pos );
Add( map[2], repname );
if Length( arg ) = 5 then
Add( map[3], arg[5] );
else
Add( map[3], fail );
fi;
end;
#############################################################################
##
#F AGR.CompareAsNumbersAndNonnumbers( <nam1>, <nam2> )
##
## This function is available as `BrowseData.CompareAsNumbersAndNonnumbers'
## if the Browse package is available.
## But we must deal also with the case that this package is not available.
##
AGR.CompareAsNumbersAndNonnumbers:= function( nam1, nam2 )
local len1, len2, len, digit, comparenumber, i;
len1:= Length( nam1 );
len2:= Length( nam2 );
len:= len1;
if len2 < len then
len:= len2;
fi;
digit:= false;
comparenumber:= 0;
for i in [ 1 .. len ] do
if nam1[i] in DIGITS then
if nam2[i] in DIGITS then
digit:= true;
if comparenumber = 0 then
# first digit of a number, or previous digits were equal
if nam1[i] < nam2[i] then
comparenumber:= 1;
elif nam1[i] <> nam2[i] then
comparenumber:= -1;
fi;
fi;
else
# if digit then the current number in `nam2' is shorter,
# so `nam2' is smaller;
# if not digit then a number starts in `nam1' but not in `nam2',
# so `nam1' is smaller
return not digit;
fi;
elif nam2[i] in DIGITS then
# if digit then the current number in `nam1' is shorter,
# so `nam1' is smaller;
# if not digit then a number starts in `nam2' but not in `nam1',
# so `nam2' is smaller
return digit;
else
# both characters are non-digits
if digit then
# first evaluate the current numbers (which have the same length)
if comparenumber = 1 then
# nam1 is smaller
return true;
elif comparenumber = -1 then
# nam2 is smaller
return false;
fi;
digit:= false;
fi;
# now compare the non-digits
if nam1[i] <> nam2[i] then
return nam1[i] < nam2[i];
fi;
fi;
od;
if digit then
# The suffix of the shorter string is a number.
# If the longer string continues with a digit then it is larger,
# otherwise the first digits of the number decide.
if len < len1 and nam1[ len+1 ] in DIGITS then
# nam2 is smaller
return false;
elif len < len2 and nam2[ len+1 ] in DIGITS then
# nam1 is smaller
return true;
elif comparenumber = 1 then
# nam1 is smaller
return true;
elif comparenumber = -1 then
# nam2 is smaller
return false;
fi;
fi;
# Now the longer string is larger.
return len1 < len2;
end;
#############################################################################
##
#F AGR.SetGAPnamesSortDisp()
##
## Bind the component `AtlasOfGroupRepresentationsInfo.GAPnamesSortDisp'.
##
AGR.SetGAPnamesSortDisp:= function()
local list;
list:= ShallowCopy( AtlasOfGroupRepresentationsInfo.GAPnames );
SortParallel( List( list, x -> x[1] ), list,
AGR.CompareAsNumbersAndNonnumbers );
AtlasOfGroupRepresentationsInfo.GAPnamesSortDisp:= list;
end;
#############################################################################
##
#F AGR.ParseFilenameFormat( <string>, <format> )
##
AGR.ParseFilenameFormat:= function( string, format )
local result, i, res;
string:= SplitString( string, "-" );
if Length( string ) <> Length( format[1] ) then
return fail;
fi;
result:= [];
for i in [ 1 .. Length( string ) ] do
# Loop over the '-' separated components.
res:= format[2][i]( string[i], format[1][i] );
if res = fail then
return fail;
fi;
Append( result, res );
od;
return result;
end;
#############################################################################
##
#F AtlasDataGAPFormatFile( <filename> )
##
InstallGlobalFunction( AtlasDataGAPFormatFile, function( filename )
local record;
InfoRead1( "#I reading `", filename, "' started\n" );
record:= ReadAsFunction( filename );
InfoRead1( "#I reading `", filename, "' done\n" );
if record = fail then
Info( InfoAtlasRep, 1,
"problem reading `", filename, "' as function\n" );
else
record:= record();
fi;
return record;
end );
#############################################################################
##
#F AtlasStringOfFieldOfMatrixEntries( <mats> )
#F AtlasStringOfFieldOfMatrixEntries( <filename> )
##
InstallGlobalFunction( AtlasStringOfFieldOfMatrixEntries, function( mats )
local F, n, str;
if IsString( mats ) then
mats:= AtlasDataGAPFormatFile( mats ).generators;
fi;
if IsCyclotomicCollCollColl( mats ) then
F:= Field( Rationals, Flat( mats ) );
elif ForAll( mats, IsQuaternionCollColl ) then
F:= Field( Flat( List( Flat( mats ), ExtRepOfObj ) ) );
else
Error( "<mats> must be a matrix list of cyclotomics or quaternions" );
fi;
n:= Conductor( F );
if DegreeOverPrimeField( F ) = 2 then
# The field is quadratic,
# so it is generated by `Sqrt(n)' if $`n' \equiv 1 \pmod{4}$,
# by `Sqrt(-n)' if $`n' \equiv 3 \pmod{4}$,
# and by one of `Sqrt(n/4)', `Sqrt(-n/4)' otherwise.
if n mod 4 = 1 then
str:= Concatenation( "[Sqrt(", String( n ), ")]" );
elif n mod 4 = 3 then
str:= Concatenation( "[Sqrt(-", String( n ), ")]" );
elif Sqrt( -n/4 ) in F then
str:= Concatenation( "[Sqrt(-", String( n/4 ), ")]" );
else
str:= Concatenation( "[Sqrt(", String( n/4 ), ")]" );
fi;
elif IsCyclotomicField( F ) then
# The field is not quadratic but cyclotomic.
str:= Concatenation( "[E(", String( n ), ")]" );
else
str:= "";
fi;
if IsCyclotomicCollCollColl( mats ) then
if str = "" then
str:= String( F );
else
str:= Concatenation( "Field(", str, ")" );
fi;
elif F = Rationals then
str:= "QuaternionAlgebra(Rationals)";
elif str = "" then
str:= Concatenation( "QuaternionAlgebra(", String( F ), ")" );
else
str:= Concatenation( "QuaternionAlgebra(", str, ")" );
fi;
return [ F, str ];
end );
#############################################################################
##
#F AtlasOfGroupRepresentationsScanFilename( <name>, <result> )
##
BindGlobal( "AtlasOfGroupRepresentationsScanFilename",
function( name, result )
local filename, pos, type, format, groupname;
filename:= name;
pos:= Position( filename, '/' );
while pos <> fail do
filename:= filename{ [ pos+1 .. Length( filename ) ] };
pos:= Position( filename, '/' );
od;
for type in AGR.DataTypes( "rep", "prg" ) do
format:= AGR.ParseFilenameFormat( filename, type[2].FilenameFormat );
if format <> fail then
groupname:= format[1];
if not IsBound( result.( groupname ) ) then
result.( groupname ):= rec();
fi;
if not IsBound( result.( groupname ).( type[1] ) ) then
result.( groupname ).( type[1] ):= [];
fi;
return type[2].AddFileInfo( result.( groupname ).( type[1] ),
format, name );
fi;
od;
return false;
end );
#############################################################################
##
#F AtlasOfGroupRepresentationsUpdateTableOfContentsFile( <dstfilename> )
##
## Fetch the current table of contents from the package's home directory,
## and write it to the file <A>dstfilename</A>,
## which is interpreted as an absolute path.
##
## This function is used by the script <F>etc/maketoc</F> and in the call
## <C>AtlasTableOfContents( "remote" )</C>.
##
BindGlobal( "AtlasOfGroupRepresentationsUpdateTableOfContentsFile",
function( dstfilename )
local version, inforec, home, server, path;
version:= InstalledPackageVersion( "atlasrep" );
inforec:= First( PackageInfo( "atlasrep" ), r -> r.Version = version );
home:= inforec.PackageWWWHome;
if home{ [ 1 .. 7 ] } = "http://" then
home:= home{ [ 8 .. Length( home ) ] };
fi;
server:= home{ [ 1 .. Position( home, '/' ) - 1 ] };
path:= home{ [ Position( home, '/' ) + 1 .. Length( home ) ] };
# Fetch the file if possible.
return AtlasOfGroupRepresentationsTransferFile( server,
Concatenation( path, "/atlasprm.g" ),
dstfilename );
end );
#############################################################################
##
#F AtlasOfGroupRepresentationsComposeTableOfContents( <filelist>,
#F <groupnames> )
##
## This code is used by `AtlasTableOfContents'.
##
BindGlobal( "AtlasOfGroupRepresentationsComposeTableOfContents",
function( filelist, groupnames )
local result, name, len, groupname, record, type, listtosort;
# Initialize the result record.
result:= rec( otherfiles:= [] );
# Deal with the case of `gzip'ped files, and omit obvious garbage.
for name in Set( filelist ) do
len:= Length( name );
if 3 <= len and name{ [ len-2 .. len ] } = ".gz" then
name:= name{ [ 1 .. len-3 ] };
fi;
if AtlasOfGroupRepresentationsScanFilename( name, result ) = false then
if not ( name in [ "dummy", ".", "..", "CVS", "CVS:", "./CVS:",
"Entries", "Repository", "Root", "toc.g",
".cvsignore" ] or
name[ Length( name ) ] = '%' or
( 3 <= Length( name )
and name{ Length( name ) + [ - 2 .. 0 ] } = "BAK" ) ) then
Info( InfoAtlasRep, 3,
"t.o.c. construction: ignoring name `", name, "'" );
AddSet( result.otherfiles, name );
fi;
fi;
od;
# Postprocessing,
# and *sort* the representations as given in the type definition.
for groupname in List( groupnames, x -> x[3] ) do
if IsBound( result.( groupname ) ) then
record:= result.( groupname );
for type in AGR.DataTypes( "rep", "prg" ) do
if IsBound( record.( type[1] ) ) then
type[2].PostprocessFileInfo( result, record );
# Sort the data of the given type as defined.
if IsBound( type[2].SortTOCEntries ) then
listtosort:= List( record.( type[1] ), type[2].SortTOCEntries );
SortParallel( listtosort, record.( type[1] ) );
fi;
fi;
od;
fi;
od;
# Store the current date in Coordinated Universal Time
# (Greenwich Mean Time).
result.lastupdated:= CurrentDateTimeString();
return result;
end );
#############################################################################
##
#F AtlasTableOfContents( <dirname> )
##
InstallGlobalFunction( AtlasTableOfContents, function( string )
local toc, groupnames, prefix, filenames, dstdir, dstfile, tocremote,
typeinfo, groupname, r, pair, type, entry, fileinfo, filename, loc,
dirname, privdir, dir, result;
toc:= AtlasOfGroupRepresentationsInfo.TableOfContents;
# Take the stored version if it is already available.
groupnames:= AtlasOfGroupRepresentationsInfo.groupnames;
if IsBound( toc.( string ) ) then
return rec( groupnames := groupnames,
TableOfContents := toc.( string ) );
fi;
prefix:= "";
filenames:= [];
if string = "remote" then
# Someone has unbound the value, we interpret this as the wish
# to transfer the current file `atlasprm.g' from the package homepage,
# and to read it.
# If we are allowed to overwrite the file from the installation
# then we do this, otherwise we use a temporary file.
dstdir:= DirectoriesPackageLibrary( "atlasrep", "gap" );
dstfile:= Filename( dstdir, "atlasprm.g" );
if not IsWritableFile( dstfile ) then
dstfile:= Filename( DirectoryTemporary(), "atlasprm.g" );
fi;
if not AtlasOfGroupRepresentationsUpdateTableOfContentsFile( dstfile )
then
return fail;
fi;
ReplaceAtlasTableOfContents( dstfile );
return rec( groupnames := AtlasOfGroupRepresentationsInfo.groupnames,
TableOfContents := toc.remote );
elif string = "local" then
# List the information that is locally available,
# by testing which of the files in the remote t.o.c. are stored.
tocremote:= AtlasTableOfContents( "remote" ).TableOfContents;
typeinfo:= Concatenation(
List( AGR.DataTypes( "rep" ), x -> [ "datagens", x ] ),
List( AGR.DataTypes( "prg" ), x -> [ "dataword", x ] ) );
for groupname in RecNames( tocremote ) do
r:= tocremote.( groupname );
if IsRecord( r ) then
for pair in typeinfo do
type:= pair[2];
if IsBound( r.( type[1] ) ) then
for entry in r.( type[1] ) do
fileinfo:= entry[ Length( entry ) ];
if IsString( fileinfo ) then
fileinfo:= [ fileinfo ];
fi;
for filename in fileinfo do
loc:= AtlasOfGroupRepresentationsLocalFilename( pair[1],
groupname, filename, type );
if not IsEmpty( loc ) and ForAll( loc[1][2], x -> x[2] ) then
Add( filenames, filename );
fi;
od;
od;
fi;
od;
fi;
od;
else
# List the contents of the private data directories.
dirname:= First( AtlasOfGroupRepresentationsInfo.private,
pair -> pair[2] = string )[1];
if IsExistingFile( dirname ) then
# List the information available in the given private directory.
# (Up to one directory layer above the data files is supported.)
filenames:= [];
privdir:= Directory( dirname );
for dir in Difference( DirectoryContents( dirname ), [ ".", ".." ] ) do
dstfile:= Filename( privdir, dir );
if IsDirectoryPath( dstfile ) then
Append( filenames, List( DirectoryContents( dstfile ),
x -> Concatenation( dir, "/", x ) ) );
else
Add( filenames, dir );
fi;
od;
fi;
fi;
# Compose the result record.
result:= AtlasOfGroupRepresentationsComposeTableOfContents( filenames,
groupnames );
result.diridPrivate:= string;
# Store the newly computed table of contents.
toc.( string ):= result;
# Return the result record.
return rec( groupnames := groupnames,
TableOfContents := result );
end );
#############################################################################
##
#F ReloadAtlasTableOfContents( \"local\" )
#F ReloadAtlasTableOfContents( \"remote\" )
##
InstallGlobalFunction( ReloadAtlasTableOfContents, function( string )
local toc, old, new;
toc:= AtlasOfGroupRepresentationsInfo.TableOfContents;
if IsBound( toc.( string ) ) then
old:= toc.( string );
Unbind( toc.( string ) );
fi;
new:= AtlasTableOfContents( string );
if new = fail then
if IsBound( old ) then
toc.( string ):= old;
fi;
Info( InfoAtlasRep, 1,
"could not reload ", string, " table of contents" );
return fail;
fi;
toc.( string ):= new.TableOfContents;
if string = "remote" then
AtlasOfGroupRepresentationsInfo.groupnames:= new.groupnames;
fi;
return true;
end );
#############################################################################
##
#F StoreAtlasTableOfContents( <filename> )
##
## Note that we must put everything into a single file because this is the
## one that can be fetched automatically by users.
##
InstallGlobalFunction( StoreAtlasTableOfContents, function( filename )
if IsExistingFile( filename ) and not IsWritableFile( filename ) then
Error( "<filename> must be writable if exists" );
elif not IsBound( AtlasOfGroupRepresentationsInfo.TableOfContents.(
"remote" ) ) then
Error( "no remote t.o.c. exists" );
fi;
# Add the data.
FileString( filename, StringOfAtlasTableOfContents( "remote" ) );
end );
#############################################################################
##
#F ReplaceAtlasTableOfContents( <filename> )
##
InstallGlobalFunction( ReplaceAtlasTableOfContents, function( filename )
local priv, pair;
if not IsReadableFile( filename ) then
Error( "<filename> must be the name of a readable file" );
fi;
# Remove the information we are going to add by reading the file.
AtlasOfGroupRepresentationsInfo.TableOfContents.( "remote" ):= rec();
priv:= AtlasOfGroupRepresentationsInfo.private;
AtlasOfGroupRepresentationsInfo.GAPnames := [];
AtlasOfGroupRepresentationsInfo.GAPnamesSortDisp:= [];
AtlasOfGroupRepresentationsInfo.groupnames := [];
AtlasOfGroupRepresentationsInfo.ringinfo := [];
AtlasOfGroupRepresentationsInfo.private := [];
AtlasOfGroupRepresentationsInfo.characterinfo:= rec();
AtlasOfGroupRepresentationsInfo.permrepinfo:= rec();
AGR.MapNameToGAPName:= [ [], [] ];
AGR.GAPnamesRec:= rec();
# Replace the old version by the new one.
AtlasOfGroupRepresentationsInfo.checkData:= true;
Reread( filename );
Unbind( AtlasOfGroupRepresentationsInfo.checkData );
# Reinsert the information from private directories.
for pair in priv do
AtlasOfGroupRepresentationsNotifyPrivateDirectory( pair[1], pair[2] );
od;
end );
#############################################################################
##
#F StringOfAtlasTableOfContents( <string> )
##
InstallGlobalFunction( StringOfAtlasTableOfContents, function( string )
local str, pos, toc, sepline, triple, groupname, reps, type, entry,
lines, nam, line, r, map, i, j;
# Currently only the argument `"remote"' is supported.
if string <> "remote" then
Error( "sorry, private tables of contents are not yet supported here" );
fi;
# Copy the constant part of the data to the desired file.
str:= StringFile( Filename( DirectoriesPackageLibrary( "atlasrep", "gap" ),
"atlasprm.g" ) );
pos:= PositionSublist( str, "AGR.SetGAPnamesSortDisp();" );
pos:= Position( str, '\n', pos );
str:= str{ [ 1 .. pos ] };
toc:= AtlasTableOfContents( string ).TableOfContents;
sepline:= RepeatedString( "#", 77 );
Append( str, "\n\n" );
Append( str, sepline );
Append( str, "\n##\n## " );
Append( str, "Store group orders.\n" );
Append( str, "##\n" );
lines:= [];
for nam in RecNames( AGR.GAPnamesRec ) do
if IsBound( AGR.GAPnamesRec.( nam )[3].size ) then
entry:= AGR.GAPnamesRec.( nam )[3].size;
Add( lines, Concatenation( "AGR.GRS(\"", nam, "\",",
String( entry ), ");\n" ) );
fi;
od;
Sort( lines );
for line in lines do
Append( str, line );
od;
Append( str, "\n\n" );
Append( str, sepline );
Append( str, "\n##\n## " );
Append( str, "Store numbers of classes of maximal subgroups.\n" );
Append( str, "##\n" );
lines:= [];
for nam in RecNames( AGR.GAPnamesRec ) do
if IsBound( AGR.GAPnamesRec.( nam )[3].nrMaxes ) then
entry:= AGR.GAPnamesRec.( nam )[3].nrMaxes;
Add( lines, Concatenation( "AGR.MXN(\"", nam, "\",",
String( entry ), ");\n" ) );
fi;
od;
Sort( lines );
for line in lines do
Append( str, line );
od;
Append( str, "\n\n" );
Append( str, sepline );
Append( str, "\n##\n## " );
Append( str, "Store orders of maximal subgroups.\n" );
Append( str, "##\n" );
lines:= [];
for nam in RecNames( AGR.GAPnamesRec ) do
if IsBound( AGR.GAPnamesRec.( nam )[3].sizesMaxes ) then
entry:= AGR.GAPnamesRec.( nam )[3].sizesMaxes;
Add( lines, Concatenation( "AGR.MXO(\"", nam, "\",",
ReplacedString( String( entry ), " ", "" ), ");\n" ) );
fi;
od;
Sort( lines );
for line in lines do
Append( str, line );
od;
Append( str, "\n\n" );
Append( str, sepline );
Append( str, "\n##\n## " );
Append( str, "Store structures of maximal subgroups.\n" );
Append( str, "##\n" );
lines:= [];
for nam in RecNames( AGR.GAPnamesRec ) do
if IsBound( AGR.GAPnamesRec.( nam )[3].structureMaxes ) then
entry:= AGR.GAPnamesRec.( nam )[3].structureMaxes;
Add( lines, Concatenation( "AGR.MXS(\"", nam, "\",",
ReplacedString( String( entry ), " ", "" ), ");\n" ) );
fi;
od;
Sort( lines );
for line in lines do
Append( str, line );
od;
Append( str, "\n\n" );
Append( str, sepline );
Append( str, "\n##\n## " );
Append( str, "Store information about generators of kernels.\n" );
Append( str, "##\n" );
lines:= [];
for nam in RecNames( AGR.GAPnamesRec ) do
if IsBound( AGR.GAPnamesRec.( nam )[3].kernelPrograms ) then
for entry in AGR.GAPnamesRec.( nam )[3].kernelPrograms do
Add( lines, Concatenation( "AGR.KERPRG(\"", nam, "\",",
ReplacedString( String( entry ), " ", "" ), ");\n" ) );
od;
fi;
od;
Sort( lines );
for line in lines do
Append( str, line );
od;
Append( str, "\n\n" );
Append( str, sepline );
Append( str, "\n##\n## " );
Append( str, "In the following, the table of contents is built\n" );
Append( str, "## using `AGR.GRP' and `AGR.TOC'.\n" );
Append( str, "## This part of the file is created by the function\n" );
Append( str, "## `StringOfAtlasTableOfContents',\n" );
Append( str, "## do not edit below this line!\n" );
Append( str, "##\n\n" );
for triple in AtlasOfGroupRepresentationsInfo.groupnames do
# Start with the notification of the group.
Append( str, "# " );
Append( str, triple[3] );
Append( str, "\n" );
Append( str, "AGR.GRP(\"" );
Append( str, triple[1] );
Append( str, "\",\"" );
Append( str, triple[2] );
Append( str, "\",\"" );
Append( str, triple[3] );
Append( str, "\");\n" );
# Append the notifications of data entries for the group.
groupname:= triple[3];
if IsBound( toc.( groupname ) ) then
reps:= toc.( groupname );
for type in AGR.DataTypes( "rep", "prg" ) do
if IsBound( reps.( type[1] ) ) then
for entry in reps.( type[1] ) do
if IsBound( type[2].TOCEntryString ) then
Append( str, type[2].TOCEntryString( type[1], entry ) );
else
Info( InfoAtlasRep, 1,
"no component `TOCEntryString' for data type `",
type[1], "'" );
fi;
od;
fi;
od;
fi;
Append( str, "\n" );
od;
Append( str, "\n" );
Append( str, sepline );
Append( str, "\n##\n## " );
Append( str,
"What follows now are the representation dependent additional data.\n" );
Append( str, "## They must be read after the notification of the representations,\n" );
Append( str, "## in order to give us a chance to check the existence of the underlying\n");
Append( str, "## representation.\n" );
Append( str, "##\n" );
# Append the compatibility information w.r.t. factors.
Append( str, "\n\n" );
Append( str, sepline );
Append( str, "\n##\n## " );
Append( str, "Store info about compatibility of generators with those of factor groups.\n" );
Append( str, "##\n" );
lines:= [];
for nam in RecNames( AGR.GAPnamesRec ) do
if IsBound( AGR.GAPnamesRec.( nam )[3].factorCompatibility ) then
for entry in AGR.GAPnamesRec.( nam )[3].factorCompatibility do
Add( lines, Concatenation( "AGR.STDCOMP(\"", nam, "\",",
ReplacedString( String( entry ), " ", "" ), ");\n" ) );
od;
fi;
od;
Sort( lines );
for line in lines do
Append( str, line );
od;
# Append the information about the base rings of char. zero repres.
Append( str, "\n\n" );
Append( str, sepline );
Append( str, "\n##\n## " );
Append( str, "Store information about the rings over which characteristic zero\n" );
Append( str, "## representations are written if known.\n" );
Append( str, "## Note that the filenames do not contain this information,\n" );
Append( str, "## so it has to be stored explicitly.\n" );
Append( str, "##\n" );
lines:= [];
for entry in AtlasOfGroupRepresentationsInfo.ringinfo do
Add( lines, Concatenation( "AGR.RNG(\"", entry[1], "\",\"", entry[2],
"\");\n" ) );
od;
Sort( lines );
for line in lines do
Append( str, line );
od;
# Append the information about compatibility of maxes scripts.
Append( str, "\n\n" );
Append( str, sepline );
Append( str, "\n##\n## " );
Append( str,
"Store information which straight line programs for restricting to maximal\n" );
Append( str,
"## subgroups of a group can be used also for restricting to maximal\n" );
Append( str, "## subgroups of downward extensions.\n" );
Append( str, "##\n" );
lines:= [];
for nam in RecNames( toc ) do
if IsRecord( toc.( nam ) ) and IsBound( toc.( nam ).maxext ) then
for entry in toc.( nam ).maxext do
Add( lines, Concatenation( "AGR.TOCEXT(\"", nam, "\",",
String( entry[1] ), ",", String( entry[2] ), ",",
ReplacedString( String( entry[3] ), " ", "" ), ");\n" ) );
od;
fi;
od;
Sort( lines );
for line in lines do
Append( str, line );
od;
# Append the primitivity information.
Append( str, "\n\n" );
Append( str, sepline );
Append( str, "\n##\n## " );
Append( str,
"Store information about the point stabilizers of permutation\n" );
Append( str, "## representations if known.\n" );
Append( str,
"## Note that the filenames do not contain this information,\n" );
Append( str, "## so it has to be stored explicitly.\n" );
Append( str, "##\n" );
lines:= [];
for nam in RecNames( AtlasOfGroupRepresentationsInfo.permrepinfo ) do
r:= AtlasOfGroupRepresentationsInfo.permrepinfo.( nam );
if not IsBound( r.isPrivate ) then
line:= Concatenation( "AGR.API(\"", nam, "\",[" );
Append( line, String( r.transitivity ) );
Append( line, "," );
if r.transitivity = 0 then
Append( line, ReplacedString( String( r.orbits ), " ", "" ) );
Append( line, "]);\n" );
else
Append( line, String( r.rankAction ) );
Append( line, "," );
if r.isPrimitive then
Append( line, "\"prim\"" );
else
Append( line, "\"imprim\"" );
fi;
Append( line, ",\"" );
Append( line, r.stabilizer );
Append( line, "\"" );
if r.isPrimitive then
Append( line, "," );
if IsInt( r.maxnr ) then
Append( line, String( r.maxnr ) );
else
Append( line, "\"" );
Append( line, String( r.maxnr ) );
Append( line, "\"" );
fi;
fi;
Append( line, "]);\n" );
fi;
Add( lines, line );
fi;
od;
Sort( lines );
for line in lines do
Append( str, line );
od;
# Append the character information.
Append( str, "\n\n" );
Append( str, sepline );
Append( str, "\n##\n## precomputed character data\n" );
Append( str, "##\n" );
lines:= [];
for nam in RecNames( AtlasOfGroupRepresentationsInfo.characterinfo ) do
map:= AtlasOfGroupRepresentationsInfo.characterinfo.( nam );
for i in [ 1 .. Length( map ) ] do
if IsBound( map[i] ) then
for j in [ 1 .. Length( map[i][1] ) ] do
line:= Concatenation( "AGR.CHAR(\"", nam, "\",\"", map[i][2][j], "\"," );
#T only for the non-private ones!
if i = 1 then
Append( line, "0" );
else
Append( line, String( i ) );
fi;
Append( line, "," );
Append( line, ReplacedString( String( map[i][1][j] ), " ", "" ) );
if map[i][3][j] <> fail then
Append( line, ",\"" );
Append( line, map[i][3][j] );
Append( line, "\"" );
fi;
Append( line, ");\n" );
Add( lines, line );
od;
fi;
od;
od;
Sort( lines );
for line in lines do
Append( str, line );
od;
# Finally, append the current date and time.
Append( str, "\n\n" );
Append( str, "AtlasOfGroupRepresentationsInfo.TableOfContents.( \"" );
Append( str, string );
Append( str, "\" ).lastupdated:=\n \"" );
Append( str, toc.lastupdated );
Append( str, "\";\n\n" );
Append( str, sepline );
Append( str, "\n##\n#E\n\n" );
return str;
end );
#############################################################################
##
#F AGR.TablesOfContents( <descr> )
##
## Admissible arguments are
## 1 the string "all",
## 2 the string "public",
## 3 a string describing a private table of contents,
## (which occurs as the second component of an entry in
## `AtlasOfGroupRepresentationsInfo.private') or
## 4 a list of conditions such as [ <std>, "contents", <...> ]
## 5 a list of strings as 1-3.
##
AGR.TablesOfContents:= function( descr )
local pos, tocid, i, label, tocs, flag, toc, pair;
if descr = [] then
descr:= [ "all" ];
elif IsString( descr ) then
descr:= [ descr ];
elif not IsList( descr ) then
Error( "<descr> must be a string or a list of strings/conditions" );
fi;
pos:= Position( descr, "contents" );
if pos <> fail then
# `descr' is a list of conditions.
# Evaluate its "contents" part,
# i. e., restrict the tables of contents, and remove this condition.
tocid:= descr[ pos+1 ];
for i in [ pos .. Length( descr ) - 2 ] do
descr[i]:= descr[ i+2 ];
od;
Unbind( descr[ Length( descr ) ] );
Unbind( descr[ Length( descr ) ] );
if IsString( tocid ) then
descr:= [ tocid ];
else
descr:= tocid;
fi;
elif ForAny( descr, x -> x <> "all" and x <> "public"
and ForAll( AtlasOfGroupRepresentationsInfo.private,
pair -> x <> pair[2] ) ) then
# `descr' is a list of conditions that does not restrict the
# table of contents.
descr:= [ "all" ];
fi;
# Now `descr' is a list of identifiers of tables of contents.
label:= JoinStringsWithSeparator( SortedList( descr ), "|" );
if not IsBound( AtlasOfGroupRepresentationsInfo.TOC_Cache.( label ) ) then
tocs:= [];
if descr = "all" or descr = "public" or
"all" in descr or "public" in descr then
flag:= String( AtlasOfGroupRepresentationsInfo.remote );
if not IsBound( AtlasOfGroupRepresentationsInfo.TOC_Cache.(
flag ) ) then
if flag = "true" then
toc:= AtlasTableOfContents( "remote" ).TableOfContents;
else
toc:= AtlasTableOfContents( "local" ).TableOfContents;
fi;
AtlasOfGroupRepresentationsInfo.TOC_Cache.( flag ):= toc;
fi;
Add( tocs, AtlasOfGroupRepresentationsInfo.TOC_Cache.( flag ) );
fi;
for pair in AtlasOfGroupRepresentationsInfo.private do
if descr = "all" or "all" in descr or
descr = pair[2] or pair[2] in descr then
Add( tocs,
AtlasOfGroupRepresentationsInfo.TableOfContents.( pair[2] ) );
fi;
od;
AtlasOfGroupRepresentationsInfo.TOC_Cache.( label ):= tocs;
fi;
return AtlasOfGroupRepresentationsInfo.TOC_Cache.( label );
end;
#############################################################################
##
#F AtlasOfGroupRepresentationsNotifyPrivateDirectory( <dir>[, <dirid>]
#F [, <test>] )
##
InstallGlobalFunction( AtlasOfGroupRepresentationsNotifyPrivateDirectory,
function( arg )
local dirname, dirid, test, oldtest, olddata, dir, prm, toc,
allfilenames, RemovedDirectories,
groupname, record, type, entry, name, tocs, ok, oldtoc, names,
unknown, nam;
# Get and check the arguments.
if 1 <= Length( arg ) and Length( arg ) <= 3 and
( IsString( arg[1] ) or IsDirectory( arg[1] ) ) and
( Length( arg ) = 1 or ( IsString( arg[2] ) or IsBool( arg[2] ) ) ) and
( Length( arg ) < 3 or ( IsString( arg[2] ) and IsBool( arg[3] ) ) ) then
if IsString( arg[1] ) then
dirname:= ShallowCopy( arg[1] );
else
dir:= arg[1];
dirname:= ShallowCopy( dir![1] );
#T this is not clean!
#T (how to call DirectoryContents for a directory given as an object?)
fi;
if Length( arg ) = 1 then
dirid:= dirname;
elif IsString( arg[2] ) or Length( arg ) = 3 then
dirid:= arg[2];
else
dirid:= dirname;
fi;
if Length( arg ) = 1 then
test:= false;
elif IsBool( arg[2] ) then
test:= arg[2];
elif Length( arg ) = 3 then
test:= arg[3];
else
test:= false;
fi;
else
Error( "usage: AtlasOfGroupRepresentationsNotifyPrivateDirectory(\n",
" <dirname>[,<dirid>][,<test>])" );
fi;
# Add the directory name.
if IsEmpty( dirname ) then
Error( "<dirname> must not be empty" );
elif dirname[ Length( dirname ) ] <> '/' then
Add( dirname, '/' );
fi;
if ForAny( AtlasOfGroupRepresentationsInfo.private,
pair -> pair[2] = dirid and pair[1] <> dirname ) then
Error( dirid,
" is already the identifier of another private directory" );
fi;
AddSet( AtlasOfGroupRepresentationsInfo.private, [ dirname, dirid ] );
# Read the primary file (for group declarations and describing data).
# The file may contain calls of `AGR.GNAN' (which must come *before* the
# data for the groups in question can be notified) and of `AGR.API' and
# `AGR.CHAR' (which must come *afterwards*).
# So we must postpone the calls of `AGR.IsRepNameAvailable'.
oldtest:= IsBound( AtlasOfGroupRepresentationsInfo.checkData );
Unbind( AtlasOfGroupRepresentationsInfo.checkData );
olddata:= rec(
permrepinfo:= RecNames( AtlasOfGroupRepresentationsInfo.permrepinfo ),
);
#T same for characters!
prm:= Filename( dir, "toc.g" );
if IsExistingFile( prm ) = true then
Read( prm );
fi;
# Set up a table of contents for the private directory.
toc:= AtlasTableOfContents( dirid ).TableOfContents;
Unbind( toc.otherfiles );
# Check that no filename of this table of contents exists already in
# other tables of contents.
# 1. Compute the list of all filenames.
allfilenames:= [];
RemovedDirectories:= function( string )
string:= SplitString( string, "/" );
return string[ Length( string ) ];
end;
for groupname in RecNames( toc ) do
record:= toc.( groupname );
if IsRecord( record ) then
for type in RecNames( record ) do
for entry in record.( type ) do
name:= entry[ Length( entry ) ];
if IsString( name ) then
Add( allfilenames, RemovedDirectories( name ) );
else
Append( allfilenames, List( name, RemovedDirectories ) );
fi;
od;
od;
fi;
od;
# 2. Check whether the list is disjoint to the other tables of contents.
#T this is expensive
if AtlasOfGroupRepresentationsInfo.remote = true then
tocs:= [ AtlasTableOfContents( "remote" ).TableOfContents ];
else
tocs:= [ AtlasTableOfContents( "local" ).TableOfContents ];
fi;
Append( tocs, List( Filtered( AtlasOfGroupRepresentationsInfo.private,
pair -> pair[2] <> dirid ),
x -> AtlasOfGroupRepresentationsInfo.TableOfContents.( x[2] ) ) );
ok:= true;
for oldtoc in tocs do
for groupname in RecNames( toc ) do
if IsBound( oldtoc.( groupname ) ) then
record:= oldtoc.( groupname );
if IsRecord( record ) then
for type in RecNames( record ) do
for entry in record.( type ) do
names:= entry[ Length( entry ) ];
if IsString( names ) then
names:= [ names ];
fi;
for name in names do
if name in allfilenames then
ok:= false;
Info( InfoAtlasRep, 1,
"file `", name, "' was already in another t.o.c." );
#T better remove the entry for this file
fi;
od;
od;
od;
fi;
fi;
od;
od;
# Add group names that were not notified.
unknown:= Set( Filtered( RecNames( toc ),
x -> ForAll( AtlasOfGroupRepresentationsInfo.GAPnames,
pair -> x <> pair[2] ) ) );
RemoveSet( unknown, "diridPrivate" );
RemoveSet( unknown, "lastupdated" );
if not IsEmpty( unknown ) then
ok:= false;
Info( InfoAtlasRep, 1,
"no GAP names defined for ", unknown );
UniteSet( AtlasOfGroupRepresentationsInfo.GAPnames,
List( unknown, x -> [ x, x ] ) );
fi;
AGR.SetGAPnamesSortDisp();
AtlasOfGroupRepresentationsInfo.TOC_Cache:= rec();
AtlasOfGroupRepresentationsInfo.TableOfContents.merged:= rec();
# Run the postponed tests.
if test then
AtlasOfGroupRepresentationsInfo.checkData:= true;
for nam in Difference(
RecNames( AtlasOfGroupRepresentationsInfo.permrepinfo ),
olddata.permrepinfo ) do
AtlasOfGroupRepresentationsInfo.permrepinfo.( nam ).isPrivate:= true;
ok:= AGR.IsRepNameAvailable( nam ) and ok;
od;
#T check also the characters!
fi;
# Restore the original flag.
if not oldtest then
Unbind( AtlasOfGroupRepresentationsInfo.checkData );
fi;
# Return the flag.
return ok;
end );
#############################################################################
##
#F AtlasOfGroupRepresentationsForgetPrivateDirectory( <dirid> )
##
InstallGlobalFunction( AtlasOfGroupRepresentationsForgetPrivateDirectory,
function( dirid )
local private, i, dirname, dir, prm, str, file, GAPnames, pos, pos2,
gapname, j;
Unbind( AtlasOfGroupRepresentationsInfo.TableOfContents.( dirid ) );
GAPnames:= AtlasOfGroupRepresentationsInfo.GAPnames;
private:= AtlasOfGroupRepresentationsInfo.private;
for i in [ 1 .. Length( private ) ] do
if private[i][2] = dirid then
# Remove the group declarations.
dirname:= private[i][1];
dir:= Directory( dirname );
prm:= Filename( dir, "toc.g" );
if IsExistingFile( prm ) = true then
str:= StringFile( prm );
pos:= PositionSublist( str, "GNAN" );
while pos <> fail do
while pos <= Length( str ) and str[ pos ] <> '"' do
pos:= pos + 1;
pos2:= Position( str, '"', pos );
if pos2 <> fail then
gapname:= str{ [ pos+1 .. pos2-1 ] };
for j in [ 1 .. Length( GAPnames ) ] do
if IsBound( GAPnames[j] ) and GAPnames[j][1] = gapname then
Unbind( GAPnames[j] );
Unbind( AGR.GAPnamesRec.( gapname ) );
break;
fi;
od;
fi;
od;
pos:= PositionSublist( str, "GNAN", pos2 );
od;
fi;
# Remove the information concerning the private directory.
Unbind( private[i] );
AtlasOfGroupRepresentationsInfo.private:= Compacted( private );
break;
fi;
od;
AtlasOfGroupRepresentationsInfo.GAPnames:= Compacted( GAPnames );
AGR.SetGAPnamesSortDisp();
AtlasOfGroupRepresentationsInfo.TOC_Cache:= rec();
AtlasOfGroupRepresentationsInfo.TableOfContents.merged:= rec();
end );
if IsString( SingleHTTPRequest ) then
Unbind( SingleHTTPRequest );
fi;
if IsString( IO_stat ) then
Unbind( IO_stat );
fi;
#############################################################################
##
#E