/*****************************************************************************
* *
* Auxiliary source file for version 2 . 8 of nauty . *
* *
* Copyright ( 1984 - 2020 ) Brendan McKay . All rights reserved . *
* Subject to waivers and disclaimers in nauty . h . *
* *
* CHANGE HISTORY *
* 10 - Nov - 87 : final changes for version 1 . 2 *
* 5 - Dec - 87 : renamed to version 1 . 3 ( no changes to this file ) *
* 28 - Sep - 88 : renamed to version 1 . 4 ( no changes to this file ) *
* 23 - Mar - 89 : changes for version 1 . 5 : *
* - added procedure refine1 ( ) *
* - changed type of ptn from int * to nvector * in fmptn ( ) *
* - declared level in breakout ( ) *
* - changed char [ ] to char * in a few places *
* - minor rearrangement in bestcell ( ) *
* 31 - Mar - 89 : - added procedure doref ( ) *
* 5 - Apr - 89 : - changed MAKEEMPTY uses to EMPTYSET *
* 12 - Apr - 89 : - changed writeperm ( ) and fmperm ( ) to not use MARKing *
* 5 - May - 89 : - redefined MASH to gain about 8 % efficiency *
* 18 - Oct - 90 : changes for version 1 . 6 : *
* - improved line breaking in writeperm ( ) *
* 10 - Nov - 90 : - added dummy routine nautil_null ( ) *
* 27 - Aug - 92 : changes for version 1 . 7 : *
* - made linelength < = 0 mean no line breaks *
* 5 - Jun - 93 : renamed to version 1 . 7 + ( no changes to this file ) *
* 18 - Aug - 93 : renamed to version 1 . 8 ( no changes to this file ) *
* 17 - Sep - 93 : renamed to version 1 . 9 ( no changes to this file ) *
* 29 - Jun - 95 : changes for version 1 . 10 : *
* - replaced loop in nextelement ( ) to save reference past *
* end of array ( thanks to Kevin Maylsiak ) *
* 11 - Jul - 96 : changes for version 2 . 0 : *
* - added alloc_error ( ) *
* - added dynamic allocation *
* 21 - Oct - 98 : use 077777 in place of INFINITY for CLEANUP ( ) *
* 9 - Jan - 00 : added nautil_check ( ) *
* 12 - Feb - 00 : did a little formating of the code *
* 28 - May - 00 : added nautil_freedyn ( ) *
* 16 - Aug - 00 : added OLDNAUTY behaviour *
* 16 - Nov - 00 : moved graph - specific things to naugraph . c *
* use function prototypes , remove UPROC , nvector *
* 22 - Apr - 01 : added code for compilation into Magma *
* removed nautil_null ( ) *
* removed EXTDEFS and included labelorg *
* 21 - Nov - 01 : use NAUTYREQUIRED in nautil_check ( ) *
* 26 - Jun - 02 : revised permset ( ) to avoid fetch past the end of *
* the array ( thanks to Jan Kieffer ) *
* 17 - Nov - 03 : changed INFINITY to NAUTY_INFINITY *
* 14 - Sep - 04 : extended prototypes to recursive functions *
* 23 - Nov - 06 : replave targetcell ( ) by maketargetcell ( ) *
* 10 - Dec - 06 : remove BIGNAUTY *
* 10 - Dec - 10 : remove shortish and permutation types *
* 11 - May - 10 : use sorttemplates . c *
* 27 - Mar - 11 : add writegroupsize ( ) *
* 15 - Jan - 12 : add TLS_ATTR attributes *
* 16 - Sep - 12 : small change to objoin ( ) , more efficient for sparse case *
* 22 - Sep - 12 : change documentation of orbjoin ( ) *
* 18 - Jan - 12 : changes for version 2 . 6 : *
* - declare nauty_kill_request *
* 8 - May - 20 : add const declarations to prototypes *
* *
*****************************************************************************/
#define ONE_WORD_SETS
#include "nauty.h"
#ifdef NAUTY_IN_MAGMA
#include "io.e"
#endif
/* macros for hash-codes: */
/* Don't use NAUTY_INFINITY here as that would make the canonical
* labelling depend on whether BIGNAUTY is in operation */
#define MASH(l,i) ((((l) ^ 065435 ) + (i)) & 077777 )
/* : expression whose long value depends only on long l and int/long i.
Anything goes, preferably non-commutative. */
#define CLEANUP(l) ((int )((l) % 077777 ))
/* : expression whose value depends on long l and is less than 077777
when converted to int then short. Anything goes. */
#if MAXM==1
#define M 1
#else
#define M m
#endif
#if !MAXN
DYNALLSTAT(int ,workperm,workperm_sz);
#else
static TLS_ATTR int workperm[MAXN];
#endif
int labelorg = 0 ; /* no TLS_ATTR on purpose */
volatile int nauty_kill_request = 0 ; /* no TLS_ATTR on purpose */
/* aproto: header new_nauty_protos.h */
/*****************************************************************************
* *
* nextelement ( set1 , m , pos ) = the position of the first element in set set1 *
* which occupies a position greater than pos . If no such element exists , *
* the value is - 1 . pos can have any value less than n , including negative *
* values . *
* *
* GLOBALS ACCESSED : none *
* *
*****************************************************************************/
int
nextelement(const set *set1, int m, int pos)
{
setword setwd;
int w;
if (m == 1 )
{
if (pos < 0 ) setwd = set1[0 ];
else setwd = set1[0 ] & BITMASK(pos);
if (setwd == 0 ) return -1 ;
else return FIRSTBITNZ(setwd);
}
if (pos < 0 )
{
w = 0 ;
setwd = set1[0 ];
}
else
{
w = SETWD(pos);
setwd = set1[w] & BITMASK(SETBT(pos));
}
for (;;)
{
if (setwd != 0 ) return TIMESWORDSIZE(w) + FIRSTBITNZ(setwd);
if (++w == m) return -1 ;
setwd = set1[w];
}
}
/*****************************************************************************
* *
* permset ( set1 , set2 , m , perm ) defines set2 to be the set *
* { perm [ i ] | i in set1 } . *
* *
* GLOBALS ACCESSED : bit < r > , leftbit < r > *
* *
*****************************************************************************/
void
permset(const set *set1, set *set2, int m, int *perm)
{
setword setw;
int pos,b;
int w;
if (m == 1 )
{
*set2 = 0 ;
setw = set1[0 ];
while (setw != 0 )
{
TAKEBIT(b,setw);
*set2 |= bit[perm[b]];
}
}
else
{
EMPTYSET0(set2,m);
for (w = 0 ; w < m; ++w)
{
setw = set1[w];
while (setw != 0 )
{
TAKEBIT(b,setw);
pos = perm[TIMESWORDSIZE(w)+b];
ADDELEMENT0(set2,pos);
}
}
}
}
/*****************************************************************************
* *
* putstring ( f , s ) writes the nul - terminated string s to file f . *
* *
*****************************************************************************/
void
putstring(FILE *f, const char *s)
{
while (*s != '\0' )
{
PUTC(*s,f);
++s;
}
}
/*****************************************************************************
* *
* itos ( i , s ) converts the int i to a nul - terminated decimal character *
* string s . The value returned is the number of characters excluding *
* the nul . *
* *
* GLOBALS ACCESSED : NONE *
* *
*****************************************************************************/
int
itos(int i, char *s)
{
int digit,j,k;
char c;
int ans;
if (i < 0 )
{
k = 0 ;
i = -i;
j = 1 ;
s[0 ] = '-' ;
}
else
{
k = -1 ;
j = 0 ;
}
do
{
digit = i % 10 ;
i = i / 10 ;
s[++k] = (char )(digit + '0' );
}
while (i);
s[k+1 ] = '\0' ;
ans = k + 1 ;
for (; j < k; ++j, --k)
{
c = s[j];
s[j] = s[k];
s[k] = c;
}
return ans;
}
/*****************************************************************************
* *
* orbits represents a partition of { 0 , 1 , . . . , n - 1 } , by orbits [ i ] = the *
* smallest element in the same cell as i . map [ ] is any array with values *
* in { 0 , 1 , . . . , n - 1 } . orbjoin ( orbits , map , n ) joins the cells of orbits [ ] *
* together to the minimum extent such that for each i , i and map [ i ] are in *
* the same cell . The function value returned is the new number of cells . *
* *
* GLOBALS ACCESSED : NONE *
* *
*****************************************************************************/
int
orbjoin(int *orbits, const int *map, int n)
{
int i,j1,j2;
for (i = 0 ; i < n; ++i)
if (map[i] != i)
{
j1 = orbits[i];
while (orbits[j1] != j1) j1 = orbits[j1];
j2 = orbits[map[i]];
while (orbits[j2] != j2) j2 = orbits[j2];
if (j1 < j2) orbits[j2] = j1;
else if (j1 > j2) orbits[j1] = j2;
}
j1 = 0 ;
for (i = 0 ; i < n; ++i)
if ((orbits[i] = orbits[orbits[i]]) == i) ++j1;
return j1;
}
/*****************************************************************************
* *
* writeperm ( f , perm , cartesian , linelength , n ) writes the permutation perm to *
* the file f . The cartesian representation ( i . e . perm itself ) is used if *
* cartesian ! = FALSE ; otherwise the cyclic representation is used . No *
* more than linelength characters ( not counting ' \ n ' ) are written on each *
* line , unless linelength is ridiculously small . linelength < = 0 causes no *
* line breaks at all to be made . The global int labelorg is added to each *
* vertex number . *
* *
* GLOBALS ACCESSED : itos ( ) , putstring ( ) *
* *
*****************************************************************************/
void
writeperm(FILE *f, const int *perm, boolean cartesian, int linelength, int n)
{
int i,k,l,curlen,intlen;
char s[30 ];
#if !MAXN
DYNALLOC1(int ,workperm,workperm_sz,n,"writeperm" );
#endif
/* CONDNL(x) writes end-of-line and 3 spaces if x characters
won't fit on the current line. */
#define CONDNL(x) if (linelength>0 && curlen+(x)>linelength)\
{putstring(f,"\n " );curlen=3 ;}
curlen = 0 ;
if (cartesian)
{
for (i = 0 ; i < n; ++i)
{
intlen = itos(perm[i]+labelorg,s);
CONDNL(intlen+1 );
PUTC(' ' ,f);
putstring(f,s);
curlen += intlen + 1 ;
}
PUTC('\n' ,f);
}
else
{
for (i = n; --i >= 0 ;) workperm[i] = 0 ;
for (i = 0 ; i < n; ++i)
{
if (workperm[i] == 0 && perm[i] != i)
{
l = i;
intlen = itos(l+labelorg,s);
if (curlen > 3 ) CONDNL(2 *intlen+4 );
PUTC('(' ,f);
do
{
putstring(f,s);
curlen += intlen + 1 ;
k = l;
l = perm[l];
workperm[k] = 1 ;
if (l != i)
{
intlen = itos(l+labelorg,s);
CONDNL(intlen+2 );
PUTC(' ' ,f);
}
}
while (l != i);
PUTC(')' ,f);
++curlen;
}
}
if (curlen == 0 ) putstring(f,"(1)\n" );
else PUTC('\n' ,f);
}
}
/*****************************************************************************
* *
* fmperm ( perm , fix , mcr , m , n ) uses perm to construct fix and mcr . fix *
* contains those points are fixed by perm , while mcr contains the set of *
* those points which are least in their orbits . *
* *
* GLOBALS ACCESSED : bit < r > *
* *
*****************************************************************************/
void
fmperm(const int *perm, set *fix, set *mcr, int m, int n)
{
int i,k,l;
#if !MAXN
DYNALLOC1(int ,workperm,workperm_sz,n,"writeperm" );
#endif
EMPTYSET(fix,m);
EMPTYSET(mcr,m);
for (i = n; --i >= 0 ;) workperm[i] = 0 ;
for (i = 0 ; i < n; ++i)
if (perm[i] == i)
{
ADDELEMENT(fix,i);
ADDELEMENT(mcr,i);
}
else if (workperm[i] == 0 )
{
l = i;
do
{
k = l;
l = perm[l];
workperm[k] = 1 ;
}
while (l != i);
ADDELEMENT(mcr,i);
}
}
/*****************************************************************************
* *
* fmptn ( lab , ptn , level , fix , mcr , m , n ) uses the partition at the specified *
* level in the partition nest ( lab , ptn ) to make sets fix and mcr . fix *
* represents the points in trivial cells of the partition , while mcr *
* represents those points which are least in their cells . *
* *
* GLOBALS ACCESSED : bit < r > *
* *
*****************************************************************************/
void
fmptn(const int *lab, const int *ptn, int level, set *fix, set *mcr, int m, int n)
{
int i,lmin;
EMPTYSET(fix,m);
EMPTYSET(mcr,m);
for (i = 0 ; i < n; ++i)
if (ptn[i] <= level)
{
ADDELEMENT(fix,lab[i]);
ADDELEMENT(mcr,lab[i]);
}
else
{
lmin = lab[i];
do
if (lab[++i] < lmin) lmin = lab[i];
while (ptn[i] > level);
ADDELEMENT(mcr,lmin);
}
}
#define SORT_TYPE1 int
#define SORT_TYPE2 int
#define SORT_OF_SORT 2
#define SORT_NAME sortparallel
#include "sorttemplates.c"
/*****************************************************************************
* *
* doref ( g , lab , ptn , level , numcells , qinvar , invar , active , code , refproc , *
* invarproc , mininvarlev , maxinvarlev , invararg , digraph , m , n ) *
* is used to perform a refinement on the partition at the given level in *
* ( lab , ptn ) . The number of cells is * numcells both for input and output . *
* The input active is the active set for input to the refinement procedure *
* ( * refproc ) ( ) , which must have the argument list of refine ( ) . *
* active may be arbitrarily changed . invar is used for working storage . *
* First , ( * refproc ) ( ) is called . Then , if invarproc ! = NULL and *
* | mininvarlev | < = level < = | maxinvarlev | , the routine ( * invarproc ) ( ) is *
* used to compute a vertex - invariant which may refine the partition *
* further . If it does , ( * refproc ) ( ) is called again , using an active set *
* containing all but the first fragment of each old cell . Unless g is a *
* digraph , this guarantees that the final partition is equitable . The *
* arguments invararg and digraph are passed to ( * invarproc ) ( ) *
* uninterpretted . The output argument code is a composite of the codes *
* from all the calls to ( * refproc ) ( ) . The output argument qinvar is set *
* to 0 if ( * invarproc ) ( ) is not applied , 1 if it is applied but fails to *
* refine the partition , and 2 if it succeeds . *
* See the file nautinv . c for a further discussion of vertex - invariants . *
* Note that the dreadnaut I command generates a call to this procedure *
* with level = mininvarlevel = maxinvarlevel = 0 . *
* *
*****************************************************************************/
void
doref(graph *g, int *lab, int *ptn, int level, int *numcells,
int *qinvar, int *invar, set *active, int *code,
void (*refproc)(graph*,int *,int *,int ,int *,int *,set*,int *,int ,int ),
void (*invarproc)(graph*,int *,int *,int ,int ,int ,int *,
int ,boolean,int ,int ),
int mininvarlev, int maxinvarlev, int invararg,
boolean digraph, int m, int n)
{
int pw;
int i,cell1,cell2,nc,tvpos,minlev,maxlev;
long longcode;
boolean same;
#if !MAXN
DYNALLOC1(int ,workperm,workperm_sz,n,"doref" );
#endif
if ((tvpos = nextelement(active,M,-1 )) < 0 ) tvpos = 0 ;
(*refproc)(g,lab,ptn,level,numcells,invar,active,code,M,n);
minlev = (mininvarlev < 0 ? -mininvarlev : mininvarlev);
maxlev = (maxinvarlev < 0 ? -maxinvarlev : maxinvarlev);
if (invarproc != NULL && *numcells < n
&& level >= minlev && level <= maxlev)
{
(*invarproc)(g,lab,ptn,level,*numcells,tvpos,invar,invararg,
digraph,M,n);
EMPTYSET(active,m);
for (i = n; --i >= 0 ;) workperm[i] = invar[lab[i]];
nc = *numcells;
for (cell1 = 0 ; cell1 < n; cell1 = cell2 + 1 )
{
pw = workperm[cell1];
same = TRUE ;
for (cell2 = cell1; ptn[cell2] > level; ++cell2)
if (workperm[cell2+1 ] != pw) same = FALSE ;
if (same) continue ;
sortparallel(workperm+cell1, lab+cell1, cell2-cell1+1 );
for (i = cell1 + 1 ; i <= cell2; ++i)
if (workperm[i] != workperm[i-1 ])
{
ptn[i-1 ] = level;
++*numcells;
ADDELEMENT(active,i);
}
}
if (*numcells > nc)
{
*qinvar = 2 ;
longcode = *code;
(*refproc)(g,lab,ptn,level,numcells,invar,active,code,M,n);
longcode = MASH(longcode,*code);
*code = CLEANUP(longcode);
}
else
*qinvar = 1 ;
}
else
*qinvar = 0 ;
}
/*****************************************************************************
* *
* maketargetcell ( g , lab , ptn , level , tcell , tcellsize , & cellpos , *
* tc_level , digraph , hint , targetcell , m , n ) *
* calls targetcell ( ) to determine the target cell at the specified level *
* in the partition nest ( lab , ptn ) . It must be a nontrivial cell ( if not , *
* the first cell . The intention of hint is that , if hint > = 0 and there *
* is a suitable non - trivial cell starting at position hint in lab , *
* that cell is chosen . *
* tc_level and digraph are input options . *
* When a cell is chosen , tcell is set to its contents , * tcellsize to its *
* size , and cellpos to its starting position in lab . *
* *
* GLOBALS ACCESSED : bit < r > *
* *
*****************************************************************************/
void
maketargetcell(graph *g, int *lab, int *ptn, int level,
set *tcell, int *tcellsize, int *cellpos, int tc_level,
boolean digraph, int hint,
int (*targetcell)(graph*,int *,int *,int ,int ,boolean,int ,int ,int ),
int m, int n)
{
int i,j,k;
i = (*targetcell)(g,lab,ptn,level,tc_level,digraph,hint,m,n);
for (j = i + 1 ; ptn[j] > level; ++j) {}
*tcellsize = j - i + 1 ;
EMPTYSET(tcell,m);
for (k = i; k <= j; ++k) ADDELEMENT(tcell,lab[k]);
*cellpos = i;
}
/*****************************************************************************
* *
* shortprune ( set1 , set2 , m ) ANDs the contents of set set2 into set set1 . *
* *
* GLOBALS ACCESSED : NONE *
* *
*****************************************************************************/
void
shortprune(set *set1, const set *set2, int m)
{
int i;
for (i = 0 ; i < M; ++i) INTERSECT(set1[i],set2[i]);
}
/*****************************************************************************
* *
* breakout ( lab , ptn , level , tc , tv , active , m ) operates on the partition at *
* the specified level in the partition nest ( lab , ptn ) . It finds the *
* element tv , which is in the cell C starting at index tc in lab ( it had *
* better be ) and splits C in the two cells { tv } and C \ { tv } , in that order . *
* It also sets the set active to contain just the element tc . *
* *
* GLOBALS ACCESSED : bit < r > *
* *
*****************************************************************************/
void
breakout(int *lab, int *ptn, int level, int tc, int tv,
set *active, int m)
{
int i,prev,next;
EMPTYSET(active,m);
ADDELEMENT(active,tc);
i = tc;
prev = tv;
do
{
next = lab[i];
lab[i++] = prev;
prev = next;
}
while (prev != tv);
ptn[tc] = level;
}
/*****************************************************************************
* *
* longprune ( tcell , fix , bottom , top , m ) removes zero or elements of the set *
* tcell . It is assumed that addresses bottom through top - 1 contain *
* contiguous pairs of sets ( f1 , m1 ) , ( f2 , m2 ) , . . . . tcell is intersected *
* with each mi such that fi is a subset of fix . *
* *
* GLOBALS ACCESSED : NONE *
* *
*****************************************************************************/
void
longprune(set *tcell, set *fix, set *bottom, set *top, int m)
{
int i;
while (bottom < top)
{
for (i = 0 ; i < M; ++i)
if (NOTSUBSET(fix[i],bottom[i])) break ;
bottom += M;
if (i == M)
for (i = 0 ; i < M; ++i) INTERSECT(tcell[i],bottom[i]);
bottom += M;
}
}
/*****************************************************************************
* *
* writegroupsize ( f , gpsize1 , gpsize2 ) writes a real number gpsize1 * 10 ^ gpsize2 *
* It is assumed that gpsize1 > = 1 and that gpsize1 equals an integer in the *
* case that gpsize2 = = 0 . These assumptions are true for group sizes *
* computed by nauty . *
* *
*****************************************************************************/
void
writegroupsize(FILE *f, double gpsize1, int gpsize2)
{
if (gpsize2 == 0 )
fprintf(f,"%.0f" ,gpsize1+0 .1 );
else
{
while (gpsize1 >= 10 .0 )
{
gpsize1 /= 10 .0 ;
++gpsize2;
}
fprintf(f,"%14.12fe%d" ,gpsize1,gpsize2);
}
}
/*****************************************************************************
* *
* nautil_check ( ) checks that this file is compiled compatibly with the *
* given parameters . If not , call exit ( 1 ) . *
* *
*****************************************************************************/
void
nautil_check(int wordsize, int m, int n, int version)
{
if (wordsize != WORDSIZE)
{
fprintf(ERRFILE,"Error: WORDSIZE mismatch in nautil.c\n" );
exit (1 );
}
#if MAXN
if (m > MAXM)
{
fprintf(ERRFILE,"Error: MAXM inadequate in nautil.c\n" );
exit (1 );
}
if (n > MAXN)
{
fprintf(ERRFILE,"Error: MAXN inadequate in nautil.c\n" );
exit (1 );
}
#endif
if (version < NAUTYREQUIRED)
{
fprintf(ERRFILE,"Error: nautil.c version mismatch\n" );
exit (1 );
}
}
/*****************************************************************************
* *
* alloc_error ( ) writes a message and exits . Used by DYNALLOC ? macros . *
* *
*****************************************************************************/
void
alloc_error(const char *s)
{
fprintf(ERRFILE,"Dynamic allocation failed: %s\n" ,s);
exit (2 );
}
/*****************************************************************************
* *
* nautil_freedyn ( ) - free the dynamic memory in this module *
* *
*****************************************************************************/
void
nautil_freedyn(void )
{
#if !MAXN
DYNFREE(workperm,workperm_sz);
#endif
}
Messung V0.5 in Prozent C=97 H=70 G=84
¤ Dauer der Verarbeitung: 0.25 Sekunden
(vorverarbeitet am 2026-06-29)
¤
*© Formatika GbR, Deutschland