GAP 4.8.9 installation with standard packages -- copy to your CoCalc project to get it
#############################################################################
####
##
#W anupqios.gi ANUPQ package Greg Gamble
##
## This file installs core functions used with iostreams.
##
#Y Copyright (C) 2001 Lehrstuhl D fuer Mathematik, RWTH Aachen, Germany
##
#############################################################################
##
#F PQ_START( <workspace>, <setupfile> ) . . . open a stream for a pq process
##
## ensures the images file written by the `pq' binary when in the Standard
## Presentation menu is empty, opens an io stream to a `pq' process (if
## <setupfile> is `fail') or a file stream for a setup file (if <setupfile>
## is a filename i.e. a string) and returns a record with fields `menu'
## (current menu for the `pq' binary), `opts' (the runtime switches used by
## the `pq' process), `workspace' (the value of <workspace> which should be
## a positive integer), and `stream' (the io or file stream opened).
##
InstallGlobalFunction(PQ_START, function( workspace, setupfile )
local opts, iorec, topqlogfile;
PrintTo(ANUPQData.SPimages, ""); #to ensure it's empty
if setupfile = fail then
opts := [ "-G" ];
else
opts := [ "-i", "-k", "-g" ];
fi;
if workspace <> 10000000 then
Append( opts, [ "-s", String(workspace) ] );
fi;
iorec := rec( menu := "SP",
opts := opts,
workspace := workspace );
if setupfile = fail then
iorec.stream := InputOutputLocalProcess( ANUPQData.tmpdir,
ANUPQData.binary,
opts );
if iorec.stream = fail then
Error( "failed to launch child process" );
fi;
# menus are flushed at InfoANUPQ level 6, prompts at level 5
FLUSH_PQ_STREAM_UNTIL(iorec.stream, 6, 5, PQ_READ_NEXT_LINE, IS_PQ_PROMPT);
else
iorec.stream := OutputTextFile(setupfile, false);
iorec.setupfile := setupfile;
ToPQk(iorec, [], [ "#call pq with flags: '",
JoinStringsWithSeparator(opts, " "),
"'" ]);
fi;
return iorec;
end );
#############################################################################
##
#F PqStart(<G>,<workspace> : <options>) . Initiate interactive ANUPQ session
#F PqStart(<G> : <options>)
#F PqStart(<workspace> : <options>)
#F PqStart( : <options>)
##
## activate an iostream for an interactive {\ANUPQ} process (i.e. `PqStart'
## starts up a `pq' binary process and opens a {\GAP} iostream to ``talk''
## to that process) and returns an integer <i> that can be used to identify
## that process. The argument <G>, if given, should be an *fp group* or *pc
## group* that the user intends to manipute using interactive {\ANUPQ}
## functions. If `PqStart' is given an integer argument <workspace> then the
## `pq' binary is started up with a workspace (an integer array) of size
## <workspace> (i.e. $4 \times <workspace>$ bytes in a 32-bit environment);
## otherwise, the `pq' binary sets a default workspace of $10000000$.
##
## The only <options> currently recognised by `PqStart' are `Prime' and
## `Exponent' (see~"Pq" for details) and if provided they are essentially
## global for the interactive {\ANUPQ} process, except that any interactive
## function interacting with the process and passing new values for these
## options will over-ride the global values.
##
InstallGlobalFunction(PqStart, function(arg)
local opts, iorec, procId, G, workspace, optname;
if 2 < Length(arg) then
Error("at most two arguments expected.\n");
fi;
if not IsEmpty(arg) and IsGroup( arg[1] ) then
G := arg[1];
if not( IsFpGroup(G) or IsPcGroup(G) ) then
Error( "argument <G> should be an fp group or a pc group\n" );
fi;
arg := arg{[2 .. Length(arg)]};
fi;
if not IsEmpty(arg) then
workspace := arg[1];
if not IsPosInt(workspace) then
Error("argument <workspace> should be a positive integer.\n");
fi;
else
workspace := 10000000;
fi;
iorec := PQ_START( workspace, fail );
if IsBound( G ) then
iorec.group := G;
fi;
iorec.calltype := "interactive";
for optname in ANUPQGlobalOptions do
VALUE_PQ_OPTION(optname, iorec);
od;
procId := Length(ANUPQData.io) + 1;
iorec.procId := procId;
ANUPQData.io[ procId ] := iorec;
return procId;
end);
#############################################################################
##
#F PqQuit( <i> ) . . . . . . . . . . . . Close an interactive ANUPQ session
#F PqQuit()
##
## closes the stream of the <i>th or default interactive {\ANUPQ} process
## and unbinds its `ANUPQData.io' record.
##
InstallGlobalFunction(PqQuit, function(arg)
local ioIndex;
ioIndex := ANUPQ_IOINDEX(arg);
# No need to bother about descending through the menus.
CloseStream(ANUPQData.io[ioIndex].stream);
Unbind(ANUPQData.io[ioIndex]);
end);
#############################################################################
##
#F PqQuitAll() . . . . . . . . . . . . Close all interactive ANUPQ sessions
##
## closes the streams of all active interactive {\ANUPQ} process and unbinds
## their `ANUPQData.io' records.
##
InstallGlobalFunction(PqQuitAll, function()
local ioIndex;
for ioIndex in [1 .. Length(ANUPQData.io)] do
if IsBound(ANUPQData.io[ioIndex]) then
CloseStream(ANUPQData.io[ioIndex].stream);
Unbind(ANUPQData.io[ioIndex]);
fi;
od;
end);
#############################################################################
##
#F ANUPQ_IOINDEX . . . . the number identifying an interactive ANUPQ session
##
## returns the index of the record in the `ANUPQData.io' list corresponding
## to an interactive {\ANUPQ} session. With no argument the first bound
## index in `ANUPQData.io' is returned. With integer (first) argument <i>,
## <i> is returned if `ANUPQData.io[<i>]' is bound.
##
InstallGlobalFunction(ANUPQ_IOINDEX, function(arglist)
local ioIndex;
if IsEmpty(arglist) then
# Find the first bound ioIndex
ioIndex := 1;
while not(IsBound(ANUPQData.io[ioIndex])) and
ioIndex < Length(ANUPQData.io) do
ioIndex := ioIndex + 1;
od;
if IsBound(ANUPQData.io[ioIndex]) then
return ioIndex;
else
Info(InfoANUPQ + InfoWarning, 1,
"No interactive ANUPQ sessions are currently active");
return fail;
fi;
elif IsBound(ANUPQData.io[ arglist[1] ]) then
return arglist[1];
else
Error("no such interactive ANUPQ session\n");
fi;
end);
#############################################################################
##
#F ANUPQ_IOINDEX_ARG_CHK . Checks ANUPQ_IOINDEX has the right no. of arg'ts
##
InstallGlobalFunction(ANUPQ_IOINDEX_ARG_CHK, function(arglist)
if Length(arglist) > 1 then
Info(InfoANUPQ + InfoWarning, 1,
"Expected 0 or 1 arguments, all but first argument ignored");
fi;
end);
#############################################################################
##
#F ANUPQDataRecord([<i>]) . . . . . . . returns the data record of a process
##
InstallGlobalFunction(ANUPQDataRecord, function( arg )
if not IsEmpty(arg) and arg[1] = 0 and IsBound( ANUPQData.ni ) then
return ANUPQData.ni;
else
return ANUPQData.io[ CallFuncList(PqProcessIndex, arg) ];
fi;
end);
#############################################################################
##
#F PqProcessIndex( <i> ) . . . . . . . . . . . User version of ANUPQ_IOINDEX
#F PqProcessIndex()
##
## If given (at least) one integer argument `PqProcessIndex' returns its
## first argument if it corresponds to an active interactive process or
## raises an error; otherwise, with no arguments, 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 to `Info' at `InfoANUPQ' or `InfoWarning' level 1).
##
InstallGlobalFunction(PqProcessIndex, function(arg)
ANUPQ_IOINDEX_ARG_CHK(arg);
return ANUPQ_IOINDEX(arg);
end);
#############################################################################
##
#F PqProcessIndices() . . . . the list of active interactive ANUPQ processes
##
## returns the list of (integer) indices of all active interactive {\ANUPQ}
## processes.
##
InstallGlobalFunction(PqProcessIndices, function()
return Filtered( [1..Length(ANUPQData.io)], i -> IsBound( ANUPQData.io[i] ) );
end);
#############################################################################
##
#F IsPqProcessAlive( <i> ) . . checks an interactive ANUPQ process iostream
#F IsPqProcessAlive()
##
## return `true' if the {\GAP} iostream of the <i>th (or default)
## interactive {\ANUPQ} process is alive (i.e. can still be written to), or
## `false', otherwise.
##
InstallGlobalFunction(IsPqProcessAlive, function(arg)
return not IsEndOfStream( ANUPQData.io[ PqProcessIndex(arg) ].stream );
end);
#############################################################################
##
#V PQ_MENUS . . . . . . . . . . . data describing the menus of the pq binary
##
## a record whose fields are abbreviated names of the menus of the `pq'
## binary and whose values are themselves records with fields:
##
## name
## long name of menu;
## depth
## the number of times 0 must be passed to the `pq' binary for it to
## exit;
## prev
## the menu one gets to from the current menu via option 0 (or `""' in
## the case of the menu `SP';
## nextopt
## a record whose fields are the new menus of greater depth that can
## be reached by an option of the current menu, and whose values are
## the corresponding numbers of the options of the current menu needed
## to get to the new menus.
##
InstallValue(PQ_MENUS, rec(
SP := rec( name := "Standard Presentation Menu",
depth := 1, prev := "", nextopt := rec( pQ := 7 ) ),
pQ := rec( name := "(Main) p-Quotient Menu",
depth := 2, prev := "SP", nextopt := rec( pG := 9, ApQ := 8 ) ),
pG := rec( name := "(Main) p-Group Generation Menu",
depth := 3, prev := "pQ", nextopt := rec( ApG := 6 ) ),
ApQ := rec( name := "Advanced p-Quotient Menu",
depth := 3, prev := "pQ", nextopt := rec() ),
ApG := rec( name := "Advanced p-Group Gen'n Menu",
depth := 4, prev := "pG", nextopt := rec() )
) );
#############################################################################
##
#F PQ_MENU( <datarec>, <newmenu> ) . . . . . . change/get menu of pq process
#F PQ_MENU( <datarec> )
##
InstallGlobalFunction(PQ_MENU, function(arg)
local datarec, newmenu, nextmenu, tomenu, infolev;
datarec := arg[1];
if 2 = Length(arg) then
newmenu := arg[2];
if datarec.menu in ["SP", "pQ"] and newmenu in ["ApQ", "pG", "ApG"] then
PQ_GRP_EXISTS_CHK( datarec ); #We try to avoid seg-faults!
fi;
while datarec.menu <> newmenu do
if PQ_MENUS.(datarec.menu).depth >= PQ_MENUS.(newmenu).depth then
datarec.menu := PQ_MENUS.(datarec.menu).prev;
tomenu := PQ_MENUS.(datarec.menu).name;
ToPQk(datarec, [ 0 ], [ " #to ", tomenu]);
infolev := 5;
elif datarec.menu = "pQ" and newmenu = "ApQ" then
datarec.menu := "ApQ";
tomenu := PQ_MENUS.(datarec.menu).name;
ToPQk(datarec, [ PQ_MENUS.pQ.nextopt.ApQ ], [ " #to ", tomenu ]);
infolev := 6;
else
nextmenu := RecNames( PQ_MENUS.(datarec.menu).nextopt )[1];
tomenu := PQ_MENUS.(nextmenu).name;
ToPQk(datarec, [ PQ_MENUS.(datarec.menu).nextopt.(nextmenu) ],
[ " #to ", tomenu ]);
datarec.menu := nextmenu;
infolev := 6;
fi;
# menus are flushed at InfoANUPQ level 6, prompts at level 5
if not IsBound(datarec.setupfile) then
FLUSH_PQ_STREAM_UNTIL(datarec.stream, infolev, 5, PQ_READ_NEXT_LINE,
IS_PQ_PROMPT);
fi;
od;
fi;
return datarec.menu;
end);
#############################################################################
##
#F IS_PQ_PROMPT( <line> ) . . . . checks whether the line is a prompt of pq
##
## returns `true' if the string <line> is a `pq' prompt, or otherwise
## returns `false'.
##
InstallGlobalFunction(IS_PQ_PROMPT,
line -> IS_ALL_PQ_LINE(line) and ANUPQData.linetype = "prompt"
);
#############################################################################
##
#F IS_ALL_PQ_LINE( <line> ) . checks whether line is a complete line from pq
##
## returns `true' if the string <line> is a `pq' prompt or a request from
## `pq' to {\GAP} to compute stabilisers or simply ends in a newline and
## sets `ANUPQData.linetype' to `"prompt"', `"request"' or `"hasnewline"',
## accordingly; otherwise `ANUPQData.linetype' is set to `"unknown"' and
## `false' is returned.
##
InstallGlobalFunction(IS_ALL_PQ_LINE, function( line )
local len;
ANUPQData.linetype := "unknown";
len := Length(line);
if 0 < len then
if line[len] = '\n' then
if 4 < len and line{[1 .. 3]} = "GAP" and line[len - 1] = '!' then
ANUPQData.linetype := "request";
elif 6 < len and line{[1 .. 6]} in ["Enter ", "Input "] then
ANUPQData.linetype := "prompt";
else
ANUPQData.linetype := "hasnewline";
fi;
elif line = "Select option: " or
1 < len and line{[len - 1 .. len]} = "? " or
8 < len and line{[len - 1 .. len]} = ": " and
line{[1 .. 6]} in ["Enter ", "Input ", "Add ne"] then
ANUPQData.linetype := "prompt";
fi;
fi;
return ANUPQData.linetype <> "unknown";
end);
#############################################################################
##
#F PQ_READ_ALL_LINE( <iostream> ) . read line from pq but poss. return fail
##
## reads a complete line from <iostream> or return `fail'.
##
InstallGlobalFunction(PQ_READ_ALL_LINE,
iostream -> ReadAllLine(iostream, false, IS_ALL_PQ_LINE)
);
#############################################################################
##
#F PQ_READ_NEXT_LINE( <iostream> ) . read line from pq but never return fail
##
## Essentially, like `PQ_READ_ALL_LINE' but we know there is a complete line
## to be got, so we wait for it, before returning.
##
InstallGlobalFunction(PQ_READ_NEXT_LINE,
iostream -> ReadAllLine(iostream, true, IS_ALL_PQ_LINE)
);
#############################################################################
##
#F FLUSH_PQ_STREAM_UNTIL(<stream>,<infoLev>,<infoLevMy>,<readln>,<IsMyLine>)
## . . . . . . . . . . . . . . read lines from a stream until a wanted line
##
## calls <readln> (which should be one of `ReadLine', `PQ_READ_NEXT_LINE' or
## `PQ_READ_ALL_LINE') to read lines from a stream <stream> and `Info's each
## line read at `InfoANUPQ' level <infoLev> until a line <line> is read for
## which `<IsMyLine>(<line>)' is `true'; <line> is `Info'-ed at `InfoANUPQ'
## level <infoLevMy> and returned. <IsMyLine> should be a boolean-valued
## function that expects a string as its only argument, and <infoLev> and
## <infoLevMy> should be positive integers. An <infoLevMy> of 10 means that
## the line <line> matched by `<IsMyLine>(<line>)' should never be
## `Info'-ed.
##
InstallGlobalFunction(FLUSH_PQ_STREAM_UNTIL,
function(stream, infoLev, infoLevMy, readln, IsMyLine)
local line;
line := readln(stream);
while not IsMyLine(line) do
Info(InfoANUPQ, infoLev, Chomp(line));
line := readln(stream);
od;
if line <> fail and infoLevMy < 10 then
Info(InfoANUPQ, infoLevMy, Chomp(line));
fi;
return line;
end);
#############################################################################
##
#V PQ_ERROR_EXIT_MESSAGES . . . error messages emitted by the pq before exit
##
## A list of the error messages the `pq' emits just before exiting.
##
InstallValue(PQ_ERROR_EXIT_MESSAGES,
[ "Evaluation in compute_degree may cause integer overflow",
"A relation is too long -- increase the value of MAXWORD",
"Ran out of space during computation" ]);
#############################################################################
##
#F FILTER_PQ_STREAM_UNTIL_PROMPT( <datarec> )
##
## reads `pq' output from `<datarec>.stream' until a `pq' prompt and `Info's
## any lines that are prompts, blank lines, menu exits or start with the
## strings in the list `<datarec>.filter' (if bound) at `InfoANUPQ' level 5;
## all other lines are either `Info'-ed at `InfoANUPQ' level 3 if
## `datarec.nonuser' is set, or, more usually, are `Info'-ed at `InfoANUPQ'
## level 2 if they are computation times or at `InfoANUPQ' level 1,
## otherwise.
##
InstallGlobalFunction(FILTER_PQ_STREAM_UNTIL_PROMPT, function( datarec )
local match, filter, lowlev, ctimelev;
filter := ["Exiting", "pq,", "Now enter",
"Presentation listing images", "(use generators x1,x2"];
if IsBound(datarec.match) then
if datarec.match = true then
match := ["Group:", "Group completed"];
else
match := [datarec.match];
fi;
fi;
if IsBound(datarec.filter) then
Append(filter, datarec.filter);
fi;
if ValueOption("nonuser") = true then
lowlev := 3;
ctimelev := 3;
else
ctimelev := 2;
if not IsBound(datarec.OutputLevel) or datarec.OutputLevel = 0 then
lowlev := 3;
else
lowlev := 1;
fi;
fi;
repeat
datarec.line := PQ_READ_NEXT_LINE(datarec.stream);
if ANUPQData.linetype in ["prompt", "request"] then
Info( InfoANUPQ, 5, Chomp(datarec.line) );
break;
elif ForAny(["seconds", "Lused", "*** Final "],
s -> PositionSublist(datarec.line, s) <> fail) then
Info( InfoANUPQ, ctimelev, Chomp(datarec.line) );
elif datarec.line = "\n" or
ForAny( filter, s -> IsMatchingSublist(datarec.line, s) ) then
Info( InfoANUPQ, 5, Chomp(datarec.line) );
elif PositionSublist(datarec.line, " saved on file") <> fail then
Info( InfoANUPQ, ctimelev, Chomp(datarec.line) );
elif ForAny( PQ_ERROR_EXIT_MESSAGES,
s -> IsMatchingSublist(datarec.line, s) ) then
Info( InfoANUPQ + InfoWarning, 1, Chomp(datarec.line) );
Error( "pq program terminated, with error condition:\n ", datarec.line );
else
Info( InfoANUPQ, lowlev, Chomp(datarec.line) );
fi;
if IsBound(match) then
if ForAny( match, s -> IsMatchingSublist(datarec.line, s) ) then
datarec.matchedline := datarec.line;
datarec.complete := IsBound(datarec.complete) and datarec.complete or
IsMatchingSublist(datarec.line, "Group completed");
fi;
elif IsBound(datarec.matchlist) and
ForAny( datarec.matchlist,
s -> PositionSublist(datarec.line, s) <> fail ) then
Add(datarec.matchedlines, datarec.line);
fi;
until false;
end);
#############################################################################
##
#F ToPQk( <datarec>, <cmd>, <comment> ) . . . . . . . writes to a pq stream
##
## writes <cmd> (and <comment>, in setup file case) to stream
## `<datarec>.stream' and `Info's <cmd> and <comment> at `InfoANUPQ' level 3
## after a ```ToPQ> ''' prompt, and returns `true' if successful and `fail'
## otherwise. The ``k'' at the end of the function name is mnemonic for
## ``keyword'' (for ``keyword'' inputs to the `pq' binary one never wants to
## flush output).
##
InstallGlobalFunction(ToPQk, function(datarec, cmd, comment)
local ok, line, i, j, closed, fragment, sepchars, words, filterones;
if not IsOutputTextStream(datarec.stream) and
IsEndOfStream(datarec.stream) then
Error("sorry! Process stream has died!\n");
fi;
if cmd in ["gens", "rels"] then
# these are done specially because of their potential to be enormously long
if cmd = "gens" then
line := "generators { ";
sepchars := ", ";
else
line := "relators { ";
sepchars := "*^, ";
fi;
words := datarec.(cmd);
filterones := cmd = "rels" and not IsBound(datarec.Relators) and
(IsFpGroup(datarec.group) or not IsPGroup(datarec.group));
i := 1;
while filterones and i <= Length(words) and IsOne(words[i]) do
i := i + 1;
od;
if i <= Length(words) then
Append(line, String(words[i]));
i := i + 1;
fi;
ok := true;
closed := false;
repeat
while filterones and i <= Length(words) and IsOne(words[i]) do
i := i + 1;
od;
# i is the index of the next word to be added to line or > #words
if i <= Length(words) then
# if number of non-trivial words is 0 or 1 no comma is ever added
Append(line, ", ");
Append(line, String(words[i]));
i := i + 1;
else
Append(line, " }");
if cmd = "rels" then
Append(line, ";");
fi;
closed := true; # not quite equivalent to: i > Length(words)
fi;
while ok and (Length(line) >= 69 or (closed and Length(line) > 0)) do
if Length(line) >= 69 then
# find a nice break if we can
j := 68;
while j > 4 and not line[j] in sepchars do j := j - 1; od;
# no nice break
if j = 4 then
j := 69;
while j < Length(line) and not line[j] in sepchars do
j := j + 1;
od;
fi;
fragment := line{[1 .. j]};
else
fragment := line;
j := Length(line);
fi;
if j = Length(line) and closed then
line := "";
else
line := Concatenation(" ", line{[j + 1 .. Length(line)]});
fi;
Info(InfoANUPQ, 4, "ToPQ> ", fragment);
if IsBound( datarec.setupfile) then
ok := WriteLine(datarec.stream, fragment);
else
ok := WriteLine(datarec.stream, fragment);
if IsBound( ANUPQData.topqlogfile ) then
WriteLine(ANUPQData.logstream, fragment);
fi;
fi;
od;
until closed or not ok;
else
# We add a null string in case <cmd> or <comment> is []
# ... so that `Concatenation( List(., String) );' statements return strings
Add(cmd, "");
Add(comment, "");
cmd := Concatenation( List(cmd, String) );
comment := Concatenation( List(comment, String) );
Info(InfoANUPQ, 4, "ToPQ> ", cmd, comment);
if IsBound( datarec.setupfile) then
ok := WriteLine(datarec.stream, Concatenation(cmd, comment));
else
ok := WriteLine(datarec.stream, cmd);
if IsBound( ANUPQData.topqlogfile ) then
WriteLine(ANUPQData.logstream, Concatenation(cmd, comment));
fi;
fi;
fi;
if ok = fail then
Error("write to stream failed\n");
fi;
return ok;
end);
#############################################################################
##
#F ToPQ(<datarec>, <cmd>, <comment>) . . write to pq (& for iostream flush)
##
## calls `ToPQk' to write <cmd> (and <comment>, in setup file case) to
## stream `<datarec>.stream' and `Info' <cmd> and <comment> at `InfoANUPQ'
## level 3 after a ```ToPQ> ''' prompt, and then, if we are not just writing
## a setup file (determined by checking whether `<datarec>.setupfile' is
## bound), calls `FILTER_PQ_STREAM_UNTIL_PROMPT' to filter lines to `Info'
## at the various `InfoANUPQ' levels. If we are not writing a setup file the
## last line flushed is saved in `<datarec>.line'.
##
InstallGlobalFunction(ToPQ, function(datarec, cmd, comment)
ToPQk(datarec, cmd, comment);
if not IsBound( datarec.setupfile ) then
FILTER_PQ_STREAM_UNTIL_PROMPT(datarec);
while ANUPQData.linetype = "request" do
HideGlobalVariables( "ANUPQglb", "F", "gens", "relativeOrders",
"ANUPQsize", "ANUPQagsize" );
Read( Filename( ANUPQData.tmpdir, "GAP_input" ) );
Read( Filename( ANUPQData.tmpdir, "GAP_rep" ) );
UnhideGlobalVariables( "ANUPQglb", "F", "gens", "relativeOrders",
"ANUPQsize", "ANUPQagsize" );
ToPQk( datarec, [ "pq, stabiliser is ready!" ], [] );
FILTER_PQ_STREAM_UNTIL_PROMPT(datarec);
od;
fi;
end);
#############################################################################
##
#F ToPQ_BOOL( <datarec>, <optval>, <comment> ) . . . . pass a boolean to pq
##
## converts a {\GAP} boolean <optval> to a C boolean and appends the
## appropriate adjustment to the string <comment> before calling `ToPQ' (we
## assume that <optval> is boolean ... `VALUE_PQ_OPTION' should already have
## checked that).
##
InstallGlobalFunction( ToPQ_BOOL, function( datarec, optval, comment )
if optval = true then
ToPQ( datarec, [ 1 ], [ " #do ", comment ] );
else
ToPQ( datarec, [ 0 ], [ " #do not ", comment ] );
fi;
end);
#############################################################################
##
#F PqRead( <i> ) . . . primitive read of a single line from ANUPQ iostream
#F PqRead()
##
## read a complete line of {\ANUPQ} output, from the <i>th or default
## interactive {\ANUPQ} process, if there is output to be read and returns
## `fail' otherwise. When successful, the line is returned as a string
## complete with trailing newline, colon, or question-mark character. Please
## note that it is possible to be ``too quick'' (i.e.~the return can be
## `fail' purely because the output from {\ANUPQ} is not there yet), but if
## `PqRead' finds any output at all, it waits for a complete line. `PqRead'
## also writes the line read via `Info' at `InfoANUPQ' level 2. It doesn't
## try to distinguish banner and menu output from other output of the `pq'
## binary.
##
InstallGlobalFunction(PqRead, function(arg)
local line;
line := PQ_READ_ALL_LINE( ANUPQData.io[ PqProcessIndex(arg) ].stream );
Info(InfoANUPQ, 2, Chomp(line));
return line;
end);
#############################################################################
##
#F PqReadAll( <i> ) . . . . . read all current output from an ANUPQ iostream
#F PqReadAll()
##
## read and return as many *complete* lines of {\ANUPQ} output, from the
## <i>th or default interactive {\ANUPQ} process, as there are to be read,
## *at the time of the call*, as a list of strings with any trailing
## newlines removed and returns the empty list otherwise. `PqReadAll' also
## writes each line read via `Info' at `InfoANUPQ' level 2. It doesn't try
## to distinguish banner and menu output from other output of the `pq'
## binary. Whenever `PqReadAll' finds only a partial line, it waits for the
## complete line, thus increasing the probability that it has captured all
## the output to be had from {\ANUPQ}.
##
InstallGlobalFunction(PqReadAll, function(arg)
local lines, stream, line;
stream := ANUPQData.io[ PqProcessIndex(arg) ].stream;
lines := [];
line := PQ_READ_ALL_LINE(stream);
while line <> fail do
line := Chomp(line);
Info(InfoANUPQ, 2, line);
Add(lines, line);
line := PQ_READ_ALL_LINE(stream);
od;
return lines;
end);
#############################################################################
##
#F PqReadUntil( <i>, <IsMyLine> ) . read from ANUPQ iostream until a cond'n
#F PqReadUntil( <IsMyLine> )
#F PqReadUntil( <i>, <IsMyLine>, <Modify> )
#F PqReadUntil( <IsMyLine>, <Modify> )
##
## read complete lines of {\ANUPQ} output, from the <i>th or default
## interactive {\ANUPQ} process, ``chomps'' them (i.e.~removes any trailing
## newline character), emits them to `Info' at `InfoANUPQ' level 2 (without
## trying to distinguish banner and menu output from other output of the
## `pq' binary), and applies the function <Modify> (where <Modify> is just
## the identity map/function for the first two forms) until a ``chomped''
## line <line> for which `<IsMyLine>( <Modify>(<line>) )' is true.
## `PqReadUntil' returns the list of <Modify>-ed ``chomped'' lines read.
##
InstallGlobalFunction(PqReadUntil, 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 := ANUPQData.io[ ANUPQ_IOINDEX(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( PQ_READ_NEXT_LINE(stream) );
Info(InfoANUPQ, 2, line);
line := Modify(line);
Add(lines, line);
until IsMyLine(line);
return lines;
fi;
end);
#############################################################################
##
#F PqWrite( <i>, <string> ) . . . . . . . primitive write to ANUPQ iostream
#F PqWrite( <string> )
##
## write <string> to the <i>th or default interactive {\ANUPQ} process;
## <string> must be in exactly the form the {\ANUPQ} standalone expects. The
## command is echoed via `Info' at `InfoANUPQ' level 3 (with a ```ToPQ> '''
## prompt); i.e.~do `SetInfoLevel(InfoANUPQ, 3);' to see what is transmitted
## to the `pq' binary. `PqWrite' returns `true' if successful in writing to
## the stream of the interactive {\ANUPQ} process, and `fail' otherwise.
##
InstallGlobalFunction(PqWrite, function(arg)
local ioIndex, line;
if Length(arg) in [1, 2] then
ioIndex := ANUPQ_IOINDEX(arg{[1..Length(arg) - 1]});
return ToPQk( ANUPQData.io[ioIndex], arg{[Length(arg)..Length(arg)]}, [] );
else
Error("expected 1 or 2 arguments ... not ", Length(arg), " arguments\n");
fi;
end);
#############################################################################
##
#F ANUPQ_ARG_CHK( <funcname>, <args> ) . . . . check args of int/non-int fns
##
## checks the argument list <args> for a function that has both interactive
## and non-interactive versions, where <funcname> is the generic name of the
## function. If <args> has length more than 1 then it contains options for
## the function that have been passed in one of the {\GAP} 3-compatible ways
## only available non-interactively. `ANUPQ_ARG_CHK' returns <datarec> which
## is either `ANUPQData.ni' in the non-interactive case or
## `ANUPQData.io[<i>]' for some <i> in the interactive case, after setting
## <datarec>.calltype' to one of `"interactive"', `"non-interactive"' or
## `"GAP3compatible"'.
##
InstallGlobalFunction(ANUPQ_ARG_CHK, function(funcname, args)
local ioIndex, datarec, optrec, optnames;
PQ_OTHER_OPTS_CHK( funcname, IsEmpty(args) or IsPosInt( args[1] ) );
if IsEmpty(args) or IsPosInt( args[1] ) then
datarec := ANUPQData.io[ CallFuncList( PqProcessIndex, args ) ];
datarec.outfname := ANUPQData.outfile; # not always needed
#datarec.calltype := "interactive"; # PqStart sets this
if not IsBound(datarec.group) then
Error( "huh! Interactive process has no group\n" );
elif IsMatchingSublist(funcname, "PqDescendants") then
if not IsPcGroup( datarec.group ) then
Error( "group of process must be a pc group\n" );
fi;
else # Check for Prime, ClassBound if nec.
PQ_OPTION_CHECK( funcname, datarec );
fi;
elif 1 = Length(args) then
if not IsPcGroup( args[1] ) then
if IsMatchingSublist(funcname, "PqDescendants") then
Error( "first argument <args[1]> must be a pc group\n" );
elif not IsFpGroup( args[1] ) then
Error( "first argument <args[1]> must be a pc group or an fp group\n" );
fi;
fi;
ANUPQData.ni := PQ_START( VALUE_PQ_OPTION( "PqWorkspace", 10000000 ),
VALUE_PQ_OPTION( "SetupFile" ) );
datarec := ANUPQData.ni;
datarec.group := args[1];
datarec.calltype := "non-interactive";
datarec.procId := 0;
PQ_OPTION_CHECK( funcname, datarec ); # Check for Prime, ClassBound if nec.
if IsBound( datarec.setupfile ) then
datarec.outfname := "PQ_OUTPUT";
else
datarec.outfname := ANUPQData.outfile; # not always needed
fi;
else
# GAP 3 way of passing options is supported in non-interactive use
if funcname = "PqDescendantsTreeCoclassOne" then
Error("GAP 3-compatible ways of passing options not supported");
elif IsRecord(args[2]) then
optrec := ShallowCopy(args[2]);
optnames := Set( REC_NAMES(optrec) );
SubtractSet( optnames, Set( ANUPQoptions.(funcname) ) );
if not IsEmpty(optnames) then
Error(ANUPQoptError( funcname, optnames ), "\n");
fi;
else
optrec := ANUPQextractOptions(funcname, args{[2 .. Length(args)]});
fi;
PushOptions(optrec);
PQ_FUNCTION.(funcname)( args{[1]} );
PopOptions();
datarec := ANUPQData.ni;
datarec.calltype := "GAP3compatible";
datarec.procId := 0;
fi;
return datarec;
end );
#############################################################################
##
#F PQ_COMPLETE_NONINTERACTIVE_FUNC_CALL( <datarec> )
##
## writes the final commands to the `pq' setup file so that the `pq' binary
## makes a clean exit, or just closes the stream to kill the `pq' process.
##
InstallGlobalFunction(PQ_COMPLETE_NONINTERACTIVE_FUNC_CALL, function(datarec)
if IsBound( datarec.setupfile ) then
PQ_MENU(datarec, "SP");
ToPQk(datarec, [ 0 ], [ " #exit program" ]);
fi;
CloseStream(datarec.stream);
if IsBound( datarec.setupfile ) then
Info(InfoANUPQ, 1, "Input file: '", datarec.setupfile, "' written.");
Info(InfoANUPQ, 1, "Run `pq' with '", datarec.opts, "' flags.");
Info(InfoANUPQ, 1, "The result will be saved in: '",
datarec.outfname, "'.");
fi;
end );
#############################################################################
##
#F ToPQLog([<filename>]) . . . . . . log or stop logging pq commands to file
##
## With string argument <filename>, `ToPQLog' opens the file with name
## <filename> for logging; all commands written to the `pq' binary (that are
## `Info'-ed behind a ```ToPQ> ''' prompt at `InfoANUPQ' level 4) are then
## also written to that file (but without prompts). With no argument,
## `ToPQLog' stops logging to whatever file was being logged to. If a file
## was already being logged to, that file is closed and the file with name
## <filename> is opened for logging.
##
InstallGlobalFunction(ToPQLog, function(arg)
if not( IsEmpty(arg) or IsString( arg[1] ) ) then
Error( "expected no arguments or one string argument\n" );
fi;
if IsBound(ANUPQData.topqlogfile) then
CloseStream(ANUPQData.logstream);
PQ_UNBIND(ANUPQData, ["topqlogfile", "logstream"]);
elif IsEmpty(arg) then
Info(InfoANUPQ + InfoWarning, 1, "No file currently being logged to.");
return;
fi;
if not( IsEmpty(arg) ) and IsString(arg[1]) then
ANUPQData.topqlogfile := arg[1];
ANUPQData.logstream := OutputTextFile(ANUPQData.topqlogfile, false);
fi;
end);
#E anupqios.gi . . . . . . . . . . . . . . . . . . . . . . . . . . ends here