#############################################################################
####
##
#W  anupqopt.gi                ANUPQ package                    Werner Nickel
#W                                                                Greg Gamble
##
##  Install file for functions to do with option manipulation.
##    
#Y  Copyright (C) 2001  Lehrstuhl D fuer Mathematik,  RWTH Aachen,  Germany
##

#############################################################################
##
#V  PQ_FUNCTION . . . . . . . . . internal functions called by user functions 
##
##  A record whose fields are (function)  names  and  whose  values  are  the
##  internal functions called by the functions with those names.
##
InstallValue( PQ_FUNCTION, 
              rec( Pq                   := PQ_EPI_OR_PCOVER,
                   PqDescendants        := PQ_DESCENDANTS,
                   StandardPresentation := PQ_EPIMORPHISM_STANDARD_PRESENTATION,
                   PqDescendantsTreeCoclassOne := PqDescendantsTreeCoclassOne
                  )
             );

#############################################################################
##
#V  ANUPQoptions  . . . . . . . . . . . . . . . . . . . .  admissible options
##
##  is a record of lists of names of admissible {\ANUPQ} options,  such  that
##  each field is either the name of a (``key'') {\ANUPQ}  function  and  the
##  corresponding value is the list of option names that are  admissible  for
##  the function.
##
InstallValue( ANUPQoptions, 
              rec( # options for `Pq' and `PqEpimorphism'
                   Pq  := [ "Prime", 
                            "ClassBound", 
                            "Exponent", 
                            "Metabelian", 
                            "OutputLevel", 
                            "Relators", 
                            "Identities",
                            "GroupName", 
                            "SetupFile",
                            "PqWorkspace",
                            "RedoPcp" ],

                   # options for `PqDescendants'
                   PqDescendants
                       := [ "ClassBound", 
                            "OrderBound", 
                            "Relators", 
                            "GroupName", 
                            "StepSize", 
                            "PcgsAutomorphisms", 
                            "RankInitialSegmentSubgroups", 
                            "SpaceEfficient", 
                            "CapableDescendants", 
                            "AllDescendants", 
                            "Exponent", 
                            "Metabelian", 
                            "SubList", 
                            "BasicAlgorithm",
                            "CustomiseOutput",
                            "SetupFile",
                            "PqWorkspace" ],

                   # options for `[Epimorphism][Pq]StandardPresentation'
                   StandardPresentation
                       := [ "Prime", 
                            "pQuotient",
                            "ClassBound", 
                            "Relators", 
                            "GroupName", 
                            "PcgsAutomorphisms", 
                            "Exponent", 
                            "Metabelian", 
                            "OutputLevel", 
                            "StandardPresentationFile", 
                            "SetupFile",
                            "PqWorkspace" ],

                   # options for `PqDescendantsTreeCoclassOne'
                   PqDescendantsTreeCoclassOne
                       := [ "ClassBound", 
                            "OrderBound", 
                            "Relators", 
                            "GroupName", 
                            "StepSize", 
                            "PcgsAutomorphisms", 
                            "RankInitialSegmentSubgroups", 
                            "SpaceEfficient", 
                            "CapableDescendants", 
                            "AllDescendants", 
                            "Exponent", 
                            "Metabelian", 
                            "SubList", 
                            "BasicAlgorithm",
                            "CustomiseOutput",
                            "TreeDepth",
                            "SetupFile",
                            "PqWorkspace" ],

                   PqList 
                       := [ "SubList" ],
                   PqPcPresentation
                       := [ "Prime", 
                            "ClassBound", 
                            "Exponent", 
                            "Metabelian", 
                            "OutputLevel", 
                            "Relators", 
                            "Identities",
                            "GroupName" ],
                   PqNextClass
                       := [ "QueueFactor" ],
                   PqEvaluateIdentities
                       := [ "Identities" ],
                   PqDoExponentChecks
                       := [ "Bounds" ],
                   PqDisplayStructure
                       := [ "Bounds" ],
                   PqDisplayAutomorphisms
                       := [ "Bounds" ],
                   PqSPComputePcpAndPCover
                       := [ "Prime", 
                            "ClassBound", 
                            "Exponent", 
                            "Metabelian", 
                            "OutputLevel", 
                            "Relators", 
                            "GroupName" ],
                   PqSPSavePresentation
                       := [ "ClassBound", 
                            "PcgsAutomorphisms", 
                            "StandardPresentationFile" ],
                   PqPGSetDescendantToPcp
                       := [ "Filename" ], 
                   PqPGSupplyAutomorphisms
                       := [ "NumberOfSolubleAutomorphisms",
                            "RelativeOrders" ],
                   PqPGConstructDescendants
                       := [ "ClassBound", 
                            "OrderBound", 
                            "StepSize", 
                            "PcgsAutomorphisms", 
                            "RankInitialSegmentSubgroups", 
                            "SpaceEfficient", 
                            "CapableDescendants", 
                            "AllDescendants", 
                            "Exponent", 
                            "Metabelian", 
                            "BasicAlgorithm",
                            "CustomiseOutput" ],
                   PqAPGDegree
                       := [ "Exponent" ],
                   PqAPGPermutations
                       := [ "PcgsAutomorphisms",
                            "SpaceEfficient",
                            "PrintAutomorphisms",
                            "PrintPermutations" ],
                   PqAPGOrbits
                       := [ "PcgsAutomorphisms",
                            "SpaceEfficient",
                            "CustomiseOutput" ],
                   PqAPGOrbitRepresentatives
                       := [ "PcgsAutomorphisms", 
                            "SpaceEfficient", 
                            "CapableDescendants", 
                            "AllDescendants", 
                            "Exponent", 
                            "Metabelian", 
                            "CustomiseOutput",
                            "Filename" ], 
                   PqAPGSingleStage
                       := [ "StepSize", 
                            "PcgsAutomorphisms", 
                            "RankInitialSegmentSubgroups", 
                            "SpaceEfficient", 
                            "CapableDescendants", 
                            "AllDescendants", 
                            "Exponent", 
                            "Metabelian", 
                            "BasicAlgorithm",
                            "CustomiseOutput" ]
                  )
             );

#############################################################################
##
#F  AllANUPQoptions() . . . . . . . .  lists all options of the ANUPQ package
##
##  lists all the {\GAP}  options  defined  for  functions  of  the  {\ANUPQ}
##  package.
##
InstallGlobalFunction( AllANUPQoptions, function()
  return Set( Concatenation(
                  List( RecNames(ANUPQoptions), fld -> ANUPQoptions.(fld) )
                  ) );
end );

#############################################################################
##
#V  ANUPQGlobalOptions . . . . .  options that can be set globally by PqStart
##
##  A list of the options that `PqStart' can set and thereby  make  available
##  to any function  interacting  with  the  {\ANUPQ}  process  initiated  by
##  `PqStart'.
##
InstallValue( ANUPQGlobalOptions, [ "Prime", "Exponent", "Relators" ] );

#############################################################################
##
#V  ANUPQoptionChecks . . . . . . . . . . . the checks for admissible options
##
##  A record whose fields are the names of admissible ANUPQ options and whose
##  values are one-argument functions that return `true' when given  a  value
##  that is a valid value for the option, and `false' otherwise.
##
InstallValue( ANUPQoptionChecks,
              rec( Prime := x -> IsInt(x) and IsPrimeInt(x),
                   pQuotient  := IsPcGroup and IsPGroup,
                   ClassBound := IsPosInt,
                   OrderBound := IsPosInt,
                   Exponent   := IsPosInt,
                   Metabelian := IsBool,
                   GroupName  := IsString,
                   Identities := x -> IsList(x) and ForAll(x, IsFunction),
                   OutputLevel := x -> x in [0..3],
                   Relators   := x -> IsList(x) and ForAll(x, IsString),
                   StandardPresentationFile := IsString,
                   SetupFile  := IsString,
                   PqWorkspace := IsPosInt,
                   StepSize := x -> IsPosInt(x) or
                                    (IsList(x) and ForAll(x, IsPosInt)), 
                   PcgsAutomorphisms := IsBool,
                   BasicAlgorithm := IsBool,
                   RankInitialSegmentSubgroups := x -> x = 0 or IsPosInt(x),
                   SpaceEfficient := IsBool,
                   CapableDescendants := IsBool,
                   AllDescendants := IsBool,
                   SubList := x -> IsPosInt(x) or
                                   (IsSet(x) and ForAll(x, IsInt)
                                    and IsPosInt(x[1])),
                   CustomiseOutput := IsRecord,
                   Bounds := x -> IsSet(x) and 2 = Length(x) and 
                                  ForAll(x, IsPosInt),
                   QueueFactor := IsPosInt,
                   RedoPcp := IsBool,
                   PrintAutomorphisms := IsBool,
                   PrintPermutations := IsBool,
                   NumberOfSolubleAutomorphisms := x -> x = 0 or IsPosInt(x),
                   RelativeOrders := x -> IsList(x) and ForAll(x, IsPosInt),
                   Filename := IsString,
                   TreeDepth := IsPosInt
                   )
             );

#############################################################################
##
#V  ANUPQoptionTypes . . . . . .  the types (in words) for admissible options
##
##  A record whose fields are the names of admissible ANUPQ options and whose
##  values are valid types of the options in plain words.
##
InstallValue( ANUPQoptionTypes,
              rec( Prime := "prime integer",
                   pQuotient  := "pc p-group",
                   ClassBound := "positive integer",
                   OrderBound := "positive integer",
                   Exponent   := "positive integer",
                   Metabelian := "boolean",
                   GroupName  := "string",
                   Identities := "list of functions",
                   OutputLevel := "integer in [0..3]",
                   Relators   := "list of strings",
                   StandardPresentationFile := "string",
                   SetupFile  := "string",
                   PqWorkspace := "positive integer",
                   StepSize := "positive integer or positive integer list",
                   PcgsAutomorphisms := "boolean",
                   BasicAlgorithm := "boolean",
                   RankInitialSegmentSubgroups := "nonnegative integer",
                   SpaceEfficient := "boolean",
                   CapableDescendants := "boolean",
                   AllDescendants := "boolean",
                   SubList 
                       := "pos've integer or increasing pos've integer list",
                   CustomiseOutput := "record",
                   Bounds := "pair of increasing positive integers",
                   QueueFactor := "positive integer",
                   RedoPcp := "boolean",
                   PrintAutomorphisms := "boolean",
                   PrintPermutations := "boolean",
                   NumberOfSolubleAutomorphisms := "nonnegative integer",
                   RelativeOrders := "list of positive integers",
                   Filename := "string",
                   TreeDepth := "positive integer"
                   )
             );

#############################################################################
##
#F  PQ_OTHER_OPTS_CHK( <funcname>, <interactive> ) . check opts belong to f'n
##
##  checks the `OptionsStack'  only  has  recognised  options  for  (generic)
##  function <funcname> and if not and if  `ANUPQWarnOfOtherOptions  =  true'
##  (see~"ANUPQWarnOfOtherOptions") `Info's  the  non-<funcname>  options  at
##  `InfoANUPQ' level 1.
##
##  The argument <interactive> is only relevant for those functions that have
##  both an interactive and non-interactive form, namely those with fields in
##  `PQ_FUNCTION', for which some options need to be excluded.
##
InstallGlobalFunction(PQ_OTHER_OPTS_CHK, function(funcname, interactive)
local optnames, excopts, generic, interactivestr;
  if ANUPQWarnOfOtherOptions and ValueOption("recursive") = fail and 
     not IsEmpty(OptionsStack) then
    excopts := [];
    if funcname in RecNames(PQ_FUNCTION) then
      if interactive then
        excopts := ["PqWorkspace", "SetupFile"];
        interactivestr := "interactive";
      else
        interactivestr := "non-interactive";
        if funcname = "Pq" then
          excopts  := ["RedoPcp"];
        fi;
      fi;
      generic := "generic ";
    else
      generic := "";
    fi;
    optnames := Difference( RecNames( OptionsStack[ Length(OptionsStack) ] ),
                            Difference( ANUPQoptions.(funcname), excopts ) );
    if funcname = "Pq" then
      optnames := Difference( optnames, ["PqEpiOrPCover"] );
    fi;
    if not IsEmpty(optnames) then
      Info( InfoANUPQ + InfoWarning, 1, 
            "ANUPQ Warning: Options: ", optnames, " ignored" );
      if IsSubset(excopts, optnames) then
        Info( InfoANUPQ + InfoWarning, 1, 
              "(invalid for ", interactivestr, " call of generic function: `",
              funcname, "')." );
      else
        Info( InfoANUPQ + InfoWarning, 1,
              "(invalid for ", generic, "function: `", funcname, "')." );
      fi;
    fi;
  fi;
end);

#############################################################################
##
#F  VALUE_PQ_OPTION( <optname> ) . . . . . . . . . enhancement of ValueOption
#F  VALUE_PQ_OPTION( <optname>, <defaultval> ) 
#F  VALUE_PQ_OPTION( <optname>, <datarec> ) 
#F  VALUE_PQ_OPTION( <optname>, <defaultval>, <datarec> ) 
##
##  If the value <optval> of <optname> is not `fail' and it is  an  ok  value
##  for <optname> then <optval> is returned; if <optval> is not an  ok  value
##  an error is signalled. If <optval> is `fail' and <datarec> is  given  and
##  <datarec>.(<optname>) is already  bound  then  that  value  is  returned;
##  otherwise, if  <optval>  is  `fail'  and  a  default  value  <defaultval>
##  different  from  `fail'  is  supplied  then  <defaultval>  is   returned.
##  Supplying a <defaultval> of `fail' is special; it indicates  that  option
##  <optname> must have a value i.e. <optval> is not allowed to be `fail' and
##  if it is an error is signalled. If  a  <datarec>  argument  is  supplied,
##  which must be a record, then the return value, if not `fail' and a  legal
##  value, is also stored in `<datarec>.(<optname>)'.
##
##  *Note:* <defaultval> cannot be a record.
##
InstallGlobalFunction(VALUE_PQ_OPTION, function(arg)
local optname, optval, len;
  optname := arg[1];
  optval := ValueOption(optname);
  len := Length(arg);
  if optval = fail then
    if 1 = len then
      return optval;
    elif IsRecord( arg[len] ) and IsBound( arg[len].(optname) ) then
      # return the previously recorded value
      return arg[len].(optname);
    elif not IsRecord(arg[2]) then
      if arg[2] = fail then
        Error("you must supply a value for option: \"", optname, "\"\n");
      fi;
      optval := arg[2]; 
    fi;
  elif not ANUPQoptionChecks.(optname)(optval) then
    Error("\"", optname, "\" value must be a ", 
          ANUPQoptionTypes.(optname), "\n");
  fi;
  if (optval <> fail) and (2 <= len) and IsRecord(arg[len]) then
    arg[len].(optname) := optval;
  fi;
  return optval;
end);
  
#############################################################################
##
#F  PQ_OPTION_CHECK(<basefn>,<datarec>) . check optns present/setable if nec.
##
##  If `<basefn> = "Pq"' (i.e. this check is  carried  out  if  the  function
##  called is `Pq', `PqEpimorphism' or  `PqPCover')  check  that  the  option
##  `Prime' has been  passed  or,  in  a  special  `PqPCover'  case,  can  be
##  determined from the `<datarec>.group'  which  must  be  present.  In  the
##  special `PqPCover' case, the options `Prime' and `ClassBound'  determined
##  are saved in <datarec>. If `Prime' is not supplied in the cases where  it
##  needs to be, an error is emitted.
##
InstallGlobalFunction(PQ_OPTION_CHECK, function(basefn, datarec)
local optname, out;
  if basefn = "Pq" then
    if datarec.calltype = "interactive" then
      if VALUE_PQ_OPTION("RedoPcp", false) then
        PQ_UNBIND(datarec, ["Prime",  "ClassBound", "Exponent", "Metabelian",
                            "pCover", "pQuotient",  "pQepi"] );
      fi;
    fi;
    if ValueOption("PqEpiOrPCover") = "pCover" and
       HasIsPGroup(datarec.group) and IsPGroup(datarec.group) then
      if VALUE_PQ_OPTION("Prime", datarec) = fail then
        if not HasPrimePGroup(datarec.group) then
          Error( "supplied group is not known to be a p-group or p unknown.\n",
                 "Option `Prime' must be supplied" );
        else
          datarec.Prime := PrimePGroup(datarec.group);
        fi;
      fi;
      if VALUE_PQ_OPTION("ClassBound", datarec) = fail and
         HasPClassPGroup(datarec.group) then
        datarec.ClassBound := PClassPGroup(datarec.group);
      fi;
    else
      VALUE_PQ_OPTION("Prime", fail, datarec);
    fi;
    VALUE_PQ_OPTION("ClassBound", 63, datarec);
  elif basefn = "StandardPresentation" then
    if VALUE_PQ_OPTION("Prime", datarec) = fail and
       VALUE_PQ_OPTION("pQuotient", datarec) = fail then
      if IsPcGroup(datarec.group) and IsPGroup(datarec.group) then
        datarec.Prime := PrimePGroup(datarec.group);
      else
        Error( "since group of process is not a pc p-group, a prime or\n",
               "p-quotient (pc group) of the group of the process ",
               "must be supplied\n" );
      fi;
    fi;
  fi;
end);

#############################################################################
##
#F  PQ_CUSTOMISE_OUTPUT(<datarec>, <subopt>, <suboptstring>, <suppstrings>)
##    
##  writes the required output to the `pq' binary for the sub-option <subopt>
##  of  the  option  `CustomiseOutput',  the  value  of  that  option  having
##  previously been stored in `<datarec>.des.CustomiseOutput'; <suboptstring>
##  is part of the comment written to the `pq' binary for the sub-option  and
##  <suppstrings> is a list of such comments for the supplementary  questions
##  asked by the `pq' binary for the sub-option <subopt>.
##
InstallGlobalFunction( PQ_CUSTOMISE_OUTPUT, 
function(datarec, subopt, suboptstring, suppstrings)
local optrec, isOptionSet, i;
  optrec := datarec.des.CustomiseOutput;
  if IsEmpty(suppstrings) then
    isOptionSet := IsBound( optrec.(subopt) ) and optrec.(subopt) in [1, true];
    ToPQ_BOOL(datarec, isOptionSet, suboptstring);
  elif IsBound( optrec.(subopt) ) and IsList( optrec.(subopt) ) then
    ToPQ(datarec, [ 0 ], [ "  #customise ", suboptstring ]);
    for i in [1 .. Length(suppstrings)] do
      isOptionSet := IsBound( optrec.(subopt)[i] ) and
                     optrec.(subopt)[i] in [1, true];
      ToPQ_BOOL(datarec, isOptionSet, suppstrings[i]);
    od;
  else
    ToPQ(datarec, [ 1 ], [ "  #default ", suboptstring ]);
  fi;
end);
  
#############################################################################
##
#F  PQ_APG_CUSTOM_OUTPUT(<datarec>, <subopt>, <suboptstring>, <suppstrings>)
##    
##  writes the required output to the `pq' binary for the sub-option <subopt>
##  of the option `CustomiseOutput',  as  required  by  an  Advanced  p-Group
##  Generation Menu item, the value of that  option  having  previously  been
##  stored in `<datarec>.des.CustomiseOutput'; <suboptstring> is part of  the
##  comment written to the `pq' binary for the sub-option  and  <suppstrings>
##  is a list of such comments for the supplementary questions asked  by  the
##  `pq' binary for the sub-option <subopt>.
##
InstallGlobalFunction( PQ_APG_CUSTOM_OUTPUT, 
function(datarec, subopt, suboptstring, suppstrings)
local optrec, optlist, isOptionSet, i;
  optrec := datarec.des.CustomiseOutput;
  if not( IsRecord(optrec) and IsBound( optrec.(subopt) ) and 
          IsList( optrec.(subopt) ) ) then
    optlist := [];
    datarec.des.CustomiseOutput.(subopt) := optlist;
  else
    optlist := optrec.(subopt);
  fi;
  for i in [1 .. Length(suppstrings)] do
    isOptionSet := IsBound( optlist[i] ) and optlist[i] in [1, true];
    ToPQ_BOOL(datarec, isOptionSet, suppstrings[i]);
  od;
end);
  
#############################################################################
##
#F  SET_ANUPQ_OPTIONS( <funcname>, <fnname> )  . set options from OptionStack
##    
##  When called by a function with name  <funcname>  sets  the  options  from
##  `OptionsStack'    checking    that    they    are     a     subset     of
##  `ANUPQoptions.<fnname>'. Both <funcname> and <fnname> should be strings.
##
InstallGlobalFunction( SET_ANUPQ_OPTIONS, function( funcname, fnname )
    local optrec, optnames, opt;

    # there should be options
    if IsEmpty( OptionsStack ) then
        # no options??
        optrec := rec();
        Info( InfoANUPQ, 1, funcname, " called with no options!" );
    else
        optrec := ShallowCopy( OptionsStack[ Length( OptionsStack ) ] );
        optnames := Set( REC_NAMES(optrec) );
        SubtractSet( optnames, Set( ANUPQoptions.(fnname) ) );
        Info( InfoANUPQ, 2, funcname, " called with options: ", 
                            OptionsStack[ Length( OptionsStack ) ] );
        if 0 < Length(optnames) then
            # it's not an error to have unknown options,
            # function may have been called recursively and the 
            # options may be intended for some other function
            Info( InfoWarning + InfoANUPQ, 2,
                  funcname, " called with unknown options: ", optnames);
        fi;
        for opt in optnames do
            Unbind( optrec.(opt) );
        od;
    fi;
    return optrec;
end );

#############################################################################
##
#F  ANUPQoptError( <funcname>, <illegal> )  . . . . . create an error message
##
##  creates an error message  for  the  function  with  name  <funcname>.  If
##  <illegal> is a string it is taken to be  the  first  line  of  the  error
##  message. Otherwise <illegal> should be alist of illegal options (strings)
##  found. The error message (string) returned also gives the list  of  valid
##  options together with the value types expected for function <funcname>.
##
InstallGlobalFunction( ANUPQoptError, function( funcname, illegal )
    local Optstring, Valstring, errmsg, optname;

    Optstring := optname -> Concatenation("\"", optname, "\"");
    Valstring := optval  -> Concatenation("<",  optval,  ">");

    if IsString(illegal) then
        errmsg := illegal;
    else # IsList(illegal)
        errmsg := Concatenation("Illegal ", funcname, " option");
        if Length(illegal) > 1 then
            Append(errmsg, "s");
        fi;
        Append(errmsg, ": ");
        Append(errmsg, JoinStringsWithSeparator( List(illegal, Optstring) ));
    fi;
    Append(errmsg, Concatenation(".\nValid ", funcname, " options:\n"));
    for optname in ANUPQoptions.(funcname) do
        Append(errmsg, "    ");
        Append(errmsg, Optstring(optname));
        if ANUPQoptionChecks.(optname) <> IsBool then
            Append(errmsg, ", ");
            Append(errmsg, Valstring( ANUPQoptionTypes.(optname) ));
        fi;
        Append(errmsg, "\n");
    od;
    return errmsg;
end );

#############################################################################
##
#F  ANUPQextractOptions( <funcname>, <args> ) . . . . . . . . extract options
##
##  extracts options from  <args>  for  function  with  name  <funcname>  and
##  returns a record suitable for use with `PushOptions'.  Abbreviations  are
##  allowed for option names so long as  each  abbreviates  a  unique  option
##  name.
##
InstallGlobalFunction( ANUPQextractOptions, function(funcname, args)
    local   Match, error, optrec, i, optname;

    # allow to give only a prefix
    Match := function( argi )
        local matches;
        if IsString(argi) then
            matches := Filtered(ANUPQoptions.(funcname), 
                                optname -> 0 < Length(argi) and
                                           Length(argi) <= Length(optname) and
                                           optname{[1..Length(argi)]} = argi);
            if 1 = Length(matches) then
                return matches[1];
            fi;
            error := Concatenation( "argument: \"", argi, 
                                    "\" doesn't abbreviate a unique option");
        else
            error := Concatenation( "argument: ", String(argi),
                                    " is not a (non-null) string (option name)"
                                    );
        fi;
        return fail;
    end;

    # extract options from args
    optrec := rec();
    i := 1;
    while i <= Length(args)  do
        optname := Match( args[i] );
        if optname = fail then 
            Error( ANUPQoptError( funcname, error ) );
        elif ANUPQoptionChecks.(optname) = IsBool then
            optrec.(optname) := true;
            i := i + 1;
        elif i = Length(args) then
            # all remaining options are non-boolean and expect a value to
            # follow
            Error( ANUPQoptError( 
                       funcname,
                       Concatenation( "Expected value for option: ", args[i] )
                       ) );
        else
            # checking values are ok is done later
            optrec.(optname) := args[i + 1];
            i := i + 2;
        fi;
    od;
    return optrec;

end );

#E  anupqopt.gi . . . . . . . . . . . . . . . . . . . . . . . . . . ends here 
