|
#############################################################################
##
#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);
--> --------------------
--> maximum size reached
--> --------------------
[ Verzeichnis aufwärts0.53unsichere Verbindung
Übersetzung europäischer Sprachen durch Browser
]
|