1 /* file fsarevmid.c  18. 4. 96.
2  *
3  * 16.7.98. added fsa_miexists1, fsa_miexists2 to existentialise over
4  * the two variables of a 2-variable midfa machine.
5  *
6  * 11.9.97. wrote a different version of fsa_reverse
7  * called fsa_mireverse which * returns a labeled MIDFA with the
8  * singleton accept states of the input fsa as initial states.
9  *
10  * This file contains the routine fsa_reverse.
11  * It  takes a determinstic fsa F as input.
12  * fsa_reverse returns an fsa that accepts a word a_1a_2...a_n  if and only
13  * if F accepts a_n...a_2a_1.
14  */
15 
16 #include <stdio.h>
17 #include "defs.h"
18 #include "fsa.h"
19 #include "hash.h"
20 #include "externals.h"
21 
22 
23 /* Functions defined in this file: */
24 static fsa *fsa_reverse_short(fsa *fsaptr, storage_type op_table_type,
25                               boolean destroy, boolean subsets,
26                               char *tempfilename);
27 static fsa *fsa_reverse_int(fsa *fsaptr, storage_type op_table_type,
28                             boolean destroy, boolean subsets,
29                             char *tempfilename);
30 static fsa *fsa_mireverse_short(fsa *fsaptr, storage_type op_table_type,
31                                 boolean destroy, char *tempfilename);
32 static fsa *fsa_mireverse_int(fsa *fsaptr, storage_type op_table_type,
33                               boolean destroy, char *tempfilename);
34 static fsa *fsa_miexists1_short(fsa *fsaptr, storage_type op_table_type,
35                                 boolean destroy, char *tempfilename);
36 static fsa *fsa_miexists1_int(fsa *fsaptr, storage_type op_table_type,
37                               boolean destroy, char *tempfilename);
38 static fsa *fsa_miexists2_short(fsa *fsaptr, storage_type op_table_type,
39                                 boolean destroy, char *tempfilename);
40 static fsa *fsa_miexists2_int(fsa *fsaptr, storage_type op_table_type,
41                               boolean destroy, char *tempfilename);
42 
43 
44 /* *fsaptr must be a deterministic fsa.
45  * The returned *fsa accepts words accepted by *fsaptr but read
46  * backwards.
47  * If subsets is true, then the returned fsa will have state-set of type
48  * "list of integers", where the list represents the subset of the state-set
49  * of *fsaptr that correponds to the state of the returned fsa.
50  */
fsa_reverse(fsa * fsaptr,storage_type op_table_type,boolean destroy,boolean subsets,char * tempfilename)51 fsa *fsa_reverse(fsa *fsaptr, storage_type op_table_type, boolean destroy,
52                  boolean subsets, char *tempfilename)
53 {
54   if (kbm_print_level >= 3)
55     printf("    #Calling fsa_reverse.\n");
56   if (fsaptr->states->size < MAXUSHORT)
57     return fsa_reverse_short(fsaptr, op_table_type, destroy, subsets,
58                              tempfilename);
59   else
60     return fsa_reverse_int(fsaptr, op_table_type, destroy, subsets,
61                            tempfilename);
62 }
63 
fsa_reverse_short(fsa * fsaptr,storage_type op_table_type,boolean destroy,boolean subsets,char * tempfilename)64 static fsa *fsa_reverse_short(fsa *fsaptr, storage_type op_table_type,
65                               boolean destroy, boolean subsets,
66                               char *tempfilename)
67 {
68   int **table, ne, nsi, ns, is, dr, *fsarow, nt, cstate, cs, csi, im, i, g, n,
69       len = 0, ct;
70   unsigned short *ht_ptr, *ht_chptr, *ht_ptrb, *ht_ptre, *cs_ptr, *cs_ptre,
71       *ptr;
72   boolean dense_ip, dense_op;
73   short_hash_table ht;
74   fsa *reverse;
75   FILE *tempfile;
76 
77   if (kbm_print_level >= 3)
78     printf("    #Calling fsa_reverse_short.\n");
79   if (!fsaptr->flags[DFA]) {
80     fprintf(stderr, "Error: fsa_reverse only applies to DFA's.\n");
81     return 0;
82   }
83 
84   tmalloc(reverse, fsa, 1);
85   fsa_init(reverse);
86   srec_copy(reverse->alphabet, fsaptr->alphabet);
87   reverse->flags[DFA] = TRUE;
88   reverse->flags[ACCESSIBLE] = TRUE;
89   reverse->flags[BFS] = TRUE;
90 
91   reverse->states->type = subsets ? LISTOFINTS : SIMPLE;
92 
93   reverse->table->table_type = op_table_type;
94   reverse->table->denserows = 0;
95   reverse->table->printing_format = op_table_type;
96 
97   if (fsaptr->num_initial == 0 || fsaptr->num_accepting == 0) {
98     reverse->num_initial = 0;
99     reverse->num_accepting = 0;
100     reverse->states->size = 0;
101     if (destroy)
102       fsa_clear(fsaptr);
103     return reverse;
104   }
105   ne = fsaptr->alphabet->size;
106   nsi = fsaptr->states->size;
107 
108   dense_ip = fsaptr->table->table_type == DENSE;
109   dr = fsaptr->table->denserows;
110   dense_op = op_table_type == DENSE;
111   table = fsaptr->table->table_data_ptr;
112 
113   reverse->num_initial = 1;
114   tmalloc(reverse->initial, int, 2);
115   reverse->initial[1] = 1;
116 
117   /* The states of *reverse will be subsets of the state-set of *fsaptr.
118    * The initial state consists of all accept states of *fsaptr.
119    */
120   short_hash_init(&ht, FALSE, 0, 0, 0);
121   ht_ptrb = ht.current_ptr;
122   ht_ptre = ht_ptrb - 1;
123   fsa_set_is_accepting(fsaptr);
124   for (i = 1; i <= nsi; i++)
125     if (fsaptr->is_accepting[i])
126       *(++ht_ptre) = i;
127   tfree(fsaptr->is_accepting);
128   im = short_hash_locate(&ht, ht_ptre - ht_ptrb + 1);
129   if (im != 1) {
130     fprintf(stderr, "Hash-initialisation problem in fsa_reverse.\n");
131     return 0;
132   }
133   if ((tempfile = fopen(tempfilename, "w")) == 0) {
134     fprintf(stderr, "Error: cannot open file %s\n", tempfilename);
135     return 0;
136   }
137   if (dense_op)
138     tmalloc(fsarow, int, ne) else tmalloc(fsarow, int, 2 * ne + 1)
139 
140         cstate = 0;
141   if (dense_op)
142     len = ne; /* The length of the fsarow output. */
143   nt = 0;     /* Number of transitions in reverse */
144 
145   while (++cstate <= ht.num_recs) {
146     if (kbm_print_level >= 3) {
147       if ((cstate <= 1000 && cstate % 100 == 0) ||
148           (cstate <= 10000 && cstate % 1000 == 0) ||
149           (cstate <= 100000 && cstate % 5000 == 0) || cstate % 50000 == 0)
150         printf("    #cstate = %d;  number of states = %d.\n", cstate,
151                ht.num_recs);
152     }
153     cs_ptr = short_hash_rec(&ht, cstate);
154     cs_ptre = short_hash_rec(&ht, cstate) + short_hash_rec_len(&ht, cstate) - 1;
155     if (!dense_op)
156       len = 0;
157 
158     for (g = 1; g <= ne; g++) {
159       /* Calculate action of label g on state cstate  - to get the image, we
160        * have to look for all inverse images under g of all states cs in the
161        * subset cstate. The subset formed will be the image.
162        */
163       ht_ptrb = ht.current_ptr;
164       ht_ptre = ht_ptrb - 1;
165       ptr = cs_ptr - 1;
166       while (++ptr <= cs_ptre) {
167         cs = *ptr;
168         for (csi = 1; csi <= nsi; csi++)
169           if (target(dense_ip, table, g, csi, dr) == cs) {
170             /* csi has to be added to the subset of states that forms the image
171              * of cstate under g - we want to keep this subset in ascending
172              * order.
173              */
174             if (ht_ptrb > ht_ptre || csi > *ht_ptre) {
175               *(++ht_ptre) = csi;
176             }
177             else {
178               ht_chptr = ht_ptrb;
179               while (*ht_chptr < csi)
180                 ht_chptr++;
181               if (csi < *ht_chptr) {
182                 ht_ptr = ++ht_ptre;
183                 while (ht_ptr > ht_chptr) {
184                   *ht_ptr = *(ht_ptr - 1);
185                   ht_ptr--;
186                 }
187                 *ht_ptr = csi;
188               }
189             }
190           }
191       }
192       im = short_hash_locate(&ht, ht_ptre - ht_ptrb + 1);
193       if (dense_op)
194         fsarow[g - 1] = im;
195       else if (im > 0) {
196         fsarow[++len] = g;
197         fsarow[++len] = im;
198       }
199       if (im > 0)
200         nt++;
201     }
202     if (!dense_op)
203       fsarow[0] = len++;
204     fwrite((void *)fsarow, sizeof(int), (size_t)len, tempfile);
205   }
206   fclose(tempfile);
207 
208   ns = reverse->states->size = ht.num_recs;
209   reverse->table->numTransitions = nt;
210 
211   /* Locate the accept states. These are those that contain the initial
212    * state of *fsaptr.
213    * Also, if subsets is true, record the states in the intlist field
214    * of reverse->states.
215    */
216   is = fsaptr->initial[1];
217   tmalloc(reverse->is_accepting, boolean, ns + 1);
218   for (i = 1; i <= ns; i++)
219     reverse->is_accepting[i] = FALSE;
220   if (subsets)
221     tmalloc(reverse->states->intlist, int *, ns + 1);
222   ct = 0;
223   for (cstate = 1; cstate <= ns; cstate++) {
224     len = short_hash_rec_len(&ht, cstate);
225     cs_ptr = short_hash_rec(&ht, cstate);
226     cs_ptre = cs_ptr + len - 1;
227     /* See if the set contains the initial state */
228     ptr = cs_ptr - 1;
229     while (++ptr <= cs_ptre)
230       if (*ptr == is) {
231         reverse->is_accepting[cstate] = TRUE;
232         ct++;
233         break;
234       }
235     if (subsets) {
236       tmalloc(reverse->states->intlist[cstate], int, len + 1);
237       reverse->states->intlist[cstate][0] = len;
238       n = 0;
239       ptr = cs_ptr - 1;
240       while (++ptr <= cs_ptre)
241         reverse->states->intlist[cstate][++n] = *ptr;
242     }
243   }
244 
245   reverse->num_accepting = ct;
246   if (ct == 1 || ct != ns) {
247     tmalloc(reverse->accepting, int, ct + 1);
248     ct = 0;
249     for (i = 1; i <= ns; i++)
250       if (reverse->is_accepting[i])
251         reverse->accepting[++ct] = i;
252   }
253   tfree(reverse->is_accepting);
254   short_hash_clear(&ht);
255   tfree(fsarow);
256 
257   if (destroy)
258     fsa_clear(fsaptr);
259 
260   /* Now read the transition table back in */
261   tempfile = fopen(tempfilename, "r");
262   compressed_transitions_read(reverse, tempfile);
263   fclose(tempfile);
264 
265   unlink(tempfilename);
266 
267   return reverse;
268 }
269 
fsa_reverse_int(fsa * fsaptr,storage_type op_table_type,boolean destroy,boolean subsets,char * tempfilename)270 fsa *fsa_reverse_int(fsa *fsaptr, storage_type op_table_type, boolean destroy,
271                      boolean subsets, char *tempfilename)
272 {
273   fprintf(stderr, "Sorry - fsa_reverse is not yet implemented for machines.\n");
274   fprintf(stderr, "with more than 65536 states.\n");
275   return 0;
276 }
277 
278 /* *fsaptr must be a deterministic fsa.
279  * The returned *fsa accepts words accepted by *fsaptr read backwards,
280  * and is a labeled MIDFA, with initial states the singleton accept states of
281  * *fsaptr.
282  */
fsa_mireverse(fsa * fsaptr,storage_type op_table_type,boolean destroy,char * tempfilename)283 fsa *fsa_mireverse(fsa *fsaptr, storage_type op_table_type, boolean destroy,
284                    char *tempfilename)
285 {
286   if (kbm_print_level >= 3)
287     printf("    #Calling fsa_mireverse.\n");
288   if (fsaptr->states->size < MAXUSHORT)
289     return fsa_mireverse_short(fsaptr, op_table_type, destroy, tempfilename);
290   else
291     return fsa_mireverse_int(fsaptr, op_table_type, destroy, tempfilename);
292 }
293 
fsa_mireverse_short(fsa * fsaptr,storage_type op_table_type,boolean destroy,char * tempfilename)294 static fsa *fsa_mireverse_short(fsa *fsaptr, storage_type op_table_type,
295                                 boolean destroy, char *tempfilename)
296 {
297   int **table, ne, nsi, ns, nai, is, dr, *fsarow, nt, cstate, cs, csi, im, i, g,
298       len = 0, ct;
299   unsigned short *ht_ptr, *ht_chptr, *ht_ptrb, *ht_ptre, *cs_ptr, *cs_ptre,
300       *ptr;
301   boolean dense_ip, dense_op;
302   short_hash_table ht;
303   fsa *reverse;
304   srec *labs;
305   FILE *tempfile;
306 
307   if (kbm_print_level >= 3)
308     printf("    #Calling fsa_mireverse_short.\n");
309   if (!fsaptr->flags[DFA]) {
310     fprintf(stderr, "Error: fsa_mireverse only applies to DFA's.\n");
311     return 0;
312   }
313 
314   tmalloc(reverse, fsa, 1);
315   fsa_init(reverse);
316   srec_copy(reverse->alphabet, fsaptr->alphabet);
317   reverse->flags[MIDFA] = TRUE;
318   reverse->flags[ACCESSIBLE] = TRUE;
319 
320   ne = fsaptr->alphabet->size;
321   nsi = fsaptr->states->size;
322 
323   /* set up the labels for the states of reverse */
324   reverse->states->type = LABELED;
325   tmalloc(reverse->states->labels, srec, 1);
326   labs = reverse->states->labels;
327   nai = fsaptr->num_accepting;
328   labs->size = nai + 2;
329   labs->type = LISTOFINTS;
330   /* The labels will be [i] for all initial states i of fsa,
331    * [0] for other accepting states of reverse, and [] for other states.
332    */
333   tmalloc(labs->intlist, int *, nai + 3);
334   for (i = 1; i <= nai; i++) {
335     tmalloc(labs->intlist[i], int, 2);
336     labs->intlist[i][0] = 1;
337     labs->intlist[i][1] = nsi == nai ? i : fsaptr->accepting[i];
338   }
339   tmalloc(labs->intlist[nai + 1], int, 2);
340   labs->intlist[nai + 1][0] = 1;
341   labs->intlist[nai + 1][1] = 0;
342   tmalloc(labs->intlist[nai + 2], int, 1);
343   labs->intlist[nai + 2][0] = 0;
344 
345   reverse->table->table_type = op_table_type;
346   reverse->table->denserows = 0;
347   reverse->table->printing_format = op_table_type;
348 
349   if (fsaptr->num_initial == 0 || fsaptr->num_accepting == 0) {
350     reverse->num_initial = 0;
351     reverse->num_accepting = 0;
352     reverse->states->size = 0;
353     if (destroy)
354       fsa_clear(fsaptr);
355     return reverse;
356   }
357 
358   dense_ip = fsaptr->table->table_type == DENSE;
359   dr = fsaptr->table->denserows;
360   dense_op = op_table_type == DENSE;
361   table = fsaptr->table->table_data_ptr;
362 
363   reverse->num_initial = nai;
364   tmalloc(reverse->initial, int, nai + 1);
365   for (i = 1; i <= nai; i++)
366     reverse->initial[i] = i;
367 
368   /* The states of *reverse will be subsets of the state-set of *fsaptr.
369    * The initial states consists of all singleton accept states of *fsaptr.
370    */
371   short_hash_init(&ht, FALSE, 0, 0, 0);
372   for (i = 1; i <= nai; i++) {
373     ht_ptrb = ht.current_ptr;
374     *ht_ptrb = nsi == nai ? i : fsaptr->accepting[i];
375     im = short_hash_locate(&ht, 1);
376     if (im != i) {
377       fprintf(stderr, "Hash-initialisation problem in fsa_reverse.\n");
378       return 0;
379     }
380   }
381   if ((tempfile = fopen(tempfilename, "w")) == 0) {
382     fprintf(stderr, "Error: cannot open file %s\n", tempfilename);
383     return 0;
384   }
385   if (dense_op)
386     tmalloc(fsarow, int, ne) else tmalloc(fsarow, int, 2 * ne + 1)
387 
388         cstate = 0;
389   if (dense_op)
390     len = ne; /* The length of the fsarow output. */
391   nt = 0;     /* Number of transitions in reverse */
392 
393   while (++cstate <= ht.num_recs) {
394     if (kbm_print_level >= 3) {
395       if ((cstate <= 1000 && cstate % 100 == 0) ||
396           (cstate <= 10000 && cstate % 1000 == 0) ||
397           (cstate <= 100000 && cstate % 5000 == 0) || cstate % 50000 == 0)
398         printf("    #cstate = %d;  number of states = %d.\n", cstate,
399                ht.num_recs);
400     }
401     cs_ptr = short_hash_rec(&ht, cstate);
402     cs_ptre = short_hash_rec(&ht, cstate) + short_hash_rec_len(&ht, cstate) - 1;
403     if (!dense_op)
404       len = 0;
405 
406     for (g = 1; g <= ne; g++) {
407       /* Calculate action of label g on state cstate  - to get the image, we
408        * have to look for all inverse images under g of all states cs in the
409        * subset cstate. The subset formed will be the image.
410        */
411       ht_ptrb = ht.current_ptr;
412       ht_ptre = ht_ptrb - 1;
413       ptr = cs_ptr - 1;
414       while (++ptr <= cs_ptre) {
415         cs = *ptr;
416         for (csi = 1; csi <= nsi; csi++)
417           if (target(dense_ip, table, g, csi, dr) == cs) {
418             /* csi has to be added to the subset of states that forms the image
419              * of cstate under g - we want to keep this subset in ascending
420              * order.
421              */
422             if (ht_ptrb > ht_ptre || csi > *ht_ptre) {
423               *(++ht_ptre) = csi;
424             }
425             else {
426               ht_chptr = ht_ptrb;
427               while (*ht_chptr < csi)
428                 ht_chptr++;
429               if (csi < *ht_chptr) {
430                 ht_ptr = ++ht_ptre;
431                 while (ht_ptr > ht_chptr) {
432                   *ht_ptr = *(ht_ptr - 1);
433                   ht_ptr--;
434                 }
435                 *ht_ptr = csi;
436               }
437             }
438           }
439       }
440       im = short_hash_locate(&ht, ht_ptre - ht_ptrb + 1);
441       if (dense_op)
442         fsarow[g - 1] = im;
443       else if (im > 0) {
444         fsarow[++len] = g;
445         fsarow[++len] = im;
446       }
447       if (im > 0)
448         nt++;
449     }
450     if (!dense_op)
451       fsarow[0] = len++;
452     fwrite((void *)fsarow, sizeof(int), (size_t)len, tempfile);
453   }
454   fclose(tempfile);
455 
456   ns = reverse->states->size = ht.num_recs;
457   reverse->table->numTransitions = nt;
458 
459   /* Initialize the state setToLabels ptr.
460    * Then locate the accept states. These are those that contain the initial
461    * state of *fsaptr.
462    */
463   tmalloc(reverse->states->setToLabels, setToLabelsType, ns + 1);
464   for (i = 1; i <= nai; i++)
465     reverse->states->setToLabels[i] = i;
466 
467   is = fsaptr->initial[1];
468   tmalloc(reverse->is_accepting, boolean, ns + 1);
469   for (i = 1; i <= ns; i++)
470     reverse->is_accepting[i] = FALSE;
471   ct = 0;
472   for (cstate = 1; cstate <= ns; cstate++) {
473     len = short_hash_rec_len(&ht, cstate);
474     cs_ptr = short_hash_rec(&ht, cstate);
475     cs_ptre = cs_ptr + len - 1;
476     /* See if the set contains the initial state */
477     ptr = cs_ptr - 1;
478     while (++ptr <= cs_ptre)
479       if (*ptr == is) {
480         reverse->is_accepting[cstate] = TRUE;
481         ct++;
482         break;
483       }
484     if (cstate > nai)
485       reverse->states->setToLabels[cstate] =
486           reverse->is_accepting[cstate] ? nai + 1 : nai + 2;
487   }
488 
489   reverse->num_accepting = ct;
490   if (ct == 1 || ct != ns) {
491     tmalloc(reverse->accepting, int, ct + 1);
492     ct = 0;
493     for (i = 1; i <= ns; i++)
494       if (reverse->is_accepting[i])
495         reverse->accepting[++ct] = i;
496   }
497   tfree(reverse->is_accepting);
498   short_hash_clear(&ht);
499   tfree(fsarow);
500 
501   if (destroy)
502     fsa_clear(fsaptr);
503 
504   /* Now read the transition table back in */
505   tempfile = fopen(tempfilename, "r");
506   compressed_transitions_read(reverse, tempfile);
507   fclose(tempfile);
508 
509   unlink(tempfilename);
510 
511   return reverse;
512 }
513 
fsa_mireverse_int(fsa * fsaptr,storage_type op_table_type,boolean destroy,char * tempfilename)514 static fsa *fsa_mireverse_int(fsa *fsaptr, storage_type op_table_type,
515                               boolean destroy, char *tempfilename)
516 {
517   fprintf(stderr,
518           "Sorry - fsa_mireverse is not yet implemented for machines.\n");
519   fprintf(stderr, "with more than 65536 states.\n");
520   return 0;
521 }
522 
523 /* *fsaptr must be a 2-variable MIDFA fsa.
524  * The returned midfa accepts a word w_1 iff (w_1,w_2) is accepted by *fsaptr,
525  * for some word w_2 - but here the padding symbol is part of the
526  * alphabet of the output midfa.
527  * In fact, fsa_miexists1 calls fsa_miexists1_int or (more usually)
528  * fsa_miexists1_short, the latter using the short hash table functions.
529  */
fsa_miexists1(fsa * fsaptr,storage_type op_table_type,boolean destroy,char * tempfilename)530 fsa *fsa_miexists1(fsa *fsaptr, storage_type op_table_type, boolean destroy,
531                    char *tempfilename)
532 {
533   if (kbm_print_level >= 3)
534     printf("    #Calling fsa_miexists1.\n");
535   if (fsaptr->states->size < MAXUSHORT)
536     return fsa_miexists1_short(fsaptr, op_table_type, destroy, tempfilename);
537   else
538     return fsa_miexists1_int(fsaptr, op_table_type, destroy, tempfilename);
539 }
540 
fsa_miexists1_short(fsa * fsaptr,storage_type op_table_type,boolean destroy,char * tempfilename)541 static fsa *fsa_miexists1_short(fsa *fsaptr, storage_type op_table_type,
542                                 boolean destroy, char *tempfilename)
543 {
544   int **table, ne, ngens, ns, nsi, dr, *fsarow, e, es, ef, nt, cstate, cs, csi,
545       im, i, g1, len = 0, ct, ni;
546   unsigned short *ht_ptr, *ht_chptr, *ht_ptrb, *ht_ptre, *cs_ptr, *cs_ptre,
547       *ptr;
548   boolean dense_ip, dense_op;
549   short_hash_table ht;
550   fsa *miexists1;
551   FILE *tempfile;
552 
553   if (kbm_print_level >= 3)
554     printf("    #Calling fsa_miexists1_short.\n");
555   if (!fsaptr->flags[MIDFA]) {
556     fprintf(stderr, "Error: fsa_miexists1 only applies to MIDFA's.\n");
557     exit(1);
558   }
559 
560   if (fsaptr->alphabet->type != PRODUCT || fsaptr->alphabet->arity != 2) {
561     fprintf(stderr, "Error in fsa_miexists1: fsa must be 2-variable.\n");
562     exit(1);
563   }
564 
565   tmalloc(miexists1, fsa, 1);
566   fsa_init(miexists1);
567   nsi = fsaptr->states->size;
568   ne = fsaptr->alphabet->size;
569   ngens = fsaptr->alphabet->base->size;
570 
571   if (ne != (ngens + 1) * (ngens + 1) - 1) {
572     fprintf(stderr, "Error: in a 2-variable fsa, alphabet size should = "
573                     "(ngens+1)^2 - 1.\n");
574     exit(1);
575   }
576   /* alphabet will include padding symbol */
577   miexists1->alphabet->type = IDENTIFIERS;
578   miexists1->alphabet->size = ngens + 1;
579   tmalloc(miexists1->alphabet->names, char *, ngens + 2);
580   for (i = 1; i <= ngens; i++) {
581     tmalloc(miexists1->alphabet->names[i], char,
582             stringlen(fsaptr->alphabet->base->names[i]) + 1);
583     strcpy(miexists1->alphabet->names[i], fsaptr->alphabet->base->names[i]);
584   }
585   tmalloc(miexists1->alphabet->names[ngens + 1], char, 2);
586   miexists1->alphabet->names[ngens + 1][0] = fsaptr->alphabet->padding;
587   miexists1->alphabet->names[ngens + 1][1] = '\0';
588   miexists1->flags[MIDFA] = TRUE;
589   miexists1->flags[ACCESSIBLE] = TRUE;
590 
591   miexists1->states->type = LABELED;
592   tmalloc(miexists1->states->labels, srec, 1);
593   srec_copy(miexists1->states->labels, fsaptr->states->labels);
594 
595   miexists1->table->table_type = op_table_type;
596   miexists1->table->denserows = 0;
597   miexists1->table->printing_format = op_table_type;
598 
599   if (fsaptr->num_initial == 0 || fsaptr->num_accepting == 0) {
600     miexists1->num_initial = 0;
601     miexists1->num_accepting = 0;
602     miexists1->states->size = 0;
603     if (destroy)
604       fsa_clear(fsaptr);
605     return miexists1;
606   }
607 
608   fsa_set_is_accepting(fsaptr);
609 
610   dense_ip = fsaptr->table->table_type == DENSE;
611   dr = fsaptr->table->denserows;
612   dense_op = op_table_type == DENSE;
613   table = fsaptr->table->table_data_ptr;
614 
615   ni = fsaptr->num_initial;
616   miexists1->num_initial = ni;
617   tmalloc(miexists1->initial, int, ni + 1);
618   for (i = 1; i <= ni; i++)
619     miexists1->initial[i] = i;
620 
621   short_hash_init(&ht, FALSE, 0, 0, 0);
622   for (i = 1; i <= ni; i++) {
623     ht_ptrb = ht.current_ptr;
624     *ht_ptrb = nsi == ni ? i : fsaptr->initial[i];
625     im = short_hash_locate(&ht, 1);
626     if (im != i) {
627       fprintf(stderr, "Hash-initialisation problem in fsa_miexists.\n");
628       exit(1);
629     }
630   }
631   /* Each state in 'miexists1' will be represented as a subset of the states
632    * of *fsaptr. The initial states are  one-element set containing the initial
633    * states of *fsaptr. A subset is an accept-state if it contains an accept
634    * state of *fsaptr.
635    * The subsets will be stored as variable-length records in the hash-table,
636    * always in increasing order.
637    */
638   if ((tempfile = fopen(tempfilename, "w")) == 0) {
639     fprintf(stderr, "Error: cannot open file %s\n", tempfilename);
640     exit(1);
641   }
642   if (dense_op)
643     tmalloc(fsarow, int, ngens) else tmalloc(fsarow, int, 2 * ngens + 1)
644 
645         cstate = 0;
646   if (dense_op)
647     len = ngens + 1; /* The length of the fsarow output. */
648   nt = 0;            /* Number of transitions in miexists1 */
649 
650   while (++cstate <= ht.num_recs) {
651     if (kbm_print_level >= 3) {
652       if ((cstate <= 1000 && cstate % 100 == 0) ||
653           (cstate <= 10000 && cstate % 1000 == 0) ||
654           (cstate <= 100000 && cstate % 5000 == 0) || cstate % 50000 == 0)
655         printf("    #cstate = %d;  number of states = %d.\n", cstate,
656                ht.num_recs);
657     }
658     cs_ptr = short_hash_rec(&ht, cstate);
659     cs_ptre = short_hash_rec(&ht, cstate) + short_hash_rec_len(&ht, cstate) - 1;
660     if (!dense_op)
661       len = 0;
662 
663     for (g1 = 1; g1 <= ngens + 1; g1++) {
664       /* Calculate action of generator g1 on state cstate  - to get the image,
665        * we have to apply (g1,g2) to each element in the subset corresponding to
666        * cstate, and this for each generator g2 of the base-alphabet (including
667        * the padding symbol).
668        */
669       ht_ptrb = ht.current_ptr;
670       ht_ptre = ht_ptrb - 1;
671       ptr = cs_ptr - 1;
672       es = (g1 - 1) * (ngens + 1) + 1;
673       ef = g1 == ngens + 1 ? g1 * (ngens + 1) - 1 : g1 * (ngens + 1);
674       /* As g2 ranges from 1 to ngens+1 in the pair (g1,g2), for fixed g1, the
675        * corresponding edge number in the fsa ranges from es to ef.
676        */
677       while (++ptr <= cs_ptre) {
678         cs = *ptr;
679         for (e = es; e <= ef; e++) {
680           csi = target(dense_ip, table, e, cs, dr);
681           if (csi == 0)
682             continue;
683           if (ht_ptrb > ht_ptre || csi > *ht_ptre) {
684             /* We have a new state for the image subset to be added to the end
685              */
686             *(++ht_ptre) = csi;
687           }
688           else {
689             ht_chptr = ht_ptrb;
690             while (*ht_chptr < csi)
691               ht_chptr++;
692             if (csi < *ht_chptr) {
693               /* we have a new state for the image subset to be added in the
694                * middle */
695               ht_ptr = ++ht_ptre;
696               while (ht_ptr > ht_chptr) {
697                 *ht_ptr = *(ht_ptr - 1);
698                 ht_ptr--;
699               }
700               *ht_ptr = csi;
701             }
702           }
703         }
704       }
705       im = short_hash_locate(&ht, ht_ptre - ht_ptrb + 1);
706       if (dense_op)
707         fsarow[g1 - 1] = im;
708       else if (im > 0) {
709         fsarow[++len] = g1;
710         fsarow[++len] = im;
711       }
712       if (im > 0)
713         nt++;
714     }
715     if (!dense_op)
716       fsarow[0] = len++;
717     fwrite((void *)fsarow, sizeof(int), (size_t)len, tempfile);
718   }
719   fclose(tempfile);
720 
721   ns = miexists1->states->size = ht.num_recs;
722   miexists1->table->numTransitions = nt;
723 
724   /* Locate the initial and accept states, and assign labels.
725    * A state is an accept state if the
726    * corresponding subset contains an accept state of *fsaptr.
727    * We will need to use the array miexists1->is_accepting.
728    */
729   tmalloc(miexists1->states->setToLabels, setToLabelsType, ns + 1);
730   for (i = 1; i <= ni; i++)
731     miexists1->states->setToLabels[i] =
732         fsaptr->states->setToLabels[fsaptr->initial[i]];
733   tmalloc(miexists1->is_accepting, boolean, ns + 1);
734   for (i = 1; i <= ns; i++)
735     miexists1->is_accepting[i] = FALSE;
736   ct = 0;
737   for (cstate = ns; cstate >= 1; cstate--) {
738     cs_ptr = short_hash_rec(&ht, cstate);
739     cs_ptre = short_hash_rec(&ht, cstate) + short_hash_rec_len(&ht, cstate) - 1;
740     /* See if the set an accept-state */
741     ptr = cs_ptr - 1;
742     while (++ptr <= cs_ptre)
743       if (fsaptr->is_accepting[*ptr]) {
744         miexists1->is_accepting[cstate] = TRUE;
745         ct++;
746         break;
747       }
748     if (cstate > ni)
749       miexists1->states->setToLabels[cstate] =
750           miexists1->is_accepting[cstate] ? ni + 1 : ni + 2;
751   }
752 
753   miexists1->num_accepting = ct;
754   if (ct == 1 || ct != ns) {
755     tmalloc(miexists1->accepting, int, ct + 1);
756     ct = 0;
757     for (i = 1; i <= ns; i++)
758       if (miexists1->is_accepting[i])
759         miexists1->accepting[++ct] = i;
760   }
761   tfree(fsaptr->is_accepting);
762   tfree(miexists1->is_accepting);
763   short_hash_clear(&ht);
764   tfree(fsarow);
765 
766   if (destroy)
767     fsa_clear(fsaptr);
768 
769   /* Now read the transition table back in */
770   tempfile = fopen(tempfilename, "r");
771   compressed_transitions_read(miexists1, tempfile);
772   fclose(tempfile);
773 
774   unlink(tempfilename);
775 
776   return miexists1;
777 }
778 
fsa_miexists1_int(fsa * fsaptr,storage_type op_table_type,boolean destroy,char * tempfilename)779 static fsa *fsa_miexists1_int(fsa *fsaptr, storage_type op_table_type,
780                               boolean destroy, char *tempfilename)
781 {
782   fprintf(stderr,
783           "Sorry - fsa_miexists1 is not yet implemented for machines.\n");
784   fprintf(stderr, "with more than 65536 states.\n");
785   exit(1);
786 }
787 
788 /* *fsaptr must be a 2-variable MIDFA fsa.
789  * The returned midfa accepts a word w_1 iff (w_1,w_2) is accepted by *fsaptr,
790  * for some word w_2 - but here the padding symbol is part of the
791  * alphabet of the output midfa.
792  * In fact, fsa_miexists2 calls fsa_miexists2_int or (more usually)
793  * fsa_miexists2_short, the latter using the short hash table functions.
794  */
fsa_miexists2(fsa * fsaptr,storage_type op_table_type,boolean destroy,char * tempfilename)795 fsa *fsa_miexists2(fsa *fsaptr, storage_type op_table_type, boolean destroy,
796                    char *tempfilename)
797 {
798   if (kbm_print_level >= 3)
799     printf("    #Calling fsa_miexists2.\n");
800   if (fsaptr->states->size < MAXUSHORT)
801     return fsa_miexists2_short(fsaptr, op_table_type, destroy, tempfilename);
802   else
803     return fsa_miexists2_int(fsaptr, op_table_type, destroy, tempfilename);
804 }
805 
fsa_miexists2_short(fsa * fsaptr,storage_type op_table_type,boolean destroy,char * tempfilename)806 static fsa *fsa_miexists2_short(fsa *fsaptr, storage_type op_table_type,
807                                 boolean destroy, char *tempfilename)
808 {
809   int **table, ne, ngens, ngens1, ns, nsi, dr, *fsarow, e, es, ef, nt, cstate,
810       cs, csi, im, i, g2, len = 0, ct, ni;
811   unsigned short *ht_ptr, *ht_chptr, *ht_ptrb, *ht_ptre, *cs_ptr, *cs_ptre,
812       *ptr;
813   boolean dense_ip, dense_op;
814   short_hash_table ht;
815   fsa *miexists2;
816   FILE *tempfile;
817 
818   if (kbm_print_level >= 3)
819     printf("    #Calling fsa_miexists2_short.\n");
820   if (!fsaptr->flags[MIDFA]) {
821     fprintf(stderr, "Error: fsa_miexists2 only applies to MIDFA's.\n");
822     exit(1);
823   }
824 
825   if (fsaptr->alphabet->type != PRODUCT || fsaptr->alphabet->arity != 2) {
826     fprintf(stderr, "Error in fsa_miexists2: fsa must be 2-variable.\n");
827     exit(1);
828   }
829 
830   tmalloc(miexists2, fsa, 1);
831   fsa_init(miexists2);
832   nsi = fsaptr->states->size;
833   ne = fsaptr->alphabet->size;
834   ngens = fsaptr->alphabet->base->size;
835   ngens1 = ngens + 1;
836 
837   if (ne != ngens1 * ngens1 - 1) {
838     fprintf(stderr, "Error: in a 2-variable fsa, alphabet size should = "
839                     "(ngens+1)^2 - 1.\n");
840     exit(1);
841   }
842   /* alphabet will include padding symbol */
843   miexists2->alphabet->type = IDENTIFIERS;
844   miexists2->alphabet->size = ngens1;
845   tmalloc(miexists2->alphabet->names, char *, ngens + 2);
846   for (i = 1; i <= ngens; i++) {
847     tmalloc(miexists2->alphabet->names[i], char,
848             stringlen(fsaptr->alphabet->base->names[i]) + 1);
849     strcpy(miexists2->alphabet->names[i], fsaptr->alphabet->base->names[i]);
850   }
851   tmalloc(miexists2->alphabet->names[ngens1], char, 2);
852   miexists2->alphabet->names[ngens1][0] = fsaptr->alphabet->padding;
853   miexists2->alphabet->names[ngens1][1] = '\0';
854   miexists2->flags[MIDFA] = TRUE;
855   miexists2->flags[ACCESSIBLE] = TRUE;
856 
857   miexists2->states->type = LABELED;
858   tmalloc(miexists2->states->labels, srec, 1);
859   srec_copy(miexists2->states->labels, fsaptr->states->labels);
860 
861   miexists2->table->table_type = op_table_type;
862   miexists2->table->denserows = 0;
863   miexists2->table->printing_format = op_table_type;
864 
865   if (fsaptr->num_initial == 0 || fsaptr->num_accepting == 0) {
866     miexists2->num_initial = 0;
867     miexists2->num_accepting = 0;
868     miexists2->states->size = 0;
869     if (destroy)
870       fsa_clear(fsaptr);
871     return miexists2;
872   }
873 
874   fsa_set_is_accepting(fsaptr);
875 
876   dense_ip = fsaptr->table->table_type == DENSE;
877   dr = fsaptr->table->denserows;
878   dense_op = op_table_type == DENSE;
879   table = fsaptr->table->table_data_ptr;
880 
881   ni = fsaptr->num_initial;
882   miexists2->num_initial = ni;
883   tmalloc(miexists2->initial, int, ni + 1);
884   for (i = 1; i <= ni; i++)
885     miexists2->initial[i] = i;
886 
887   short_hash_init(&ht, FALSE, 0, 0, 0);
888   for (i = 1; i <= ni; i++) {
889     ht_ptrb = ht.current_ptr;
890     *ht_ptrb = nsi == ni ? i : fsaptr->initial[i];
891     im = short_hash_locate(&ht, 1);
892     if (im != i) {
893       fprintf(stderr, "Hash-initialisation problem in fsa_miexists.\n");
894       exit(1);
895     }
896   }
897   /* Each state in 'miexists2' will be represented as a subset of the states
898    * of *fsaptr. The initial states are  one-element set containing the initial
899    * states of *fsaptr. A subset is an accept-state if it contains an accept
900    * state of *fsaptr.
901    * The subsets will be stored as variable-length records in the hash-table,
902    * always in increasing order.
903    */
904   if ((tempfile = fopen(tempfilename, "w")) == 0) {
905     fprintf(stderr, "Error: cannot open file %s\n", tempfilename);
906     exit(1);
907   }
908   if (dense_op)
909     tmalloc(fsarow, int, ngens) else tmalloc(fsarow, int, 2 * ngens1)
910 
911         cstate = 0;
912   if (dense_op)
913     len = ngens1; /* The length of the fsarow output. */
914   nt = 0;         /* Number of transitions in miexists2 */
915 
916   while (++cstate <= ht.num_recs) {
917     if (kbm_print_level >= 3) {
918       if ((cstate <= 1000 && cstate % 100 == 0) ||
919           (cstate <= 10000 && cstate % 1000 == 0) ||
920           (cstate <= 100000 && cstate % 5000 == 0) || cstate % 50000 == 0)
921         printf("    #cstate = %d;  number of states = %d.\n", cstate,
922                ht.num_recs);
923     }
924     cs_ptr = short_hash_rec(&ht, cstate);
925     cs_ptre = short_hash_rec(&ht, cstate) + short_hash_rec_len(&ht, cstate) - 1;
926     if (!dense_op)
927       len = 0;
928 
929     for (g2 = 1; g2 <= ngens1; g2++) {
930       /* Calculate action of generator g2 on state cstate  - to get the image,
931        * we have to apply (g1,g2) to each element in the subset corresponding to
932        * cstate, and this for each generator g1 of the base-alphabet (including
933        * the padding symbol).
934        */
935       ht_ptrb = ht.current_ptr;
936       ht_ptre = ht_ptrb - 1;
937       ptr = cs_ptr - 1;
938       es = g2;
939       ef = g2 == ngens1 ? ngens * ngens + g2 : ngens * ngens1 + g2;
940       /* As g2 ranges from 1 to ngens+1 in the pair (g2,g2), for fixed g2, the
941        * corresponding edge number in the fsa ranges from es to ef.
942        */
943       while (++ptr <= cs_ptre) {
944         cs = *ptr;
945         for (e = es; e <= ef; e += ngens1) {
946           csi = target(dense_ip, table, e, cs, dr);
947           if (csi == 0)
948             continue;
949           if (ht_ptrb > ht_ptre || csi > *ht_ptre) {
950             /* We have a new state for the image subset to be added to the end
951              */
952             *(++ht_ptre) = csi;
953           }
954           else {
955             ht_chptr = ht_ptrb;
956             while (*ht_chptr < csi)
957               ht_chptr++;
958             if (csi < *ht_chptr) {
959               /* we have a new state for the image subset to be added in the
960                * middle */
961               ht_ptr = ++ht_ptre;
962               while (ht_ptr > ht_chptr) {
963                 *ht_ptr = *(ht_ptr - 1);
964                 ht_ptr--;
965               }
966               *ht_ptr = csi;
967             }
968           }
969         }
970       }
971       im = short_hash_locate(&ht, ht_ptre - ht_ptrb + 1);
972       if (dense_op)
973         fsarow[g2 - 1] = im;
974       else if (im > 0) {
975         fsarow[++len] = g2;
976         fsarow[++len] = im;
977       }
978       if (im > 0)
979         nt++;
980     }
981     if (!dense_op)
982       fsarow[0] = len++;
983     fwrite((void *)fsarow, sizeof(int), (size_t)len, tempfile);
984   }
985   fclose(tempfile);
986 
987   ns = miexists2->states->size = ht.num_recs;
988   miexists2->table->numTransitions = nt;
989 
990   /* Locate the initial and accept states, and assign labels.
991    * A state is an accept state if the
992    * corresponding subset contains an accept state of *fsaptr.
993    * We will need to use the array miexists2->is_accepting.
994    */
995   tmalloc(miexists2->states->setToLabels, setToLabelsType, ns + 1);
996   for (i = 1; i <= ni; i++)
997     miexists2->states->setToLabels[i] =
998         fsaptr->states->setToLabels[fsaptr->initial[i]];
999   tmalloc(miexists2->is_accepting, boolean, ns + 1);
1000   for (i = 1; i <= ns; i++)
1001     miexists2->is_accepting[i] = FALSE;
1002   ct = 0;
1003   for (cstate = ns; cstate >= 1; cstate--) {
1004     cs_ptr = short_hash_rec(&ht, cstate);
1005     cs_ptre = short_hash_rec(&ht, cstate) + short_hash_rec_len(&ht, cstate) - 1;
1006     /* See if the set an accept-state */
1007     ptr = cs_ptr - 1;
1008     while (++ptr <= cs_ptre)
1009       if (fsaptr->is_accepting[*ptr]) {
1010         miexists2->is_accepting[cstate] = TRUE;
1011         ct++;
1012         break;
1013       }
1014     if (cstate > ni)
1015       miexists2->states->setToLabels[cstate] =
1016           miexists2->is_accepting[cstate] ? ni + 1 : ni + 2;
1017   }
1018 
1019   miexists2->num_accepting = ct;
1020   if (ct == 1 || ct != ns) {
1021     tmalloc(miexists2->accepting, int, ct + 1);
1022     ct = 0;
1023     for (i = 1; i <= ns; i++)
1024       if (miexists2->is_accepting[i])
1025         miexists2->accepting[++ct] = i;
1026   }
1027   tfree(fsaptr->is_accepting);
1028   tfree(miexists2->is_accepting);
1029   short_hash_clear(&ht);
1030   tfree(fsarow);
1031 
1032   if (destroy)
1033     fsa_clear(fsaptr);
1034 
1035   /* Now read the transition table back in */
1036   tempfile = fopen(tempfilename, "r");
1037   compressed_transitions_read(miexists2, tempfile);
1038   fclose(tempfile);
1039 
1040   unlink(tempfilename);
1041 
1042   return miexists2;
1043 }
1044 
fsa_miexists2_int(fsa * fsaptr,storage_type op_table_type,boolean destroy,char * tempfilename)1045 static fsa *fsa_miexists2_int(fsa *fsaptr, storage_type op_table_type,
1046                               boolean destroy, char *tempfilename)
1047 {
1048   fprintf(stderr,
1049           "Sorry - fsa_miexists2 is not yet implemented for machines.\n");
1050   fprintf(stderr, "with more than 65536 states.\n");
1051   exit(1);
1052 }
1053