1 
2 /**************************************************************************
3 
4         postproc.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 This is the post (enumeration) processing stuff for stand-alone ACE.  Note
18 that many of the routines here could be considered as Level 0 or Level 1
19 functions, since they perform general-purpose table manipulations; however,
20 some of them use Level 2's error-handler, so they couldn't be moved as they
21 stand to a lower level.  They have been written to be as versatile and as
22 `robust' as possible, although Level 2 may not take full advantage of this.
23 
24 **************************************************************************/
25 
26 #include "al2.h"
27 
28 #include <ctype.h>
29 
30 	/******************************************************************
31 	void al2_oo(int arg)
32 
33 	Find cosets with order a multiple of |arg|, modulo the subgroup.
34 	If arg=0, print all orders.  Otherwise, print the first (arg>0) or
35 	all (arg<0) coset numbers with order a multiple of |arg|, along
36 	with their reps.
37 	******************************************************************/
38 
al2_oo(int arg)39 void al2_oo(int arg)
40   {
41   int i,j,k, aarg, ord;
42   Logic found;
43 
44   if (arg < 0)
45     { aarg = -arg; }
46   else
47     { aarg = arg; }
48 
49   found = FALSE;
50   for (i = 2; i < nextdf; i++)
51     {
52     if (COL1(i) >= 0)
53       {
54       if (al1_bldrep(i))
55         {
56         if ((ord = al1_ordrep()) == 0)
57           { continue; }
58         if ((arg == 0) || (ord%aarg == 0))
59           {
60           if (!found)
61             {
62             found = TRUE;
63             fprintf(fop, "  coset |  order  rep\n");
64             fprintf(fop, "--------+------------\n");
65             }
66 
67           fprintf(fop, "%7d | %6d  ", i, ord);
68           for (j = 0; j < repsiz; j++)
69             {
70             k = colgen[currrep[j]];             /* generator number */
71             if (!galpha)
72               { fprintf(fop, "%d ", k); }
73             else
74               { fprintf(fop, "%c",
75                              (k > 0) ? algen[k] : toupper(algen[-k])); }
76             }
77           fprintf(fop, "\n");
78 
79           if ((arg > 0) && (ord%aarg == 0))
80             { break; }
81           }
82         }
83       else
84         { al2_continue("unable to build coset rep've"); }
85       }
86     }
87 
88   if (!found)
89     { fprintf(fop, "* Nothing found\n"); }
90   }
91 
92 	/******************************************************************
93 	Logic al2_normal(int cos)
94 
95 	Coset cos is normalising if for the subgroup H = <w_1,...,w_s>,
96 	then cos*w_j = cos (for all j=1...s).  Return T if this is the
97 	case, else F (incl. out-of-range/redundant).
98 
99 	Warning: this routine traces thro the subgrp gens, using the
100 	enumerator's data structure.  Thus, it can only be used if the
101 	al1_start() routine has been called & nsgpg has *not* been altered.
102 	Similar remarks apply to al2_normcl().
103 	******************************************************************/
104 
al2_normal(int cos)105 Logic al2_normal(int cos)
106   {
107   int s, *beg, *end, *pi, next;
108 
109   if (cos < 1 || cos >= nextdf || COL1(cos) < 0)
110     { return(FALSE); }
111 
112   for (s = 1; s <= nsgpg; s++)
113     {
114     beg = &(subggen[subgindex[s]]);
115     end = beg-1 + subglength[s];
116 
117     next = cos;
118     for (pi = beg; pi <= end; pi++)
119       {
120       if ((next = CT(next,*pi)) == 0 || COL1(next) < 0)
121         { return(FALSE); }
122       }
123     if (next != cos)
124       { return(FALSE); }
125     }
126 
127   return(TRUE);
128   }
129 
130 	/******************************************************************
131 	void al2_sc(int arg)
132 
133 	Print the stabilising cosets of the subgrp.  arg>0 prints the first
134 	arg of them, arg<0 prints the first |arg| + reps, and arg=0 prints
135 	all of them + reps.
136 	******************************************************************/
137 
al2_sc(int arg)138 void al2_sc(int arg)
139   {
140   int i,j,k, aarg;
141   Logic found;
142 
143   if (arg < 0)
144     { aarg = -arg; }
145   else
146     { aarg = arg; }
147 
148   found = FALSE;
149   for (i = 2; i < nextdf; i++)
150     {
151     if (COL1(i) >= 0)
152       {
153       if (al2_normal(i))
154         {
155         if (!found)
156           {
157           found = TRUE;
158           if (arg <= 0)
159             { fprintf(fop, "Stabilising cosets (+ reps):\n"); }
160           else
161             { fprintf(fop, "Stabilising cosets:\n"); }
162           }
163 
164         fprintf(fop, "%7d", i);
165         if (arg <= 0)
166           {
167           if (!al1_bldrep(i))
168             { al2_continue("unable to build coset rep've"); }
169           fprintf(fop, "  ");
170           for (j = 0; j < repsiz; j++)
171             {
172             k = colgen[currrep[j]];
173             if (!galpha)
174               { fprintf(fop, "%d ", k); }
175             else
176               { fprintf(fop, "%c",
177                              (k > 0) ? algen[k] : toupper(algen[-k])); }
178             }
179           }
180         fprintf(fop, "\n");
181 
182         if ((aarg != 0) && (--aarg == 0))
183           { break; }
184         }
185       }
186     }
187 
188   if (!found)
189     { fprintf(fop, "* Nothing found\n"); }
190   }
191 
192 	/******************************************************************
193 	void al2_cycles(void)
194 
195 	Print out the coset table in cycles (permutation representation).
196 	This *must* only be called when a completed *and* compacted coset
197 	table is present; ie, when a finite index has been computed & a
198 	(final) CO phase has been run.  The dispatcher code in parser.c
199 	enforces this.  Note the use of the sign bit to track processed
200 	cosets for each generator.
201 
202 	ToDo: what about faithfulness?!
203 	******************************************************************/
204 
al2_cycles(void)205 void al2_cycles(void)
206   {
207   int i, j, k, kn, t, length;
208   Logic id;
209 
210   for (j = 1; j <= ndgen; j++)
211     {
212     k = gencol[ndgen+j];	/* find the column k for generator j */
213     id = TRUE;        		/* assume action is the identity */
214 
215     if (!galpha)		/* print lhs & record its length */
216       {
217       fprintf(fop, "%d = ", j);
218       length = al2_outlen(j) + 3;
219       }
220     else
221       {
222       fprintf(fop, "%c = ", algen[j]);
223       length = 4;
224       }
225 
226     for (i = 1; i <= nalive; i++)
227       {
228       if (CT(i, k) == i)	/* skip if i is a one-cycle */
229         {
230         CT(i, k) = -i;
231         continue;
232         }
233 
234       /* have we used coset i in previous cycle?  */
235 
236       if (CT((kn = i), k) < 0)
237         { continue; }
238 
239       id = FALSE;   		/* action of generator not identity */
240 
241       /* no, trace out this cycle  */
242 
243       length += al2_outlen(kn) + 1;
244       if (length < LLL)
245         { fprintf(fop, "(%d", kn); }
246       else
247         {
248         fprintf(fop, "\n  (%d", kn);
249         length = al2_outlen(kn) + 3;
250         }
251 
252       t = CT(kn, k);
253       CT(kn, k) = -t;   	/* mark this coset as used */
254       kn = t;
255 
256       while (CT(kn,k) > 0)
257         {
258         length += al2_outlen(kn) + 1;
259         if (length < LLL)
260           { fprintf(fop, ",%d", kn); }
261         else
262           {
263           fprintf(fop, ",\n  %d", kn);
264           length = al2_outlen(kn) + 2;
265           }
266 
267         t = CT(kn, k);
268         CT(kn, k) = -t;
269         kn = t;
270         }
271 
272       /* we have reached the end of the cycle */
273 
274       fprintf(fop, ")");
275       length++;
276       }
277 
278     if (id)
279       { fprintf(fop, "identity\n"); }
280     else
281       { fprintf(fop, "\n"); }
282 
283     /* change all the (negative) values in this column back to positive */
284 
285     for (i = 1; i <= nalive; i++)
286       { CT(i, k) = -CT(i, k); }
287     }
288   }
289 
290 	/******************************************************************
291 	void al2_normcl(Logic build)
292 
293 	Check normal closure.  Trace g^-1 * w * g and g * w * g^-1 for all
294 	group generators g and all subgroup generator words w, noting
295 	whether we get back to coset 1 or not.  Note that 1.w^g = 1 iff
296         1.Gwg = 1 iff 1.Gw = 1.G (hence the apparent switch in the sense of
297         first when we set it).  If build is T, then the conjugates of subgroup
298         generators by group generators that cannot be traced to the subgroup
299 	are added to the list of subgroup generators; the *user* has to rerun
300         the enumeration.  Note that coset #1 is never redundant; however,
301         others may be, and the table may be incomplete.
302 
303         Note: if g has a finite order, say n, then G=g^{n-1}.  So either
304         both or neither of gwG and Gwg are in the subgroup (ie, we need
305         check only one). However, g may have infinite/unknown order so,
306         in general, we have to check both.
307 
308 	Remark: we choose to ignore those g/w pairs where the trace does
309 	not complete.  It could be argued that we should include them in
310 	the list of added conjugates (as ACE2 did).  If we did, this would
311         require definitions to be made during the rerun of the SG phase.
312         By including only those pairs which do trace, but not to 1, we
313         effectively introduce coincidences.
314 	******************************************************************/
315 
al2_normcl(Logic build)316 void al2_normcl(Logic build)
317   {
318   int col, first, next, s, *beg, *end, *pi, j,k,l;
319   Logic found;
320   Wlist *list;
321   Wlelt *lelt;
322 
323   found = FALSE;
324   list  = NULL;
325 
326   for (col = 1; col <= ncol; col++)	/* all `significant' gen'rs */
327     {
328     if ((first = CT(1,invcol[col])) == 0 || COL1(first) < 0)
329       { continue; }			/* trace incomplete, next col */
330 
331     for (s = 1; s <= nsgpg; s++)	/* all (original) subgrp gens */
332       {
333       beg = &subggen[subgindex[s]];
334       end = beg-1 + subglength[s];
335 
336       next = first;
337       for (pi = beg; pi <= end; pi++)
338         {
339         if ((next = CT(next,*pi)) == 0 || COL1(next) < 0)
340           { goto next_s; }		/* trace incomplete, next gen */
341         }
342       if (next == first)
343         { continue; }			/* closes, next gen */
344 
345       /* At this point, we know that the trace of s^col completes but does
346       not get back to 1.  So we have a conjugate that's not in the subgrp. */
347 
348       found = TRUE; 			/* at least 1 conjugate not in sgp */
349 
350       k = colgen[col];			/* (signed) generator number */
351       if (!galpha)
352         {
353         fprintf(fop, "Conjugate by grp gen'r \"%d\" of", k);
354         fprintf(fop, " subgrp gen'r \"");
355         for (pi = beg; pi <= end; pi++)
356           { fprintf(fop, " %d", colgen[*pi]); }
357         }
358       else
359         {
360         fprintf(fop, "Conjugate by grp gen'r \"%c\" of",
361                      (k > 0) ? algen[k] : toupper(algen[-k]));
362         fprintf(fop, " subgrp gen'r \"");
363         for (pi = beg; pi <= end; pi++)
364           {
365           if ((l = colgen[*pi]) > 0)
366             { fprintf(fop, "%c", algen[l]); }
367           else
368             { fprintf(fop, "%c", toupper(algen[-l])); }
369           }
370         }
371       fprintf(fop, "\" not in subgrp\n");
372 
373       if (build)
374         {
375         if (list == NULL)
376           {
377           if ((list = al1_newwl()) == NULL)
378             { al2_continue("unable to create new subgrp gen'r list"); }
379           }
380 
381         if ((lelt = al1_newelt()) == NULL)
382           {
383           al1_emptywl(list);
384           free(list);
385           al2_continue("unable to create subgrp gen'r list elt");
386           }
387 
388         lelt->len = subglength[s] + 2;		/* gen'r + col/col^-1 */
389         if ((lelt->word = (int*)malloc((lelt->len+1)*sizeof(int))) == NULL)
390           {
391           al1_emptywl(list);
392           free(list);
393           free(lelt);
394           al2_continue("unable to create subgrp gen'r list elt word");
395           }
396         lelt->exp = 1;
397 
398         lelt->word[1] = -k;
399         for (pi = beg, j = 2; pi <= end; pi++, j++)
400           { lelt->word[j] = colgen[*pi]; }
401         lelt->word[lelt->len] = k;
402 
403         al1_addwl(list,lelt);
404         }
405 
406       next_s:
407         ;
408       }
409     }
410 
411   if (!found)
412     { fprintf(fop, "* All (traceable) conjugates in subgroup\n"); }
413 
414   /* If list != NULL then we must have created a list with at least one new
415   subgrp gen'r; so found is T & genlst is non-NULL/non-empty!  Append the
416   list of new gen'rs & update the enumeration status. */
417 
418   if (list != NULL)
419     {
420     al1_concatwl(genlst,list);
421 
422     nsgpg = genlst->len;
423 
424     okcont  = FALSE;
425     tabinfo = tabindex = FALSE;
426 
427     fprintf(fop, "* Subgroup generators have been augmented\n");
428     }
429   }
430 
431 	/******************************************************************
432 	void al2_cc(int cos)
433 
434 	cos is guaranteed (by the caller) to be a non-redundant coset in
435 	the range 2..nextdf-1.  Get its rep & add it to the subgroup gens.
436 	******************************************************************/
437 
al2_cc(int cos)438 void al2_cc(int cos)
439   {
440   int i,j;
441   Wlelt *lelt;
442 
443   /* Build & printout the representative */
444 
445   if (!al1_bldrep(cos))
446     { al2_continue("unable to build rep've"); }
447 
448   fprintf(fop, "Coset #%d: ", cos);
449   for (i = 0; i < repsiz; i++)
450     {
451     j = colgen[currrep[i]];
452     if (!galpha)
453       { fprintf(fop, "%d ", j); }
454     else
455       { fprintf(fop, "%c", (j > 0) ? algen[j] : toupper(algen[-j])); }
456     }
457   fprintf(fop, "\n");
458 
459   /* Add the rep to the subgroup generators */
460 
461   if ((lelt = al1_newelt()) == NULL)
462     { al2_continue("unable to create new subgrp gen'r"); }
463 
464   lelt->len = repsiz;
465   if ((lelt->word = (int*)malloc((lelt->len+1)*sizeof(int))) == NULL)
466     {
467     free(lelt);
468     al2_continue("unable to create subgrp gen'r word");
469     }
470   lelt->exp = 1;
471 
472   for (i = 0; i < repsiz; i++)
473     { lelt->word[i+1] = colgen[currrep[i]]; }
474 
475   /* Add the new element to the (possibly non-existent) gen list */
476 
477   if (genlst == NULL)
478     {
479     if ((genlst = al1_newwl()) == NULL)
480       {
481       free(lelt->word);
482       free(lelt);
483       al2_continue("unable to create subgrp gen'r list");
484       }
485     }
486   al1_addwl(genlst,lelt);
487   nsgpg++;
488 
489   /* Reset enumeration status & `remind' the user */
490 
491   okcont  = FALSE;
492   tabinfo = tabindex = FALSE;
493 
494   fprintf(fop, "* Subgroup generators have been augmented\n");
495   }
496 
497 	/******************************************************************
498 	void al2_rc(int desire, int count)
499 
500 	Try to find a nontrival subgroup with index a multiple of a desired
501         index `desire' by repeatedly putting randomly chosen cosets
502 	coincident with coset 1 and seeing what happens.  The special value
503 	desire=0 accepts *any* non-trivial finite index, while desire=1
504 	accepts *any* finite index.  We use the (not very good, but ok for
505 	our purposes) random number generator rand(), which returns numbers
506 	in the range 0..32767 (ie, lower 15 bits).  We take care to ensure
507 	that we generate a `valid' coset to set coincident with #1.  If an
508 	attempt fails, we restore the original subgrp gens, rerun the
509 	original enumeration, and try again (making up to count attempts in
510 	all).  We use the asis flag to prevent subgroup generator
511 	reordering, so that we can easily blow away the added generators.
512 
513 	Notes:
514 	(i) This routine presupposes that an enumeration has already been
515 	performed (this may or may not have yielded a finite index).  The
516 	presentation and all the control parameters (apart from asis) are
517 	frozen at their current values during this call; only the subgroup
518 	generator list is altered.  Any redo (or start) calls to the
519 	enumerator use the current settings, including any messaging.
520 	(ii) This routine can take a *long* time.
521 	(iii) On success, the presentation/table reflects the discovered
522 	subgroup.  On failure, it reflects the original status.
523 	(iv) We try hard to ensure that the system is always left in a
524 	consistent state, and that all errors are picked up.  However, it
525 	is *strongly* recommended that a positive result is checked (by
526 	doing a complete enumeration), and that nothing is assumed about
527 	the presentation/table on a negative result or on an error (note
528 	that the call to al2_cc() could cause an error return).
529 	(v) Note that the value of cos, before it is reduced modulo nextdf,
530 	is limited to 30 bits (ie, 0..1073741823).
531 	******************************************************************/
532 
al2_rc(int desire,int count)533 void al2_rc(int desire, int count)
534   {
535   int r1, r2, cos, old, i, cnt;
536   Logic tmp;
537   Wlelt *p, *q;
538 
539   /* Record current status; asis flag & subgrp gen list */
540 
541   tmp  = asis;
542   asis = TRUE;
543   old  = nsgpg;
544 
545   for (cnt = 1; cnt <= count; cnt++)	/* Try up to count times */
546     {
547     fprintf(fop, "* Attempt %d ...\n", cnt);
548 
549     while (TRUE)			/* Try until success / too small */
550       {
551       do
552         {
553         r1 = rand();
554         r2 = rand();
555         cos = ((r1 << 15) + r2)%nextdf;
556         }
557       while (cos < 2 || COL1(cos) < 0);
558 
559       al2_cc(cos);
560 
561       /* This chunk of code, for redo, is pinched from the parser */
562 
563       al1_rslt(lresult = al1_start(2));
564 
565       if (lresult > 0 && sgdone)
566         {
567         okcont  = TRUE;
568         tabinfo = tabindex = TRUE;
569         }
570       else if (lresult >= -259 && sgdone)
571         {
572         okcont   = TRUE;
573         tabinfo  = TRUE;
574         tabindex = FALSE;
575         }
576       else
577         {
578         okcont  = FALSE;
579         tabinfo = tabindex = FALSE;
580         }
581       if (lresult < -260)
582         { okredo = FALSE; }
583 
584       /* Try and sort out what happened! */
585 
586       if (!(okcont && okredo && tabinfo))
587         {
588         asis = tmp;
589         al2_restart("* An unknown problem has occurred");
590         }
591 
592       if (desire == 0)
593         {
594         if (tabindex && lresult > 1)
595           {
596           fprintf(fop, "* An appropriate subgroup has been found\n");
597           asis = tmp;
598           return;
599           }
600         if (tabindex && lresult == 1)
601           { goto restore; }
602         }
603       else
604         {
605         if (tabindex && lresult%desire == 0)
606           {
607           fprintf(fop, "* An appropriate subgroup has been found\n");
608           asis = tmp;
609           return;
610           }
611         if (tabindex && lresult < desire)
612           { goto restore; }
613         }
614       };
615 
616     /* Setup for another attempt */
617 
618     restore:
619 
620     fprintf(fop, "* Recalculating original table\n");
621 
622     /* Remove added subgroup generators (of which there is at least 1) */
623 
624     if (old == 0)
625       {
626       al1_emptywl(genlst);
627       nsgpg = 0;
628       }
629     else
630       {
631       for (i = 1, p = genlst->first; i < old; i++, p = p->next)
632         { ; }
633 
634       q = p->next;		/* Points to first added generator */
635 
636       genlst->last        = p;
637       genlst->last->next  = NULL;
638       genlst->len = nsgpg = old;
639 
640       for (p = q; p != NULL; )
641         {
642         q = p->next;
643 
644         if (p->word != NULL)
645           { free(p->word); }
646         free(p);
647 
648         p = q;
649         }
650       }
651 
652     /* Rerun the (original) enumeration (using code pinched from the
653     parser), and then try to sort out what happened. */
654 
655     al1_rslt(lresult = al1_start(0));
656 
657     if (lresult > 0 && sgdone)
658       {
659       okcont  = okredo   = TRUE;
660       tabinfo = tabindex = TRUE;
661       }
662     else if (lresult >= -259 && sgdone)
663       {
664       okcont   = okredo = TRUE;
665       tabinfo  = TRUE;
666       tabindex = FALSE;
667       }
668     else
669       {
670       okcont  = okredo   = FALSE;
671       tabinfo = tabindex = FALSE;
672       }
673 
674     if (!(okcont && okredo && tabinfo))
675       {
676       asis = tmp;
677       al2_restart("* An unknown problem has occurred");
678       }
679 
680     if (desire == 0)
681       {
682       if (tabindex && lresult > 1)
683         {
684         fprintf(fop, "* The original subgroup is appropriate!\n");
685         asis = tmp;
686         return;
687         }
688       }
689     else
690       {
691       if (tabindex && lresult%desire == 0)
692         {
693         fprintf(fop, "* The original subgroup is appropriate!\n");
694         asis = tmp;
695         return;
696         }
697       }
698 
699     if (tabindex && lresult == 1)
700       {
701       asis = tmp;
702       al2_restart("* Unable to restore original status");
703       }
704     if (desire >= nalive)
705       {
706       asis = tmp;
707       al2_restart("* Unable to restore original status");
708       }
709     };
710 
711   /* Our efforts failed.  The last time through the outer loop restored the
712   original subgrp gens & table, so just restore asis & print a message. */
713 
714   fprintf(fop, "* No success; original status restored\n");
715   asis = tmp;
716   }
717 
718 	/******************************************************************
719 	void al2_dw(Wlist *p)
720 
721 	Delete the list of words given by intarr[] from the word list p.
722 	Both intarr[] & *p are guaranteed to be non-empty.
723 	******************************************************************/
724 
al2_dw(Wlist * p)725 void al2_dw(Wlist *p)
726   {
727   int i,j;
728   Wlelt *old, *tmp;
729 
730   /* Check the 1st value, and then ensure that (any) others are strictly
731   increasing and don't exceed the list length. */
732 
733   if (intarr[0] < 1 || intarr[0] > p->len)
734     { al2_continue("first argument out of range"); }
735   for (i = 1; i < intcnt; i++)
736     {
737     if (intarr[i] <= intarr[i-1] || intarr[i] > p->len)
738       { al2_continue("bad argument list"); }
739     }
740 
741   /* Trace through the list, `moving' the required words & dropping those
742   not required (freeing their space). */
743 
744   old = p->first;		/* Start at front of old list ... */
745   i   = 0;
746 
747   j = 0;			/* First deletion is position intarr[0] */
748 
749   p->first = p->last = NULL;	/* Clear `new' list ... */
750   p->len   = 0;
751 
752   while (old != NULL)
753     {
754     tmp = old;			/* `Chop' head of old list off */
755     old = old->next;
756     i++;			/* Current position */
757 
758     if (i == intarr[j])		/* Delete this one */
759       {
760       if (tmp->word != NULL)
761         { free(tmp->word); }
762       free(tmp);
763 
764       j++;
765       }
766     else			/* Keep this one */
767       {
768       if (p->first == NULL)
769         { p->first = tmp; }
770       else
771         { p->last->next = tmp; }
772       tmp->next = NULL;
773       p->last   = tmp;
774       p->len++;
775       }
776     }
777   }
778 
779 /**************************************************************************
780 The stuff under here is all concerned with testing various equivalent
781 presentations; either doing a random selection thereof, or all of them.  It
782 is guaranteed that the (top-level) routines are only called if the relator
783 list is non-empty.  The code here is all very naive, but there is little
784 point in trying to be clever/efficient.  Note that, no matter how we
785 cycle/invert/permute the relators, the data attached to each word (ie, its
786 length & exponent, and how it was entered) remains valid.
787 **************************************************************************/
788 
789 	/******************************************************************
790 	void al2_inv_rel(Wlelt *p)
791 
792 	Formally invert the word pointed to by p.
793 	******************************************************************/
794 
al2_inv_rel(Wlelt * p)795 void al2_inv_rel(Wlelt *p)
796   {
797   int j,k, len;
798 
799   len = p->len;
800 
801   for (j = 1; j <= len/2; j++)
802     {
803     k                =  p->word[j];
804     p->word[j]       = -p->word[len+1-j];
805     p->word[len+1-j] = -k;
806     }
807   if (len%2 == 1)
808     { p->word[1 + len/2] = -p->word[1 + len/2]; }
809   }
810 
811 	/******************************************************************
812 	void al2_cyc_rel(Wlelt *p)
813 
814 	Cycle the word pointed to by p by 1 position.
815 	******************************************************************/
816 
al2_cyc_rel(Wlelt * p)817 void al2_cyc_rel(Wlelt *p)
818   {
819   int j,k;
820 
821   k = p->word[1];
822   for (j = 1; j <= p->len-1; j++)
823     { p->word[j] = p->word[j+1]; }
824   p->word[p->len] = k;
825   }
826 
827 	/******************************************************************
828 	void al2_per_rel(void)
829 
830 	Randomly pick a position in the relator list, and move it to the
831 	front of the list.  The list is guaranteed to contain at least 2
832 	elements.
833 	******************************************************************/
834 
al2_per_rel(void)835 void al2_per_rel(void)
836   {
837   Wlelt *p, *pp;
838   int c,i;
839 
840   c = 1 + rand()%rellst->len;		/* 1 <= c <= rellst->len */
841   if (c == 1)
842     { return; }				/* do nothing */
843 
844   pp = rellst->first;
845   p  = pp->next;
846   i  = 2;
847   for ( ; i < c; i++)
848     { pp = p;  p = p->next; }
849 
850   if (rellst->last == p)
851     {
852     pp->next     = NULL;
853     rellst->last = pp;
854     }
855   else
856     { pp->next = p->next; }
857 
858   p->next       = rellst->first;
859   rellst->first = p;
860   }
861 
862 	/******************************************************************
863 	void al2_munge_cyc(void)
864 	void al2_munge_inv(void)
865 	void al2_munge_per(void)
866 
867 	These 3 routines implement random cyclings, inversions &
868 	permutations of the relators respectively.  Note that we have to
869 	take a `guess' as to how many relator list element moves are needed
870 	to `randomly' reorder the relators.  The permutation becomes
871 	progressively `better' the more runs we do.
872 	******************************************************************/
873 
al2_munge_cyc(void)874 void al2_munge_cyc(void)
875   {
876   Wlelt *p;
877   int c;
878 
879   for (p = rellst->first; p != NULL; p = p->next)
880     {
881     if ((c = rand()%p->len) > 0)
882       {
883       while (c-- > 0)
884         { al2_cyc_rel(p); }
885       }
886     }
887   }
888 
al2_munge_inv(void)889 void al2_munge_inv(void)
890   {
891   Wlelt *p;
892 
893   for (p = rellst->first; p != NULL; p = p->next)
894     {
895     if (rand()%2 == 1)
896       { al2_inv_rel(p); }
897     }
898   }
899 
al2_munge_per(void)900 void al2_munge_per(void)
901   {
902   int len = rellst->len;
903 
904   while ((len /= 2) >= 1)
905     { al2_per_rel(); }
906   }
907 
908 	/******************************************************************
909 	void al2_rep(int cntrl, int cnt)
910 
911 	Do cnt enumerations using random equivalent presentations.  The 3
912 	lsbs of cntrl control cycling, inverting & permuting respectively.
913 	We turn messaging off, dump the relators *after* each run (ie,
914 	after al1_start() processes them, so that we see what they actually
915 	were for the run), and use asis to prevent al1_start() from
916 	messing up the relator ordering.
917 	******************************************************************/
918 
al2_rep(int cntrl,int cnt)919 void al2_rep(int cntrl, int cnt)
920   {
921   Logic tmpa, tmpm;
922 
923   tmpa    = asis;
924   asis    = TRUE;
925   tmpm    = msgctrl;
926   msgctrl = FALSE;
927 
928   while (cnt-- > 0)
929     {
930     if ((cntrl & 0x1) != 0)
931       { al2_munge_cyc(); }
932     if ((cntrl & 0x2) != 0)
933       { al2_munge_inv(); }
934     if ((cntrl & 0x4) != 0)
935       { al2_munge_per(); }
936 
937     /* (Re)run the enumeration, and then try to sort out what happened. */
938 
939     lresult = al1_start(0);
940     fprintf(fop, "Group Relators: ");
941     al1_prtwl(rellst, 16);
942     fprintf(fop, ";\n");
943     al1_rslt(lresult);
944 
945     if (lresult > 0 && sgdone)
946       {
947       okcont  = okredo   = TRUE;
948       tabinfo = tabindex = TRUE;
949       }
950     else if (lresult >= -259 && sgdone)
951       {
952       okcont   = okredo = TRUE;
953       tabinfo  = TRUE;
954       tabindex = FALSE;
955       }
956     else
957       {
958       okcont  = okredo   = FALSE;
959       tabinfo = tabindex = FALSE;
960       }
961 
962     if (!(okcont && okredo && tabinfo))
963       {
964       asis    = tmpa;
965       msgctrl = tmpm;
966       al2_restart("* An unknown problem has occurred");
967       }
968     }
969 
970   asis    = tmpa;
971   msgctrl = tmpm;
972   }
973 
974 	/******************************************************************
975 	void al2_aep2(Wlelt *p, int *d)
976 
977 	For this permutation, recursively do all cycles/inversions.
978 	******************************************************************/
979 
al2_aep2(Wlelt * p,int * d)980 void al2_aep2(Wlelt *p, int *d)
981   {
982   Logic flg;
983   int i,blen;
984 
985   if (p == NULL)			/* End of list, run enumerator */
986     {
987     /* Run the enumeration, and then try to sort out what happened. */
988 
989     d[2]++;
990     lresult = al1_start(0);
991 
992     if (lresult > 0 && sgdone)
993       {
994       okcont  = okredo   = TRUE;
995       tabinfo = tabindex = TRUE;
996       }
997     else if (lresult >= -259 && sgdone)
998       {
999       okcont   = okredo = TRUE;
1000       tabinfo  = TRUE;
1001       tabindex = FALSE;
1002       }
1003     else
1004       {
1005       okcont  = okredo   = FALSE;
1006       tabinfo = tabindex = FALSE;
1007       }
1008 
1009     if (!(okcont && okredo && tabinfo))
1010       {
1011       asis    = (Logic)d[0];
1012       msgctrl = (Logic)d[1];
1013       al2_restart("* An unknown problem has occurred");
1014       }
1015 
1016     /* Did we get an index?  Any new best/worst values? */
1017 
1018     if (tabindex)
1019       {
1020       d[8]++;
1021 
1022       flg = FALSE;
1023       if (maxcos < d[3])
1024         {
1025         d[3] = maxcos;
1026         flg = TRUE;
1027         }
1028       if (maxcos > d[4])
1029         {
1030         d[4] = maxcos;
1031         flg = TRUE;
1032         }
1033       if (totcos < d[5])
1034         {
1035         d[5] = totcos;
1036         flg = TRUE;
1037         }
1038       if (totcos > d[6])
1039         {
1040         d[6] = totcos;
1041         flg = TRUE;
1042         }
1043       if (flg)
1044         {
1045         fprintf(fop, "Group Relators: ");
1046         al1_prtwl(rellst, 16);
1047         fprintf(fop, ";\n");
1048         al1_rslt(lresult);
1049         }
1050 
1051       /* DTT code: dump *all* totcos values */
1052       /*
1053       fprintf(fop, "DTT: totcos=%d\n", totcos);
1054       */
1055       }
1056     }
1057 
1058   /* Cycle and/or invert this word, and then recurse.  Note the care to
1059   ensure that we always do just what is required; in particular, we must
1060   ensure we restore a word to its original form.  Note that we correctly
1061   cope with cycling in the presence of non-1 exponents.  We *cannot*
1062   suppress inverting (x)^n, if x is an involution, since the geninv[]
1063   array is recalculated by al1_start() & may change since we're
1064   manipulating asis.  To implement this, we'd need to duplicate the code in
1065   the al1_chkinvol() function.  In fact, there's no end to this, since
1066   inverting (ab)^n, if a & b are involutions, is equivalent to cycling it,
1067   and doing *both* is wasteful! */
1068 
1069   else
1070     {
1071     blen = p->len/p->exp;		/* Baselength of word */
1072 
1073     if ((d[7] & 0x3) == 0)		/* Do nothing */
1074       { al2_aep2(p->next, d); }
1075     else if ((d[7] & 0x3) == 1)		/* Cycle only */
1076       {
1077       for (i = 0; i < blen; i++)
1078         {
1079         al2_cyc_rel(p);
1080         al2_aep2(p->next, d);
1081         }
1082       }
1083     else if ((d[7] & 0x3) == 2)		/* Invert only */
1084       {
1085       al2_aep2(p->next, d);
1086       al2_inv_rel(p);
1087       al2_aep2(p->next, d);
1088       al2_inv_rel(p);
1089       }
1090     else				/* Cycle & invert */
1091       {
1092       for (i = 0; i < blen; i++)
1093         {
1094         al2_cyc_rel(p);
1095         al2_aep2(p->next, d);
1096         }
1097       al2_inv_rel(p);
1098       for (i = 0; i < blen; i++)
1099         {
1100         al2_cyc_rel(p);
1101         al2_aep2(p->next, d);
1102         }
1103       al2_inv_rel(p);
1104       }
1105     }
1106   }
1107 
1108 	/******************************************************************
1109 	void al2_aep1(int *d, Wlelt *p)
1110 
1111 	Recursively generate the permutations, calling al2_aep2() for each
1112 	one.  p is a pointer to parent node of the unprocessed `tail' of
1113 	rellst.  rellst contains >1 elts & p is (initially) the 1st elt.
1114 	The node pointed to by the parent node is put in all positions, and
1115 	then we recurse.  So 123 yields 321, 231, 213, 312, 132, 123.
1116 	******************************************************************/
1117 
al2_aep1(int * d,Wlelt * p)1118 void al2_aep1(int *d, Wlelt *p)
1119   {
1120   Wlelt *t0, *t1;
1121 
1122   if (p->next == NULL)
1123     { al2_aep2(rellst->first, d); }
1124   else
1125     {
1126     /* Move the head of the unprocessed tail to all possible positions. */
1127 
1128     t0 = p->next;			/* Node being moved */
1129     p->next = t0->next;			/* Slice it out ... */
1130     if (rellst->last == t0)
1131       { rellst->last = p; }
1132 
1133     /* The head ... */
1134 
1135     t0->next = rellst->first;
1136     rellst->first = t0;
1137 
1138     al2_aep1(d, p);
1139 
1140     rellst->first = t0->next;
1141 
1142     /* The middle ... */
1143 
1144     for (t1 = rellst->first; t1 != p; t1 = t1->next)
1145       {
1146       t0->next = t1->next;
1147       t1->next = t0;
1148 
1149       al2_aep1(d, p);
1150 
1151       t1->next = t0->next;
1152       }
1153 
1154     /* The tail (where it started) ... */
1155 
1156     t0->next = p->next;
1157     p->next = t0;
1158     if (rellst->last == p)
1159       { rellst->last = t0; }
1160 
1161     al2_aep1(d, p->next);
1162     }
1163   }
1164 
1165 	/******************************************************************
1166 	void al2_aep(int cntrl)
1167 
1168 	Do all enumerations using equivalent presentations; see comments
1169 	for al2_rep().  To prevent having lots of global data floating
1170 	around, we pass a pointer to the datum array, which contains:
1171 		[0]	original asis
1172 		[1]	original msgctrl
1173 		[2]	number of runs
1174 		[3]	min maxcos
1175 		[4]	max maxcos
1176 		[5]	min totcos
1177 		[6]	max totcos
1178 		[7]	cntrl
1179 		[8]	number of successes
1180 	******************************************************************/
1181 
al2_aep(int cntrl)1182 void al2_aep(int cntrl)
1183   {
1184   int datum[9];
1185 
1186   /* Temporary code, until we split al1_start() and do the presentation
1187   altering in the middle.  We need to ensure that the current presentation
1188   has been processed so that the word exponents are correctly set.  We do
1189   this run using whatever the current setup is, *before* we set asis &
1190   turn messaging off.  After this run, the exponents will be fixed.
1191   However, setting asis may screw up involutions (ie, whether or not we'd
1192   need to invert some relators, if requested).  Note that this also sets
1193   maxrow to a valid U.B. for maxcos/totcos. */
1194 
1195   fprintf(fop, "* Priming run ...\n");
1196   al1_rslt(lresult = al1_start(0));
1197 
1198   if (lresult > 0 && sgdone)
1199     {
1200     okcont  = okredo   = TRUE;
1201     tabinfo = tabindex = TRUE;
1202     }
1203   else if (lresult >= -259 && sgdone)
1204     {
1205     okcont   = okredo = TRUE;
1206     tabinfo  = TRUE;
1207     tabindex = FALSE;
1208     }
1209   else
1210     {
1211     okcont  = okredo   = FALSE;
1212     tabinfo = tabindex = FALSE;
1213     }
1214 
1215   if (!(okcont && okredo && tabinfo))
1216     { al2_restart("* An unknown problem has occurred"); }
1217 
1218   /* Start of the `proper' code. */
1219 
1220   datum[0] = (int)asis;
1221   asis     = TRUE;
1222   datum[1] = (int)msgctrl;
1223   msgctrl  = FALSE;
1224   datum[2] = 0;
1225   datum[3] = maxrow+1;
1226   datum[4] = 0;
1227   datum[5] = maxrow+1;
1228   datum[6] = 0;
1229   datum[7] = cntrl;
1230   datum[8] = 0;
1231 
1232   fprintf(fop, "* Equivalent runs ...\n");
1233   if ((cntrl & 0x4) == 0 || rellst->len < 2)	/* No permutations */
1234     { al2_aep2(rellst->first, datum); }
1235   else
1236     { al2_aep1(datum, rellst->first); }
1237 
1238   if (datum[8] == 0)
1239     { fprintf(fop, "* There were no successes in %d runs\n", datum[2]); }
1240   else
1241     {
1242     fprintf(fop, "* There were %d successes in %d runs:\n",
1243                  datum[8], datum[2]);
1244     fprintf(fop, "*   maxcos=%d..%d, totcos=%d..%d\n",
1245                  datum[3], datum[4], datum[5], datum[6]);
1246     }
1247 
1248   asis    = (Logic)datum[0];
1249   msgctrl = (Logic)datum[1];
1250   }
1251 
1252