/* schreier.c - procedures for manipulating a permutation group using * the random schreier algorithm. There is a separate file schreier.txt * which describes the usage. * * Written for nauty and traces, Brendan McKay 2010-2013. */ #include "schreier.h" TLS_ATTR long long multcount = 0; TLS_ATTR long long filtercount = 0; static permnode id_permnode; /* represents identity, no actual content, doesn't need TLS_ATTR */ #define ID_PERMNODE (&id_permnode) #if !MAXN DYNALLSTAT(int,workperm,workperm_sz); DYNALLSTAT(int,workperm2,workperm2_sz); DYNALLSTAT(int,workpermA,workpermA_sz); DYNALLSTAT(int,workpermB,workpermB_sz); DYNALLSTAT(set,workset,workset_sz); DYNALLSTAT(set,workset2,workset2_sz); #else static TLS_ATTR int workperm[MAXN]; static TLS_ATTR int workperm2[MAXN]; static TLS_ATTR int workpermA[MAXN]; static TLS_ATTR int workpermB[MAXN]; static TLS_ATTR set workset[MAXM]; static TLS_ATTR set workset2[MAXM]; #endif static TLS_ATTR schreier *schreier_freelist = NULL; /* Freelist of scheier structures connected by next field. * vec, pwr and orbits fields are assumed allocated. */ static TLS_ATTR permnode *permnode_freelist = NULL; /* Freelist of permnode structures connected by next field. * p[] is assumed extended. */ static TLS_ATTR int schreierfails = SCHREIERFAILS; #define TMP static boolean filterschreier(schreier*,int*,permnode**,boolean,int,int); #define PNCODE(x) ((int)(((size_t)(x)>>3)&0xFFFUL)) /* #define TESTP(id,p,n) testispermutation(id,p,n) */ #define TESTP(id,p,n) /************************************************************************/ static void testispermutation(int id, int *p, int n) /* For debugging purposes, crash with a message if p[0..n-1] is not a permutation. */ { int i,m; DYNALLSTAT(set,seen,seen_sz); for (i = 0; i < n; ++i) if (p[i] < 0 || p[i] > n) break; if (i < n) { fprintf(stderr,">E Bad permutation (id=%d): n=%d p[%d]=%d\n", id,n,i,p[i]); exit(1); } m = SETWORDSNEEDED(n); DYNALLOC1(set,seen,seen_sz,m,"malloc seen"); EMPTYSET(seen,m); for (i = 0; i < n; ++i) { if (ISELEMENT(seen,p[i])) { fprintf(stderr, ">E Bad permutation (id=%d): n=%d p[%d]=%d is a repeat\n", id,n,i,p[i]); exit(1); } ADDELEMENT(seen,p[i]); } } /************************************************************************/ int schreier_fails(int nfails) /* Set the number of consecutive failures for filtering; * A value of <= 0 defaults to SCHREIERFAILS. * The function value is the previous setting. */ { int prev; prev = schreierfails; if (nfails <= 0) schreierfails = SCHREIERFAILS; else schreierfails = nfails; return prev; } /************************************************************************/ static void clearfreelists(void) /* Clear the schreier and permnode freelists */ { schreier *sh,*nextsh; permnode *p,*nextp; nextsh = schreier_freelist; while (nextsh) { sh = nextsh; nextsh = sh->next; free(sh->vec); free(sh->pwr); free(sh->orbits); free(sh); } schreier_freelist = NULL; nextp = permnode_freelist; while (nextp) { p = nextp; nextp = p->next; free(p); } permnode_freelist = NULL; } /************************************************************************/ static permnode *newpermnode(int n) /* Allocate a new permode structure, with initialized next/prev fields */ { permnode *p; while (permnode_freelist) { p = permnode_freelist; permnode_freelist = p->next; if (p->nalloc >= n && p->nalloc <= n+100) { p->next = p->prev = NULL; p->mark = 0; return p; } else free(p); } #if FLEX_ARRAY_OK p = (permnode*) malloc(sizeof(permnode)+n*sizeof(int)); #else p = (permnode*) malloc(sizeof(permnode)+(n-2)*sizeof(int)); #endif if (p == NULL) { fprintf(ERRFILE,">E malloc failed in newpermnode()\n"); exit(1); } p->next = p->prev = NULL; p->nalloc = n; return p; } /************************************************************************/ static schreier *newschreier(int n) /* Allocate a new schreier structure, with initialised next field */ { schreier *sh; while (schreier_freelist) { sh = schreier_freelist; schreier_freelist = sh->next; if (sh->nalloc >= n && sh->nalloc <= n+100) { sh->next = NULL; return sh; } else { free(sh->vec); free(sh->pwr); free(sh->orbits); free(sh); } } sh = (schreier*) malloc(sizeof(schreier)); if (sh == NULL) { fprintf(ERRFILE,">E malloc failed in newschreier()\n"); exit(1); } sh->vec = (permnode**) malloc(sizeof(permnode*)*n); sh->pwr = (int*) malloc(sizeof(int)*n); sh->orbits = (int*) malloc(sizeof(int)*n); if (sh->vec == NULL || sh->pwr == NULL || sh->orbits == NULL) { fprintf(ERRFILE,">E malloc failed in newschreier()\n"); exit(1); } sh->next = NULL; sh->nalloc = n; return sh; } /************************************************************************/ void freeschreier(schreier **gp, permnode **gens) /* Free schreier structure and permutation ring. Assume this is everything. */ /* Use NULL for arguments which don't need freeing. */ { schreier *sh,*nextsh; permnode *p,*nextp; if (gp && *gp) { nextsh = *gp; while (nextsh) { sh = nextsh; nextsh = sh->next; sh->next = schreier_freelist; schreier_freelist = sh; } *gp = NULL; } if (gens && *gens) { p = *gens; do { nextp = p->next; p->next = permnode_freelist; permnode_freelist = p; p = nextp; } while (p != *gens); *gens = NULL; } } /************************************************************************/ permnode* findpermutation(permnode *pn, int *p, int n) /* Return a pointer to permutation p in the circular list, * or NULL if it isn't present. */ { permnode *rn; int i; if (!pn) return NULL; rn = pn; do { for (i = 0; i < n; ++i) if (rn->p[i] != p[i]) break; if (i == n) return rn; rn = rn->next; } while (rn != pn); return NULL; } /************************************************************************/ void addpermutation(permnode **ring, int *p, int n) /* Add new permutation to circular list, marked. * and return pointer to it in *ring. */ { permnode *pn,*rn; pn = newpermnode(n); rn = *ring; memcpy(pn->p,p,n*sizeof(int)); if (!rn) pn->next = pn->prev = pn; else { pn->next = rn->next; pn->prev = rn; rn->next = pn->next->prev = pn; } pn->refcount = 0; pn->mark = 1; *ring = pn; } /************************************************************************/ static void addpermutationunmarked(permnode **ring, int *p, int n) /* Add new permutation to circular list, not marked. * and return pointer to it in *ring. */ { TESTP(3,p,n); addpermutation(ring,p,n); (*ring)->mark = 0; } /************************************************************************/ boolean addgenerator(schreier **gp, permnode **ring, int *p, int n) /* Add new permutation to group, unless it is discovered to be * already in the group. It is is possible to be in the group * and yet this fact is not discovered. * Return TRUE if the generator (or an equivalent) is added or the * group knowledge with the current partial base is improved. */ { TESTP(2,p,n); return filterschreier(*gp,p,ring,FALSE,-1,n); } /************************************************************************/ boolean condaddgenerator(schreier **gp, permnode **ring, int *p, int n) /* Add new permutation to group, unless it is discovered to be * already in the group. It is is possible to be in the group * and yet this fact is not discovered, but this version will * always notice if this permutation precisely is present. * Return TRUE if the generator (or an equivalent) is added or the * group knowledge with the current partial base is improved. */ { TESTP(4,p,n); if (findpermutation(*ring,p,n)) return FALSE; else return filterschreier(*gp,p,ring,FALSE,-1,n); } /************************************************************************/ static void delpermnode(permnode **ring) /* Delete permnode at head of circular list, making the next node head. */ { permnode *newring; if (!*ring) return; if ((*ring)->next == *ring) newring = NULL; else { newring = (*ring)->next; newring->prev = (*ring)->prev; (*ring)->prev->next = newring; } (*ring)->next = permnode_freelist; permnode_freelist = *ring; *ring = newring; } /************************************************************************/ void deleteunmarked(permnode **ring) /* Delete all permutations in the ring that are not marked */ { permnode *pn,*firstmarked; pn = *ring; firstmarked = NULL; while (pn != NULL && pn != firstmarked) { if (pn->mark) { if (!firstmarked) firstmarked = pn; pn = pn->next; } else delpermnode(&pn); } *ring = pn; } /************************************************************************/ static void clearvector(permnode **vec, permnode **ring, int n) /* clear vec[0..n-1], freeing permnodes that have no other references * and are not marked */ { int i; for (i = 0; i < n; ++i) if (vec[i]) { if (vec[i] != ID_PERMNODE) { --(vec[i]->refcount); if (vec[i]->refcount == 0 && !vec[i]->mark) { *ring = vec[i]; delpermnode(ring); } } vec[i] = NULL; } } /************************************************************************/ static void initschreier(schreier *sh, int n) /* Initialise schreier structure to trivial orbits and empty vector */ { int i; sh->fixed = -1; for (i = 0; i < n; ++i) { sh->vec[i] = NULL; sh->orbits[i] = i; } } /************************************************************************/ void newgroup(schreier **sh, permnode **ring, int n) /* Make the trivial group, allow for ring to be set elsewhere */ { *sh = newschreier(n); initschreier(*sh,n); if (ring) *ring = NULL; } /************************************************************************/ static void applyperm(int *wp, int *p, int k, int n) /* Apply the permutation p, k times to each element of wp */ { int i,j,cyclen,kk,m; TESTP(1,p,n); if (k <= 5) { if (k == 0) return; else if (k == 1) for (i = 0; i < n; ++i) wp[i] = p[wp[i]]; else if (k == 2) for (i = 0; i < n; ++i) wp[i] = p[p[wp[i]]]; else if (k == 3) for (i = 0; i < n; ++i) wp[i] = p[p[p[wp[i]]]]; else if (k == 4) for (i = 0; i < n; ++i) wp[i] = p[p[p[p[wp[i]]]]]; else if (k == 5) for (i = 0; i < n; ++i) wp[i] = p[p[p[p[p[wp[i]]]]]]; } else if (k <= 19) { #if !MAXN DYNALLOC1(int,workpermA,workpermA_sz,n,"applyperm"); #endif for (i = 0; i < n; ++i) workpermA[i] = p[p[p[i]]]; for (; k >= 6; k -= 6) for (i = 0; i < n; ++i) wp[i] = workpermA[workpermA[wp[i]]]; if (k == 1) for (i = 0; i < n; ++i) wp[i] = p[wp[i]]; else if (k == 2) for (i = 0; i < n; ++i) wp[i] = p[p[wp[i]]]; else if (k == 3) for (i = 0; i < n; ++i) wp[i] = workpermA[wp[i]]; else if (k == 4) for (i = 0; i < n; ++i) wp[i] = p[workpermA[wp[i]]]; else if (k == 5) for (i = 0; i < n; ++i) wp[i] = p[p[workpermA[wp[i]]]]; } else { m = SETWORDSNEEDED(n); #if !MAXN DYNALLOC1(int,workpermA,workpermA_sz,n,"applyperm"); DYNALLOC1(int,workpermB,workpermB_sz,n,"applyperm"); DYNALLOC1(set,workset2,workset2_sz,m,"applyperm"); #endif EMPTYSET(workset2,m); /* We will construct p^k in workpermB one cycle at a time. */ for (i = 0; i < n; ++i) { if (ISELEMENT(workset2,i)) continue; if (p[i] == i) workpermB[i] = i; else { cyclen = 1; workpermA[0] = i; for (j = p[i]; j != i; j = p[j]) { workpermA[cyclen++] = j; ADDELEMENT(workset2,j); } kk = k % cyclen; for (j = 0; j < cyclen; ++j) { workpermB[workpermA[j]] = workpermA[kk]; if (++kk == cyclen) kk = 0; } } } for (i = 0; i < n; ++i) wp[i] = workpermB[wp[i]]; } } /************************************************************************/ static boolean filterschreier(schreier *gp, int *p, permnode **ring, boolean ingroup, int maxlevel, int n) /* Filter permutation p up to level maxlevel of gp. * Use ingroup=TRUE if p is known to be in the group, otherwise * at least one equivalent generator is added unless it is proved * (nondeterministically) that it is in the group already. * maxlevel < 0 means no limit, maxlevel=0 means top level only, etc. * Return TRUE iff some change is made. */ { int i,j,j1,j2,lev; int ipwr; schreier *sh; int *orbits,*pwr; permnode **vec,*curr; boolean changed,lchanged,ident; #if !MAXN DYNALLOC1(int,workperm,workperm_sz,n,"filterschreier"); #endif ++filtercount; memcpy(workperm,p,n*sizeof(int)); if (*ring && p == (*ring)->p) { ingroup = TRUE; curr = *ring; } else curr = NULL; /* curr is the location of workperm in ring, if anywhere */ sh = gp; changed = FALSE; if (maxlevel < 0) maxlevel = n+1; for (lev = 0; lev <= maxlevel; ++lev) { for (i = 0; i < n; ++i) if (workperm[i] != i) break; ident = (i == n); if (ident) break; lchanged = FALSE; orbits = sh->orbits; vec = sh->vec; pwr = sh->pwr; for (i = 0; i < n; ++i) { j1 = orbits[i]; while (orbits[j1] != j1) j1 = orbits[j1]; j2 = orbits[workperm[i]]; while (orbits[j2] != j2) j2 = orbits[j2]; if (j1 != j2) { lchanged = TRUE; if (j1 < j2) orbits[j2] = j1; else orbits[j1] = j2; } } if (lchanged) for (i = 0; i < n; ++i) orbits[i] = orbits[orbits[i]]; if (lchanged) changed = TRUE; if (sh->fixed >= 0) { for (i = 0; i < n; ++i) if (vec[i] && !vec[workperm[i]]) { changed = TRUE; ipwr = 0; for (j = workperm[i]; !vec[j] ; j = workperm[j]) ++ipwr; for (j = workperm[i]; !vec[j] ; j = workperm[j]) { if (!curr) { if (!ingroup) addpermutation(ring,workperm,n); else addpermutationunmarked(ring,workperm,n); ingroup = TRUE; curr = *ring; } vec[j] = curr; pwr[j] = ipwr--; ++curr->refcount; } } j = workperm[sh->fixed]; while (j != sh->fixed) { applyperm(workperm,vec[j]->p,pwr[j],n); ++multcount; curr = NULL; j = workperm[sh->fixed]; } sh = sh->next; } else break; } if (!ident && !ingroup) { changed = TRUE; addpermutation(ring,p,n); } return changed; } /************************************************************************/ boolean expandschreier(schreier *gp, permnode **ring, int n) /* filter random elements until schreierfails failures. * Return true if it ever expanded. */ { int i,j,nfails,wordlen,skips; boolean changed; permnode *pn; #if !MAXN DYNALLOC1(int,workperm2,workperm2_sz,n,"expandschreier"); #endif pn = *ring; if (pn == NULL) return FALSE; nfails = 0; changed = FALSE; for (skips = KRAN(17); --skips >= 0; ) pn = pn->next; memcpy(workperm2,pn->p,n*sizeof(int)); while (nfails < schreierfails) { wordlen = 1 + KRAN(3); for (j = 0; j < wordlen; ++j) { for (skips = KRAN(17); --skips >= 0; ) pn = pn->next; for (i = 0; i < n; ++i) workperm2[i] = pn->p[workperm2[i]]; } if (filterschreier(gp,workperm2,ring,TRUE,-1,n)) { changed = TRUE; nfails = 0; } else ++nfails; } return changed; } /************************************************************************/ int* getorbits(int *fix, int nfix, schreier *gp, permnode **ring, int n) /* Get a pointer to the orbits for this partial base. The pointer * remains valid until pruneset(), getorbits(), getorbitsmin() * or grouporder() is called with an incompatible base (neither a * prefix nor an extension). The contents of the array pointed to * MUST NOT BE MODIFIED by the calling program. */ { int k; schreier *sh,*sha; sh = gp; for (k = 0; k < nfix; ++k) { if (sh->fixed != fix[k]) break; sh = sh->next; } if (k == nfix) return sh->orbits; sh->fixed = fix[k]; clearvector(sh->vec,ring,n); sh->vec[fix[k]] = ID_PERMNODE; for (sha = sh->next; sha ; sha = sha->next) clearvector(sha->vec,ring,n); for (++k; k <= nfix; ++k) { if (!sh->next) sh->next = newschreier(n); sh = sh->next; initschreier(sh,n); if (k < nfix) { sh->fixed = fix[k]; sh->vec[fix[k]] = ID_PERMNODE; } else sh->fixed = -1; } if (*ring) expandschreier(gp,ring,n); return sh->orbits; } /************************************************************************/ int getorbitsmin(int *fix, int nfix, schreier *gp, permnode **ring, int **orbits, int *cell, int ncell, int n, boolean changed) /* If the basis elements fix[0..nfix-1] are minimal in their orbits, * as far as we know, return value nfix and set *orbits to point * to orbits fixing fix[0..nfix-1]. If fix[i] is seen to be not * minimal for some i <= nfix-1, return i and set *orbits to point * to orbits fixing fix[0..i-1]. If the partial base is already * known, or fix[0..nfix-1] can already be seen to be non-minimal, * do this work without more filtering. This shortcut is turned * off if changed==TRUE. Otherwise, filter until schreierfails * failures. * The pointer returned remains valid until pruneset(), getorbits(), * getorbitsmin() or grouporder() is called with an incompatible base * (neither a prefix nor an extension). The contents of the array * pointed to MUST NOT BE MODIFIED by the calling program. * If cell != NULL, return early if possible when cell[0..ncell-1] * are all in the same orbit fixing fix[0..nfix-1]. Otherwise * cell,ncell play no part in the computation. */ { schreier *sh,*sha; int *fixorbs; int i,j,k,icell,nfails,wordlen,skips; permnode *pn; #if !MAXN DYNALLOC1(int,workperm2,workperm2_sz,n,"expandschreier"); #endif sh = gp; k = 0; if (!changed) for (k = 0; k < nfix; ++k) { if (sh->orbits[fix[k]] != fix[k]) { *orbits = sh->orbits; return k; } if (sh->fixed != fix[k]) break; sh = sh->next; } if (k == nfix) { *orbits = sh->orbits; return nfix; } sh->fixed = fix[k]; clearvector(sh->vec,ring,n); sh->vec[fix[k]] = ID_PERMNODE; for (sha = sh->next; sha ; sha = sha->next) clearvector(sha->vec,ring,n); for (++k; k <= nfix; ++k) { if (!sh->next) sh->next = newschreier(n); sh = sh->next; initschreier(sh,n); if (k < nfix) { sh->fixed = fix[k]; sh->vec[fix[k]] = ID_PERMNODE; } else sh->fixed = -1; } *orbits = fixorbs = sh->orbits; if (cell) { for (icell = 1; icell < ncell; ++icell) if (fixorbs[cell[icell]] != fixorbs[cell[0]]) break; if (icell >= ncell) return nfix; } if (*ring) { pn = *ring; nfails = 0; for (skips = KRAN(17); --skips >= 0; ) pn = pn->next; memcpy(workperm2,pn->p,n*sizeof(int)); while (nfails < schreierfails) { wordlen = 1 + KRAN(3); for (j = 0; j < wordlen; ++j) { for (skips = KRAN(17); --skips >= 0; ) pn = pn->next; for (i = 0; i < n; ++i) workperm2[i] = pn->p[workperm2[i]]; } if (filterschreier(gp,workperm2,ring,TRUE,-1,n)) { nfails = 0; sh = gp; for (k = 0; k < nfix; ++k) { if (sh->orbits[fix[k]] != fix[k]) { *orbits = sh->orbits; return k; } sh = sh->next; } if (cell) { for ( ; icell < ncell; ++icell) if (fixorbs[cell[icell]] != fixorbs[cell[0]]) break; if (icell >= ncell) return nfix; } } else ++nfails; } } return nfix; } /************************************************************************/ void pruneset(set *fixset, schreier *gp, permnode **ring, set *x, int m, int n) /* Remove from x any point not minimal for the orbits for this base. * If the base is already known, just provide the orbits without * more filtering. Otherwise, filter until schreierfails failures. */ { int i,k; schreier *sh,*sha; int *orbits; #if !MAXN DYNALLOC1(set,workset,workset_sz,m,"pruneset"); #endif for (i = 0; i < m; ++i) workset[i] = fixset[i]; sh = gp; while (sh->fixed >= 0 && ISELEMENT(workset,sh->fixed)) { DELELEMENT(workset,sh->fixed); sh = sh->next; } k = nextelement(workset,m,-1); if (k < 0) orbits = sh->orbits; else { sh->fixed = k; clearvector(sh->vec,ring,n); sh->vec[k] = ID_PERMNODE; for (sha = sh->next; sha ; sha = sha->next) clearvector(sha->vec,ring,n); while ((k = nextelement(workset,m,k)) >= 0) { if (!sh->next) sh->next = newschreier(n); sh = sh->next; initschreier(sh,n); sh->fixed = k; sh->vec[k] = ID_PERMNODE; } if (!sh->next) sh->next = newschreier(n); sh = sh->next; initschreier(sh,n); sh->fixed = -1; if (*ring) expandschreier(gp,ring,n); orbits = sh->orbits; } for (k = -1; (k = nextelement(x,m,k)) >= 0; ) if (orbits[k] != k) DELELEMENT(x,k); } /************************************************************************/ int schreier_gens(permnode *ring) /* Returns the number of generators in the ring */ { int j; permnode *pn; if (!ring) j = 0; else for (j = 1, pn = ring->next; pn != ring; pn = pn->next) ++j; return j; } /************************************************************************/ void dumpschreier(FILE *f, schreier *gp, permnode *ring, int n) /* Dump the whole schreier structure to file f. */ { schreier *sh; permnode *pn; int i,j,jj,k; fprintf(f,"Schreier structure n=%d; ",n); jj = -1; for (j = 0, sh = gp; sh; sh = sh->next) { ++j; if (sh->fixed < 0 && jj < 0) jj = j; } fprintf(f," levels=%d (%d used); ",j,jj); if (!ring) j = 0; else for (j = 1, pn = ring->next; pn != ring; pn = pn->next) ++j; fprintf(f,"gens=%d; ",j); for (j = 0, sh = schreier_freelist; sh; sh = sh->next) ++j; for (k = 0, pn = permnode_freelist; pn; pn = pn->next) ++k; fprintf(f,"freelists: %d,%d\n",j,k); if (ring) { fprintf(f,"Generators:\n"); pn = ring; do { fprintf(f," %03x ref=%lu mk=%d alloc=%d p=",PNCODE(pn), pn->refcount,pn->mark,pn->nalloc); for (i = 0; i < n; ++i) fprintf(f," %d",pn->p[i]); fprintf(f,"\n"); pn = pn->next; } while (pn != ring); } if (gp) { fprintf(f,"Levels:\n"); for (sh = gp; sh; sh = sh->next) { fprintf(f,"fixed=%2d alloc=%d vec=",sh->fixed,sh->nalloc); for (i = 0; i < n; ++i) { if (sh->vec[i] == ID_PERMNODE) fprintf(f," %d=e",i); else if (sh->vec[i]) { k = sh->pwr[i]; j = (sh->vec[i])->p[i]; fprintf(f," %03x",PNCODE(sh->vec[i])); if (k == 1) fprintf(f,"(%d,%d)",i,j); else { fprintf(f,"^%d",k); while (--k >= 1) j = (sh->vec[i])->p[j]; fprintf(f,"(%d,%d)",i,j); } } } fprintf(f,"\n Orb="); j = 0; for (i = 0; i < n; ++i) { fprintf(f," %d",sh->orbits[i]); if (sh->orbits[i] == i) ++j; } fprintf(f," [%d]\n",j); if (sh->fixed < 0) break; } } } /************************************************************************/ void grouporder(int *fix, int nfix, schreier *gp, permnode **ring, double *grpsize1, int *grpsize2, int n) /* process the base like in getorbits(), then return the product of the * orbits along the base, using the largest orbit at the end if the * base is not complete. */ { schreier *sh; int i,j,k,fx; int *orb; #if !MAXN DYNALLOC1(int,workperm,workperm_sz,n,"grouporder"); #endif getorbits(fix,nfix,gp,ring,n); expandschreier(gp,ring,n); expandschreier(gp,ring,n); *grpsize1 = 1.0; *grpsize2 = 0; for (i = 0, sh = gp; i < nfix; ++i, sh = sh->next) { orb = sh->orbits; fx = orb[sh->fixed]; k = 0; for (j = fx; j < n; ++j) if (orb[j] == fx) ++k; MULTIPLY(*grpsize1,*grpsize2,k); } orb = sh->orbits; k = 1; for (i = 0; i < n; ++i) if (orb[i] == i) workperm[i] = 1; else { ++workperm[orb[i]]; if (workperm[orb[i]] > k) k = workperm[orb[i]]; } MULTIPLY(*grpsize1,*grpsize2,k); } /***************************************************************************** * * * schreier_check() checks that this file is compiled compatibly with the * * given parameters. If not, call exit(1). * * * *****************************************************************************/ void schreier_check(int wordsize, int m, int n, int version) { if (wordsize != WORDSIZE) { fprintf(ERRFILE,"Error: WORDSIZE mismatch in schreier.c\n"); exit(1); } #if MAXN if (m > MAXM) { fprintf(ERRFILE,"Error: MAXM inadequate in schreier.c\n"); exit(1); } if (n > MAXN) { fprintf(ERRFILE,"Error: MAXN inadequate in schreier.c\n"); exit(1); } #endif if (version < NAUTYREQUIRED) { fprintf(ERRFILE,"Error: schreier.c version mismatch\n"); exit(1); } } /************************************************************************/ void schreier_freedyn(void) { #if !MAXN DYNFREE(workperm,workperm_sz); DYNFREE(workperm2,workperm2_sz); DYNFREE(workpermA,workpermA_sz); DYNFREE(workpermB,workpermB_sz); DYNFREE(workset,workset_sz); DYNFREE(workset2,workset2_sz); #endif clearfreelists(); }