1 /*****************************************************************************
2 *                                                                            *
3 *  Auxiliary source file for version 2.7 of nauty.                           *
4 *                                                                            *
5 *   Copyright (1984-2013) Brendan McKay.  All rights reserved.               *
6 *   Subject to waivers and disclaimers in nauty.h.                           *
7 *                                                                            *
8 *   CHANGE HISTORY                                                           *
9 *       10-Nov-87 : final changes for version 1.2                            *
10 *        5-Dec-87 : renamed to version 1.3 (no changes to this file)         *
11 *       28-Sep-88 : renamed to version 1.4 (no changes to this file)         *
12 *       23-Mar-89 : changes for version 1.5 :                                *
13 *                   - added procedure refine1()                              *
14 *                   - changed type of ptn from int* to nvector* in fmptn()   *
15 *                   - declared level in breakout()                           *
16 *                   - changed char[] to char* in a few places                *
17 *                   - minor rearrangement in bestcell()                      *
18 *       31-Mar-89 : - added procedure doref()                                *
19 *        5-Apr-89 : - changed MAKEEMPTY uses to EMPTYSET                     *
20 *       12-Apr-89 : - changed writeperm() and fmperm() to not use MARKing    *
21 *        5-May-89 : - redefined MASH to gain about 8% efficiency             *
22 *       18-Oct-90 : changes for version 1.6 :                                *
23 *                   - improved line breaking in writeperm()                  *
24 *       10-Nov-90 : - added dummy routine nautil_null()                      *
25 *       27-Aug-92 : changes for version 1.7 :                                *
26 *                   - made linelength <= 0 mean no line breaks               *
27 *        5-Jun-93 : renamed to version 1.7+ (no changes to this file)        *
28 *       18-Aug-93 : renamed to version 1.8 (no changes to this file)         *
29 *       17-Sep-93 : renamed to version 1.9 (no changes to this file)         *
30 *       29-Jun-95 : changes for version 1.10 :                               *
31 *                   - replaced loop in nextelement() to save reference past  *
32 *                     end of array (thanks to Kevin Maylsiak)                *
33 *       11-Jul-96 : changes for version 2.0 :                                *
34 *                   - added alloc_error()                                    *
35 *                   - added dynamic allocation                               *
36 *       21-Oct-98 : use 077777 in place of INFINITY for CLEANUP()            *
37 *        9-Jan-00 : added nautil_check()                                     *
38 *       12-Feb-00 : did a little formating of the code                       *
39 *       28-May-00 : added nautil_freedyn()                                   *
40 *       16-Aug-00 : added OLDNAUTY behaviour                                 *
41 *       16-Nov-00 : moved graph-specific things to naugraph.c                *
42 *                   use function prototypes, remove UPROC, nvector           *
43 *       22-Apr-01 : added code for compilation into Magma                    *
44 *                   removed nautil_null()                                    *
45 *                   removed EXTDEFS and included labelorg                    *
46 *       21-Nov-01 : use NAUTYREQUIRED in nautil_check()                      *
47 *       26-Jun-02 : revised permset() to avoid fetch past the end of         *
48 *                     the array (thanks to Jan Kieffer)                      *
49 *       17-Nov-03 : changed INFINITY to NAUTY_INFINITY                       *
50 *       14-Sep-04 : extended prototypes to recursive functions               *
51 *       23-Nov-06 : replave targetcell() by maketargetcell()                 *
52 *       10-Dec-06 : remove BIGNAUTY                                          *
53 *       10-Dec-10 : remove shortish and permutation types                    *
54 *       11-May-10 : use sorttemplates.c                                      *
55 *       27-Mar-11 : add writegroupsize()                                     *
56 *       15-Jan-12 : add TLS_ATTR attributes                                  *
57 *       16-Sep-12 : small change to objoin(), more efficient for sparse case *
58 *       22-Sep-12 : change documentation of orbjoin()                        *
59 *       18-Jan-12 : changes for version 2.6 :                                *
60 *                 - declare nauty_kill_request                               *
61 *                                                                            *
62 *****************************************************************************/
63 
64 #define ONE_WORD_SETS
65 #include "nauty.h"
66 #ifdef NAUTY_IN_MAGMA
67 #include "io.e"
68 #endif
69 
70     /* macros for hash-codes: */
71     /* Don't use NAUTY_INFINITY here as that would make the canonical
72      * labelling depend on whether BIGNAUTY is in operation */
73 #define MASH(l,i) ((((l) ^ 065435) + (i)) & 077777)
74     /* : expression whose long value depends only on long l and int/long i.
75          Anything goes, preferably non-commutative. */
76 
77 #define CLEANUP(l) ((int)((l) % 077777))
78     /* : expression whose value depends on long l and is less than 077777
79          when converted to int then short.  Anything goes. */
80 
81 #if  MAXM==1
82 #define M 1
83 #else
84 #define M m
85 #endif
86 
87 #if !MAXN
88 DYNALLSTAT(int,workperm,workperm_sz);
89 #else
90 static TLS_ATTR int workperm[MAXN];
91 #endif
92 
93 int labelorg = 0;   /* no TLS_ATTR on purpose */
94 volatile int nauty_kill_request = 0;   /* no TLS_ATTR on purpose */
95 
96 /* aproto: header new_nauty_protos.h */
97 
98 /*****************************************************************************
99 *                                                                            *
100 *  nextelement(set1,m,pos) = the position of the first element in set set1   *
101 *  which occupies a position greater than pos.  If no such element exists,   *
102 *  the value is -1.  pos can have any value less than n, including negative  *
103 *  values.                                                                   *
104 *                                                                            *
105 *  GLOBALS ACCESSED: none                                                    *
106 *                                                                            *
107 *****************************************************************************/
108 
109 int
nextelement(set * set1,int m,int pos)110 nextelement(set *set1, int m, int pos)
111 {
112     setword setwd;
113 
114 #if  MAXM==1
115     if (pos < 0) setwd = set1[0];
116     else         setwd = set1[0] & BITMASK(pos);
117 
118     if (setwd == 0) return -1;
119     else            return FIRSTBITNZ(setwd);
120 #else
121     int w;
122 
123     if (pos < 0)
124     {
125         w = 0;
126         setwd = set1[0];
127     }
128     else
129     {
130         w = SETWD(pos);
131         setwd = set1[w] & BITMASK(SETBT(pos));
132     }
133 
134     for (;;)
135     {
136         if (setwd != 0) return  TIMESWORDSIZE(w) + FIRSTBITNZ(setwd);
137         if (++w == m) return -1;
138         setwd = set1[w];
139     }
140 
141 #endif
142 }
143 
144 /*****************************************************************************
145 *                                                                            *
146 *  permset(set1,set2,m,perm)  defines set2 to be the set                     *
147 *  {perm[i] | i in set1}.                                                    *
148 *                                                                            *
149 *  GLOBALS ACCESSED: bit<r>,leftbit<r>                                       *
150 *                                                                            *
151 *****************************************************************************/
152 
153 void
permset(set * set1,set * set2,int m,int * perm)154 permset(set *set1, set *set2, int m, int *perm)
155 {
156     setword setw;
157     int pos,b;
158 #if MAXM!=1
159     int w;
160 #endif
161 
162     EMPTYSET(set2,m);
163 
164 #if MAXM==1
165     setw = set1[0];
166     while (setw  != 0)
167     {
168         TAKEBIT(b,setw);
169         pos = perm[b];
170         ADDELEMENT(set2,pos);
171     }
172 #else
173     for (w = 0; w < m; ++w)
174     {
175         setw = set1[w];
176         while (setw != 0)
177         {
178             TAKEBIT(b,setw);
179             pos = perm[TIMESWORDSIZE(w)+b];
180             ADDELEMENT(set2,pos);
181         }
182     }
183 #endif
184 }
185 
186 /*****************************************************************************
187 *                                                                            *
188 *  putstring(f,s) writes the nul-terminated string s to file f.              *
189 *                                                                            *
190 *****************************************************************************/
191 
192 void
putstring(FILE * f,char * s)193 putstring(FILE *f, char *s)
194 {
195     while (*s != '\0')
196     {
197         PUTC(*s,f);
198         ++s;
199     }
200 }
201 
202 /*****************************************************************************
203 *                                                                            *
204 *  itos(i,s) converts the int i to a nul-terminated decimal character        *
205 *  string s.  The value returned is the number of characters excluding       *
206 *  the nul.                                                                  *
207 *                                                                            *
208 *  GLOBALS ACCESSED: NONE                                                    *
209 *                                                                            *
210 *****************************************************************************/
211 
212 int
itos(int i,char * s)213 itos(int i, char *s)
214 {
215     int digit,j,k;
216     char c;
217     int ans;
218 
219     if (i < 0)
220     {
221         k = 0;
222         i = -i;
223         j = 1;
224         s[0] = '-';
225     }
226     else
227     {
228         k = -1;
229         j = 0;
230     }
231 
232     do
233     {
234         digit = i % 10;
235         i = i / 10;
236         s[++k] = (char)(digit + '0');
237     }
238     while (i);
239 
240     s[k+1] = '\0';
241     ans = k + 1;
242 
243     for (; j < k; ++j, --k)
244     {
245         c = s[j];
246         s[j] = s[k];
247         s[k] = c;
248     }
249 
250     return ans;
251 }
252 
253 /*****************************************************************************
254 *                                                                            *
255 *  orbits represents a partition of {0,1,...,n-1}, by orbits[i] = the        *
256 *  smallest element in the same cell as i.  map[] is any array with values   *
257 *  in {0,1,...,n-1}.  orbjoin(orbits,map,n) joins the cells of orbits[]      *
258 *  together to the minimum extent such that for each i, i and map[i] are in  *
259 *  the same cell.  The function value returned is the new number of cells.   *
260 *                                                                            *
261 *  GLOBALS ACCESSED: NONE                                                    *
262 *                                                                            *
263 *****************************************************************************/
264 
265 int
orbjoin(int * orbits,int * map,int n)266 orbjoin(int *orbits, int *map, int n)
267 {
268     int i,j1,j2;
269 
270     for (i = 0; i < n; ++i)
271     if (map[i] != i)
272     {
273         j1 = orbits[i];
274         while (orbits[j1] != j1) j1 = orbits[j1];
275         j2 = orbits[map[i]];
276         while (orbits[j2] != j2) j2 = orbits[j2];
277 
278         if (j1 < j2)      orbits[j2] = j1;
279         else if (j1 > j2) orbits[j1] = j2;
280     }
281 
282     j1 = 0;
283     for (i = 0; i < n; ++i)
284         if ((orbits[i] = orbits[orbits[i]]) == i) ++j1;
285 
286     return j1;
287 }
288 
289 /*****************************************************************************
290 *                                                                            *
291 *  writeperm(f,perm,cartesian,linelength,n) writes the permutation perm to   *
292 *  the file f.  The cartesian representation (i.e. perm itself) is used if   *
293 *  cartesian != FALSE; otherwise the cyclic representation is used.  No      *
294 *  more than linelength characters (not counting '\n') are written on each   *
295 *  line, unless linelength is ridiculously small.  linelength<=0 causes no   *
296 *  line breaks at all to be made.  The global int labelorg is added to each  *
297 *  vertex number.                                                            *
298 *                                                                            *
299 *  GLOBALS ACCESSED: itos(),putstring()                                      *
300 *                                                                            *
301 *****************************************************************************/
302 
303 void
writeperm(FILE * f,int * perm,boolean cartesian,int linelength,int n)304 writeperm(FILE *f, int *perm, boolean cartesian, int linelength, int n)
305 {
306     int i,k,l,curlen,intlen;
307     char s[30];
308 
309 #if !MAXN
310     DYNALLOC1(int,workperm,workperm_sz,n,"writeperm");
311 #endif
312 
313     /* CONDNL(x) writes end-of-line and 3 spaces if x characters
314        won't fit on the current line. */
315 #define CONDNL(x) if (linelength>0 && curlen+(x)>linelength)\
316               {putstring(f,"\n   ");curlen=3;}
317 
318     curlen = 0;
319     if (cartesian)
320     {
321         for (i = 0; i < n; ++i)
322         {
323             intlen = itos(perm[i]+labelorg,s);
324             CONDNL(intlen+1);
325             PUTC(' ',f);
326             putstring(f,s);
327             curlen += intlen + 1;
328         }
329         PUTC('\n',f);
330     }
331     else
332     {
333         for (i = n; --i >= 0;) workperm[i] = 0;
334 
335         for (i = 0; i < n; ++i)
336         {
337             if (workperm[i] == 0 && perm[i] != i)
338             {
339                 l = i;
340                 intlen = itos(l+labelorg,s);
341                 if (curlen > 3) CONDNL(2*intlen+4);
342                 PUTC('(',f);
343                 do
344                 {
345                     putstring(f,s);
346                     curlen += intlen + 1;
347                     k = l;
348                     l = perm[l];
349                     workperm[k] = 1;
350                     if (l != i)
351                     {
352                         intlen = itos(l+labelorg,s);
353                         CONDNL(intlen+2);
354                         PUTC(' ',f);
355                     }
356                 }
357                 while (l != i);
358                 PUTC(')',f);
359                 ++curlen;
360             }
361         }
362 
363         if (curlen == 0) putstring(f,"(1)\n");
364         else             PUTC('\n',f);
365     }
366 }
367 
368 /*****************************************************************************
369 *                                                                            *
370 *  fmperm(perm,fix,mcr,m,n) uses perm to construct fix and mcr.  fix         *
371 *  contains those points are fixed by perm, while mcr contains the set of    *
372 *  those points which are least in their orbits.                             *
373 *                                                                            *
374 *  GLOBALS ACCESSED: bit<r>                                                  *
375 *                                                                            *
376 *****************************************************************************/
377 
378 void
fmperm(int * perm,set * fix,set * mcr,int m,int n)379 fmperm(int *perm, set *fix, set *mcr, int m, int n)
380 {
381     int i,k,l;
382 
383 #if !MAXN
384     DYNALLOC1(int,workperm,workperm_sz,n,"writeperm");
385 #endif
386 
387     EMPTYSET(fix,m);
388     EMPTYSET(mcr,m);
389 
390     for (i = n; --i >= 0;) workperm[i] = 0;
391 
392     for (i = 0; i < n; ++i)
393         if (perm[i] == i)
394         {
395             ADDELEMENT(fix,i);
396             ADDELEMENT(mcr,i);
397         }
398         else if (workperm[i] == 0)
399         {
400             l = i;
401             do
402             {
403                 k = l;
404                 l = perm[l];
405                 workperm[k] = 1;
406             }
407             while (l != i);
408 
409             ADDELEMENT(mcr,i);
410         }
411 }
412 
413 /*****************************************************************************
414 *                                                                            *
415 *  fmptn(lab,ptn,level,fix,mcr,m,n) uses the partition at the specified      *
416 *  level in the partition nest (lab,ptn) to make sets fix and mcr.  fix      *
417 *  represents the points in trivial cells of the partition, while mcr        *
418 *  represents those points which are least in their cells.                   *
419 *                                                                            *
420 *  GLOBALS ACCESSED: bit<r>                                                  *
421 *                                                                            *
422 *****************************************************************************/
423 
424 void
fmptn(int * lab,int * ptn,int level,set * fix,set * mcr,int m,int n)425 fmptn(int *lab, int *ptn, int level, set *fix, set *mcr, int m, int n)
426 {
427     int i,lmin;
428 
429     EMPTYSET(fix,m);
430     EMPTYSET(mcr,m);
431 
432     for (i = 0; i < n; ++i)
433         if (ptn[i] <= level)
434         {
435             ADDELEMENT(fix,lab[i]);
436             ADDELEMENT(mcr,lab[i]);
437         }
438         else
439         {
440             lmin = lab[i];
441             do
442                 if (lab[++i] < lmin) lmin = lab[i];
443             while (ptn[i] > level);
444             ADDELEMENT(mcr,lmin);
445         }
446 }
447 
448 #define SORT_TYPE1 int
449 #define SORT_TYPE2 int
450 #define SORT_OF_SORT 2
451 #define SORT_NAME sortparallel
452 #include "sorttemplates.c"
453 
454 /*****************************************************************************
455 *                                                                            *
456 *  doref(g,lab,ptn,level,numcells,qinvar,invar,active,code,refproc,          *
457 *        invarproc,mininvarlev,maxinvarlev,invararg,digraph,m,n)             *
458 *  is used to perform a refinement on the partition at the given level in    *
459 *  (lab,ptn).  The number of cells is *numcells both for input and output.   *
460 *  The input active is the active set for input to the refinement procedure  *
461 *  (*refproc)(), which must have the argument list of refine().              *
462 *  active may be arbitrarily changed.  invar is used for working storage.    *
463 *  First, (*refproc)() is called.  Then, if invarproc!=NULL and              *
464 *  |mininvarlev| <= level <= |maxinvarlev|, the routine (*invarproc)() is    *
465 *  used to compute a vertex-invariant which may refine the partition         *
466 *  further.  If it does, (*refproc)() is called again, using an active set   *
467 *  containing all but the first fragment of each old cell.  Unless g is a    *
468 *  digraph, this guarantees that the final partition is equitable.  The      *
469 *  arguments invararg and digraph are passed to (*invarproc)()               *
470 *  uninterpretted.  The output argument code is a composite of the codes     *
471 *  from all the calls to (*refproc)().  The output argument qinvar is set    *
472 *  to 0 if (*invarproc)() is not applied, 1 if it is applied but fails to    *
473 *  refine the partition, and 2 if it succeeds.                               *
474 *  See the file nautinv.c for a further discussion of vertex-invariants.     *
475 *  Note that the dreadnaut I command generates a call to  this procedure     *
476 *  with level = mininvarlevel = maxinvarlevel = 0.                           *
477 *                                                                            *
478 *****************************************************************************/
479 
480 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)481 doref(graph *g, int *lab, int *ptn, int level, int *numcells,
482      int *qinvar, int *invar, set *active, int *code,
483      void (*refproc)(graph*,int*,int*,int,int*,int*,set*,int*,int,int),
484      void (*invarproc)(graph*,int*,int*,int,int,int,int*,
485                                                  int,boolean,int,int),
486      int mininvarlev, int maxinvarlev, int invararg,
487      boolean digraph, int m, int n)
488 {
489     int pw;
490     int i,cell1,cell2,nc,tvpos,minlev,maxlev;
491     long longcode;
492     boolean same;
493 
494 #if !MAXN
495     DYNALLOC1(int,workperm,workperm_sz,n,"doref");
496 #endif
497 
498     if ((tvpos = nextelement(active,M,-1)) < 0) tvpos = 0;
499 
500     (*refproc)(g,lab,ptn,level,numcells,invar,active,code,M,n);
501 
502     minlev = (mininvarlev < 0 ? -mininvarlev : mininvarlev);
503     maxlev = (maxinvarlev < 0 ? -maxinvarlev : maxinvarlev);
504     if (invarproc != NULL && *numcells < n
505                         && level >= minlev && level <= maxlev)
506     {
507         (*invarproc)(g,lab,ptn,level,*numcells,tvpos,invar,invararg,
508                                                              digraph,M,n);
509         EMPTYSET(active,m);
510         for (i = n; --i >= 0;) workperm[i] = invar[lab[i]];
511         nc = *numcells;
512         for (cell1 = 0; cell1 < n; cell1 = cell2 + 1)
513         {
514             pw = workperm[cell1];
515             same = TRUE;
516             for (cell2 = cell1; ptn[cell2] > level; ++cell2)
517                 if (workperm[cell2+1] != pw) same = FALSE;
518 
519             if (same) continue;
520 
521             sortparallel(workperm+cell1, lab+cell1, cell2-cell1+1);
522 
523             for (i = cell1 + 1; i <= cell2; ++i)
524                 if (workperm[i] != workperm[i-1])
525                 {
526                     ptn[i-1] = level;
527                     ++*numcells;
528                     ADDELEMENT(active,i);
529                 }
530         }
531 
532         if (*numcells > nc)
533         {
534             *qinvar = 2;
535             longcode = *code;
536             (*refproc)(g,lab,ptn,level,numcells,invar,active,code,M,n);
537             longcode = MASH(longcode,*code);
538             *code = CLEANUP(longcode);
539         }
540         else
541             *qinvar = 1;
542     }
543     else
544         *qinvar = 0;
545 }
546 
547 /*****************************************************************************
548 *                                                                            *
549 *  maketargetcell(g,lab,ptn,level,tcell,tcellsize,&cellpos,                  *
550 *                 tc_level,digraph,hint,targetcell,m,n)                      *
551 *  calls targetcell() to determine the target cell at the specified level    *
552 *  in the partition nest (lab,ptn).  It must be a nontrivial cell (if not,   *
553 *  the first cell.  The intention of hint is that, if hint >= 0 and there    *
554 *  is a suitable non-trivial cell starting at position hint in lab,          *
555 *  that cell is chosen.                                                      *
556 *  tc_level and digraph are input options.                                   *
557 *  When a cell is chosen, tcell is set to its contents, *tcellsize to its    *
558 *  size, and cellpos to its starting position in lab.                        *
559 *                                                                            *
560 *  GLOBALS ACCESSED: bit<r>                                                  *
561 *                                                                            *
562 *****************************************************************************/
563 
564 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)565 maketargetcell(graph *g, int *lab, int *ptn, int level, set *tcell,
566        int *tcellsize, int *cellpos, int tc_level, boolean digraph,
567        int hint,
568        int (*targetcell)(graph*,int*,int*,int,int,boolean,int,int,int),
569        int m, int n)
570 {
571     int i,j,k;
572 
573     i = (*targetcell)(g,lab,ptn,level,tc_level,digraph,hint,m,n);
574     for (j = i + 1; ptn[j] > level; ++j) {}
575 
576     *tcellsize = j - i + 1;
577 
578     EMPTYSET(tcell,m);
579     for (k = i; k <= j; ++k) ADDELEMENT(tcell,lab[k]);
580 
581     *cellpos = i;
582 }
583 
584 /*****************************************************************************
585 *                                                                            *
586 *  shortprune(set1,set2,m) ANDs the contents of set set2 into set set1.      *
587 *                                                                            *
588 *  GLOBALS ACCESSED: NONE                                                    *
589 *                                                                            *
590 *****************************************************************************/
591 
592 void
shortprune(set * set1,set * set2,int m)593 shortprune(set *set1, set *set2, int m)
594 {
595     int i;
596 
597     for (i = 0; i < M; ++i) INTERSECT(set1[i],set2[i]);
598 }
599 
600 /*****************************************************************************
601 *                                                                            *
602 *  breakout(lab,ptn,level,tc,tv,active,m) operates on the partition at       *
603 *  the specified level in the partition nest (lab,ptn).  It finds the        *
604 *  element tv, which is in the cell C starting at index tc in lab (it had    *
605 *  better be) and splits C in the two cells {tv} and C\{tv}, in that order.  *
606 *  It also sets the set active to contain just the element tc.               *
607 *                                                                            *
608 *  GLOBALS ACCESSED: bit<r>                                                  *
609 *                                                                            *
610 *****************************************************************************/
611 
612 void
breakout(int * lab,int * ptn,int level,int tc,int tv,set * active,int m)613 breakout(int *lab, int *ptn, int level, int tc, int tv,
614      set *active, int m)
615 {
616     int i,prev,next;
617 
618     EMPTYSET(active,m);
619     ADDELEMENT(active,tc);
620 
621     i = tc;
622     prev = tv;
623 
624     do
625     {
626         next = lab[i];
627         lab[i++] = prev;
628         prev = next;
629     }
630     while (prev != tv);
631 
632     ptn[tc] = level;
633 }
634 
635 /*****************************************************************************
636 *                                                                            *
637 *  longprune(tcell,fix,bottom,top,m) removes zero or elements of the set     *
638 *  tcell.  It is assumed that addresses bottom through top-1 contain         *
639 *  contiguous pairs of sets (f1,m1),(f2,m2), ... .  tcell is intersected     *
640 *  with each mi such that fi is a subset of fix.                             *
641 *                                                                            *
642 *  GLOBALS ACCESSED: NONE                                                    *
643 *                                                                            *
644 *****************************************************************************/
645 
646 void
longprune(set * tcell,set * fix,set * bottom,set * top,int m)647 longprune(set *tcell, set *fix, set *bottom, set *top, int m)
648 {
649     int i;
650 
651     while (bottom < top)
652     {
653         for (i = 0; i < M; ++i)
654             if (NOTSUBSET(fix[i],bottom[i])) break;
655         bottom += M;
656 
657         if (i == M)
658             for (i = 0; i < M; ++i) INTERSECT(tcell[i],bottom[i]);
659         bottom += M;
660     }
661 }
662 
663 /*****************************************************************************
664 *                                                                            *
665 *  writegroupsize(f,gpsize1,gpsize2) writes a real number gpsize1*10^gpsize2 *
666 *  It is assumed that gpsize1 >= 1 and that gpsize1 equals an integer in the *
667 *  case that gpsize2==0.  These assumptions are true for group sizes         *
668 *  computed by nauty.                                                        *
669 *                                                                            *
670 *****************************************************************************/
671 
672 void
writegroupsize(FILE * f,double gpsize1,int gpsize2)673 writegroupsize(FILE *f, double gpsize1, int gpsize2)
674 {
675     if (gpsize2 == 0)
676         fprintf(f,"%.0f",gpsize1+0.1);
677     else
678     {
679         while (gpsize1 >= 10.0)
680         {
681             gpsize1 /= 10.0;
682             ++gpsize2;
683         }
684         fprintf(f,"%14.12fe%d",gpsize1,gpsize2);
685     }
686 }
687 
688 /*****************************************************************************
689 *                                                                            *
690 *  nautil_check() checks that this file is compiled compatibly with the      *
691 *  given parameters.   If not, call exit(1).                                 *
692 *                                                                            *
693 *****************************************************************************/
694 
695 void
nautil_check(int wordsize,int m,int n,int version)696 nautil_check(int wordsize, int m, int n, int version)
697 {
698     if (wordsize != WORDSIZE)
699     {
700         fprintf(ERRFILE,"Error: WORDSIZE mismatch in nautil.c\n");
701         exit(1);
702     }
703 
704 #if MAXN
705     if (m > MAXM)
706     {
707         fprintf(ERRFILE,"Error: MAXM inadequate in nautil.c\n");
708         exit(1);
709     }
710 
711     if (n > MAXN)
712     {
713         fprintf(ERRFILE,"Error: MAXN inadequate in nautil.c\n");
714         exit(1);
715     }
716 #endif
717 
718     if (version < NAUTYREQUIRED)
719     {
720         fprintf(ERRFILE,"Error: nautil.c version mismatch\n");
721         exit(1);
722     }
723 }
724 
725 /*****************************************************************************
726 *                                                                            *
727 *  alloc_error() writes a message and exits.  Used by DYNALLOC? macros.      *
728 *                                                                            *
729 *****************************************************************************/
730 
731 void
alloc_error(const char * s)732 alloc_error(const char *s)
733 {
734     fprintf(ERRFILE,"Dynamic allocation failed: %s\n",s);
735     exit(2);
736 }
737 
738 /*****************************************************************************
739 *                                                                            *
740 *  nautil_freedyn() - free the dynamic memory in this module                 *
741 *                                                                            *
742 *****************************************************************************/
743 
744 void
nautil_freedyn(void)745 nautil_freedyn(void)
746 {
747 #if !MAXN
748     DYNFREE(workperm,workperm_sz);
749 #endif
750 }
751