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


Quelle  rws4.g   Sprache: unbekannt

 
#############################################################################
##
#A  rws4.g                  GAP library                  Derek Holt
##
##  This file contains those functions that deal with rewriting systems.
##
##  1.3.00. created this file from GAP3 version rws.g and started adapting
##  it to GAP4.
##
##  1.8.96.
##  Changed the ReorderGenerators command to permute the alphabet
##  themselves, and deleted the generatorOrder field. This avoids the
##  need of permuting columns of fsa's when updating.
##
##  15.3.95.
##  Each (internal) rewriting-system now has components "GpMonSgp" (for the
##  associated group or monoid), "generators" (for the generators, which
##  will include those of GpMonSgp, but may also include inverses
##  in the group case), and "namesGenerators", which again include
##  those of GpMonSgp, but will have names with "^-1" adjoined for inverses. 
##
##  When an externally created file containing a rewriting-system is read in
##  to GAP, a preprocessing external program called "ppgap" is run, which
##  creates a file called "file.gap", which includes necessary declarations
##  of a suitable underlying monoid.
##
##  23.2.95. The internal storage of a rewriting system was changed so
##  that generators are simply numbers in the range [1..ng] for some ng,
##  and words are lists of generator numbers.
##
DeclareInfoClass("InfoRWS");


#############################################################################
#V  _RWS                external variable - the name of the rewriting system
#V  _RWS_Sub            external variable - subgroup of the rewriting system
#V  _RWS_Cos            external variable - coset rewriting system
#V  _RWS.GpMonSgp  external variable - name of underlying group or monoid
#V  _RWS.FreeGpMonSgp  external variable - name of underlying group or monoid
#V  _KBExtDir   external variable - directory of external executables
#V  _KBTmpFileName      external variable - name of temporary file.
#V  _ExitCode           external variable - exit code of programs.
##
_RWS   := rec();
_RWS_Sub   := rec();
_RWS_Cos   := rec();

_ExitCode := 0;

#############################################################################
##
#F  IsConfluentRWS(<x>) . . . . . . . test whether x is a confluent rws
##
##  Public function.
IsConfluentRWS := function ( x )
    if not IsKBMAGRewritingSystemRep(x) then return false; fi;
    if not IsBound(x!.isConfluent) then return false; fi;
    return  x!.isConfluent;
end;

#############################################################################
##
#F  IsGroupRWS(<rws>) . . . test whether all gens of rws <rws> have inverses
##
##  Public function.
IsGroupRWS := function ( rws )
    local gp, g;
    if not IsKBMAGRewritingSystemRep(rws) then return false; fi;
    gp:=true;
    for g in rws!.invAlphabet do
      if g = false then gp:=false; fi;
    od;
    return gp;
end;

#############################################################################
##
#F  IsMonoidRWS(<rws>) . . . does <rws> represent a monid
##
##  This merely returns the value of rws!.hasOne.
##  Note that if this is false, then there should be no inverses!
##  Public function.
IsMonoidRWS := function ( rws )
    if not IsKBMAGRewritingSystemRep(rws) then return false; fi;
    return rws!.hasOne;
end;

#############################################################################
##
#F  LinePrintRWS(<line> [,<filename>]) . . . . . . . print the line x
##
##  LinePrintRWS prints the line (a string) to the terminal (default)
##  or to file filename  if specified, formatting nicely.
##  It works by building up the material to be printed line by line as strings,
##  and calling LinePrintRWS to print each individual line.
LinePrintRWS := function ( arg )
    local line, filename;
    line := arg[1];
    if Length(arg) = 1 then
      filename := "";
    else
      filename := arg[2];
    fi;
    if filename = "" then
      Print(line,"\n");
    else
      AppendTo(filename,line,"\n");
    fi;
end;

#############################################################################
##
#F  FpStructureRWS(<rws>) . finitely presented group or semigroup defining <rws>
##
##  Public function.
FpStructureRWS := function ( rws )
  local F, M, IdWord, rels, gens, ng, i, ig, eqn, w1, w2;
  if not IsKBMAGRewritingSystemRep(rws)  then
     Error("Argument is not an KBMAG rewriting system.");
  fi;
  if IsBound(rws!.GpMonSgp) then
     return rws!.GpMonSgp;
  fi;
  ## We have to calculate it!
  M := rws!.WordMonoid;
  IdWord := One(M);
  rels := Set([]);
  gens := rws!.alphabet;
  ng := Length(rws!.alphabet);
  for i in [1..ng] do
    ig := rws!.invAlphabet[i];
    if ig <> false then
      AddSet(rels,[gens[i]*gens[ig],IdWord]);
      AddSet(rels,[gens[ig]*gens[i],IdWord]);
    fi;
  od;
  for eqn in rws!.equations do
    w1 := ListToWordRWS(eqn[1],gens);
    w2 := ListToWordRWS(eqn[2],gens);
    if w1<>w2 then AddSet(rels,[w1,w2]); fi;
  od;
  #Now convert to external representation.
  F := rws!.FreeGpMonSgp;
  rels := List(rels,r -> [rws!.IntToExt(rws!.ExtIntCorr,r[1]),
                          rws!.IntToExt(rws!.ExtIntCorr,r[2])] );
  if IsGroup(F) then
    rels := List(rels,r -> r[1]/r[2] );
  fi;
    
  rws!.GpMonSgp := F/rels;
  return rws!.GpMonSgp;
end;

#############################################################################
##
#F  IsAvailableNormalFormRWS(<x>) . . . . test whether x has a normal form
##
##  Public function.
IsAvailableNormalFormRWS := function ( x )
    return IsKBMAGRewritingSystemRep(x) and
           IsBound(x!.isAvailableNormalForm) and
                   x!.isAvailableNormalForm=true;
end;

#############################################################################
##
#F  IsAvailableReductionRWS(<x>) . . test whether x has a reduction algorithm
##
##  Public function.
IsAvailableReductionRWS := function ( x )
    return IsKBMAGRewritingSystemRep(x) and
           IsBound(x!.isAvailableReduction)
                 and x!.isAvailableReduction=true;
end;

#############################################################################
##
#F  IsAvailableSizeRWS(<x>) . . test whether x has a size algorithm
##
##  Public function.
IsAvailableSizeRWS := function ( x )
    return IsKBMAGRewritingSystemRep(x) and
           IsBound(x!.isAvailableSize)
                 and x!.isAvailableSize=true;
end;

#############################################################################
##
#F  ResetRWS(<rws>)  . . . . . . . . . . . reset rws after a call of KBRUN.
##
##  Public function.
##  This resets a rewriting system back to the original equations, after a
##  call of KBRUN or AutRWS.
ResetRWS := function ( rws )
    if not IsKBMAGRewritingSystemRep(rws)  then
       Error("First argument is not a rewriting system.");
    fi;
    Unbind(rws!.KBRun);
    Unbind(rws!.isConfluent);
    Unbind(rws!.isAvailableNormalForm);
    Unbind(rws!.isAvailableReduction);
    Unbind(rws!.isAvailableSize);
    Unbind(rws!.warningOn);
    Unbind(rws!.reductionFSA);
    Unbind(rws!.wa);
    Unbind(rws!.diff1);
    Unbind(rws!.diff1c);
    Unbind(rws!.diff2);
    Unbind(rws!.gm);
    if IsBound(rws!.originalEquations) then
       Unbind(rws!.equations);
       rws!.equations := rws!.originalEquations;
       Unbind(rws!.originalEquations);
    fi;
end;

#############################################################################
##
#F  NumberSubgroupRWS(<rws>, <subrws>) . . . number of a subgroup of an <rws>
##
##  <rws> should be a rewriting system and <subrws> a subgroup.
##  The number of the subgroup is returned, or fail if it is not a subgroup.
##  (Should be in rwssub4.g really but needed by next function.)
NumberSubgroupRWS := function(rws, subrws)
  local i;
  if not IsGroupRWS(rws) then
     Error("NumberSubgroupRWS only applies to rewriting systems from groups.");
  fi;
  if not IsKBMAGRewritingSystemRep(subrws) or
                    not IsBound(subrws!.alphabet) then
     Error(
    "Second argument of NumberSubgroupRWS must be have generators.");
  fi;
  if not IsBound(rws!.subgroups)
    then return fail;
  fi;
  for i in [1..rws!.numSubgroups] do
    if rws!.subgroups[i]!.alphabet = subrws!.alphabet then
       return i;
    fi;
  od;
  return fail;
end;

#############################################################################
##
#F  SetOrderingRWS(<rws>,<ord>[,list]) 
##                          . . .  specify the ordering of a rewriting system
##
##  <rws> should be a rewriting system, and <ord> one of the strings that
##  defines an ordering on the words in the alphabet of <rws>.
##  These are "shortlex", "recursive", rt_recursive", "wtlex" and "wreathprod".
##  In the case of "wtlex" and "wreathprod", the extra parameter <list> is
##  required, and it should be a list of ng (= number of alphabet of <rws>)
##  non-negative integers, specifying the weights or the levels of the
##  alphabet, respectively, for this ordering.
##  Public function.
SetOrderingRWS := function ( arg )
    local rws, ord, list, ng, go, i;
    if Length(arg)<2 or Length(arg)>3 then
       Error("SetOrderingRWS has 2 or 3 arguments");
    fi;
    rws:=arg[1];
    ord:=arg[2];
    if Length(arg)=3 then
      list:=arg[3];
    else
      list:=[];
    fi;
    if not IsKBMAGRewritingSystemRep(rws)  then
       Error("First argument is not an KBMAG rewriting system.");
    fi;
    if not IsString(ord) then
       Error("Second argument is not a string.");
    fi;

    ng := Length(rws!.alphabet);
    if Length(arg)=3 then
      if not IsList(list) or Length(list)<>ng then
         Error("Third argument is not a list of length <ng>.");
      fi;
      for i in [1..ng] do
        if not IsInt(list[i]) or list[i]<0 then
          Error("Third argument is not a list of non-negative integers.");
        fi;
      od;
    fi;
    
    if ord="shortlex" or ord="recursive" or ord="rt_recursive" or
       ord="wtlex" or ord="wreathprod" then
      rws!.ordering:=ord;
    else
      Error("Unknown ordering",ord);
    fi;
    if (ord="wtlex" or ord="wreathprod") and list=[] then
      Error("Third argument required for ordering",ord);
    fi;
    if ord="wtlex" then rws!.weight:=list; fi;
    if ord="wreathprod" then rws!.level:=list; fi;
end;

#############################################################################
##
#F  ReorderGeneratorsRWS(<rws>,<p>) . reorder alphabet of a rewriting system
##
##  <rws> should be a rewriting system, and <p>  a permutation of the set
##  [1..ng], where <rws> has <ng> = length of alphabet.
##  The alphabet of <rws> is reordered by applying the permutation <p> to
##  its existing order.
##  Public function.
ReorderGeneratorsRWS := function ( rws, p )
    local ng, list, i, eqn;
    if not IsKBMAGRewritingSystemRep(rws)  then
       Error("First argument is not an KBMAG rewriting system.");
    fi;
    if not IsPerm(p) then
       Error("Second argument is not a permutation.");
    fi;
    ng := Length(rws!.alphabet);
    if LargestMovedPointPerm(p) > ng then
       Error("Permutation is on more points than there are alphabet!");
    fi;
    
    list:=[];
    for i in [1..ng] do list[i^p]:=rws!.alphabet[i]; od;
    rws!.alphabet:=list;

    list:=[];
    for i in [1..ng] do 
      if IsInt(rws!.invAlphabet[i]) then
        list[i^p]:=rws!.invAlphabet[i]^p;
      else list[i^p] := false;
      fi;
    od;
    rws!.invAlphabet:=list;

    for eqn in rws!.equations do
      list := List(eqn[1],i->i^p);
      eqn[1]:=list;
      list := List(eqn[2],i->i^p);
      eqn[2]:=list;
    od;

    if IsBound(rws!.originalEquations) then
      for eqn in rws!.originalEquations do
        list := List(eqn[1],i->i^p);
        eqn[1]:=list;
        list := List(eqn[2],i->i^p);
        eqn[2]:=list;
      od;
    fi;

    if IsBound(rws!.weight) then
      list:=[];
      for i in [1..ng] do list[i^p]:=rws!.weight[i]; od;
      rws!.weight:=list;
    fi;

    if IsBound(rws!.level) then
      list:=[];
      for i in [1..ng] do list[i^p]:=rws!.level[i]; od;
      rws!.level:=list;
    fi;
end;

#############################################################################
##
#F  ReadRWS(<filename>, [semigp])  . . . .read and convert a rewriting system
##
##  ReadRWS reads a rewriting system, which must be declared with the
##  external variable name "_RWS" from the file <filename>, and converts it
##  to internal format. First it creates and reads the GAP preprocessor file
##  <filename>.gap, for the declarations of variable names.
##  This is created using the external program "ppgap".
##  The rewriting system is returned.
##  If the optional second argument is true, then the rewriting system is
##  regarded as being for a semigroup rather than for a monoid (default).
##  This means that the empty word is not part of the language.
##  For this to be the case there must be no inverses, or an error will result.
##  Public function.
ReadRWS := function ( arg )
    local i, rgm, rfgm, rws, ng, p, ig, ri,
          eqn, filename, semigp, mnames, fam, isgp, l, s, gtom, igtom;
    
    filename:=arg[1];
    if Length(arg)>1 then
      semigp := arg[2];
    else
      semigp := false;
    fi;
    Exec(Concatenation(Filename(_KBExtDir,"ppgap4")," ",filename));
    Read(Concatenation(filename,".gap4"));
    Exec(Concatenation("/bin/rm -f ",filename,".gap4")); 
 
    rfgm := _RWS.FreeGpMonSgp;
                  #This is about to get overwritten, so we remember it! 

    if not READ(filename) then Error("Could not open file for reading"); fi;

    rws := _RWS; _RWS := rec(); #reset _RWS for next time
    
    rws.FreeGpMonSgp := rfgm;
    isgp := IsGroup(rfgm);
    rws.hasOne := not semigp;
    rws.options := rec();

    #Several of the fields of the rewriting system are stored differently
    #or have different names in the internal storage, than the way they
    #appear in the file.
    ng := Length(rws.generatorOrder);

    #Internally, inverses are not stored as a list of alphabet, but as
    #a list of integers (in the field invAlphabet), giving the position
    #of the inverse generator in the generator list.
    ri := rws.inverses;
    ig := []; rws.invAlphabet := ig;
    for i in [1..ng] do
      if IsBound(ri[i]) then
        ig[i] := Position(rws.generatorOrder,ri[i]);
        if semigp then
          Error("There can be no inverse in a semigroup.");
        fi;
      else
        ig[i] := false;
      fi;
    od;
    Unbind(rws.inverses);


    #The left- and right-hand sides of the equations are not stored
    #internally as words, but as lists of integers, giving the numbers of
    #the alphabet appearing in the list.
    for eqn in rws.equations do
        eqn[1] := WordToListRWS(eqn[1],rws.generatorOrder);
        eqn[2] := WordToListRWS(eqn[2],rws.generatorOrder);
    od;

    mnames := [];
    for i in [1..ng] do
      if isgp then mnames[i] := Concatenation("_g",String(i));
      else mnames[i] := Concatenation("_m",String(i));
      fi;
    od;
    rws.WordMonoid := FreeMonoid(mnames);
    rws.alphabet := GeneratorsOfMonoid(rws.WordMonoid);

    #Now we have to set up the Ext/Int correspondence.
    #This is tricky for groups, because some of the names in the
    #external file might have form "x^-1".
    if isgp then
      gtom := []; igtom :=[];
      for i in [1..ng] do
        s:=String(rws.generatorOrder[i]);
        l:=Length(s);
        if l<=3 or s{[l-2..l]} <> "^-1" then
          Add(gtom,i);
          Add(igtom,rws.invAlphabet[i]);
        fi;
      od;
      rws.ExtIntCorr :=
         CorrespondenceGroupMonoid(rfgm,rws.WordMonoid,gtom,igtom);
      rws.ExtToInt := FreeGroup2FreeMonoid;
      rws.IntToExt := FreeMonoid2FreeGroup;
    else
      rws.ExtIntCorr :=
         CorrespondenceGroupMonoid(rfgm,rws.WordMonoid);
      rws.ExtToInt := FreeMS2FreeMonoid;
      rws.IntToExt := FreeMonoid2FreeMS;
    fi;
    Unbind(rws.generatorOrder); #we no longer use this field.

    fam := NewFamily("Family of Knuth-Bendix Rewriting systems",
                IsKnuthBendixRewritingSystem);
    rws := Objectify(NewType(fam,
                IsMutable and IsKnuthBendixRewritingSystem
                and IsKBMAGRewritingSystemRep),
                rws);
    FpStructureRWS(rws);
    return rws;
end;

#############################################################################
##
#F  ExtVarHandlerRWS(<rws>, <filename>) . . write file to handle externals
##
## This is hopefully a temporary hack, for use until GAP V4, where should be
## a better solution. A GAP file is written to preserve any existing values
## of external variables corresponding to the generator names of the
## rewriting system <rws>, and then to declare these variables to be equal
## to the corresponding alphabet of <rws>. This is necessary for reading
## back in the output of KBRWS or AutRWS, which uses these names.
## This first file is called <rws>.gap1.
## A second file <rws>.gap2 is written for reading afterwards, which restores
## the values of any previously existing externals with those names.
## The files are read by the following two functions below.
##
ExtVarHandlerRWS  := function(rws, filename)
    local file1, file2, ng, names, line, i, ni, l;
    file1 := Concatenation(filename,".gap1");
    file2 := Concatenation(filename,".gap2");
    PrintTo(file1,"_RWS.oldNames:=false;\n");
    PrintTo(file2,"");
    
    ng := Length(rws!.alphabet);
    names := List(rws!.alphabet,x->String(x));
    _RWS.WordMonoid := rws!.WordMonoid;
    for i in [1..ng] do _RWS.(i) := rws!.alphabet[i]; od;

    for i in [1..ng] do
      ni := names[i]; l := Length(ni);
      if l <= 3 or ni{[l-2..l]} <> "^-1" then
        line := Concatenation("if IsBound(",ni,") and ",ni,
            " <> _RWS.WordMonoid.",String(i)," then _RWS.",String(i+ng),":=",
               ni,"; _RWS.oldNames:=true; fi;\n");
        line := Concatenation(line,ni,":=_RWS.WordMonoid.",String(i),";\n");
        AppendTo(file1,line);
        line := Concatenation("if IsBound(_RWS.",String(i+ng),") then ",
                ni,":=_RWS.",String(i+ng),"; fi;\n");
        AppendTo(file2,line);
      fi;
    od;
    line := Concatenation(
       "if IsBound(_) and _ <> One(_RWS.WordMonoid) then _RWS.",
       String(2*ng+1), ":=_;\n    _RWS.oldNames:=true; fi;\n");
    line := Concatenation(line,"_:=One(_RWS.WordMonoid);\n");
    AppendTo(file1,line);
    line := Concatenation("if IsBound(_RWS.",String(2*ng+1),
          ") then  _:=_RWS.",String(2*ng+1),"; fi;\n");
    AppendTo(file2,line);
    line := Concatenation(
    "if IsBound(IdWord) and IdWord <> One(_RWS.WordMonoid) then _RWS.",
          String(2*ng+2), ":=IdWord;\n     _RWS.oldNames:=true; fi;\n");
    line := Concatenation(line,"IdWord:=One(_RWS.WordMonoid);\n");
    AppendTo(file1,line);
    line := Concatenation("if IsBound(_RWS.",String(2*ng+2),
          ") then  IdWord:=_RWS.",String(2*ng+2),"; fi;\n");
    AppendTo(file2,line);
end;

#############################################################################
##
#F  StoreNamesRWS(<rws>, <filename>) 
##  Store existing variables before reading external file.
StoreNamesRWS := function(rws, filename)
    local i;
    ExtVarHandlerRWS(rws,filename);
    Read(Concatenation(filename,".gap1"));
    rws!.oldNames := _RWS.oldNames;
end;

#############################################################################
##
#F  RedefineNamesRWS(<rws>, <filename>) 
##  Redefine existing variables after reading external file.
##  Store existing variables.
RedefineNamesRWS := function(rws, filename)
    local i;
    if rws!.oldNames then
      Read(Concatenation(filename,".gap2"));
    fi;
    _RWS := rec();
    _RWS_Cos := rec();
end;

#############################################################################
##
#F  UpdateRWS(<rws>, <filename>, <kb>, [<cosets>])
##                              . . update rws, after run of external program
##
## This function is called after a run of one of the "documented" external
## programs (currently KBRWS and AutRWS) on the rewriting system <rws>.
## It updates <rws> being careful to reset any external variables that were
## used by the external program, but previously existed in the current GAP
## session.   <filename> should be the file in which the
## original rewriting-system was stored. <kb> should be true or false,
## according to whether the function is being called from a Knuth-Bendix
## application (KBRWS) or automatic groups (AutRWS).
## In the Knuth-Bendix case, <filename>.kbprog is read in, for the updated
## version of the equations. Then <filename>.reduce is read in
## for the reduction machine.
## In the automatic groups case, <filename>.wa is read in for the word-acceptor,
## and then <filename.diff2> and <filename>.diff1c for the word-difference
## machines used in word  reduction.
## 

UpdateRWS := function(arg)
 local rws, filename, kb, cosets, _RWSrec, x, i, j, k, l, eqn, twovar,
              fsa, fsa2, fsa3, newrow, ig, mg, alph, la, efilename;

    rws := arg[1];
    filename := arg[2];
    kb := arg[3];
    cosets := false;
    if Length(arg)>=4 then
      cosets := arg[4];
    fi;

    #Make preprocessing file
    StoreNamesRWS(rws, filename);
    if cosets then
      _RWS_Cos := _RWS;
    fi;

    mg := GeneratorsOfMonoid(rws!.WordMonoid);
    if cosets then
      alph := rws!.baseAlphabet;
    else
      alph := rws!.alphabet;
    fi;
    la := Length(alph);
    if kb then
      #Read in updated version of equations.
      if not READ(Concatenation(filename,".kbprog")) then
         Error("Could not open output of external Knuth-Bendix program.");
      fi;
      if cosets then _RWSrec := _RWS_Cos; else _RWSrec := _RWS; fi;
      rws!.equations := _RWSrec.equations;
      for eqn in rws!.equations do
        eqn[1] := WordToListRWS(eqn[1],mg);
        eqn[2] := WordToListRWS(eqn[2],mg);
      od;
      rws!.isConfluent := _RWSrec.isConfluent;
      if cosets then
        rws!.ordering := _RWSrec.ordering;
        rws!.level := _RWSrec.level;
      fi;
    fi;

    # read automata
    if kb then
      if not READ(Concatenation(filename,".reduce")) then
         Error("Could not open reduction machine file");
      fi;
      fsa:= _RWSrec.reductionFSA;
      rws!.reductionFSA := fsa;
      if not rws!.hasOne then # empty word should not be accepted.
        fsa.accepting := [2..fsa.states.size];
      fi;
      for i in [1..la] do
        fsa.alphabet.names[i] := alph[i];
        #They may got re-ordered!
      od;
    else
      if cosets then _RWSrec := _RWS_Cos; else _RWSrec := _RWS; fi;
      if not READ(Concatenation(filename,".wa")) then
         Error("Could not open word-acceptor file");
      fi;
      fsa := _RWSrec.wa;
      rws!.wa := fsa;
      for i in [1..la] do
        fsa.alphabet.names[i] := alph[i];
        #They may got re-ordered!
      od;
      if cosets then efilename:=Concatenation(filename,".midiff1");
      else efilename:=Concatenation(filename,".diff1c");
      fi;
      if not READ(efilename) then
         Error("Could not open word-difference file");
      fi;
      if cosets then fsa2 := _RWS.midiff1; else fsa2 := _RWS.diff1c; fi;
      if cosets then rws!.midiff1 := fsa2; else rws!.diff1 := fsa2; fi;
      if cosets then efilename:=Concatenation(filename,".midiff2");
      else efilename:=Concatenation(filename,".diff2");
      fi;
      if not READ(efilename) then
         Error("Could not open word-difference file");
      fi;
      if cosets then fsa3 := _RWS.midiff2; else fsa3 := _RWS.diff2; fi;
      if cosets then rws!.midiff2 := fsa3; else rws!.diff2:=fsa3; fi;
      #fsa2.alphabet.type := "simple";
      #fsa3.alphabet.type := "simple";
      for i in [1..la] do
        fsa2.states.alphabet[i] := alph[i];
        fsa3.states.alphabet[i] := alph[i];
        #They may got re-ordered!
      od;
    fi;

    #Reset any lost existing externals
    RedefineNamesRWS(rws, filename);
    
    InitializeFSA(fsa);
    #Make sure the table is stored densely
    DenseDTableFSA(fsa);
    fsa.table.format:="dense deterministic";
    fsa.table.transitions:=fsa.denseDTable;
    Unbind(fsa.sparseTable);
    fsa.alphabet.printingStrings:=List(rws!.alphabet,x->String(x));
    if not kb then
       InitializeFSA(fsa2);
       InitializeFSA(fsa3);
       #Make sure the table is stored densely
       DenseDTableFSA(fsa2);
       DenseDTableFSA(fsa3);
       #fsa2.alphabet.base.printingStrings:=List(rws!.alphabet,x->String(x));
       #fsa3.alphabet.base.printingStrings:=List(rws!.alphabet,x->String(x));
       fsa2.states.printingStrings:=List(rws!.alphabet,x->String(x));
       fsa3.states.printingStrings:=List(rws!.alphabet,x->String(x));
       fsa2.table.format:="dense deterministic";
       fsa3.table.format:="dense deterministic";
       fsa2.table.transitions:=fsa.denseDTable;
       fsa3.table.transitions:=fsa.denseDTable;
       Unbind(fsa2.sparseTable);
       Unbind(fsa3.sparseTable);
    fi;
end;

#############################################################################
##
#F  WriteRWS(<rws>, [<filename>], [<endsymbol>])
##           . . . . . . . . . . . .write an rws to a file in external format
##
##  WriteRWS prints the rws <rws> to the  file <filename> formatting nicely.
##  It works by building up the material to be printed line by line as strings,
##  and calling LinePrintRWS to print each individual line.
##  If <filename> is not present, or empty, then writing is to the terminal
##  and is simply of form rec(..).
##  Otherwise, printing takes form _RWS := rec(...)<endsymbol>
##  where <endsymbol> is a string which is ";" by default.
##  (_RWS is a global variable.)
##
##  Public function.
WriteRWS := function ( arg )
    local rws, name, filename, gapfilename, line, i, eqn, endsymbol,
          ng, en, gn, ls, ig;

    if Length(arg)<1 then
       Error("WriteRWS has 1, 2 or 3 arguments");
    fi;
    rws := arg[1];
    filename := "";
    if Length(arg)>=2 then filename := arg[2]; fi;
    if filename="" then endsymbol := ""; else endsymbol := ";"; fi;
    if Length(arg)>=3 then endsymbol := arg[3]; fi;
    
    if not IsKBMAGRewritingSystemRep(rws) then
      Error("First argument is not an KBMAG rewriting system.");
    fi;

    ng := Length(rws!.alphabet);
    en := List(rws!.alphabet,x->String(x));

    #Now print main file
    if filename="" then Print("rec(\n");
    else PrintTo(filename,"_RWS := rec (\n");
    fi;

    line := String("isRWS",16);
    line := Concatenation(line," := true,");
    LinePrintRWS(line,filename);

    if IsBound(rws!.isConfluent) then
      line := String("isConfluent",16);
      line := Concatenation(line," := ",String(rws!.isConfluent),",");
      LinePrintRWS(line,filename);
    fi;

#Now come all of the optional parameters
    if IsBound(rws!.options.tidyint) then
      line := String("tidyint",16);
      line := Concatenation(line," := ",String(rws!.options.tidyint),",");
      LinePrintRWS(line,filename);
    fi;
    if IsBound(rws!.options.maxeqns) then
      line := String("maxeqns",16);
      line := Concatenation(line," := ",String(rws!.options.maxeqns),",");
      LinePrintRWS(line,filename);
    fi;
    if IsBound(rws!.options.maxstates) then
      line := String("maxstates",16);
      line := Concatenation(line," := ",String(rws!.options.maxstates),",");
      LinePrintRWS(line,filename);
    fi;
    if IsBound(rws!.options.maxreducelen) then
      line := String("maxreducelen",16);
      line := Concatenation(line," := ",String(rws!.options.maxreducelen),",");
      LinePrintRWS(line,filename);
    fi;
    if IsBound(rws!.options.confnum) then
      line := String("confnum",16);
      line := Concatenation(line," := ",String(rws!.options.confnum),",");
      LinePrintRWS(line,filename);
    fi;
    if IsBound(rws!.options.maxwdiffs) then
      line := String("maxwdiffs",16);
      line := Concatenation(line," := ",String(rws!.options.maxwdiffs),",");
      LinePrintRWS(line,filename);
    fi;
    if IsBound(rws!.options.maxstoredlen) then
      line := String("maxstoredlen",16);
      line := Concatenation(line, " := [",
              String(rws!.options.maxstoredlen[1]),",",
                               String(rws!.options.maxstoredlen[2]),"],");
      LinePrintRWS(line,filename);
    fi;
    if IsBound(rws!.options.sorteqns) then
      line := String("sorteqns",16);
      line := Concatenation(line," := ",String(rws!.options.sorteqns),",");
      LinePrintRWS(line,filename);
    fi;
    if IsBound(rws!.options.maxoplen) then
      line := String("maxoplen",16);
      line := Concatenation(line," := ",String(rws!.options.maxoplen),",");
      LinePrintRWS(line,filename);
    fi;
    if InfoLevel(InfoRWS)=0 then
      line := String("silent",16);
      line := Concatenation(line," := true,");
      LinePrintRWS(line,filename);
    fi;
    if InfoLevel(InfoRWS)>1 then
      line := String("verbose",16);
      line := Concatenation(line," := true,");
      LinePrintRWS(line,filename);
    fi;
    if InfoLevel(InfoRWS)>2 then
      line := String("veryVerbose",16);
      line := Concatenation(line," := true,");
      LinePrintRWS(line,filename);
    fi;

    line := Concatenation(String("generatorOrder",16)," := [");
    for i in [1..ng] do
      if i > 1 then
        line := Concatenation(line,",");
      fi;
      if i=1 or Length(line)+Length(en[i]) <= 76 then
        line := Concatenation(line,en[i]);
      else
        LinePrintRWS(line,filename);
        line := String("",21);
        line := Concatenation(line,en[i]);
      fi;
    od;
    line := Concatenation(line,"],");
    LinePrintRWS(line,filename);

    ig := rws!.invAlphabet;
    line := Concatenation(String("inverses",16)," := [");
    for i in [1..ng] do
      if i > 1 then line := Concatenation(line,","); fi;
      if IsInt(ig[i]) and ig[i]>0 then
        if i=1 or Length(line)+Length(en[ig[i]]) <= 76 then
          line := Concatenation(line,en[ig[i]]);
        else
          LinePrintRWS(line,filename);
          line := String("",21);
          line := Concatenation(line,en[ig[i]]);
        fi;
      fi;
    od;
    line := Concatenation(line,"],");
    LinePrintRWS(line,filename);

    if not IsString(rws!.ordering) then
       Error("Can only output orderings that are strings");
    fi;
    line := String("ordering",16);
    line := Concatenation(line," := \"",rws!.ordering,"\",");
    LinePrintRWS(line,filename);

    if rws!.ordering="wtlex" and IsBound(rws!.weight) then
      line := Concatenation(String("weight",16)," := [");
      for i in [1..ng] do
        if i > 1 then
          line := Concatenation(line,",");
        fi;
        line := Concatenation(line,String(rws!.weight[i]));
      od;
      line := Concatenation(line,"],");
      LinePrintRWS(line,filename);
    fi;

    if rws!.ordering="wreathprod" and IsBound(rws!.level) then
      line := Concatenation(String("level",16)," := [");
      for i in [1..ng] do
        if i > 1 then
          line := Concatenation(line,",");
        fi;
        line := Concatenation(line,String(rws!.level[i]));
      od;
      line := Concatenation(line,"],");
      LinePrintRWS(line,filename);
    fi;

    line := Concatenation(String("equations",16)," := [");
    for i in [1..Length(rws!.equations)] do
      if i > 1 then line := Concatenation(line,","); fi;
      LinePrintRWS(line,filename);
      eqn := rws!.equations[i];
      line := Concatenation(String("[",10),
                          StringRWS(ListToWordRWS(eqn[1],rws!.alphabet)),",");
      if Length(line)>=40 then
        LinePrintRWS(line,filename);
        line := String("",10);
      fi;
      line :=Concatenation(line,
                  StringRWS(ListToWordRWS(eqn[2],rws!.alphabet)),"]");
    od;
    LinePrintRWS(line,filename);
    line := String("]",8);
    LinePrintRWS(line,filename);
    line := Concatenation(")",endsymbol);
    LinePrintRWS(line,filename);
end;

#############################################################################
##
#F  IsReducedWordRWS(<rws>,<w>) . . . . tests if a word is reduced
##
##  IsReducedWordRWS tests whether the word <w>
##  is reduced, using the  rewriting system <rws>.
##  <w> can be given either as a list of integers (internal format) or as
##  a word in the generators of the underlying group or monoid.
##  Either the word-acceptor (automatic group case) or the reduction FSA
##  must be defined.
##  It merely calls the corresponding FSA function.
##
##  Public function.
IsReducedWordRWS := function ( rws, w )
    local i, ng;
    if not IsKBMAGRewritingSystemRep(rws)  then
       Error("First argument is not an KBMAG rewriting system.");
    fi;
    if not IsAvailableReductionRWS(rws) then
       Error(
   "Reduction algorithm unavailable. Run KnuthBendix or AutomaticStructure.");
    fi;
    if not IsList(w) and not IsWord(w) then
       Error("Second argument is not a word or list.");
    fi;
    ng := Length(rws!.alphabet);
    if IsWord(w) then
       w:=WordToListRWS(w,rws!.alphabet);
    fi;
    if IsBound(rws!.wa) then
    # Automatic group case - use word-acceptor
      return IsAcceptedWordDFA( rws!.wa,w );
    fi;
    if not IsBound(rws!.reductionFSA)  then
       Error("First argument does not have initialized dfa as field.");
    fi;
    return IsAcceptedWordDFA( rws!.reductionFSA,w );
end;
      
#############################################################################
##
#F  ReduceWordWD(<wd>,<w>)
##                   . . . . .reduces a word using word-difference automaton
##
##  ReduceWordWD calculates the reduction of the word <w> (list of integers)
##  using the word-difference automaton <wd>.
##  <wd> should be two-variable, where <w> is a list of integers in the range
##  [1..ng], where ng is the size of the base alphabet.
##  WARNING: No validity checks are carried out!
##
##  Private function.
ReduceWordWD := function ( wd, w)
    local  ndiff, ngens, ng1, identity, level, cf, gpref, gct, gptr,
           diff, newdiff, deqi, gen1, gen2, donesub, donediffs, subvert,dosub,
           g2ltg1, diffct, t, nlen, olen, i, l, table;
    if not IsInitializedFSA(wd) then
       Error("First argument is not an initialized dfa.");
    fi;

    ndiff := wd.states.size;
    ngens := wd.alphabet.base.size;
    ng1 := ngens+1;            
    identity := wd.initial[1];
    if Length(w) <= 0 then
      return w;
    fi;
    cf := [];
    # cf is used as a characteristic function, when constructing a subset of the
    # set  D  of word differences.
    gpref := []; gct := 0; gpref[1] := 0; gptr := [];
    # gpref[n]  is the number of "vertices" that have been defined after
    # reading the first n-1 elements of the word.
    # These vertices are gptr[1],...,gptr[gpref[n]].
    # A vertex is a record with 4 components, backptr, genno, diffno, sublen,
    # It represents a vertex in the graph of strings that may eventually
    # be used as substituted strings in the word w.
    # backptr is either undefined or another vertex.
    # gen is the generator at the vertex.
    # diffno is the word-difference number of the string at which the vertex
    #        is at the end - this string is reconstructed using backptr.
    # sublen is plus or minus the length of this string. sublen is positive
    #        iff the string lexicographically precedes the corresponding
    #        prefix of the word being reduced.

    # Now we start scanning the word.
    table := DenseDTableFSA(wd);
    level := 1;
    while level <= Length(w) do
      for i in [1..ndiff] do cf[i] := false; od;
      gen1 := w[level];
      # The next loop is over the identity, and the subset of the set of
      # word-differences (states of wd) defined at the previous level (level-1)

      diff := identity;
      donesub := false;
      donediffs := false;
      while not donesub and not donediffs do
        deqi := diff = identity;
      # First look for a possible substitution of a shorter string
        newdiff := table[diff][ng1*gen1];
        if newdiff=identity then
          #Make substitution  reducing length of word by 1
          SubstitutedListFSA(w,level,level,[]);
          i := level-1;
          if not deqi then
            subvert := gptr[diffct];
     dosub := true;
            while dosub do
              w[i] := subvert.gen;
              i := i-1;
              if IsBound(subvert.backptr) then
         subvert := subvert.backptr;
              else
                dosub := false;
              fi;
            od;
          fi;
          #Whenever we make a substitution, we have to go back one level more
          #than expected, because of our policy of looking ahead for
          #substitutions that reduce the length by 2.
          if i>0 then level:=i-1; else level:=i; fi;
          gct := gpref[level+1];
          donesub := true;
        elif newdiff>0 and level<Length(w) then
          #See if there is a substitution reducing length by 2.
          gen2 := w[level+1];
          t := table[newdiff][ng1*gen2];
          if t=identity then
            #Make substitution  reducing length of word by 2
            SubstitutedListFSA(w,level,level+1,[]);
            i := level-1;
            if not deqi then
              subvert := gptr[diffct];
         dosub := true;
              while dosub do
                w[i] := subvert.gen;
                i := i-1;
                if IsBound(subvert.backptr) then
             subvert := subvert.backptr;
                else
                  dosub := false;
                fi;
              od;
            fi;
            if i>0 then level:=i-1; else level:=i; fi;
            gct := gpref[level+1];
            donesub := true;
          fi;
        fi;

        if not donesub then
          #Now we loop over the generator that is a candidate for
          #substitution at this point.
          for gen2 in [1..ngens] do
            g2ltg1 := gen2 < gen1;
            newdiff := table[diff][ng1*(gen1-1)+gen2];
            if donesub then
              donesub := true;
              #i.e. do nothing - we really want to break from the for loop here.
            elif newdiff=identity then
              if deqi then #only occurs when gen2 and gen1 are equal in group
                if g2ltg1 then
                  w[level] := gen2;
                  if level>1 then level:=level-2; else level:=level-1; fi;
                  gct := gpref[level+1];
                  donesub := true;
                fi;
              elif gptr[diffct].sublen>0 then
                #Make a substitution by a string of equal length.
                w[level] := gen2;
                i := level-1;
                subvert := gptr[diffct];
             dosub := true;
                while dosub do
                  w[i] := subvert.gen;
                  i := i-1;
                  if IsBound(subvert.backptr) then
                 subvert := subvert.backptr;
                  else
                    dosub := false;
                  fi;
                od;
                if i>0 then level:=i-1; else level:=i; fi;
                gct := gpref[level+1];
                donesub := true;
              fi;
            elif newdiff>0 then
              if cf[newdiff] then
                #We have this word difference stored already, but we will check
                #to see if the current string precedes the existing one.
                i := gpref[level];
                repeat
                  i := i+1;
                  subvert := gptr[i];
                until subvert.diffno=newdiff;
                olen := subvert.sublen;
                if deqi then 
                  if g2ltg1 then nlen:=1; else nlen:= -1; fi;
                else
                  l := gptr[diffct].sublen;
                  if l>0 then nlen:=l+1; else nlen:=l-1; fi;
                fi;
                if nlen > olen then # new string is better than existing one
                  subvert.gen := gen2;
                  subvert.sublen := nlen;
                  if deqi then Unbind(subvert.backptr);
                  else subvert.backptr := gptr[diffct];
                  fi;
                fi;
              else
               # this is a new word-difference at this level, so
               # we define a new vertex.
                gct := gct+1;
                gptr[gct] := rec();
                if deqi then 
                  if g2ltg1 then nlen:=1; else nlen:= -1; fi;
                else
                  l := gptr[diffct].sublen;
                  if l>0 then nlen:=l+1; else nlen:=l-1; fi;
                fi;
                subvert := gptr[gct];
                subvert.gen := gen2;
                subvert.diffno := newdiff;
                subvert.sublen := nlen;
                if not deqi then subvert.backptr := gptr[diffct]; fi;
                cf[newdiff] := true;
              fi;
            fi;
          od; # End of loop over gen2

          if not donesub then
            #Go on to next word-difference from the previous level
            if diff=identity then
              if level=1 then
                donediffs := true;
              else
                diffct := gpref[level-1] + 1;
              fi;
            else
              diffct := diffct+1;
            fi;
            if not donesub and not donediffs then
              if diffct > gpref[level] then
                donediffs := true;
              else
                diff := gptr[diffct].diffno;
              fi;
            fi;
          fi;
        fi;
      od; #end of loop over word-differences at previous level

      level := level+1;
      gpref[level] := gct;
    od;
    return w;
end;

#############################################################################
##
#F  ReduceWordRWS(<rws>,<w>) . . . . reduces a word using rewriting-system
##
##  ReduceWordRWS reduces the word <w>, using the rewriting-system <rws>.
##  <w> can be given either as a list of integers (internal format) or as
##  a word in the generators of the underlying group or monoid.
##  Either the reduction FSA, or one of the word-difference automata (in the
##  automatic group case) must be defined for <rws>.
##  In the latter case, the separate function ReduceWordWD is called.
##
##  Public function.
ReduceWordRWS := function ( rws, w )
    local fsa, pos, label, state, history, eqn, sublen, table, ng,  i, word;
    if not IsKBMAGRewritingSystemRep(rws)  then
       Error("First argument is not an KBMAG rewriting system.");
    fi;
    if not IsAvailableReductionRWS(rws) then
       Error(
   "Reduction algorithm unavailable. Run KnuthBendix or AutomaticStructure.");
    fi;
    if not IsList(w) and not IsWord(w) then
       Error("Second argument is not a word or list.");
    fi;
    ng := Length(rws!.alphabet);
    if IsWord(w) then
       word :=true;
       w:=ShallowCopy(WordToListRWS(w,rws!.alphabet));
    else word := false;
    fi;
    if IsBound(rws!.warningOn) and rws!.warningOn then
      if IsBound(rws!.KBRun) and rws!.KBRun then
        Print(
 "#WARNING: system is not confluent, so reductions may not be to normal form.\n"
      );
      else
        Print(
 "#WARNING: word-difference reduction machine is not proven correct,\n",
 "          so reductions may not be to normal form.\n");
      fi;
      rws!.warningOn:=false;
           # only give the warning once, or it could become irritating!
    fi;
    if IsBound(rws!.diff2) then
     # automatic group case
       w := ReduceWordWD(rws!.diff2,w);
    elif IsBound(rws!.diff1c) then
     # automatic group case
       w := ReduceWordWD(rws!.diff1c,w);
    elif IsBound(rws!.diff1) then
     # automatic group case
       w := ReduceWordWD(rws!.diff1,w);
    elif IsBound(rws!.reductionFSA)  then
       fsa := rws!.reductionFSA;
       if not IsInitializedFSA(fsa) or IsDeterministicFSA(fsa)=false  then
          Error("First argument does not have initialized dfa as field.");
       fi;
   
       state := fsa.initial[1];
       pos := 1;
       history:= [];
       history[1] := state; # history[i] = state before reading i-th symbol.
       table := DenseDTableFSA(fsa);
       while pos <= Length(w) do
         state := table[state][w[pos]];
         if state>0 then
           pos := pos+1;
           history[pos] := state;
         else
           state := -state;
           eqn := rws!.equations[state];
           sublen := Length(eqn[1]);
           SubstitutedListFSA(w,pos-sublen+1,pos,eqn[2]);
           pos := pos-sublen+1;
           state := history[pos];
         fi;
       od;
    else
       Error("First argument does not have initialized dfa as field.");
    fi;

    if not rws!.hasOne and Length(w)=0 then
      Error("The empty word does not represent an element of a semigroup.");
    fi;
    if word then
       w := ListToWordRWS(w,rws!.alphabet);
    fi;
    return w;
end;
      
#############################################################################
##
#F  SizeRWS(<rws>>) . . . . . number of reduced words in a rewriting system
##
##  This merely calls the corresponding FSA function.
##
##  Public function.
SizeRWS := function ( rws )
    if not IsKBMAGRewritingSystemRep(rws)  then
       Error("First argument is not a rewriting system.");
    fi;
    if not IsAvailableSizeRWS(rws) then
       Error(
   "Size algorithm unavailable. Run KnuthBendix or AutomaticStructure.");
    fi;
    if IsBound(rws!.warningOn) and rws!.warningOn then
      if rws!.KBRun then
        Print(
 "#WARNING: system is not confluent, so size returned may not be correct.\n"
      );
      else
        Print(
 "#WARNING: word-difference reduction machine is not proven correct,\n",
 "          so size returned may not be correct.\n");
      fi;
      rws!.warningOn:=false;
           # only give the warning once, or it could become irritating!
    fi;
    if IsBound(rws!.wa) then
     # automatic group case
      return LSizeDFA( rws!.wa );
    fi;
    return LSizeDFA( rws!.reductionFSA );
end;

#############################################################################
##
#F  EnumerateRWS(<rws>, <min>, <max>) . . . enumerate reduced words in a rws
##
##  This merely calls the corresponding FSA function.
##  Words are converted to words in generators of underlying group or monoid
##  before returning.
##
##  Public function.
EnumerateRWS := function ( rws, min, max )
    local ret, x, i;
    if not IsKBMAGRewritingSystemRep(rws)  then
       Error("First argument is not a rewriting system.");
    fi;
    if not IsAvailableSizeRWS(rws) then
       Error(
   "Enumeration algorithm unavailable. Run KnuthBendix or AutomaticStructure.");
    fi;
    if IsBound(rws!.wa) then
     # automatic group case
      ret := LEnumerateDFA( rws!.wa,min,max );
    else
      ret := LEnumerateDFA( rws!.reductionFSA,min,max );
    fi;
    return ret;
end;

#############################################################################
##
#F  SortEnumerateRWS(<rws>, <min>, <max>)  . . enumerate reduced words and sort
##
##  This merely calls the corresponding FSA function.
##  Words are converted to words in generators of underlying group or monoid
##  before returning.
##
##  Public function.
SortEnumerateRWS := function ( rws, min, max )
    local ret, x, i;
    if not IsKBMAGRewritingSystemRep(rws)  then
       Error("First argument is not a rewriting system.");
    fi;
    if not IsAvailableSizeRWS(rws) then
       Error(
   "Enumeration algorithm unavailable. Run KnuthBendix or AutomaticStructure.");
    fi;
    if IsBound(rws!.wa) then
     # automatic group case
      ret := SortLEnumerateDFA( rws!.wa,min,max );
    else
      ret := SortLEnumerateDFA( rws!.reductionFSA,min,max );
    fi;
    return ret;
end;

#############################################################################
##
#F  SizeEnumerateRWS(<rws>, <min>, <max>)  . . . . number of reduced words 
##
##  This merely calls the corresponding FSA function.
##
##  Public function.
SizeEnumerateRWS := function ( rws, min, max )
    if not IsKBMAGRewritingSystemRep(rws)  then
       Error("First argument is not a rewriting system.");
    fi;
    if not IsAvailableSizeRWS(rws) then
       Error(
   "Enumeration algorithm unavailable. Run KnuthBendix or AutomaticStructure.");
    fi;
    if IsBound(rws!.wa) then
     # automatic group case
      return SizeLEnumerateDFA( rws!.wa,min,max );
    fi;
    return SizeLEnumerateDFA( rws!.reductionFSA,min,max );
end;

#############################################################################
##
#F  OrderRWS(<rws>,<w>) . . . . order of word <w> in group or monoid
##
##  OrderRWS tries to calculate the order of the element represented by the
##  word <w> in the group  or monoid of the rewriting system <rws>.
##  Either the word-acceptor (automatic group case) or the reduction FSA
##  must be defined.
##  It could conceivably not terminate, but I have never known that happen!
##
##  Public function.
OrderRWS := function ( rws, w )
    local i, ng, fsa, prefix, preford, pt, t, targets, sufford, tracing, x,
          z, cr, l;
    if not IsKBMAGRewritingSystemRep(rws)  then
       Error("First argument is not an KBMAG rewriting system.");
    fi;
    if not rws!.hasOne then
       Error("Order algorithm is only possible in a monoid or group");
    fi;
    if not IsAvailableReductionRWS(rws) then
       Error(
   "Reduction algorithm unavailable. Run KnuthBendix or AutomaticStructure.");
    fi;
    if not IsList(w) and not IsWord(w) then
       Error("Second argument is not a word or list.");
    fi;
    ng := Length(rws!.alphabet);
    if IsWord(w) then
       w:=ShallowCopy(WordToListRWS(w,rws!.alphabet));
    fi;
    w := ReduceWordRWS(rws, w);
    if Length(w)=0 then
       return 1;
    fi;
    if IsBound(rws!.wa) then
    # Automatic group case - use word-acceptor
      fsa := rws!.wa;
    else
      fsa := rws!.reductionFSA;
    fi;
    prefix := w;
    preford := 1;
    while true do
      #Check prefix is cyclically reduced
      cr := true;
      while cr do
        l := Length(prefix);
        if l>1 and rws!.invAlphabet[prefix[1]]=prefix[l] then
          #remove first and last terms of prefix, but we must also
          #perform the same conjugation operation on w.
          w:=Concatenation([prefix[l]],w,[prefix[1]]);
          w := ReduceWordRWS(rws,w);
          prefix := prefix{[2..l-1]};
        else
          cr := false;
        fi;
      od;
      #First see if all powers of prefix are reduced - if so, then a
      #state of fsa will eventually repeat on tracing w^n, and w will have
      #infinite order.
      pt := WordTargetDFA(fsa, prefix);
      t := pt;
      targets := Set([t]);
      tracing:=true;
      while tracing do
        for x in prefix do
          t := TargetDFA(fsa, x, t);
          if t<=0 then
            break;
          fi;
        od; #for x in prefix
        if t<=0 then
          tracing := false;
        elif t in targets then
          return infinity;
        else
          AddSet(targets,t);
        fi;
      od; # while tracing
      #not all powers of prefix are reduced, so we need to replace prefix
      #by reduced word for a higher power.
      sufford := 0;
      tracing := true;
      t := pt;
      while tracing do
        sufford := sufford+1;
        for x in w do
          t := TargetDFA(fsa, x, t);
          if t<=0 then
            tracing := false;
            for i in [1..sufford] do
              prefix := Concatenation(prefix,w);
            od;
            prefix := ReduceWordRWS(rws, prefix);
            preford := preford + sufford;
            if Length(prefix)=0 then
              return preford;
            fi;
            #To improve chance of proving order infinite, we replace
            #el and w by cyclic conjugates.
            z := rws!.invAlphabet[prefix[1]];
            if  z <> false then
              w:=Concatenation([z],w,[prefix[1]]);
              w := ReduceWordRWS(rws,w);
              prefix:=Concatenation([z],prefix,[prefix[1]]);
              prefix := ReduceWordRWS(rws,prefix);
            fi;
            break;
          fi;
        od; #for x in w
      od; #while tracing
    od; #while true
end;

#############################################################################
##
#F  AddOriginalEqnsRWS(<rws>).
##           . . . . add original equations to rws after a call of KBRWS.
##
##  This appends the original equations to the list of equations, after a
##  call of KBRWS. Useful for a re-check, if the external program may have
##  deleted some equations.
##  After this function, rewriting is no longer possible.
##  Public function.
AddOriginalEqnsRWS := function ( rws )
    if not IsKBMAGRewritingSystemRep(rws)  then
       Error("First argument is not a rewriting system.");
    fi;
    Unbind(rws!.reductionFSA);
    Unbind(rws!.isConfluent);
    if IsBound(rws!.originalEquations) then
      Append(rws!.equations,rws!.originalEquations);
    fi;
end;

#############################################################################
##
#F  KBRWS(<rws>)  . . . . call external Knuth-Bendix program on rws
##
##  This returns true if a confluent rewriting system results - otherwise
##  false. In the latter case, words can still be rewritten with respect to
##  the current equations, but they are not guaranteed to reduce to the unique
##  representative of the group element.
##  An error message results if the external program aborts without outputting.
##  Public function.
KBRWS := function ( rws )
    local O, callstring;
    if not IsKBMAGRewritingSystemRep(rws)  then
       Error("First argument is not a rewriting system.");
    fi;
    if IsConfluentRWS(rws) then
       Print("#The rewriting system is already confluent.\n");
       Print("#Call - ResetRWS(<rws>) to restart.\n");
       return fail;
    fi;
    #If we have already run KBRWS then the original equations will
    #have been kept and should be re-inserted.
    AddOriginalEqnsRWS(rws);
    #Keep the original equations, in case we want them again.
    if not IsBound(rws!.originalEquations) then
      rws!.originalEquations := StructuralCopy(rws!.equations);
    fi;
    WriteRWS(rws,_KBTmpFileName);
    callstring := Concatenation(Filename(_KBExtDir,"kbprog")," ",_KBTmpFileName);
    Info(InfoRWS,1,"Calling external Knuth-Bendix program.");
    Info(InfoRWS,3,"  ", callstring);
    Exec(callstring);
    UpdateRWS(rws,_KBTmpFileName,true);
    Exec(Concatenation("/bin/rm -f ",_KBTmpFileName,"*"));
    Info(InfoRWS,1,"External Knuth-Bendix program complete.");
    
    if rws!.isConfluent then
      O := rws!.options;
      if IsBound(O.maxstoredlen) or IsBound(O.maxoplen) then
        Print(
 "#WARNING: Because of the control parameters you set, the system may\n");
        Print(
 "#         not be confluent. Unbind the parameters and re-run KnuthBendix\n");
        Print(
 "#         to check!\n");
        rws!.isConfluent:=false;
      fi;
    fi;
    if rws!.isConfluent then
      Info(InfoRWS,1,"System computed is confluent.");
      rws!.isAvailableNormalForm := true;
      rws!.warningOn := false;
    else
      Info(InfoRWS,1,"System computed is NOT confluent.");
      rws!.isAvailableNormalForm := false;
      rws!.warningOn := true;
    fi;
    rws!.KBRun := true;
    rws!.isAvailableReduction := true;
    rws!.isAvailableSize := true;
    return rws!.isConfluent;
end;

#############################################################################
##
#F  AutRWS(<rws>, [<large>], [<filestore>], [<diff1>]) 
##                      . . . . call external automatic group program on rws
##
##  This returns true if the automatic group programs succeed - otherwise
##  false.
##  The optional parameters are all boolean, and false by default.
##  <large> means problem is large - the external programs use bigger tables.
##  <filestore> means external programs use less core memory and more external
##         files - they run a little slower.
##  <diff1> is necessary on some examples - see manual for information.
##  Public function.
AutRWS := function ( arg )
    local  narg, rws, large, filestore, diff1, callstring, optstring;
    narg := Number(arg);
    if narg<1  or  not IsKBMAGRewritingSystemRep(arg[1]) then
       Error("First argument is not a rewriting system.");
    fi;
    rws := arg[1];
    if not IsGroupRWS(rws) then
      Error("AutRWS can only be applied when all generators have inverses.");
    fi;
    if IsBound(rws!.KBRun) and rws!.KBRun then
      Print("Knuth-Bendix has already been run on this rewriting system.\n");
      Print("Call - ResetRWS( <rws> ) before proceeding.\n");
      return;
    fi;
    if not rws!.ordering = "shortlex" then
       Error("AutRWS only works for shortlex ordering");
    fi;
    large:=false; filestore:=false; diff1:=false;
    if narg>=2 and arg[2]=true then large:=true; fi;
    if narg>=3 and arg[3]=true then filestore:=true; fi;
    if narg>=4 and arg[4]=true then diff1:=true; fi;
    WriteRWS(rws,_KBTmpFileName);
    callstring := Filename(_KBExtDir,"autgroup");
    optstring := " ";
    if large then optstring := Concatenation(optstring," -l "); fi;
    if filestore then optstring := Concatenation(optstring," -f "); fi;
    if diff1 then optstring := Concatenation(optstring," -d "); fi;
    if InfoLevel(InfoRWS)=0 then
                      optstring := Concatenation(optstring," -s "); fi;
    if InfoLevel(InfoRWS)>1 then
                      optstring := Concatenation(optstring," -v "); fi;
    if InfoLevel(InfoRWS)>2 then
                      optstring := Concatenation(optstring," -vv "); fi;
    callstring := Concatenation(callstring,optstring,_KBTmpFileName);
    Info(InfoRWS,1,"Calling external automatic groups program.");
    Info(InfoRWS,3,"  ", callstring);
    Exec(callstring);
    callstring := Filename(_KBExtDir,"gpminkb");
    optstring := " ";
    if InfoLevel(InfoRWS)=0 then
                      optstring := Concatenation(optstring," -s "); fi;
    if InfoLevel(InfoRWS)>1 then
                      optstring := Concatenation(optstring," -v "); fi;
    if InfoLevel(InfoRWS)>2 then
                      optstring := Concatenation(optstring," -vv "); fi;
    callstring := Concatenation(callstring,optstring,_KBTmpFileName);
    if READ(Concatenation(_KBTmpFileName,".success")) then
     Info(InfoRWS,1,
         "Computation was successful - automatic structure computed.");
      Info(InfoRWS,3,"  ", callstring);
      Exec(callstring);
      UpdateRWS(rws,_KBTmpFileName,false);
      Exec(Concatenation("/bin/rm -f ",_KBTmpFileName,"*"));
      rws!.isAvailableNormalForm := true;
      rws!.isAvailableReduction := true;
      rws!.isAvailableSize := true;
      rws!.warningOn := false;
      return true;
    else
      Exec(Concatenation("/bin/rm -f ",_KBTmpFileName,"*"));
      Info(InfoRWS,1,"Computation was not successful.");
      return false;
    fi;
end;

#############################################################################
## The remaining functions in the file enable the user to to call the
## different parts of the automata program individually.
## They are experimental and less well supported than KBRUN and AutRWS.
#############################################################################
##
#F  KBWD(<rws>, [<haltingfactor>], [<large>]) 
##                . . . . call external Knuth-Bendix program with -wd on rws
##
##  Runs KBRUN, computes word differences, and sets the diff1 and diff2 flags 
##  of rws to be the appropriate difference machines.
##  An error message results if the external program aborts without outputting.
##  Public function.
KBWD := function ( arg )
    local narg,rws, haltingfactor,large, callstring, optstring, mg, IdWord;
    narg := Number(arg);
    if narg<1  or  not IsKBMAGRewritingSystemRep(arg[1]) then
       Error("First argument is not a rewriting system.");
    fi;
    large:=false; haltingfactor:=100;
    rws := arg[1];
    if not IsGroupRWS(rws) then
      Error("KBWD can only be applied when all generators have inverses.");
    fi;
    if IsBound(rws!.KBRun) and rws!.KBRun then
      Print("Knuth-Bendix has already been run on this rewriting system.\n");
      Print("Call - ResetRWS( <rws> ) before proceeding.\n");
    fi;
    if narg>1 then haltingfactor := arg[2]; fi;
    if narg>2 then large := arg[3]; fi;
    WriteRWS(rws,_KBTmpFileName);
    callstring := Concatenation(Filename(_KBExtDir,"kbprog")," -wd -hf ");
    callstring := Concatenation(callstring,String(haltingfactor)," ");
    optstring := "";
    if large then 
       optstring := Concatenation(optstring," -cn 0 -me 262144 -t 500 "); 
    fi;
    if InfoLevel(InfoRWS)=0 then
                      optstring := Concatenation(optstring," -silent "); fi;
    if InfoLevel(InfoRWS)>1 then
                      optstring := Concatenation(optstring," -v "); fi;
    if InfoLevel(InfoRWS)>2 then
                      optstring := Concatenation(optstring," -vv "); fi;
    callstring := Concatenation(callstring,optstring,_KBTmpFileName);
    Info(InfoRWS,1,
        "Calling external Knuth-Bendix program for word-differences.");
    Info(InfoRWS,3,"  ", callstring);
    Exec(callstring);
    Info(InfoRWS,1,"External Knuth-Bendix program complete.");

    StoreNamesRWS(rws,_KBTmpFileName);
    if not READ(Concatenation(_KBTmpFileName,".diff1")) then
       Error("Could not open diff1 file");
    fi;
    if not READ(Concatenation(_KBTmpFileName,".diff2")) then
       Error("Could not open diff2 file");
    fi;
    if not READ(Concatenation(_KBTmpFileName,".kbprog.ec")) then
       Error("Could not open exit-code file");
    fi;
    rws!.diff1 := _RWS.diff1;
    rws!.diff2 := _RWS.diff2;
    RedefineNamesRWS(rws,_KBTmpFileName);

    InitializeFSA(rws!.diff1);
    rws!.diff1.alphabet.base.printingStrings:=List(rws!.alphabet,x->String(x));
    rws!.diff1.states.printingStrings:=List(rws!.alphabet,x->String(x));

    DenseDTableFSA(rws!.diff1);
    rws!.diff1.table.format:="dense deterministic";
    rws!.diff1.table.transitions:=rws!.diff1.denseDTable;
    Unbind(rws!.diff1.sparseTable);
    InitializeFSA(rws!.diff2);
    rws!.diff2.alphabet.base.printingStrings:=List(rws!.alphabet,x->String(x));
    rws!.diff2.states.printingStrings:=List(rws!.alphabet,x->String(x));
    DenseDTableFSA(rws!.diff2);
    rws!.diff2.table.format:="dense deterministic";
    rws!.diff2.table.transitions:=rws!.diff2.denseDTable;
    Unbind(rws!.diff2.sparseTable);
    if _ExitCode=2 then
      Print(
  "#WARNING: Knuth-Bendix program terminated with halting factor condition\n");
      Print("         not satisfied.\n");
      return false;
    fi;
    rws!.isAvailableReduction := true;
    rws!.warningOn := true;
    Exec(Concatenation("/bin/rm -f ",_KBTmpFileName,"*"));
    return true;
end;

#############################################################################
##
#F  GpWA(<rws>, [<large>], [<filestore>], [<diff1>]) 
##                      . . . . call external word-acceptor program on rws
##
##  This assumes that KBWD has already been called on rws
##  Public function.
GpWA := function ( arg )
    local  narg, rws, large, filestore, diff1, callstring, optstring;
    narg := Number(arg);
    if narg<1  or  not IsKBMAGRewritingSystemRep(arg[1]) then
       Error("First argument is not a rewriting system.");
    fi;
    rws := arg[1];
    if not rws!.ordering = "shortlex"  then
       Error("Ordering must be shortlex for external word-acceptor program");
    fi;
    large:=false; filestore:=false; diff1:=false;
    if narg>=2 and arg[2]=true then large:=true; fi;
    if narg>=3 and arg[3]=true then filestore:=true; fi;
    if narg>=4 and arg[4]=true then diff1:=true; fi;
    if diff1 then
      WriteFSA(
          rws!.diff1,"_RWS.diff1",Concatenation(_KBTmpFileName,".diff1"),";");
    else
      WriteFSA(
          rws!.diff2,"_RWS.diff2",Concatenation(_KBTmpFileName,".diff2"),";");
    fi;
    callstring := Filename(_KBExtDir,"gpwa");
    optstring := " ";
    if large then optstring := Concatenation(optstring," -l "); fi;
    if filestore then optstring := Concatenation(optstring," -f "); fi;
    if diff1 then optstring := Concatenation(optstring," -d "); fi;
    if InfoLevel(InfoRWS)=0 then
      optstring := Concatenation(optstring," -silent ");
    fi;
    if InfoLevel(InfoRWS)>1 then
      optstring := Concatenation(optstring," -v ");
    fi;
    if InfoLevel(InfoRWS)>2 then
      optstring := Concatenation(optstring," -vv ");
    fi;
    callstring := Concatenation(callstring,optstring,_KBTmpFileName);
    Info(InfoRWS,1,"Calling external word-acceptor program.");
    Info(InfoRWS,3,"  ", callstring);
    Exec(callstring);
    Info(InfoRWS,1,"External word-acceptor program complete.");

    StoreNamesRWS(rws,_KBTmpFileName);
    if not READ(Concatenation(_KBTmpFileName,".wa")) then
       Error("Could not open wa file");
    fi;
    rws!.wa := _RWS.wa;
    RedefineNamesRWS(rws,_KBTmpFileName);

    InitializeFSA(rws!.wa);
    rws!.wa.alphabet.printingStrings:=List(rws!.alphabet,x->String(x));
    rws!.isAvailableSize := true;
    rws!.warningOn := true;
    Exec(Concatenation("/bin/rm -f ",_KBTmpFileName,"*"));
end;

#############################################################################
##
#F  GpGenMult(<rws>, [<large>], [<filestore>], [<diff1>] ) 
##                 . . . . call external generalised multiplier program on rws
##
##  This assumes that KBWD and GpWA have already been called on rws
##  Public function.
GpGenMult := function ( arg )
    local  narg, rws, large, filestore, diff1, callstring, optstring;
--> --------------------

--> maximum size reached

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

[ Dauer der Verarbeitung: 0.31 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge