Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/GAP/pkg/anupq/lib/   (Algebra von RWTH Aachen Version 4.15.1©)  Datei vom 28.7.2025 mit Größe 141 kB image not shown  

Quelle  anupqi.gi   Sprache: unbekannt

 
#############################################################################
####
##
#W  anupqi.gi              ANUPQ package                          Greg Gamble
##
##  This file installs interactive functions that execute individual pq  menu
##  options.
##
#Y  Copyright (C) 2001  Lehrstuhl D fuer Mathematik,  RWTH Aachen,  Germany
##

#############################################################################
##
#F  PQ_UNBIND( <datarec>, <fields> ) . . . . . unbind fields of a data record
##
##  unbinds the fields in the list <fields> of that data record <datarec>.
##
InstallGlobalFunction( PQ_UNBIND, function( datarec, fields )
local field;
  for field in fields do
    Unbind( datarec.(field) );
  od;
end );

#############################################################################
##
#F  PQ_AUT_GROUP( <G> ) . . . . . . . . . . . . . . . . .  automorphism group
##
##  returns the automorphism group of  a  $p$-group  as  a  record,  avoiding
##  computation if possible (currently it *isn't*  possible),  or  else  uses
##  {\AutPGrp}'s `AutomorphismGroupPGroup'.
##
InstallGlobalFunction( PQ_AUT_GROUP, function( G )

  local autgrp;

  if not IsPGroup(G) then
      Error("group <G> must be a p-group\n");
  fi;
  if false and HasANUPQAutomorphisms(G) then
      # Can't use this because we currently don't know how to interpret
      # the automorphism information returned by the standalone properly.

      autgrp := PqSupplementInnerAutomorphisms(G);
    
  elif false and HasAutomorphismGroup(G) then

      # Can't use existing automorphism information because it does not
      # contain the information required by the standalone.

      autgrp := AutomorphismGroup( G );

  elif LoadPackage("autpgrp") = true or IsAbelian(G) then

      autgrp := AutomorphismGroupPGroup(G);

  else
      return Error( "since package `AutPGrp' is not installed\n",
                    "<G> must have class 1 or <G>'s aut. group must be known.\n",
                    "Please install the `AutPGrp' package\n" );
  fi;
  return autgrp;
end );

#############################################################################
##
#F  PQ_AUT_INPUT( <datarec>, <G> : <options> ) . . . . . . automorphism input
##
##  inputs automorphism data for `<datarec>.group' given by <options> to  the
##  `pq' binary derived from the pc group  <G>  (used  in  option  1  of  the
##  $p$-Group Generation menu and option 2 of the Standard Presentation menu).
##
InstallGlobalFunction( PQ_AUT_INPUT, function( datarec, G )

  local   autrec,  nrautos,  rank,  gens,  i,  aut,  j,  g, exponents;
    
  autrec  := PQ_AUT_GROUP( G );
  nrautos := Length( autrec.glAutos ) + Length( autrec.agAutos );

  ## the automorphisms have to be in a special form which PQ_AUT_GROUP()
  ## *must* deliver.
  
  rank := RankPGroup( G );
  gens := PcgsPCentralSeriesPGroup( G );

  ToPQ(datarec, [ nrautos ], [ "  #number of automorphisms" ]);

  ##  First write out the automorphisms generating a soluble normal subgroup 
  ##  of the automorphism group of the p-group.  These automorphisms may
  ##  not have a faithful representation on the Frattini quotient of the
  ##  p-group and are treated accordingly by the standalone.
  ##
  ##  They are written out in bottom up fashion as this is the order in
  ##  which the orbit algorithm for a group given by an ag-system needs
  ##  them.  
  for i in Reversed([1..Length(autrec.agAutos)]) do
      aut := autrec.agAutos[i];

      for j in [1..rank] do
          g := gens[j];
          exponents := Flat( List( ExponentsOfPcElement(gens, Image( aut, g )),
                                   e -> [ String(e), " "] ) );

          ToPQ(datarec, [ exponents ],
               [ " #gen'r exp'ts of im(ag aut ", i, ", gen ", j, ")" ]);
      od;
  od;

  ##  Now output the automorphisms from the insoluble quotient of the
  ##  automorphism group of the p-group.  These have a faithful
  ##  representation on the Frattini quotient of the p-group and are
  ##  treated accordingly by the standalone.
  for i in Reversed( [1..Length(autrec.glAutos)] ) do
      aut := autrec.glAutos[i];

      for j in [1..rank] do
          g := gens[j];
          exponents := Flat( List( ExponentsOfPcElement(gens, Image( aut, g )),
                                   e -> [ String(e), " "] ) );

          ToPQ(datarec, [ exponents ],
               [ " #gen'r exp'ts of im(gl aut ", i, ", gen ", j, ")" ]);
      od;
  od;

  if PQ_MENU(datarec) = "pG" then
      ##  ?? Why only the pG menu ??
      ##  Finally, tell the standalone the number of soluble automorphisms
      ##  and the relative order of each automorphism. 
      ToPQ(datarec, [ Length(autrec.agOrder) ], 
           [ "  #number of soluble automorphisms" ]);
    
      for i in Reversed( [1..Length( autrec.agOrder )] ) do
          ToPQ( datarec, [ autrec.agOrder[i] ], 
                [ "  #rel order of ", i, "th ag automorphism" ] );
      od;
  fi;

end );

#############################################################################
##
#F  PQ_MANUAL_AUT_INPUT(<datarec>,<mlist>) . automorphism input w/o an Aut gp
##
##  inputs automorphism data for `<datarec>.group' given by  <mlist>  to  the
##  `pq' binary.
##
InstallGlobalFunction( PQ_MANUAL_AUT_INPUT, function( datarec, mlist )
local line, nauts, nsolauts, rank, nexpts, i, j, aut, exponents;
  nauts  := Length(mlist);
  rank   := Length(mlist[1]);
  ToPQ(datarec, [ nauts ], [ "  #no. of auts" ]);
  if datarec.line = "Input the number of exponents: " then
    nexpts := Length(mlist[1][1]);
    ToPQ(datarec, [ nexpts ], [ "  #no. of exponents" ]);
  fi;
  for i in [1..nauts] do
    aut := mlist[i];
    for j in [1..rank] do
      exponents := Flat( List( aut[j], e -> [ String(e), " "] ) );
      ToPQ(datarec, [ exponents ], 
                    [ " #gen'r exp'ts of im(aut ", i, ", gen ", j, ")" ]);
    od;
  od;
  if PQ_MENU(datarec) = "pG" then
    ##  ?? Why only the pG menu ??
    ##  Finally, tell the standalone the number of soluble automorphisms
    ##  and the relative order of each automorphism. 
    ToPQ(datarec, [ datarec.NumberOfSolubleAutomorphisms ], 
                  [ "  #number of soluble automorphisms" ]);
    if datarec.NumberOfSolubleAutomorphisms > 0 then
      for i in datarec.RelativeOrders do
        ToPQ( datarec, [ datarec.RelativeOrders[i] ], 
                       [ "  #rel order of ", i, "th ag automorphism" ] );
      od;
    fi;
  fi;
end );

#############################################################################
##
#F  PQ_AUT_ARG_CHK(<minnargs>, <args>) . checks args for a func defining auts
##
##  checks that  the  arguments  make  sense  for  a  function  that  defines
##  automorphisms, and if one of the arguments is a list checks as much as is
##  possible that it is a list of  matrices  that  will  be  valid  input  as
##  automorphisms for the `pq' binary.  If  the  arguments  look  ok  a  list
##  containing the `ANUPQData.io' index of the data record and, if  relevant,
##  a list of matrices is returned.
##
InstallGlobalFunction( PQ_AUT_ARG_CHK, function( minnargs, args )
local ioIndex, datarec, mlist, rank, nexpts;
  if Length(args) < minnargs then
    Error("expected at least 1 argument\n"); #minnargs is 0 or 1
  elif 2 < Length(args) then
    Error("expected at most 2 arguments\n");
  fi;
  if not IsEmpty(args) and IsList(args[ Length(args) ]) then
    mlist := args[ Length(args) ];
    args := args{[1 .. Length(args) - 1]};
  fi;
  ioIndex := CallFuncList(PqProcessIndex, args);
  if not IsBound(mlist) then
    return [ioIndex];
  elif not( IsList(mlist) and ForAll(mlist, IsMatrix) and
            ForAll(Flat(mlist), i -> IsInt(i) and i >= 0) ) then
    Error("<mlist> must be a list of matrices with ",
          "non-negative integer coefficients\n");
  fi;
  datarec := ANUPQData.io[ ioIndex ];
  if IsBound( datarec.pQuotient ) then
    rank := RankPGroup( datarec.pQuotient );
  else
    rank := Length(mlist[1]); # Should we allow this?
  fi;
  if not ForAll(mlist, mat -> Length(mat) = rank) then
    Error("no. of rows in each matrix of <mlist> must be the rank of ",
          "p-quotient (", rank, ")\n");
  fi;
  nexpts := Length(mlist[1][1]);
  if not ForAll(mlist, mat -> Length(mat[1]) = nexpts) then
    Error("each matrix of <mlist> must have the same no. of columns\n");
  fi;
  return [ioIndex, mlist];
end );

#############################################################################
##
#F  PQ_PC_PRESENTATION( <datarec>, <menu> ) . . . . . .  p-Q/SP menu option 1
##
##  inputs  data  given  by  <options>  to  the   `pq'   binary   for   group
##  `<datarec>.group' to compute a  pc  presentation  (do  option  1  of  the
##  relevant menu) according to the  <menu>  menu,  where  <menu>  is  either
##  `"pQ"' (main $p$-Quotient menu) or `"SP' (Standard Presentation menu).
##
InstallGlobalFunction( PQ_PC_PRESENTATION, function( datarec, menu )
local gens, rels, p, fpgrp, identities, pcgs, len, strp, i, j, Rel, line;

  p := VALUE_PQ_OPTION("Prime", fail, datarec); # "Prime" is a `global' option

  PQ_MENU(datarec, menu);

  identities := menu = "pQ" and
                VALUE_PQ_OPTION("Identities", [], datarec) <> [];

  # Option 1 of p-Quotient/Standard Presentation Menu: defining the group
  ToPQk(datarec, [1], ["  #define group"]);
  if VALUE_PQ_OPTION("GroupName", "[grp]", datarec) = "[grp]" and
     IsBound(datarec.group) and IsBound(datarec.group!.Name) then
    datarec.GroupName := datarec.group!.Name;
  fi;
  ToPQk(datarec, ["name ",  datarec.GroupName], []);
  ToPQk(datarec, ["prime ", p], []);
  if identities then
    datarec.prevngens := 0;
    ToPQk(datarec, ["class ", 1], []);
  else
    ToPQk(datarec, ["class ", VALUE_PQ_OPTION("ClassBound", 63, datarec)], []);
  fi;
  ToPQk(datarec, ["exponent ", VALUE_PQ_OPTION("Exponent", 0, datarec)], []);
                                             # "Exponent" is a `global' option
  if VALUE_PQ_OPTION( "Metabelian", false, datarec ) = true then
    ToPQk(datarec, [ "metabelian" ], []);
  fi;
  ToPQk(datarec, ["output ", VALUE_PQ_OPTION("OutputLevel", 0, datarec)], []);

  if IsFpGroup(datarec.group) then
    gens := FreeGeneratorsOfFpGroup(datarec.group);
    rels := VALUE_PQ_OPTION("Relators", datarec);
    if rels = fail then
      rels := RelatorsOfFpGroup(datarec.group);
    elif ForAll( rels, rel -> PqParseWord(datarec.group, rel) ) then
      Info(InfoANUPQ, 2, "Relators parsed ok.");
    fi;
  elif not( IsPGroup(datarec.group) ) then
    fpgrp := FpGroupPcGroup( datarec.group );
    gens := FreeGeneratorsOfFpGroup(fpgrp);
    rels := RelatorsOfFpGroup(fpgrp);
  else
    pcgs := PcgsPCentralSeriesPGroup(datarec.group);
    datarec.pcgs := pcgs;
    len  := Length(pcgs);
    gens := List( [1..len], i -> Concatenation( "g", String(i) ) );
    strp := String(p);

    Rel := function(elt, eltstr)
      local rel, expts, factors;

      rel := eltstr;
      expts := ExponentsOfPcElement( pcgs, elt );
      if ForAny( expts, x -> x<>0 )  then
        factors 
            := Filtered(
                   List( [1..len], 
                         function(i)
                           if expts[i] = 0 then
                             return "";
                           fi;
                           return Concatenation(gens[i], "^", String(expts[i]));
                         end ),
                   factor -> factor <> "");
        Append(rel, "=");
        Append(rel, JoinStringsWithSeparator(factors, "*"));
      fi;
      return rel;
    end;

    rels := List( [1..len], 
                  i -> Rel( pcgs[i]^p, Concatenation(gens[i], "^", strp) ) );
    for i in [1..len] do
      for j in [1..i-1]  do
        Add(rels, Rel( Comm( pcgs[i], pcgs[j] ), 
                       Concatenation("[", gens[i], ",", gens[j], "]") ));
      od;
    od;
  fi;
  if Length(gens) > 511 then
    # The pq program defines MAXGENS to be 511 in `../include/runtime.h'
    # ... on the other hand, the number of pc gen'rs can be up to 65535
    Error("number of defining generators, ", Length(gens), ", too large.\n",
          "The pq program defines MAXGENS (the maximum number of defining\n",
          "generators) to be 511.\n");
  fi;
  datarec.gens := gens;
  datarec.rels := rels;
  ToPQk(datarec, "gens", []);
  datarec.match := true;
  ToPQ(datarec, "rels", []);
  ## pq is intolerant of long lines and integers that are split over lines
  #rels := Concatenation(
  #            "relators   { ", JoinStringsWithSeparator( rels, ", " ), " };");
  #while Length(rels) >= 69 do
  #  i := 68;
  #  while not (rels[i] in "*^, ") do i := i - 1; od;
  #  ToPQk(datarec, [ rels{[1 .. i]} ], []);
  #  rels := Concatenation( "  ", rels{[i + 1 .. Length(rels)]} );
  #od;
  #ToPQ(datarec, [ rels ], []);
  datarec.haspcp := true;
  # The `pq' only sets OutputLevel locally within the menu item
  # ... for the GAP interface this would be too confusing; so we
  # set it `globally'
  PQ_SET_OUTPUT_LEVEL(datarec, datarec.OutputLevel);
  PQ_SET_GRP_DATA(datarec);
  if identities and datarec.ngens[1] <> 0 then
    PQ_EVALUATE_IDENTITIES(datarec);
    VALUE_PQ_OPTION("ClassBound", 63, datarec);
    while datarec.class < datarec.ClassBound and 
          datarec.prevngens <> datarec.ngens[ datarec.class ] do
      PQ_NEXT_CLASS(datarec);
    od;
  fi;
end );

#############################################################################
##
#F  PqPcPresentation( <i> : <options> ) . . user version of p-Q menu option 1
#F  PqPcPresentation( : <options> )
##
##  for the <i>th or default interactive {\ANUPQ} process,  direct  the  `pq'
##  binary to compute the pc presentation  of  the  quotient  (determined  by
##  <options>) of the group of the process, which for process <i>  is  stored
##  as `ANUPQData.io[<i>].group'.
##
##  The  possible  <options>  are  the  same  as  for  the  interactive  `Pq'
##  (see~"Pq!interactive")   function,   namely:    `Prime',    `ClassBound',
##  `Exponent', `Relators', `GroupName', `Metabelian' and `OutputLevel'  (see
##  Chapter~"ANUPQ options" for a detailed description  for  these  options).
##  The option `Prime' is required  unless  already  provided  to  `PqStart'.
##  Also, option `ClassBound' *must* be supplied.
##
##  *Notes*
##
##  The pc presentation is held by the `pq' binary. There is no output  of  a
##  {\GAP} pc group; see~`PqCurrentGroup' ("PqCurrentGroup") if you need  the
##  corresponding {\GAP} pc group.
##
##  For those familiar with the `pq' binary, `PqPcPresentation' performs menu
##  item 1 of the main $p$-Quotient menu.
##
InstallGlobalFunction( PqPcPresentation, function( arg )
local datarec;
  PQ_OTHER_OPTS_CHK("PqPcPresentation", true);
  datarec := CallFuncList(ANUPQDataRecord, arg);
  PQ_PC_PRESENTATION( datarec, "pQ" );
end );

#############################################################################
##
#F  PQ_SAVE_PC_PRESENTATION( <datarec>, <filename> ) . . .  p-Q menu option 2
##
##  directs the `pq' binary to save the pc presentation  previously  computed
##  for  `<datarec>.group'  to  <filename>  using  option  2  of   the   main
##  $p$-Quotient menu.
##
InstallGlobalFunction( PQ_SAVE_PC_PRESENTATION, function( datarec, filename )
  PQ_MENU(datarec, "pQ");
  ToPQ(datarec, [ 2 ], [ "  #save pc presentation to file" ]);
  datarec.filter := ["Presentation"];
  ToPQ(datarec, [ filename ], [ "  #filename" ]);
  Unbind(datarec.filter);
end );

#############################################################################
##
#F  PQ_PATH_CURRENT_DIRECTORY() . . . . . . . . . .  essentially the UNIX pwd
##
##  returns a string that is the path of the current directory.
##
InstallGlobalFunction( PQ_PATH_CURRENT_DIRECTORY, function()
local path, stream;
  path := "";
  stream := OutputTextString(path, true);
  if 0 = Process( DirectoryCurrent(), 
                  Filename(DirectoriesSystemPrograms(), "pwd"),
                  InputTextNone(), 
                  stream,
                  [] ) then
    CloseStream(stream);
    return Chomp(path);
  fi;
  Error("could not determine the path of the current directory!?!\n");
end );

#############################################################################
##
#F  PQ_CHK_PATH(<filename>, <rw>, <datarec>) . . . . . . .  check/add to path
##
##  checks <filename> is a non-empty string, if it doesn't begin with  a  `/'
##  prepends a path for the current directory, and checks the result  is  the
##  name of a readable (resp. writable) if <rw> is `"r"' (resp.  if  <rw>  is
##  `"w"') and if there is no error returns the result.
##
InstallGlobalFunction( PQ_CHK_PATH, function( filename, rw, datarec )
  if not IsString(filename) or filename = "" then
    Error( "argument <filename> must be a non-empty string\n" );
  fi;
  if filename[1] <> '/' then
    # we need to do this as pq executes in ANUPQData.tmpdir
    filename := Concatenation(PQ_PATH_CURRENT_DIRECTORY(), "/", filename);
  fi;
  if rw = "r" then
    if IsReadableFile(filename) <> true then
      Error( "file with name <filename> is not readable\n" );
    fi;
  else # rw = "w"
    if not IsBound(datarec.setupfile) then
      PrintTo(filename, ""); # This is what will generate the error
                             # but it also ensures it's empty
    fi;
    if IsWritableFile(filename) <> true then
      Error( "file with name <filename> cannot be written to\n" );
    fi;
  fi;
  return filename;
end );

#############################################################################
##
#F  PqSavePcPresentation( <i>, <filename> ) . .  user ver. of p-Q menu opt. 2
#F  PqSavePcPresentation( <filename> )
##
##  for the <i>th or default interactive {\ANUPQ} process,  direct  the  `pq'
##  program to save the pc presentation previously computed for the  quotient
##  of the group of that process to the file with  name  <filename>.  If  the
##  first character of the  string  <filename>  is  not  `/',  <filename>  is
##  assumed to be the path of a writable file relative to  the  directory  in
##  which  {\GAP}  was  started.  A   saved   file   may   be   restored   by
##  `PqRestorePcPresentation' (see~"PqRestorePcPresentation").
##
##  *Note:* For those familiar with the `pq'  binary,  `PqSavePcPresentation'
##  performs menu item 2 of the main $p$-Quotient menu.
##
InstallGlobalFunction( PqSavePcPresentation, function( arg )
local datarec, filename;
  if 0 = Length(arg) or Length(arg) > 2 then
    Error( "expected 1 or 2 arguments\n" );
  fi;
  datarec := CallFuncList(ANUPQDataRecord, arg{[1..Length(arg) - 1]});
  filename := PQ_CHK_PATH( arg[Length(arg)], "w", datarec );
  PQ_SAVE_PC_PRESENTATION( datarec, filename );
end );

#############################################################################
##
#F  PQ_RESTORE_PC_PRESENTATION( <datarec>, <filename> ) . . p-Q menu option 3
##
##  directs the `pq' binary to restore the pc presentation  previously  saved
##  to <filename> using option 3 of the main $p$-Quotient menu.
##
InstallGlobalFunction( PQ_RESTORE_PC_PRESENTATION, function( datarec, filename )
  PQ_MENU(datarec, "pQ");
  ToPQ(datarec, [ 3 ], [ "  #restore pc presentation from file" ]);
  datarec.match := true;
  ToPQ(datarec, [ filename ], [ "  #filename" ]);
  datarec.haspcp := true;
  PQ_SET_GRP_DATA(datarec);
end );

#############################################################################
##
#F  PqRestorePcPresentation( <i>, <filename> ) . user ver. of p-Q menu opt. 3
#F  PqRestorePcPresentation( <filename> )
##
##  for the <i>th or default interactive {\ANUPQ} process,  direct  the  `pq'
##  program to restore the pc presentation previously saved to <filename>, by
##  `PqSavePcPresentation'   (see~"PqSavePcPresentation").   If   the   first
##  character of the string <filename> is not `/', <filename> is  assumed  to
##  be the path of a readable file relative to the directory in which  {\GAP}
##  was started.
##
##  *Note:*
##  For  those  familiar  with  the  `pq'  binary,  `PqRestorePcPresentation'
##  performs menu item 3 of the main $p$-Quotient menu.
##
InstallGlobalFunction( PqRestorePcPresentation, function( arg )
local datarec, filename;
  if 0 = Length(arg) or Length(arg) > 2 then
    Error( "expected 1 or 2 arguments\n" );
  fi;
  datarec := CallFuncList(ANUPQDataRecord, arg{[1..Length(arg) - 1]});
  filename := PQ_CHK_PATH( arg[Length(arg)], "r", datarec );
  PQ_RESTORE_PC_PRESENTATION( datarec, filename );
end );

#############################################################################
##
#F  PQ_DISPLAY_PRESENTATION( <datarec> ) . . . . . . . . .  any menu option 4
##
##  directs the `pq' binary to display the pc presentation of  the  group  to
##  the current class, using option 4 of the current menu.
##
InstallGlobalFunction( PQ_DISPLAY_PRESENTATION, function( datarec )
  if datarec.menu[ Length(datarec.menu) ] <> 'G' and
     VALUE_PQ_OPTION("OutputLevel", datarec) <> fail then
    PQ_SET_OUTPUT_LEVEL( datarec, datarec.OutputLevel );
  fi;
  ToPQ(datarec, [ 4 ], [ "  #display presentation" ]);
end );

#############################################################################
##
#F  PQ_GRP_EXISTS_CHK( <datarec> ) . . check the `pq' binary knows about a gp
##
##  checks that `<datarec>.ngens' is set and non-empty (which can only happen
##  if the `pq' binary has been fed a group) and generates an error if not.
##
InstallGlobalFunction( PQ_GRP_EXISTS_CHK, function( datarec )
  if not IsBound(datarec.ngens) or IsEmpty(datarec.ngens) then
    Error( "huh! No current group defined for this process!?\n" );
  fi;
end );

#############################################################################
##
#F  PQ_SET_GRP_DATA( <datarec> ) .  save group data of current class of group
##
##  If `<datarec>.matchedline' is not  set  the  `pq'  binary  is  called  to
##  display the presentation; usually  `<datarec>.matchedline'  is  set  when
##  filtering `pq' output for lines starting with `"Group"' (the  value  set
##  for `<datarec>.match'), but no such  lines  occur  when  computing  a  pc
##  presentation with the `OutputLevel' option set to 0, or when restoring  a
##  pc presentation, or when computing tails etc. From this line  the  fields
##  `name', `class' and `forder' of the record <datarec> are set to the name,
##  class  and  factored   order   of   that   group,   respectively.   Also,
##  `<datarec>.ngens' is updated, and if it is afterwards incomplete and  the
##  call to `PQ_SET_GRP_DATA' was not initiated by `PQ_DATA'  then  `PQ_DATA'
##  is called to ensure `<datarec>.ngens' is complete.
##
InstallGlobalFunction( PQ_SET_GRP_DATA, function( datarec )
local line, classpos;
  if IsBound(datarec.setupfile) then 
    # A fudge ... some things we can only know by actually running it!
    Info(InfoANUPQ + InfoWarning,1, 
         "Guess made of `class' and `ngens' fields");
    Info(InfoANUPQ + InfoWarning,1, 
         "... please check commands ok by running without `SetupFile' option");
    Info(InfoANUPQ + InfoWarning,1, 
         "and comparing with `ToPQ> ' commands observed at InfoANUPQ level 4");
    datarec.class := datarec.ClassBound;
    datarec.ngens := [ 1 ];
    return;
  fi;
  # Either datarec.matchedline is of one of the following forms:
  # Group completed. Lower exponent-<p> central class = <c>, Order = <p>^<n>
  # Group: [grp] to lower exponent-<p> central class <c> has order <p>^<n>
  if not IsBound(datarec.matchedline) then
    PushOptions(rec(nonuser := true));
    ToPQ(datarec, [ 4 ], [ "  #display presentation" ]);
    PopOptions();
  fi;
  line := SplitString(datarec.matchedline, "", ":,. ^\n");
  if line[2] = "completed" then
    classpos := Position(line, "class") + 2;
    #if not IsBound(datarec.name) then #do we need to bother?
    #  datarec.name := "[grp]";
    #fi;
  else
    # Only the ``incomplete'' form of datarec.matchedline gives the name
    datarec.name := line[2];
    datarec.gpnum := JoinStringsWithSeparator( 
                         line{[3 .. Position(line, "to") - 1]}, " " );
    classpos := Position(line, "class") + 1;
  fi;
  datarec.class  := Int( line[classpos] );
  datarec.forder := List( line{[classpos + 3, classpos + 4]}, Int);
  PQ_UNBIND(datarec, ["match", "matchedline"]);
  # First see if we can update datarec.ngens cheaply
  if not IsBound(datarec.ngens) then
    datarec.ngens := [];
  fi;
  if datarec.class > 0 then
    datarec.ngens[ datarec.class ] := datarec.forder[2];
    #The `pq' binary reduces the class by 1 
    #if the no. of gen'rs doesn't increase
    Unbind( datarec.ngens[ datarec.class + 1 ] );
  fi;

  if not IsBound(datarec.inPQ_DATA) and not IsDenseList(datarec.ngens) then
    # It wasn't possible to update datarec.ngens cheaply
    PQ_DATA( datarec );
  fi;
end );

#############################################################################
##
#F  PQ_DATA( <datarec> ) . . . . gets class/gen'r data from (A)p-Q menu opt 4
##
##  ensures that the menu is a $p$-Quotient menu and that the output level is
##  3 and using option 4 of the now  current  menu  extracts  the  number  of
##  generators of each class currently known to the `pq' binary.  (The  order
##  of each $p$-class quotient is taken as $p^n$ where $n$ is the  number  of
##  generators for the class; this may be an over-estimate if tails have been
##  added  and  the  necessary  consistency  checks,  relation   collections,
##  exponent law checks and redundant generator eliminations  have  not  been
##  done for a class.) All output that would  have  appeared  at  `InfoANUPQ'
##  levels 1 or 2 if user-initiated is `Info'-ed at `InfoANUPQ' level 3.  The
##  menu and output level are reset to their original values (if changed)  on
##  leaving.
##
InstallGlobalFunction( PQ_DATA, function( datarec )
local menu, lev, ngen, i, line, class;
  if not( IsBound(datarec.haspcp) and datarec.haspcp ) then
    Error( "a pc presentation for the group of the process ",
           "has not yet been defined\n" );
  fi;
  PushOptions(rec(nonuser := true));
  datarec.inPQ_DATA := true;
  if datarec.menu[ Length(datarec.menu) ] <> 'Q' then
    menu := datarec.menu;
    PQ_MENU(datarec, "pQ");
  fi;
  if not IsBound(datarec.OutputLevel) then
    lev := 0;
    PQ_SET_OUTPUT_LEVEL( datarec, 3 );
  elif datarec.OutputLevel < 3 then
    lev := datarec.OutputLevel;
    PQ_SET_OUTPUT_LEVEL( datarec, 3 );
  fi;
  datarec.matchlist := ["Group", "Class", " is defined on "];
  datarec.matchedlines := [];
  ToPQ(datarec, [ 4 ], [ "  #display presentation" ]);
  datarec.matchedline := datarec.matchedlines[1];
  PQ_SET_GRP_DATA(datarec);
  for i in [2 .. Length(datarec.matchedlines)] do
    line := SplitString(datarec.matchedlines[i], "", " \n");
    if line[1] = "Class" then
      class := Int( line[2] );
      if class > 1 then
        datarec.ngens[class - 1] := Int(ngen);
        if class = datarec.class then
          break;
        fi;
      fi;
    else
      ngen := line[1];
    fi;
  od;
  if IsBound(menu) then
    PQ_MENU(datarec, menu);
  fi;
  if IsBound(lev) then
    PQ_SET_OUTPUT_LEVEL( datarec, lev );
  fi;
  PQ_UNBIND( datarec, ["matchlist", "matchedlines", "inPQ_DATA"] );
  PopOptions();
end );

#############################################################################
##
#F  PQ_DATA_CHK( <args> ) . . .  call PQ_DATA if class/gen'r data out-of-date
##
##  determines the data record <datarec>, calls `PQ_DATA'  if  necessary  and
##  returns <datarec>.
##
InstallGlobalFunction( PQ_DATA_CHK, function( args )
local datarec;
  datarec := CallFuncList(ANUPQDataRecord, args);
  if not IsBound(datarec.ngens) or IsEmpty(datarec.ngens) or 
     not IsDenseList(datarec.ngens) then
    PQ_DATA( datarec );
  fi;
  return datarec;
end );

#############################################################################
##
#F  PqFactoredOrder( <i> ) . the `pq' binary's current group's factored order
#F  PqFactoredOrder()
##
##  for the <i>th or default interactive {\ANUPQ} process, return an estimate
##  of the factored order of the lower exponent  $p$-class  quotient  of  the
##  group currently determined by the process as a list `[<p>, <n> ]'.
##
##  *Note:* The order of each $p$-class quotient is taken as $p^n$ where  $n$
##  is the number of generators for the class; this may be  an  over-estimate
##  if tails have been added and the necessary consistency  checks,  relation
##  collections, exponent law checks  and  redundant  generator  eliminations
##  have not yet been done for a class.
##
InstallGlobalFunction( PqFactoredOrder, function( arg )
  return PQ_DATA_CHK(arg).forder;
end );

#############################################################################
##
#F  PqOrder( <i> ) . . . .  the order of the current group of the `pq' binary
#F  PqOrder()
##
##  for the <i>th or default interactive {\ANUPQ} process, return an estimate
##  of the order of the  lower  exponent  $p$-class  quotient  of  the  group
##  currently determined by the process.
##
##  *Note:* The order of each $p$-class quotient is taken as $p^n$ where  $n$
##  is the number of generators for the class; this may be  an  over-estimate
##  if tails have been added and the necessary consistency  checks,  relation
##  collections, exponent law checks  and  redundant  generator  eliminations
##  have not been done for a class.
##
InstallGlobalFunction( PqOrder, function( arg )
local forder;
  forder := CallFuncList( PqFactoredOrder, arg );
  return forder[1]^forder[2];
end );

#############################################################################
##
#F  PqPClass( <i> ) . . . the p class of the current group of the `pq' binary
#F  PqPClass()
##
##  for the <i>th or default interactive {\ANUPQ} process, return  the  lower
##  exponent $p$-class of the quotient  group  currently  determined  by  the
##  process.
##
InstallGlobalFunction( PqPClass, function( arg )
  return PQ_DATA_CHK(arg).class;
end );

#############################################################################
##
#F  PqNrPcGenerators( <i> ) . number of pc gen'rs of `pq' binary's current gp
#F  PqNrPcGenerators()
##
##  for the <i>th or default interactive {\ANUPQ} process, return the  number
##  of pc generators of the lower exponent $p$-class quotient  of  the  group
##  currently determined by the process.
##
InstallGlobalFunction( PqNrPcGenerators, function( arg )
  return PQ_DATA_CHK(arg).forder[2];
end );

#############################################################################
##
#F  PqWeight( <i>, <j> ) . . . . . . . . . . . . . . .  weight of a generator
#F  PqWeight( <j> )
##
##  for the <i>th or default interactive {\ANUPQ} process, return the  weight
##  of the <j>th pc generator of the lower exponent $p$-class quotient of the
##  group currently determined by the process, or `fail' if there is no  such
##  numbered pc generator.
##
InstallGlobalFunction( PqWeight, function( arg )
local ngens, i, j;
  if not Length(arg) in [1, 2] then
    Error( "expected 1 or 2 arguments\n" );
  fi;
  j := arg[ Length(arg) ];
  if not IsPosInt(j) then
    Error( "argument <j> should be a positive integer\n" );
  fi;
  Unbind( arg[ Length(arg) ] );
  ngens := PQ_DATA_CHK(arg).ngens;
  return First([1 .. Length(ngens)], i -> ngens[i] >= j);
end );

#############################################################################
##
#F  PqCurrentGroup( <i> ) . extracts current p-quotient or p-cover as a pc gp
#F  PqCurrentGroup()
##
##  for the <i>th or default interactive {\ANUPQ} process, return  the  lower
##  exponent $p$-class quotient of the group or $p$-covering group  currently
##  determined by the process as a {\GAP} pc group.
##
InstallGlobalFunction( PqCurrentGroup, function( arg )
local datarec, out;
  datarec := PQ_DATA_CHK(arg);
  datarec.outfname := ANUPQData.outfile;
  PushOptions( rec(nonuser := true) );
  PQ_WRITE_PC_PRESENTATION(datarec, datarec.outfname);
  PopOptions();
  if IsBound(datarec.pcoverclass) and datarec.pcoverclass = datarec.class then
    out := "pCover";
  else
    out := "pQuotient";
  fi;
  PQ_GROUP_FROM_PCP( datarec, out );
  return datarec.(out);
end );

#############################################################################
##
#F  PqDisplayPcPresentation( <i> ) . . . .  user version of any menu option 4
#F  PqDisplayPcPresentation()
##
##  for the <i>th or default interactive {\ANUPQ} process,  direct  the  `pq'
##  binary to display the pc presentation of  the  lower  exponent  $p$-class
##  quotient of the group currently determined by the process.
##
##  Except if the last command communicating  with  the  `pq'  binary  was  a
##  $p$-group generation command (for which there is only  a  verbose  output
##  level), to set the amount of information this command  displays  you  may
##  wish  to  call  `PqSetOutputLevel'  first  (see~"PqSetOutputLevel"),   or
##  equivalently pass the option `OutputLevel' (see~"option OutputLevel").
##
##  *Note:*
##  For  those  familiar  with  the  `pq'  binary,  `PqDisplayPcPresentation'
##  performs menu item 4 of the current menu of the `pq' binary.
##
InstallGlobalFunction( PqDisplayPcPresentation, function( arg )
local datarec;
  datarec := CallFuncList(ANUPQDataRecord, arg);
  PQ_GRP_EXISTS_CHK( datarec );
  PQ_DISPLAY_PRESENTATION( datarec );
end );

#############################################################################
##
#F  PQ_SET_OUTPUT_LEVEL(<datarec>, <lev>) . . . .  p-Q/SP/A p-Q menu option 5
##
##  inputs data to the `pq' binary to set the print level  to  <lev>  in  the
##  current menu or the ``basic'' $p$-Quotient menu if the current menu is  a
##  $p$-Group generation menu.
##
InstallGlobalFunction( PQ_SET_OUTPUT_LEVEL, function( datarec, lev )
  if datarec.menu[ Length(datarec.menu) ] = 'G' then
    PQ_MENU(datarec, "pQ");
  fi;
  ToPQ(datarec, [ 5 ], [ "  #set output level" ]);
  ToPQ(datarec, [ lev ], [ "  #output level" ]);
  datarec.OutputLevel := lev;
end );

#############################################################################
##
#F  PqSetOutputLevel( <i>, <lev> ) .  user version of p-Q/SP/A p-Q menu opt 5
#F  PqSetOutputLevel( <lev> )
##
##  for the <i>th or default interactive {\ANUPQ} process,  direct  the  `pq'
##  binary to set the output level of the `pq' binary to <lev>.
##
##  *Note:* For those  familiar  with  the  `pq'  binary,  `PqSetOutputLevel'
##  performs menu item 5 of the main (or advanced) $p$-Quotient menu, or  the
##  Standard Presentation menu.
##
InstallGlobalFunction( PqSetOutputLevel, function( arg )
local datarec, lev;
  if not(Length(arg) in [1, 2]) then
    Error( "1 or 2 arguments expected\n");
  fi;
  lev := arg[Length(arg)];
  if not(lev in [0..3]) then
    Error( "argument <lev> should be an integer in [0 .. 3]\n" );
  fi;
  datarec := CallFuncList(ANUPQDataRecord, arg{[1..Length(arg) - 1]});
  PQ_SET_OUTPUT_LEVEL( datarec, lev);
end );

#############################################################################
##
#F  PQ_NEXT_CLASS( <datarec> ) . . . . . . . . . . . . . .  p-Q menu option 6
##
##  directs the `pq' binary to calculate the next class of `<datarec>.group',
##  using option 6 of the main $p$-Quotient menu.
##
#T  Another possibility for checking for whether a queue factor is needed
#T  is to test for `<datarec>.hasAuts'.
##
InstallGlobalFunction( PQ_NEXT_CLASS, function( datarec )
local line;
  PQ_MENU(datarec, "pQ");
  PQ_UNBIND(datarec, ["pQuotient", "pQepi", "pCover"]);
  if VALUE_PQ_OPTION("Identities", [], datarec) <> [] then
    if datarec.class >= 1 then
      datarec.prevngens := datarec.ngens[ datarec.class ];
    fi;
    PQ_P_COVER(datarec);
    PQ_FINISH_NEXT_CLASS(datarec);
  else
    datarec.match := true;
    ToPQ(datarec, [ 6 ], [ "  #calculate next class" ]);
    if IsMatchingSublist(datarec.line, "Input queue factor:") then
      ToPQ(datarec, [ VALUE_PQ_OPTION("QueueFactor", 15) ],
                    [ " #queue factor"]);
    fi;
    PQ_SET_GRP_DATA(datarec);
  fi;
end );

#############################################################################
##
#F  PqNextClass( <i> [: <option>]) . . . .  user version of p-Q menu option 6
#F  PqNextClass( [: <option>])
##
##  for the <i>th or default interactive {\ANUPQ} process, direct the `pq' to
##  calculate the next class of `ANUPQData.io[<i>].group'.
##
##  \atindex{option QueueFactor}{@option \noexpand`QueueFactor'}
##  `PqNextClass'  accepts  the  option   `QueueFactor'   (see   also~"option
##  QueueFactor") which should be a positive integer  if  automorphisms  have
##  been previously supplied. If the `pq' binary requires a queue factor  and
##  none is supplied via the option `QueueFactor' a default of 15 is taken.
##
##  *Notes*
##
##  The single command: `PqNextClass(<i>);' is equivalent to executing
##
##  \){\kernttindent}PqSetupTablesForNextClass(<i>);
##  \){\kernttindent}PqTails(<i>, 0);
##  \){\kernttindent}PqDoConsistencyChecks(<i>, 0, 0);
##  \){\kernttindent}PqCollectDefiningRelations(<i>);
##  \){\kernttindent}PqDoExponentChecks(<i>);
##  \){\kernttindent}PqEliminateRedundantGenerators(<i>);
##
##  For those familiar with the `pq' binary, `PqNextClass' performs menu item
##  6 of the main $p$-Quotient menu.
##
InstallGlobalFunction( PqNextClass, function( arg )
local datarec;
  PQ_OTHER_OPTS_CHK("PqNextClass", true);
  datarec := CallFuncList(ANUPQDataRecord, arg);
  PQ_GRP_EXISTS_CHK( datarec );
  PQ_NEXT_CLASS( datarec );
end );

#############################################################################
##
#F  PQ_P_COVER( <datarec> ) . . . . . . . . . . . . . . . . p-Q menu option 7
##
##  directs  the  `pq'  binary  to  compute   the   $p$-covering   group   of
##  `<datarec>.group', using option 7 of the main $p$-Quotient menu.
##
InstallGlobalFunction( PQ_P_COVER, function( datarec )
local savefile;
  PQ_MENU(datarec, "pQ");
  Unbind( datarec.pCover );
  datarec.match := true;
  ToPQ(datarec, [ 7 ], [ "  #compute p-cover" ]);
  PQ_SET_GRP_DATA(datarec);
  datarec.pcoverclass := datarec.class;
  Unbind(datarec.capable);
end );

#############################################################################
##
#F  PqComputePCover( <i> ) . . . . . . . .  user version of p-Q menu option 7
#F  PqComputePCover()
##
##  for the <i>th or default interactive {\ANUPQ} process, direct the `pq' to
##  compute the $p$-covering group of `ANUPQData.io[<i>].group'.
##
##  *Notes*
##
##  The single command: `PqComputePCover(<i>);' is equivalent to executing
##
##  \){\kernttindent}PqSetupTablesForNextClass(<i>);
##  \){\kernttindent}PqTails(<i>, 0);
##  \){\kernttindent}PqDoConsistencyChecks(<i>, 0, 0);
##  \){\kernttindent}PqEliminateRedundantGenerators(<i>);
##
##  For those familiar with the `pq' binary, `PqComputePCover' performs  menu
##  item 7 of the main $p$-Quotient menu.
##
InstallGlobalFunction( PqComputePCover, function( arg )
local datarec;
  datarec := CallFuncList(ANUPQDataRecord, arg);
  PQ_GRP_EXISTS_CHK( datarec );
  PQ_P_COVER( datarec );
end );

#############################################################################
##
#F  PQ_EVALUATE_IDENTITIES(<datarec>) . evaluate Identities option identities
##
InstallGlobalFunction( PQ_EVALUATE_IDENTITIES, function( datarec )
local identity, procId;
  procId := datarec.procId;
  for identity in VALUE_PQ_OPTION("Identities", [], datarec) do
    PQ_EVALUATE_IDENTITY(procId, identity);
  od;
  PQ_ELIMINATE_REDUNDANT_GENERATORS( datarec );
  Info(InfoANUPQ, 1, "Class ", datarec.class, " with ",
                     PqNrPcGenerators(procId), " generators." );
end );

#############################################################################
##
#F  PqEvaluateIdentities( <i> ) . . . . evaluate Identities option identities
#F  PqEvaluateIdentities()
##
##  for the  <i>th  or  default  interactive  {\ANUPQ}  process,  invoke  the
##  evaluation  of  identities  defined  by  the  `Identities'  option,   and
##  eliminate any redundant pc generators formed. Since a previous  value  of
##  `Identities'  is  saved  in  the  data  record  of  the  process,  it  is
##  unnecessary to pass the `Identities' if set previously.
##
##  *Note:* This function is mainly implemented at the {\GAP} level. It  does
##  not correspond to a menu item of the `pq' program.
##
InstallGlobalFunction( PqEvaluateIdentities, function( arg )
  PQ_OTHER_OPTS_CHK("PqEvaluateIdentities", true);
  PQ_EVALUATE_IDENTITIES( CallFuncList(ANUPQDataRecord, arg) );
end );

#############################################################################
##
#F  PQ_FINISH_NEXT_CLASS( <datarec> ) . . .  take the p-cover to a next class
##
##  does the usual operations required after calculating the  <p>-cover  that
##  brings the pcp back to a next class, except that it  also  slips  in  the
##  evaluation of the identities of the `Identities' option.
##
InstallGlobalFunction( PQ_FINISH_NEXT_CLASS, function( datarec )
  PushOptions( rec(nonuser := true) );
  PQ_COLLECT_DEFINING_RELATIONS( datarec );
  PQ_DO_EXPONENT_CHECKS( datarec, [1, datarec.class] );
  PQ_EVALUATE_IDENTITIES( datarec );
  PopOptions();
end );

#############################################################################
##
#F  PQ_COLLECT( <datarec>, <word> ) . . . . . . . . . . . A p-Q menu option 1
##
##  instructs the  `pq'  binary  to  do  a  collection  on  <word>  a  string
##  representing a word in the  current  pc  generators,  e.g.  `"x3*x2*x1"',
##  using option 1 of the interactive $p$-Quotient menu.
##
InstallGlobalFunction( PQ_COLLECT, function( datarec, word )

  PQ_MENU(datarec, "ApQ"); #we need options from the Advanced p-Q Menu
  ToPQ(datarec, [ 1 ], [ "  #do individual collection" ]);
  datarec.match := "The result of collection is";
  ToPQ(datarec, [ word, ";"], [ "  #word to be collected" ]);
  return PQ_WORD(datarec);
end );

#############################################################################
##
#F  PQ_CHECK_WORD( <datarec>, <wordOrList>, <ngens> ) . .  check word or list
##
##  checks that <wordOrList> is a valid word in  the  current  pc  generators
##  (<ngens> is the number of current pc  generators)  or  a  valid  list  of
##  generator number-exponent pairs that  will  generate  such  a  word,  and
##  either emits an error or returns the valid word.
##
InstallGlobalFunction( PQ_CHECK_WORD, function( datarec, wordOrList, ngens )
local parts, gens;
  if not IsList(wordOrList) or 
     not IsString(wordOrList) and 
     not ForAll(wordOrList, pair -> IsList(pair) and 2 = Length(pair) and
                                    ForAll(pair, IsInt) ) then
    Error( "argument <wordOrList> should be a string e.g. \"x3*x2^2*x1\",\n",
           "or a list of gen'r no.-exponent pairs from which such a word ",
           "may be generated\n" );
  fi;
  if IsString(wordOrList) then
    #check word makes sense
    PqParseWord(ngens, wordOrList);
    
  elif IsList(wordOrList) then
    if not ForAll(wordOrList, 
                  pair -> IsPosInt(pair[1]) and pair[1] <= ngens) then
      Error( "generator numbers in argument <wordOrList> must be in the ",
             "range: ", "[1 .. ", ngens, "]\n" );
    fi;
    wordOrList := JoinStringsWithSeparator(
                      List( wordOrList, 
                            pair -> Concatenation( "x", String(pair[1]),
                                                   "^", String(pair[2]) ) ),
                      "*" );
  fi;
  if IsEmpty(wordOrList) then
    wordOrList := "x1^0";
  fi;
  return wordOrList;
end );

#############################################################################
##
#F  PQ_WORD( <datarec> ) . . . .  parse pq output for a word in pc generators
##
##  parses `<datarec>.matchedline' for a word in the  current  pc  generators
##  and returns it as a list of gen'r no.-exponent  pairs;  `<datarec>.match'
##  must have previously been set.
##
InstallGlobalFunction( PQ_WORD, function( datarec )
local word;
  word := SplitString( datarec.matchedline{[Length(datarec.match) + 1 ..
                                            Length(datarec.matchedline)]},
                       "", " \n" );
  if word = [ "IDENTITY" ] then
    word := [];
  else
    word := List( word, 
                  function(syl)
                    syl := List( SplitString(syl, "", ".^"), Int );
                    if 1 = Length(syl) then
                      Add(syl, 1);
                    fi;
                    return syl;
                  end );
  fi;
  PQ_UNBIND(datarec, ["match", "matchedline"]);
  return word;
end );

#############################################################################
##
#F  PQ_CHK_COLLECT_COMMAND_ARGS( <args> ) . . check args for a collect cmd ok
##
##  returns a list of valid arguments for  a  low-level  collect  command  or
##  generates an error.
##
InstallGlobalFunction( PQ_CHK_COLLECT_COMMAND_ARGS, function( args )
local datarec, wordOrList, ngens;
  if IsEmpty(args) or 2 < Length(args) then
    Error( "1 or 2 arguments expected\n");
  fi;
  wordOrList := args[Length(args)];
  datarec := CallFuncList(ANUPQDataRecord, args{[1..Length(args) - 1]});
  ngens := datarec.ngens[ Length(datarec.ngens) ];
  wordOrList := PQ_CHECK_WORD(datarec, wordOrList, ngens);
  return [datarec, wordOrList];
end );

#############################################################################
##
#F  PqCollect( <i>, <word> ) . . . . . .  user version of A p-Q menu option 1
#F  PqCollect( <word> )
##
##  for the <i>th or default interactive {\ANUPQ} process, instruct the  `pq'
##  program to do a collection on <word>, a word in the current pc generators
##  (the form of <word> required is described below). `PqCollect' returns the
##  resulting word of the collection as a list of generator number,  exponent
##  pairs (the same form as the second allowed  input  form  of  <word>;  see
##  below).
##
##  The argument <word> may be input in either of the following ways:
##
##  \beginlist%ordered
##
##  \item{1.}
##  <word> may be a string, where the <i>th pc generator  is  represented  by
##  `x<i>', e.g.~`"x3*x2^2*x1"'. This way is quite versatile  as  parentheses
##  and left-normed commutators -- using square brackets, in the same way  as
##  `PqGAPRelators' (see~"PqGAPRelators") -- are permitted; <word> is checked
##  for correct syntax via `PqParseWord' (see~"PqParseWord").
##
##  \item{2.}
##  Otherwise, <word> must be a list of generator number, exponent  pairs  of
##  integers, i.e.~ each pair represents a ``syllable'' so that  `[  [3,  1],
##  [2, 2], [1, 1] ]' represents the same word as that of the  example  given
##  for the first allowed form of <word>.
##
##  \endlist
##
##  *Note:* For those familiar with the  `pq'  program,  `PqCollect'  performs
##  menu item 1 of the Advanced $p$-Quotient menu.
##
InstallGlobalFunction( PqCollect, function( arg )
  return CallFuncList( PQ_COLLECT, PQ_CHK_COLLECT_COMMAND_ARGS(arg) );
end );

#############################################################################
##
#F  PQ_SOLVE_EQUATION( <datarec>, <a>, <b> ) . . . . . .  A p-Q menu option 2
##
##  inputs data to the `pq' binary for option 2 of the Advanced  $p$-Quotient
##  menu, to solve $<a> * <x> = <b>$ for <x>.
##
InstallGlobalFunction( PQ_SOLVE_EQUATION, function( datarec, a, b )
  PQ_MENU(datarec, "ApQ"); #we need options from the Advanced p-Q Menu
  ToPQ(datarec, [ 2 ], [ "  #solve equation" ]);
  ToPQ(datarec, [ a, ";" ], [ "  #word a" ]);
  ToPQ(datarec, [ b, ";" ], [ "  #word b" ]);
end );

#############################################################################
##
#F  PqSolveEquation( <i>, <a>, <b> ) . .  user version of A p-Q menu option 2
#F  PqSolveEquation( <a>, <b> )
##
##  for the <i>th or default interactive {\ANUPQ} process,  direct  the  `pq'
##  binary to solve $<a> * <x> = <b>$ for <x>.
##
##  *Note:*
##  For those familiar  with  the  `pq'  binary,  `PqSolveEquation'  performs
##  menu item 2 of the Advanced $p$-Quotient menu.
##
InstallGlobalFunction( PqSolveEquation, function( arg )
local len, datarec;
  len := Length(arg);
  if not(len in [2,3]) then
    Error("expected 2 or 3 arguments\n");
  fi;
  #@need to add argument checking for a and b@
  datarec := CallFuncList(ANUPQDataRecord, arg{[1 .. len - 2]});
  PQ_SOLVE_EQUATION( datarec, arg[len - 1], arg[len] );
end );

#############################################################################
##
#F  PQ_COMMUTATOR( <datarec>, <words>, <pow>, <item> ) . A p-Q menu opts 3/24
##
##  inputs data to the `pq' binary  for  option  3  or  24  of  the  Advanced
##  $p$-Quotient menu, to compute the left  normed  commutator  of  the  list
##  <words> of words in the generators raised to  the  integer  power  <pow>,
##  where <item> is `"3 #commutator"' for option 3  or  `"24  #commutator  of
##  defining genrs"' for option 24.
##
InstallGlobalFunction( PQ_COMMUTATOR, function( datarec, words, pow, item )
local i;
  PQ_MENU(datarec, "ApQ"); #we need options from the Advanced p-Q Menu
  ToPQ(datarec, item[1], item[2]);
  ToPQ(datarec, [ Length(words) ], [ "  #no. of components" ]);
  for i in [1..Length(words)] do
    ToPQ(datarec, [ words[i], ";" ], [ "  #word ", i ]);
  od;
  datarec.match := "The commutator is";
  ToPQ(datarec, [ pow ], [ "  #power" ]);
  return PQ_WORD(datarec);
end );

#############################################################################
##
#F  PQ_COMMUTATOR_CHK_ARGS( <args> ) . . . . check args for commutator cmd ok
##
##  returns a list of valid arguments for a low-level commutator  command  or
##  generates an error.
##
InstallGlobalFunction( PQ_COMMUTATOR_CHK_ARGS, function( args )
local len, words, pow, item, datarec, ngens;
  len := Length(args);
  if not(len in [3, 4]) then
    Error("expected 3 or 4 arguments\n");
  fi;
  words := args[len - 2];
  pow   := args[len - 1];
  item  := args[len];
  if not IsPosInt(pow) then
    Error( "argument <pow> must be a positive integer\n" );
  fi;
  datarec := CallFuncList(ANUPQDataRecord, args{[1 .. len - 3]});
  if item[1][1] = 3 then
    ngens := datarec.ngens[ Length(datarec.ngens) ];
  else
    ngens := datarec.ngens[ 1 ];
  fi;
  words := List( words, w -> PQ_CHECK_WORD(datarec, w, ngens) );
  return [datarec, words, pow, item];
end );

#############################################################################
##
#F  PqCommutator( <i>, <words>, <pow> ) . user version of A p-Q menu option 3
#F  PqCommutator( <words>, <pow> )
##
##  for  the  <i>th  or  default  interactive  {\ANUPQ}  process,  compute  a
##  user-defined commutator in the pc generators of  the  class  1  quotient,
##  i.e.~the pc generators that correspond to the original fp or pc group  of
##  the process, and return  the  result  as  a  list  of  generator  number,
##  exponent pairs. The form required for each word of <words> is the same as
##  that required for the <word> argument of  `PqCollect'  (see~"PqCollect").
##  The form of  the  output  word  is  also  the  same  as  for  `PqCollect'
##  (see~"PqCollect").
##
##  *Notes*
##
##  It is illegal for any word of <words> to contain pc generators of  weight
##  larger      than      1.      Except      for      this      distinction,
##  `PqCommutatorDefiningGenerators'   works   just    like    `PqCommutator'
##  (see~"PqCommutator"). 
##
##  For those familiar with the `pq' program, `PqCommutatorDefiningGenerators'
##  performs menu item 24 of the Advanced $p$-Quotient menu.
##
InstallGlobalFunction( PqCommutator, function( arg )
  return CallFuncList( PQ_COMMUTATOR, 
                       PQ_COMMUTATOR_CHK_ARGS( 
                           Concatenation( arg, [[[3], ["  #commutator"]]] ) ) );
end );

#############################################################################
##
#F  PQ_SETUP_TABLES_FOR_NEXT_CLASS( <datarec> ) . . . . . A p-Q menu option 6
##
##  inputs data to the `pq' binary for option 6 of the Advanced  $p$-Quotient
##  menu to set up tables for next class.
##
InstallGlobalFunction( PQ_SETUP_TABLES_FOR_NEXT_CLASS, function( datarec )
  PQ_MENU(datarec, "ApQ"); #we need options from the Advanced p-Q Menu
  ToPQ(datarec, [ 6 ], [ "  #set up tables for next class" ]);
  datarec.match := true;
  PQ_SET_GRP_DATA(datarec); #Just to be sure it's up-to-date
  datarec.setupclass := datarec.class;
end );

#############################################################################
##
#F  PqSetupTablesForNextClass( <i> ) . .  user version of A p-Q menu option 6
#F  PqSetupTablesForNextClass()
##
##  for the <i>th or default interactive {\ANUPQ} process,  direct  the  `pq'
##  binary  to  set  up  tables  for  the  next  class.  As  as  side-effect,
##  after   `PqSetupTablesForNextClass(<i>)'   the    value    returned    by
##  `PqPClass(<i>)' will be one more than it was previously.
##
##  *Note:*
##  For those familiar  with  the  `pq'  binary,  `PqSetupTablesForNextClass'
##  performs menu item 6 of the Advanced $p$-Quotient menu.
##
InstallGlobalFunction( PqSetupTablesForNextClass, function( arg )
local datarec;
  datarec := CallFuncList(ANUPQDataRecord, arg);
  PQ_SETUP_TABLES_FOR_NEXT_CLASS( datarec );
end );

#############################################################################
##
#F  PQ_INSERT_TAILS( <datarec>, <weight>, <which> )  . .  A p-Q menu option 7
##
##  inputs data to the `pq' binary for option 7 of the Advanced  $p$-Quotient
##  menu, to add and/or compute tails.
##
InstallGlobalFunction( PQ_INSERT_TAILS, function( datarec, weight, which )
local intwhich;
  PQ_MENU(datarec, "ApQ"); #we need options from the Advanced p-Q Menu
  intwhich := Position( [ "compute and add", "add", "compute" ], which ) - 1;
  ToPQ(datarec, [ 7 ], [ "  #", which, " tails" ]);
  ToPQ(datarec, [ weight ], [ " #weight of tails" ]);
  ToPQ(datarec, [ intwhich ], [ "  #", which ]);
  if intwhich <= 1 then
    datarec.match := true;
    PQ_SET_GRP_DATA(datarec);
  fi;
end );

#############################################################################
##
#F  PQ_CHK_TAILS_ARGS( <args> ) . . . . .  check args for insert tails cmd ok
##
InstallGlobalFunction( PQ_CHK_TAILS_ARGS, function( args )
local weight, datarec;
  if IsEmpty(args) or 2 < Length(args) then
    Error( "1 or 2 arguments expected\n");
  fi;
  weight := args[Length(args)];
  datarec := CallFuncList(ANUPQDataRecord, args{[1 .. Length(args) - 1]});
  if not IsBound(datarec.setupclass) or datarec.class <> datarec.setupclass then
    Error( "tables to start next class have not been set up.\n",
           "Please call `PqSetupTablesForNextClass' first\n" );
  fi;
  if not(weight = 0 or weight in [2 .. datarec.class]) then
    Error( "argument <weight> should be an integer in [0] U [2 .. <class>],\n",
           "where <class> is the current class (", datarec.class, ")\n" );
  fi;
  return datarec;
end );

#############################################################################
##
#F  PqAddTails( <i>, <weight> ) . . . .  adds tails using A p-Q menu option 7
#F  PqAddTails( <weight> )
##
##  for the <i>th or default interactive {\ANUPQ} process,  direct  the  `pq'
##  binary to add tails of weight <weight> if  <weight>  is  in  the  integer
##  range `[2 .. PqPClass(<i>)]' (assuming <i> is the number of the  process)
##  or for all weights if `<weight> = 0'. See `PqTails' ("PqTails") for  more
##  details.
##
##  *Note:*
##  For those familiar with the `pq' binary, `PqAddTails' uses menu item 7 of
##  the Advanced $p$-Quotient menu.
##
InstallGlobalFunction( PqAddTails, function( arg )
  PQ_INSERT_TAILS( PQ_CHK_TAILS_ARGS(arg), arg[Length(arg)], "add" );
end );

#############################################################################
##
#F  PqComputeTails( <i>, <weight> ) . . computes tails using A p-Q menu opt 7
#F  PqComputeTails( <weight> )
##
##  for the <i>th or default interactive {\ANUPQ} process,  direct  the  `pq'
##  binary to compute tails of weight <weight> if <weight> is in the  integer
##  range `[2 .. PqPClass(<i>)]' (assuming <i> is the number of the  process)
##  or for all weights if `<weight> = 0'. See `PqTails' ("PqTails") for  more
##  details.
##
##  *Note:*
##  For those familiar with the `pq' binary, `PqComputeTails' uses menu  item
##  7 of the Advanced $p$-Quotient menu.
##
InstallGlobalFunction( PqComputeTails, function( arg )
  PQ_INSERT_TAILS( PQ_CHK_TAILS_ARGS(arg), arg[Length(arg)], "compute" );
end );

#############################################################################
##
#F  PqTails( <i>, <weight> ) . computes and adds tails using A p-Q menu opt 7
#F  PqTails( <weight> )
##
##  for the <i>th or default interactive {\ANUPQ} process,  direct  the  `pq'
##  binary to compute and add tails of weight <weight> if <weight> is in  the
##  integer range `[2 .. PqPClass(<i>)]' (assuming <i> is the number  of  the
##  process) or for all weights if `<weight> = 0'.
##
##  If <weight> is non-zero, then tails that  introduce  new  generators  for
##  only weight <weight> are computed and added, and  in  this  case  and  if
##  `<weight> \< PqPClass(<i>)', it is assumed that the tails that  introduce
##  new  generators  for  each  weight  from  `PqPClass(<i>)'  downto  weight
##  `<weight>  +  1'  have  already  been  added.  You  may  wish   to   call
##  `PqSetMetabelian' (see~"PqSetMetabelian") prior to calling `PqTails'.
##
##  *Notes*
##
##  For its use in the context of finding the next class  see  "PqNextClass";
##  in     particular,     a     call     to      `PqSetupTablesForNextClass'
##  (see~"PqSetupTablesForNextClass")  needs  to  have  been  made  prior  to
##  calling `PqTails'.
##
##  The single command: `PqTails(<i>, <weight>);' is equivalent to
##
##  \){\kernttindent}PqComputeTails(<i>, <weight>);
##  \){\kernttindent}PqAddTails(<i>, <weight>);
##
##  For those familiar with the `pq' binary, `PqTails' uses menu  item  7  of
##  the Advanced $p$-Quotient menu.
##
InstallGlobalFunction( PqTails, function( arg )
  PQ_INSERT_TAILS(PQ_CHK_TAILS_ARGS(arg), arg[Length(arg)], "compute and add");
end );

#############################################################################
##
#F  PQ_DO_CONSISTENCY_CHECKS(<datarec>, <weight>, <type>) .  A p-Q menu opt 8
##
##  inputs data to the `pq' binary for option 8 of the Advanced  $p$-Quotient
##  menu, to do consistency checks.
##
InstallGlobalFunction( PQ_DO_CONSISTENCY_CHECKS, 
function( datarec, weight, type )
  PQ_MENU(datarec, "ApQ"); #we need options from the Advanced p-Q Menu
  ToPQ(datarec, [ 8 ], [ "  #check consistency" ]);
  ToPQ(datarec, [ weight ], [ " #weight to be checked" ]);
  ToPQ(datarec, [ type ], [ "  #type" ]);
end );

#############################################################################
##
#F  PqDoConsistencyChecks(<i>,<weight>,<type>) . user ver of A p-Q menu opt 8
#F  PqDoConsistencyChecks( <weight>, <type> )
##
##  for the <i>th or default interactive  {\ANUPQ}  process,  do  consistency
##  checks for weight <weight> if <weight> is in the  integer  range  `[3  ..
##  PqPClass(<i>)]' (assuming <i> is the number of the process)  or  for  all
##  weights if `<weight> = 0', and for type <type> if <type> is in the  range
##  `[1, 2, 3]' (see below) or for all types if `<type> = 0'. (For its use in
##  the context of finding the next class see "PqNextClass".)
##
##  The  *type*   of   a   consistency   check   is   defined   as   follows.
##  `PqDoConsistencyChecks(<i>, <weight>, <type>)' for  <weight>  in  `[3  ..
##  PqPClass(<i>)]' and the given  value  of  <type>  invokes  the  following
##  `PqJacobi' checks (see~"PqDoConsistencyCheck"):
##
##  \beginitems
##
##  `<type> = 1':&
##  `PqJacobi(<i>, <a>, <a>, <a>)' checks for  pc  generators  of  index  <a>
##  satisfying `2 * PqWeight(<i>, <a>) + 1 = <weight>'.
##
##  `<type> = 2':&
##  `PqJacobi(<i>, <b>, <b>, <a>)' checks for pc generators of  indices  <b>,
##  <a> satisfying `<b> > <a>' and `PqWeight(<i>, <b>) + PqWeight(<i>, <a>) +
##  1 = <weight>'.
##
##  `<type> = 3':&
##  `PqJacobi(<i>, <c>, <b>, <a>)' checks for pc generators of  indices  <c>,
##  <b>, <a> satisfying `<c> > <b> > <a>' and the sum of the weights of these
##  generators equals <weight>.
##
##  \enditems
##
##  *Notes*
##
##  `PqWeight(<i>, <j>)' returns the weight of the <j>th  pc  generator,  for
##  process <i> (see~"PqWeight").
##
##  It is assumed that tails for the given weight (or weights)  have  already
##  been added (see~"PqTails").
##
##  For those familiar with the `pq' binary, `PqDoConsistencyChecks' performs
##  menu item 8 of the Advanced $p$-Quotient menu.
##
InstallGlobalFunction( PqDoConsistencyChecks, function( arg )
local len, datarec, weight, type;
  len := Length(arg);
  if not(len in [2, 3]) then
    Error("expected 2 or 3 arguments\n");
  fi;
  weight := arg[len - 1];
  type   := arg[len];
  arg := arg{[1 .. len - 2]};
  datarec := CallFuncList(ANUPQDataRecord, arg);
  if not IsBound(datarec.setupclass) or datarec.class <> datarec.setupclass then
    Error( "tables to start next class have not been set up.\n",
           "Please call `PqSetupTablesForNextClass' first\n" );
  fi;
  if not(weight = 0 or weight in [3 .. datarec.class]) then
    Error( "argument <weight> should be an integer in [0] U [3 .. <class>],\n",
           "where <class> is the current class (", datarec.class, ")\n" );
  fi;
  if not(type in [0..3]) then
    Error( "argument <type> should be in [0,1,2,3]\n" );
  fi;
  PQ_DO_CONSISTENCY_CHECKS( datarec, weight, type );
end );

#############################################################################
##
#F  PQ_COLLECT_DEFINING_RELATIONS( <datarec> ) . . . . .  A p-Q menu option 9
##
##  inputs data to the `pq' binary for option 9 of the Advanced  $p$-Quotient
##  menu, to collect defining relations.
##
InstallGlobalFunction( PQ_COLLECT_DEFINING_RELATIONS, function( datarec )
  PQ_MENU(datarec, "ApQ"); #we need options from the Advanced p-Q Menu
  ToPQ(datarec, [ 9 ], [ "  #collect defining relations" ]);
end );

#############################################################################
##
#F  PqCollectDefiningRelations( <i> ) . . user version of A p-Q menu option 9
#F  PqCollectDefiningRelations()
##
##  for the <i>th or default interactive {\ANUPQ} process,  direct  the  `pq'
##  binary to collect the images of the defining relations of the original fp
##  group of the process, with respect to the current pc presentation, in the
##  context of finding the  next  class  (see~"PqNextClass").  If  the  tails
##  operation  is  not  complete  then  the  relations   may   be   evaluated
##  incorrectly.
##
##  *Note:*
##  For those familiar with  the  `pq'  binary,  `PqCollectDefiningRelations'
##  performs menu item 9 of the Advanced $p$-Quotient menu.
##
InstallGlobalFunction( PqCollectDefiningRelations, function( arg )
local datarec;
  datarec := CallFuncList(ANUPQDataRecord, arg);
  PQ_COLLECT_DEFINING_RELATIONS( datarec );
end );

#############################################################################
##
#F  PQ_DO_EXPONENT_CHECKS( <datarec>, <bnds> ) . . . . . A p-Q menu option 10
##
##  inputs data to the `pq' binary to do exponent checks for weights  between
##  <bnds> inclusive, using option 10 of the Advanced $p$-Quotient menu.
##
InstallGlobalFunction( PQ_DO_EXPONENT_CHECKS, function( datarec, bnds )
  #@does default only at the moment@
  PQ_MENU(datarec, "ApQ"); #we need options from the Advanced p-Q Menu
  datarec.match := "Group is complete";
  ToPQ(datarec, [ 10 ], [ " #do exponent checks" ]);
  if IsBound(datarec.matchedline) and
     IsMatchingSublist(datarec.matchedline, "Group is complete") then
    PQ_UNBIND(datarec, ["match", "matchedline"]);
    datarec.complete := true;
    return;
  elif IsMatchingSublist(datarec.line, "Input exponent law") then
    ToPQ(datarec, [ VALUE_PQ_OPTION("Exponent", 0, datarec) ],
                  [ "  #exponent" ]);
  fi;
  ToPQ(datarec, [ bnds[1] ], [ " #start weight" ]);
  ToPQ(datarec, [ bnds[2] ], [ " #end weight"   ]);
  ToPQ(datarec, [ 1 ], [ "  #do default check" ]);
  Unbind(datarec.match);
end );

#############################################################################
##
#F  PqDoExponentChecks(<i>[: Bounds := <list>]) . user ver A p-Q menu opt. 10
#F  PqDoExponentChecks([: Bounds := <list>])
##
##  for the <i>th or default interactive {\ANUPQ} process, direct  the  `pq'
##  binary to do exponent checks for weights (inclusively) between the bounds
##  of `Bounds' or for all weights if `Bounds' is not given. The value <list>
##  of `Bounds' (assuming the interactive process is numbered <i>) should  be
##  a list of  two  integers  <low>,  <high>  satisfying  $1  \le  <low>  \le
##  <high> \le `PqPClass(<i>)'$ (see~"PqPClass").
##
##  *Note:*
##  For those familiar with the `pq'  binary,  `PqDoExponentChecks'  performs
##  menu item 10 of the Advanced $p$-Quotient menu.
##
InstallGlobalFunction( PqDoExponentChecks, function( arg )
local datarec;
  PQ_OTHER_OPTS_CHK("PqDoExponentChecks", true);
  datarec := PQ_DATA_CHK(arg);
  PQ_DO_EXPONENT_CHECKS( datarec, PQ_BOUNDS(datarec, datarec.class) );
end );

#############################################################################
##
#F  PQ_ELIMINATE_REDUNDANT_GENERATORS( <datarec> ) . . . A p-Q menu option 11
##
##  inputs data to the `pq' binary for option 11 of the Advanced $p$-Quotient
##  menu, to eliminate redundant generators.
##
InstallGlobalFunction( PQ_ELIMINATE_REDUNDANT_GENERATORS, function( datarec )
  PQ_MENU(datarec, "ApQ"); #we need options from the Advanced p-Q Menu
  ToPQ(datarec, [ 11 ], [ " #eliminate redundant generators" ]);
  datarec.match := true;
  PQ_SET_GRP_DATA(datarec);
end );

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

--> maximum size reached

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

[ Dauer der Verarbeitung: 0.43 Sekunden  (vorverarbeitet)  ]