1 #include "defs.h"
2 
3 extern char rs,ginrel[];
4 extern int space,mxc;
5 extern short mpt,rel[],cosno[],gno[],inv[],gch[],*imcos[];
6 short ng,ngi,nsg,nr,endsg,endr,maxcos,str,len,ad,*fpt,*bpt,
7       ccos,maxd,totd,lastd,cind,nfree,stcr,endcr,fcos,bcos,lcl;
8 char fullsc,clsd,lkah;
9 FILE *op;
10 
inperr(char * s,int n)11 void inperr (char *s, int n)
12 { fprintf(stderr,"Input error in %s no %d\n",s,n); }
13 
digit(int j)14 int digit (int j)
15 { if (j>='0' && j<='9') return(1); else return(0); }
16 
letter(int j)17 int letter (int j)
18 { if ((j>='a' && j<='z') || (j>='A' && j<='Z')) return(1); else return(0);}
19 
seeknln(void)20 void seeknln (void) { while (getchar()!='\n'); }
21 
readrel(int s,int no)22 int readrel (int s, int no)
23 /* Reads relation (s=1) or subgroup generator (s=0) number "no" into array
24   rel, after expanding powers, brackets etc.
25    Each relation or subgen is preceded by its length.
26   The global variables ad and str are used to mark the current position
27   in rel.
28 */
29 { short stbr,endbr,exp,l,m,n; char ch;
30   char gotg,br,clbr,emptybr,name[10];
31   if (s) strcpy(name,"relation"); else strcpy(name,"subgen");
32   gotg=0; br=0; clbr=0; ad=str; len=0;
33   ch=getchar();
34   while (ch!='\n')
35   { if (ch==' ') ch=getchar();
36     else if (ch=='(')
37     { if (br || clbr) { inperr(name,no); return(-1); }
38       emptybr=1; br=1; stbr=ad+1; gotg=0; ch=getchar();
39     }
40     else if (ch==')')
41     { if (br==0 || emptybr) {inperr(name,no); return(-1); }
42       br=0; gotg=0; clbr=1; endbr=ad; ch=getchar();
43     }
44     else if (letter(ch))
45     { if (clbr || gch[ch]==0) {inperr(name,no); return(-1);}
46       emptybr=0; gotg=1; len++; ad++; rel[ad]=gch[ch]; ch=getchar();
47     }
48     else if (ch=='-' || digit(ch))
49     { if (gotg==0 && clbr==0) { inperr(name,no); return(-1); }
50       gotg=0;
51       if (ch=='-')
52       { ch=getchar();
53         if (digit(ch)==0) exp= -1;
54         else
55         { exp=0; while (digit(ch)) {exp*=10; exp-=(ch-'0'); ch=getchar();}}
56       }
57       else
58       { exp=0; while (digit(ch)) {exp*=10; exp+=(ch-'0'); ch=getchar();}}
59       if (exp==0) {inperr(name,no); return(-1);}
60       if (clbr)
61       { if (exp<0) for (m=stbr,n=endbr;m<=n;m++,n--)
62         { if (m==n) rel[m]= -rel[m];
63           else { l= -rel[m]; rel[m]= -rel[n]; rel[n]=l; }
64         }
65         exp=abs(exp); exp--; clbr=0;
66         for (n=1;n<=exp;n++) for (m=stbr;m<=endbr;m++)
67         { len++; ad++; rel[ad]=rel[m]; }
68       }
69       else
70       { n=rel[ad];
71         if (exp<0) { n= -n; rel[ad]=n; exp= -exp; }
72         exp--;
73         for (m=1;m<=exp;m++) { len++; ad++; rel[ad]=n; }
74       }
75     }
76     else {inperr(name,no); return(-1); }
77   }
78   if (br || clbr) {inperr(name,no); return(-1); }
79   rel[str]=len; str=ad+1;
80   return(0);
81 }
82 
83 int
tcprog(void)84 tcprog (void)
85 /* This is the main routine */
86 { short i,j,k,ch,*p;  int quot; char redspace,fname[80];
87   printf("Todd-Coxeter Coset Enumeration Algorithm. (HLT + Lookahead).\n\n");
88   printf("    Total space for relations and coset table = %d.\n",space);
89   printf("    Generators should be upper or lower case letters.\n");
90   printf("    Relations and subgroup generators are input as strings in the");
91   printf(" generators.\n");
92   printf("    Generators may be followed by a positive or negative exponent");
93   printf(" ('-' = '-1').\n");
94   printf("    Brackets (but no nested brackets) are allowed, and a bracketed");
95   printf(" expression.\n");
96   printf("      must be followed by an exponent.\n");
97   printf("    Example:  ab-3c6(ab)-2(XYZ)-ba-\n\n\n");
98   printf("Input numbers of generators, subgroup generators and relations.\n");
99   scanf("%hd%hd%hd",&ng,&nsg,&nr);
100   for (i=0;i<=127;i++) gch[i]=0;
101   printf("Input names of generators (upper or lower case letters).\n");
102 /* Read generators as characters, and assign gen no gch[ch] to generator
103    "ch", and -gch[ch] to its inverse. This numbering will be changed later.
104 */
105   for (i=1;i<=ng;i++)
106   { ch= ' '; while (ch==' ' || ch=='\n') ch=getchar();
107     if (letter(ch)==0)
108     { fprintf(stderr,"Generators must be letters.\n"); return(-1);}
109     if (gch[ch]!=0)
110     { fprintf(stderr,"Repeated generator.\n"); return(-1); }
111     gch[ch]=i; gno[i]=0; ginrel[i]=0;
112   }
113   seeknln();
114   if (nsg>0)
115   printf("Input subgroup generators (separated by new lines).\n");
116   str=0; ad= -1;
117   for (i=1;i<=nsg;i++) if (readrel(0,i)== -1) return(-1);
118 /* endsg marks address in array rel where subgens stop */
119   endsg=ad;
120   if (nr>0) printf("Input relators (separated by new lines).\n");
121   for (i=1;i<=nr;i++)
122   { if (readrel(1,i)== -1) return(-1);
123 /* Relations of form x^2 are not normally stored. We record the fact at this
124    stage by putting gno[x]=1.
125    ginrel[x]=1 means that generator x occurs in some relator.
126 */
127     if (len==2 && rel[ad]==rel[ad-1])
128     { gno[abs(rel[ad])]=1; str-=3; ad-=3; }
129     else
130     { k=ad-len+1; for (j=k;j<=ad;j++) ginrel[abs(rel[j])]=1; }
131   }
132 /* For involutions x with ginrel[x]=0, we store x^2 after all, to avoid
133    a number of problems.
134 */
135   for (i=1;i<=ng;i++) if (gno[i]==1 && ginrel[i]==0)
136   { rel[ad+1]=2; rel[ad+2]=i; rel[ad+3]=i; ad+=3; gno[i]=0; }
137   endr=ad;
138 /* endr is the address in rel where relations end. */
139 
140   ngi= -1;
141 /* Now we start the renumbering process, and do the renumbering of the
142    generators of the words stored in rel. Numbers will now all be >=0.
143    inv[x] is the inverse of generator number x.
144    If gen. no. x is an involution, then inv[x]=x. Otherwise inv[x]=x+1 (or x-1).
145    gno[i] is the new number of the old generator number  i.
146 */
147   for (i=1;i<=ng;i++) if (gno[i]==1)
148   { ngi++; gno[i]=ngi; gno[i+52]=ngi; inv[ngi]=ngi; }
149   else
150   { ngi+=2; gno[i]=ngi-1; gno[i+52]=ngi; inv[ngi]=ngi-1; inv[ngi-1]=ngi;}
151   i= -1;
152   while (++i<=endr)
153   { j=rel[i]; k=i+j;
154     while (++i<=k) { p=rel+i; *p= (*p<0) ? gno[52- *p] : gno[*p]; }
155     i=k;
156   }
157 
158 /* Now we arrange the remaining space in the array rel into the coset table.
159    imcos[i][j]=image of coset j under generator i (or 0 when undefined).
160    Some space is also required for the arrays fpt and bpt, which are used
161    as forward and backward pointers in the linked list of active cosets.
162    (But they are used somewhat differently in the coincidence routine.)
163    maxcos is the maximum number of cosets that we have room for.
164 */
165 compmaxcos:
166   quot= (space-endr-2)/(ngi+3); if (quot>mxc) quot=mxc; maxcos=quot;
167   fpt=rel+1+endr; bpt=fpt+maxcos;
168   redspace=0;
169   for (i=0;i<=ngi;i++) imcos[i]=fpt+maxcos*(2+i);
170 /* Allow user to reduce maxcos if -r was set. */
171   if (rs)
172   { printf("Maxcos=%d.  If you wish to reduce this, input smaller number:  ",
173             maxcos);
174     if ((j=getchar())!='\n')
175     { i=0; while (j==' ') j=getchar();
176       if (digit(j))
177       { i=j-'0'; j=getchar(); while (digit(j)) {i=10*i+j-'0'; j=getchar();}
178         if (j!='\n') seeknln();
179       }
180       else if (j!='\n') seeknln();
181       if (i>0 && i<maxcos) { maxcos=i; redspace=1;}
182       printf("Maxcos=%d.",maxcos);
183     }
184     printf("\n\n");
185   }
186   else printf("Maxcos=%d.\n",maxcos);
187 
188 /* Now we are ready to start the enumeration.
189    ccos=current coset being scanned.
190    lastd=last coset defined.
191    maxd=max no of cosets that were defined at any one time.
192    totd=total number of cosets defined.
193    cind=current index=current no of cosets defined
194    nfree=next available coset no.
195    The defined coset nos. form doubly linked list, using fpt and bpt
196    The available coset nos are linked forward with fpt, starting at nfree.
197    When nfree=0, there are no further nos. free (cind=maxcos), and then
198    lookahead is entered (lkah=1)
199 */
200   ccos=1; lastd=1; maxd=1; totd=1; cind=1; nfree=2;
201   for (i=0;i<=ngi;i++) imcos[i][1]=0;
202   for (i=0;i<maxcos;i++) fpt[i]=i+1;
203   fpt[1]=0; fpt[maxcos]=0;
204   lkah=0; bpt[1]=0; endcr= -1;
205 /* endcr runs thro' addresses in rel, whilst scanning relations.
206    First we scan coset 1 under the subgens.
207 */
208   while (endcr!=endsg)
209   { scanrel();
210 /*
211     The external variable fullsc is set 1 or 0 in scanrel.
212     fullsc=1 if the relation or subgen is completely scanned (possibly after
213     making new definitions). If not, then cind=maxcos, and no more
214     definitions could be made. If this occurs at this stage, then we give up
215     straightaway! (I have never known this to happen.)
216 */
217     if (fullsc==0)
218     { fprintf(stderr,"Space overflow in subgroup generation phase.\n");
219       return(-1);
220     }
221   }
222 /* Now we scan each coset under the relations.
223    If fullsc=0, then we enter lookahead. clsd=0 means that a given coset
224    was not fully scanned under all relations, so it must be rescanned after
225    we come out of lookahead. lcl marks the last fully scanned coset before
226    entering lookahead, so this is the return point.
227 */
228   while (ccos!=0)
229   { clsd=1; endcr=endsg;
230     while (endcr!=endr)
231     { scanrel();
232       if (fullsc==0)
233       { clsd=0;
234         if (lkah==0)
235         { lcl=bpt[ccos]; printf("Entering lookahead.\n"); lkah=1; }
236       }
237     }
238     if (lkah)
239     { i=fpt[ccos];
240 /* If the coset was fully scanned, then we change the linking to put it on
241    the end of the list of scanned cosets.
242 */
243       if (clsd)
244       { j=bpt[ccos];
245         if (j!=lcl)
246         { fpt[j]=i;
247           if (i==0) lastd=j; else bpt[i]=j;
248           j=fpt[lcl]; fpt[lcl]=ccos; bpt[ccos]=lcl; fpt[ccos]=j; bpt[j]=ccos;
249         }
250         lcl=ccos;
251       }
252       ccos=i;
253       if (ccos==0)
254 /* End of lookahead */
255       { if (cind==maxcos)
256         { fprintf(stderr,"Not enough space.\n");
257           if (redspace) goto compmaxcos;
258           return(-1);
259         }
260         printf("Exiting lookahead. No. of cosets=%d\n",cind);
261         ccos=fpt[lcl]; lkah=0;
262       }
263     }
264     else ccos=fpt[ccos];
265   }
266 
267 /* That ends the enumeration. In case ginrel[i] is 0 for any generator, we
268    must check that the coset table is full for that gen. Otherwise index is
269    certainly infinite.
270 */
271   for (i=1;i<=ng;i++) if (ginrel[i]==0)
272   { k=gno[i]; j=lastd;
273     while (j!=0)
274     { if (imcos[k][j]==0)
275       { fprintf(stderr,"Coset table incomplete at end of scan. Index is infinite.\n");
276        return(-1);
277       }
278       j=bpt[j];
279     }
280   }
281   printf("Algorithm complete. maxdef,totdef=%d,%d.\n\n",maxd,totd);
282   if (nsg==0) printf("The order of the group is %d.\n\n",cind);
283   else printf("The index of the subgroup is %d.\n\n",cind);
284   if (cind<=mpt)
285 /* Now we compute and store the permutation action of the generators if required
286 */
287   { printf("Do you wish to store the permutations? (y/n)    ");
288     ch=getchar(); seeknln();
289     if (ch=='y')
290     { printf("Input filename    "); scanf("%s",fname);
291       op=fopen(fname,"w"); fprintf(op,"%4d%4d%4d%4d\n",cind,ng,0,0);
292       bpt[1]=1; cosno[1]=1; j=1;
293       for (i=2;i<=cind;i++) { j=fpt[j]; cosno[i]=j; bpt[j]=i; }
294       for (i=1;i<=ng;i++)
295       { k=gno[i];
296         if (cind>=1000)
297         for (j=1;j<=cind;j++) fprintf(op,"%5d",bpt[imcos[k][cosno[j]]]);
298         else
299         for (j=1;j<=cind;j++) fprintf(op,"%4d",bpt[imcos[k][cosno[j]]]);
300         fprintf(op,"\n");
301       }
302     }
303   }
304 }
305 
306 int
scanrel(void)307 scanrel (void)
308 /* Scan ccos under relation or subgen starting at rel[endcr] */
309 { short i,j,k,l,m; char comp;
310 /* Put endcr to point to next relation */
311   fullsc=1; stcr=endcr+2; endcr+=(1+rel[stcr-1]);
312   fcos=ccos; bcos=ccos; comp=1;
313   for (i=stcr;i<=endcr;i++)
314 /* Forward scan. If incomplete, try backward scan
315    If complete, check for coincidence.
316 */
317   { k=imcos[rel[i]][fcos];
318     if (k==0) { comp=0; break;}
319     fcos=k;
320   }
321   if (comp) { if (fcos!=bcos) coinc(fcos,bcos); return(0); }
322   stcr=i;
323   for (i=endcr;i>=stcr;i--)
324 /* Backward scan. If incomplete, make new definitions if possible */
325   { l=rel[i]; m=inv[l]; k=imcos[m][bcos];
326     if (k==0)
327     { if (i==stcr)
328       { k=imcos[l][fcos];
329         if (k==0) { imcos[l][fcos]=bcos; imcos[m][bcos]=fcos; }
330         else if (k!=bcos) coinc(k,bcos);
331         return(0);
332       }
333       if (lkah || nfree==0)
334       { fullsc=0; return(0); /* No new definition possible */}
335       for (j=0;j<=ngi;j++) imcos[j][nfree]=0;
336       totd++; cind++;
337       if (maxd<cind) maxd++;
338       imcos[m][bcos]=nfree; imcos[l][nfree]=bcos; bcos=nfree;
339       bpt[nfree]=lastd; fpt[lastd]=nfree; lastd=nfree;
340       nfree=fpt[nfree]; fpt[lastd]=0;
341     }
342     else bcos=k;
343   }
344   if (fcos!=bcos) coinc(fcos,bcos);
345 }
346 
347 int
coinc(int c1,int c2)348 coinc (int c1, int c2)
349 /* Process coincidence c1 = c2.
350    fpt and bpt are used differently in this routine.
351    For pairs d1,d2 of coincidences in the queue waiting to be processed,
352    (d1>d2), bpt[d1] is the next coset in the queue, and fpt[d1]= -d2.
353    d1 will be eliminated eventually.
354 */
355 { short lc,hc,qh,qt,i,j,x,fhc,bhc,lim,him;
356   if (c1<c2) { lc=c1; hc=c2; }
357   else { lc=c2; hc=c1; }
358 /* hc will be eliminated. qh,qt are head and tail of coincidence queue */
359   qh=0; qt=0;
360 /* Unlink hc from linked list of live cosets */
361   fhc=fpt[hc]; bhc=bpt[hc]; fpt[bhc]=fhc;
362   if (fhc==0) lastd=bhc; else bpt[fhc]=bhc;
363   if (ccos==hc) { ccos=bhc; endcr=endr; clsd=0; }
364   if (lkah && lcl==hc) lcl=bhc;
365   while (1)
366 /* Each loop corresponds to one coincidence pair hc=lc */
367   { fpt[hc]=nfree; nfree=hc; cind--;
368 /* Look at images of hc,lc under each generator, to deduce further possible
369    coincidences
370 */
371     for (i=0;i<=ngi;i++)
372     { him=imcos[i][hc];
373       if (him!=0)
374       { j=inv[i]; lim=imcos[i][lc];
375         if (him==hc) him=lc; else imcos[j][him]=0;
376 /* imcos[j][him] was previously hc, so we remove this entry. It will either be
377    put equal to lc at the end of this loop, or later filled in as a
378    consequence of another coincidence involving lc.
379 */
380         if (lim==0) imcos[i][lc]=him;
381         else
382         { if (lim==hc) { imcos[i][lc]=lc; lim=lc; }
383 /* him,lim is a new coincident pair. First we reduce it using the queue */
384           while (fpt[him]<0) him= -fpt[him];
385           while (fpt[lim]<0) lim= -fpt[lim];
386           if (him!=lim)
387           { if (lim>him) { x=lim; lim=him; him=x; }
388 /* Eliminate him from live cosets */
389             fhc=fpt[him]; bhc=bpt[him]; fpt[bhc]=fhc;
390             if (fhc==0) lastd=bhc; else bpt[fhc]=bhc;
391             if (ccos==him) { ccos=bhc; endcr=endr; clsd=0; }
392             if (lkah && lcl==him) lcl=bhc;
393 /* Add him,lim to coincidence queue */
394             fpt[him]= -lim;
395             if (qh==0) qh=him; else bpt[qt]=him;
396             qt=him; bpt[qt]=0;
397           }
398         }
399         x=imcos[i][lc];
400         if (imcos[j][x]==0) imcos[j][x]=lc;
401       }
402     }
403 /* Get next coincident pair from head of queue, if any */
404     if (qh==0) break;
405     hc=qh; qh=bpt[qh]; lc= -fpt[hc]; bpt[hc]=0;
406   }
407 }
408