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