1############################################################################# 2## 3#A fsa4.g GAP library Derek Holt 4## 5## 1.3.00. created this file from GAP3 version fsa.g and started adapting 6## it to GAP4. 7## 8## 24/9/98 - corrected dreadful bug in DeleteStateFSA 9## This file contains those functions that deal with finite state automata. 10## 11## 1.5.96. - code added to deal with the new "list of words" and 12## "list of integers" set-record types. 13## 14DeclareInfoClass("InfoFSA"); 15############################################################################# 16#V _RWST_ external variable - temporary name of list of strings 17#V _FSA external variable - finite state automaton 18#V _FSA_determinize external variable -determinized finite state automaton 19#V _FSA_min external variable - minimized finite state automaton 20#V _FSA_not external variable - `not' finite state automaton 21#V _FSA_star external variable - `star' finite state automaton 22#V _FSA_reverse external variable - `reverse' finite state automaton 23#V _FSA_exists external variable - `exists' finite state automaton 24#V _FSA_swap_coords external variable - `swap' finite state automaton 25#V _FSA_and external variable - `and' finite state automaton 26#V _FSA_or external variable - `or' finite state automaton 27#V _FSA_concat external variable - `concat' finite state automaton 28_RWST_ := []; 29_FSA := rec(); 30_FSA_determinize := rec(); 31_FSA_min := rec(); 32_FSA_not := rec(); 33_FSA_star := rec(); 34_FSA_reverse := rec(); 35_FSA_exists := rec(); 36_FSA_swap_coords := rec(); 37_FSA_and := rec(); 38_FSA_or := rec(); 39_FSA_concat := rec(); 40_KBExtDir := DirectoriesPackagePrograms("kbmag"); 41_KBTmpFileName := TmpName(); 42 43############################################################################# 44## 45#F IsFSA(<x>) . . . . . . . test whether x is an fsa 46## 47## Public function. 48IsFSA := function ( x ) 49 return IsRecord(x) and IsBound(x.isFSA) and x.isFSA; 50end; 51 52############################################################################# 53## 54#F IsInitializedFSA(<x>) . . . . . . . test whether x is an intialized fsa 55## 56## Public function. 57IsInitializedFSA := function ( x ) 58 return IsRecord(x) and IsBound(x.isInitializedFSA) and x.isInitializedFSA; 59end; 60 61############################################################################# 62## 63#F ExpandFieldSR(<list>) . . expand a sparsely stored name list 64## 65## ExpandFieldSR takes a sparse <list> as argument and 66## returns the corresponding dense list (which may have gaps). 67## e.g. [[1,2],[3,4]] produces output [2,,4]. 68## 69## Private function. 70ExpandFieldSR := function ( list ) 71 local newlist, term; 72 newlist := []; 73 for term in list do 74 newlist[term[1]] := term[2]; 75 od; 76 return newlist; 77end; 78 79############################################################################# 80## 81#F InitializeSR(<sr>) . . . . . . . initialize a set-record 82## 83## <sr> should be a set-record conforming to the criteria. 84## The criteria are checked, various other fields are calculated and set, 85## and the existing fields are (partially) checked for validity. 86## 87## Public function. 88InitializeSR := function ( sr ) 89 local i, j, k, s, ba, lba, nv, fld; 90 if not IsBound(sr.type) or not IsBound(sr.size) then 91 Error("Subfield 'type' or 'size' of set-record field is not set."); 92 fi; 93 94 if IsBound(sr.base) then 95 InitializeSR(sr.base); 96 fi; 97 if IsBound(sr.labels) then 98 InitializeSR(sr.labels); 99 fi; 100 101 # if the set record names are stored in sparse format, we 102 # convert to dense format for internal use - they will still be 103 # printed in sparse format. 104 k := sr.type; 105 if k="words" or k="identifiers" or k="strings" or 106 k="list of words" or k="list of integers" then 107 if not IsBound(sr.format) or not IsBound(sr.names) 108 or not IsList(sr.names) then 109 Error( 110 "Subfield 'format' or 'names' of set-record field is not set or invalid."); 111 fi; 112 sr.printingFormat := sr.format; 113 if sr.format="sparse" then 114 sr.format := "dense"; 115 sr.names := ExpandFieldSR(sr.names); 116 fi; 117 fi; 118 119 if k="words" or k="list of words" then 120 if not IsBound(sr.alphabet) or not IsList(sr.alphabet) then 121 Error( 122 "Subfield 'alphabet' of set-record field is not set or invalid."); 123 fi; 124 fi; 125 126 if k="labeled" or k="labelled" then 127 if not IsBound(sr.format) 128 or not IsBound(sr.labels) or not IsRecord(sr.labels) 129 or not IsBound(sr.setToLabels) or not IsList(sr.setToLabels) then 130 Error( 131 "Some required field for type \"labeled\" is not set or invalid."); 132 fi; 133 sr.printingFormat := sr.format; 134 if sr.format="sparse" then 135 sr.format := "dense"; 136 sr.setToLabels := ExpandFieldSR(sr.setToLabels); 137 fi; 138 fi; 139 140 if k="product" then 141 if not IsBound(sr.padding) or not IsBound(sr.arity) 142 or not IsBound(sr.base) or not IsRecord(sr.base) then 143 Error("Some required field for type \"product\" is not set."); 144 fi; 145 if IsBound(sr.base.names) then 146 # calculate names of set-record members 147 ba := Concatenation(sr.base.names,[sr.padding]); 148 lba := Length(ba); 149 nv := sr.arity; 150 sr.names := []; 151 fld := sr.names; 152 s := sr.size; 153 for i in [1..s] do 154 fld[i]:=[]; 155 k := i-1; 156 for j in Reversed([1..nv]) do 157 fld[i][j] := ba[k mod lba + 1]; 158 k := Int(k/lba); 159 od; 160 od; 161 fi; 162 fi; 163 164end; 165 166############################################################################# 167## 168#F InitializeFSA(<fsa>) . . . . . . . initialize an fsa 169## 170## <fsa> should be an fsa-record conforming to the criteria. 171## The criteria are checked, various other fields are calculated and set, 172## and the existing fields are (partially) checked for validity. 173## The entries in the flag field ("DFA", "minimized", "BFS", etc.) 174## are assumed to be valid if set. 175## 176## Public function. 177InitializeFSA := function ( fsa ) 178 local ns, ne, i, j; 179 # First check that the fsa has the 7 compulsory fields. 180 if not IsFSA(fsa) then 181 Error("Argument is not an fsa."); 182 fi; 183 if IsBound(fsa.isInitialized) and fsa.isInitializedFSA = true then 184 Print("This fsa is already initialized.\n"); 185 return; 186 fi; 187 if not IsBound(fsa.alphabet) or not IsRecord(fsa.alphabet) then 188 Error("'alphabet' field is not set, or invalid."); 189 fi; 190 if not IsBound(fsa.states) or not IsRecord(fsa.states) then 191 Error("'states' field is not set, or invalid."); 192 fi; 193 if not IsBound(fsa.initial) or not IsList(fsa.initial) then 194 Error("'initial' field is not set, or invalid."); 195 fi; 196 if not IsBound(fsa.accepting) or not IsList(fsa.accepting) then 197 Error("'accepting' field is not set, or invalid."); 198 fi; 199 if not IsBound(fsa.table) or not IsRecord(fsa.table) then 200 Error("'table' field is not set, or invalid."); 201 fi; 202 if not IsBound(fsa.flags) or not IsList(fsa.flags) then 203 Error("'flags' field is not set, or invalid."); 204 fi; 205 206 InitializeSR(fsa.states); 207 InitializeSR(fsa.alphabet); 208 209 ns := fsa.states.size; 210 ne := fsa.alphabet.size; 211 212 fsa.initial := Set(fsa.initial); 213 fsa.accepting := Set(fsa.accepting); 214 fsa.flags := Set(fsa.flags); 215 216 if not IsBound(fsa.table.format) or not IsBound(fsa.table.transitions) then 217 Error("Subfield 'format' or 'transitions' of table field is not set."); 218 fi; 219 fsa.table.printingFormat := fsa.table.format; 220 if fsa.table.format = "dense nondeterministic" then 221 Error("Sorry - dense nondeterministic tables not yet supported."); 222 elif fsa.table.format = "dense deterministic" then 223 fsa.denseDTable := fsa.table.transitions; 224 if Length(fsa.denseDTable) <> ns then 225 Error("Length of transition table wrong."); 226 fi; 227 for i in [1..ns] do 228 for j in [1..ne] do 229 if not IsBound(fsa.denseDTable[i][j]) then 230 fsa.denseDTable[i][j] := 0; 231 fi; 232 od; 233 od; 234 elif fsa.table.format = "sparse" then 235 fsa.sparseTable := fsa.table.transitions; 236 if Length(fsa.sparseTable) <> ns then 237 Error("Length of transition table wrong."); 238 fi; 239 else 240 Error("Invalid transition table format."); 241 fi; 242 243 fsa.isInitializedFSA := true; 244end; 245 246############################################################################# 247## 248#F FSA(alphabet) . . . . . . . make an initialized FSA with a 249## specified alphabet and a single state which is both accepting and initial 250## 251## Public function. 252FSA := function ( alphabet ) 253 local F; 254 F := rec(); 255 F.isFSA := true; 256 F.alphabet := alphabet; 257 F.states := rec(type := "simple",size := 1); 258 F.flags := ["DFA"]; 259 F.initial := [1]; 260 F.accepting := [1]; 261 F.table := rec( 262 format := "dense deterministic", 263 numTransitions := 0, 264 transitions := [0 * [1..alphabet.size]] 265 ); 266 InitializeFSA(F); 267 268 return F; 269end; 270 271############################################################################# 272#F AlphabetFSA 273## 274## Public function. 275AlphabetFSA := fsa -> fsa.alphabet; 276 277############################################################################# 278#F StatesFSA 279## 280## Public function. 281StatesFSA := fsa -> fsa.states; 282 283############################################################################# 284#F NumberOfStatesFSA 285## 286## Public function. 287NumberOfStatesFSA := fsa -> fsa.states.size; 288 289############################################################################# 290#F NumberOfLettersFSA 291#F SizeOfAlphabetFSA 292## 293## Public function. 294NumberOfLettersFSA := fsa -> fsa.alphabet.size; 295SizeOfAlphabetFSA := fsa -> fsa.alphabet.size; 296 297############################################################################# 298## 299#F IsDeterministicFSA(<fsa>) . . . . . . . test if fsa is deterministic 300## 301## Tests if the fsa <fsa> is deterministic. 302## The definition of deterministic used here is that of a partial 303## deterministic finite state automaton, rather than a total one. 304## This means, no epsilon-transtions, *at most* one initial state and 305## *at most one* transition from any state with a given label. 306## An FSA is is non-deterministic (NFA) if one of these conditions fails. 307## 308## Public function. 309IsDeterministicFSA := function ( fsa ) 310 local term, subterm, dfa_check; 311 if not IsInitializedFSA(fsa) then 312 InitializeFSA(fsa); 313 fi; 314 if "DFA" in fsa.flags then 315 return true; 316 fi; 317 if "NFA" in fsa.flags then 318 return false; 319 fi; 320 if Length(fsa.initial) > 1 then 321 AddSet(fsa.flags,"NFA"); 322 return false; 323 fi; 324 if IsBound(fsa.denseDTable) then 325 # This must imply DFA 326 AddSet(fsa.flags,"DFA"); 327 return true; 328 fi; 329 for term in fsa.sparseTable do 330 # sparseTable must be bound at this point 331 dfa_check:=[]; 332 for subterm in term do 333 if subterm[1]=0 or subterm[1]="epsilon" or 334 IsBound(dfa_check[subterm[1]]) then 335 AddSet(fsa.flags,"NFA"); 336 return false; 337 else 338 dfa_check[subterm[1]] := 1; 339 fi; 340 od; 341 od; 342 343 AddSet(fsa.flags,"DFA"); 344 return true; 345end; 346 347############################################################################# 348## 349#F SparseTableToDenseDTableFSA(<fsa>) . . get DenseDTable from SparseTable 350## SparseTableToDenseDTableFSA calculates the DenseDTable of the fsa from 351## the SparseTable and returns it. 352## 353## Private function. 354SparseTableToDenseDTableFSA := function ( fsa ) 355 local denseDTable, sparseTable, row, newrow, dt, ne, term, i; 356 ne := fsa.alphabet.size; 357 sparseTable := fsa.sparseTable; 358 if IsBound(fsa.table.defaultTarget) then 359 dt:=fsa.table.defaultTarget; 360 else 361 dt:=0; 362 fi; 363 denseDTable := []; 364 for row in sparseTable do 365 newrow:=[]; 366 for i in [1..ne] do newrow[i]:=dt; od; 367 for term in row do 368 if term[1]=0 or newrow[term[1]] <> dt then 369 AddSet(fsa.flags,"NFA"); 370 Error("This fsa is nondeterministic, so cannot create DenseDTable.\n"); 371 fi; 372 newrow[term[1]]:=term[2]; 373 od; 374 Add(denseDTable,newrow); 375 od; 376 return denseDTable; 377end; 378 379############################################################################# 380## 381#F DenseDTableToSparseTableFSA(<fsa>) . . get SparseTable from DenseDTable 382## DenseDTableToSparseTableFSA calculates the SparseTable of the fsa 383## from the DenseDTable and returns it. 384## 385## Private function. 386DenseDTableToSparseTableFSA := function ( fsa ) 387 local denseDTable, sparseTable, row, newrow, dt, ne, i; 388 ne := fsa.alphabet.size; 389 denseDTable := fsa.denseDTable; 390 if IsBound(fsa.table.defaultTarget) then 391 dt:=fsa.table.defaultTarget; 392 else 393 dt:=0; 394 fi; 395 sparseTable := []; 396 for row in denseDTable do 397 newrow:=[]; 398 for i in [1..ne] do 399 if row[i] <> dt then 400 Add(newrow,[i,row[i]]); 401 fi; 402 od; 403 Add(sparseTable,newrow); 404 od; 405 return sparseTable; 406end; 407 408############################################################################# 409## 410#F DenseDTableToBackTableFSA(<fsa>) . . . . get BackTable from DenseDTable 411## DenseDTableToBackTableFSA calculates the BackTable of the fsa from the 412## DenseDTable and returns it. 413## 414## Private function. 415DenseDTableToBackTableFSA := function ( fsa ) 416 local denseDTable, backTable, row, ne, ns, i, j; 417 ne := fsa.alphabet.size; 418 ns := fsa.states.size; 419 denseDTable := fsa.denseDTable; 420 backTable := []; 421 for i in [1..ns] do 422 backTable[i] := []; 423 od; 424 for i in [1..ns] do 425 row := denseDTable[i]; 426 for j in [1..ne] do 427 if row[j] in [1..ns] then 428 Add(backTable[row[j]],[j,i]); 429 fi; 430 od; 431 od; 432 return backTable; 433end; 434 435############################################################################# 436## 437#F SparseTableToBackTableFSA(<fsa>) . . . . get BackTable from SparseTable 438## SparseTableToBackTableFSA calculates the BackTable of the fsa from 439## the SparseTable and returns it. 440## The backTable still does not include "default-target" edges. 441## 442## Private function. 443SparseTableToBackTableFSA := function ( fsa ) 444 local sparseTable, backTable, row, ne, ns, i, term; 445 ne := fsa.alphabet.size; 446 ns := fsa.states.size; 447 sparseTable := fsa.sparseTable; 448 backTable := []; 449 for i in [1..ns] do 450 backTable[i] := []; 451 od; 452 for i in [1..ns] do 453 row := sparseTable[i]; 454 for term in row do 455 if term[2] in [1..ns] then 456 Add(backTable[term[2]],[term[1],i]); 457 fi; 458 od; 459 od; 460 return backTable; 461end; 462 463############################################################################# 464## 465#F DenseDTableFSA(<fsa>) . . . . . . . calculates DenseDTable of dfa 466## DenseDTableFSA calculates the DenseDTable of the fsa <fsa> and returns it. 467## Public function. 468DenseDTableFSA := function ( fsa ) 469 if not IsInitializedFSA(fsa) then 470 InitializeFSA(fsa); 471 fi; 472 if not IsBound(fsa.denseDTable) then 473 fsa.denseDTable := SparseTableToDenseDTableFSA(fsa); 474 fi; 475 return fsa.denseDTable; 476end; 477 478 479############################################################################# 480## 481#F SparseTableFSA(<fsa>) . . . . . . . calculates DenseDTable of fsa 482## SparseTableFSA calculates the SparseTable of the fsa and returns it. 483## Public function. 484SparseTableFSA := function ( fsa ) 485 if not IsInitializedFSA(fsa) then 486 InitializeFSA(fsa); 487 fi; 488 if not IsBound(fsa.sparseTable) then 489 fsa.sparseTable := DenseDTableToSparseTableFSA(fsa); 490 fi; 491 return fsa.sparseTable; 492end; 493 494############################################################################# 495## 496#F BackTableFSA(<fsa>) . . . . . . . calculates BackTable of fsa 497## BackTableFSA calculates the BackTable of the fsa and returns it. 498## If calculated from the SparseTable, it will not include "default-target" 499## edges. 500## Public function. 501BackTableFSA := function ( fsa ) 502 if not IsInitializedFSA(fsa) then 503 InitializeFSA(fsa); 504 fi; 505 if not IsBound(fsa.backTable) then 506 if IsBound(fsa.denseDTable) then 507 fsa.backTable := DenseDTableToBackTableFSA(fsa); 508 else 509 fsa.backTable := SparseTableToBackTableFSA(fsa); 510 fi; 511 fi; 512 return fsa.backTable; 513end; 514 515############################################################################# 516## 517#F LinePrintFSA(<line> [,<filename>]) . . . . . . . print the line x 518## 519## LinePrintFSA prints the line (a string) to the terminal (default) 520## or to file filename if specified, formatting nicely. 521## It works by building up the material to be printed line by line as strings, 522## and calling LinePrintFSA to print each individual line. 523## 524LinePrintFSA := function ( arg ) 525 local line, filename; 526 line := arg[1]; 527 if Length(arg) = 1 then filename := ""; 528 else filename := arg[2]; 529 fi; if filename = "" then Print(line,"\n"); 530 else AppendTo(filename,line,"\n"); 531 fi; 532end; 533 534############################################################################# 535## 536#F WordToStringSR( <word>, <gens>, <names> ) 537## . . . . . converts <word> to a string 538## 539## <word> is a word in genrators <gens>. 540## <names> is a list of printing strings for <gens>. 541## The word is converted into a string representing the word for printing. 542WordToStringSR := function ( word, gens, names ) 543 local string, i, l, ls, ng, g, ct, lg; 544 l := Length(word); 545 if l=0 then 546 return "IdWord"; 547 fi; 548 string := ""; 549 lg := 0; ct := 0; 550 for i in [1..l] do 551 g := Position(gens,Subword(word,i,i)); 552 if g <> lg and lg <> 0 then 553 if string<>"" then string := Concatenation(string,"*"); fi; 554 555 ng := names[lg]; 556 if ct > 1 then 557 #Check to see if names[lg] ends "^-1" 558 ls := Length(ng); 559 if ls>3 and ng[ls-2]='^' and ng[ls-1]='-' and ng[ls]='1' then 560 string := Concatenation(string,ng{[1..ls-1]},String(ct)); 561 else 562 string := Concatenation(string,ng,"^",String(ct)); 563 fi; 564 else string := Concatenation(string,ng); 565 fi; 566 ct := 0; 567 fi; 568 lg := g; ct := ct+1; 569 od; 570 if string<>"" then string := Concatenation(string,"*"); fi; 571 ng := names[lg]; 572 if ct > 1 then 573 #Check to see if names[lg] ends "^-1" 574 ls := Length(ng); 575 if ls>3 and ng[ls-2]='^' and ng[ls-1]='-' and ng[ls]='1' then 576 string := Concatenation(string,ng{[1..ls-1]},String(ct)); 577 else 578 string := Concatenation(string,ng,"^",String(ct)); 579 fi; 580 else string := Concatenation(string,ng); 581 fi; 582 583 return string; 584end; 585 586############################################################################# 587## 588#F WriteSetRecordSR(<sr> [<name>, <filename>, <offset>, <endsymbol>]) 589## . . print the set record <sr> 590## 591## WriteSetRecordSR prints the set record <sr> to the terminal (default) 592## or to file <filename> if specified, formatting nicely. 593## It works by building up the material to be printed line by line as strings, 594## and calling LinePrintFSA to print each individual line. 595## If the optional string <name> is present, printing is preceded by an 596## assignment "name:=", so that the resulting file can be read back in. 597## if the optional positive integer <offset> is present then each line 598## is preceded by <offset> spaces. 599## If the optional string <endsymbol> is present, then this is printed at 600## the end (it is likely to be ";" or ","). 601## 602## Private function. 603WriteSetRecordSR := function ( arg ) 604 local sr, filename, name, offset, endsymbol, offstring, line, ct, first, 605 pn, wstring, i, tempfn, ids; 606 sr := arg[1]; 607 if (sr.type="identifiers" or sr.type="words" or sr.type="list of words") 608 and not IsBound(sr.printingStrings) then 609 ## To find out what the printing strings should be, the ony way is to 610 ## output the identifiers as strings to a temporary file and read back in. 611 tempfn:=TmpName(); 612 if sr.type="identifiers" then 613 ids:=sr.names; 614 else 615 ids:=sr.alphabet; 616 fi; 617 PrintTo(tempfn,"_RWST_:=["); 618 for i in [1..Length(ids)] do 619 if i>1 then AppendTo(tempfn,","); fi; 620 AppendTo(tempfn,"\"",ids[i],"\""); 621 od; 622 AppendTo(tempfn,"];\n"); 623 Read(tempfn); 624 sr.printingStrings:=_RWST_; 625 Exec(Concatenation("/bin/rm -f ",tempfn)); 626 fi; 627 filename := ""; 628 name := ""; 629 endsymbol := ""; 630 offset:=0; 631 if Length(arg)>=2 then 632 name := arg[2]; 633 fi; 634 if Length(arg)>=3 then 635 filename := arg[3]; 636 fi; 637 if Length(arg)>=4 then 638 offset := arg[4]; 639 fi; 640 if Length(arg)>=5 then 641 endsymbol := arg[5]; 642 fi; 643 if not IsInt(offset) or offset<=0 then 644 offset := 0; 645 fi; 646 offstring := String("",offset); 647 if name = "" then 648 line := "rec ("; 649 else 650 line := Concatenation(String(name,16+offset-4)," := rec ("); 651 fi; 652 LinePrintFSA(line,filename); 653 654 line := Concatenation(offstring,String("type",16), 655 " := ","\"",sr.type,"\"",","); 656 LinePrintFSA(line,filename); 657 line := Concatenation(offstring,String("size",16)," := ",String(sr.size)); 658 if sr.type <> "simple" then 659 line := Concatenation(line,","); 660 fi; 661 LinePrintFSA(line,filename); 662 if sr.type = "product" then 663 line:=Concatenation(offstring,String("arity",16)," := ", 664 String(sr.arity),","); 665 LinePrintFSA(line,filename); 666 line:=Concatenation(offstring,String("padding",16)," := _,"); 667 LinePrintFSA(line,filename); 668 WriteSetRecordSR(sr.base,"base",filename,offset+4); 669 elif sr.type="words" or sr.type="identifiers" or sr.type="strings" 670 or sr.type="list of words" or sr.type="list of integers" then 671 if sr.type="strings" then 672 pn := []; 673 for i in [1..sr.size] do 674 pn[i] := Concatenation("\"",sr.names[i],"\""); 675 od; 676 elif sr.type="words" or sr.type="identifiers" or 677 sr.type="list of words" then 678 pn := sr.printingStrings; 679 fi; 680 if sr.type="words" or sr.type="list of words" then 681 line := Concatenation(offstring,String("alphabet",16)," := ["); 682 ct := 1; 683 while ct <= Length(sr.alphabet) do 684 if ct=1 or Length(line)+Length(pn[ct]) <= 76 then 685 if ct > 1 then 686 line := Concatenation(line,","); 687 fi; 688 line := Concatenation(line,pn[ct]); 689 else 690 line := Concatenation(line,","); 691 LinePrintFSA(line,filename); 692 line := String("",21+offset); 693 line := Concatenation(line,pn[ct]); 694 fi; 695 ct := ct+1; 696 od; 697 line := Concatenation(line,"],"); 698 LinePrintFSA(line,filename); 699 fi; 700 line := Concatenation(offstring,String("format",16)," := "); 701 line := Concatenation(line,"\"",sr.printingFormat,"\"",","); 702 LinePrintFSA(line,filename); 703 line := Concatenation(offstring,String("names",16)," := ["); 704 if sr.printingFormat="dense" then 705 # recall that the names are always stored internally in dense format. 706 ct := 1; 707 while ct <= sr.size do 708 if sr.type="words" then 709 wstring := WordToStringSR(sr.names[ct],sr.alphabet,pn); 710 elif sr.type="list of words" then 711 wstring:="["; 712 for i in [1..Length(sr.names[ct])] do 713 if i>1 then 714 wstring:=Concatenation(wstring,","); 715 fi; 716 wstring:=Concatenation(wstring, 717 WordToStringSR(sr.names[ct][i],sr.alphabet,pn) ); 718 od; 719 wstring:=Concatenation(wstring,"]"); 720 elif sr.type="list of integers" then 721 wstring:="["; 722 for i in [1..Length(sr.names[ct])] do 723 if i>1 then 724 wstring:=Concatenation(wstring,","); 725 fi; 726 wstring:=Concatenation(wstring,String(sr.names[ct][i])); 727 od; 728 wstring:=Concatenation(wstring,"]"); 729 else wstring := pn[ct]; 730 fi; 731 if ct=1 or Length(line)+Length(wstring) <= 76 then 732 if ct > 1 then 733 line := Concatenation(line,","); 734 fi; 735 line := Concatenation(line,wstring); 736 else 737 line := Concatenation(line,","); 738 LinePrintFSA(line,filename); 739 line := String("",21+offset); 740 line := Concatenation(line,wstring); 741 fi; 742 ct := ct+1; 743 od; 744 line := Concatenation(line,"]"); 745 LinePrintFSA(line,filename); 746 else 747 ct := 1; 748 first := true; 749 while ct <= sr.size do 750 if IsBound(sr.names[ct]) then 751 if sr.type="words" then 752 wstring := 753 WordToStringSR(sr.names[ct],sr.alphabet,pn); 754 elif sr.type="list of words" then 755 wstring:="["; 756 for i in [1..Length(sr.names[ct])] do 757 if i>1 then 758 wstring:=Concatenation(wstring,","); 759 fi; 760 wstring:=Concatenation(wstring, 761 WordToStringSR(sr.names[ct][i],sr.alphabet,pn) ); 762 od; 763 wstring:=Concatenation(wstring,"]"); 764 elif sr.type="list of integers" then 765 wstring:="["; 766 for i in [1..Length(sr.names[ct])] do 767 if i>1 then 768 wstring:=Concatenation(wstring,","); 769 fi; 770 wstring:=Concatenation(wstring,String(sr.names[ct][i])); 771 od; 772 wstring:=Concatenation(wstring,"]"); 773 else wstring := pn[ct]; 774 fi; 775 if first then 776 line := Concatenation 777 (line,"[",String(ct),",",wstring,"]"); 778 first := false; 779 else 780 line := Concatenation(line,","); 781 LinePrintFSA(line,filename); 782 line := String("",21+offset); 783 line := Concatenation 784 (line,"[",String(ct),",",wstring,"]"); 785 fi; 786 fi; 787 ct := ct+1; 788 od; 789 LinePrintFSA(line,filename); 790 line := Concatenation(String("",20+offset),"]"); 791 LinePrintFSA(line,filename); 792 fi; 793 elif sr.type = "labeled" or sr.type="labelled" then 794 WriteSetRecordSR(sr.labels,"labels",filename,offset+4,","); 795 line := Concatenation(offstring,String("format",16)," := "); 796 line := Concatenation(line,"\"",sr.printingFormat,"\"",","); 797 LinePrintFSA(line,filename); 798 line := Concatenation(offstring,String("setToLabels",16)," := ["); 799 if sr.printingFormat="dense" then 800 ct := 1; 801 while ct <= sr.size do 802 if not IsBound(sr.setToLabels[ct]) then 803 if ct>1 then 804 line := Concatenation(line,","); 805 fi; 806 elif ct=1 or 807 Length(line)+Length(String(sr.setToLabels[ct])) <= 76 then 808 if ct > 1 then 809 line := Concatenation(line,","); 810 fi; 811 line := Concatenation(line,String(sr.setToLabels[ct])); 812 else 813 line := Concatenation(line,","); 814 LinePrintFSA(line,filename); 815 line := String("",21+offset); 816 line := Concatenation(line,String(sr.setToLabels[ct])); 817 fi; 818 ct := ct+1; 819 od; 820 line := Concatenation(line,"]"); 821 LinePrintFSA(line,filename); 822 else 823 ct := 1; 824 first := true; 825 while ct <= sr.size do 826 if IsBound(sr.setToLabels[ct]) then 827 if first then 828 line := Concatenation 829 (line,"[",String(ct),",",String(sr.setToLabels[ct]),"]"); 830 first := false; 831 else 832 line := Concatenation(line,","); 833 LinePrintFSA(line,filename); 834 line := String("",21+offset); 835 line := Concatenation 836 (line,"[",String(ct),",",String(sr.setToLabels[ct]),"]"); 837 fi; 838 fi; 839 ct := ct+1; 840 od; 841 LinePrintFSA(line,filename); 842 line := Concatenation(String("",20+offset),"]"); 843 LinePrintFSA(line,filename); 844 fi; 845 elif sr.type <> "simple" then 846 Error("Invalid type for set record."); 847 fi; 848 if name = "" then 849 line := Concatenation(")",endsymbol); 850 else 851 line := String(")",16+offset-4); 852 line := Concatenation(line,endsymbol); 853 fi; 854 LinePrintFSA(line,filename); 855end; 856 857############################################################################# 858## 859#F WriteFSA(<fsa> [<name>, <filename>, <endsymbol>]) . . print the fsa "fsa" 860## 861## WriteFSA prints the fsa <fsa> to the terminal (default) or to 862## file <filename> if specified, formatting nicely. 863## It works by building up the material to be printed line by line as strings, 864## and calling LinePrintFSA to print each individual line. 865## If the optional string <name> is present, printing is preceded by an 866## assignment "name:=", so that the resulting file can be read back in. 867## If the optional string <endsymbol> is present, then this is printed at 868## the end (it is likely to be ";" or ","). 869## 870## Public function. 871WriteFSA := function ( arg ) 872 local fsa, ns, ne, filename, name, tabletype, table, endsymbol, 873 line, ct, first, i, j; 874 fsa := arg[1]; 875 filename := ""; 876 name := ""; 877 endsymbol := ""; 878 if Length(arg)>=2 then 879 name := arg[2]; 880 fi; 881 if Length(arg)>=3 then 882 filename := arg[3]; 883 fi; 884 if Length(arg)>=4 then 885 endsymbol := arg[4]; 886 fi; 887 888 if not IsInitializedFSA(fsa) then 889 InitializeFSA(fsa); 890 fi; 891 ns := fsa.states.size; 892 ne := fsa.alphabet.size; 893 if filename = "" and name = "" then 894 Print("rec (\n"); 895 elif filename = "" and name <> "" then 896 Print(name," := \nrec (\n"); 897 elif filename <> "" and name = "" then 898 PrintTo(filename,"rec (\n"); 899 else 900 PrintTo(filename,name," := \nrec (\n"); 901 fi; 902 903 line := String("isFSA",16); 904 line := Concatenation(line," := true,"); 905 LinePrintFSA(line,filename); 906 907 WriteSetRecordSR(fsa.alphabet,"alphabet",filename,4,","); 908 WriteSetRecordSR(fsa.states,"states",filename,4,","); 909 910 line := String("flags",16); 911 line := Concatenation(line," := ["); 912 first := true; 913 for i in fsa.flags do 914 if not first then 915 line := Concatenation(line,","); 916 fi; 917 first := false; 918 line := Concatenation(line,"\"",i,"\""); 919 od; 920 line := Concatenation(line,"],"); 921 LinePrintFSA(line,filename); 922 923 line := String("initial",16); 924 line := Concatenation(line," := ["); 925 ct := 1; 926 while ct<= Length(fsa.initial) do 927 if ct=1 or Length(line)+Length(String(fsa.initial[ct])) <= 76 then 928 if ct > 1 then 929 line := Concatenation(line,","); 930 fi; 931 line := Concatenation(line,String(fsa.initial[ct])); 932 else 933 line := Concatenation(line,","); 934 LinePrintFSA(line,filename); 935 line := String("",21); 936 line := Concatenation(line,String(fsa.initial[ct])); 937 fi; 938 ct := ct+1; 939 od; 940 line := Concatenation(line,"],"); 941 LinePrintFSA(line,filename); 942 943 line := String("accepting",16); 944 line := Concatenation(line," := ["); 945 ct := 1; 946 if ns>1 and fsa.accepting=[1..ns] then 947 line := Concatenation(line,"1..",String(ns)); 948 else 949 while ct<= Length(fsa.accepting) do 950 if ct=1 or Length(line)+Length(String(fsa.accepting[ct])) <= 76 then 951 if ct > 1 then 952 line := Concatenation(line,","); 953 fi; 954 line := Concatenation(line,String(fsa.accepting[ct])); 955 else 956 line := Concatenation(line,","); 957 LinePrintFSA(line,filename); 958 line := String("",21); 959 line := Concatenation(line,String(fsa.accepting[ct])); 960 fi; 961 ct := ct+1; 962 od; 963 fi; 964 line := Concatenation(line,"],"); 965 LinePrintFSA(line,filename); 966 967 tabletype := fsa.table.printingFormat; 968 969 if tabletype="dense deterministic" then 970 if IsDeterministicFSA(fsa) = false then 971 fsa.table.printingFormat := "sparse"; 972 tabletype := fsa.table.printingFormat; 973 elif not IsBound(fsa.denseDTable) then 974 DenseDTableFSA(fsa); 975 fi; 976 fi; 977 if tabletype="sparse" and not IsBound(fsa.sparseTable) then 978 SparseTableFSA(fsa); 979 fi; 980 if tabletype="dense deterministic" then 981 table := fsa.denseDTable; 982 fsa.table.format := "dense deterministic"; 983 # Calculate number of nontrivial transitions 984 ct := 0; 985 for i in [1..ns] do 986 for j in [1..ne] do 987 if table[i][j] <> 0 then 988 ct := ct+1; 989 fi; 990 od; 991 od; 992 else 993 table := fsa.sparseTable; 994 fsa.table.format := "sparse"; 995 # Calculate number of nontrivial transitions 996 ct := 0; 997 for i in [1..ns] do 998 ct := ct + Length(table[i]); 999 od; 1000 fi; 1001 fsa.table.numTransitions := ct; 1002 1003 line := Concatenation(String("table",16)," := rec("); 1004 LinePrintFSA(line,filename); 1005 line := Concatenation(String("format",20), 1006 " := ","\"",fsa.table.format,"\"",","); 1007 LinePrintFSA(line,filename); 1008 line := Concatenation(String("numTransitions",20)," := ", 1009 String(fsa.table.numTransitions),","); 1010 LinePrintFSA(line,filename); 1011 if tabletype = "sparse" and IsBound(fsa.table.defaultTarget) then 1012 line := Concatenation(line,String("defaultTarget",20)," := "); 1013 line := Concatenation(line,String(fsa.table.defaultTarget),","); 1014 LinePrintFSA(line,filename); 1015 fi; 1016 line := Concatenation(String("transitions",20)," := ["); 1017 if ns=0 then 1018 LinePrintFSA(line,filename); 1019 fi; 1020 first := true; 1021 for i in [1..ns] do 1022 if first then 1023 line := Concatenation(line,"["); 1024 first := false; 1025 else 1026 line := Concatenation(String("",25),"["); 1027 fi; 1028 ct := 1; 1029 while ct<= Length(table[i]) do 1030 if ct=1 or Length(line)+Length(String(table[i][ct])) <= 76 then 1031 if ct > 1 then 1032 line := Concatenation(line,","); 1033 fi; 1034 line := Concatenation(line,String(table[i][ct])); 1035 else 1036 line := Concatenation(line,","); 1037 LinePrintFSA(line,filename); 1038 line := String("",25); 1039 line := Concatenation(line,String(table[i][ct])); 1040 fi; 1041 ct := ct+1; 1042 od; 1043 line := Concatenation(line,"]"); 1044 if i<ns then 1045 line := Concatenation(line,","); 1046 fi; 1047 LinePrintFSA(line,filename); 1048 od; 1049 line := Concatenation(String("",20),"]"); 1050 LinePrintFSA(line,filename); 1051 line := Concatenation(String("",16),")"); 1052 LinePrintFSA(line,filename); 1053 1054 line := Concatenation(")",endsymbol); 1055 LinePrintFSA(line,filename); 1056end; 1057 1058############################################################################# 1059## 1060#F ElementNumberSR(<sr>, <el>) . . . get number of set-record element. 1061## 1062## <sr> should be a set-record. <el> should either be a positive integer 1063## representing an element of <sr> or a name for such an element. 1064## In either case, the number of the element is returned. 1065## False is returned if <el> is invalid. 1066## 1067## Private function. 1068ElementNumberSR := function ( sr, el ) 1069 local IdWord; 1070 if IsAssocWordWithOne(el) then 1071 IdWord := el^0; 1072 else IdWord := false; 1073 fi; 1074 if IsInt(el) then 1075 if el>sr.size+1 then 1076 return false; 1077 else 1078 return el; 1079 fi; 1080 elif el=IdWord then 1081 return sr.size+1; 1082 elif IsBound(sr.names) then 1083 return Position(sr.names,el); 1084 else 1085 return false; 1086 fi; 1087end; 1088 1089############################################################################# 1090## 1091#F TargetDFA(<fsa>,<e>,<s>) . . . . . . . target of edge in a dfa 1092## TargetDFA calculates and returns the target of the edge in the dfa <fsa> 1093## with edge <e> and source-state <s>. 1094## 0 is returned if there is no edge. 1095## <e> can either be the number of an edge or an edge-name. 1096## <s> can either be the number of a state or a state-name. 1097## The returned value has same type as <s> (or 0). 1098## Public function. 1099TargetDFA := function ( fsa,e,s ) 1100 local tname, term, row, ns, t, ng; 1101 if not IsInitializedFSA(fsa) then 1102 InitializeFSA(fsa); 1103 fi; 1104 if IsDeterministicFSA(fsa)=false then 1105 Error("First argument is not a dfa."); 1106 fi; 1107 if IsList(e) then 1108 ng := fsa.alphabet.base.size; 1109 e := (ElementNumberSR(fsa.alphabet.base,e[1])-1) * (ng+1) + 1110 ElementNumberSR(fsa.alphabet.base,e[2]); 1111 else 1112 e := ElementNumberSR(fsa.alphabet,e); 1113 fi; 1114 if e=false then 1115 Error("Second argument is not a valid edge number or label."); 1116 fi; 1117 tname := not IsInt(s); 1118 s := ElementNumberSR(fsa.states,s); 1119 if s=false then 1120 Error("Third argument is not a valid state number or name."); 1121 fi; 1122 1123 ns := fsa.states.size; 1124 if IsBound(fsa.denseDTable) then 1125 t := fsa.denseDTable[s][e]; 1126 if t=0 then 1127 return 0; 1128 fi; 1129 if tname then 1130 return fsa.states.names[t]; 1131 fi; 1132 return t; 1133 fi; 1134 1135 row := fsa.sparseTable[s]; 1136 for term in row do 1137 if term[1]=e then 1138 t := term[2]; 1139 if tname then 1140 if t=0 then 1141 return 0; 1142 fi; 1143 return fsa.states.names[t]; 1144 fi; 1145 return t; 1146 fi; 1147 od; 1148 if IsBound(fsa.table.defaultTarget) then 1149 t := fsa.table.defaultTarget; 1150 else 1151 return 0; 1152 fi; 1153 if tname then 1154 if t=0 then 1155 return 0; 1156 fi; 1157 return fsa.states.names[t]; 1158 fi; 1159 return t; 1160 1161end; 1162 1163############################################################################# 1164## 1165#F TargetsFSA(<fsa>,<e>,<s>) . . . . . . . targets of edge 1166## TargetsFSA calculates the targets of the edges in the fsa <fsa> 1167## with edge <e> and source-state <s>. 1168## The result is returned as a list of targets. 1169## <l> can either be the number of an edge-label or an edge-label-name. 1170## <s> can either be the number of a state or a state-name. 1171## The members of the returned list have same type as <s>. 1172## Public function. 1173TargetsFSA := function ( fsa,e,s ) 1174 local tname, term, targets, row, ns, t, ng; 1175 if not IsInitializedFSA(fsa) then 1176 InitializeFSA(fsa); 1177 fi; 1178 if IsList(e) then 1179 ng := fsa.alphabet.base.size; 1180 e := (ElementNumberSR(fsa.alphabet.base,e[1])-1) * (ng+1) + 1181 ElementNumberSR(fsa.alphabet.base,e[2]); 1182 else 1183 e := ElementNumberSR(fsa.alphabet,e); 1184 fi; 1185 if e=false then 1186 Error("Second argument is not a valid edge number or label."); 1187 fi; 1188 tname := not IsInt(s); 1189 s := ElementNumberSR(fsa.states,s); 1190 if s=false then 1191 Error("Third argument is not a valid state number or name."); 1192 fi; 1193 1194 ns := fsa.states.size; 1195 if IsBound(fsa.denseDTable) then 1196 if e=0 then 1197 return []; 1198 fi; 1199 t := fsa.denseDTable[s][e]; 1200 if t=0 then 1201 return []; 1202 fi; 1203 if tname then 1204 return [ fsa.states.names[t] ]; 1205 fi; 1206 return [ t ]; 1207 fi; 1208 1209 row := fsa.sparseTable[s]; 1210 targets := []; 1211 for term in row do 1212 if term[1]=e then 1213 if tname then 1214 if term[2]>0 and term[2]<=ns then 1215 Add(targets,fsa.states.names[term[2]]); 1216 fi; 1217 else 1218 Add(targets,term[2]); 1219 fi; 1220 fi; 1221 od; 1222 if targets=[] and IsBound(fsa.table.defaultTarget) then 1223 if tname then 1224 Add(targets,fsa.states.names[fsa.table.defaultTarget]); 1225 else 1226 Add(targets,fsa.table.defaultTarget); 1227 fi; 1228 fi; 1229 return targets; 1230 1231end; 1232 1233############################################################################# 1234## 1235#F SourcesFSA(<fsa>,<e>,<s>) . . . . . . . sources of edge 1236## SourcesFSA calculates the sources of the edges in the fsa <fsa> 1237## with edge <e> and source-state <s>. 1238## The result is returned as a list of sources. 1239## <l> can either be the number of an edge-label or an edge-label-name. 1240## <s> can either be the number of a state or a state-name. 1241## The members of the returned list have same type as <s>. 1242## Public function. 1243SourcesFSA := function ( fsa,e,s ) 1244 local tname, term, sources, row, ns, i, none, ng; 1245 if not IsInitializedFSA(fsa) then 1246 InitializeFSA(fsa); 1247 fi; 1248 if IsList(e) then 1249 ng := fsa.alphabet.base.size; 1250 e := (ElementNumberSR(fsa.alphabet.base,e[1])-1) * (ng+1) + 1251 ElementNumberSR(fsa.alphabet.base,e[2]); 1252 else 1253 e := ElementNumberSR(fsa.alphabet,e); 1254 fi; 1255 if e=false then 1256 Error("Second argument is not a valid edge number or label."); 1257 fi; 1258 tname := not IsInt(s); 1259 s := ElementNumberSR(fsa.states,s); 1260 if s=false then 1261 Error("Third argument is not a valid state number or name."); 1262 fi; 1263 1264 #We need the backTable for this. 1265 BackTableFSA(fsa); 1266 row := fsa.backTable[s]; 1267 sources := []; 1268 ns := fsa.states.size; 1269 for term in row do 1270 if term[1]=e then 1271 if tname then 1272 if term[2]>0 and term[2]<=ns then 1273 Add(sources,fsa.states.names[term[2]]); 1274 fi; 1275 else 1276 Add(sources,term[2]); 1277 fi; 1278 fi; 1279 od; 1280 if IsBound(fsa.table.defaultTarget) and fsa.table.defaultTarget=s and 1281 IsBound(fsa.sparseTable) then 1282 # there may be some "default-target" edges. 1283 for i in [1..ns] do 1284 row := fsa.sparseTable[i]; 1285 none := true; 1286 for term in row do 1287 if term[1]=e then none:=false; fi; 1288 od; 1289 if none then 1290 Add(sources,i); 1291 fi; 1292 od; 1293 sources := Set(sources); 1294 fi; 1295 return sources; 1296end; 1297 1298############################################################################# 1299## 1300#F IsAcceptedWordDFA(<fsa>,<w>) . . . . . . . tests if word accepted by fsa 1301## IsAcceptedWordDFA tests whether the word <w> is accepted by the 1302## deterministic fsa <fsa>, and returns true or false. 1303## <w> can either be a list of labels or a list of label numbers, 1304## or a word in the labels for <fsa>. 1305## (In the last case, the labels must be abstract generators.) 1306## For an n-variable fsa, it can also be a list of n words (which will 1307## be padded out at the end by the padding symbol). 1308## Public function. 1309IsAcceptedWordDFA := function ( fsa,w ) 1310 local state, label, len, iw, inw, nl, ps, ns, nv, i, pos, dead; 1311 if not IsInitializedFSA(fsa) then 1312 InitializeFSA(fsa); 1313 fi; 1314 if IsDeterministicFSA(fsa)=false then 1315 Error("Argument is not a dfa."); 1316 fi; 1317 if fsa.initial=[] then 1318 return false; 1319 fi; 1320 ns := fsa.states.size; 1321 state := fsa.initial[1]; 1322 iw := IsWord(w); 1323 inw := false; 1324 if not iw and not IsList(w) then 1325 Error("Second argument must be a word or a list."); 1326 fi; 1327 if IsBound(fsa.alphabet.arity) and IsList(w) and IsWord(w[1]) then 1328 if not IsBound(fsa.alphabet.base.names) then 1329 Error("Can only span n-tuple of words if base-alphabet has names."); 1330 fi; 1331 if not IsBound(fsa.alphabet.padding) then 1332 Error("Can only span n-tuple of words if there is a padding symbol."); 1333 fi; 1334 ps := fsa.alphabet.padding; 1335 inw := true; 1336 nv := fsa.alphabet.arity; 1337 nl := []; 1338 for i in [1..nv] do 1339 nl[i] := Length(w[i]); 1340 od; 1341 len := Maximum(nl); 1342 elif iw then 1343 len := Length(w); 1344 else 1345 len := Length(w); 1346 fi; 1347 dead := false; 1348 pos := 1; 1349 while not dead and pos <= len do 1350 if inw then 1351 label := []; 1352 for i in [1..nv] do 1353 if pos <= nl[i] then 1354 label[i] := Subword(w[i],pos,pos); 1355 else 1356 label[i] := ps; 1357 fi; 1358 od; 1359 elif iw then 1360 label := Subword(w,pos,pos); 1361 else 1362 label := w[pos]; 1363 fi; 1364 state := TargetDFA(fsa,label,state); 1365 if not state in [1..ns] then 1366 dead:=true; 1367 fi; 1368 pos := pos+1; 1369 od; 1370 if dead then 1371 return false; 1372 fi; 1373 return state in fsa.accepting; 1374end; 1375 1376############################################################################# 1377## 1378#F WordTargetDFA(<fsa>,<w>) . . . . . . . target of word under DFA 1379## WordTargetDFA finds the target state when the word <w> is read by 1380## the deterministic fsa <fsa>, and returns this state or 0. 1381## <w> can either be a list of labels or a list of label numbers, 1382## or a word in the labels for <fsa>. 1383## (In the last case, the labels must be abstract generators.) 1384## For an n-variable fsa, it can also be a list of n words (which will 1385## be padded out at the end by the padding symbol). 1386## Public function. 1387WordTargetDFA := function ( fsa,w ) 1388 local state, label, len, iw, inw, nl, ps, ns, nv, i, pos, dead; 1389 if not IsInitializedFSA(fsa) then 1390 InitializeFSA(fsa); 1391 fi; 1392 if IsDeterministicFSA(fsa)=false then 1393 Error("Argument is not a dfa."); 1394 fi; 1395 if fsa.initial=[] then 1396 return 0; 1397 fi; 1398 ns := fsa.states.size; 1399 state := fsa.initial[1]; 1400 iw := IsWord(w); 1401 inw := false; 1402 if not iw and not IsList(w) then 1403 Error("Second argument must be a word or a list."); 1404 fi; 1405 if IsBound(fsa.alphabet.arity) and IsList(w) and IsWord(w[1]) then 1406 if not IsBound(fsa.alphabet.base.names) then 1407 Error("Can only span n-tuple of words if base-alphabet has names."); 1408 fi; 1409 if not IsBound(fsa.alphabet.padding) then 1410 Error("Can only span n-tuple of words if there is a padding symbol."); 1411 fi; 1412 ps := fsa.alphabet.padding; 1413 inw := true; 1414 nv := fsa.alphabet.arity; 1415 nl := []; 1416 for i in [1..nv] do 1417 nl[i] := Length(w[i]); 1418 od; 1419 len := Maximum(nl); 1420 elif iw then 1421 len := Length(w); 1422 else 1423 len := Length(w); 1424 fi; 1425 dead := false; 1426 pos := 1; 1427 while not dead and pos <= len do 1428 if inw then 1429 label := []; 1430 for i in [1..nv] do 1431 if pos <= nl[i] then 1432 label[i] := Subword(w[i],pos,pos); 1433 else 1434 label[i] := ps; 1435 fi; 1436 od; 1437 elif iw then 1438 label := Subword(w,pos,pos); 1439 else 1440 label := w[pos]; 1441 fi; 1442 state := TargetDFA(fsa,label,state); 1443 if not state in [1..ns] then 1444 dead:=true; 1445 fi; 1446 pos := pos+1; 1447 od; 1448 if dead then 1449 return 0; 1450 fi; 1451 return state; 1452end; 1453 1454############################################################################# 1455## 1456#F AddStateFSA(<fsa>[,<name>]) . . . . . . . adds state to an fsa 1457## AddStateFSA adds a state to the end of the statelist of the fsa <fsa>. 1458## It has the optional name or label <name> (but if the state-type is 1459## "named", then the name must be supplied). 1460## Public function. 1461AddStateFSA := function ( arg ) 1462 local fsa, name, ns, new, i, dt; 1463 fsa := arg[1]; 1464 if fsa.states.type = "product" then 1465 Error("Cannot alter a product-type state-list"); 1466 fi; 1467 if Length(arg)=1 and IsBound(fsa.states.names) then 1468 Error("You must supply a name for the new state."); 1469 fi; 1470 name:=""; 1471 if Length(arg)=2 then 1472 name:=arg[2]; 1473 fi; 1474 if not IsInitializedFSA(fsa) then 1475 InitializeFSA(fsa); 1476 fi; 1477 fsa.states.size := fsa.states.size+1; 1478 ns := fsa.states.size; 1479 if name<>"" and IsBound(fsa.states.names) then 1480 fsa.states.names[ns] := name; 1481 fi; 1482 if IsBound(fsa.denseDTable) then 1483 if IsBound(fsa.table.defaultTarget) then 1484 dt := fsa.table.defaultTarget; 1485 else 1486 dt := 0; 1487 fi; 1488 new:=[]; 1489 for i in [1..fsa.alphabet.size] do new[i]:=dt; od; 1490 Add(fsa.denseDTable,new); 1491 fi; 1492 if IsBound(fsa.sparseTable) then 1493 new:=[]; 1494 Add(fsa.sparseTable,new); 1495 fi; 1496 Unbind(fsa.backTable); 1497 RemoveSet(fsa.flags,"minimized"); 1498 RemoveSet(fsa.flags,"trim"); 1499 RemoveSet(fsa.flags,"accessible"); 1500end; 1501 1502############################################################################# 1503## 1504#F DeleteListFSA(<l>,<n>) . . . . . . . delete element from list 1505## DeleteListFSA deletes the <n>-th element from list <l>, and closes 1506## up. Used for deleting state from an fsa. 1507## 1508DeleteListFSA := function(l,n) 1509 local i, len; 1510 len := Length(l); 1511 for i in [n..len] do 1512 if IsBound(l[i+1]) then 1513 l[i]:=l[i+1]; 1514 else 1515 Unbind(l[i]); 1516 fi; 1517 od; 1518end; 1519 1520############################################################################# 1521## 1522#F DeleteStateFSA(<fsa>) . . . . . . . delete state from an fsa 1523## DeleteStateFSA deletes the final state of the fsa <fsa>. 1524## All edges to and from that state are deleted. 1525## To delete a state other than the final, first call PermuteStatesFSA 1526## Public function. 1527DeleteStateFSA := function ( fsa ) 1528 local ns, ng, row, i, j; 1529 if fsa.states.type = "product" then 1530 Error("Cannot alter a product-type state-list"); 1531 fi; 1532 if not IsInitializedFSA(fsa) then 1533 InitializeFSA(fsa); 1534 fi; 1535 ns := fsa.states.size; 1536 ng := fsa.alphabet.size; 1537 fsa.states.size:=ns-1; 1538 if IsBound(fsa.states.names) then 1539 Unbind(fsa.states.names[ns]); 1540 fi; 1541 if IsBound(fsa.states.setToLabels) then 1542 Unbind(fsa.states.setToLabels[ns]); 1543 fi; 1544 j := Position(fsa.initial,ns); 1545 if j <> fail then 1546 DeleteListFSA(fsa.initial,j); 1547 fi; 1548 j := Position(fsa.accepting,ns); 1549 if j <> fail then 1550 DeleteListFSA(fsa.accepting,j); 1551 fi; 1552 if IsBound(fsa.table.defaultTarget) and fsa.table.defaultTarget=ns then 1553 Unbind(fsa.table.defaultTarget); 1554 fi; 1555 if IsBound(fsa.denseDTable) then 1556 for i in [1..ns-1] do 1557 row:=fsa.denseDTable[i]; 1558 for j in [1..ng] do 1559 if row[j]=ns then 1560 row[j]:=0; 1561 fi; 1562 od; 1563 od; 1564 DeleteListFSA(fsa.denseDTable,ns); 1565 fi; 1566 if IsBound(fsa.sparseTable) then 1567 for i in [1..ns-1] do 1568 row:=fsa.sparseTable[i]; 1569 j := 1; 1570 while j <= Length(row) do 1571 if row[j][2]=ns then 1572 DeleteListFSA(row,j); 1573 j:=j-1; 1574 fi; 1575 j:=j+1; 1576 od; 1577 od; 1578 DeleteListFSA(fsa.sparseTable,ns); 1579 fi; 1580 Unbind(fsa.backTable); 1581 RemoveSet(fsa.flags,"NFA"); 1582 RemoveSet(fsa.flags,"BFS"); 1583 RemoveSet(fsa.flags,"minimized"); 1584 RemoveSet(fsa.flags,"trim"); 1585 RemoveSet(fsa.flags,"accessible"); 1586end; 1587 1588############################################################################# 1589## 1590#F PermuteStatesFSA(<fsa>,p) . . . . . . . permute states of an fsa 1591## PermuteStatesFSA permutes the states of the fsa <fsa>. 1592## <p> should be a permutation of the state numbers. 1593## What was state n is renumbered state n^p. 1594## Public function. 1595PermuteStatesFSA := function ( fsa,p ) 1596 local ns, term, row, i, j, new, ne; 1597 if fsa.states.type = "product" then 1598 Error("Cannot alter a product-type state-list"); 1599 fi; 1600 if not IsInitializedFSA(fsa) then 1601 InitializeFSA(fsa); 1602 fi; 1603 ns := fsa.states.size; 1604 ne := fsa.alphabet.size; 1605 if p = () then 1606 return; 1607 fi; 1608 if not IsPerm(p) or LargestMovedPointPerm(p)>ns then 1609 Error("Second argument is invalid."); 1610 fi; 1611 if IsBound(fsa.states.names) then 1612 new := []; 1613 for i in [1..ns] do 1614 new[i^p]:=fsa.states.names[i]; 1615 od; 1616 fsa.states.names := new; 1617 fi; 1618 if IsBound(fsa.states.setToLabels) then 1619 new := []; 1620 for i in [1..ns] do 1621 if IsBound(fsa.states.setToLabels[i]) then 1622 new[i^p]:=fsa.states.setToLabels[i]; 1623 fi; 1624 od; 1625 fsa.states.setToLabels := new; 1626 fi; 1627 for i in [1..Length(fsa.initial)] do 1628 fsa.initial[i]:=fsa.initial[i]^p; 1629 od; 1630 fsa.initial := Set(fsa.initial); 1631 for i in [1..Length(fsa.accepting)] do 1632 fsa.accepting[i]:=fsa.accepting[i]^p; 1633 od; 1634 fsa.accepting := Set(fsa.accepting); 1635 if IsBound(fsa.table.defaultTarget) and fsa.table.defaultTarget>0 then 1636 fsa.table.defaultTarget := fsa.table.defaultTarget^p; 1637 fi; 1638 if IsBound(fsa.denseDTable) then 1639 new := []; 1640 for i in [1..ns] do 1641 row := fsa.denseDTable[i]; 1642 new[i^p] := row; 1643 for j in [1..ne] do 1644 if row[j]>0 then 1645 row[j] := row[j]^p; 1646 fi; 1647 od; 1648 od; 1649 fsa.denseDTable := new; 1650 fi; 1651 if IsBound(fsa.sparseTable) then 1652 new := []; 1653 for i in [1..ns] do 1654 row := fsa.sparseTable[i]; 1655 new[i^p] := row; 1656 for term in row do 1657 if term[2]>0 then 1658 term[2] := term[2]^p; 1659 fi; 1660 od; 1661 od; 1662 fsa.sparseTable := new; 1663 fi; 1664 if fsa.table.format = "dense deterministic" then 1665 fsa.table.transitions := fsa.denseDTable; 1666 else 1667 fsa.table.transitions := fsa.sparseTable; 1668 fi; 1669 Unbind(fsa.backTable); 1670 RemoveSet(fsa.flags,"BFS"); 1671end; 1672 1673############################################################################# 1674## 1675#F AddLetterFSA(<fsa>[,<name>]) . . . . . . . adds letter to alphabet of fsa 1676## AddLetterFSA adds an extra symbol to the alphabet of the fsa <fsa>. 1677## It has the optional name or label <name> (but if the alphabet-type is 1678## a named type, then the name must be supplied). 1679## Public function. 1680AddLetterFSA := function ( arg ) 1681 local fsa, name, ne, new, term; 1682 fsa := arg[1]; 1683 if not IsInitializedFSA(fsa) then 1684 InitializeFSA(fsa); 1685 fi; 1686 if fsa.alphabet.type = "product" then 1687 Error("Cannot alter a product-type alphabet"); 1688 fi; 1689 if Length(arg)=1 and IsBound(fsa.alphabet.names) then 1690 Error("You must supply a name for the new state."); 1691 fi; 1692 name := ""; 1693 if Length(arg)=2 then 1694 name := arg[2]; 1695 fi; 1696 if not IsInitializedFSA(fsa) then 1697 Error("First argument is not an initialized fsa."); 1698 fi; 1699 fsa.alphabet.size := fsa.alphabet.size+1; 1700 ne := fsa.alphabet.size; 1701 if name<>"" and IsBound(fsa.alphabet.names) then 1702 fsa.alphabet.names[ne] := name; 1703 fi; 1704 if IsBound(fsa.denseDTable) then 1705 for term in fsa.denseDTable do 1706 if IsBound(fsa.table.defaultTarget) then 1707 Add(term,fsa.table.defaultTarget); 1708 else 1709 Add(term,0); 1710 fi; 1711 od; 1712 fi; 1713end; 1714 1715############################################################################# 1716## 1717#F DeleteLetterFSA(<fsa>) . . . . . . . deletes alphabet letter from an fsa 1718## DeleteLetterFSA deletes the final alphabet label from the fsa <fsa>. 1719## All edges with that label are deleted. 1720## To delete an edge-label other than the final, first call 1721## PermuteLettersFSA 1722## Public function. 1723DeleteLetterFSA := function ( fsa ) 1724 local ns, ne, term, row, i, j; 1725 if not IsInitializedFSA(fsa) then 1726 InitializeFSA(fsa); 1727 fi; 1728 if fsa.alphabet.type = "product" then 1729 Error("Cannot alter a product-type alphabet"); 1730 fi; 1731 ns := fsa.states.size; 1732 ne := fsa.alphabet.size; 1733 fsa.alphabet.size := ne-1; 1734 if IsBound(fsa.alphabet.names) then 1735 Unbind(fsa.alphabet.names[ne]); 1736 fi; 1737 if IsBound(fsa.alphabet.setToLabels) then 1738 Unbind(fsa.alphabet.setToLabels[ne]); 1739 fi; 1740 RemoveSet(fsa.flags,"BFS"); 1741 RemoveSet(fsa.flags,"minimized"); 1742 if IsBound(fsa.denseDTable) then 1743 for row in fsa.denseDTable do 1744 DeleteListFSA(row,ne); 1745 od; 1746 fi; 1747 if IsBound(fsa.sparseTable) then 1748 for row in fsa.sparseTable do 1749 j := 1; 1750 while j <= Length(row) do 1751 if row[j][1]=ne then 1752 DeleteListFSA(row,j); 1753 fi; 1754 j:=j+1; 1755 od; 1756 od; 1757 fi; 1758 Unbind(fsa.backTable); 1759 RemoveSet(fsa.flags,"NFA"); 1760 RemoveSet(fsa.flags,"BFS"); 1761 RemoveSet(fsa.flags,"minimized"); 1762 RemoveSet(fsa.flags,"trim"); 1763 RemoveSet(fsa.flags,"accessible"); 1764end; 1765 1766############################################################################# 1767## 1768#F PermuteLettersFSA(<fsa>,p) . . . . . . . permute alphabet labels of an fsa 1769## PermuteLettersFSA permutes the alphabet labels of the fsa <fsa>. 1770## <p> should be a permutation of the alphabet labels numbers. 1771## What was edge-label n is renumbered alphabet label n^p. 1772## Public function. 1773PermuteLettersFSA := function ( fsa,p ) 1774 local ns, ne, term, row, i, j, new; 1775 if not IsInitializedFSA(fsa) then 1776 InitializeFSA(fsa); 1777 fi; 1778 if fsa.alphabet.type = "product" then 1779 Error("Cannot alter a product-type alphabet"); 1780 fi; 1781 ns := fsa.states.size; 1782 ne := fsa.alphabet.size; 1783 if not IsPerm(p) or LargestMovedPointPerm(p)>ne then 1784 Error("Second argument is invalid."); 1785 fi; 1786 if IsBound(fsa.alphabet.names) then 1787 new := []; 1788 for i in [1..ne] do 1789 new[i^p]:=fsa.alphabet.names[i]; 1790 od; 1791 fsa.alphabet.names := new; 1792 fi; 1793 if IsBound(fsa.alphabet.setToLabels) then 1794 new := []; 1795 for i in [1..ne] do 1796 if IsBound(fsa.alphabet.setToLabels[i]) then 1797 new[i^p]:=fsa.alphabet.setToLabels[i]; 1798 fi; 1799 od; 1800 fsa.alphabet.setToLabels := new; 1801 fi; 1802 if IsBound(fsa.denseDTable) then 1803 for i in [1..ns] do 1804 row := fsa.denseDTable[i]; 1805 new := []; 1806 for j in [1..ne] do 1807 new[j^p] := row[j]; 1808 od; 1809 fsa.denseDTable[i] := new; 1810 od; 1811 fi; 1812 if IsBound(fsa.sparseTable) then 1813 new := []; 1814 for i in [1..ns] do 1815 row := fsa.sparseTable[i]; 1816 for term in row do 1817 if term[1]>0 then 1818 term[1] := term[1]^p; 1819 fi; 1820 od; 1821 od; 1822 fi; 1823 if fsa.table.format = "dense deterministic" then 1824 fsa.table.transitions := fsa.denseDTable; 1825 else 1826 fsa.table.transitions := fsa.sparseTable; 1827 fi; 1828 Unbind(fsa.backTable); 1829 RemoveSet(fsa.flags,"BFS"); 1830end; 1831 1832############################################################################# 1833#F AddEdgeFSA(<fsa>,<e>,<s>,<t>) . . . . . . . adds edge to an fsa 1834## AddEdge adds an edge with source <s>, label <e> and target <t> 1835## to the fsa <fsa> (if there isn't one already. 1836## <s> and <t> can be either numbers or names of states, 1837## and <e> a number or name of an edge-label. 1838## Public function. 1839AddEdgeFSA := function ( fsa, e, s, t ) 1840 local row, term, ng; 1841 if not IsInitializedFSA(fsa) then 1842 InitializeFSA(fsa); 1843 fi; 1844 if IsList(e) then 1845 ng := fsa.alphabet.base.size; 1846 e := (ElementNumberSR(fsa.alphabet,e[1])-1) * (ng+1) + 1847 ElementNumberSR(fsa.alphabet,e[2]); 1848 else 1849 e := ElementNumberSR(fsa.alphabet,e); 1850 fi; 1851 if e=false then 1852 Error("Second argument is not a valid edge number or label."); 1853 fi; 1854 s := ElementNumberSR(fsa.states,s); 1855 if s=false then 1856 Error("Third argument is not a valid state number or name."); 1857 fi; 1858 t := ElementNumberSR(fsa.states,t); 1859 if t=false then 1860 Error("Fourth argument is not a valid state number or name."); 1861 fi; 1862 if e=0 or (IsBound(fsa.denseDTable) and fsa.denseDTable[s][e]>0 and 1863 fsa.denseDTable[s][e]<=fsa.states.size and 1864 fsa.denseDTable[s][e] <> t) then 1865 # makes non-deterministic. 1866 Print("Warning: gone non-deterministic!\n"); 1867 SparseTableFSA(fsa); 1868 fsa.table.format := "sparse"; 1869 fsa.table.transitions := fsa.sparseTable; 1870 Unbind(fsa.denseDTable); 1871 RemoveSet(fsa.flags,"DFA"); 1872 AddSet(fsa.flags,"NFA"); 1873 fi; 1874 if IsBound(fsa.denseDTable) then 1875 fsa.denseDTable[s][e] := t; 1876 fi; 1877 if IsBound(fsa.sparseTable) then 1878 row := fsa.sparseTable[s]; 1879 for term in row do 1880 if term[1]=e and term[2]>0 and term[2]<=fsa.states.size 1881 and term[2] <> t then 1882 #makes non-deterministic 1883 RemoveSet(fsa.flags,"DFA"); 1884 AddSet(fsa.flags,"NFA"); 1885 fsa.table.format := "sparse"; 1886 fi; 1887 od; 1888 Add(row,[e,t]); 1889 fsa.sparseTable[s]:=Set(row); 1890 fi; 1891 Unbind(fsa.backTable); 1892 RemoveSet(fsa.flags,"BFS"); 1893 RemoveSet(fsa.flags,"minimized"); 1894end; 1895 1896############################################################################# 1897## 1898#F DeleteEdgeFSA(<fsa>,<e>,<s>,<t>) . . . . . . . deletes edge from an fsa 1899## DeleteEdgeFSA deletes an edge with source <s>, label <e> and target <t> 1900## from the fsa <fsa> (if there is one). 1901## <s> and <t> can be either numbers or names of states (but both the same), 1902## and <e> a number or name of an edge-label. 1903## Public function. 1904DeleteEdgeFSA := function ( fsa, e, s, t ) 1905 local ng, row, term, subterm, j, dfa_check; 1906 if not IsInitializedFSA(fsa) then 1907 InitializeFSA(fsa); 1908 fi; 1909 if IsList(e) then 1910 ng := fsa.alphabet.base.size; 1911 e := (ElementNumberSR(fsa.alphabet,e[1])-1) * (ng+1) + 1912 ElementNumberSR(fsa.alphabet,e[2]); 1913 else 1914 e := ElementNumberSR(fsa.alphabet,e); 1915 fi; 1916 if e=false then 1917 Error("Second argument is not a valid edge number or label."); 1918 fi; 1919 s := ElementNumberSR(fsa.states,s); 1920 if s=false then 1921 Error("Third argument is not a valid state number or name."); 1922 fi; 1923 t := ElementNumberSR(fsa.states,t); 1924 if t=false then 1925 Error("Fourth argument is not a valid state number or name."); 1926 fi; 1927 if IsBound(fsa.denseDTable) and fsa.denseDTable[s][e] = t then 1928 fsa.denseDTable[s][e]:=0; 1929 fi; 1930 if IsBound(fsa.sparseTable) then 1931 row := fsa.sparseTable[s]; 1932 j := 1; 1933 while j <= Length(row) do 1934 term := row[j]; 1935 if term[1]=e and term[2] = t then 1936 DeleteListFSA(row,j); 1937 fi; 1938 j := j+1; 1939 od; 1940 fi; 1941 Unbind(fsa.backTable); 1942 RemoveSet(fsa.flags,"BFS"); 1943 RemoveSet(fsa.flags,"minimized"); 1944 RemoveSet(fsa.flags,"NFA"); 1945 # may have gone deterministic. 1946 RemoveSet(fsa.flags,"accessible"); 1947 RemoveSet(fsa.flags,"trim"); 1948end; 1949 1950############################################################################# 1951## 1952#F AcceptingStatesFSA(<fsa>) . . . the accepting states of <fsa> 1953## 1954AcceptingStatesFSA := function(fsa) 1955 return fsa.accepting; 1956end; 1957 1958############################################################################# 1959## 1960#F SetAcceptingFSA(<fsa>, <s>, <flag>) . . . set category of state <s> 1961## 1962## s should be a number or name of a state in fsa <fsa>. This state 1963## is made into an accepting state or not according to whether 1964## <flag> is true or false. 1965## 1966## Public function 1967SetAcceptingFSA := function(fsa, s, flag) 1968 if not IsInitializedFSA(fsa) then 1969 InitializeFSA(fsa); 1970 fi; 1971 s := ElementNumberSR(fsa.states,s); 1972 if s=false then 1973 Error("Second argument is not a valid state number or name."); 1974 fi; 1975 if flag = true then 1976 AddSet(fsa.accepting,s); 1977 else 1978 RemoveSet(fsa.accepting,s); 1979 fi; 1980 RemoveSet(fsa.flags,"trim"); 1981 RemoveSet(fsa.flags,"minimized"); 1982end; 1983 1984############################################################################# 1985## 1986#F InitialStatesFSA(<fsa>) . . . the initial states of <fsa> 1987## 1988InitialStatesFSA := function(fsa) 1989 return fsa.initial; 1990end; 1991 1992############################################################################# 1993## 1994#F SetInitialFSA(<fsa>, <s>, <flag>) . . . set initiality of state <s> 1995## 1996## s should be a number or name of a state in fsa <fsa>. This state 1997## is made into an initial state or not according to whether 1998## <flag> is true or false. 1999## 2000## Public function 2001SetInitialFSA := function(fsa, s, flag) 2002 if not IsInitializedFSA(fsa) then 2003 InitializeFSA(fsa); 2004 fi; 2005 s := ElementNumberSR(fsa.states,s); 2006 if s=false then 2007 Error("Second argument is not a valid state number or name."); 2008 fi; 2009 if flag = true then 2010 AddSet(fsa.initial,s); 2011 else 2012 RemoveSet(fsa.initial,s); 2013 fi; 2014 RemoveSet(fsa.flags,"trim"); 2015 RemoveSet(fsa.flags,"accessible"); 2016 RemoveSet(fsa.flags,"BFS"); 2017 RemoveSet(fsa.flags,"minimized"); 2018 if Length(fsa.initial) > 1 then 2019 RemoveSet(fsa.flags,"DFA"); 2020 else 2021 RemoveSet(fsa.flags,"NFA"); 2022 fi; 2023end; 2024 2025############################################################################# 2026## 2027#F IsAccessibleFSA(<fsa>) . . . . . . . test whether fsa is a accessible fsa 2028## 2029## An accessible FSA is one in which there is a word in the language 2030## leading to every state. 2031## The string "accessible" is inserted in the list of flags when it is known 2032## to be accessible. 2033## Note that "trim" implies "accessible". 2034## 2035## Public function. 2036IsAccessibleFSA := function ( fsa ) 2037 local ns, ne, ct, i, j, got, s, x, t; 2038 if not IsInitializedFSA(fsa) then 2039 InitializeFSA(fsa); 2040 fi; 2041 if "accessible" in fsa.flags or "trim" in fsa.flags then 2042 return true; 2043 fi; 2044 ns := fsa.states.size; 2045 ne := fsa.alphabet.size; 2046 # Find all accessible states i.e. states accessible from an initial state. 2047 got := ShallowCopy(fsa.initial); 2048 ct := Length(got); 2049 i := 1; 2050 while i <= ct do 2051 s := got[i]; 2052 for j in [0..ne] do 2053 x := TargetsFSA(fsa,j,s); 2054 for t in x do 2055 if t>0 and not t in got then 2056 ct := ct+1; 2057 got[ct] := t; 2058 fi; 2059 od; 2060 od; 2061 i := i+1; 2062 od; 2063 if ct <> ns then 2064 # there are some inaccessible states, so fsa is not accessible. 2065 return false; 2066 fi; 2067 2068 AddSet(fsa.flags,"accessible"); 2069 return true; 2070end; 2071 2072############################################################################# 2073## 2074#F AccessibleFSA(<fsa>) . . . . . . . replace fsa by an accessible fsa 2075## 2076## AccessibleFSA(<fsa>) removes non-accessible from 2077## <fsa> to make it accessible, without changing the accepted language. 2078## An accessible FSA is one in which there is a word in the language 2079## leading to every state. 2080## The string "accessible" is inserted in the list of flags when it is known 2081## to be accessible. 2082## 2083## Public function. 2084AccessibleFSA := function ( fsa ) 2085 local ns, ne, ct, i, j, got, s, x, t; 2086 if not IsInitializedFSA(fsa) then 2087 InitializeFSA(fsa); 2088 fi; 2089 if "accessible" in fsa.flags or "trim" in fsa.flags then 2090 return; 2091 fi; 2092 ns := fsa.states.size; 2093 ne := fsa.alphabet.size; 2094 # Find all accessible states i.e. states accessible from an initial state. 2095 got := ShallowCopy(fsa.initial); 2096 ct := Length(got); 2097 i := 1; 2098 while i <= ct do 2099 s := got[i]; 2100 for j in [0..ne] do 2101 x := TargetsFSA(fsa,j,s); 2102 for t in x do 2103 if t>0 and not t in got then 2104 ct := ct+1; 2105 got[ct] := t; 2106 fi; 2107 od; 2108 od; 2109 i := i+1; 2110 od; 2111 if ct <> ns then 2112 # there are some inaccessible states, so remove them - because we can only 2113 # remove the last state, we have to work from the back. 2114 for s in Reversed([1..ns]) do 2115 if not s in got then 2116 if s<ns then 2117 PermuteStatesFSA(fsa,(s,ns)); 2118 fi; 2119 DeleteStateFSA(fsa); 2120 ns := ns-1; 2121 fi; 2122 od; 2123 fi; 2124 2125 AddSet(fsa.flags,"accessible"); 2126end; 2127 2128############################################################################# 2129## 2130#F IsTrimFSA(<fsa>) . . . . . . . test whether fsa is a trim fsa 2131## 2132## A trim FSA is one in which there is an accepted word in the language 2133## through every state. 2134## The string "trim" is inserted in the list of flags when it is known 2135## to be trim. 2136## 2137## Public function. 2138IsTrimFSA := function ( fsa ) 2139 local ns, ne, ct, i, j, got, s, x, t; 2140 if not IsInitializedFSA(fsa) then 2141 InitializeFSA(fsa); 2142 fi; 2143 if "trim" in fsa.flags then 2144 return true; 2145 fi; 2146 ns := fsa.states.size; 2147 ne := fsa.alphabet.size; 2148 # First find all accessible states 2149 # i.e. states accessible from an initial state. 2150 got := ShallowCopy(fsa.initial); 2151 ct := Length(got); 2152 i := 1; 2153 while i <= ct do 2154 s := got[i]; 2155 for j in [0..ne] do 2156 x := TargetsFSA(fsa,j,s); 2157 for t in x do 2158 if t>0 and not t in got then 2159 ct := ct+1; 2160 got[ct] := t; 2161 fi; 2162 od; 2163 od; 2164 i := i+1; 2165 od; 2166 if ct <> ns then 2167 # there are some inaccessible states, so fsa is not trim. 2168 return false; 2169 fi; 2170 2171 # Next find all co-accessible states 2172 # i.e. states from which a path starts to an accepting state 2173 got := ShallowCopy(fsa.accepting); 2174 ct := Length(got); 2175 i := 1; 2176 while i <= ct do 2177 s := got[i]; 2178 for j in [0..ne] do 2179 x := SourcesFSA(fsa,j,s); 2180 for t in x do 2181 if not t in got then 2182 ct := ct+1; 2183 got[ct] := t; 2184 fi; 2185 od; 2186 od; 2187 i := i+1; 2188 od; 2189 if ct <> ns then 2190 # there are some non co-accessible states, so fsa is not trim. 2191 return false; 2192 fi; 2193 AddSet(fsa.flags,"trim"); 2194 return true; 2195end; 2196 2197############################################################################# 2198## 2199#F TrimFSA(<fsa>) . . . . . . . replace fsa by a trim fsa 2200## 2201## TrimFSA(<fsa>) removes non-accessible and non-coaccessible states from 2202## <fsa> to make it trim, without changing the accepted language. 2203## A trim FSA is one in which there is an accepted word in the language 2204## through every state. 2205## The string "trim" is inserted in the list of flags when it is known 2206## to be trim. 2207## (Removing the non-coaccessible states cannot possibly make any other 2208## states non-accessible, so there is no need to repeat the process.) 2209## 2210## Public function. 2211TrimFSA := function ( fsa ) 2212 local ns, ne, ct, i, j, got, s, x, t; 2213 if not IsInitializedFSA(fsa) then 2214 InitializeFSA(fsa); 2215 fi; 2216 if "trim" in fsa.flags then 2217 return; 2218 fi; 2219 ns := fsa.states.size; 2220 ne := fsa.alphabet.size; 2221 # First find all accessible states 2222 # i.e. states accessible from an initial state. 2223 got := ShallowCopy(fsa.initial); 2224 ct := Length(got); 2225 i := 1; 2226 while i <= ct do 2227 s := got[i]; 2228 for j in [0..ne] do 2229 x := TargetsFSA(fsa,j,s); 2230 for t in x do 2231 if t>0 and not t in got then 2232 ct := ct+1; 2233 got[ct] := t; 2234 fi; 2235 od; 2236 od; 2237 i := i+1; 2238 od; 2239 if ct <> ns then 2240 # there are some inaccessible states, so remove them - because we can only 2241 # remove the last state, we have to work from the back. 2242 for s in Reversed([1..ns]) do 2243 if not s in got then 2244 if s<ns then 2245 PermuteStatesFSA(fsa,(s,ns)); 2246 fi; 2247 DeleteStateFSA(fsa); 2248 ns := ns-1; 2249 fi; 2250 od; 2251 fi; 2252 2253 # Next find all co-accessible states 2254 # i.e. states from which a path starts to an accepting state 2255 got := ShallowCopy(fsa.accepting); 2256 ct := Length(got); 2257 i := 1; 2258 while i <= ct do 2259 s := got[i]; 2260 for j in [0..ne] do 2261 x := SourcesFSA(fsa,j,s); 2262 for t in x do 2263 if not t in got then 2264 ct := ct+1; 2265 got[ct] := t; 2266 fi; 2267 od; 2268 od; 2269 i := i+1; 2270 od; 2271 if ct <> ns then 2272 # there are some non-coaccessible states, so remove them - because we can only 2273 # remove the last state, we have to work from the back. 2274 for s in Reversed([1..ns]) do 2275 if not s in got then 2276 if s<ns then 2277 PermuteStatesFSA(fsa,(s,ns)); 2278 fi; 2279 DeleteStateFSA(fsa); 2280 ns := ns-1; 2281 fi; 2282 od; 2283 fi; 2284 AddSet(fsa.flags,"trim"); 2285 RemoveSet(fsa.flags,"accessible"); #trim implies accessible 2286 RemoveSet(fsa.flags,"BFS"); 2287end; 2288 2289############################################################################# 2290## 2291#F IsBFSFSA(<fsa>) . . . . . . . decide if fsa has the "bfs" property 2292## 2293## IsBFSFSA(<fsa>) decides if the fsa <fsa> has the breadth-first-search 2294## property. This means that it is accessible and, scanning the transition table 2295## along the states, one encounters the states in ascending numerical order. 2296## It is useful for comparing two fsa's. 2297## 2298## Public function. 2299IsBFSFSA := function ( fsa ) 2300 local ns, ne, ct, i, j, got, s, x, t; 2301 if not IsInitializedFSA(fsa) then 2302 InitializeFSA(fsa); 2303 fi; 2304 if "BFS" in fsa.flags then 2305 return true; 2306 fi; 2307 if not IsAccessibleFSA(fsa) then 2308 return false; 2309 fi; 2310 ns := fsa.states.size; 2311 ne := fsa.alphabet.size; 2312 if fsa.initial <> [1..Length(fsa.initial)] then 2313 return false; 2314 fi; 2315 ct := Length(fsa.initial); 2316 i := 1; 2317 while i <= ct do 2318 for j in [0..ne] do 2319 x := TargetsFSA(fsa,j,i); 2320 for t in x do 2321 if t>ct then 2322 if t <> ct+1 then 2323 return false; 2324 fi; 2325 ct := ct+1; 2326 if ct = ns then 2327 AddSet(fsa.flags,"BFS"); 2328 return true; 2329 fi; 2330 fi; 2331 od; 2332 od; 2333 i := i+1; 2334 od; 2335 2336end; 2337 2338############################################################################# 2339## 2340#F BFSFSA(<fsa>) . . . . . . . replace fsa by an fsa with the "bfs" property 2341## 2342## BFSFSA(<fsa>) replaces the fsa <fsa> by one with the same language 2343## that has the breadth-first-search property. This means that, scanning 2344## the transition table along the states, one encounters the states 2345## in ascending numerical order. It is useful for comparing two fsa's. 2346## It first makes the fsa trim, and then calculates the required 2347## state-permutation to achieve bfs-form, and calls PermuteStatesFSA. 2348## 2349## Public function. 2350BFSFSA := function ( fsa ) 2351 local ns, ne, ct, perm, i, j, got, s, x, t; 2352 if not IsInitializedFSA(fsa) then 2353 InitializeFSA(fsa); 2354 fi; 2355 if "BFS" in fsa.flags then 2356 return; 2357 fi; 2358 AccessibleFSA(fsa); 2359 ns := fsa.states.size; 2360 ne := fsa.alphabet.size; 2361 # We calculate the required permutation by building up the list perm 2362 # perm[i]=j means that the i-th state in the new order will be the 2363 # current j-th state - so we will call PermuteStatesFSA with the 2364 # inverse of perm. 2365 perm := ShallowCopy(fsa.initial); 2366 ct := Length(perm); 2367 i := 1; 2368 while i <= ct do 2369 s := perm[i]; 2370 for j in [0..ne] do 2371 x := TargetsFSA(fsa,j,s); 2372 for t in x do 2373 if t>0 and not t in perm then 2374 ct := ct+1; 2375 perm[ct] := t; 2376 fi; 2377 od; 2378 od; 2379 i := i+1; 2380 od; 2381 2382 perm := PermList(perm)^-1; 2383 PermuteStatesFSA(fsa,perm); 2384 AddSet(fsa.flags,"BFS"); 2385end; 2386 2387############################################################################# 2388## 2389#F PSizeFSA(<fsa>,[<state-list>]) . . . . . number of accepted paths of an fsa 2390## 2391## <fsa> should be a finite state automaton. 2392## The number of accepted paths is calculated and returned. 2393## WARNING: if there are epsilon transitions, then this is not necessarily 2394## the same as the size of the accepted language. 2395## If this is infinite, "infinity" is returned. 2396## If <fsa. is not trim, then a diagnostic will be printed. 2397## 2398## If the optional argument [<state-list>] is present, then the number 2399## of accepted strings starting at one of the states in <state-list> will 2400## be returned instead, BUT ONLY IF THE TOTAL ACCEPTED LANGUAGE IS FINITE. 2401## Public function. 2402PSizeFSA := function ( arg ) 2403 local fsa, slist, ns, ne, indeg, st, olist, nacc, total, ct, i, j, s, t, x; 2404 fsa := arg[1]; 2405 if Length(arg)=2 then 2406 slist := arg[2]; 2407 else 2408 slist := fsa.initial; 2409 fi; 2410 if not IsInitializedFSA(fsa) then 2411 InitializeFSA(fsa); 2412 fi; 2413 if not IsTrimFSA(fsa) then 2414 Print("#The fsa is not trim. Call TrimFSA(fsa) to make it trim.\n"); 2415 return "unknown"; 2416 fi; 2417 2418 ns := fsa.states.size; 2419 if ns=0 then 2420 return 0; 2421 fi; 2422 ne := fsa.alphabet.size; 2423 2424 # We first count the number of edges going into each vertex. 2425 indeg := []; 2426 for s in [1..ns] do 2427 indeg[s] := 0; 2428 od; 2429 for s in [1..ns] do 2430 for i in [0..ne] do 2431 x := TargetsFSA(fsa,i,s); 2432 for t in x do 2433 if t > 0 then indeg[t] := indeg[t]+1; fi; 2434 od; 2435 od; 2436 od; 2437 2438 # Now we seek to order the states so that if state s <= state t, then there 2439 # is no path from state t to state s. If this is not possible, then the 2440 # accepted language must be infinite. 2441 # The ordering will be in the list olist. 2442 2443 st := 0; 2444 for s in fsa.initial do 2445 if indeg[s]=0 then 2446 st := s; 2447 fi; 2448 od; 2449 if st = 0 then 2450 return infinity; 2451 fi; 2452 olist := [st]; 2453 ct := 1; 2454 i := 1; 2455 while i<=ct do 2456 s := olist[i]; 2457 for j in [0..ne] do 2458 x := TargetsFSA(fsa,j,s); 2459 for t in x do 2460 if t>0 then 2461 indeg[t] := indeg[t]-1; 2462 if indeg[t]=0 then 2463 ct := ct+1; 2464 olist[ct] := t; 2465 fi; 2466 fi; 2467 od; 2468 od; 2469 i := i+1; 2470 od; 2471 if ct <> ns then 2472 return infinity; 2473 fi; 2474 2475 # We have built the list, so the accepted language is finite. Now we work 2476 # backwards through the list, calculating the number of accepted words 2477 # starting at that state. 2478 # We store the numbers in nacc. 2479 2480 indeg := 0; #no longer needed. 2481 nacc := []; 2482 for i in Reversed([1..ns]) do 2483 s := olist[i]; 2484 nacc[s] := 0; 2485 for j in [0..ne] do 2486 x := TargetsFSA(fsa,j,s); 2487 for t in x do 2488 if t>0 then nacc[s] := nacc[s]+nacc[t]; fi; 2489 od; 2490 od; 2491 if s in fsa.accepting then 2492 nacc[s] := nacc[s]+1; 2493 fi; 2494 od; 2495 2496 # Finally we count the total number of accepted strings starting from 2497 # one of the states in slist. 2498 total := 0; 2499 for s in slist do 2500 total := total+nacc[s]; 2501 od; 2502 return total; 2503end; 2504 2505############################################################################# 2506## 2507#F LSizeDFA(<fsa>,[<initial state>]) . . . . . size of language of an fsa 2508## 2509## <fsa> should be a deterministic finite state automaton. 2510## The size of the accepted language is calculated. 2511## This should be quicker than PSizeFSA for deterministic automata. 2512## If the language is infinite, "infinity" is returned. 2513## If <fsa> is not trim, then a diagnostic will be printed. 2514## 2515## If the optional argument [<initial state>] is present, then the number 2516## of accepted strings starting at the state in <initial state> will 2517## be returned instead. 2518## 2519## Public function. 2520LSizeDFA := function ( arg ) 2521 local fsa, slist, ns, ne, ttable, indeg, st, olist, nacc, total, ct, 2522 i, j, s, t, accstates; 2523 fsa := arg[1]; 2524 if not IsInitializedFSA(fsa) then 2525 InitializeFSA(fsa); 2526 fi; 2527 if IsDeterministicFSA(fsa)=false then 2528 Error("First argument is not a dfa."); 2529 fi; 2530 if not IsTrimFSA(fsa) then 2531 Print("#The fsa is not trim. Call TrimFSA(fsa) to make it trim.\n"); 2532 return "unknown"; 2533 fi; 2534 2535 ns := fsa.states.size; 2536 if ns=0 then 2537 return 0; 2538 fi; 2539 ne := fsa.alphabet.size; 2540 2541 # First make sure that the dense deterministic table is present. 2542 DenseDTableFSA(fsa); 2543 ttable := fsa.denseDTable; 2544 2545 if Length(arg)=2 then 2546 slist := [arg[2]]; 2547 #In this case, we restrict attention to states that can be reached from 2548 #initial states. 2549 accstates := ShallowCopy(slist); 2550 ct := 1; 2551 i := 1; 2552 while i<=ct do 2553 s := accstates[i]; 2554 for j in [1..ne] do 2555 t := ttable[s][j]; 2556 if t > 0 and Position(accstates,t) = fail then 2557 ct := ct+1; 2558 Add(accstates,t); 2559 fi; 2560 od; 2561 i := i+1; 2562 od; 2563 else 2564 slist := fsa.initial; 2565 accstates := [1..ns]; 2566 fi; 2567 2568 # We first count the number of edges going into each vertex. 2569 indeg := []; 2570 for s in accstates do 2571 indeg[s] := 0; 2572 od; 2573 for s in accstates do 2574 for i in [1..ne] do 2575 t := ttable[s][i]; 2576 if t > 0 then indeg[t] := indeg[t]+1; fi; 2577 od; 2578 od; 2579 2580 # Now we seek to order the states so that if state s <= state t, then there 2581 # is no path from state t to state s. If this is not possible, then the 2582 # accepted language must be infinite. 2583 # The ordering will be in the list olist. 2584 2585 ns := Length(accstates); 2586 st := slist[1]; 2587 if indeg[st] <> 0 then 2588 return infinity; 2589 fi; 2590 olist := [st]; 2591 ct := 1; 2592 i := 1; 2593 while i<=ct do 2594 s := olist[i]; 2595 for j in [1..ne] do 2596 t := ttable[s][j]; 2597 if t>0 then 2598 indeg[t] := indeg[t]-1; 2599 if indeg[t]=0 then 2600 ct := ct+1; 2601 olist[ct] := t; 2602 fi; 2603 fi; 2604 od; 2605 i := i+1; 2606 od; 2607 if ct <> ns then 2608 return infinity; 2609 fi; 2610 2611 # We have built the list, so the accepted language is finite. Now we work 2612 # backwards through the list, calculating the number of accepted words 2613 # starting at that state. 2614 # We store the numbers in nacc. 2615 2616 indeg := 0; #no longer needed. 2617 nacc := []; 2618 for i in Reversed([1..ns]) do 2619 s := olist[i]; 2620 nacc[s] := 0; 2621 for j in [1..ne] do 2622 t := ttable[s][j]; 2623 if t>0 then nacc[s] := nacc[s]+nacc[t]; fi; 2624 od; 2625 if s in fsa.accepting then 2626 nacc[s] := nacc[s]+1; 2627 fi; 2628 od; 2629 2630 # Finally we count the total number of accepted strings starting from 2631 # one of the states in slist. 2632 total := 0; 2633 for s in slist do 2634 total := total+nacc[s]; 2635 od; 2636 return total; 2637end; 2638 2639############################################################################# 2640## 2641#F ListWordSR(<sr>,<w>) . . . . converts word in sr-generators to list 2642## 2643## ListWordSR converts the word <w> in the generators of the 2644## set-record <sr> to a list of integers. 2645## It only works if <sr> has type "identifiers" or "product". 2646## In the latter case, w should be a list of n words, where n is the 2647## "arity" of the set-record. 2648## 2649## Public function. 2650ListWordSR := function ( sr, w ) 2651 local i, j, l, n, wl, gens, prod, nv, tup, id; 2652 if not IsRecord(sr) or not IsBound(sr.type) or 2653 (sr.type <> "identifiers" and sr.type <> "product") then 2654 Error("First argument must be a set-record of type \"identifiers\"."); 2655 fi; 2656 prod := sr.type="product"; 2657 #We deal with product type separately. 2658 if prod then 2659 nv := sr.arity; 2660 if not IsList(w) or Length(w)<>nv then 2661 Error("Second argument must be a list of length sr.arity."); 2662 fi; 2663 l := 0; 2664 for i in w do 2665 if not IsWord(i) then 2666 Error("An entry in second argument is not a word."); 2667 fi; 2668 if Length(i)>l then 2669 l := Length(i); 2670 fi; 2671 od; 2672 wl := []; 2673 gens := sr.names; 2674 for i in [1..l] do 2675 tup := []; 2676 for j in [1..nv] do 2677 if i > Length(w[j]) then 2678 id := sr.padding; 2679 else 2680 id := Subword(w[j],i,i); 2681 fi; 2682 tup[j] := id; 2683 od; 2684 n := Position(gens,tup); 2685 if n=fail then 2686 Error("Invalid tuple in word."); 2687 fi; 2688 Add(wl,n); 2689 od; 2690 else 2691 if not IsWord(w) then 2692 Error("Second argument is not a word."); 2693 fi; 2694 l := Length(w); 2695 wl := []; 2696 gens := sr.names; 2697 for i in [1..l] do 2698 n := Position(gens,Subword(w,i,i)); 2699 if n=fail then 2700 Error("Invalid generator in word."); 2701 fi; 2702 Add(wl,n); 2703 od; 2704 fi; 2705 2706 return wl; 2707end; 2708 2709############################################################################# 2710## 2711#F WordListSR(<sr>,<wl>) . . . . converts list of sr-generators to word 2712## 2713## WordListSR converts the list of positive integers <wl> to a word 2714## in the generators of the rewriting system <sr>. Each integer in the 2715## list must be a valid generator number. 2716## This is the inverse function to ListWordSR. 2717## However, it works when sr has type "identifiers", "strings" or 2718## "products". 2719## 2720## Public function. 2721WordListSR := function ( sr, wl ) 2722 local i, j, l, w, gens, ng, nv, tup, IdWord; 2723 if sr.type="identifiers" and IsAssocWordWithOne(sr.names[1]) then 2724 IdWord := sr.names[1]^0; 2725 elif sr.type="words" and IsAssocWordWithOne(sr.alphabet[1]) then 2726 IdWord := sr.alphabet[1]^0; 2727 else IdWord:=false; 2728 fi; 2729 if not IsRecord(sr) or not IsBound(sr.type) or (sr.type <> "identifiers" 2730 and sr.type <> "words" and sr.type <> "strings" and 2731 sr.type <> "product") then 2732 Error("First argument must be a set-record of appropriate type."); 2733 fi; 2734 if not IsList(wl) then 2735 Error("Second argument is not a list."); 2736 fi; 2737 l := Length(wl); 2738 gens := sr.names; 2739 ng := sr.size; 2740 if l=0 then 2741 if sr.type="identifiers" or sr.type="words" then 2742 return IdWord; 2743 elif sr.type="strings" then 2744 return ""; 2745 else 2746 nv := sr.arity; 2747 return List([1..nv],i->IdWord); 2748 fi; 2749 else 2750 if not wl[1] in [1..ng] then 2751 Error("List element is not a valid generator number."); 2752 fi; 2753 w := ShallowCopy(gens[wl[1]]); 2754 fi; 2755 for i in [2..l] do 2756 if not wl[i] in [1..ng] then 2757 Error("List element is not a valid generator number."); 2758 fi; 2759 if sr.type="identifiers" or sr.type="words" then 2760 w := w*gens[wl[i]]; 2761 elif sr.type="strings" then 2762 w := Concatenation(w,gens[wl[i]]); 2763 else 2764 tup := gens[wl[i]]; 2765 nv := sr.arity; 2766 for j in [1..nv] do 2767 w[j] := w[j]*tup[j]; 2768 od; 2769 fi; 2770 od; 2771 2772 return w; 2773end; 2774 2775############################################################################# 2776## 2777#F LEnumerateDFA(<fsa>, <min>, <max>, [<is>] ) . . enumerate language of dfa 2778## 2779## <fsa> should be a deterministic finite state automaton. 2780## All words in the language accepted by <fsa> having length l 2781## satisfying <min> <= l <= <max> will be calculated and output in a 2782## list. These will be in lexacographical order. 2783## To get shortlex order, call SortLEnumerateDFA, which merely calls this 2784## function repeatedly. 2785## If the optional argument <is> is present, the initial state will be 2786## taken to be is. 2787## 2788## Public function. 2789LEnumerateDFA := function ( arg ) 2790 local fsa, min, max, is, words, convert, ttable, sr, ne, as, cword, 2791 cstatelist, clength, done, backtrack, cstate, fe, i; 2792 2793 if Length(arg) < 3 then 2794 Error("LEnumerateDFA must have at least three arguments"); 2795 fi; 2796 fsa:=arg[1]; min:=arg[2]; max:=arg[3]; 2797 if not IsInitializedFSA(fsa) then 2798 InitializeFSA(fsa); 2799 fi; 2800 if IsDeterministicFSA(fsa)=false then 2801 Error("First argument is not a dfa."); 2802 fi; 2803 if not IsInt(min) or not IsInt(max) or min<0 or min>max then 2804 Error("2nd and 3rd arguments must be nonneg. integers with 2nd <= 3rd."); 2805 fi; 2806 if fsa.initial=[] and Length(arg)=3 then 2807 return []; 2808 fi; 2809 if Length(arg)>=4 then is:=arg[4]; else is:=fsa.initial[1]; fi; 2810 2811 # The enumeration process itself will use lists of integers. These will be 2812 # converted to words if necessary and collected in the lists "words" for 2813 # output. 2814 2815 sr := fsa.alphabet; 2816 ne := sr.size; 2817 convert := sr.type="identifiers" or sr.type="strings" or sr.type="words" 2818 or sr.type="product"; 2819 2820 # Make sure that the dense deterministic table is present. 2821 DenseDTableFSA(fsa); 2822 ttable := fsa.denseDTable; 2823 2824 words := []; 2825 # cword will be the current word in the search (as a list of integers), 2826 # clength its current length, and cstatelist the list of states of fsa 2827 # arising when scanning the word. 2828 cword := []; 2829 cstatelist := [is]; 2830 clength := 0; 2831 2832 as := fsa.accepting; 2833 # Now the backtrack search begins 2834 done := false; 2835 while not done do 2836 # first check if we want the current word. 2837 if clength>=min and cstatelist[clength+1] in as then 2838 if convert then 2839 Add(words,WordListSR(sr,cword)); 2840 else 2841 Add(words,ShallowCopy(cword)); 2842 fi; 2843 fi; 2844 2845 # now proceed to next word in search. 2846 fe := 1; 2847 backtrack:=true; 2848 while backtrack and not done do 2849 if clength < max then 2850 cstate := cstatelist[clength+1]; 2851 i := fe; 2852 while backtrack and i<= ne do 2853 if ttable[cstate][i] > 0 then 2854 # found next node 2855 clength := clength+1; 2856 cword[clength] := i; 2857 cstatelist[clength+1] := ttable[cstate][i]; 2858 backtrack := false; 2859 fi; 2860 i := i+1; 2861 od; 2862 fi; 2863 if backtrack then 2864 if clength=0 then 2865 done := true; 2866 else 2867 fe := cword[clength]+1; 2868 Unbind(cword[clength]); 2869 clength := clength-1; 2870 fi; 2871 fi; 2872 od; 2873 od; 2874 2875 return words; 2876end; 2877 2878############################################################################# 2879## 2880#F SortLEnumerateDFA(<fsa>, <min>, <max> [,<is>]) 2881## . . . . . . . . . . . . . . . . . . . enumerate language of dfa and sort 2882## 2883## This function merely calls LEnumerateFSA repeatedly to get the shortlex 2884## order of output. 2885## 2886## If the optional argument <is> is present, the initial state will be 2887## taken to be is. 2888## 2889## Public function. 2890SortLEnumerateDFA := function ( arg ) 2891 local fsa, min, max, is, words, i; 2892 2893 if Length(arg) < 3 then 2894 Error("SortLEnumerateDFA must have at least three arguments"); 2895 fi; 2896 fsa:=arg[1]; min:=arg[2]; max:=arg[3]; 2897 2898 words := []; 2899 for i in [min..max] do 2900 if Length(arg)=3 then 2901 words := Concatenation(words,LEnumerateDFA(fsa,i,i)); 2902 else 2903 is := arg[4]; 2904 words := Concatenation(words,LEnumerateDFA(fsa,i,i,is)); 2905 fi; 2906 od; 2907 2908 return words; 2909end; 2910 2911############################################################################# 2912## 2913#F SizeLEnumerateDFA(<fsa>, <min>, <max> [,<is>]) 2914## . . . . . . . . . . . . . . . . . . . size of enumerated language of dfa 2915## 2916## <fsa> should be a deterministic finite state automaton. 2917## This is like LEnumerateFSA, but only the number of accepted words is 2918## output. 2919## 2920## If the optional argument <is> is present, the initial state will be 2921## taken to be is. 2922## 2923## Public function. 2924SizeLEnumerateDFA := function ( arg ) 2925 local fsa, min, max, is, nwords, ttable, sr, ne, as, cword, cstatelist, 2926 clength, done, backtrack, cstate, fe, i; 2927 2928 if Length(arg) < 3 then 2929 Error("LEnumerateDFA must have at least three arguments"); 2930 fi; 2931 fsa:=arg[1]; min:=arg[2]; max:=arg[3]; 2932 2933 if not IsInitializedFSA(fsa) then 2934 InitializeFSA(fsa); 2935 fi; 2936 if IsDeterministicFSA(fsa)=false then 2937 Error("First argument is not a dfa."); 2938 fi; 2939 if not IsInt(min) or not IsInt(max) or min<0 or min>max then 2940 Error("2nd and 3rd arguments must be nonneg. integers with 2nd <= 3rd."); 2941 fi; 2942 if fsa.initial=[] and Length(arg)=3 then 2943 return 0; 2944 fi; 2945 2946 if Length(arg)>=4 then is:=arg[4]; else is:=fsa.initial[1]; fi; 2947 2948 # The enumeration process itself will use lists of integers. 2949 2950 sr := fsa.alphabet; 2951 ne := sr.size; 2952 2953 # Make sure that the dense deterministic table is present. 2954 DenseDTableFSA(fsa); 2955 ttable := fsa.denseDTable; 2956 2957 nwords := 0; 2958 # cword will be the current word in the search (as a list of integers), 2959 # clength its current length, and cstatelist the list of states of fsa 2960 # arising when scanning the word. 2961 cword := []; 2962 cstatelist := [is]; 2963 clength := 0; 2964 2965 as := fsa.accepting; 2966 # Now the backtrack search begins 2967 done := false; 2968 while not done do 2969 # first check if we want the current word. 2970 if clength>=min and cstatelist[clength+1] in as then 2971 nwords := nwords+1; 2972 fi; 2973 2974 # now proceed to next word in search. 2975 fe := 1; 2976 backtrack:=true; 2977 while backtrack and not done do 2978 if clength < max then 2979 cstate := cstatelist[clength+1]; 2980 i := fe; 2981 while backtrack and i<= ne do 2982 if ttable[cstate][i] > 0 then 2983 # found next node 2984 clength := clength+1; 2985 cword[clength] := i; 2986 cstatelist[clength+1] := ttable[cstate][i]; 2987 backtrack := false; 2988 fi; 2989 i := i+1; 2990 od; 2991 fi; 2992 if backtrack then 2993 if clength=0 then 2994 done := true; 2995 else 2996 fe := cword[clength]+1; 2997 Unbind(cword[clength]); 2998 clength := clength-1; 2999 fi; 3000 fi; 3001 od; 3002 od; 3003 3004 return nwords; 3005end; 3006 3007############################################################################# 3008## 3009#F SubstitutedListFSA(<l>,<pos1>,<pos2>,<ss>) 3010## . . . . . . . . . substitute a new list in a list 3011## 3012## The part of the list <l> form position <pos1> to <pos2> is substituted by 3013## the list <ss>. This is easy if lengths are equal. Otherwise not so. 3014## 3015## Private function. 3016SubstitutedListFSA := function(l,pos1,pos2,ss) 3017 local len, oldlen, newlen, diff, i; 3018 len := Length(l); 3019 oldlen := pos2-pos1+1; newlen := Length(ss); 3020 if oldlen=newlen then 3021 l{[pos1..pos2]} := ss; 3022 elif newlen<oldlen then 3023 diff := oldlen-newlen; 3024 for i in [pos2+1..len] do l[i-diff]:=l[i]; od; 3025 for i in [len-diff+1..len] do Unbind(l[i]); od; 3026 l{[pos1..pos2-diff]} := ss; 3027 elif newlen>oldlen then 3028 diff := newlen-oldlen; 3029 for i in Reversed([pos2+1..len]) do l[i+diff] := l[i]; od; 3030 l{[pos1..pos2+diff]} := ss; 3031 fi; 3032end; 3033 3034############################################################################# 3035## 3036#F DeterminizeFSA(<fsa>) . . . call external program to determinize fsa <fsa> 3037## 3038## Determinized FSA is returned. 3039## Public function. 3040DeterminizeFSA := function(fsa) 3041 local callstring, filename, alph; 3042 3043 if not IsInitializedFSA(fsa) then 3044 InitializeFSA(fsa); 3045 fi; 3046 if IsDeterministicFSA(fsa) then 3047 return fsa; 3048 fi; 3049 ## We replace the alphabet by simple-type alphabet for the 3050 ## I/O phase. 3051 alph := fsa.alphabet; 3052 fsa.alphabet := rec(type:="simple",size:=alph.size); 3053 InitializeSR(fsa.alphabet); 3054 filename := Concatenation(_KBTmpFileName,".fsafordet"); 3055 WriteFSA(fsa,"_FSA",filename,";"); 3056 callstring := Filename(_KBExtDir,"nfadeterminize"); 3057 if InfoLevel(InfoFSA)=0 then 3058 callstring := Concatenation(callstring," -silent "); 3059 elif InfoLevel(InfoFSA)>1 then 3060 callstring := Concatenation(callstring," -v "); 3061 fi; 3062 callstring := Concatenation(callstring," ",filename); 3063 Info(InfoFSA,1,"Calling fsa determinization program.\n"); 3064 Info(InfoFSA,3," ",callstring); 3065 Exec(callstring); 3066 Info(InfoFSA,1,"External fsa determinization program complete.\n"); 3067 if not READ(Concatenation(_KBTmpFileName,".fsafordet.determinize")) then 3068 Error("Could not open determinized fsa file"); 3069 fi; 3070 Exec(Concatenation("/bin/rm -f ",_KBTmpFileName,".fsafordet*")); 3071 InitializeFSA(_FSA_determinize); 3072 fsa.alphabet := alph; 3073 _FSA_min.alphabet := alph; 3074 return _FSA_determinize; 3075end; 3076 3077############################################################################# 3078## 3079#F MinimizeFSA(<fsa>) . . . call external program to minimize fsa <fsa> 3080## 3081## Minimized FSA is returned. 3082## Public function. 3083MinimizeFSA := function(fsa) 3084 local callstring, filename, alph; 3085 3086 if not IsInitializedFSA(fsa) then 3087 InitializeFSA(fsa); 3088 fi; 3089 if IsDeterministicFSA(fsa)=false then 3090 Error("First argument is not a dfa."); 3091 fi; 3092 ## We replace the alphabet by simple-type alphabet for the 3093 ## I/O phase. 3094 alph := fsa.alphabet; 3095 fsa.alphabet := rec(type:="simple",size:=alph.size); 3096 InitializeSR(fsa.alphabet); 3097 filename := Concatenation(_KBTmpFileName,".fsaformin"); 3098 WriteFSA(fsa,"_FSA",filename,";"); 3099 callstring := Filename(_KBExtDir,"fsamin"); 3100 if InfoLevel(InfoFSA)=0 then 3101 callstring := Concatenation(callstring," -silent "); 3102 elif InfoLevel(InfoFSA)>1 then 3103 callstring := Concatenation(callstring," -v "); 3104 fi; 3105 callstring := Concatenation(callstring," ",filename); 3106 Info(InfoFSA,1,"Calling fsa minimization program.\n"); 3107 Info(InfoFSA,3," ",callstring); 3108 Exec(callstring); 3109 Info(InfoFSA,1,"External fsa minimization program complete.\n"); 3110 if not READ(Concatenation(_KBTmpFileName,".fsaformin.min")) then 3111 Error("Could not open minimized fsa file"); 3112 fi; 3113 Exec(Concatenation("/bin/rm -f ",_KBTmpFileName,".fsaformin*")); 3114 InitializeFSA(_FSA_min); 3115 fsa.alphabet := alph; 3116 _FSA_min.alphabet := alph; 3117 return _FSA_min; 3118end; 3119 3120############################################################################# 3121## 3122#F NotFSA(<fsa>) . . . call external program to negate fsa <fsa> 3123## 3124## An FSA is returned in which a word is accepted iff it is not 3125## accepted by <fsa>. 3126## Public function. 3127NotFSA := function(fsa) 3128 local callstring, filename, alph; 3129 3130 if not IsInitializedFSA(fsa) then 3131 InitializeFSA(fsa); 3132 fi; 3133 if IsDeterministicFSA(fsa)=false then 3134 Error("First argument is not a dfa."); 3135 fi; 3136 ## We replace the alphabet by simple-type alphabet for the 3137 ## I/O phase. 3138 alph := fsa.alphabet; 3139 fsa.alphabet := rec(type:="simple",size:=alph.size); 3140 InitializeSR(fsa.alphabet); 3141 filename := Concatenation(_KBTmpFileName,".fsafornot"); 3142 WriteFSA(fsa,"_FSA",filename,";"); 3143 callstring := Filename(_KBExtDir,"fsanot"); 3144 if InfoLevel(InfoFSA)=0 then 3145 callstring := Concatenation(callstring," -silent "); 3146 elif InfoLevel(InfoFSA)>1 then 3147 callstring := Concatenation(callstring," -v "); 3148 fi; 3149 callstring := Concatenation(callstring," ",filename); 3150 Info(InfoFSA,1,"Calling fsa `not' program.\n"); 3151 Info(InfoFSA,3," ",callstring); 3152 Exec(callstring); 3153 Info(InfoFSA,1,"External fsa `not' program complete.\n"); 3154 if not READ(Concatenation(_KBTmpFileName,".fsafornot.not")) then 3155 Error("Could not open `not' fsa file"); 3156 fi; 3157 Exec(Concatenation("/bin/rm -f ",_KBTmpFileName,".fsafornot*")); 3158 InitializeFSA(_FSA_not); 3159 fsa.alphabet := alph; 3160 _FSA_not.alphabet := alph; 3161 return _FSA_not; 3162end; 3163 3164############################################################################# 3165## 3166#F StarFSA(<fsa>) . . . call external program to star fsa <fsa> 3167## 3168## An FSA is returned in which a word is accepted iff it is a 3169## concatenation of 0 or more words accepted by <fsa>. 3170## Public function. 3171StarFSA := function(fsa) 3172 local callstring, filename, alph; 3173 3174 if not IsInitializedFSA(fsa) then 3175 InitializeFSA(fsa); 3176 fi; 3177 if IsDeterministicFSA(fsa)=false then 3178 Error("First argument is not a dfa."); 3179 fi; 3180 ## We replace the alphabet by simple-type alphabet for the 3181 ## I/O phase. 3182 alph := fsa.alphabet; 3183 fsa.alphabet := rec(type:="simple",size:=alph.size); 3184 InitializeSR(fsa.alphabet); 3185 filename := Concatenation(_KBTmpFileName,".fsaforstar"); 3186 WriteFSA(fsa,"_FSA",filename,";"); 3187 callstring := Filename(_KBExtDir,"fsastar"); 3188 if InfoLevel(InfoFSA)=0 then 3189 callstring := Concatenation(callstring," -silent "); 3190 elif InfoLevel(InfoFSA)>1 then 3191 callstring := Concatenation(callstring," -v "); 3192 fi; 3193 callstring := Concatenation(callstring," ",filename); 3194 Info(InfoFSA,1,"Calling fsa `star' program.\n"); 3195 Info(InfoFSA,3," ",callstring); 3196 Exec(callstring); 3197 Info(InfoFSA,1,"External fsa `star' program complete.\n"); 3198 if not READ(Concatenation(_KBTmpFileName,".fsaforstar.star")) then 3199 Error("Could not open `star' fsa file"); 3200 fi; 3201 Exec(Concatenation("/bin/rm -f ",_KBTmpFileName,".fsaforstar*")); 3202 InitializeFSA(_FSA_star); 3203 fsa.alphabet := alph; 3204 _FSA_star.alphabet := alph; 3205 return _FSA_star; 3206end; 3207 3208############################################################################# 3209## 3210#F ReverseFSA(<fsa>,[<subsets>]) . call external program to reverse fsa <fsa> 3211## 3212## An FSA is returned in which a word is accepted iff it is the reverses 3213## of a word accepted by <fsa>. 3214## If the optional 'subsets' argument is true, then the states of the 3215## returned fsa are given labels specifying the subsets of the original 3216## state-set to which they correspond. 3217## Public function. 3218ReverseFSA := function(arg) 3219 local callstring, filename, alph, fsa, subsets; 3220 3221 fsa := arg[1]; 3222 if not IsInitializedFSA(fsa) then 3223 InitializeFSA(fsa); 3224 fi; 3225 if IsDeterministicFSA(fsa)=false then 3226 Error("First argument is not a dfa."); 3227 fi; 3228 subsets := false; 3229 if Length(arg)>=2 and arg[2]=true then 3230 subsets:=true; 3231 fi; 3232 ## We replace the alphabet by simple-type alphabet for the 3233 ## I/O phase. 3234 alph := fsa.alphabet; 3235 fsa.alphabet := rec(type:="simple",size:=alph.size); 3236 InitializeSR(fsa.alphabet); 3237 filename := Concatenation(_KBTmpFileName,".fsaforreverse"); 3238 WriteFSA(fsa,"_FSA",filename,";"); 3239 callstring := Filename(_KBExtDir,"fsareverse"); 3240 if InfoLevel(InfoFSA)=0 then 3241 callstring := Concatenation(callstring," -silent "); 3242 elif InfoLevel(InfoFSA)>1 then 3243 callstring := Concatenation(callstring," -v "); 3244 fi; 3245 if subsets then 3246 callstring := Concatenation(callstring," -s "); 3247 fi; 3248 callstring := Concatenation(callstring," ",filename); 3249 Info(InfoFSA,1,"Calling fsa `reverse' program.\n"); 3250 Info(InfoFSA,3," ",callstring); 3251 Exec(callstring); 3252 Info(InfoFSA,1,"External fsa `reverse' program complete.\n"); 3253 if not READ(Concatenation(_KBTmpFileName,".fsaforreverse.reverse")) then 3254 Error("Could not open `reverse' fsa file"); 3255 fi; 3256 Exec(Concatenation("/bin/rm -f ",_KBTmpFileName,".fsaforreverse*")); 3257 InitializeFSA(_FSA_reverse); 3258 fsa.alphabet := alph; 3259 _FSA_reverse.alphabet := alph; 3260 return _FSA_reverse; 3261end; 3262 3263############################################################################# 3264## 3265#F ExistsFSA(<fsa>) . . . call external program to 'exist' fsa <fsa> 3266## 3267## Here <fsa> must be a 2-variable FSA. 3268## An FSA is returned in which a word w1 is accepted iff 3269## (w1,w2) is accepted by <fsa> for some word w2. 3270## Public function. 3271ExistsFSA := function(fsa) 3272 local callstring, filename, alph; 3273 3274 if not IsInitializedFSA(fsa) then 3275 InitializeFSA(fsa); 3276 fi; 3277 if IsDeterministicFSA(fsa)=false then 3278 Error("First argument is not a dfa."); 3279 fi; 3280 if fsa.alphabet.type <> "product" or fsa.alphabet.arity <> 2 then 3281 Error("Alphabet of argument is not 2-variable."); 3282 fi; 3283 ## We replace the base alphabet by simple-type alphabet for the 3284 ## I/O phase. 3285 alph := fsa.alphabet.base; 3286 fsa.alphabet.base := rec(type:="simple",size:=alph.size); 3287 InitializeSR(fsa.alphabet.base); 3288 filename := Concatenation(_KBTmpFileName,".fsaforexists"); 3289 WriteFSA(fsa,"_FSA",filename,";"); 3290 callstring := Filename(_KBExtDir,"fsaexists"); 3291 if InfoLevel(InfoFSA)=0 then 3292 callstring := Concatenation(callstring," -silent "); 3293 elif InfoLevel(InfoFSA)>1 then 3294 callstring := Concatenation(callstring," -v "); 3295 fi; 3296 callstring := Concatenation(callstring," ",filename); 3297 Info(InfoFSA,1,"Calling fsa `exists' program.\n"); 3298 Info(InfoFSA,3," ",callstring); 3299 Exec(callstring); 3300 Info(InfoFSA,1,"External fsa `exists' program complete.\n"); 3301 if not READ(Concatenation(_KBTmpFileName,".fsaforexists.exists")) then 3302 Error("Could not open `exists' fsa file"); 3303 fi; 3304 Exec(Concatenation("/bin/rm -f ",_KBTmpFileName,".fsaforexists*")); 3305 InitializeFSA(_FSA_exists); 3306 fsa.alphabet.base := alph; 3307 _FSA_exists.alphabet := alph; 3308 return _FSA_exists; 3309end; 3310 3311############################################################################# 3312## 3313#F SwapCoordsFSA(<fsa>) 3314## . . . call external program to swap co-ordinates of fsa <fsa> 3315## 3316## Here <fsa> must be a 2-variable FSA. 3317## An 2-variable FSA is returned in which (w1,w2) is accepted iff 3318## (w2,w1) is accepted by <fsa>. 3319## Public function. 3320SwapCoordsFSA := function(fsa) 3321 local callstring, filename, alph; 3322 3323 if not IsInitializedFSA(fsa) then 3324 InitializeFSA(fsa); 3325 fi; 3326 if IsDeterministicFSA(fsa)=false then 3327 Error("First argument is not a dfa."); 3328 fi; 3329 if fsa.alphabet.type <> "product" or fsa.alphabet.arity <> 2 then 3330 Error("Alphabet of argument is not 2-variable."); 3331 fi; 3332 ## We replace the base alphabet by simple-type alphabet for the 3333 ## I/O phase. 3334 alph := fsa.alphabet.base; 3335 fsa.alphabet.base := rec(type:="simple",size:=alph.size); 3336 InitializeSR(fsa.alphabet.base); 3337 filename := Concatenation(_KBTmpFileName,".fsaforswap_coords"); 3338 WriteFSA(fsa,"_FSA",filename,";"); 3339 callstring := Filename(_KBExtDir,"fsaswapcoords"); 3340 if InfoLevel(InfoFSA)=0 then 3341 callstring := Concatenation(callstring," -silent "); 3342 elif InfoLevel(InfoFSA)>1 then 3343 callstring := Concatenation(callstring," -v "); 3344 fi; 3345 callstring := Concatenation(callstring," ",filename," ",filename,".o"); 3346 #Print(callstring,"\n"); 3347 Info(InfoFSA,1,"Calling fsa `swap_coords' program.\n"); 3348 Info(InfoFSA,3," ",callstring); 3349 Exec(callstring); 3350 Info(InfoFSA,1,"External fsa `swap_coords' program complete.\n"); 3351 if not 3352 READ(Concatenation(_KBTmpFileName,".fsaforswap_coords.o")) 3353 then Error("Could not open `swapcoords' fsa file"); 3354 fi; 3355 Exec(Concatenation("/bin/rm -f ",_KBTmpFileName,".fsaforswap_coords*")); 3356 InitializeFSA(_FSA_swap_coords); 3357 fsa.alphabet.base := alph; 3358 _FSA_swap_coords.alphabet.base := alph; 3359 return _FSA_swap_coords; 3360end; 3361 3362############################################################################# 3363## 3364#F AndFSA(<fsa1>, <fsa2>) . . call external program to and fsa's <fsa1>,<fsa2> 3365## 3366## An FSA is returned in which a word is accepted iff it is a 3367## accepted by both of the fsa's <fsa1> and <fsa2>. 3368## Public function. 3369AndFSA := function(fsa1, fsa2) 3370 local callstring, filename1, filename2, filename3, alph; 3371 3372 if not IsInitializedFSA(fsa1) then 3373 InitializeFSA(fsa1); 3374 fi; 3375 if not IsInitializedFSA(fsa2) then 3376 InitializeFSA(fsa2); 3377 fi; 3378 if IsDeterministicFSA(fsa1)=false or IsDeterministicFSA(fsa2)=false then 3379 Error("One of the arguments is not a dfa."); 3380 fi; 3381 ## We replace the alphabet by simple-type alphabet for the 3382 ## I/O phase. 3383 alph := fsa1.alphabet; 3384 if alph <> fsa2.alphabet then 3385 Error("Arguments have different alphabets."); 3386 fi; 3387 fsa1.alphabet := rec(type:="simple",size:=alph.size); 3388 InitializeSR(fsa1.alphabet); 3389 fsa2.alphabet := rec(type:="simple",size:=alph.size); 3390 InitializeSR(fsa2.alphabet); 3391 filename1 := Concatenation(_KBTmpFileName,".fsaforand1"); 3392 filename2 := Concatenation(_KBTmpFileName,".fsaforand2"); 3393 filename3 := Concatenation(_KBTmpFileName,".fsaforand3"); 3394 WriteFSA(fsa1,"_FSA",filename1,";"); 3395 WriteFSA(fsa2,"_FSA",filename2,";"); 3396 callstring := Filename(_KBExtDir,"fsaand"); 3397 if InfoLevel(InfoFSA)=0 then 3398 callstring := Concatenation(callstring," -silent "); 3399 elif InfoLevel(InfoFSA)>1 then 3400 callstring := Concatenation(callstring," -v "); 3401 fi; 3402 callstring := Concatenation(callstring," ", 3403 filename1," ",filename2," ",filename3); 3404 Info(InfoFSA,1,"Calling fsa `and' program.\n"); 3405 Info(InfoFSA,3," ",callstring); 3406 Exec(callstring); 3407 Info(InfoFSA,1,"External fsa `and' program complete.\n"); 3408 if not READ(filename3) then 3409 Error("Could not open `and' fsa file"); 3410 fi; 3411 Exec(Concatenation("/bin/rm -f ",_KBTmpFileName,".fsaforand*")); 3412 InitializeFSA(_FSA_and); 3413 fsa1.alphabet := alph; 3414 fsa2.alphabet := alph; 3415 _FSA_and.alphabet := alph; 3416 return _FSA_and; 3417end; 3418 3419############################################################################# 3420## 3421#F OrFSA(<fsa1>, <fsa2>) . . . call external program to or fsa's <fsa1>,<fsa2> 3422## 3423## An FSA is returned in which a word is accepted iff it is a 3424## accepted by either of the fsa's <fsa1> or <fsa2>. 3425## Public function. 3426OrFSA := function(fsa1, fsa2) 3427 local callstring, filename1, filename2, filename3, alph; 3428 3429 if not IsInitializedFSA(fsa1) then 3430 InitializeFSA(fsa1); 3431 fi; 3432 if not IsInitializedFSA(fsa2) then 3433 InitializeFSA(fsa2); 3434 fi; 3435 if IsDeterministicFSA(fsa1)=false or IsDeterministicFSA(fsa2)=false then 3436 Error("One of the arguments is not a dfa."); 3437 fi; 3438 ## We replace the alphabet by simple-type alphabet for the 3439 ## I/O phase. 3440 alph := fsa1.alphabet; 3441 if alph <> fsa2.alphabet then 3442 Error("Arguments have different alphabets."); 3443 fi; 3444 fsa1.alphabet := rec(type:="simple",size:=alph.size); 3445 InitializeSR(fsa1.alphabet); 3446 fsa2.alphabet := rec(type:="simple",size:=alph.size); 3447 InitializeSR(fsa2.alphabet); 3448 filename1 := Concatenation(_KBTmpFileName,".fsaforor1"); 3449 filename2 := Concatenation(_KBTmpFileName,".fsaforor2"); 3450 filename3 := Concatenation(_KBTmpFileName,".fsaforor3"); 3451 WriteFSA(fsa1,"_FSA",filename1,";"); 3452 WriteFSA(fsa2,"_FSA",filename2,";"); 3453 callstring := Filename(_KBExtDir,"fsaor"); 3454 if InfoLevel(InfoFSA)=0 then 3455 callstring := Concatenation(callstring," -silent "); 3456 elif InfoLevel(InfoFSA)>1 then 3457 callstring := Concatenation(callstring," -v "); 3458 fi; 3459 callstring := Concatenation(callstring," ", 3460 filename1," ",filename2," ",filename3); 3461 Info(InfoFSA,1,"Calling fsa `or' program.\n"); 3462 Info(InfoFSA,3," ",callstring); 3463 Exec(callstring); 3464 Info(InfoFSA,1,"External fsa `or' program complete.\n"); 3465 if not READ(filename3) then 3466 Error("Could not open `or' fsa file"); 3467 fi; 3468 Exec(Concatenation("/bin/rm -f ",_KBTmpFileName,".fsaforor*")); 3469 InitializeFSA(_FSA_or); 3470 fsa1.alphabet := alph; 3471 fsa2.alphabet := alph; 3472 _FSA_or.alphabet := alph; 3473 return _FSA_or; 3474end; 3475 3476############################################################################# 3477## 3478#F ConcatFSA(<fsa1>, <fsa2>) 3479## . . . call external program to concat fsa's <fsa1>,<fsa2> 3480## 3481## An FSA is returned in which a word is accepted iff it is the concatenation 3482## of words accepted by the fsa's <fsa1> and <fsa2>. 3483## Public function. 3484ConcatFSA := function(fsa1, fsa2) 3485 local callstring, filename1, filename2, filename3, alph; 3486 3487 if not IsInitializedFSA(fsa1) then 3488 InitializeFSA(fsa1); 3489 fi; 3490 if not IsInitializedFSA(fsa2) then 3491 InitializeFSA(fsa2); 3492 fi; 3493 if IsDeterministicFSA(fsa1)=false or IsDeterministicFSA(fsa2)=false then 3494 Error("One of the arguments is not a dfa."); 3495 fi; 3496 ## We replace the alphabet by simple-type alphabet for the 3497 ## I/O phase. 3498 alph := fsa1.alphabet; 3499 if alph <> fsa2.alphabet then 3500 Error("Arguments have different alphabets."); 3501 fi; 3502 fsa1.alphabet := rec(type:="simple",size:=alph.size); 3503 InitializeSR(fsa1.alphabet); 3504 fsa2.alphabet := rec(type:="simple",size:=alph.size); 3505 InitializeSR(fsa2.alphabet); 3506 filename1 := Concatenation(_KBTmpFileName,".fsaforconcat1"); 3507 filename2 := Concatenation(_KBTmpFileName,".fsaforconcat2"); 3508 filename3 := Concatenation(_KBTmpFileName,".fsaforconcat3"); 3509 WriteFSA(fsa1,"_FSA",filename1,";"); 3510 WriteFSA(fsa2,"_FSA",filename2,";"); 3511 callstring := Filename(_KBExtDir,"fsaconcat"); 3512 if InfoLevel(InfoFSA)=0 then 3513 callstring := Concatenation(callstring," -silent "); 3514 elif InfoLevel(InfoFSA)>1 then 3515 callstring := Concatenation(callstring," -v "); 3516 fi; 3517 callstring := Concatenation(callstring," ", 3518 filename1," ",filename2," ",filename3); 3519 Info(InfoFSA,1,"Calling fsa `concat' program.\n"); 3520 Info(InfoFSA,3," ",callstring); 3521 Exec(callstring); 3522 Info(InfoFSA,1,"External fsa `concat' program complete.\n"); 3523 if not READ(filename3) then 3524 Error("Could not open `concat' fsa file"); 3525 fi; 3526 Exec(Concatenation("/bin/rm -f ",_KBTmpFileName,".fsaforconcat*")); 3527 InitializeFSA(_FSA_concat); 3528 fsa1.alphabet := alph; 3529 fsa2.alphabet := alph; 3530 _FSA_concat.alphabet := alph; 3531 return _FSA_concat; 3532end; 3533 3534############################################################################# 3535## 3536#F LanguagesEqualFSA(<fsa1>, <fsa2>) 3537## . . . decide whether <fsa1>, <fsa2> havbe the same language 3538## 3539## <fsa1> and <fsa2> must have the same alphabet, or an error results. 3540## true or false is returned according to whether <fsa1> and <fsa2> have 3541## the same language. 3542## 3543## Public function. 3544LanguagesEqualFSA := function(fsa1, fsa2) 3545 local mfsa1, mfsa2; 3546 if not IsInitializedFSA(fsa1) then 3547 InitializeFSA(fsa1); 3548 fi; 3549 if not IsInitializedFSA(fsa2) then 3550 InitializeFSA(fsa2); 3551 fi; 3552 if IsDeterministicFSA(fsa1)=false or IsDeterministicFSA(fsa2)=false then 3553 Error("One of the arguments is not a dfa."); 3554 fi; 3555 if fsa1.alphabet <> fsa2.alphabet then 3556 Error("Parameters do not have the same alphabet."); 3557 fi; 3558 3559 mfsa1 := MinimizeFSA(fsa1); 3560 mfsa2 := MinimizeFSA(fsa2); 3561 BFSFSA(mfsa1); 3562 BFSFSA(mfsa2); 3563 return DenseDTableFSA(mfsa1) = DenseDTableFSA(mfsa2); 3564end; 3565 3566############################################################################# 3567## 3568#F GrowthFSA(<fsa>) . . . call external program to find growth function 3569## of <fsa> 3570## 3571## A rational function is returned. The coefficient of x^n in this function is 3572## the number of words of length n in the language of <fsa>, 3573## 3574## Public function. 3575GrowthFSA := function(fsa) 3576 local callstring, filename, alph, gf; 3577 3578 if not IsInitializedFSA(fsa) then 3579 InitializeFSA(fsa); 3580 fi; 3581 if IsDeterministicFSA(fsa)=false then 3582 Error("First argument is not a dfa."); 3583 fi; 3584 ## We replace the alphabet by simple-type alphabet for the 3585 ## I/O phase. 3586 alph := fsa.alphabet; 3587 fsa.alphabet := rec(type:="simple",size:=alph.size); 3588 InitializeSR(fsa.alphabet); 3589 filename := Concatenation(_KBTmpFileName,".fsaforgrowth"); 3590 WriteFSA(fsa,"_FSA",filename,";"); 3591 callstring := Filename(_KBExtDir,"fsagrowth"); 3592 if InfoLevel(InfoFSA)>1 then 3593 callstring := Concatenation(callstring," -v "); 3594 fi; 3595 callstring := Concatenation(callstring," ",filename); 3596 Info(InfoFSA,1,"Calling fsa `growth' program.\n"); 3597 Info(InfoFSA,3," ",callstring); 3598 Exec(callstring); 3599 Info(InfoFSA,1,"External fsa `growth' program complete.\n"); 3600 gf := ReadAsFunction(Concatenation(_KBTmpFileName,".fsaforgrowth.growth")); 3601 Exec(Concatenation("/bin/rm -f ",_KBTmpFileName,".fsaforgrowth*")); 3602 fsa.alphabet := alph; 3603 if gf=fail then 3604 return fail; 3605 fi; 3606 return gf(); 3607end; 3608