1 /* gpaxioms.c   14.12.94.
2  * 13/10/98 large scale reorganisation to omit globals, etc.
3  * 25/5/98 added -n option, which means do not form the generalised
4  * length 2 multiplier, but check all group relations, including the
5  * inverse relations, by forming composites of the multipliers.
6  * This may be advantageous when there is a very large number of
7  * generators.
8  *
9  * 5/2/98 change generators from type char to type `gen'.
10  * 1/11/96 added experimental option -x, meaning read group relators from a
11  * different file (groupname_x) and do not balance relators when axiom checking.
12  * This is for the  case when generators may reduce to words of length
13  * greater than one, when using orderings other than shortlex.
14  *
15  * 8/1/96  - output a file gpname.kbprog.exitcode containing a GAP statement
16  *        giving the exit code - for use with GAP interface
17  * 15/3/96 changed syntax of -cos call to
18  * gpmakefsa -cos groupname [cosname]
19  * where cosname defaults to "cos".
20  * In the coset case, we can now read the relators from groupname rather than
21  * groupname.cosname, which is better.
22  *
23  * 17/1/96 - remove all of the processing of redundant generators, which is
24  * no longer necessary, and merely complicates the program.
25  *
26  * 25/9/95 -added capability of doing the calculations for coset
27  * automatic groups - with the -cos option
28  *
29  * Carry out the axiom-checking for a short-lex (or wtlex) automatic group.
30  *
31  * SYNOPSIS:
32  * gpaxioms [-ip d/s[dr]] [-op d/s] [-silent] [-v] [-l] [-f] [-s] [-n]
33  * [-cos] groupname [-cosname]
34  *
35  * OPTIONS:
36  * -ip d/s[dr]  input in dense or sparse format - dense is default
37  * -op d/s      output in dense or sparse format - sparse is default
38  * -v           verbose
39  * -silent      no diagnostics
40  * -l/-h        large/huge hash-tables (for big examples)
41  * -f           read the transition table repeatedly from file while mimimizing,
42  *              in calls of fsa_genmult2 and fsa_composite.
43  *              This avoids storing large tables, but is a little slower.
44  * -s		Throw away files immediately after use whenever possible, to
45  *              save disk-space. This will slow things down considerably.
46  * -n		do not form generalised length 2 multiplier.
47  * -cos         Do the calculation for coset automatic groups (by setting
48  *              global variable cosets to be TRUE).
49  */
50 
51 #include <stdio.h>
52 
53 #include "defs.h"
54 #include "fsa.h"
55 #include "rws.h"
56 #include "definitions.h"
57 
58 #define MAXEQNS 1024
59 #define MAXREDUCELEN 2048
60 
61 static rewriting_system rws, *rwsptr;
62 static boolean xset = FALSE;
63 
64 static char gpname[100], cosgpname[100], inf[100], outf[100], outfec[100],
65     fsaname[100], tablefilename[100], *rwsfilename;
66 static fsa genmult, genmult2, *genmult2ptr, mult1, mult2, *compmult;
67 static int ngens, neqns, *inv, nlabgm2;
68 static int numstoredmult;
69 static reduction_equation *eqn;
70 static gen *lhs, *rhs, ***labgm2;
71 static char **storedmult;
72 static boolean allshort;
73 static storage_type ip_store = DENSE;
74 static int dr = 0;
75 static storage_type op_store = SPARSE;
76 static boolean readback = TRUE;
77 static boolean keepfiles = TRUE;
78 static boolean usegm2 = TRUE;
79 static FILE *rfile, *wfile;
80 
81 /* Functions defined in this file */
82 
83 static void balance_equations(void);
84 static int check_short_relation(void);
85 static int check_long_relation(void);
86 static char *file_suffix(gen *w);
87 static int long_word_multiplier(gen *w, char *s);
88 static void badusage(void);
89 
main(int argc,char * argv[])90 int main(int argc, char *argv[])
91 {
92   int arg, i, ct, clr;
93   boolean seengpname, seencosname, cosets;
94 
95   setbuf(stdout, (char *)0);
96   setbuf(stderr, (char *)0);
97 
98   rwsptr = &rws;
99   rwsptr->maxeqns = MAXEQNS;
100   rwsptr->maxreducelen = MAXREDUCELEN;
101   rwsptr->maxreducelenset = FALSE;
102   rwsptr->cosets = FALSE;
103   /* even in the cosets case we read relations from original group file */
104   rwsptr->inv_of = 0;
105   rwsptr->weight = 0;
106   rwsptr->level = 0;
107   rwsptr->history = 0;
108   rwsptr->slowhistory = 0;
109   rwsptr->slowhistorysp = 0;
110   rwsptr->preflen = 0;
111   rwsptr->prefno = 0;
112   arg = 1;
113   seengpname = seencosname = cosets = FALSE;
114   while (argc > arg) {
115     if (strcmp(argv[arg], "-ip") == 0) {
116       arg++;
117       if (arg >= argc)
118         badusage();
119       if (strcmp(argv[arg], "d") == 0)
120         ip_store = DENSE;
121       else if (argv[arg][0] == 's') {
122         ip_store = SPARSE;
123         if (stringlen(argv[arg]) > 1)
124           dr = atoi(argv[arg] + 1);
125       }
126       else
127         badusage();
128     }
129     else if (strcmp(argv[arg], "-op") == 0) {
130       arg++;
131       if (arg >= argc)
132         badusage();
133       if (strcmp(argv[arg], "d") == 0)
134         op_store = DENSE;
135       else if (strcmp(argv[arg], "s") == 0)
136         op_store = SPARSE;
137       else
138         badusage();
139     }
140     else if (strcmp(argv[arg], "-silent") == 0)
141       kbm_print_level = 0;
142     else if (strcmp(argv[arg], "-v") == 0)
143       kbm_print_level = 2;
144     else if (strcmp(argv[arg], "-vv") == 0)
145       kbm_print_level = 3;
146     else if (strcmp(argv[arg], "-l") == 0)
147       kbm_large = TRUE;
148     else if (strcmp(argv[arg], "-h") == 0)
149       kbm_huge = TRUE;
150     else if (strcmp(argv[arg], "-f") == 0)
151       readback = FALSE;
152     else if (strcmp(argv[arg], "-s") == 0)
153       keepfiles = FALSE;
154     else if (strncmp(argv[arg], "-cos", 4) == 0)
155       cosets = TRUE;
156     else if (strcmp(argv[arg], "-x") == 0)
157       xset = TRUE;
158     else if (strcmp(argv[arg], "-n") == 0)
159       usegm2 = FALSE;
160     else if (argv[arg][0] == '-')
161       badusage();
162     else if (!seengpname) {
163       seengpname = TRUE;
164       strcpy(gpname, argv[arg]);
165     }
166     else if (!seencosname) {
167       seencosname = TRUE;
168       sprintf(cosgpname, "%s.%s", gpname, argv[arg]);
169     }
170     else
171       badusage();
172     arg++;
173   }
174   if (!seengpname)
175     badusage();
176   if (cosets && !seencosname)
177     sprintf(cosgpname, "%s.cos", gpname);
178 
179   if (cosets)
180     sprintf(outfec, "%s.axioms.ec", cosgpname);
181   else
182     sprintf(outfec, "%s.axioms.ec", gpname);
183 
184   rwsfilename = cosets ? cosgpname : gpname;
185   strcpy(tablefilename, rwsfilename);
186   strcat(tablefilename, "temp_axXXX");
187 
188   /* First read in the defining relations for the group. */
189   strcpy(inf, gpname);
190   if (xset)
191     strcat(inf, "_x");
192   if ((rfile = fopen(inf, "r")) == 0) {
193     fprintf(stderr, "Cannot open file %s.\n", inf);
194     exit(1);
195   }
196   read_kbinput_simple(rfile, FALSE, rwsptr);
197   fclose(rfile);
198   ngens = rws.num_gens;
199   neqns = rws.num_eqns;
200   inv = rws.inv_of;
201   eqn = rws.eqns;
202 
203   /* Now balance the equations. */
204   balance_equations();
205 
206   if (usegm2) {
207     allshort = TRUE;
208     /* See if all relations are short (length(lhs)=2 ) */
209     for (i = 1; i <= neqns; i++)
210       if (genstrlen(eqn[i].lhs) > 2) {
211         allshort = FALSE;
212         break;
213       }
214   }
215   else
216     allshort = FALSE;
217 
218   if (usegm2) {
219     /* Now calculate the general multiplier for words of length 2.
220      * If allshort is true, we don't need the transitions - only the state
221      * labels.
222      */
223     strcpy(inf, rwsfilename);
224     strcat(inf, ".gm");
225     if ((rfile = fopen(inf, "r")) == 0) {
226       fprintf(stderr, "Cannot open file %s.\n", inf);
227       exit(1);
228     }
229     fsa_read(rfile, &genmult, ip_store, dr, 0, TRUE, fsaname);
230     fclose(rfile);
231 
232     if (kbm_print_level > 1)
233       printf("  #Calculating general word-length 2 multiplier.\n");
234     if (allshort) {
235       genmult2ptr = fsa_genmult2(&genmult, op_store, TRUE, "", FALSE);
236       if (genmult2ptr == 0)
237         exit(1);
238       if (kbm_print_level > 1)
239         printf("  #Number of states of genmult2 = %d.\n",
240                genmult2ptr->states->size);
241     }
242     else {
243       genmult2ptr =
244           fsa_genmult2(&genmult, op_store, TRUE, tablefilename, readback);
245       if (genmult2ptr == 0)
246         exit(1);
247       if (kbm_print_level > 1)
248         printf("  #Number of states of genmult2 = %d.\n",
249                genmult2ptr->states->size);
250 
251       if (readback) {
252         if (fsa_labeled_minimize(genmult2ptr) == -1)
253           exit(1);
254       }
255       else {
256         if (fsa_ip_labeled_minimize(genmult2ptr) == -1)
257           exit(1);
258       }
259       if (kbm_print_level > 1)
260         printf("  #Number of states of genmult2 after minimization = %d.\n",
261                genmult2ptr->states->size);
262       strcpy(fsaname, rws.name);
263       strcat(fsaname, ".gm2");
264       strcpy(outf, rwsfilename);
265       strcat(outf, ".gm2");
266       wfile = fopen(outf, "w");
267       fsa_print(wfile, genmult2ptr, fsaname);
268       if (kbm_print_level > 0)
269         printf("#General length-2 multiplier with %d states computed.\n",
270                genmult2ptr->states->size);
271       fclose(wfile);
272     }
273 
274     /* Now we check axioms for inverse-relators and short-relators, by simply
275      * checking that the state-labels for the lhs and rhs correspond in
276      * *genmult2ptr.
277      */
278     if (kbm_print_level > 0) {
279       printf("#Checking inverse and short relations.\n");
280     }
281     nlabgm2 = genmult2ptr->states->labels->size;
282     labgm2 = genmult2ptr->states->labels->wordslist;
283     lhs = rws.testword1;
284     rhs = rws.testword2;
285     for (i = 1; i <= ngens; i++)
286       if (inv[i]) {
287         lhs[0] = i;
288         lhs[1] = inv[i];
289         lhs[2] = '\0';
290         rhs[0] = '\0';
291         if (check_short_relation() == 2)
292           exit(2);
293       }
294     for (i = 1; i <= neqns; i++)
295       if (genstrlen(eqn[i].lhs) == 2) {
296         lhs = eqn[i].lhs;
297         rhs = eqn[i].rhs;
298         if (check_short_relation() == 2)
299           exit(2);
300       }
301     fsa_clear(genmult2ptr);
302   }
303 
304   /* Finally we deal with the other relations, if any */
305   if (!allshort) {
306     /* If keepfiles is false, then we will throw away every multiplier
307      * immediately after it has been used. Otherwise, we will keep them, in case
308      * they are needed again. We store a list of words for which we have got the
309      * multipliers in storedmult. We first form a rough upper bound on how long
310      * this list could get - ngens + total relator length - 1.
311      */
312     strcpy(fsaname, rws.name);
313     strcat(fsaname, ".mult"); /* this is unimportant, since file is temporary */
314     if (keepfiles) {
315       ct = usegm2 ? ngens : 2 * ngens;
316       for (i = 1; i <= neqns; i++)
317         if (!usegm2 || genstrlen(eqn[i].lhs) > 2)
318           ct += (genstrlen(eqn[i].lhs) + genstrlen(eqn[i].rhs));
319       tmalloc(storedmult, char *, ct);
320       numstoredmult = 0;
321     }
322 
323     if (!usegm2)
324       for (i = 1; i <= ngens; i++)
325         if (inv[i]) {
326           lhs = rws.testword1;
327           rhs = rws.testword2;
328           lhs[0] = i;
329           lhs[1] = inv[i];
330           lhs[2] = '\0';
331           rhs[0] = '\0';
332           clr = check_long_relation();
333           if (clr == -1)
334             exit(1);
335           if (clr == 2)
336             exit(2);
337         }
338     for (i = 1; i <= neqns; i++)
339       if (!usegm2 || genstrlen(eqn[i].lhs) > 2) {
340         lhs = eqn[i].lhs;
341         rhs = eqn[i].rhs;
342         clr = check_long_relation();
343         if (clr == -1)
344           exit(1);
345         if (clr == 2)
346           exit(2);
347       }
348     if (keepfiles) {
349       for (i = 1; i <= numstoredmult; i++) {
350         sprintf(outf, "%s.m%s", rwsfilename, storedmult[i]);
351         unlink(outf);
352         tfree(storedmult[i]);
353       }
354       tfree(storedmult);
355     }
356     strcpy(outf, rwsfilename);
357     strcat(outf, ".gm2");
358     unlink(outf);
359   }
360   tfree(genmult2ptr);
361   rws_clear(&rws);
362   if (kbm_print_level > 0)
363     printf("#Axiom checking succeeded.\n");
364   wfile = fopen(outfec, "w");
365   fprintf(wfile, "_ExitCode := 0;\n");
366   fclose(wfile);
367   exit(0);
368 }
369 
balance_equations(void)370 void balance_equations(void)
371 {
372   int i, l1, l2;
373   gen *lhs, *rhs;
374   if (kbm_print_level > 1)
375     printf("  #Simplifying and balancing inverse-list and relations.\n");
376 
377   for (i = 1; i <= neqns; i++) {
378     lhs = eqn[i].lhs;
379     rhs = eqn[i].rhs;
380     l1 = genstrlen(lhs);
381     l2 = genstrlen(rhs);
382     if (l1 + l2 <= 2) { /*We no longer need keep this equation */
383       if (kbm_print_level > 1)
384         printf("  #Equation number %d is being discarded.\n", i);
385       lhs[0] = rhs[0] = '\0';
386     }
387     else {           /* balance the lengths of the lhs and rhs. */
388       if (l2 > l1) { /* swap lhs and rhs */
389         eqn[i].lhs = rhs;
390         eqn[i].rhs = lhs;
391         lhs = eqn[i].lhs;
392         rhs = eqn[i].rhs;
393         l1 = genstrlen(lhs);
394         l2 = genstrlen(rhs);
395       }
396       if (!xset)
397         while (l1 - l2 > 1) { /* move a generator from lhs to rhs */
398           rhs[l2] = inv[lhs[l1 - 1]];
399           l2++;
400           rhs[l2] = '\0';
401           l1--;
402           lhs[l1] = '\0';
403         }
404     }
405   }
406 }
407 
408 /* Check that the general multiplier automaton *genmult2ptr for words of
409  * length two satisfies the short-equation lhs = rhs.
410  * This is done merely by looking at the state-labels of *genmult2ptr.
411  * The transitions are not needed.
412  */
check_short_relation(void)413 int check_short_relation(void)
414 {
415   int i, j;
416   gen **lab;
417   boolean foundlhs, foundrhs;
418   if (kbm_print_level > 1) {
419     kbm_buffer[0] = '\0';
420     add_to_buffer(0, "  #Checking short relation:  ");
421     add_word_to_buffer(stdout, lhs, rws.gen_name);
422     add_to_buffer(0, " = ");
423     add_word_to_buffer(stdout, rhs, rws.gen_name);
424     printbuffer(stdout);
425   }
426   /* pad rhs to length 2 if necessary */
427   if (genstrlen(rhs) == 0) {
428     rhs[0] = rhs[1] = ngens + 1;
429     rhs[2] = '\0';
430   }
431   else if (genstrlen(rhs) == 1) {
432     rhs[1] = ngens + 1;
433     rhs[2] = '\0';
434   }
435   /* Search through the state-labels of genmult2ptr.
436    * For the relation to be satisfied by the fsa, it must be the case that
437    * lhs occurs in a label <=> rhs occurs in that label.
438    */
439   for (i = 1; i <= nlabgm2; i++) {
440     foundlhs = foundrhs = FALSE;
441     lab = labgm2[i];
442     j = 0;
443     while (lab[j]) {
444       if (lab[j][0] == lhs[0] && lab[j][1] == lhs[1])
445         foundlhs = TRUE;
446       if (lab[j][0] == rhs[0] && lab[j][1] == rhs[1])
447         foundrhs = TRUE;
448       j++;
449     }
450     if (foundlhs != foundrhs) {
451       kbm_buffer[0] = '\0';
452       add_to_buffer(0, "#Relation check fails for short relation:  ");
453       add_word_to_buffer(stdout, lhs, rws.gen_name);
454       add_to_buffer(0, " = ");
455       add_word_to_buffer(stdout, rhs, rws.gen_name);
456       printbuffer(stdout);
457       wfile = fopen(outfec, "w");
458       fprintf(wfile, "_ExitCode := 2;\n");
459       fclose(wfile);
460       return 2;
461     }
462   }
463   return 0;
464 }
465 
466 /* Check that the multipliers satisfy the long relation of which the
467  * left and right hand sides are store din lhs and rhs, by forming
468  * the necessary composites.
469  */
check_long_relation(void)470 int check_long_relation(void)
471 {
472   int j, l;
473   char *suffl, *suffr;
474   boolean gotl, gotr;
475   if (kbm_print_level > 0) {
476     kbm_buffer[0] = '\0';
477     add_to_buffer(0, "#Checking relation:  ");
478     add_word_to_buffer(stdout, lhs, rws.gen_name);
479     add_to_buffer(0, " = ");
480     if ((l = stringlen(kbm_buffer)) > 44) {
481       printbuffer(stdout);
482       add_to_buffer(21, "");
483     }
484     add_word_to_buffer(stdout, rhs, rws.gen_name);
485     printbuffer(stdout);
486   }
487   suffl = file_suffix(lhs);
488   suffr = file_suffix(rhs);
489   gotl = gotr = FALSE;
490   if (keepfiles) {
491     /* Check to see if we have either of these multipliers already */
492     for (j = 1; j <= numstoredmult; j++) {
493       if (strcmp(suffl, storedmult[j]) == 0)
494         gotl = TRUE;
495       if (strcmp(suffr, storedmult[j]) == 0)
496         gotr = TRUE;
497     }
498   }
499   if (!gotl) {
500     if (long_word_multiplier(lhs, suffl) == -1)
501       return -1;
502   }
503   if (!gotr) {
504     if (long_word_multiplier(rhs, suffr) == -1)
505       return -1;
506   }
507   /* Read in the two multipliers and compare them */
508   sprintf(inf, "%s.m%s", rwsfilename, suffl);
509 
510   if ((rfile = fopen(inf, "r")) == 0) {
511     fprintf(stderr, "Cannot open file %s.\n", inf);
512     exit(1);
513   }
514   fsa_read(rfile, &mult1, ip_store, 0, 0, TRUE, fsaname);
515   fclose(rfile);
516   sprintf(inf, "%s.m%s", rwsfilename, suffr);
517   if ((rfile = fopen(inf, "r")) == 0) {
518     fprintf(stderr, "Cannot open file %s.\n", inf);
519     exit(1);
520   }
521   fsa_read(rfile, &mult2, ip_store, 0, 0, TRUE, fsaname);
522   fclose(rfile);
523   if (!fsa_equal(&mult1, &mult2)) {
524     kbm_buffer[0] = '\0';
525     add_to_buffer(0, "#Relation check fails for relation:  ");
526     add_word_to_buffer(stdout, lhs, rws.gen_name);
527     add_to_buffer(0, " = ");
528     add_word_to_buffer(stdout, rhs, rws.gen_name);
529     printbuffer(stdout);
530     wfile = fopen(outfec, "w");
531     fprintf(wfile, "_ExitCode := 2;\n");
532     fclose(wfile);
533     return 2;
534   }
535   fsa_clear(&mult1);
536   fsa_clear(&mult2);
537   if (keepfiles) {
538     if (gotl)
539       tfree(suffl) else storedmult[++numstoredmult] = suffl;
540     if (gotr)
541       tfree(suffr) else storedmult[++numstoredmult] = suffr;
542   }
543   else {
544     sprintf(inf, "%s.m%s", rwsfilename, suffl);
545     unlink(inf);
546     sprintf(inf, "%s.m%s", rwsfilename, suffr);
547     unlink(inf);
548     tfree(suffl);
549     tfree(suffr);
550   }
551   return 0;
552 }
553 
554 /* For a word w in the generators, this function returns a corresponding
555  * string with the terms of w replaced by integers separated by '_'.
556  * This is used as a suffix in the filenames used for storing the
557  * corresponding multiplier fsa's.
558  */
file_suffix(gen * w)559 char *file_suffix(gen *w)
560 {
561   char *s;
562   gen *p;
563   boolean first;
564   int len;
565   /* First work out the length of the required string. */
566   len = genstrlen(w);
567   if (len == 0) {
568     tmalloc(s, char, 2);
569     s[0] = '0';
570     s[1] = '\0';
571     return s;
572   }
573   p = w - 1;
574   while (*(++p) != 0)
575     len += int_len((int)(*p));
576   tmalloc(s, char, len);
577   s[0] = '\0';
578   first = TRUE;
579   p = w - 1;
580   while (*(++p) != 0) {
581     if (first)
582       first = FALSE;
583     else
584       sprintf(s + stringlen(s), "_");
585     sprintf(s + stringlen(s), "%d", *p);
586   }
587   return s;
588 }
589 
590 /* Calculate the multiplier associated with the word w.
591  * s is the suffix of the file in which it will be stored.
592  * (s has been derived from w by a call of file_suffix).
593  */
long_word_multiplier(gen * w,char * s)594 int long_word_multiplier(gen *w, char *s)
595 {
596   int i, l;
597   gen *wl, *wlt, *wr, *wrt;
598   char *suffl, *sufflt, *suffr, *suffrt;
599   boolean gotl, gotr, gotlt, gotrt;
600   if (kbm_print_level >= 3) {
601     kbm_buffer[0] = '\0';
602     add_to_buffer(0, "  #Calculating multiplier for word:  ");
603     add_word_to_buffer(stdout, w, rws.gen_name);
604     printbuffer(stdout);
605   }
606   l = genstrlen(w);
607 
608   if (l <= 1) { /* Length <=1 - use fsa_makemult */
609     strcpy(inf, rwsfilename);
610     strcat(inf, ".gm");
611     if ((rfile = fopen(inf, "r")) == 0) {
612       fprintf(stderr, "Cannot open file %s.\n", inf);
613       exit(1);
614     }
615     fsa_read(rfile, &genmult, op_store, 0, 0, TRUE, fsaname);
616     fclose(rfile);
617     if (fsa_makemult(&genmult, w[0]) == -1)
618       return -1;
619     if (fsa_minimize(&genmult) == -1)
620       return -1;
621     sprintf(outf, "%s.m%s", rwsfilename, s);
622     wfile = fopen(outf, "w");
623     fsa_print(wfile, &genmult, fsaname);
624     fclose(wfile);
625     fsa_clear(&genmult);
626   }
627   else if (usegm2 && l == 2) { /* Length 2 - use fsa_makemult2 */
628     strcpy(inf, rwsfilename);
629     strcat(inf, ".gm2");
630     if ((rfile = fopen(inf, "r")) == 0) {
631       fprintf(stderr, "Cannot open file %s.\n", inf);
632       exit(1);
633     }
634     fsa_read(rfile, &genmult2, op_store, 0, 0, TRUE, fsaname);
635     fclose(rfile);
636     if (fsa_makemult2(&genmult2, w[0], w[1]) == -1)
637       return -1;
638     if (fsa_minimize(&genmult2) == -1)
639       return -1;
640     sprintf(outf, "%s.m%s", rwsfilename, s);
641     wfile = fopen(outf, "w");
642     fsa_print(wfile, &genmult2, fsaname);
643     fclose(wfile);
644     fsa_clear(&genmult2);
645   }
646   else { /* general case - we have to split w up */
647     if (l % 2 == 0) {
648       tmalloc(wl, gen, l / 2 + 1);
649       tmalloc(wr, gen, l / 2 + 1);
650       for (i = 0; i < l / 2; i++)
651         wl[i] = w[i];
652       wl[l / 2] = '\0';
653       genstrcpy(wr, w + l / 2);
654       suffl = file_suffix(wl);
655       suffr = file_suffix(wr);
656     }
657     else {
658       tmalloc(wl, gen, l / 2 + 2);
659       tmalloc(wr, gen, l / 2 + 1);
660       for (i = 0; i <= l / 2; i++)
661         wl[i] = w[i];
662       wl[l / 2 + 1] = '\0';
663       genstrcpy(wr, w + l / 2 + 1);
664       suffl = file_suffix(wl);
665       suffr = file_suffix(wr);
666     }
667     /* See whether we have either of them already */
668     gotl = gotr = FALSE;
669     if (keepfiles) {
670       for (i = 1; i <= numstoredmult; i++) {
671         if (strcmp(suffl, storedmult[i]) == 0)
672           gotl = TRUE;
673         if (strcmp(suffr, storedmult[i]) == 0)
674           gotr = TRUE;
675       }
676     }
677 
678     if (keepfiles && l % 2 == 1 && (!gotl || !gotr)) {
679       /* In this case, there are two possible ways to split w up -
680        * we see if the other way has more multipliers already stored.
681        */
682       tmalloc(wlt, gen, l / 2 + 1);
683       tmalloc(wrt, gen, l / 2 + 2);
684       for (i = 0; i < l / 2; i++)
685         wlt[i] = w[i];
686       wlt[l / 2] = '\0';
687       genstrcpy(wrt, w + l / 2);
688       sufflt = file_suffix(wlt);
689       suffrt = file_suffix(wrt);
690       gotlt = gotrt = FALSE;
691       for (i = 1; i <= numstoredmult; i++) {
692         if (strcmp(sufflt, storedmult[i]) == 0)
693           gotlt = TRUE;
694         if (strcmp(suffrt, storedmult[i]) == 0)
695           gotrt = TRUE;
696       }
697       if ((gotlt && gotrt) || ((gotlt || gotrt) && !gotl && !gotr)) {
698         tfree(wl);
699         tfree(wr);
700         tfree(suffl);
701         tfree(suffr);
702         wl = wlt;
703         wr = wrt;
704         suffl = sufflt;
705         suffr = suffrt;
706         gotl = gotlt;
707         gotr = gotrt;
708       }
709       else {
710         tfree(wlt);
711         tfree(wrt);
712         tfree(sufflt);
713         tfree(suffrt);
714       }
715     }
716     if (!gotl) {
717       if (long_word_multiplier(wl, suffl) == -1)
718         return -1;
719     }
720     if (!gotr && genstrcmp(wl, wr) != 0) {
721       if (keepfiles) {
722         /* Check again to see if we have got it recently */
723         for (i = 1; i <= numstoredmult; i++)
724           if (strcmp(suffr, storedmult[i]) == 0)
725             gotr = TRUE;
726       }
727       if (!gotr) {
728         if (long_word_multiplier(wr, suffr) == -1)
729           return -1;
730       }
731     }
732     /* Read back in the two multipliers and form their composite */
733     sprintf(inf, "%s.m%s", rwsfilename, suffl);
734     if ((rfile = fopen(inf, "r")) == 0) {
735       fprintf(stderr, "Cannot open file %s.\n", inf);
736       exit(1);
737     }
738     fsa_read(rfile, &mult1, ip_store, dr, 0, TRUE, fsaname);
739     fclose(rfile);
740     sprintf(inf, "%s.m%s", rwsfilename, suffr);
741     if ((rfile = fopen(inf, "r")) == 0) {
742       fprintf(stderr, "Cannot open file %s.\n", inf);
743       exit(1);
744     }
745     fsa_read(rfile, &mult2, ip_store, dr, 0, TRUE, fsaname);
746     fclose(rfile);
747 
748     compmult =
749         fsa_composite(&mult1, &mult2, op_store, TRUE, tablefilename, readback);
750     if (compmult == 0)
751       return -1;
752     fsa_clear(&mult1);
753     fsa_clear(&mult2);
754     if (readback) {
755       if (fsa_minimize(compmult) == -1)
756         return -1;
757     }
758     else {
759       if (fsa_ip_minimize(compmult) == -1)
760         return -1;
761     }
762     sprintf(outf, "%s.m%s", rwsfilename, s);
763     wfile = fopen(outf, "w");
764     fsa_print(wfile, compmult, fsaname);
765     fclose(wfile);
766     fsa_clear(compmult);
767     tfree(compmult);
768 
769     if (keepfiles) {
770       if (gotl)
771         tfree(suffl) else storedmult[++numstoredmult] = suffl;
772       if (gotr)
773         tfree(suffr) else storedmult[++numstoredmult] = suffr;
774     }
775     else {
776       sprintf(inf, "%s.m%s", rwsfilename, suffl);
777       unlink(inf);
778       sprintf(inf, "%s.m%s", rwsfilename, suffr);
779       unlink(inf);
780       tfree(suffl);
781       tfree(suffr);
782     }
783     tfree(wl);
784     tfree(wr);
785   }
786   return 0;
787 }
788 
badusage(void)789 void badusage(void)
790 {
791   fprintf(stderr, "Usage: gpaxioms [-ip d/s[dr]] [-op d/s] [-silent] [-v] [-l] "
792                   "[-f] [-n]\n");
793   fprintf(stderr, "\t\t[-cos] groupname [cosname].\n");
794   exit(1);
795 }
796