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