Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  interact.gi   Sprache: unbekannt

 
#############################################################################
##
#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

--> --------------------

[ Dauer der Verarbeitung: 0.55 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge