SSL fsa4.g
Sprache: unbekannt
|
|
Haftungsausschluß.g KontaktUnknown {[0] [0] [0]}diese Dinge liegen außhalb unserer Verantwortung #############################################################################
##
#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");
--> --------------------
--> maximum size reached
--> --------------------
[ Verzeichnis aufwärts0.132unsichere Verbindung
]
|
2026-03-28
|