/* schreier.c - procedures for manipulating a permutation group using *therandomschreieralgorithm.Thereisaseparatefileschreier.txt *whichdescribestheusage. * *Writtenfornautyandtraces,BrendanMcKay2010-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;
staticvoid
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; *Avalueof<=0defaultstoSCHREIERFAILS.
* The function value is the previous setting. */
{
int prev;
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);
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;
staticvoid
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 *alreadyinthegroup.Itisispossibletobeinthegroup *andyetthisfactisnotdiscovered. *ReturnTRUEifthegenerator(oranequivalent)isaddedorthe
* 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 *alreadyinthegroup.Itisispossibletobeinthegroup *andyetthisfactisnotdiscovered,butthisversionwill *alwaysnoticeifthispermutationpreciselyispresent. *ReturnTRUEifthegenerator(oranequivalent)isaddedorthe
* 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);
}
staticvoid
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;
}
}
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;
}
staticvoid
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. *Useingroup=TRUEifpisknowntobeinthegroup,otherwise *atleastoneequivalentgeneratorisaddedunlessitisproved *(nondeterministically)thatitisinthegroupalready. *maxlevel<0meansnolimit,maxlevel=0meanstoplevelonly,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;
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
int*
getorbits(int *fix, int nfix, schreier *gp, permnode **ring, int n) /* Get a pointer to the orbits for this partial base. The pointer *remainsvaliduntilpruneset(),getorbits(),getorbitsmin() *orgrouporder()iscalledwithanincompatiblebase(neithera *prefixnoranextension).Thecontentsofthearraypointedto *MUSTNOTBEMODIFIEDbythecallingprogram.
*/
{
int k;
schreier *sh,*sha;
sh = gp;
for (k = 0; k < nfix; ++k)
{
if (sh->fixed != fix[k]) break;
sh = sh->next;
}
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, *asfarasweknow,returnvaluenfixandset*orbitstopoint *toorbitsfixingfix[0..nfix-1].Iffix[i]isseentobenot *minimalforsomei<=nfix-1,returniandset*orbitstopoint *toorbitsfixingfix[0..i-1].Ifthepartialbaseisalready *known,orfix[0..nfix-1]canalreadybeseentobenon-minimal, *dothisworkwithoutmorefiltering.Thisshortcutisturned *offifchanged==TRUE.Otherwise,filteruntilschreierfails *failures. *Thepointerreturnedremainsvaliduntilpruneset(),getorbits(), *getorbitsmin()orgrouporder()iscalledwithanincompatiblebase *(neitheraprefixnoranextension).Thecontentsofthearray *pointedtoMUSTNOTBEMODIFIEDbythecallingprogram. *Ifcell!=NULL,returnearlyifpossiblewhencell[0..ncell-1] *areallinthesameorbitfixingfix[0..nfix-1].Otherwise *cell,ncellplaynopartinthecomputation.
*/
{
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;
}
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. *Ifthebaseisalreadyknown,justprovidetheorbitswithout *morefiltering.Otherwise,filteruntilschreierfailsfailures.
*/
{
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);
}
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 *orbitsalongthebase,usingthelargestorbitattheendifthe *baseisnotcomplete.
*/
{
schreier *sh;
int i,j,k,fx;
int *orb;
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);
}
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);
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.