GAP 4.8.9 installation with standard packages -- copy to your CoCalc project to get it
#############################################################################
####
##
#W general.gi ACE Package Alexander Hulpke
#W Greg Gamble
##
## This file installs mainly non-interactive ACE variables and functions.
## Though Alexander will barely recognise it, some of his ideas are still
## present.
##
#Y Copyright (C) 2000 Centre for Discrete Mathematics and Computing
#Y Department of Information Technology & Electrical Eng.
#Y University of Queensland, Australia.
##
#############################################################################
####
##
#V ACETCENUM . . . . . . . . The ACE version of the coset enumerator TCENUM
## . . . . CosetTableFromGensAndRels is set to ACECosetTableFromGensAndRels
##
InstallValue(ACETCENUM, rec(
name := "ACE (Advanced Coset Enumerator)",
CosetTableFromGensAndRels := ACECosetTableFromGensAndRels
));
#############################################################################
####
##
#F InfoACELevel . . . . . . . . . . . . . . . Get the InfoLevel for InfoACE
##
##
InstallGlobalFunction(InfoACELevel, function()
return InfoLevel(InfoACE);
end);
#############################################################################
####
##
#F SetInfoACELevel . . . . . . . . . . . . . . Set the InfoLevel for InfoACE
##
##
InstallGlobalFunction(SetInfoACELevel, function(arg)
if IsEmpty(arg) then
SetInfoLevel(InfoACE, 1); # Set to default level
else
SetInfoLevel(InfoACE, arg[1]);
fi;
end);
#############################################################################
####
##
#F ACEPackageVersion()
##
## returns the version number of the current ACE package.
##
InstallGlobalFunction(ACEPackageVersion, function()
return GAPInfo.PackagesInfo.ace[1].Version;
end);
#############################################################################
####
##
#F CALL_ACE . . . . . . . . . Called by ACECosetTable, ACEStats and ACEStart
##
##
InstallGlobalFunction(CALL_ACE, function(ACEfname, fgens, rels, sgens)
local optnames, echo, errmsg, onbreakmsg, infile, datarec, ToACE, gens,
standard, ignored;
if ValueOption("aceexampleoptions") = true and
IsBound(ACEData.aceexampleoptions) then
SANITISE_ACE_OPTIONS(OptionsStack[ Length(OptionsStack) ],
ACEData.aceexampleoptions);
PushOptions(ACEData.aceexampleoptions);
Unbind(ACEData.aceexampleoptions);
ACEData.options := OptionsStack[ Length(OptionsStack) ];
PopOptions();
OptionsStack[ Length(OptionsStack) ] := ACEData.options;
Unbind(ACEData.options);
fi;
optnames := ACE_OPT_NAMES();
# We have hijacked ACE's echo option ... we don't actually pass it to ACE
echo := ACE_VALUE_ECHO(optnames);
ECHO_ACE_ARGS( echo, ACEfname, rec(fgens := fgens,
rels := rels,
sgens := sgens) );
# Check arguments are valid
while IsEmpty(fgens) do
errmsg :=
["fgens (arg[1]) must be a non-empty list of group generators ..."];
onbreakmsg :=
["Type: 'quit;' to quit to outer loop, or",
"type: 'fgens := <val>; return;' to assign <val> to fgens to continue."
];
Error(ACE_ERROR(errmsg, onbreakmsg), "\n");
od;
fgens := ACE_FGENS_ARG_CHK(fgens);
rels := ACE_WORDS_ARG_CHK(fgens, rels, "relators");
sgens := ACE_WORDS_ARG_CHK(fgens, sgens, "subgp gen'rs");
infile := VALUE_ACE_OPTION(optnames, fail, "aceinfile");
if ACEfname = "ACECosetTableFromGensAndRels" and infile <> fail then
datarec := rec(
infile := infile,
outfile := VALUE_ACE_OPTION(optnames, ACEData.outfile, "aceoutfile"),
stream := OutputTextFile(infile, false) );
ToACE := function(list) WRITE_LIST_TO_ACE_STREAM(datarec.stream, list); end;
else
datarec := rec(
stream := InputOutputLocalProcess(ACEData.tmpdir, ACEData.binary, []) );
if datarec.stream = fail then
Error("sorry! Run out of pseudo-ttys. Can't initiate stream\n");
fi;
if ACEfname <> ACEStart then
datarec.procId := 0;
ACEData.ni := datarec;
fi;
FLUSH_ACE_STREAM_UNTIL(datarec.stream, 3, 3, ACE_READ_NEXT_LINE,
line -> IsMatchingSublist(line, "name", 3));
ToACE := function(list)
INTERACT_TO_ACE_WITH_ERRCHK(datarec, list);
end;
fi;
datarec.args := rec(fgens := fgens, rels := rels, sgens := sgens);
datarec.options := ACE_OPTIONS();
standard := ACE_COSET_TABLE_STANDARD( datarec.options );
# Define the group generators ACE will use
gens := TO_ACE_GENS(fgens);
ToACE([ "Group Generators: ", gens.toace, ";"]);
datarec.acegens := gens.acegens;
# Define the group relators ACE will use
datarec.enforceAsis :=
(ACEfname <> "ACEStats") and (standard = "lenlex") and
not IsACEGeneratorsInPreferredOrder(fgens, rels, "noargchk");
ToACE([ "Group Relators: ",
ACE_RELS(rels, fgens, datarec.acegens, datarec.enforceAsis), ";" ]);
# Define the subgroup generators ACE will use
ToACE([ "Subgroup Generators: ",
ACE_WORDS(sgens, fgens, datarec.acegens), ";" ]);
if ACEfname = "ACECosetTableFromGensAndRels" then
ignored := [ ];
else
ignored := [ "aceinfile" ];
fi;
if ACEfname = "ACEStart" then
Add(ignored, "aceoutfile");
fi;
if datarec.enforceAsis then
Add(ignored, "asis");
ToACE([ "Asis: 1;" ]);
fi;
PROCESS_ACE_OPTIONS(
ACEfname, optnames, optnames, echo, datarec,
rec(group := ACE_ERRORS.argnotopt, # disallowed options
generators := ACE_ERRORS.argnotopt,
relators := ACE_ERRORS.argnotopt),
ignored
);
if not IsInputOutputStream(datarec.stream) then
if VALUE_ACE_OPTION(optnames, fail, ["start", "aep", "rep"]) = fail then
# if the user hasn't issued there own enumeration initiation directive
# ... initiate the enumeration
ToACE([ "Start;" ]);
fi;
if ACEfname = "ACECosetTableFromGensAndRels" then
if standard = "lenlex" then
ToACE([ "Standard;" ]);
fi;
ToACE([ "Print Table;" ]);
fi;
CloseStream(datarec.stream);
elif ACEfname <> "ACEStart" then
if VALUE_ACE_OPTION(optnames, fail, ["start", "aep", "rep"]) = fail then
ACE_MODE( "Start", datarec );
fi;
if ACEfname = "ACECosetTableFromGensAndRels" and standard = "lenlex" then
ToACE([ "Standard;" ]);
fi;
fi;
if ACEfname = "ACEStart" then
datarec.procId := Length(ACEData.io) + 1;
Add(ACEData.io, datarec);
return Length(ACEData.io);
elif ACEfname = "ACECosetTableFromGensAndRels" then
datarec.silent := VALUE_ACE_OPTION(optnames, false, "silent");
fi;
return datarec;
end);
#############################################################################
####
##
#F ACECosetTableFromGensAndRels . . . . . . . Non-interactive ACECosetTable
##
##
InstallGlobalFunction(ACECosetTableFromGensAndRels, function(fgens, rels, sgens)
# Use ACECosetTable non-interactively
return ACECosetTable(fgens, rels, sgens);
end);
#############################################################################
####
##
#F IsACEStandardCosetTable . . . . . . Returns true if table is standardised
## . . . . . . . . . . . . . . . . . . according to GAP's default scheme or
## . . . . . . . . . . . . . . . . . . with the lenlex option, according to
## . . . . . . . . . . . . . . . . . . . . the lenlex standardisation scheme
##
InstallGlobalFunction(IsACEStandardCosetTable, function(table)
local standard, geninvIndices, index, next, j, i;
standard := ACE_COSET_TABLE_STANDARD( ACE_OPTIONS() );
if standard in ["lenlex", "GAPlenlex"] then
geninvIndices := [1 .. Length(table)];
elif standard in ["semilenlex", "GAPsemilenlex"] then
geninvIndices := [1, 3 .. Length(table) - 1];
else
return IsStandardized(table); # Should only get here with GAP 4.3+
# ... by which time `IsStandardized'
# will hopefully have been generalised
# to cope with any other standardisation
# schemes
fi;
index := Length( table[1] );
next := 2;
for j in [1 .. index - 1] do
for i in geninvIndices do
if table[i][j] >= next then
if table[i][j] > next then
return false;
fi;
next := next + 1;
fi;
od;
od;
return true;
end);
#############################################################################
####
##
#F IsACEGeneratorsInPreferredOrder . . . . . Returns true if the generators
## . . . . . . . . . . . . . . . . . . . . . gens are already in the order
## . . . . . . . . . . . . . . . . . . . . . . . . . . . . preferred by ACE
##
## For a presentation with more than one generator, the first generator of
## which is an involution and the second is not, ACE prefers to switch the
## first two generators. IsACEGeneratorsInPreferredOrder returns true if the
## order of the generators gens would not be changed by ACE and false,
## otherwise. When necessary, the argument rels (the relators) is scanned
## for relators that determine whether or not gens[1] and gens[2] are
## involutions.
##
## If IsACEGeneratorsInPreferredOrder would return false, it is possible to
## enforce a user's order of the generators within ACE, by enforcing ACE's
## `asis' option and passing the relator, that determines gens[1] is an
## involution, explicitly to ACE as: gens[1]*gens[1] (rather than
## gens[1]^2).
##
InstallGlobalFunction(IsACEGeneratorsInPreferredOrder, function(arg)
local ioIndex, gens, rels;
if Length(arg) < 2 then
ioIndex := CallFuncList(ACEProcessIndex, arg);
gens := ACEGroupGenerators(ioIndex);
rels := ACERelators(ioIndex);
elif Length(arg) = 2 then
gens := ACE_FGENS_ARG_CHK(arg[1]);
rels := ACE_WORDS_ARG_CHK(gens, arg[2], "relators");
elif Length(arg) = 3 and arg[3] = "noargchk" then
# This scenario only intended for use internally
gens := arg[1];
rels := arg[2];
else
Error("expected at most 2 arguments, not ", Length(arg), " arguments.\n");
fi;
if Length(gens) = 1 or not ForAny(rels, rel -> rel = gens[1]^2) then
return true;
else
return ForAny(rels, rel -> rel = gens[2]^2);
fi;
end);
#############################################################################
####
##
#F ACE_READ_AS_FUNC . . . . . . . . . . . . . . . Variant of ReadAsFunction
## . . . . . . . . . . . . that allows the passing of the function argument
## . . . . . . . . . . . . . . . . . . . . ACEfunc to ReadAsFunction(file)()
##
##
InstallGlobalFunction(ACE_READ_AS_FUNC, function(file, ACEfunc)
local line, instream, rest;
instream := InputTextFile(file);
# We don't want the user to see this ... so we flush at InfoACE level 10.
line := FLUSH_ACE_STREAM_UNTIL( instream, 10, 10, ReadLine,
line -> IsMatchingSublist(line, "local") );
rest := ReadAll(instream);
CloseStream(instream);
return ReadAsFunction(
InputTextString(
Concatenation([ ReplacedString(line, ";", ", ACEfunc;"),
"ACEfunc := ", NameFunction(ACEfunc), ";",
rest ]) ) )();
end);
#############################################################################
####
##
#F ACEExample( )
#F ACEExample( <file>[, <ACEfunc>] )
##
## With no arguments, or with single argument "index", or a string that is
## not a filename in the `examples' directory, an index of available
## examples is displayed.
##
## With argument <file> that is a filename in the `examples' directory other
## than "index" the example is displayed as it would be when called with
## <ACEfunc> (or `ACEStats', if the 2nd argument is omitted) and then
## executed via a call to `ReadAsFunction' and a little internal ``magic''.
## <ACEfunc> must be one of `ACEStats' (the default),
## `ACECosetTableFromGensAndRels' (or equivalently `ACECosetTable', or
## `ACEStart'.
##
InstallGlobalFunction(ACEExample, function(arg)
local name, file, instream, line, ACEfunc,
EnquoteIfString, optnames, lastoptname, optname;
if IsEmpty(arg) then
name := "index";
else
name := arg[1];
if Length(arg) > 1 then
ACEfunc := arg[2];
else
ACEfunc := ACEStats;
fi;
if not IsEmpty(OptionsStack) then
ACEData.aceexampleoptions := OptionsStack[ Length(OptionsStack) ];
PopOptions();
PushOptions( rec(aceexampleoptions := true) );
fi;
fi;
file := Filename( DirectoriesPackageLibrary( "ace", "examples"), name );
if file = fail then
Info(InfoACE + InfoWarning, 1,
"Sorry! There is no ACE example file with name `", name, "'");
name := "index";
file := Filename( DirectoriesPackageLibrary("ace", "examples"), name );
fi;
# Display file ... after a few minor modifications
instream := InputTextFile(file);
if name <> "index" then
line := FLUSH_ACE_STREAM_UNTIL( instream, 1, 10, ReadLine,
line -> IsMatchingSublist(line, "local") );
Info(InfoACE, 1,
"#", line{[Position(line, ' ')..Position(line, ';') - 1]},
" are local to ACEExample");
line := FLUSH_ACE_STREAM_UNTIL( instream, 1, 10, ReadLine,
line -> IsMatchingSublist(line, "return") );
Info(InfoACE, 1,
Chomp(ReplacedString(line, "return ACEfunc", NameFunction(ACEfunc)))
);
if IsBound(ACEData.aceexampleoptions) then
line := FLUSH_ACE_STREAM_UNTIL(
instream, 1, 10, ReadLine,
line -> PositionSublist(line, ");") <> fail );
Info(InfoACE, 1, Chomp(ReplacedString(line, ");", ", ")));
Info(InfoACE, 1, " # User Options");
optnames := ShallowCopy( RecNames(ACEData.aceexampleoptions) );
lastoptname := optnames[ Length(optnames) ];
Unbind(optnames[ Length(optnames) ]);
EnquoteIfString := function(optval)
# Puts quotes around optval if it's a string
if IsString(optval) then
return Concatenation(["\"", optval, "\""]);
else
return optval;
fi;
end;
for optname in optnames do
Info(InfoACE, 1, " ", optname, " := ",
EnquoteIfString(
ACEData.aceexampleoptions.(optname) ), ",");
od;
Info(InfoACE, 1, " ", lastoptname, " := ",
EnquoteIfString(
ACEData.aceexampleoptions.(lastoptname) ), ");");
fi;
fi;
FLUSH_ACE_STREAM_UNTIL( instream, 1, 10, ReadLine, line -> line = fail );
CloseStream(instream);
if name <> "index" then
return ACE_READ_AS_FUNC(file, ACEfunc);
fi;
end);
#############################################################################
####
##
#F ACEReadResearchExample . . . . . . . . Read an ACE research example
## . . . . . . . . . . . . . . . . . . . . from the res-examples directory.
##
## Currently, all examples in the res-examples directory depend on
## pgrelfind.g, which with Info text doubles as an index. This function is
## essentially equivalent to doing a Read of its argument or "pgrelfind.g",
## if there is no argument.
##
InstallGlobalFunction(ACEReadResearchExample, function(arg)
local name, file;
if IsEmpty(arg) then
name := "pgrelfind.g"; # If there is ever more than one key research
# example, we should replace this with an index
else
name := arg[1];
fi;
file := Filename( DirectoriesPackageLibrary( "ace", "res-examples"), name );
if file = fail then
Error("ACEReadResearchExample: Sorry! There is no ACE research example\n",
"file with name \"", name, "\"\n");
else
Read(file);
fi;
end);
#############################################################################
####
##
#F ACEPrintResearchExample . . . . . . . . Print an ACE research example
## . . . . . . . . . . . . . . . . . . . . from the res-examples directory
## . . . . . . . . . . . . . . . . . . . . to the terminal or to a file,
## . . . . . . . . . . . . . . . . . . . . . . . minus header and Info text.
##
## ACEPrintResearchExample(examplefile)
## prints examplefile in res-examples directory to the terminal
##
## ACEPrintResearchExample(examplefile, outfile)
## prints examplefile in res-examples directory to outfile
##
InstallGlobalFunction(ACEPrintResearchExample, function(arg)
local outstream, print, file, instream, line;
if IsEmpty(arg) then
Error("expected 1 or 2 arguments\n");
fi;
file := Filename( DirectoriesPackageLibrary( "ace", "res-examples"), arg[1] );
if file = fail then
Error("ACEPrintResearchExample: Sorry! There is no ACE research example ",
"file with name `", arg[1], "'\n");
fi;
if Length(arg) > 1 then
outstream := OutputTextFile(arg[2], false);
print := function(line) WriteAll(outstream, line); end;
else
print := Print;
fi;
instream := InputTextFile(file);
repeat
line := ReadLine(instream);
until IsMatchingSublist(line, "## Begin");
line := ReadLine(instream);
while not IsMatchingSublist(line, "## End") do
print(line);
line := ReadLine(instream);
od;
CloseStream(instream);
if print <> Print then
CloseStream(outstream);
fi;
end);
#############################################################################
####
##
#F ACEDirectoryTemporary( <dir> )
##
## calls the UNIX command `mkdir' to create <dir>, which must be a string,
## and if successful a directory object for <dir> is both assigned to
## `ACEData.tmpdir' and returned. The fields `ACEData.infile' and
## `ACEData.outfile' are also set to be files in `ACEData.tmpdir', and on
## exit from {\GAP} <dir> is removed.
##
InstallGlobalFunction(ACEDirectoryTemporary, function(dir)
local created;
# check arguments
if not IsString(dir) then
Error("usage: ACEDirectoryTemporary( <dir> ) : <dir> must be a string.\n");
fi;
# create temporary directory
created := Process(DirectoryCurrent(),
Filename( DirectoriesSystemPrograms(), "sh" ),
InputTextUser(),
OutputTextUser(),
[ "-c", Concatenation("mkdir ", dir) ]);
if created = fail then
return fail;
fi;
Add( GAPInfo.DirectoriesTemporary, dir );
ACEData.tmpdir := Directory(dir);
ACEData.infile := Filename(ACEData.tmpdir, "in");
ACEData.outfile := Filename(ACEData.tmpdir, "out");
return ACEData.tmpdir;
end);
#############################################################################
####
##
#F ACE_ERROR(<errmsg>, <onbreakmsg>)
##
## sets `OnBreakMessage' to print <onbreakmsg> in order to generate a
## one-off user-friendly message of how the user may recover from the error,
## and returns an error message formed from <errmsg> to be used by Error.
##
## <errmsg> and <onbreakmsg> should be lists of strings; <onbreakmsg> must
## be non-empty and its first member must not be a null string.
##
InstallGlobalFunction(ACE_ERROR, function(errmsg, onbreakmsg)
local NormalOnBreak, NormalOnBreakMessage;
errmsg := JoinStringsWithSeparator(errmsg, "\n ");
NormalOnBreakMessage := OnBreakMessage;
onbreakmsg[1]{[1]} := LowercaseString( onbreakmsg[1]{[1]} );
OnBreakMessage := function()
local s;
for s in onbreakmsg do
Print(" ", s, "\n");
od;
OnBreakMessage := NormalOnBreakMessage;
end;
return errmsg;
end);
#############################################################################
####
##
#F CallACE . . . . . . . . . . . . . . . . . . . . . . . . . . . deprecated
##
InstallGlobalFunction(CallACE, function(arg)
Error("CallACE is deprecated: Use `ACECosetTableFromGensAndRels' or\n",
"`ACECosetTable'.\n");
end);
#E general.gi . . . . . . . . . . . . . . . . . . . . . . . . . . ends here