1 
2 /**************************************************************************
3 
4         parser.c
5         Colin Ramsay (cram@itee.uq.edu.au)
6 	2 Mar 01
7 
8         ADVANCED COSET ENUMERATOR, Version 3.001
9 
10         Copyright 2000
11         Centre for Discrete Mathematics and Computing,
12         Department of Mathematics and
13           Department of Computer Science & Electrical Engineering,
14         The University of Queensland, QLD 4072.
15 	(http://staff.itee.uq.edu.au/havas)
16 
17 Parser and dispatcher code for stand-alone ACE.  We try to ensure that we
18 only change things in response to a command if the entire command is ok.
19 This means that the state is always consistent, and we can usually just
20 continue.
21 
22 Note: the al2_continue() routine is intended for cases where an `error'
23 does not effect the ability to continue, while al2_restart() is intended
24 for errors which (may) mean that continuing is not possible, so we have to
25 (re)start an enumeration.  I'm not sure that I'm always careful in calling
26 the `right' one; we may have to tinker with this in the light of
27 experience.
28 
29 **************************************************************************/
30 
31 #include "al2.h"
32 
33 #include <ctype.h>
34 #include <string.h>
35 
36 int al2_pwrd(int);	/* Forward declaration (parser is recursive) */
37 
38 	/******************************************************************
39         void al2_readkey(void)
40 
41         Read a keyword into currkey[], converting it to LC.  This removes
42 	all leading WS, compresses middle WS to single ' ', and removes
43         trailing WS.  It checks for bad characters and too short/long key,
44         and advances position to argument (if necessary).  Note that
45 	currkey has 64 posns (0..63), and we have to reserve one for the
46 	string terminating '\0' character.
47 	******************************************************************/
48 
al2_readkey(void)49 void al2_readkey(void)
50   {
51   int i = 0;
52 
53   /* Copy the keyword into currkey[] */
54   while ( currip != ':' && currip != ';' && currip != '\n'
55             && currip != '\r' && currip != EOF )
56     {
57     if (islower(currip))
58      {
59      if (i > 62)
60        { al2_continue("keyword too long"); }
61      currkey[i++] = currip;
62      }
63     else if (isupper(currip))
64       {
65       if (i > 62)
66         { al2_continue("keyword too long"); }
67       currkey[i++] = tolower(currip);
68       }
69     else if (currip == ' ' || currip == '\t')
70       {
71       if (i > 0 && currkey[i-1] != ' ')	/* leading/multiple spaces? */
72         {
73         if (i > 63)          	/* may be removable trailing space */
74           { al2_continue("keyword too long"); }
75         currkey[i++] = ' ';
76         }
77       }
78     else
79       { al2_continue("keywords must only contain letters"); }
80     al2_nextip();
81     }
82 
83   if (i > 0 && currkey[i-1] == ' ')	/* remove trailing space */
84     { i--; }
85   currkey[i] = '\0';              	/* string terminator */
86 
87   if (i == 0)
88     { al2_continue("empty keyword"); }
89 
90   if (currip == ':')            	/* skip any following ':' & WS */
91     { al2_nextnw(); }
92   }
93 
94 	/******************************************************************
95         void al2_readname(void)
96 
97         Read a `name' (ie, command argument).  Used for group/subgroup
98 	names/descriptions, I/O filenames, and system calls.  There is only
99 	one of these (a fixed length <128 global), so we may need to take a
100 	copy if it'll be required later.  Note that currip has been setup
101 	to point to a non-blank char (ie, either the first char of the
102 	string or an end-of-command char).  Note that we strip trailing
103 	spaces & tabs from the name, for `neatness'.  We assume ASCII.
104 	******************************************************************/
105 
al2_readname(void)106 void al2_readname(void)
107   {
108   int i = 0;
109 
110   while ( currip != ';' && currip != '\n' && currip != '\r' &&
111           currip != EOF )
112     {
113     if (!((currip >= ' ' && currip <= '~') || (currip == '\t')))
114       { al2_continue("string contains invalid character"); }
115     if (i > 126)                        /* 0..126 is data, 127 is '\0' */
116       { al2_continue("string too long"); }
117 
118     currname[i++] = currip;
119     al2_nextip();
120     }
121 
122   while (i > 0 && (currname[i-1] == ' ' || currname[i-1] == '\t'))
123     { i--; }
124   currname[i] = '\0';
125   }
126 
127 	/******************************************************************
128 	int al2_readmult(void)
129 
130 	Reads the multiplier for the workspace size, if we recognise it.
131 	******************************************************************/
132 
al2_readmult(void)133 int al2_readmult(void)
134   {
135   int u = 1;				/* Default is x1 */
136 
137   if (currip == 'k' || currip == 'K')
138     {
139     u = KILO;
140     al2_nextnw();
141     }
142   else if (currip == 'm' || currip == 'M')
143     {
144     u = MEGA;
145     al2_nextnw();
146     }
147   else if (currip == 'g' || currip == 'G')
148     {
149     u = GIGA;
150     al2_nextnw();
151     }
152 
153   return u;
154   }
155 
156 	/******************************************************************
157 	int al2_readgen(void)
158 
159 	Reads in a (possibly comma separated) list of generator letters.
160 	These are stored (in lower case) in the order they're read in the
161 	currname array.  Duplicates are verboten and the number of
162 	generators read is returned.  Currip is guaranteed to be a letter,
163 	so j > 0 on return is certain (in the absence of errors).
164 	******************************************************************/
165 
al2_readgen(void)166 int al2_readgen(void)
167   {
168   int i, j = 0;
169 
170   while ( currip != ';' && currip != '\n' && currip != '\r' &&
171           currip != EOF )
172     {
173     if (islower(currip))
174       {
175       for (i = 1; i <= j; i++)
176         {
177         if (currname[i] == currip)
178           { al2_continue("duplicated generator"); }
179         }
180       currname[++j] = currip;
181       }
182     else
183       { al2_continue("generators are letters between 'a' & 'z'"); }
184 
185     al2_nextnw();
186     if (currip == ',')
187       { al2_nextnw(); }
188     }
189 
190   return(j);
191   }
192 
193 	/******************************************************************
194 	Logic al2_match(char *pattern)
195 
196 	Test whether currkey can be matched to pattern.
197 	******************************************************************/
198 
al2_match(char * pattern)199 Logic al2_match(char *pattern)
200   {
201   int i;
202 
203   /* first try to match the required part */
204   for (i = 0; pattern[i] != '\0' && pattern[i] != '['; i++)
205     {
206     if (pattern[i] != currkey[i])
207       { return FALSE; }
208     }
209 
210   /* if the rest is optional, try to match it */
211   if (pattern[i] == '[')
212     {
213     for ( ; pattern[i+1] != '\0' && pattern[i+1] != ']'; i++)
214       {
215       if (pattern[i+1] != currkey[i])
216         { return (currkey[i] == '\0'); }
217       }
218     }
219 
220   /* everything matched, but the keyword should not be longer */
221   return (currkey[i] == '\0');
222   }
223 
224 	/******************************************************************
225         void al2_endcmd(void)
226 
227 	To terminate a command, we must see a ';' or a newline.
228 	******************************************************************/
229 
al2_endcmd(void)230 void al2_endcmd(void)
231   {
232   if (currip != ';' && currip != '\n' && currip != '\r' && currip != EOF)
233     { al2_continue("command must be terminated by ';' or <newline>"); }
234   }
235 
236 	/******************************************************************
237         int al2_readuint(void)
238 
239 	Read in an unsigned integer
240 	******************************************************************/
241 
al2_readuint(void)242 int al2_readuint(void)
243   {
244   int u = 0;
245 
246   if (isdigit(currip))
247     {
248     while (isdigit(currip))
249       {
250       u = 10*u + (currip - '0');
251       al2_nextip();
252       }
253     al2_skipws();
254     }
255   else
256     { al2_continue("number must begin with digit"); }
257 
258   return(u);
259   }
260 
261 	/******************************************************************
262         int al2_readint(void)
263 
264 	Read in a (possibly signed) integer
265 	******************************************************************/
266 
al2_readint(void)267 int al2_readint(void)
268   {
269   if (isdigit(currip))
270     { return(al2_readuint()); }
271   else if (currip == '+')
272     {
273     al2_nextnw();
274     return(al2_readuint());
275     }
276   else if (currip == '-')
277     {
278     al2_nextnw();
279     return(-al2_readuint());
280     }
281   else
282     { al2_continue("number must begin with digit or '+' or '-'"); }
283 
284   return(-1);		/* Stops compiler warning; never get here! */
285   }
286 
287 	/******************************************************************
288         void al2_readia(void)
289 
290 	Read comma-separated list of <= 32 integers into the integer array.
291 	******************************************************************/
292 
al2_readia(void)293 void al2_readia(void)
294   {
295   intcnt = 0;
296 
297   if ( !(isdigit(currip) || currip == '+' || currip == '-') )
298     { return; }					/* list is empty */
299 
300   intarr[intcnt++] = al2_readint();
301   while (currip == ',')
302     {
303     if (intcnt == 32)
304       { al2_continue("too many integers in sequence"); }
305 
306     al2_nextnw();
307     intarr[intcnt++] = al2_readint();
308     }
309   }
310 
311 /**************************************************************************
312 The functions from hereon, until al2_cmdloop(), are responsible for
313 implementing the recursive-descent parser.  The current word is built-up in
314 currword, and when this has been done successfully it is added to a temp
315 list of words.  If an error occurs, then this list will be `valid'; it will
316 contain all words up to, but not including, the one in error.  Currently
317 this list is accessed via a pointer in the `top-level' function _rdwl() or
318 _rdrl().  This pointer should really be made a global, so that we could
319 attempt error-recovery or free up the space it uses (currently, errors may
320 cause memory leakage).  A successful call to either of the top-level
321 functions returns a new list, which should be used to replace the current
322 list of either group relators or subgroup generators.  It is the caller's
323 (of the parser) responsibility to deallocate any replaced list.
324 **************************************************************************/
325 
326 	/******************************************************************
327 	void al2_addgen(int pos, int gen)
328 
329 	Add a generator to the current word, growing the word as necessary.
330 	******************************************************************/
331 
al2_addgen(int pos,int gen)332 void al2_addgen(int pos, int gen)
333   {
334   if (currword == NULL)
335     {
336     currsiz = 16;
337     if ((currword = (int *)malloc(currsiz*sizeof(int))) == NULL)
338       { al2_continue("out of memory (initial word)"); }
339     }
340   else if (pos >= currsiz)	/* valid entries are [0] .. [currsiz-1] */
341     {
342     currsiz *= 2;
343     if ((currword = (int *)realloc(currword, currsiz*sizeof(int))) == NULL)
344       { al2_continue("out of memory (adding generator)"); }
345     }
346 
347   currword[pos] = gen;
348   }
349 
350 	/******************************************************************
351 	void al2_addwrd(int dst, int src, int len)
352 
353 	Add a word to the current word.  Note that this is used to copy
354 	from currword to itself, so either dst <= src or dst >= src+len.
355 	******************************************************************/
356 
al2_addwrd(int dst,int src,int len)357 void al2_addwrd(int dst, int src, int len)
358   {
359   int i;
360 
361   for (i = 0; i < len; i++)
362     { al2_addgen(dst+i, currword[src+i]); }
363   }
364 
365 	/******************************************************************
366 	void al2_invwrd(int pos, int len)
367 
368 	Sneakily invert a subword of the current word.  Note that we have
369 	to reverse the order _and_ invert all entries.  So we have to touch
370 	all posns; hence some of the apparently unnecessary work.
371 	******************************************************************/
372 
al2_invwrd(int pos,int len)373 void al2_invwrd(int pos, int len)
374   {
375   int i, gen1, gen2;
376 
377   for (i = 1; i <= (len+1)/2; i++)
378     {
379     gen1 = currword[pos + i-1];
380     gen2 = currword[pos + len-i];
381 
382     currword[pos + i-1]   = -gen2;
383     currword[pos + len-i] = -gen1;
384     }
385   }
386 
387 	/******************************************************************
388 	Wlelt *al2_newwrd(int len)
389 
390 	Make a new word-list element, and copy the first len values from
391 	currword into it.  Note that currword is indexed from 0, while data
392 	in the list is indexed from 1!  At this stage all words are fully
393 	expanded, and have exponent 1.  However, we need to flag those
394 	words which were _entered_ as involutions (ie, as x^2, not xx).
395 	******************************************************************/
396 
al2_newwrd(int len)397 Wlelt *al2_newwrd(int len)
398   {
399   Wlelt *p;
400   int i;
401 
402   if ((p = al1_newelt()) == NULL)
403     { al2_restart("no memory for new word-list element"); }
404   if ((p->word = (int *)malloc((len+1)*sizeof(int))) == NULL)
405     { al2_restart("no memory for word-list element data"); }
406 
407   for (i = 1; i <= len; i++)
408     { p->word[i] = currword[i-1]; }
409   p->len = len;
410   p->exp = 1;
411 
412   if (len == 2 && currword[0] == currword[1] && currexp == 2)
413     { p->invol = TRUE; }
414   else
415     { p->invol = FALSE; }
416 
417   return(p);
418   }
419 
420 	/******************************************************************
421 	int al2_pelt(int beg)
422 
423 	Parses an element into currword, beginning at position beg, and
424 	returns the length of the parsed element.  The BNF for an element:
425 
426 		<element> = <generator> ["'"]
427 			  | "(" <word> { "," <word> } ")" ["'"]
428 			  | "[" <word> { "," <word> } "]" ["'"]
429 
430 	Note that (a,b) is parsed as [a,b], but (ab) as ab.  Also, [a,b,c]
431 	is parsed as [[a,b],c].
432 	******************************************************************/
433 
al2_pelt(int beg)434 int al2_pelt(int beg)
435   {
436   int len, len2, gen, sign;
437   char ch;
438 
439   if (isalpha(currip))		/* we have 'a'..'z' or 'A'..'Z' */
440     {
441     if (!galpha)
442       { al2_restart("you specified numeric generators"); }
443 
444     if (islower(currip))
445       {
446       ch = currip;
447       sign = 1;
448       }
449     else
450       {
451       ch = tolower(currip);
452       sign = -1;
453       }
454     al2_nextnw();
455 
456     gen = genal[ch-'a'+1];
457     if (gen == 0)
458       { al2_restart("<letter> must be one of the generator letters"); }
459     al2_addgen(beg, sign*gen);
460     len = 1;
461     }
462   else if (isdigit(currip) || currip == '+' || currip == '-')
463     {				/* parse a numeric generator */
464     if (galpha)
465       { al2_restart("you specified alphabetic generators"); }
466 
467     sign = 1;
468     if (currip == '+')
469       {
470       al2_nextnw();
471       if (!isdigit(currip))
472         { al2_restart("'+' must be followed by generator number"); }
473       }
474     else if (currip == '-')
475       {
476       al2_nextnw();
477       if (!isdigit(currip))
478         { al2_restart("'-' must be followed by generator number"); }
479       sign = -1;
480       }
481 
482     gen = al2_readuint();
483     if (gen == 0 || gen > ndgen)
484       { al2_restart("<number> must be one of the generator numbers"); }
485     al2_addgen(beg, sign*gen);
486     len = 1;
487     }
488   else if (currip == '(' || currip == '[')
489     { 				/* parse parenthesised word / commutator */
490     ch = currip;
491     al2_nextnw();
492     len = al2_pwrd(beg);
493 
494     while (currip == ',')
495       {
496       al2_nextnw();
497       len2 = al2_pwrd(beg+len);
498       al2_addwrd(beg+len+len2, beg, len+len2);
499       al2_invwrd(beg, len);
500       al2_invwrd(beg+len, len2);
501       len = 2*(len + len2);
502       }
503 
504     if (ch == '(' && currip != ')')
505       { al2_restart("'(' must have a matching ')'"); }
506     if (ch == '[' && currip != ']')
507       { al2_restart("'[' must have a matching ']'"); }
508     al2_nextnw();
509     }
510   else				/* otherwise this is an error */
511     {
512     al2_restart("<word> must begin with a <generator>, a '(' or a '['");
513     }
514 
515   /* A "'" inverts the current element.  "''" is not allowed. */
516 
517   if (currip == '\'')
518     {
519     al2_invwrd(beg, len);
520     al2_nextnw();
521     }
522 
523   return len;                   /* return the length */
524   }
525 
526 	/******************************************************************
527 	int al2_pfact(int beg)
528 
529 	Parses a factor into currword, beginning at position beg, and
530 	returns the length of the parsed factor.  The BNF for a factor:
531 
532 		<factor> = <element> [ ["^"] <integer> | "^" <element> ]
533 
534 	Note that if alphabetic generators are used then the exponentiation
535 	"^" can be dropped (but not the conjugation "^"), and the exponent
536 	"-1" can be abbreviated to "-".  So "a^-1 b" can be written as
537 	"a^-1b", "a-1b", "a^-b", or "a-b".
538 
539 	******************************************************************/
540 
al2_pfact(int beg)541 int al2_pfact(int beg)
542   {
543   int len, len2, i;
544 
545   len = al2_pelt(beg);			/* parse (first) element */
546 
547   if ( currip == '^' ||
548        (galpha && (isdigit(currip) || currip == '+' || currip == '-')) )
549     {
550     if (currip == '^')			/* strip away the '^' */
551       { al2_nextnw(); }
552 
553     if (isdigit(currip) || currip == '-' || currip == '+')
554       {
555       if (currip == '+')
556         {
557         al2_nextnw();
558         if (!galpha && !isdigit(currip))
559           { al2_restart("'+' must be followed by exponent number"); }
560         }
561       else if (currip == '-')
562         {
563         al2_invwrd(beg, len);
564         al2_nextnw();
565         if (!galpha && !isdigit(currip))
566           { al2_restart("'-' must be followed by exponent number"); }
567         }
568 
569       /* If we're using alphabetic generators & dropping the "^", then
570       "a^-1" can be coded as "a-", so we might not have a digit here.
571       We'll fall through, using the element as already parsed! */
572 
573       if (isdigit(currip))
574         {
575         currexp = al2_readuint();
576         for (i = 2; i <= currexp; i++)
577           { al2_addwrd(beg + (i-1)*len, beg, len); }
578         len = len*currexp;
579         }
580       }
581     else if (isalpha(currip) || currip == '(' || currip == '[')
582       {
583       /* This is sneaky! */
584 
585       len2 = al2_pelt(beg+len);
586       al2_addwrd(beg+len+len2, beg+len, len2);
587       al2_invwrd(beg, len);
588       al2_invwrd(beg, len+len2);
589       len = len2 + len + len2;
590       }
591     else
592       { al2_restart("'^' must be followed by exponent or element"); }
593     }
594 
595   return len;
596   }
597 
598 	/******************************************************************
599 	int al2_pwrd(int beg)
600 
601 	Parses a word into currword starting at position beg.  Words are
602 	defined by the following BNF:
603 
604 		<word> = <factor> { "*" | "/" <factor> }
605 
606 	The "*" can be dropped everywhere; but of course two numeric
607 	generators, or a numeric exponent and a numeric generator, must be
608 	separated by a whitespace.
609 
610 	We use currexp to help detect when a relator/generator of the form
611 	x^2/X^2 (or one of its variants) has been entered.  At the _start_
612 	of every word we prime it to 1.
613 	******************************************************************/
614 
al2_pwrd(int beg)615 int al2_pwrd(int beg)
616   {
617   int len, len2;
618   char ch;
619 
620   if (beg == 0)
621     { currexp = 1; }
622 
623   len = al2_pfact(beg);
624 
625   while ( currip == '*'   || currip == '/' || isalpha(currip) ||
626           isdigit(currip) || currip == '+' || currip == '-'   ||
627           currip == '('   || currip == '[' )
628     {
629     if (currip == '*')
630       {
631       ch = '*';
632       al2_nextnw();
633       }
634     else if (currip == '/')
635       {
636       ch = '/';
637       al2_nextnw();
638       }
639     else
640       { ch = '*'; }
641 
642     len2 = al2_pfact(beg+len);
643     if (ch == '/')
644       { al2_invwrd(beg+len, len2); }
645     len += len2;
646     }
647 
648   return len;
649   }
650 
651 	/******************************************************************
652 	Wlelt *al2_rdwrd(void)
653 
654 	This parses a word into currword, copies it into a properly setup
655 	new word-list element, and returns a pointer to it.
656 	******************************************************************/
657 
al2_rdwrd(void)658 Wlelt *al2_rdwrd(void)
659   { return(al2_newwrd(al2_pwrd(0))); }
660 
661 	/******************************************************************
662 	void al2_pawrd(Wlist *p)
663 
664 	Parse a word and add it to the list of words.
665 	******************************************************************/
666 
al2_pawrd(Wlist * p)667 void al2_pawrd(Wlist *p)
668   { al1_addwl(p, al2_rdwrd()); }
669 
670 	/******************************************************************
671 	Wlist *al2_rdwl(void)
672 
673 	Reads and returns a list of words.
674 	******************************************************************/
675 
al2_rdwl(void)676 Wlist *al2_rdwl(void)
677   {
678   Wlist *p;
679 
680   if ((p = al1_newwl()) == NULL)	/* allocate a new list of words */
681     { al2_continue("unable to create new word-list"); }
682 
683   if (currip != ';')			/* parse a sequence of words */
684     {
685     al2_pawrd(p);
686     while (currip == ',')
687       {
688       al2_nextnw();
689       al2_pawrd(p);
690       }
691     }
692 
693   return(p);                     	/* return the list of words */
694   }
695 
696 	/******************************************************************
697 	void al2_parel(Wlist *l)
698 
699 	Note that W1 = W2 = W3 becomes W1W2' & W1W3'!
700 	******************************************************************/
701 
al2_parel(Wlist * l)702 void al2_parel(Wlist *l)
703   {
704   int len1, len2;
705 
706   len1 = al2_pwrd(0);			/* parse left hand side word */
707 
708   len2 = 0;
709   while (currip == '=')		/* parse a sequence of right-hand sides */
710     {
711     al2_nextnw();
712     len2 = al2_pwrd(len1);
713     al2_invwrd(len1, len2);
714     al1_addwl(l, al2_newwrd(len1+len2));
715     }
716 
717   if (len2 == 0) 		/* no RH side, take LH side as relator */
718     { al1_addwl(l, al2_newwrd(len1)); }
719   }
720 
721 	/******************************************************************
722 	Wlist *al2_rdrl(void)
723 
724 	Reads and returns a list of relators.  Note that this is _not_ the
725 	same as a list of words (ie, subgroup generators) since we're
726 	allowed things like W1 = W2.  So we have to invoke the parser via
727 	the parse relator function _parel().
728 	******************************************************************/
729 
al2_rdrl(void)730 Wlist *al2_rdrl(void)
731   {
732   Wlist *p;
733 
734   if ((p = al1_newwl()) == NULL)	/* allocate a new list of words */
735     { al2_continue("unable to create new word-list"); }
736 
737   if (currip != ';')
738     {
739     al2_parel(p);
740     while (currip == ',')
741       {
742       al2_nextnw();
743       al2_parel(p);
744       }
745     }
746 
747   return(p);
748   }
749 
750 	/******************************************************************
751         void al2_cmdloop(void)
752 	******************************************************************/
753 
al2_cmdloop(void)754 void al2_cmdloop(void)
755   {
756   int i,j,k;
757   Wlist *p;
758   Logic f, li, lj;
759 
760   f = FALSE;
761   while (TRUE)
762     {
763     /* Do the necessary for the next command (or end-of-file).  Note that
764     the next command may follow on the same line, or we may have to skip
765     over a '\n' to the next line.  (Not sure if this is bomb-proof under
766     all (error) conditions.) */
767 
768     al2_nextnw();
769     skipnl = TRUE;
770     al2_skipws();
771     skipnl = FALSE;
772 
773     if (currip == EOF)
774       { break; }
775 
776     al2_readkey();
777 
778     /* The work-horse; just plow through until the first match, do it,
779     and then skip to the end of the while(). */
780 
781     if (al2_match("add gen[erators]") || al2_match("sg"))
782       {
783       if (ndgen < 1)
784         { al2_continue("there are no generators as yet"); }
785 
786       skipnl = TRUE;
787       al2_skipws();
788 
789       p = al2_rdwl();
790       al2_endcmd();
791 
792       if (genlst == NULL)
793         { genlst = p; }
794       else
795         { al1_concatwl(genlst,p); }
796 
797       nsgpg = genlst->len;
798 
799       okcont  = FALSE;
800       tabinfo = tabindex = FALSE;
801 
802       continue;
803       }
804 
805     if (al2_match("add rel[ators]") || al2_match("rl"))
806       {
807       if (ndgen < 1)
808         { al2_continue("there are no generators as yet"); }
809 
810       skipnl = TRUE;
811       al2_skipws();
812 
813       p = al2_rdrl();
814       al2_endcmd();
815 
816       if (rellst == NULL)
817         { rellst = p; }
818       else
819         { al1_concatwl(rellst,p); }
820 
821       ndrel = rellst->len;
822 
823       okcont   = FALSE;
824       tabindex = FALSE;
825 
826       continue;
827       }
828 
829     /* All Equivalent Presentations */
830 
831     if (al2_match("aep"))
832       {
833       al2_readia();
834       al2_endcmd();
835 
836       if (intcnt != 1)
837         { al2_continue("bad number of parameters"); }
838       if (intarr[0] < 1 || intarr[0] > 7)
839         { al2_continue("invalid first argument"); }
840 
841       if (!okstart)
842         { al2_continue("can't start (no generators/workspace)"); }
843       if (rellst == NULL || rellst->len == 0)
844         { al2_continue("can't start (no relators)"); }
845 
846       al2_aep(intarr[0]);
847 
848       continue;
849       }
850 
851     if (al2_match("ai") || al2_match("alter i[nput]"))
852       {
853       al2_readname();
854       al2_endcmd();
855 
856       if (strlen(currname) == 0)
857         { strcpy(currname, "stdin"); }
858       al2_aip(currname);
859 
860       continue;
861       }
862 
863     if (al2_match("ao") || al2_match("alter o[utput]"))
864       {
865       al2_readname();
866       al2_endcmd();
867 
868       if (strlen(currname) == 0)
869         { strcpy(currname, "stdout"); }
870       al2_aop(currname);
871 
872       continue;
873       }
874 
875     /* What to do with asis in continue/redo?  It's (current) value in a
876     printout may not match that actually used at the start of a run, when
877     the involutary generators are picked up & the columns allocated, and
878     these settings are frozen until the next start/begin/end!  */
879 
880     if (al2_match("as[is]"))
881       {
882       al2_readia();
883       al2_endcmd();
884 
885       if ( (intcnt > 0 && (intarr[0] < 0 || intarr[0] > 1)) || intcnt > 1 )
886         { al2_continue("bad parameter"); }
887       else if (intcnt == 0)
888         { fprintf(fop, "asis = %s\n", asis ? "true" : "false"); }
889       else
890         { asis = (intarr[0] == 1); }
891 
892       continue;
893       }
894 
895     if (al2_match("beg[in]") || al2_match("end") || al2_match("start"))
896       {
897       al2_endcmd();
898 
899       if (!okstart)
900         { al2_continue("can't start (no generators?)"); }
901 
902       al1_rslt(lresult = al1_start(0));
903 
904       /* If something `sensible' happened, then it'll be ok to continue or
905       redo this run.  If not, then we make sure that we must begin a new
906       run.  Note that here (& in continue/redo) we play it safe by
907       enforcing a new run, even if there may be no need to.  Note that the
908       SG phase is 1st in start mode, so should _always_ be done. */
909 
910       if (lresult > 0 && sgdone)		/* finite index */
911         {
912         okcont  = okredo   = TRUE;
913         tabinfo = tabindex = TRUE;
914         }
915       else if (lresult >= -259 && sgdone)	/* holey/overflow/limit */
916         {
917         okcont   = okredo = TRUE;
918         tabinfo  = TRUE;
919         tabindex = FALSE;
920         }
921       else					/* SG overflow/`error' */
922         {
923         okcont  = okredo   = FALSE;
924         tabinfo = tabindex = FALSE;
925         }
926 
927       continue;
928       }
929 
930     if (al2_match("bye") || al2_match("exit") || al2_match("q[uit]"))
931       {
932       al2_endcmd();
933 
934       break;
935       }
936 
937     if (al2_match("cc") || al2_match("coset coinc[idence]"))
938       {
939       al2_readia();
940       al2_endcmd();
941 
942       if (intcnt != 1)
943         { al2_continue("bad number of parameters"); }
944       if (!tabinfo)
945         { al2_continue("there is no table information"); }
946       if (intarr[0] < 2 || intarr[0] >= nextdf || COL1(intarr[0]) < 0)
947         { al2_continue("invalid/redundant coset number"); }
948 
949       al2_cc(intarr[0]);
950 
951       continue;
952       }
953 
954     if (al2_match("c[factor]") || al2_match("ct[ factor]"))
955       {
956       al2_readia();
957       al2_endcmd();
958 
959       if (intcnt > 1)
960         { al2_continue("bad parameter"); }
961       else if (intcnt == 0)
962         { fprintf(fop, "ct factor = %d\n", cfactor1); }
963       else
964         { cfactor1 = intarr[0]; }
965 
966       continue;
967       }
968 
969     /* See comments for "begin". */
970 
971     if (al2_match("check") || al2_match("redo"))
972       {
973       al2_endcmd();
974 
975       if (!okredo)
976         { al2_continue("can't redo (different presentation?)"); }
977 
978       al1_rslt(lresult = al1_start(2));
979 
980       if (lresult > 0 && sgdone)
981         {
982         okcont  = TRUE;
983         tabinfo = tabindex = TRUE;
984         }
985       else if (lresult >= -259 && sgdone)
986         {
987         okcont   = TRUE;
988         tabinfo  = TRUE;
989         tabindex = FALSE;
990         }
991       else
992         {
993         okcont  = FALSE;
994         tabinfo = tabindex = FALSE;
995         }
996       if (lresult < -260)
997         { okredo = FALSE; }
998 
999       continue;
1000       }
1001 
1002     if (al2_match("com[paction]"))
1003       {
1004       al2_readia();
1005       al2_endcmd();
1006 
1007       if ( (intcnt > 0 && (intarr[0] < 0 || intarr[0] > 100)) ||
1008             intcnt > 1 )
1009         { al2_continue("bad parameter"); }
1010       else if (intcnt == 0)
1011         { fprintf(fop, "compaction = %d\n", comppc); }
1012       else
1013         { comppc = intarr[0]; }
1014 
1015       continue;
1016       }
1017 
1018     /* See comments for "begin". */
1019 
1020     if (al2_match("con[tinue]"))
1021       {
1022       al2_endcmd();
1023 
1024       if (!okcont)
1025         { al2_continue("can't continue (altered presentation?)"); }
1026 
1027       al1_rslt(lresult = al1_start(1));
1028 
1029       if (lresult > 0 && sgdone)
1030         { tabinfo = tabindex = TRUE; }
1031       else if (lresult >= -259 && sgdone)
1032         {
1033         tabinfo  = TRUE;
1034         tabindex = FALSE;
1035         }
1036       else
1037         {
1038         okcont  = FALSE;
1039         tabinfo = tabindex = FALSE;
1040         }
1041 
1042       continue;
1043       }
1044 
1045     if (al2_match("cy[cles]"))
1046       {
1047       al2_endcmd();
1048 
1049       if (!tabindex)
1050         { al2_continue("there is no completed table"); }
1051 
1052       begintime = al0_clock();
1053       li = al0_compact();
1054       endtime = al0_clock();
1055       if (li)
1056         { fprintf(fop, "CO"); }
1057       else
1058         { fprintf(fop, "co"); }
1059       fprintf(fop, ": a=%d r=%d h=%d n=%d; c=+%4.2f\n",
1060                    nalive, knr, knh, nextdf, al0_diff(begintime,endtime));
1061 
1062       al2_cycles();
1063 
1064       continue;
1065       }
1066 
1067     if (al2_match("ded mo[de]") || al2_match("dmod[e]"))
1068       {
1069       al2_readia();
1070       al2_endcmd();
1071 
1072       if (intcnt == 0)
1073         { fprintf(fop, "deduction mode = %d\n", dedmode); }
1074       else if (intcnt == 1)
1075         {
1076         if (intarr[0] < 0 || intarr[0] > 4)
1077           { al2_continue("bad mode parameter"); }
1078         dedmode = intarr[0];
1079         }
1080       else
1081         { al2_continue("bad parameter count"); }
1082 
1083       continue;
1084       }
1085 
1086     if (al2_match("ded si[ze]") || al2_match("dsiz[e]"))
1087       {
1088       al2_readia();
1089       al2_endcmd();
1090 
1091       if ( (intcnt > 0 && intarr[0] < 0) || intcnt > 1 )
1092         { al2_continue("bad parameter"); }
1093       else if (intcnt == 0)
1094         { fprintf(fop, "deduction stack = %d\n", dedsiz1); }
1095       else
1096         { dedsiz1 = intarr[0]; }
1097 
1098       continue;
1099       }
1100 
1101     if (al2_match("def[ault]"))
1102       {
1103       al2_endcmd();
1104 
1105       cfactor1 = 0;
1106       comppc   = 10;
1107       dedmode  = 4;
1108       dedsiz1  = 1000;
1109       ffactor1 = 0;
1110       lahead   = 0;
1111       mendel   = FALSE;
1112       nrinsgp1 = -1;
1113       pdefn    = 3;
1114       pdsiz1   = 256;
1115       rfactor1 = 0;
1116       rfill    = TRUE;
1117       pcomp    = FALSE;
1118 
1119       continue;
1120       }
1121 
1122     if (al2_match("del gen[erators]") || al2_match("ds"))
1123       {
1124       al2_readia();
1125       al2_endcmd();
1126 
1127       if (intcnt < 1 || genlst == NULL || genlst->len < 1)
1128         { al2_continue("empty argument list / generator list"); }
1129       al2_dw(genlst);
1130       nsgpg = genlst->len;
1131 
1132       okcont  = okredo   = FALSE;
1133       tabinfo = tabindex = FALSE;
1134 
1135       continue;
1136       }
1137 
1138     if (al2_match("del rel[ators]") || al2_match("dr"))
1139       {
1140       al2_readia();
1141       al2_endcmd();
1142 
1143       if (intcnt < 1 || rellst == NULL || rellst->len < 1)
1144         { al2_continue("empty argument list / relator list"); }
1145       al2_dw(rellst);
1146       ndrel = rellst->len;
1147 
1148       okcont  = okredo   = FALSE;
1149       tabinfo = tabindex = FALSE;
1150 
1151       continue;
1152       }
1153 
1154     if (al2_match("d[ump]"))
1155       {
1156       al2_readia();
1157       al2_endcmd();
1158 
1159       if ( (intcnt > 0 && (intarr[0] < 0 || intarr[0] > 2)) ||
1160            (intcnt > 1 && (intarr[1] < 0 || intarr[1] > 1)) ||
1161             intcnt > 2 )
1162         { al2_continue("bad parameters"); }
1163       else if (intcnt == 0)
1164         { al0_dump(FALSE); }
1165       else if (intcnt == 1)
1166         {
1167         if (intarr[0] == 0)
1168           { al0_dump(FALSE); }
1169         else if (intarr[0] == 1)
1170           { al1_dump(FALSE); }
1171         else
1172           { al2_dump(FALSE); }
1173         }
1174       else
1175         {
1176         if (intarr[0] == 0)
1177           { al0_dump(intarr[1] == 1); }
1178         else if (intarr[0] == 1)
1179           { al1_dump(intarr[1] == 1); }
1180         else
1181           { al2_dump(intarr[1] == 1); }
1182         }
1183 
1184       continue;
1185       }
1186 
1187     if (al2_match("easy"))
1188       {
1189       al2_endcmd();
1190 
1191       cfactor1 = 0;
1192       comppc   = 100;
1193       dedmode  = 0;
1194       dedsiz1  = 1000;
1195       ffactor1 = 1;
1196       lahead   = 0;
1197       mendel   = FALSE;
1198       nrinsgp1 = 0;
1199       pdefn    = 0;
1200       pdsiz1   = 256;
1201       rfactor1 = 1000;
1202       rfill    = TRUE;
1203       pcomp    = FALSE;
1204 
1205       continue;
1206       }
1207 
1208     if (al2_match("echo"))
1209       {
1210       al2_readia();
1211       al2_endcmd();
1212 
1213       if ( (intcnt > 0 && (intarr[0] < 0 || intarr[0] > 1)) || intcnt > 1 )
1214         { al2_continue("bad parameter"); }
1215       else if (intcnt == 0)
1216         { fprintf(fop, "echo = %s\n", echo ? "true" : "false"); }
1217       else
1218         { echo = (intarr[0] == 1); }
1219 
1220       continue;
1221       }
1222 
1223     /* Note that it is ok to set the name to "".  If the call to _strdup()
1224     fails, then _continue() will be invoked.  This could leave grpname
1225     still pointing to freed storage, hence the explicit setting to NULL. */
1226 
1227     if (al2_match("enum[eration]") || al2_match("group name"))
1228       {
1229       al2_readname();
1230       al2_endcmd();
1231 
1232       if (grpname != NULL)
1233         { free(grpname); }
1234       grpname = NULL;
1235       grpname = al2_strdup(currname);
1236 
1237       continue;
1238       }
1239 
1240     if (al2_match("fel[sch]"))
1241       {
1242       al2_readia();
1243       al2_endcmd();
1244 
1245       if ( (intcnt > 0 && (intarr[0] < 0 || intarr[0] > 1)) || intcnt > 1 )
1246         { al2_continue("bad parameter"); }
1247 
1248       if (intcnt == 1 && intarr[0] == 1)	/* `Enhanced' Felsch */
1249         {
1250         ffactor1 = 0;
1251         nrinsgp1 = -1;
1252         pdefn    = 3;
1253         }
1254       else					/* Felsch (~ Pure C) */
1255         {
1256         ffactor1 = 1;
1257         nrinsgp1 = 0;
1258         pdefn    = 0;
1259         }
1260 
1261       cfactor1 = 1000;
1262       comppc   = 10;
1263       dedmode  = 4;
1264       dedsiz1  = 1000;
1265       lahead   = 0;
1266       mendel   = FALSE;
1267       pdsiz1   = 256;
1268       rfactor1 = 0;
1269       rfill    = FALSE;
1270       pcomp    = FALSE;
1271 
1272       continue;
1273       }
1274 
1275     /* If you set this to 0, Level 1 will set ffactor to a `sensible'
1276     default (eg, 5(ncol+2)/4). */
1277 
1278     if (al2_match("f[factor]") || al2_match("fi[ll factor]"))
1279       {
1280       al2_readia();
1281       al2_endcmd();
1282 
1283       if ( (intcnt > 0 && intarr[0] < 0) || intcnt > 1 )
1284         { al2_continue("bad parameter"); }
1285       else if (intcnt == 0)
1286         { fprintf(fop, "fill factor = %d\n", ffactor1); }
1287       else
1288         { ffactor1 = intarr[0]; }
1289 
1290       continue;
1291       }
1292 
1293     if (al2_match("gen[erators]") || al2_match("subgroup gen[erators]"))
1294       {
1295       if (ndgen < 1)
1296         { al2_continue("there are no generators as yet"); }
1297 
1298       skipnl = TRUE;
1299       al2_skipws();
1300 
1301       p = al2_rdwl();
1302       al2_endcmd();
1303 
1304       if (genlst != NULL)
1305         { al1_emptywl(genlst);  free(genlst); }
1306       genlst = p;
1307       nsgpg  = p->len;
1308 
1309       okcont  = okredo   = FALSE;
1310       tabinfo = tabindex = FALSE;
1311 
1312       continue;
1313       }
1314 
1315     if (al2_match("gr[oup generators]"))
1316       {
1317       if (isdigit(currip) || currip == '+' || currip == '-')
1318         {
1319         i = al2_readint();
1320         al2_endcmd();
1321         if (i < 1)
1322           { al2_continue("bad parameter"); }
1323 
1324         ndgen  = i;
1325         galpha = FALSE;
1326 
1327         okstart = (costable != NULL);
1328         okcont  = okredo   = FALSE;
1329         tabinfo = tabindex = FALSE;
1330 
1331         /* The current relator & generator lists are now invalid */
1332 
1333         if (rellst != NULL)
1334           { al1_emptywl(rellst);  free(rellst); }
1335         rellst = NULL;
1336         ndrel  = 0;
1337         if (genlst != NULL)
1338           { al1_emptywl(genlst);  free(genlst); }
1339         genlst = NULL;
1340         nsgpg  = 0;
1341         }
1342       else if (isalpha(currip))
1343         {
1344         i = al2_readgen();
1345         al2_endcmd();
1346 
1347         ndgen  = i;
1348         galpha = TRUE;
1349         for (j = 1; j <= ndgen; j++)
1350           { algen[j] = currname[j]; }
1351         algen[ndgen+1] = '\0';		/* &algen[1] is printable string */
1352 
1353         for (j = 1; j <= 26; j++)
1354           { genal[j] = 0;}
1355         for (j = 1; j <= ndgen; j++)
1356           { genal[algen[j]-'a'+1] = j;  }
1357 
1358         okstart = (costable != NULL);
1359         okcont  = okredo   = FALSE;
1360         tabinfo = tabindex = FALSE;
1361 
1362         if (rellst != NULL)
1363           { al1_emptywl(rellst);  free(rellst); }
1364         rellst = NULL;
1365         ndrel  = 0;
1366         if (genlst != NULL)
1367           { al1_emptywl(genlst);  free(genlst); }
1368         genlst = NULL;
1369         nsgpg  = 0;
1370         }
1371       else
1372         {
1373         al2_endcmd();
1374 
1375         fprintf(fop, "group generators = ");
1376         if (ndgen < 1)
1377           { fprintf(fop, "none\n"); }
1378         else if (galpha)
1379           {
1380           for (i = 1; i <= ndgen; i++)
1381             { fprintf(fop, "%c", algen[i]); }
1382           fprintf(fop, "\n");
1383           }
1384         else
1385           { fprintf(fop, "1..%d\n", ndgen); }
1386         }
1387 
1388       continue;
1389       }
1390 
1391     if (al2_match("group relators") || al2_match("rel[ators]"))
1392       {
1393       if (ndgen < 1)
1394         { al2_continue("there are no generators as yet"); }
1395 
1396       skipnl = TRUE;
1397       al2_skipws();
1398 
1399       p = al2_rdrl();
1400       al2_endcmd();
1401 
1402       if (rellst != NULL)
1403         { al1_emptywl(rellst);  free(rellst); }
1404       rellst = p;
1405       ndrel  = p->len;
1406 
1407       okcont  = okredo   = FALSE;
1408       tabinfo = tabindex = FALSE;
1409 
1410       continue;
1411       }
1412 
1413     if (al2_match("hard"))
1414       {
1415       al2_endcmd();
1416 
1417       cfactor1 = 1000;
1418       comppc   = 10;
1419       dedmode  = 4;
1420       dedsiz1  = 1000;
1421       ffactor1 = 0;
1422       lahead   = 0;
1423       mendel   = FALSE;
1424       nrinsgp1 = -1;
1425       pdefn    = 3;
1426       pdsiz1   = 256;
1427       rfactor1 = 1;
1428       rfill    = TRUE;
1429       pcomp    = FALSE;
1430 
1431       continue;
1432       }
1433 
1434     if (al2_match("h[elp]"))
1435       {
1436       al2_endcmd();
1437       al2_help();
1438 
1439       continue;
1440       }
1441 
1442     if (al2_match("hlt"))
1443       {
1444       al2_endcmd();
1445 
1446       cfactor1 = 0;
1447       comppc   = 10;
1448       dedmode  = 0;
1449       dedsiz1  = 1000;
1450       ffactor1 = 1;
1451       lahead   = 1;
1452       mendel   = FALSE;
1453       nrinsgp1 = 0;
1454       pdefn    = 0;
1455       pdsiz1   = 256;
1456       rfactor1 = 1000;
1457       rfill    = TRUE;
1458       pcomp    = FALSE;
1459 
1460       continue;
1461       }
1462 
1463     if (al2_match("ho[le limit]"))
1464       {
1465       al2_readia();
1466       al2_endcmd();
1467 
1468       if ( (intcnt > 0 && (intarr[0] < -1 || intarr[0] > 100)) ||
1469            intcnt > 1 )
1470         { al2_continue("bad parameter"); }
1471       else if (intcnt == 0)
1472         { fprintf(fop, "hole limit = %d\n", hlimit); }
1473       else
1474         { hlimit = intarr[0]; }
1475 
1476       continue;
1477       }
1478 
1479     if (al2_match("look[ahead]"))
1480       {
1481       al2_readia();
1482       al2_endcmd();
1483 
1484       if ( (intcnt > 0 && (intarr[0] < 0 || intarr[0] > 4)) || intcnt > 1 )
1485         { al2_continue("bad parameter"); }
1486       else if (intcnt == 0)
1487         { fprintf(fop, "lookahead = %d\n", lahead); }
1488       else
1489         { lahead = intarr[0]; }
1490 
1491       continue;
1492       }
1493 
1494     if (al2_match("loop[ limit]"))
1495       {
1496       al2_readia();
1497       al2_endcmd();
1498 
1499       if ( (intcnt > 0 && intarr[0] < 0) || intcnt > 1 )
1500         { al2_continue("bad parameter"); }
1501       else if (intcnt == 0)
1502         { fprintf(fop, "loop limit = %d\n", llimit); }
1503       else
1504         { llimit = intarr[0]; }
1505 
1506       continue;
1507       }
1508 
1509     if (al2_match("max[ cosets]"))
1510       {
1511       al2_readia();
1512       al2_endcmd();
1513 
1514       if ( (intcnt > 0 && (intarr[0] < 0 || intarr[0] == 1)) ||
1515            intcnt > 1 )
1516         { al2_continue("bad parameter"); }
1517       else if (intcnt == 0)
1518         { fprintf(fop, "max cosets = %d\n", maxrow1); }
1519       else
1520         { maxrow1 = intarr[0]; }
1521 
1522       continue;
1523       }
1524 
1525     if (al2_match("mess[ages]") || al2_match("mon[itor]"))
1526       {
1527       al2_readia();
1528       al2_endcmd();
1529 
1530       if (intcnt > 1)
1531         { al2_continue("too many parameters"); }
1532       else if (intcnt == 0)
1533         {
1534         if (msgctrl)
1535           {
1536           if (msghol)
1537             { fprintf(fop, "messages = %d (+ holes)\n", msgincr); }
1538           else
1539             { fprintf(fop, "messages = %d (- holes)\n", msgincr); }
1540           }
1541         else
1542           { fprintf(fop, "messages = off\n"); }
1543         }
1544       else if (intarr[0] == 0)
1545         {
1546         msgctrl = FALSE;
1547         msghol  = FALSE;
1548         msgincr = 0;
1549         }
1550       else if (intarr[0] < 0)
1551         {
1552         msgctrl = TRUE;
1553         msghol  = TRUE;
1554         msgincr = -intarr[0];
1555         }
1556       else
1557         {
1558         msgctrl = TRUE;
1559         msghol  = FALSE;
1560         msgincr = intarr[0];
1561         }
1562 
1563       continue;
1564       }
1565 
1566     if (al2_match("mend[elsohn]"))
1567       {
1568       al2_readia();
1569       al2_endcmd();
1570 
1571       if ( (intcnt > 0 && (intarr[0] < 0 || intarr[0] > 1)) || intcnt > 1 )
1572         { al2_continue("bad parameter"); }
1573       else if (intcnt == 0)
1574         { fprintf(fop, "mendelsohn = %s\n", mendel ? "true" : "false"); }
1575       else
1576         { mendel = (intarr[0] == 1); }
1577 
1578       continue;
1579       }
1580 
1581     if (al2_match("mo[de]"))
1582       {
1583       al2_endcmd();
1584 
1585       if (okstart)
1586         { fprintf(fop, "start = yes,"); }
1587       else
1588         { fprintf(fop, "start = no,"); }
1589       if (okcont)
1590         { fprintf(fop, " continue = yes,"); }
1591       else
1592         { fprintf(fop, " continue = no,"); }
1593       if (okredo)
1594         { fprintf(fop, " redo = yes\n"); }
1595       else
1596         { fprintf(fop, " redo = no\n"); }
1597 
1598       continue;
1599       }
1600 
1601     if (al2_match("nc") || al2_match("normal[ closure]"))
1602       {
1603       al2_readia();
1604       al2_endcmd();
1605 
1606       if ( (intcnt > 0 && (intarr[0] < 0 || intarr[0] > 1)) || intcnt > 1 )
1607         { al2_continue("bad parameter"); }
1608       if (!tabinfo)
1609         { al2_continue("there is no table information"); }
1610 
1611       begintime = al0_clock();
1612       li = al0_compact();
1613       endtime = al0_clock();
1614       if (li)
1615         { fprintf(fop, "CO"); }
1616       else
1617         { fprintf(fop, "co"); }
1618       fprintf(fop, ": a=%d r=%d h=%d n=%d; c=+%4.2f\n",
1619                    nalive, knr, knh, nextdf, al0_diff(begintime,endtime));
1620 
1621       if (intcnt == 0)
1622         { al2_normcl(FALSE); }
1623       else
1624         { al2_normcl(intarr[0] == 1); }
1625 
1626       continue;
1627       }
1628 
1629     if (al2_match("no[ relators in subgroup]"))
1630       {
1631       al2_readia();
1632       al2_endcmd();
1633 
1634       if ( (intcnt > 0 && intarr[0] < -1) || intcnt > 1 )
1635         { al2_continue("bad parameter"); }
1636       else if (intcnt == 0)
1637         { fprintf(fop, "no. rels in subgr = %d\n", nrinsgp1); }
1638       else
1639         { nrinsgp1 = intarr[0]; }
1640 
1641       continue;
1642       }
1643 
1644     if (al2_match("oo") || al2_match("order[ option]"))
1645       {
1646       al2_readia();
1647       al2_endcmd();
1648 
1649       if (intcnt != 1)
1650         { al2_continue("missing argument / too many arguments"); }
1651       if (!tabinfo)
1652         { al2_continue("no information in table"); }
1653 
1654       al2_oo(intarr[0]);
1655 
1656       continue;
1657       }
1658 
1659     if (al2_match("opt[ions]"))
1660       {
1661       al2_endcmd();
1662       al2_opt();
1663 
1664       continue;
1665       }
1666 
1667     /* an old command, which we quietly ignore */
1668 
1669     if (al2_match("par[ameters]"))
1670       {
1671       al2_endcmd();
1672       continue;
1673       }
1674 
1675     if (al2_match("path[ compression]"))
1676       {
1677       al2_readia();
1678       al2_endcmd();
1679 
1680       if ( (intcnt > 0 && (intarr[0] < 0 || intarr[0] > 1)) || intcnt > 1 )
1681         { al2_continue("bad parameter"); }
1682       else if (intcnt == 0)
1683         { fprintf(fop, "path compression = %s\n", pcomp ? "on" : "off"); }
1684       else
1685         { pcomp = (intarr[0] == 1); }
1686 
1687       continue;
1688       }
1689 
1690     if (al2_match("pd mo[de]") || al2_match("pmod[e]"))
1691       {
1692       al2_readia();
1693       al2_endcmd();
1694 
1695       if ( (intcnt > 0 && (intarr[0] < 0 || intarr[0] > 3)) || intcnt > 1 )
1696         { al2_continue("bad parameter"); }
1697       else if (intcnt == 0)
1698         { fprintf(fop, "pref. definition mode = %d\n", pdefn); }
1699       else
1700         { pdefn = intarr[0]; }
1701 
1702       continue;
1703       }
1704 
1705     if (al2_match("pd si[ze]") || al2_match("psiz[e]"))
1706       {
1707       al2_readia();
1708       al2_endcmd();
1709 
1710       if ( (intcnt > 0 && intarr[0] < 0) || intcnt > 1 )
1711         { al2_continue("bad parameter"); }
1712       else if (intcnt == 0)
1713         { fprintf(fop, "pref. definition list = %d\n", pdsiz1); }
1714       else if (intarr[0] == 0)
1715         { pdsiz1 = intarr[0]; }			/* use default value */
1716       else if (intarr[0]%2 == 1)
1717         { al2_continue("bad parameter"); }	/* odd (incl. 1) */
1718       else
1719         {					/* even parameter, >= 2 */
1720         i = intarr[0];
1721         while (i%2 == 0)
1722           { i /= 2; }
1723         if (i == 1)
1724           { pdsiz1 = intarr[0]; }
1725         else
1726           { al2_continue("bad parameter"); }	/* not power of 2 */
1727         }
1728 
1729       continue;
1730       }
1731 
1732     if (al2_match("print det[ails]") || al2_match("sr"))
1733       {
1734       al2_readia();
1735       al2_endcmd();
1736 
1737       if ((intcnt > 0 && (intarr[0] < 0 || intarr[0] > 5)) || intcnt > 1)
1738         { al2_continue("bad parameter"); }
1739       else if (intcnt == 0)
1740         { al1_prtdetails(0); }
1741       else
1742         { al1_prtdetails(intarr[0]); }
1743 
1744       continue;
1745       }
1746 
1747     /* Negative first parameter means include order/rep, else don't.  No
1748     parameters means all the table, one parameter "x" means (1,x,1), two
1749     parameters "x,y" means (x,y,1), and three parameters "x,y,z" means
1750     (x,y,z).  Note the compulsory compaction, to prevent utterly confusing
1751     the user!  (This may cause disded to become true.) */
1752 
1753     if (al2_match("pr[int table]"))
1754       {
1755       al2_readia();
1756       al2_endcmd();
1757 
1758       if (!tabinfo)
1759         { al2_continue("no information in table"); }
1760 
1761       if (intcnt == 0)
1762         {
1763         f = FALSE;
1764         intarr[0] = 1;
1765         intarr[1] = nextdf-1;
1766         intarr[2] = 1;
1767         }
1768       else if (intcnt <= 3)
1769         {
1770         if (intarr[0] < 0)
1771           {
1772           f = TRUE;
1773           intarr[0] = -intarr[0];
1774           }
1775         else
1776           { f = FALSE; }
1777         }
1778       else
1779         { al2_continue("too many parameters"); }
1780 
1781       if (intcnt == 1)
1782         {
1783         intarr[1] = intarr[0];
1784         intarr[0] = intarr[2] = 1;
1785         }
1786       else if (intcnt == 2)
1787         { intarr[2] = 1; }
1788 
1789       if (intarr[0] >= nextdf)
1790         { intarr[0] = nextdf-1; }
1791       if (intarr[1] >= nextdf)
1792         { intarr[1] = nextdf-1; }
1793 
1794       if (intarr[0] < 1 || intarr[1] < intarr[0] || intarr[2] < 1 )
1795         { al2_continue("bad parameters"); }
1796 
1797       begintime = al0_clock();
1798       li = al0_compact();
1799       endtime = al0_clock();
1800       if (li)
1801         { fprintf(fop, "CO"); }
1802       else
1803         { fprintf(fop, "co"); }
1804       fprintf(fop, ": a=%d r=%d h=%d n=%d; c=+%4.2f\n",
1805                    nalive, knr, knh, nextdf, al0_diff(begintime,endtime));
1806 
1807       al1_prtct(intarr[0], intarr[1], intarr[2], FALSE, f);
1808 
1809       continue;
1810       }
1811 
1812     if (al2_match("pure c[t]"))
1813       {
1814       al2_endcmd();
1815 
1816       cfactor1 = 1000;
1817       comppc   = 100;
1818       dedmode  = 4;
1819       dedsiz1  = 1000;
1820       ffactor1 = 1;
1821       lahead   = 0;
1822       mendel   = FALSE;
1823       nrinsgp1 = 0;
1824       pdefn    = 0;
1825       pdsiz1   = 256;
1826       rfactor1 = 0;
1827       rfill    = FALSE;
1828       pcomp    = FALSE;
1829 
1830       continue;
1831       }
1832 
1833     if (al2_match("pure r[t]"))
1834       {
1835       al2_endcmd();
1836 
1837       cfactor1 = 0;
1838       comppc   = 100;
1839       dedmode  = 0;
1840       dedsiz1  = 1000;
1841       ffactor1 = 1;
1842       lahead   = 0;
1843       mendel   = FALSE;
1844       nrinsgp1 = 0;
1845       pdefn    = 0;
1846       pdsiz1   = 256;
1847       rfactor1 = 1000;
1848       rfill    = FALSE;
1849       pcomp    = FALSE;
1850 
1851       continue;
1852       }
1853 
1854     /* This is a `dangerous' option, since it can go wrong, or `corrupt'
1855     the status, in so many ways.  We try to minimise problems by being
1856     very strict as to when we allow it to be called.  How much of this is
1857     necessary/desirable is moot. */
1858 
1859     if (al2_match("rc") || al2_match("random coinc[idences]"))
1860       {
1861       al2_readia();
1862       al2_endcmd();
1863 
1864       if (intcnt < 1 || intcnt > 2)
1865         { al2_continue("bad number of parameters"); }
1866       if (intarr[0] < 0)
1867         { al2_continue("invalid first argument"); }
1868       if (intcnt == 2 && intarr[1] < 1)
1869         { al2_continue("invalid second argument"); }
1870 
1871       if (!tabinfo)
1872         { al2_continue("there is no table information"); }
1873       if (!okredo)
1874         { al2_continue("can't redo (different presentation?)"); }
1875 
1876       if (lresult == 1)
1877         { al2_continue("trivial finite index already exists"); }
1878 
1879       if (intarr[0] == 0)
1880         {
1881         if (lresult > 0)
1882           { al2_continue("non-trivial finite index already present"); }
1883         }
1884       else
1885         {
1886         if (lresult > 0 && lresult < intarr[0])
1887           { al2_continue("finite index already < argument"); }
1888         if (lresult > 0 && lresult%intarr[0] == 0)
1889           { al2_continue("finite index already multiple of argument"); }
1890         }
1891 
1892       if (intarr[0] >= nalive)
1893         { al2_continue("not enough active cosets available"); }
1894 
1895       if (intcnt == 1)			/* Try 8 times, by default */
1896         { al2_rc(intarr[0],8); }
1897       else
1898         { al2_rc(intarr[0],intarr[1]); }
1899 
1900       continue;
1901       }
1902 
1903     if (al2_match("rec[over]") || al2_match("contig[uous]"))
1904       {
1905       if (!tabinfo)
1906         { al2_continue("there is no table information"); }
1907 
1908       begintime = al0_clock();
1909       li = al0_compact();
1910       endtime = al0_clock();
1911       if (li)
1912         { fprintf(fop, "CO"); }
1913       else
1914         { fprintf(fop, "co"); }
1915       fprintf(fop, ": a=%d r=%d h=%d n=%d; c=+%4.2f\n",
1916                    nalive, knr, knh, nextdf, al0_diff(begintime,endtime));
1917 
1918       continue;
1919       }
1920 
1921     /* Random Equivalent Presentations */
1922 
1923     if (al2_match("rep"))
1924       {
1925       al2_readia();
1926       al2_endcmd();
1927 
1928       if (intcnt < 1 || intcnt > 2)
1929         { al2_continue("bad number of parameters"); }
1930       if (intarr[0] < 1 || intarr[0] > 7)
1931         { al2_continue("invalid first argument"); }
1932       if (intcnt == 2 && intarr[1] < 1)
1933         { al2_continue("invalid second argument"); }
1934 
1935       if (!okstart)
1936         { al2_continue("can't start (no generators/workspace)"); }
1937       if (rellst == NULL || rellst->len == 0)
1938         { al2_continue("can't start (no relators)"); }
1939 
1940       if (intcnt == 1)
1941         { al2_rep(intarr[0], 8); }
1942       else
1943         { al2_rep(intarr[0], intarr[1]); }
1944 
1945       continue;
1946       }
1947 
1948     /* an old command, which we quietly ignore */
1949 
1950     if (al2_match("restart"))
1951       {
1952       al2_endcmd();
1953       continue;
1954       }
1955 
1956     if (al2_match("r[factor]") || al2_match("rt[ factor]"))
1957       {
1958       al2_readia();
1959       al2_endcmd();
1960 
1961       if (intcnt > 1)
1962         { al2_continue("bad parameter"); }
1963       else if (intcnt == 0)
1964         { fprintf(fop, "rt factor = %d\n", rfactor1); }
1965       else
1966         { rfactor1 = intarr[0]; }
1967 
1968       continue;
1969       }
1970 
1971     if (al2_match("row[ filling]"))
1972       {
1973       al2_readia();
1974       al2_endcmd();
1975 
1976       if ( (intcnt > 0 && (intarr[0] < 0 || intarr[0] > 1)) || intcnt > 1 )
1977         { al2_continue("bad parameter"); }
1978       else if (intcnt == 0)
1979         { fprintf(fop, "row fill = %s\n", rfill ? "on" : "off"); }
1980       else
1981         { rfill = (intarr[0] == 1); }
1982 
1983       continue;
1984       }
1985 
1986     if (al2_match("sc") || al2_match("stabil[ising cosets]"))
1987       {
1988       al2_readia();
1989       al2_endcmd();
1990 
1991       if (intcnt != 1)
1992         { al2_continue("missing argument / too many arguments"); }
1993       if (!tabinfo)
1994         { al2_continue("no information in table"); }
1995 
1996       al2_sc(intarr[0]);
1997 
1998       continue;
1999       }
2000 
2001     /* We emulate, as best we can, the odd-numbered enumeration strategies
2002     given in Table 5.5.1 (p. 245) of C.C. Sims' book.  The even-numbered
2003     ones involve `standardise-as-you-go', which we don't do; however we can
2004     standardise the table once we're done, or we can pause an enumeration
2005     at any time, standardise, and then continue.  (This last is not as daft
2006     as it seems and does, in fact, sometimes prove beneficial.)  The
2007     strategies are: 1) HLT, no save; 3) HLT, save; 5) CHLT, no save; 7)
2008     CHLT, save; 9) Felsch (save). */
2009 
2010     if (al2_match("sims"))
2011       {
2012       al2_readia();
2013       al2_endcmd();
2014 
2015       if ( intcnt != 1 ||
2016            intarr[0] < 1 || intarr[0] > 9 || intarr[0]%2 == 0 )
2017         { al2_continue("bad parameter"); }
2018 
2019       switch(intarr[0])
2020         {
2021         case 1:			/* cf. "pure r" + row-fill */
2022           cfactor1 = 0;
2023           dedmode  = 0;
2024           mendel   = FALSE;
2025           rfactor1 = 1000;
2026           rfill    = TRUE;
2027          break;
2028         case 3:
2029           cfactor1 = 0;
2030           dedmode  = 4;
2031           mendel   = FALSE;
2032           rfactor1 = -1000;
2033           rfill    = TRUE;
2034           break;
2035         case 5:
2036           cfactor1 = 0;
2037           dedmode  = 0;
2038           mendel   = TRUE;
2039           rfactor1 = 1000;
2040           rfill    = TRUE;
2041           break;
2042         case 7:
2043           cfactor1 = 0;
2044           dedmode  = 4;
2045           mendel   = TRUE;
2046           rfactor1 = -1000;
2047           rfill    = TRUE;
2048           break;
2049         case 9:			/* cf. "pure c" / "Felsch" */
2050           cfactor1 = 1000;
2051           dedmode  = 4;
2052           mendel   = FALSE;
2053           rfactor1 = 0;
2054           rfill    = FALSE;
2055           break;
2056         }
2057 
2058       /* These parameters are common to all modes. */
2059 
2060       comppc   = 10;		/* compaction always allowed */
2061       dedsiz1  = 1000;		/* default (starting) size */
2062       ffactor1 = 1;		/* fill-factor not active */
2063       lahead   = 0;		/* never lookahead */
2064       nrinsgp1 = 0;		/* no (active) RS phase */
2065       pdefn    = 0;		/* no preferred/immediate defns ... */
2066       pdsiz1   = 256;
2067       pcomp    = FALSE;
2068 
2069       continue;
2070       }
2071 
2072     if (al2_match("st[andard table]"))
2073       {
2074       al2_endcmd();
2075 
2076       if (!tabinfo)
2077         { al2_continue("no information in table"); }
2078 
2079       begintime = al0_clock();
2080       li = al0_compact();
2081       lj = al0_stdct();
2082       endtime = al0_clock();
2083       if (li)
2084         { fprintf(fop, "CO"); }
2085       else
2086         { fprintf(fop, "co"); }
2087       if (lj)
2088         { fprintf(fop, "/ST"); }
2089       else
2090         { fprintf(fop, "/st"); }
2091       fprintf(fop, ": a=%d r=%d h=%d n=%d; c=+%4.2f\n",
2092                    nalive, knr, knh, nextdf, al0_diff(begintime,endtime));
2093 
2094       continue;
2095       }
2096 
2097     /* this stuff is done if the statistics package is included */
2098 
2099 #ifdef AL0_STAT
2100     if (al2_match("stat[istics]") || al2_match("stats"))
2101       {
2102       al2_endcmd();
2103       STATDUMP;
2104 
2105       continue;
2106       }
2107 #endif
2108 
2109     if (al2_match("style"))
2110       {
2111       al2_endcmd();
2112 
2113       if (rfactor1 < 0)
2114         {
2115         if (cfactor1 < 0)
2116           { fprintf(fop, "style = R/C\n"); }
2117         else if (cfactor1 == 0)
2118           { fprintf(fop, "style = R*\n"); }
2119         else
2120           { fprintf(fop, "style = Cr\n"); }
2121         }
2122       else if (rfactor1 == 0)
2123         {
2124         if (cfactor1 < 0)
2125           { fprintf(fop, "style = C* (aka C-style)\n"); }
2126         else if (cfactor1 == 0)
2127           { fprintf(fop, "style = R/C (Rt & Ct values defaulted)\n"); }
2128         else
2129           { fprintf(fop, "style = C\n"); }
2130         }
2131       else
2132         {
2133         if (cfactor1 < 0)
2134           { fprintf(fop, "style = Rc\n"); }
2135         else if (cfactor1 == 0)
2136           { fprintf(fop, "style = R\n"); }
2137         else
2138           { fprintf(fop, "style = CR\n"); }
2139         }
2140 
2141       continue;
2142       }
2143 
2144     /* see comment for "enum[eration]" */
2145 
2146     if (al2_match("subg[roup name]"))
2147       {
2148       al2_readname();
2149       al2_endcmd();
2150 
2151       if (subgrpname != NULL)
2152         { free(subgrpname); }
2153       subgrpname = NULL;
2154       subgrpname = al2_strdup(currname);
2155 
2156       continue;
2157       }
2158 
2159     /* Allows access to the system; ie, fires up a shell & passes it the
2160     (non-empty) argument.  Use with caution, of course!  The argument must
2161     consist of one line of printable characters (plus '\t'), excluding ';'.
2162     Trailing WS is removed.  We do _no_ error checking on the call. */
2163 
2164     if (al2_match("sys[tem]"))
2165       {
2166       al2_readname();
2167       al2_endcmd();
2168 
2169       if (strlen(currname) == 0)
2170         { al2_continue("empty argument"); }
2171       else
2172         { system(currname); }
2173 
2174       continue;
2175       }
2176 
2177     if (al2_match("text"))
2178       {
2179       al2_readname();
2180       al2_endcmd();
2181       fprintf(fop, "%s\n", currname);
2182 
2183       continue;
2184       }
2185 
2186     if (al2_match("ti[me limit]"))
2187       {
2188       al2_readia();
2189       al2_endcmd();
2190 
2191       if ( (intcnt > 0 && intarr[0] < -1) || intcnt > 1 )
2192         { al2_continue("bad parameter"); }
2193       else if (intcnt == 0)
2194         { fprintf(fop, "time limit = %d\n", tlimit); }
2195       else
2196         { tlimit = intarr[0]; }
2197 
2198       continue;
2199       }
2200 
2201     /* The trace word command takes as arguments a coset number & a word.
2202     Unlike ACE2, we do not allow a multi-line word. */
2203 
2204     if (al2_match("tw") || al2_match("trace[ word]"))
2205       {
2206       i = al2_readint();
2207       if (currip != ',')
2208         { al2_continue("missing argument"); }
2209       al2_nextnw();
2210       if ((j = al2_pwrd(0)) == 0)
2211         { al2_continue("empty argument"); }
2212       al2_endcmd();
2213 
2214       if (!tabinfo)
2215         { al2_continue("table d.n.e. or has no information"); }
2216       if (i < 1 || i >= nextdf || COL1(i) < 0)
2217         { al2_continue("invalid/redundant coset number"); }
2218 
2219       /* Now copy currword (gen'r nos) to currrep (col nos) */
2220       repsiz = 0;
2221       for (k = 0; k < j; k++)
2222         {
2223         if ( !al1_addrep( gencol[ndgen+currword[k]] ) )
2224           { al2_continue("unable to build coset rep've"); }
2225         }
2226 
2227       if ((k = al1_trrep(i)) == 0)
2228         { fprintf(fop, "* Trace does not complete\n"); }
2229       else
2230         { fprintf(fop, "%d * word = %d\n", i, k); }
2231 
2232       continue;
2233       }
2234 
2235     /* Negative workspace sizes are errors, zero size selects DEFWORK, and
2236     values <1K are rounded up to 1K. */
2237 
2238     if (al2_match("wo[rkspace]"))
2239       {
2240       if ( !(isdigit(currip) || currip == '+' || currip == '-') )
2241         {
2242         al2_endcmd();		/* Error if currip not ';' or '\n'! */
2243         fprintf(fop, "workspace = %d x %d\n", workspace, workmult);
2244         }
2245       else
2246         {
2247         i = al2_readint();
2248         j = al2_readmult();
2249         al2_endcmd();
2250 
2251         if (i < 0)
2252           { al2_continue("argument must be non-negative"); }
2253         else if (i == 0)		/* Use default value */
2254           {
2255           i = DEFWORK;
2256           j = 1;
2257           }
2258         else if (j == 1 && i < KILO)	/* Minimum allowed is 1xKILO */
2259           { i = KILO; }
2260 
2261         workspace = i;
2262         workmult  = j;
2263 
2264         /* The casts to long are to allow 64-bit systems (ie, IP27/R10000)
2265         to break the 4G physical memory barrier. */
2266 
2267         if (costable != NULL)
2268           { free(costable); }
2269         costable =
2270           (int *)malloc((long)workspace*(long)workmult*(long)sizeof(int));
2271         if (costable == NULL)
2272           {
2273           okstart = okcont   = okredo = FALSE;	/* Problem, no table! */
2274           tabinfo = tabindex = FALSE;
2275           al2_restart("unable to resize workspace (will try default)");
2276           }
2277 
2278         okstart = (ndgen > 0);			/* New table ... */
2279         okcont  = okredo   = FALSE;
2280         tabinfo = tabindex = FALSE;
2281         }
2282 
2283       continue;
2284       }
2285 
2286     /* ... no match; signal an error */
2287 
2288     al2_continue("there is no such keyword");
2289     }
2290   }
2291 
2292