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