############################################################################# ## #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) > 1then
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)
############################################################################# #### ## #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)
############################################################################# #### ## #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 + 1then
Error("expected 1 or 2 function arguments, not ",
Length(arg) - idx1stfn + 1, "\n"); elif idx1stfn > 2then
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, "", " .");
############################################################################# #### ## #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);
if Length(arg) > 5then
Error("expected at most 5 arguments ... not ", Length(arg), " arguments.\n"); elif Length(arg) = 2 and arg[1] <> 0then
Error("when called with 2 arguments, first argument should be 0.\n"); elif not IsEmpty(arg) and arg[1] = 0then
start := false;
arg := arg{[2..Length(arg)]}; else
start := true; fi;
if Length(arg) in [3, 4] then if Length(arg) = 3then#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) = 3then
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) = 1then
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;
############################################################################# ## #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;
############################################################################# #### ## #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;
############################################################################# #### ## #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", "------------------------------------");
############################################################################# #### ## #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 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);
if Length(arg) > 2then
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) <= 1then
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 < 0then
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;
############################################################################# #### ## #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;
############################################################################# #### ## #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] > 0then
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 = 1then
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] > 0then
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);
if Length(arg) = 2 or Length(arg) > 3then
Error("expected 0, 1 or 3 arguments ... not ", Length(arg), " arguments\n"); elif Length(arg) <= 1then # 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 = 0then
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;
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 = 0then
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 > 0then # 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) <= 1then # 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) = 3then# 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 <> 0then
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 = 0then
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 - 1then
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 = 1then
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);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.