GAP 4.8.9 installation with standard packages -- copy to your CoCalc project to get it
#############################################################################
##
#W interact.gi ACE Package Greg Gamble
##
## This file installs commands for using ACE interactively via IO Streams.
##
#Y Copyright (C) 2000 Centre for Discrete Mathematics and Computing
#Y Department of Information Technology & Electrical Eng.
#Y University of Queensland, Australia.
##
#############################################################################
####
##
#F ACE_IOINDEX . . . . . . . . . . . . Get the index of the ACEData.io list
## . . . . . . . . . . . . . . . . . . . . . for an interactive ACE session.
##
InstallGlobalFunction(ACE_IOINDEX, function(arglist)
local ioIndex;
if IsEmpty(arglist) then
# Find the first bound ioIndex
ioIndex := 1;
while not(IsBound(ACEData.io[ioIndex])) and ioIndex < Length(ACEData.io) do
ioIndex := ioIndex + 1;
od;
if IsBound(ACEData.io[ioIndex]) then
return ioIndex;
else
Info(InfoACE + InfoWarning, 1,
"No interactive ACE sessions are currently active");
return fail;
fi;
elif IsBound(ACEData.io[ arglist[1] ]) then
return arglist[1];
else
Error("no such interactive ACE session\n");
fi;
end);
#############################################################################
####
##
#F ACE_IOINDEX_ARG_CHK . . . . . . . . Checks for the right no. of arguments
## . . . . . . . . . . . . . . . . . . warns user of any ignored arguments
##
InstallGlobalFunction(ACE_IOINDEX_ARG_CHK, function(arglist)
if Length(arglist) > 1 then
Info(InfoACE + InfoWarning, 1,
"Expected 0 or 1 arguments, all but first argument ignored");
fi;
end);
#############################################################################
##
#F ACEDataRecord([<i>]) . . . . . . . . returns the data record of a process
##
InstallGlobalFunction(ACEDataRecord, function( arg )
if not IsEmpty(arg) and arg[1] = 0 and IsBound( ACEData.ni ) then
return ACEData.ni;
else
return ACEData.io[ CallFuncList(ACEProcessIndex, arg) ];
fi;
end);
#############################################################################
####
##
#F ACEProcessIndex . . . . . . . . . . . . . . . User version of ACE_IOINDEX
##
## If given (at least) one integer argument returns the first argument if it
## corresponds to an active interactive process or raises an error,
## otherwise it returns the default active interactive process. If the user
## provides more than one argument then all arguments other than the first
## argument are ignored (and a warning is issued).
##
InstallGlobalFunction(ACEProcessIndex, function(arg)
local ioIndex;
ACE_IOINDEX_ARG_CHK(arg);
ioIndex := ACE_IOINDEX(arg);
if ioIndex = fail then
Error( "no currently active interactive ACE sessions" );
fi;
return ioIndex;
end);
#############################################################################
####
##
#F ACEProcessIndices . . . . . . . . . . Returns the list of indices of all
## . . . . . . . . . . . . . . . . . . . active interactive ACE processes
##
##
InstallGlobalFunction(ACEProcessIndices, function()
return Filtered( [1..Length(ACEData.io)], i -> IsBound( ACEData.io[i] ) );
end);
#############################################################################
####
##
#F IsACEProcessAlive . . . . . . . . . . Returns true if the stream of the
## . . . . . . . . . . . . . . . . . . . interactive ACE process determined
## . . . . . . . . . . . . . . . . . . . by arg can be written to (i.e. is
## . . . . . . . . . . . . . . . . . . . . still alive) and false otherwise
##
InstallGlobalFunction(IsACEProcessAlive, function(arg)
return not IsEndOfStream( CallFuncList(ACEDataRecord, arg).stream );
end);
#############################################################################
####
##
#F ACEResurrectProcess . . . . . . . . . Re-generates the stream of the i-th
## . . . . . . . . . . . . . . . . . . . interactive ACE process, where i is
## . . . . . . . . . . . . . . . . . . . determined by arg, and tries to
## . . . . . . . . . . . . . . . . . . . recover as much as possible of the
## . . . . . . . . . . . . . . . . . . . previous state from saved values of
## . . . . . . . . . . . . . . . . . . . . . the args and parameter options
##
## The args of the i-th interactive ACE process are stored in
## ACEData.io[i].args (a record with fields fgens, rels and sgens, which are
## the GAP group generators, relators and subgroup generators,
## respectively). Option information is saved in ACEData.io[i].options when
## a user uses an interactive ACE interface function with options or uses
## SetACEOptions. Option information is saved in ACEData.io[i].parameters if
## ACEParameters is used to extract from ACE the current values of the ACE
## parameter options (this is generally less reliable unless one of the ACE
## modes has been run previously).
##
## By default, ACEResurrectProcess recovers parameter option information
## from ACEData.io[i].options if it is bound, or from
## ACEData.io[i].parameters if is bound, otherwise. To alter this behaviour,
## the user is provided two options:
##
## use := list . list may contain one or both of "options" and
## "parameters". By default: use = ["options", "parameters"]
##
## useboth . . . (boolean) By default: useboth = false
##
## If useboth is true, ACEResurrectProcess applies SetACEOptions with each
## ACEData.io[i].(field) for each field ("options" or "parameters") that is
## bound and in use's list, in the order implied by list. If useboth is
## false, ACEResurrectProcess applies SetACEOptions with
## ACEData.io[i].(field) for only the first field that is bound in use's
## list.
##
InstallGlobalFunction(ACEResurrectProcess, function(arg)
local ioIndex, datarec, gens, ToACE, uselist, useone, saved, optname, field;
ioIndex := CallFuncList(ACEProcessIndex, arg);
datarec := ACEData.io[ ioIndex ];
if not IsEndOfStream( datarec.stream ) then
Info(InfoACE + InfoWarning, 1,
"Huh? Stream of interactive ACE process ", ioIndex, " not dead?");
return fail;
fi;
# Restart the stream
datarec.stream := InputOutputLocalProcess(ACEData.tmpdir, ACEData.binary, []);
if IsBound(datarec.args) and IsBound(datarec.args.fgens) then
gens := TO_ACE_GENS(datarec.args.fgens);
ToACE := function(list) WRITE_LIST_TO_ACE_STREAM(datarec.stream, list); end;
ToACE([ "Group Generators: ", gens.toace, ";" ]);
Info(InfoACE, 1, "Group generators:", datarec.args.fgens);
if IsBound(datarec.args.rels) then
ToACE([ "Group Relators: ",
ACE_WORDS(datarec.args.rels, datarec.args.fgens, gens.acegens),
";" ]);
Info(InfoACE, 1, "Relators:", datarec.args.rels);
else
Info(InfoACE + InfoWarning, 1, "No relators.");
fi;
if IsBound(datarec.args.sgens) then
ToACE([ "Subgroup Generators: ",
ACE_WORDS(datarec.args.sgens, datarec.args.fgens, gens.acegens),
";" ]);
Info(InfoACE, 1, "Subgroup generators:", datarec.args.sgens);
else
Info(InfoACE + InfoWarning, 1, "No subgroup generators.");
fi;
else
Info(InfoACE + InfoWarning, 1, "No group generators.");
fi;
uselist := Filtered(ACE_VALUE_OPTION("use", ["options", "parameters"]),
field -> IsBound(datarec.(field)) );
useone := not ACE_VALUE_OPTION("useboth", false);
if IsEmpty(uselist) then
Info(InfoACE + InfoWarning, 1, "Sorry. No parameter options recovered.");
else
if useone then
uselist := uselist{[1]};
fi;
if "options" in uselist then
# Scrub any non{-parameter,-strategy,-echo} options
for optname in Filtered(
RecNames(datarec.options),
function(optname)
local prefname;
prefname := ACEPreferredOptionName(optname);
return prefname <> "echo" and
not (prefname in ACEStrategyOptions) and
not (prefname in RecNames(
ACEParameterOptions
));
end
)
do
Unbind( datarec.options.(optname) );
od;
saved := rec(options := datarec.options);
if IsBound(datarec.parameters) then
saved.parameters := datarec.parameters;
fi;
else
saved := rec( parameters := ShallowCopy(datarec.parameters) );
if IsBound(datarec.options) then
for optname in Filtered(
RecNames(datarec.options),
optname -> ACEPreferredOptionName(optname) = "echo"
)
do
saved.parameters.(optname) := datarec.options.(optname);
od;
fi;
fi;
Unbind( datarec.options );
for field in uselist do
PushOptions( saved.(field) );
INTERACT_SET_ACE_OPTIONS("ACEResurrectProcess", datarec);
PopOptions();
od;
Info(InfoACE, 1, "Options set to: ", GetACEOptions(ioIndex));
fi;
if not IsBound(datarec.options) then
datarec.options := rec();
fi;
end);
#############################################################################
####
##
#F READ_ACE_ERRORS . . . . . . . . . . . . . . . . . . . . Internal function
## . . . . . . . . . . . . . . . . . . . . reads interactive ACE output from
## . . . . . . . . . . . . . . . . . . . . stream when none is expected.
##
## Writes any output read to Info at InfoACE + InfoWarning level 1.
##
## This function may miss data output by ACE purely because it wasn't ready
## at the time of the call. If it turns out that READ_ACE_ERRORS is used in
## a place where it's important that all data be collected from ACE, then
## the call to READ_ACE_ERRORS should be replaced by a call to
## ENSURE_NO_ACE_ERRORS.
##
InstallGlobalFunction(READ_ACE_ERRORS, function(datarec)
local line;
line := ReadAllLine(datarec.stream);
while line <> fail do
if not IsMatchingSublist(line, "** ERROR") and
Length(line) > 1 and line[ Length(line) - 1 ] = ')' then
#a `start', `aep' or `rep' option was slipped in
datarec.enumResult := Chomp(line);
datarec.stats := ACE_STATS(datarec.enumResult);
fi;
Info(InfoACE + InfoWarning, 1, Chomp(line));
line := ReadAllLine(datarec.stream);
od;
end);
#############################################################################
####
##
#F ENSURE_NO_ACE_ERRORS . . . . . . . . . . . . . . . . . Internal function
## . . . . . . . . . . . . . . . . . . . . . . . purges all interactive ACE
## . . . . . . . . . . . . . . . . . . . . . . . . . . . output from stream
##
## Writes any output read to Info at InfoACE + InfoWarning level 1.
##
## This function is like READ_ACE_ERRORS but makes ACE write "***" which we
## use as a sentinel to ensure we get all output due to be collected from
## ACE.
##
InstallGlobalFunction(ENSURE_NO_ACE_ERRORS, function(datarec)
PROCESS_ACE_OPTION(datarec.stream, "text", "***"); # Causes ACE to print "***"
FLUSH_ACE_STREAM_UNTIL(datarec.stream, 3, 3, ACE_READ_NEXT_LINE,
line -> IsMatchingSublist(line, "***"));
end);
#############################################################################
####
##
#F INTERACT_TO_ACE_WITH_ERRCHK . . . . . . . . . . . . . Internal procedure
## . . . . . . . . . . . . . . interactive ToACE procedure with error check
##
## Writes list to the interactive ACE iostream stream and reads from stream
## to check for errors. Any output read is written to Info at InfoACE +
## InfoWarning level 1. Used where no output is expected.
##
InstallGlobalFunction(INTERACT_TO_ACE_WITH_ERRCHK, function(datarec, list)
WRITE_LIST_TO_ACE_STREAM(datarec.stream, list);
READ_ACE_ERRORS(datarec);
end);
#############################################################################
####
##
#F ACE_ENUMERATION_RESULT . . . . Get and return an ACE enumeration result
##
##
InstallGlobalFunction(ACE_ENUMERATION_RESULT, function(stream, readline)
# Call LAST_ACE_ENUM_RESULT with first (3rd argument) set to true,
# so that it returns on the first enumeration result (or error) found
return LAST_ACE_ENUM_RESULT(stream, readline, true);
end);
#############################################################################
####
##
#F LAST_ACE_ENUM_RESULT . . . . Get and return the last enumeration result
##
## Enumeration result lines are recognised by being ones that end in ")\n"
## but not starting with "** " or " " (as ACE error diagnostics do) ... this
## is potentially flaky.
##
## Reads and Infos lines from stream via function readline until a sentinel
## "***" and returns the last enumeration result (or error) found, unless
## first = true, in which case, it simply returns on the first enumeration
## result (or error) found (without looking for a sentinel "***").
##
InstallGlobalFunction(LAST_ACE_ENUM_RESULT, function(stream, readline, first)
local errmsg, onbreakmsg, IsLastLine, IsEnumLine, line, enumResult;
if first = true then
IsLastLine := line -> true;
IsEnumLine := line -> Length(line) > 1 and line[ Length(line) - 1 ] = ')';
else
IsLastLine := line -> IsMatchingSublist(line, "***");
IsEnumLine := line -> line = fail or IsMatchingSublist(line, "***") or
Length(line) > 1 and line[ Length(line) - 1 ] = ')';
fi;
repeat
line := Chomp(FLUSH_ACE_STREAM_UNTIL(stream, 3, 10, readline, IsEnumLine));
if line = fail then
errmsg := ["expected to find output ...",
"possibly, you have reached the limit of what can be",
"written to ACEData.tmpdir (temporary directory)."];
onbreakmsg :=
["You can only 'quit;' from here.",
"You will have to redo the calculation, but before that",
"try running 'ACEDirectoryTemporary(<dir>);' for some",
"directory <dir> where you know you will not be so limited."];
Error(ACE_ERROR(errmsg, onbreakmsg), "\n");
elif IsMatchingSublist(line, "** ERROR") then
Info(InfoACE + InfoWarning, 1, line);
line := Chomp( readline(stream) );
Info(InfoACE + InfoWarning, 1, line);
enumResult := Concatenation("ACE Enumeration failed: ", line);
elif (first = true) or not IsLastLine(line) then
Info(InfoACE, 2, line);
enumResult := line;
else
Info(InfoACE, 3, line);
fi;
until IsLastLine(line);
if IsMatchingSublist(enumResult, "ACE Enum") and first <> fail then
Error(enumResult, "\n");
fi;
return enumResult;
end);
#############################################################################
####
##
#F ACEWrite . . . . . . . . . . . . . . . . . . . . Primitive write to ACE
##
## Writes the last argument to the i-th interactive ACE process, where i is
## the first argument if there are 2 arguments or the default process if
## there is only 1 argument. The action is echoed via Info at InfoACE level
## 4 (with a `ToACE> ' prompt). Returns true if successful in writing to the
## stream and fail otherwise.
##
InstallGlobalFunction(ACEWrite, function(arg)
if Length(arg) in [1, 2] then
return WRITE_LIST_TO_ACE_STREAM(
CallFuncList(ACEDataRecord, arg{[1..Length(arg) - 1]}).stream,
arg{[Length(arg)..Length(arg)]} );
else
Error("expected 1 or 2 arguments ... not ", Length(arg), " arguments\n");
fi;
end);
#############################################################################
####
##
#F ACERead . . . . . . . . . . . . . . . . . . . . . Primitive read from ACE
##
## Reads a complete line of ACE output, from the i-th interactive ACE
## process, if there is output to be read and returns fail otherwise, where
## i is the first argument if there is 1 argument or the default process if
## there are no arguments.
##
InstallGlobalFunction(ACERead, function(arg)
return ReadAllLine( CallFuncList(ACEDataRecord, arg).stream );
end);
#############################################################################
####
##
#F ACEReadAll . . . . . . . . . . . . . . . . . . . Primitive read from ACE
##
## Reads and returns as many complete lines of ACE output, from the i-th
## interactive ACE process, as there are to be read, as a list of strings
## with the trailing newlines removed and returns the empty list otherwise,
## where i is the first argument if there is 1 argument or the default
## process if there are no arguments. Also writes via Info at InfoACE level
## 3 each line read.
##
InstallGlobalFunction(ACEReadAll, function(arg)
local stream, lines, line;
stream := CallFuncList(ACEDataRecord, arg).stream;
lines := [];
line := ReadAllLine(stream);
while line <> fail do
line := Chomp(line);
Info(InfoACE, 3, line);
Add(lines, line);
line := ReadAllLine(stream);
od;
return lines;
end);
#############################################################################
####
##
#F ACEReadUntil . . . . . . . . . . . . . . . . . . Primitive read from ACE
##
## Reads complete lines of ACE output, from the i-th interactive ACE
## process, until a line for which IsMyLine(line) is true, where i is the
## first argument if the first argument is an integer or the default process
## otherwise, and IsMyLine is the first function argument. The lines read
## are returned as a list of strings with the trailing newlines removed. If
## IsMyLine(line) is never true ACEReadUntil will wait indefinitely. Also
## writes via Info at InfoACE level 3 each line read. If there is a second
## function argument it is used to modify each returned line; in this case,
## each line is emitted to Info before modification, but each line is
## modified before the IsMyLine test.
##
InstallGlobalFunction(ACEReadUntil, function(arg)
local idx1stfn, stream, IsMyLine, Modify, lines, line;
idx1stfn := First([1..Length(arg)], i -> IsFunction(arg[i]));
if idx1stfn = fail then
Error("expected at least one function argument\n");
elif Length(arg) > idx1stfn + 1 then
Error("expected 1 or 2 function arguments, not ",
Length(arg) - idx1stfn + 1, "\n");
elif idx1stfn > 2 then
Error("expected 0 or 1 integer arguments, not ", idx1stfn - 1, "\n");
else
stream := CallFuncList(ACEDataRecord, arg{[1..idx1stfn - 1]}).stream;
IsMyLine := arg[idx1stfn];
if idx1stfn = Length(arg) then
Modify := line -> line; # The identity function
else
Modify := arg[Length(arg)];
fi;
lines := [];
repeat
line := Chomp( ACE_READ_NEXT_LINE(stream) );
Info(InfoACE, 3, line);
line := Modify(line);
Add(lines, line);
until IsMyLine(line);
return lines;
fi;
end);
#############################################################################
####
##
#F ACE_STATS . . . . . . . . . . . . . . . . Called by ACEStart and ACEStats
##
##
InstallGlobalFunction(ACE_STATS, function(line)
local stats;
# Parse line for statistics and return
stats := Filtered(line, char -> char in ". " or char in CHARS_DIGITS);
if not IsMatchingSublist(line, "INDEX") then
# Enumeration failed so the index is missing
# ... shove a 0 index on the front of stats
stats := Concatenation("0 ", stats);
fi;
stats := SplitString(stats, "", " .");
return rec(index := Int(stats[1]),
cputime := Int(stats[7])*10^Length(stats[8])+Int(stats[8]),
cputimeUnits := Concatenation("10^-", String(Length(stats[8])),
" seconds"),
activecosets := Int(stats[2]),
maxcosets := Int(stats[9]),
totcosets := Int(stats[10]));
end);
#############################################################################
####
##
#F ACE_COSET_TABLE
##
##
InstallGlobalFunction(ACE_COSET_TABLE,
function(activecosets, acegens, iostream, readline)
local n, line, genColIndex, invColIndex, table, i, rowi, j, colj, invcolj;
n := Length(acegens);
# Skip some header until the ` coset ' line
line := FLUSH_ACE_STREAM_UNTIL(iostream, 3, 3, readline,
line -> Length(line)>5 and
line{[1..6]} in [" coset", "** ERR"]);
if IsMatchingSublist(line, "** ERROR") then
line := Chomp(readline(iostream));
Info(InfoACE, 1, line);
Error(line{[3..Length(line)]}, ". Try running ACEStart first.\n");
fi;
# Extract the coset table column headers
rowi := SplitString(line, "", " |\n");
# Look at the coset table column headers and determine the column
# corresponding to each generator:
# colIndex[j] = Index of column(acegens[j])
genColIndex := List(acegens, gen -> Position(rowi, gen));
invColIndex := List(genColIndex,
i -> ACE_IF_EXPR(
i + 1 in genColIndex or i + 1 > Length(rowi),
i,
i + 1,
0 # doesn't occur
));
# Discard the `---' line
line := Chomp( readline(iostream) );
Info(InfoACE, 3, line);
# Now read the body of the coset table into table as a GAP List
table := List([1 .. 2*n], j -> []);
i := 0;
repeat
line := Chomp( readline(iostream) );
Info(InfoACE, 3, line);
i := i + 1;
rowi := SplitString(line, "", " :|");
for j in [1..n] do
Add(table[2*j - 1], Int(rowi[ genColIndex[j] ]));
Add(table[2*j], Int(rowi[ invColIndex[j] ]));
od;
until i = activecosets;
return table;
end);
#############################################################################
####
##
#F ACE_MODE . . . . . . . . . . . . Start, continue or redo an enumeration
## . . . . . . . . . . . . also sets enumResult and stats fields of datarec
##
InstallGlobalFunction(ACE_MODE, function(mode, datarec)
ENSURE_NO_ACE_ERRORS(datarec); # purge any output not yet collected
# e.g. error messages due to unknown
# or inappropriate options
WRITE_LIST_TO_ACE_STREAM(datarec.stream, [ mode, ";" ]);
datarec.enumResult := ACE_ENUMERATION_RESULT(datarec.stream,
ACE_READ_NEXT_LINE);
datarec.stats := ACE_STATS(datarec.enumResult);
end);
#############################################################################
####
##
#F ACE_MODE_AFTER_SET_OPTS . . . . . . . Gets ACE stream index, sets options
## . . . . . . . . . . . . . . . . . . . and then calls ACE_MODE to start,
## . . . . . . . . . . . . . . . . . . . . . continue or redo an enumeration
##
InstallGlobalFunction(ACE_MODE_AFTER_SET_OPTS, function(mode, arglist)
local ioIndex;
ioIndex := CallFuncList(ACEProcessIndex, arglist);
INTERACT_SET_ACE_OPTIONS(Flat( ["ACE", mode] ), ACEData.io[ioIndex]);
if IsEmpty( ACEGroupGenerators(ioIndex) ) then
Info(InfoACE + InfoWarning, 1, "ACE", mode, " : No group generators?!");
else
ACE_MODE(mode, ACEData.io[ioIndex]);
fi;
return ioIndex;
end);
#############################################################################
####
##
#F CHEAPEST_ACE_MODE . . . . . . . . . . . . . Does ACE_MODE(mode, datarec)
## . . . . . . . . . . . . . . . . . . . . . for the cheapest mode available
##
InstallGlobalFunction(CHEAPEST_ACE_MODE, function(datarec)
local modes, mode;
modes := ACE_MODES( datarec );
mode := First( RecNames(modes), ACEmode -> modes.(ACEmode) );
if mode = fail then
Error("none of ACEContinue, ACERedo or ACEStart is possible. Huh???\n");
else
ACE_MODE(mode{[4..Length(mode)]}, datarec);
fi;
end);
#############################################################################
####
##
#F ACE_LENLEX_CHK . . . . . . . . . . . . . . . . . . . . Internal function
## . . . . . . . . . . . for the interactive ACE process indexed by ioIndex,
## . . . . . . . . . . . determine the coset table standardisation scheme
## . . . . . . . . . . . desired by the user: if "lenlex" ensure `asis' is
## . . . . . . . . . . . enforced and re-emit the relators using ACE_RELS
## . . . . . . . . . . . with 4th arg `true' to avoid ACE swapping the first
## . . . . . . . . . . . two generators, if necessary; when found to be
## . . . . . . . . . . . necessary `start' is invoked and if dostandard is
## . . . . . . . . . . . true, `standard' is invoked. Finally the determined
## . . . . . . . . . . . . . coset table standardisation scheme is returned.
##
InstallGlobalFunction(ACE_LENLEX_CHK, function(ioIndex, dostandard)
local datarec, standard;
datarec := ACEData.io[ ioIndex ];
standard := ACE_COSET_TABLE_STANDARD( datarec.options );
if (standard = "lenlex") and IsBound(datarec.enumResult) then
if (not IsBound(datarec.enforceAsis) or not datarec.enforceAsis) and
not IsACEGeneratorsInPreferredOrder(ioIndex) then
datarec.enforceAsis := true;
PROCESS_ACE_OPTION(datarec.stream, "relators",
ACE_RELS(ACERelators(ioIndex),
ACEGroupGenerators(ioIndex),
datarec.acegens,
true));
PROCESS_ACE_OPTION(datarec.stream, "asis", 1);
ACE_MODE("Start", datarec);
fi;
if dostandard then
PROCESS_ACE_OPTION(datarec.stream, "standard", "");
fi;
fi;
return standard;
end);
#############################################################################
####
##
#F SET_ACE_ARGS . . . . . . . . . . . . . . . . . . . . . Set ACEStart args
##
##
InstallGlobalFunction(SET_ACE_ARGS, function(ioIndex, fgens, rels, sgens)
local datarec, gens;
ioIndex := ACEProcessIndex(ioIndex); # Ensure ioIndex is valid
fgens := ACE_FGENS_ARG_CHK(fgens);
rels := ACE_WORDS_ARG_CHK(fgens, rels, "relators");
sgens := ACE_WORDS_ARG_CHK(fgens, sgens, "subgp gen'rs");
gens := TO_ACE_GENS(fgens);
datarec := ACEData.io[ ioIndex ];
datarec.enforceAsis
:= ( DATAREC_VALUE_ACE_OPTION(datarec, false, "lenlex") or
VALUE_ACE_OPTION( ACE_OPT_NAMES(), false, "lenlex") ) and
not IsACEGeneratorsInPreferredOrder(fgens, rels, "noargchk");
datarec.echoargs := true; # If echo option is set INTERACT_SET_ACE_OPTIONS
# will echo args
PROCESS_ACE_OPTION(datarec.stream, "group", gens.toace);
PROCESS_ACE_OPTION(datarec.stream, "relators",
ACE_RELS(rels, fgens, gens.acegens, datarec.enforceAsis));
PROCESS_ACE_OPTION(datarec.stream, "generators",
ACE_WORDS(sgens, fgens, gens.acegens));
if datarec.enforceAsis then
PROCESS_ACE_OPTION(datarec.stream, "asis", 1);
fi;
datarec.args := rec(fgens := fgens, rels := rels, sgens := sgens);
datarec.acegens := gens.acegens;
return ioIndex;
end);
#############################################################################
####
##
#F NO_START_DO_ACE_OPTIONS . . . . . . . . . . . . . . . . Internal function
## . . . . . . . . . . . . . . If set is true, set options for the ACEStart
## . . . . . . . . . . . . . . process indexed by ioIndex. If one of the new
## . . . . . . . . . . . . . . options evokes an enumeration the enumResult
## . . . . . . . . . . . . . . and stats fields are re-set. All ACE output
## . . . . . . . . . . . . . . is flushed. Called when no `start' directive
## . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . is needed.
##
##
InstallGlobalFunction(NO_START_DO_ACE_OPTIONS, function(ioIndex, set)
local datarec, setEnumResult;
datarec := ACEDataRecord(ioIndex);
if not IsEmpty(OptionsStack) then
setEnumResult := VALUE_ACE_OPTION( ACE_OPT_NAMES(),
fail,
["start", "aep", "rep"] ) <> fail;
if set then
INTERACT_SET_ACE_OPTIONS("ACEStart", datarec);
fi;
PROCESS_ACE_OPTION(datarec.stream, "text", "***");
if setEnumResult then
datarec.enumResult
:= LAST_ACE_ENUM_RESULT(datarec.stream, ACE_READ_NEXT_LINE, fail);
if IsEmpty( ACEGroupGenerators(ioIndex) ) then
Info(InfoACE + InfoWarning, 1, "ACEStart : No group generators?!");
Unbind(datarec.enumResult);
Unbind(datarec.stats);
else
datarec.stats := ACE_STATS(datarec.enumResult);
fi;
else
FLUSH_ACE_STREAM_UNTIL(datarec.stream, 3, 3, ACE_READ_NEXT_LINE,
line -> IsMatchingSublist(line, "***"));
fi;
fi;
end);
#############################################################################
####
##
#F ACEStart . . . . . . . . . . . . . . Initiate an interactive ACE session
##
##
InstallGlobalFunction(ACEStart, function(arg)
local start, ioIndex, stream, datarec, gens;
if Length(arg) > 5 then
Error("expected at most 5 arguments ... not ", Length(arg),
" arguments.\n");
elif Length(arg) = 2 and arg[1] <> 0 then
Error("when called with 2 arguments, first argument should be 0.\n");
elif not IsEmpty(arg) and arg[1] = 0 then
start := false;
arg := arg{[2..Length(arg)]};
else
start := true;
fi;
if Length(arg) in [3, 4] then
if Length(arg) = 3 then #args are: fgens, rels, sgens
ioIndex := CALL_ACE( "ACEStart", arg[1], arg[2], arg[3] );
else #arg{[2..4]} are: fgens, rels, sgens
ioIndex := SET_ACE_ARGS( arg[1], arg[2], arg[3], arg[4] );
NO_START_DO_ACE_OPTIONS(ioIndex, true);
fi;
if start then
if IsEmpty( ACEGroupGenerators(ioIndex) ) then
Info(InfoACE + InfoWarning, 1, "ACEStart : No group generators?!");
else
ACE_MODE( "Start", ACEData.io[ ioIndex ] );
fi;
elif Length(arg) = 3 then
NO_START_DO_ACE_OPTIONS(ioIndex, false);
fi;
elif Length(arg) <= 1 and start then
ioIndex := ACE_MODE_AFTER_SET_OPTS("Start", arg);
else # start = false
if Length(arg) = 1 then
ioIndex := CallFuncList(ACEProcessIndex, arg);
else
stream := InputOutputLocalProcess(ACEData.tmpdir, ACEData.binary, []);
if stream = fail then
Error("sorry! Run out of pseudo-ttys. Can't initiate stream.\n");
else
Add( ACEData.io, rec(stream := stream, options := rec()) );
ioIndex := Length(ACEData.io);
ACEData.io[ioIndex].procId := ioIndex;
fi;
fi;
NO_START_DO_ACE_OPTIONS(ioIndex, true);
fi;
ACE_LENLEX_CHK(ioIndex, false);
return ioIndex;
end);
#############################################################################
##
#F ACEQuit . . . . . . . . . . . . . . . . Close an interactive ACE session
##
InstallGlobalFunction(ACEQuit, function(arg)
local ioIndex;
ioIndex := CallFuncList(ACEProcessIndex, arg);
CloseStream(ACEData.io[ioIndex].stream);
Unbind(ACEData.io[ioIndex]);
end);
#############################################################################
##
#F ACEQuitAll . . . . . . . . . . . . . . Close all interactive ACE sessions
##
InstallGlobalFunction(ACEQuitAll, function()
local ioIndex;
for ioIndex in [1 .. Length(ACEData.io)] do
if IsBound(ACEData.io[ioIndex]) then
CloseStream(ACEData.io[ioIndex].stream);
Unbind(ACEData.io[ioIndex]);
fi;
od;
end);
#############################################################################
##
#F ACE_MODES . . . . . . . . . . Returns a record of which of the ACE modes
## . . . . . . . . . . . . . . . . . . Continue, Redo and Start are possible
##
InstallGlobalFunction(ACE_MODES, function(datarec)
local modes;
READ_ACE_ERRORS(datarec);
WRITE_LIST_TO_ACE_STREAM(datarec.stream, [ "mode;" ]);
modes := SplitString(FLUSH_ACE_STREAM_UNTIL(
datarec.stream, 3, 2, ACE_READ_NEXT_LINE,
line -> IsMatchingSublist(line, "start = ")
),
"",
" =,\n");
return rec(ACEContinue := modes[4] = "yes", # Modes in order of `cheapness'
ACERedo := modes[6] = "yes",
ACEStart := modes[2] = "yes");
end);
#############################################################################
##
#F ACEModes . . . . . . . . . . . . Returns a record of which of the modes
## . . . . . . . . . . . . . ACEContinue, ACERedo and ACEStart are possible
##
InstallGlobalFunction(ACEModes, function(arg)
return ACE_MODES( CallFuncList(ACEDataRecord, arg) );
end);
#############################################################################
####
##
#F ACEContinue . . . . . . . . . . . . Continue an interactive ACE session
##
##
InstallGlobalFunction(ACEContinue, function(arg)
return ACE_MODE_AFTER_SET_OPTS("Continue", arg);
end);
#############################################################################
####
##
#F ACERedo . . . . . . . . . . . . . . . . . Redo an interactive ACE session
##
##
InstallGlobalFunction(ACERedo, function(arg)
return ACE_MODE_AFTER_SET_OPTS("Redo", arg);
end);
#############################################################################
####
##
#F ACE_EQUIV_PRESENTATIONS . . . . . . . . . . . . . . . . Internal function
## . . . . . . . . . . . . . . . . . . called by ACEAllEquivPresentations
## . . . . . . . . . . . . . . . . . . and ACERandomEquivPresentations where
## . . . . . . . . . . . . . . . . . . . . string matches the last line read
##
InstallGlobalFunction(ACE_EQUIV_PRESENTATIONS, function(ioIndex, string)
local datarec, out, run;
datarec := ACEData.io[ ioIndex ];
out := rec(line := FLUSH_ACE_STREAM_UNTIL(
datarec.stream, 3, 3, ACE_READ_NEXT_LINE,
line -> Length(line) > 5 and
line{[1..6]} in [ "Group ", "** ERR" ]
),
runs := []);
if IsMatchingSublist(out.line, "** ERROR") then
# Can only happen for ACERandomEquivPresentations
out.line := ACEReadUntil(ioIndex,
line -> IsMatchingSublist(line, string))[1];
Error("ACERandomEquivPresentations:", out.line{[3..Length(out.line)]});
fi;
while not IsMatchingSublist(out.line, string) do
run := rec(rels := ACE_GAP_WORDS(datarec,
ACE_PARAMETER_WITH_LINE(ioIndex,
"Group Relators",
out.line)),
enumResult := ACE_ENUMERATION_RESULT(datarec.stream,
ACE_READ_NEXT_LINE));
run.stats := ACE_STATS(run.enumResult);
Add(out.runs, run);
out.line := ACE_READ_NEXT_LINE(datarec.stream);
Info(InfoACE, 3, Chomp(out.line));
od;
return out;
end);
#############################################################################
####
##
#F ACEAllEquivPresentations . . . . . . . Tests all equivalent presentations
##
## For the i-th interactive ACE process, generates and tests an enumeration
## for combinations of relator ordering, relator rotations, and relator
## inversions, according to the value of optval, where i and optval are
## determined by arg. The argument optval is considered as a binary number;
## its three bits are treated as flags, and control relator rotations (the
## 2^0 bit), relator inversions (the 2^1 bit) and relator orderings (the 2^2
## bit), respectively; 1 means `active' and 0 means `inactive'.
##
## Outputs a record with fields:
##
## primingResult
## the ACE enumeration result message of the priming run;
##
## primingStats
## the enumeration result of the priming run as a GAP ACEStats-like
## record;
##
## equivRuns
## a list of data records, one for each run, where each record has
## fields:
##
## rels
## the relators in the order used for the run,
##
## enumResult
## the ACE enumeration result message of the run, and
##
## stats
## the enumeration result as a GAP ACEStats-like record;
##
## summary
## a record with fields:
##
## successes
## the total number of successful (i.e. having finite enumeration
## index) runs,
##
## runs
## the total number of equivalent presentation runs executed,
##
## maxcosetsRange
## the range of values as a GAP list inside which each
## `equivRuns[i].maxcosets' lies, and
##
## totcosetsRange
## the range of values as a {\GAP} list inside which each
## `equivRuns[i].totcosets' lies.
##
InstallGlobalFunction(ACEAllEquivPresentations, function(arg)
local ioIndexAndOptval, ioIndex, datarec, aep, epRec, line;
ioIndexAndOptval := ACE_IOINDEX_AND_ONE_VALUE(arg);
ioIndex := ioIndexAndOptval[1];
datarec := ACEData.io[ ioIndex ];
line := EXEC_ACE_DIRECTIVE_OPTION(ioIndexAndOptval, "aep", 3,
line -> IsMatchingSublist(line, "* P"),
"", false);
if not IsMatchingSublist(line, "* P") then
Error("ACEAllEquivPresentations:", line{[3..Length(line)]});
fi;
aep := rec(primingResult := ACE_ENUMERATION_RESULT(datarec.stream,
ACE_READ_NEXT_LINE));
aep.primingStats := ACE_STATS(aep.primingResult);
epRec := ACE_EQUIV_PRESENTATIONS(ioIndex, "* There were");
aep.equivRuns := epRec.runs;
line := SplitString(epRec.line, "", "* Therwsucinu:\n");
aep.summary := rec(successes := Int(line[1]), runs := Int(line[2]));
line := Chomp( ACE_READ_NEXT_LINE(datarec.stream) );
Info(InfoACE, 3, line);
line := SplitString(line, "", "* maxcost=,");
aep.summary.maxcosetsRange
:= EvalString( Concatenation( "[", line[1], "]" ) );
aep.summary.totcosetsRange
:= EvalString( Concatenation( "[", line[2], "]" ) );
return aep;
end);
#############################################################################
####
##
#F ACERandomEquivPresentations . . . . . Tests a number of random equivalent
## . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . presentations
##
## For the i-th interactive ACE process, generates and tests n random
## enumeration for combinations of relator ordering, relator rotations, and
## relator inversions, according to the value of optval, where n is
## determined by optval, and i and optval are determined by arg. The
## argument optval is considered as a binary number; its three bits are
## treated as flags, and control relator rotations (the 2^0 bit), relator
## inversions (the 2^1 bit) and relator orderings (the 2^2 bit),
## respectively; 1 means `active' and 0 means `inactive'.
##
## Outputs a list of records, each record of which has fields:
##
## rels
## the relators in the order used for a presentation run,
##
## enumResult
## the ACE enumeration result message of the run, and
##
## stats
## the enumeration result of the run as a GAP ACEStats-like record.
##
InstallGlobalFunction(ACERandomEquivPresentations, function(arg)
local ioIndexAndOptval, ioIndex, datarec, stream;
ioIndexAndOptval := ACE_IOINDEX_AND_ONE_VALUE(arg);
ioIndex := ioIndexAndOptval[1];
datarec := ACEData.io[ ioIndex ];
stream := datarec.stream;
READ_ACE_ERRORS(datarec); # purge any output not yet collected
PROCESS_ACE_OPTION(stream, "rep", ioIndexAndOptval[2]);
PROCESS_ACE_OPTION(stream, "text", "------------------------------------");
return ACE_EQUIV_PRESENTATIONS(ioIndex, "------------").runs;
end);
#############################################################################
####
##
#F ACEGroupGenerators . . . . . . . . . . . Return the GAP group generators
## . . . . . . . . . . . . . . . . . . . . . . of an interactive ACE session
##
##
InstallGlobalFunction(ACEGroupGenerators, function(arg)
local datarec, ioIndex;
datarec := CallFuncList(ACEDataRecord, arg);
ioIndex := datarec.procId;
if not( IsBound( datarec.args ) and IsBound( datarec.args.fgens ) ) then
Info(InfoACE + InfoWarning, 1,
"No group generators saved. Setting value(s) from ACE ...");
return ACE_ARGS(ioIndex, "fgens");
else
return datarec.args.fgens;
fi;
end);
#############################################################################
####
##
#F ACERelators . . . . . . . . . . . . . . . . . . . Return the GAP relators
## . . . . . . . . . . . . . . . . . . . . . . of an interactive ACE session
##
##
InstallGlobalFunction(ACERelators, function(arg)
local datarec, ioIndex;
datarec := CallFuncList(ACEDataRecord, arg);
ioIndex := datarec.procId;
if not( IsBound( datarec.args ) and IsBound( datarec.args.rels ) ) then
Info(InfoACE + InfoWarning, 1,
"No relators saved. Setting value(s) from ACE ...");
return ACE_ARGS(ioIndex, "rels");
else
return datarec.args.rels;
fi;
end);
#############################################################################
####
##
#F ACESubgroupGenerators . . . . . . . . Return the GAP subgroup generators
## . . . . . . . . . . . . . . . . . . . . . . of an interactive ACE session
##
##
InstallGlobalFunction(ACESubgroupGenerators, function(arg)
local datarec, ioIndex;
datarec := CallFuncList(ACEDataRecord, arg);
ioIndex := datarec.procId;
if not( IsBound( datarec.args ) and IsBound( datarec.args.sgens ) ) then
Info(InfoACE + InfoWarning, 1,
"No subgroup generators saved. Setting value(s) from ACE ...");
return ACE_ARGS(ioIndex, "sgens");
else
return datarec.args.sgens;
fi;
end);
#############################################################################
####
##
#F DISPLAY_ACE_REC_FIELD . . . . . . . . . . . . . Displays a record that
## . . . . . . . . . . . . . . . . . . . . . . . . is itself a record field
##
##
InstallGlobalFunction(DISPLAY_ACE_REC_FIELD, function(datarec, field)
if not IsBound(datarec.(field)) or datarec.(field) = rec() then
Print("No ", field, ".\n");
else
Display(datarec.(field));
Print("\n");
fi;
end);
#############################################################################
####
##
#F DisplayACEOptions . . . . . . . . . . . Displays the current ACE options
##
##
InstallGlobalFunction(DisplayACEOptions, function(arg)
DISPLAY_ACE_REC_FIELD( CallFuncList(ACEDataRecord, arg), "options" );
end);
#############################################################################
####
##
#F DisplayACEArgs . . . . . . . . . . . . . . Displays the current ACE args
##
##
InstallGlobalFunction(DisplayACEArgs, function(arg)
DISPLAY_ACE_REC_FIELD( CallFuncList(ACEDataRecord, arg), "args" );
end);
#############################################################################
####
##
#F GET_ACE_REC_FIELD . . . . . . . . . . . . . . . Returns a record that is
## . . . . . . . . . . . . . . . . . . . . . . . . itself a record field
## . . . . . . . . . . . . . . . . . . . . . . . . associated with an
## . . . . . . . . . . . . . . . . . . . . . . . . . interactive ACE process
##
##
InstallGlobalFunction(GET_ACE_REC_FIELD, function(arglist, field)
local datarec;
datarec := CallFuncList(ACEDataRecord, arglist);
if not IsBound(datarec.(field)) or datarec.(field) = rec() then
Info(InfoACE + InfoWarning, 1, "No ", field, " saved.");
return rec();
else
return datarec.(field);
fi;
end);
#############################################################################
####
##
#F GetACEOptions . . . . . . . . . . . . . . Returns the current ACE options
##
##
InstallGlobalFunction(GetACEOptions, function(arg)
return GET_ACE_REC_FIELD(arg, "options");
end);
#############################################################################
####
##
#F GetACEArgs . . . . . . . . . . . . . . . . . Returns the current ACE args
##
##
InstallGlobalFunction(GetACEArgs, function(arg)
return GET_ACE_REC_FIELD(arg, "args");
end);
#############################################################################
####
##
#F SET_ACE_OPTIONS . . . . . . . . . . . . . . . . . . . Internal procedure
## . . . . . . . . . . . . . . . . . . . . . . . . . Called by SetACEOptions
##
## SetACEOptions has two forms: the interactive version (below) and the
## non-interactive version defined locally within ACECosetTable. For the
## interactive version the data record datarec is ACEData.io[ioIndex] for
## some integer ioIndex. For the non-interactive version, which will only be
## invoked from within a break-loop, datarec is ACEData.
##
InstallGlobalFunction(SET_ACE_OPTIONS, function(datarec)
local newoptnames;
datarec.newoptions := NEW_ACE_OPTIONS();
# First we need to scrub any option names in datarec.options that
# match those in datarec.newoptions ... to ensure that *all* new
# options are at the end of the stack
SANITISE_ACE_OPTIONS(datarec.options, datarec.newoptions);
PopOptions();
Add(OptionsStack, datarec.options);
PushOptions(datarec.newoptions);
# The following is needed when SetACEOptions is invoked via ACEExample
Unbind(OptionsStack[ Length(OptionsStack) ].aceexampleoptions);
datarec.options := ShallowCopy( OptionsStack[ Length(OptionsStack) ] );
# We ensure OptionsStack is the same length as before the call to
# SET_ACE_OPTIONS, and ensure the updated options are on top
PopOptions();
PopOptions();
Add(OptionsStack, datarec.options);
newoptnames := RecNames(datarec.newoptions);
Unbind(datarec.newoptions);
return newoptnames;
end);
#############################################################################
####
##
#F ECHO_ACE_ARGS . . . . . . . . . . . . . . . . . . . . Internal procedure
## . . . . . . . . . . . . . . . . . . . . . . Echoes the values of the
## . . . . . . . . . . . . . . . . . . . . . . fields: fgens, rels, sgens of
## . . . . . . . . . . . . . . . . . . . . . . args submitted to function
## . . . . . . . . . . . . . . . . . . . . . . ACEfname if echo is positive.
##
InstallGlobalFunction(ECHO_ACE_ARGS, function(echo, ACEfname, args)
if echo > 0 then
Print(ACEfname, " called with the following arguments:\n");
Print(" Group generators : ", args.fgens, "\n");
Print(" Group relators : ", args.rels, "\n");
Print(" Subgroup generators : ", args.sgens, "\n");
fi;
end);
#############################################################################
####
##
#F INTERACT_SET_ACE_OPTIONS . . . . . . . . . . . . . . Internal procedure
## . . . . . . . . . . . . . . . . . . . . . . . Passes new options to ACE
## . . . . . . . . . . . . . . . . . . . . . . . and updates stored options
##
## Called by the ACE function with name ACEfname and with datarec equal to
## ACEData.io[ioIndex] for some integer ioIndex, the updated options are
## stored in datarec.options.
##
InstallGlobalFunction(INTERACT_SET_ACE_OPTIONS, function(ACEfname, datarec)
local newoptnames, s, optnames, echo, ignored;
datarec.modereqd := false;
if not(IsEmpty(OptionsStack) or
ForAll(RecNames(OptionsStack[ Length(OptionsStack) ]),
optname -> optname in ACE_INTERACT_FUNC_OPTIONS)) then
if IsBound(datarec.options) then
newoptnames := SET_ACE_OPTIONS(datarec);
else
datarec.options := NEW_ACE_OPTIONS();
newoptnames := RecNames(datarec.options);
fi;
optnames := RecNames(datarec.options);
newoptnames := Filtered(
newoptnames,
optname -> not(optname in ACE_INTERACT_FUNC_OPTIONS));
ignored := List(VALUE_ACE_OPTION(newoptnames, [], "aceignore"),
optname -> ACEPreferredOptionName(optname));
datarec.modereqd := ForAny(newoptnames,
function(optname)
local prefname;
prefname := ACEPreferredOptionName(optname);
return not(prefname in NonACEbinOptions or
prefname in ignored);
end);
if ForAny(newoptnames,
optname -> ACEPreferredOptionName(optname)
in ["group", "relators", "generators"]) then
for s in [ "Detected usage of a synonym of one (or more) of the options:",
" `group', `relators', `generators'.",
"Discarding current values of args.",
"(The new args will be extracted from ACE, later)." ]
do
Info(InfoACE + InfoWarning, 1, s);
od;
Unbind(datarec.args);
Unbind(datarec.acegens);
fi;
echo := ACE_VALUE_ECHO(optnames);
if IsBound(datarec.echoargs) then
if IsBound(datarec.args) then
ECHO_ACE_ARGS( echo, ACEfname, datarec.args );
fi;
Unbind(datarec.echoargs);
fi;
PROCESS_ACE_OPTIONS(ACEfname, optnames, newoptnames, echo, datarec,
# disallowed (options) ... none
rec(),
# ignored
Concatenation( [ "aceinfile", "aceoutfile" ],
ignored,
ACE_IF_EXPR(
IsBound(datarec.enforceAsis)
and datarec.enforceAsis,
[ "asis" ], [], []) ));
fi;
end);
#############################################################################
####
##
#F SetACEOptions . . . . . . . . . . . . Interactively, passes new options
## . . . . . . . . . . . . . . . . . . . to ACE and updates stored options
##
InstallGlobalFunction(SetACEOptions, function(arg)
local datarec;
if Length(arg) > 2 then
Error("expected 0, 1 or 2 arguments ... not ", Length(arg), " arguments\n");
elif Length(arg) in [1, 2] and IsRecord( arg[Length(arg)] ) then
if not IsEmpty(OptionsStack) then
Info(InfoACE + InfoWarning, 1,
"Non-empty OptionsStack: SetACEOptions may have been called with");
Info(InfoACE + InfoWarning, 1,
"both a record argument and options. The order options are listed");
Info(InfoACE + InfoWarning, 1,
"may be incorrect. Please use separate calls to SetACEOptions,");
Info(InfoACE + InfoWarning, 1,
"e.g. 'SetACEOptions(<optionsRec>); SetACEOptions(: <options>);' ");
fi;
PushOptions( arg[Length(arg)] );
datarec := CallFuncList(ACEDataRecord, arg{[1..Length(arg) - 1]});
INTERACT_SET_ACE_OPTIONS("SetACEOptions", datarec);
PopOptions();
elif Length(arg) <= 1 then
datarec := CallFuncList(ACEDataRecord, arg);
INTERACT_SET_ACE_OPTIONS("SetACEOptions", datarec);
else
Error("2nd argument should have been a record\n");
fi;
if datarec.modereqd then
CHEAPEST_ACE_MODE(datarec);
fi;
ACE_LENLEX_CHK(datarec.procId, false);
end);
#############################################################################
####
##
#F ACE_PARAMETER_WITH_LINE . . . . . . . . . . . . . . . . Internal function
## . . . . . . . . . . . . . . . . . for the ACE process of index ioIndex
## . . . . . . . . . . . . . . . . . returns ACE's value of the parameter
## . . . . . . . . . . . . . . . . . identified by string starting with line
##
InstallGlobalFunction(ACE_PARAMETER_WITH_LINE, function(ioIndex, string, line)
# Remove "<string>: " and trailing newline
line := line{[Length(string) + 3 .. Length(line) - 1]};
if line = "" or line[ Length(line) ] <> ';' then
line := Flat([line,
List(ACEReadUntil(ioIndex, line -> line[Length(line)] = ';'),
line -> line{[3..Length(line)]}) # Scrub two blanks at
# beginning of lines
]);
fi;
# Remove any blanks after commas and trailing ';'
return ReplacedString(line{[1..Length(line) - 1]}, ", ", ",");
end);
#############################################################################
####
##
#F ACE_PARAMETER . . . . . . . . . . . . . . . . . . . . . Internal function
## . . . . . . . . . . . . . . . . . . for the ACE process of index ioIndex
## . . . . . . . . . . . . . . . . . . returns ACE's value of the parameter
## . . . . . . . . . . . . . . . . . . . . . . . . . . identified by string
##
InstallGlobalFunction(ACE_PARAMETER, function(ioIndex, string)
local line;
line := FLUSH_ACE_STREAM_UNTIL(ACEData.io[ ioIndex ].stream, 3, 3,
ACE_READ_NEXT_LINE,
line -> Length(line) >= Length(string) and
line{[1..Length(string)]} = string);
return ACE_PARAMETER_WITH_LINE(ioIndex, string, line);
end);
#############################################################################
####
##
#F ACE_GAP_WORDS . . . . . . . . . . . . . . . . . . . . . Internal function
## . . . . . . . . returns the translation into GAP of an ACE list of words
##
## ACE stores words according to the BNF:
## <word> = <element> <word> | "(" <word> ")^" <power>
## <power> = <integer>
## <element> = <generator> | <inverse>
## <generator> = <integer> <space> | <lowercase letter>
## <inverse> = "-" <generator> | <uppercase letter>
##
InstallGlobalFunction(ACE_GAP_WORDS, function(datarec, words)
local GAPWord;
GAPWord := function(word)
local power, parts, elements;
if word[1] = '(' then
parts := SplitString(word, "", "()^");
word := parts[1];
power := Int(parts[2]);
else
power := 1;
fi;
if IsDigitChar(word[1]) or word[1] = '-' then
elements := List(SplitString(word, " "), Int);
# Convert to GAP elements
elements := List(elements,
function(element)
if element < 0 then
return datarec.args.fgens[ AbsInt(element) ]^-1;
else
return datarec.args.fgens[element];
fi;
end);
else
elements := List([1..Length(word)], i -> WordAlp(word, i));
# Convert to GAP elements
elements := List(elements,
function(element)
if IsUpperAlphaChar(element[1]) then
return datarec.args.fgens[
Position(
datarec.acegens,
LowercaseString(element)
)
]^-1;
else
return datarec.args.fgens[ Position(datarec.acegens,
element) ];
fi;
end);
fi;
return Product( elements, One(datarec.args.fgens[1]) )^power;
end;
return List(SplitString(words, ','), GAPWord);
end);
#############################################################################
####
##
#F ACE_GENS . . . . . . . . . . . . . . . . . . . . . . Internal procedure
## . . . . . . . . . . . . . . . sets datarec.args.fgens and datarec.acegens
## . . . . . . . . . . . . . . . from the value of ACE's "Group Generators"
## . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . parameter
##
InstallGlobalFunction(ACE_GENS, function(datarec, string)
local line;
if IsAlphaChar(string[1]) then
datarec.acegens := List([1..Length(string)], i -> WordAlp(string, i));
datarec.args.fgens := GeneratorsOfGroup( FreeGroup(datarec.acegens) );
else
datarec.acegens := List([1..Int(string)], i -> String(i));
datarec.args.fgens := GeneratorsOfGroup(FreeGroup(
List(datarec.acegens,
s -> Flat(["x", s]))
));
fi;
end);
#############################################################################
####
##
#F ACE_ARGS . . . . . . . . . . . . . . . . . . . . . . . Internal function
## . . . . . . . . . . . . . . for the ACE process indexed by ioIndex sets
## . . . . . . . . . . . . . and returns ACEDataRecord(ioIndex).args.(field)
## . . . . . . . . . . . . . . . . . . . according to ACE's parameter value
##
## If ACEDataRecord(ioIndex).args is unset, it and
## ACEDataRecord(ioIndex).acegens are set according to the values held by
## the ACE process indexed by ioIndex.
##
InstallGlobalFunction(ACE_ARGS, function(ioIndex, field)
local datarec, line;
datarec := ACEDataRecord(ioIndex);
if not IsBound(datarec.args) then
datarec.args := rec();
fi;
if not IsBound(datarec.args.fgens) or field = "fgens" then
WRITE_LIST_TO_ACE_STREAM(datarec.stream, [ "sr:1;" ]);
line := FLUSH_ACE_STREAM_UNTIL(datarec.stream, 3, 3, ACE_READ_NEXT_LINE,
line -> Length(line) > 8 and
line{[1..9]} in [ "Group Gen",
"Group Rel" ]);
if IsMatchingSublist(line, "Group Gen") then
ACE_GENS(datarec, ACE_PARAMETER_WITH_LINE(ioIndex,
"Group Generators",
line));
else
datarec.acegens := [];
datarec.args.fgens := [];
fi;
else
WRITE_LIST_TO_ACE_STREAM(datarec.stream, [ "sr;" ]);
fi;
if not IsBound(datarec.args.rels) or field = "rels" then
if not IsBound(line) or not IsMatchingSublist(line, "Group Rel") then
line := FLUSH_ACE_STREAM_UNTIL(
datarec.stream, 3, 3, ACE_READ_NEXT_LINE,
line -> IsMatchingSublist(line, "Group Rel") );
fi;
datarec.args.rels := ACE_GAP_WORDS(datarec,
ACE_PARAMETER_WITH_LINE(
ioIndex, "Group Relators", line
));
fi;
if not IsBound(datarec.args.sgens) or field = "sgens" then
datarec.args.sgens := ACE_GAP_WORDS(datarec,
ACE_PARAMETER(ioIndex,
"Subgroup Generators"));
fi;
FLUSH_ACE_STREAM_UNTIL(datarec.stream, 3, 3, ACE_READ_NEXT_LINE,
line -> IsMatchingSublist(line, " #--"));
return datarec.args.(field);
end);
#############################################################################
####
##
#F ACEParameters . . . . . . Returns the ACE value of ACE parameter options
##
## Also ensures for the interactive ACE process indexed by i that the args
## and acegens fields of ACEData.io[i] are set. If not, it sets them
## according to the values held by ACE process i (the assumption being that
## the user started the process via 'ACEStart(0);').
##
InstallGlobalFunction(ACEParameters, function(arg)
local ioIndex, datarec, line, fieldsAndValues, parameters, sgens, i, opt, val;
ioIndex := CallFuncList(ACEProcessIndex, arg);
datarec := ACEData.io[ ioIndex ];
READ_ACE_ERRORS(datarec);
WRITE_LIST_TO_ACE_STREAM(datarec.stream, [ "sr:1;" ]);
datarec.parameters
:= rec(enumeration := ACE_PARAMETER(ioIndex, "Group Name"));
parameters := datarec.parameters;
if not IsBound(datarec.args) then
datarec.args := rec();
fi;
if not IsBound(datarec.acegens) or not IsBound(datarec.args.fgens) then
line := FLUSH_ACE_STREAM_UNTIL(datarec.stream, 3, 3, ACE_READ_NEXT_LINE,
line -> Length(line) > 8 and
line{[1..9]} in [ "Group Gen",
"Group Rel" ]);
if IsMatchingSublist(line, "Group Gen") then
ACE_GENS(datarec, ACE_PARAMETER_WITH_LINE(ioIndex,
"Group Generators",
line));
else
datarec.args.fgens := [];
datarec.acegens := [];
fi;
fi;
if not IsBound(datarec.args.rels) then
if not IsBound(line) or not IsMatchingSublist(line, "Group Rel") then
line := FLUSH_ACE_STREAM_UNTIL(
datarec.stream, 3, 3, ACE_READ_NEXT_LINE,
line -> IsMatchingSublist(line, "Group Rel"));
fi;
datarec.args.rels := ACE_GAP_WORDS(datarec,
ACE_PARAMETER_WITH_LINE(
ioIndex, "Group Relators", line
));
fi;
parameters.subgroup := ACE_PARAMETER(ioIndex, "Subgroup Name");
sgens := ACE_PARAMETER(ioIndex, "Subgroup Generators");
if not IsBound(datarec.args.sgens) then
datarec.args.sgens := ACE_GAP_WORDS(datarec, sgens);
fi;
fieldsAndValues :=
SplitString(
ReplacedString(
Flat( ACEReadUntil(ioIndex,
line -> IsMatchingSublist(line, "C:")) ),
"Fi:", "Fil:"
),
"", " :;"
);
FLUSH_ACE_STREAM_UNTIL(datarec.stream, 3, 3, ACE_READ_NEXT_LINE,
line -> IsMatchingSublist(line, " #---"));
i := 1;
while i < Length(fieldsAndValues) do
val := Int(fieldsAndValues[i + 1]);
if val = fail then
# workspace can be an integer or a string
val := fieldsAndValues[i + 1];
fi;
parameters.(ACEOptionData( fieldsAndValues[i] ).synonyms[1]) := val;
i := i + 2;
od;
return parameters;
end);
#############################################################################
####
##
#F ACEBinaryVersion
##
## Infos the version and component compilation details of the ACE binary,
## and returns the version of the ACE binary.
##
InstallGlobalFunction(ACEBinaryVersion, function(arg)
local ioIndex, datarec;
ACE_IOINDEX_ARG_CHK(arg);
ioIndex := ACE_IOINDEX(arg);
if ioIndex = fail then
# Fire up a new stream ... which we'll close when we're finished
datarec := ACEData.ni;
datarec.stream
:= InputOutputLocalProcess( ACEData.tmpdir, ACEData.binary, [] );
else
# Use interactive ACE process: ioIndex
datarec := ACEData.io[ ioIndex ];
fi;
READ_ACE_ERRORS(datarec); # purge any output not yet collected
# e.g. error messages due to unknown options
Info(InfoACE, 1, "ACE Binary Version: ", ACEData.version);
WRITE_LIST_TO_ACE_STREAM(datarec.stream, [ "options;" ]);
FLUSH_ACE_STREAM_UNTIL(datarec.stream, 1, 1, ACE_READ_NEXT_LINE,
line -> IsMatchingSublist(line, " host info ="));
if ioIndex = fail then
CloseStream(datarec.stream);
fi;
return ACEData.version;
end);
#############################################################################
####
##
#F EXEC_ACE_DIRECTIVE_OPTION . . . . . . . . . . . . . . . Internal Function
## . . . . . . . . . . . . . . . . . . . executes an ACE `directive' option
##
## An ACE `directive' option is an ACE option with name optname that returns
## output; most are implemented by a function of form: ACEOptname.
##
## For the stream and option value defined by arglist pass optname (the name
## of an ACE option that expects a value) to ACE and flush the output until
## a line for which IsMyLine(line) is true or an error is encountered and
## then return the final line. If IsMyLine is the the null string then ACE
## is also directed to print closeline via option `text' and IsMyLine is
## defined to be true if a line matches closeline; in this way closeline is
## a sentinel. If both IsMyLine and closeline are null strings then we
## expect no ACE output and just check for error output from ACE. If
## IsMyLine is the null string, closeline is a non-null string and readUntil
## is true then all lines read are returned rather than just the last line.
##
InstallGlobalFunction(EXEC_ACE_DIRECTIVE_OPTION,
function(arglist, optname, infoLevel, IsMyLine, closeline, readUntil)
local datarec, optval, line;
datarec := ACEData.io[ arglist[1] ];
optval := arglist[2];
READ_ACE_ERRORS(datarec); # purge any output not yet collected
# e.g. error messages due to unknown options
PROCESS_ACE_OPTION(datarec.stream, optname, optval);
if IsMyLine = "" then
if closeline = "" then
# We don't expect any ACE output ... just check for errors
READ_ACE_ERRORS(datarec);
return;
else
PROCESS_ACE_OPTION(datarec.stream, "text", closeline);
IsMyLine := line -> Chomp(line) = closeline;
if readUntil then
return ACEReadUntil(arglist[1], IsMyLine);
fi;
fi;
else
line := FLUSH_ACE_STREAM_UNTIL(datarec.stream, infoLevel, infoLevel,
ACE_READ_NEXT_LINE,
line -> IsMyLine(line) or
IsMatchingSublist(line, "** ERROR"));
if IsMatchingSublist(line, "** ERROR") then
IsMyLine := line -> IsMatchingSublist(line, " "); # 1 more line to flush
else
return line;
fi;
fi;
return FLUSH_ACE_STREAM_UNTIL(datarec.stream, infoLevel, infoLevel,
ACE_READ_NEXT_LINE, IsMyLine);
end);
#############################################################################
####
##
#F ACE_IOINDEX_AND_NO_VALUE . . . . . . . . . . . . . . . Internal Function
## . . . . . . . . . . . . . . . . . . . . returns a list [ioIndex, optval]
## . . . . . . . . . . . . . . . . . . . . for a no-value ACE `directive'
## . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . option
##
InstallGlobalFunction(ACE_IOINDEX_AND_NO_VALUE, function(arglist)
return [ CallFuncList(ACEProcessIndex, arglist), "" ];
end);
#############################################################################
####
##
#F ACE_IOINDEX_AND_ONE_VALUE . . . . . . . . . . . . . . . Internal Function
## . . . . . . . . . . . . . . . . . . . . returns a list [ioIndex, optval]
## . . . . . . . . . . . . . . . . . . . . for a one-value ACE `directive'
## . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . option
##
InstallGlobalFunction(ACE_IOINDEX_AND_ONE_VALUE, function(arglist)
if Length(arglist) in [1,2] then
return [ CallFuncList(ACEProcessIndex, arglist{[1..Length(arglist) - 1]}),
arglist[Length(arglist)] ];
else
Error("expected 1 or 2 arguments ... not ",
Length(arglist), " arguments\n");
fi;
end);
#############################################################################
####
##
#F ACE_IOINDEX_AND_ONE_LIST . . . . . . . . . . . . . . . Internal Function
## . . . . . . . . . . . . . . . . . . . . returns a list [ioIndex, optval]
## . . . . . . . . . . . . . . . . . . . . for a one-value ACE `directive'
## . . . . . . . . . . . . . . . . . . . . option, where that one-value
## . . . . . . . . . . . . . . . . . . . . . . . . . . . . . must be a list
##
InstallGlobalFunction(ACE_IOINDEX_AND_ONE_LIST, function(arglist)
if not(Length(arglist) in [1,2]) then
Error("expected 1 or 2 arguments ... not ",
Length(arglist), " arguments\n");
elif IsString(arglist[ Length(arglist) ]) or
not IsList(arglist[ Length(arglist) ]) then
Error("last argument should be a list\n");
else
return [ CallFuncList(ACEProcessIndex, arglist{[1..Length(arglist) - 1]}),
arglist[Length(arglist)] ];
fi;
end);
#############################################################################
####
##
#F ACE_IOINDEX_AND_LIST . . . . . . . . . . . . . . . . . Internal Function
## . . . . . . . . . . . . . . . . . . . . returns a list [ioIndex, optval]
## . . . . . . . . . . . . . . . . . . . . for a no-value or list-value ACE
## . . . . . . . . . . . . . . . . . . . . . . . . . . . `directive' option
##
InstallGlobalFunction(ACE_IOINDEX_AND_LIST, function(arglist)
if Length(arglist) > 2 then
Error("expected 0, 1 or 2 arguments ... not ",
Length(arglist), " arguments\n");
elif Length(arglist) in [1, 2] and IsList( arglist[Length(arglist)] ) then
return [ CallFuncList(ACEProcessIndex, arglist{[1..Length(arglist) - 1]}),
arglist[Length(arglist)] ];
elif Length(arglist) <= 1 then
return [ CallFuncList(ACEProcessIndex, arglist), "" ];
else
Error("2nd argument should have been a list\n");
fi;
end);
#############################################################################
####
##
#F ACEDumpVariables . . . . . . . . . . . . . Dumps ACE's internal variables
##
##
InstallGlobalFunction(ACEDumpVariables, function(arg)
EXEC_ACE_DIRECTIVE_OPTION(
ACE_IOINDEX_AND_LIST(arg), "dump", 1,
line -> IsMatchingSublist(line, " #----"), "", false);
end);
#############################################################################
####
##
#F ACEDumpStatistics . . . . . . . . . . . . Dumps ACE's internal statistics
##
##
InstallGlobalFunction(ACEDumpStatistics, function(arg)
EXEC_ACE_DIRECTIVE_OPTION(
ACE_IOINDEX_AND_NO_VALUE(arg), "statistics", 1,
line -> IsMatchingSublist(line, " #----"), "", false);
end);
#############################################################################
####
##
#F ACEStyle . . . . . . . . . . . . . Returns the current enumeration style
##
##
InstallGlobalFunction(ACEStyle, function(arg)
local splitstyle;
splitstyle := SplitString(
EXEC_ACE_DIRECTIVE_OPTION(
ACE_IOINDEX_AND_NO_VALUE(arg), "style", 3,
line -> IsMatchingSublist(line, "style"), "", false
),
"", " =\n"
);
if Length(splitstyle) = 2 then
return splitstyle[2];
else
return Flat([ splitstyle[2], " (defaulted)" ]);
fi;
end);
#############################################################################
####
##
#F ACEDisplayCosetTable . . . . . . . . Prints the current ACE coset table
## . . . . . . . . . . . . . . . . . . at its current level of completeness
##
##
InstallGlobalFunction(ACEDisplayCosetTable, function(arg)
local ioIndexAndValue, stream, closeline;
ioIndexAndValue := ACE_IOINDEX_AND_LIST(arg);
stream := ACEData.io[ ioIndexAndValue[1] ].stream;
PROCESS_ACE_OPTION(stream, "print", ioIndexAndValue[2]);
closeline := "------------------------------------------------------------";
PROCESS_ACE_OPTION(stream, "text", closeline);
FLUSH_ACE_STREAM_UNTIL(stream, 3, 3, ACE_READ_NEXT_LINE,
line -> IsMatchingSublist(line, "CO:") or
IsMatchingSublist(line, "co:") or
IsMatchingSublist(line, "** ERROR"));
FLUSH_ACE_STREAM_UNTIL(stream, 1, 3, ACE_READ_NEXT_LINE,
line -> IsMatchingSublist(line, closeline));
end);
#############################################################################
####
##
#F IsCompleteACECosetTable . . . . . Returns true if the current coset table
## . . . . . . . . . . . . . . . . . is complete, as determined by the
## . . . . . . . . . . . . . . . . . current value of the enumeration index,
## . . . . . . . . . . . . . . . . . . . . . . . . . . . and false otherwise
##
InstallGlobalFunction(IsCompleteACECosetTable, function(arg)
local datarec;
datarec := CallFuncList(ACEDataRecord, arg);
if not IsBound(datarec.stats) then
CHEAPEST_ACE_MODE(datarec);
fi;
return datarec.stats.index <> 0;
end);
#############################################################################
####
##
#F ACECosetRepresentative . . . . . . . . Returns the coset representative
## . . . . . . . . . . . . . . . . . . . . of coset n, for the current coset
## . . . . . . . . . . . . . . . . . . . . table held by interactive process
## . . . . . . . . . . . . . . . . . . . . . . i, for i, n determined by arg
##
InstallGlobalFunction(ACECosetRepresentative, function(arg)
local ioIndexAndValue, datarec, coset, line, list;
ioIndexAndValue := ACE_IOINDEX_AND_ONE_VALUE(arg);
datarec := ACEData.io[ ioIndexAndValue[1] ];
coset := ioIndexAndValue[2];
READ_ACE_ERRORS(datarec); # purge any output not yet collected
if coset = 1 then
return One(ACEGroupGenerators( ioIndexAndValue[1] )[1]);
elif coset > datarec.stats.activecosets then
Error("ACECosetRepresentative: coset table has only ",
datarec.stats.activecosets, " (<", coset, ") active coset nos.\n");
fi;
PROCESS_ACE_OPTION(datarec.stream, "print", [-coset, coset]);
line := FLUSH_ACE_STREAM_UNTIL(datarec.stream, 3, 3, ACE_READ_NEXT_LINE,
line -> Length(line) > 1 and
line{[1..2]} in ["--", " "]);
if IsMatchingSublist(line, " ") then
Error("ACECosetRepresentative: ", line{[4..Length(line)]});
fi;
list := ACEReadUntil(ioIndexAndValue[1], list -> true,
line -> SplitString(line, "", "| "))[1];
return ACE_GAP_WORDS(datarec, list[ Length(list) ])[1];
end);
#############################################################################
####
##
#F ACECosetRepresentatives . . . . . . . . Returns the coset representatives
## . . . . . . . . . . . . . . . . . . . . . . of ACE's current coset table
##
##
InstallGlobalFunction(ACECosetRepresentatives, function(arg)
local ioIndex, datarec, line, activecosets, cosetreps;
ioIndex := CallFuncList(ACEProcessIndex, arg);
datarec := ACEData.io[ ioIndex ];
if not IsBound(datarec.stats) then
Error("ACECosetRepresentatives: no current table?\n");
fi;
READ_ACE_ERRORS(datarec); # purge any output not yet collected
PROCESS_ACE_OPTION(datarec.stream, "print", -datarec.stats.activecosets);
line := FLUSH_ACE_STREAM_UNTIL(datarec.stream, 3, 3, ACE_READ_NEXT_LINE,
line -> Length(line) > 1 and
line{[1..2]} in ["co", "CO", " "]);
if IsMatchingSublist(line, " ") then
Error("ACECosetRepresentatives: ", line{[4..Length(line)]});
fi;
activecosets := Int( SplitString(line, "", "coCO: a=")[1] );
FLUSH_ACE_STREAM_UNTIL(datarec.stream, 3, 3, ACE_READ_NEXT_LINE,
line -> IsMatchingSublist(line, " 1 "));
cosetreps := List(ACEReadUntil(
ioIndex,
list -> Int(list[1]) = Minimum(
activecosets,
datarec.stats.activecosets),
line -> SplitString(line, "", "| ")
),
list -> ACE_GAP_WORDS(datarec, list[ Length(list) ])[1]
);
if datarec.stats.activecosets < activecosets then
# We missed some
PROCESS_ACE_OPTION(datarec.stream, "print",
[-(datarec.stats.activecosets + 1), activecosets]);
line := FLUSH_ACE_STREAM_UNTIL(datarec.stream, 3, 3, ACE_READ_NEXT_LINE,
line -> IsMatchingSublist(line, "---"));
return Concatenation([One(ACEGroupGenerators(ioIndex)[1])],
cosetreps,
List(ACEReadUntil(ioIndex,
list -> Int(list[1]) = activecosets,
line -> SplitString(line, "", "| ")),
list -> ACE_GAP_WORDS(
datarec, list[ Length(list) ])[1]
)
);
else
return Concatenation([One(ACEGroupGenerators(ioIndex)[1])], cosetreps);
fi;
end);
#############################################################################
####
##
#F ACETransversal . . . . . . . . . Returns ACECosetRepresentatives(arg) if
## . . . . . . . . . . . . . . . . . the current coset table is complete,
## . . . . . . . . . . . . . . . . . . . . . . . . . . . and fail otherwise
##
InstallGlobalFunction(ACETransversal, function(arg)
local ioIndex;
ioIndex := CallFuncList(ACEProcessIndex, arg);
if IsCompleteACECosetTable(ioIndex) then
return ACECosetRepresentatives(ioIndex);
else
Info(InfoACE + InfoWarning, 1,
"ACETransversal: coset table is not complete");
return fail;
fi;
end);
#############################################################################
####
##
#F ACECycles . . . . . . . . . . . . . Display the cycles (permutations) of
## . . . . . . . . . . . . . . . . . . . . . the permutation representation
##
InstallGlobalFunction(ACECycles, function(arg)
local datarec, error, cycles;
datarec := CallFuncList(ACEDataRecord, arg);
READ_ACE_ERRORS(datarec); # purge any output not yet collected
# e.g. error messages due to unknown options
PROCESS_ACE_OPTION(datarec.stream, "cycles", "");
PROCESS_ACE_OPTION(datarec.stream, "text", ""); # Make ACE print a blank line
# ... that we use as sentinel
error := IsMatchingSublist(
FLUSH_ACE_STREAM_UNTIL(
datarec.stream, 3, 3, ACE_READ_NEXT_LINE,
line -> Length(line) > 1 and
line{[1..2]} in ["**", "CO", "co"]
),
"**", 1);
cycles := ACEReadUntil(datarec.procId, line -> line = "");
if error then
Info(InfoACE + InfoWarning, 1,
ReplacedString(cycles[1], " ", "ACECycles: "));
return fail;
else
cycles := List(cycles,
function(line)
local posEq;
posEq := Position(line, '=');
if posEq = fail then
return line;
elif IsMatchingSublist(line, "= identity", posEq) then
return ", ()";
else
return ReplacedString(line, line{[1..posEq]}, ",");
fi;
end);
cycles[1][1] := '[';
Add(cycles, "]");
return EvalString( Concatenation(cycles) );
fi;
end);
#############################################################################
####
##
#F ACETraceWord . . . . . . . . . . . . Traces word through the coset table
## . . . . . . . . . . . . . . . . . . . of the i-th interactive ACE process
## . . . . . . . . . . . . . . . . . . . starting at coset n, for i, n, word
## . . . . . . . . . . . . . . . . . . . determined by arg, and return the
## . . . . . . . . . . . . . . . . . . . final coset number if the trace
## . . . . . . . . . . . . . . . . . . . . . . completes, and fail otherwise
##
InstallGlobalFunction(ACETraceWord, function(arg)
local ioIndex, datarec, twArgs, acegen, expected, line;
if Length(arg) in [2,3] then
datarec := CallFuncList(ACEDataRecord, arg{[1..Length(arg) - 2]});
ioIndex := datarec.procId;
twArgs := arg{[Length(arg) - 1..Length(arg)]};
if not IsPosInt(twArgs[1]) then
Error("ACETraceWord: coset number must be a positive integer\n");
fi;
else
Error("expected 2 or 3 arguments ... not ",
Length(arg), " arguments\n");
fi;
READ_ACE_ERRORS(datarec); # purge any output not yet collected
if IsOne(twArgs[2]) and twArgs[2] = One( ACEGroupGenerators(ioIndex)[1] ) then
acegen := datarec.acegens[1];
# The ACE binary does not recognise the empty string as the identity
WRITE_LIST_TO_ACE_STREAM(
datarec.stream, [ "tw:", twArgs[1], ",", acegen, "*", acegen, "^-1;" ]);
else
PROCESS_ACE_OPTION(datarec.stream, "tw", twArgs);
fi;
expected := Flat([String(twArgs[1]), " * word = "]){[1..8]};
line := FLUSH_ACE_STREAM_UNTIL(datarec.stream, 3, 3, ACE_READ_NEXT_LINE,
line -> Length(line) > 7 and
line{[1..8]} in [expected,
"* Trace ",
"** ERROR"]);
if IsMatchingSublist(line, expected) then
return Int(SplitString(line, "", " *word=\n")[2]);
elif IsMatchingSublist(line, "* Trace ") then
Info(InfoACE + InfoWarning, 1,
"ACETraceWord:", line{[2..Length(line) - 1]});
return fail;
else
line := Chomp( ACE_READ_NEXT_LINE(datarec.stream) );
Info(InfoACE, 3, line);
Error("ACETraceWord:", line{[3..Length(line)]});
fi;
end);
#############################################################################
####
##
#F ACE_ORDER . . . . . . . . . . . . . . . . . . . . . . . Internal function
## . . . . . . . . . . . . . . . . . . . . called by ACEOrder and ACEOrders
##
##
InstallGlobalFunction(ACE_ORDER, function(ACEfname, ioIndexAndValue)
local lines, line, datarec;
lines := EXEC_ACE_DIRECTIVE_OPTION(
ioIndexAndValue, "order", 3, "", "---------------------", true);
if lines[Length(lines) - 1][1] = '*' then
line := lines[Length(lines) - 1];
Info(InfoACE + InfoWarning, 1, ACEfname, ":", line{[2..Length(line)]});
if ioIndexAndValue[2] > 0 then
return fail;
else
return [];
fi;
elif IsMatchingSublist(lines[Length(lines) - 2], "** ERROR", 1) then
line := lines[Length(lines) - 1];
Error(ACEfname, ":", line{[3..Length(line)]}, "\n",
"(most probably the value passed to ", ACEfname,
"\nwas inappropriate)\n");
else
datarec := ACEData.io[ ioIndexAndValue[1] ];
return List(lines{[First([1..Length(lines)],
i -> IsMatchingSublist(lines[i], "--------")) + 1
.. Length(lines) - 1]},
function(line)
line := SplitString(line, "", "| ");
return rec(coset := Int(line[1]),
order := Int(line[2]),
rep := ACE_GAP_WORDS(datarec, line[3])[1]);
end);
fi;
end);
#############################################################################
####
##
#F ACEOrders . . . . . . . . . . . . . . . . . . . Returns a list of records
## . . . . . . . . . . . . . . . . . . rec(coset := n, order := o, rep := r)
## . . . . . . . . . . . . . . . . . . of all coset numbers whose
## . . . . . . . . . . . . . . . . . . representatives' orders (modulo the
## . . . . . . . . . . . . . . . . . . subgroup) are either finite, or, if
## . . . . . . . . . . . . . . . . . . invoked with the `suborder' option,
## . . . . . . . . . . . . . . . . . . are multiples of the value assigned
## . . . . . . . . . . . . . . . . . . to ` suborder', for the interactive
## . . . . . . . . . . . . . . . . . . . . . . ACE process determined by arg
##
InstallGlobalFunction(ACEOrders, function(arg)
local ioIndex, suborder;
ioIndex := CallFuncList(ACEProcessIndex, arg);
suborder := ValueOption("suborder");
if IsPosInt(suborder) then
return ACE_ORDER("ACEOrders", [ioIndex, -suborder]);
else
if suborder <> fail then
Info(InfoACE + InfoWarning, 1,
"ACEOrders: Expected positive integer value of suborder option");
Info(InfoACE + InfoWarning, 1,
"but received: ", suborder, ". Ignoring ... giving all orders.");
fi;
return ACE_ORDER("ACEOrders", [ioIndex, 0]);
fi;
end);
#############################################################################
####
##
#F ACEOrder . . . . . . . . . . . . . . . . . . . . . . . Returns a record
## . . . . . . . . . . . . . . . . . . rec(coset := n, order := o, rep := r)
## . . . . . . . . . . . . . . . . . . whose representative's order (modulo
## . . . . . . . . . . . . . . . . . . the subgroup) is a multiple of
## . . . . . . . . . . . . . . . . . . suborder, a positive integer, or
## . . . . . . . . . . . . . . . . . . `fail' if there is no such coset
## . . . . . . . . . . . . . . . . . . number, for the i-th interactive ACE
## . . . . . . . . . . . . . . . . . . process, for i, suborder determined
## . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . by arg
##
## Actually, suborder is also allowed to be a negative integer -n, in which
## case, `ACEOrder(i, -n)' is equivalent to `ACEOrders(i : suborder := n)';
## or suborder may be zero, in which case, `ACEOrder(i, 0)' is equivalent to
## `ACEOrders(i)'.
##
InstallGlobalFunction(ACEOrder, function(arg)
local ioIndexAndValue, orderlist;
ioIndexAndValue := ACE_IOINDEX_AND_ONE_VALUE(arg);
orderlist := ACE_ORDER("ACEOrder", ioIndexAndValue);
if IsList(orderlist) and ioIndexAndValue[2] > 0 then
return orderlist[1];
else
return orderlist;
fi;
end);
#############################################################################
####
##
#F ACECosetOrderFromRepresentative( <i>, <cosetrep> )
#F ACECosetOrderFromRepresentative( <cosetrep> )
##
## for the <i>-th (or default) interactive {\ACE} process return the order
## (modulo the subgroup) of the coset with representative <cosetrep> a word
## in the free group generators.
##
## *Note:*
## `ACECosetOrderFromRepresentative' calls `ACETraceWord' to determine the
## coset (number) to which <cosetrep> belongs, and then scans the output of
## `ACEOrders' to determine the order of the coset (number).
##
InstallGlobalFunction(ACECosetOrderFromRepresentative, function(arg)
local ioIndexAndValue, ioIndex, cosetrep, coset, entry;
ioIndexAndValue := ACE_IOINDEX_AND_ONE_VALUE(arg);
ioIndex := ioIndexAndValue[1];
cosetrep := ioIndexAndValue[2];
if IsOne(cosetrep) and cosetrep = One( ACEGroupGenerators(ioIndex)[1] ) then
return 1;
fi;
ACERecover(ioIndex);
coset := ACETraceWord(ioIndex, 1, ioIndexAndValue[2]);
if coset = fail or coset = 1 then
return coset;
fi;
entry := First(ACE_ORDER("ACEOrder", [ioIndex, 0]),
entry -> entry.coset = coset);
if entry = fail then
return fail;
fi;
return entry.order;
end);
#############################################################################
####
##
#F ACECosetsThatNormaliseSubgroup . . . . . . . Determine coset numbers
## . . . . . . . . . . . . . . . . . . . . . . . whose representatives
## . . . . . . . . . . . . . . . . . . . . . . . normalise the subgroup
##
## For the i-th interactive ACE process and n, where i and n are determined
## by arg:
##
## * If n > 0, the list of the first n non-trivial (i.e. excluding coset 1)
## coset numbers whose representatives normalise the subgroup is returned.
## * If n < 0, a list of records with fields `coset' and `rep' which
## represent the coset number and a representative, respectively, of the
## first n non-trivial coset numbers whose representatives normalise the
## subgroup is returned.
## * If n = 0, a list of records with fields `coset' and `rep' which
## represent the coset number and a representative, respectively, of all
## non-trivial coset numbers whose representatives normalise the subgroup
## is returned.
##
InstallGlobalFunction(ACECosetsThatNormaliseSubgroup, function(arg)
local ACEfname, ioIndexAndValue, lines, line, datarec;
ACEfname := "ACECosetsThatNormaliseSubgroup";
ioIndexAndValue := ACE_IOINDEX_AND_ONE_VALUE(arg);
lines := EXEC_ACE_DIRECTIVE_OPTION(
ioIndexAndValue, "sc", 3, "", "---------------------", true);
if Length(lines) > 2 and
IsMatchingSublist(lines[Length(lines) - 2], "** ERROR", 1) then
line := lines[Length(lines) - 1];
Error(ACEfname, ":", line{[3..Length(line)]}, "\n",
"(most probably the value passed to ", ACEfname,
"\nwas inappropriate)\n");
else
if IsMatchingSublist(lines[Length(lines) - 1], "* Nothing found", 1) then
lines := [];
Info(InfoACE + InfoWarning, 1, "no nontrivial normalising cosets found");
else
lines := lines{[First([1..Length(lines)],
i -> IsMatchingSublist(lines[i], "Stabil")) + 1 ..
Length(lines) - 1]};
fi;
if ioIndexAndValue[2] > 0 then
return List(lines, line -> Int( SplitString(line, "", " ")[1] ));
else
datarec := ACEData.io[ ioIndexAndValue[1] ];
return List(lines,
function(line)
line := SplitString(line, "", " ");
return rec(coset := Int(line[1]),
rep := ACE_GAP_WORDS(datarec, line[2])[1]);
end);
fi;
fi;
end);
#############################################################################
##
#F ACECosetTable . . . . . . . . . . . . Extracts the coset table from ACE
##
InstallGlobalFunction(ACECosetTable, function(arg)
local ioIndex, iostream, datarec, fgens, standard, incomplete,
cosettable, errmsg, onbreakmsg, SetACEOptions, DisplayACEOptions;
if Length(arg) = 2 or Length(arg) > 3 then
Error("expected 0, 1 or 3 arguments ... not ", Length(arg), " arguments\n");
elif Length(arg) <= 1 then
# Called as an interactive ACE command
ioIndex := CallFuncList(ACEProcessIndex, arg);
datarec := ACEData.io[ ioIndex ];
INTERACT_SET_ACE_OPTIONS("ACECosetTable", datarec);
if not IsEmpty(OptionsStack) or not IsBound(datarec.stats) then
CHEAPEST_ACE_MODE(datarec);
fi;
standard := ACE_LENLEX_CHK(ioIndex, true);
incomplete := datarec.stats.index = 0 and
DATAREC_VALUE_ACE_OPTION(datarec, false, "incomplete");
if not incomplete and datarec.stats.index = 0 then
Info(InfoACE + InfoWarning, 1,
"The `ACE' coset enumeration failed with the result:");
Info(InfoACE + InfoWarning, 1, datarec.enumResult);
Info(InfoACE + InfoWarning, 1, "Try relaxing any restrictive options.");
Info(InfoACE + InfoWarning, 1, "For interactive ACE process <i>,");
Info(InfoACE + InfoWarning, 1,
"type: 'DisplayACEOptions(<i>);' to see current ACE options.");
return fail;
else
WRITE_LIST_TO_ACE_STREAM(datarec.stream, [ "Print Table;" ]);
cosettable := ACE_COSET_TABLE(datarec.stats.activecosets,
datarec.acegens,
datarec.stream,
ACE_READ_NEXT_LINE);
fi;
else
# Called non-interactively
ACEData.ni := rec();
onbreakmsg := ["Try relaxing any restrictive options",
"e.g. try the `hard' strategy or increasing `workspace'",
"type: '?strategy options' for info on strategies",
"type: '?options for ACE' for info on options",
"type: 'DisplayACEOptions();' to see current ACE options;",
"type: 'SetACEOptions(:<option1> := <value1>, ...);'",
"to set <option1> to <value1> etc.",
"(i.e. pass options after the ':' in the usual way)",
"... and then, type: 'return;' to continue.",
"Otherwise, type: 'quit;' to quit to outer loop."];
SetACEOptions := function()
if not IsEmpty(OptionsStack) and
datarec.optionsStackDepth in [0, Length(OptionsStack)] then
SET_ACE_OPTIONS(datarec);
fi;
end;
DisplayACEOptions := function()
DISPLAY_ACE_REC_FIELD( datarec, "options" );
end;
repeat
datarec :=
CALL_ACE( "ACECosetTableFromGensAndRels", arg[1], arg[2], arg[3] );
standard := ACE_COSET_TABLE_STANDARD( ACE_OPTIONS() );
if IsBound(datarec.infile) then
# User only wanted an ACE input file to use directly with standalone
Info(InfoACE, 1, "ACE standalone input file: ", datarec.infile);
return;
fi;
incomplete := datarec.stats.index = 0 and
VALUE_ACE_OPTION(ACE_OPT_NAMES(), false, "incomplete");
if not incomplete and datarec.stats.index = 0 then
CloseStream(datarec.stream);
if datarec.silent then
return fail;
else
datarec.options := ACE_OPTIONS();
datarec.optionsStackDepth := Length(OptionsStack);
if not IsBound(datarec.origOptionsStackDepth) then
datarec.origOptionsStackDepth := datarec.optionsStackDepth;
fi;
if datarec.optionsStackDepth > 0 then
# We pop options here, in case the user decides to quit
PopOptions();
fi;
errmsg := ["no coset table ...",
"the `ACE' coset enumeration failed with the result:",
datarec.enumResult];
Error(ACE_ERROR(errmsg, onbreakmsg), "\n");
if datarec.options <> rec() then
Add(OptionsStack, datarec.options);
Unbind(datarec.options);
fi;
fi;
else
if IsBound(datarec.cosettable) then
cosettable := datarec.cosettable;
Unbind(datarec.cosettable);
else
WRITE_LIST_TO_ACE_STREAM(datarec.stream, [ "Print Table;" ]);
cosettable := ACE_COSET_TABLE(datarec.stats.activecosets,
datarec.acegens,
datarec.stream,
ACE_READ_NEXT_LINE);
fi;
CloseStream(datarec.stream);
if IsBound(datarec.origOptionsStackDepth) and
(datarec.origOptionsStackDepth = 0) and
not IsEmpty(OptionsStack)
then
PopOptions();
fi;
Unbind(datarec.optionsStackDepth);
Unbind(datarec.origOptionsStackDepth);
break;
fi;
until false;
fi;
if incomplete then
StandardizeTable(cosettable, "lenlex");
Info(InfoACE + InfoWarning, 1,
"ACECosetTable: Coset table is incomplete, reduced ",
"& lenlex standardised.");
elif standard = "semilenlex" then
StandardizeTable(cosettable, "semilenlex");
elif IsMatchingSublist(standard, "GAP") or standard = "semilenlex" then
StandardizeTable(cosettable);
fi;
return cosettable;
end);
#############################################################################
####
##
#F ACEStats . . . Get the subgroup index, time and number of cosets defined
## . . . . . . . . . . during an interactive or non-interactive ACE session
##
InstallGlobalFunction(ACEStats, function(arg)
local datarec, iostream, line, stats;
if Length(arg) <= 1 then
# Called as an interactive ACE command
datarec := CallFuncList(ACEDataRecord, arg);
INTERACT_SET_ACE_OPTIONS("ACEStats", datarec);
if not IsEmpty(OptionsStack) then
CHEAPEST_ACE_MODE(datarec);
fi;
return datarec.stats;
elif Length(arg) = 3 then # args are: fgens, rels, sgens
# Called non-interactively
datarec := CALL_ACE("ACEStats", arg[1], arg[2], arg[3]);
CloseStream( datarec.stream );
return datarec.stats;
else
Error("expected 0, 1 or 3 arguments ... not ", Length(arg), " arguments\n");
fi;
end);
#############################################################################
####
##
#F ACERecover . . . . . . . . . . . . Recover space from dead coset numbers
## . . . . . . . . . . . . . . for interactive ACE process determined by arg
##
InstallGlobalFunction(ACERecover, function(arg)
EXEC_ACE_DIRECTIVE_OPTION(
ACE_IOINDEX_AND_NO_VALUE(arg), "recover", 3,
line -> Length(line) > 1 and line{[1..2]} in ["CO", "co"], "", false);
end);
#############################################################################
####
##
#F ACEStandardCosetNumbering . . Reassigns coset numbers in lenlex standard
## . . . . . . . . . . . . . . . order for interactive ACE process
## . . . . . . . . . . . . . . . . . . . . . . . . . . . . determined by arg
##
InstallGlobalFunction(ACEStandardCosetNumbering, function(arg)
EXEC_ACE_DIRECTIVE_OPTION(
ACE_IOINDEX_AND_NO_VALUE(arg), "standard", 3,
line -> Length(line) > 1 and line{[1..2]} in ["CO", "co"], "", false);
end);
#############################################################################
####
##
#F ACEAddRelators . . . . . . . . . . . . . . . Add relatorlist to relators
## . . . . . . . . . . . . . . . for interactive ACE process and relatorlist
## . . . . . . . . . . . . . . . . . . . . . . . . . . . . determined by arg
##
## Also sets and returns ACEData.io[i].args.rels, where i is the index of
## the interactive ACE process.
##
InstallGlobalFunction(ACEAddRelators, function(arg)
local ioIndexAndOptval, ioIndex, datarec;
ioIndexAndOptval := ACE_IOINDEX_AND_ONE_LIST(arg);
ioIndex := ioIndexAndOptval[1];
datarec := ACEData.io[ ioIndex ];
if not IsBound(datarec.enforceAsis) then
datarec.enforceAsis := false;
fi;
EXEC_ACE_DIRECTIVE_OPTION(
[ ioIndex, ACE_RELS(ioIndexAndOptval[2], # relatorlist
ACEGroupGenerators(ioIndex),
datarec.acegens,
datarec.enforceAsis) ],
"rl", 3, "", "", false
);
CHEAPEST_ACE_MODE(datarec);
return ACE_ARGS(ioIndex, "rels");
end);
#############################################################################
####
##
#F ACEAddSubgroupGenerators . . . . . . . . . Add generatorlist to relators
## . . . . . . . . . . . . . . for interactive ACE process and generatorlist
## . . . . . . . . . . . . . . . . . . . . . . . . . . . . determined by arg
##
## Also sets and returns ACEData.io[i].args.sgens, where i is the index of
## the interactive ACE process.
##
InstallGlobalFunction(ACEAddSubgroupGenerators, function(arg)
local ioIndexAndOptval, ioIndex, datarec;
ioIndexAndOptval := ACE_IOINDEX_AND_ONE_LIST(arg);
ioIndex := ioIndexAndOptval[1];
datarec := ACEData.io[ ioIndex ];
EXEC_ACE_DIRECTIVE_OPTION(
[ ioIndex, ACE_WORDS(ioIndexAndOptval[2], # generatorlist
ACEGroupGenerators(ioIndex),
datarec.acegens) ],
"sg", 3, "", "", false
);
CHEAPEST_ACE_MODE(datarec);
return ACE_ARGS(ioIndex, "sgens");
end);
#############################################################################
####
##
#F ACE_WORDS_OR_UNSORTED . . . . . . . . . . . . . . . . . Internal function
## . . . . . . . . . . . . . . check val is a word list of goodwords, if so
## . . . . . . . . . . . . . . return the sorted list of indices of word
## . . . . . . . . . . . . . . list in goodwords or report that some words
## . . . . . . . . . . . . . . are not of wordtype. If val is an integer
## . . . . . . . . . . . . . . list a sorted integer list is returned.
## . . . . . . . . . . . . . . Otherwise, if val is not a list or not
## . . . . . . . . . . . . . . . . . . . . . . homogeneous, val is returned.
##
InstallGlobalFunction(ACE_WORDS_OR_UNSORTED, function(val, goodwords, wordtype)
local badwords;
if IsList(val) then
if ForAll(val, IsWord) then
badwords := Filtered(val, w -> not(w in goodwords));
if IsEmpty(badwords) then
return SortedList(List(val, w -> Position(goodwords, w)));
else
Error(badwords, " are not ", wordtype, "\n");
fi;
elif ForAll(val, IsInt) then
return SortedList(val);
fi;
fi;
# Let the default error message sort it out
return val;
end);
#############################################################################
####
##
#F ACEDeleteRelators . . . . . . . . . . . Delete relatorlist from relators
## . . . . . . . . . . . . . . . for interactive ACE process and relatorlist
## . . . . . . . . . . . . . . . . . . . . . . . . . . . . determined by arg
##
## Also sets and returns ACEData.io[i].args.rels, where i is the index of
## the interactive ACE process.
##
InstallGlobalFunction(ACEDeleteRelators, function(arg)
local ioIndexAndOptval;
ioIndexAndOptval := ACE_IOINDEX_AND_ONE_LIST(arg);
ioIndexAndOptval[2] := ACE_WORDS_OR_UNSORTED(ioIndexAndOptval[2],
ACERelators(ioIndexAndOptval[1]),
"relators");
EXEC_ACE_DIRECTIVE_OPTION(ioIndexAndOptval, "dr", 3, "", "", false);
CHEAPEST_ACE_MODE(ACEData.io[ ioIndexAndOptval[1] ]);
return ACE_ARGS(ioIndexAndOptval[1], "rels");
end);
#############################################################################
####
##
#F ACEDeleteSubgroupGenerators . . . . . Delete generatorlist from relators
## . . . . . . . . . . . . . . for interactive ACE process and generatorlist
## . . . . . . . . . . . . . . . . . . . . . . . . . . . . determined by arg
##
## Also sets and returns ACEData.io[i].args.sgens, where i is the index of
## the interactive ACE process.
##
InstallGlobalFunction(ACEDeleteSubgroupGenerators, function(arg)
local ioIndexAndOptval;
ioIndexAndOptval := ACE_IOINDEX_AND_ONE_LIST(arg);
ioIndexAndOptval[2] := ACE_WORDS_OR_UNSORTED(ioIndexAndOptval[2],
ACESubgroupGenerators(
ioIndexAndOptval[1]
),
"subgroup generators");
EXEC_ACE_DIRECTIVE_OPTION(ioIndexAndOptval, "ds", 3, "", "", false);
CHEAPEST_ACE_MODE(ACEData.io[ ioIndexAndOptval[1] ]);
return ACE_ARGS(ioIndexAndOptval[1], "sgens");
end);
#############################################################################
####
##
#F ACECosetCoincidence . . . . . . . . . . Force the coincidence of coset n
## . . . . . . . . . . . . . . . . . . . . with coset 1, for the interactive
## . . . . . . . . . . . . . . . . . . . . ACE process i and integer n
## . . . . . . . . . . . . . . . . . . . . . . . . . . . . determined by arg
##
## Essentially, the coset representative of coset n is added to the subgroup
## generators, ACERedo and ACESubgroupGenerators are invoked, and the coset
## representative of coset n is returned.
##
InstallGlobalFunction(ACECosetCoincidence, function(arg)
local ioIndexAndOptval, cosetrep, datarec;
ioIndexAndOptval := ACE_IOINDEX_AND_ONE_VALUE(arg);
cosetrep := EXEC_ACE_DIRECTIVE_OPTION(
ioIndexAndOptval, "cc", 3,
line -> IsMatchingSublist(line, "Coset"), "", false);
if IsMatchingSublist(cosetrep, " ") then
return fail; # Error in input
fi;
datarec := ACEData.io[ ioIndexAndOptval[1] ];
FLUSH_ACE_STREAM_UNTIL(datarec.stream, 3, 3, ACE_READ_NEXT_LINE,
line -> IsMatchingSublist(line, "*"));
CHEAPEST_ACE_MODE(datarec);
ACE_ARGS(ioIndexAndOptval[1], "sgens");
return ACE_GAP_WORDS(
datarec,
cosetrep{[Position(cosetrep, ':') + 2..Length(cosetrep) - 1]}
)[1];
end);
#############################################################################
####
##
#F ACERandomCoincidences( <i>, <subindex> )
#F ACERandomCoincidences( <subindex>)
#F ACERandomCoincidences( <i>, [<subindex>] )
#F ACERandomCoincidences( [<subindex>] )
#F ACERandomCoincidences( <i>, [<subindex>, <attempts>] )
#F ACERandomCoincidences( [<subindex>, <attempts>] )
##
## for the <i>th (or default) interactive {\ACE} process started by
## `ACEStart', attempt up to <attempts> (or, in the first four forms, 8)
## times to find nontrivial subgroups with index a multiple of <subindex> by
## repeatedly making random coset numbers coincident with coset 1 and seeing
## what happens. The starting coset table must be non-empty, but must *not*
## be complete (use `ACERandomlyApplyCosetCoincidence'
## (see~"ACERandomlyApplyCosetCoincidence") if your table is already
## complete). For each attempt, we repeatedly add random coset
## representatives to the subgroup and `redo' the enumeration. If the table
## becomes too small, the attempt is aborted, the original subgroup
## generators restored, and another attempt made. If an attempt succeeds,
## then the new set of subgroup generators is retained.
## `ACERandomCoincidences' returns the list of new subgroup generators
## added. Use `ACESubgroupGenerators' (see~"ACESubgroupGenerators") to
## determine the current subgroup generator list.
##
InstallGlobalFunction(ACERandomCoincidences, function(arg)
local ioIndexAndOptval, datarec, index, sgens, lines, newsgens;
ioIndexAndOptval := ACE_IOINDEX_AND_ONE_VALUE(arg);
datarec := ACEData.io[ ioIndexAndOptval[1] ];
sgens := ACE_ARGS(ioIndexAndOptval[1], "sgens");
READ_ACE_ERRORS(datarec); # purge any output not yet collected
if not IsBound(datarec.stats) then
CHEAPEST_ACE_MODE(datarec);
fi;
index := datarec.stats.index;
if index <> 0 then
Error("ACERandomCoincidences: enumeration index is already finite!\n");
fi;
PROCESS_ACE_OPTION(datarec.stream, "rc", ioIndexAndOptval[2]);
# Perhaps it's wasteful to use ACEReadUntil here ...
lines := ACEReadUntil(ioIndexAndOptval[1],
line -> Length(line)>12 and
line{[1..13]} in ["* No success;",
"* An appropri",
" finite ind",
" * Unable t"]);
if IsMatchingSublist(lines[Length(lines)], "* An appropri", 1) then
datarec.enumResult := lines[Length(lines) - 1];
datarec.stats := ACE_STATS(datarec.enumResult);
else
Info(InfoACE + InfoWarning, 1, "ACERandomCoincidences: Unsuccessful!");
newsgens := Difference(ACE_ARGS(ioIndexAndOptval[1], "sgens"), sgens);
if not IsEmpty(newsgens) then
ACEDeleteSubgroupGenerators(ioIndexAndOptval[1], newsgens);
Info(InfoACE + InfoWarning, 1, "Subgroup generators restored.");
fi;
fi;
return Difference(ACE_ARGS(ioIndexAndOptval[1], "sgens"), sgens);
end);
#############################################################################
####
##
#F ACERandomlyApplyCosetCoincidence( <i> [: subindex := <subindex>,
## hibound := <hibound>,
## lobound := <lobound>,
## attempts := <attempts>] )
#F ACERandomlyApplyCosetCoincidence( [: subindex := <subindex>,
## hibound := <hibound>,
## lobound := <lobound>,
## attempts := <attempts>] )
##
## for the <i>th (or default) interactive {\ACE} process started by
## `ACEStart', attempt up to <attempts> (or, by default, 8) times to find a
## larger proper subgroup, by repeatedly applying `ACECosetCoincidence'
## (see~"ACECosetCoincidence") and seeing what happens. The starting coset
## table must already be complete (use `ACERandomCoincidences'
## (see~"ACERandomCoincidences") if your table is not already complete). By
## default, `<subindex> = 1', <hibound> is the existing subgroup index and
## `<lobound> = 1'. If after an attempt the new index is a multiple of
## <subindex>, less than <hibound> and greater than <lobound> then the the
## process terminates and the list of new subgroup representatives is
## returned. Otherwise, if an attempt reaches a stage where the criteria
## cannot be satisfied, the attempt is aborted, the original subgroup
## generators restored, and another attempt made. Use
## `ACESubgroupGenerators' (see~"ACESubgroupGenerators") to determine the
## current subgroup generator list.
##
InstallGlobalFunction(ACERandomlyApplyCosetCoincidence, function(arg)
local ioIndex, datarec, index, opt, sgens, try, trycosetrep, tries, newsgens;
ioIndex := CallFuncList(ACEProcessIndex, arg);
datarec := ACEData.io[ ioIndex ];
index := ACEStats(ioIndex).index;
if index = 0 then
Error("ACERandomlyApplyCosetCoincidence: coset table must be complete.\n");
fi;
opt := rec();
if ACE_VALUE_OPTION_ERROR(
opt, "subindex", 1, d -> IsPosInt(d) and (index mod d = 0),
"option `subindex' must be a positive divisor of current index"
) or
ACE_VALUE_OPTION_ERROR(
opt, "hibound", index, h -> 1 < h and h <= index,
"option `hibound' must be > 1 and at most the current index"
) or
ACE_VALUE_OPTION_ERROR(
opt, "lobound", 1, lo -> 1 <= lo and lo < index,
"option `lobound' must be at least 1 and less than the current index"
) or
ACE_VALUE_OPTION_ERROR(
opt, "attempts", 8, IsPosInt,
"option `attempts' must be a positive integer"
)
then
opt.onbreakmsg := ["You can only 'quit;' from here."];
PopOptions();
Error(ACE_ERROR(opt.errmsg, opt.onbreakmsg), "\n");
fi;
if opt.attempts > index - 1 then
opt.attempts := index - 1;
fi;
sgens := ACESubgroupGenerators(ioIndex);
tries := [];
newsgens := [];
ACERecover(ioIndex);
while Length(tries) < opt.attempts and (datarec.stats.index >= opt.hibound) do
repeat
try := Random([2 .. datarec.stats.index]);
trycosetrep := ACECosetRepresentative(ioIndex, try);
until not(trycosetrep in tries);
Add(tries, try);
Add(newsgens, ACECosetCoincidence(ioIndex, try));
Info(InfoACE, 1, "Added new subgroup gen'r:");
Info(InfoACE, 1, " ", newsgens[ Length(newsgens) ]);
if datarec.stats.index <= opt.lobound or
(datarec.stats.index mod opt.subindex <> 0) then
# abort
Info(InfoACE, 1, "Subgroup index (", datarec.stats.index, ") ",
"has become too small ...");
Info(InfoACE, 1, "restoring original subgroup gen'rs.");
ACEDeleteSubgroupGenerators(ioIndex, newsgens);
newsgens := [];
else
Info(InfoACE, 1, "New subgroup index = ", datarec.stats.index);
fi;
ACERecover(ioIndex);
od;
if ACEStats(ioIndex).index >= opt.hibound then
Info(InfoACE, 1, "ACERandomlyApplyCosetCoincidence: Unsuccessful!");
fi;
return Difference(ACESubgroupGenerators(ioIndex), sgens);
end);
#############################################################################
####
##
#F ACEConjugatesForSubgroupNormalClosure . . Returns conjugates of subgroup
## . . . . . . . . . . . . . . . . . . . . . generators by generators (that
## . . . . . . . . . . . . . . . . . . . . . can be determined to be)
## . . . . . . . . . . . . . . . . . . . . . needed for normal closure of
## . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . the subgroup
##
## Tests that each conjugate of a subgroup generator by a group generator
## can be traced from coset 1 to a coset number other than coset 1, for the
## i-th interactive ACE process, where i is determined by arg. The list of
## conjugates that were determined to belong to cosets other than coset 1
## (the subgroup) is returned; and, if called with the `add' option, these
## conjugates are also added to the existing list of subgroup generators.
##
InstallGlobalFunction(ACEConjugatesForSubgroupNormalClosure, function(arg)
local ACEfname, ioIndex, add, lines, line, datarec;
ACEfname := "ACEConjugatesForSubgroupNormalClosure";
ioIndex := CallFuncList(ACEProcessIndex, arg);
add := ValueOption("add");
if not IsBool(add) then
Info(InfoACE + InfoWarning, 1,
ACEfname, ": Expected boolean value of add option");
Info(InfoACE + InfoWarning, 1,
"but received: ", add, ". Ignoring ... no new generators will be.");
Info(InfoACE + InfoWarning, 1,
"added to the subgroup");
add := "";
elif add <> true then
add := "";
else
add := 1;
fi;
lines := EXEC_ACE_DIRECTIVE_OPTION(
[ioIndex, add], "nc", 3, "", "---------------------", true);
if lines[Length(lines) - 1] = "* All (traceable) conjugates in subgroup" then
Info(InfoACE + InfoWarning, 1,
ACEfname, ": All (traceable) conjugates in subgp");
return [];
elif IsMatchingSublist(lines[Length(lines) - 2], "** ERROR", 1) then
line := lines[Length(lines) - 1];
Error(ACEfname, ":", line{[3..Length(line)]}, "\n",
"(most probably the value passed to ", ACEfname,
"\nwas inappropriate)\n");
else
datarec := ACEData.io[ ioIndex ];
if add = 1 then
CHEAPEST_ACE_MODE(datarec);
ACE_ARGS(ioIndex, "sgens"); # Update saved subgroup generators
fi;
return List(Filtered(lines,
line -> IsMatchingSublist(line, "Conjugate by grp") or
# in case we have an old src for ACE
IsMatchingSublist(line, "Grp")),
function(line)
line := SplitString(line, '"');
return ACE_GAP_WORDS(datarec, line[4])[1]
^ ACE_GAP_WORDS(datarec, line[2])[1];
end);
fi;
end);
#E interact.gi . . . . . . . . . . . . . . . . . . . . . . . . . ends here