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