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


Quelle  fsa4.g   Sprache: unbekannt

 
Spracherkennung für: .g vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

#############################################################################
##
#A  fsa4.g                  GAP library                  Derek Holt
##
##  1.3.00. created this file from GAP3 version fsa.g and started adapting
##  it to GAP4.
##
##  24/9/98 - corrected dreadful bug in DeleteStateFSA
##  This file contains those functions that deal with finite state automata.
##
## 1.5.96. - code added to deal with the new "list of words" and
## "list of integers" set-record types.
##
DeclareInfoClass("InfoFSA");
#############################################################################
#V  _RWST_                external variable - temporary name of list of strings
#V  _FSA    external variable - finite state automaton
#V  _FSA_determinize   external variable -determinized finite state automaton
#V  _FSA_min    external variable - minimized finite state automaton
#V  _FSA_not    external variable - `not' finite state automaton
#V  _FSA_star    external variable - `star' finite state automaton
#V  _FSA_reverse   external variable - `reverse' finite state automaton
#V  _FSA_exists      external variable - `exists' finite state automaton
#V  _FSA_swap_coords     external variable - `swap' finite state automaton
#V  _FSA_and        external variable - `and' finite state automaton
#V  _FSA_or        external variable - `or' finite state automaton
#V  _FSA_concat     external variable - `concat' finite state automaton
_RWST_ := [];
_FSA := rec();
_FSA_determinize := rec();
_FSA_min := rec();
_FSA_not := rec();
_FSA_star := rec();
_FSA_reverse := rec();
_FSA_exists := rec();
_FSA_swap_coords := rec();
_FSA_and := rec();
_FSA_or := rec();
_FSA_concat := rec();
_KBExtDir  :=  DirectoriesPackagePrograms("kbmag");
_KBTmpFileName := TmpName();

#############################################################################
##
#F  IsFSA(<x>) . . . . . . . test whether x is an fsa
##
##  Public function.
IsFSA := function ( x )
    return IsRecord(x) and IsBound(x.isFSA) and x.isFSA; 
end;

#############################################################################
##
#F  IsInitializedFSA(<x>) . . . . . . . test whether x is an initialized fsa
##
##  Public function.
IsInitializedFSA := function ( x )
    return IsRecord(x) and IsBound(x.isInitializedFSA) and x.isInitializedFSA; 
end;

#############################################################################
##
#F  ExpandFieldSR(<list>) . . expand a sparsely stored name list
##
##  ExpandFieldSR takes a sparse <list> as argument and
##  returns the corresponding dense list (which may have gaps).
##  e.g. [[1,2],[3,4]] produces output [2,,4].
##
##  Private function.
ExpandFieldSR := function ( list )
    local newlist, term;
    newlist := [];
    for term in list do
      newlist[term[1]] := term[2];
    od;
    return newlist;
end;

#############################################################################
##
#F  InitializeSR(<sr>) . . . . . . . initialize a set-record
##
##  <sr> should be a set-record conforming to the criteria.
##  The criteria are checked, various other fields are calculated and set,
##  and the existing fields are (partially) checked for validity.
##
##  Public function.
InitializeSR := function ( sr )
  local  i, j, k, s, ba, lba, nv, fld;
    if not IsBound(sr.type) or not IsBound(sr.size) then
       Error("Subfield 'type' or 'size' of set-record field is not set.");
    fi;
    
    if IsBound(sr.base) then
      InitializeSR(sr.base);
    fi;
    if IsBound(sr.labels) then
      InitializeSR(sr.labels);
    fi;

    # if the set record names are stored in sparse format, we
    # convert to dense format for internal use - they will still be
    # printed in sparse format.
    k := sr.type;
    if k="words" or k="identifiers" or k="strings" or
                    k="list of words" or k="list of integers" then
      if not IsBound(sr.format) or not IsBound(sr.names)
                                        or not IsList(sr.names) then
         Error(
  "Subfield 'format' or 'names' of set-record field is not set or invalid.");
      fi;
      sr.printingFormat := sr.format;
      if sr.format="sparse" then
         sr.format := "dense";
         sr.names := ExpandFieldSR(sr.names);
      fi;
    fi;

    if k="words" or k="list of words" then
      if not IsBound(sr.alphabet) or not IsList(sr.alphabet) then
         Error(
  "Subfield 'alphabet' of set-record field is not set or invalid.");
      fi;
    fi;

    if k="labeled" or k="labelled" then
      if not IsBound(sr.format)
         or not IsBound(sr.labels) or not IsRecord(sr.labels)
         or not IsBound(sr.setToLabels) or not IsList(sr.setToLabels) then
         Error(
           "Some required field for type \"labeled\"  is not set or invalid.");
      fi;
      sr.printingFormat := sr.format;
      if  sr.format="sparse" then
        sr.format := "dense";
        sr.setToLabels := ExpandFieldSR(sr.setToLabels);
      fi;
    fi;

    if k="product" then
      if not IsBound(sr.padding) or not IsBound(sr.arity)
          or not IsBound(sr.base) or not IsRecord(sr.base)  then
         Error("Some required field for type \"product\" is not set.");
      fi;
      if IsBound(sr.base.names) then
    # calculate names of set-record members
        ba := Concatenation(sr.base.names,[sr.padding]);
        lba := Length(ba);
        nv := sr.arity; 
        sr.names := [];
        fld := sr.names;
        s := sr.size;
        for i in [1..s] do
          fld[i]:=[];
          k := i-1;
          for j in Reversed([1..nv]) do
            fld[i][j] := ba[k mod lba + 1];
              k := Int(k/lba);
          od;
        od;
      fi;
    fi;

end;

#############################################################################
##
#F  InitializeFSA(<fsa>) . . . . . . . initialize an fsa
##
##  <fsa> should be an fsa-record conforming to the criteria.
##  The criteria are checked, various other fields are calculated and set,
##  and the existing fields are (partially) checked for validity.
##  The entries in the flag field ("DFA", "minimized", "BFS", etc.)
##  are assumed to be valid if set.
##  
##  Public function.
InitializeFSA := function ( fsa )
    local ns, ne, i, j;
 # First check that the fsa has the 7 compulsory fields.
    if not IsFSA(fsa) then
       Error("Argument is not an fsa.");
    fi;
    if IsBound(fsa.isInitialized) and fsa.isInitializedFSA = true then
       Print("This fsa is already initialized.\n");
       return;
    fi;
    if not IsBound(fsa.alphabet) or not IsRecord(fsa.alphabet) then
       Error("'alphabet' field is not set, or invalid.");
    fi;
    if not IsBound(fsa.states) or not IsRecord(fsa.states) then
       Error("'states' field is not set, or invalid.");
    fi;
    if not IsBound(fsa.initial) or not IsList(fsa.initial) then
       Error("'initial' field is not set, or invalid.");
    fi;
    if not IsBound(fsa.accepting) or not IsList(fsa.accepting) then
       Error("'accepting' field is not set, or invalid.");
    fi;
    if not IsBound(fsa.table) or not IsRecord(fsa.table) then
       Error("'table' field is not set, or invalid.");
    fi;
    if not IsBound(fsa.flags) or not IsList(fsa.flags) then
       Error("'flags' field is not set, or invalid.");
    fi;

    InitializeSR(fsa.states);
    InitializeSR(fsa.alphabet);

    ns := fsa.states.size;
    ne := fsa.alphabet.size;

    fsa.initial := Set(fsa.initial);
    fsa.accepting := Set(fsa.accepting);
    fsa.flags := Set(fsa.flags);

    if not IsBound(fsa.table.format) or not IsBound(fsa.table.transitions) then
       Error("Subfield 'format' or 'transitions' of table field is not set.");
    fi;
    fsa.table.printingFormat := fsa.table.format;
    if fsa.table.format = "dense nondeterministic" then
 Error("Sorry - dense nondeterministic tables not yet supported.");
    elif fsa.table.format = "dense deterministic" then
      fsa.denseDTable := fsa.table.transitions;
      if Length(fsa.denseDTable) <> ns then
        Error("Length of transition table wrong.");
      fi;
      for i in [1..ns] do
        for j in [1..ne] do
          if not IsBound(fsa.denseDTable[i][j]) then
            fsa.denseDTable[i][j] := 0;
          fi;
        od;
      od;
    elif fsa.table.format = "sparse" then
      fsa.sparseTable := fsa.table.transitions;
      if Length(fsa.sparseTable) <> ns then
        Error("Length of transition table wrong.");
      fi;
    else
      Error("Invalid transition table format.");
    fi; 
    
    fsa.isInitializedFSA := true;
end;

#############################################################################
##
#F  FSA(alphabet) . . . . . . . make an initialized FSA with a 
##  specified alphabet and a single state which is both accepting and initial
##
##  Public function.
FSA := function ( alphabet )
    local F;
    F := rec(); 
    F.isFSA := true;
    F.alphabet := alphabet;
    F.states := rec(type := "simple",size := 1);
    F.flags := ["DFA"];
    F.initial := [1];
    F.accepting := [1];
    F.table := rec(
      format := "dense deterministic",
      numTransitions := 0,
         transitions := [0 * [1..alphabet.size]]
    );
    InitializeFSA(F); 

    return F; 
end;

#############################################################################
#F  AlphabetFSA 
##
##  Public function.
AlphabetFSA := fsa -> fsa.alphabet;

#############################################################################
#F  StatesFSA 
##
##  Public function.
StatesFSA := fsa -> fsa.states;

#############################################################################
#F  NumberOfStatesFSA 
##
##  Public function.
NumberOfStatesFSA := fsa -> fsa.states.size;

#############################################################################
#F  NumberOfLettersFSA
#F  SizeOfAlphabetFSA
##
##  Public function.
NumberOfLettersFSA := fsa -> fsa.alphabet.size;
SizeOfAlphabetFSA := fsa -> fsa.alphabet.size;

#############################################################################
##
#F  IsDeterministicFSA(<fsa>) . . . . . . . test if fsa is deterministic
##
##  Tests if the fsa <fsa> is deterministic.
##  The definition of deterministic used here is that of a partial
##  deterministic finite state automaton, rather than a total one.
##  This means, no epsilon-transtions, *at most* one initial state and
##  *at most one* transition from any state with a given label.
##  An FSA is is non-deterministic (NFA) if one of these conditions fails.
##
##  Public function.
IsDeterministicFSA := function ( fsa )
    local term, subterm, dfa_check;
    if not IsInitializedFSA(fsa) then
       InitializeFSA(fsa);
    fi;
    if "DFA" in fsa.flags then
      return true;
    fi;
    if "NFA" in fsa.flags then
      return false;
    fi;
    if Length(fsa.initial) > 1 then
      AddSet(fsa.flags,"NFA");
      return false;
    fi;
    if IsBound(fsa.denseDTable) then
 # This must imply DFA
      AddSet(fsa.flags,"DFA");
      return true;
    fi;
    for term in fsa.sparseTable do
    # sparseTable must be bound at this point
      dfa_check:=[];
      for subterm in term do
        if subterm[1]=0 or subterm[1]="epsilon" or
             IsBound(dfa_check[subterm[1]]) then
          AddSet(fsa.flags,"NFA");
          return false;
        else
          dfa_check[subterm[1]] := 1;
        fi;
      od;
    od;

    AddSet(fsa.flags,"DFA");
    return true;
end;

#############################################################################
##
#F  SparseTableToDenseDTableFSA(<fsa>) . . get DenseDTable from SparseTable
##  SparseTableToDenseDTableFSA calculates the DenseDTable of the fsa from
##  the SparseTable and  returns it.
##
##  Private function.
SparseTableToDenseDTableFSA := function ( fsa )
    local denseDTable, sparseTable, row, newrow, dt, ne, term, i;
    ne := fsa.alphabet.size;
    sparseTable := fsa.sparseTable;
    if IsBound(fsa.table.defaultTarget) then
      dt:=fsa.table.defaultTarget;
    else
      dt:=0;
    fi;
    denseDTable := [];
    for row in sparseTable do
      newrow:=[];
      for i in [1..ne] do newrow[i]:=dt; od;
      for term in row do
        if term[1]=0 or newrow[term[1]] <> dt then
          AddSet(fsa.flags,"NFA");
       Error("This fsa is nondeterministic, so cannot create DenseDTable.\n");
        fi;
        newrow[term[1]]:=term[2];
      od;
      Add(denseDTable,newrow);
    od;
    return denseDTable;
end;

#############################################################################
##
#F  DenseDTableToSparseTableFSA(<fsa>) . . get SparseTable from DenseDTable
##  DenseDTableToSparseTableFSA calculates the SparseTable of the fsa
##  from the DenseDTable and returns it.
##
##  Private function.
DenseDTableToSparseTableFSA := function ( fsa )
    local denseDTable, sparseTable, row, newrow, dt, ne, i;
    ne := fsa.alphabet.size;
    denseDTable := fsa.denseDTable;
    if IsBound(fsa.table.defaultTarget) then
      dt:=fsa.table.defaultTarget;
    else
      dt:=0;
    fi;
    sparseTable := [];
    for row in denseDTable do
      newrow:=[];
      for i in [1..ne] do
        if row[i] <> dt then
          Add(newrow,[i,row[i]]);
        fi;
      od;
      Add(sparseTable,newrow);
    od;
    return sparseTable;
end;

#############################################################################
##
#F  DenseDTableToBackTableFSA(<fsa>)  . . . . get BackTable from DenseDTable
##  DenseDTableToBackTableFSA calculates the BackTable of the fsa from the
##  DenseDTable and  returns it.
##
##  Private function.
DenseDTableToBackTableFSA := function ( fsa )
    local denseDTable, backTable, row, ne, ns, i, j;
    ne := fsa.alphabet.size;
    ns := fsa.states.size;
    denseDTable := fsa.denseDTable;
    backTable := [];
    for i in [1..ns] do
      backTable[i] := [];
    od;
    for i in [1..ns] do
      row := denseDTable[i];
      for j in [1..ne] do
        if row[j] in [1..ns] then
          Add(backTable[row[j]],[j,i]);
        fi;
      od;
    od;
    return backTable;
end;

#############################################################################
##
#F  SparseTableToBackTableFSA(<fsa>)  . . . . get BackTable from SparseTable
##  SparseTableToBackTableFSA calculates the BackTable of the fsa from
##  the SparseTable and returns it.
##  The backTable still does not include "default-target" edges.
##
##  Private function.
SparseTableToBackTableFSA := function ( fsa )
    local sparseTable, backTable, row, ne, ns, i, term;
    ne := fsa.alphabet.size;
    ns := fsa.states.size;
    sparseTable := fsa.sparseTable;
    backTable := [];
    for i in [1..ns] do
      backTable[i] := [];
    od;
    for i in [1..ns] do
      row := sparseTable[i];
      for term in row do
        if term[2] in [1..ns] then
          Add(backTable[term[2]],[term[1],i]);
        fi;
      od;
    od;
    return backTable;
end;

#############################################################################
##
#F  DenseDTableFSA(<fsa>) . . . . . . . calculates DenseDTable of dfa
##  DenseDTableFSA calculates the DenseDTable of the fsa <fsa> and returns it.
##  Public function.
DenseDTableFSA := function ( fsa )
    if not IsInitializedFSA(fsa) then
       InitializeFSA(fsa);
    fi;
    if not IsBound(fsa.denseDTable) then
       fsa.denseDTable := SparseTableToDenseDTableFSA(fsa);
    fi;
    return fsa.denseDTable;
end;
    

#############################################################################
##
#F  SparseTableFSA(<fsa>) . . . . . . . calculates DenseDTable of fsa
##  SparseTableFSA calculates the SparseTable of the fsa and returns it.
##  Public function.
SparseTableFSA := function ( fsa )
    if not IsInitializedFSA(fsa) then
       InitializeFSA(fsa);
    fi;
    if not IsBound(fsa.sparseTable) then
       fsa.sparseTable := DenseDTableToSparseTableFSA(fsa);
    fi;
    return fsa.sparseTable;
end;

#############################################################################
##
#F  BackTableFSA(<fsa>) . . . . . . . calculates BackTable of fsa
##  BackTableFSA calculates the BackTable of the fsa and returns it.
##  If calculated from the SparseTable, it will not include "default-target"
##  edges.
##  Public function.
BackTableFSA := function ( fsa )
    if not IsInitializedFSA(fsa) then
       InitializeFSA(fsa);
    fi;
    if not IsBound(fsa.backTable) then
       if IsBound(fsa.denseDTable) then
         fsa.backTable := DenseDTableToBackTableFSA(fsa);
       else
         fsa.backTable := SparseTableToBackTableFSA(fsa);
       fi;
    fi;
    return fsa.backTable;
end;

#############################################################################
##
#F  LinePrintFSA(<line> [,<filename>]) . . . . . . . print the line x
##
##  LinePrintFSA 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 LinePrintFSA to print each individual line.
##
LinePrintFSA := 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  WordToStringSR( <word>, <gens>, <names> )
##                                     . . . . . converts <word> to a string
##
## <word> is a word in generators <gens>.
## <names> is a list of printing strings for <gens>.
## The word is converted into a string representing the word for printing.
WordToStringSR := function ( word, gens, names )
   local string, i, l, ls, ng, g, ct, lg;
   l := Length(word);
   if l=0 then
      return "IdWord";
   fi;
   string := "";
   lg := 0; ct := 0;
   for i in [1..l] do
     g := Position(gens,Subword(word,i,i));
     if g <> lg and lg <> 0 then
        if string<>"" then string := Concatenation(string,"*"); fi;

        ng := names[lg];
        if ct > 1 then
           #Check to see if names[lg] ends "^-1"
           ls := Length(ng);
           if ls>3 and ng[ls-2]='^' and ng[ls-1]='-' and ng[ls]='1' then
             string := Concatenation(string,ng{[1..ls-1]},String(ct));
           else
             string := Concatenation(string,ng,"^",String(ct));
           fi;
        else string := Concatenation(string,ng);
        fi;
        ct := 0;
     fi;
     lg := g; ct := ct+1;
   od;
   if string<>"" then string := Concatenation(string,"*"); fi;
   ng := names[lg];
   if ct > 1 then
       #Check to see if names[lg] ends "^-1"
       ls := Length(ng);
       if ls>3 and ng[ls-2]='^' and ng[ls-1]='-' and ng[ls]='1' then
         string := Concatenation(string,ng{[1..ls-1]},String(ct));
       else
         string := Concatenation(string,ng,"^",String(ct));
       fi;
   else string := Concatenation(string,ng);
   fi;

   return string;
end;

#############################################################################
##
#F  WriteSetRecordSR(<sr> [<name>, <filename>, <offset>, <endsymbol>])
##                                  . . print the set record <sr>
##
##  WriteSetRecordSR prints the set record <sr> 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 LinePrintFSA to print each individual line.
##  If the optional string <name> is present, printing is preceded by an
##  assignment "name:=", so that the resulting file can be read back in.
##  if the optional positive integer <offset> is present then each line
##  is preceded by <offset> spaces.
##  If the optional string <endsymbol> is present, then this is printed at
##  the end (it is likely to be ";" or ",").
##
##  Private function.
WriteSetRecordSR := function ( arg )
    local  sr, filename, name, offset, endsymbol, offstring, line, ct, first,
    pn,  wstring, i, tempfn, ids;
    sr := arg[1];
    if (sr.type="identifiers" or sr.type="words" or sr.type="list of words")
         and not IsBound(sr.printingStrings) then
    ## To find out what the printing strings should be, the only way is to
    ## output the identifiers as strings to a temporary file and read back in.
       tempfn:=TmpName();
       if sr.type="identifiers" then
         ids:=sr.names;
       else
         ids:=sr.alphabet;
       fi;
       PrintTo(tempfn,"_RWST_:=[");
       for i in [1..Length(ids)] do
         if i>1 then AppendTo(tempfn,","); fi;
         AppendTo(tempfn,"\"",ids[i],"\"");
       od;
       AppendTo(tempfn,"];\n");
       Read(tempfn);
       sr.printingStrings:=_RWST_;
       Exec(Concatenation("/bin/rm -f ",tempfn));
    fi; 
    filename := "";
    name := "";
    endsymbol := "";
    offset:=0;
    if Length(arg)>=2 then
      name := arg[2];
    fi;
    if Length(arg)>=3 then
      filename := arg[3];
    fi;
    if Length(arg)>=4 then
      offset := arg[4];
    fi;
    if Length(arg)>=5 then
      endsymbol := arg[5];
    fi;
    if not IsInt(offset) or offset<=0 then
       offset := 0;
    fi;
    offstring := String("",offset);
    if name = "" then
      line := "rec (";
    else
      line := Concatenation(String(name,16+offset-4)," := rec (");
    fi;
    LinePrintFSA(line,filename);

    line := Concatenation(offstring,String("type",16),
                                                 " := ","\"",sr.type,"\"",",");
    LinePrintFSA(line,filename);
    line := Concatenation(offstring,String("size",16)," := ",String(sr.size));
    if sr.type <> "simple" then
      line := Concatenation(line,",");
    fi;
    LinePrintFSA(line,filename);
    if sr.type = "product" then
      line:=Concatenation(offstring,String("arity",16)," := ",
       String(sr.arity),",");
      LinePrintFSA(line,filename);
      line:=Concatenation(offstring,String("padding",16)," := _,");
      LinePrintFSA(line,filename);
      WriteSetRecordSR(sr.base,"base",filename,offset+4);
    elif sr.type="words" or sr.type="identifiers" or sr.type="strings"
         or sr.type="list of words" or  sr.type="list of integers" then
      if sr.type="strings" then
        pn := [];
        for i in [1..sr.size] do
           pn[i] := Concatenation("\"",sr.names[i],"\"");
        od;
      elif sr.type="words" or sr.type="identifiers" or
           sr.type="list of words" then 
        pn := sr.printingStrings;
      fi;
      if sr.type="words"  or sr.type="list of words" then
        line := Concatenation(offstring,String("alphabet",16)," := [");
        ct := 1;
        while ct <= Length(sr.alphabet) do
          if ct=1 or Length(line)+Length(pn[ct]) <= 76 then
            if ct > 1 then
               line := Concatenation(line,",");
            fi;
            line := Concatenation(line,pn[ct]);
          else
            line := Concatenation(line,",");
            LinePrintFSA(line,filename);
            line := String("",21+offset);
            line := Concatenation(line,pn[ct]);
          fi;
          ct := ct+1;
        od;
        line := Concatenation(line,"],");
        LinePrintFSA(line,filename);
      fi;
      line := Concatenation(offstring,String("format",16)," := ");
      line := Concatenation(line,"\"",sr.printingFormat,"\"",",");
      LinePrintFSA(line,filename);
      line := Concatenation(offstring,String("names",16)," := [");
      if sr.printingFormat="dense" then
 # recall that the names are always stored internally in dense format.
        ct := 1;
        while ct <= sr.size do
          if sr.type="words" then
            wstring := WordToStringSR(sr.names[ct],sr.alphabet,pn);
          elif sr.type="list of words" then
            wstring:="[";
            for i in [1..Length(sr.names[ct])] do
              if i>1 then
                wstring:=Concatenation(wstring,",");
              fi;
              wstring:=Concatenation(wstring,
                 WordToStringSR(sr.names[ct][i],sr.alphabet,pn) );
            od;
            wstring:=Concatenation(wstring,"]");
          elif sr.type="list of integers" then
            wstring:="[";
            for i in [1..Length(sr.names[ct])] do
              if i>1 then
                wstring:=Concatenation(wstring,",");
              fi;
              wstring:=Concatenation(wstring,String(sr.names[ct][i]));
            od;
            wstring:=Concatenation(wstring,"]");
          else wstring := pn[ct];
          fi;
          if ct=1 or Length(line)+Length(wstring) <= 76 then
            if ct > 1 then
               line := Concatenation(line,",");
            fi;
            line := Concatenation(line,wstring);
          else
            line := Concatenation(line,",");
            LinePrintFSA(line,filename);
            line := String("",21+offset);
            line := Concatenation(line,wstring);
          fi;
          ct := ct+1;
        od;
        line := Concatenation(line,"]");
        LinePrintFSA(line,filename);
      else
        ct := 1;
        first := true;
        while ct <= sr.size do
          if IsBound(sr.names[ct]) then
             if sr.type="words" then
                 wstring :=
                     WordToStringSR(sr.names[ct],sr.alphabet,pn);
             elif sr.type="list of words" then
               wstring:="[";
               for i in [1..Length(sr.names[ct])] do
                 if i>1 then
                   wstring:=Concatenation(wstring,",");
                 fi;
                 wstring:=Concatenation(wstring,
                   WordToStringSR(sr.names[ct][i],sr.alphabet,pn) );
               od;
               wstring:=Concatenation(wstring,"]");
             elif sr.type="list of integers" then
               wstring:="[";
               for i in [1..Length(sr.names[ct])] do
                 if i>1 then
                   wstring:=Concatenation(wstring,",");
                 fi;
                 wstring:=Concatenation(wstring,String(sr.names[ct][i]));
               od;
               wstring:=Concatenation(wstring,"]");
             else wstring := pn[ct];
             fi;
             if first then
               line := Concatenation
                     (line,"[",String(ct),",",wstring,"]");
               first := false;
             else
               line := Concatenation(line,",");
               LinePrintFSA(line,filename);
               line := String("",21+offset);
               line := Concatenation
                     (line,"[",String(ct),",",wstring,"]");
             fi;
          fi;
          ct := ct+1;
        od;
        LinePrintFSA(line,filename);
        line := Concatenation(String("",20+offset),"]");
        LinePrintFSA(line,filename);
      fi;
    elif sr.type = "labeled" or sr.type="labelled"  then
      WriteSetRecordSR(sr.labels,"labels",filename,offset+4,",");
      line := Concatenation(offstring,String("format",16)," := ");
      line := Concatenation(line,"\"",sr.printingFormat,"\"",",");
      LinePrintFSA(line,filename);
      line := Concatenation(offstring,String("setToLabels",16)," := [");
      if sr.printingFormat="dense" then
        ct := 1;
        while ct <= sr.size do
          if not IsBound(sr.setToLabels[ct]) then
            if ct>1 then
              line := Concatenation(line,",");
            fi;
          elif ct=1 or
           Length(line)+Length(String(sr.setToLabels[ct])) <= 76 then
            if ct > 1 then
               line := Concatenation(line,",");
            fi;
            line := Concatenation(line,String(sr.setToLabels[ct]));
          else
            line := Concatenation(line,",");
            LinePrintFSA(line,filename);
            line := String("",21+offset);
            line := Concatenation(line,String(sr.setToLabels[ct]));
          fi;
          ct := ct+1;
        od;
        line := Concatenation(line,"]");
        LinePrintFSA(line,filename);
      else
        ct := 1;
        first := true;
        while ct <= sr.size do
          if IsBound(sr.setToLabels[ct]) then
             if first then
               line := Concatenation
                     (line,"[",String(ct),",",String(sr.setToLabels[ct]),"]");
               first := false;
             else
               line := Concatenation(line,",");
               LinePrintFSA(line,filename);
               line := String("",21+offset);
               line := Concatenation
                     (line,"[",String(ct),",",String(sr.setToLabels[ct]),"]");
             fi;
          fi;
          ct := ct+1;
        od;
        LinePrintFSA(line,filename);
        line := Concatenation(String("",20+offset),"]");
        LinePrintFSA(line,filename);
      fi;
    elif sr.type <> "simple" then
       Error("Invalid type for set record.");
    fi;
    if name = "" then
      line := Concatenation(")",endsymbol);
    else 
      line := String(")",16+offset-4);
      line := Concatenation(line,endsymbol);
    fi;
    LinePrintFSA(line,filename);
end;

#############################################################################
##
#F  WriteFSA(<fsa> [<name>, <filename>, <endsymbol>]) . . print the fsa "fsa"
##
##  WriteFSA prints the fsa <fsa> 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 LinePrintFSA to print each individual line.
##  If the optional string <name> is present, printing is preceded by an
##  assignment "name:=", so that the resulting file can be read back in.
##  If the optional string <endsymbol> is present, then this is printed at
##  the end (it is likely to be ";" or ",").
##
##  Public function.
WriteFSA := function ( arg )
    local  fsa, ns, ne, filename, name, tabletype, table, endsymbol,
           line, ct, first, i, j;
    fsa := arg[1];
    filename := "";
    name := "";
    endsymbol := "";
    if Length(arg)>=2 then
      name := arg[2];
    fi;
    if Length(arg)>=3 then
      filename := arg[3];
    fi;
    if Length(arg)>=4 then
      endsymbol := arg[4];
    fi;

    if not IsInitializedFSA(fsa) then
      InitializeFSA(fsa);
    fi;
    ns := fsa.states.size;
    ne := fsa.alphabet.size;
    if filename = "" and name = "" then
      Print("rec (\n");
    elif filename = "" and name <> "" then
      Print(name," := \nrec (\n");
    elif filename <> "" and name = "" then
      PrintTo(filename,"rec (\n");
    else
      PrintTo(filename,name," := \nrec (\n");
    fi;

    line := String("isFSA",16);
    line := Concatenation(line," := true,");
    LinePrintFSA(line,filename);

    WriteSetRecordSR(fsa.alphabet,"alphabet",filename,4,",");
    WriteSetRecordSR(fsa.states,"states",filename,4,",");

    line := String("flags",16);
    line := Concatenation(line," := [");
    first := true;
    for i in fsa.flags do
      if not first then
        line := Concatenation(line,",");
      fi;
      first := false;
      line := Concatenation(line,"\"",i,"\"");
    od;
    line := Concatenation(line,"],");
    LinePrintFSA(line,filename);

    line := String("initial",16);
    line := Concatenation(line," := [");
    ct := 1;
    while ct<= Length(fsa.initial) do
      if ct=1 or Length(line)+Length(String(fsa.initial[ct])) <= 76 then
        if ct > 1 then
           line := Concatenation(line,",");
        fi;
        line := Concatenation(line,String(fsa.initial[ct]));
      else
        line := Concatenation(line,",");
        LinePrintFSA(line,filename);
        line := String("",21);
        line := Concatenation(line,String(fsa.initial[ct]));
      fi;
      ct := ct+1;
    od;
    line := Concatenation(line,"],");
    LinePrintFSA(line,filename);

    line := String("accepting",16);
    line := Concatenation(line," := [");
    ct := 1;
    if ns>1 and fsa.accepting=[1..ns] then
       line := Concatenation(line,"1..",String(ns));
    else
      while ct<= Length(fsa.accepting) do
        if ct=1 or Length(line)+Length(String(fsa.accepting[ct])) <= 76 then
          if ct > 1 then
             line := Concatenation(line,",");
          fi;
          line := Concatenation(line,String(fsa.accepting[ct]));
        else
          line := Concatenation(line,",");
          LinePrintFSA(line,filename);
          line := String("",21);
          line := Concatenation(line,String(fsa.accepting[ct]));
        fi;
        ct := ct+1;
      od;
    fi;
    line := Concatenation(line,"],");
    LinePrintFSA(line,filename);
    
    tabletype := fsa.table.printingFormat;

    if tabletype="dense deterministic"  then
      if IsDeterministicFSA(fsa) = false then
        fsa.table.printingFormat := "sparse"; 
        tabletype := fsa.table.printingFormat;
      elif not IsBound(fsa.denseDTable) then
        DenseDTableFSA(fsa);
      fi;
    fi;
    if tabletype="sparse" and not IsBound(fsa.sparseTable) then
      SparseTableFSA(fsa);
    fi;
    if tabletype="dense deterministic" then
      table := fsa.denseDTable;
      fsa.table.format := "dense deterministic";
 # Calculate number of nontrivial transitions
      ct := 0;
      for i in [1..ns] do
        for j in [1..ne] do
          if table[i][j] <> 0 then
            ct := ct+1;
          fi;
        od;
      od;
    else
      table := fsa.sparseTable;
      fsa.table.format := "sparse";
 # Calculate number of nontrivial transitions
      ct := 0;
      for i in [1..ns] do
        ct := ct + Length(table[i]);
      od;
    fi;
    fsa.table.numTransitions := ct;

    line := Concatenation(String("table",16)," := rec(");
    LinePrintFSA(line,filename);
    line := Concatenation(String("format",20),
                                      " := ","\"",fsa.table.format,"\"",",");
    LinePrintFSA(line,filename);
    line := Concatenation(String("numTransitions",20)," := ",
                           String(fsa.table.numTransitions),",");
    LinePrintFSA(line,filename);
    if tabletype = "sparse" and IsBound(fsa.table.defaultTarget) then
      line := Concatenation(line,String("defaultTarget",20)," := ");
      line := Concatenation(line,String(fsa.table.defaultTarget),",");
      LinePrintFSA(line,filename);
    fi;
    line := Concatenation(String("transitions",20)," := [");
    if ns=0 then
      LinePrintFSA(line,filename);
    fi;
    first := true;
    for i in [1..ns] do
      if first then
        line := Concatenation(line,"[");
        first := false;
      else
        line := Concatenation(String("",25),"[");
      fi;
      ct := 1;
      while ct<= Length(table[i]) do
        if ct=1 or Length(line)+Length(String(table[i][ct])) <= 76 then
          if ct > 1 then
             line := Concatenation(line,",");
          fi;
          line := Concatenation(line,String(table[i][ct]));
        else
          line := Concatenation(line,",");
          LinePrintFSA(line,filename);
          line := String("",25);
          line := Concatenation(line,String(table[i][ct]));
        fi;
        ct := ct+1;
      od;
      line := Concatenation(line,"]");
      if i<ns then
        line := Concatenation(line,",");
      fi;
      LinePrintFSA(line,filename);
    od;
    line := Concatenation(String("",20),"]");
    LinePrintFSA(line,filename);
    line := Concatenation(String("",16),")");
    LinePrintFSA(line,filename);

    line := Concatenation(")",endsymbol);
    LinePrintFSA(line,filename);
end;

#############################################################################
##
#F  ElementNumberSR(<sr>, <el>)  . . . get number of set-record element.
##
##  <sr> should be a set-record. <el> should either be a positive integer
##  representing an element of <sr> or a name for such an element.
##  In either case, the number of the element is returned.
##  False is returned if <el> is invalid.
##
##  Private function.
ElementNumberSR := function ( sr, el )
  local IdWord;
  if IsAssocWordWithOne(el) then
    IdWord := el^0;
  else IdWord := false;
  fi;
  if IsInt(el) then
    if el>sr.size+1 then
      return false;
    else
      return el;
    fi;
  elif el=IdWord then
    return sr.size+1;
  elif IsBound(sr.names) then
    return Position(sr.names,el);
  else
    return false;
  fi;
end;

#############################################################################
##
#F  TargetDFA(<fsa>,<e>,<s>) . . . . . . . target of edge in a dfa
##  TargetDFA calculates  and returns the target of the edge in the dfa <fsa>
##  with edge <e> and source-state <s>.
##  0 is returned if there is no edge.
##  <e> can either be the number of an edge or an edge-name.
##  <s> can either be the number of a state or a state-name.
##  The returned value has same type as <s> (or 0).
##  Public function.
TargetDFA := function ( fsa,e,s )
    local tname, term, row, ns, t, ng;
    if not IsInitializedFSA(fsa) then
       InitializeFSA(fsa);
    fi;
    if IsDeterministicFSA(fsa)=false  then
       Error("First argument is not a dfa.");
    fi;
    if IsList(e) then
      ng := fsa.alphabet.base.size;
      e := (ElementNumberSR(fsa.alphabet.base,e[1])-1) * (ng+1) + 
       ElementNumberSR(fsa.alphabet.base,e[2]);
    else 
      e := ElementNumberSR(fsa.alphabet,e);
    fi;
    if e=false then
      Error("Second argument is not a valid edge number or label.");
    fi;
    tname := not IsInt(s);
    s := ElementNumberSR(fsa.states,s);
    if s=false then
       Error("Third argument is not a valid state number or name.");
    fi;

    ns := fsa.states.size;
    if IsBound(fsa.denseDTable) then
      t := fsa.denseDTable[s][e];
      if t=0 then
        return 0;
      fi;
      if tname then
        return  fsa.states.names[t];
      fi;
      return t;
    fi;

    row := fsa.sparseTable[s];
    for term in row do
      if term[1]=e then
        t := term[2];
        if tname then
          if t=0 then
            return 0;
          fi;
          return fsa.states.names[t];
        fi;
        return t;
      fi;
    od;
    if IsBound(fsa.table.defaultTarget) then
      t := fsa.table.defaultTarget;
    else
      return 0;
    fi;
    if tname then
      if t=0 then
        return 0;
      fi;
      return fsa.states.names[t];
    fi;
    return t;

end;

#############################################################################
##
#F  TargetsFSA(<fsa>,<e>,<s>) . . . . . . . targets of edge
##  TargetsFSA calculates the targets of the edges in the fsa <fsa>
##  with edge <e> and source-state <s>.
##  The result is returned as a list of targets.
##  <l> can either be the number of an edge-label or an edge-label-name.
##  <s> can either be the number of a state or a state-name.
##  The members of the returned list have same type as <s>.
##  Public function.
TargetsFSA := function ( fsa,e,s )
    local tname, term, targets, row, ns, t, ng;
    if not IsInitializedFSA(fsa) then
       InitializeFSA(fsa);
    fi;
    if IsList(e) then
      ng := fsa.alphabet.base.size;
      e := (ElementNumberSR(fsa.alphabet.base,e[1])-1) * (ng+1) + 
       ElementNumberSR(fsa.alphabet.base,e[2]);
    else 
      e := ElementNumberSR(fsa.alphabet,e);
    fi;
    if e=false then
      Error("Second argument is not a valid edge number or label.");
    fi;
    tname := not IsInt(s);
    s := ElementNumberSR(fsa.states,s);
    if s=false then
       Error("Third argument is not a valid state number or name.");
    fi;

    ns := fsa.states.size;
    if IsBound(fsa.denseDTable) then
      if e=0 then
         return [];
      fi;
      t := fsa.denseDTable[s][e];
      if t=0 then
        return [];
      fi;
      if tname then
        return [ fsa.states.names[t] ];
      fi;
      return [ t ];
    fi;

    row := fsa.sparseTable[s];
    targets := [];
    for term in row do
      if term[1]=e then
        if tname then
          if term[2]>0 and term[2]<=ns then
            Add(targets,fsa.states.names[term[2]]);
          fi;
        else
          Add(targets,term[2]);
        fi;
      fi;
    od;
    if targets=[] and IsBound(fsa.table.defaultTarget) then
      if tname then
        Add(targets,fsa.states.names[fsa.table.defaultTarget]);
      else
        Add(targets,fsa.table.defaultTarget);
      fi;
    fi;
    return targets;

end;

#############################################################################
##
#F  SourcesFSA(<fsa>,<e>,<s>) . . . . . . . sources of edge
##  SourcesFSA calculates the sources of the edges in the fsa <fsa>
##  with edge <e> and source-state <s>.
##  The result is returned as a list of sources.
##  <l> can either be the number of an edge-label or an edge-label-name.
##  <s> can either be the number of a state or a state-name.
##  The members of the returned list have same type as <s>.
##  Public function.
SourcesFSA := function ( fsa,e,s )
    local tname, term, sources, row, ns, i, none, ng;
    if not IsInitializedFSA(fsa) then
       InitializeFSA(fsa);
    fi;
    if IsList(e) then
      ng := fsa.alphabet.base.size;
      e := (ElementNumberSR(fsa.alphabet.base,e[1])-1) * (ng+1) + 
       ElementNumberSR(fsa.alphabet.base,e[2]);
    else 
      e := ElementNumberSR(fsa.alphabet,e);
    fi;
    if e=false then
      Error("Second argument is not a valid edge number or label.");
    fi;
    tname := not IsInt(s);
    s := ElementNumberSR(fsa.states,s);
    if s=false then
       Error("Third argument is not a valid state number or name.");
    fi;

  #We need the backTable for this.
    BackTableFSA(fsa);
    row := fsa.backTable[s];
    sources := [];
    ns := fsa.states.size;
    for term in row do
      if term[1]=e then
        if tname then
          if term[2]>0 and term[2]<=ns then
            Add(sources,fsa.states.names[term[2]]);
          fi;
        else
          Add(sources,term[2]);
        fi;
      fi;
    od;
    if IsBound(fsa.table.defaultTarget) and fsa.table.defaultTarget=s and
          IsBound(fsa.sparseTable) then
    # there may be some "default-target" edges.
       for i in [1..ns] do
         row := fsa.sparseTable[i];
         none := true;
         for term in row do
           if term[1]=e then none:=false; fi;
         od;
         if none then
           Add(sources,i);
         fi;
       od;
       sources := Set(sources);
    fi;
    return sources;
end;

#############################################################################
##
#F  IsAcceptedWordDFA(<fsa>,<w>) . . . . . . . tests if word accepted by fsa
##  IsAcceptedWordDFA tests whether the word <w> is accepted by the
##  deterministic fsa <fsa>, and returns true or false.
##  <w> can either be a list of labels or a list of label numbers,
##  or a word in the labels for <fsa>.
##  (In the last case, the labels must be abstract generators.)
##  For an n-variable fsa, it can also be a list of n words (which will
##  be padded out at the end by the padding symbol).
##  Public function.
IsAcceptedWordDFA := function ( fsa,w )
    local state, label, len, iw, inw, nl, ps, ns, nv, i, pos, dead;
    if not IsInitializedFSA(fsa) then
       InitializeFSA(fsa);
    fi;
    if IsDeterministicFSA(fsa)=false then
       Error("Argument is not a dfa.");
    fi;
    if fsa.initial=[] then
      return false;
    fi;
    ns := fsa.states.size;
    state := fsa.initial[1];
    iw := IsWord(w);
    inw := false;
    if not iw and not IsList(w) then
      Error("Second argument must be a word or a list.");
    fi;
    if IsBound(fsa.alphabet.arity) and IsList(w) and IsWord(w[1]) then
      if not IsBound(fsa.alphabet.base.names) then
        Error("Can only span n-tuple of words if base-alphabet has names.");
      fi;
      if not IsBound(fsa.alphabet.padding) then
        Error("Can only span n-tuple of words if there is a padding symbol.");
      fi;
      ps := fsa.alphabet.padding;
      inw := true;
      nv := fsa.alphabet.arity;
      nl := [];
      for i in [1..nv] do
        nl[i] := Length(w[i]);
      od;
      len := Maximum(nl);
    elif iw then
      len := Length(w);
    else
      len := Length(w);
    fi;
    dead := false;
    pos := 1;
    while not dead and pos <= len do
      if inw then
         label := [];
         for i in [1..nv] do
           if pos <= nl[i] then
             label[i] := Subword(w[i],pos,pos);
           else
             label[i] := ps;
           fi;
         od;
      elif iw then
        label := Subword(w,pos,pos);
      else
        label := w[pos];
      fi;
      state := TargetDFA(fsa,label,state);
      if not state in [1..ns] then
        dead:=true;
      fi;
      pos := pos+1;
    od;
    if dead then
      return false;
    fi;
    return state in fsa.accepting;
end;

#############################################################################
##
#F  WordTargetDFA(<fsa>,<w>) . . . . . . . target of word under DFA
##  WordTargetDFA finds the target state when the word <w> is read by
##  the deterministic fsa <fsa>, and returns this state or 0.
##  <w> can either be a list of labels or a list of label numbers,
##  or a word in the labels for <fsa>.
##  (In the last case, the labels must be abstract generators.)
##  For an n-variable fsa, it can also be a list of n words (which will
##  be padded out at the end by the padding symbol).
##  Public function.
WordTargetDFA := function ( fsa,w )
    local state, label, len, iw, inw, nl, ps, ns, nv, i, pos, dead;
    if not IsInitializedFSA(fsa) then
       InitializeFSA(fsa);
    fi;
    if IsDeterministicFSA(fsa)=false then
       Error("Argument is not a dfa.");
    fi;
    if fsa.initial=[] then
      return 0;
    fi;
    ns := fsa.states.size;
    state := fsa.initial[1];
    iw := IsWord(w);
    inw := false;
    if not iw and not IsList(w) then
      Error("Second argument must be a word or a list.");
    fi;
    if IsBound(fsa.alphabet.arity) and IsList(w) and IsWord(w[1]) then
      if not IsBound(fsa.alphabet.base.names) then
        Error("Can only span n-tuple of words if base-alphabet has names.");
      fi;
      if not IsBound(fsa.alphabet.padding) then
        Error("Can only span n-tuple of words if there is a padding symbol.");
      fi;
      ps := fsa.alphabet.padding;
      inw := true;
      nv := fsa.alphabet.arity;
      nl := [];
      for i in [1..nv] do
        nl[i] := Length(w[i]);
      od;
      len := Maximum(nl);
    elif iw then
      len := Length(w);
    else
      len := Length(w);
    fi;
    dead := false;
    pos := 1;
    while not dead and pos <= len do
      if inw then
         label := [];
         for i in [1..nv] do
           if pos <= nl[i] then
             label[i] := Subword(w[i],pos,pos);
           else
             label[i] := ps;
           fi;
         od;
      elif iw then
        label := Subword(w,pos,pos);
      else
        label := w[pos];
      fi;
      state := TargetDFA(fsa,label,state);
      if not state in [1..ns] then
        dead:=true;
      fi;
      pos := pos+1;
    od;
    if dead then
      return 0;
    fi;
    return state;
end;

#############################################################################
##
#F  AddStateFSA(<fsa>[,<name>]) . . . . . . . adds state to an fsa
##  AddStateFSA adds a state to the end of the statelist of the fsa <fsa>.
##  It has the optional name or label <name> (but if the state-type is
##  "named", then the name must be supplied).
##  Public function.
AddStateFSA := function ( arg )
    local fsa, name, ns, new, i, dt;
    fsa := arg[1];
    if fsa.states.type = "product" then
      Error("Cannot alter a product-type state-list");
    fi;
    if Length(arg)=1 and IsBound(fsa.states.names) then
       Error("You must supply a name for the new state.");
    fi;
    name:="";
    if Length(arg)=2 then
      name:=arg[2];
    fi;
    if not IsInitializedFSA(fsa) then
       InitializeFSA(fsa);
    fi;
    fsa.states.size := fsa.states.size+1;
    ns := fsa.states.size;
    if name<>"" and IsBound(fsa.states.names) then
        fsa.states.names[ns] := name;
    fi;
    if IsBound(fsa.denseDTable) then
      if IsBound(fsa.table.defaultTarget) then
        dt := fsa.table.defaultTarget;
      else
        dt := 0;
      fi;
      new:=[];
      for i in [1..fsa.alphabet.size] do new[i]:=dt; od;
      Add(fsa.denseDTable,new);
    fi;
    if IsBound(fsa.sparseTable) then
      new:=[];
      Add(fsa.sparseTable,new);
    fi;
    Unbind(fsa.backTable);
    RemoveSet(fsa.flags,"minimized");
    RemoveSet(fsa.flags,"trim");
    RemoveSet(fsa.flags,"accessible");
end;

#############################################################################
##
#F  DeleteListFSA(<l>,<n>) . . . . . . . delete element from list
##  DeleteListFSA deletes the <n>-th element from list <l>, and closes
##  up. Used for deleting state from an fsa.
##
DeleteListFSA := function(l,n)
    local i, len;
    len := Length(l);
    for i in [n..len] do
      if IsBound(l[i+1]) then
        l[i]:=l[i+1];
      else
        Unbind(l[i]);
      fi;
    od;
end;

#############################################################################
##
#F  DeleteStateFSA(<fsa>) . . . . . . . delete state from an fsa
##  DeleteStateFSA deletes the final state of the fsa <fsa>.
##  All edges to and from that state are deleted.
##  To delete a state other than the final, first call PermuteStatesFSA
##  Public function.
DeleteStateFSA := function ( fsa )
    local  ns, ng, row, i, j;
    if fsa.states.type = "product" then
      Error("Cannot alter a product-type state-list");
    fi;
    if not IsInitializedFSA(fsa) then
       InitializeFSA(fsa);
    fi;
    ns := fsa.states.size;
    ng := fsa.alphabet.size;
    fsa.states.size:=ns-1;
    if IsBound(fsa.states.names) then
      Unbind(fsa.states.names[ns]);
    fi;
    if IsBound(fsa.states.setToLabels) then
      Unbind(fsa.states.setToLabels[ns]);
    fi;
    j := Position(fsa.initial,ns);
    if j <> fail then
      DeleteListFSA(fsa.initial,j);
    fi;
    j := Position(fsa.accepting,ns);
    if j <> fail then
      DeleteListFSA(fsa.accepting,j);
    fi;
    if IsBound(fsa.table.defaultTarget) and fsa.table.defaultTarget=ns then
      Unbind(fsa.table.defaultTarget);
    fi;
    if IsBound(fsa.denseDTable) then
      for i in [1..ns-1] do
        row:=fsa.denseDTable[i];
        for j in [1..ng] do
          if row[j]=ns then
            row[j]:=0;
          fi;
        od;
      od;
      DeleteListFSA(fsa.denseDTable,ns);
    fi;
    if IsBound(fsa.sparseTable) then
      for i in [1..ns-1] do
        row:=fsa.sparseTable[i];
        j := 1;
        while j <= Length(row) do
          if row[j][2]=ns then
            DeleteListFSA(row,j);
            j:=j-1;
          fi;
          j:=j+1;
        od;
      od;
      DeleteListFSA(fsa.sparseTable,ns);
    fi;
    Unbind(fsa.backTable);
    RemoveSet(fsa.flags,"NFA");
    RemoveSet(fsa.flags,"BFS");
    RemoveSet(fsa.flags,"minimized");
    RemoveSet(fsa.flags,"trim");
    RemoveSet(fsa.flags,"accessible");
end;

#############################################################################
##
#F  PermuteStatesFSA(<fsa>,p) . . . . . . . permute states of an fsa
##  PermuteStatesFSA permutes the states of the fsa <fsa>.
##  <p> should be a permutation of the state numbers.
##  What was state n is renumbered state n^p.
##  Public function.
PermuteStatesFSA := function ( fsa,p )
    local  ns, term, row, i, j, new, ne;
    if fsa.states.type = "product" then
      Error("Cannot alter a product-type state-list");
    fi;
    if not IsInitializedFSA(fsa) then
       InitializeFSA(fsa);
    fi;
    ns := fsa.states.size;
    ne := fsa.alphabet.size;
    if p = () then
      return;
    fi;
    if not IsPerm(p) or LargestMovedPointPerm(p)>ns then
      Error("Second argument is invalid.");
    fi;
    if IsBound(fsa.states.names) then
      new := [];
      for i in [1..ns] do
         new[i^p]:=fsa.states.names[i];
      od;
      fsa.states.names := new;
    fi;
    if IsBound(fsa.states.setToLabels) then
      new := [];
      for i in [1..ns] do
         if IsBound(fsa.states.setToLabels[i]) then
           new[i^p]:=fsa.states.setToLabels[i];
         fi;
      od;
      fsa.states.setToLabels := new;
    fi;
    for i in [1..Length(fsa.initial)] do
       fsa.initial[i]:=fsa.initial[i]^p;
    od;
    fsa.initial := Set(fsa.initial);
    for i in [1..Length(fsa.accepting)] do
       fsa.accepting[i]:=fsa.accepting[i]^p;
    od;
    fsa.accepting := Set(fsa.accepting);
    if IsBound(fsa.table.defaultTarget) and fsa.table.defaultTarget>0 then
      fsa.table.defaultTarget := fsa.table.defaultTarget^p;
    fi;
    if IsBound(fsa.denseDTable) then
      new := [];
      for i in [1..ns] do
        row := fsa.denseDTable[i];
        new[i^p] := row;
        for j in [1..ne] do
          if row[j]>0 then
            row[j] := row[j]^p;
          fi;
        od;
      od;
      fsa.denseDTable := new;
    fi;
    if IsBound(fsa.sparseTable) then
      new := [];
      for i in [1..ns] do
        row := fsa.sparseTable[i];
        new[i^p] := row;
        for term in row do
          if term[2]>0 then
            term[2] := term[2]^p;
          fi;
        od;
      od;
      fsa.sparseTable := new;
    fi;
    if fsa.table.format = "dense deterministic" then
      fsa.table.transitions := fsa.denseDTable;
    else
      fsa.table.transitions := fsa.sparseTable;
    fi;
    Unbind(fsa.backTable);
    RemoveSet(fsa.flags,"BFS");
end;

#############################################################################
##
#F  AddLetterFSA(<fsa>[,<name>]) . . . . . . . adds letter to alphabet of fsa
##  AddLetterFSA adds an extra symbol to the alphabet of the fsa <fsa>.
##  It has the optional name or label <name> (but if the alphabet-type is
##  a named type, then the name must be supplied).
##  Public function.
AddLetterFSA := function ( arg )
    local  fsa, name,  ne, new, term;
    fsa := arg[1];
    if not IsInitializedFSA(fsa) then
       InitializeFSA(fsa);
    fi;
    if fsa.alphabet.type = "product" then
      Error("Cannot alter a product-type alphabet");
    fi;
    if Length(arg)=1 and IsBound(fsa.alphabet.names) then
       Error("You must supply a name for the new state.");
    fi;
    name := "";
    if Length(arg)=2 then
      name := arg[2];
    fi;
    if not IsInitializedFSA(fsa) then
       Error("First argument is not an initialized fsa.");
    fi;
    fsa.alphabet.size := fsa.alphabet.size+1;
    ne := fsa.alphabet.size;
    if name<>""  and  IsBound(fsa.alphabet.names) then
        fsa.alphabet.names[ne] := name;
    fi;
    if IsBound(fsa.denseDTable) then
      for term in fsa.denseDTable do
        if IsBound(fsa.table.defaultTarget) then
          Add(term,fsa.table.defaultTarget);
        else
          Add(term,0);
        fi;
      od;
    fi;
end;

#############################################################################
##
#F  DeleteLetterFSA(<fsa>) . . . . . . . deletes alphabet letter from an fsa
##  DeleteLetterFSA deletes the final alphabet label from the fsa <fsa>.
##  All edges with that label are deleted.
##  To delete an edge-label other than the final, first call
##  PermuteLettersFSA
##  Public function.
DeleteLetterFSA := function ( fsa )
    local  ns, ne, term, row, i, j;
    if not IsInitializedFSA(fsa) then
       InitializeFSA(fsa);
    fi;
    if fsa.alphabet.type = "product" then
      Error("Cannot alter a product-type alphabet");
    fi;
    ns := fsa.states.size;
    ne := fsa.alphabet.size;
    fsa.alphabet.size := ne-1;
    if IsBound(fsa.alphabet.names) then
      Unbind(fsa.alphabet.names[ne]);
    fi;
    if IsBound(fsa.alphabet.setToLabels) then
      Unbind(fsa.alphabet.setToLabels[ne]);
    fi;
    RemoveSet(fsa.flags,"BFS");
    RemoveSet(fsa.flags,"minimized");
    if IsBound(fsa.denseDTable) then
      for row in fsa.denseDTable do
        DeleteListFSA(row,ne);
      od;
    fi;
    if IsBound(fsa.sparseTable) then
      for row in fsa.sparseTable do
        j := 1;
        while j <= Length(row) do
          if row[j][1]=ne then
            DeleteListFSA(row,j);
          fi;
          j:=j+1;
        od;
      od;
    fi;
    Unbind(fsa.backTable);
    RemoveSet(fsa.flags,"NFA");
    RemoveSet(fsa.flags,"BFS");
    RemoveSet(fsa.flags,"minimized");
    RemoveSet(fsa.flags,"trim");
    RemoveSet(fsa.flags,"accessible");
end;

#############################################################################
##
#F  PermuteLettersFSA(<fsa>,p) . . . . . . . permute alphabet labels of an fsa
##  PermuteLettersFSA permutes the alphabet labels of the fsa <fsa>.
##  <p> should be a permutation of the alphabet labels numbers.
##  What was edge-label n is renumbered alphabet label n^p.
##  Public function.
PermuteLettersFSA := function ( fsa,p )
    local  ns, ne, term, row, i, j, new;
    if not IsInitializedFSA(fsa) then
       InitializeFSA(fsa);
    fi;
    if fsa.alphabet.type = "product" then
      Error("Cannot alter a product-type alphabet");
    fi;
    ns := fsa.states.size;
    ne := fsa.alphabet.size;
    if not IsPerm(p) or LargestMovedPointPerm(p)>ne then
      Error("Second argument is invalid.");
    fi;
    if IsBound(fsa.alphabet.names) then
      new := [];
      for i in [1..ne] do
        new[i^p]:=fsa.alphabet.names[i];
      od;
      fsa.alphabet.names := new;
    fi;
    if IsBound(fsa.alphabet.setToLabels) then
      new := [];
      for i in [1..ne] do
         if IsBound(fsa.alphabet.setToLabels[i]) then
           new[i^p]:=fsa.alphabet.setToLabels[i];
         fi;
      od;
      fsa.alphabet.setToLabels := new;
    fi;
    if IsBound(fsa.denseDTable) then
      for i in [1..ns] do
        row := fsa.denseDTable[i];
        new := [];
        for j in [1..ne] do
          new[j^p] := row[j];
        od;
        fsa.denseDTable[i] := new;
      od;
    fi;
    if IsBound(fsa.sparseTable) then
      new := [];
      for i in [1..ns] do
        row := fsa.sparseTable[i];
        for term in row do
          if term[1]>0 then
            term[1] := term[1]^p;
          fi;
        od;
      od;
    fi;
    if fsa.table.format = "dense deterministic" then
      fsa.table.transitions := fsa.denseDTable;
    else
      fsa.table.transitions := fsa.sparseTable;
    fi;
    Unbind(fsa.backTable);
    RemoveSet(fsa.flags,"BFS");
end;

#############################################################################
#F  AddEdgeFSA(<fsa>,<e>,<s>,<t>) . . . . . . . adds edge to an fsa
##  AddEdge adds an edge with source <s>, label <e> and target <t>
##  to the fsa <fsa> (if there isn't one already. 
##  <s> and <t> can be either numbers or names of states,
##  and <e> a number or name of an edge-label.
##  Public function.
AddEdgeFSA := function ( fsa, e, s, t )
    local row, term, ng;
    if not IsInitializedFSA(fsa) then
       InitializeFSA(fsa);
    fi;
    if IsList(e) then
      ng := fsa.alphabet.base.size;
      e := (ElementNumberSR(fsa.alphabet,e[1])-1) * (ng+1) + 
       ElementNumberSR(fsa.alphabet,e[2]);
    else 
      e := ElementNumberSR(fsa.alphabet,e);
    fi;
    if e=false then
      Error("Second argument is not a valid edge number or label.");
    fi;
    s := ElementNumberSR(fsa.states,s);
    if s=false then
       Error("Third argument is not a valid state number or name.");
    fi;
    t := ElementNumberSR(fsa.states,t);
    if t=false then
       Error("Fourth argument is not a valid state number or name.");
    fi;
    if e=0 or (IsBound(fsa.denseDTable) and fsa.denseDTable[s][e]>0 and
       fsa.denseDTable[s][e]<=fsa.states.size and
       fsa.denseDTable[s][e] <> t) then
    # makes non-deterministic.
      Print("Warning: gone non-deterministic!\n");
      SparseTableFSA(fsa);
      fsa.table.format := "sparse";
      fsa.table.transitions := fsa.sparseTable;
      Unbind(fsa.denseDTable);
      RemoveSet(fsa.flags,"DFA");
      AddSet(fsa.flags,"NFA");
    fi;
    if IsBound(fsa.denseDTable) then
      fsa.denseDTable[s][e] := t;
    fi;
    if IsBound(fsa.sparseTable) then
      row := fsa.sparseTable[s];
      for term in row do
        if term[1]=e and term[2]>0 and term[2]<=fsa.states.size
            and term[2] <> t then
          #makes non-deterministic
          RemoveSet(fsa.flags,"DFA");
          AddSet(fsa.flags,"NFA");
          fsa.table.format := "sparse";
        fi;
      od;
      Add(row,[e,t]);
      fsa.sparseTable[s]:=Set(row);
    fi;
    Unbind(fsa.backTable);
    RemoveSet(fsa.flags,"BFS");
    RemoveSet(fsa.flags,"minimized");
end;

#############################################################################
##
#F  DeleteEdgeFSA(<fsa>,<e>,<s>,<t>) . . . . . . . deletes edge from an fsa
##  DeleteEdgeFSA deletes an edge with source <s>, label <e> and target <t>
##  from the fsa <fsa> (if there is one). 
##  <s> and <t> can be either numbers or names of states (but both the same),
##  and <e> a number or name of an edge-label.
##  Public function.
DeleteEdgeFSA := function ( fsa, e, s, t )
    local ng, row, term, subterm,  j, dfa_check;
    if not IsInitializedFSA(fsa) then
       InitializeFSA(fsa);
    fi;
    if IsList(e) then
      ng := fsa.alphabet.base.size;
      e := (ElementNumberSR(fsa.alphabet,e[1])-1) * (ng+1) + 
       ElementNumberSR(fsa.alphabet,e[2]);
    else 
      e := ElementNumberSR(fsa.alphabet,e);
    fi;
    if e=false then
      Error("Second argument is not a valid edge number or label.");
    fi;
    s := ElementNumberSR(fsa.states,s);
    if s=false then
       Error("Third argument is not a valid state number or name.");
    fi;
    t := ElementNumberSR(fsa.states,t);
    if t=false then
       Error("Fourth argument is not a valid state number or name.");
    fi;
    if IsBound(fsa.denseDTable) and fsa.denseDTable[s][e] = t then
      fsa.denseDTable[s][e]:=0;
    fi;
    if IsBound(fsa.sparseTable) then
      row := fsa.sparseTable[s];
      j := 1;
      while j <= Length(row) do
        term := row[j];
        if term[1]=e and term[2] = t then
          DeleteListFSA(row,j);
        fi;
        j := j+1;
      od;
    fi;
    Unbind(fsa.backTable);
    RemoveSet(fsa.flags,"BFS");
    RemoveSet(fsa.flags,"minimized");
    RemoveSet(fsa.flags,"NFA");
    # may have gone deterministic.
    RemoveSet(fsa.flags,"accessible");
    RemoveSet(fsa.flags,"trim");
end;

#############################################################################
##
#F  AcceptingStatesFSA(<fsa>) . . . the accepting states of <fsa>
##
AcceptingStatesFSA := function(fsa)
  return fsa.accepting;
end;

#############################################################################
##
#F  SetAcceptingFSA(<fsa>, <s>, <flag>) . . . set category of state <s>
##
##  s should be a number or name of a state in fsa <fsa>. This state
##  is made into an accepting state or not according to whether
##  <flag> is true or false.
##
##  Public function
SetAcceptingFSA := function(fsa, s, flag)
    if not IsInitializedFSA(fsa) then
       InitializeFSA(fsa);
    fi;
    s := ElementNumberSR(fsa.states,s);
    if s=false then
       Error("Second argument is not a valid state number or name.");
    fi;
    if flag = true then
      AddSet(fsa.accepting,s);
    else
      RemoveSet(fsa.accepting,s);
    fi;
    RemoveSet(fsa.flags,"trim");
    RemoveSet(fsa.flags,"minimized");
end;

#############################################################################
##
#F  InitialStatesFSA(<fsa>) . . . the initial states of <fsa>
##
InitialStatesFSA := function(fsa)
  return fsa.initial;
end;

#############################################################################
##
#F  SetInitialFSA(<fsa>, <s>, <flag>) . . . set initiality of state <s>
##
##  s should be a number or name of a state in fsa <fsa>. This state
##  is made into an initial state or not according to whether
##  <flag> is true or false.
##
##  Public function
SetInitialFSA := function(fsa, s, flag)
    if not IsInitializedFSA(fsa) then
       InitializeFSA(fsa);
    fi;
    s := ElementNumberSR(fsa.states,s);
    if s=false then
       Error("Second argument is not a valid state number or name.");
    fi;
    if flag = true then
      AddSet(fsa.initial,s);
    else
      RemoveSet(fsa.initial,s);
    fi;
    RemoveSet(fsa.flags,"trim");
    RemoveSet(fsa.flags,"accessible");
    RemoveSet(fsa.flags,"BFS");
    RemoveSet(fsa.flags,"minimized");
    if Length(fsa.initial) > 1 then
      RemoveSet(fsa.flags,"DFA");
    else
      RemoveSet(fsa.flags,"NFA");
    fi;
end;

#############################################################################
##
#F  IsAccessibleFSA(<fsa>) . . . . . . . test whether fsa is a accessible fsa
##
## An accessible FSA is one in which there is a word in the language
## leading to every state.
## The string "accessible" is inserted in the list of flags when it is known
## to be accessible.
## Note that "trim" implies "accessible".
##
## Public function.
IsAccessibleFSA := function ( fsa )
    local ns, ne, ct, i, j, got, s, x, t;
    if not IsInitializedFSA(fsa) then
       InitializeFSA(fsa);
    fi;
    if "accessible" in fsa.flags or "trim" in fsa.flags then
      return true;
    fi;
    ns := fsa.states.size;
    ne := fsa.alphabet.size;
 # Find all accessible states i.e. states accessible from an initial state.
    got := ShallowCopy(fsa.initial);
    ct := Length(got);
    i := 1;
    while i <= ct do
      s := got[i];
      for j in [0..ne] do
        x := TargetsFSA(fsa,j,s);
        for t in x do
          if t>0 and not t in got then
            ct := ct+1;
            got[ct] := t;
          fi;
        od;
      od;
      i := i+1;
    od;
    if ct <> ns then
 # there are some inaccessible states, so fsa is not accessible.
      return false;
    fi;

    AddSet(fsa.flags,"accessible");
    return true;
end;

#############################################################################
##
#F  AccessibleFSA(<fsa>) . . . . . . . replace fsa by an accessible fsa
##
## AccessibleFSA(<fsa>) removes non-accessible from
## <fsa> to make it accessible, without changing the accepted language.
## An accessible FSA is one in which there is a word in the language
## leading to every state.
## The string "accessible" is inserted in the list of flags when it is known
## to be accessible.
##
## Public function.
AccessibleFSA := function ( fsa )
    local ns, ne, ct, i, j, got, s, x, t;
    if not IsInitializedFSA(fsa) then
       InitializeFSA(fsa);
    fi;
    if "accessible" in fsa.flags or "trim" in fsa.flags then
      return;
    fi;
    ns := fsa.states.size;
    ne := fsa.alphabet.size;
 # Find all accessible states i.e. states accessible from an initial state.
    got := ShallowCopy(fsa.initial);
    ct := Length(got);
    i := 1;
    while i <= ct do
      s := got[i];
      for j in [0..ne] do
        x := TargetsFSA(fsa,j,s);
        for t in x do
          if t>0 and not t in got then
            ct := ct+1;
            got[ct] := t;
          fi;
        od;
      od;
      i := i+1;
    od;
    if ct <> ns then
 # there are some inaccessible states, so remove them - because we can only
 # remove the last state, we have to work from the back.
      for s in Reversed([1..ns]) do
        if not s in got then
          if s<ns then
            PermuteStatesFSA(fsa,(s,ns));
          fi;
          DeleteStateFSA(fsa);
          ns := ns-1;
        fi;
      od;
    fi;
      
    AddSet(fsa.flags,"accessible");
end;
 
#############################################################################
##
#F  IsTrimFSA(<fsa>) . . . . . . . test whether fsa is a trim fsa
##
## A trim FSA is one in which there is an accepted word in the language
## through every state.
## The string "trim" is inserted in the list of flags when it is known
## to be trim.
##
## Public function.
IsTrimFSA := function ( fsa )
    local ns, ne, ct, i, j, got, s, x, t;
    if not IsInitializedFSA(fsa) then
       InitializeFSA(fsa);
    fi;
    if "trim" in fsa.flags  then
      return true;
    fi;
    ns := fsa.states.size;
    ne := fsa.alphabet.size;
 # First find all accessible states
 # i.e. states accessible from an initial state.
    got := ShallowCopy(fsa.initial);
    ct := Length(got);
    i := 1;
    while i <= ct do
      s := got[i];
      for j in [0..ne] do
        x := TargetsFSA(fsa,j,s);
        for t in x do
          if t>0 and not t in got then
            ct := ct+1;
            got[ct] := t;
          fi;
        od;
      od;
      i := i+1;
    od;
    if ct <> ns then
 # there are some inaccessible states, so fsa is not trim.
      return false;
    fi;
      
 # Next find all co-accessible states
 # i.e. states from which a path starts to an accepting state
    got := ShallowCopy(fsa.accepting);
    ct := Length(got);
    i := 1;
    while i <= ct do
      s := got[i];
      for j in [0..ne] do
        x := SourcesFSA(fsa,j,s);
        for t in x do
          if not t in got then
            ct := ct+1;
            got[ct] := t;
          fi;
        od;
      od;
      i := i+1;
    od;
    if ct <> ns then
 # there are some non co-accessible states, so fsa is not trim.
      return false;
    fi;
    AddSet(fsa.flags,"trim");
    return true;
end;

#############################################################################
##
#F  TrimFSA(<fsa>) . . . . . . . replace fsa by a trim fsa
##
## TrimFSA(<fsa>) removes non-accessible and non-coaccessible states from
## <fsa> to make it trim, without changing the accepted language.
## A trim FSA is one in which there is an accepted word in the language
## through every state.
## The string "trim" is inserted in the list of flags when it is known
## to be trim.
## (Removing the non-coaccessible states cannot possibly make any other
##  states non-accessible, so there is no need to repeat the process.)
##
## Public function.
TrimFSA := function ( fsa )
    local ns, ne, ct, i, j, got, s, x, t;
    if not IsInitializedFSA(fsa) then
       InitializeFSA(fsa);
    fi;
    if "trim" in fsa.flags  then
      return;
    fi;
    ns := fsa.states.size;
    ne := fsa.alphabet.size;
 # First find all accessible states
 # i.e. states accessible from an initial state.
    got := ShallowCopy(fsa.initial);
    ct := Length(got);
    i := 1;
    while i <= ct do
      s := got[i];
      for j in [0..ne] do
        x := TargetsFSA(fsa,j,s);
        for t in x do
          if t>0 and not t in got then
            ct := ct+1;
            got[ct] := t;
          fi;
        od;
      od;
      i := i+1;
    od;
    if ct <> ns then
 # there are some inaccessible states, so remove them - because we can only
 # remove the last state, we have to work from the back.
      for s in Reversed([1..ns]) do
        if not s in got then
          if s<ns then
            PermuteStatesFSA(fsa,(s,ns));
          fi;
          DeleteStateFSA(fsa);
          ns := ns-1;
        fi;
      od;
    fi;
      
 # Next find all co-accessible states
 # i.e. states from which a path starts to an accepting state
    got := ShallowCopy(fsa.accepting);
    ct := Length(got);
    i := 1;
    while i <= ct do
      s := got[i];
      for j in [0..ne] do
        x := SourcesFSA(fsa,j,s);
        for t in x do
          if not t in got then
            ct := ct+1;
            got[ct] := t;
          fi;
        od;
      od;
      i := i+1;
    od;
    if ct <> ns then
 # there are some non-coaccessible states, so remove them - because we can only
 # remove the last state, we have to work from the back.
      for s in Reversed([1..ns]) do
        if not s in got then
          if s<ns then
            PermuteStatesFSA(fsa,(s,ns));
          fi;
          DeleteStateFSA(fsa);
          ns := ns-1;
        fi;
      od;
    fi;
    AddSet(fsa.flags,"trim");
    RemoveSet(fsa.flags,"accessible"); #trim implies accessible
    RemoveSet(fsa.flags,"BFS");
end;
 
#############################################################################
##
#F  IsBFSFSA(<fsa>) . . . . . . . decide if fsa has the "bfs" property
##
## IsBFSFSA(<fsa>) decides if the fsa <fsa> has the breadth-first-search
## property. This means that it is accessible and, scanning the transition table
## along the states, one encounters the states in ascending numerical order.
## It is useful for comparing two fsa's.
##
## Public function.
IsBFSFSA := function ( fsa )
    local ns, ne, ct, i, j, got, s, x, t;
    if not IsInitializedFSA(fsa) then
       InitializeFSA(fsa);
    fi;
    if "BFS" in fsa.flags  then
      return true;
    fi;
    if not IsAccessibleFSA(fsa) then
      return false;
    fi;
    ns := fsa.states.size;
    ne := fsa.alphabet.size;
    if fsa.initial <> [1..Length(fsa.initial)] then
      return false;
    fi;
    ct := Length(fsa.initial);
    i := 1;
    while i <= ct do
      for j in [0..ne] do
        x := TargetsFSA(fsa,j,i);
        for t in x do
          if t>ct then
            if t <> ct+1 then
              return false;
            fi;
            ct := ct+1;
            if ct = ns then
              AddSet(fsa.flags,"BFS");
              return true;
            fi;
          fi;
        od;
      od;
      i := i+1;
    od;

end;

#############################################################################
##
#F  BFSFSA(<fsa>) . . . . . . . replace fsa by an fsa with the "bfs" property
##
## BFSFSA(<fsa>) replaces the fsa <fsa> by one with the same language
## that has the breadth-first-search property. This means that, scanning
## the transition table along the states, one encounters the states
## in ascending numerical order. It is useful for comparing two fsa's.
## It first makes the fsa trim, and then calculates the required
## state-permutation to achieve bfs-form, and calls PermuteStatesFSA.
##
## Public function.
BFSFSA := function ( fsa )
    local ns, ne, ct, perm, i, j, got, s, x, t;
    if not IsInitializedFSA(fsa) then
       InitializeFSA(fsa);
    fi;
    if "BFS" in fsa.flags  then
      return;
    fi;
    AccessibleFSA(fsa);
    ns := fsa.states.size;
    ne := fsa.alphabet.size;
 # We calculate the required permutation by building up the list perm
 # perm[i]=j means that the i-th state in the new order will be the
 # current j-th state - so we will call PermuteStatesFSA with the
 # inverse of perm.
    perm := ShallowCopy(fsa.initial);
    ct := Length(perm);
    i := 1;
    while i <= ct do
      s := perm[i];
      for j in [0..ne] do
        x := TargetsFSA(fsa,j,s);
        for t in x do
          if t>0 and not t in perm then
            ct := ct+1;
            perm[ct] := t;
          fi;
        od;
      od;
      i := i+1;
    od;

    perm := PermList(perm)^-1;
    PermuteStatesFSA(fsa,perm);
    AddSet(fsa.flags,"BFS");
end;

#############################################################################
##
#F  PSizeFSA(<fsa>,[<state-list>]) . . . . . number of accepted paths of an fsa
##
##  <fsa> should be a finite state automaton.
##  The number of accepted paths is calculated and returned.
##  WARNING: if there are epsilon transitions, then this is not necessarily
##  the same as the size of the accepted language.
##  If this is infinite, "infinity" is returned.
##  If <fsa. is not trim, then a diagnostic will be printed.
##  
##  If the optional argument [<state-list>] is present, then the number
##  of accepted strings starting at one of the states in <state-list> will
##  be returned instead, BUT ONLY IF THE TOTAL ACCEPTED LANGUAGE IS FINITE.
##  Public function.
PSizeFSA := function ( arg )
    local fsa, slist, ns, ne, indeg, st, olist, nacc, total, ct, i, j, s, t, x;
    fsa := arg[1];
    if Length(arg)=2 then
      slist := arg[2];
    else
      slist := fsa.initial;
    fi;
    if not IsInitializedFSA(fsa) then
       InitializeFSA(fsa);
    fi;
    if not IsTrimFSA(fsa) then
      Print("#The fsa is not trim. Call TrimFSA(fsa) to make it trim.\n");
      return "unknown";
    fi;

    ns := fsa.states.size;
    if ns=0 then
      return 0;
    fi;
    ne := fsa.alphabet.size;

 # We first count the number of edges going into each vertex.
    indeg := [];
    for s in [1..ns] do
      indeg[s] := 0;
    od;
    for s in [1..ns] do
      for i in [0..ne] do
        x := TargetsFSA(fsa,i,s);
        for t in x do
          if t > 0 then indeg[t] := indeg[t]+1; fi;
        od;
      od;
    od;

 # Now we seek to order the states so that if state s <= state t, then there
 # is no path from state t to state s. If this is not possible, then the
 # accepted language must be infinite.
 # The ordering will be in the list olist.

    st := 0;
    for s in fsa.initial do 
      if indeg[s]=0 then
        st := s;
      fi;
    od;
    if st = 0 then
      return infinity;
    fi;
    olist := [st];
    ct := 1;
    i := 1;
    while i<=ct do
      s := olist[i];
      for j in [0..ne] do
        x := TargetsFSA(fsa,j,s);
        for t in x do
          if t>0 then
            indeg[t] := indeg[t]-1;
            if indeg[t]=0 then
              ct := ct+1;
              olist[ct] := t;
            fi;
          fi;
        od;
      od;
      i := i+1;
    od;
    if ct <> ns then
      return infinity;
    fi;

 # We have built the list, so the accepted language is finite. Now we work
 # backwards through the list, calculating the number of accepted words
 # starting at that state.
 # We store the numbers in nacc.
 
  indeg := 0; #no longer needed.
  nacc := [];
  for i in Reversed([1..ns]) do
    s := olist[i];
    nacc[s] := 0;
    for j in [0..ne] do
      x := TargetsFSA(fsa,j,s);
      for t in x do
        if t>0 then nacc[s] := nacc[s]+nacc[t]; fi;
      od;
    od;
    if s in fsa.accepting then
      nacc[s] := nacc[s]+1;
    fi;
  od;

 # Finally we count the total number of accepted strings starting from
 # one of the states in slist.
  total := 0;
  for s in slist do
    total := total+nacc[s];
  od;
  return total;
end;

#############################################################################
##
#F  LSizeDFA(<fsa>,[<initial state>]) . . . . . size of language of an fsa
##
##  <fsa> should be a deterministic finite state automaton.
##  The size of the accepted language is calculated.
##  This should be quicker than PSizeFSA for deterministic automata.
##  If the language is infinite, "infinity" is returned.
##  If <fsa> is not trim, then a diagnostic will be printed.
##  
##  If the optional argument [<initial state>] is present, then the number
##  of accepted strings starting at the state in <initial state> will
##  be returned instead.
##  
##  Public function.
LSizeDFA := function ( arg )
    local fsa, slist, ns, ne, ttable, indeg, st, olist, nacc, total, ct,
          i, j, s, t, accstates;
    fsa := arg[1];
    if not IsInitializedFSA(fsa) then
       InitializeFSA(fsa);
    fi;
    if IsDeterministicFSA(fsa)=false  then
       Error("First argument is not a dfa.");
    fi;
    if not IsTrimFSA(fsa) then
      Print("#The fsa is not trim. Call TrimFSA(fsa) to make it trim.\n");
      return "unknown";
    fi;

    ns := fsa.states.size;
    if ns=0 then
      return 0;
    fi;
    ne := fsa.alphabet.size;

 # First make sure that the dense deterministic table is present.
    DenseDTableFSA(fsa);
    ttable := fsa.denseDTable;

    if Length(arg)=2 then
      slist := [arg[2]];
      #In this case, we restrict attention to states that can be reached from
      #initial states.
      accstates := ShallowCopy(slist);
      ct := 1;
      i := 1;
      while i<=ct do
        s := accstates[i];
        for j in [1..ne] do
          t := ttable[s][j];
          if t > 0 and Position(accstates,t) = fail then
            ct := ct+1;
            Add(accstates,t);
          fi;
        od;
        i := i+1;
      od;
    else
      slist := fsa.initial;
      accstates := [1..ns];
    fi;

 # We first count the number of edges going into each vertex.
    indeg := [];
    for s in accstates do
      indeg[s] := 0;
    od;
    for s in accstates do
      for i in [1..ne] do
        t := ttable[s][i];
        if t > 0 then indeg[t] := indeg[t]+1; fi;
      od;
    od;

 # Now we seek to order the states so that if state s <= state t, then there
 # is no path from state t to state s. If this is not possible, then the
 # accepted language must be infinite.
 # The ordering will be in the list olist.

    ns := Length(accstates);
    st := slist[1];
    if indeg[st] <> 0 then
      return infinity;
    fi;
    olist := [st];
    ct := 1;
    i := 1;
    while i<=ct do
      s := olist[i];
      for j in [1..ne] do
        t := ttable[s][j];
        if t>0 then
            indeg[t] := indeg[t]-1;
            if indeg[t]=0 then
              ct := ct+1;
              olist[ct] := t;
            fi;
        fi;
      od;
      i := i+1;
    od;
    if ct <> ns then
      return infinity;
    fi;

 # We have built the list, so the accepted language is finite. Now we work
 # backwards through the list, calculating the number of accepted words
 # starting at that state.
 # We store the numbers in nacc.
 
  indeg := 0; #no longer needed.
  nacc := [];
  for i in Reversed([1..ns]) do
    s := olist[i];
    nacc[s] := 0;
    for j in [1..ne] do
      t := ttable[s][j];
      if t>0 then nacc[s] := nacc[s]+nacc[t]; fi;
    od;
    if s in fsa.accepting then
      nacc[s] := nacc[s]+1;
    fi;
  od;

 # Finally we count the total number of accepted strings starting from
 # one of the states in slist.
  total := 0;
  for s in slist do
    total := total+nacc[s];
  od;
  return total;
end;

#############################################################################
##
#F  ListWordSR(<sr>,<w>) . . . . converts word in sr-generators to list
##
##  ListWordSR converts the word <w> in the generators of the
##  set-record <sr> to a list of integers.
##  It only works if <sr> has type "identifiers" or "product".
##  In the latter case, w should be a list of n words, where n is the
##  "arity" of the set-record.
##
##  Public function.
ListWordSR := function ( sr, w )
    local i, j, l, n, wl, gens, prod, nv, tup, id;
    if not IsRecord(sr) or not IsBound(sr.type) or
           (sr.type <> "identifiers" and sr.type <> "product") then
       Error("First argument must be a set-record of type \"identifiers\".");
    fi;
    prod := sr.type="product";
 #We deal with product type separately.
    if prod then
      nv := sr.arity;
      if not IsList(w) or Length(w)<>nv then
         Error("Second argument must be a list of length sr.arity.");
      fi;
      l := 0;
      for i in w do
        if not IsWord(i) then
           Error("An entry in second argument is not a word.");
        fi;
        if Length(i)>l then
          l := Length(i);
        fi;
      od;
      wl := [];
      gens := sr.names;
      for i in [1..l] do
        tup := [];
        for j in [1..nv] do
          if i > Length(w[j]) then
            id := sr.padding;
          else
            id := Subword(w[j],i,i);
          fi;
          tup[j] := id;
        od;
        n := Position(gens,tup);
        if n=fail then
          Error("Invalid tuple in word.");
        fi;
        Add(wl,n);
      od;
    else
      if not IsWord(w) then
         Error("Second argument is not a word.");
      fi;
      l := Length(w);
      wl := [];
      gens := sr.names;
      for i in [1..l] do
        n := Position(gens,Subword(w,i,i));
        if n=fail then
          Error("Invalid generator in word.");
        fi;
        Add(wl,n);
      od;
    fi;

    return wl;
end;

#############################################################################
##
#F  WordListSR(<sr>,<wl>) . . . . converts list of sr-generators to word
##
##  WordListSR converts the list of positive integers <wl> to a word
##  in the generators of the rewriting system <sr>. Each integer in the
##  list must be a valid generator number.
##  This is the inverse function to ListWordSR.
##  However, it works when sr has type "identifiers", "strings" or
##  "products".
##
##  Public function.
WordListSR := function ( sr, wl )
    local i, j, l, w, gens, ng, nv, tup, IdWord;
    if sr.type="identifiers"   and IsAssocWordWithOne(sr.names[1]) then
      IdWord := sr.names[1]^0;
    elif sr.type="words"   and IsAssocWordWithOne(sr.alphabet[1]) then
      IdWord := sr.alphabet[1]^0;
    else IdWord:=false;
    fi;
    if not IsRecord(sr) or not IsBound(sr.type) or (sr.type <> "identifiers" 
        and sr.type <> "words"  and sr.type <> "strings" and
        sr.type <> "product") then
       Error("First argument must be a set-record of appropriate type.");
    fi;
    if not IsList(wl) then
       Error("Second argument is not a list.");
    fi;
    l := Length(wl);
    gens := sr.names;
    ng := sr.size;
    if l=0 then
      if sr.type="identifiers" or sr.type="words" then
        return IdWord;
      elif sr.type="strings" then
        return "";
      else
        nv := sr.arity;
        return List([1..nv],i->IdWord);
      fi;
    else
      if not wl[1] in [1..ng] then
        Error("List element is not a valid generator number.");
      fi;
      w := ShallowCopy(gens[wl[1]]);
    fi;
    for i in [2..l] do
      if not wl[i] in [1..ng] then
        Error("List element is not a valid generator number.");
      fi;
      if sr.type="identifiers" or sr.type="words" then
        w := w*gens[wl[i]];
      elif sr.type="strings" then
        w := Concatenation(w,gens[wl[i]]);
      else
        tup := gens[wl[i]];
        nv := sr.arity;
        for j in [1..nv] do
          w[j] := w[j]*tup[j];
        od;
      fi;
    od;

    return w;
end;

#############################################################################
##
#F  LEnumerateDFA(<fsa>, <min>, <max>, [<is>] ) . . enumerate language of dfa
##
##  <fsa> should be a deterministic finite state automaton.
##  All words in the language accepted by <fsa> having length l
##  satisfying <min> <= l <= <max> will be calculated and output in a
##  list. These will be in lexacographical order.
##  To get shortlex order, call SortLEnumerateDFA, which merely calls this
##  function repeatedly.
##  If the optional argument <is> is present, the initial state will be
##  taken to be is.
##
##  Public function.
LEnumerateDFA := function ( arg )
    local fsa, min, max, is,  words, convert, ttable, sr, ne, as, cword,
          cstatelist, clength, done, backtrack, cstate, fe, i;

    if Length(arg) < 3 then
       Error("LEnumerateDFA must have at least three arguments");
    fi;
    fsa:=arg[1]; min:=arg[2]; max:=arg[3];
    if not IsInitializedFSA(fsa) then
       InitializeFSA(fsa);
    fi;
    if  IsDeterministicFSA(fsa)=false  then
       Error("First argument is not a dfa.");
    fi;
    if not IsInt(min) or not IsInt(max) or min<0 or min>max then
      Error("2nd and 3rd arguments must be nonneg. integers with 2nd <= 3rd.");
    fi;
    if fsa.initial=[] and Length(arg)=3 then
      return [];
    fi;
    if Length(arg)>=4 then is:=arg[4]; else is:=fsa.initial[1]; fi;

 # The enumeration process itself will use lists of integers. These will be
 # converted to words if necessary and collected in the lists "words" for
 # output.

    sr := fsa.alphabet;
    ne := sr.size;
    convert := sr.type="identifiers" or sr.type="strings" or sr.type="words"
                or sr.type="product";

  # Make sure that the dense deterministic table is present.
    DenseDTableFSA(fsa);
    ttable := fsa.denseDTable;

    words := [];
  # cword will be the current word in the search (as a list of integers),
  # clength its current length, and cstatelist the list of states of fsa
  # arising when scanning the word.
    cword := [];
    cstatelist := [is];
    clength := 0;

    as := fsa.accepting;
  # Now the backtrack search begins
    done := false;
    while not done do
  # first check if we want the current word.
      if clength>=min and cstatelist[clength+1] in as then
         if convert then
           Add(words,WordListSR(sr,cword));
         else
           Add(words,ShallowCopy(cword));
         fi;
      fi;

  # now proceed to next word in search.
      fe := 1;
      backtrack:=true;
      while backtrack and not done  do
        if clength < max then
          cstate := cstatelist[clength+1];
          i := fe;
          while backtrack and i<= ne do
            if ttable[cstate][i] > 0 then
           # found next node
              clength := clength+1;
              cword[clength] := i;
              cstatelist[clength+1] := ttable[cstate][i];
              backtrack := false;
            fi;
            i := i+1;
          od;
        fi;
        if backtrack then
           if clength=0 then
             done := true;
           else
             fe := cword[clength]+1;
             Unbind(cword[clength]);
             clength := clength-1;
           fi;
        fi;
      od;
    od;

    return words;
end;

#############################################################################
##
#F  SortLEnumerateDFA(<fsa>, <min>, <max> [,<is>])
##  . . . . . . . . . . . . . . . . . . . enumerate language of dfa and sort
##
##  This function merely calls LEnumerateFSA repeatedly to get the shortlex
##  order of output.
##
##  If the optional argument <is> is present, the initial state will be
##  taken to be is.
##
##  Public function.
SortLEnumerateDFA := function ( arg )
    local fsa, min, max, is,  words, i;

    if Length(arg) < 3 then
       Error("SortLEnumerateDFA must have at least three arguments");
    fi;
    fsa:=arg[1]; min:=arg[2]; max:=arg[3];

    words := [];
    for i in [min..max] do
      if Length(arg)=3 then
        words := Concatenation(words,LEnumerateDFA(fsa,i,i));
      else
        is := arg[4];
        words := Concatenation(words,LEnumerateDFA(fsa,i,i,is));
      fi;
    od;

    return words;
end;

#############################################################################
##
#F  SizeLEnumerateDFA(<fsa>, <min>, <max> [,<is>])
##  . . . . . . . . . . . . . . . . . . . size of enumerated language of dfa
##
##  <fsa> should be a deterministic finite state automaton.
##  This is like LEnumerateFSA, but only the number of accepted words is
##  output.
##
##  If the optional argument <is> is present, the initial state will be
##  taken to be is.
##
##  Public function.
SizeLEnumerateDFA := function ( arg )
    local fsa, min, max, is, nwords, ttable, sr, ne, as, cword, cstatelist,
          clength, done, backtrack, cstate, fe, i;

    if Length(arg) < 3 then
       Error("LEnumerateDFA must have at least three arguments");
    fi;
    fsa:=arg[1]; min:=arg[2]; max:=arg[3];

    if not IsInitializedFSA(fsa) then
       InitializeFSA(fsa);
    fi;
    if IsDeterministicFSA(fsa)=false  then
       Error("First argument is not a dfa.");
    fi;
    if not IsInt(min) or not IsInt(max) or min<0 or min>max then
      Error("2nd and 3rd arguments must be nonneg. integers with 2nd <= 3rd.");
    fi;
    if fsa.initial=[] and Length(arg)=3 then
      return 0;
    fi;

    if Length(arg)>=4 then is:=arg[4]; else is:=fsa.initial[1]; fi;

  # The enumeration process itself will use lists of integers.

    sr := fsa.alphabet;
    ne := sr.size;

  # Make sure that the dense deterministic table is present.
    DenseDTableFSA(fsa);
    ttable := fsa.denseDTable;

    nwords := 0;
  # cword will be the current word in the search (as a list of integers),
  # clength its current length, and cstatelist the list of states of fsa
  # arising when scanning the word.
    cword := [];
    cstatelist := [is];
    clength := 0;

    as := fsa.accepting;
  # Now the backtrack search begins
    done := false;
    while not done do
  # first check if we want the current word.
      if clength>=min and cstatelist[clength+1] in as then
        nwords := nwords+1;
      fi;

  # now proceed to next word in search.
      fe := 1;
      backtrack:=true;
      while backtrack and not done  do
        if clength < max then
          cstate := cstatelist[clength+1];
          i := fe;
          while backtrack and i<= ne do
            if ttable[cstate][i] > 0 then
           # found next node
              clength := clength+1;
              cword[clength] := i;
              cstatelist[clength+1] := ttable[cstate][i];
              backtrack := false;
            fi;
            i := i+1;
          od;
        fi;
        if backtrack then
           if clength=0 then
             done := true;
           else
             fe := cword[clength]+1;
             Unbind(cword[clength]);
             clength := clength-1;
           fi;
        fi;
      od;
    od;

    return nwords;
end;

#############################################################################
##
#F  SubstitutedListFSA(<l>,<pos1>,<pos2>,<ss>)
##                        . . . . . . . . . substitute a new list in a list
##
##  The part of the list <l> form position <pos1> to <pos2> is substituted by
##  the list <ss>. This is easy if lengths are equal. Otherwise not so.
##
##  Private function.
SubstitutedListFSA := function(l,pos1,pos2,ss)
    local len, oldlen, newlen, diff, i;
    len := Length(l);
    oldlen := pos2-pos1+1; newlen := Length(ss);
    if oldlen=newlen then
      l{[pos1..pos2]} := ss;
    elif newlen<oldlen then
      diff := oldlen-newlen;
      for i in [pos2+1..len] do l[i-diff]:=l[i]; od;
      for i in [len-diff+1..len] do Unbind(l[i]); od;
      l{[pos1..pos2-diff]} := ss;
    elif newlen>oldlen then
      diff := newlen-oldlen;
      for i in Reversed([pos2+1..len]) do l[i+diff] := l[i]; od;
      l{[pos1..pos2+diff]} := ss;
    fi;
end;

#############################################################################
##
#F  DeterminizeFSA(<fsa>) . . . call external program to determinize fsa <fsa>
##
## Determinized FSA is returned.
## Public function.
DeterminizeFSA := function(fsa)
  local callstring, filename, alph;
    
    if not IsInitializedFSA(fsa) then
       InitializeFSA(fsa);
    fi;
    if  IsDeterministicFSA(fsa)  then
       return  fsa;
    fi;
    ## We replace the alphabet by simple-type alphabet for the
    ## I/O phase.
    alph := fsa.alphabet;
    fsa.alphabet := rec(type:="simple",size:=alph.size);
    InitializeSR(fsa.alphabet);
    filename := Concatenation(_KBTmpFileName,".fsafordet");
    WriteFSA(fsa,"_FSA",filename,";");
    callstring := Filename(_KBExtDir,"nfadeterminize");
    if InfoLevel(InfoFSA)=0 then
       callstring := Concatenation(callstring," -silent ");
    elif InfoLevel(InfoFSA)>1 then
       callstring := Concatenation(callstring," -v ");
    fi;
    callstring := Concatenation(callstring," ",filename);
    Info(InfoFSA,1,"Calling fsa determinization program.\n");
    Info(InfoFSA,3,"  ",callstring);
    Exec(callstring);
    Info(InfoFSA,1,"External fsa determinization program complete.\n");
    if not READ(Concatenation(_KBTmpFileName,".fsafordet.determinize")) then
       Error("Could not open determinized fsa file");
    fi;
    Exec(Concatenation("/bin/rm -f ",_KBTmpFileName,".fsafordet*"));
    InitializeFSA(_FSA_determinize);
    fsa.alphabet := alph;
    _FSA_min.alphabet := alph;
    return _FSA_determinize;
end;

#############################################################################
##
#F  MinimizeFSA(<fsa>) . . . call external program to minimize fsa <fsa>
##
## Minimized FSA is returned.
## Public function.
MinimizeFSA := function(fsa)
  local callstring, filename, alph;
    
    if not IsInitializedFSA(fsa) then
       InitializeFSA(fsa);
    fi;
    if  IsDeterministicFSA(fsa)=false  then
       Error("First argument is not a dfa.");
    fi;
    ## We replace the alphabet by simple-type alphabet for the
    ## I/O phase.
    alph := fsa.alphabet;
    fsa.alphabet := rec(type:="simple",size:=alph.size);
    InitializeSR(fsa.alphabet);
    filename := Concatenation(_KBTmpFileName,".fsaformin");
    WriteFSA(fsa,"_FSA",filename,";");
    callstring := Filename(_KBExtDir,"fsamin");
    if InfoLevel(InfoFSA)=0 then
       callstring := Concatenation(callstring," -silent ");
    elif InfoLevel(InfoFSA)>1 then
       callstring := Concatenation(callstring," -v ");
    fi;
    callstring := Concatenation(callstring," ",filename);
    Info(InfoFSA,1,"Calling fsa minimization program.\n");
    Info(InfoFSA,3,"  ",callstring);
    Exec(callstring);
    Info(InfoFSA,1,"External fsa minimization program complete.\n");
    if not READ(Concatenation(_KBTmpFileName,".fsaformin.min")) then
       Error("Could not open minimized fsa file");
    fi;
    Exec(Concatenation("/bin/rm -f ",_KBTmpFileName,".fsaformin*"));
    InitializeFSA(_FSA_min);
    fsa.alphabet := alph;
    _FSA_min.alphabet := alph;
    return _FSA_min;
end;

#############################################################################
##
#F  NotFSA(<fsa>) . . . call external program to negate fsa <fsa>
##
## An FSA is returned in which a word is accepted iff it is not
## accepted by <fsa>.
## Public function.
NotFSA := function(fsa)
  local callstring, filename, alph;
    
    if not IsInitializedFSA(fsa) then
       InitializeFSA(fsa);
    fi;
    if  IsDeterministicFSA(fsa)=false  then
       Error("First argument is not a dfa.");
    fi;
    ## We replace the alphabet by simple-type alphabet for the
    ## I/O phase.
    alph := fsa.alphabet;
    fsa.alphabet := rec(type:="simple",size:=alph.size);
    InitializeSR(fsa.alphabet);
    filename := Concatenation(_KBTmpFileName,".fsafornot");
    WriteFSA(fsa,"_FSA",filename,";");
    callstring := Filename(_KBExtDir,"fsanot");
    if InfoLevel(InfoFSA)=0 then
       callstring := Concatenation(callstring," -silent ");
    elif InfoLevel(InfoFSA)>1 then
       callstring := Concatenation(callstring," -v ");
    fi;
    callstring := Concatenation(callstring," ",filename);
    Info(InfoFSA,1,"Calling fsa `not' program.\n");
    Info(InfoFSA,3,"  ",callstring);
    Exec(callstring);
    Info(InfoFSA,1,"External fsa `not' program complete.\n");
    if not READ(Concatenation(_KBTmpFileName,".fsafornot.not")) then
       Error("Could not open `not' fsa file");
    fi;
    Exec(Concatenation("/bin/rm -f ",_KBTmpFileName,".fsafornot*"));
    InitializeFSA(_FSA_not);
    fsa.alphabet := alph;
    _FSA_not.alphabet := alph;
    return _FSA_not;
end;

#############################################################################
##
#F  StarFSA(<fsa>) . . . call external program to star fsa <fsa>
##
## An FSA is returned in which a word is accepted iff it is a
## concatenation of 0 or more words accepted by <fsa>.
## Public function.
StarFSA := function(fsa)
  local callstring, filename, alph;
    
    if not IsInitializedFSA(fsa) then
       InitializeFSA(fsa);
    fi;
    if  IsDeterministicFSA(fsa)=false  then
       Error("First argument is not a dfa.");
    fi;
    ## We replace the alphabet by simple-type alphabet for the
    ## I/O phase.
    alph := fsa.alphabet;
    fsa.alphabet := rec(type:="simple",size:=alph.size);
    InitializeSR(fsa.alphabet);
    filename := Concatenation(_KBTmpFileName,".fsaforstar");
    WriteFSA(fsa,"_FSA",filename,";");
    callstring := Filename(_KBExtDir,"fsastar");
    if InfoLevel(InfoFSA)=0 then
       callstring := Concatenation(callstring," -silent ");
    elif InfoLevel(InfoFSA)>1 then
       callstring := Concatenation(callstring," -v ");
    fi;
    callstring := Concatenation(callstring," ",filename);
    Info(InfoFSA,1,"Calling fsa `star' program.\n");
    Info(InfoFSA,3,"  ",callstring);
    Exec(callstring);
    Info(InfoFSA,1,"External fsa `star' program complete.\n");
    if not READ(Concatenation(_KBTmpFileName,".fsaforstar.star")) then
       Error("Could not open `star' fsa file");
    fi;
    Exec(Concatenation("/bin/rm -f ",_KBTmpFileName,".fsaforstar*"));
    InitializeFSA(_FSA_star);
    fsa.alphabet := alph;
    _FSA_star.alphabet := alph;
    return _FSA_star;
end;

#############################################################################
##
#F  ReverseFSA(<fsa>,[<subsets>]) . call external program to reverse fsa <fsa>
##
## An FSA is returned in which a word is accepted iff it is the reverses
## of a word accepted by <fsa>.
## If the optional 'subsets' argument is true, then the states of the
## returned fsa are given labels specifying the subsets of the original
## state-set to which they correspond.
## Public function.
ReverseFSA := function(arg)
  local callstring, filename, alph, fsa, subsets;
    
    fsa := arg[1];
    if not IsInitializedFSA(fsa) then
       InitializeFSA(fsa);
    fi;
    if  IsDeterministicFSA(fsa)=false  then
       Error("First argument is not a dfa.");
    fi;
    subsets := false;
    if Length(arg)>=2 and arg[2]=true then
      subsets:=true;
    fi;
    ## We replace the alphabet by simple-type alphabet for the
    ## I/O phase.
    alph := fsa.alphabet;
    fsa.alphabet := rec(type:="simple",size:=alph.size);
    InitializeSR(fsa.alphabet);
    filename := Concatenation(_KBTmpFileName,".fsaforreverse");
    WriteFSA(fsa,"_FSA",filename,";");
    callstring := Filename(_KBExtDir,"fsareverse");
    if InfoLevel(InfoFSA)=0 then
       callstring := Concatenation(callstring," -silent ");
    elif InfoLevel(InfoFSA)>1 then
       callstring := Concatenation(callstring," -v ");
    fi;
    if subsets then
      callstring := Concatenation(callstring," -s ");
    fi;
    callstring := Concatenation(callstring," ",filename);
    Info(InfoFSA,1,"Calling fsa `reverse' program.\n");
    Info(InfoFSA,3,"  ",callstring);
    Exec(callstring);
    Info(InfoFSA,1,"External fsa `reverse' program complete.\n");
    if not READ(Concatenation(_KBTmpFileName,".fsaforreverse.reverse")) then
       Error("Could not open `reverse' fsa file");
    fi;
    Exec(Concatenation("/bin/rm -f ",_KBTmpFileName,".fsaforreverse*"));
    InitializeFSA(_FSA_reverse);
    fsa.alphabet := alph;
    _FSA_reverse.alphabet := alph;
    return _FSA_reverse;
end;

#############################################################################
##
#F  ExistsFSA(<fsa>) . . . call external program to 'exist' fsa <fsa>
##
## Here <fsa> must be a 2-variable FSA.
## An FSA is returned in which a word  w1 is accepted iff 
## (w1,w2) is accepted by <fsa> for some word w2.
## Public function.
ExistsFSA := function(fsa)
  local callstring, filename, alph;
    
    if not IsInitializedFSA(fsa) then
       InitializeFSA(fsa);
    fi;
    if  IsDeterministicFSA(fsa)=false  then
       Error("First argument is not a dfa.");
    fi;
    if fsa.alphabet.type <> "product" or fsa.alphabet.arity <> 2 then
       Error("Alphabet of argument is not 2-variable.");
    fi;
    ## We replace the base alphabet by simple-type alphabet for the
    ## I/O phase.
    alph := fsa.alphabet.base;
    fsa.alphabet.base := rec(type:="simple",size:=alph.size);
    InitializeSR(fsa.alphabet.base);
    filename := Concatenation(_KBTmpFileName,".fsaforexists");
    WriteFSA(fsa,"_FSA",filename,";");
    callstring := Filename(_KBExtDir,"fsaexists");
    if InfoLevel(InfoFSA)=0 then
       callstring := Concatenation(callstring," -silent ");
    elif InfoLevel(InfoFSA)>1 then
       callstring := Concatenation(callstring," -v ");
    fi;
    callstring := Concatenation(callstring," ",filename);
    Info(InfoFSA,1,"Calling fsa `exists' program.\n");
    Info(InfoFSA,3,"  ",callstring);
    Exec(callstring);
    Info(InfoFSA,1,"External fsa `exists' program complete.\n");
    if not READ(Concatenation(_KBTmpFileName,".fsaforexists.exists")) then
       Error("Could not open `exists' fsa file");
    fi;
    Exec(Concatenation("/bin/rm -f ",_KBTmpFileName,".fsaforexists*"));
    InitializeFSA(_FSA_exists);
    fsa.alphabet.base := alph;
    _FSA_exists.alphabet := alph;
    return _FSA_exists;
end;

#############################################################################
##
#F  SwapCoordsFSA(<fsa>)
##     . . . call external program to swap coordinates of fsa <fsa>
##
## Here <fsa> must be a 2-variable FSA.
## An 2-variable FSA is returned in which (w1,w2) is accepted iff 
## (w2,w1) is accepted by <fsa>.
## Public function.
SwapCoordsFSA := function(fsa)
  local callstring, filename, alph;
    
    if not IsInitializedFSA(fsa) then
       InitializeFSA(fsa);
    fi;
    if  IsDeterministicFSA(fsa)=false  then
       Error("First argument is not a dfa.");
    fi;
    if fsa.alphabet.type <> "product" or fsa.alphabet.arity <> 2 then
       Error("Alphabet of argument is not 2-variable.");
    fi;
    ## We replace the base alphabet by simple-type alphabet for the
    ## I/O phase.
    alph := fsa.alphabet.base;
    fsa.alphabet.base := rec(type:="simple",size:=alph.size);
    InitializeSR(fsa.alphabet.base);
    filename := Concatenation(_KBTmpFileName,".fsaforswap_coords");
    WriteFSA(fsa,"_FSA",filename,";");
    callstring := Filename(_KBExtDir,"fsaswapcoords");
    if InfoLevel(InfoFSA)=0 then
       callstring := Concatenation(callstring," -silent ");
    elif InfoLevel(InfoFSA)>1 then
       callstring := Concatenation(callstring," -v ");
    fi;
    callstring := Concatenation(callstring," ",filename," ",filename,".o");
 #Print(callstring,"\n");
    Info(InfoFSA,1,"Calling fsa `swap_coords' program.\n");
    Info(InfoFSA,3,"  ",callstring);
    Exec(callstring);
    Info(InfoFSA,1,"External fsa `swap_coords' program complete.\n");
    if not
       READ(Concatenation(_KBTmpFileName,".fsaforswap_coords.o"))
          then Error("Could not open `swapcoords' fsa file");
    fi;
    Exec(Concatenation("/bin/rm -f ",_KBTmpFileName,".fsaforswap_coords*"));
    InitializeFSA(_FSA_swap_coords);
    fsa.alphabet.base := alph;
    _FSA_swap_coords.alphabet.base := alph;
    return _FSA_swap_coords;
end;

#############################################################################
##
#F  AndFSA(<fsa1>, <fsa2>) . . call external program to and fsa's <fsa1>,<fsa2>
##
## An FSA is returned in which a word is accepted iff it is a
## accepted by both of the fsa's <fsa1> and <fsa2>.
## Public function.
AndFSA := function(fsa1, fsa2)
  local callstring, filename1, filename2, filename3, alph;
    
    if not IsInitializedFSA(fsa1) then
       InitializeFSA(fsa1);
    fi;
    if not IsInitializedFSA(fsa2) then
       InitializeFSA(fsa2);
    fi;
    if  IsDeterministicFSA(fsa1)=false or IsDeterministicFSA(fsa2)=false then
       Error("One of the arguments is not a dfa.");
    fi;
    ## We replace the alphabet by simple-type alphabet for the
    ## I/O phase.
    alph := fsa1.alphabet;
    if alph <> fsa2.alphabet then
      Error("Arguments have different alphabets.");
    fi;
    fsa1.alphabet := rec(type:="simple",size:=alph.size);
    InitializeSR(fsa1.alphabet);
    fsa2.alphabet := rec(type:="simple",size:=alph.size);
    InitializeSR(fsa2.alphabet);
    filename1 := Concatenation(_KBTmpFileName,".fsaforand1");
    filename2 := Concatenation(_KBTmpFileName,".fsaforand2");
    filename3 := Concatenation(_KBTmpFileName,".fsaforand3");
    WriteFSA(fsa1,"_FSA",filename1,";");
    WriteFSA(fsa2,"_FSA",filename2,";");
    callstring := Filename(_KBExtDir,"fsaand");
    if InfoLevel(InfoFSA)=0 then
       callstring := Concatenation(callstring," -silent ");
    elif InfoLevel(InfoFSA)>1 then
       callstring := Concatenation(callstring," -v ");
    fi;
    callstring := Concatenation(callstring," ",
                     filename1," ",filename2," ",filename3);
    Info(InfoFSA,1,"Calling fsa `and' program.\n");
    Info(InfoFSA,3,"  ",callstring);
    Exec(callstring);
    Info(InfoFSA,1,"External fsa `and' program complete.\n");
    if not READ(filename3) then
       Error("Could not open `and' fsa file");
    fi;
    Exec(Concatenation("/bin/rm -f ",_KBTmpFileName,".fsaforand*"));
    InitializeFSA(_FSA_and);
    fsa1.alphabet := alph;
    fsa2.alphabet := alph;
    _FSA_and.alphabet := alph;
    return _FSA_and;
end;

#############################################################################
##
#F  OrFSA(<fsa1>, <fsa2>) . . . call external program to or fsa's <fsa1>,<fsa2>
##
## An FSA is returned in which a word is accepted iff it is a
## accepted by either of the fsa's <fsa1> or <fsa2>.
## Public function.
OrFSA := function(fsa1, fsa2)
  local callstring, filename1, filename2, filename3, alph;
    
    if not IsInitializedFSA(fsa1) then
       InitializeFSA(fsa1);
    fi;
    if not IsInitializedFSA(fsa2) then
       InitializeFSA(fsa2);
    fi;
    if  IsDeterministicFSA(fsa1)=false or IsDeterministicFSA(fsa2)=false then
       Error("One of the arguments is not a dfa.");
    fi;
    ## We replace the alphabet by simple-type alphabet for the
    ## I/O phase.
    alph := fsa1.alphabet;
    if alph <> fsa2.alphabet then
      Error("Arguments have different alphabets.");
    fi;
    fsa1.alphabet := rec(type:="simple",size:=alph.size);
    InitializeSR(fsa1.alphabet);
    fsa2.alphabet := rec(type:="simple",size:=alph.size);
    InitializeSR(fsa2.alphabet);
    filename1 := Concatenation(_KBTmpFileName,".fsaforor1");
    filename2 := Concatenation(_KBTmpFileName,".fsaforor2");
    filename3 := Concatenation(_KBTmpFileName,".fsaforor3");
    WriteFSA(fsa1,"_FSA",filename1,";");
    WriteFSA(fsa2,"_FSA",filename2,";");
    callstring := Filename(_KBExtDir,"fsaor");
    if InfoLevel(InfoFSA)=0 then
       callstring := Concatenation(callstring," -silent ");
    elif InfoLevel(InfoFSA)>1 then
       callstring := Concatenation(callstring," -v ");
    fi;
    callstring := Concatenation(callstring," ",
                     filename1," ",filename2," ",filename3);
    Info(InfoFSA,1,"Calling fsa `or' program.\n");
    Info(InfoFSA,3,"  ",callstring);
    Exec(callstring);
    Info(InfoFSA,1,"External fsa `or' program complete.\n");
    if not READ(filename3) then
       Error("Could not open `or' fsa file");
    fi;
    Exec(Concatenation("/bin/rm -f ",_KBTmpFileName,".fsaforor*"));
    InitializeFSA(_FSA_or);
    fsa1.alphabet := alph;
    fsa2.alphabet := alph;
    _FSA_or.alphabet := alph;
    return _FSA_or;
end;

#############################################################################
##
#F  ConcatFSA(<fsa1>, <fsa2>)
##            . . . call external program to concat fsa's <fsa1>,<fsa2>
##
## An FSA is returned in which a word is accepted iff it is the concatenation
## of words accepted by the fsa's <fsa1> and <fsa2>.
## Public function.
ConcatFSA := function(fsa1, fsa2)
  local callstring, filename1, filename2, filename3, alph;
    
    if not IsInitializedFSA(fsa1) then
       InitializeFSA(fsa1);
    fi;
    if not IsInitializedFSA(fsa2) then
       InitializeFSA(fsa2);
    fi;
    if  IsDeterministicFSA(fsa1)=false or IsDeterministicFSA(fsa2)=false then
       Error("One of the arguments is not a dfa.");
    fi;
    ## We replace the alphabet by simple-type alphabet for the
    ## I/O phase.
    alph := fsa1.alphabet;
    if alph <> fsa2.alphabet then
      Error("Arguments have different alphabets.");
    fi;
    fsa1.alphabet := rec(type:="simple",size:=alph.size);
    InitializeSR(fsa1.alphabet);
    fsa2.alphabet := rec(type:="simple",size:=alph.size);
    InitializeSR(fsa2.alphabet);
    filename1 := Concatenation(_KBTmpFileName,".fsaforconcat1");
    filename2 := Concatenation(_KBTmpFileName,".fsaforconcat2");
    filename3 := Concatenation(_KBTmpFileName,".fsaforconcat3");
    WriteFSA(fsa1,"_FSA",filename1,";");
    WriteFSA(fsa2,"_FSA",filename2,";");
    callstring := Filename(_KBExtDir,"fsaconcat");
    if InfoLevel(InfoFSA)=0 then
       callstring := Concatenation(callstring," -silent ");
    elif InfoLevel(InfoFSA)>1 then
       callstring := Concatenation(callstring," -v ");
    fi;
    callstring := Concatenation(callstring," ",
                     filename1," ",filename2," ",filename3);
    Info(InfoFSA,1,"Calling fsa `concat' program.\n");
    Info(InfoFSA,3,"  ",callstring);
    Exec(callstring);
    Info(InfoFSA,1,"External fsa `concat' program complete.\n");
    if not READ(filename3) then
       Error("Could not open `concat' fsa file");
    fi;
    Exec(Concatenation("/bin/rm -f ",_KBTmpFileName,".fsaforconcat*"));
    InitializeFSA(_FSA_concat);
    fsa1.alphabet := alph;
    fsa2.alphabet := alph;
    _FSA_concat.alphabet := alph;
    return _FSA_concat;
end;

#############################################################################
##
#F  LanguagesEqualFSA(<fsa1>, <fsa2>)
##            . . . decide whether  <fsa1>, <fsa2> havbe the same language
##
## <fsa1> and <fsa2> must have the same alphabet, or an error results.
## true or false is returned according to whether <fsa1> and <fsa2> have
## the same language.
## 
## Public function.
LanguagesEqualFSA := function(fsa1, fsa2)
    local mfsa1, mfsa2;
    if not IsInitializedFSA(fsa1) then
       InitializeFSA(fsa1);
    fi;
    if not IsInitializedFSA(fsa2) then
       InitializeFSA(fsa2);
    fi;
    if  IsDeterministicFSA(fsa1)=false or IsDeterministicFSA(fsa2)=false then
       Error("One of the arguments is not a dfa.");
    fi;
    if fsa1.alphabet <> fsa2.alphabet then
      Error("Parameters do not have the same alphabet.");
    fi;

    mfsa1 := MinimizeFSA(fsa1);
    mfsa2 := MinimizeFSA(fsa2);
    BFSFSA(mfsa1);
    BFSFSA(mfsa2);
    return DenseDTableFSA(mfsa1) = DenseDTableFSA(mfsa2);
end;

#############################################################################
##
#F  GrowthFSA(<fsa>) . . . call external program to find growth function
##                         of <fsa>
##
## A rational function is returned. The coefficient of x^n in this function is
## the number of words of length n in the language of <fsa>,
## 
## Public function.
GrowthFSA := function(fsa)
  local callstring, filename, alph, gf;
    
    if not IsInitializedFSA(fsa) then
       InitializeFSA(fsa);
    fi;
    if  IsDeterministicFSA(fsa)=false  then
       Error("First argument is not a dfa.");
    fi;
    ## We replace the alphabet by simple-type alphabet for the
    ## I/O phase.
    alph := fsa.alphabet;
    fsa.alphabet := rec(type:="simple",size:=alph.size);
    InitializeSR(fsa.alphabet);
    filename := Concatenation(_KBTmpFileName,".fsaforgrowth");
    WriteFSA(fsa,"_FSA",filename,";");
    callstring := Filename(_KBExtDir,"fsagrowth");
    if InfoLevel(InfoFSA)>1 then
       callstring := Concatenation(callstring," -v ");
    fi;
    callstring := Concatenation(callstring," ",filename);
    Info(InfoFSA,1,"Calling fsa `growth' program.\n");
    Info(InfoFSA,3,"  ",callstring);
    Exec(callstring);
    Info(InfoFSA,1,"External fsa `growth' program complete.\n");
    gf := ReadAsFunction(Concatenation(_KBTmpFileName,".fsaforgrowth.growth"));
    Exec(Concatenation("/bin/rm -f ",_KBTmpFileName,".fsaforgrowth*"));
    fsa.alphabet := alph;
    if gf=fail then
      return fail;
    fi;
    return gf();
end;

[zur Elbe Produktseite wechseln0.94QuellennavigatorsAnalyse erneut starten2026-05-01]

                                                                                                                                                                                                                                                                                                                                                                                                     


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