diff options
48 files changed, 37928 insertions, 133 deletions
@@ -1,6 +1,8 @@ .* !.gitignore polygon +*.o +*.a # Rust target diff --git a/graph-checker/Cargo.lock b/graph-checker/Cargo.lock index 4feb4c5..f5c0ae0 100644 --- a/graph-checker/Cargo.lock +++ b/graph-checker/Cargo.lock @@ -3,5 +3,66 @@ version = 3 [[package]] +name = "cc" +version = "1.0.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" + +[[package]] +name = "either" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" + +[[package]] name = "graph-checker" version = "0.1.0" +dependencies = [ + "cc", + "rayon", +] + +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] diff --git a/graph-checker/Cargo.toml b/graph-checker/Cargo.toml index ffce39c..e2e9bfc 100644 --- a/graph-checker/Cargo.toml +++ b/graph-checker/Cargo.toml @@ -4,3 +4,8 @@ version = "0.1.0" edition = "2021" [dependencies] +rayon = "1.10.0" + +[build-dependencies] +cc = "1.0" + diff --git a/graph-checker/build.rs b/graph-checker/build.rs new file mode 100644 index 0000000..b706131 --- /dev/null +++ b/graph-checker/build.rs @@ -0,0 +1,26 @@ +use std::process::Command; + +fn main() { + Command::new("make") + .arg("-C") + .arg("nauty") + .arg("libnauty.a") + .status() + .unwrap(); + cc::Build::new() + .file("nauty/geng.c") + .file("nauty/geng-iter.c") + .flag("-O3") + .flag("-Wno-unused-parameter") + .flag("-Wno-sign-compare") + .flag("-Wno-unused-variable") + .define("_XOPEN_SOURCE", None) + .define("MAXN", "WORDSIZE") + .define("WORDSIZE", "32") + .compile("geng"); + + println!("cargo:rerun-if-changed=./nauty/geng.c"); + println!("cargo:rerun-if-changed=./nauty/geng-iter.c"); + println!("cargo:rustc-link-search=./nauty"); + println!("cargo:rustc-link-lib=static=nauty"); +} diff --git a/graph-checker/nauty/.gitignore b/graph-checker/nauty/.gitignore new file mode 100644 index 0000000..4ef51c7 --- /dev/null +++ b/graph-checker/nauty/.gitignore @@ -0,0 +1,5 @@ +.* +!.gitignore +*.o +*.a +config.* diff --git a/graph-checker/nauty/COPYRIGHT b/graph-checker/nauty/COPYRIGHT new file mode 100644 index 0000000..8f3b54a --- /dev/null +++ b/graph-checker/nauty/COPYRIGHT @@ -0,0 +1,41 @@ +This is the license for the software package Nauty and +Traces, package versions 2.6r3 and later. + +Five categories of software are included in the package: +A. All files not listed as B-E below, copyright Brendan McKay (1984-) +B. Files traces.h, traces.c and dretodot.c, copyright Adolfo Piperno (2008-) +C. File watercluster2.c and genposetg.c, copyright Gunnar Brinkmann (2009-) +D. Files planarity.h and planarity.c, copyright Magma project. +E. Files nautycliquer.h and nautycliquer.c, copyright to Sampo + Niskanen and Patric ÖstergÃ¥rd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this software except in compliance with the License. +You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +Brendan McKay: Australian National University; Brendan.McKay@anu.edu.au +Adolfo Piperno: University of Rome "Sapienza"; piperno@di.uniroma1.it +Gunnar Brinkmann: University of Ghent; Gunnar.Brinkmann@UGent.be +Magma Administration: University of Sydney; admin@maths.usyd.edu.au +Patric Ostergard: Aalto Univerity; patric.ostergard@aalto.fi + +---END-OF-FORMAL-COPYRIGHT-NOTICE--- + +Earlier (pre-2.6) versions of this package carried a different +notice: "Permission is hereby given for use and/or distribution +with the exception of sale for profit or application with nontrivial +military significance." These days most people use nauty via a +larger package such as Magma, Sage, or GAP, and often they don't +even know they are using nauty. Due to the legal nonsense that +large package distributors need to worry about, it has proved too +much trouble to maintain an idiosyncratic licence. I didn't change +my opinion about military use, but it is no longer part of the +formal notice. Brendan McKay (Jan 20, 2016) diff --git a/graph-checker/nauty/README b/graph-checker/nauty/README new file mode 100644 index 0000000..6a62a64 --- /dev/null +++ b/graph-checker/nauty/README @@ -0,0 +1,107 @@ +README file for nauty 2.8 + +Brendan McKay, bdm@cs.anu.edu.au +Adolfo Piperno, piperno@di.uniroma1.it + +------------------------------------------------------------ + +The most recent distribution of nauty and Traces can be found at +http://cs.anu.edu.au/~bdm/nauty and http://pallini.di.uniroma1.it . + +The manual nug28.pdf is available at that site and is also included +in the distribution package. + +Note that nauty and Traces are copyright but free to use for most +purposes. The details are in the file COPYRIGHT. + +------------------------------------------------------------ + +INSTALLATION. + +See the manual for more information. + +If you have a working shell, and "make", you can run + ./configure +followed by + make +to compile nauty and Traces for your system. + +If that succeeds without problem, you will have the +program dreadnaut ready to run. + +There are some options that can be specified at the ./configure +step; see the manual. + +If you don't have a shell or make, manually edit the files nauty.h, +naututil.h and gtools.h as distributed. The parts between the lines +======= near the start are the main things to look at. After this +manual editing, you can use makefile.basic as a guide to compilation. + +Programs which use an older version of nauty need to be +recompiled (** not just relinked **). Make sure they define +the options structure using one of +DEFAULTOPTIONS_GRAPH +DEFAULTOPTIONS_SPARSEGRAPH +DEFAULTOPTIONS_DIGRAPH +DEFAULTOPTIONS_SPARSEDIGRAPH +DEFAULTOPTIONS_TRACES + +------------------------------------------------------------ + +TESTING. + +After compiling nauty successfully, it is recommended that you run +the included test programs. The simplest way is + make checks + +------------------------------------------------------------ + +MAILING LIST. + +There is a mailing list for announcements and discussion about +nauty and related topics. You can subscribe at +http://mailman.anu.edu.au/mailman/listinfo/nauty + +------------------------------------------------------------ + +OTHER FILES IN THE PACKAGE. + +Also in the package (documentation at the start of each source file). + +sumlines.c - This is a program designed to digest the outputs from + multiple runs of a program (such as a computation split into multiple + parts). Lines matching given patterns can be counted and checked, + and numbers appearing in them can be accumulated. Instructions appear + in the source file. See the option GMP near the head of the program + before trying to compile. + +sorttemplates.c - Some carefully tuned generic quicksort procedures. + +------------------------------------------------------------ + +Windows. + +For running nauty in Windows, Cygwin is recommended. + +If configure gives an error message similar to this: + can not guess host type: you must specify one +then try + ./configure --build=unknown + +------------------------------------------------------------ + +Making 32-bit executables on 64-bit Linux systems. + +(In bash or sh:) +CFLAGS=-m32 CXXFLAGS=-m32 LDFLAGS=-m32 ./configure +make clean; make + +This requires 32-bit libraries to be available. On Ubuntu +they are called ia32-libs and libc6-dev-i386. + +------------------------------------------------------------ + +RECENT CHANGES. + +See the file changes24-28.txt for a longer list. +See the file README_24 for a list of older changes. diff --git a/graph-checker/nauty/README_24 b/graph-checker/nauty/README_24 new file mode 100644 index 0000000..35feb62 --- /dev/null +++ b/graph-checker/nauty/README_24 @@ -0,0 +1,292 @@ +README file for nauty 2.4 +This file is only of historical interest as this version is very old. + +Brendan McKay, bdm@cs.anu.edu.au + +------------------------------------------------------------ + +The most recent distribution of nauty can be found at +http://cs.anu.edu.au/~bdm/nauty . + +The manual nug.pdf is available at that site and is also included +in the distribution package. + +Note that nauty is copyright but free to use for most purposes. +The details are in the file nauty.h. + +The code in the file planarity.c (used by the planarg program) +is copyright to the Magma project. + +------------------------------------------------------------ + +INSTALLATION. + +The first step is to unpack the archive. On Unix-ish systems +you can use one of these commands: + + tar xzf nauty24.tar.gz +or + gunzip -c nauty24.tar.gz | tar xf - + +This will write all the files into the subdirectory nauty24. +Go to that directory. + +If you have a working shell, and make, you can run + ./configure +followed by + make all +to compile nauty for your system. + +If that succeeds without problem, you will have have the +program dreadnaut ready to run. + +If you have problems during compilation, it may be that the +configuration scripts are inadequate for your system. Usually it +is because of some missing system header, incompatible typedef, +or similar. Please send the details to the author. + +If you don't have a shell or make, manually edit the files nauty.h, +naututil.h and gtools.h as distributed. The parts between the lines +======= near the start are the main things to look at. After this +manual editing, you can use makefile as a guide to compilation. + +Programs which use an older version of nauty need to be +recompiled (** not just relinked **). Make sure they use the +DEFAULTOPTIONS_GRAPH or DEFAULTOPTIONS_SPARSEGRAPH macro to define +the fields of the options parameter. + +See below for compiling on a PC under DJGPP. + +If you are using Windows in an environment that needs Windows line +endings (which is a configuration option in Cygwin, for example), +then you might prefer to use nauty24.zip rather than +nauty24.tar.gz. + +------------------------------------------------------------ + +TESTING. + +After compiling nauty successfully, it is recommended that you run +the included test programs. The simplest way is + make checks + +------------------------------------------------------------ + +MAILING LIST. + +There is a mailing list for announcements and discussion about +nauty and related topics. You can subscribe at +http://dcsmail.anu.edu.au/cgi-bin/mailman/listinfo/nauty-list + +------------------------------------------------------------ + +OTHER FILES IN THE PACKAGE. + +A few additional goodies are included. + +sumlines.c - This is a program designed to digest the outputs from + multiple runs of a program (such as a computation split into multiple + parts). Lines matching given patterns can be counted and checked, + and numbers appearing in them can be accumulated. Instructions appear + in the source file. See the option GMP near the head of the program + before trying to compile. + +naugroup.h, naugroup.c - These define procedures for exhaustively + listing a group found by nauty. This is done in a space-efficient way. + A sample program appears in nautyex3.c, but so far there is no + complete documentation. + +------------------------------------------------------------ + +DJGPP. + +The Unix-like environment DJGPP can be used to run nauty and gtools on +DOS/Win computers. DJGPP is available at http://www.delorie.com/djgpp . +The program shortg does not work since DJGPP does not provide a working +pipe() system call. Using the bash shell is recommended. In DOS, +Windows NT and early Windows editions, you will need to convert all +long file names to the 8+3 limits. Thanks to Guenter Sterntenbrink +for helping with this. + +If configure gives an error message similar to this: + can not guess host type: you must specify one +then try + ./configure --host=i686 +or use i586 for Pentium 2. If all of those fail, try + ./configure --host=unknown + +------------------------------------------------------------ + +Making 32-bit executables on 64-bit Linux systems. + +(In bash or sh:) +CFLAGS=-m32 CXXFLAGS=-m32 LDFLAGS=-m32 ./configure +make clean; make + +This requires the libraries ia32-libs and libc6-dev-i386. + +------------------------------------------------------------ + +RECENT CHANGES. + +Here we list substantive changes made since the first 2.2 release. + +Nov 16, 2002: Replaced rng.c after communication with Don Knuth. + The previous version had a bug (mine!) when there was no explicit + initialization done by the user. It appears the error had no + impact on nauty (which only uses rng.c for the "s" command in + dreadnaut, and for genrang, but both always initialize). + No change to the nauty version number but beta=2. + +Nov 18, 2000: Adjusted the makefile and countg/testg to work in + the DOS/Win environment DJGPPP (see the previous section). + +May 1, 2003: Fixed PRUNE feature of genbg. + +May 3, 2003: Added utility directg for making all orientations of graphs. + +Oct 4, 2003: Added options -a, -Z, -d, -z to genbg. Also, the -l + (canonical label) option now preserves the colouring. + +Nov 17, 2003: Renamed INFINITY to NAUTY_INFINITY since many C header + libraries define INFINITY. If INFINITY is not defined by + the system, you can still use it. + +Nov 19, 2003: Added program biplabg to relabel bipartite graphs with the + colour classes contiguous. + +Feb 13, 2004: Revised C options for solaris on pentium + +Mar 1, 2004: dretog knows !...\n type of comment + +May 7, 2004: geng can be called from another program (see instructions + in geng.c.) + +May 29, 2004: added definition of SETWORD_FORMAT used to write a setword + with printf( ) - see nauty.h + +Sep 11, 2004: Added utility multig for making multigraphs based on + provided simple graphs; similar to directg + +Oct 16, 2004: To avoid problems caused by system-dependent handling of + external declarations, nauty() no longer accepts NULL as + the value of options.dispatch. To get the previous + behaviour, use the value &graph_dispatch. This will be + handled automatically if programs calling nauty use + DEFAULTOPTIONS to declare options and are recompiled. + Even better is to use DEFAULTOPTIONS_GRAPH. + +May 5, 2005: A bug in the writing of sparse6 was found and fixed. + This is procedure ntos6() in gtools.c, which is invoked + by writes6(). The bug could only happen if all the + following are true: + 1. n = 2, 4, 8 or 16 (for n=2, only if the graph has loops) + 2. Vertex n-2 has non-zero degree, but vertex n-1 has + zero degree. + These conditions never happen for graphs generated by + geng or genbg, nor for regular graphs or connected graphs, + nor for graphs canonically labelled by nauty (except maybe + with some unusual vertex colouring or invariant). + If the conditions do happen, the buggy routine may + (with some probability) add a spurious loop to vertex n-1. + + In the package is a utility checks6: + + Usage: checks6 [-w] [infile [outfile]] + Check a file of graphs, optionally write corrected version + -w Write corrected graphs (default is not to write) + +------now we start version 2.3 (not released) and 2.4------ + +Nov 10, 2004: Use faster routine getc_unlocked() for reading graphs if + available. It can make a surprising difference. + +Nov 17, 2004: If putenv() or setenv() are available, we set LC_COLLATE to + "C" before executing "sort" in shortg. This should alleviate + collation issues with sort. However, note that many + utilities use the locale these days so you are advised to + have LC_COLLATE defined to be "C" always when you are dealing + with files of graphs. + + Six counters in statsblk became "unsigned long" instead of + "long". nauty doesn't actually use these, but we might as + well give them twice as long before they overflow. + +Nov 24, 2004: Made geng faster for generating trees. The output labelling + may be different from before. A very much faster tree + generator is in the works. + +Jan 17, 2005: Added two items to dispatch vectors: + init : used for initialising something at the start + cleanup : used for doing something at the end, such as + freeing space allocated by init() + See the manual for calling sequences. + +May 20, 2005: Update graph6 and sparse6 formats to allow much large sizes. + The limit is now 68719476735 vertices (best of luck getting + close to that!). + +Nov 12, 2005: Changed NAUTY_INFINITY to 2^30+2 in BIGNAUTY case + +2006 various: Procedures for sparse graphs implemented. + + New program planarg to test for planarity and find + planar embeddings: planarg -help for details. + The planarity code was written by Paulette Lieby for the + Magma project and used with permission. + + labelg got -S to use sparse graphs. + genbg -N changed to genbg -n (only Gordon uses this). + genrang gained -R switch for regular graphs in text format. + gtools.c has code for reading and writing planarcode. + listg got a compile time option to select "Matrix" or + "array" for Maple output. + pickg/countg got -T for counting triangles + + Better configuration for MacOSX. + +Nov 22, 2006: Removed usertcellproc from options. Greater functionality + is now available using the new targetcell field in the + dispatch vector. The u8 command has gone from dreadnaut. + + Changed bestcell to targetcell in dispatch vector. + +Nov 29, 2006: Added extraoptions field (currently unused) to optionblk + +Dec 9, 2006: Added an invariant adjacencies_sg(), recommended for digraphs + when using sparse representation. + +Dec 10, 2006: Remove BIGNAUTY, whose usefulness has passed. Now the types + shortish and permutation are synonymous with int always. + The limit on the number of vertices is 2^30 unless int + has only 16 bits (still any of them around?) in which + case it is 2^15-3. Programs previously linked with + files like nautyB.o can now be linked with nauty.o. + Alternatively, "make bigs" will create files like + nautyB.o by copying. + +June 26, 2007: Fixed an error in listg -s reported by Evan Heidtmann. + +July 12, 2007: Added -f option to directg. + +Aug 14, 2007: Added -i,-I,-K options to shortg, parallel to labelg. + Since -k is used in labelg in place of -I, changed labelg + to use -I also, with -k remaining as an undocumented + compatibility feature. + +Aug-Sep 2007: Minor things: + * naututil-h.in now defines CPUTIME=0.0 as a last resort + * gtools.c now implements EDGECODE (not used anywhere yet) + * fixed definition of SG_FREE in nausparse.h (not used) + * geng favours space over time for n > 28 + +Oct 14, 2007: Added -T switch to shortg to specify scratch directory. + +Mar 3, 2008: Fixed makefile for compilation in a 64-bit environment. + +Oct 11, 2008: Added -l and -m to genrang + +Nov 29, 2008: Slightly improved -c for geng and genbg + Added tournament generator gentourng. + +Mar 3, 2009: Added -V to directg. diff --git a/graph-checker/nauty/geng-iter.c b/graph-checker/nauty/geng-iter.c new file mode 100644 index 0000000..e812f49 --- /dev/null +++ b/graph-checker/nauty/geng-iter.c @@ -0,0 +1,128 @@ +#include "geng-iter.h" + +#include "geng.h" +#include "gtools.h" +#include <ucontext.h> +#include <stdint.h> +#include <stdbool.h> + +// TODO: only on macos +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + +void +printgraph(graph *g, int n) +{ + set *gi; + for (int i = 0; i < n; ++i) + { + gi = GRAPHROW(g, 1, i); + for (int j = 0; j < n; ++j) + { + int q = ISELEMENT(gi, j); + printf("%i ", q); + } + printf("\n"); + } + printf("\n"); +} + +// TODO: probably don't need to name this function with macro +void +outproc(__attribute__((unused)) FILE *outfile, + graph *g, int n, struct geng_iterator *iter) +{ + memcpy(&iter->batch[iter->batch_size * n], g, sizeof(set) * n); + iter->graph_size = n; + iter->batch_size++; + + if (iter->batch_size == iter->batch_capacity) + swapcontext(&iter->geng_worker, &iter->geng_user); +} + +// geng_iterator_init gives ownership of iterator memory to caller +void +geng_iterator_create(struct geng_iterator **iterator_ptr, + size_t graph_size, + size_t batch_capacity) +{ + *iterator_ptr = malloc(sizeof(struct geng_iterator)); + + struct geng_iterator *iterator = *iterator_ptr; + iterator->batch = malloc(sizeof(set) * graph_size * batch_capacity); + iterator->batch_capacity = batch_capacity; + iterator->batch_size = 0; + + iterator->iteration_done = false; + + // TODO: add support for more arguments + int geng_argc = 3; + char **geng_argv; + geng_argv = malloc((geng_argc + 1) * sizeof(char *)); + geng_argv[0] = "geng"; + geng_argv[1] = "-q"; + char n_str[20]; + snprintf(n_str, sizeof(n_str), "%zu", graph_size); + geng_argv[2] = n_str; + geng_argv[3] = NULL; + + // TODO: make macro + uint32_t p_argv[2]; + p_argv[0] = (uint32_t) (((size_t) &geng_argv) & ((1llu << 32) - 1llu)); + p_argv[1] = ((size_t) &geng_argv) >> 32; + + uint32_t p_iter[2]; + p_iter[0] = (uint32_t) (((size_t) &iterator) & ((1llu << 32) - 1llu)); + p_iter[1] = ((size_t) &iterator) >> 32; + + getcontext(&iterator->geng_user); + iterator->geng_worker = iterator->geng_user; + iterator->geng_worker.uc_stack.ss_sp = iterator->geng_stack; + iterator->geng_worker.uc_stack.ss_size = sizeof(iterator->geng_stack); + iterator->geng_worker.uc_link = &iterator->geng_user; + + makecontext( + &iterator->geng_worker, (void (*) (void)) geng_main, + 5, geng_argc, p_argv[0], p_argv[1], p_iter[0], p_iter[1] + ); + swapcontext(&iterator->geng_user, &iterator->geng_worker); + free(geng_argv); +} + +bool +geng_iterator_next(struct geng_iterator *iter, set *g) +{ + static int i = 0; + + if (i == iter->batch_size && iter->generation_done) + iter->iteration_done = true; + + if (iter->iteration_done) return false; + else + { + if (i == iter->batch_size && !iter->generation_done) + { + iter->batch_size = 0; + i = 0; + swapcontext(&iter->geng_user, &iter->geng_worker); + } + + memcpy( + g, + &iter->batch[i * iter->graph_size], + sizeof(set) * iter->graph_size + ); + i++; + return true; + } +} + +void +geng_iterator_destroy(struct geng_iterator *iter) +{ + free(iter->batch); + free(iter); +} + +// TODO: only on macos +#pragma GCC diagnostic pop diff --git a/graph-checker/nauty/geng-iter.h b/graph-checker/nauty/geng-iter.h new file mode 100644 index 0000000..1e78b93 --- /dev/null +++ b/graph-checker/nauty/geng-iter.h @@ -0,0 +1,23 @@ +#ifndef GENG_ITER_H +#define GENG_ITER_H + +#include "gtools.h" +#include <ucontext.h> +#include <stdbool.h> + +struct geng_iterator +{ + ucontext_t geng_worker, geng_user; + char geng_stack[1 << 20]; + int graph_size; + bool generation_done; + bool iteration_done; + int batch_size; + int batch_capacity; + set *batch; +}; + +void +outproc(FILE *, graph *, int n, struct geng_iterator *); + +#endif // GENG_ITER_H diff --git a/graph-checker/nauty/geng.c b/graph-checker/nauty/geng.c new file mode 100644 index 0000000..ad0eafe --- /dev/null +++ b/graph-checker/nauty/geng.c @@ -0,0 +1,2238 @@ +/* geng.c version 3.6; B D McKay, October 2022. */ + +#define NAUTY_PGM 1 /* 1 = geng, 2 = genbg, 3 = gentourng, 4 = gentreeg */ + +#ifndef MAXN +#define MAXN WORDSIZE /* not more than WORDSIZE */ +#endif + +#if MAXN > WORDSIZE || MAXN > 64 + #error "Can't have MAXN greater than min(64,WORDSIZE)" +#endif + +#define ONE_WORD_SETS +#include "gtools.h" /* which includes nauty.h and stdio.h */ +#include "geng.h" +#include "geng-iter.h" +#include <stdint.h> + +typedef setword xword; + +static TLS_ATTR FILE *outfile; /* file for output graphs */ +static TLS_ATTR int connec; /* 1 for -c, 2 for -C, 0 for neither */ +static TLS_ATTR boolean bipartite; /* presence of -b */ +static TLS_ATTR boolean trianglefree; /* presence of -t */ +static TLS_ATTR boolean squarefree; /* presence of -f */ +static TLS_ATTR boolean k4free; /* presence of -k */ +static TLS_ATTR boolean splitgraph; /* presence of -S */ +static TLS_ATTR boolean chordal; /* presence of -T */ +static TLS_ATTR boolean perfect; /* presence of -P */ +static TLS_ATTR boolean clawfree; /* presence of -F */ +static TLS_ATTR boolean savemem; /* presence of -m */ +static TLS_ATTR boolean verbose; /* presence of -v */ +boolean TLS_ATTR nautyformat; /* presence of -n */ +boolean TLS_ATTR graph6; /* presence of -g */ +boolean TLS_ATTR sparse6; /* presence of -s */ +boolean TLS_ATTR nooutput; /* presence of -u */ +boolean TLS_ATTR canonise; /* presence of -l */ +boolean TLS_ATTR quiet; /* presence of -q */ +boolean TLS_ATTR header; /* presence of -h */ +statsblk TLS_ATTR nauty_stats; +static TLS_ATTR int mindeg,maxdeg,maxn,mine,maxe,mod,res; +#define PRUNEMULT 50 /* bigger -> more even split at greater cost */ +static TLS_ATTR int min_splitlevel,odometer,splitlevel,multiplicity; +static TLS_ATTR graph gcan[MAXN]; + +#define XBIT(i) ((xword)1 << (i)) +#define XPOPCOUNT(x) POPCOUNT(x) +#define XNEXTBIT(x) (WORDSIZE-1-FIRSTBITNZ(x)) /* Assumes non-zero */ + +typedef struct +{ + int ne,dmax; /* values used for xlb,xub calculation */ + int xlb,xub; /* saved bounds on extension degree */ + xword lo,hi; /* work purposes for orbit calculation */ + xword xstart[MAXN+1]; /* index into xset[] for each cardinality */ + xword *xset; /* array of all x-sets in card order */ + xword *xcard; /* cardinalities of all x-sets */ + xword *xinv; /* map from x-set to index in xset */ + xword *xorb; /* min orbit representative */ + xword *xx; /* (-b, -t, -s, -m) candidate x-sets */ + /* note: can be the same as xcard */ + xword xlim; /* number of x-sets in xx[] */ +} leveldata; + +static TLS_ATTR leveldata data[MAXN]; /* data[n] is data for n -> n+1 */ +static TLS_ATTR nauty_counter ecount[1+MAXN*(MAXN-1)/2]; /* counts by number of edges */ +static TLS_ATTR nauty_counter nodes[MAXN]; /* nodes at each level */ + +#ifdef INSTRUMENT +static TLS_ATTR nauty_counter rigidnodes[MAXN],fertilenodes[MAXN]; +static TLS_ATTR nauty_counter a1calls,a1nauty,a1succs; +static TLS_ATTR nauty_counter a2calls,a2nauty,a2uniq,a2succs; +#endif + +/* The numbers below are actual maximum edge counts. + geng works correctly with any upper bounds. + To extend known upper bounds upwards: + (n-1, E) -> (n, E + floor(2*E/(n-2))), + which is done by the procedure findmaxe(). +*/ + +static TLS_ATTR int maxeb[65] = /* max edges for -b */ + {0,0,1,2,4, -1}; +static TLS_ATTR int maxet[65] = /* max edges for -t */ + {0,0,1,2,4, -1}; +static TLS_ATTR int maxef[65] = /* max edges for -f */ + {0,0,1,3,4, 6,7,9,11,13, + 16,18,21,24,27, 30,33,36,39,42, + 46,50,52,56,59, 63,67,71,76,80, + 85,90,92,96,102, 106,110,113,117,122, + 127, -1}; +static TLS_ATTR int maxeft[65] = /* max edges for -ft */ + {0,0,1,2,3, 5,6,8,10,12, + 15,16,18,21,23, 26,28,31,34,38, + 41,44,47,50,54, 57,61,65,68,72, + 76,80,85,87,90, 95,99,104,109,114, + 120,124,129,134,139, 145,150,156,162,168, + 175,176,178, -1}; +static TLS_ATTR int maxebf[65] = /* max edges for -bf */ + {0,0,1,2,3, 4,6,7,9,10, + 12,14,16,18,21, 22,24,26,29,31, + 34,36,39,42,45, 48,52,53,56,58, + 61,64,67,70,74, 77,81,84,88,92, + 96,100,105,106,108, 110,115,118,122,126, + 130,134,138,142,147, 151,156,160,165,170, + 175,180,186,187, -1}; + +#ifdef PLUGIN +#include PLUGIN +#endif + +#ifdef PRUNE +extern int PRUNE(graph*,int,int); +#endif +#ifdef PREPRUNE +extern int PREPRUNE(graph*,int,int); +#endif +#ifdef SUMMARY +extern void SUMMARY(nauty_counter,double); +#endif + +#if defined(PRUNE) || defined(PREPRUNE) +int TLS_ATTR geng_mindeg, geng_maxdeg, geng_mine, geng_maxe, geng_connec; +#endif + +/************************************************************************/ + +#define EXTEND(table,n) ((n) <= 1 ? 0 : (n) == 2 ? 1 : \ + table[(n)-1] + (2*table[(n)-1]/((n)-2))) + +static int +findmaxe(int *table, int n) +/* Extend table to MAXN vertices if necessary, and return table[n]. */ +{ + int i; + + for (i = 0; i <= MAXN && table[i] >= 0; ++i) {} + for ( ; i <= MAXN; ++i) table[i] = EXTEND(table,i); + + return table[n]; +} + +/*********************************************************************/ + +static boolean +isconnected(graph *g, int n) +/* test if g is connected */ +{ + setword seen,expanded,toexpand,allbits; + int i; + + allbits = ALLMASK(n); + + expanded = bit[n-1]; + seen = expanded | g[n-1]; + + while (seen != allbits && (toexpand = (seen & ~expanded))) /* not == */ + { + i = FIRSTBITNZ(toexpand); + expanded |= bit[i]; + seen |= g[i]; + } + + return seen == allbits; +} + +static boolean +connpreprune(graph *g, int n, int maxn) +/* This function speeds up the generation of connected graphs + with not many edges. */ +{ + setword notvisited,queue; + int ne,nc,i; + + if (n == maxn || maxe - maxn >= 5) return 0; + + ne = 0; + for (i = 0; i < n; ++i) ne += POPCOUNT(g[i]); + ne /= 2; + + nc = 0; + notvisited = ALLMASK(n); + + while (notvisited) + { + ++nc; + queue = SWHIBIT(notvisited); + notvisited &= ~queue; + while (queue) + { + TAKEBIT(i,queue); + notvisited &= ~bit[i]; + queue |= g[i] & notvisited; + } + } + + if (ne - n + nc > maxe - maxn + 1) return TRUE; + + return FALSE; +} + +/**********************************************************************/ + +static boolean +isbiconnected(graph *g, int n) +/* test if g is biconnected */ +{ + int sp,v,w; + setword sw; + setword visited; + int numvis,num[MAXN],lp[MAXN],stack[MAXN]; + + if (n <= 2) return FALSE; + + visited = bit[0]; + stack[0] = 0; + num[0] = 0; + lp[0] = 0; + numvis = 1; + sp = 0; + v = 0; + + for (;;) + { + if ((sw = g[v] & ~visited)) /* not "==" */ + { + w = v; + v = FIRSTBITNZ(sw); /* visit next child */ + stack[++sp] = v; + visited |= bit[v]; + lp[v] = num[v] = numvis++; + sw = g[v] & visited & ~bit[w]; + while (sw) + { + w = FIRSTBITNZ(sw); + sw &= ~bit[w]; + if (num[w] < lp[v]) lp[v] = num[w]; + } + } + else + { + w = v; /* back up to parent */ + if (sp <= 1) return numvis == n; + v = stack[--sp]; + if (lp[w] >= num[v]) return FALSE; + if (lp[w] < lp[v]) lp[v] = lp[w]; + } + } +} + +/**********************************************************************/ + +static boolean +distinvar(graph *g, int *invar, int n) +/* make distance invariant + return FALSE if n-1 not maximal else return TRUE */ +{ + int w; + setword workset,frontier; + setword sofar; + int inv,d,v; + + for (v = n-1; v >= 0; --v) + { + inv = 0; + sofar = frontier = bit[v]; + for (d = 1; frontier != 0; ++d) + { + workset = 0; + inv += POPCOUNT(frontier) ^ (0x57 + d); + while (frontier) + { + w = FIRSTBITNZ(frontier); + frontier ^= bit[w]; + workset |= g[w]; + } + frontier = workset & ~sofar; + sofar |= frontier; + } + invar[v] = inv; + if (v < n-1 && inv > invar[n-1]) return FALSE; + } + return TRUE; +} + +/**************************************************************************/ + +static void +makexgraph(graph *g, xword *h, int n) +/* make x-format graph from nauty format graph */ +{ + setword gi; + int i,j; + xword hi; + + for (i = 0; i < n; ++i) + { + hi = 0; + gi = g[i]; + while (gi) + { + j = FIRSTBITNZ(gi); + gi ^= bit[j]; + hi |= XBIT(j); + } + h[i] = hi; + } +} + +/**************************************************************************/ + +static void +make0graph(graph *g, xword *h, int n) +/* make x-format graph without edges */ +{ + int i; + + for (i = 0; i < n; ++i) h[i] = 0; +} + +/**************************************************************************/ + +static void +makebgraph(graph *g, xword *h, int n) +/* make x-format graph of different colour graph */ +{ + setword seen1,seen2,expanded,w; + setword restv; + xword xseen1,xseen2; + int i; + + restv = 0; + for (i = 0; i < n; ++i) restv |= bit[i]; + + seen1 = seen2 = 0; + expanded = 0; + + while (TRUE) + { + if ((w = ((seen1 | seen2) & ~expanded)) == 0) + { + xseen1 = 0; + w = seen1; + while (w) + { + i = FIRSTBITNZ(w); + w ^= bit[i]; + xseen1 |= XBIT(i); + } + xseen2 = 0; + w = seen2; + while (w) + { + i = FIRSTBITNZ(w); + w ^= bit[i]; + xseen2 |= XBIT(i); + } + + w = seen1; + while (w) + { + i = FIRSTBITNZ(w); + w ^= bit[i]; + h[i] = xseen2; + } + w = seen2; + while (w) + { + i = FIRSTBITNZ(w); + w ^= bit[i]; + h[i] = xseen1; + } + + restv &= ~(seen1 | seen2); + if (restv == 0) return; + i = FIRSTBITNZ(restv); + seen1 = bit[i]; + seen2 = 0; + } + else + i = FIRSTBITNZ(w); + + expanded |= bit[i]; + if (bit[i] & seen1) seen2 |= g[i]; + else seen1 |= g[i]; + } +} + +/**************************************************************************/ + +static void +makeb6graph(graph *g, xword *h, int n) +/* make x-format bipartite girth 6 graph */ +{ + setword w,x; + xword hi; + int i,j; + + makebgraph(g,h,n); + + for (i = 0; i < n; ++i) + { + w = g[i]; + x = 0; + while (w) + { + j = FIRSTBITNZ(w); + w ^= bit[j]; + x |= g[j]; + } + x &= ~bit[i]; + hi = h[i]; + while (x) + { + j = FIRSTBITNZ(x); + x ^= bit[j]; + hi |= XBIT(j); + } + h[i] = hi; + } +} + +/**************************************************************************/ + +static void +makesgraph(graph *g, xword *h, int n) +/* make x-format square graph */ +{ + setword w,x; + xword hi; + int i,j; + + for (i = 0; i < n; ++i) + { + w = g[i]; + x = 0; + while (w) + { + j = FIRSTBITNZ(w); + w ^= bit[j]; + x |= g[j]; + } + x &= ~bit[i]; + hi = 0; + while (x) + { + j = FIRSTBITNZ(x); + x ^= bit[j]; + hi |= XBIT(j); + } + h[i] = hi; + } +} + +/**************************************************************************/ + +static void +makeg5graph(graph *g, xword *h, int n) +/* make x-format girth-5 graph */ +{ + setword w,x; + xword hi; + int i,j; + + for (i = 0; i < n; ++i) + { + w = g[i]; + x = g[i]; + while (w) + { + j = FIRSTBITNZ(w); + w ^= bit[j]; + x |= g[j]; + } + x &= ~bit[i]; + hi = 0; + while (x) + { + j = FIRSTBITNZ(x); + x ^= bit[j]; + hi |= XBIT(j); + } + h[i] = hi; + } +} + +/**************************************************************************/ + +static xword +arith(xword a, xword b, xword c) +/* Calculate a*b/c, assuming a*b/c and (c-1)*b are representable integers */ +{ + return (a/c)*b + ((a%c)*b)/c; +} + +/**************************************************************************/ + +static void +makeleveldata(boolean restricted) +/* make the level data for each level */ +{ + long h; + int n,nn; + xword ncj; + leveldata *d; + xword *xcard,*xinv; + xword *xset,xw,nxsets; + xword cw; + xword i,ilast,j; + size_t tttn; + + for (n = 1; n < maxn; ++n) + { + nn = maxdeg <= n ? maxdeg : n; + ncj = nxsets = 1; + for (j = 1; j <= nn; ++j) + { + ncj = arith(ncj,n-j+1,j); + nxsets += ncj; + } + + d = &data[n]; + d->ne = d->dmax = d->xlb = d->xub = -1; + + if (restricted) + { + d->xorb = (xword*) calloc(nxsets,sizeof(xword)); + d->xx = (xword*) calloc(nxsets,sizeof(xword)); + if (d->xorb == NULL || d->xx == NULL) + { + fprintf(stderr, + ">E geng: calloc failed in makeleveldata()\n"); + exit(2); + } + continue; /* <--- NOTE THIS! */ + } + + tttn = (size_t)1 << n; + d->xset = xset = (xword*) calloc(nxsets,sizeof(xword)); + d->xcard = xcard = (xword*) calloc(nxsets,sizeof(xword)); + d->xinv = xinv = (xword*) calloc(tttn,sizeof(xword)); + d->xorb = (xword*) calloc(nxsets,sizeof(xword)); + d->xx = d->xcard; + + if (xset==NULL || xcard==NULL || xinv==NULL || d->xorb==NULL) + { + fprintf(stderr,">E geng: calloc failed in makeleveldata()\n"); + exit(2); + } + + j = 0; + + ilast = (n == WORDSIZE ? ~(setword)0 : XBIT(n)-1); + for (i = 0;; ++i) + { + if ((h = XPOPCOUNT(i)) <= maxdeg) + { + xset[j] = i; + xcard[j] = h; + ++j; + } + if (i == ilast) break; + } + + if (j != nxsets) + { + fprintf(stderr,">E geng: j=" SETWORD_DEC_FORMAT + " nxsets=" SETWORD_DEC_FORMAT "\n", + j,nxsets); + exit(2); + } + + h = 1; + do + h = 3 * h + 1; + while (h < nxsets); + + do /* Shell sort, consider replacing */ + { + for (i = h; i < nxsets; ++i) + { + xw = xset[i]; + cw = xcard[i]; + for (j = i; xcard[j-h] > cw || + (xcard[j-h] == cw && xset[j-h] > xw); ) + { + xset[j] = xset[j-h]; + xcard[j] = xcard[j-h]; + if ((j -= h) < h) break; + } + xset[j] = xw; + xcard[j] = cw; + } + h /= 3; + } + while (h > 0); + + for (i = 0; i < nxsets; ++i) xinv[xset[i]] = i; + + d->xstart[0] = 0; + for (i = 1; i < nxsets; ++i) + if (xcard[i] > xcard[i-1]) d->xstart[xcard[i]] = i; + d->xstart[xcard[nxsets-1]+1] = nxsets; + } +} + +/**************************************************************************/ + +static void +userautomproc(int count, int *p, int *orbits, + int numorbits, int stabvertex, int n) +/* form orbits on powerset of VG + called by nauty; operates on data[n] */ +{ + xword i,j1,j2,moved,pi,pxi; + xword lo,hi; + xword *xorb,*xinv,*xset,w; + + xorb = data[n].xorb; + xset = data[n].xset; + xinv = data[n].xinv; + lo = data[n].lo; + hi = data[n].hi; + + if (count == 1) /* first automorphism */ + for (i = lo; i < hi; ++i) xorb[i] = i; + + moved = 0; + for (i = 0; i < n; ++i) + if (p[i] != i) moved |= XBIT(i); + + for (i = lo; i < hi; ++i) + { + if ((w = xset[i] & moved) == 0) continue; + pxi = xset[i] & ~moved; + while (w) + { + j1 = XNEXTBIT(w); + w ^= XBIT(j1); + pxi |= XBIT(p[j1]); + } + pi = xinv[pxi]; + + j1 = xorb[i]; + while (xorb[j1] != j1) j1 = xorb[j1]; + j2 = xorb[pi]; + while (xorb[j2] != j2) j2 = xorb[j2]; + + if (j1 < j2) xorb[j2] = xorb[i] = xorb[pi] = j1; + else if (j1 > j2) xorb[j1] = xorb[i] = xorb[pi] = j2; + } +} + +/**************************************************************************/ + +static void +userautomprocb(int count, int *p, int *orbits, + int numorbits, int stabvertex, int n) +/* form orbits on powerset of VG + called by nauty; operates on data[n] */ +{ + xword j1,j2,moved,pi,pxi,lo,hi,x; + xword i,*xorb,*xx,w,xlim,xlb; + + xorb = data[n].xorb; + xx = data[n].xx; + xlim = data[n].xlim; + + if (count == 1) /* first automorphism */ + { + j1 = 0; + xlb = data[n].xlb; + + for (i = 0; i < xlim; ++i) + { + x = xx[i]; + if (XPOPCOUNT(x) >= xlb) + { + xx[j1] = x; + xorb[j1] = j1; + ++j1; + } + } + data[n].xlim = xlim = j1; + } + + moved = 0; + for (i = 0; i < n; ++i) + if (p[i] != i) moved |= XBIT(i); + + for (i = 0; i < xlim; ++i) + { + if ((w = xx[i] & moved) == 0) continue; + pxi = xx[i] & ~moved; + while (w) + { + j1 = XNEXTBIT(w); + w ^= XBIT(j1); + pxi |= XBIT(p[j1]); + } + /* pi = position of pxi */ + + lo = 0; + hi = xlim - 1; + + for (;;) + { + pi = (lo + hi) >> 1; + if (xx[pi] == pxi) break; + else if (xx[pi] < pxi) lo = pi + 1; + else hi = pi - 1; + } + + j1 = xorb[i]; + while (xorb[j1] != j1) j1 = xorb[j1]; + j2 = xorb[pi]; + while (xorb[j2] != j2) j2 = xorb[j2]; + + if (j1 < j2) xorb[j2] = xorb[i] = xorb[pi] = j1; + else if (j1 > j2) xorb[j1] = xorb[i] = xorb[pi] = j2; + } +} + +/***************************************************************************** +* * +* refinex(g,lab,ptn,level,numcells,count,active,goodret,code,m,n) is a * +* custom version of refine() which can exit quickly if required. * +* * +* Only use at level==0. * +* goodret : whether to do an early return for code 1 * +* code := -1 for n-1 not max, 0 for maybe, 1 for definite * +* * +*****************************************************************************/ + +static void +refinex(graph *g, int *lab, int *ptn, int level, int *numcells, + int *count, set *active, boolean goodret, int *code, int m, int n) +{ + int i,c1,c2,labc1; + setword x,lact; + int split1,split2,cell1,cell2; + int cnt,bmin,bmax; + set *gptr; + setword workset; + int workperm[MAXN]; + int bucket[MAXN+2]; + + if (n == 1) + { + *code = 1; + return; + } + + *code = 0; + lact = *active; + + while (*numcells < n && lact) + { + TAKEBIT(split1,lact); + + for (split2 = split1; ptn[split2] > 0; ++split2) {} + if (split1 == split2) /* trivial splitting cell */ + { + gptr = GRAPHROW(g,lab[split1],1); + for (cell1 = 0; cell1 < n; cell1 = cell2 + 1) + { + for (cell2 = cell1; ptn[cell2] > 0; ++cell2) {} + if (cell1 == cell2) continue; + + c1 = cell1; + c2 = cell2; + while (c1 <= c2) + { + labc1 = lab[c1]; + if (ISELEMENT1(gptr,labc1)) + ++c1; + else + { + lab[c1] = lab[c2]; + lab[c2] = labc1; + --c2; + } + } + if (c2 >= cell1 && c1 <= cell2) + { + ptn[c2] = 0; + ++*numcells; + lact |= bit[c1]; + } + } + } + + else /* nontrivial splitting cell */ + { + workset = 0; + for (i = split1; i <= split2; ++i) workset |= bit[lab[i]]; + + for (cell1 = 0; cell1 < n; cell1 = cell2 + 1) + { + for (cell2 = cell1; ptn[cell2] > 0; ++cell2) {} + if (cell1 == cell2) continue; + i = cell1; + if ((x = workset & g[lab[i]]) != 0) cnt = POPCOUNT(x); + else cnt = 0; + count[i] = bmin = bmax = cnt; + bucket[cnt] = 1; + while (++i <= cell2) + { + if ((x = workset & g[lab[i]]) != 0) + cnt = POPCOUNT(x); + else + cnt = 0; + + while (bmin > cnt) bucket[--bmin] = 0; + while (bmax < cnt) bucket[++bmax] = 0; + ++bucket[cnt]; + count[i] = cnt; + } + if (bmin == bmax) continue; + c1 = cell1; + for (i = bmin; i <= bmax; ++i) + if (bucket[i]) + { + c2 = c1 + bucket[i]; + bucket[i] = c1; + if (c1 != cell1) + { + lact |= bit[c1]; + ++*numcells; + } + if (c2 <= cell2) ptn[c2-1] = 0; + c1 = c2; + } + for (i = cell1; i <= cell2; ++i) + workperm[bucket[count[i]]++] = lab[i]; + for (i = cell1; i <= cell2; ++i) lab[i] = workperm[i]; + } + } + + if (ptn[n-2] == 0) + { + if (lab[n-1] == n-1) + { + *code = 1; + if (goodret) return; + } + else + { + *code = -1; + return; + } + } + else + { + i = n - 1; + while (TRUE) + { + if (lab[i] == n-1) break; + --i; + if (ptn[i] == 0) + { + *code = -1; + return; + } + } + } + } +} + +/**************************************************************************/ + +static void +makecanon(graph *g, graph *gcan, int n) +/* gcan := canonise(g) */ +{ + int lab[MAXN],ptn[MAXN],orbits[MAXN]; + static TLS_ATTR DEFAULTOPTIONS_GRAPH(options); + setword workspace[50]; + + options.getcanon = TRUE; + + nauty(g,lab,ptn,NULL,orbits,&options,&nauty_stats, + workspace,50,1,n,gcan); +} + +/**************************************************************************/ + +static boolean +hask4(graph *g, int n, int maxn) +/* Return TRUE iff there is a K4 including the last vertex */ +{ + setword gx,w; + int i,j; + + gx = g[n-1]; + while (gx) + { + TAKEBIT(i,gx); + w = g[i] & gx; + while (w) + { + TAKEBIT(j,w); + if ((g[j] & w)) return TRUE; + } + } + return FALSE; +} + +/**************************************************************************/ + +static boolean +hasclaw(graph *g, int n, int maxn) +/* Return TRUE if there is a claw (induced K(1,3)) involving the last vertex */ +{ + int i,j,k; + setword x,y; + + x = g[n-1]; + while (x) + { + TAKEBIT(j,x); + y = x & ~g[j]; + while (y) + { + TAKEBIT(k,y); + if (y & ~g[k]) return TRUE; + } + } + + x = g[n-1]; + while (x) + { + TAKEBIT(i,x); + y = g[i] & ~(bit[n-1]|g[n-1]); + while (y) + { + TAKEBIT(k,y); + if (y & ~g[k]) return TRUE; + } + } + + return FALSE; +} + +static boolean +hasinducedpath(graph *g, int start, setword body, setword last) +/* return TRUE if there is an induced path in g starting at start, + extravertices within body and ending in last. + * {start}, body and last should be disjoint. */ +{ + setword gs,w; + int i; + + gs = g[start]; + if ((gs & last)) return TRUE; + + w = gs & body; + while (w) + { + TAKEBIT(i,w); + if (hasinducedpath(g,i,body&~gs,last&~bit[i]&~gs)) + return TRUE; + } + + return FALSE; +} + +static boolean +notchordal(graph *g, int n, int maxn) +/* g is a graph of order n. Return TRUE if there is a + chordless cycle of length at least 4 that includes + the last vertex. */ +{ + setword all,gn,w,gs; + int v,s; + + all = ALLMASK(n); + gn = g[n-1]; + + while (gn) + { + TAKEBIT(v,gn); + gs = g[v] & ~(bit[n-1]|g[n-1]); + while (gs) + { + TAKEBIT(s,gs); + if (hasinducedpath(g,s,all&~(g[n-1]|g[v]),gn&~g[v])) + return TRUE; + } + } + + return FALSE; +} + +static boolean +notsplit(graph *g, int n, int maxn) +/* g is a graph of order n. Return TRUE if either g or its + complement has a chordless cycle of length at least 4 that + includes the last vertex. */ +{ + graph gc[MAXN]; + setword w; + int i; + + if (notchordal(g,n,maxn)) return TRUE; + + w = ALLMASK(n); + for (i = 0; i < n; ++i) gc[i] = g[i] ^ w ^ bit[i]; + return notchordal(gc,n,maxn); +} + +static boolean +hasinducedoddpath(graph *g, int start, setword body, setword last, boolean parity) +/* return TRUE if there is an induced path of odd length >= 3 in g + starting at start, extravertices within body and ending in last. + {start}, body and last should be disjoint. */ +{ + setword gs,w; + int i; + + gs = g[start]; + if ((gs & last) && parity) return TRUE; + + w = gs & body; + while (w) + { + TAKEBIT(i,w); + if (hasinducedoddpath(g,i,body&~gs,last&~bit[i]&~gs,!parity)) + return TRUE; + } + + return FALSE; +} + +static boolean +oddchordless(graph *g, int n, int maxn) +/* g is a graph of order n. Return TRUE if there is a + chordless cycle of odd length at least 5 that includes + the last vertex. */ +{ + setword all,gn,w,gs; + int v,s; + + all = ALLMASK(n); + gn = g[n-1]; + + while (gn) + { + TAKEBIT(v,gn); + gs = g[v] & ~(bit[n-1]|g[n-1]); + while (gs) + { + TAKEBIT(s,gs); + if (hasinducedoddpath(g,s,all&~(g[n-1]|g[v]),gn&~g[v],FALSE)) + return TRUE; + } + } + + return FALSE; +} + +static boolean +notperfect(graph *g, int n, int maxn) +/* g is a graph of order n. Return TRUE if either g or its + complement has a chordless cycle of odd length at least 5 that + includes the last vertex. I.e., if it is not perfect. */ +{ + graph gc[MAXN]; + setword w; + int i; + + if (oddchordless(g,n,maxn)) return TRUE; + + w = ALLMASK(n); + for (i = 0; i < n; ++i) gc[i] = g[i] ^ w ^ bit[i]; + return oddchordless(gc,n,maxn); +} + +/**************************************************************************/ + +static boolean +accept1(graph *g, int n, xword x, graph *gx, int *deg, boolean *rigid) +/* decide if n in theta(g+x) - version for n+1 < maxn */ +{ + int i; + int lab[MAXN],ptn[MAXN],orbits[MAXN]; + int count[MAXN]; + graph h[MAXN]; + xword xw; + int nx,numcells,code; + int i0,i1,degn; + set active[MAXM]; + statsblk stats; + static TLS_ATTR DEFAULTOPTIONS_GRAPH(options); + setword workspace[50]; + +#ifdef INSTRUMENT + ++a1calls; +#endif + + nx = n + 1; + for (i = 0; i < n; ++i) gx[i] = g[i]; + gx[n] = 0; + deg[n] = degn = XPOPCOUNT(x); + + xw = x; + while (xw) + { + i = XNEXTBIT(xw); + xw ^= XBIT(i); + gx[i] |= bit[n]; + gx[n] |= bit[i]; + ++deg[i]; + } + + if (k4free && hask4(gx,n+1,maxn)) return FALSE; + if (clawfree && hasclaw(gx,n+1,maxn)) return FALSE; +#ifdef PREPRUNE + if (PREPRUNE(gx,n+1,maxn)) return FALSE; +#endif + if (connec == 2 && n+2 == maxn && !isconnected(gx,n+1)) return FALSE; + if (((connec ==2 && n+2 < maxn) || (connec == 1 && n+2 <= maxn)) + && connpreprune(gx,n+1,maxn)) return FALSE; + + i0 = 0; + i1 = n; + for (i = 0; i < nx; ++i) + { + if (deg[i] == degn) lab[i1--] = i; + else lab[i0++] = i; + ptn[i] = 1; + } + ptn[n] = 0; + if (i0 == 0) + { + numcells = 1; + active[0] = bit[0]; + } + else + { + numcells = 2; + active[0] = bit[0] | bit[i1+1]; + ptn[i1] = 0; + } + refinex(gx,lab,ptn,0,&numcells,count,active,FALSE,&code,1,nx); + + if (code < 0) return FALSE; + + if (numcells == nx) + { + *rigid = TRUE; +#ifdef INSTRUMENT + ++a1succs; +#endif + return TRUE; + } + + options.getcanon = TRUE; + options.defaultptn = FALSE; + options.userautomproc = userautomproc; + + active[0] = 0; +#ifdef INSTRUMENT + ++a1nauty; +#endif + nauty(gx,lab,ptn,active,orbits,&options,&stats,workspace,50,1,nx,h); + + if (orbits[lab[n]] == orbits[n]) + { + *rigid = stats.numorbits == nx; +#ifdef INSTRUMENT + ++a1succs; +#endif + return TRUE; + } + else + return FALSE; +} + +/**************************************************************************/ + +static boolean +accept1b(graph *g, int n, xword x, graph *gx, int *deg, boolean *rigid, + void (*makeh)(graph*,xword*,int)) +/* decide if n in theta(g+x) -- version for n+1 < maxn */ +{ + int i,v; + xword z,hv,bitv,ixx; + int lab[MAXN],ptn[MAXN],orbits[MAXN]; + int count[MAXN]; + graph gc[MAXN]; + xword h[MAXN],xw,jxx,kxx,*xx; + int nx,numcells,code; + int i0,i1,degn,xubx; + set active[MAXM]; + statsblk stats; + static TLS_ATTR DEFAULTOPTIONS_GRAPH(options); + setword workspace[50]; + +#ifdef INSTRUMENT + ++a1calls; +#endif + + nx = n + 1; + for (i = 0; i < n; ++i) gx[i] = g[i]; + gx[n] = 0; + deg[n] = degn = XPOPCOUNT(x); + + xw = x; + while (xw) + { + i = XNEXTBIT(xw); + xw ^= XBIT(i); + gx[i] |= bit[n]; + gx[n] |= bit[i]; + ++deg[i]; + } + + if (k4free && hask4(gx,n+1,maxn)) return FALSE; + if (clawfree && hasclaw(gx,n+1,maxn)) return FALSE; +#ifdef PREPRUNE + if (PREPRUNE(gx,n+1,maxn)) return FALSE; +#endif + if (connec == 2 && n+2 == maxn && !isconnected(gx,n+1)) return FALSE; + if (((connec ==2 && n+2 < maxn) || (connec == 1 && n+2 <= maxe)) + && connpreprune(gx,n+1,maxn)) return FALSE; + + i0 = 0; + i1 = n; + for (i = 0; i < nx; ++i) + { + if (deg[i] == degn) lab[i1--] = i; + else lab[i0++] = i; + ptn[i] = 1; + } + ptn[n] = 0; + if (i0 == 0) + { + numcells = 1; + active[0] = bit[0]; + } + else + { + numcells = 2; + active[0] = bit[0] | bit[i1+1]; + ptn[i1] = 0; + } + refinex(gx,lab,ptn,0,&numcells,count,active,FALSE,&code,1,nx); + + if (code < 0) return FALSE; + + (*makeh)(gx,h,nx); + xx = data[nx].xx; + xubx = data[nx].xub; + + xx[0] = 0; + kxx = 1; + for (v = 0; v < nx; ++v) + { + bitv = XBIT(v); + hv = h[v]; + jxx = kxx; + for (ixx = 0; ixx < jxx; ++ixx) + if ((hv & xx[ixx]) == 0) + { + z = xx[ixx] | bitv; + if (XPOPCOUNT(z) <= xubx) xx[kxx++] = z; + } + } + data[nx].xlim = kxx; + + if (numcells == nx) + { + *rigid = TRUE; +#ifdef INSTRUMENT + ++a1succs; +#endif + return TRUE; + } + + options.getcanon = TRUE; + options.defaultptn = FALSE; + options.userautomproc = userautomprocb; + + active[0] = 0; +#ifdef INSTRUMENT + ++a1nauty; +#endif + nauty(gx,lab,ptn,active,orbits,&options,&stats,workspace,50,1,nx,gc); + + if (orbits[lab[n]] == orbits[n]) + { + *rigid = stats.numorbits == nx; +#ifdef INSTRUMENT + ++a1succs; +#endif + return TRUE; + } + else + return FALSE; +} + +/**************************************************************************/ + +static boolean +accept2(graph *g, int n, xword x, graph *gx, int *deg, boolean nuniq) +/* decide if n in theta(g+x) -- version for n+1 == maxn */ +{ + int i; + int lab[MAXN],ptn[MAXN],orbits[MAXN]; + int degx[MAXN],invar[MAXN]; + setword vmax,gv; + int qn,qv; + int count[MAXN]; + xword xw; + int nx,numcells,code; + int degn,i0,i1,j,j0,j1; + set active[MAXM]; + statsblk stats; + static TLS_ATTR DEFAULTOPTIONS_GRAPH(options); + setword workspace[50]; + boolean cheapacc; + +#ifdef INSTRUMENT + ++a2calls; + if (nuniq) ++a2uniq; +#endif + nx = n + 1; + for (i = 0; i < n; ++i) + { + gx[i] = g[i]; + degx[i] = deg[i]; + } + gx[n] = 0; + degx[n] = degn = XPOPCOUNT(x); + + xw = x; + while (xw) + { + i = XNEXTBIT(xw); + xw ^= XBIT(i); + gx[i] |= bit[n]; + gx[n] |= bit[i]; + ++degx[i]; + } + + if (k4free && hask4(gx,n+1,maxn)) return FALSE; + if (clawfree && hasclaw(gx,n+1,maxn)) return FALSE; +#ifdef PREPRUNE + if (PREPRUNE(gx,n+1,maxn)) return FALSE; +#endif + if (connec == 2 && n+2 == maxn && !isconnected(gx,n+1)) return FALSE; + if (((connec ==2 && n+2 < maxn) || (connec == 1 && n+2 <= maxe)) + && connpreprune(gx,n+1,maxn)) return FALSE; + + if (nuniq) + { +#ifdef INSTRUMENT + ++a2succs; +#endif + if (canonise) makecanon(gx,gcan,nx); + return TRUE; + } + + i0 = 0; + i1 = n; + for (i = 0; i < nx; ++i) + { + if (degx[i] == degn) lab[i1--] = i; + else lab[i0++] = i; + ptn[i] = 1; + } + ptn[n] = 0; + if (i0 == 0) + { + numcells = 1; + active[0] = bit[0]; + + if (!distinvar(gx,invar,nx)) return FALSE; + qn = invar[n]; + j0 = 0; + j1 = n; + while (j0 <= j1) + { + j = lab[j0]; + qv = invar[j]; + if (qv < qn) + ++j0; + else + { + lab[j0] = lab[j1]; + lab[j1] = j; + --j1; + } + } + if (j0 > 0) + { + if (j0 == n) + { +#ifdef INSTRUMENT + ++a2succs; +#endif + if (canonise) makecanon(gx,gcan,nx); + return TRUE; + } + ptn[j1] = 0; + ++numcells; + active[0] |= bit[j0]; + } + } + else + { + numcells = 2; + ptn[i1] = 0; + active[0] = bit[0] | bit[i1+1]; + + vmax = 0; + for (i = i1+1; i < nx; ++i) vmax |= bit[lab[i]]; + + gv = gx[n] & vmax; + qn = POPCOUNT(gv); + + j0 = i1+1; + j1 = n; + while (j0 <= j1) + { + j = lab[j0]; + gv = gx[j] & vmax; + qv = POPCOUNT(gv); + if (qv > qn) + return FALSE; + else if (qv < qn) + ++j0; + else + { + lab[j0] = lab[j1]; + lab[j1] = j; + --j1; + } + } + if (j0 > i1+1) + { + if (j0 == n) + { +#ifdef INSTRUMENT + ++a2succs; +#endif + if (canonise) makecanon(gx,gcan,nx); + return TRUE; + } + ptn[j1] = 0; + ++numcells; + active[0] |= bit[j0]; + } + } + + refinex(gx,lab,ptn,0,&numcells,count,active,TRUE,&code,1,nx); + + if (code < 0) return FALSE; + + cheapacc = FALSE; + if (code > 0 || numcells >= nx-4) + cheapacc = TRUE; + else if (numcells == nx-5) + { + for (j1 = nx-2; j1 >= 0 && ptn[j1] > 0; --j1) {} + if (nx - j1 != 5) cheapacc = TRUE; + } + else + { + j1 = nx; + j0 = 0; + for (i1 = 0; i1 < nx; ++i1) + { + --j1; + if (ptn[i1] > 0) + { + ++j0; + while (ptn[++i1] > 0) {} + } + } + if (j1 <= j0 + 1) cheapacc = TRUE; + } + + if (cheapacc) + { +#ifdef INSTRUMENT + ++a2succs; +#endif + if (canonise) makecanon(gx,gcan,nx); + return TRUE; + } + + options.getcanon = TRUE; + options.defaultptn = FALSE; + + active[0] = 0; +#ifdef INSTRUMENT + ++a2nauty; +#endif + nauty(gx,lab,ptn,active,orbits,&options,&stats,workspace,50,1,nx,gcan); + + if (orbits[lab[n]] == orbits[n]) + { +#ifdef INSTRUMENT + ++a2succs; +#endif + if (canonise) makecanon(gx,gcan,nx); + return TRUE; + } + else + return FALSE; +} + +/**************************************************************************/ + +static void +xbnds(int n, int ne, int dmax) +/* find bounds on extension degree; store answer in data[*].* */ +{ + int xlb,xub,d,nn,m,xc; + + xlb = n == 1 ? 0 : (dmax > (2*ne + n - 2)/(n - 1) ? + dmax : (2*ne + n - 2)/(n - 1)); + xub = n < maxdeg ? n : maxdeg; + + for (xc = xub; xc >= xlb; --xc) + { + d = xc; + m = ne + d; + for (nn = n+1; nn < maxn; ++nn) + { + if (d < (2*m + nn - 2)/(nn - 1)) d = (2*m + nn - 2)/(nn - 1); + m += d; + } + if (d > maxdeg || m > maxe) xub = xc - 1; + else break; + } + + if (ne + xlb < mine) + for (xc = xlb; xc <= xub; ++xc) + { + m = ne + xc; + for (nn = n + 1; nn < maxn; ++nn) + m += maxdeg < nn ? maxdeg : nn; + if (m < mine) xlb = xc + 1; + else break; + } + + data[n].ne = ne; + data[n].dmax = dmax; + data[n].xlb = xlb; + data[n].xub = xub; +} + +/**************************************************************************/ + +static void +spaextend(graph *g, int n, int *deg, int ne, boolean rigid, + int xlb, int xub, void (*makeh)(graph*,xword*,int), + struct geng_iterator *iter) +/* extend from n to n+1 -- version for restricted graphs */ +{ + xword x,d,dlow; + xword xlim,*xorb; + int xc,nx,i,j,dmax,dcrit,xlbx,xubx; + graph gx[MAXN]; + xword *xx,ixx; + int degx[MAXN]; + boolean rigidx; + +#ifdef INSTRUMENT + boolean haschild; + + haschild = FALSE; + if (rigid) ++rigidnodes[n]; +#endif + ++nodes[n]; + + nx = n + 1; + dmax = deg[n-1]; + dcrit = mindeg - maxn + n; + d = dlow = 0; + for (i = 0; i < n; ++i) + { + if (deg[i] == dmax) d |= XBIT(i); + if (deg[i] == dcrit) dlow |= XBIT(i); + } + + if (xlb == dmax && XPOPCOUNT(d) + dmax > n) ++xlb; + if (nx == maxn && xlb < mindeg) xlb = mindeg; + if (xlb > xub) return; + + if (splitgraph && notsplit(g,n,maxn)) return; + if (chordal && notchordal(g,n,maxn)) return; + if (perfect && notperfect(g,n,maxn)) return; +#ifdef PRUNE + if (PRUNE(g,n,maxn)) return; +#endif + + xorb = data[n].xorb; + xx = data[n].xx; + xlim = data[n].xlim; + + if (nx == maxn) + { + for (ixx = 0; ixx < xlim; ++ixx) + { + x = xx[ixx]; + xc = XPOPCOUNT(x); + if (xc < xlb || xc > xub) continue; + if ((rigid || xorb[ixx] == ixx) + && (xc > dmax || (xc == dmax && (x & d) == 0)) + && (dlow & ~x) == 0) + { + if (accept2(g,n,x,gx,deg, + xc > dmax+1 || (xc == dmax+1 && (x & d) == 0)) + && (!connec || + (connec==1 && isconnected(gx,nx)) || + (connec>1 && isbiconnected(gx,nx)))) + { + if (splitgraph && notsplit(gx,nx,maxn)) continue; + if (chordal && notchordal(gx,nx,maxn)) continue; + if (perfect && notperfect(gx,nx,maxn)) continue; +#ifdef PRUNE + if (!PRUNE(gx,nx,maxn)) +#endif + { +#ifdef INSTRUMENT + haschild = TRUE; +#endif + ++ecount[ne+xc]; + outproc(outfile,canonise ? gcan : gx,nx, iter); + } + } + } + } + } + else + { + for (ixx = 0; ixx < xlim; ++ixx) + { + if (nx == splitlevel) + { + if (odometer-- != 0) continue; + odometer = mod - 1; + } + x = xx[ixx]; + xc = XPOPCOUNT(x); + if (xc < xlb || xc > xub) continue; + if ((rigid || xorb[ixx] == ixx) + && (xc > dmax || (xc == dmax && (x & d) == 0)) + && (dlow & ~x) == 0) + { + for (j = 0; j < n; ++j) degx[j] = deg[j]; + if (data[nx].ne != ne+xc || data[nx].dmax != xc) + xbnds(nx,ne+xc,xc); + + xlbx = data[nx].xlb; + xubx = data[nx].xub; + if (xlbx <= xubx + && accept1b(g,n,x,gx,degx,&rigidx,makeh)) + { +#ifdef INSTRUMENT + haschild = TRUE; +#endif + spaextend(gx,nx,degx,ne+xc,rigidx,xlbx,xubx,makeh,iter); + } + } + } + if (n == splitlevel - 1 && n >= min_splitlevel + && nodes[n] >= multiplicity) + --splitlevel; + } +#ifdef INSTRUMENT + if (haschild) ++fertilenodes[n]; +#endif +} + +/**************************************************************************/ + +static void +genextend(graph *g, int n, int *deg, int ne, boolean rigid, int xlb, int xub, struct geng_iterator *iter) +/* extend from n to n+1 -- version for general graphs */ +{ + xword x,d,dlow; + xword *xset,*xcard,*xorb; + xword i,imin,imax; + int nx,xc,j,dmax,dcrit; + int xlbx,xubx; + graph gx[MAXN]; + int degx[MAXN]; + boolean rigidx; + +#ifdef INSTRUMENT + boolean haschild; + + haschild = FALSE; + if (rigid) ++rigidnodes[n]; +#endif + ++nodes[n]; + + nx = n + 1; + dmax = deg[n-1]; + dcrit = mindeg - maxn + n; + d = dlow = 0; + for (i = 0; i < n; ++i) + { + if (deg[i] == dmax) d |= XBIT(i); + if (deg[i] == dcrit) dlow |= XBIT(i); + } + + if (xlb == dmax && XPOPCOUNT(d) + dmax > n) ++xlb; + if (nx == maxn && xlb < mindeg) xlb = mindeg; + if (xlb > xub) return; + + if (splitgraph && notsplit(g,n,maxn)) return; + if (chordal && notchordal(g,n,maxn)) return; + if (perfect && notperfect(g,n,maxn)) return; +#ifdef PRUNE + if (PRUNE(g,n,maxn)) return; +#endif + + imin = data[n].xstart[xlb]; + imax = data[n].xstart[xub+1]; + xset = data[n].xset; + xcard = data[n].xcard; + xorb = data[n].xorb; + + if (nx == maxn) + for (i = imin; i < imax; ++i) + { + if (!rigid && xorb[i] != i) continue; + x = xset[i]; + xc = (int)xcard[i]; + if (xc == dmax && (x & d) != 0) continue; + if ((dlow & ~x) != 0) continue; + + if (accept2(g,n,x,gx,deg, + xc > dmax+1 || (xc == dmax+1 && (x & d) == 0))) + if (!connec || (connec==1 && isconnected(gx,nx)) + || (connec>1 && isbiconnected(gx,nx))) + { + if (splitgraph && notsplit(gx,nx,maxn)) continue; + if (chordal && notchordal(gx,nx,maxn)) continue; + if (perfect && notperfect(gx,nx,maxn)) continue; +#ifdef PRUNE + if (!PRUNE(gx,nx,maxn)) +#endif + { +#ifdef INSTRUMENT + haschild = TRUE; +#endif + ++ecount[ne+xc]; + outproc(outfile,canonise ? gcan : gx,nx, iter); + } + } + } + else + for (i = imin; i < imax; ++i) + { + if (!rigid && xorb[i] != i) continue; + x = xset[i]; + xc = (int)xcard[i]; + if (xc == dmax && (x & d) != 0) continue; + if ((dlow & ~x) != 0) continue; + if (nx == splitlevel) + { + if (odometer-- != 0) continue; + odometer = mod - 1; + } + + for (j = 0; j < n; ++j) degx[j] = deg[j]; + if (data[nx].ne != ne+xc || data[nx].dmax != xc) + xbnds(nx,ne+xc,xc); + xlbx = data[nx].xlb; + xubx = data[nx].xub; + if (xlbx > xubx) continue; + + data[nx].lo = data[nx].xstart[xlbx]; + data[nx].hi = data[nx].xstart[xubx+1]; + if (accept1(g,n,x,gx,degx,&rigidx)) + { +#ifdef INSTRUMENT + haschild = TRUE; +#endif + genextend(gx,nx,degx,ne+xc,rigidx,xlbx,xubx,iter); + } + } + + if (n == splitlevel-1 && n >= min_splitlevel + && nodes[n] >= multiplicity) + --splitlevel; +#ifdef INSTRUMENT + if (haschild) ++fertilenodes[n]; +#endif +} + +/**************************************************************************/ +/**************************************************************************/ + +void +geng_main( + int argc, + uint32_t argv1, uint32_t argv2, + uint32_t iter1, uint32_t iter2 +) +{ + // TODO: make macro + size_t *p_argv = (size_t *) (((size_t) argv1) | (((size_t) argv2) << 32)); + char **argv = (char **) *p_argv; + + size_t *p_iter = (size_t *) (((size_t) iter1) | (((size_t) iter2) << 32)); + struct geng_iterator *iter = (struct geng_iterator *) *p_iter; + iter->generation_done = false; + + char *arg; + boolean badargs,gote,gotmr,gotf,gotd,gotD,gotx,gotX; + boolean secret,connec1,connec2,safe,sparse; + char *outfilename,sw; + int i,j,argnum; + graph g[1]; + int tmaxe,deg[1]; + nauty_counter nout; + int splitlevinc; + double t1,t2; + char msg[201]; + + nauty_check(WORDSIZE,1,MAXN,NAUTYVERSIONID); + + badargs = FALSE; + trianglefree = bipartite = squarefree = FALSE; + k4free = splitgraph = chordal = perfect = clawfree = FALSE; + verbose = quiet = FALSE; + nautyformat = graph6 = sparse6 = nooutput = FALSE; + savemem = canonise = header = FALSE; + outfilename = NULL; + secret = safe = FALSE; + connec1 = connec2 = FALSE; + + maxdeg = MAXN; + mindeg = 0; + + gotX = gotx = gotd = gotD = gote = gotmr = gotf = FALSE; + + argnum = 0; + for (j = 1; !badargs && j < argc; ++j) + { + arg = argv[j]; + if (arg[0] == '-' && arg[1] != '\0') + { + ++arg; + while (*arg != '\0') + { + sw = *arg++; + SWBOOLEAN('n',nautyformat) + else SWBOOLEAN('u',nooutput) + else SWBOOLEAN('g',graph6) + else SWBOOLEAN('s',sparse6) + else SWBOOLEAN('t',trianglefree) + else SWBOOLEAN('f',squarefree) + else SWBOOLEAN('k',k4free) + else SWBOOLEAN('S',splitgraph) + else SWBOOLEAN('T',chordal) + else SWBOOLEAN('P',perfect) + else SWBOOLEAN('F',clawfree) + else SWBOOLEAN('b',bipartite) + else SWBOOLEAN('v',verbose) + else SWBOOLEAN('l',canonise) + else SWBOOLEAN('h',header) + else SWBOOLEAN('m',savemem) + else SWBOOLEAN('c',connec1) + else SWBOOLEAN('C',connec2) + else SWBOOLEAN('q',quiet) + else SWBOOLEAN('$',secret) + else SWBOOLEAN('S',safe) + else SWINT('d',gotd,mindeg,"geng -d") + else SWINT('D',gotD,maxdeg,"geng -D") + else SWINT('x',gotx,multiplicity,"geng -x") + else SWINT('X',gotX,splitlevinc,"geng -X") +#ifdef PLUGIN_SWITCHES +PLUGIN_SWITCHES +#endif + else badargs = TRUE; + } + } + else if (arg[0] == '-' && arg[1] == '\0') + gotf = TRUE; + else + { + if (argnum == 0) + { + if (sscanf(arg,"%d",&maxn) != 1) badargs = TRUE; + ++argnum; + } + else if (gotf) + badargs = TRUE; + else + { + if (!gotmr) + { + if (sscanf(arg,"%d/%d",&res,&mod) == 2) + { + gotmr = TRUE; + continue; + } + } + if (!gote) + { + if (sscanf(arg,"%d:%d",&mine,&maxe) == 2 + || sscanf(arg,"%d-%d",&mine,&maxe) == 2) + { + gote = TRUE; + if (maxe == 0 && mine > 0) maxe = MAXN*(MAXN-1)/2; + continue; + } + else if (sscanf(arg,"%d",&mine) == 1) + { + gote = TRUE; + maxe = mine; + continue; + } + } + if (!gotf) + { + outfilename = arg; + gotf = TRUE; + continue; + } + } + } + } + + if (argnum == 0) + badargs = TRUE; + else if (maxn < 1 || maxn > MAXN || maxn > 64) + { + fprintf(stderr,">E geng: n must be in the range 1..%d\n",MAXN); + badargs = TRUE; + } + + if (!gotmr) + { + mod = 1; + res = 0; + } + + if (!gote) + { + mine = 0; + maxe = (maxn*maxn - maxn) / 2; + } + + if (trianglefree || squarefree || bipartite) k4free = FALSE; + if (bipartite) perfect = FALSE; /* bipartite graphs are perfect */ + if (splitgraph) chordal = perfect = FALSE; /* split graphs are chordal */ + if (chordal) perfect = FALSE; /* chordal graphs are perfect */ + if (clawfree && bipartite) + { + clawfree = FALSE; + if (maxdeg > 2) maxdeg = 2; + } + if (chordal && bipartite && maxe >= maxn) maxe = maxn - 1; + if (splitgraph && bipartite && maxe >= maxn) maxe = maxn - 1; + + if (connec1 && mindeg < 1 && maxn > 1) mindeg = 1; + if (connec2 && mindeg < 2 && maxn > 2) mindeg = 2; + if (maxdeg >= maxn) maxdeg = maxn - 1; + if (maxe > maxn*maxdeg / 2) maxe = maxn*maxdeg / 2; + if (maxdeg > maxe) maxdeg = maxe; + if (mindeg < 0) mindeg = 0; + if (mine < (maxn*mindeg+1) / 2) mine = (maxn*mindeg+1) / 2; + if (maxdeg > 2*maxe - mindeg*(maxn-1)) maxdeg = 2*maxe - mindeg*(maxn-1); + + if (connec2) connec = 2; + else if (connec1) connec = 1; + else connec = 0; + if (connec && mine < maxn-1) mine = maxn - 2 + connec; + +#if defined(PRUNE) || defined(PREPRUNE) + geng_mindeg = mindeg; + geng_maxdeg = maxdeg; + geng_mine = mine; + geng_maxe = maxe; + geng_connec = connec; +#endif + + if (!badargs && (mine > maxe || maxe < 0 || maxdeg < 0)) + { + fprintf(stderr, + ">E geng: impossible mine,maxe,mindeg,maxdeg values\n"); + badargs = TRUE; + } + + if (!badargs && (res < 0 || res >= mod)) + { + fprintf(stderr,">E geng: must have 0 <= res < mod\n"); + badargs = TRUE; + } + + if (badargs) + { + fprintf(stderr,">E Bad arguments\n"); + exit(1); + } + + if ((nautyformat!=0) + (graph6!=0) + (sparse6!=0) + (nooutput!=0) > 1) + gt_abort(">E geng: -ungs are incompatible\n"); + +#ifdef PLUGIN_INIT +PLUGIN_INIT +#endif + + for (i = 0; i <= maxe; ++i) ecount[i] = 0; + for (i = 0; i < maxn; ++i) nodes[i] = 0; + + if (nooutput) + outfile = stdout; + else if (!gotf || outfilename == NULL) + { + outfilename = "stdout"; + outfile = stdout; + } + else if ((outfile = fopen(outfilename, + nautyformat ? "wb" : "w")) == NULL) + { + fprintf(stderr, + ">E geng: can't open %s for writing\n",outfilename); + gt_abort(NULL); + } + + if (bipartite) + if (squarefree) tmaxe = findmaxe(maxebf,maxn); + else tmaxe = findmaxe(maxeb,maxn); + else if (trianglefree) + if (squarefree) tmaxe = findmaxe(maxeft,maxn); + else tmaxe = findmaxe(maxet,maxn); + else if (squarefree) tmaxe = findmaxe(maxef,maxn); + else tmaxe = (maxn*maxn - maxn) / 2; + + if (safe) ++tmaxe; + + if (maxe > tmaxe) maxe = tmaxe; + + if (gotx) + { + if (multiplicity < 3 * mod || multiplicity > 999999999) + gt_abort(">E geng: -x value must be in [3*mod,10^9-1]\n"); + } + else + { + multiplicity = PRUNEMULT * mod; + if (multiplicity / PRUNEMULT != mod) + gt_abort(">E geng: mod value is too large\n"); + } + + if (!gotX) splitlevinc = 0; + + if (!quiet) + { + msg[0] = '\0'; + if (strlen(argv[0]) > 75) + fprintf(stderr,">A %s",argv[0]); + else + CATMSG1(">A %s",argv[0]); + + CATMSG7(" -%s%s%s%s%s%s%s", + connec2 ? "C" : connec1 ? "c" : "", + trianglefree ? "t" : "", + squarefree ? "f" : "", + k4free ? "k" : "", + bipartite ? "b" : "", + canonise ? "l" : "", + savemem ? "m" : ""); + if (splitgraph || chordal || perfect || clawfree) + CATMSG4(" -%s%s%s%s", + splitgraph ? "S" : "", + chordal ? "T" : "", + perfect ? "P" : "", + clawfree ? "F" : ""); + if (mod > 1) + CATMSG2("X%dx%d",splitlevinc,multiplicity); + CATMSG4("d%dD%d n=%d e=%d",mindeg,maxdeg,maxn,mine); + if (maxe > mine) CATMSG1("-%d",maxe); + if (mod > 1) CATMSG2(" class=%d/%d",res,mod); + CATMSG0("\n"); + fputs(msg,stderr); + fflush(stderr); + } + + g[0] = 0; + deg[0] = 0; + + sparse = bipartite || squarefree || trianglefree || savemem; + + t1 = CPUTIME; + + if (header) + { + if (sparse6) + { + writeline(outfile,SPARSE6_HEADER); + fflush(outfile); + } + else if (!nautyformat && !nooutput) + { + writeline(outfile,GRAPH6_HEADER); + fflush(outfile); + } + } + + if (maxn == 1) + { + if (res == 0 && connec < 2) + { + ++ecount[0]; + outproc(outfile,g,1,iter); + } + } + else + { + if (maxn > 28 || maxn+4 > 8*sizeof(xword)) + savemem = sparse = TRUE; + if (maxn == maxe+1 && connec) + bipartite = squarefree = sparse = TRUE; /* trees */ + + makeleveldata(sparse); + + if (maxn >= 14 && mod > 1) splitlevel = maxn - 4; + else if (maxn >= 6 && mod > 1) splitlevel = maxn - 3; + else splitlevel = -1; + + if (splitlevel > 0) splitlevel += splitlevinc; + if (splitlevel > maxn - 1) splitlevel = maxn - 1; + if (splitlevel < 3) splitlevel = -1; + + min_splitlevel = 6; + odometer = secret ? -1 : res; + + if (maxe >= mine && + (mod <= 1 || (mod > 1 && (splitlevel > 2 || res == 0)))) + { + xbnds(1,0,0); + if (sparse) + { + data[1].xx[0] = 0; + if (maxdeg > 0) data[1].xx[1] = XBIT(0); + data[1].xlim = data[1].xub + 1; + } + + if (bipartite) + if (squarefree) + spaextend(g,1,deg,0,TRUE, + data[1].xlb,data[1].xub,makeb6graph, + iter); + else + spaextend(g,1,deg,0,TRUE, + data[1].xlb,data[1].xub,makebgraph, + iter); + else if (trianglefree) + if (squarefree) + spaextend(g,1,deg,0,TRUE, + data[1].xlb,data[1].xub,makeg5graph, + iter); + else + spaextend(g,1,deg,0,TRUE, + data[1].xlb,data[1].xub,makexgraph, + iter); + else if (squarefree) + spaextend(g,1,deg,0,TRUE, + data[1].xlb,data[1].xub,makesgraph, + iter); + else if (savemem) + spaextend(g,1,deg,0,TRUE, + data[1].xlb,data[1].xub,make0graph, + iter); + else + genextend(g,1,deg,0,TRUE,data[1].xlb,data[1].xub,iter); + } + } + t2 = CPUTIME; + + nout = 0; + for (i = 0; i <= maxe; ++i) nout += ecount[i]; + + if (verbose) + { + for (i = 0; i <= maxe; ++i) + if (ecount[i] != 0) + { + fprintf(stderr,">C " COUNTER_FMT " graphs with %d edges\n", + ecount[i],i); + } + } + +#ifdef INSTRUMENT + fprintf(stderr,"\n>N node counts\n"); + for (i = 1; i < maxn; ++i) + { + fprintf(stderr," level %2d: ",i); + fprintf(stderr,COUNTER_FMT " (" COUNTER_FMT + " rigid, " COUNTER_FMT " fertile)\n", + nodes[i],rigidnodes[i],fertilenodes[i]); + } + fprintf(stderr,">A1 " COUNTER_FMT " calls to accept1, " + COUNTER_FMT " nauty, " COUNTER_FMT " succeeded\n", + a1calls,a1nauty,a1succs); + fprintf(stderr,">A2 " COUNTER_FMT " calls to accept2, " COUNTER_FMT + " nuniq, "COUNTER_FMT " nauty, " COUNTER_FMT " succeeded\n", + a2calls,a2uniq,a2nauty,a2succs); + fprintf(stderr,"\n"); +#endif + +#ifdef SUMMARY + SUMMARY(nout,t2-t1); +#endif + + if (!quiet) + { + fprintf(stderr,">Z " COUNTER_FMT " graphs generated in %3.2f sec\n", + nout,t2-t1); + } + + for (i = 1; i < maxn; ++i) + if (sparse) + { + free(data[i].xorb); + free(data[i].xx); + } + else + { + free(data[i].xorb); + free(data[i].xset); + free(data[i].xinv); + free(data[i].xcard); + } + iter->generation_done = true; +} diff --git a/graph-checker/nauty/geng.h b/graph-checker/nauty/geng.h new file mode 100644 index 0000000..18ff082 --- /dev/null +++ b/graph-checker/nauty/geng.h @@ -0,0 +1,13 @@ +#ifndef GENG_H +#define GENG_H + +#include <stdint.h> + +void +geng_main( + int argc, + uint32_t argv1, uint32_t argv2, + uint32_t iter1, uint32_t iter2 +); + +#endif // GENG_H diff --git a/graph-checker/nauty/gtnauty.c b/graph-checker/nauty/gtnauty.c new file mode 100644 index 0000000..e6a54ac --- /dev/null +++ b/graph-checker/nauty/gtnauty.c @@ -0,0 +1,830 @@ +/* gtnauty.c : nauty-related routines for gtools. + + Jan 15, 2001 : Increased graph order limit from 2^16-1 to 2^22-1. + Aug 9, 2001 : Added fgroup_inv() and fcanonise_inv() + Sep 15, 2004 : Completed prototypes + Oct 16, 2004 : DEAFULTOPTIONS_GRAPH + Nov 17, 2005 : Added fcanonise_inv_sg() + May 11, 2010 : use sorttemplates.c + Sep 5, 2013 : Unify format processing and remove 2^22 limit + Oct 14, 2017 : Include code for n=0 + Sep 28, 2019 : Define breakcellwt + +**************************************************************************/ + +#include "gtools.h" /* which includes naututil.h, nausparse.h, stdio.h */ + +static boolean issymm; +static set *g0; +static int gm; +static int fuzz2[] = {006532,070236,035523,062437}; +#define FUZZ2(x) ((x) ^ fuzz2[(x)&3]) + +int gt_numorbits; + +#ifdef REFINE +void REFINE(graph*,int*,int*,int,int*,int*,set*,int*,int,int); +#endif + +#define MIN_SCHREIER 33 /* If n is this large, schreier will be + turned on. */ + +/**************************************************************************/ + +#define SORT_OF_SORT 3 +#define SORT_NAME sortwt +#define SORT_TYPE1 int +#define SORT_TYPE2 int +#include "sorttemplates.c" +/* Creates static void sortwt(int *lab, int *wt, int n) */ + +void +setlabptn(int *weight, int *lab, int *ptn, int n) +/* Define (lab,ptn) with cells in increasing order of weight. */ +{ + int i; + + if (n == 0) return; + + for (i = 0; i < n; ++i) lab[i] = i; + + if (weight) + { + sortwt(lab,weight,n); + for (i = 0; i < n-1; ++i) + { + if (weight[lab[i]] != weight[lab[i+1]]) + ptn[i] = 0; + else + ptn[i] = 1; + } + ptn[n-1] = 0; + } + else + { + for (i = 0; i < n-1; ++i) ptn[i] = 1; + ptn[n-1] = 0; + } +} + +int +breakcellwt(int *weight, int *lab, int *ptn, int n1, int n2) +/* Break (lab[n1..n2-1],ptn[n1..n2-1]) into cells in increasing + order of weight. If is assumed that lab[n1..n2-1] are defined + but ptn[n1..n2-1] are ignored. + The weight of lab[i] is weight[lab[i]]. + The number of cells is returned. */ +{ + int i,nc; + + if (n2 <= n1) return 0; + + nc = 1; + if (weight) + { + sortwt(lab+n1,weight,n2-n1); + for (i = n1; i < n2-1; ++i) + { + if (weight[lab[i]] != weight[lab[i+1]]) + { + ptn[i] = 0; + ++nc; + } + else + ptn[i] = 1; + } + ptn[n2-1] = 0; + } + else + { + for (i = n1; i < n2-1; ++i) ptn[i] = 1; + ptn[n2-1] = 0; + } + + return nc; +} + +static int +setlabptnfmt(char *fmt, int *lab, int *ptn, set *active, int m, int n) +/* Define (lab,ptn,active) according to format string. + Return number of cells */ +{ + int i,nc; +#if MAXN + int wt[MAXN]; +#else + DYNALLSTAT(int,wt,wt_sz); + + DYNALLOC1(int,wt,wt_sz,n,"setlabptnfmt"); +#endif + + if (n == 0) return 0; + + EMPTYSET(active,m); + ADDELEMENT(active,0); + nc = 1; + + if (fmt != NULL && fmt[0] != '\0') + { +#if !MAXN + DYNALLOC1(int,wt,wt_sz,n,"setlabptnfmt"); +#endif + for (i = 0; i < n && fmt[i] != '\0'; ++i) + wt[i] = (unsigned char)fmt[i]; + for ( ; i < n; ++i) + wt[i] = 'z'; + + setlabptn(wt,lab,ptn,n); + for (i = 0; i < n-1; ++i) + if (ptn[i] == 0) + { + ++nc; + ADDELEMENT(active,i+1); + } + } + else + { + for (i = 0; i < n; ++i) + { + lab[i] = i; + ptn[i] = 1; + } + ptn[n-1] = 0; + } + + return nc; +} + +/**************************************************************************/ + +static boolean +hasloops(graph *g, int m, int n) +/* Test for loops */ +{ + int i; + set *gi; + + for (i = 0, gi = g; i < n; ++i, gi += m) + if (ISELEMENT(gi,i)) return TRUE; + + return FALSE; +} + +static boolean +hasloops_sg(sparsegraph *sg) +{ + size_t *v,vi,j; + int *d,*e,n,i; + + n = sg->nv; + SG_VDE(sg,v,d,e); + for (i = 0; i < n; ++i) + { + vi = v[i]; + for (j = vi; j < vi + d[i]; ++j) + if (e[vi] == i) return TRUE; + } + + return FALSE; +} + +void +fcanonise(graph *g, int m, int n, graph *h, char *fmt, boolean digraph) +/* canonise g under format fmt; result in h. + fmt is either NULL (for no vertex classification) or is a string + with char-valued colours for the vertices. If it ends early, it + is assumed to continue with the colour 'z' indefinitely. */ +{ +#if MAXN + int lab[MAXN],ptn[MAXN],orbits[MAXN]; + int count[MAXN]; + set active[MAXM]; + setword workspace[24*MAXM]; +#else + DYNALLSTAT(int,lab,lab_sz); + DYNALLSTAT(int,ptn,ptn_sz); + DYNALLSTAT(int,orbits,orbits_sz); + DYNALLSTAT(int,count,count_sz); + DYNALLSTAT(set,active,active_sz); + DYNALLSTAT(setword,workspace,workspace_sz); +#endif + int i; + int numcells,code; + statsblk stats; + static DEFAULTOPTIONS_GRAPH(options); + + if (n == 0) return; + +#if MAXN + if (n > MAXN || m > MAXM) + { + fprintf(stderr,">E fcanonise: m or n too large\n"); + ABORT(">E fcanonise"); + } +#else + DYNALLOC1(int,lab,lab_sz,n,"fcanonise"); + DYNALLOC1(int,ptn,ptn_sz,n,"fcanonise"); + DYNALLOC1(int,orbits,orbits_sz,n,"fcanonise"); + DYNALLOC1(int,count,count_sz,n,"fcanonise"); + DYNALLOC1(set,active,active_sz,m,"fcanonise"); + DYNALLOC1(setword,workspace,workspace_sz,24*m,"fcanonise"); +#endif + + digraph = digraph || hasloops(g,m,n); + + numcells = setlabptnfmt(fmt,lab,ptn,active,m,n); + + if (m == 1) + refine1(g,lab,ptn,0,&numcells,count,active,&code,1,n); + else + refine(g,lab,ptn,0,&numcells,count,active,&code,m,n); + + if (numcells == n || (numcells == n-1 && !digraph)) + { + for (i = 0; i < n; ++i) count[i] = lab[i]; + updatecan(g,h,count,0,m,n); + gt_numorbits = numcells; + } + else + { + options.getcanon = TRUE; + options.defaultptn = FALSE; + options.digraph = digraph; +#ifdef REFINE + options.userrefproc = REFINE; +#endif + if (n >= MIN_SCHREIER) options.schreier = TRUE; + + EMPTYSET(active,m); + nauty(g,lab,ptn,active,orbits,&options,&stats, + workspace,24*m,m,n,h); + gt_numorbits = stats.numorbits; + } +} + +/**************************************************************************/ + +void +fcanonise_inv(graph *g, int m, int n, graph *h, char *fmt, + void (*invarproc)(graph*,int*,int*,int,int,int,int*,int, + boolean,int,int), int mininvarlevel, int maxinvarlevel, + int invararg, boolean digraph) +/* Canonise g under format fmt; result in h. + fmt is either NULL (for no vertex classification) or is a string + with char-valued colours for the vertices. If it ends early, it + is assumed to continue with the colour 'z' indefinitely. + This is like fcanonise() except that a invariant and its arguments + can be specified. */ +{ +#if MAXN + int lab[MAXN],ptn[MAXN],orbits[MAXN]; + int count[MAXN]; + set active[MAXM]; + setword workspace[24*MAXM]; +#else + DYNALLSTAT(int,lab,lab_sz); + DYNALLSTAT(int,ptn,ptn_sz); + DYNALLSTAT(int,orbits,orbits_sz); + DYNALLSTAT(int,count,count_sz); + DYNALLSTAT(set,active,active_sz); + DYNALLSTAT(setword,workspace,workspace_sz); +#endif + int i; + int numcells,code; + statsblk stats; + static DEFAULTOPTIONS_GRAPH(options); + + if (n == 0) return; + +#if MAXN + if (n > MAXN || m > MAXM) + { + fprintf(stderr,">E fcanonise: m or n too large\n"); + ABORT(">E fcanonise"); + } +#else + DYNALLOC1(int,lab,lab_sz,n,"fcanonise"); + DYNALLOC1(int,ptn,ptn_sz,n,"fcanonise"); + DYNALLOC1(int,orbits,orbits_sz,n,"fcanonise"); + DYNALLOC1(int,count,count_sz,n,"fcanonise"); + DYNALLOC1(set,active,active_sz,m,"fcanonise"); + DYNALLOC1(setword,workspace,workspace_sz,24*m,"fcanonise"); +#endif + + numcells = setlabptnfmt(fmt,lab,ptn,active,m,n); + digraph = digraph || hasloops(g,m,n); + + if (m == 1) + refine1(g,lab,ptn,0,&numcells,count,active,&code,1,n); + else + refine(g,lab,ptn,0,&numcells,count,active,&code,m,n); + + if (numcells == n || (!digraph && numcells >= n-1)) + { + for (i = 0; i < n; ++i) count[i] = lab[i]; + updatecan(g,h,count,0,m,n); + gt_numorbits = numcells; + } + else + { + options.getcanon = TRUE; + options.digraph = digraph; + options.defaultptn = FALSE; + if (invarproc) + { + options.invarproc = invarproc; + options.mininvarlevel = mininvarlevel; + options.maxinvarlevel = maxinvarlevel; + options.invararg = invararg; + } +#ifdef REFINE + options.userrefproc = REFINE; +#endif + if (n >= MIN_SCHREIER) options.schreier = TRUE; + + EMPTYSET(active,m); + nauty(g,lab,ptn,active,orbits,&options,&stats,workspace,24*m,m,n,h); + gt_numorbits = stats.numorbits; + } +} + +/**************************************************************************/ + +void +fcanonise_inv_sg(sparsegraph *g, int m, int n, sparsegraph *h, char *fmt, + void (*invarproc)(graph*,int*,int*,int,int,int,int*,int, + boolean,int,int), int mininvarlevel, int maxinvarlevel, + int invararg, boolean digraph) +/* canonise g under format fmt; result in h. + fmt is either NULL (for no vertex classification) or is a string + with char-valued colours for the vertices. If it ends early, it + is assumed to continue with the colour 'z' indefinitely. + This is like fcanonise() except that a invariant and its arguments + can be specified. Version for sparse graphs. */ +{ +#if MAXN + int lab[MAXN],ptn[MAXN],orbits[MAXN]; + int count[MAXN]; + set active[MAXM]; + setword workspace[24*MAXM]; +#else + DYNALLSTAT(int,lab,lab_sz); + DYNALLSTAT(int,ptn,ptn_sz); + DYNALLSTAT(int,orbits,orbits_sz); + DYNALLSTAT(int,count,count_sz); + DYNALLSTAT(set,active,active_sz); + DYNALLSTAT(setword,workspace,workspace_sz); +#endif + int i; + int numcells,code; + statsblk stats; + static DEFAULTOPTIONS_SPARSEGRAPH(options); + + if (n == 0) + { + h->nv = 0; + h->nde = 0; + return; + } + +#if MAXN + if (n > MAXN || m > MAXM) + { + fprintf(stderr,">E fcanonise: m or n too large\n"); + ABORT(">E fcanonise"); + } +#else + DYNALLOC1(int,lab,lab_sz,n,"fcanonise"); + DYNALLOC1(int,ptn,ptn_sz,n,"fcanonise"); + DYNALLOC1(int,orbits,orbits_sz,n,"fcanonise"); + DYNALLOC1(int,count,count_sz,n,"fcanonise"); + DYNALLOC1(set,active,active_sz,m,"fcanonise"); + DYNALLOC1(setword,workspace,workspace_sz,24*m,"fcanonise"); +#endif + + numcells = setlabptnfmt(fmt,lab,ptn,active,m,n); + digraph = digraph || hasloops_sg(g); + + refine_sg((graph*)g,lab,ptn,0,&numcells,count,active,&code,1,n); + + if (numcells == n || (!digraph && numcells == n-1)) + { + for (i = 0; i < n; ++i) count[i] = lab[i]; + updatecan_sg((graph*)g,(graph*)h,count,0,m,n); + gt_numorbits = numcells; + } + else + { + options.getcanon = TRUE; + options.digraph = digraph; + options.defaultptn = FALSE; + if (invarproc) + { + options.invarproc = invarproc; + options.mininvarlevel = mininvarlevel; + options.maxinvarlevel = maxinvarlevel; + options.invararg = invararg; + } +#ifdef REFINE + options.userrefproc = REFINE; +#endif + if (n >= MIN_SCHREIER) options.schreier = TRUE; + + EMPTYSET(active,m); + nauty((graph*)g,lab,ptn,active,orbits,&options,&stats, + workspace,24*m,m,n,(graph*)h); + gt_numorbits = stats.numorbits; + } +} + +/**************************************************************************/ + +void +fgroup(graph *g, int m, int n, char *fmt, int *orbits, int *numorbits) +/* Find the orbits of undirected graph g stabilised by format fmt. + The orbits are put into orbits[] and the number of them into *numorbits + fmt is either NULL (for no vertex classification) or is a string + with char-valued colours for the vertices. If it ends early, it + is assumed to continue with the colour 'z' indefinitely. */ +{ +#if MAXN + int lab[MAXN],ptn[MAXN]; + int count[MAXN]; + set active[MAXM]; + setword workspace[24*MAXM]; +#else + DYNALLSTAT(int,lab,lab_sz); + DYNALLSTAT(int,ptn,ptn_sz); + DYNALLSTAT(int,count,count_sz); + DYNALLSTAT(set,active,active_sz); + DYNALLSTAT(setword,workspace,workspace_sz); +#endif + int i,j; + int orbrep; + int numcells,code; + boolean digraph; + statsblk stats; + static DEFAULTOPTIONS_GRAPH(options); + + if (n == 0) + { + *numorbits = 0; + return; + } + +#if MAXN + if (n > MAXN || m > MAXM) + { + fprintf(stderr,">E fcanonise: m or n too large\n"); + ABORT(">E fcanonise"); + } +#else + DYNALLOC1(int,lab,lab_sz,n,"fcanonise"); + DYNALLOC1(int,ptn,ptn_sz,n,"fcanonise"); + DYNALLOC1(int,count,count_sz,n,"fcanonise"); + DYNALLOC1(set,active,active_sz,m,"fcanonise"); + DYNALLOC1(setword,workspace,workspace_sz,24*m,"fcanonise"); +#endif + + numcells = setlabptnfmt(fmt,lab,ptn,active,m,n); + digraph = hasloops(g,m,n); + + if (m == 1) + refine1(g,lab,ptn,0,&numcells,count,active,&code,1,n); + else + refine(g,lab,ptn,0,&numcells,count,active,&code,m,n); + + if (cheapautom(ptn,0,digraph,n)) + { + for (i = 0; i < n; ) + { + if (ptn[i] == 0) + { + orbits[lab[i]] = lab[i]; + ++i; + } + else + { + orbrep = n; + j = i; + do + { + if (lab[j] < orbrep) orbrep = lab[j]; + } while (ptn[j++] != 0); + + for (; i < j; ++i) orbits[lab[i]] = orbrep; + } + } + *numorbits = gt_numorbits = numcells; + } + else + { + options.getcanon = FALSE; + options.defaultptn = FALSE; + options.digraph = digraph; +#ifdef REFINE + options.userrefproc = REFINE; +#endif + if (n >= MIN_SCHREIER) options.schreier = TRUE; + + EMPTYSET(active,m); + nauty(g,lab,ptn,active,orbits,&options,&stats,workspace,24*m,m,n,NULL); + *numorbits = gt_numorbits = stats.numorbits; + } +} + +/**************************************************************************/ + +void +fgroup_inv(graph *g, int m, int n, char *fmt, int *orbits, int *numorbits, + void (*invarproc)(graph*,int*,int*,int,int,int,int*,int, + boolean,int,int), int mininvarlevel, int maxinvarlevel, int invararg) +/* Find the orbits of undirected graph g stabilised by format fmt. + The orbits are put into orbits[] and the number of them into *numorbits + fmt is either NULL (for no vertex classification) or is a string + with char-valued colours for the vertices. If it ends early, it + is assumed to continue with the colour 'z' indefinitely. + This is like fgroup() except that a invariant and its arguments + can be specified. */ +{ +#if MAXN + int lab[MAXN],ptn[MAXN]; + int count[MAXN]; + set active[MAXM]; + setword workspace[24*MAXM]; +#else + DYNALLSTAT(int,lab,lab_sz); + DYNALLSTAT(int,ptn,ptn_sz); + DYNALLSTAT(int,count,count_sz); + DYNALLSTAT(set,active,active_sz); + DYNALLSTAT(setword,workspace,workspace_sz); +#endif + int i,j; + int orbrep; + boolean digraph; + int numcells,code; + statsblk stats; + static DEFAULTOPTIONS_GRAPH(options); + + if (n == 0) + { + *numorbits = 0; + return; + } + +#if MAXN + if (n > MAXN || m > MAXM) + { + fprintf(stderr,">E fcanonise: m or n too large\n"); + ABORT(">E fcanonise"); + } +#else + DYNALLOC1(int,lab,lab_sz,n,"fcanonise"); + DYNALLOC1(int,ptn,ptn_sz,n,"fcanonise"); + DYNALLOC1(int,count,count_sz,n,"fcanonise"); + DYNALLOC1(set,active,active_sz,m,"fcanonise"); + DYNALLOC1(setword,workspace,workspace_sz,24*m,"fcanonise"); +#endif + + numcells = setlabptnfmt(fmt,lab,ptn,active,m,n); + digraph = hasloops(g,m,n); + + if (m == 1) + refine1(g,lab,ptn,0,&numcells,count,active,&code,1,n); + else + refine(g,lab,ptn,0,&numcells,count,active,&code,m,n); + + if (cheapautom(ptn,0,digraph,n)) + { + for (i = 0; i < n; ) + { + if (ptn[i] == 0) + { + orbits[lab[i]] = lab[i]; + ++i; + } + else + { + orbrep = n; + j = i; + do + { + if (lab[j] < orbrep) orbrep = lab[j]; + } while (ptn[j++] != 0); + + for (; i < j; ++i) orbits[lab[i]] = orbrep; + } + } + *numorbits = gt_numorbits = numcells; + } + else + { + options.getcanon = FALSE; + options.defaultptn = FALSE; + options.digraph = digraph; + if (invarproc) + { + options.invarproc = invarproc; + options.mininvarlevel = mininvarlevel; + options.maxinvarlevel = maxinvarlevel; + options.invararg = invararg; + } +#ifdef REFINE + options.userrefproc = REFINE; +#endif + if (n >= MIN_SCHREIER) options.schreier = TRUE; + + EMPTYSET(active,m); + nauty(g,lab,ptn,active,orbits,&options,&stats,workspace,24*m,m,n,NULL); + *numorbits = gt_numorbits = stats.numorbits; + } +} + +/**************************************************************************/ + +static void +userlevel(int *lab, int *ptn, int level, int *orbits, statsblk *stats, + int tv, int index, int tcellsize, int numcells, int cc, int n) +{ + int i0,i; + + if (level != 2) return; + + issymm = TRUE; + + i0 = nextelement(g0,gm,-1); + if (i0 >= 0) + for (i = i0; (i = nextelement(g0,gm,i)) >= 0;) + if (orbits[i] != i0) + { + issymm = FALSE; + return; + } +} + +/*******************************************************************/ + +/* istransitive(g,m,n,h) + + g is an input undirected graph without loops + m,n of standard meaning. + h is a place to put an output graph. + + If g is transitive, return 1 or 2 and put a canonically labelled + version of g into h. The value is 2 for symmetric graphs, + and 1 for other transitive graphs. + If g is not transitive, return 0. In that case h may or + may not have something in it. +*/ +int +istransitive(graph *g, int m, int n, graph *h) +{ + int i,inv; + set *gw; + short wt; + int d,inv0,v,w; + statsblk stats; + static DEFAULTOPTIONS_GRAPH(options); +#if MAXN + int lab[MAXN],ptn[MAXN],orbits[MAXN]; + long x[MAXN]; + int count[MAXN]; + setword workspace[24*MAXM]; + set workset[MAXM]; + set sofar[MAXM],frontier[MAXM]; +#else + DYNALLSTAT(int,lab,lab_sz); + DYNALLSTAT(int,ptn,ptn_sz); + DYNALLSTAT(int,orbits,orbits_sz); + DYNALLSTAT(int,count,count_sz); + DYNALLSTAT(setword,workspace,workspace_sz); + DYNALLSTAT(set,workset,workset_sz); + DYNALLSTAT(set,sofar,sofar_sz); + DYNALLSTAT(set,frontier,frontier_sz); +#endif + + if (n == 0) return 2; + +#if MAXN + if (m > MAXM || n > MAXN) + { + fprintf(stderr, + ">E istransitive: bad input parameters (n=%d m=%d)\n",n,m); + exit(1); + } +#else + DYNALLOC1(int,lab,lab_sz,n,"istransitive"); + DYNALLOC1(int,ptn,ptn_sz,n,"istransitive"); + DYNALLOC1(int,orbits,orbits_sz,n,"istransitive"); + DYNALLOC1(int,count,count_sz,n,"istransitive"); + DYNALLOC1(setword,workspace,workspace_sz,24*m,"istransitive"); + DYNALLOC1(set,workset,workset_sz,m,"istransitive"); + DYNALLOC1(set,sofar,sofar_sz,m,"istransitive"); + DYNALLOC1(set,frontier,frontier_sz,m,"istransitive"); +#endif + + for (v = 0; v < n; ++v) + { + inv = 0; + EMPTYSET(sofar,m); + ADDELEMENT(sofar,v); + EMPTYSET(frontier,m); + ADDELEMENT(frontier,v); + for (d = 1; d < n; ++d) + { + EMPTYSET(workset,m); + wt = 0; + for (w = -1; (w = nextelement(frontier,m,w)) >= 0;) + { + ++wt; + gw = GRAPHROW(g,w,m); + for (i = m; --i >= 0;) workset[i] |= gw[i]; + } + if (wt == 0) break; + wt += (short)(0x73 ^ d); + wt = (short)FUZZ2(wt); + inv += wt; + for (i = m; --i >= 0;) + { + frontier[i] = workset[i] & ~sofar[i]; + sofar[i] |= frontier[i]; + } + } + if (v == 0) inv0 = inv; + else if (inv != inv0) return 0; + } + + options.getcanon = TRUE; + options.userlevelproc = userlevel; +#ifdef REFINE + options.userrefproc = REFINE; +#endif + if (n >= MIN_SCHREIER) options.schreier = TRUE; + + issymm = TRUE; + g0 = (set*) g; + gm = m; + + nauty(g,lab,ptn,NULL,orbits,&options,&stats,workspace,24*m,m,n,h); + + if (stats.numorbits != 1) return 0; + else if (!issymm) return 1; + else return 2; +} + +/**************************************************************************/ + +void +tg_canonise(graph *g, graph *h, int m, int n) +/* Canonise vertex-transitive graph */ +{ + int i; +#if MAXN + int lab[MAXN],ptn[MAXN],orbits[MAXN]; + set active[MAXM]; + setword workspace[24*MAXM]; +#else + DYNALLSTAT(int,lab,lab_sz); + DYNALLSTAT(int,ptn,ptn_sz); + DYNALLSTAT(int,orbits,orbits_sz); + DYNALLSTAT(set,active,active_sz); + DYNALLSTAT(setword,workspace,workspace_sz); +#endif + statsblk stats; + static DEFAULTOPTIONS_GRAPH(options); + +#if MAXN + if (n > MAXN || m > MAXM) + { + fprintf(stderr,">E tg_canonise: m or n too large\n"); + ABORT(">E tg_canonise"); + } +#else + DYNALLOC1(int,lab,lab_sz,n,"tg_canonise"); + DYNALLOC1(int,ptn,ptn_sz,n,"tg_canonise"); + DYNALLOC1(int,orbits,orbits_sz,n,"tg_canonise"); + DYNALLOC1(set,active,active_sz,m,"tg_canonise"); + DYNALLOC1(setword,workspace,workspace_sz,24*m,"tg_canonise"); +#endif + + if (n == 0) return; + + options.getcanon = TRUE; + options.defaultptn = FALSE; +#ifdef REFINE + options.userrefproc = REFINE; +#endif + + for (i = 0; i < n; ++i) + { + lab[i] = i; + ptn[i] = 1; + } + ptn[0] = ptn[n-1] = 0; + + EMPTYSET(active,m); + ADDELEMENT(active,0); + + if (n >= MIN_SCHREIER) options.schreier = TRUE; + nauty(g,lab,ptn,active,orbits,&options,&stats,workspace,24*m,m,n,h); +} diff --git a/graph-checker/nauty/gtools.c b/graph-checker/nauty/gtools.c new file mode 100644 index 0000000..5341841 --- /dev/null +++ b/graph-checker/nauty/gtools.c @@ -0,0 +1,2924 @@ +/* gtools.c : Common routines for gtools programs. */ +/* Version 4.4, Nov 2022. */ + +/* Todo: size check if MAXN>0; option to free memory */ + +#include "gtools.h" + +#ifndef SEEK_SET +#define SEEK_SET 0 +#define SEEK_CUR 1 +#define SEEK_END 2 +#endif + +TLS_ATTR size_t ogf_linelen; +TLS_ATTR boolean is_pipe; + +#if HAVE_FSEEKO +#define FSEEK_VER fseeko +#define FTELL_VER ftello +#define OFF_T_VER off_t +#else +#if !FTELL_DEC +extern long ftell(FILE*); +extern int fseek(FILE*,long,int); +#endif +#define FSEEK_VER fseek +#define FTELL_VER ftell +#define OFF_T_VER long +#endif + +#if !POPEN_DEC +extern FILE *popen(const char*,const char*); +#endif + +/* + Version 1.1: Fixed sparse6 input for powers of 2. May 9, 1998 + Version 1.2: Added "cmd: ..." option for opengraphfile(). + Fixed readg() bug (could not be invisible). Oct 5, 1998 + Version 1.3: Added "is_pipe". June 20, 2002 + Version 1.4: Stuff for autoconf. August 30, 2002 + Version 1.5: Unlocked stdio for efficiency. October 31, 2004 + Also fwrite() in place of fputs() for writeline(). + Version 1.6: i/o for sparsegraph; use of s6len; improve allocations + Version 1.7: Add stringcounts() + Add very long size code (see formats.txt) + Version 1.8: Add gtools_check() + Version 1.9: Add writepc_sg(), readpc_sg() and readpcle_sg() + Add planar_code options to opengraphfile() + Version 2.4: Add writeec_sg(), readec_sg() (MISSING!) + Add edge_code options to opengraphfile() + Version 2.5: Remove sortints(), not used + Version 2.6: Add sgtog6() and writeg6_sg() + Version 2.7: Add lots of explicit casts. + Fix planar code output for n > 255. + Version 3.0: Procedures for incremental sparse6 format. + Add checkgline() + Version 4.0: Procedures for digraph6 format. + Version 4.1: Made encodegraphsize() external. + Version 4.2: Fixes for null graphs; thanks to Kevin Ryde. + Version 4.4: Use fgets for gtools_getline() as it is faster except for + very small graphs. +*/ + +#define B(i) (1 << ((i)-1)) +#define M(i) ((1 << (i))-1) + +/********************************************************************* +opengraphfile(filename,codetype,assumefixed,position) + opens and positions a file for reading graphs. + + filename = the name of the file to open + (NULL means stdin, assumed already open) + If filename starts with "cmd:", the remainder is taken + to be a command to open a subshell for, using a pipe. + codetype = returns a code for the format. + This is a combination of SPARSE6, GRAPH6, PLANARCODE, + PLANARCODELE, PLANARCODEBE, EDGECODE, DIGRAPH6, + UNKNOWN_TYPE and HAS_HEADER. + If a header is present, that overrides the data. + If there is no header, the first graph is examined. + assumefixed = nonzero if files other than stdin or pipes should be + assumed to be seekable and have equal record sizes. + Ignored if there is a sparse6 header or the first + graph has sparse6 format. The most common example + is that graph6 and digraph6 records have lengths + that depend only on the number of vertices. + position = the number of the record to position to + (the first is number 1; 0 and -NOLIMIT also mean + to position at start). planar_code files can only + be positioned at the start. + + If the file starts with ">", there must be a header. + Otherwise opengraphfile() fails. + + The value returned is a file pointer or NULL. + If assumedfixed is not zero and position > 1, the global variable + ogf_linelen is set to the length (including \n) of the length of the + first record other than the header. + + The global variable is_pipe is set to whether the input file is a pipe. + +**********************************************************************/ + +FILE* +opengraphfile(char *filename, int *codetype, int assumefixed, long position) +{ + FILE *f; + int c,bl,firstc; + long i,l; + OFF_T_VER pos,pos1,pos2; + boolean bad_header; + + is_pipe = FALSE; + + if (filename == NULL) + { + f = stdin; + assumefixed = FALSE; + } + else + { + if (filename[0] == 'c' && filename[1] == 'm' + && filename[2] == 'd' && filename[3] == ':') + { +#if !HAVE_POPEN + gt_abort + (">E The \"cmd:\" option is not available in this version.\n"); +#else + filename += 4; + while (*filename == ' ') ++filename; + f = popen(filename,"r"); +#endif + assumefixed = FALSE; + is_pipe = TRUE; + } + else + f = fopen(filename,"r"); + + if (f == NULL) + { + fprintf(stderr,">E opengraphfile: can't open %s\n",filename); + return NULL; + } + } + + FLOCKFILE(f); + firstc = c = GETC(f); + if (c == EOF) + { + *codetype = GRAPH6; + FUNLOCKFILE(f); + return f; + } + + if (c != '>') + { + *codetype = firstc == ':' ? SPARSE6 : firstc == '&' ? DIGRAPH6 : GRAPH6; + ungetc(c,f); + } + else + { + bad_header = FALSE; + if ((c = GETC(f)) == EOF || c != '>') + bad_header = TRUE; + if (!bad_header && ((c = GETC(f)) == EOF || + (c != 'g' && c != 's' && c != 'p' && c != 'd' && c != 'e'))) + bad_header = TRUE; + if (!bad_header && c == 'g') + { + if ((c = GETC(f)) == EOF || c != 'r' || + (c = GETC(f)) == EOF || c != 'a' || + (c = GETC(f)) == EOF || c != 'p' || + (c = GETC(f)) == EOF || c != 'h' || + (c = GETC(f)) == EOF || c != '6' || + (c = GETC(f)) == EOF || c != '<' || + (c = GETC(f)) == EOF || c != '<') + bad_header = TRUE; + else + *codetype = GRAPH6 | HAS_HEADER; + } + else if (!bad_header && c == 'd') + { + if ((c = GETC(f)) == EOF || c != 'i' || + (c = GETC(f)) == EOF || c != 'g' || + (c = GETC(f)) == EOF || c != 'r' || + (c = GETC(f)) == EOF || c != 'a' || + (c = GETC(f)) == EOF || c != 'p' || + (c = GETC(f)) == EOF || c != 'h' || + (c = GETC(f)) == EOF || c != '6' || + (c = GETC(f)) == EOF || c != '<' || + (c = GETC(f)) == EOF || c != '<') + bad_header = TRUE; + else + *codetype = DIGRAPH6 | HAS_HEADER; + } + else if (!bad_header && c == 'e') + { + if ((c = GETC(f)) == EOF || c != 'd' || + (c = GETC(f)) == EOF || c != 'g' || + (c = GETC(f)) == EOF || c != 'e' || + (c = GETC(f)) == EOF || c != '_' || + (c = GETC(f)) == EOF || c != 'c' || + (c = GETC(f)) == EOF || c != 'o' || + (c = GETC(f)) == EOF || c != 'd' || + (c = GETC(f)) == EOF || c != 'e' || + (c = GETC(f)) == EOF || c != '<' || + (c = GETC(f)) == EOF || c != '<') + bad_header = TRUE; + else + *codetype = EDGECODE | HAS_HEADER; + } + else if (!bad_header && c == 's') + { + if ((c = GETC(f)) == EOF || c != 'p' || + (c = GETC(f)) == EOF || c != 'a' || + (c = GETC(f)) == EOF || c != 'r' || + (c = GETC(f)) == EOF || c != 's' || + (c = GETC(f)) == EOF || c != 'e' || + (c = GETC(f)) == EOF || c != '6' || + (c = GETC(f)) == EOF || c != '<' || + (c = GETC(f)) == EOF || c != '<') + bad_header = TRUE; + else + *codetype = SPARSE6 | HAS_HEADER; + } + else if (!bad_header && c == 'p') + { + if ((c = GETC(f)) == EOF || c != 'l' || + (c = GETC(f)) == EOF || c != 'a' || + (c = GETC(f)) == EOF || c != 'n' || + (c = GETC(f)) == EOF || c != 'a' || + (c = GETC(f)) == EOF || c != 'r' || + (c = GETC(f)) == EOF || c != '_' || + (c = GETC(f)) == EOF || c != 'c' || + (c = GETC(f)) == EOF || c != 'o' || + (c = GETC(f)) == EOF || c != 'd' || + (c = GETC(f)) == EOF || c != 'e') + bad_header = TRUE; + else + { + if ((c = GETC(f)) == EOF) + bad_header = TRUE; + else if (c == ' ') + { + if ((bl = GETC(f)) == EOF || (bl != 'l' && bl != 'b') || + (c = GETC(f)) == EOF || c != 'e' || + (c = GETC(f)) == EOF || c != '<' || + (c = GETC(f)) == EOF || c != '<') + bad_header = TRUE; + else if (bl == 'l') + *codetype = PLANARCODELE | HAS_HEADER; + else + *codetype = PLANARCODEBE | HAS_HEADER; + } + else if (c == '<') + { + if ((c = GETC(f)) == EOF || c != '<') + bad_header = TRUE; + else + *codetype = PLANARCODE | HAS_HEADER; + } + else + bad_header = TRUE; + } + } + + if (bad_header) + { + fprintf(stderr,">E opengraphfile: illegal header in %s\n", + filename == NULL ? "stdin" : filename); + *codetype = UNKNOWN_TYPE | HAS_HEADER; + FUNLOCKFILE(f); + return NULL; + } + } + + if (position <= 1) return f; + + if (*codetype&PLANARCODEANY) + { + fprintf(stderr, + ">E opengraphfile: planar_code files can only be opened at the start\n"); + *codetype = UNKNOWN_TYPE | HAS_HEADER; + FUNLOCKFILE(f); + fclose(f); + return NULL; + } + + if (*codetype&EDGECODE) + { + fprintf(stderr, + ">E opengraphfile: edge_code files can only be opened at the start\n"); + *codetype = UNKNOWN_TYPE | HAS_HEADER; + FUNLOCKFILE(f); + fclose(f); + return NULL; + } + + if (!assumefixed || (*codetype&SPARSE6) || firstc == ':') + { + l = 1; + while ((c = GETC(f)) != EOF) + { + if (c == '\n') + { + ++l; + if (l == position) break; + } + } + if (l == position) return f; + + fprintf(stderr, + ">E opengraphfile: can't find line %ld in %s\n",position, + filename == NULL ? "stdin" : filename); + return NULL; + } + else + { + pos1 = FTELL_VER(f); + if (pos1 < 0) + { + fprintf(stderr,">E opengraphfile: error on first ftell\n"); + return NULL; + } + + for (i = 1; (c = GETC(f)) != EOF && c != '\n'; ++i) {} + ogf_linelen = i; + + if (c == EOF) + { + fprintf(stderr, + ">E opengraphfile: required record no present\n"); + FUNLOCKFILE(f); + return NULL; + } + + pos2 = FTELL_VER(f); + if (pos2 < 0) + { + fprintf(stderr,">E opengraphfile: error on second ftell\n"); + return NULL; + } + + pos = pos1 + (position-1)*(pos2-pos1); + if (FSEEK_VER(f,pos,SEEK_SET) < 0) + { + fprintf(stderr,">E opengraphfile: seek failed\n"); + return NULL; + } + } + + FUNLOCKFILE(f); + return f; +} + +/*********************************************************************/ + +void +writeline(FILE *f, char *s) +/* write a line with error checking */ +/* \n is not appended automatically */ +{ + size_t slen; + + slen = strlen(s); + + if (fwrite(s,1,slen,f) != slen || ferror(f)) + gt_abort(">E writeline : error on writing\n"); +} + +/*********************************************************************/ +/* This function used to be called getline(), but this was changed due + to too much confusion with the GNU function of that name. +*/ + +char* +gtools_getline(FILE *f) /* read a line with error checking */ +/* includes \n (if present) and \0. Immediate EOF causes NULL return. */ +{ + DYNALLSTAT(char,s,s_sz); + int c; + size_t i; + boolean eof; + + DYNALLOC1(char,s,s_sz,5000,"gtools_getline"); + + FLOCKFILE(f); + i = 0; + eof = FALSE; + for (;;) + { + if (fgets(s+i,s_sz-i-4,f) == NULL) + { + if (feof(f)) eof = TRUE; + else gt_abort(">E file error when reading\n"); + } + else + i += strlen(s+i); + + if (eof || (i > 0 && s[i-1] == '\n')) break; + if (i >= s_sz-5) + DYNREALLOC(char,s,s_sz,3*(s_sz/2)+10000,"gtools_getline"); + } + FUNLOCKFILE(f); + + if (i == 0 && eof) return NULL; + if (i == 0 || (i > 0 && s[i-1] != '\n')) s[i++] = '\n'; + s[i] = '\0'; + + return s; +} + +#if 0 +char* +gtools_getline(FILE *f) /* read a line with error checking */ +/* includes \n (if present) and \0. Immediate EOF causes NULL return. */ +{ + DYNALLSTAT(char,s,s_sz); + int c; + long i; + + DYNALLOC1(char,s,s_sz,5000,"gtools_getline"); + + FLOCKFILE(f); + i = 0; + while ((c = GETC(f)) != EOF && c != '\n') + { + if (i == s_sz-3) + DYNREALLOC(char,s,s_sz,3*(s_sz/2)+10000,"gtools_getline"); + s[i++] = (char)c; + } + FUNLOCKFILE(f); + + if (i == 0 && c == EOF) return NULL; + + if (c == '\n') s[i++] = '\n'; + s[i] = '\0'; + return s; +} +#endif + +/****************************************************************************/ + +char* +getecline(FILE *f) /* read an edge_code line */ +/* No trailing \n or \0 is added. Immediate EOF causes NULL return. */ +{ + size_t headsize,bodysize; + int sizesize,edgesize; + int c1,c,i; + DYNALLSTAT(unsigned char,s,s_sz); + + FLOCKFILE(f); + if ((c1 = GETC(f)) == EOF) return NULL; + + if (c1 > 0) + { + bodysize = c1; + edgesize = 1; + headsize = 1; + } + else + { + if ((c = GETC(f)) == EOF) + gt_abort(">E Incomplete edge_code line\n"); + else + { + sizesize = c >> 4; + edgesize = c & 0xF; + bodysize = 0; + for (i = 0; i < sizesize; ++i) + { + if ((c = GETC(f)) == EOF) + gt_abort(">E Incomplete edge_code line\n"); + else + bodysize = (bodysize << 8) + c; + } + headsize = 2 + sizesize; + } + } + + DYNALLOC1(unsigned char,s,s_sz,headsize+bodysize,"getecline"); + + s[0] = (unsigned char)c1; + if (c1 == 0) + { + s[1] = (char)((sizesize << 4) + edgesize); + for (i = 0; i < sizesize; ++i) + s[headsize-1-i] = (bodysize >> 8*i) & 0xFF; + } + + if (bodysize > 0 && fread(s+headsize,bodysize,1,f) != bodysize) + gt_abort(">E Incomplete edge_code line\n"); + + FUNLOCKFILE(f); + return (char*)s; +} + +int +graphsize(char *s) +/* Get size of graph out of graph6, digraph6 or sparse6 string. */ +{ + char *p; + int n; + + if (s[0] == ':' || s[0] == '&') p = s+1; + else p = s; + n = *p++ - BIAS6; + + if (n > SMALLN) + { + n = *p++ - BIAS6; + if (n > SMALLN) + { + n = *p++ - BIAS6; + n = (n << 6) | (*p++ - BIAS6); + n = (n << 6) | (*p++ - BIAS6); + n = (n << 6) | (*p++ - BIAS6); + n = (n << 6) | (*p++ - BIAS6); + n = (n << 6) | (*p++ - BIAS6); + } + else + { + n = (n << 6) | (*p++ - BIAS6); + n = (n << 6) | (*p++ - BIAS6); + } + } + return n; +} + +/****************************************************************************/ + +void +encodegraphsize(int n, char **pp) +/* Encode the size n in a string starting at **p, and reset **p + to point to the character after the size */ +{ + char *p; + + p = *pp; + if (n <= SMALLN) + *p++ = (char)(BIAS6 + n); + else if (n <= SMALLISHN) + { + *p++ = MAXBYTE; + *p++ = (char)(BIAS6 + (n >> 12)); + *p++ = (char)(BIAS6 + ((n >> 6) & C6MASK)); + *p++ = (char)(BIAS6 + (n & C6MASK)); + } + else + { + *p++ = MAXBYTE; + *p++ = MAXBYTE; + *p++ = (char)(BIAS6 + (n >> 30)); + *p++ = (char)(BIAS6 + ((n >> 24) & C6MASK)); + *p++ = (char)(BIAS6 + ((n >> 18) & C6MASK)); + *p++ = (char)(BIAS6 + ((n >> 12) & C6MASK)); + *p++ = (char)(BIAS6 + ((n >> 6) & C6MASK)); + *p++ = (char)(BIAS6 + (n & C6MASK)); + } + + *pp = p; +} + +/****************************************************************************/ + +void +stringcounts(char *s, int *pn, size_t *pe) +/* Determine number of edges of graph6, digraph6 or sparse6 string */ +{ + char *p; + int i,j,k,x,nb,v,n,need; + size_t count; + boolean done; + + n = graphsize(s); + *pn = n; + + p = s + (s[0] == ':' || s[0] == '&') + SIZELEN(n); + + if (s[0] == ':') /* sparse6 */ + { + count = 0; + + for (i = n-1, nb = 0; i > 0 ; i >>= 1, ++nb) {} + k = 0; + v = 0; + done = FALSE; + while (!done) + { + if (k == 0) + { + x = *(p++); + if (x == '\n' || x == '\0') + { + done = TRUE; continue; + } + else + { + x -= BIAS6; k = 6; + } + } + if ((x & B(k))) ++v; + --k; + + need = nb; + j = 0; + while (need > 0 && !done) + { + if (k == 0) + { + x = *(p++); + if (x == '\n' || x == '\0') + { + done = TRUE; continue; + } + else + { + x -= BIAS6; k = 6; + } + } + if (need >= k) + { + j = (j << k) | (x & M(k)); + need -= k; k = 0; + } + else + { + k -= need; + j = (j << need) | ((x >> k) & M(need)); + need = 0; + } + } + if (done) continue; + + if (j > v) + v = j; + else if (v < n) + ++count; + } + } + else /* graph6 or digraph6 */ + { + count = 0; + for (; *p != '\n' && *p != '\0'; ++p) + count += bytecount[*p - BIAS6]; + } + + *pe = count; +} + +/****************************************************************************/ + +void +stringtograph(char *s, graph *g, int m) +/* Convert string (graph6, digraph6 or sparse6 format) to graph. */ +/* Assumes g is big enough to hold it. */ +{ + char *p; + int n,i,j,k,v,x,nb,need; + size_t ii; + set *gi,*gj; + boolean done; + + n = graphsize(s); + if (n == 0) return; + + p = s + (s[0] == ':' || s[0] == '&') + SIZELEN(n); + + if (TIMESWORDSIZE(m) < n) + gt_abort(">E stringtograph: impossible m value\n"); + + for (ii = m*(size_t)n; --ii > 0;) g[ii] = 0; + g[0] = 0; + + if (s[0] != ':' && s[0] != '&') /* graph6 format */ + { + k = 1; + for (j = 1; j < n; ++j) + { + gj = GRAPHROW(g,j,m); + + for (i = 0; i < j; ++i) + { + if (--k == 0) + { + k = 6; + x = *(p++) - BIAS6; + } + + if ((x & TOPBIT6)) + { + gi = GRAPHROW(g,i,m); + ADDELEMENT(gi,j); + ADDELEMENT(gj,i); + } + x <<= 1; + } + } + } + else if (s[0] == '&') + { + k = 1; + for (i = 0; i < n; ++i) + { + gi = GRAPHROW(g,i,m); + + for (j = 0; j < n; ++j) + { + if (--k == 0) + { + k = 6; + x = *(p++) - BIAS6; + } + + if ((x & TOPBIT6)) + { + ADDELEMENT(gi,j); + } + x <<= 1; + } + } + } + else /* sparse6 format */ + { + for (i = n-1, nb = 0; i > 0 ; i >>= 1, ++nb) {} + + k = 0; + v = 0; + done = FALSE; + while (!done) + { + if (k == 0) + { + x = *(p++); + if (x == '\n' || x == '\0') + { + done = TRUE; continue; + } + else + { + x -= BIAS6; k = 6; + } + } + if ((x & B(k))) ++v; + --k; + + need = nb; + j = 0; + while (need > 0 && !done) + { + if (k == 0) + { + x = *(p++); + if (x == '\n' || x == '\0') + { + done = TRUE; continue; + } + else + { + x -= BIAS6; k = 6; + } + } + if (need >= k) + { + j = (j << k) | (x & M(k)); + need -= k; k = 0; + } + else + { + k -= need; + j = (j << need) | ((x >> k) & M(need)); + need = 0; + } + } + if (done) continue; + + if (j > v) + v = j; + else if (v < n) + { + ADDELEMENT(GRAPHROW(g,v,m),j); + ADDELEMENT(GRAPHROW(g,j,m),v); + } + } + } +} + +/****************************************************************************/ + +void +stringtograph_inc(char *s, graph *g, int m, + graph *prevg, int prevn) +/* Convert string (graph6, digraph6 or sparse6 format) to graph, + allowing incremental sparse6 format with a prior graph assumed + to have matching m,n values. + If prevg != NULL and type is is6, use prevg as prior graph. + Assumes g is big enough to hold it. + *digraph is set according to the graph type. +*/ +{ + char *p; + int n,i,j,k,v,x,nb,need; + size_t ii; + set *gi,*gj; + boolean done; + + if (s[0] == ';' && !prevg) + gt_abort(">E stringtograph_inc missing prior graph\n"); + + if (s[0] == ';') + { + n = prevn; + if (n == 0) return; + p = s + 1; + for (ii = m*(size_t)n; --ii > 0;) g[ii] = prevg[ii]; + g[0] = prevg[0]; + } + else + { + n = graphsize(s); + if (n == 0) return; + p = s + (s[0] == ':' || s[0] == '&') + SIZELEN(n); + for (ii = m*(size_t)n; --ii > 0;) g[ii] = 0; + g[0] = 0; + } + + if (TIMESWORDSIZE(m) < n) + gt_abort(">E stringtograph_inc: impossible m value\n"); + + if (s[0] != ':' && s[0] != ';' && s[0] != '&') /* graph6 format */ + { + k = 1; + for (j = 1; j < n; ++j) + { + gj = GRAPHROW(g,j,m); + + for (i = 0; i < j; ++i) + { + if (--k == 0) + { + k = 6; + x = *(p++) - BIAS6; + } + + if ((x & TOPBIT6)) + { + gi = GRAPHROW(g,i,m); + FLIPELEMENT(gi,j); + if (i != j) FLIPELEMENT(gj,i); + } + x <<= 1; + } + } + } + else if (s[0] == '&') /* digraph6 format */ + { + k = 1; + for (j = 0; j < n; ++j) + { + gj = GRAPHROW(g,j,m); + + for (i = 0; i < n; ++i) + { + if (--k == 0) + { + k = 6; + x = *(p++) - BIAS6; + } + + if ((x & TOPBIT6)) + { + FLIPELEMENT(gj,i); + } + x <<= 1; + } + } + } + else /* sparse6 format */ + { + for (i = n-1, nb = 0; i != 0 ; i >>= 1, ++nb) {} + + k = 0; + v = 0; + done = FALSE; + while (!done) + { + if (k == 0) + { + x = *(p++); + if (x == '\n' || x == '\0') + { + done = TRUE; continue; + } + else + { + x -= BIAS6; k = 6; + } + } + if ((x & B(k))) ++v; + --k; + + need = nb; + j = 0; + while (need > 0 && !done) + { + if (k == 0) + { + x = *(p++); + if (x == '\n' || x == '\0') + { + done = TRUE; continue; + } + else + { + x -= BIAS6; k = 6; + } + } + if (need >= k) + { + j = (j << k) | (x & M(k)); + need -= k; k = 0; + } + else + { + k -= need; + j = (j << need) | ((x >> k) & M(need)); + need = 0; + } + } + if (done) continue; + + if (j > v) + v = j; + else if (v < n) + { + FLIPELEMENT(GRAPHROW(g,v,m),j); + if (j != v) FLIPELEMENT(GRAPHROW(g,j,m),v); + } + } + } +} + +/***********************************************************************/ + +graph* /* read graph into nauty format */ +readgg(FILE *f, graph *g, int reqm, int *pm, int *pn, boolean *digraph) +/* graph6, digraph6 and sparse6 formats are supported + f = an open file + g = place to put the answer (NULL for dynamic allocation) + reqm = the requested value of m (0 => compute from n) + *pm = the actual value of m + *pn = the value of n + *digraph = whether the input is a digraph +*/ +{ + char *s,*p; + int m,n; + + if ((readg_line = gtools_getline(f)) == NULL) return NULL; + + s = readg_line; + if (s[0] == ':') + { + readg_code = SPARSE6; + *digraph = FALSE; + p = s + 1; + } + else if (s[0] == '&') + { + readg_code = DIGRAPH6; + *digraph = TRUE; + p = s + 1; + } + else + { + readg_code = GRAPH6; + *digraph = FALSE; + p = s; + } + + while (*p >= BIAS6 && *p <= MAXBYTE) + ++p; + if (*p == '\0') + gt_abort(">E readgg: missing newline\n"); + else if (*p != '\n') + gt_abort(">E readgg: illegal character\n"); + + n = graphsize(s); + if (readg_code == GRAPH6 && p - s != G6LEN(n)) + gt_abort(">E readgg: truncated graph6 line\n"); + if (readg_code == DIGRAPH6 && p - s != D6LEN(n)) + gt_abort(">E readgg: truncated digraph6 line\n"); + + if (reqm > 0 && TIMESWORDSIZE(reqm) < n) + gt_abort(">E readgg: reqm too small\n"); + else if (reqm > 0) + m = reqm; + else + m = (n + WORDSIZE - 1) / WORDSIZE; + + if (g == NULL) + { + if ((g = (graph*)ALLOCS(n,m*sizeof(graph))) == NULL) + gt_abort(">E readgg: malloc failed\n"); + } + + *pn = n; + *pm = m; + + stringtograph(s,g,m); + return g; +} + +/***********************************************************************/ + +graph* /* read undirected graph into nauty format */ +readg(FILE *f, graph *g, int reqm, int *pm, int *pn) +/* graph6 and sparse6 formats are supported + f = an open file + g = place to put the answer (NULL for dynamic allocation) + reqm = the requested value of m (0 => compute from n) + *pm = the actual value of m + *pn = the value of n + + Only allows undirected graphs. +*/ +{ + boolean digraph; + graph *gg; + + gg = readgg(f,g,reqm,pm,pn,&digraph); + + if (!gg) return NULL; + if (digraph) + gt_abort(">E readg() doesn't know digraphs; use readgg()\n"); + return gg; +} + +/***********************************************************************/ + +int +checkgline(char *s) +/* Check if s[0..] appears to be a graph input line. A complete check + is not performed. Note that graph input lines must end with \n. + The value returned is 0 if no errors are found, otherwise: + 1 = missing newline + 2 = illegal character + 3 = graph6 or digraph6 line with wrong length +*/ +{ + char *p; + int n,t; + + if (s[0] == ':' || s[0] == ';') + { + t = SPARSE6; + p = s + 1; + } + else if (s[0] == '&') + { + t = DIGRAPH6; + p = s + 1; + } + else + { + t = GRAPH6; + p = s; + } + + while (*p >= BIAS6 && *p <= MAXBYTE) + ++p; + if (*p == '\0') + return 1; + else if (*p != '\n') + return 2; + + if (t == GRAPH6) + { + n = graphsize(s); + if (p - s != G6LEN(n)) return 3; + } + + if (t == DIGRAPH6) + { + n = graphsize(s); + if (p - s != D6LEN(n)) return 3; + } + + return 0; +} + +/***********************************************************************/ + +graph* /* read graph into nauty format */ +readgg_inc(FILE *f, graph *g, int reqm, int *pm, int *pn, + graph *prevg, int prevm, int prevn, boolean *digraph) +/* graph6, digraph6 and sparse6 formats are supported + f = an open file + g = place to put the answer (NULL for dynamic allocation) + reqm = the requested value of m (0 => compute from n) + This is ignored for an incremental input. + *pm = the actual value of m + *pn = the value of n + *digraph = whether the input is a digraph + If prevg!=NULL, it is a prior graph for use in case the next + input is a sparse6 increment. +*/ +{ + char *s,*p; + int m,n; + + if ((readg_line = gtools_getline(f)) == NULL) return NULL; + + s = readg_line; + if (s[0] == ':') + { + readg_code = SPARSE6; + *digraph = FALSE; + p = s + 1; + } + else if (s[0] == ';') + { + readg_code = INCSPARSE6; + *digraph = FALSE; + p = s + 1; + } + else if (s[0] == '&') + { + readg_code = DIGRAPH6; + *digraph = TRUE; + p = s + 1; + } + else + { + readg_code = GRAPH6; + *digraph = FALSE; + p = s; + } + + while (*p >= BIAS6 && *p <= MAXBYTE) + ++p; + if (*p == '\0') + gt_abort(">E readg_inc: missing newline\n"); + else if (*p != '\n') + gt_abort(">E readg_inc: illegal character\n"); + + if (readg_code == INCSPARSE6) + { + if (prevg == NULL) gt_abort(">E readg_inc: missing prior\n"); + n = prevn; + m = prevm; + } + else + { + n = graphsize(s); + if (readg_code == GRAPH6 && p - s != G6LEN(n)) + gt_abort(">E readg_inc: truncated graph6 line\n"); + if (readg_code == DIGRAPH6 && p - s != D6LEN(n)) + gt_abort(">E readg_inc: truncated digraph6 line\n"); + + if (reqm > 0 && TIMESWORDSIZE(reqm) < n) + gt_abort(">E readg_inc: reqm too small\n"); + else if (reqm > 0) + m = reqm; + else + m = SETWORDSNEEDED(n); + } + + if (g == NULL) + { + if ((g = (graph*)ALLOCS(n,m*sizeof(graph))) == NULL) + gt_abort(">E readg_inc: malloc failed\n"); + } + + *pn = n; + *pm = m; + + stringtograph_inc(s,g,m,prevg,prevn); + + return g; +} + +/***********************************************************************/ + +graph* /* read undirected graph into nauty format */ +readg_inc(FILE *f, graph *g, int reqm, int *pm, int *pn, + graph *prevg, int prevm, int prevn) +/* graph6 and sparse6 formats are supported + f = an open file + g = place to put the answer (NULL for dynamic allocation) + reqm = the requested value of m (0 => compute from n) + This is ignored for an incremental input. + *pm = the actual value of m + *pn = the value of n + *digraph = whether the input is a digraph + If prevg!=NULL, it is a prior graph for use in case the next + input is a sparse6 increment. +*/ +{ + boolean digraph; + graph *gg; + + gg = readgg_inc(f,g,reqm,pm,pn,prevg,prevm,prevn,&digraph); + + if (!gg) return NULL; + if (digraph) + gt_abort(">E readg_inc() doesn't all digraphs; use readgg_inc()\n"); + return gg; +} + +/****************************************************************************/ + +void +stringtosparsegraph(char *s, sparsegraph *sg, int *nloops) +/* Convert string (graph6, digraph6 or sparse6 format) + * to sparse graph. + * Assumes sg exists and is initialised + * Also returns the number of loops */ +{ + char *p,*q; + int n,nde,i,j,k,vv,x,nb,need; + int *d,*e; + size_t *v; + int loops; + boolean done; + + n = graphsize(s); + + q = s + (s[0] == ':' || s[0] == '&') + SIZELEN(n); + + sg->nv = n; + + DYNALLOC1(size_t,sg->v,sg->vlen,n,"stringtosparsegraph"); + DYNALLOC1(int,sg->d,sg->dlen,n,"stringtosparsegraph"); + + v = sg->v; + d = sg->d; + for (i = 0; i < n; ++i) d[i] = 0; + + if (s[0] != ':' && s[0] != '&') /* graph6 format */ + { + p = q; + k = 1; + for (j = 1; j < n; ++j) + { + for (i = 0; i < j; ++i) + { + if (--k == 0) + { + k = 6; + x = *(p++) - BIAS6; + } + + if ((x & TOPBIT6)) + { + d[i]++; + d[j]++; + } + x <<= 1; + } + } + + nde = 0; + for (i = 0; i < n; ++i) + { + v[i] = nde; nde += d[i]; d[i] = 0; + } + sg->nde = nde; + DYNALLOC1(int,sg->e,sg->elen,nde,"stringtosparsegraph"); + e = sg->e; + + p = q; + k = 1; + + for (j = 1; j < n; ++j) + { + for (i = 0; i < j; ++i) + { + if (--k == 0) + { + k = 6; + x = *(p++) - BIAS6; + } + + if ((x & TOPBIT6)) + { + e[v[i]+d[i]++] = j; + e[v[j]+d[j]++] = i; + } + x <<= 1; + } + } + + *nloops = 0; + } + else if (s[0] == '&') /* digraph6 */ + { + p = q; + k = 1; + for (j = 0; j < n; ++j) + { + for (i = 0; i < n; ++i) + { + if (--k == 0) + { + k = 6; + x = *(p++) - BIAS6; + } + + if ((x & TOPBIT6)) + d[j]++; + x <<= 1; + } + } + + nde = 0; + for (i = 0; i < n; ++i) + { + v[i] = nde; nde += d[i]; d[i] = 0; + } + sg->nde = nde; + DYNALLOC1(int,sg->e,sg->elen,nde,"stringtosparsegraph"); + e = sg->e; + + p = q; + k = 1; + + *nloops = 0; + for (j = 0; j < n; ++j) + { + for (i = 0; i < n; ++i) + { + if (--k == 0) + { + k = 6; + x = *(p++) - BIAS6; + } + + if ((x & TOPBIT6)) + { + e[v[j]+d[j]++] = i; + if (i == j) ++*nloops; + } + x <<= 1; + } + } + } + else /* sparse6 format */ + { + for (i = n-1, nb = 0; i > 0 ; i >>= 1, ++nb) {} + + p = q; + + k = 0; + vv = 0; + done = FALSE; + loops = 0; + while (!done) + { + if (k == 0) + { + x = *(p++); + if (x == '\n' || x == '\0') + { + done = TRUE; continue; + } + else + { + x -= BIAS6; k = 6; + } + } + if ((x & B(k))) ++vv; + --k; + + need = nb; + j = 0; + while (need > 0 && !done) + { + if (k == 0) + { + x = *(p++); + if (x == '\n' || x == '\0') + { + done = TRUE; continue; + } + else + { + x -= BIAS6; k = 6; + } + } + if (need >= k) + { + j = (j << k) | (x & M(k)); + need -= k; k = 0; + } + else + { + k -= need; + j = (j << need) | ((x >> k) & M(need)); + need = 0; + } + } + if (done) continue; + + if (j > vv) + vv = j; + else if (vv < n) + { + d[vv]++; + if (vv != j) d[j]++; + else ++loops; + } + } + + nde = 0; + for (i = 0; i < n; ++i) + { + v[i] = nde; nde += d[i]; d[i] = 0; + } + sg->nde = nde; + DYNALLOC1(int,sg->e,sg->elen,nde,"stringtosparsegraph"); + e = sg->e; + + p = q; + + k = 0; + vv = 0; + done = FALSE; + while (!done) + { + if (k == 0) + { + x = *(p++); + if (x == '\n' || x == '\0') + { + done = TRUE; continue; + } + else + { + x -= BIAS6; k = 6; + } + } + if ((x & B(k))) ++vv; + --k; + + need = nb; + j = 0; + while (need > 0 && !done) + { + if (k == 0) + { + x = *(p++); + if (x == '\n' || x == '\0') + { + done = TRUE; continue; + } + else + { + x -= BIAS6; k = 6; + } + } + if (need >= k) + { + j = (j << k) | (x & M(k)); + need -= k; k = 0; + } + else + { + k -= need; + j = (j << need) | ((x >> k) & M(need)); + need = 0; + } + } + if (done) continue; + + if (j > vv) + vv = j; + else if (vv < n) + { + e[v[vv]+d[vv]++] = j; + if (vv != j) e[v[j]+d[j]++] = vv; + } + } + *nloops = loops; + } +} + +/***********************************************************************/ + +sparsegraph* /* read graph into sparsegraph format */ +read_sgg_loops(FILE *f, sparsegraph *sg, int *nloops, boolean *digraph) +/* graph6, digraph6 and sparse6 formats are supported + * f = an open file + * sg = place to put the answer (NULL for dynamic allocation) + * - must be initialised if not NULL + * nloops := number of loops (each loop in a sparse6 string + * gives one loop in the sparse representation) + */ +{ + char *s,*p; + int n,loops; + + if ((readg_line = gtools_getline(f)) == NULL) return NULL; + + s = readg_line; + if (s[0] == ':') + { + readg_code = SPARSE6; + *digraph = FALSE; + p = s + 1; + } + else if (s[0] == '&') + { + readg_code = DIGRAPH6; + *digraph = TRUE; + p = s + 1; + } + else + { + readg_code = GRAPH6; + *digraph = FALSE; + p = s; + } + + while (*p >= BIAS6 && *p <= MAXBYTE) + ++p; + if (*p == '\0') + gt_abort(">E read_sg: missing newline\n"); + else if (*p != '\n') + gt_abort(">E read_sg: illegal character\n"); + + n = graphsize(s); + if (readg_code == GRAPH6 && p - s != G6LEN(n)) + gt_abort(">E read_sg: truncated graph6 line\n"); + if (readg_code == DIGRAPH6 && p - s != D6LEN(n)) + gt_abort(">E read_sg: truncated digraph6 line\n"); + + if (sg == NULL) + { + if ((sg = (sparsegraph*)ALLOCS(1,sizeof(sparsegraph))) == NULL) + gt_abort(">E read_sg: malloc failed\n"); + SG_INIT(*sg); + } + + stringtosparsegraph(s,sg,&loops); + *nloops = loops; + + return sg; +} + +/***********************************************************************/ + +sparsegraph* /* read undirected graph into sparsegraph format */ +read_sg_loops(FILE *f, sparsegraph *sg, int *nloops) +/* graph6 and sparse6 formats are supported + * f = an open file + * sg = place to put the answer (NULL for dynamic allocation) + * - must be initialised if not NULL + * nloops := number of loops (each loop in a sparse6 string + * gives one loop in the sparse representation) + * digraph = whether input line was a digraph + */ +{ + sparsegraph *sgg; + boolean digraph; + + sgg = read_sgg_loops(f,sg,nloops,&digraph); + if (!sgg) return NULL; + if (digraph) gt_abort(">E read_sg_loops() can't handle digraphs," + " use read_sgg_loops()\n"); + return sgg; +} + +/***********************************************************************/ + +sparsegraph* /* read graph into sparsegraph format */ +read_sg(FILE *f, sparsegraph *sg) +/* graph6 and sparse6 formats are supported + * *f = an open file + * *sg = place to put the answer (NULL for dynamic allocation) + * - must be initialised if not NULL + */ +{ + int loops; + sparsegraph *sgg; + boolean digraph; + + sgg = read_sgg_loops(f,sg,&loops,&digraph); + if (!sgg) return NULL; + if (digraph) gt_abort(">E read_sg() can't handle digraphs," + " use read_sgg_loops()\n"); + return sgg; +} + +/****************************************************************************/ + +DYNALLSTAT(char,gcode,gcode_sz); /* Used by ntog6, ntos6, ntod6 and sgtos6 */ +TLS_ATTR size_t s6len; +TLS_ATTR int readg_code; +TLS_ATTR char *readg_line; + +/****************************************************************************/ + +char* +ntod6(graph *g, int m, int n) +/* convert nauty graph to digraph6 string, including \n and \0 */ +{ + int i,j,k; + char *p,x; + set *gj; + size_t ii; + + ii = D6LEN(n)+3; + + DYNALLOC1(char,gcode,gcode_sz,ii,"ntod6"); + + p = gcode; + *p++ = '&'; + encodegraphsize(n,&p); + + k = 6; + x = 0; + + for (j = 0; j < n; ++j) + { + gj = GRAPHROW(g,j,m); + for (i = 0; i < n; ++i) + { + x <<= 1; + if (ISELEMENT(gj,i)) x |= 1; + if (--k == 0) + { + *p++ = (char)(BIAS6 + x); + k = 6; + x = 0; + } + } + } + + if (k != 6) *p++ = (char)(BIAS6 + (x << k)); + + *p++ = '\n'; + *p = '\0'; + + return gcode; +} + +/****************************************************************************/ + +char* +ntog6(graph *g, int m, int n) +/* convert nauty graph to graph6 string, including \n and \0 */ +{ + int i,j,k; + char *p,x; + set *gj; + size_t ii; + + ii = G6LEN(n)+3; + + DYNALLOC1(char,gcode,gcode_sz,ii,"ntog6"); + + p = gcode; + encodegraphsize(n,&p); + + k = 6; + x = 0; + + for (j = 1; j < n; ++j) + { + gj = GRAPHROW(g,j,m); + for (i = 0; i < j; ++i) + { + x <<= 1; + if (ISELEMENT(gj,i)) x |= 1; + if (--k == 0) + { + *p++ = (char)(BIAS6 + x); + k = 6; + x = 0; + } + } + } + + if (k != 6) *p++ = (char)(BIAS6 + (x << k)); + + *p++ = '\n'; + *p = '\0'; + + return gcode; +} + +/****************************************************************************/ + +char* +ntos6(graph *g, int m, int n) +/* convert nauty graph to sparse6 string, including \n and \0 */ +{ + int i,j,k; + char *p,x; + set *gj; + size_t ii; + int r,rr,topbit,nb,lastj; + char *plim; + + DYNALLOC1(char,gcode,gcode_sz,5000,"ntos6"); + + plim = gcode + gcode_sz - 20; + + gcode[0] = ':'; + p = gcode+1; + encodegraphsize(n,&p); + + for (i = n-1, nb = 0; i > 0 ; i >>= 1, ++nb) + {} + topbit = 1 << (nb-1); + k = 6; + x = 0; + + lastj = 0; + for (j = 0; j < n; ++j) + { + gj = GRAPHROW(g,j,m); + for (i = 0; i <= j; ++i) + { + if (ISELEMENT(gj,i)) + { + if (p >= plim) + { + ii = p - gcode; + DYNREALLOC(char,gcode,gcode_sz, + 3*(gcode_sz/2)+10000,"ntos6"); + p = gcode + ii; + plim = gcode + gcode_sz - 20; + } + if (j == lastj) + { + x <<= 1; + if (--k == 0) + { + *p++ = (char)(BIAS6 + x); + k = 6; + x = 0; + } + } + else + { + x = (x << 1) | (char)1; + if (--k == 0) + { + *p++ = (char)(BIAS6 + x); + k = 6; + x = 0; + } + if (j > lastj+1) + { + for (r = 0, rr = j; r < nb; ++r, rr <<= 1) + { + if ((rr & topbit)) x = (x << 1) | (char)1; + else x <<= 1; + if (--k == 0) + { + *p++ = (char)(BIAS6 + x); + k = 6; + x = 0; + } + } + x <<= 1; + if (--k == 0) + { + *p++ = (char)(BIAS6 + x); + k = 6; + x = 0; + } + } + lastj = j; + } + for (r = 0, rr = i; r < nb; ++r, rr <<= 1) + { + if ((rr & topbit)) x = (x << 1) | (char)1; + else x <<= 1; + if (--k == 0) + { + *p++ = (char)(BIAS6 + x); + k = 6; + x = 0; + } + } + } + } + } + + if (k != 6) + { + if (k >= nb+1 && lastj == n-2 && n == (1<<nb)) + *p++ = (char)(BIAS6 + ((x << k) | ((1 << (k-1)) - 1))); + else + *p++ = (char)(BIAS6 + ((x << k) | ((1 << k) - 1))); + } + + *p++ = '\n'; + *p = '\0'; + s6len = p - gcode; + return gcode; +} + +/****************************************************************************/ + +char* +ntois6(graph *g, graph *prevg, int m, int n) +/* convert nauty graph to incremental sparse6 string, including \n and \0. + prevg == NULL implies there is no prior graph */ +{ + int i,j,k; + char *p,x; + set *gj,*pgj; + setword gdiff; + size_t ii; + int r,rr,topbit,nb,lastj,iw,nwords; + char *plim; + + if (!prevg) return ntos6(g,m,n); + + DYNALLOC1(char,gcode,gcode_sz,5000,"ntois6"); + + plim = gcode + gcode_sz - 20; + + gcode[0] = ';'; + p = gcode+1; + + for (i = n-1, nb = 0; i > 0 ; i >>= 1, ++nb) + {} + topbit = 1 << (nb-1); + k = 6; + x = 0; + + lastj = 0; + for (j = 0; j < n; ++j) + { + gj = GRAPHROW(g,j,m); + pgj = GRAPHROW(prevg,j,m); + nwords = SETWORDSNEEDED(j+1); + for (iw = 0; iw < nwords; ++iw) + { + gdiff = gj[iw] ^ pgj[iw]; + if (TIMESWORDSIZE(iw+1) > j+1) gdiff &= ALLMASK(SETBT(j+1)); + while (gdiff) + { + TAKEBIT(i,gdiff); + i += TIMESWORDSIZE(iw); + + if (p >= plim) + { + ii = p - gcode; + DYNREALLOC(char,gcode,gcode_sz, + 3*(gcode_sz/2)+10000,"ntois6"); + p = gcode + ii; + plim = gcode + gcode_sz - 20; + } + if (j == lastj) + { + x <<= 1; + if (--k == 0) + { + *p++ = (char)(BIAS6 + x); + k = 6; + x = 0; + } + } + else + { + x = (x << 1) | (char)1; + if (--k == 0) + { + *p++ = (char)(BIAS6 + x); + k = 6; + x = 0; + } + if (j > lastj+1) + { + for (r = 0, rr = j; r < nb; ++r, rr <<= 1) + { + if ((rr & topbit)) x = (x << 1) | (char)1; + else x <<= 1; + if (--k == 0) + { + *p++ = (char)(BIAS6 + x); + k = 6; + x = 0; + } + } + x <<= 1; + if (--k == 0) + { + *p++ = (char)(BIAS6 + x); + k = 6; + x = 0; + } + } + lastj = j; + } + for (r = 0, rr = i; r < nb; ++r, rr <<= 1) + { + if ((rr & topbit)) x = (x << 1) | (char)1; + else x <<= 1; + if (--k == 0) + { + *p++ = (char)(BIAS6 + x); + k = 6; + x = 0; + } + } + } + } + } + + if (k != 6) + { + if (k >= nb+1 && lastj == n-2 && n == (1<<nb)) + *p++ = (char)(BIAS6 + ((x << k) | ((1 << (k-1)) - 1))); + else + *p++ = (char)(BIAS6 + ((x << k) | ((1 << k) - 1))); + } + + *p++ = '\n'; + *p = '\0'; + s6len = p - gcode; + return gcode; +} + +/*************************************************************************/ + +char* +sgtos6(sparsegraph *sg) +/* Convert undirected sparse graph to sparse6 string including '\n'. + It is null-terminated and its address (static memory) is returned. + The length, not including the null, is put in s6len. */ +{ + int *d,*e; + int i,j,n; + char *p,x,*plim; + int nb,topbit; + int dj,k,lastj; + int r,rr; + size_t ii,*v,vj,l; + + SG_VDE(sg,v,d,e); + n = sg->nv; + for (i = n-1, nb = 0; i > 0 ; i >>= 1, ++nb) {} + + ii = (size_t)(nb+1)*(n/6+sg->nde/3); + DYNALLOC1(char,gcode,gcode_sz,ii+1000,"sgtos6"); + plim = gcode + gcode_sz - 20; + + p = gcode; + *p++ = ':'; + encodegraphsize(n,&p); + + topbit = 1 << (nb-1); + k = 6; + x = 0; + + lastj = 0; + for (j = 0; j < n; ++j) + { + vj = v[j]; + dj = d[j]; + for (l = 0; l < dj; ++l) + { + i = e[vj+l]; + if (i <= j) + { + if (p >= plim) + { + ii = p - gcode; + DYNREALLOC(char, + gcode,gcode_sz,5*(gcode_sz/4)+1000,"sgtos6"); + p = gcode + ii; + plim = gcode + gcode_sz - 20; + } + if (j == lastj) + { + x <<= 1; + if (--k == 0) + { + *p++ = (char)(BIAS6 + x); + k = 6; + x = 0; + } + } + else + { + x = (x << 1) | (char)1; + if (--k == 0) + { + *p++ = (char)(BIAS6 + x); + k = 6; + x = 0; + } + if (j > lastj+1) + { + for (r = 0, rr = j; r < nb; ++r, rr <<= 1) + { + if ((rr & topbit)) x = (x << 1) | (char)1; + else x <<= 1; + if (--k == 0) + { + *p++ = (char)(BIAS6 + x); + k = 6; + x = 0; + } + } + x <<= 1; + if (--k == 0) + { + *p++ = (char)(BIAS6 + x); + k = 6; + x = 0; + } + } + lastj = j; + } + for (r = 0, rr = i; r < nb; ++r, rr <<= 1) + { + if ((rr & topbit)) x = (x << 1) | (char)1; + else x <<= 1; + if (--k == 0) + { + *p++ = (char)(BIAS6 + x); + k = 6; + x = 0; + } + } + } + } + } + + if (k != 6) + { + if (k >= nb+1 && lastj == n-2 && n == (1<<nb)) + *p++ = (char)(BIAS6 + ((x << k) | ((1 << (k-1)) - 1))); + else + *p++ = (char)(BIAS6 + ((x << k) | ((1 << k) - 1))); + } + + *p++ = '\n'; + *p = '\0'; + s6len = p - gcode; + return gcode; +} + +/*************************************************************************/ + +char* +sgtog6(sparsegraph *sg) +/* Convert undirected sparse graph to graph6 string including '\n','\0'. + It is null-terminated and its address (static memory) is returned. */ +{ + int *d,*e,*ei; + int i,j,n; + char *p; + size_t ii,*v,bodylen,org; + static char g6bit[] = {32,16,8,4,2,1}; + + SG_VDE(sg,v,d,e); + n = sg->nv; + + ii = G6LEN(n)+3; + + DYNALLOC1(char,gcode,gcode_sz,ii,"sgtog6"); + + p = gcode; + encodegraphsize(n,&p); + + bodylen = G6BODYLEN(n); + for (ii = 0; ii < bodylen; ++ii) p[ii] = 0; + p[bodylen] = '\n'; + p[bodylen+1] = '\0'; + + for (i = 0, org = 0; i < n; org += i, ++i) + { + ei = e + v[i]; + for (j = 0; j < d[i]; ++j) + if (ei[j] < i) + { + ii = ei[j] + org; + p[ii/6] |= g6bit[ii%6]; + } + } + + for (ii = 0; ii < bodylen; ++ii) p[ii] += BIAS6; + + return gcode; +} + +/*************************************************************************/ + +char* +sgtod6(sparsegraph *sg) +/* Convert undirected sparse graph to digraph6 string including '\n','\0'. + It is null-terminated and its address (static memory) is returned. */ +{ + int *d,*e,*ei; + int i,j,n; + char *p; + size_t ii,*v,bodylen,org; + static char g6bit[] = {32,16,8,4,2,1}; + + SG_VDE(sg,v,d,e); + n = sg->nv; + + ii = D6LEN(n)+3; + + DYNALLOC1(char,gcode,gcode_sz,ii,"sgtog6"); + + p = gcode; + *p++ = '&'; + encodegraphsize(n,&p); + + bodylen = D6BODYLEN(n); + for (ii = 0; ii < bodylen; ++ii) p[ii] = 0; + p[bodylen] = '\n'; + p[bodylen+1] = '\0'; + + for (i = 0, org = 0; i < n; org += n, ++i) + { + ei = e + v[i]; + for (j = 0; j < d[i]; ++j) + { + ii = ei[j] + org; + p[ii/6] |= g6bit[ii%6]; + } + } + + for (ii = 0; ii < bodylen; ++ii) p[ii] += BIAS6; + + return gcode; +} + +/**************************************************************************/ + +void +writeg6(FILE *f, graph *g, int m, int n) +/* write graph to file in graph6 format */ +{ + writeline(f,ntog6(g,m,n)); +} + +/**************************************************************************/ + +void +writed6(FILE *f, graph *g, int m, int n) +/* write graph to file in digraph6 format */ +{ + writeline(f,ntod6(g,m,n)); +} + +/**************************************************************************/ + +void +writes6(FILE *f, graph *g, int m, int n) +/* write graph to file in sparse6 format */ +{ + char *s; + + s = ntos6(g,m,n); + + if (fwrite(s,1,s6len,f) != s6len || ferror(f)) + gt_abort(">E writes6 : error on writing\n"); +} + +/**************************************************************************/ + +void +writeis6(FILE *f, graph *g, graph *prevg, int m, int n) +/* write graph to file in incremental sparse6 format + prevg can be NULL if there is no previous graph */ +{ + char *s; + + s = ntois6(g,prevg,m,n); + + if (fwrite(s,1,s6len,f) != s6len || ferror(f)) + gt_abort(">E writeis6 : error on writing\n"); +} + +/**************************************************************************/ + +void +writeg6_sg(FILE *f, sparsegraph *g) +/* write undirected sparse graph to file in sparse6 format */ +{ + writeline(f,sgtog6(g)); +} + +/**************************************************************************/ + +void +writed6_sg(FILE *f, sparsegraph *g) +/* write undirected sparse graph to file in sparse6 format */ +{ + writeline(f,sgtod6(g)); +} + +/**************************************************************************/ + +void +writes6_sg(FILE *f, sparsegraph *g) +/* write undirected sparse graph to file in sparse6 format */ +{ + char *s; + + s = sgtos6(g); + + if (fwrite(s,1,s6len,f) != s6len || ferror(f)) + gt_abort(">E writes6 : error on writing\n"); +} + +/**************************************************************************/ + +DYNALLSTAT(unsigned char,buff,buff_sz); + +void +writepc_sg(FILE *f, sparsegraph *sg) +/* write a sparse graph in planar_code format + *f = an open file + *sg = the graph to write +*/ +{ + int bytes; + size_t i,j,len,k,*v,vi; + unsigned int w; + int n,*d,*e,di; + +#define BEPUT1(x) buff[j++]=(unsigned char)(x); +#define BEPUT2(x) w=(x); buff[j++]=(unsigned char)((w>>8)&0xFF); \ + buff[j++]=(unsigned char)(w&0xff); +#define BEPUT4(x) w=(x); buff[j++]=(unsigned char)((w>>24)&0xFF); \ + buff[j++]=(unsigned char)((w>>16)&0xff); \ + buff[j++]=(unsigned char)((w>>8)&0xFF); \ + buff[j++]=(unsigned char)(w&0xff); + + n = sg->nv; + SG_VDE(sg,v,d,e); + + if (n <= 255) bytes = 1; + else if (n <= 65535) bytes = 2; + else bytes = 4; + + len = bytes * (1 + n + (size_t)(sg->nde)); + if (bytes == 2) len += 1; + else if (bytes == 4) len += 3; + + DYNALLOC1(unsigned char,buff,buff_sz,len,"writepc_sg"); + + if (bytes == 1) + { + j = 0; + BEPUT1(n); + for (i = 0; i < n; ++i) + { + vi = v[i]; + di = d[i]; + for (k = 0; k < di; ++k) { BEPUT1(e[vi+k]+1); } + BEPUT1(0); + } + } + else if (bytes == 2) + { + j = 0; + BEPUT1(n); + BEPUT2(n); + for (i = 0; i < n; ++i) + { + vi = v[i]; + di = d[i]; + for (k = 0; k < di; ++k) { BEPUT2(e[vi+k]+1); } + BEPUT2(0); + } + } + else /* bytes==4 */ + { + j = 0; + BEPUT1(n); + BEPUT2(n); + BEPUT4(n); + for (i = 0; i < n; ++i) + { + vi = v[i]; + di = d[i]; + for (k = 0; k < di; ++k) { BEPUT4(e[vi+k]+1); } + BEPUT4(0); + } + } + + if (fwrite((void*)buff,1,j,f) != j) + gt_abort(">E writepc_sg : error on writing\n"); +} + +/**************************************************************************/ + +sparsegraph* +readpc_sg(FILE *f,sparsegraph *sg) +/* read a planar_code graph into sparse graph format + *f = an open file + *sg = place to put the answer (NULL for dynamic allocation) + - must be initialised if not NULL +*/ +{ +#define BEGET1(x) { x = GETC(f); } +#define BEGET2(x) { w1=GETC(f); w2=GETC(f); if (w2==EOF) x = EOF; else \ + x = (w1<<8) | w2; } +#define BEGET4(x) { w1=GETC(f); w2=GETC(f); w3=GETC(f); w4=GETC(f); \ + if (w4==EOF) x = EOF; \ + else x = (w1<<24) | (w2<<16) | (w3<<8) | w4; } + int w1,w2,w3,w4; + int bytes,n; + int i,j,*d,*e,di; + size_t *v,vi; + + BEGET1(n); + if (n == EOF || n < 0) return NULL; + else if (n > 0) + bytes = 1; + else + { + BEGET2(n); + if (n == EOF || n < 0) + gt_abort(">E readpc_sg : error 1 on reading\n"); + else if (n > 0) + bytes = 2; + else + { + BEGET4(n); + if (n == EOF || n < 0) + gt_abort(">E readpc_sg : error 2 on reading\n"); + else if (n > 0) + bytes = 4; + else + gt_abort(">E readpc_sg : error 3 on reading\n"); + } + } + + if (sg == NULL) + { + if ((sg = (sparsegraph*)ALLOCS(1,sizeof(sparsegraph))) == NULL) + gt_abort(">E readpc_sg: malloc failed\n"); + SG_INIT(*sg); + } + + SG_ALLOC(*sg,n,2*(size_t)n,"readpc_sg"); + SG_VDE(sg,v,d,e); + + vi = 0; + for (i = 0; i < n; ++i) + { + v[i] = vi; + di = 0; + do + { + if (bytes == 1) BEGET1(j) + else if (bytes == 2) BEGET2(j) + else BEGET4(j); + if (j == EOF) gt_abort(">E readpc_sg : error 4 on reading\n"); + + if (j > 0) + { + if (vi == sg->elen) + { + DYNREALLOC(int,sg->e,sg->elen,2*sg->elen,"readpc_sg"); + e = sg->e; + } + e[vi++] = j-1; + ++di; + } + else if (j == 0) + d[i] = di; + else + gt_abort(">E readpc_sg : error 5 on reading\n"); + } while (j != 0); + } + + sg->nv = n; + sg->nde = vi; + return sg; +} + +/**************************************************************************/ + +sparsegraph* +readpcle_sg(FILE *f,sparsegraph *sg) +/* read a planar_code graph into sparse graph format + *f = an open file + *sg = place to put the answer (NULL for dynamic allocation) + - must be initialised if not NULL +*/ +{ +#define LEGET1(x) { x = GETC(f); } +#define LEGET2(x) { w2=GETC(f); w1=GETC(f); if (w1==EOF) x = EOF; else \ + x = (w1<<8) | w2; } +#define LEGET4(x) { w4=GETC(f); w3=GETC(f); w2=GETC(f); w1=GETC(f); \ + if (w1==EOF) x = EOF; \ + else x = (w1<<24) | (w2<<16) | (w3<<8) | w4; } + int w1,w2,w3,w4; + int bytes,n; + int i,j,*d,*e,di; + size_t *v,vi; + + LEGET1(n); + if (n == EOF || n < 0) return NULL; + else if (n > 0) + bytes = 1; + else + { + LEGET2(n); + if (n == EOF || n < 0) + gt_abort(">E readpcle_sg : error 1 on reading\n"); + else if (n > 0) + bytes = 2; + else + { + LEGET4(n); + if (n == EOF || n < 0) + gt_abort(">E readpcle_sg : error 2 on reading\n"); + else if (n > 0) + bytes = 4; + else + gt_abort(">E readpcle_sg : error 3 on reading\n"); + } + } + + if (sg == NULL) + { + if ((sg = (sparsegraph*)ALLOCS(1,sizeof(sparsegraph))) == NULL) + gt_abort(">E readpcle_sg: malloc failed\n"); + SG_INIT(*sg); + } + + SG_ALLOC(*sg,n,2*(size_t)n,"readpcle_sg"); + SG_VDE(sg,v,d,e); + + vi = 0; + for (i = 0; i < n; ++i) + { + v[i] = vi; + di = 0; + do + { + if (bytes == 1) LEGET1(j) + else if (bytes == 2) LEGET2(j) + else LEGET4(j); + if (j == EOF) gt_abort(">E readpcle_sg : error 4 on reading\n"); + + if (j > 0) + { + if (vi == sg->elen) + { + DYNREALLOC(int,sg->e,sg->elen,2*sg->elen,"readpcle_sg"); + e = sg->e; + } + e[vi++] = j-1; + ++di; + } + else if (j == 0) + d[i] = di; + else + gt_abort(">E readpcle_sg : error 5 on reading\n"); + } while (j != 0); + } + + sg->nv = n; + sg->nde = vi; + return sg; +} + +/**************************************************************************/ + +void +writelast(FILE *f) +/* write last graph read by readg() assuming no intervening gtools_getline() */ +{ + writeline(f,readg_line); +} + +/**************************************************************************/ + +int +longvalue(char **ps, long *l) +{ + boolean neg,pos; + long sofar,last; + char *s; + + s = *ps; + pos = neg = FALSE; + if (*s == '-') + { + neg = TRUE; + ++s; + } + else if (*s == '+') + { + pos = TRUE; + ++s; + } + + if (*s < '0' || *s > '9') + { + *ps = s; + return (pos || neg) ? ARG_ILLEGAL : ARG_MISSING; + } + + sofar = 0; + + for (; *s >= '0' && *s <= '9'; ++s) + { + last = sofar; + sofar = sofar * 10 + (*s - '0'); + if (sofar < last || sofar > MAXLONGARG) + { + *ps = s; + return ARG_TOOBIG; + } + } + *ps = s; + *l = neg ? -sofar : sofar; + return ARG_OK; +} + +/**************************************************************************/ + +int +doublevalue(char **ps, double *l) +{ + boolean neg,pos; + double sofar,weight; + char *s; + + s = *ps; + pos = neg = FALSE; + if (*s == '-') + { + neg = TRUE; + ++s; + } + else if (*s == '+') + { + pos = TRUE; + ++s; + } + + if ((*s < '0' || *s > '9') && *s != '.') + { + *ps = s; + return (pos || neg) ? ARG_ILLEGAL : ARG_MISSING; + } + + sofar = 0.0; + + for (; *s >= '0' && *s <= '9'; ++s) + sofar = sofar * 10 + (*s - '0'); + + if (*s == '.') + { + weight = 1.0; + for (++s; *s >= '0' && *s <= '9'; ++s) + { + weight /= 10.0; + sofar += weight * (*s - '0'); + } + } + + *ps = s; + *l = neg ? -sofar : sofar; + return ARG_OK; +} + +/*************************************************************************/ + +void +arg_long(char **ps, long *val, char *id) +{ + int code; + + code = longvalue(ps,val); + if (code == ARG_MISSING || code == ARG_ILLEGAL) + { + fprintf(stderr,">E %s: missing argument value\n",id); + gt_abort(NULL); + } + else if (code == ARG_TOOBIG) + { + fprintf(stderr,">E %s: argument value too large\n",id); + gt_abort(NULL); + } +} + +/*************************************************************************/ + +void +arg_int(char **ps, int *val, char *id) +{ + int code; + long longval; + + code = longvalue(ps,&longval); + *val = longval; + if (code == ARG_MISSING || code == ARG_ILLEGAL) + { + fprintf(stderr,">E %s: missing argument value\n",id); + gt_abort(NULL); + } + else if (code == ARG_TOOBIG || *val != longval) + { + fprintf(stderr,">E %s: argument value too large\n",id); + gt_abort(NULL); + } +} + +/*************************************************************************/ + +void +arg_double(char **ps, double *val, char *id) +{ + int code; + + code = doublevalue(ps,val); + if (code == ARG_MISSING || code == ARG_ILLEGAL) + { + fprintf(stderr,">E %s: missing argument value\n",id); + gt_abort(NULL); + } +} + +/************************************************************************/ + +boolean +strhaschar(char *s, int c) +/* Check if s contains c. Saves the bother of figuring out whether + strchr() is available, or index() or whatever. */ +{ + int i; + + for (i = 0; s[i] != '\0'; ++i) + if (s[i] == c) return TRUE; + + return FALSE; +} + +/************************************************************************/ + +void +arg_range(char **ps, char *sep, long *val1, long *val2, char *id) +{ + int code; + char *s; + + s = *ps; + code = longvalue(&s,val1); + if (code != ARG_MISSING) + { + if (code == ARG_ILLEGAL) + { + fprintf(stderr,">E %s: bad range\n",id); + gt_abort(NULL); + } + else if (code == ARG_TOOBIG) + { + fprintf(stderr,">E %s: value too big\n",id); + gt_abort(NULL); + } + } + else if (*s == '\0' || !strhaschar(sep,*s)) + { + fprintf(stderr,">E %s: missing value\n",id); + gt_abort(NULL); + } + else + *val1 = -NOLIMIT; + + if (*s != '\0' && strhaschar(sep,*s)) + { + ++s; + code = longvalue(&s,val2); + if (code == ARG_MISSING) + *val2 = NOLIMIT; + else if (code == ARG_TOOBIG) + { + fprintf(stderr,">E %s: value too big\n",id); + gt_abort(NULL); + } + else if (code == ARG_ILLEGAL) + { + fprintf(stderr,">E %s: illegal range\n",id); + gt_abort(NULL); + } + } + else + *val2 = *val1; + + *ps = s; +} + +/************************************************************************/ + +void +arg_sequence(char **ps, char *sep, + long *val, int maxvals, int *numvals, char *id) +{ + int code,ival; + char *s; + + s = *ps; + + for (ival = 0; ival < maxvals; ++ival) + { + code = longvalue(&s,&val[ival]); + if (code == ARG_ILLEGAL) + { + fprintf(stderr,">E %s: illegal value\n",id); + gt_abort(NULL); + } + else if (code == ARG_TOOBIG) + { + fprintf(stderr,">E %s: value too big\n",id); + gt_abort(NULL); + } + else if (code == ARG_MISSING) + { + fprintf(stderr,">E %s: value missing\n",id); + gt_abort(NULL); + } + + if (*s == '\0' || !strhaschar(sep,*s)) + { + *numvals = ival+1; + *ps = s; + return; + } + ++s; + } + fprintf(stderr,">E %s: too many values\n",id); + gt_abort(NULL); +} + +/************************************************************************/ + +void +arg_sequence_min(char **ps, char *sep, + long *val, int minvals, int maxvals, int *numvals, char *id) +{ + int code,ival; + char *s; + + s = *ps; + + for (ival = 0; ival < maxvals; ++ival) + { + code = longvalue(&s,&val[ival]); + if (code == ARG_ILLEGAL) + { + fprintf(stderr,">E %s: illegal value\n",id); + gt_abort(NULL); + } + else if (code == ARG_TOOBIG) + { + fprintf(stderr,">E %s: value too big\n",id); + gt_abort(NULL); + } + else if (code == ARG_MISSING) + { + fprintf(stderr,">E %s: value missing\n",id); + gt_abort(NULL); + } + + if (*s == '\0' || !strhaschar(sep,*s)) + { + *numvals = ival+1; + *ps = s; + if (*numvals < minvals) + { + fprintf(stderr,">E %s: too few values\n",id); + gt_abort(NULL); + } + return; + } + ++s; + } + fprintf(stderr,">E %s: too many values\n",id); + gt_abort(NULL); +} + +/************************************************************************/ + +void +arg_doublerange(char **ps, char *sep, double *val1, double *val2, char *id) +{ + int code; + char *s; + + s = *ps; + code = doublevalue(&s,val1); + if (code != ARG_MISSING) + { + if (code == ARG_ILLEGAL) + { + fprintf(stderr,">E %s: bad range\n",id); + gt_abort(NULL); + } + } + else if (*s == '\0' || !strhaschar(sep,*s)) + { + fprintf(stderr,">E %s: missing value\n",id); + gt_abort(NULL); + } + else + *val1 = -NOLIMIT; + + if (*s != '\0' && strhaschar(sep,*s)) + { + ++s; + code = doublevalue(&s,val2); + if (code == ARG_MISSING) + *val2 = NOLIMIT; + else if (code == ARG_ILLEGAL) + { + fprintf(stderr,">E %s: illegal range\n",id); + gt_abort(NULL); + } + } + else + *val2 = *val1; + + *ps = s; +} + +/***********************************************************************/ + +void +writerange(FILE *f, int c, long lo, long hi) /* Write a range. */ +{ + if (c != '\0') fprintf(f,"%c",c); + if (lo != -NOLIMIT) fprintf(f,"%ld",lo); + if (lo != hi) + { + fprintf(f,":"); + if (hi != NOLIMIT) fprintf(f,"%ld",hi); + } +} + +/************************************************************************/ + +void +gt_abort(const char *msg) /* Write message and halt. */ +{ + if (msg) fprintf(stderr,"%s",msg); + ABORT(">E gtools"); +} + +/************************************************************************/ + +char* +stringcopy(char *s) /* duplicate string */ +{ + char *scopy; + size_t i,len; + + for (len = 0; s[len] != '\0'; ++len) + {} + + if ((scopy = (char*)ALLOCS(len+1,1)) == NULL) + gt_abort(">E stringcopy: malloc failed\n"); + + for (i = 0; i <= len; ++i) + scopy[i] = s[i]; + + return scopy; +} + +/***************************************************************************** +* * +* gtools_check() checks that this file is compiled compatibly with the * +* given parameters. If not, call exit(1). * +* * +*****************************************************************************/ + +void +gtools_check(int wordsize, int m, int n, int version) +{ + if (wordsize != WORDSIZE) + { + fprintf(ERRFILE,"Error: WORDSIZE mismatch in gtools.c\n"); + exit(1); + } + +#if MAXN + if (m > MAXM) + { + fprintf(ERRFILE,"Error: MAXM inadequate in gtools.c\n"); + exit(1); + } + + if (n > MAXN) + { + fprintf(ERRFILE,"Error: MAXN inadequate in gtools.c\n"); + exit(1); + } +#endif + + if (version < NAUTYREQUIRED) + { + fprintf(ERRFILE,"Error: gtools.c version mismatch\n"); + exit(1); + } + +#if !HAVE_TLS + if ((version & 1)) + { + fprintf(ERRFILE, + "*** Warning: program with TLS calling gtools without TLS ***\n"); + } +#endif +} diff --git a/graph-checker/nauty/gtools.h b/graph-checker/nauty/gtools.h new file mode 100644 index 0000000..55b64c6 --- /dev/null +++ b/graph-checker/nauty/gtools.h @@ -0,0 +1,326 @@ +/***************************************************************************** +* This is the main header file for gtools. nauty version 2.8.6 +* Subject to the copyright notice in nauty.h. * +* gtools.h. Generated from gtools-h.in by configure. +*****************************************************************************/ + +/* The parts between the ==== lines are modified by configure when +creating gtools.h out of gtools-h.in. If configure is not being +used, it is necessary to check they are correct. +====================================================================*/ + +#ifndef _GTOOLS_H_ /* only process this file once */ +#define _GTOOLS_H_ + +#define HAVE_ERRNO_H 1 /* <errno.h> exists */ +#define HAVE_PERROR 1 /* perror() exists */ +#define HAVE_PIPE 1 /* pipe() exists */ +#define HAVE_WAIT 1 /* wait() exists */ +#define HAVE_WAIT_H 1 /* <sys/wait.h> exists */ +#define HAVE_POPEN 1 /* popen() and pclose() exist */ +#define POPEN_DEC 1 /* popen() is declared */ +#define FTELL_DEC 1 /* ftell() is declared */ +#define FDOPEN_DEC 1 /* fdopen() is declared */ +#define SORTPROG "sort" /* name of sort program */ +#define SORT_NEWKEY 1 /* if -k is supported */ +#define HAVE_PID_T 1 /* pid_t is defined */ +#define PUTENV_DEC 1 /* putenv() is declared */ +#define SETENV_DEC 1 /* setenv() is declared */ +#define HAVE_PUTENV 1 /* putenv() exists */ +#define HAVE_SETENV 1 /* setenv() exists */ +#define HAVE_FORK 1 /* fork() exists */ +#define HAVE_SIGNAL_H 1 /* <signal.h> exists */ +#define HAVE_FSEEKO 1 /* fseeko() and ftello() exist */ +#define HAVE_SIGACTION 1 /* sigaction() exists */ +#define HAVE_SIGPROCMASK 1 /* sigprocmask() exists */ +#define ALLOW_INTERRUPT 1 /* no --disable-interrupt */ +#define HAVE_GUNZIP 1 /* gunzip program is available */ + +/* ++++++ This file is automatically generated, don't edit it by hand! ++++++ */ + +/*==================================================================*/ + +#ifndef MAXN +#define MAXN 0 +#endif + +#define SIZELEN(n) ((n)<=SMALLN?1:((n)<=SMALLISHN?4:8)) + /* length of size code in bytes */ +#define G6BODYLEN(n) \ + (((size_t)(n)/12)*((size_t)(n)-1) + (((size_t)(n)%12)*((size_t)(n)-1)+11)/12) +#define G6LEN(n) (SIZELEN(n) + G6BODYLEN(n)) + /* exact graph6 string length excluding \n\0 + This twisted expression works up to n=227023 in 32-bit arithmetic + and for larger n if size_t has 64 bits. */ +#define D6BODYLEN(n) \ + ((n)*(size_t)((n)/6) + (((n)*(size_t)((n)%6)+5)/6)) +#define D6LEN(n) (1 + SIZELEN(n) + D6BODYLEN(n)) + /* exact digraph6 string length excluding \n\0 + This twisted expression works up to n=160529 in 32-bit arithmetic + and for larger n if size_t has 64 bits. */ + +#include "naututil.h" /* which includes stdio.h */ +#include "nausparse.h" + +#if HAVE_ERRNO_H +#include <errno.h> +#else +extern int errno; +#endif + +#if HAVE_WAIT_H && !defined(AVOID_SYS_WAIT_H) +#include <sys/wait.h> +#endif + +#if HAVE_SIGNAL_H +#include <signal.h> +#endif + +#if HAVE_PERROR +#define ABORT(msg) do {if (errno != 0) perror(msg); exit(1);} while(0) +#else +#define ABORT(msg) do {exit(1);} while(0) +#endif + +/* Here we set environment variables that determine the sorting order + for the shortg program. Older docs for sort say that it uses + LC_COLLATE, but the POSIX description of locales says that the + LC_ALL variable takes precedence over LC_COLLATE. To be safe, + we will define both. Also, define this to be nothing if the + variable KEEP_SORT_LOCALE is defined. */ +#ifdef KEEP_SORT_LOCALE +#define SET_C_COLLATION +#else +#if PUTENV_DEC && HAVE_PUTENV +#define SET_C_COLLATION putenv("LC_ALL=C"); putenv("LC_COLLATE=C") +#elif SETENV_DEC && HAVE_SETENV +#define SET_C_COLLATION setenv("LC_ALL","C",1); setenv("LC_COLLATE","C",1) +#elif HAVE_PUTENV +int putenv(char*); +#define SET_C_COLLATION putenv("LC_ALL=C"); putenv("LC_COLLATE=C") +#elif HAVE_SETENV +int setenv(const char*,const char*,int); +#define SET_C_COLLATION setenv("LC_ALL","C",1); setenv("LC_COLLATE","C",1) +#else +#define SET_C_COLLATION +#endif +#endif + +#if HAVE_FLOCKFILE +#define FLOCKFILE(f) flockfile(f) +#define FUNLOCKFILE(f) funlockfile(f) +#else +#define FLOCKFILE(f) +#define FUNLOCKFILE(f) +#endif + +#if HAS_STDIO_UNLOCK && !defined(NAUTY_IN_MAGMA) && !defined(IS_JAVA) +#define GETC(f) getc_unlocked(f) +#undef PUTC +#define PUTC(c,f) putc_unlocked(c,f) +#else +#define GETC(f) getc(f) +#undef PUTC +#define PUTC(c,f) putc(c,f) +#endif + +#define BIAS6 63 +#define MAXBYTE 126 +#define SMALLN 62 +#define SMALLISHN 258047 +#define TOPBIT6 32 +#define C6MASK 63 + +#define GRAPH6_HEADER ">>graph6<<" +#define SPARSE6_HEADER ">>sparse6<<" +#define DIGRAPH6_HEADER ">>digraph6<<" +#define PLANARCODE_HEADER ">>planar_code<<" +#define PLANARCODELE_HEADER ">>planar_code le<<" +#define PLANARCODEBE_HEADER ">>planar_code be<<" +#define EDGECODE_HEADER ">>edge_code<<" + +#define GRAPH6 1 +#define SPARSE6 2 +#define PLANARCODE 4 +#define PLANARCODELE 8 +#define PLANARCODEBE 16 +#define EDGECODE 32 +#define INCSPARSE6 64 +#define PLANARCODEANY (PLANARCODE|PLANARCODELE|PLANARCODEBE) +#define DIGRAPH6 128 +#define UNKNOWN_TYPE 256 +#define HAS_HEADER 512 + +#define NODIGRAPHSYET(code) if (((code)&DIGRAPH6)) \ + gt_abort(">E Sorry, this program doesn't support digraphs yet.\n") + +#define ARG_OK 0 +#define ARG_MISSING 1 +#define ARG_TOOBIG 2 +#define ARG_ILLEGAL 3 + +#define MAXARG 2140000000L +#define NOLIMIT (MAXARG+31L) +#if SIZEOF_LONG == 8 +#define MAXLONGARG 9220000000000000000L +#else +#define MAXLONGARG MAXARG +#endif + +#define SWBOOLEAN(c,boool) if (sw==c) boool=TRUE; +#define SWCOUNT(c,count) if (sw==c) ++count; +#define SWINT(c,boool,val,id) if (sw==c) \ + {boool=TRUE;arg_int(&arg,&val,id);} +#define SWLONG(c,boool,val,id) if (sw==c) \ + {boool=TRUE;arg_long(&arg,&val,id);} +#define SWRANGE(c,sep,boool,val1,val2,id) if (sw==c) \ + {boool=TRUE;arg_range(&arg,sep,&val1,&val2,id);} +#define SWREAL(c,boool,val,id) if (sw==c) \ + {boool=TRUE;arg_double(&arg,&val,id);} +#define SWREALRANGE(c,sep,boool,val1,val2,id) if (sw==c) \ + {boool=TRUE;arg_doublerange(&arg,sep,&val1,&val2,id);} +#define SWSEQUENCE(c,sep,boool,val,maxvals,numvals,id) if (sw==c) \ + {boool=TRUE;arg_sequence(&arg,sep,val,maxvals,&numvals,id);} +#define SWSEQUENCEMIN(c,sep,boool,val,minvals,maxvals,numvals,id) if (sw==c) \ + {boool=TRUE;arg_sequence_min(&arg,sep,val,minvals,maxvals,&numvals,id);} + +#ifdef HELPUSECMD +#ifdef HELPTEXT2 +#define PUTHELPTEXT printf("\nUsage: %s %s\n\n%s",argv[0],USAGE,HELPTEXT1);\ + printf("%s",HELPTEXT2); +#else +#define PUTHELPTEXT printf("\nUsage: %s %s\n\n%s",argv[0],USAGE,HELPTEXT) +#endif +#else +#ifdef HELPTEXT2 +#define PUTHELPTEXT printf("\nUsage: %s\n\n%s",USAGE,HELPTEXT1);\ + printf("%s",HELPTEXT2); +#else +#define PUTHELPTEXT printf("\nUsage: %s\n\n%s",USAGE,HELPTEXT) +#endif +#endif + +#define HELP if (argc > 1 && (strcmp(argv[1],"-help")==0 \ + || strcmp(argv[1],"/?")==0 \ + || strcmp(argv[1],"--help")==0)) \ + { PUTHELPTEXT; return 0; } + +#define PUTVERSION if (argc > 1 && (strcmp(argv[1],"-version")==0 \ + || strcmp(argv[1],"--version")==0)) \ + { printf("Nauty&Traces version %.4f (%d bits)\n",\ + NAUTYVERSIONID/10000.0,WORDSIZE); return 0; } + +#define GETHELP \ +fprintf(stderr," Use %s -help to see more detailed instructions.\n",argv[0]) + +#define alloc_error gt_abort + +#define CATMSG0(fmt) sprintf(msg+strlen(msg),fmt) +#define CATMSG1(fmt,x1) sprintf(msg+strlen(msg),fmt,x1) +#define CATMSG2(fmt,x1,x2) sprintf(msg+strlen(msg),fmt,x1,x2) +#define CATMSG3(fmt,x1,x2,x3) sprintf(msg+strlen(msg),fmt,x1,x2,x3) +#define CATMSG4(fmt,x1,x2,x3,x4) sprintf(msg+strlen(msg),fmt,x1,x2,x3,x4) +#define CATMSG5(fmt,x1,x2,x3,x4,x5) sprintf(msg+strlen(msg),fmt,x1,x2,x3,x4,x5) +#define CATMSG6(fmt,x1,x2,x3,x4,x5,x6) \ + sprintf(msg+strlen(msg),fmt,x1,x2,x3,x4,x5,x6) +#define CATMSG7(fmt,x1,x2,x3,x4,x5,x6,x7) \ + sprintf(msg+strlen(msg),fmt,x1,x2,x3,x4,x5,x6,x7) +#define CATMSG8(fmt,x1,x2,x3,x4,x5,x6,x7,x8) \ + sprintf(msg+strlen(msg),fmt,x1,x2,x3,x4,x5,x6,x7,x8) +#define CATMSG9(fmt,x1,x2,x3,x4,x5,x6,x7,x8,x9) \ + sprintf(msg+strlen(msg),fmt,x1,x2,x3,x4,x5,x6,x7,x8,x9) + +/************************************************************************/ + +/* ++++++ This file is automatically generated, don't edit it by hand! ++++++ */ + +#ifdef __cplusplus +extern "C" { +#endif + +extern void gtools_check(int,int,int,int); +extern FILE *opengraphfile(char*,int*,boolean,long); +extern void writeline(FILE*,char*); +extern char *gtools_getline(FILE*); /* formerly getline() */ +extern int graphsize(char*); +extern void encodegraphsize(int,char**); +extern void stringcounts(char*,int*,size_t*); +extern void stringtograph(char*,graph*,int); +extern void stringtograph_inc(char*,graph*,int,graph*,int); +extern size_t edgecount(char*); +extern int checkgline(char*); +extern graph *readgg(FILE*,graph*,int,int*,int*,boolean*); +extern graph *readg(FILE*,graph*,int,int*,int*); +extern graph *readgg_inc(FILE*,graph*,int,int*,int*,graph*,int,int,boolean*); +extern graph *readg_inc(FILE*,graph*,int,int*,int*,graph*,int,int); +extern char *ntog6(graph*,int,int); +extern char *ntos6(graph*,int,int); +extern char *ntod6(graph*,int,int); +extern char *sgtos6(sparsegraph*); +extern char *sgtog6(sparsegraph*); +extern char *sgtod6(sparsegraph*); +extern void writeg6(FILE*,graph*,int,int); +extern void writed6(FILE*,graph*,int,int); +extern void writes6(FILE*,graph*,int,int); +extern void writeg6_sg(FILE*,sparsegraph*); +extern void writes6_sg(FILE*,sparsegraph*); +extern void writed6_sg(FILE*,sparsegraph*); +extern char *ntois6(graph*,graph*,int,int); +extern void writeis6(FILE*,graph*,graph*,int,int); +extern void writepc_sg(FILE*,sparsegraph*); +extern void stringtosparsegraph(char*,sparsegraph*,int*); +extern sparsegraph *read_sg(FILE*,sparsegraph*); +extern sparsegraph *read_sg_loops(FILE*,sparsegraph*,int*); +extern sparsegraph *read_sgg_loops(FILE*,sparsegraph*,int*,boolean*); +extern sparsegraph *readpc_sg(FILE*,sparsegraph*); +extern sparsegraph *readpcle_sg(FILE*,sparsegraph*); +extern char *getecline(FILE*); +extern void writelast(FILE*); +extern int longval(char**,long*); +extern void arg_int(char**,int*,char*); +extern void arg_long(char**,long*,char*); +extern void arg_range(char**,char*,long*,long*,char*); +extern int doublevalue(char**,double*); +extern void arg_double(char**,double*,char*); +extern void arg_doublerange(char**,char*,double*,double*,char*); +extern void arg_sequence(char**,char*,long*,int,int*,char*); +extern void arg_sequence_min(char**,char*,long*,int,int,int*,char*); + +extern void writerange(FILE*,int,long,long); +extern void gt_abort(const char*); +extern char *stringcopy(char*); +extern boolean strhaschar(char*,int); + +extern void fcanonise(graph*,int,int,graph*,char*,boolean); +extern void fcanonise_inv + (graph*,int,int,graph*,char*,void(*)(graph*,int*,int*,int, + int,int,int*,int,boolean,int,int),int,int,int,boolean); +extern void fcanonise_inv_sg + (sparsegraph*,int,int,sparsegraph*,char*,void(*)(graph*,int*,int*, + int,int,int,int*,int,boolean,int,int),int,int,int,boolean); +extern void setlabptn(int*,int*,int*,int); +extern int breakcellwt(int*,int*,int*,int,int); + +extern void fgroup(graph*,int,int,char*,int*,int*); +extern void fgroup_inv + (graph*,int,int,char*,int*,int*,void(*)(graph*,int*,int*,int, + int,int,int*,int,boolean,int,int),int,int,int); +extern int istransitive(graph*,int,int,graph*); +extern void tg_canonise(graph*,graph*,int,int); + +extern TLS_ATTR int readg_code; +extern TLS_ATTR char *readg_line; +extern TLS_ATTR size_t ogf_linelen; +extern TLS_ATTR boolean is_pipe; + +#ifdef __cplusplus +} +#endif + +#ifdef CPUDEFS +CPUDEFS +#endif + +/* ++++++ This file is automatically generated, don't edit it by hand! ++++++ */ + +#endif /* _GTOOLS_H_ */ diff --git a/graph-checker/nauty/gutil1.c b/graph-checker/nauty/gutil1.c new file mode 100644 index 0000000..28df5d2 --- /dev/null +++ b/graph-checker/nauty/gutil1.c @@ -0,0 +1,1222 @@ +/* gutil1.c: Some graph utilities. */ + +#include "gtools.h" +#include "gutils.h" + +/**************************************************************************/ + +void +degstats(graph *g, int m, int n, unsigned long *edges, int *mindeg, + int *mincount, int *maxdeg, int *maxcount, boolean *eulerian) +/* Compute degree-related graph properties. + *edges = number of edges + *mindeg, *mincount = minimum degree and how many there are + *maxdeg, *maxcount = maximum degree and how many there are + *eulerian = whether the graph has only even degrees +*/ +{ + setword *pg; + int i,j,d,dor; + int mind,mindc,maxd,maxdc; + unsigned long ned; + + mind = n; + mindc = 0; + maxd = 0; + maxdc = 0; + ned = 0; + dor = 0; + + pg = (setword*)g; + for (i = 0; i < n; ++i) + { + d = 0; + for (j = 0; j < m; ++j, ++pg) + if (*pg) d += POPCOUNT(*pg); + + if (d == mind) + ++mindc; + else if (d < mind) + { + mind = d; + mindc = 1; + } + + if (d == maxd) + ++maxdc; + else if (d > maxd) + { + maxd = d; + maxdc = 1; + } + + dor |= d; + ned += d; + } + + *mindeg = mind; + *mincount = mindc; + *maxdeg = maxd; + *maxcount = maxdc; + *edges = ned / 2; + *eulerian = (dor & 1) == 0; +} + +/**************************************************************************/ + +void +degstats3(graph *g, int m, int n, unsigned long *edges, int *mindeg, + int *mincount, int *maxdeg, int *maxcount, int *odddeg) +/* Compute degree-related graph properties. + *edges = number of edges + *mindeg, *mincount = minimum degree and how many there are + *maxdeg, *maxcount = maximum degree and how many there are + *odddeg = number of vertices of odd degree +*/ +{ + setword *pg; + int i,j,d,dodd; + int mind,mindc,maxd,maxdc; + unsigned long ned; + + mind = n; + mindc = 0; + maxd = 0; + maxdc = 0; + ned = 0; + dodd = 0; + + pg = (setword*)g; + for (i = 0; i < n; ++i) + { + d = 0; + for (j = 0; j < m; ++j, ++pg) + if (*pg) d += POPCOUNT(*pg); + + if (d == mind) + ++mindc; + else if (d < mind) + { + mind = d; + mindc = 1; + } + + if (d == maxd) + ++maxdc; + else if (d > maxd) + { + maxd = d; + maxdc = 1; + } + + dodd += d % 2; + ned += d; + } + + *mindeg = mind; + *mincount = mindc; + *maxdeg = maxd; + *maxcount = maxdc; + *edges = ned / 2; + *odddeg = dodd; +} + +/**************************************************************************/ + +void +degstats2(graph *g, boolean digraph, int m, int n, + unsigned long *edges, int *loops, + int *minindeg, int *minincount, int *maxindeg, int *maxincount, + int *minoutdeg, int *minoutcount, int *maxoutdeg, int *maxoutcount, + boolean *eulerian) +/* Compute degree-related graph properties. + *edges = number of edges (including loops), directed edges for digraphs + *loops = number of loops + *minindeg, *minincount = minimum in-degree and how many there are + *maxindeg, *maxincount = maximum in-degree and how many there are + *minoutdeg,*minoutcount,*maxoutdeg,*maxoutcount = similarly for out-degree + *eulerian = whether the undirected graph has only even degrees, + or the directed graph has indegree=outdegree at each vertex. + A loop contributes 2 to the degrees of an undirected graph and + 1 to each degree for a directed graph. +*/ +{ + setword *pg; + int i,j,d,dor; + int mind,mindc,maxd,maxdc; + unsigned long ned; + int nloops; +#if MAXN + int indeg[MAXN]; + int outdeg[MAXN]; +#else + DYNALLSTAT(int,indeg,indeg_sz); + DYNALLSTAT(int,outdeg,outdeg_sz); +#endif + + if (n == 0) + { + *edges = *loops = 0; + *minindeg = *minincount = *maxindeg = *maxincount = 0; + *minoutdeg = *minoutcount = *maxoutdeg = *maxoutcount = 0; + *eulerian = TRUE; + return; + } + +#if !MAXN + if (digraph) + { + DYNALLOC1(int,indeg,indeg_sz,n,"degstats2"); + DYNALLOC1(int,outdeg,outdeg_sz,n,"degstats2"); + } +#endif + + if (!digraph) + { + mind = n+2; + mindc = 0; + maxd = 0; + maxdc = 0; + ned = 0; + dor = 0; + nloops = 0; + + pg = (setword*)g; + for (i = 0; i < n; ++i) + { + d = 0; + if (ISELEMENT(pg,i)) + { + ++d; + ++nloops; + } + for (j = 0; j < m; ++j, ++pg) + if (*pg) d += POPCOUNT(*pg); + + if (d == mind) + ++mindc; + else if (d < mind) + { + mind = d; + mindc = 1; + } + + if (d == maxd) + ++maxdc; + else if (d > maxd) + { + maxd = d; + maxdc = 1; + } + + dor |= d; + ned += d; + } + + *minindeg = *minoutdeg = mind; + *minincount = *minoutcount = mindc; + *maxindeg = *maxoutdeg = maxd; + *maxincount = *maxoutcount = maxdc; + *edges = ned / 2; + *eulerian = (dor & 1) == 0; + *loops = nloops; + } + else + { + for (i = 0; i < n; ++i) indeg[i] = outdeg[i] = 0; + + nloops = 0; + ned = 0; + for (i = 0, pg = (setword*)g; i < n; ++i, pg += m) + { + if (ISELEMENT(pg,i)) ++nloops; + for (j = -1; (j = nextelement(pg,m,j)) >= 0;) + { + ++outdeg[i]; + ++indeg[j]; + } + ned += outdeg[i]; + } + *edges = ned; + *loops = nloops; + + mind = maxd = indeg[0]; + mindc = maxdc = 1; + + for (i = 1; i < n; ++i) + { + d = indeg[i]; + + if (d == mind) + ++mindc; + else if (d < mind) + { + mind = d; + mindc = 1; + } + + if (d == maxd) + ++maxdc; + else if (d > maxd) + { + maxd = d; + maxdc = 1; + } + } + *minindeg = mind; + *minincount = mindc; + *maxindeg = maxd; + *maxincount = maxdc; + + mind = maxd = outdeg[0]; + mindc = maxdc = 1; + + for (i = 1; i < n; ++i) + { + d = outdeg[i]; + + if (d == mind) + ++mindc; + else if (d < mind) + { + mind = d; + mindc = 1; + } + + if (d == maxd) + ++maxdc; + else if (d > maxd) + { + maxd = d; + maxdc = 1; + } + } + *minoutdeg = mind; + *minoutcount = mindc; + *maxoutdeg = maxd; + *maxoutcount = maxdc; + + for (i = 0; i < n; ++i) + if (indeg[i] != outdeg[i]) break; + *eulerian = (i == n); + } +} + +/*********************************************************************/ + +void +sources_sinks(graph *g, int m, int n, int *sources, int *sinks) +/* Count the sources and sinks. For an undirected graph, these + are just the isolated vertices. For a directed graph, they have + their usual meanings. A vertex with a loop is neither a source + nor a sink. +*/ +{ + setword rowor,wk; + int i,j,nsource,nsink; + set *gi; +#if MAXM + setword work[MAXM+1]; +#else + DYNALLSTAT(setword,work,work_sz); + DYNALLOC1(setword,work,work_sz,m,"sources_sinks"); +#endif + + if (n == 0) { *sources = *sinks = 0; return; } + + if (m == 1) + { + nsink = 0; + wk = 0; + for (i = 0; i < n; ++i) + { + if (g[i] == 0) ++nsink; + wk |= g[i]; + } + *sinks = nsink; + *sources = n - POPCOUNT(wk); + return; + } + + for (i = 0; i < m; ++i) work[i] = 0; + nsink = 0; + for (i = 0, gi = g; i < n; ++i, gi += m) + { + rowor = 0; + for (j = 0; j < m; ++j) + { + rowor |= gi[j]; + work[j] |= gi[j]; + } + if (rowor == 0) ++nsink; + } + *sinks = nsink; + nsource = n; + for (j = 0; j < m; ++j) + nsource -= POPCOUNT(work[j]); + *sources = nsource; +} + +/*********************************************************************/ + +boolean +isconnected1(graph *g, int n) +/* test if g is connected (m=1) */ +{ + setword seen,expanded,toexpand; + int i; + + if (n == 0) return FALSE; + + seen = bit[0]; + expanded = 0; + + while ((toexpand = (seen & ~expanded)) != 0) + { + i = FIRSTBITNZ(toexpand); + expanded |= bit[i]; + seen |= g[i]; + } + + return POPCOUNT(seen) == n; +} + +/**************************************************************************/ + +boolean +isconnected(graph *g, int m, int n) +/* Test if g is connected */ +{ + int i,head,tail,w; + set *gw; +#if MAXN + int queue[MAXN],visited[MAXN]; +#else + DYNALLSTAT(int,queue,queue_sz); + DYNALLSTAT(int,visited,visited_sz); +#endif + + if (n == 0) return FALSE; + if (m == 1) return isconnected1(g,n); + +#if !MAXN + DYNALLOC1(int,queue,queue_sz,n,"isconnected"); + DYNALLOC1(int,visited,visited_sz,n,"isconnected"); +#endif + + for (i = 0; i < n; ++i) visited[i] = 0; + + queue[0] = 0; + visited[0] = 1; + + head = 0; + tail = 1; + while (head < tail) + { + w = queue[head++]; + gw = GRAPHROW(g,w,m); + for (i = -1; (i = nextelement(gw,m,i)) >= 0;) + { + if (!visited[i]) + { + visited[i] = 1; + queue[tail++] = i; + } + } + } + + return tail == n; +} + +/**************************************************************************/ + +boolean +issubconnected(graph *g, set *sub, int m, int n) +/* Test if the subset of g induced by sub is connected. Empty is connected. */ +/* Note that the value for empty subgraphs disagrees with isconnected() */ +{ + int i,head,tail,w,subsize; + set *gw; +#if MAXN + int queue[MAXN],visited[MAXN]; + setword subw[MAXM]; +#else + DYNALLSTAT(int,queue,queue_sz); + DYNALLSTAT(int,visited,visited_sz); + DYNALLSTAT(set,subw,subw_sz); + + DYNALLOC1(int,queue,queue_sz,n,"issubconnected"); + DYNALLOC1(int,visited,visited_sz,n,"issubconnected"); + DYNALLOC1(set,subw,subw_sz,m,"issubconnected"); +#endif + + subsize = 0; + for (i = 0; i < m; ++i) subsize += (sub[i] ? POPCOUNT(sub[i]) : 0); + + if (subsize <= 1) return TRUE; + + for (i = 0; i < n; ++i) visited[i] = 0; + + i = nextelement(sub,m,-1); + queue[0] = i; + visited[i] = 1; + + head = 0; + tail = 1; + while (head < tail) + { + w = queue[head++]; + gw = GRAPHROW(g,w,m); + for (i = 0; i < m; ++i) subw[i] = gw[i] & sub[i]; + + for (i = -1; (i = nextelement(subw,m,i)) >= 0;) + { + if (!visited[i]) + { + visited[i] = 1; + queue[tail++] = i; + } + } + } + + return tail == subsize; +} + +/**********************************************************************/ + +boolean +isbiconnected1(graph *g, int n) +/* Test if g is biconnected; version for m=1. */ +{ + int sp,v,w; + setword sw; + int numvis; + setword visited; + int num[WORDSIZE],lp[WORDSIZE],stack[WORDSIZE]; + + if (n <= 2) return FALSE; + + visited = bit[0]; + stack[0] = 0; + num[0] = 0; + lp[0] = 0; + numvis = 1; + sp = 0; + v = 0; + + for (;;) + { + if ((sw = g[v] & ~visited)) /* not "==" */ + { + w = v; + v = FIRSTBITNZ(sw); /* visit next child */ + stack[++sp] = v; + visited |= bit[v]; + lp[v] = num[v] = numvis++; + sw = g[v] & visited & ~bit[w]; + while (sw) + { + w = FIRSTBITNZ(sw); + sw &= ~bit[w]; + if (num[w] < lp[v]) lp[v] = num[w]; + } + } + else + { + w = v; /* back up to parent */ + if (sp <= 1) return numvis == n; + v = stack[--sp]; + if (lp[w] >= num[v]) return FALSE; + if (lp[w] < lp[v]) lp[v] = lp[w]; + } + } +} + +/**********************************************************************/ + +boolean +isbiconnected(graph *g, int m, int n) +/* test if g is biconnected */ +{ + int sp,v,vc; + int numvis; + set *gv; +#if MAXN + int num[MAXN],lp[MAXN],stack[MAXN]; +#else + DYNALLSTAT(int,num,num_sz); + DYNALLSTAT(int,lp,lp_sz); + DYNALLSTAT(int,stack,stack_sz); +#endif + + if (n <= 2) return FALSE; + if (m == 1) return isbiconnected1(g,n); + +#if !MAXN + DYNALLOC1(int,num,num_sz,n,"isbiconnected"); + DYNALLOC1(int,lp,lp_sz,n,"isbiconnected"); + DYNALLOC1(int,stack,stack_sz,n,"isbiconnected"); +#endif + + num[0] = 0; + for (v = 1; v < n; ++v) num[v] = -1; + lp[0] = 0; + numvis = 1; + sp = 0; + v = 0; + vc = -1; + gv = (set*)g; + + for (;;) + { + vc = nextelement(gv,m,vc); + if (vc < 0) + { + if (sp <= 1) return numvis == n; + vc = v; + v = stack[--sp]; + gv = GRAPHROW(g,v,m); + if (lp[vc] >= num[v]) return FALSE; + if (lp[vc] < lp[v]) lp[v] = lp[vc]; + } + else if (num[vc] < 0) + { + stack[++sp] = vc; + v = vc; + gv = GRAPHROW(g,v,m); + vc = -1; + lp[v] = num[v] = numvis++; + } + else if (vc != v) + { + if (num[vc] < lp[v]) lp[v] = num[vc]; + } + } +} + +/**************************************************************************/ + +boolean +twocolouring(graph *g, int *colour, int m, int n) +/* If g is bipartite, set colour[*] to 0 or 1 to indicate an example + of 2-colouring and return TRUE. Otherwise return FALSE. + Colour 0 is assigned to the first vertex of each component. */ +{ + int i,head,tail,v,w,need; + set *gw; + setword xg; +#if MAXN + int queue[MAXN]; +#else + DYNALLSTAT(int,queue,queue_sz); +#endif + +#if !MAXN + DYNALLOC1(int,queue,queue_sz,n,"twocolouring"); +#endif + + if (n == 0) return TRUE; + for (i = 0; i < n; ++i) colour[i] = -1; + + if (m == 1) + { + for (v = 0; v < n; ++v) + if (colour[v] < 0) + { + queue[0] = v; + colour[v] = 0; + + head = 0; + tail = 1; + while (head < tail) + { + w = queue[head++]; + need = 1 - colour[w]; + xg = g[w]; + while (xg) + { + TAKEBIT(i,xg); + if (colour[i] < 0) + { + colour[i] = need; + queue[tail++] = i; + } + else if (colour[i] != need) + return FALSE; + } + } + } + } + else + { + for (v = 0; v < n; ++v) + if (colour[v] < 0) + { + queue[0] = v; + colour[v] = 0; + + head = 0; + tail = 1; + while (head < tail) + { + w = queue[head++]; + need = 1 - colour[w]; + gw = GRAPHROW(g,w,m); + for (i = -1; (i = nextelement(gw,m,i)) >= 0;) + { + if (colour[i] < 0) + { + colour[i] = need; + queue[tail++] = i; + } + else if (colour[i] != need) + return FALSE; + } + } + } + } + + return TRUE; +} + +/**************************************************************************/ + +boolean +isbipartite(graph *g, int m, int n) +/* Test if g is bipartite */ +{ +#if MAXN + int colour[MAXN]; +#else + DYNALLSTAT(int,colour,colour_sz); + + DYNALLOC1(int,colour,colour_sz,n,"isbipartite"); +#endif + + return twocolouring(g,colour,m,n); +} + +/**************************************************************************/ + +int +bipartiteside(graph *g, int m, int n) +/* If g is not bipartite, return 0. + Otherwise return the size of the smallest of the two parts + of a 2-coluring for which this value is smallest. */ +{ + int i,head,tail,v,w,need; + set *gw; + setword xg; + int ans,col[2]; +#if MAXN + int queue[MAXN]; + int colour[MAXN]; +#else + DYNALLSTAT(int,queue,queue_sz); + DYNALLSTAT(int,colour,colour_sz); +#endif + +#if !MAXN + DYNALLOC1(int,queue,queue_sz,n,"twocolouring"); + DYNALLOC1(int,colour,colour_sz,n,"isbipartite"); +#endif + + if (n == 0) return 0; + for (i = 0; i < n; ++i) colour[i] = -1; + ans = 0; + + if (m == 1) + { + for (v = 0; v < n; ++v) + { + if (colour[v] < 0) + { + queue[0] = v; + colour[v] = 0; + col[0] = 1; col[1] = 0; + + head = 0; + tail = 1; + while (head < tail) + { + w = queue[head++]; + need = 1 - colour[w]; + xg = g[w]; + while (xg) + { + TAKEBIT(i,xg); + if (colour[i] < 0) + { + colour[i] = need; + ++col[need]; + queue[tail++] = i; + } + else if (colour[i] != need) + return 0; + } + } + if (col[0] <= col[1]) ans += col[0]; + else ans += col[1]; + } + } + } + else + { + for (v = 0; v < n; ++v) + { + if (colour[v] < 0) + { + queue[0] = v; + colour[v] = 0; + col[0] = 1; col[1] = 0; + + head = 0; + tail = 1; + while (head < tail) + { + w = queue[head++]; + need = 1 - colour[w]; + gw = GRAPHROW(g,w,m); + for (i = -1; (i = nextelement(gw,m,i)) >= 0;) + { + if (colour[i] < 0) + { + colour[i] = need; + ++col[need]; + queue[tail++] = i; + } + else if (colour[i] != need) + return 0; + } + } + if (col[0] <= col[1]) ans += col[0]; + else ans += col[1]; + } + } + } + + return ans; +} + +/**************************************************************************/ + +int +girth(graph *g, int m, int n) +/* Find the girth of graph g. 0 means acyclic. */ +{ + int i,head,tail,v,w; + int best,c,dw1; + set *gw; +#if MAXN + int dist[MAXN],queue[MAXN]; +#else + DYNALLSTAT(int,queue,queue_sz); + DYNALLSTAT(int,dist,dist_sz); + + DYNALLOC1(int,queue,queue_sz,n,"girth"); + DYNALLOC1(int,dist,dist_sz,n,"girth"); +#endif + + if (n == 0) return 0; + best = n+3; + + for (v = 0; v < n; ++v) + { + for (i = 0; i < n; ++i) dist[i] = -1; + + queue[0] = v; + dist[v] = 0; + + head = 0; + tail = 1; + while (head < tail) + { + w = queue[head++]; + gw = GRAPHROW(g,w,m); + dw1 = dist[w] + 1; + for (i = -1; (i = nextelement(gw,m,i)) >= 0;) + { + if (dist[i] < 0) + { + dist[i] = dw1; + queue[tail++] = i; + } + else if (dist[i] >= dist[w]) + { + c = dw1 + dist[i]; + if (c < best) best = c; + if ((c & 1) != 0 || c > best) break; + } + } + if (i >= 0) break; + } + if (best == 3) return 3; + } + + return (best > n ? 0 : best); +} + +/**************************************************************************/ + +void +find_dist(graph *g, int m, int n, int v, int *dist) +/* Put in dist[0..n-1] the distance of each vertex from v. + Vertices in a different component are given the distance n. */ +{ + int i,head,tail,w; + set *gw; +#if MAXN + int queue[MAXN]; +#else + DYNALLSTAT(int,queue,queue_sz); +#endif + +#if !MAXN + DYNALLOC1(int,queue,queue_sz,n,"isconnected"); +#endif + + if (n == 0) return; + for (i = 0; i < n; ++i) dist[i] = n; + + queue[0] = v; + dist[v] = 0; + + head = 0; + tail = 1; + while (tail < n && head < tail) + { + w = queue[head++]; + gw = GRAPHROW(g,w,m); + for (i = -1; (i = nextelement(gw,m,i)) >= 0;) + { + if (dist[i] == n) + { + dist[i] = dist[w] + 1; + queue[tail++] = i; + } + } + } +} + +/**************************************************************************/ + +void +find_dist2(graph *g, int m, int n, int v, int w, int *dist) +/* Put in dist[0..n-1] the distance of each vertex from {v,w}. + Vertices in a different component are given the distance n. */ +{ + int i,head,tail,x; + set *gx; +#if MAXN + int queue[MAXN]; +#else + DYNALLSTAT(int,queue,queue_sz); +#endif + +#if !MAXN + DYNALLOC1(int,queue,queue_sz,n,"isconnected"); +#endif + + if (n == 0) return; + for (i = 0; i < n; ++i) dist[i] = n; + + queue[0] = v; + queue[1] = w; + dist[v] = dist[w] = 0; + + head = 0; + tail = 2; + while (tail < n && head < tail) + { + x = queue[head++]; + gx = GRAPHROW(g,x,m); + for (i = -1; (i = nextelement(gx,m,i)) >= 0;) + { + if (dist[i] == n) + { + dist[i] = dist[x] + 1; + queue[tail++] = i; + } + } + } +} + +/**************************************************************************/ + +int +numcomponents1(graph *g, int n) +/* Find number of components of undirected graph; case m=1 */ +{ + setword notvisited,queue; + int nc,i; + + nc = 0; + notvisited = ALLMASK(n); + + while (notvisited) + { + ++nc; + queue = SWHIBIT(notvisited); + notvisited &= ~queue; + while (queue) + { + TAKEBIT(i,queue); + notvisited &= ~bit[i]; + queue |= g[i] & notvisited; + } + } + + return nc; +} + +int +numcomponents(graph *g, int m, int n) +/* Find number of components of undirected graph */ +{ + int i,nc,head,tail,v,w; + set *gw; +#if MAXN + int queue[MAXN]; + set notvisited[MAXM+1]; /* +1 to satisfy gcc12 on ARM */ +#else + DYNALLSTAT(int,queue,queue_sz); + DYNALLSTAT(set,notvisited,notvisited_sz); +#endif + + if (n == 0) return 0; + if (m == 1) return numcomponents1(g,n); + +#if !MAXN + DYNALLOC1(int,queue,queue_sz,n,"numcomponents"); + DYNALLOC1(set,notvisited,notvisited_sz,m,"numcomponents"); +#endif + + EMPTYSET(notvisited,m); + for (i = 0; i < n; ++i) ADDELEMENT(notvisited,i); + + nc = 0; + + for (v = -1; (v = nextelement(notvisited,m,v)) >= 0;) + { + ++nc; + queue[0] = v; + + head = 0; + tail = 1; + while (head < tail) + { + w = queue[head++]; + gw = GRAPHROW(g,w,m); + for (i = -1; (i = nextelement(gw,m,i)) >= 0;) + { + if (ISELEMENT(notvisited,i)) + { + DELELEMENT(notvisited,i); + queue[tail++] = i; + } + } + } + } + + return nc; +} + +/**************************************************************************/ + +void +diamstats(graph *g, int m, int n, int *radius, int *diameter) +/* Find the radius and diameter. Both -1 if g is disconnected. + We use an O(mn) algorithm, which is pretty disgraceful. */ +{ + int v,i,head,tail,w; + int ecc,diam,rad; + set *gw; +#if MAXN + int queue[MAXN],dist[MAXN]; +#else + DYNALLSTAT(int,queue,queue_sz); + DYNALLSTAT(int,dist,dist_sz); +#endif + + /* if (m == 1) {diamstats1(g,n,radius,diameter); return; } */ + +#if !MAXN + DYNALLOC1(int,queue,queue_sz,n,"isconnected"); + DYNALLOC1(int,dist,dist_sz,n,"isconnected"); +#endif + + if (n == 0) + { + *radius = *diameter = 0; + return; + } + + diam = -1; + rad = n; + + for (v = 0; v < n; ++v) + { + for (i = 0; i < n; ++i) dist[i] = -1; + + queue[0] = v; + dist[v] = 0; + + head = 0; + tail = 1; + while (tail < n && head < tail) + { + w = queue[head++]; + gw = GRAPHROW(g,w,m); + for (i = -1; (i = nextelement(gw,m,i)) >= 0;) + { + if (dist[i] < 0) + { + dist[i] = dist[w] + 1; + queue[tail++] = i; + } + } + } + + if (tail < n) + { + *diameter = *radius = -1; + return; + } + + ecc = dist[queue[n-1]]; + + if (ecc > diam) diam = ecc; + if (ecc < rad) rad = ecc; + } + + *diameter = diam; + *radius = rad; +} + +/**************************************************************************/ + +static long +maxclnode1(graph *g, setword cliq, setword cov, int maxv) +/* Internal search node. cov has all the vertices outside cliq that + * cover all of cliq. maxv is the last vertex of cliq. + */ +{ + long ans; + int i; + setword w; + + if (cov == 0) return 1; + + ans = 0; + w = cov & BITMASK(maxv); + while (w) + { + TAKEBIT(i,w); + ans += maxclnode1(g,cliq|bit[i],cov&g[i]&~bit[i],i); + } + return ans; +} + +long +maxcliques(graph *g, int m, int n) +/* Find the number of maximal cliques */ +{ + int i; + long ans; + + if (n == 0) return 0; + + if (m == 1) + { + ans = 0; + for (i = 0; i < n; ++i) + ans += maxclnode1(g,bit[i],g[i],i); + } + else + { + fprintf(stderr,">E maxcliques() is only implemented for m=1\n"); + exit(1); + } + + return ans; +} + +/**************************************************************************/ + +static void +maxcsnode1(int *best, graph *g, setword cliq, setword cov, int maxv) +/* Internal search node. cov has all the vertices outside cliq that + * cover all of cliq. maxv is the last vertex of cliq. + * *best is the largest clique known so far. + */ +{ + int i,s; + setword w; + + if (cov == 0) return; + + w = cov & BITMASK(maxv); + + s = POPCOUNT(cliq); + if (s + POPCOUNT(w) <= *best) return; + if (w) + { + if (s + 1 > *best) *best = s + 1; + while (w) + { + TAKEBIT(i,w); + maxcsnode1(best,g,cliq|bit[i],cov&g[i]&~bit[i],i); + } + } +} + +int +maxcliquesize(graph *g, int m, int n) +/* Find the largest clique size */ +{ + int i,best; + + if (n == 0) return 0; + + if (m == 1) + { + best = 1; + for (i = 0; i < n; ++i) + maxcsnode1(&best,g,bit[i],g[i],i); + } + else + { + fprintf(stderr,">E maxcliquesize() is only implemented for m=1\n"); + exit(1); + } + + return best; +} + +int +maxindsetsize(graph *g, int m, int n) +/* Find the largest independent set size */ +{ + int i,best; + graph gc[WORDSIZE]; + setword all; + + if (n == 0) return 0; + + if (m == 1) + { + all = ALLMASK(n); + for (i = 0; i < n; ++i) gc[i] = g[i] ^ all ^ bit[i]; + + best = 1; + for (i = 0; i < n; ++i) + maxcsnode1(&best,gc,bit[i],gc[i],i); + } + else + { + fprintf(stderr,">E maxindsetsize() is only implemented for m=1\n"); + exit(1); + } + + return best; +} diff --git a/graph-checker/nauty/gutil2.c b/graph-checker/nauty/gutil2.c new file mode 100644 index 0000000..87af574 --- /dev/null +++ b/graph-checker/nauty/gutil2.c @@ -0,0 +1,762 @@ +/* gutil2.c: Some more graph utilities. */ + +#include "gtools.h" +#include "gutils.h" + +/**************************************************************************/ + +long +digoncount(graph *g, int m, int n) +/* Number of digons (cycles of length 2). Useful for digraphs. */ +{ + int i,j; + set *gi; + setword w; + long ans; + + ans = 0; + + if (m == 1) + { + for (i = 0; i < n; ++i) + { + w = g[i] & BITMASK(i); + while (w) + { + TAKEBIT(j,w); + if ((g[j] & bit[i])) ++ans; + } + } + } + else + { + for (i = 0, gi = g; i < n; ++i, gi += m) + { + for (j = i; (j = nextelement(gi,m,j)) > 0; ) + if (ISELEMENT(g+j*m,i)) ++ans; + } + } + + return ans; +} + +/**************************************************************************/ + +int +loopcount(graph *g, int m, int n) +/* Number of loops */ +{ + set *gi; + int i,nl; + + nl = 0; + for (i = 0, gi = g; i < n; ++i, gi += m) + if (ISELEMENT(gi,i)) ++nl; + + return nl; +} + +/**************************************************************************/ + +long +pathcount1(graph *g, int start, setword body, setword last) +/* Number of paths in g starting at start, lying within body and + ending in last. {start} and last should be disjoint subsets of body. */ +{ + long count; + setword gs,w; + int i; + + gs = g[start]; + w = gs & last; + count = POPCOUNT(w); + + body &= ~bit[start]; + w = gs & body; + while (w) + { + TAKEBIT(i,w); + count += pathcount1(g,i,body,last&~bit[i]); + } + + return count; +} + +/**************************************************************************/ + +long +cyclecount1(graph *g, int n) +/* The total number of cycles in g (assumed no loops), m=1 only */ +{ + setword body,nbhd; + long total; + int i,j; + + body = ALLMASK(n); + total = 0; + + for (i = 0; i < n-2; ++i) + { + body ^= bit[i]; + nbhd = g[i] & body; + while (nbhd) + { + TAKEBIT(j,nbhd); + total += pathcount1(g,j,body,nbhd); + } + } + + return total; +} + +/**************************************************************************/ + +long +cyclecount(graph *g, int m, int n) +/* The total number of cycles in g (assumed no loops) */ +{ + if (n == 0) return 0; + if (m == 1) return cyclecount1(g,n); + + gt_abort(">E cycle counting is only implemented for n <= WORDSIZE\n"); + return 0; +} + +/**************************************************************************/ + +long +indpathcount1(graph *g, int start, setword body, setword last) +/* Number of induced paths in g starting at start, extravertices within + * body and ending in last. + * {start}, body and last should be disjoint. */ +{ + long count; + setword gs,w; + int i; + + gs = g[start]; + w = gs & last; + count = POPCOUNT(w); + + w = gs & body; + while (w) + { + TAKEBIT(i,w); + count += indpathcount1(g,i,body&~gs,last&~bit[i]&~gs); + } + + return count; +} + +/**************************************************************************/ + +long +indcyclecount1(graph *g, int n) +/* The total number of induced cycles in g (assumed no loops), m=1 only */ +{ + setword body,last,cni; + long total; + int i,j; + + body = ALLMASK(n); + total = 0; + + for (i = 0; i < n-2; ++i) + { + body ^= bit[i]; + last = g[i] & body; + cni = g[i] | bit[i]; + while (last) + { + TAKEBIT(j,last); + total += indpathcount1(g,j,body&~cni,last); + } + } + + return total; +} + +/**************************************************************************/ + +long +indcyclecount(graph *g, int m, int n) +/* The total number of induced cycles in g (assumed no loops) */ +{ + if (n == 0) return 0; + if (m == 1) return indcyclecount1(g,n); + + gt_abort( + ">E induced cycle counting is only implemented for n <= WORDSIZE\n"); + return 0; +} + +/**************************************************************************/ + +long +numind3sets1(graph *g, int n) +/* The number of triangles in g; undirected only */ +{ + int i,j; + setword gi,w; + long total; + + total = 0; + for (i = 2; i < n; ++i) + { + gi = ALLMASK(i) & ~g[i]; + while (gi) + { + TAKEBIT(j,gi); + w = gi & ~g[j]; + total += POPCOUNT(w); + } + } + + return total; +} + +/**************************************************************************/ + +long +numind3sets(graph *g, int m, int n) +/* The number of independent 3-sets in g; undirected only */ +{ + if (m == 1) return numind3sets1(g,n); + + gt_abort(">E numind3sets is only implemented for n <= WORDSIZE\n"); + return 0; +} + +/**************************************************************************/ + +long +numtriangles1(graph *g, int n) +/* The number of triangles in g; undirected only */ +{ + int i,j; + setword gi,w; + long total; + + total = 0; + for (i = 0; i < n-2; ++i) + { + gi = g[i] & BITMASK(i); + while (gi) + { + TAKEBIT(j,gi); + w = g[j] & gi; + total += POPCOUNT(w); + } + } + + return total; +} + +/**************************************************************************/ + +long +numtriangles(graph *g, int m, int n) +/* The number of triangles in g; undirected only */ +{ + int i,j,k,kw; + setword *gi,*gj,w; + long total; + + if (m == 1) return numtriangles1(g,n); + + total = 0; + for (i = 0, gi = g; i < n-2; ++i, gi += m) + for (j = i; (j = nextelement(gi,m,j)) > 0; ) + { + gj = GRAPHROW(g,j,m); + kw = SETWD(j); + w = gi[kw] & gj[kw] & BITMASK(SETBT(j)); + if (w) total += POPCOUNT(w); + for (k = kw+1; k < m; ++k) + { + w = gi[k] & gj[k]; + total += POPCOUNT(w); + } + } + + return total; +} + +/**************************************************************************/ + +long +numdirtriangles1(graph *g, int n) +{ + long total; + int i,j,k; + setword biti,bm,wi,wj; + + total = 0; + for (i = 0; i < n; ++i) + { + biti = bit[i]; + bm = BITMASK(i); + wi = g[i] & bm; + while (wi) + { + TAKEBIT(j,wi); + wj = g[j] & bm; + while (wj) + { + TAKEBIT(k,wj); + if ((g[k] & biti)) ++total; + } + } + } + + return total; +} + +/**************************************************************************/ + +long +numdirtriangles(graph *g, int m, int n) +/* The number of directed triangles in g */ +{ + long total; + int i,j,k; + set *gi,*gj; + + if (m == 1) return numdirtriangles1(g,n); + + total = 0; + for (i = 0, gi = g; i < n-2; ++i, gi += m) + for (j = i; (j = nextelement(gi,m,j)) >= 0;) + { + gj = GRAPHROW(g,j,m); + for (k = i; (k = nextelement(gj,m,k)) >= 0; ) + if (k != j && ISELEMENT(GRAPHROW(g,k,m),i)) ++total; + } + + return total; +} + +/**************************************************************************/ + +void +commonnbrs(graph *g, int *minadj, int *maxadj, int *minnon, int *maxnon, + int m, int n) +/* Count the common neighbours of pairs of vertices, and give the minimum + and maximum for adjacent and non-adjacent vertices. Undirected only. + Null minimums are n+1 and null maximums are -1. +*/ +{ + int j,k; + int mina,maxa,minn,maxn; + int cn; + set *gi,*gj; + setword w; + + if (n == 0) + { + *minadj = *maxadj = *minnon = *maxnon = 0; + return; + } + + mina = minn = n+1; + maxa = maxn = -1; + + for (j = 0, gj = g; j < n; ++j, gj += m) + for (gi = g; gi != gj; gi += m) + { + cn = 0; + for (k = 0; k < m; ++k) + { + w = gi[k] & gj[k]; + if (w) cn += POPCOUNT(w); + } + + if (ISELEMENT(gi,j)) + { + if (cn < mina) mina = cn; + if (cn > maxa) maxa = cn; + } + else + { + if (cn < minn) minn = cn; + if (cn > maxn) maxn = cn; + } + } + + *minadj = mina; + *maxadj = maxa; + *minnon = minn; + *maxnon = maxn; +} + +/**************************************************************************/ + +void +delete1(graph *g, graph *h, int v, int n) +/* Delete vertex v from g, result in h */ +{ + setword mask1,mask2,gi; + int i; + + mask1 = ALLMASK(v); + mask2 = BITMASK(v); + + for (i = 0; i < v; ++i) + { + gi = g[i]; + h[i] = (gi & mask1) | ((gi & mask2) << 1); + } + for (i = v; i < n-1; ++i) + { + gi = g[i+1]; + h[i] = (gi & mask1) | ((gi & mask2) << 1); + } +} + +/**************************************************************************/ + +void +contract1(graph *g, graph *h, int v, int w, int n) +/* Contract distinct vertices v and w (not necessarily adjacent) + with result in h. No loops are created. */ +{ + int x,y; + setword bitx,bity,mask1,mask2; + int i; + + if (w < v) + { + x = w; + y = v; + } + else + { + x = v; + y = w; + } + + bitx = bit[x]; + bity = bit[y]; + mask1 = ALLMASK(y); + mask2 = BITMASK(y); + + for (i = 0; i < n; ++i) + if (g[i] & bity) + h[i] = (g[i] & mask1) | bitx | ((g[i] & mask2) << 1); + else + h[i] = (g[i] & mask1) | ((g[i] & mask2) << 1); + + h[x] |= h[y]; + for (i = y+1; i < n; ++i) h[i-1] = h[i]; + h[x] &= ~bitx; +} + +/**************************************************************************/ + +static TLS_ATTR int knm[18][16]; /* knm[n,m] = conncontent(K_n - m*K_2) */ +static TLS_ATTR boolean knm_computed = FALSE; + +int +conncontent(graph *g, int m, int n) +/* number of connected spanning subgraphs with an even number + of edges minus the number with an odd number of edges */ +{ + graph h[WORDSIZE]; + setword gj; + int i,j,v1,v2,x,y; + int minv,mindeg,deg,goodv; + long ne; + + if (m > 1) ABORT("conncontent only implemented for m=1"); + + /* First handle tiny graphs */ + + if (n <= 3) + { + if (n == 1) return 1; + if (n == 2) return (g[0] ? -1 : 0); + if (!g[0] || !g[1] || !g[2]) return 0; /* disconnected */ + if (g[0]^g[1]^g[2]) return 1; /* path */ + return 2; /* triangle */ + } + + /* Now compute + ne = number of edges + mindeg = minimum degree + minv = a vertex of minimum degree + goodv = a vertex with a clique neighbourhood (-1 if none) + */ + + mindeg = n; + ne = 0; + goodv = -1; + for (j = 0; j < n; ++j) + { + gj = g[j]; + deg = POPCOUNT(gj); + ne += deg; + if (deg < mindeg) + { + mindeg = deg; + minv = j; + if (deg == 1) goodv = j; + } + if (deg >= 3 && deg <= 4 && goodv < 0) + { + while (gj) + { + TAKEBIT(i,gj); + if (gj & ~g[i]) break; + } + if (!gj) goodv = j; + } + } + ne /= 2; + +/* Cases of isolated vertex or tree */ + + if (mindeg == 0) return 0; + +#if 0 + if (mindeg == 1 && ne == n-1) + { + if (isconnected1(g,n)) return ((n&1) ? 1 : -1); + else return 0; + } +#endif + +/* Cases of clique and near-clique */ + + if (mindeg == n-1) + { + j = -1; + for (i = 2; i < n; ++i) j *= -i; + return j; + } + + if (mindeg == n-2 && n < 16) + { + if (!knm_computed) + { + knm_computed = TRUE; + knm[1][0] = 1; + for (i = 2; i < 16; ++i) + { + knm[i][0] = -knm[i-1][0] * (i-1); + for (j = 1; j+j <= i; ++j) + knm[i][j] = knm[i][j-1] + knm[i-1][j-1]; + } + } + return knm[n][(n*n-n)/2-ne]; + } + +/* Case of vertex with clique neighbourhood */ + + if (goodv >= 0) + { + delete1(g,h,goodv,n); + return -POPCOUNT(g[goodv]) * conncontent(h,m,n-1); + } + +/* Case of minimum degree 2 */ + + if (mindeg == 2) + { + x = FIRSTBITNZ(g[minv]); + y = FIRSTBITNZ(g[minv]^bit[x]); + if (x > minv) --x; + if (y > minv) --y; + delete1(g,h,minv,n); + v1 = conncontent(h,m,n-1); + if (h[x] & bit[y]) return -2*v1; /* adjacent neighbours */ + + h[x] |= bit[y]; + h[y] |= bit[x]; + v2 = conncontent(h,m,n-1); + return -v1 - v2; + } + +/* Case of more than 2/3 dense but not complete */ + + if (3*ne > n*n-n) + { + j = FIRSTBITNZ(g[minv] ^ bit[minv] ^ ALLMASK(n)); /* non-neighbour */ + + g[minv] ^= bit[j]; + g[j] ^= bit[minv]; + v1 = conncontent(g,m,n); + g[minv] ^= bit[j]; + g[j] ^= bit[minv]; + + contract1(g,h,minv,j,n); + v2 = conncontent(h,m,n-1); + + return v1 + v2; + } + +/* All remaining cases */ + + j = FIRSTBITNZ(g[minv]); /* neighbour */ + + g[minv] ^= bit[j]; + g[j] ^= bit[minv]; + v1 = conncontent(g,m,n); + g[minv] ^= bit[j]; + g[j] ^= bit[minv]; + + contract1(g,h,minv,j,n); + v2 = conncontent(h,m,n-1); + + return v1 - v2; +} + +boolean +stronglyconnected(graph *g, int m, int n) +/* test if digraph g is strongly connected */ +{ + int sp,v,vc; + int numvis; + set *gv; +#if MAXN + int num[MAXN],lowlink[MAXN],stack[MAXN]; +#else + DYNALLSTAT(int,num,num_sz); + DYNALLSTAT(int,lowlink,lowlink_sz); + DYNALLSTAT(int,stack,stack_sz); +#endif + +#if !MAXN + DYNALLOC1(int,num,num_sz,n,"stronglyconnected"); + DYNALLOC1(int,lowlink,lowlink_sz,n,"stronglyconnected"); + DYNALLOC1(int,stack,stack_sz,n,"stronglyconnected"); +#endif + + if (n == 0) return FALSE; + + num[0] = 0; + for (v = 1; v < n; ++v) num[v] = -1; + lowlink[0] = 0; + numvis = 1; + sp = 0; + v = 0; + vc = -1; + gv = (set*)g; + + for (;;) + { + vc = nextelement(gv,m,vc); + if (vc < 0) + { + if (sp == 0) break; + if (lowlink[v] == num[v]) return FALSE; + vc = v; + v = stack[--sp]; + gv = GRAPHROW(g,v,m); + if (lowlink[vc] < lowlink[v]) lowlink[v] = lowlink[vc]; + } + else if (num[vc] < 0) + { + stack[++sp] = vc; + v = vc; + gv = GRAPHROW(g,v,m); + vc = -1; + lowlink[v] = num[v] = numvis++; + } + else if (vc != v) + { + if (num[vc] < lowlink[v]) lowlink[v] = num[vc]; + } + } + + return numvis == n; +} + +/**************************************************************************/ + +long +numsquares(graph *g, int m, int n) +/* Number of 4-cycles. Undirected graphs only, loops ok. */ +{ + setword w,bitij; + int i,j,k; + graph *gi,*gj; + unsigned long total,t; + boolean iloop,jloop; + + total = 0; + + if (m == 1) + { + for (j = 1; j < n; ++j) + for (i = 0; i < j; ++i) + { + w = (g[i] & g[j]) & ~(bit[i] | bit[j]); + t = POPCOUNT(w); + total += t*(t-1)/2; + } + return (long)(total / 2); + } + else + { + for (j = 1, gj = g + m; j < n; ++j, gj += m) + { + jloop = ISELEMENT(gj,j); + if (jloop) DELELEMENT(gj,j); + for (i = 0, gi = g; i < j; ++i, gi += m) + { + iloop = ISELEMENT(gi,i); + if (iloop) DELELEMENT(gi,i); + t = 0; + for (k = 0; k < m; ++k) + t += POPCOUNT(gi[k]&gj[k]); + total += t*(t-1)/2; + if (iloop) ADDELEMENT(gi,i); + } + if (jloop) ADDELEMENT(gj,j); + } + return (long)(total / 2); + } +} + +/**************************************************************************/ + +long +numdiamonds(graph *g, int m, int n) +/* Number of diamonds (squares with diagonal). Undirected only, no loops. */ +{ + int i,j,k; + setword w; + long ans,c; + set *gi,*gj; + + ans = 0; + + if (m == 1) + { + for (i = 0; i < n; ++i) + { + w = g[i] & BITMASK(i); + while (w) + { + TAKEBIT(j,w); + c = POPCOUNT(g[i]&g[j]); + ans += c*(c-1)/2; + } + } + } + else + { + for (i = 0, gi = g; i < n; ++i, gi += m) + { + for (j = i; (j = nextelement(gi,m,j)) >= 0; ) + { + gj = g + m*j; + c = 0; + for (k = 0; k < m; ++k) c += POPCOUNT(gi[k]&gj[k]); + ans += c*(c-1)/2; + } + } + } + + return ans; +} diff --git a/graph-checker/nauty/gutils.h b/graph-checker/nauty/gutils.h new file mode 100644 index 0000000..0b44a04 --- /dev/null +++ b/graph-checker/nauty/gutils.h @@ -0,0 +1,65 @@ +/* gutils.h - procedure declarations for gutil1.c and gutil2.c */ + +#ifndef _GUTILS_H_ /* only process this file once */ +#define _GUTILS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +extern void degstats(graph*,int,int, + unsigned long*,int*,int*,int*,int*,boolean*); +extern void degstats2(graph*,boolean,int,int,unsigned long*,int*l, + int*,int*,int*,int*, int*, int*,int*,int*, boolean*); +extern void degstats3(graph*,int,int, + unsigned long*,int*,int*,int*,int*,int*); +extern void diamstats(graph*,int,int,int*,int*); +extern void find_dist(graph*,int,int,int,int*); +extern void find_dist2(graph*,int,int,int,int,int*); +extern int numcomponents(graph*,int,int); +extern int numcomponents1(graph*,int); +extern int girth(graph*,int,int); +extern boolean isbiconnected1(graph*,int); +extern boolean isbiconnected(graph*,int,int); +extern boolean isbipartite(graph*,int,int); +extern int bipartiteside(graph*,int,int); +extern boolean twocolouring(graph*,int*,int,int); +extern boolean isconnected1(graph*,int); +extern boolean isconnected(graph*,int,int); +extern boolean issubconnected(graph*,set*,int,int); +extern long maxcliques(graph*,int,int); +extern int maxcliquesize(graph*,int,int); +extern int maxindsetsize(graph*,int,int); +extern void sources_sinks(graph*,int,int,int*,int*); + +extern long digoncount(graph*,int,int); +extern int loopcount(graph*,int,int); +extern long pathcount1(graph*,int,setword,setword); +extern long cyclecount1(graph*,int); +extern long cyclecount(graph*,int,int); +extern long indpathcount1(graph*,int,setword,setword); +extern long indcyclecount1(graph*,int); +extern long indcyclecount(graph*,int,int); +extern void commonnbrs(graph*,int*,int*,int*,int*,int,int); +extern void contract1(graph*,graph*,int,int,int); +extern int cstarcontent(graph*,int,int); +extern long numtriangles1(graph*,int); +extern long numtriangles(graph*,int,int); +extern long numtriangles1(graph*,int); +extern long numind3sets(graph*,int,int); +extern long numind3sets1(graph*,int); +extern long numdirtriangles(graph*,int,int); +extern long numdirtriangles1(graph*,int); +extern long numsquares(graph*,int,int); +extern long numdiamonds(graph*,int,int); +extern void delete1(graph*,graph*,int,int); +extern int conncontent(graph*,int,int); +extern boolean stronglyconnected(graph*,int,int); + +/* extern int diameter_sg(sparsegraph*,int*,int*); */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/graph-checker/nauty/makefile b/graph-checker/nauty/makefile new file mode 100644 index 0000000..c805164 --- /dev/null +++ b/graph-checker/nauty/makefile @@ -0,0 +1,53 @@ +CC=gcc +CFLAGS= -O3 +W=-DWORDSIZE=32 +W1=-DMAXN=WORDSIZE -DWORDSIZE=32 +CCOBJ=${CC} -c ${CFLAGS} -o $@ +GTOOLSH=gtools.h nauty.h naututil.h nausparse.h naurng.h gutils.h \ + naugroup.h nautinv.h schreier.h nautycliquer.h traces.h \ + naugstrings.h planarity.h quarticirred28.h +NAUTYW1O=nautyW1.o nautilW1.o nausparseW.o naugraphW1.o schreierW.o naurng.o + +all: libnauty.a + +nautyW1.o: nauty.h schreier.h nauty.c + ${CCOBJ} ${W1} nauty.c +nautilW1.o: nauty.h nautil.c sorttemplates.c + ${CCOBJ} ${W1} nautil.c +nausparseW.o: nauty.h nausparse.h nausparse.c sorttemplates.c + ${CCOBJ} ${W} nausparse.c +naugraphW1.o: nauty.h naugraph.c + ${CCOBJ} ${W1} naugraph.c +schreierW.o : nauty.h naurng.h schreier.h schreier.c + ${CCOBJ} ${W} schreier.c +naurng.o: naurng.c nauty.h + ${CCOBJ} naurng.c +traces.o : nauty.h naurng.h schreier.h traces.h nausparse.h traces.c + ${CCOBJ} traces.c +gtoolsW.o : ${GTOOWSH} gtools.c + ${CCOBJ} ${W} gtools.c +nautinvW1.o: nauty.h naututil.h nautinv.c + ${CCOBJ} ${W1} nautinv.c +gutil1W1.o : ${GTOOWSH} gutils.h gutil1.c + ${CCOBJ} ${W1} gutil1.c +gutil2W1.o : ${GTOOWSH} gutils.h gutil2.c + ${CCOBJ} ${W1} gutil2.c +gtnautyW1.o : ${GTOOWSH} gtnauty.c sorttemplates.c + ${CCOBJ} ${W1} gtnauty.c +naugroupW.o : nauty.h naugroup.h naugroup.c + ${CCOBJ} ${W} naugroup.c +nautycliquerW.o : nauty.h naugroup.h naugroup.c nautycliquer.c nautycliquer.h + ${CCOBJ} ${W} nautycliquer.c +naututilW1.o: nauty.h naututil.h nausparse.h naututil.c + ${CCOBJ} ${W1} naututil.c + +libnauty.a: ${NAUTYW1O} traces.o gtoolsW.o naututilW1.o nautinvW1.o \ + gutil1W1.o gutil2W1.o gtnautyW1.o naugroupW.o nautycliquerW.o + rm -f libnauty.a + ${AR} crs libnauty.a ${NAUTYW1O} traces.o gtoolsW.o naututilW1.o \ + nautinvW1.o gutil1W1.o gutil2W1.o gtnautyW1.o naugroupW.o nautycliquerW.o + +clean: + rm -f libnauty.a ${NAUTYW1O} traces.o gtoolsW.o naututilW1.o \ + nautinvW1.o gutil1W1.o gutil2W1.o gtnautyW1.o \ + naugroupW.o nautycliquerW.o diff --git a/graph-checker/nauty/naugraph.c b/graph-checker/nauty/naugraph.c new file mode 100644 index 0000000..a880dae --- /dev/null +++ b/graph-checker/nauty/naugraph.c @@ -0,0 +1,717 @@ +/***************************************************************************** +* * +* Graph-specific auxiliary source file for version 2.8 of nauty. * +* * +* Copyright (1984-2019) Brendan McKay. All rights reserved. * +* Subject to waivers and disclaimers in nauty.h. * +* * +* CHANGE HISTORY * +* 16-Nov-00 : initial creation out of nautil.c * +* 22-Apr-01 : added aproto line for Magma * +* EXTDEFS is no longer required * +* removed dynamic allocation from refine1() * +* 21-Nov-01 : use NAUTYREQUIRED in naugraph_check() * +* 23-Nov-06 : add targetcell(); make bestcell() local * +* 10-Dec-06 : remove BIGNAUTY * +* 10-Nov-09 : remove shortish and permutation types * +* 23-May-10 : add densenauty() * +* 15-Jan-12 : add TLS_ATTR attributes * +* 23-Jan-13 : add some parens to make icc happy * +* 15-Oct-19 : fix default size of dnwork[] to match densenauty() * +* 6-Apr-21 : increase work space in densenauty() * +* * +*****************************************************************************/ + +#define ONE_WORD_SETS +#include "nauty.h" + + /* macros for hash-codes: */ +#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 + +/* aproto: header new_nauty_protos.h */ + +dispatchvec dispatch_graph = + {isautom,testcanlab,updatecan,refine,refine1,cheapautom,targetcell, + naugraph_freedyn,naugraph_check,NULL,NULL}; + +#if !MAXN +DYNALLSTAT(set,workset,workset_sz); +DYNALLSTAT(int,workperm,workperm_sz); +DYNALLSTAT(int,bucket,bucket_sz); +DYNALLSTAT(set,dnwork,dnwork_sz); +#else +static TLS_ATTR set workset[MAXM]; /* used for scratch work */ +static TLS_ATTR int workperm[MAXN]; +static TLS_ATTR int bucket[MAXN+2]; +static TLS_ATTR set dnwork[2*500*MAXM]; +#endif + +/***************************************************************************** +* * +* isautom(g,perm,digraph,m,n) = TRUE iff perm is an automorphism of g * +* (i.e., g^perm = g). Symmetry is assumed unless digraph = TRUE. * +* * +*****************************************************************************/ + +boolean +isautom(graph *g, int *perm, boolean digraph, int m, int n) +{ + set *pg; + int pos; + set *pgp; + int posp,i; + + for (pg = g, i = 0; i < n; pg += M, ++i) + { + pgp = GRAPHROW(g,perm[i],M); + pos = (digraph ? -1 : i); + + while ((pos = nextelement(pg,M,pos)) >= 0) + { + posp = perm[pos]; + if (!ISELEMENT(pgp,posp)) return FALSE; + } + } + return TRUE; +} + +/***************************************************************************** +* * +* testcanlab(g,canong,lab,samerows,m,n) compares g^lab to canong, * +* using an ordering which is immaterial since it's only used here. The * +* value returned is -1,0,1 if g^lab <,=,> canong. *samerows is set to * +* the number of rows (0..n) of canong which are the same as those of g^lab. * +* * +* GLOBALS ACCESSED: workset<rw>,permset(),workperm<rw> * +* * +*****************************************************************************/ + +int +testcanlab(graph *g, graph *canong, int *lab, int *samerows, int m, int n) +{ + int i,j; + set *ph; + +#if !MAXN + DYNALLOC1(int,workperm,workperm_sz,n,"testcanlab"); + DYNALLOC1(set,workset,workset_sz,m,"testcanlab"); +#endif + + for (i = 0; i < n; ++i) workperm[lab[i]] = i; + + for (i = 0, ph = canong; i < n; ++i, ph += M) + { + permset(GRAPHROW(g,lab[i],M),workset,M,workperm); + for (j = 0; j < M; ++j) + if (workset[j] < ph[j]) + { + *samerows = i; + return -1; + } + else if (workset[j] > ph[j]) + { + *samerows = i; + return 1; + } + } + + *samerows = n; + return 0; +} + +/***************************************************************************** +* * +* updatecan(g,canong,lab,samerows,m,n) sets canong = g^lab, assuming * +* the first samerows of canong are ok already. * +* * +* GLOBALS ACCESSED: permset(),workperm<rw> * +* * +*****************************************************************************/ + +void +updatecan(graph *g, graph *canong, int *lab, int samerows, int m, int n) +{ + int i; + set *ph; + +#if !MAXN + DYNALLOC1(int,workperm,workperm_sz,n,"updatecan"); +#endif + + for (i = 0; i < n; ++i) workperm[lab[i]] = i; + + for (i = samerows, ph = GRAPHROW(canong,samerows,M); + i < n; ++i, ph += M) + permset(GRAPHROW(g,lab[i],M),ph,M,workperm); +} + +/***************************************************************************** +* * +* refine(g,lab,ptn,level,numcells,count,active,code,m,n) performs a * +* refinement operation on the partition at the specified level of the * +* partition nest (lab,ptn). *numcells is assumed to contain the number of * +* cells on input, and is updated. The initial set of active cells (alpha * +* in the paper) is specified in the set active. Precisely, x is in active * +* iff the cell starting at index x in lab is active. * +* The resulting partition is equitable if active is correct (see the paper * +* and the Guide). * +* *code is set to a value which depends on the fine detail of the * +* algorithm, but which is independent of the labelling of the graph. * +* count is used for work space. * +* * +* GLOBALS ACCESSED: workset<w>,bit<r>,nextelement(),bucket<w>,workperm<w> * +* * +*****************************************************************************/ + +void +refine(graph *g, int *lab, int *ptn, int level, int *numcells, + int *count, set *active, int *code, int m, int n) +{ + +#if MAXM==1 + refine1(g,lab,ptn,level,numcells,count,active,code,m,n); +} +#else + + int i,c1,c2,labc1; + setword x; + set *set1,*set2; + int split1,split2,cell1,cell2; + int cnt,bmin,bmax; + long longcode; + set *gptr; + int maxcell,maxpos,hint; + +#if !MAXN + DYNALLOC1(int,workperm,workperm_sz,n,"refine"); + DYNALLOC1(set,workset,workset_sz,m,"refine"); + DYNALLOC1(int,bucket,bucket_sz,n+2,"refine"); +#endif + + longcode = *numcells; + split1 = -1; + hint = 0; + while (*numcells < n && ((split1 = hint, ISELEMENT(active,split1)) + || (split1 = nextelement(active,M,split1)) >= 0 + || (split1 = nextelement(active,M,-1)) >= 0)) + { + DELELEMENT(active,split1); + for (split2 = split1; ptn[split2] > level; ++split2) {} + longcode = MASH(longcode,split1+split2); + if (split1 == split2) /* trivial splitting cell */ + { + gptr = GRAPHROW(g,lab[split1],M); + for (cell1 = 0; cell1 < n; cell1 = cell2 + 1) + { + for (cell2 = cell1; ptn[cell2] > level; ++cell2) {} + if (cell1 == cell2) continue; + c1 = cell1; + c2 = cell2; + while (c1 <= c2) + { + labc1 = lab[c1]; + if (ISELEMENT(gptr,labc1)) + ++c1; + else + { + lab[c1] = lab[c2]; + lab[c2] = labc1; + --c2; + } + } + if (c2 >= cell1 && c1 <= cell2) + { + ptn[c2] = level; + longcode = MASH(longcode,c2); + ++*numcells; + if (ISELEMENT(active,cell1) || c2-cell1 >= cell2-c1) + { + ADDELEMENT(active,c1); + if (c1 == cell2) hint = c1; + } + else + { + ADDELEMENT(active,cell1); + if (c2 == cell1) hint = cell1; + } + } + } + } + + else /* nontrivial splitting cell */ + { + EMPTYSET(workset,m); + for (i = split1; i <= split2; ++i) + ADDELEMENT(workset,lab[i]); + longcode = MASH(longcode,split2-split1+1); + + for (cell1 = 0; cell1 < n; cell1 = cell2 + 1) + { + for (cell2 = cell1; ptn[cell2] > level; ++cell2) {} + if (cell1 == cell2) continue; + i = cell1; + set1 = workset; + set2 = GRAPHROW(g,lab[i],m); + cnt = 0; + for (c1 = m; --c1 >= 0;) + if ((x = ((*set1++) & (*set2++))) != 0) + cnt += POPCOUNT(x); + + count[i] = bmin = bmax = cnt; + bucket[cnt] = 1; + while (++i <= cell2) + { + set1 = workset; + set2 = GRAPHROW(g,lab[i],m); + cnt = 0; + for (c1 = m; --c1 >= 0;) + if ((x = ((*set1++) & (*set2++))) != 0) + cnt += POPCOUNT(x); + + while (bmin > cnt) bucket[--bmin] = 0; + while (bmax < cnt) bucket[++bmax] = 0; + ++bucket[cnt]; + count[i] = cnt; + } + if (bmin == bmax) + { + longcode = MASH(longcode,bmin+cell1); + continue; + } + c1 = cell1; + maxcell = -1; + for (i = bmin; i <= bmax; ++i) + if (bucket[i]) + { + c2 = c1 + bucket[i]; + bucket[i] = c1; + longcode = MASH(longcode,i+c1); + if (c2-c1 > maxcell) + { + maxcell = c2-c1; + maxpos = c1; + } + if (c1 != cell1) + { + ADDELEMENT(active,c1); + if (c2-c1 == 1) hint = c1; + ++*numcells; + } + if (c2 <= cell2) ptn[c2-1] = level; + c1 = c2; + } + for (i = cell1; i <= cell2; ++i) + workperm[bucket[count[i]]++] = lab[i]; + for (i = cell1; i <= cell2; ++i) lab[i] = workperm[i]; + if (!ISELEMENT(active,cell1)) + { + ADDELEMENT(active,cell1); + DELELEMENT(active,maxpos); + } + } + } + } + + longcode = MASH(longcode,*numcells); + *code = CLEANUP(longcode); +} +#endif /* else case of MAXM==1 */ + +/***************************************************************************** +* * +* refine1(g,lab,ptn,level,numcells,count,active,code,m,n) is the same as * +* refine(g,lab,ptn,level,numcells,count,active,code,m,n), except that * +* m==1 is assumed for greater efficiency. The results are identical in all * +* respects. See refine (above) for the specs. * +* * +*****************************************************************************/ + +void +refine1(graph *g, int *lab, int *ptn, int level, int *numcells, + int *count, set *active, int *code, int m, int n) +{ + int i,c1,c2,labc1; + setword x; + int split1,split2,cell1,cell2; + int cnt,bmin,bmax; + long longcode; + set *gptr,workset0; + int maxcell,maxpos,hint; + +#if !MAXN + DYNALLOC1(int,workperm,workperm_sz,n,"refine1"); + DYNALLOC1(int,bucket,bucket_sz,n+2,"refine1"); +#endif + + longcode = *numcells; + split1 = -1; + + hint = 0; + while (*numcells < n && ((split1 = hint, ISELEMENT1(active,split1)) + || (split1 = nextelement(active,1,split1)) >= 0 + || (split1 = nextelement(active,1,-1)) >= 0)) + { + DELELEMENT1(active,split1); + for (split2 = split1; ptn[split2] > level; ++split2) {} + longcode = MASH(longcode,split1+split2); + if (split1 == split2) /* trivial splitting cell */ + { + gptr = GRAPHROW(g,lab[split1],1); + for (cell1 = 0; cell1 < n; cell1 = cell2 + 1) + { + for (cell2 = cell1; ptn[cell2] > level; ++cell2) {} + if (cell1 == cell2) continue; + c1 = cell1; + c2 = cell2; + while (c1 <= c2) + { + labc1 = lab[c1]; + if (ISELEMENT1(gptr,labc1)) + ++c1; + else + { + lab[c1] = lab[c2]; + lab[c2] = labc1; + --c2; + } + } + if (c2 >= cell1 && c1 <= cell2) + { + ptn[c2] = level; + longcode = MASH(longcode,c2); + ++*numcells; + if (ISELEMENT1(active,cell1) || c2-cell1 >= cell2-c1) + { + ADDELEMENT1(active,c1); + if (c1 == cell2) hint = c1; + } + else + { + ADDELEMENT1(active,cell1); + if (c2 == cell1) hint = cell1; + } + } + } + } + + else /* nontrivial splitting cell */ + { + workset0 = 0; + for (i = split1; i <= split2; ++i) + ADDELEMENT1(&workset0,lab[i]); + longcode = MASH(longcode,split2-split1+1); + + for (cell1 = 0; cell1 < n; cell1 = cell2 + 1) + { + for (cell2 = cell1; ptn[cell2] > level; ++cell2) {} + if (cell1 == cell2) continue; + i = cell1; + if ((x = workset0 & g[lab[i]]) != 0) + cnt = POPCOUNT(x); + else + cnt = 0; + count[i] = bmin = bmax = cnt; + bucket[cnt] = 1; + while (++i <= cell2) + { + if ((x = workset0 & g[lab[i]]) != 0) + cnt = POPCOUNT(x); + else + cnt = 0; + while (bmin > cnt) bucket[--bmin] = 0; + while (bmax < cnt) bucket[++bmax] = 0; + ++bucket[cnt]; + count[i] = cnt; + } + if (bmin == bmax) + { + longcode = MASH(longcode,bmin+cell1); + continue; + } + c1 = cell1; + maxcell = -1; + for (i = bmin; i <= bmax; ++i) + if (bucket[i]) + { + c2 = c1 + bucket[i]; + bucket[i] = c1; + longcode = MASH(longcode,i+c1); + if (c2-c1 > maxcell) + { + maxcell = c2-c1; + maxpos = c1; + } + if (c1 != cell1) + { + ADDELEMENT1(active,c1); + if (c2-c1 == 1) hint = c1; + ++*numcells; + } + if (c2 <= cell2) ptn[c2-1] = level; + c1 = c2; + } + for (i = cell1; i <= cell2; ++i) + workperm[bucket[count[i]]++] = lab[i]; + for (i = cell1; i <= cell2; ++i) lab[i] = workperm[i]; + if (!ISELEMENT1(active,cell1)) + { + ADDELEMENT1(active,cell1); + DELELEMENT1(active,maxpos); + } + } + } + } + + longcode = MASH(longcode,*numcells); + *code = CLEANUP(longcode); +} + +/***************************************************************************** +* * +* cheapautom(ptn,level,digraph,n) returns TRUE if the partition at the * +* specified level in the partition nest (lab,ptn) {lab is not needed here} * +* satisfies a simple sufficient condition for its cells to be the orbits of * +* some subgroup of the automorphism group. Otherwise it returns FALSE. * +* It always returns FALSE if digraph!=FALSE. * +* * +* nauty assumes that this function will always return TRUE for any * +* partition finer than one for which it returns TRUE. * +* * +*****************************************************************************/ + +boolean +cheapautom(int *ptn, int level, boolean digraph, int n) +{ + int i,k,nnt; + + if (digraph) return FALSE; + + k = n; + nnt = 0; + for (i = 0; i < n; ++i) + { + --k; + if (ptn[i] > level) + { + ++nnt; + while (ptn[++i] > level) {} + } + } + + return (k <= nnt + 1 || k <= 4); +} + +/***************************************************************************** +* * +* bestcell(g,lab,ptn,level,tc_level,m,n) returns the index in lab of the * +* start of the "best non-singleton cell" for fixing. If there is no * +* non-singleton cell it returns n. * +* This implementation finds the first cell which is non-trivially joined * +* to the greatest number of other cells. * +* * +* GLOBALS ACCESSED: bit<r>,workperm<rw>,workset<rw>,bucket<rw> * +* * +*****************************************************************************/ + +static int +bestcell(const graph *g, const int *lab, const int *ptn, int level, + int tc_level, int m, int n) +{ + int i; + set *gp; + setword setword1,setword2; + int v1,v2,nnt; + +#if !MAXN + DYNALLOC1(int,workperm,workperm_sz,n,"bestcell"); + DYNALLOC1(set,workset,workset_sz,m,"bestcell"); + DYNALLOC1(int,bucket,bucket_sz,n+2,"bestcell"); +#endif + + /* find non-singleton cells: put starts in workperm[0..nnt-1] */ + + i = nnt = 0; + + while (i < n) + { + if (ptn[i] > level) + { + workperm[nnt++] = i; + while (ptn[i] > level) ++i; + } + ++i; + } + + if (nnt == 0) return n; + + /* set bucket[i] to # non-trivial neighbours of n.s. cell i */ + + for (i = nnt; --i >= 0;) bucket[i] = 0; + + for (v2 = 1; v2 < nnt; ++v2) + { + EMPTYSET(workset,m); + i = workperm[v2] - 1; + do + { + ++i; + ADDELEMENT(workset,lab[i]); + } + while (ptn[i] > level); + for (v1 = 0; v1 < v2; ++v1) + { + gp = GRAPHROW(g,lab[workperm[v1]],m); +#if MAXM==1 + setword1 = *workset & *gp; + setword2 = *workset & ~*gp; +#else + setword1 = setword2 = 0; + for (i = m; --i >= 0;) + { + setword1 |= workset[i] & gp[i]; + setword2 |= workset[i] & ~gp[i]; + } +#endif + if (setword1 != 0 && setword2 != 0) + { + ++bucket[v1]; + ++bucket[v2]; + } + } + } + + /* find first greatest bucket value */ + + v1 = 0; + v2 = bucket[0]; + for (i = 1; i < nnt; ++i) + if (bucket[i] > v2) + { + v1 = i; + v2 = bucket[i]; + } + + return (int)workperm[v1]; +} + +/***************************************************************************** +* * +* targetcell(g,lab,ptn,level,tc_level,digraph,hint,m,n) returns the index * +* in lab of the next cell to split. * +* hint is a suggestion for the answer, which is obeyed if it is valid. * +* Otherwise we use bestcell() up to tc_level and the first non-trivial * +* cell after that. * +* * +*****************************************************************************/ + +int +targetcell(graph *g, int *lab, int *ptn, int level, + int tc_level, boolean digraph, int hint, int m, int n) +{ + int i; + + if (hint >= 0 && ptn[hint] > level && + (hint == 0 || ptn[hint-1] <= level)) + return hint; + else if (level <= tc_level) + return bestcell(g,lab,ptn,level,tc_level,m,n); + else + { + for (i = 0; i < n && ptn[i] <= level; ++i) {} + return (i == n ? 0 : i); + } +} + +/***************************************************************************** +* * +* densenauty(g,lab,ptn,orbits,&options,&stats,m,n,h) * +* is a slightly simplified interface to nauty(). It allocates enough * +* workspace for 500 automorphisms and checks that the densegraph dispatch * +* vector is in use. * +* * +*****************************************************************************/ + +void +densenauty(graph *g, int *lab, int *ptn, int *orbits, + optionblk *options, statsblk *stats, int m, int n, graph *h) +{ + if (options->dispatch != &dispatch_graph) + { + fprintf(ERRFILE,"Error: densenauty() needs standard options block\n"); + exit(1); + } + +#if !MAXN + /* Don't increase 2*500*m in the next line unless you also increase + the default declaration of dnwork[] earlier. */ + DYNALLOC1(set,dnwork,dnwork_sz,2*500*m,"densenauty malloc"); +#endif + + nauty(g,lab,ptn,NULL,orbits,options,stats,dnwork,2*500*m,m,n,h); +} + +/***************************************************************************** +* * +* naugraph_check() checks that this file is compiled compatibly with the * +* given parameters. If not, call exit(1). * +* * +*****************************************************************************/ + +void +naugraph_check(int wordsize, int m, int n, int version) +{ + if (wordsize != WORDSIZE) + { + fprintf(ERRFILE,"Error: WORDSIZE mismatch in naugraph.c\n"); + exit(1); + } + +#if MAXN + if (m > MAXM) + { + fprintf(ERRFILE,"Error: MAXM inadequate in naugraph.c\n"); + exit(1); + } + + if (n > MAXN) + { + fprintf(ERRFILE,"Error: MAXN inadequate in naugraph.c\n"); + exit(1); + } +#endif + + if (version < NAUTYREQUIRED) + { + fprintf(ERRFILE,"Error: naugraph.c version mismatch\n"); + exit(1); + } +} + +/***************************************************************************** +* * +* naugraph_freedyn() - free the dynamic memory in this module * +* * +*****************************************************************************/ + +void +naugraph_freedyn(void) +{ +#if !MAXN + DYNFREE(workset,workset_sz); + DYNFREE(workperm,workperm_sz); + DYNFREE(bucket,bucket_sz); + DYNFREE(dnwork,dnwork_sz); +#endif +} diff --git a/graph-checker/nauty/naugroup.c b/graph-checker/nauty/naugroup.c new file mode 100644 index 0000000..9ec2610 --- /dev/null +++ b/graph-checker/nauty/naugroup.c @@ -0,0 +1,527 @@ +/* naugroup.c + +Procedures for handling groups found by nauty. +*/ + +#include "naugroup.h" + +static permrec *freelist = NULL; +static int freelist_n = 0; + +static grouprec *group = NULL; +static int group_depth = 0; +DYNALLSTAT(cosetrec,coset,coset_sz); +static permrec *gens; +DYNALLSTAT(set,workset,workset_sz); +DYNALLSTAT(int,allp,allp_sz); +DYNALLSTAT(int,id,id_sz); + +/**************************************************************************/ + +permrec +*newpermrec(int n) +/* Get a permrec of order n. This procedure and the next one are +designed to be efficient if lots of group ops are done with the +same value of n. */ +{ + permrec *p; + + if (freelist_n != n) + { + while (freelist != NULL) + { + p = freelist; + freelist = freelist->ptr; + free(p); + } + freelist_n = n; + } + + if (freelist != NULL) + { + p = freelist; + freelist = freelist->ptr; + return p; + } + + p = (permrec*) malloc(sizeof(permrec)+(freelist_n-2)*sizeof(int)); + + if (p == NULL) + { + fprintf(ERRFILE,">E malloc failed in newpermrec()\n"); + exit(1); + } + + return p; +} + +/**************************************************************************/ + +void +freepermrec(permrec *p, int n) +/* Free a permrec of given size. */ +{ + permrec *q; + + if (p == NULL) return; + + if (freelist_n != n) + { + while (freelist) + { + q = freelist; + freelist = freelist->ptr; + free(q); + } + freelist_n = n; + } + + p->ptr = freelist; + freelist = p; +} + +/**************************************************************************/ + +grouprec * +groupptr(boolean cutloose) +/* Give the address of the group structure, cutting it loose + if requested. */ +{ + grouprec *p; + + p = group; + + if (cutloose) + { + group = NULL; + group_depth = 0; + coset = NULL; + coset_sz = 0; + } + + return p; +} + +/**************************************************************************/ + +void +freegroup(grouprec *grp) +/* Free (or pretend to free) group structure. */ +{ + int i,j; + cosetrec *p; + permrec *q,*qq; + + for (i = 0; i < grp->depth; ++i) + { + p = grp->levelinfo[i].replist; + if (p != NULL) + for (j = grp->levelinfo[i].orbitsize; --j >= 0; ) + { + freepermrec(p[j].rep,grp->n); + p[j].rep = NULL; + } + } + + if (grp->depth > 0) + { + p = grp->levelinfo[0].replist; + if (p != NULL && p != coset) + { + free(p); + grp->levelinfo[0].replist = NULL; + } + + q = grp->levelinfo[0].gens; + while (q != NULL) + { + qq = q; + q = q->ptr; + freepermrec(qq,grp->n); + } + grp->levelinfo[0].gens = NULL; + } +} + +/**************************************************************************/ + +void +groupautomproc(int count, int *perm, int *orbits, + int numorbits, int stabvertex, int n) +{ + permrec *p; + int i; + + p = newpermrec(n); + for (i = 0; i < n; ++i) p->p[i] = perm[i]; + p->ptr = gens; + gens = p; +} + +/**************************************************************************/ + +void +grouplevelproc(int *lab, int *ptn, int level, int *orbits, statsblk *stats, + int tv, int index, int tcellsize, int numcells, int cc, int n) +{ + int depth; + size_t sz; + + if (numcells == n) /* first call */ + { + depth = level - 1; + + if (group) freegroup(group); + + if (depth > group_depth || !group) + { + if (depth <= 1) sz = sizeof(grouprec); + else sz = sizeof(grouprec) + (depth-1)*sizeof(levelrec); + if (group) group = (grouprec*)realloc((void*)group,sz); + else group = (grouprec*)malloc(sz); + if (group == NULL) + { + fprintf(ERRFILE,">E malloc failed in grouplevelproc\n"); + exit(1); + } + group_depth = depth; + } + + group->n = n; + group->depth = depth; + gens = NULL; + return; + } + + group->levelinfo[level-1].fixedpt = tv; + group->levelinfo[level-1].orbitsize = index; + group->levelinfo[level-1].gens = gens; + group->levelinfo[level-1].replist = NULL; + + if (level == 1) group->numorbits = stats->numorbits; +} + +/**************************************************************************/ + +void +makecosetreps(grouprec *grp) +/* Make all coset representatives for this group */ +{ + int i,j,k,n,depth; + int l,index; + int *p,*q; + permrec *gen,*g; + cosetrec *cr; + int head,tail; + DYNALLSTAT(int,queue,queue_sz); + DYNALLSTAT(int,lab,lab_sz); + + n = grp->n; + depth = grp->depth; + + DYNALLOC1(int,queue,queue_sz,n,"malloc"); + DYNALLOC1(int,lab,lab_sz,n,"malloc"); + + j = 0; + for (i = 0; i < depth; ++i) + j += grp->levelinfo[i].orbitsize; + + if (j > 0) DYNALLOC1(cosetrec,coset,coset_sz,j,"malloc"); + + cr = coset; + for (i = 0; i < depth; ++i) + { + grp->levelinfo[i].replist = cr; + cr += grp->levelinfo[i].orbitsize; + } + + for (i = 0; i < depth; ++i) + { + cr = grp->levelinfo[i].replist; + gen = grp->levelinfo[i].gens; + for (j = 0; j < n; ++j) lab[j] = -1; + queue[0] = grp->levelinfo[i].fixedpt; + lab[queue[0]] = 0; + cr[0].image = queue[0]; + cr[0].rep = NULL; + head = 0; + tail = 1; + index = 0; + while (head < tail) + { + j = queue[head++]; + p = (cr[lab[j]].rep ? cr[lab[j]].rep->p : NULL); + for (g = gen; g != NULL; g = g->ptr) + { + k = g->p[j]; + if (lab[k] < 0) + { + ++index; + lab[k] = index; + queue[tail++] = k; + cr[index].image = k; + cr[index].rep = newpermrec(n); + q = cr[index].rep->p; + if (p == NULL) + for (l = 0; l < n; ++l) q[l] = g->p[l]; + else + for (l = 0; l < n; ++l) q[l] = g->p[p[l]]; + } + } + } + } +} + +/**************************************************************************/ + +int +permcycles(int *p, int n, int *len, boolean sort) +/* Puts in len[0..] the cycle lengths of p. If sort, sort them. + Return the number of cycles. */ +{ + int m,i,j,k,h,nc,leni; + + m = (n + WORDSIZE - 1) / WORDSIZE; + DYNALLOC1(set,workset,workset_sz,m,"malloc"); + + EMPTYSET(workset,m); + + nc = 0; + for (i = 0; i < n; ++i) + if (!ISELEMENT(workset,i)) + { + k = 1; + for (j = p[i]; j != i; j = p[j]) + { + ADDELEMENT(workset,j); + ++k; + } + len[nc++] = k; + } + + if (sort && nc > 1) + { + j = nc / 3; + h = 1; + do + h = 3 * h + 1; + while (h < j); + + do + { + for (i = h; i < nc; ++i) + { + leni = len[i]; + for (j = i; len[j-h] > leni; ) + { + len[j] = len[j-h]; + if ((j -= h) < h) break; + } + len[j] = leni; + } + h /= 3; + } + while (h > 0); + } + + return nc; +} + +/**************************************************************************/ + +static void +groupelts(levelrec *lr, int n, int level, void (*action)(int*,int), + int *before, int *after, int *id) +/* Recursive routine used by allgroup. */ +{ + int i,j,orbsize; + int *p,*cr; + cosetrec *coset; + + coset = lr[level].replist; + orbsize = lr[level].orbitsize; + + for (j = 0; j < orbsize; ++j) + { + cr = (coset[j].rep == NULL ? NULL : coset[j].rep->p); + if (before == NULL) + p = cr; + else if (cr == NULL) + p = before; + else + { + p = after; + for (i = 0; i < n; ++i) p[i] = cr[before[i]]; + } + + if (level == 0) + (*action)((p == NULL ? id : p),n); + else + groupelts(lr,n,level-1,action,p,after+n,id); + } +} + +/**************************************************************************/ + +void +allgroup(grouprec *grp, void (*action)(int*,int)) +/* Call action(p,n) for every element of the group, including the identity. + The identity is always the first call. */ +{ + int i,depth,n; + + depth = grp->depth; + n = grp->n; + + DYNALLOC1(int,id,id_sz,n,"malloc"); + for (i = 0; i < n; ++i) id[i] = i; + + if (depth == 0) + { + (*action)(id,n); + return; + } + + DYNALLOC1(int,allp,allp_sz,n*depth,"malloc"); + + groupelts(grp->levelinfo,n,depth-1,action,NULL,allp,id); +} + +/**************************************************************************/ + +static void +groupelts2(levelrec *lr, int n, int level, + void (*action)(int*,int,int*), int *before, + int *after, int *id, int *abort) +/* Recursive routine used by allgroup2. */ +{ + int i,j,orbsize; + int *p,*cr; + cosetrec *coset; + + coset = lr[level].replist; + orbsize = lr[level].orbitsize; + + for (j = 0; j < orbsize; ++j) + { + cr = (coset[j].rep == NULL ? NULL : coset[j].rep->p); + if (before == NULL) + p = cr; + else if (cr == NULL) + p = before; + else + { + p = after; + for (i = 0; i < n; ++i) p[i] = cr[before[i]]; + } + + if (level == 0) + (*action)((p == NULL ? id : p),n,abort); + else + groupelts2(lr,n,level-1,action,p,after+n,id,abort); + if (*abort) return; + } +} + +/**************************************************************************/ + +int +allgroup2(grouprec *grp, void (*action)(int*,int,int*)) +/* Call action(p,n,&abort) for every element of the group, including + the identity. The identity is always the first call. + If action() stores a non-zero value in abort, group generation is + aborted and the abort value is returned by this procedure. If no + non-zero value is ever returned in abort by action(), this + procedure returns 0. */ +{ + int i,depth,n,abort; + + depth = grp->depth; + n = grp->n; + + DYNALLOC1(int,id,id_sz,n,"malloc"); + for (i = 0; i < n; ++i) id[i] = i; + + abort = 0; + if (depth == 0) + { + (*action)(id,n,&abort); + return abort; + } + + DYNALLOC1(int,allp,allp_sz,n*depth,"malloc"); + + groupelts2(grp->levelinfo,n,depth-1,action,NULL,allp,id,&abort); + + return abort; +} + +/**************************************************************************/ + +static void +groupelts3(levelrec *lr, int n, int level, + void (*action)(int*,int,int*,void*), int *before, + int *after, int *id, int *abort, void *userptr) +/* Recursive routine used by allgroup3. */ +{ + int i,j,orbsize; + int *p,*cr; + cosetrec *coset; + + coset = lr[level].replist; + orbsize = lr[level].orbitsize; + + for (j = 0; j < orbsize; ++j) + { + cr = (coset[j].rep == NULL ? NULL : coset[j].rep->p); + if (before == NULL) + p = cr; + else if (cr == NULL) + p = before; + else + { + p = after; + for (i = 0; i < n; ++i) p[i] = cr[before[i]]; + } + + if (level == 0) + (*action)((p == NULL ? id : p),n,abort,userptr); + else + groupelts3(lr,n,level-1,action,p,after+n,id,abort,userptr); + if (*abort) return; + } +} + +/**************************************************************************/ + +int +allgroup3(grouprec *grp, void (*action)(int*,int,int*,void*), void *userptr) +/* Call action(p,n,&abort,userptr) for every element of the group, + including the identity. The identity is always the first call. + If action() stores a non-zero value in abort, group generation is + aborted and the abort value is returned by this procedure. If no + non-zero value is ever returned in abort by action(), this + procedure returns 0. The pointer userptr is not interpretted and + is passed to action() to use as it likes. */ +{ + int i,depth,n,abort; + + depth = grp->depth; + n = grp->n; + + DYNALLOC1(int,id,id_sz,n,"malloc"); + for (i = 0; i < n; ++i) id[i] = i; + + abort = 0; + if (depth == 0) + { + (*action)(id,n,&abort,userptr); + return abort; + } + + DYNALLOC1(int,allp,allp_sz,n*depth,"malloc"); + + groupelts3(grp->levelinfo,n,depth-1,action,NULL,allp,id,&abort,userptr); + + return abort; +} diff --git a/graph-checker/nauty/naugroup.h b/graph-checker/nauty/naugroup.h new file mode 100644 index 0000000..a43c262 --- /dev/null +++ b/graph-checker/nauty/naugroup.h @@ -0,0 +1,55 @@ +/* naugroup.h + +Procedures for handling groups found by nauty. +*/ + +#include "nauty.h" + +typedef struct perm_struct +{ + struct perm_struct *ptr; /* general-purpose pointer */ + int p[2]; /* extendable section */ +} permrec; + +typedef struct coset_struct +{ + int image; /* image of fixed point */ + permrec *rep; /* pointer to a representative */ +} cosetrec; + +typedef struct level_struct +{ + int fixedpt; /* point that is fixed in this level */ + int orbitsize; /* the size of the orbit containing fixedpt */ + permrec *gens; /* pointer to list of generators */ + cosetrec *replist; /* array of orbitsize representatives */ +} levelrec; + +typedef struct group_struct +{ + int n; /* number of points */ + int numorbits; /* number of orbits */ + int depth; /* number of points in base */ + levelrec levelinfo[1]; /* extendable section */ +} grouprec; + +#ifdef __cplusplus +extern "C" { +#endif + +extern void freepermrec(permrec*, int); +extern grouprec *groupptr(boolean); +extern permrec *newpermrec(int); +extern void groupautomproc(int,int*,int*,int,int,int); +extern void + grouplevelproc(int*,int*,int,int*,statsblk*,int,int,int,int,int,int); +extern void makecosetreps(grouprec*); +extern int permcycles(int*,int,int*,boolean); +extern void allgroup(grouprec*,void(*)(int*,int)); +extern int allgroup2(grouprec*,void(*)(int*,int,int*)); +extern int allgroup3(grouprec*,void(*)(int*,int,int*,void*),void*); +extern void freegroup(grouprec*); + +#ifdef __cplusplus +} +#endif diff --git a/graph-checker/nauty/naugstrings.h b/graph-checker/nauty/naugstrings.h new file mode 100644 index 0000000..f59c1d5 --- /dev/null +++ b/graph-checker/nauty/naugstrings.h @@ -0,0 +1,20 @@ +/* naugstrings.h : Write graph6 or sparse6 strings into array. */ +/* Version 1.1, Jun 2015. */ + +#include "gtools.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern void gtog6string(graph*,char**,int,int); +extern void gtos6string(graph*,char**,int,int); +extern void gtod6string(graph*,char**,int,int); +extern void sgtos6string(sparsegraph*,char**); +extern void sgtog6string(sparsegraph*,char**); +extern void sgtod6string(sparsegraph*,char**); +extern void gtois6string(graph*,graph*,char**,int,int); + +#ifdef __cplusplus +} +#endif diff --git a/graph-checker/nauty/naurng.c b/graph-checker/nauty/naurng.c new file mode 100644 index 0000000..dcd4d2c --- /dev/null +++ b/graph-checker/nauty/naurng.c @@ -0,0 +1,153 @@ +/* naurng.c + * + This file contains the code for a high-quality random number + generator written by Don Knuth. The auxilliary routine + ran_arr_cycle() has been modified slightly, and ran_init() is new. + + To use it: + + 0. #include "naurng.h" (or "naututil.h" if you are using nauty) + + 1. Call ran_init(seed), where seed is any long integer. + This step is optional, but if you don't use it you + will always get the same sequence of random numbers. + + 2. For each random number, use the NEXTRAN macro. It will + give a random value in the range 0..2^30-1. Alternatively, + KRAN(k) will have a random value in the range 0..k-1. + KRAN(k) actually gives you NEXTRAN mod k, so it is not + totally uniform if k is very large. In that case, you + can use the slightly slower GETKRAN(k,var) to set the + variable var to a better random number from 0..k-1. + + Brendan McKay, July 2002. Fixed Nov 2002 on advice of DEK. + Nov 2022. Added ran_init_time(). +*/ + +/* This program by D E Knuth is in the public domain and freely copyable + * AS LONG AS YOU MAKE ABSOLUTELY NO CHANGES! + * It is explained in Seminumerical Algorithms, 3rd edition, Section 3.6 + * (or in the errata to the 2nd edition --- see + * http://www-cs-faculty.stanford.edu/~knuth/taocp.html + * in the changes to Volume 2 on pages 171 and following). */ + +/* N.B. The MODIFICATIONS introduced in the 9th printing (2002) are + included here; there's no backwards compatibility with the original. */ + +/* If you find any bugs, please report them immediately to + * taocp@cs.stanford.edu + * (and you will be rewarded if the bug is genuine). Thanks! */ + +/************ see the book for explanations and caveats! *******************/ +/************ in particular, you need two's complement arithmetic **********/ + +#include "naurng.h" + +#define KK 100 /* the long lag */ +#define LL 37 /* the short lag */ +#define MM (1L<<30) /* the modulus */ +#define mod_diff(x,y) (((x)-(y))&(MM-1)) /* subtraction mod MM */ + +static TLS_ATTR long ran_x[KK]; /* the generator state */ + +static void +ran_array(long aa[],int n) +{ + int i,j; + for (j=0;j<KK;j++) aa[j]=ran_x[j]; + for (;j<n;j++) aa[j]=mod_diff(aa[j-KK],aa[j-LL]); + for (i=0;i<LL;i++,j++) ran_x[i]=mod_diff(aa[j-KK],aa[j-LL]); + for (;i<KK;i++,j++) ran_x[i]=mod_diff(aa[j-KK],ran_x[i-LL]); +} + +/* the following routines are from exercise 3.6--15 */ +/* after calling ran_start, get new randoms by, e.g., "x=ran_arr_next()" */ + +#define RNG_QUALITY 1009 /* recommended quality level for high-res use */ +static TLS_ATTR long ran_arr_buf[RNG_QUALITY]; +static long ran_arr_dummy=-1; +static long ran_arr_started=-1; +static TLS_ATTR long *ran_arr_ptr = &ran_arr_dummy; + +#define TT 70 /* guaranteed separation between streams */ +#define is_odd(x) ((x)&1) /* units bit of x */ + +static void +ran_start(long seed) +{ + int t,j; + long x[KK+KK-1]; /* the preparation buffer */ + long ss=(seed+2)&(MM-2); + + for (j=0;j<KK;j++) { + x[j]=ss; /* bootstrap the buffer */ + ss<<=1; if (ss>=MM) ss-=MM-2; /* cyclic shift 29 bits */ + } + x[1]++; /* make x[1] (and only x[1]) odd */ + for (ss=seed&(MM-1),t=TT-1; t; ) { + for (j=KK-1;j>0;j--) x[j+j]=x[j], x[j+j-1]=0; /* "square" */ + for (j=KK+KK-2;j>=KK;j--) + x[j-(KK-LL)]=mod_diff(x[j-(KK-LL)],x[j]), + x[j-KK]=mod_diff(x[j-KK],x[j]); + if (is_odd(ss)) { /* "multiply by z" */ + for (j=KK;j>0;j--) x[j]=x[j-1]; + x[0]=x[KK]; /* shift the buffer cyclically */ + x[LL]=mod_diff(x[LL],x[KK]); + } + if (ss) ss>>=1; else t--; + } + for (j=0;j<LL;j++) ran_x[j+KK-LL]=x[j]; + for (;j<KK;j++) ran_x[j-LL]=x[j]; + for (j=0;j<10;j++) ran_array(x,KK+KK-1); /* warm things up */ + ran_arr_ptr=&ran_arr_started; +} + +void +ran_init(long seed) +/* Added by BDM: use instead of ran_start. */ +{ + ran_start((unsigned long)seed % (MM-2)); +} + +long +ran_init_time(long extra) +/* Added by BDM: use the real time and the argument to initialise. + Returns the value of the seed, so the same sequence can be + obtained again by calling ran_init(seed). +*/ +{ + double t; + nauty_counter ul; /* 64-bit unsigned */ + long seed; + REALTIMEDEFS + + t = NAUTYREALTIME; + if (t > 1660000000.0) ul = (nauty_counter)(t*2100001.0); + else ul = (nauty_counter)(t+212300021.0); + ul ^= (nauty_counter)(extra) * 997; + seed = (long)ul; + ran_init(seed); + + return seed; +} + +static long +ran_arr_cycle(void) +/* Modified by BDM to automatically initialise + if no explicit initialisation has been done */ +{ + if (ran_arr_ptr==&ran_arr_dummy) + ran_start(314159L); /* the user forgot to initialize */ + + ran_array(ran_arr_buf,RNG_QUALITY); + + ran_arr_buf[KK]=-1; + ran_arr_ptr=ran_arr_buf+1; + return ran_arr_buf[0]; +} + +long +ran_nextran(void) +{ + return (*ran_arr_ptr>=0 ? *ran_arr_ptr++ : ran_arr_cycle()); +} diff --git a/graph-checker/nauty/naurng.h b/graph-checker/nauty/naurng.h new file mode 100644 index 0000000..876a1c4 --- /dev/null +++ b/graph-checker/nauty/naurng.h @@ -0,0 +1,41 @@ +/* naurng.h : definitions for using Don Knuth's random number generator. + This version uses the attribute TLS_ATTR from nauty.h. + + To use it: + 1. Call ran_init(seed) with any long seed. (Optional, + but you will always get the same sequence otherwise.) + 2. Use NEXTRAN to get the next number (0..2^30-1). + Alternatively, use KRAN(k) to get a random number 0..k-1. + For large k, KRAN(k) is not quite uniform. In that case + use GETKRAN(k,var) to set the variable var to a better + random number 0..k-1. +*/ + +#ifndef NAURNG_H +#include "naututil.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern void ran_init(long seed); +extern long ran_init_time(long extra); +extern long ran_nextran(void); + +#ifdef __cplusplus +} +#endif + +#define MAXRAN (0x3fffffffL) /* Values are 0..MAXRAN */ +#define NEXTRAN (ran_nextran()) +#define KRAN(k) (NEXTRAN%(k)) +#define RANREAL ((NEXTRAN+0.5)/(MAXRAN+1.0)) /* Uniform (0,1) */ + +#define MAXSAFE(k) (((MAXRAN+1)/(k))*(k)) +#define GETKRAN(k,var) {long __getkran; \ + do {__getkran = NEXTRAN;} while (__getkran >= MAXSAFE(k)); \ + var = __getkran % (k);} +#define INITRANBYTIME ran_init_time(0) + +#define NAURNG_H +#endif diff --git a/graph-checker/nauty/nausparse.c b/graph-checker/nauty/nausparse.c new file mode 100644 index 0000000..d3b30cc --- /dev/null +++ b/graph-checker/nauty/nausparse.c @@ -0,0 +1,1757 @@ +/***************************************************************************** +* * +* Sparse-graph-specific auxiliary source file for version 2.8 of nauty. * +* * +* Copyright (2004-2022) Brendan McKay. All rights reserved. * +* Subject to waivers and disclaimers in nauty.h. * +* * +* CHANGE HISTORY * +* 26-Oct-04 : initial creation * +* 23-Nov-06 : dispatch uses targetcell_sg, not bestcell_sg * +* 8-Dec-06 : add adjacencies_sg() * +* 10-Nov-09 : remove types shortish and permutation * +* 14-Nov-09 : added copy_sg() * +* 11-May-10 : use sorttemplates.c for sorting procedures * +* 19-May-10 : add two *_tr procedures for traces. * +* 21-May-10 : fixes for nde,v fields becoming size_t * +* 23-May-10 : add sparsenauty() * +* 15-Jan-12 : add TLS_ATTR attributes * +* 17-Dec-15 : extend sortlists_sg() to sort weights * +* : add weights to copy_sg() and updatecan_sg() * +* 11-Mar-16 : add cleanup_sg(). This can be used in the cleanup * +* field of the dispatch vector to sort the lists of the * +* canonical graph, but isn't there by default. * +* 15-Oct-19 : fix static declaration of snwork[] * +* 6-Apr-21 : increase work space in sparsenauty() * +* 16-Nov-22 : fix an error in the Traces utility comparelab_tr() * +* * +*****************************************************************************/ + +#define TMP + +/* #define ONE_WORD_SETS not sure about this! See notes.txt. */ +#include "nausparse.h" + + /* macros for hash-codes: */ +#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 + +#define ACCUM(x,y) x = (((x) + (y)) & 077777) + +static const int fuzz1[] = {037541,061532,005257,026416}; +static const int fuzz2[] = {006532,070236,035523,062437}; + +#define FUZZ1(x) ((x) ^ fuzz1[(x)&3]) +#define FUZZ2(x) ((x) ^ fuzz2[(x)&3]) + +/* aproto: header new_nauty_protos.h */ + +dispatchvec dispatch_sparse = + {isautom_sg,testcanlab_sg,updatecan_sg,refine_sg,refine_sg,cheapautom_sg, + targetcell_sg,nausparse_freedyn,nausparse_check,init_sg,NULL}; + +#if !MAXN +DYNALLSTAT(short,vmark1,vmark1_sz); +DYNALLSTAT(short,vmark2,vmark2_sz); +DYNALLSTAT(int,work1,work1_sz); +DYNALLSTAT(int,work2,work2_sz); +DYNALLSTAT(int,work3,work3_sz); +DYNALLSTAT(int,work4,work4_sz); +DYNALLSTAT(set,snwork,snwork_sz); +#else +static TLS_ATTR short vmark1[MAXN]; +static TLS_ATTR short vmark2[MAXN]; +static TLS_ATTR int work1[MAXN]; +static TLS_ATTR int work2[MAXN]; +static TLS_ATTR int work3[MAXN]; +static TLS_ATTR int work4[MAXN]; +static TLS_ATTR set snwork[2*500*MAXM]; +#endif + +static TLS_ATTR short vmark1_val = 32000; +#define MARK1(i) vmark1[i] = vmark1_val +#define UNMARK1(i) vmark1[i] = 0 +#define ISMARKED1(i) (vmark1[i] == vmark1_val) +#define ISNOTMARKED1(i) (vmark1[i] != vmark1_val) + +static TLS_ATTR short vmark2_val = 32000; +#define MARK2(i) vmark2[i] = vmark2_val +#define UNMARK2(i) vmark2[i] = 0 +#define ISMARKED2(i) (vmark2[i] == vmark2_val) +#define ISNOTMARKED2(i) (vmark2[i] != vmark2_val) + +#if !MAXN +#define RESETMARKS1 {if (vmark1_val++ >= 32000) \ + {size_t ij; for (ij=0;ij<vmark1_sz;++ij) vmark1[ij]=0; vmark1_val=1;}} +#define PREPAREMARKS1(nn) preparemarks1(nn) +#define RESETMARKS2 {if (vmark2_val++ >= 32000) \ + {size_t ij; for (ij=0;ij<vmark2_sz;++ij) vmark2[ij]=0; vmark2_val=1;}} +#define PREPAREMARKS2(nn) preparemarks2(nn) +#else +#define RESETMARKS1 {if (vmark1_val++ >= 32000) \ + {size_t ij; for (ij=0;ij<MAXN;++ij) vmark1[ij]=0; vmark1_val=1;}} +#define PREPAREMARKS1(nn) +#define RESETMARKS2 {if (vmark2_val++ >= 32000) \ + {size_t ij; for (ij=0;ij<MAXN;++ij) vmark2[ij]=0; vmark2_val=1;}} +#define PREPAREMARKS2(nn) +#endif + +/***************************************************************************** +* * +* preparemarks1(N) and preparemarks2(N) * +* make vmarks array large enough to mark 0..N-1 and such that * +* the next RESETMARKS command will work correctly * +* * +*****************************************************************************/ + +#if !MAXN +static void +preparemarks1(size_t nn) +{ + size_t oldsize; + short *oldpos; + + oldsize = vmark1_sz; + oldpos = vmark1; + DYNALLOC1(short,vmark1,vmark1_sz,nn,"preparemarks"); + if (vmark1_sz != oldsize || vmark1 != oldpos) vmark1_val = 32000; +} +#endif + +#if !MAXN +static void +preparemarks2(size_t nn) +{ + size_t oldsize; + short *oldpos; + + oldsize = vmark2_sz; + oldpos = vmark2; + DYNALLOC1(short,vmark2,vmark2_sz,nn,"preparemarks"); + if (vmark2_sz != oldsize || vmark2 != oldpos) vmark2_val = 32000; +} +#endif + +/***************************************************************************** +* * +* isautom_sg(g,perm,digraph,m,n) = TRUE iff perm is an automorphism of g * +* (i.e., g^perm = g). Symmetry is assumed unless digraph = TRUE. * +* * +*****************************************************************************/ + +boolean +isautom_sg(graph *g, int *p, boolean digraph, int m, int n) +{ + int *d,*e; + size_t *v; + int i,pi,di; + size_t vi,vpi,j; + + SG_VDE(g,v,d,e); + + PREPAREMARKS1(n); + + for (i = 0; i < n; ++i) + if (p[i] != i || digraph) + { + pi = p[i]; + di = d[i]; + if (d[pi] != di) return FALSE; + + vi = v[i]; + vpi = v[pi]; + RESETMARKS1; + for (j = 0; j < di; ++j) MARK1(p[e[vi+j]]); + for (j = 0; j < di; ++j) if (ISNOTMARKED1(e[vpi+j])) return FALSE; + } + + return TRUE; +} + +/***************************************************************************** +* * +* aresame_sg(g1,g2) = TRUE iff g1 and g2 are identical as labelled digraphs * +* * +*****************************************************************************/ + +boolean +aresame_sg(sparsegraph *g1, sparsegraph *g2) +{ + int *d1,*e1; + int *d2,*e2; + int n,i,di; + size_t vi,*v1,*v2,j; + + n = g1->nv; + if (g2->nv != n || g2->nde != g1->nde) return FALSE; + + SG_VDE(g1,v1,d1,e1); + SG_VDE(g2,v2,d2,e2); + + PREPAREMARKS1(n); + + for (i = 0; i < n; ++i) + { + di = d1[i]; + if (d2[i] != di) return FALSE; + + vi = v1[i]; + RESETMARKS1; + for (j = 0; j < di; ++j) MARK1(e1[vi+j]); + vi = v2[i]; + for (j = 0; j < di; ++j) if (ISNOTMARKED1(e2[vi+j])) return FALSE; + } + + return TRUE; +} + +/***************************************************************************** +* * +* testcanlab_sg(g,canong,lab,samerows,m,n) compares g^lab to canong, * +* using an ordering which is immaterial since it's only used here. The * +* value returned is -1,0,1 if g^lab <,=,> canong. *samerows is set to * +* the number of rows (0..n) of canong which are the same as those of g^lab. * +* * +*****************************************************************************/ + +int +testcanlab_sg(graph *g, graph *canong, int *lab, int *samerows, int m, int n) +{ + int *d,*e; + int *cd,*ce; + int i,k,di,dli; + size_t j,vi,vli,*v,*cv; + int mina; + + SG_VDE(g,v,d,e); + SG_VDE(canong,cv,cd,ce); + +#if !MAXN + DYNALLOC1(int,work1,work1_sz,n,"testcanlab_sg"); +#endif +#define INVLAB work1 + + PREPAREMARKS1(n); + + for (i = 0; i < n; ++i) INVLAB[lab[i]] = i; + + for (i = 0; i < n; ++i) + { + /* compare g[lab[i]]^INVLAB to canong[i] */ + vi = cv[i]; + di = cd[i]; + vli = v[lab[i]]; + dli = d[lab[i]]; + + if (di != dli) + { + *samerows = i; + if (di < dli) return -1; + return 1; + } + + RESETMARKS1; + mina = n; + for (j = 0; j < di; ++j) MARK1(ce[vi+j]); + for (j = 0; j < di; ++j) + { + k = INVLAB[e[vli+j]]; + if (ISMARKED1(k)) UNMARK1(k); + else if (k < mina) mina = k; + } + if (mina != n) + { + *samerows = i; + for (j = 0; j < di; ++j) + { + k = ce[vi+j]; + if (ISMARKED1(k) && k < mina) return -1; + } + return 1; + } + } + + *samerows = n; + return 0; +} + +/***************************************************************************** +* * +* updatecan_sg(g,canong,lab,samerows,m,n) sets canong = g^lab, assuming * +* the first samerows vertices of canong are ok already. Also assumes * +* contiguity and ample space in canong. * +* * +*****************************************************************************/ + +void +updatecan_sg(graph *g, graph *canong, int *lab, int samerows, int m, int n) +{ + int *d,*e; + int *cd,*ce; + int i,dli; + size_t *v,*cv,vli,j,k; + sg_weight *wt,*cwt; + + SWG_VDE(g,v,d,e,wt); + SWG_VDE(canong,cv,cd,ce,cwt); + +#if !MAXN + DYNALLOC1(int,work1,work1_sz,n,"testcanlab_sg"); +#endif +#define INVLAB work1 + + ((sparsegraph*)canong)->nv = n; + ((sparsegraph*)canong)->nde = ((sparsegraph*)g)->nde; + + for (i = 0; i < n; ++i) INVLAB[lab[i]] = i; + + if (samerows == 0) k = 0; + else k = cv[samerows-1]+cd[samerows-1]; + + for (i = samerows; i < n; ++i) + { + cv[i] = k; + cd[i] = dli = d[lab[i]]; + vli = v[lab[i]]; + if (wt) + { + for (j = 0; j < dli; ++j) + { + ce[k] = INVLAB[e[vli+j]]; + cwt[k] = wt[vli+j]; + ++k; + } + } + else + for (j = 0; j < dli; ++j) ce[k++] = INVLAB[e[vli+j]]; + } +} + +/***************************************************************************** +* * +* comparelab_tr(g,lab1,invlab1,lab2,invlab2,cls,col) compares * +* g^lab1 to g^lab2 and returns -1,0,1 according to the comparison. * +* invlab1[] and invlab2[] are assumed to hold inverses of lab1,lab2. * +* * +*****************************************************************************/ + +int +comparelab_tr(sparsegraph *g, + int *lab1, int *invlab1, int *lab2, int *invlab2, int *cls, int *col) +{ + int d1,*e1,d2,*e2; + int i,j,k,n,c,end; + int mina; + + n = g->nv; + +#if !MAXN + DYNALLOC1(int,work1,work1_sz,n,"comparelab_tr"); +#endif +#define NGHCOUNTS work1 + + memset(NGHCOUNTS,0,n*sizeof(int)); + for (c=0; c<n; c+=cls[c]) + { + if (cls[c] == 1) + { + end = c+cls[c]; + for (i = c; i < end; ++i) + { + e1 = g->e + g->v[lab1[i]]; + d1 = g->d[lab1[i]]; + e2 = g->e + g->v[lab2[i]]; + d2 = g->d[lab2[i]]; + if (d1 < d2) return -1; + else if (d1 > d2) return 1; + + mina = n; + for (j = 0; j < d1; ++j) { + (NGHCOUNTS[col[invlab1[e1[j]]]])++; + } + for (j = 0; j < d1; ++j) + { + k = col[invlab2[e2[j]]]; + if (NGHCOUNTS[k]) { + (NGHCOUNTS[k])--; + } + else { + if (k < mina) mina = k; + } + } + if (mina != n) + { + for (j = 0; j < d1; ++j) + { + k = col[invlab1[e1[j]]]; + if (NGHCOUNTS[k] && k < mina) return -1; + } + return 1; + } + } + } + } + + return 0; +} + +/***************************************************************************** +* * +* testcanlab_tr(g,canong,lab,invlab,samerows) compares g^lab to canong, * +* using an ordering which is immaterial since it's only used here. The * +* value returned is -1,0,1 if g^lab <,=,> canong. *samerows is set to * +* the number of rows (0..n) of canong which are the same as those of g^lab. * +* invlab[] is assumed to hold the inverse of lab[] * +* * +*****************************************************************************/ + +int +testcanlab_tr(sparsegraph *g, sparsegraph *canong, + int *lab, int *invlab, int *samerows) +{ + int *d,*e; + int *cd,*ce; + int i,di,dli; + int k,n; + size_t *v,*cv,vi,vli,j; + int mina; + + SG_VDE(g,v,d,e); + SG_VDE(canong,cv,cd,ce); + n = g->nv; + + PREPAREMARKS1(n); + + for (i = 0; i < n; ++i) + { + /* compare g[lab[i]]^invlab to canong[i] */ + vi = cv[i]; + di = cd[i]; + vli = v[lab[i]]; + dli = d[lab[i]]; + + if (di != dli) + { + *samerows = i; + if (di < dli) return -1; + return 1; + } + + RESETMARKS1; + mina = n; + for (j = 0; j < di; ++j) MARK1(ce[vi+j]); + + for (j = 0; j < di; ++j) + { + k = invlab[e[vli+j]]; + if (ISMARKED1(k)) UNMARK1(k); + else if (k < mina) mina = k; + } + if (mina != n) + { + *samerows = i; + for (j = 0; j < di; ++j) + { + k = ce[vi+j]; + if (ISMARKED1(k) && k < mina) return -1; + } + return 1; + } + } + + *samerows = n; + return 0; +} + +/***************************************************************************** +* * +* updatecan_tr(g,canong,lab,invlab,samerows) sets canong = g^lab, * +* assuming the first samerows vertices of canong are ok already. * +* Also assumes contiguity and ample space in canong. * +* Assumes invlab[] holds the inverse of lab[] * +* * +*****************************************************************************/ + +void +updatecan_tr(sparsegraph *g, sparsegraph *canong, + int *lab, int *invlab, int samerows) +{ + int *d,*e; + int *cd,*ce; + int i,dli,n; + size_t *v,*cv,vli,j,k; + + SG_VDE(g,v,d,e); + SG_VDE(canong,cv,cd,ce); + n = g->nv; + + PREPAREMARKS1(n); + + canong->nv = n; + canong->nde = g->nde; + + if (samerows == 0) k = 0; + else k = cv[samerows-1]+cd[samerows-1]; + + for (i = samerows; i < n; ++i) + { + cv[i] = k; + cd[i] = dli = d[lab[i]]; + vli = v[lab[i]]; + for (j = 0; j < dli; ++j) ce[k++] = invlab[e[vli+j]]; + } +} + +#define SORT_OF_SORT 3 +#define SORT_NAME sortindirect +#define SORT_TYPE1 int +#define SORT_TYPE2 int +#include "sorttemplates.c" + +#define SORT_OF_SORT 1 +#define SORT_NAME sortints +#define SORT_TYPE1 int +#include "sorttemplates.c" + +#define SORT_OF_SORT 2 +#define SORT_NAME sortweights +#define SORT_TYPE1 int +#define SORT_TYPE2 sg_weight +#include "sorttemplates.c" + +/***************************************************************************** +* * +* init_sg(graph *gin, graph **gout, graph *hin, graph **hout, * +* int *lab, int *ptn, set *active, optionblk *options, * +* int *status, int m, int n) * +* Initialise routine for dispatch vector. This one just makes sure * +* that *hin has enough space and sets fields for n=0. * +* * +*****************************************************************************/ + +void +init_sg(graph *gin, graph **gout, graph *hin, graph **hout, int *lab, + int *ptn, set *active, struct optionstruct *options, int *status, + int m, int n) +{ + sparsegraph *sg,*sh; + + if (options->getcanon) + { + sg = (sparsegraph*)gin; + sh = (sparsegraph*)hin; + SG_ALLOC(*sh,sg->nv,sg->nde,"init_sg"); + sh->nv = sg->nv; + sh->nde = sg->nde; + } + *status = 0; +} + +/***************************************************************************** +* * +* cleanup_sg(graph *gin, graph **gout, graph *hin, graph **hout, * +* int *lab, int *ptn, optionblk *options, * +* statsblk *stats, int m, int n) * +* Cleanup routine for dispatch vector. This one sorts the adjacency * +* lists for the canonical labelling. * +* * +*****************************************************************************/ + +void +cleanup_sg(graph *gin, graph **gout, graph *hin, graph **hout, int *lab, + int *ptn, optionblk *options, statsblk *stats, int m, int n) +{ + sparsegraph *sh; + + if (options->getcanon + && (stats->errstatus == 0 || stats->errstatus == NAUABORTED)) + { + sh = (sparsegraph*)hin; + sortlists_sg(sh); + } +} + +/***************************************************************************** +* * +* distvals(sparsegraph *sg, int v0, int *dist, int n) sets dist[i] * +* to the distance from v0 to i, for each i, or to n if there is no such * +* distance. work4[] is used as a queue. * +* * +*****************************************************************************/ + +void +distvals(sparsegraph *g, int v0, int *dist, int n) +{ + int *d,*e; + int i,head,tail; + int di,k; + size_t *v,vi,j; + + SG_VDE(g,v,d,e); +#if !MAXN + DYNALLOC1(int,work4,work4_sz,n,"distvals"); +#endif +#define QUEUE work4 + + for (i = 0; i < n; ++i) dist[i] = n; + + QUEUE[0] = v0; + dist[v0] = 0; + + head = 0; + tail = 1; + while (tail < n && head < tail) + { + i = QUEUE[head++]; + vi = v[i]; + di = d[i]; + for (j = 0; j < di; ++j) + { + k = e[vi+j]; + if (dist[k] == n) + { + dist[k] = dist[i] + 1; + QUEUE[tail++] = k; + } + } + } +} + +/***************************************************************************** +* * +* refine_sg(g,lab,ptn,level,numcells,count,active,code,m,n) performs a * +* refinement operation on the partition at the specified level of the * +* partition nest (lab,ptn). *numcells is assumed to contain the number of * +* cells on input, and is updated. The initial set of active cells (alpha * +* in the paper) is specified in the set active. Precisely, x is in active * +* iff the cell starting at index x in lab is active. * +* The resulting partition is equitable if active is correct (see the paper * +* and the Guide). * +* *code is set to a value which depends on the fine detail of the * +* algorithm, but which is independent of the labelling of the graph. * +* count is used for work space. * +* * +*****************************************************************************/ + +void +refine_sg(graph *g, int *lab, int *ptn, int level, int *numcells, + int *count, set *active, int *code, int m, int n) +{ + int i,j,k,l,v1,v2,v3,isplit; + int w1,w2,w3; + long longcode; + int *d,*e; + int size,bigsize,bigpos; + int nactive,hitcells; + int lj,di,splitv; + boolean trivsplit; + size_t *v,vi,ii; + + SG_VDE(g,v,d,e); + +#if !MAXN + DYNALLOC1(int,work1,work1_sz,n,"refine_sg"); + DYNALLOC1(int,work2,work2_sz,n,"refine_sg"); + DYNALLOC1(int,work3,work3_sz,n,"refine_sg"); + DYNALLOC1(int,work4,work4_sz,n,"refine_sg"); +#endif +#define CELLSTART work1 +#define ACTIVE work2 +#define HITS work3 +#define HITCELL work4 + + PREPAREMARKS1(n); + PREPAREMARKS2(n); + + longcode = *numcells; + + /* Set ACTIVE[0..nactive-1] = queue of active cell starts */ + + nactive = 0; + for (i = -1; (i = nextelement(active,m,i)) >= 0;) + ACTIVE[nactive++] = i; + + if (nactive == 0) + { + *code = CLEANUP(longcode); + return; + } + + /* Set CELLSTART[i] = starting point in lab[] of nontrivial cell + containing i, or n if i is a singleton */ + + for (i = 0; i < n; ) + { + /* Just here, i is a cell starting position */ + if (ptn[i] <= level) + { + CELLSTART[lab[i]] = n; + ++i; + } + else + { + j = i; + do + { + CELLSTART[lab[i]] = j; + } while (ptn[i++] > level); + } + } + + if (level <= 2 && nactive == 1 && ptn[ACTIVE[0]] <= level + && *numcells <= n/8) + { + isplit = ACTIVE[--nactive]; + DELELEMENT(active,isplit); + + distvals((sparsegraph*)g,lab[isplit],HITS,n); + + for (v1 = 0; v1 < n; ) + { + if (ptn[v1] <= level) + { + ++v1; + continue; + } + + longcode = MASH(longcode,v1); + w1 = HITS[lab[v1]]; + + v2 = v1+1; + while (ptn[v2-1] > level && HITS[lab[v2]] == w1) ++v2; + + if (ptn[v2-1] <= level) + { + v1 = v2; + continue; + } + + w2 = NAUTY_INFINITY; + v3 = j = v2; + + do + { + lj = lab[j]; + w3 = HITS[lj]; + if (w3 == w1) + { + lab[j] = lab[v3]; + lab[v3] = lab[v2]; + lab[v2] = lj; + ++v2; + ++v3; + } + else if (w3 == w2) + { + lab[j] = lab[v3]; + lab[v3] = lj; + ++v3; + } + else if (w3 < w1) + { + lab[j] = lab[v2]; + lab[v2] = lab[v1]; + lab[v1] = lj; + v3 = v2 + 1; + v2 = v1 + 1; + w2 = w1; + w1 = w3; + } + else if (w3 < w2) + { + lab[j] = lab[v2]; + lab[v2] = lj; + v3 = v2 + 1; + w2 = w3; + } + } while (ptn[j++] > level); + + longcode = MASH(longcode,w2); + longcode = MASH(longcode,v2); + if (j != v2) /* At least two fragments + * v1..v2-1 = w1; v2..v3-1 = w2 */ + { + if (v2 == v1+1) + CELLSTART[lab[v1]] = n; + + if (v3 == v2+1) + CELLSTART[lab[v2]] = n; + else + for (k = v2; k < v3; ++k) + CELLSTART[lab[k]] = v2; + ++*numcells; + ptn[v2-1] = level; + + if (j == v3) + { + /* Two fragments only */ + if (v2-v1 <= v3-v2 && !ISELEMENT(active,v1)) + { + ADDELEMENT(active,v1); + ACTIVE[nactive++] = v1; + } + else + { + ADDELEMENT(active,v2); + ACTIVE[nactive++] = v2; + } + } + else + { + /* Extra fragments: v3..j-1 > w2 */ + sortindirect(lab+v3,HITS,j-v3); + ACTIVE[nactive++] = v2; + ADDELEMENT(active,v2); + if (v2-v1 >= v3-v2) + { + bigpos = -1; + bigsize = v2-v1; + } + else + { + bigpos = nactive-1; + bigsize = v3-v2; + } + for (k = v3-1; k < j-1;) + { + ptn[k] = level; + longcode = MASH(longcode,k); + ++*numcells; + l = k+1; + ADDELEMENT(active,l); + ACTIVE[nactive++] = l; + w3 = HITS[lab[l]]; + for (k = l; k < j-1 + && HITS[lab[k+1]] == w3; ++k) + CELLSTART[lab[k+1]] = l; + size = k-l+1; + if (size == 1) + CELLSTART[lab[l]] = n; + else + { + CELLSTART[lab[l]] = l; + if (size > bigsize) + { + bigsize = size; + bigpos = nactive-1; + } + } + } + + if (bigpos >= 0 && !ISELEMENT(active,v1)) + { + longcode = MASH(longcode,bigpos); + DELELEMENT(active,ACTIVE[bigpos]); + ADDELEMENT(active,v1); + ACTIVE[bigpos] = v1; + } + } + } + v1 = j; + } + } + + /* Iterate until complete */ + while (nactive > 0 && *numcells < n) + { + for (i = 0; i < nactive && i < 10; ++i) + if (ptn[ACTIVE[i]] <= level) break; + + if (i < nactive && i < 10) + { + trivsplit = TRUE; + isplit = ACTIVE[i]; + ACTIVE[i] = ACTIVE[--nactive]; + } + else + { + isplit = ACTIVE[--nactive]; + trivsplit = ptn[isplit] <= level; + } + + DELELEMENT(active,isplit); + longcode = MASH(longcode,isplit); + + if (trivsplit) + { + RESETMARKS1; + RESETMARKS2; + hitcells = 0; + splitv = lab[isplit]; + vi = v[splitv]; + di = d[splitv]; + for (ii = 0; ii < di; ++ii) + { + j = e[vi+ii]; + MARK2(j); + k = CELLSTART[j]; + if (k != n && ISNOTMARKED1(k)) + { + MARK1(k); + HITCELL[hitcells++] = k; + } + } + + if (hitcells > 1) sortints(HITCELL,hitcells); + longcode = MASH(longcode,hitcells); + + /* divide cells according to which vertices are hit */ + + for (i = 0; i < hitcells; ++i) + { + j = v1 = v2 = HITCELL[i]; + longcode = MASH(longcode,v2); + k = 0; + do + { + lj = lab[j]; + if (ISMARKED2(lj)) + HITS[k++] = lj; + else + lab[v2++] = lj; + } while (ptn[j++] > level); + + longcode = MASH(longcode,k); + v3 = v2; + while (--k >= 0) + { + j = HITS[k]; + CELLSTART[j] = v2; + lab[v3++] = j; + } + + if (v2 != v3 && v2 != v1) + { + ++*numcells; + if (v2 == v1+1) CELLSTART[lab[v1]] = n; + if (v3 == v2+1) CELLSTART[lab[v2]] = n; + ptn[v2-1] = level; + longcode = MASH(longcode,v2); + if (v2-v1 <= v3-v2 && !ISELEMENT(active,v1)) + { + ADDELEMENT(active,v1); + ACTIVE[nactive++] = v1; + } + else + { + ADDELEMENT(active,v2); + ACTIVE[nactive++] = v2; + } + } + } + } + else /* non-trivial splitting */ + { + /* isplit is the start of the splitting cell. + Set HITS[i] = hits of i for i in non-trivial cells, + HITCELL[0..hitcells-1] = starts of hit non-trivial cells */ + + RESETMARKS1; + hitcells = 0; + do + { + vi = v[lab[isplit]]; + di = d[lab[isplit]]; + for (ii = 0; ii < di; ++ii) + { + j = e[vi+ii]; + k = CELLSTART[j]; + if (k != n) + { + if (ISNOTMARKED1(k)) + { + MARK1(k); + HITCELL[hitcells++] = k; + do + { + HITS[lab[k]] = 0; + } while (ptn[k++] > level); + } + ++HITS[j]; + } + } + } while (ptn[isplit++] > level); + + if (hitcells > 1) sortints(HITCELL,hitcells); + + /* divide cells according to hit counts */ + + longcode = MASH(longcode,hitcells); + for (i = 0; i < hitcells; ++i) + { + v1 = HITCELL[i]; + w1 = HITS[lab[v1]]; + longcode = MASH(longcode,v1); + + v2 = v1+1; + while (ptn[v2-1] > level && HITS[lab[v2]] == w1) ++v2; + + if (ptn[v2-1] <= level) continue; + w2 = NAUTY_INFINITY; + v3 = j = v2; + + do + { + lj = lab[j]; + w3 = HITS[lj]; + if (w3 == w1) + { + lab[j] = lab[v3]; + lab[v3] = lab[v2]; + lab[v2] = lj; + ++v2; + ++v3; + } + else if (w3 == w2) + { + lab[j] = lab[v3]; + lab[v3] = lj; + ++v3; + } + else if (w3 < w1) + { + lab[j] = lab[v2]; + lab[v2] = lab[v1]; + lab[v1] = lj; + v3 = v2 + 1; + v2 = v1 + 1; + w2 = w1; + w1 = w3; + } + else if (w3 < w2) + { + lab[j] = lab[v2]; + lab[v2] = lj; + v3 = v2 + 1; + w2 = w3; + } + } while (ptn[j++] > level); + + longcode = MASH(longcode,w1); + longcode = MASH(longcode,v2); + if (j != v2) /* At least two fragments + * v1..v2-1 = w1; v2..v3-1 = w2 */ + { + if (v2 == v1+1) + CELLSTART[lab[v1]] = n; + + if (v3 == v2+1) + CELLSTART[lab[v2]] = n; + else + for (k = v2; k < v3; ++k) + CELLSTART[lab[k]] = v2; + ++*numcells; + ptn[v2-1] = level; + + if (j == v3) + { + /* Two fragments only */ + if (v2-v1 <= v3-v2 && !ISELEMENT(active,v1)) + { + ADDELEMENT(active,v1); + ACTIVE[nactive++] = v1; + } + else + { + ADDELEMENT(active,v2); + ACTIVE[nactive++] = v2; + } + } + else + { + /* Extra fragments: v3..j-1 > w2 */ + longcode = MASH(longcode,v3); + sortindirect(lab+v3,HITS,j-v3); + ACTIVE[nactive++] = v2; + ADDELEMENT(active,v2); + if (v2-v1 >= v3-v2) + { + bigpos = -1; + bigsize = v2-v1; + } + else + { + bigpos = nactive-1; + bigsize = v3-v2; + longcode = MASH(longcode,bigsize); + } + for (k = v3-1; k < j-1;) + { + ptn[k] = level; + ++*numcells; + l = k+1; + ADDELEMENT(active,l); + ACTIVE[nactive++] = l; + w3 = HITS[lab[l]]; + longcode = MASH(longcode,w3); + for (k = l; k < j-1 + && HITS[lab[k+1]] == w3; ++k) + CELLSTART[lab[k+1]] = l; + size = k-l+1; + if (size == 1) + CELLSTART[lab[l]] = n; + else + { + CELLSTART[lab[l]] = l; + if (size > bigsize) + { + bigsize = size; + bigpos = nactive-1; + } + } + } + + if (bigpos >= 0 && !ISELEMENT(active,v1)) + { + DELELEMENT(active,ACTIVE[bigpos]); + ADDELEMENT(active,v1); + ACTIVE[bigpos] = v1; + } + } + } + } + } + } + + longcode = MASH(longcode,*numcells); + *code = CLEANUP(longcode); +} + +/***************************************************************************** +* * +* cheapautom_sg(ptn,level,digraph,n) returns TRUE if the partition at the * +* specified level in the partition nest (lab,ptn) {lab is not needed here} * +* satisfies a simple sufficient condition for its cells to be the orbits of * +* some subgroup of the automorphism group. Otherwise it returns FALSE. * +* It always returns FALSE if digraph!=FALSE. * +* * +* nauty assumes that this function will always return TRUE for any * +* partition finer than one for which it returns TRUE. * +* * +*****************************************************************************/ + +boolean +cheapautom_sg(int *ptn, int level, boolean digraph, int n) +{ + int i,k,nnt; + + if (digraph) return FALSE; + + k = n; + nnt = 0; + for (i = 0; i < n; ++i) + { + --k; + if (ptn[i] > level) + { + ++nnt; + while (ptn[++i] > level) {} + } + } + + return (k <= nnt + 1 || k <= 4); +} + +/***************************************************************************** +* * +* bestcell_sg(g,lab,ptn,level,tc_level,m,n) returns the index in lab of * +* the start of the "best non-singleton cell" for fixing. If there is no * +* non-singleton cell it returns n. * +* This implementation finds the first cell which is non-trivially joined * +* to the greatest number of other cells, assuming equitability. * +* This is not good for digraphs! * +* * +*****************************************************************************/ + +static int +bestcell_sg(graph *g, int *lab, int *ptn, int level, + int tc_level, int m, int n) +{ + int nnt; + int *d,*e; + int i,k,di; + int *work1b; + int maxcnt; + size_t *v,vi,j; + + SG_VDE(g,v,d,e); + +#if !MAXN + DYNALLOC1(int,work1,work1_sz,n,"bestcell_sg"); + DYNALLOC1(int,work2,work2_sz,n,"bestcell_sg"); + DYNALLOC1(int,work3,work3_sz,n,"bestcell_sg"); + DYNALLOC1(int,work4,work4_sz,n,"bestcell_sg"); +#endif + work1b = work1 + (n/2); +#define START work1 +#define SIZE work1b +#define NNTCELL work2 +#define HITS work3 +#define COUNT work4 + + /* find non-singleton cells: put starts in START[0..nnt-1], + sizes in SIZE[0..nnt-1]. + Also NNTCELL[i] = n if {i} is a singelton, else index of + nontriv cell containing i. */ + + i = nnt = 0; + + while (i < n) + { + if (ptn[i] > level) + { + START[nnt] = i; + j = i; + do + NNTCELL[lab[j]] = nnt; + while (ptn[j++] > level); + SIZE[nnt] = j-i; + ++nnt; + i = j; + } + else + { + NNTCELL[lab[i]] = n; + ++i; + } + } + + if (nnt == 0) return n; + + /* set COUNT[i] to # non-trivial neighbours of n.s. cell i */ + + for (i = 0; i < nnt; ++i) HITS[i] = COUNT[i] = 0; + + for (i = 0; i < nnt; ++i) + { + vi = v[lab[START[i]]]; + di = d[lab[START[i]]]; + + for (j = 0; j < di; ++j) + { + k = NNTCELL[e[vi+j]]; + if (k != n) ++HITS[k]; + } + for (j = 0; j < di; ++j) + { + k = NNTCELL[e[vi+j]]; + if (k != n) + { + if (HITS[k] > 0 && HITS[k] < SIZE[k]) ++COUNT[i]; + HITS[k] = 0; + } + } + } + + /* find first greatest bucket value */ + + j = 0; + maxcnt = COUNT[0]; + for (i = 1; i < nnt; ++i) + if (COUNT[i] > maxcnt) + { + j = i; + maxcnt = COUNT[i]; + } + + return (int)START[j]; +} +/***************************************************************************** +* * +* targetcell_sg(g,lab,ptn,level,tc_level,digraph,hint,m,n) returns the * +* index in lab of the next cell to split. * +* hint is a suggestion for the answer, which is obeyed if it is valid. * +* Otherwise we use bestcell() up to tc_level and the first non-trivial * +* cell after that. * +* * +*****************************************************************************/ + +int +targetcell_sg(graph *g, int *lab, int *ptn, int level, int tc_level, + boolean digraph, int hint, int m, int n) +{ + int i; + + if (hint >= 0 && ptn[hint] > level && + (hint == 0 || ptn[hint-1] <= level)) + return hint; + else if (level <= tc_level) + return bestcell_sg(g,lab,ptn,level,tc_level,m,n); + else + { + for (i = 0; i < n && ptn[i] <= level; ++i) {} + return (i == n ? 0 : i); + } +} + +/***************************************************************************** +* * +* sortlists_sg(g) sorts the adjacency lists into numerical order * +* * +*****************************************************************************/ + +void +sortlists_sg(sparsegraph *g) +{ + int *d,*e; + int n,i; + size_t *v; + sg_weight *wt; + + SWG_VDE(g,v,d,e,wt); + n = g->nv; + + if (wt) + { + for (i = 0; i < n; ++i) + if (d[i] > 1) sortweights(e+v[i],wt+v[i],d[i]); + } + else + { + for (i = 0; i < n; ++i) + if (d[i] > 1) sortints(e+v[i],d[i]); + } +} + +/***************************************************************************** +* * +* put_sg(f,sg,digraph,linelength) writes the sparse graph to file f using * +* at most linelength characters per line. If digraph then all directed * +* edges are written; else one v-w for w>=v is written. * +* * +*****************************************************************************/ + +void +put_sg(FILE *f, sparsegraph *sg, boolean digraph, int linelength) +{ + int *d,*e; + int n,di; + int i,curlen,slen; + size_t *v,vi,j; + char s[12]; + + SG_VDE(sg,v,d,e); + n = sg->nv; + + for (i = 0; i < n; ++i) + { + vi = v[i]; + di = d[i]; + if (di == 0) continue; + slen = itos(i+labelorg,s); + putstring(f,s); + putstring(f," :"); + curlen = slen + 2; + + for (j = 0; j < di; ++j) + { + if (!digraph && e[vi+j] < i) continue; + slen = itos(e[vi+j]+labelorg,s); + if (linelength && curlen + slen + 1 >= linelength) + { + putstring(f,"\n "); + curlen = 2; + } + PUTC(' ',f); + putstring(f,s); + curlen += slen + 1; + } + PUTC('\n',f); + } +} + +/***************************************************************************** +* * +* sg_to_nauty(sg,g,reqm,&m) creates a nauty-format graph from a sparse * +* graph. reqm is the required m value (computed from n if reqm=0), and * +* m is the actual value used. g is dynamically generated if NULL is given. * +* A pointer to g is returned. * +* * +*****************************************************************************/ + +graph* +sg_to_nauty(sparsegraph *sg, graph *g, int reqm, int *pm) +{ + int *d,*e; + int m,n,i,di; + size_t *v,vi,j; + set *gi; + + SG_VDE(sg,v,d,e); + n = sg->nv; + if (reqm != 0 && reqm*WORDSIZE < n) + { + fprintf(ERRFILE,"sg_to_nauty: reqm is impossible\n"); + exit(1); + } + + if (reqm != 0) m = reqm; + else m = (n+WORDSIZE-1)/WORDSIZE; + + *pm = m; + + if (g == NULL) + { + if ((g = (graph*)ALLOCS(n,m*sizeof(graph))) == NULL) + { + fprintf(ERRFILE,"sg_to_nauty: malloc failed\n"); + exit(1); + } + } + + for (i = 0, gi = g; i < n; ++i, gi += m) + { + vi = v[i]; + di = d[i]; + EMPTYSET(gi,m); + for (j = 0; j < di; ++j) ADDELEMENT(gi,e[vi+j]); + } + + return g; +} + +/***************************************************************************** +* * +* copy_sg(sg1,sg2) makes a copy of sg1 into sg2. * +* If sg2 is not NULL, it is assumed that the vlen,dlen,elen fields are * +* correct and v,d,e are dynamically allocated (or NULL); they are * +* reallocated if necessary. If sg2==NULL, a new structure is allocated. * +* A pointer to the copy is returned. * +* The new graph e component is the same, no compression is done. * +* * +*****************************************************************************/ + +sparsegraph* +copy_sg(sparsegraph *sg1, sparsegraph *sg2) +{ + int *d1,*e1,*d2,*e2; + int i,n; + size_t *v1,*v2,k; + sg_weight *wt1,*wt2; + + if (!sg2) + { + if ((sg2 = (sparsegraph*)ALLOCS(1,sizeof(sparsegraph))) == NULL) + { + fprintf(ERRFILE,"copy_sg: malloc failed\n"); + exit(1); + } + SG_INIT(*sg2); + } + + SWG_VDE(sg1,v1,d1,e1,wt1); + + n = sg1->nv; + + k = 0; + for (i = 0; i < n; ++i) + if (v1[i]+d1[i]>k) k = v1[i] + d1[i]; + + if (wt1) + SWG_ALLOC(*sg2,n,k,"copy_sg malloc"); + else + { + SG_ALLOC(*sg2,n,k,"copy_sg malloc"); + DYNFREE(sg2->w,sg2->wlen); + } + SWG_VDE(sg2,v2,d2,e2,wt2); + + sg2->nv = n; + sg2->nde = sg1->nde; + memcpy(v2,v1,n*sizeof(size_t)); + memcpy(d2,d1,n*sizeof(int)); + memcpy(e2,e1,k*sizeof(int)); + if (wt1) memcpy(wt2,wt1,k*sizeof(sg_weight)); + + return sg2; +} + +/***************************************************************************** +* * +* nauty_to_sg(g,sg,m,n) creates a sparse graph from a nauty format graph * +* If sg is not NULL, it is assumed that the vlen,dlen,elen fields are * +* correct and v,d,e are dynamically allocated (or NULL); they are * +* reallocated if necessary. If sg==NULL, a new structure is allocated. * +* A pointer to the sparse graph is returned. * +* * +*****************************************************************************/ + +sparsegraph* +nauty_to_sg(graph *g, sparsegraph *sg, int m, int n) +{ + int *d,*e; + int i,k; + set *gi; + size_t j,*v,nde; + + if (!sg) + { + if ((sg = (sparsegraph*)ALLOCS(1,sizeof(sparsegraph))) == NULL) + { + fprintf(ERRFILE,"nauty_to_sg: malloc failed\n"); + exit(1); + } + SG_INIT(*sg); + } + + nde = 0; + for (gi = g + (size_t)m*(size_t)n; --gi >= g; ) + if (*gi != 0) nde += POPCOUNT(*gi); + + sg->nv = n; + sg->nde = nde; + + SG_ALLOC(*sg,n,nde,"nauty_to_sg"); + + SG_VDE(sg,v,d,e); + + j = 0; + for (i = 0, gi = g; i < n; ++i, gi += m) + { + v[i] = j; + for (k = -1; (k = nextelement(gi,m,k)) >= 0; ) + e[j++] = k; + d[i] = j - v[i]; + } + + return sg; +} + +/***************************************************************************** +* * +* distances_sg() assigns to each vertex v a value depending on the number * +* of vertices at each distance from v, and what cells they lie in. * +* If we find any cell which is split in this manner, we don't try any * +* further cells. * +* * +*****************************************************************************/ + +void +distances_sg(graph *g, int *lab, int *ptn, int level, int numcells, int tvpos, + int *invar, int invararg, boolean digraph, int m, int n) +{ + int *d,*e; + int i,k,dlim,wt; + int di; + int cell1,cell2,iv,liv,kcode; + int head,tail; + long longcode; + size_t *v,vi,j; + boolean success; + + SG_VDE(g,v,d,e); + +#if !MAXN + DYNALLOC1(int,work1,work1_sz,n,"distances_sg"); + DYNALLOC1(int,work4,work4_sz,n,"distances_sg"); + DYNALLOC1(int,work3,work3_sz,n,"distances_sg"); +#endif +#define CELLCODE work1 +#define QUEUE work4 +#define DIST work3 + + for (i = n; --i >= 0;) invar[i] = 0; + + wt = 1; + for (i = 0; i < n; ++i) + { + CELLCODE[lab[i]] = FUZZ1(wt); + if (ptn[i] <= level) ++wt; + } + + if (invararg > n || invararg == 0) dlim = n; + else dlim = invararg+1; + + success = FALSE; + for (cell1 = 0; cell1 < n; cell1 = cell2 + 1) + { + for (cell2 = cell1; ptn[cell2] > level; ++cell2) {} + if (cell2 == cell1) continue; + + for (iv = cell1; iv <= cell2; ++iv) + { + liv = lab[iv]; + QUEUE[0] = liv; + DIST[liv] = 0; + RESETMARKS1; + MARK1(liv); + longcode = 0; + head = 0; + tail = 1; + + while (tail < n && head < tail) + { + i = QUEUE[head++]; + if (DIST[i] >= dlim) break; + vi = v[i]; + di = d[i]; + + for (j = 0; j < di; ++j) + { + k = e[vi+j]; + if (ISNOTMARKED1(k)) + { + MARK1(k); + DIST[k] = DIST[i] + 1; + kcode = DIST[k]+CELLCODE[k]; + ACCUM(longcode,FUZZ1(kcode)); + QUEUE[tail++] = k; + } + } + } + invar[liv] = CLEANUP(longcode); + if (invar[liv] != invar[lab[cell1]]) success = TRUE; + } + if (success) break; + } +} + +/***************************************************************************** +* * +* adjacencies_sg() assigns to each vertex v a code depending on which cells * +* it is joined to and from, and how many times. It is intended to provide * +* better partitioning that the normal refinement routine for digraphs. * +* It will not help with undirected graphs in nauty at all. * +* * +*****************************************************************************/ + +void +adjacencies_sg(graph *g, int *lab, int *ptn, int level, int numcells, + int tvpos, int *invar, int invararg, boolean digraph, + int m, int n) +{ + int *d,*e; + int vwt,wwt; + int *ei,di,i; + size_t *v,j; + + SG_VDE(g,v,d,e); + +#if !MAXN + DYNALLOC1(int,work2,work2_sz,n,"adjacencies_sg"); +#endif + + vwt = 1; + for (i = 0; i < n; ++i) + { + work2[lab[i]] = vwt; + if (ptn[i] <= level) ++vwt; + invar[i] = 0; + } + + for (i = 0; i < n; ++i) + { + vwt = FUZZ1(work2[i]); + wwt = 0; + di = d[i]; + ei = e + v[i]; + for (j = 0; j < di; ++j) + { + ACCUM(wwt,FUZZ2(work2[ei[j]])); + ACCUM(invar[ei[j]],vwt); + } + ACCUM(invar[i],wwt); + } +} + +/***************************************************************************** +* * +* sparsenauty(g,lab,ptn,orbits,&options,&stats,h) * +* is a slightly simplified interface to nauty(). It allocates enough * +* workspace for 500 automorphisms and checks that the sparsegraph dispatch * +* vector is in use. * +* * +*****************************************************************************/ + +void +sparsenauty(sparsegraph *g, int *lab, int *ptn, int *orbits, + optionblk *options, statsblk *stats, sparsegraph *h) +{ + int m,n; + + if (options->dispatch != &dispatch_sparse) + { + fprintf(ERRFILE,"Error: sparsenauty() needs standard options block\n"); + exit(1); + } + + n = g->nv; + m = SETWORDSNEEDED(n); + +#if !MAXN + /* Don't increase 2*500*m in the following without also increasing + the static decalaration of snwork[] above. */ + DYNALLOC1(set,snwork,snwork_sz,2*500*m,"densenauty malloc"); +#endif + + nauty((graph*)g,lab,ptn,NULL,orbits,options,stats, + snwork,2*500*m,m,n,(graph*)h); +} + +/***************************************************************************** +* * +* nausparse_check() checks that this file is compiled compatibly with the * +* given parameters. If not, call exit(1). * +* * +*****************************************************************************/ + +void +nausparse_check(int wordsize, int m, int n, int version) +{ + if (wordsize != WORDSIZE) + { + fprintf(ERRFILE,"Error: WORDSIZE mismatch in nausparse.c\n"); + exit(1); + } + +#if MAXN + if (m > MAXM) + { + fprintf(ERRFILE,"Error: MAXM inadequate in nausparse.c\n"); + exit(1); + } + + if (n > MAXN) + { + fprintf(ERRFILE,"Error: MAXN inadequate in nausparse.c\n"); + exit(1); + } +#endif + + if (version < NAUTYREQUIRED) + { + fprintf(ERRFILE,"Error: nausparse.c version mismatch\n"); + exit(1); + } +} + +/***************************************************************************** +* * +* nausparse_freedyn() - free the dynamic memory in this module * +* * +*****************************************************************************/ + +void +nausparse_freedyn(void) +{ +#if !MAXN + DYNFREE(vmark1,vmark1_sz); + DYNFREE(vmark2,vmark2_sz); + DYNFREE(work1,work1_sz); + DYNFREE(work2,work2_sz); + DYNFREE(work3,work3_sz); + DYNFREE(work4,work4_sz); + DYNFREE(snwork,snwork_sz); +#endif +} diff --git a/graph-checker/nauty/nausparse.h b/graph-checker/nauty/nausparse.h new file mode 100644 index 0000000..1a5bd5a --- /dev/null +++ b/graph-checker/nauty/nausparse.h @@ -0,0 +1,130 @@ +/* nausparse.h : header file for sparse digraphs, nauty 2.8 */ +/* This version allows only simple graphs with loops but + * contains the data structures for weights on the edges + * even though they aren't implemented yet. */ + +/***************************************************************************** +* * +* Copyright (1984-2022) Brendan McKay. All rights reserved. * +* Subject to the waivers and disclaimers in nauty.h. * +* * +* CHANGE HISTORY * +* 10-Nov-09 : removed types shortish and permutation * +* 20-May-10 : make some fields type size_t * +* 23-May-10 : add sparsenauty() * +* 3-Jun-10 : add *_tr procedures used by Traces * +* 30-Jun-10 : add DEFAULTOPTIONS_SPARSEDIGRAPH() * +* 18-Aug-12 : fix SG_DECL initialization order * +* 18-Jan-13 : add usercanonproc to default options * +* 17-Dec-15 : add macros for weighted graphs * +* * +*****************************************************************************/ + +#ifndef _NAUSPARSE_H_ /* only process this file once */ +#define _NAUSPARSE_H_ + +#include "nauty.h" + +#ifndef SG_WEIGHT +#define SG_WEIGHT int +#define SG_WEIGHT_FMT "%d" +#define SG_MINWEIGHT (-NAUTY_INFINITY) +#endif +typedef SG_WEIGHT sg_weight; + +#define CHECK_SWG(sg,id) do { if ((sg)->w) { fprintf(stderr, \ + ">E procedure %s does not accept weighted graphs\n",id); exit(1); } } while (0) + +typedef struct +{ + size_t nde; /* Number of directed edges (loops contribute only 1) */ + size_t *v; /* Array of indexes into e[*] */ + int nv; /* Number of vertices */ + int *d; /* Array with out-degree of each vertex */ + int *e; /* Array to hold lists of neighbours */ + sg_weight *w; /* Not implemented, should be NULL. */ + size_t vlen,dlen,elen,wlen; /* Sizes of arrays in units of type */ +} sparsegraph; + + +#define SG_VDE(sgp,vv,dd,ee) do { vv = ((sparsegraph*)(sgp))->v; \ + dd = ((sparsegraph*)(sgp))->d; ee = ((sparsegraph*)(sgp))->e; } while(0) +#define SWG_VDE(sgp,vv,dd,ee,ww) do { vv = ((sparsegraph*)(sgp))->v; \ + dd = ((sparsegraph*)(sgp))->d; ee = ((sparsegraph*)(sgp))->e; \ + ww = ((sparsegraph*)(sgp))->w; } while(0) +#define SG_INIT(sg) do { (sg).v = NULL; (sg).d = (sg).e = (sg).w = NULL; \ + (sg).vlen = (sg).dlen = (sg).elen = (sg).wlen = 0; } while(0) +#define SWG_INIT SG_INIT +#define SG_ALLOC(sg,nlen,ndelen,msg) do { \ + DYNALLOC1(size_t,(sg).v,(sg).vlen,nlen,msg); \ + DYNALLOC1(int,(sg).d,(sg).dlen,nlen,msg); \ + DYNALLOC1(int,(sg).e,(sg).elen,ndelen,msg); \ +} while (0) +#define SWG_ALLOC(sg,nlen,ndelen,msg) do { \ + DYNALLOC1(size_t,(sg).v,(sg).vlen,nlen,msg); \ + DYNALLOC1(int,(sg).d,(sg).dlen,nlen,msg); \ + DYNALLOC1(int,(sg).e,(sg).elen,ndelen,msg); \ + DYNALLOC1(sg_weight,(sg).w,(sg).wlen,ndelen,msg); \ +} while (0) +#define SG_FREE(sg) do { \ + DYNFREE((sg).v,(sg).vlen); \ + DYNFREE((sg).d,(sg).dlen); \ + DYNFREE((sg).e,(sg).elen); \ + if ((sg).w) DYNFREE((sg).w,(sg).wlen); } while (0) +#define SWG_FREE SG_FREE + +#define SG_DECL(sg) sparsegraph sg = {0,NULL,0,NULL,NULL,NULL,0,0,0,0} +#define SWG_DECL SG_DECL + +#define DEFAULTOPTIONS_SPARSEGRAPH(options) optionblk options = \ + {0,FALSE,FALSE,FALSE,TRUE,FALSE,CONSOLWIDTH, \ + NULL,NULL,NULL,NULL,NULL,NULL,NULL,100,0,1,0,&dispatch_sparse,FALSE,NULL} +#define DEFAULTOPTIONS_SPARSEDIGRAPH(options) optionblk options = \ + {0,TRUE,FALSE,FALSE,TRUE,FALSE,CONSOLWIDTH, \ + NULL,NULL,NULL,NULL,NULL,NULL,adjacencies_sg,100,0,999,0,&dispatch_sparse,FALSE,NULL} + +#ifdef __cplusplus +extern "C" { +#endif + +extern dispatchvec dispatch_sparse; + +extern int targetcell_sg(graph*,int*,int*,int,int,boolean,int,int,int); +extern boolean cheapautom_sg(int*,int,boolean,int); +extern boolean isautom_sg(graph*,int*,boolean,int,int); +extern void refine_sg(graph*,int*,int*,int,int*,int*,set*,int*,int,int); +extern int testcanlab_sg(graph*,graph*,int*,int*,int,int); +extern void updatecan_sg(graph*,graph*,int*,int,int,int); +extern int testcanlab_tr(sparsegraph*,sparsegraph*,int*,int*,int*); +extern int comparelab_tr(sparsegraph*,int*,int*,int*,int*,int*,int*); +extern void updatecan_tr(sparsegraph*,sparsegraph*,int*,int*,int); +extern void init_sg(graph*,graph**,graph*,graph**,int*,int*,set*, + struct optionstruct*,int*,int,int); +extern void cleanup_sg(graph*,graph**,graph*,graph**,int*, + int*,optionblk*,statsblk*stats,int,int); +extern void nausparse_freedyn(void); +extern void nausparse_check(int,int,int,int); + +extern sparsegraph *nauty_to_sg(graph*,sparsegraph*,int,int); +extern graph* sg_to_nauty(sparsegraph*,graph*,int,int*); +extern void sortlists_sg(sparsegraph*); +extern boolean aresame_sg(sparsegraph*,sparsegraph*); +extern void put_sg(FILE*,sparsegraph*,boolean,int); +extern sparsegraph *copy_sg(sparsegraph*,sparsegraph*); +extern void distvals(sparsegraph*,int,int*,int); + +extern void sparsenauty(sparsegraph*g,int*,int*,int*, + optionblk*,statsblk*,sparsegraph*); + +extern void + adjacencies_sg(graph*,int*,int*,int,int,int,int*,int,boolean,int,int); +extern void + distances_sg(graph*,int*,int*,int,int,int,int*,int,boolean,int,int); +extern void + distances_sg(graph*,int*,int*,int,int,int,int*,int,boolean,int,int); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/graph-checker/nauty/nautil.c b/graph-checker/nauty/nautil.c new file mode 100644 index 0000000..3ec8b1f --- /dev/null +++ b/graph-checker/nauty/nautil.c @@ -0,0 +1,750 @@ +/***************************************************************************** +* * +* 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 +} diff --git a/graph-checker/nauty/nautinv.c b/graph-checker/nauty/nautinv.c new file mode 100644 index 0000000..dee0b18 --- /dev/null +++ b/graph-checker/nauty/nautinv.c @@ -0,0 +1,1752 @@ +/***************************************************************************** +* * +* Vertex-invariants source file for nauty 2.8. * +* * +* Copyright (1989-2022) Brendan McKay. All rights reserved. * +* Subject to waivers and disclaimers in nauty.h. * +* * +* CHANGE HISTORY * +* 13-Mar-90 : initial release for version 1.5 * +* 10-Nov-90 : changes for version 1.6 : * +* - added dummy routine nautinv_null() * +* 27-Aug-92 : renamed to version 1.7, no changes to this file * +* 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 : changes for version 1.9 : * +* - added invariant routine adjacencies() * +* 20-Jun-96 : changes for version 2.0 : * +* - added invariants cellfano() and cellfano2() * +* 11-Jul-96 - added dynamic allocation * +* 21-Oct-98 - use shortish in place of short for BIGNAUTY * +* 9-Jan-00 - added nautinv_check() * +* 12-Feb-00 - minor code formatting * +* 16-Nov-00 - made changes listed in nauty.h * +* 22-Apr-01 : changes for version 2.1 : * +* - made all large dynamic memory external to routines * +* - added nautinv_freedyn() to free all such memory * +* - include nautinv.h rather than naututil.h * +* - removed nautinv_null() * +* - added code to facilitate compilation into Magma * +* - removed EXTDEFS * +* 12-Jul-01 - use invararg in distances() * +* - fixed comments in ind and cliq routines * +* 21-Nov-01 : use NAUTYREQUIRED in nautinv_check() * +* 10-Dec-06 : remove BIGNAUTY * +* 10-Nov-09 : remove types shortish and permutation * +* 23-Nov-09 : add refinvar() * +* 12-Jun-10 : fixed identical errors in cellcliq() and cellind() * +* 15-Jan-12 : add TLS_ATTR attributes * +* 23-Aug-12 : fix getbigcells(), thanks to Fatih Demirkale * +* 23-Jan-13 : add some parens to satisfy icc * +* * +*****************************************************************************/ + +#define ONE_WORD_SETS +#include "nautinv.h" + +#if MAXM==1 +#define M 1 +#else +#define M m +#endif + +#define MASH(l,i) ((((l) ^ 056345) + (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. */ + +#define ACCUM(x,y) x = (((x) + (y)) & 077777) + /* : must be commutative. */ + +static const int fuzz1[] = {037541,061532,005257,026416}; +static const int fuzz2[] = {006532,070236,035523,062437}; + +#define FUZZ1(x) ((x) ^ fuzz1[(x)&3]) +#define FUZZ2(x) ((x) ^ fuzz2[(x)&3]) + +#define MAXCLIQUE 10 /* max clique size for cliques() and maxindset() */ + +#if MAXN +static TLS_ATTR int workshort[MAXN+2]; +static TLS_ATTR int vv[MAXN],ww[MAXN]; +static TLS_ATTR int workperm[MAXN]; +static TLS_ATTR int bucket[MAXN+2]; +static TLS_ATTR int count[MAXN]; +static TLS_ATTR set workset[MAXM]; +static TLS_ATTR set w01[MAXM],w02[MAXM],w03[MAXM],w12[MAXM],w13[MAXM],w23[MAXM]; +static TLS_ATTR set pt0[MAXM],pt1[MAXM],pt2[MAXM]; +static TLS_ATTR set wss[MAXCLIQUE-1][MAXM]; +static TLS_ATTR set ws1[MAXM],ws2[MAXM]; +#else +DYNALLSTAT(int,workshort,workshort_sz); +DYNALLSTAT(int,vv,vv_sz); +DYNALLSTAT(int,ww,ww_sz); +DYNALLSTAT(int,workperm,workperm_sz); +DYNALLSTAT(int,bucket,bucket_sz); +DYNALLSTAT(int,count,count_sz); +DYNALLSTAT(set,ws1,ws1_sz); +DYNALLSTAT(set,ws2,ws2_sz); +DYNALLSTAT(set,workset,workset_sz); +DYNALLSTAT(set,w01,w01_sz); +DYNALLSTAT(set,w02,w02_sz); +DYNALLSTAT(set,w03,w03_sz); +DYNALLSTAT(set,w12,w12_sz); +DYNALLSTAT(set,w13,w13_sz); +DYNALLSTAT(set,w23,w23_sz); +DYNALLSTAT(set,pt0,pt0_sz); +DYNALLSTAT(set,pt1,pt1_sz); +DYNALLSTAT(set,pt2,pt2_sz); +DYNALLSTAT(set,wss,wss_sz); +#endif + +/* aproto: header new_nauty_protos.h */ + +/***************************************************************************** +* * +* This file contains a number of procedures which compute vertex-invariants * +* for stronger partition refinement. Since entirely different * +* vertex-invariants seem to work better for different types of graph, we * +* cannot do more than give a small collection of representative examples. * +* Any serious computations with difficult graphs may well need to use * +* specially-written vertex-invariants. The use of vertex-invariants * +* procedures is supported by nauty from version 1.5 onwards, via the * +* options userinvarproc, mininvarlevel, maxinvarlevel and invararg. * +* The meaning of these fields in detail are as follows: * +* userinvarproc is the address of the vertex-invariant procedure. If * +* no vertex-invariants is required, this field should * +* have the value NULL. * +* maxinvarlevel The absolute value of this is the maximum level in the * +* search tree at which the vertex-invariant will be * +* computed. The root of the tree is at level 1, so the * +* vertex-invariant will not be invoked at all if * +* maxinvarlevel==0. Negative values of maxinvarlevel * +* request nauty to not compute the vertex-invariant at * +* a level greater than that of the earliest node (if any) * +* on the path to the first leaf of the search tree at * +* which the vertex-invariant refines the partition. * +* mininvarlevel The absolute value of this is the minimum level in the * +* search tree at which the vertex-invariant will be * +* computed. The root of the tree is at level 1, so there * +* is no effective limit if mininvarlevel is -1, 0 or 1. * +* Negative values of mininvarlevel request nauty to not * +* compute the vertex-invariant at a level less than * +* that of the earliest node (if any) on the path to the * +* first leaf of the search tree at which the * +* vertex-invariant refines the partition. * +* invararg is passed to the vertex-invariant procedure via the * +* argument of the same name. It can be used by the * +* procedure for any purpose. * +* Note that negative values of maxinvarlevel and mininvarlevel make the * +* canonical labelling invalid, but can speed automorphism group finding. * +* Nauty already knows this and takes their absolute values. * +* * +* A vertex-invariant must be declared thus: * +* void invarproc(g,lab,ptn,level,numcells,tvpos,invar,invararg,digraph,m,n) * +* All of these arguments must be treated as read-only except for invar. * +* g : the graph, exactly as passed to nauty() * +* lab,ptn : the current partition nest (see nauty.h for the format) * +* level : the level of this node in the search tree. * +* numcells : the number of cells in the partition at this node. * +* tvpos : the index in (lab,ptn) of one cell in the partition. * +* If level <= 1, the cell will be the first fragment of the * +* first active cell (as provided by the initial call to nauty), * +* or the first cell, if there were no active cells. * +* If level > 1, the cell will be the singleton cell which was * +* created to make this node of the search tree from its parent. * +* invararg : a copy of options.invararg * +* digraph : a copy of options.digraph * +* m,n : size parameters as passed to nauty() * +* invar : an array to return the answer in. The procedure must put in * +* each invar[i] (0 <= i < n) an invariant of the 6-tuple * +* (<vertex i>,g,<the partition nest to this level>,level, * +* invararg,digraph) * +* Note that invar[] is declared as an int array. Since the * +* absolute value of the invariant is irrelevant, only the * +* comparative values, any short, int or long value can be * +* assigned to the entries of invar[] without fear. However, * +* you should assign a value less than 077777 to ensure machine- * +* independence of the canonical labelling. * +* * +* The refinement procedure has already been called before the invariant * +* procedure is called. That means that the partition is equitable if * +* digraph==FALSE. * +* * +*****************************************************************************/ + +/***************************************************************************** +* * +* twopaths() assigns to each vertex v the sum of the weights of each vertex * +* which can be reached from v along a walk of length two (including itself * +* usually). The weight of each vertex w is defined as the ordinal number * +* of the cell containing w, starting at 1 for the first cell. * +* * +*****************************************************************************/ + +void +twopaths(graph *g, int *lab, int *ptn, int level, int numcells, int tvpos, + int *invar, int invararg, boolean digraph, int m, int n) +{ + int i,v,w; + int wt; + set *gv,*gw; + +#if !MAXN + DYNALLOC1(set,workset,workset_sz,m,"twopaths"); + DYNALLOC1(int,workshort,workshort_sz,n+2,"twopaths"); +#endif + + wt = 1; + for (i = 0; i < n; ++i) + { + workshort[lab[i]] = wt; + if (ptn[i] <= level) ++wt; + } + + for (v = 0, gv = (set*)g; v < n; ++v, gv += M) + { + EMPTYSET(workset,m); + w = -1; + while ((w = nextelement(gv,M,w)) >= 0) + { + gw = GRAPHROW(g,w,m); + for (i = M; --i >= 0;) UNION(workset[i],gw[i]); + } + wt = 0; + w = -1; + while ((w = nextelement(workset,M,w)) >= 0) ACCUM(wt,workshort[w]); + invar[v] = wt; + } +} + +/***************************************************************************** +* * +* quadruples() assigns to each vertex v a value depending on the set of * +* weights w(v,v1,v2,v3), where w(v,v1,v2,v3) depends on the number of * +* vertices adjacent to an odd number of {v,v1,v2,v3}, and to the cells * +* that v,v1,v2,v3 belong to. {v,v1,v2,v3} are permitted to range over all * +* distinct 4-tuples which contain at least one member in the cell tvpos. * +* * +*****************************************************************************/ + +void +quadruples(graph *g, int *lab, int *ptn, int level, int numcells, int tvpos, + int *invar, int invararg, boolean digraph, int m, int n) +{ + int i,pc; + setword sw; + set *gw; + int wt; + int v,iv,v1,v2,v3; + set *gv; + long wv,wv1,wv2,wv3; + +#if !MAXN + DYNALLOC1(int,workshort,workshort_sz,n+2,"quadruples"); + DYNALLOC1(set,ws1,ws1_sz,m,"quadruples"); + DYNALLOC1(set,workset,workset_sz,m,"quadruples"); +#endif + + for (i = n; --i >= 0;) invar[i] = 0; + + wt = 1; + for (i = 0; i < n; ++i) + { + workshort[lab[i]] = FUZZ2(wt); + if (ptn[i] <= level) ++wt; + } + + iv = tvpos - 1; + do + { + v = lab[++iv]; + gv = GRAPHROW(g,v,m); + wv = workshort[v]; + for (v1 = 0; v1 < n-2; ++v1) + { + wv1 = workshort[v1]; + if (wv1 == wv && v1 <= v) continue; + wv1 += wv; + gw = GRAPHROW(g,v1,m); + for (i = M; --i >= 0;) workset[i] = gv[i] ^ gw[i]; + for (v2 = v1+1; v2 < n-1; ++v2) + { + wv2 = workshort[v2]; + if (wv2 == wv && v2 <= v) continue; + wv2 += wv1; + gw = GRAPHROW(g,v2,m); + for (i = M; --i >= 0;) ws1[i] = workset[i] ^ gw[i]; + for (v3 = v2+1; v3 < n; ++v3) + { + wv3 = workshort[v3]; + if (wv3 == wv && v3 <= v) continue; + wv3 += wv2; + gw = GRAPHROW(g,v3,m); + pc = 0; + for (i = M; --i >= 0;) + if ((sw = ws1[i] ^ gw[i]) != 0) pc += POPCOUNT(sw); + wt = (FUZZ1(pc)+wv3) & 077777; + wt = FUZZ2(wt); + ACCUM(invar[v],wt); + ACCUM(invar[v1],wt); + ACCUM(invar[v2],wt); + ACCUM(invar[v3],wt); + } + } + } + } + while (ptn[iv] > level); +} + +/***************************************************************************** +* * +* triples() assigns to each vertex v a value depending on the set of * +* weights w(v,v1,v2), where w(v,v1,v2) depends on the number of vertices * +* adjacent to an odd number of {v,v1,v2}, and to the cells that * +* v,v1,v2 belong to. {v,v1,v2} are permitted to range over all distinct * +* triples which contain at least one member in the cell tvpos. * +* * +*****************************************************************************/ + +void +triples(graph *g, int *lab, int *ptn, int level, int numcells, int tvpos, + int *invar, int invararg, boolean digraph, int m, int n) +{ + int i,pc; + setword sw; + set *gw; + int wt; + int v,iv,v1,v2; + set *gv; + long wv,wv1,wv2; + +#if !MAXN + DYNALLOC1(set,workset,workset_sz,m,"triples"); + DYNALLOC1(int,workshort,workshort_sz,n+2,"triples"); +#endif + + for (i = n; --i >= 0;) invar[i] = 0; + + wt = 1; + for (i = 0; i < n; ++i) + { + workshort[lab[i]] = FUZZ1(wt); + if (ptn[i] <= level) ++wt; + } + + iv = tvpos - 1; + do + { + v = lab[++iv]; + gv = GRAPHROW(g,v,m); + wv = workshort[v]; + for (v1 = 0; v1 < n-1; ++v1) + { + wv1 = workshort[v1]; + if (wv1 == wv && v1 <= v) continue; + wv1 += wv; + gw = GRAPHROW(g,v1,m); + for (i = M; --i >= 0;) workset[i] = gv[i] ^ gw[i]; + for (v2 = v1+1; v2 < n; ++v2) + { + wv2 = workshort[v2]; + if (wv2 == wv && v2 <= v) continue; + wv2 += wv1; + gw = GRAPHROW(g,v2,m); + pc = 0; + for (i = M; --i >= 0;) + if ((sw = workset[i] ^ gw[i]) != 0) pc += POPCOUNT(sw); + wt = (FUZZ1(pc)+wv2) & 077777; + wt = FUZZ2(wt); + ACCUM(invar[v],wt); + ACCUM(invar[v1],wt); + ACCUM(invar[v2],wt); + } + } + } + while (ptn[iv] > level); +} + +/***************************************************************************** +* * +* adjtriang() assigns to each vertex v a value depending on the numbers * +* of common neighbours between each pair {v1,v2} of neighbours of v, and * +* which cells v1 and v2 lie in. The vertices v1 and v2 must be adjacent * +* if invararg == 0 and not adjacent if invararg == 1. * +* * +*****************************************************************************/ + +void +adjtriang(graph *g, int *lab, int *ptn, int level, int numcells, int tvpos, + int *invar, int invararg, boolean digraph, int m, int n) +{ + int j,pc; + setword sw; + set *gi; + int wt; + int i,v1,v2; + boolean v1v2; + set *gv1,*gv2; + +#if !MAXN + DYNALLOC1(set,workset,workset_sz,m,"adjtriang"); + DYNALLOC1(int,workshort,workshort_sz,n+2,"adjtriang"); +#endif + + for (i = n; --i >= 0;) invar[i] = 0; + + wt = 1; + for (i = 0; i < n; ++i) + { + workshort[lab[i]] = FUZZ1(wt); + if (ptn[i] <= level) ++wt; + } + + for (v1 = 0, gv1 = g; v1 < n; ++v1, gv1 += M) + { + for (v2 = (digraph ? 0 : v1+1); v2 < n; ++v2) + { + if (v2 == v1) continue; + v1v2 = (ISELEMENT(gv1,v2) != 0); + if ((invararg == 0 && !v1v2) + || (invararg == 1 && v1v2)) continue; + wt = workshort[v1]; + ACCUM(wt,workshort[v2]); + ACCUM(wt,v1v2); + + gv2 = GRAPHROW(g,v2,m); + for (i = M; --i >= 0;) workset[i] = gv1[i] & gv2[i]; + i = -1; + while ((i = nextelement(workset,M,i)) >= 0) + { + pc = 0; + gi = GRAPHROW(g,i,m); + for (j = M; --j >= 0;) + if ((sw = workset[j] & gi[j]) != 0) pc += POPCOUNT(sw); + pc = (pc + wt) & 077777; + ACCUM(invar[i],pc); + } + } + } +} + +/***************************************************************************** +* * +* getbigcells(ptn,level,minsize,bigcells,cellstart,cellsize,n) is an * +* auxiliary procedure to make a list of all the large cells in the current * +* partition. On entry, ptn, level and n have their usual meanings, * +* while minsize is the smallest size of an interesting cell. On return, * +* bigcells is the number of cells of size at least minsize, cellstart[0...] * +* contains their starting positions in ptn, and cellsize[0...] contain * +* their sizes. These two arrays are in increasing order of cell size, * +* then position. * +* * +*****************************************************************************/ + +void +getbigcells(int *ptn, int level, int minsize, int *bigcells, + int *cellstart, int *cellsize, int n) +{ + int cell1,cell2,j; + int si,st; + int bc,i,h; + + bc = 0; + for (cell1 = 0; cell1 < n; cell1 = cell2 + 1) + { + for (cell2 = cell1; ptn[cell2] > level; ++cell2) {} + + if (cell2 >= cell1 + minsize - 1) + { + cellstart[bc] = cell1; + cellsize[bc] = cell2 - cell1 + 1; + ++bc; + } + } + *bigcells = bc; + + j = bc / 3; + h = 1; + do + h = 3 * h + 1; + while (h < j); + + do /* shell sort */ + { + for (i = h; i < bc; ++i) + { + st = cellstart[i]; + si = cellsize[i]; + for (j = i; cellsize[j-h] > si || + (cellsize[j-h] == si && cellstart[j-h] > st); ) + { + cellsize[j] = cellsize[j-h]; + cellstart[j] = cellstart[j-h]; + if ((j -= h) < h) break; + } + cellsize[j] = si; + cellstart[j] = st; + } + h /= 3; + } + while (h > 0); +} + +/***************************************************************************** +* * +* celltrips() assigns to each vertex v a value depending on the set of * +* weights w(v,v1,v2), where w(v,v1,v2) depends on the number of vertices * +* adjacent to an odd number of {v,v1,v2}. {v,v1,v2} are constrained to * +* belong to the same cell. We try the cells in increasing order of size, * +* and stop as soon as any cell splits. * +* * +*****************************************************************************/ + +void +celltrips(graph *g, int *lab, int *ptn, int level, int numcells, int tvpos, + int *invar, int invararg, boolean digraph, int m, int n) +{ + int i,pc; + setword sw; + set *gw; + int wt; + int v,iv,v1,iv1,v2,iv2; + int icell,bigcells,cell1,cell2; + int *cellstart,*cellsize; + set *gv; + +#if !MAXN + DYNALLOC1(set,workset,workset_sz,m,"celltrips"); + DYNALLOC1(int,workshort,workshort_sz,n+2,"celltrips"); +#endif + + for (i = n; --i >= 0;) invar[i] = 0; + + cellstart = workshort; + cellsize = workshort + (n/2); + getbigcells(ptn,level,3,&bigcells,cellstart,cellsize,n); + + for (icell = 0; icell < bigcells; ++icell) + { + cell1 = cellstart[icell]; + cell2 = cell1 + cellsize[icell] - 1; + for (iv = cell1; iv <= cell2 - 2; ++iv) + { + v = lab[iv]; + gv = GRAPHROW(g,v,m); + for (iv1 = iv + 1; iv1 <= cell2 - 1; ++iv1) + { + v1 = lab[iv1]; + gw = GRAPHROW(g,v1,m); + for (i = M; --i >= 0;) workset[i] = gv[i] ^ gw[i]; + for (iv2 = iv1 + 1; iv2 <= cell2; ++iv2) + { + v2 = lab[iv2]; + gw = GRAPHROW(g,v2,m); + pc = 0; + for (i = M; --i >= 0;) + if ((sw = workset[i] ^ gw[i]) != 0) + pc += POPCOUNT(sw); + wt = FUZZ1(pc); + ACCUM(invar[v],wt); + ACCUM(invar[v1],wt); + ACCUM(invar[v2],wt); + } + } + } + wt = invar[lab[cell1]]; + for (i = cell1 + 1; i <= cell2; ++i) + if (invar[lab[i]] != wt) return; + } +} + +/***************************************************************************** +* * +* cellquads() assigns to each vertex v a value depending on the set of * +* weights w(v,v1,v2,v3), where w(v,v1,v2,v3) depends on the number of * +* vertices adjacent to an odd number of {v,v1,v2,v3}. {v,v1,v2,v3} are * +* constrained to belong to the same cell. We try the cells in increasing * +* order of size, and stop as soon as any cell splits. * +* * +*****************************************************************************/ + +void +cellquads(graph *g, int *lab, int *ptn, int level, int numcells, int tvpos, + int *invar, int invararg, boolean digraph, int m, int n) +{ + int i,pc; + setword sw; + set *gw; + int wt; + int v,iv,v1,iv1,v2,iv2,v3,iv3; + int icell,bigcells,cell1,cell2; + int *cellstart,*cellsize; + set *gv; + +#if !MAXN + DYNALLOC1(set,workset,workset_sz,m,"cellquads"); + DYNALLOC1(int,workshort,workshort_sz,n+2,"cellquads"); + DYNALLOC1(set,ws1,ws1_sz,m,"cellquads"); +#endif + + for (i = n; --i >= 0;) invar[i] = 0; + + cellstart = workshort; + cellsize = workshort + (n/2); + getbigcells(ptn,level,4,&bigcells,cellstart,cellsize,n); + + for (icell = 0; icell < bigcells; ++icell) + { + cell1 = cellstart[icell]; + cell2 = cell1 + cellsize[icell] - 1; + for (iv = cell1; iv <= cell2 - 3; ++iv) + { + v = lab[iv]; + gv = GRAPHROW(g,v,m); + for (iv1 = iv + 1; iv1 <= cell2 - 2; ++iv1) + { + v1 = lab[iv1]; + gw = GRAPHROW(g,v1,m); + for (i = M; --i >= 0;) workset[i] = gv[i] ^ gw[i]; + for (iv2 = iv1 + 1; iv2 <= cell2 - 1; ++iv2) + { + v2 = lab[iv2]; + gw = GRAPHROW(g,v2,m); + for (i = M; --i >= 0;) ws1[i] = workset[i] ^ gw[i]; + for (iv3 = iv2 + 1; iv3 <= cell2; ++iv3) + { + v3 = lab[iv3]; + gw = GRAPHROW(g,v3,m); + pc = 0; + for (i = M; --i >= 0;) + if ((sw = ws1[i] ^ gw[i]) != 0) + pc += POPCOUNT(sw); + wt = FUZZ1(pc); + ACCUM(invar[v],wt); + ACCUM(invar[v1],wt); + ACCUM(invar[v2],wt); + ACCUM(invar[v3],wt); + } + } + } + } + wt = invar[lab[cell1]]; + for (i = cell1 + 1; i <= cell2; ++i) + if (invar[lab[i]] != wt) return; + } +} + +/***************************************************************************** +* * +* cellquins() assigns to each vertex v a value depending on the set of * +* weights w(v,v1,v2,v3,v4), where w(v,v1,v2,v3,v4) depends on the number * +* of vertices adjacent to an odd number of {v,v1,v2,v3,v4}. * +* {v,v1,v2,v3,v4} are constrained to belong to the same cell. We try the * +* cells in increasing order of size, and stop as soon as any cell splits. * +* * +*****************************************************************************/ + +void +cellquins(graph *g, int *lab, int *ptn, int level, int numcells, int tvpos, + int *invar, int invararg, boolean digraph, int m, int n) +{ + int i,pc; + setword sw; + set *gw; + int wt; + int v,iv,v1,iv1,v2,iv2,v3,iv3,v4,iv4; + int icell,bigcells,cell1,cell2; + int *cellstart,*cellsize; + set *gv; + +#if !MAXN + DYNALLOC1(set,workset,workset_sz,m,"cellquins"); + DYNALLOC1(int,workshort,workshort_sz,n+2,"cellquins"); + DYNALLOC1(set,ws1,ws1_sz,m,"cellquins"); + DYNALLOC1(set,ws2,ws2_sz,m,"cellquins"); +#endif + + for (i = n; --i >= 0;) invar[i] = 0; + + cellstart = workshort; + cellsize = workshort + (n/2); + getbigcells(ptn,level,5,&bigcells,cellstart,cellsize,n); + + for (icell = 0; icell < bigcells; ++icell) + { + cell1 = cellstart[icell]; + cell2 = cell1 + cellsize[icell] - 1; + for (iv = cell1; iv <= cell2 - 4; ++iv) + { + v = lab[iv]; + gv = GRAPHROW(g,v,m); + for (iv1 = iv + 1; iv1 <= cell2 - 3; ++iv1) + { + v1 = lab[iv1]; + gw = GRAPHROW(g,v1,m); + for (i = M; --i >= 0;) workset[i] = gv[i] ^ gw[i]; + for (iv2 = iv1 + 1; iv2 <= cell2 - 2; ++iv2) + { + v2 = lab[iv2]; + gw = GRAPHROW(g,v2,m); + for (i = M; --i >= 0;) ws1[i] = workset[i] ^ gw[i]; + for (iv3 = iv2 + 1; iv3 <= cell2 - 1; ++iv3) + { + v3 = lab[iv3]; + gw = GRAPHROW(g,v3,m); + for (i = M; --i >= 0;) ws2[i] = ws1[i] ^ gw[i]; + for (iv4 = iv3 + 1; iv4 <= cell2; ++iv4) + { + v4 = lab[iv4]; + gw = GRAPHROW(g,v4,m); + pc = 0; + for (i = M; --i >= 0;) + if ((sw = ws2[i] ^ gw[i]) != 0) + pc += POPCOUNT(sw); + wt = FUZZ1(pc); + ACCUM(invar[v],wt); + ACCUM(invar[v1],wt); + ACCUM(invar[v2],wt); + ACCUM(invar[v3],wt); + ACCUM(invar[v4],wt); + } + } + } + } + } + wt = invar[lab[cell1]]; + for (i = cell1 + 1; i <= cell2; ++i) + if (invar[lab[i]] != wt) return; + } +} + +/***************************************************************************** +* * +* uniqinter(s1,s2,m) returns the number in both sets if it is unique, * +* or -1 if there is none or it is not unique. * +*****************************************************************************/ + +static int +uniqinter(set *s1, set *s2, int m) +{ + int i,j; + setword w; + + for (i = 0; i < M; ++i) + { + if ((w = s1[i] & s2[i]) != 0) + { + j = FIRSTBITNZ(w); + if (w != BITT[j]) return -1; + j += TIMESWORDSIZE(i); + for (++i; i < M; ++i) + if (s1[i] & s2[i]) return -1; + return j; + } + } + return -1; +} + +/***************************************************************************** +* * +* cellfano2() assigns to each vertex v a value depending on the set of * +* weights w(v,v1,v2,v3), where w(v,v1,v2,v3) depends on the number of * +* fano-plane analogues containing {v,v1,v2,v3}. {v,v1,v2,v3} are * +* constrained to belong to the same cell and being independent and * +* non-collinear. We try the cells in increasing order of size, and stop * +* as soon as any cell splits. * +* * +*****************************************************************************/ + +void +cellfano2(graph *g, int *lab, int *ptn, int level, int numcells, int tvpos, + int *invar, int invararg, boolean digraph, int m, int n) +{ + int i,pc; + setword sw; + int wt; + int v0,v1,v2,v3,iv0,iv1,iv2,iv3; + int icell,bigcells,cell1,cell2; + int *cellstart,*cellsize; + int nw,x01,x02,x03,x12,x13,x23; + int pnt0,pnt1,pnt2; + set *gv0,*gv1,*gv2,*gv3; + set *gp0,*gp1,*gp2; + +#if !MAXN + DYNALLOC1(int,workshort,workshort_sz,n+2,"cellfano2"); + DYNALLOC1(int,vv,vv_sz,n,"cellfano2"); + DYNALLOC1(int,ww,ww_sz,n,"cellfano2"); +#endif + + for (i = n; --i >= 0;) invar[i] = 0; + + cellstart = workshort; + cellsize = workshort + (n/2); + getbigcells(ptn,level,4,&bigcells,cellstart,cellsize,n); + + for (icell = 0; icell < bigcells; ++icell) + { + cell1 = cellstart[icell]; + cell2 = cell1 + cellsize[icell] - 1; + for (iv0 = cell1; iv0 <= cell2 - 3; ++iv0) + { + v0 = lab[iv0]; + gv0 = GRAPHROW(g,v0,m); + nw = 0; + for (iv1 = iv0 + 1; iv1 <= cell2; ++iv1) + { + v1 = lab[iv1]; + if (ISELEMENT(gv0,v1)) continue; + if ((x01 = uniqinter(gv0,GRAPHROW(g,v1,m),m)) < 0) continue; + vv[nw] = v1; + ww[nw] = x01; + ++nw; + } + + for (iv1 = 0; iv1 < nw-2; ++iv1) + { + v1 = vv[iv1]; + gv1 = GRAPHROW(g,v1,m); + x01 = ww[iv1]; + + for (iv2 = iv1 + 1; iv2 < nw-1; ++iv2) + { + x02 = ww[iv2]; + if (x02 == x01) continue; + v2 = vv[iv2]; + if (ISELEMENT(gv1,v2)) continue; + gv2 = GRAPHROW(g,v2,m); + if ((x12 = uniqinter(gv1,gv2,m)) < 0) continue; + + for (iv3 = iv2 + 1; iv3 < nw; ++iv3) + { + x03 = ww[iv3]; + if (x03 == x01 || x03 == x02) continue; + v3 = vv[iv3]; + if (ISELEMENT(gv1,v3) || ISELEMENT(gv2,v3)) + continue; + gv3 = GRAPHROW(g,v3,m); + if ((x13 = uniqinter(gv1,gv3,m)) < 0) continue; + if ((x23 = uniqinter(gv2,gv3,m)) < 0 + || x23 == x13) continue; + + if ((pnt0 = uniqinter(GRAPHROW(g,x01,m), + GRAPHROW(g,x23,m),m)) < 0) + continue; + if ((pnt1 = uniqinter(GRAPHROW(g,x02,m), + GRAPHROW(g,x13,m),m)) < 0) + continue; + if ((pnt2 = uniqinter(GRAPHROW(g,x03,m), + GRAPHROW(g,x12,m),m)) < 0) + continue; + + gp0 = GRAPHROW(g,pnt0,m); + gp1 = GRAPHROW(g,pnt1,m); + gp2 = GRAPHROW(g,pnt2,m); + + pc = 0; + for (i = M; --i >= 0;) + { + sw = gp0[i] & gp1[i] & gp2[i]; + if (sw) pc += POPCOUNT(sw); + } + wt = FUZZ1(pc); + ACCUM(invar[v0],wt); + ACCUM(invar[v1],wt); + ACCUM(invar[v2],wt); + ACCUM(invar[v3],wt); + } + } + } + } + wt = invar[lab[cell1]]; + for (i = cell1 + 1; i <= cell2; ++i) + if (invar[lab[i]] != wt) return; + } +} + +/***************************************************************************** +* * +* setnbhd(g,m,n,w,wn) is an auxiliary routine that sets wn to the union * +* of the neighbours of the vertices in w. * +* * +*****************************************************************************/ + +void +setnbhd(graph *g, int m, int n, set *w, set *wn) +{ + int i,j; + set *gi; + + i = nextelement(w,M,-1); + if (i < 0) + { + EMPTYSET(wn,M); + return; + } + + gi = GRAPHROW(g,i,M); + for (j = M; --j >= 0;) wn[j] = gi[j]; + + while ((i = nextelement(w,M,i)) >= 0) + { + gi = GRAPHROW(g,i,M); + for (j = M; --j >= 0;) wn[j] |= gi[j]; + } +} + +/***************************************************************************** +* * +* cellfano() assigns to each vertex v a value depending on the set of * +* weights w(v,v1,v2,v3), where w(v,v1,v2,v3) depends on the number of * +* fano-plane analogues containing {v,v1,v2,v3}. {v,v1,v2,v3} are * +* constrained to belong to the same cell and being independent. We try * +* the cells in increasing order of size, and stop as soon as any cell * +* splits. * +* * +*****************************************************************************/ + +void +cellfano(graph *g, int *lab, int *ptn, int level, int numcells, int tvpos, + int *invar, int invararg, boolean digraph, int m, int n) +{ + int i,pc; + setword sw; + int wt; + int v0,v1,v2,v3,iv0,iv1,iv2,iv3; + int icell,bigcells,cell1,cell2; + int *cellstart,*cellsize; + set *gv0,*gv1,*gv2,*gv3; + +#if !MAXN + DYNALLOC1(int,workshort,workshort_sz,n+2,"cellfano"); + DYNALLOC1(set,w01,w01_sz,m,"cellfano"); + DYNALLOC1(set,w02,w02_sz,m,"cellfano"); + DYNALLOC1(set,w03,w03_sz,m,"cellfano"); + DYNALLOC1(set,w12,w12_sz,m,"cellfano"); + DYNALLOC1(set,w13,w13_sz,m,"cellfano"); + DYNALLOC1(set,w23,w23_sz,m,"cellfano"); + DYNALLOC1(set,pt0,pt0_sz,m,"cellfano"); + DYNALLOC1(set,pt1,pt1_sz,m,"cellfano"); + DYNALLOC1(set,pt2,pt2_sz,m,"cellfano"); + DYNALLOC1(set,workset,workset_sz,m,"cellfano"); +#else +#endif + + for (i = n; --i >= 0;) invar[i] = 0; + + cellstart = workshort; + cellsize = workshort + (n/2); + getbigcells(ptn,level,4,&bigcells,cellstart,cellsize,n); + + for (icell = 0; icell < bigcells; ++icell) + { + cell1 = cellstart[icell]; + cell2 = cell1 + cellsize[icell] - 1; + for (iv0 = cell1; iv0 <= cell2 - 3; ++iv0) + { + v0 = lab[iv0]; + gv0 = GRAPHROW(g,v0,m); + for (iv1 = iv0 + 1; iv1 <= cell2 - 2; ++iv1) + { + v1 = lab[iv1]; + if (ISELEMENT(gv0,v1)) continue; + gv1 = GRAPHROW(g,v1,m); + for (i = M; --i >= 0;) workset[i] = gv0[i] & gv1[i]; + setnbhd(g,m,n,workset,w01); + + for (iv2 = iv1 + 1; iv2 <= cell2 - 1; ++iv2) + { + v2 = lab[iv2]; + if (ISELEMENT(gv0,v2) || ISELEMENT(gv1,v2)) + continue; + gv2 = GRAPHROW(g,v2,m); + for (i = M; --i >= 0;) workset[i] = gv0[i] & gv2[i]; + setnbhd(g,m,n,workset,w02); + for (i = M; --i >= 0;) workset[i] = gv1[i] & gv2[i]; + setnbhd(g,m,n,workset,w12); + + for (iv3 = iv2 + 1; iv3 <= cell2; ++iv3) + { + v3 = lab[iv3]; + if (ISELEMENT(gv0,v3) || ISELEMENT(gv1,v3) || + ISELEMENT(gv2,v3)) + continue; + gv3 = GRAPHROW(g,v3,m); + for (i = M; --i >= 0;) workset[i] = gv0[i] & gv3[i]; + setnbhd(g,m,n,workset,w03); + for (i = M; --i >= 0;) workset[i] = gv1[i] & gv3[i]; + setnbhd(g,m,n,workset,w13); + for (i = M; --i >= 0;) workset[i] = gv2[i] & gv3[i]; + setnbhd(g,m,n,workset,w23); + + for (i = M; --i >= 0;) workset[i] = w01[i] & w23[i]; + setnbhd(g,m,n,workset,pt0); + for (i = M; --i >= 0;) workset[i] = w03[i] & w12[i]; + setnbhd(g,m,n,workset,pt1); + for (i = M; --i >= 0;) workset[i] = w02[i] & w13[i]; + setnbhd(g,m,n,workset,pt2); + pc = 0; + for (i = M; --i >= 0;) + { + sw = pt0[i] & pt1[i] & pt2[i]; + if (sw) pc += POPCOUNT(sw); + } + wt = FUZZ1(pc); + ACCUM(invar[v0],wt); + ACCUM(invar[v1],wt); + ACCUM(invar[v2],wt); + ACCUM(invar[v3],wt); + } + } + } + } + wt = invar[lab[cell1]]; + for (i = cell1 + 1; i <= cell2; ++i) + if (invar[lab[i]] != wt) return; + } +} + +/***************************************************************************** +* * +* distances() assigns to each vertex v a value depending on the number of * +* vertices at each distance from v, and what cells they lie in. * +* If we find any cell which is split in this manner, we don't try any * +* further cells. * +* * +*****************************************************************************/ + +void +distances(graph *g, int *lab, int *ptn, int level, int numcells, int tvpos, + int *invar, int invararg, boolean digraph, int m, int n) +{ + int i; + set *gw; + int wt; + int d,dlim,cell1,cell2,iv,v,w; + boolean success; + +#if !MAXN + DYNALLOC1(set,workset,workset_sz,m,"distances"); + DYNALLOC1(int,workshort,workshort_sz,n+2,"distances"); + DYNALLOC1(set,ws1,ws1_sz,m,"distances"); + DYNALLOC1(set,ws2,ws2_sz,m,"distances"); +#endif + + for (i = n; --i >= 0;) invar[i] = 0; + + wt = 1; + for (i = 0; i < n; ++i) + { + workshort[lab[i]] = FUZZ1(wt); + if (ptn[i] <= level) ++wt; + } + + if (invararg > n || invararg == 0) dlim = n; + else dlim = invararg+1; + + success = FALSE; + for (cell1 = 0; cell1 < n; cell1 = cell2 + 1) + { + for (cell2 = cell1; ptn[cell2] > level; ++cell2) {} + if (cell2 == cell1) continue; + + for (iv = cell1; iv <= cell2; ++iv) + { + v = lab[iv]; + EMPTYSET(ws1,m); + ADDELEMENT(ws1,v); + EMPTYSET(ws2,m); + ADDELEMENT(ws2,v); + for (d = 1; d < dlim; ++d) + { + EMPTYSET(workset,m); + wt = 0; + w = -1; + while ((w = nextelement(ws2,M,w)) >= 0) + { + gw = GRAPHROW(g,w,m); + ACCUM(wt,workshort[w]); + for (i = M; --i >= 0;) workset[i] |= gw[i]; + } + if (wt == 0) break; + ACCUM(wt,d); + wt = FUZZ2(wt); + ACCUM(invar[v],wt); + for (i = M; --i >= 0;) + { + ws2[i] = workset[i] & ~ws1[i]; + ws1[i] |= ws2[i]; + } + } + if (invar[v] != invar[lab[cell1]]) success = TRUE; + } + if (success) break; + } +} + +/***************************************************************************** +* * +* indsets() assigns to each vertex v a value depending on which cells the * +* vertices which join v in an independent set lie in. The size of the * +* independent sets which are used is the smallest of invararg and MAXCLIQUE.* +* * +*****************************************************************************/ + +void +indsets(graph *g, int *lab, int *ptn, int level, int numcells, int tvpos, + int *invar, int invararg, boolean digraph, int m, int n) +{ + int i; + int wt; + set *gv; + int ss,setsize; + int v[MAXCLIQUE]; + long wv[MAXCLIQUE]; + set *s0,*s1; + +#if !MAXN + DYNALLOC1(int,workshort,workshort_sz,n+2,"indsets"); + DYNALLOC2(set,wss,wss_sz,m,MAXCLIQUE-1,"indsets"); +#endif + + for (i = n; --i >= 0;) invar[i] = 0; + + if (invararg <= 1 || digraph) return; + + if (invararg > MAXCLIQUE) setsize = MAXCLIQUE; + else setsize = invararg; + + wt = 1; + for (i = 0; i < n; ++i) + { + workshort[lab[i]] = FUZZ2(wt); + if (ptn[i] <= level) ++wt; + } + + for (v[0] = 0; v[0] < n; ++v[0]) + { + wv[0] = workshort[v[0]]; + s0 = (set*)wss; + EMPTYSET(s0,m); + for (i = v[0]+1; i < n; ++i) ADDELEMENT(s0,i); + gv = GRAPHROW(g,v[0],m); + for (i = M; --i >= 0;) s0[i] &= ~gv[i]; + ss = 1; + v[1] = v[0]; + while (ss > 0) + { + if (ss == setsize) + { + wt = FUZZ1(wv[ss-1]); + for (i = ss; --i >= 0;) ACCUM(invar[v[i]],wt); + --ss; + } + else if ((v[ss] = nextelement((set*)wss+M*(ss-1),M,v[ss])) < 0) + --ss; + else + { + wv[ss] = wv[ss-1] + workshort[v[ss]]; + ++ss; + if (ss < setsize) + { + gv = GRAPHROW(g,v[ss-1],m); + s1 = (set*)wss + M*(ss-2); + for (i = M; --i >= 0;) s1[i+M] = s1[i] & ~gv[i]; + v[ss] = v[ss-1]; + } + } + } + } +} + +/***************************************************************************** +* * +* cliques() assigns to each vertex v a value depending on which cells the * +* vertices which join v in a clique lie in. The size of the cliques used * +* is the smallest of invararg and MAXCLIQUE. * +* * +*****************************************************************************/ + +void +cliques(graph *g, int *lab, int *ptn, int level, int numcells, int tvpos, + int *invar, int invararg, boolean digraph, int m, int n) +{ + int i; + int wt; + set *gv; + int ss,setsize; + int v[MAXCLIQUE]; + long wv[MAXCLIQUE]; + set *ns; + +#if !MAXN + DYNALLOC1(int,workshort,workshort_sz,n+2,"cliques"); + DYNALLOC2(set,wss,wss_sz,m,MAXCLIQUE-1,"cliques"); +#else + set wss[MAXCLIQUE-1][MAXM]; +#endif + + for (i = n; --i >= 0;) invar[i] = 0; + + if (invararg <= 1 || digraph) return; + + if (invararg > MAXCLIQUE) setsize = MAXCLIQUE; + else setsize = invararg; + + wt = 1; + for (i = 0; i < n; ++i) + { + workshort[lab[i]] = FUZZ2(wt); + if (ptn[i] <= level) ++wt; + } + + for (v[0] = 0; v[0] < n; ++v[0]) + { + wv[0] = workshort[v[0]]; + gv = GRAPHROW(g,v[0],m); + ns = (set*)wss; + for (i = M; --i >= 0;) ns[i] = gv[i]; + ss = 1; + v[1] = v[0]; + while (ss > 0) + { + if (ss == setsize) + { + wt = FUZZ1(wv[ss-1]); + for (i = ss; --i >= 0;) ACCUM(invar[v[i]],wt); + --ss; + } + else if ((v[ss] = nextelement((set*)wss+M*(ss-1),M,v[ss])) < 0) + --ss; + else + { + wv[ss] = wv[ss-1] + workshort[v[ss]]; + ++ss; + if (ss < setsize) + { + gv = GRAPHROW(g,v[ss-1],m); + ns = (set*)wss + M*(ss-2); + for (i = M; --i >= 0;) ns[i+M] = ns[i] & gv[i]; + v[ss] = v[ss-1]; + } + } + } + } +} + +/***************************************************************************** +* * +* cellcliq() assigns to each vertex v a value depending on the number of * +* cliques which v lies in and which lie in the same cell as v. The size * +* of clique counted is the smallest of invararg and MAXCLIQUE. We try the * +* cells in increasing order of size and stop as soon as any cell splits. * +* * +*****************************************************************************/ + +void +cellcliq(graph *g, int *lab, int *ptn, int level, int numcells, int tvpos, + int *invar, int invararg, boolean digraph, int m, int n) +{ + int i; + int wt; + set *gv; + int ss,setsize; + int v[MAXCLIQUE]; + set *ns; + int *cellstart,*cellsize; + int iv,icell,bigcells,cell1,cell2; + int pc; + setword sw; + +#if !MAXN + DYNALLOC1(set,workset,workset_sz,m,"cellcliq"); + DYNALLOC1(int,workshort,workshort_sz,n+2,"cellcliq"); + DYNALLOC2(set,wss,wss_sz,m,MAXCLIQUE-1,"cellcliq"); +#endif + + for (i = n; --i >= 0;) invar[i] = 0; + + if (invararg <= 1 || digraph) return; + + if (invararg > MAXCLIQUE) setsize = MAXCLIQUE; + else setsize = invararg; + + cellstart = workshort; + cellsize = workshort + (n/2); + getbigcells(ptn,level,setsize > 6 ? setsize : 6,&bigcells, + cellstart,cellsize,n); + + for (icell = 0; icell < bigcells; ++icell) + { + cell1 = cellstart[icell]; + cell2 = cell1 + cellsize[icell] - 1; + + EMPTYSET(workset,m); + for (iv = cell1; iv <= cell2; ++iv) ADDELEMENT(workset,lab[iv]); + + for (iv = cell1; iv <= cell2; ++iv) + { + v[0] = lab[iv]; + gv = GRAPHROW(g,v[0],m); + ns = (set*)wss; + pc = 0; + + for (i = M; --i >= 0;) + { + ns[i] = gv[i] & workset[i]; + if ((sw = ns[i]) != 0) pc += POPCOUNT(sw); + } + if (pc <= 1 || pc >= cellsize[icell] - 2) continue; + + ss = 1; + v[1] = v[0]; + while (ss > 0) + { + if (ss == setsize) + { + for (i = ss; --i >= 0;) ++invar[v[i]]; + --ss; + } + else if ((v[ss] + = nextelement((set*)wss+M*(ss-1),M,v[ss])) < 0) + --ss; + else + { + ++ss; + if (ss < setsize) + { + gv = GRAPHROW(g,v[ss-1],m); + ns = (set*)wss + M*(ss-2); + for (i = M; --i >= 0;) ns[i+M] = ns[i] & gv[i]; + v[ss] = v[ss-1]; + } + } + } + } + wt = invar[lab[cell1]]; + for (iv = cell1 + 1; iv <= cell2; ++iv) + if (invar[lab[iv]] != wt) return; + } +} + +/***************************************************************************** +* * +* cellind() assigns to each vertex v a value depending on the number of * +* independent sets which v lies in and which lie in the same cell as v. * +* The size of clique counted is the smallest of invararg and MAXCLIQUE. * +* We try the cells in increasing order of size and stop as soon as any * +* cell splits. * +* * +*****************************************************************************/ + +void +cellind(graph *g, int *lab, int *ptn, int level, int numcells, int tvpos, + int *invar, int invararg, boolean digraph, int m, int n) +{ + int i; + int wt; + set *gv; + int ss,setsize; + int v[MAXCLIQUE]; + set *ns; + int *cellstart,*cellsize; + int iv,icell,bigcells,cell1,cell2; + int pc; + setword sw; + +#if !MAXN + DYNALLOC1(set,workset,workset_sz,m,"cellind"); + DYNALLOC1(int,workshort,workshort_sz,n+2,"cellind"); + DYNALLOC2(set,wss,wss_sz,m,MAXCLIQUE-1,"cellind"); +#endif + + for (i = n; --i >= 0;) invar[i] = 0; + + if (invararg <= 1 || digraph) return; + + if (invararg > MAXCLIQUE) setsize = MAXCLIQUE; + else setsize = invararg; + + cellstart = workshort; + cellsize = workshort + (n/2); + getbigcells(ptn,level,setsize > 6 ? setsize : 6,&bigcells, + cellstart,cellsize,n); + + for (icell = 0; icell < bigcells; ++icell) + { + cell1 = cellstart[icell]; + cell2 = cell1 + cellsize[icell] - 1; + + EMPTYSET(workset,m); + for (iv = cell1; iv <= cell2; ++iv) ADDELEMENT(workset,lab[iv]); + + for (iv = cell1; iv <= cell2; ++iv) + { + v[0] = lab[iv]; + gv = GRAPHROW(g,v[0],m); + ns = (set*)wss; + pc = 0; + + for (i = M; --i >= 0;) + { + ns[i] = ~gv[i] & workset[i]; + if ((sw = ns[i]) != 0) pc += POPCOUNT(sw); + } + if (pc <= 1 || pc >= cellsize[icell] - 2) continue; + + ss = 1; + v[1] = v[0]; + while (ss > 0) + { + if (ss == setsize) + { + for (i = ss; --i >= 0;) ++invar[v[i]]; + --ss; + } + else if ((v[ss] + = nextelement((set*)wss+M*(ss-1),M,v[ss])) < 0) + --ss; + else + { + ++ss; + if (ss < setsize) + { + gv = GRAPHROW(g,v[ss-1],m); + ns = (set*)wss + M*(ss-2); + for (i = M; --i >= 0;) ns[i+M] = ns[i] & ~gv[i]; + v[ss] = v[ss-1]; + } + } + } + } + wt = invar[lab[cell1]]; + for (iv = cell1 + 1; iv <= cell2; ++iv) + if (invar[lab[iv]] != wt) return; + } +} + +/***************************************************************************** +* * +* adjacencies() assigns to each vertex v a code depending on which cells * +* it is joined to and from, and how many times. It is intended to provide * +* better partitioning that the normal refinement routine for digraphs. * +* It will not help with undirected graphs in nauty at all. * +* * +*****************************************************************************/ + +void +adjacencies(graph *g, int *lab, int *ptn, int level, int numcells, int tvpos, + int *invar, int invararg, boolean digraph, int m, int n) +{ + int i,v,w; + int vwt,wwt; + set *gv; + +#if !MAXN + DYNALLOC1(int,workshort,workshort_sz,n+2,"adjacencies"); +#endif + + vwt = 1; + for (i = 0; i < n; ++i) + { + workshort[lab[i]] = vwt; + if (ptn[i] <= level) ++vwt; + invar[i] = 0; + } + + for (v = 0, gv = (set*)g; v < n; ++v, gv += M) + { + vwt = FUZZ1(workshort[v]); + wwt = 0; + w = -1; + while ((w = nextelement(gv,M,w)) >= 0) + { + ACCUM(wwt,FUZZ2(workshort[w])); + ACCUM(invar[w],vwt); + } + ACCUM(invar[v],wwt); + } +} + +/***************************************************************************** +* * +* nautinv_check() checks that this file is compiled compatibly with the * +* given parameters. If not, call exit(1). * +* * +*****************************************************************************/ + +void +nautinv_check(int wordsize, int m, int n, int version) +{ + if (wordsize != WORDSIZE) + { + fprintf(ERRFILE,"Error: WORDSIZE mismatch in nautinv.c\n"); + exit(1); + } + +#if MAXN + if (m > MAXM) + { + fprintf(ERRFILE,"Error: MAXM inadequate in nautinv.c\n"); + exit(1); + } + + if (n > MAXN) + { + fprintf(ERRFILE,"Error: MAXN inadequate in nautinv.c\n"); + exit(1); + } +#endif + + if (version < NAUTYREQUIRED) + { + fprintf(ERRFILE,"Error: nautinv.c version mismatch\n"); + exit(1); + } +} + +/***************************************************************************** +* * +* nautinv_freedyn() - free the dynamic memory in this module * +* * +*****************************************************************************/ + +void +nautinv_freedyn(void) +{ +#if !MAXN + DYNFREE(workset,workset_sz); + DYNFREE(workshort,workshort_sz); + DYNFREE(ws1,ws1_sz); + DYNFREE(ws2,ws2_sz); + DYNFREE(vv,vv_sz); + DYNFREE(ww,ww_sz); + DYNFREE(w01,w01_sz); + DYNFREE(w02,w02_sz); + DYNFREE(w03,w03_sz); + DYNFREE(w12,w12_sz); + DYNFREE(w13,w13_sz); + DYNFREE(w23,w23_sz); + DYNFREE(pt0,pt0_sz); + DYNFREE(pt1,pt1_sz); + DYNFREE(pt2,pt2_sz); + DYNFREE(wss,wss_sz); +#endif +} + +/*===================================================================*/ + + +/***************************************************************************** +* * +* semirefine(g,lab,ptn,level,numcells,strength,active,m,n) performs a * +* refinement operation on the partition at the specified level of the * +* partition nest (lab,ptn). *numcells is assumed to contain the number of * +* cells on input, and is updated. The initial set of active cells (alpha * +* in the paper) is specified in the set active. Precisely, x is in active * +* iff the cell starting at index x in lab is active. * +* *code is set to a value which depends on the fine detail of the * +* algorithm, but which is independent of the labelling of the graph. * +* * +*****************************************************************************/ + +static int +semirefine(graph *g, int *lab, int *ptn, int level, int *numcells, + int strength, set *active, int m, int n) +{ + int i,c1,c2,labc1; + setword x; + set *set1,*set2; + int split1,split2,cell1,cell2; + int cnt,bmin,bmax; + long longcode; + set *gptr; + int maxcell,maxpos,hint; + +#if !MAXN + DYNALLOC1(int,workperm,workperm_sz,n,"refine"); + DYNALLOC1(set,workset,workset_sz,m,"refine"); + DYNALLOC1(int,bucket,bucket_sz,n+2,"refine"); + DYNALLOC1(int,count,count_sz,n,"refine"); +#endif + + longcode = *numcells; + split1 = -1; + hint = 0; + while (*numcells < n && ((split1 = hint, ISELEMENT(active,split1)) + || (split1 = nextelement(active,M,split1)) >= 0 + || (split1 = nextelement(active,M,-1)) >= 0)) + { + DELELEMENT(active,split1); + for (split2 = split1; ptn[split2] > level; ++split2) {} + longcode = MASH(longcode,split1+split2); + if (split1 == split2) /* trivial splitting cell */ + { + gptr = GRAPHROW(g,lab[split1],M); + for (cell1 = 0; cell1 < n; cell1 = cell2 + 1) + { + for (cell2 = cell1; ptn[cell2] > level; ++cell2) {} + if (cell1 == cell2) continue; + c1 = cell1; + c2 = cell2; + while (c1 <= c2) + { + labc1 = lab[c1]; + if (ISELEMENT(gptr,labc1)) + ++c1; + else + { + lab[c1] = lab[c2]; + lab[c2] = labc1; + --c2; + } + } + if (c2 >= cell1 && c1 <= cell2) + { + ptn[c2] = level; + longcode = MASH(longcode,FUZZ1(c2)); + ++*numcells; + if (ISELEMENT(active,cell1) || c2-cell1 >= cell2-c1) + { + ADDELEMENT(active,c1); + if (c1 == cell2) hint = c1; + } + else + { + ADDELEMENT(active,cell1); + if (c2 == cell1) hint = cell1; + } + } + } + } + else /* nontrivial splitting cell */ + { + EMPTYSET(workset,m); + for (i = split1; i <= split2; ++i) + ADDELEMENT(workset,lab[i]); + longcode = MASH(longcode,FUZZ2(split2-split1+1)); + + for (cell1 = 0; cell1 < n; cell1 = cell2 + 1) + { + for (cell2 = cell1; ptn[cell2] > level; ++cell2) {} + if (cell1 == cell2) continue; + i = cell1; + set1 = workset; + set2 = GRAPHROW(g,lab[i],m); + cnt = 0; + for (c1 = m; --c1 >= 0;) + if ((x = ((*set1++) & (*set2++))) != 0) + cnt += POPCOUNT(x); + + count[i] = bmin = bmax = cnt; + bucket[cnt] = 1; + while (++i <= cell2) + { + set1 = workset; + set2 = GRAPHROW(g,lab[i],m); + cnt = 0; + for (c1 = m; --c1 >= 0;) + if ((x = ((*set1++) & (*set2++))) != 0) + cnt += POPCOUNT(x); + + while (bmin > cnt) bucket[--bmin] = 0; + while (bmax < cnt) bucket[++bmax] = 0; + ++bucket[cnt]; + count[i] = cnt; + } + if (bmin == bmax) + { + longcode = MASH(longcode,FUZZ1(bmin+cell1)); + continue; + } + c1 = cell1; + maxcell = -1; + for (i = bmin; i <= bmax; ++i) + if (bucket[i]) + { + c2 = c1 + bucket[i]; + bucket[i] = c1; + longcode = MASH(longcode,i+c1); + if (c2-c1 > maxcell) + { + maxcell = c2-c1; + maxpos = c1; + } + if (c1 != cell1) + { + ADDELEMENT(active,c1); + if (c2-c1 == 1) hint = c1; + ++*numcells; + } + if (c2 <= cell2) ptn[c2-1] = level; + c1 = c2; + } + for (i = cell1; i <= cell2; ++i) + workperm[bucket[count[i]]++] = lab[i]; + for (i = cell1; i <= cell2; ++i) lab[i] = workperm[i]; + if (!ISELEMENT(active,cell1)) + { + ADDELEMENT(active,cell1); + DELELEMENT(active,maxpos); /* check maxpos is alwas defined */ + } + } + } + if (--strength == 0) break; /* negative is fine! */ + } + + longcode = MASH(longcode,FUZZ2(*numcells)); + return CLEANUP(longcode); +} + +void +refinvar(graph *g, int *lab, int *ptn, int level, int numcells, int tvpos, + int *invar, int invararg, boolean digraph, int m, int n) +{ + int i,j; + int wt; + int icell,bigcells,cell1,cell2; + int *cellstart,*cellsize; + int newnumcells; + +#if !MAXN + DYNALLOC1(int,workshort,workshort_sz,n+2,"refinvar"); + DYNALLOC1(int,vv,vv_sz,n,"refinvar"); + DYNALLOC1(int,ww,ww_sz,n,"refinvar"); + DYNALLOC1(set,ws1,ws1_sz,n,"refinvar"); +#endif + + for (i = n; --i >= 0;) invar[i] = 0; + + cellstart = workshort; + cellsize = workshort + (n/2); + getbigcells(ptn,level,2,&bigcells,cellstart,cellsize,n); + + for (icell = 0; icell < bigcells; ++icell) + { + cell1 = cellstart[icell]; + cell2 = cell1 + cellsize[icell] - 1; + for (i = cell1; i <= cell2; ++i) + { + for (j = 0; j < n; ++j) + { + vv[j] = lab[j]; + ww[j] = ptn[j]; + } + newnumcells = numcells + 1; + ww[cell1] = level; + EMPTYSET(ws1,m); + ADDELEMENT(ws1,cell1); + vv[i] = lab[cell1]; + vv[cell1] = lab[i]; + invar[lab[i]] = semirefine(g,vv,ww,level,&newnumcells, + invararg,ws1,m,n); + } + wt = invar[lab[cell1]]; + for (i = cell1 + 1; i <= cell2; ++i) + if (invar[lab[i]] != wt) return; + } +} diff --git a/graph-checker/nauty/nautinv.h b/graph-checker/nauty/nautinv.h new file mode 100644 index 0000000..87c7715 --- /dev/null +++ b/graph-checker/nauty/nautinv.h @@ -0,0 +1,42 @@ +/***************************************************************************** +* This is the header file for versions 2.7 of nautinv.c. * +* * +* Copyright (1984-2018) Brendan McKay. All rights reserved. * +* Subject to the waivers and disclaimers in nauty.h. * +* * +* CHANGE HISTORY * +* 20-Apr-01 : initial creation out of naututil.h * +* 10-Nov-10 : remove types shortish and permutation * +* * +*****************************************************************************/ + +#include "nauty.h" /* which includes stdio.h */ + +#ifdef __cplusplus +extern "C" { +#endif + +extern void adjacencies(graph*,int*,int*,int,int,int,int*,int,boolean,int,int); +extern void adjtriang(graph*,int*,int*,int,int,int,int*,int,boolean,int,int); +extern void cellcliq(graph*,int*,int*,int,int,int,int*,int,boolean,int,int); +extern void cellfano(graph*,int*,int*,int,int,int,int*,int,boolean,int,int); +extern void cellfano2(graph*,int*,int*,int,int,int,int*,int,boolean,int,int); +extern void cellind(graph*,int*,int*,int,int,int,int*,int,boolean,int,int); +extern void cellquads(graph*,int*,int*,int,int,int,int*,int,boolean,int,int); +extern void cellquins(graph*,int*,int*,int,int,int,int*,int,boolean,int,int); +extern void celltrips(graph*,int*,int*,int,int,int,int*,int,boolean,int,int); +extern void cellstarts(int*,int,set*,int,int); +extern void cliques(graph*,int*,int*,int,int,int,int*,int,boolean,int,int); +extern void distances(graph*,int*,int*,int,int,int,int*,int,boolean,int,int); +extern void getbigcells(int*,int,int,int*,int*,int*,int); +extern void indsets(graph*,int*,int*,int,int,int,int*,int,boolean,int,int); +extern void nautinv_check(int,int,int,int); +extern void nautinv_freedyn(void); +extern void quadruples(graph*,int*,int*,int,int,int,int*,int,boolean,int,int); +extern void refinvar(graph*,int*,int*,int,int,int,int*,int,boolean,int,int); +extern void setnbhd(graph*,int,int,set*,set*); +extern void triples(graph*,int*,int*,int,int,int,int*,int,boolean,int,int); +extern void twopaths(graph*,int*,int*,int,int,int,int*,int,boolean,int,int); +#ifdef __cplusplus +} +#endif diff --git a/graph-checker/nauty/naututil.c b/graph-checker/nauty/naututil.c new file mode 100644 index 0000000..b728561 --- /dev/null +++ b/graph-checker/nauty/naututil.c @@ -0,0 +1,3233 @@ +/***************************************************************************** +* * +* miscellaneous utilities for use with nauty 2.8. * +* None of these procedures are needed by nauty, but all are by dreadnaut. * +* * +* Copyright (1984-2022) 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 : changes made for version 1.3 : * +* - added procedures readinteger() and readstring() * +* - replaced all uses of fscanf() by appropriate uses * +* of readinteger() or readstring() * +* - "N:" is now illegal in readgraph() if N is too large * +* or too small * +* 28-Sep-88 : renamed to version 1.4 (no changes to this file) * +* 23-Mar-89 : changes for version 1.5 : * +* - declared key in hash() * +* - changed file name to naututil.c * +* 29-Mar-89 : - declared workperm[] and workset[], and modified * +* many routines to use them. * +* - added putmapping() * +* - reworked some code in mathon() and rangraph() * +* 3-Apr-89 : - changed putorbits() to use O(n) algorithm * +* 5-Apr-89 : - modifed readgraph() to not require fresh line * +* - changed MAKEEMPTY uses to EMPTYSET uses * +* 26-Apr-89 : - moved ptncode() and equitable() to nautaux.c * +* - added putquotient() * +* 18-Aug-89 : - modified putset() to use "i:j" syntax optionally * +* - modified putorbits() to use putset() * +* - modified calling sequence for mathon() * +* 19-Aug-90 : - changed delimeter arg of copycomment to int * +* 14-Oct-90 : renamed to version 1.6 (no changes to this file) * +* 23-Jan-91 : changes for version 1.7 : * +* - fixed bug in complement() * +* 27-Aug-92 : - 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) * +* 13-Jul-96 : changes for version 2.0 : * +* - added dynamic allocation * +* - added limit parameter to readstring * +* - added readvperm() and sublabel() * +* 31-Aug-96 : - converted from RAN to KRAN * +* 6-Feb-97 : - corrected DYNALLOC1 call in putmapping * +* 10-Dec-97 : - KRAN now initialises automatically * +* 9-Jan-00 : - added naututil_check() * +* 12-Feb-00 : - some minor code formatting * +* 16-Nov-00 : - changes as listed in nauty.h * +* 23-Apr-01 : changes for version 2.1 : * +* - removed EXTDEFS * +* 2-Jun-01 : - added converse() * +* 21-Nov-01 : use NAUTYREQUIRED in naututil_check() * +* 11-Apr-03 : changes for version 2.2 : * +* - added rangraph2() * +* 17-Nov-03 : changed INFINITY to NAUTY_INFINITY * +* 10-Dec-06 : removed BIGNAUTY * +* 4-Nov-09 : added readgraph_sg, putgraph_sg, putcanon_sg * +* 10-Nov-09 : removed shortish and permutation types * +* 14-Nov-09 : added relabel_sg(), putdegs_sg(), sublabel_sg() * +* 19-Nov-09 : added individualise() * +* 20-Nov-09 : added hashgraph_sg(), listhash(), hashgraph() * +* 19-Dec-09 : added ranreg_sg(), rangraph2_sg() * +* 21-May-10 : conform to type changes in sparsegraph * +* 5-Jun-10 : add mathon_sg() * +* 10-Jun-10 : add putquotient_sg() and complement_sg() * +* 26-Jan-11 : fix nde error in sublabel_sg() * +* 15-Jan-12 : add TLS_ATTR attributes * +* 3-Mar-12 : add putorbitsplus() and putset_firstbold() * +* : write orbit sizes if not trivial * +* 17-Mar-12 : move seed definition to naututil.h * +* 20-Sep-12 : allow quoted strings in readstring() * +* 20-Sep-12 : the first argument of ungetc is int, not char * +* 4-Mar-13 : remove a side-effect issue in setinter() * +* 17-Dec-15 : add readgraph_swg() and update putgraph_sg() * +* 22-Jan-16 : add readintger_sl() and getint_sl() * +* 29-Feb-16 : add subpartition() * +* 6-Apr-16 : add countcells(), make subpartition return a count * +* * +*****************************************************************************/ + +#define ONE_WORD_SETS +#include "naututil.h" /* which includes nauty.h, nautinv.h and stdio.h */ +#include "nausparse.h" + +#if MAXM==1 +#define M 1 +#else +#define M m +#endif + +#if !MAXN +DYNALLSTAT(int,workperm,workperm_sz); +DYNALLSTAT(set,workset,workset_sz); +#else +static TLS_ATTR int workperm[MAXN+2]; /* used for scratch purposes */ +static TLS_ATTR set workset[MAXM]; /* used for scratch purposes */ +#endif + +#define ECHUNKSIZE 1000 /* should be a multiple of 2 */ +typedef struct echunk {struct echunk *next; int edge[ECHUNKSIZE];} echunk; +static TLS_ATTR echunk first_echunk = {NULL,{0}}; +typedef struct echunkw {struct echunkw *next; \ + struct {int v1,v2; sg_weight wt;} edge[ECHUNKSIZE];} echunkw; +static TLS_ATTR echunkw first_echunkw = {NULL,{{0,0,0}}}; + +#ifdef NLMAP +#define GETNW(c,f) do c = getc(f); while (c==' '||c=='\t') +#define GETNWC(c,f) do c = getc(f); while (c==' '||c==','||c=='\t') +#define GETNWL(c,f) do c = getc(f); while (c==' '||c=='\n'||c=='\t') +#else +#define GETNW(c,f) do c = getc(f); while (c==' '||c=='\t'||c=='\r') +#define GETNWC(c,f) do c = getc(f); while (c==' '||c==','||c=='\t'||c=='\r') +#define GETNWL(c,f) do c = getc(f); while (c==' '||c=='\n'||c=='\t'||c=='\r') +#endif + +#define ISDIGIT(c) ((c) >= '0' && (c) <= '9') + +static const long fuzz1[] = {1984625421L, 971524688L,1175081625L, 377165387L}; +static const long fuzz2[] = {2001381726L,1615243355L, 191176436L,1212176501L}; +#define FUZZ1(x) ((x) ^ fuzz1[(x)&3L]) +#define FUZZ2(x) ((x) ^ fuzz2[(x)&3L]) + +#define SORT_OF_SORT 1 +#define SORT_NAME sort1int +#define SORT_TYPE1 int +#include "sorttemplates.c" /* define sort1int(a,n) */ + +/***************************************************************************** +* * +* setinter(set1,set2,m) = the number of elements in the intersection of * +* the sets set1 and set2. * +* * +*****************************************************************************/ + +int +setinter(set *set1, set *set2, int m) +{ + setword x; + int count,i; + + count = 0; + for (i = m; --i >= 0;) + { + if ((x = (*set1 & *set2)) != 0) count += POPCOUNT(x); + ++set1; + ++set2; + } + + return count; +} + +/***************************************************************************** +* * +* setsize(set1,m) = the number of elements in the set set1. * +* * +*****************************************************************************/ + +int +setsize(set *set1, int m) +{ + int count,i; + setword x; + + if (m == 1) return POPCOUNT(*set1); + + count = 0; + for (i = m; --i >= 0;) count += POPCOUNT(set1[i]); + + return count; +} + +/***************************************************************************** +* * +* setolist(set1,m,list) Puts all the elements of the set into * +* list[0...] and returns the number of elements as function value. * +* * +*****************************************************************************/ + +int +settolist(set *set1, int m, int *list) +{ + int i,j,k,v; + setword w; + + k = 0; + for (i = 0, v = 0; i < m; ++i, v += WORDSIZE) + { + w = set1[i]; + while (w) + { + TAKEBIT(j,w); + list[k++] = v + j; + } + } + + return k; +} + +/***************************************************************************** +* * +* listtoset(list,len,set1,m) Sets set1[0..m-1] to the set {list[0..len-1]} * +* * +*****************************************************************************/ + +void +listtoset(int *list, int len, set *set1, int m) +{ + int i; + setword w; + + if (m == 1) + { + w = 0; + for (i = 0; i < len; ++i) w |= bit[list[i]]; + *set1 = w; + } + else + { + EMPTYSET0(set1,m); + for (i = 0; i < len; ++i) ADDELEMENT0(set1,list[i]); + } +} + +/***************************************************************************** +* * +* flushline(f) reads from f until '\n' or EOF. * +* If non-trivial characters are skipped, the user is informed. * +* * +*****************************************************************************/ + +void +flushline(FILE *f) +{ + boolean msg; + int c; + + msg = FALSE; + + while ((c = getc(f)) != EOF && c != '\n') + if (msg) + PUTC((char)c,ERRFILE); + else if (c != ' ' && c != '\t' && c != '\f' && + c != '\r' && c != ',') + { + msg = TRUE; + fprintf(ERRFILE,"input skipped : '%c",(char)c); + } + if (msg) fprintf(ERRFILE,"'\n\n"); +} + +/***************************************************************************** +* * +* readinteger(f,&i) reads an optionally-signed integer from f, preceded by * +* any amount of white space. The function value returned is TRUE if an * +* integer was found, FALSE otherwise. * +* * +*****************************************************************************/ + +boolean +readinteger(FILE *f, int *p) +{ + int c,ans,minus; + + GETNWL(c,f); + if (!ISDIGIT(c) && c != '-' && c != '+') + { + if (c != EOF) ungetc(c,f); + return FALSE; + } + + minus = c == '-'; + ans = (c == '-' || c == '+' ? 0 : c - '0'); + + c = getc(f); + while (ISDIGIT(c)) + { + ans *= 10; + ans += c - '0'; + c = getc(f); + } + + if (c != EOF) ungetc(c,f); + + *p = (minus ? -ans : ans); + return TRUE; +} + +/***************************************************************************** +* * +* readinteger_sl(f,&i) reads an optionally-signed integer from f, preceded * +* by any amount of white space except newlines. The function value * +* returned is TRUE if an integer was found, FALSE otherwise. * +* * +*****************************************************************************/ + +boolean +readinteger_sl(FILE *f, int *p) +{ + int c,ans,minus; + + GETNW(c,f); + if (!ISDIGIT(c) && c != '-' && c != '+') + { + if (c != EOF) ungetc(c,f); + return FALSE; + } + + minus = c == '-'; + ans = (c == '-' || c == '+' ? 0 : c - '0'); + + c = getc(f); + while (ISDIGIT(c)) + { + ans *= 10; + ans += c - '0'; + c = getc(f); + } + + if (c != EOF) ungetc(c,f); + + *p = (minus ? -ans : ans); + return TRUE; +} + +/***************************************************************************** +* * +* readstring(f,s,slen) reads a string from f. First any amount of white * +* space is skipped (including newlines). If the next character is a * +* double-quote, everything after that before the next double-quote or * +* newline is put into s. If the next character is not a double-quote, * +* everything before the next white space is put into s. A nul is added, * +* but no more than slen characters are ever put into s. The function * +* value is TRUE if a string was found and FALSE otherwise. * +* * +*****************************************************************************/ + +boolean +readstring(FILE *f, char *s, int slen) +{ + int c; + char *slim; + + slim = s + slen - 1; + GETNWL(c,f); + if (c == EOF) + { + *s = '\0'; + return FALSE; + } + + if (c == '"') + { + c = getc(f); + while (c != '"' && c != '\n' && c != '\r' && c != EOF) + { + if (s <= slim) *s++ = (char)c; + c = getc(f); + } + if (c != '"' && c != EOF) ungetc(c,f); + } + else + { + if (s <= slim) *s++ = (char)c; + c = getc(f); + while (c != ' ' && c != '\n' && c != '\t' && c != '\r' && c != EOF) + { + if (s <= slim) *s++ = (char)c; + c = getc(f); + } + if (c != EOF) ungetc(c,f); + } + if (s <= slim) *s = '\0'; + else *slim = '\0'; + + return TRUE; +} + +/***************************************************************************** +* * +* getint(f) reads an integer from f, optionally preceded by '=' * +* and white space. -1 is returned if the attempt was unsuccessful. * +* * +*****************************************************************************/ + +int +getint(FILE *f) +{ + int i,c; + + GETNWL(c,f); + if (c != '=') ungetc(c,f); + + if (readinteger(f,&i)) return i; + else return -1; +} + +/***************************************************************************** +* * +* getint_sl(f) reads an integer from f, optionally preceded by '=' and * +* white space other than newlines. -1 is returned if the attempt was * +* unsuccessful. * +*****************************************************************************/ + +int +getint_sl(FILE *f) +{ + int i,c; + + GETNW(c,f); + if (c != '=') ungetc(c,f); + + if (readinteger_sl(f,&i)) return i; + else return -1; +} + +/***************************************************************************** +* * +* putset(f,set1,curlenp,linelength,m,compress) writes the set set1 to * +* file f using at most linelength characters per line (excluding '\n'). * +* A value of linelength <= 0 dictates no line breaks at all. * +* *curlenp is the number of characters on the line so far; it is updated. * +* A range j1,j1+1,...,j2 for j2-j1>=2 is written as "j1:j2" if compress * +* is nonzero (eg. TRUE); otherwise each element is written separately. * +* No final '\n' is written. labelorg is used. * +* * +* FUNCTIONS CALLED: nextelement(),itos() * +* * +*****************************************************************************/ + +void +putset(FILE *f, set *set1, int *curlenp, int linelength, + int m, boolean compress) +{ + int slen,j1,j2; + char s[40]; + + j1 = -1; + while ((j1 = nextelement(set1,m,j1)) >= 0) + { + j2 = j1; + if (compress) + { + while (nextelement(set1,m,j2) == j2 + 1) ++j2; + if (j2 == j1+1) j2 = j1; + } + slen = itos(j1+labelorg,s); + if (j2 >= j1 + 2) + { + s[slen] = ':'; + slen += 1 + itos(j2+labelorg,&s[slen+1]); + } + + if (linelength > 0 && *curlenp + slen + 1 >= linelength) + { + fprintf(f,"\n "); + *curlenp = 3; + } + fprintf(f," %s",s); + *curlenp += slen + 1; + j1 = j2; + } +} + +/***************************************************************************** +* * +* putset_firstbold(f,set1,curlenp,linelength,m,compress) is the same as * +* putset(f,set1,curlenp,linelength,m,compress) except that the first * +* element of the set is written bold. This is only useful when output is * +* to a device that interprets ANSI control sequences. * +* * +* FUNCTIONS CALLED: nextelement(),itos() * +* * +*****************************************************************************/ + +void +putset_firstbold(FILE *f, set *set1, int *curlenp, int linelength, + int m, boolean compress) +{ + int slen,slen1,j1,j2; + char s[50],c; + boolean bold; + + bold = TRUE; + j1 = -1; + while ((j1 = nextelement(set1,m,j1)) >= 0) + { + j2 = j1; + if (compress) + { + while (nextelement(set1,m,j2) == j2 + 1) ++j2; + if (j2 == j1+1) j2 = j1; + } + slen1 = slen = itos(j1+labelorg,s); + if (j2 >= j1 + 2) + { + s[slen] = ':'; + slen += 1 + itos(j2+labelorg,&s[slen+1]); + } + c = s[slen1]; + + if (linelength > 0 && *curlenp + slen + 1 >= linelength) + { + fprintf(f,"\n "); + *curlenp = 3; + } + if (bold) + { + s[slen1] = '\0'; + fprintf(f," \033[1m%s\033[0m",s); + s[slen1] = c; + fprintf(f,"%s",&s[slen1]); + bold = FALSE; + } + else + fprintf(f," %s",s); + + *curlenp += slen + 1; + j1 = j2; + } +} + +/***************************************************************************** +* * +* readgraph(f,g,digraph,prompt,edit,linelength,m,n) reads a graph g from f. * +* Commands: (There is always a "current vertex" v, initially labelorg; * +* n is an unsigned integer.) * +* n : add edge (v,n) * +* -n : delete edge (v,n) * +* n: : set v := n, and exit if v >= n. * +* ? : type neighbours of vertex v * +* ; : increment v, and exit if v >= n. * +* . : exit * +* ! : skip rest of input line * +* * +* If digraph==FALSE, loops are illegal and (x,y) => (y,x) * +* If edit==FALSE, the graph is initialized to empty. * +* If prompt==TRUE, prompts are written to PROMPTFILE. * +* linelength is a limit on the number of characters per line caused by '?' * +* A value of linelength <= 0 dictates no line breaks at all. * +* labelorg is used. * +* * +* FUNCTIONS CALLED : putset() * +* * +*****************************************************************************/ + +void +readgraph(FILE *f, graph *g, boolean digraph, boolean prompt, + boolean edit, int linelength, int m, int n) +{ + int v,c; + int curlen,w; + graph *gv; + boolean neg; + + if (!edit) + for (v = 0, gv = g; v < n; ++v, gv += M) EMPTYSET(gv,m); + + v = 0; + gv = g; + neg = FALSE; + + while (TRUE) + { + GETNWC(c,f); + if (ISDIGIT(c)) + { + ungetc(c,f); + readinteger(f,&w); + w -= labelorg; + if (neg) + { + neg = FALSE; + if (w < 0 || w >= n || (!digraph && w == v)) + fprintf(ERRFILE,"illegal edge (%d,%d) ignored\n\n", + v+labelorg,w+labelorg); + else + { + DELELEMENT(gv,w); + if (!digraph) DELELEMENT(GRAPHROW(g,w,M),v); + } + } + else + { + GETNWC(c,f); + if (c == ':') + if (w < 0 || w >= n) + fprintf(ERRFILE, + "illegal vertex number %d ignored\n\n", + w+labelorg); + else + { + v = w; + gv = GRAPHROW(g,v,M); + } + else + { + ungetc(c,f); + if (w < 0 || w >= n || (!digraph && w == v)) + fprintf(ERRFILE,"illegal edge (%d,%d) ignored\n\n", + v+labelorg,w+labelorg); + else + { + ADDELEMENT(gv,w); + if (!digraph) ADDELEMENT(GRAPHROW(g,w,M),v); + } + } + } + } + else switch(c) + { + case ';': + neg = FALSE; + ++v; + if (v >= n) return; + gv = GRAPHROW(g,v,M); + break; + case '?': + neg = FALSE; + fprintf(PROMPTFILE,"%2d : ",v+labelorg); + curlen = 5; + putset(PROMPTFILE,gv,&curlen,linelength,M,FALSE); + fprintf(PROMPTFILE,";\n"); + break; + case '\n': + neg = FALSE; + if (prompt) fprintf(PROMPTFILE,"%2d : ",v+labelorg); + break; + case EOF: + case '.': + return; + case '-': + neg = TRUE; + break; + case '!': + do + c = getc(f); + while (c != '\n' && c != EOF); + if (c == '\n') ungetc(c,f); + break; + default : + fprintf(ERRFILE,"illegal char '%c' - use '.' to exit\n\n", + (char)c); + } + } +} + +/**************************************************************************/ + +void +ranreg_sg(sparsegraph *sg, int degree, int n) +/* Make a random regular simple undirected graph. + * For MAXN!=0, the maximum degree is MAXREG. + * sg must be initialised + */ +{ + long i,k,v,w; + boolean ok; + int *dd,*ee; + size_t *vv,nde,j; + +#if MAXN + int p[MAXREG*MAXN]; +#else + DYNALLSTAT(int,p,p_sz); + + DYNALLOC2(int,p,p_sz,degree,n,"genrang"); +#endif + + nde = (size_t)n * (size_t)degree; + + SG_ALLOC(*sg,n,nde,"ranreg_sg"); + SG_VDE(sg,vv,dd,ee); + DYNFREE(sg->w,sg->wlen); + + sg->nv = n; + sg->nde = nde; + + for (i = j = 0; i < n; ++i) + for (k = 0; k < degree; ++k) p[j++] = i; + + for (i = 0; i < n; ++i) vv[i] = i*(size_t)degree; + + do + { + ok = TRUE; + + for (j = nde; j > 0; j -= 2) + { + i = KRAN(j-1); + k = p[i]; + if (k == p[j-1]) break; + p[i] = p[j-2]; p[j-2] = k; + } + if (j > 0) { ok = FALSE; continue; } + + for (i = 0; i < n; ++i) dd[i] = 0; + + for (j = nde; j > 0; ) + { + v = p[--j]; + w = p[--j]; + if (v != w) + { + for (i = dd[w]; --i >= 0;) if (ee[vv[w]+i] == v) break; + if (i >= 0) { ok = FALSE; break; } + } + ee[vv[w]+(dd[w]++)] = v; + ee[vv[v]+(dd[v]++)] = w; + } + } + while (!ok); +} + +/***************************************************************************** +* * +* readgraph_sg(f,sg,digraph,prompt,linelength,n) reads a graph g from f. * +* Commands: (There is always a "current vertex" v, initially labelorg; * +* n is an unsigned integer.) * +* n : add edge (v,n) * +* -n : delete edge (v,n) * +* n: : set v := n, and exit if v >= n. * +* ? : type neighbours of vertex v ** NOT IMPLEMENTED ** * +* ; : increment v, and exit if v >= n. * +* . : exit * +* ! : skip rest of input line * +* sg must be initialised * +* * +* If digraph==FALSE, loops are illegal and (x,y) => (y,x) * +* If prompt==TRUE, prompts are written to PROMPTFILE. * +* linelength is a limit on the number of characters per line caused by '?' * +* A value of linelength <= 0 dictates no line breaks at all. * +* labelorg is used. * +* * +*****************************************************************************/ + +void +readgraph_sg(FILE *f, sparsegraph *sg, boolean digraph, boolean prompt, + int linelength, int n) +{ + int i,j,k,vv,ww,c; + boolean neg,done; + int *d,*e,*evi; + echunk *ec,*ecnext,*ec_end; + size_t ned,*v,iec,iec_end; + + sg->nv = n; + DYNALLOC1(size_t,sg->v,sg->vlen,n,"malloc"); + DYNALLOC1(int,sg->d,sg->dlen,n,"malloc"); + DYNFREE(sg->w,sg->wlen); + v = sg->v; + d = sg->d; + + for (i = 0; i < n; ++i) d[i] = 0; + + ec = &first_echunk; + iec = 0; + vv = 0; + neg = done = FALSE; + + while (!done) + { + GETNWC(c,f); + if (ISDIGIT(c)) + { + ungetc(c,f); + readinteger(f,&ww); + ww -= labelorg; + if (neg) + { + neg = FALSE; + if (ww < 0 || ww >= n || (!digraph && ww == vv)) + fprintf(ERRFILE,"illegal edge (%d,%d) ignored\n\n", + vv+labelorg,ww+labelorg); + else + { + if (iec == ECHUNKSIZE) + { + if (!ec->next) + { + ecnext = (echunk*)ALLOCS(1,sizeof(echunk)); + if (!ecnext) alloc_error("malloc"); + ecnext->next = NULL; + ec->next = ecnext; + } + ec = ec->next; + iec = 0; + } + ec->edge[iec++] = vv; + ec->edge[iec++] = -1 - ww; + ++d[vv]; + if (!digraph && ww != vv) ++d[ww]; + } + } + else + { + GETNWC(c,f); + if (c == ':') + { + if (ww < 0 || ww >= n) + fprintf(ERRFILE, + "illegal vertex number %d ignored\n\n", + ww+labelorg); + else + vv = ww; + } + else + { + ungetc(c,f); + if (ww < 0 || ww >= n || (!digraph && ww == vv)) + fprintf(ERRFILE,"illegal edge (%d,%d) ignored\n\n", + vv+labelorg,ww+labelorg); + else + { + if (iec == ECHUNKSIZE) + { + if (!ec->next) + { + ecnext = (echunk*)ALLOCS(1,sizeof(echunk)); + if (!ecnext) alloc_error("malloc"); + ecnext->next = NULL; + ec->next = ecnext; + } + ec = ec->next; + iec = 0; + } + ec->edge[iec++] = vv; + ec->edge[iec++] = ww; + ++d[vv]; + if (!digraph && ww != vv) ++d[ww]; + } + } + } + } + else switch(c) + { + case ';': + neg = FALSE; + ++vv; + if (vv >= n) done = TRUE; + break; + case '?': + neg = FALSE; + fprintf(ERRFILE,"Command \'?\' not implemented.\n\n"); + break; + case '\n': + neg = FALSE; + if (prompt) fprintf(PROMPTFILE,"%2d : ",vv+labelorg); + break; + case EOF: + case '.': + done = TRUE; + break; + case '-': + neg = TRUE; + break; + case '!': + do + c = getc(f); + while (c != '\n' && c != EOF); + if (c == '\n') ungetc(c,f); + break; + default : + fprintf(ERRFILE,"illegal char '%c' - use '.' to exit\n\n", + (char)c); + } + } + + ned = 0; + for (i = 0; i < n; ++i) ned += d[i]; + DYNALLOC1(int,sg->e,sg->elen,ned,"malloc"); + e = sg->e; + + v[0] = 0; + for (i = 1; i < n; ++i) v[i] = v[i-1] + d[i-1]; + for (i = 0; i < n; ++i) d[i] = 0; + + iec_end = iec; + ec_end = ec; + + iec = 0; + ec = &first_echunk; + + if (ned != 0) while (TRUE) + { + vv = ec->edge[iec++]; + ww = ec->edge[iec++]; + + if (ww >= 0) + { + e[v[vv]+(d[vv]++)] = ww; + if (!digraph && ww != vv) e[v[ww] +(d[ww]++)] = vv; + } + else + { + ww = -1 - ww; + for (i = 0; i < d[vv]; ++i) + if (e[v[vv]+i] == ww) break; + if (i < d[vv]) + { + e[v[vv]+i] = e[v[vv]+d[vv]-1]; + --d[vv]; + } + if (!digraph && ww != vv) + { + for (i = 0; i < d[ww]; ++i) + if (e[v[ww]+i] == vv) break; + if (i < d[ww]) + { + e[v[ww]+i] = e[v[ww]+d[ww]-1]; + --d[ww]; + } + } + } + if (iec == iec_end && ec == ec_end) break; + if (iec == ECHUNKSIZE) { iec = 0; ec = ec->next; } + } + + sortlists_sg(sg); + + ned = 0; + for (i = 0; i < n; ++i) + { + if (d[i] > 1) + { + evi = e + v[i]; + j = 1; + for (k = 1; k < d[i]; ++k) + if (evi[k] != evi[j-1]) evi[j++] = evi[k]; + d[i] = j; + } + ned += d[i]; + } + sg->nde = ned; +} + +/***************************************************************************** +* * +* readgraph_swg(f,sg,digraph,prompt,linelength,n) reads a sparse weighted * +* graph g from f. * +* Commands: (There is always a "current vertex" v, initially labelorg; * +* n is an unsigned integer, w is a weight.) * +* n : add edge (v,n) * +* -n : delete edge (v,n) * +* n: : set v := n, and exit if v >= n. * +* ? : type neighbours of vertex v ** NOT IMPLEMENTED ** * +* ; : increment v, and exit if v >= n. * +* . : exit * +* ! : skip rest of input line * +* w# : set the weight for the next edge only * +* W# : set the weight from now on * +* sg must be initialised * +* * +* If digraph==FALSE, loops are illegal and (x,y) => (y,x) * +* For digraphs, an unspecified opposite edge has weight SG_MINWEIGHT * +* If edges are inserted more than once, the largest weight counts. * +* If prompt==TRUE, prompts are written to PROMPTFILE. * +* linelength is a limit on the number of characters per line caused by '?' * +* A value of linelength <= 0 dictates no line breaks at all. * +* labelorg is used. * +* * +*****************************************************************************/ + +void +readgraph_swg(FILE *f, sparsegraph *sg, boolean digraph, boolean prompt, + int linelength, int n) +{ + int i,j,k,vv,ww,c; + boolean neg,done; + int *d,*e,*evi; + echunkw *ec,*ecnext,*ec_end; + size_t ned,*v,iec,iec_end; + sg_weight *wt,currwt,defwt,*wvi; + + sg->nv = n; + DYNALLOC1(size_t,sg->v,sg->vlen,n,"malloc"); + DYNALLOC1(int,sg->d,sg->dlen,n,"malloc"); + v = sg->v; + d = sg->d; + wt = sg->w; + + for (i = 0; i < n; ++i) d[i] = 0; + + defwt = currwt = 1; + ec = &first_echunkw; + iec = 0; + vv = 0; + neg = done = FALSE; + + while (!done) + { + GETNWC(c,f); + if (ISDIGIT(c)) + { + ungetc(c,f); + readinteger(f,&ww); + ww -= labelorg; + if (neg) + { + neg = FALSE; + if (ww < 0 || ww >= n || (!digraph && ww == vv)) + fprintf(ERRFILE,"illegal edge (%d,%d) ignored\n\n", + vv+labelorg,ww+labelorg); + else + { + if (iec == ECHUNKSIZE) + { + if (!ec->next) + { + ecnext = (echunkw*)ALLOCS(1,sizeof(echunkw)); + if (!ecnext) alloc_error("malloc"); + ecnext->next = NULL; + ec->next = ecnext; + } + ec = ec->next; + iec = 0; + } + ec->edge[iec].v1 = vv; + ec->edge[iec].v2 = -1 - ww; + ec->edge[iec].wt = currwt; + ++iec; + currwt = defwt; + ++d[vv]; + if (ww != vv) ++d[ww]; + } + } + else + { + GETNWC(c,f); + if (c == ':') + { + if (ww < 0 || ww >= n) + fprintf(ERRFILE, + "illegal vertex number %d ignored\n\n", + ww+labelorg); + else + vv = ww; + } + else + { + ungetc(c,f); + if (ww < 0 || ww >= n || (!digraph && ww == vv)) + fprintf(ERRFILE,"illegal edge (%d,%d) ignored\n\n", + vv+labelorg,ww+labelorg); + else + { + if (iec == ECHUNKSIZE) + { + if (!ec->next) + { + ecnext = (echunkw*)ALLOCS(1,sizeof(echunkw)); + if (!ecnext) alloc_error("malloc"); + ecnext->next = NULL; + ec->next = ecnext; + } + ec = ec->next; + iec = 0; + } + ec->edge[iec].v1 = vv; + ec->edge[iec].v2 = ww; + ec->edge[iec].wt = currwt; + ++iec; + currwt = defwt; + ++d[vv]; + if (ww != vv) ++d[ww]; + } + } + } + } + else switch(c) + { + case ';': + neg = FALSE; + ++vv; + if (vv >= n) done = TRUE; + break; + case '?': + neg = FALSE; + fprintf(ERRFILE,"Command \'?\' not implemented.\n\n"); + break; + case '\n': + neg = FALSE; + if (prompt) fprintf(PROMPTFILE,"%2d : ",vv+labelorg); + break; + case EOF: + case '.': + done = TRUE; + break; + case '-': + neg = TRUE; + break; + case 'w': + readinteger(f,&currwt); + if (currwt <= SG_MINWEIGHT) + { + fprintf(ERRFILE,"Weight too small\n\n"); + currwt = 1; + } + break; + case 'W': + readinteger(f,&currwt); + if (currwt <= SG_MINWEIGHT) + { + fprintf(ERRFILE,"Weight too small\n\n"); + currwt = 1; + } + defwt = currwt; + break; + case '!': + do + c = getc(f); + while (c != '\n' && c != EOF); + if (c == '\n') ungetc(c,f); + break; + default : + fprintf(ERRFILE,"illegal char '%c' - use '.' to exit\n\n", + (char)c); + } + } + + ned = 0; + for (i = 0; i < n; ++i) ned += d[i]; + DYNALLOC1(int,sg->e,sg->elen,ned,"malloc"); + DYNALLOC1(sg_weight,sg->w,sg->wlen,ned,"malloc"); + e = sg->e; + wt = sg->w; + + v[0] = 0; + for (i = 1; i < n; ++i) v[i] = v[i-1] + d[i-1]; + for (i = 0; i < n; ++i) d[i] = 0; + + iec_end = iec; + ec_end = ec; + + iec = 0; + ec = &first_echunkw; + + if (ned != 0) while (TRUE) + { + vv = ec->edge[iec].v1; + ww = ec->edge[iec].v2; + currwt = ec->edge[iec].wt; + ++iec; + + if (ww >= 0) + { + e[v[vv]+d[vv]] = ww; + wt[v[vv]+d[vv]] = currwt; + ++d[vv]; + if (ww != vv) + { + e[v[ww]+d[ww]] = vv; + wt[v[ww]+d[ww]] = (digraph ? SG_MINWEIGHT : currwt); + ++d[ww]; + } + } + else + { + ww = -1 - ww; + for (i = 0; i < d[vv]; ++i) + if (e[v[vv]+i] == ww) break; + if (i < d[vv]) + { + e[v[vv]+i] = e[v[vv]+d[vv]-1]; + wt[v[vv]+i] = wt[v[vv]+d[vv]-1]; + --d[vv]; + } + if (ww != vv) + { + for (i = 0; i < d[ww]; ++i) + if (e[v[ww]+i] == vv) break; + if (i < d[ww]) + { + e[v[ww]+i] = e[v[ww]+d[ww]-1]; + wt[v[ww]+i] = wt[v[ww]+d[ww]-1]; + --d[ww]; + } + } + } + if (iec == iec_end && ec == ec_end) break; + if (iec == ECHUNKSIZE) { iec = 0; ec = ec->next; } + } + + sortlists_sg(sg); + + ned = 0; + for (i = 0; i < n; ++i) + { + if (d[i] > 1) + { + evi = e + v[i]; + wvi = wt + v[i]; + j = 1; + for (k = 1; k < d[i]; ++k) + { + if (evi[k] != evi[j-1]) + { + evi[j] = evi[k]; + wvi[j] = wvi[k]; + ++j; + } + else if (wvi[k] > wvi[j-1]) + wvi[j-1] = wvi[k]; + } + d[i] = j; + } + ned += d[i]; + } + sg->nde = ned; +} + +/***************************************************************************** +* * +* putgraph(f,g,linelength,m,n) writes a list of the edges of g to f * +* using at most linelength characters per line (excluding '\n'). * +* A value of linelength <= 0 dictates no line breaks at all within the * +* list for each vertex. * +* labelorg is used. * +* * +* FUNCTIONS CALLED: putset() * +* * +*****************************************************************************/ + +void +putgraph(FILE *f, graph *g, int linelength, int m, int n) +{ + int i,curlen; + set *pg; + + for (i = 0, pg = g; i < n; ++i, pg += M) + { + fprintf(f,"%3d : ",i+labelorg); + curlen = 7; + putset(f,pg,&curlen,linelength,M,FALSE); + fprintf(f,";\n"); + } +} + +/***************************************************************************** +* * +* putgraph_sg(f,sg,linelength) writes a list of the edges of g to f * +* using at most linelength characters per line (excluding '\n'). * +* A value of linelength <= 0 dictates no line breaks at all within the * +* list for each vertex. * +* labelorg is used. * +* * +*****************************************************************************/ + +void +putgraph_sg(FILE *f, sparsegraph *sg, int linelength) +{ + int i,n,curlen,slen; + int *d,*e; + size_t *v,j; + sg_weight *wt; + char s[60]; + + n = sg->nv; + SWG_VDE(sg,v,d,e,wt); + + for (i = 0; i < n; ++i) + { + fprintf(f,"%3d : ",i+labelorg); + curlen = 7; + + for (j = v[i]; j < v[i]+d[i]; ++j) + { + if (wt && wt[j] != 1) + { + s[0] = 'w'; + if (wt[j] == SG_MINWEIGHT) + { + s[1] = 'X'; + s[2] = ' '; + slen = 3; + } + else + { + slen = 2 + itos(wt[j],s+1); + s[slen-1] = ' '; + } + slen += itos(e[j]+labelorg,s+slen); + } + else + slen = itos(e[j]+labelorg,s); + + if (linelength > 0 && curlen + slen + 1 > linelength) + { + putstring(f,"\n "); + curlen = 2; + } + PUTC(' ',f); + putstring(f,s); + curlen += slen + 1; + } + putstring(f,";\n"); + } +} + +/***************************************************************************** +* * +* putmapping(f,lab1,org1,lab2,org2,linelength,n) writes n pairs * +* (lab1[i]+org1)-(lab2[i]+org2) to file f in increasing order of lab1[i]. * +* lab1 and lab2 are assumed to be permutations. At most linelength * +* characters (excluding '\n') are written per line. * +* A value of linelength <= 0 dictates no line breaks at all. * +* * +* FUNCTIONS CALLED: itos(),putstring() * +* * +*****************************************************************************/ + +void +putmapping(FILE *f, int *lab1, int org1,int *lab2, int org2, + int linelength, int n) +{ + int i,curlen,slen; + char s[60]; + +#if !MAXN + DYNALLOC1(int,workperm,workperm_sz,n+2,"putmapping"); +#endif + + for (i = 0; i < n; ++i) workperm[lab1[i]] = lab2[i]; + + curlen = 0; + for (i = 0; i < n; ++i) + { + slen = itos(i+org1,s); + s[slen++] = '-'; + slen += itos(workperm[i]+org2,&s[slen]); + if (linelength > 0 && curlen + slen + 1 > linelength) + { + putstring(f,"\n "); + curlen = 2; + } + PUTC(' ',f); + putstring(f,s); + curlen += slen + 1; + } + PUTC('\n',f); +} + +/***************************************************************************** +* * +* putorbits(f,orbits,linelength,n) writes the orbits to f as lists * +* of integers separated by semicolons. No more than linelength characters * +* (excluding '\n') are written per line. * +* A value of linelength <= 0 dictates no line breaks at all. * +* labelorg is used. * +* * +* FUNCTIONS CALLED: itos(),putset() * +* * +*****************************************************************************/ + +void +putorbits(FILE *f, int *orbits, int linelength, int n) +{ + int i,j; + int m,curlen,sz,slen; + char s[20]; + + m = SETWORDSNEEDED(n); +#if !MAXN + DYNALLOC1(int,workperm,workperm_sz,n+2,"putorbits"); + DYNALLOC1(set,workset,workset_sz,m,"putorbits"); +#endif + + for (i = n; --i >= 0;) workperm[i] = 0; + for (i = n; --i >= 0;) + if ((j = orbits[i]) < i) + { + workperm[i] = workperm[j]; + workperm[j] = i; + } + + curlen = 0; + for (i = 0; i < n; ++i) + if (orbits[i] == i) + { + sz = 0; + EMPTYSET(workset,m); + j = i; + do + { + ADDELEMENT(workset,j); + j = workperm[j]; + ++sz; + } + while (j > 0); + putset(f,workset,&curlen,linelength-1,m,TRUE); + if (sz > 1) + { + s[0] = ' '; + s[1] = '('; + slen = 2 + itos(sz,s+2); + s[slen++] = ')'; + s[slen] = '\0'; + if (linelength > 0 && curlen + slen + 1 >= linelength) + { + fprintf(f,"\n "); + curlen = 3; + } + fprintf(f,"%s",s); + curlen += slen; + } + + PUTC(';',f); + ++curlen; + } + PUTC('\n',f); +} + +/***************************************************************************** +* * +* putorbitsplus(f,orbits,linelength,n) is the same as * +* putorbits(f,orbits,linelength,n) except that the first element of each * +* orbit is written bold. This only works if output is to a device that * +* interprets ANSI controls. * +* * +* FUNCTIONS CALLED: itos(),putset() * +* * +*****************************************************************************/ + +void +putorbitsplus(FILE *f, int *orbits, int linelength, int n) +{ + int i,j; + int m,curlen,sz,slen; + char s[20]; + + m = SETWORDSNEEDED(n); +#if !MAXN + DYNALLOC1(int,workperm,workperm_sz,n+2,"putorbits"); + DYNALLOC1(set,workset,workset_sz,m,"putorbits"); +#endif + + for (i = n; --i >= 0;) workperm[i] = 0; + for (i = n; --i >= 0;) + if ((j = orbits[i]) < i) + { + workperm[i] = workperm[j]; + workperm[j] = i; + } + + curlen = 0; + for (i = 0; i < n; ++i) + if (orbits[i] == i) + { + sz = 0; + EMPTYSET(workset,m); + j = i; + do + { + ADDELEMENT(workset,j); + j = workperm[j]; + ++sz; + } + while (j > 0); + putset_firstbold(f,workset,&curlen,linelength-1,m,TRUE); + if (sz > 1) + { + s[0] = ' '; + s[1] = '('; + slen = 2 + itos(sz,s+2); + s[slen++] = ')'; + s[slen] = '\0'; + if (linelength > 0 && curlen + slen + 1 >= linelength) + { + fprintf(f,"\n "); + curlen = 3; + } + fprintf(f,"%s",s); + curlen += slen; + } + + PUTC(';',f); + ++curlen; + } + PUTC('\n',f); +} + +/***************************************************************************** +* * +* putquotient(f,g,lab,ptn,level,linelength,m,n) writes the quotient matrix * +* of g defined by the partition at the given level. Precisely, for each * +* cell W, it writes the number w of the least vertex in W, then the size * +* of W, then the number of times w is joined FROM each cell. A complete * +* join is written as "*", while an empty join is written as "-". No more * +* than linelength characters (excluding '\n') are written per line unless * +* linelength is very small. A value of linelength <= 0 dictates no line * +* breaks at all. labelorg is used. * +* * +* FUNCTIONS CALLED: itos() * +* * +*****************************************************************************/ + +void +putquotient(FILE *f, graph *g, int *lab, int *ptn, int level, + int linelength, int m, int n) +{ + int i; + char s[50]; + int ic,curlen,v,w,cell1,cell2,numcells,jc,csize,k; + set *gw; + +#if !MAXN + DYNALLOC1(int,workperm,workperm_sz,n+2,"putquotient"); + DYNALLOC1(set,workset,workset_sz,m,"putquotient"); +#endif + + numcells = 0; + for (cell1 = 0; cell1 < n; cell1 = cell2 + 1) + { + for (cell2 = cell1; ptn[cell2] > level; ++cell2) {} + w = lab[cell1]; + for (i = cell1 + 1; i <= cell2; ++i) + if (lab[i] < w) w = lab[i]; + workperm[numcells++] = w; + } + + for (ic = cell1 = 0; ic < numcells; ++ic, cell1 = cell2 + 1) + { + for (cell2 = cell1; ptn[cell2] > level; ++cell2) {} + EMPTYSET(workset,M); + for (i = cell1; i <= cell2; ++i) ADDELEMENT(workset,lab[i]); + v = workperm[ic]; + csize = cell2 - cell1 + 1; + if (v + labelorg < 10) + { + s[0] = ' '; + curlen = 1; + } + else + curlen = 0; + curlen += itos(v+labelorg,&s[curlen]); + s[curlen++] = '['; + curlen += itos(csize,&s[curlen]); + fprintf(f,"%s",s); + if (csize < 10) + { + fprintf(f,"] :"); + curlen += 4; + } + else + { + fprintf(f,"] :"); + curlen += 3; + } + + for (jc = 0; jc < numcells; ++jc) + { + w = workperm[jc]; + gw = GRAPHROW(g,w,m); + k = setinter(gw,workset,M); + if (k == 0 || k == csize) + { + if (linelength > 0 && curlen + 2 > linelength) + { + fprintf(f,"\n "); + curlen = 4; + } + if (k == 0) fprintf(f," -"); + else fprintf(f," *"); + curlen += 2; + } + else + { + i = itos(k,s); + if (linelength > 0 && curlen + i + 1 > linelength) + { + fprintf(f,"\n "); + curlen = 4; + } + fprintf(f," %s",s); + curlen += i + 1; + } + } + fprintf(f,"\n"); + } +} + +/***************************************************************************** +* * +* putquotient_sg(f,g,lab,ptn,level,linelength) writes the quotient matrix * +* of g defined by the partition at the given level. Precisely, for each * +* cell W, it writes the number w of the least vertex in W, then the size * +* of W, then the number of times w is joined FROM each cell. A complete * +* join is written as "*", while an empty join is written as "-". No more * +* than linelength characters (excluding '\n') are written per line unless * +* linelength is very small. A value of linelength <= 0 dictates no line * +* breaks at all. labelorg is used. * +* * +* Weughts are ignored. * +* * +*****************************************************************************/ + +void +putquotient_sg(FILE *f, sparsegraph *g, int *lab, int *ptn, + int level, int linelength) +{ + int i,m,n; + char s[50]; + int ic,curlen,v,w,cell1,cell2,numcells,jc,csize,k; + int *dd,*ee; + size_t *vv,j; + + n = g->nv; + m = SETWORDSNEEDED(n); + SG_VDE(g,vv,dd,ee); + +#if !MAXN + DYNALLOC1(int,workperm,workperm_sz,n+2,"putquotient"); + DYNALLOC1(set,workset,workset_sz,m,"putquotient"); +#endif + + numcells = 0; + for (cell1 = 0; cell1 < n; cell1 = cell2 + 1) + { + for (cell2 = cell1; ptn[cell2] > level; ++cell2) {} + w = lab[cell1]; + for (i = cell1 + 1; i <= cell2; ++i) + if (lab[i] < w) w = lab[i]; + workperm[numcells++] = w; + } + + for (ic = cell1 = 0; ic < numcells; ++ic, cell1 = cell2 + 1) + { + for (cell2 = cell1; ptn[cell2] > level; ++cell2) {} + EMPTYSET(workset,M); + for (i = cell1; i <= cell2; ++i) ADDELEMENT(workset,lab[i]); + v = workperm[ic]; + csize = cell2 - cell1 + 1; + if (v + labelorg < 10) + { + s[0] = ' '; + curlen = 1; + } + else + curlen = 0; + curlen += itos(v+labelorg,&s[curlen]); + s[curlen++] = '['; + curlen += itos(csize,&s[curlen]); + fprintf(f,"%s",s); + if (csize < 10) + { + fprintf(f,"] :"); + curlen += 4; + } + else + { + fprintf(f,"] :"); + curlen += 3; + } + + for (jc = 0; jc < numcells; ++jc) + { + w = workperm[jc]; + k = 0; + for (j = vv[w]; j < vv[w]+dd[w]; ++j) + if (ISELEMENT(workset,ee[j])) ++k; + + if (k == 0 || k == csize) + { + if (linelength > 0 && curlen + 2 > linelength) + { + fprintf(f,"\n "); + curlen = 4; + } + if (k == 0) fprintf(f," -"); + else fprintf(f," *"); + curlen += 2; + } + else + { + i = itos(k,s); + if (linelength > 0 && curlen + i + 1 > linelength) + { + fprintf(f,"\n "); + curlen = 4; + } + fprintf(f," %s",s); + curlen += i + 1; + } + } + fprintf(f,"\n"); + } +} + +/***************************************************************************** +* * +* putptn(f,lab,ptn,level,linelength,n) writes the partition at the given * +* level as sorted lists of integers separated by semicolons. No more than * +* linelength characters (excluding '\n') are written per line. * +* A value of linelength <= 0 dictates no line breaks at all. * +* labelorg is used. * +* * +*****************************************************************************/ + +void +putptn(FILE *f, int *lab, int *ptn, int level, int linelength, int n) +{ + int i; + int curlen,m; + + m = SETWORDSNEEDED(n); +#if !MAXN + DYNALLOC1(set,workset,workset_sz,m,"putptn"); +#endif + + PUTC('[',f); + curlen = 1; + i = 0; + while (i < n) + { + EMPTYSET(workset,m); + while (TRUE) + { + ADDELEMENT(workset,lab[i]); + if (ptn[i] > level) ++i; + else break; + } + putset(f,workset,&curlen,linelength-2,m,TRUE); + if (i < n-1) + { + fprintf(f," |"); + curlen += 2; + } + ++i; + } + fprintf(f," ]\n"); +} + +/***************************************************************************** +* * +* putcanon(f,canonlab,canong,linelength,m,n) writes the label canonlab * +* and the graph canong to f, using at most linelength characters * +* (excluding '\n') per line. labelorg is used. * +* A value of linelength <= 0 dictates no line breaks at all. * +* * +*****************************************************************************/ + +void +putcanon(FILE *f, int *canonlab, graph *canong, int linelength, int m, int n) +{ + int i; + +#if !MAXN + DYNALLOC1(int,workperm,workperm_sz,n+2,"putcanon"); +#endif + + for (i = 0; i < n; ++i) workperm[i] = canonlab[i]; + writeperm(f,workperm,TRUE,linelength,n); + putgraph(f,canong,linelength,m,n); +} + +/***************************************************************************** +* * +* putcanon_sg(f,canonlab,canong,linelength) writes the label canonlab * +* and the graph canong to f, using at most linelength characters * +* (excluding '\n') per line. labelorg is used. * +* A value of linelength <= 0 dictates no line breaks at all. * +* * +*****************************************************************************/ + +void +putcanon_sg(FILE *f, int *canonlab, sparsegraph *canong, int linelength) +{ + int i,n; + + n = canong->nv; +#if !MAXN + DYNALLOC1(int,workperm,workperm_sz,n+2,"putcanon"); +#endif + + for (i = 0; i < n; ++i) workperm[i] = canonlab[i]; + writeperm(f,workperm,TRUE,linelength,n); + putgraph_sg(f,canong,linelength); +} + +/***************************************************************************** +* * +* readptn(f,lab,ptn,&numcells,prompt,n) reads a partition from f * +* and establishes it in (lab,ptn). * +* The format can be just a number, which is fixed alone, or an arbitrary * +* partition [...|...|...]. Ranges x:y can be used. * +* labelorg is used. * +* * +*****************************************************************************/ + +void +readptn(FILE *f, int *lab, int *ptn, int *numcells, boolean prompt, int n) +{ + int i,j; + int c,v1,v2,m; + + m = SETWORDSNEEDED(n); +#if !MAXN + DYNALLOC1(set,workset,workset_sz,m,"readptn"); +#endif + + GETNW(c,f); + if (c == '=') GETNW(c,f); + if (ISDIGIT(c)) + { + ungetc(c,f); + readinteger(f,&v1); + v1 -= labelorg; + if (v1 >= 0 && v1 < n) + fixit(lab,ptn,numcells,v1,n); + else + { + fprintf(ERRFILE,"vertex out of range (%d), fixing nothing\n\n", + v1+labelorg); + unitptn(lab,ptn,numcells,n); + } + return; + } + if (c != '[') + { + ungetc(c,f); + fprintf(ERRFILE,"illegal partition, fixing nothing\n\n"); + unitptn(lab,ptn,numcells,n); + return; + } + EMPTYSET(workset,m); + *numcells = 0; + for (i = 0; i < n; ++i) ptn[i] = NAUTY_INFINITY; + i = 0; + j = -1; + while (TRUE) + { + GETNWC(c,f); + if (ISDIGIT(c)) + { + ungetc(c,f); + readinteger(f,&v1); + v1 -= labelorg; + GETNWC(c,f); + if (c == ':') + if (!readinteger(f,&v2)) + { + fprintf(ERRFILE,"unfinished range\n\n"); + v2 = v1; + } + else + v2 -= labelorg; + else + { + ungetc(c,f); + v2 = v1; + } + while (v1 <= v2) + { + if (v1 < 0 || v1 >= n || ISELEMENT(workset,v1)) + fprintf(ERRFILE,"illegal or repeated number : %d\n\n", + v1+labelorg); + else + { + ADDELEMENT(workset,v1); + lab[++j] = v1; + } + ++v1; + } + } + else if (c == '|' || c == ']' || c == EOF) + { + if (j >= i) + { + ++*numcells; + ptn[j] = 0; + } + if (c == '|') + i = j + 1; + else if (j == n - 1) + return; + else + { + i = j + 1; + ++*numcells; + for (j = 0; j < n; ++j) + if (!ISELEMENT(workset,j)) lab[i++] = j; + ptn[n-1] = 0; + return; + } + } + else if (c == '\n') + { + if (prompt) fprintf(PROMPTFILE,"] "); + } + else + fprintf(ERRFILE,"illegal character '%c' in partition\n\n",c); + } +} + +/***************************************************************************** +* * +* unitptn(lab,ptn,&numcells,n) establishes the partition with one cell. * +* * +*****************************************************************************/ + +void +unitptn(int *lab,int *ptn, int *numcells, int n) +{ + int i; + + for (i = 0; i < n; ++i) + { + lab[i] = i; + ptn[i] = NAUTY_INFINITY; + } + ptn[n-1] = 0; + *numcells = 1; +} + +/***************************************************************************** +* * +* individualise(lab,ptn,level,v,&pos,&numcells,n) individualises vertex v. * +* numcells is updated and the position of the possibly-new singleton is * +* returned in pos. * +* * +*****************************************************************************/ + +void +individualise(int *lab,int *ptn, int level, + int v, int *pos, int *numcells, int n) +{ + int i,j; + + for (i = 0; i < n; ++i) if (lab[i] == v) break; + + for (j = i; j > 0 && ptn[j-1] > level; --j) {}; + + *pos = j; + if (ptn[j] <= level) return; /* individual already */ + + lab[i] = lab[j]; + lab[j] = v; + ptn[j] = level; + ++*numcells; +} + +/***************************************************************************** +* * +* cellstarts(ptn,level,cell,m,n) sets the set cell to contain the indices * +* of the starts in ptn of the partition at level level. * +* * +*****************************************************************************/ + +void +cellstarts(int *ptn, int level, set *cell, int m, int n) +{ + int i; + + EMPTYSET(cell,m); + i = 0; + while (i < n) + { + ADDELEMENT(cell,i); + while (ptn[i] > level) ++i; + ++i; + } +} + +/***************************************************************************** +* * +* fixit(lab,ptn,&numcells,fixedvertex,n) establishes the partition * +* with one cell {fixedvertex} and all the other vertices (if any) in * +* another cell. * +* * +*****************************************************************************/ + +void +fixit(int *lab, int *ptn, int *numcells, int fixedvertex, int n) +{ + int i; + + for (i = 1; i < n; ++i) + { + lab[i] = i; + ptn[i] = 1; + } + + lab[0] = fixedvertex; + lab[fixedvertex] = 0; + ptn[0] = 0; + ptn[n-1] = 0; + if (n == 1) *numcells = 1; + else *numcells = 2; +} + +/***************************************************************************** +* * +* sethash(s,n,seed,key) is a function whose value depends only on the * +* set s, a long seed, and an integer key. It is intended to be independent * +* of the word size provided long ints have at least 32 bits, and also * +* independent of m. n is the underlying universal set size, NOT the * +* number of setwords in the set. * +* 31 bits of seed and 15 bits of key are significant. * +* The result is in 0..2^31-1. * +* * +*****************************************************************************/ + +long +sethash(set *s, int n, long seed, int key) +{ + int i,j,lsh,rsh; + unsigned long l,res,lshmask,salt; + setword si; + + lsh = key & 0xF; + rsh = 28 - lsh; + salt = (key >> 4) & 0x7FFL; + res = seed & 0x7FFFFFFFUL; + lshmask = (1UL << lsh) - 1; + + j = 0; + for (i = 0; ; ++i) + { + si = s[i]; + l = SWCHUNK0(si); + res = (((res << lsh) ^ ((res >> rsh) & lshmask) ^ l) + salt) + & 0x7FFFFFFFUL; + res = FUZZ1(res); + if ((j += 16) >= n) break; +#if WORDSIZE > 16 + l = SWCHUNK1(si); + res = (((res << lsh) ^ ((res >> rsh) & lshmask) ^ l) + salt) + & 0x7FFFFFFFUL; + res = FUZZ1(res); + if ((j += 16) >= n) break; +#if WORDSIZE == 64 + l = SWCHUNK2(si); + res = (((res << lsh) ^ ((res >> rsh) & lshmask) ^ l) + salt) + & 0x7FFFFFFFUL; + res = FUZZ1(res); + if ((j += 16) >= n) break; + l = SWCHUNK3(si); + res = (((res << lsh) ^ ((res >> rsh) & lshmask) ^ l) + salt) + & 0x7FFFFFFFUL; + res = FUZZ1(res); + if ((j += 16) >= n) break; +#endif +#endif + } + + return res; +} + +/***************************************************************************** +* * +* listhash(x,nx,key) is a function whose value depends on the SET of values * +* in the first 'nx' entries of the array 'x', and the value of key. * +* Machine-independent if long ints have at least 32 bits, otherwise not. * +* The result is in 0..2^31-1. * +* * +*****************************************************************************/ + +long +listhash(int *x, int nx, long key) +{ + unsigned long lkey,val,accum; + int i; + + lkey = (unsigned long)key & 0x7FFFFFFFUL; + accum = nx; + + for (i = 0; i < nx; ++i) + { + val = (unsigned long)x[i] & 0x7FFFFFFFUL; + val = (val + lkey) & 0x7FFFFFFFUL; + accum += FUZZ1(val); + } + + return accum & 0x7FFFFFFFUL; +} + +/***************************************************************************** +* * +* hashgraph_sg(sg,key) is a function whose value depends on the sparse * +* graph or digraph sg. * +* Machine-independent if long ints have at least 32 bits, otherwise not. * +* The result is in 0..2^31-1. * +* * +*****************************************************************************/ + +long +hashgraph_sg(sparsegraph *sg, long key) +{ + int n,i; + int *d,*e; + size_t *v; + unsigned long val,accum; + + CHECK_SWG(sg,"hashgraph_sg"); + accum = n = sg->nv; + + SG_VDE(sg,v,d,e); + + for (i = 0; i < n; ++i) + if (d[i] == 0) + accum += FUZZ1(i); + else + { + accum = (accum>>7) | ((accum<<24)&0x7FFFFFFFUL); + val = listhash(e+v[i],d[i],key); + val = (val + i) & 0x7FFFFFFFUL; + accum += FUZZ2(val); + } + + return (long)(accum & 0x7FFFFFFFUL); +} + +/***************************************************************************** +* * +* hashgraph(g,m,n,key) is a function whose value depends on the * +* graph or digraph sg. * +* Machine-independent if long ints have at least 32 bits, otherwise not. * +* The result is in 0..2^31-1. * +* * +*****************************************************************************/ + +long +hashgraph(graph *g, int m, int n, long key) +{ + int i; + set *gi; + unsigned long val,accum; + + accum = n; + for (i = 0, gi = g; i < n; ++i, gi += m) + { + accum = (accum>>12) | ((accum<<19)&0x7FFFFFFFUL); + val = sethash(gi,n,key,(key&0xFL)+i); + val = (val + i) & 0x7FFFFFFFUL; + accum += FUZZ2(val); + } + + return (long)(accum & 0x7FFFFFFFUL); +} + +/***************************************************************************** +* * +* hash(setarray,length,key) is a function whose value depends only on the * +* first 'length' entries of the array 'setarray', and the value of key. * +* key should be in the range 1..31 and preferably odd. * +* This works best if long ints have at least 32 bits, but it's valid anyway.* +* Not machine-indpendent! Use sethash() in preference. * +* * +*****************************************************************************/ + +long +hash(set *setarray, long length, int key) +{ + long code; + set *sptr; + + code = length; + sptr = setarray + length; + + while (--sptr >= setarray) + code = (code<<key) ^ ((code>>(32-key)) + *sptr); + + return code; +} + +/***************************************************************************** +* * +* readperm is like readvperm without the last argument. It is provided * +* only for backward compatibility. * +* * +*****************************************************************************/ + +void +readperm(FILE *f, int *perm, boolean prompt, int n) +{ + int nv; + + readvperm(f,perm,prompt,n,&nv); +} + +/***************************************************************************** +* * +* readvperm(f,perm,prompt,n,nv) reads a permutation of order n from * +* f, terminated by a semicolon. Any repeated or illegal numbers or * +* characters are reported then ignored. Missing numbers are filled in * +* in numerical order. A prompt is issued for each line if prompt!=FALSE. * +* labelorg is used. *nv is set equal to the number of numbers actually * +* given. Ranges like v1:v2 are allowed. * +* * +*****************************************************************************/ + +void +readvperm(FILE *f, int *perm, boolean prompt, int n, int *nv) +{ + int i; + int m,c,v1,v2; + + m = SETWORDSNEEDED(n); +#if !MAXN + DYNALLOC1(set,workset,workset_sz,m,"readperm"); +#endif + + EMPTYSET(workset,m); + + i = 0; + + while (TRUE) + { + GETNWC(c,f); + if (c == ';' || c == EOF) break; + if (ISDIGIT(c)) + { + ungetc(c,f); + readinteger(f,&v1); + v1 -= labelorg; + GETNWC(c,f); + if (c == ':') + if (!readinteger(f,&v2)) + { + fprintf(ERRFILE,"unfinished range\n\n"); + v2 = v1; + } + else + v2 -= labelorg; + else + { + ungetc(c,f); + v2 = v1; + } + + if (v1 < 0 || v1 >= n || v2 >= n || v1 > v2) + { + if (v1 < v2) + fprintf(ERRFILE, + "illegal range in permutation : %d:%d\n\n", + v1+labelorg,v2+labelorg); + else + fprintf(ERRFILE, + "illegal number in permutation : %d\n\n", + v1+labelorg); + } + else + for (; v1 <= v2; ++v1) + { + if (!ISELEMENT(workset,v1)) + { + perm[i++] = v1; + ADDELEMENT(workset,v1); + } + else + fprintf(ERRFILE, + "repeated number in permutation : %d\n\n", + v1+labelorg); + } + } + else + { + if (c == '\n' && prompt) + fprintf(PROMPTFILE,"+ "); + if (c != '\n') + fprintf(ERRFILE,"bad character '%c' in permutation\n\n", + (char)c); + } + } + + *nv = i; + + for (v1 = 0; v1 < n; ++v1) + if (!ISELEMENT(workset,v1)) perm[i++] = v1; +} + +/***************************************************************************** +* * +* ranperm(perm,n) creates a random permutation in perm. * +* * +*****************************************************************************/ + +void +ranperm(int *perm, int n) +{ + int i,j,t; + + for (i = n; --i >= 0; ) perm[i] = i; + + for (i = n; --i > 0; ) + { + j = KRAN(i+1); + t = perm[i]; + perm[i] = perm[j]; + perm[j] = t; + } +} + +/***************************************************************************** +* * +* relabel(g,perm,lab,workg,m,n) replaces g by g^perm, using workg as * +* scratch space. If lab!=NULL, it is taken as a labelling vector and * +* also permuted. * +* * +*****************************************************************************/ + +void +relabel(graph *g, int *lab, int *perm, graph *workg, int m, int n) +{ + long li; + int i; + + for (li = (long)M * (long)n; --li >= 0;) workg[li] = g[li]; + + updatecan(workg,g,perm,0,M,n); + if (lab != NULL) + { +#if !MAXN + DYNALLOC1(int,workperm,workperm_sz,n+2,"relabel"); +#endif + for (i = 0; i < n; ++i) workperm[perm[i]] = i; + for (i = 0; i < n; ++i) lab[i] = workperm[lab[i]]; + } +} + +/***************************************************************************** +* * +* relabel_sg(g,perm,lab,workg,m,n) replaces g by g^perm, using workg as * +* scratch space. If lab!=NULL, it is taken as a labelling vector and * +* also permuted. * +* * +*****************************************************************************/ + +void +relabel_sg(sparsegraph *sg, int *lab, int *perm, sparsegraph *workg) +{ + int i,n; + sparsegraph *tempsg; + sparsegraph tmp; + + n = sg->nv; + + if (workg) + { + tempsg = copy_sg(sg,workg); + updatecan_sg((graph*)tempsg,(graph*)sg,perm,0,SETWORDSNEEDED(n),n); + } + else + { + SG_INIT(tmp); + tempsg = copy_sg(sg,&tmp); + updatecan_sg((graph*)tempsg,(graph*)sg,perm,0,SETWORDSNEEDED(n),n); + SG_FREE(tmp); + } + + if (lab != NULL) + { +#if !MAXN + DYNALLOC1(int,workperm,workperm_sz,n+2,"relabel_sg"); +#endif + for (i = 0; i < n; ++i) workperm[perm[i]] = i; + for (i = 0; i < n; ++i) lab[i] = workperm[lab[i]]; + } +} + +/***************************************************************************** +* * +* sublabel(g,perm,nperm,workg,m,n) replaces g by g^perm, using workg as * +* scratch space. perm is a partial vector, of length nperm, where it is * +* known that the elements of perm are distinct. * +* * +*****************************************************************************/ + +void +sublabel(graph *g, int *perm, int nperm, graph *workg, int m, int n) +{ + long li; + int i,j,k; + int newm; + set *gi,*wgi; + + for (li = (long)m * (long)n; --li >= 0;) workg[li] = g[li]; + + newm = SETWORDSNEEDED(nperm); + + for (li = (long)newm * (long)nperm; --li >= 0;) g[li] = 0; + + for (i = 0, gi = (set*)g; i < nperm; ++i, gi += newm) + { + wgi = GRAPHROW(workg,perm[i],m); + for (j = 0; j < nperm; ++j) + { + k = perm[j]; + if (ISELEMENT(wgi,k)) ADDELEMENT(gi,j); + } + } +} + +/***************************************************************************** +* * +* countcells(ptn,level,n) finds the number of elements of ptn[0..n-1] * +* that are <= level. * +* * +*****************************************************************************/ + +int +countcells(int *ptn, int level, int n) +{ + int i,cnt; + + cnt = 0; + for (i = 0; i < n; ++i) if (ptn[i] <= level) ++cnt; + + return cnt; +} + +/***************************************************************************** +* * +* subpartion(lab,ptn,n,perm,nperm) replaces the partition (lab,ptn) of * +* 0..n-1 by the induced partition of 0..nperm-1, using the partial * +* ordering of 0..n-1 given in perm[0..nperm-1]. * +* Return the new number of cells. * +* * +*****************************************************************************/ + +#define DEB(str,x) fprintf(stderr,"%s=%d\n",str,x); + +int +subpartition(int *lab, int *ptn, int n, int *perm, int nperm) +{ + int i,j; + +#if !MAXN + DYNALLOC1(int,workperm,workperm_sz,n+2,"subpartition"); +#endif + for (i = 0; i < n; ++i) workperm[i] = -1; + for (i = 0; i < nperm; ++i) workperm[perm[i]] = i; + + j = -1; + for (i = 0; i < n; ++i) + { + if (workperm[lab[i]] >= 0) + { + ++j; + lab[j] = workperm[lab[i]]; + ptn[j] = ptn[i]; + } + else if (j >= 0 && ptn[i] < ptn[j]) + ptn[j] = ptn[i]; + } + + return countcells(ptn,0,nperm); +} + +/***************************************************************************** +* * +* sublabel_sg(sg,perm,nperm,workg) replaces g by g^perm, using workg as * +* scratch space. perm is a partial vector, of length nperm, where it is * +* known that the elements of perm are distinct. * +* * +*****************************************************************************/ + +void +sublabel_sg(sparsegraph *sg, int *perm, int nperm, sparsegraph *workg) +{ + int i,j,k,n; + size_t newnde,kk; + sparsegraph *tempsg; + sparsegraph tmp; + int *d,*e; + int *dd,*ee; + size_t *v,*vv; + + CHECK_SWG(sg,"sublabel_sg"); + n = sg->nv; + +#if !MAXN + DYNALLOC1(int,workperm,workperm_sz,n+2,"relabel_sg"); +#endif + for (i = 0; i < n; ++i) workperm[i] = -1; + for (i = 0; i < nperm; ++i) workperm[perm[i]] = i; + + newnde = 0; + SG_VDE(sg,v,d,e); + + for (i = 0; i < nperm; ++i) + { + j = perm[i]; + for (k = 0; k < d[j]; ++k) + if (workperm[e[v[j]+k]] >= 0) ++newnde; + } + + if (workg) + tempsg = workg; + else + { + SG_INIT(tmp); + tempsg = &tmp; + } + + SG_ALLOC(*tempsg,nperm,newnde,"sublabel_sg"); + SG_VDE(tempsg,vv,dd,ee); + + kk = 0; + for (i = 0; i < nperm; ++i) + { + j = perm[i]; + vv[i] = kk; + dd[i] = 0; + for (k = 0; k < d[j]; ++k) + if (workperm[e[v[j]+k]] >= 0) + { + ee[vv[i]+dd[i]] = workperm[e[v[j]+k]]; + ++dd[i]; + } + kk += dd[i]; + } + tempsg->nv = nperm; + tempsg->nde = newnde; + + copy_sg(tempsg,sg); + + if (!workg) SG_FREE(tmp); +} + +/***************************************************************************** +* * +* copycomment(fin,fout,delimiter) copies fin to fout until either EOF or * +* the character 'delimiter' is read. The delimiter itself isn't written. * +* Escape sequences \n,\t,\b,\r,\f,\\,\',\",\\n are recognised. Otherwise, * +* '\' is ignored. * +* * +*****************************************************************************/ + +void +copycomment(FILE *fin, FILE *fout, int delimiter) +{ + int c,backslash; + + backslash = FALSE; + + while ((c = getc(fin)) != EOF && (c != delimiter || backslash)) + if (backslash) + { + switch (c) + { + case '\n': + break; + case 'n': + PUTC('\n',fout); + break; + case 't': + PUTC('\t',fout); + break; + case 'b': + PUTC('\b',fout); + break; + case 'r': + PUTC('\r',fout); + break; + case 'f': + PUTC('\f',fout); + break; + case '\\': + PUTC('\\',fout); + break; + case '\'': + PUTC('\'',fout); + break; + case '"': + PUTC('"',fout); + break; + default: + PUTC(c,fout); + } + backslash = FALSE; + } + else if (c == '\\') + backslash = TRUE; + else + PUTC(c,fout); +} + +/***************************************************************************** +* * +* converse_sg(g1,g2) performs a digraph converse operation on g1, * +* leaving the result in g2. g2 must exist and be initialised. * +* If g1 is an undirected graph, g2 will be the same. * +* * +*****************************************************************************/ + +void +converse_sg(sparsegraph *g1, sparsegraph *g2) +{ + int *e1,*d1,*e2,*d2; + size_t *v1,*v2,j; + int i,k,n; + + CHECK_SWG(g1,"converse_sg"); + n = g1->nv; + + SG_ALLOC(*g2,n,g1->nde,"converse_sg"); + g2->nv = n; + g2->nde = g1->nde; + DYNFREE(g2->w,g2->wlen); + + SG_VDE(g1,v1,d1,e1); + SG_VDE(g2,v2,d2,e2); + + for (i = 0; i < n; ++i) d2[i] = 0; + for (i = 0; i < n; ++i) + for (j = v1[i]; j < v1[i]+d1[i]; ++j) ++d2[e1[j]]; + + v2[0] = 0; + for (i = 1; i < n; ++i) v2[i] = v2[i-1] + d2[i-1]; + for (i = 0; i < n; ++i) d2[i] = 0; + + for (i = 0; i < n; ++i) + for (j = v1[i]; j < v1[i]+d1[i]; ++j) + { + k = e1[j]; + e2[v2[k] + (d2[k]++)] = i; + } +} + +/***************************************************************************** +* * +* complement_sg(g1,g2) sets g2 to the complement of g1. * +* If g1 has loops then the loop set is also complemented; otherwise * +* no loops are created. g2 must exist and be initialised. * +* * +*****************************************************************************/ + +void +complement_sg(sparsegraph *g1, sparsegraph *g2) +{ + int *e1,*d1,*e2,*d2; + size_t *v1,*v2,j,ndec; + int i,l,m,n; + int loops; + + CHECK_SWG(g1,"complement_sg"); + n = g1->nv; + SG_VDE(g1,v1,d1,e1); + + loops = 0; + for (i = 0; i < n; ++i) + for (j = v1[i]; j < v1[i] + d1[i]; ++j) + if (e1[j] == i) ++loops; + + if (loops > 1) ndec = n*(size_t)n - g1->nde; + else ndec = n*(size_t)n - n - g1->nde; + SG_ALLOC(*g2,n,ndec,"converse_sg"); + g2->nv = n; + SG_VDE(g2,v2,d2,e2); + + m = SETWORDSNEEDED(n); +#if !MAXN + DYNALLOC1(set,workset,workset_sz,m,"putorbits"); +#endif + + DYNFREE(g2->w,g2->wlen); + + ndec = 0; + + for (i = 0; i < n; ++i) + { + EMPTYSET(workset,m); + for (j = v1[i]; j < v1[i]+d1[i]; ++j) ADDELEMENT(workset,e1[j]); + if (loops == 0) ADDELEMENT(workset,i); + + v2[i] = ndec; + for (l = 0; l < n; ++l) + if (!ISELEMENT(workset,l)) e2[ndec++] = l; + d2[i] = ndec - v2[i]; + } + g2->nde = ndec; +} + +/***************************************************************************** +* * +* mathon_sg(g1,g2) performs a Mathon doubling operation on g1, * +* leaving the result in g2. g2 must exist and be initialised. * +* * +*****************************************************************************/ + +void +mathon_sg(sparsegraph *g1, sparsegraph *g2) +{ + int *e1,*d1,*e2,*d2; + size_t *v1,*v2,j; + int i,k,m,n1,n2; + + CHECK_SWG(g1,"mathon_sg"); + + n1 = g1->nv; + n2 = 2*n1 + 2; + SG_ALLOC(*g2,n2,n2*(size_t)n1,"mathon_sg"); + g2->nv = n2; + g2->nde = n2*(size_t)n1; + DYNFREE(g2->w,g2->wlen); + + SG_VDE(g1,v1,d1,e1); + SG_VDE(g2,v2,d2,e2); + + m = SETWORDSNEEDED(n1); +#if !MAXN + DYNALLOC1(set,workset,workset_sz,m,"mathon_sg"); +#endif + + for (i = 0; i < n2; ++i) + { + v2[i] = i*(size_t)n1; + d2[i] = 0; + } + + for (i = 0; i < n1; ++i) + { + e2[v2[0]+(d2[0]++)] = i+1; + e2[v2[i+1]+(d2[i+1]++)] = 0; + e2[v2[n1+1]+(d2[n1+1]++)] = i+n1+2; + e2[v2[i+n1+2]+(d2[i+n1+2]++)] = n1+1; + } + + for (i = 0; i < n1; ++i) + { + EMPTYSET(workset,m); + for (j = v1[i]; j < v1[i]+d1[i]; ++j) + { + k = e1[j]; + if (k == i) continue; /* ignore loops */ + ADDELEMENT(workset,k); + e2[v2[i+1]+(d2[i+1]++)] = k+1; + e2[v2[i+n1+2]+(d2[i+n1+2]++)] = k+n1+2; + } + for (k = 0; k < n1; ++k) + if (k != i && !ISELEMENT(workset,k)) + { + e2[v2[i+1]+(d2[i+1]++)] = k+n1+2; + e2[v2[k+n1+2]+(d2[k+n1+2]++)] = i+1; + } + } +} + +/***************************************************************************** +* * +* mathon(g1,m1,n1,g2,m2,n2) performs a Mathon doubling operation on g1, * +* leaving the result in g2. * +* m1,n1 and m2,n2 are the values of m,n before and after the operation. * +* * +*****************************************************************************/ + +void +mathon(graph *g1, int m1, int n1, graph *g2, int m2, int n2) +{ + int i,j,ii,jj; + long li; + set *rowptr,*gp; + + for (li = (long)m2 * (long)n2; --li >= 0;) g2[li] = 0; + + for (i = 1; i <= n1; ++i) + { + ii = i + n1 + 1; + gp = GRAPHROW(g2,0,m2); /* unnecessarily convoluted code */ + ADDELEMENT(gp,i); /* needed to avoid compiler bug */ + gp = GRAPHROW(g2,i,m2); /* in MSDOS version */ + ADDELEMENT(gp,0); + gp = GRAPHROW(g2,n1+1,m2); + ADDELEMENT(gp,ii); + gp = GRAPHROW(g2,ii,m2); + ADDELEMENT(gp,n1+1); + } + + for (i = 0, rowptr = g1; i < n1; ++i, rowptr += m1) + for (j = 0; j < n1; ++j) + if (j != i) + { + ii = i + n1 + 2; + jj = j + n1 + 2; + if (ISELEMENT(rowptr,j)) + { + gp = GRAPHROW(g2,i+1,m2); + ADDELEMENT(gp,j+1); + gp = GRAPHROW(g2,ii,m2); + ADDELEMENT(gp,jj); + } + else + { + gp = GRAPHROW(g2,i+1,m2); + ADDELEMENT(gp,jj); + gp = GRAPHROW(g2,ii,m2); + ADDELEMENT(gp,j+1); + } + } +} + +/***************************************************************************** +* * +* rangraph(g,digraph,invprob,m,n) makes a random graph (or digraph if * +* digraph!=FALSE) with edge probability 1/invprob. * +* * +*****************************************************************************/ + +void +rangraph(graph *g, boolean digraph, int invprob, int m, int n) +{ + int i,j; + long li; + set *row,*col; + + for (li = (long)m * (long)n; --li >= 0;) g[li] = 0; + + for (i = 0, row = g; i < n; ++i, row += m) + if (digraph) + { + for (j = 0; j < n; ++j) + if (KRAN(invprob) == 0) ADDELEMENT(row,j); + } + else + { + for (j = i + 1, col = GRAPHROW(g,j,m); j < n; ++j, col += m) + if (KRAN(invprob) == 0) + { + ADDELEMENT(row,j); + ADDELEMENT(col,i); + } + } +} + + +/***************************************************************************** +* * +* rangraph2(g,digraph,p1,p2,m,n) makes a random graph (or digraph if * +* digraph!=FALSE) with edge probability p1/p2. * +* * +*****************************************************************************/ + +void +rangraph2(graph *g, boolean digraph, int p1, int p2, int m, int n) +{ + int i,j; + long li; + set *row,*col; + + for (li = (long)m * (long)n; --li >= 0;) g[li] = 0; + + for (i = 0, row = g; i < n; ++i, row += m) + if (digraph) + { + for (j = 0; j < n; ++j) + if (KRAN(p2) < p1) ADDELEMENT(row,j); + } + else + for (j = i + 1, col = GRAPHROW(g,j,m); j < n; ++j, col += m) + if (KRAN(p2) < p1) + { + ADDELEMENT(row,j); + ADDELEMENT(col,i); + } +} + +/***************************************************************************** +* * +* rangraph2_sg(sg,digraph,p1,p2,n) makes a random graph (or digraph if * +* digraph!=FALSE) with edge probability p1/p2. sg must be initialised. * +* * +*****************************************************************************/ + +void +rangraph2_sg(sparsegraph *sg, boolean digraph, int p1, int p2, int n) +{ + int i,j,k; + int *dd,*ee; + double rn,expec,var,sd; + int ldeg; + size_t *vv,inc,nde; + + sg->nv = n; + + rn = n; + expec = (rn*rn-rn)*(double)p1/(double)p2; + var = expec*(double)(p2-p1)/(double)p2; + if (!digraph) var *= 2.0; + sd = 1.0; + if (var > 1.0) + for (i = 0; i < 19; ++i) sd = (sd + var/sd) / 2.0; + inc = sd + 20; + + SG_ALLOC(*sg,n,(size_t)expec+4*inc,"rangraph2_sg"); + SG_VDE(sg,vv,dd,ee); + DYNFREE(sg->w,sg->wlen); + + for (i = 0; i < n; ++i) dd[i] = 0; + vv[0] = 0; + nde = 0; + + if (!digraph) + { + for (i = 0; i < n; ++i) + { + ldeg = 0; + for (j = i+1; j < n; ++j) + if (KRAN(p2) < p1) + { + nde += 2; + if (nde > sg->elen) + { + DYNREALLOC(int,sg->e,sg->elen,sg->elen+inc, + "rangraph2_sg realloc"); + ee = sg->e; + } + ee[vv[i]+ldeg++] = j; + ++dd[j]; + } + if (i < n-1) vv[i+1] = vv[i] + dd[i] + ldeg; + dd[i] = ldeg; + } + for (i = 0; i < n; ++i) + for (k = 0; k < dd[i]; ++k) + { + j = ee[vv[i]+k]; + if (j > i) ee[vv[j]+dd[j]++] = i; + } + sg->nde = nde; + } + else + { + for (i = 0; i < n; ++i) + { + ldeg = 0; + for (j = 0; j < n; ++j) + if (j != i && KRAN(p2) < p1) + { + ++nde; + if (nde > sg->elen) + { + DYNREALLOC(int,sg->e,sg->elen,sg->elen+inc, + "rangraph2_sg realloc"); + ee = sg->e; + } + ee[vv[i]+ldeg++] = j; + } + if (i < n-1) vv[i+1] = vv[i] + ldeg; + dd[i] = ldeg; + } + sg->nde = nde; + } +} + +/****************************************************************************/ + +static void +putsequence(FILE *f, int *x, int linelength, int n) +/* Write n integers to f with equal values collapsed. + * labelorg is used. */ +{ + char s[60]; + int j,v1,v2,xval,curlen; + + curlen = 0; + v1 = 0; + while (v1 < n) + { + xval = x[v1]; + + for (v2 = v1; v2 < n - 1 && x[v2+1] == xval; ++v2) {} + j = itos(v1+labelorg,s); + if (v2 > v1) + { + s[j++] = '-'; + j += itos(v2+labelorg,&s[j]); + } + s[j++] = ':'; + j += itos(xval,&s[j]); + s[j] = ' '; + s[j+1] = '\0'; + if (linelength > 0 && curlen + j >= linelength) + { + PUTC('\n',f); + curlen = 0; + } + curlen += j + 1; + putstring(f,s); + v1 = v2 + 1; + } + PUTC('\n',f); +} + +/****************************************************************************/ + +static void +putnumbers(FILE *f, int *x, int linelength, int n) +/* Write n integers to f with equal values combined as multiplicities. + * labelorg is NOT used. */ +{ + char s[60]; + int j,v1,v2,xval,curlen; + + curlen = 0; + v1 = 0; + while (v1 < n) + { + xval = x[v1]; + + for (v2 = v1; v2 < n - 1 && x[v2+1] == xval; ++v2) {} + if (v2 > v1) + { + j = itos(v2-v1+1,s); + s[j++] = '*'; + } + else + j = 0; + + j += itos(xval,&s[j]); + s[j] = ' '; + s[j+1] = '\0'; + if (linelength > 0 && curlen + j >= linelength) + { + PUTC('\n',f); + curlen = 0; + } + curlen += j + 1; + putstring(f,s); + v1 = v2 + 1; + } + PUTC('\n',f); +} + +/***************************************************************************** +* * +* putdegs(f,g,linelength,m,n) writes the degree of each vertex of g to * +* file f, using at most linelength characters per line (excluding '\n'). * +* A value of linelength <= 0 dictates no line breaks at all. * +* labelorg is used. * +* * +* FUNCTIONS CALLED : itos(),putstring(),setsize() * +* * +*****************************************************************************/ + +void +putdegs(FILE *f, graph *g, int linelength, int m, int n) +{ + int i; + graph *gp; + +#if !MAXN + DYNALLOC1(int,workperm,workperm_sz,n+2,"putdegs"); +#endif + + for (i = 0, gp = g; i < n; ++i, gp += M) + workperm[i] = setsize(gp,m); + + putsequence(f,workperm,linelength,n); +} + +/***************************************************************************** +* * +* putdegseq(f,g,linelength,m,n) writes the sorted degree sequence of g * +* file f, using at most linelength characters per line (excluding '\n'). * +* A value of linelength <= 0 dictates no line breaks at all. * +* * +*****************************************************************************/ + +void +putdegseq(FILE *f, graph *g, int linelength, int m, int n) +{ + int i; + graph *gp; + +#if !MAXN + DYNALLOC1(int,workperm,workperm_sz,n,"putdegs"); +#endif + + for (i = 0, gp = g; i < n; ++i, gp += M) + workperm[i] = setsize(gp,m); + + sort1int(workperm,n); + putnumbers(f,workperm,linelength,n); +} + +/***************************************************************************** +* * +* putdegs_sg(f,sg,linelength) writes the degree of each vertex of sg to * +* file f, using at most linelength characters per line (excluding '\n'). * +* A value of linelength <= 0 dictates no line breaks at all. * +* labelorg is used. * +* * +* FUNCTIONS CALLED : itos(),putstring(), * +* * +*****************************************************************************/ + +void +putdegs_sg(FILE *f, sparsegraph *sg, int linelength) +{ + putsequence(f,sg->d,linelength,sg->nv); +} + +/***************************************************************************** +* * +* putdegseq_sg(f,sg,linelength) writes the sorted degree sequence of sg to * +* file f, using at most linelength characters per line (excluding '\n'). * +* A value of linelength <= 0 dictates no line breaks at all. * +* * +*****************************************************************************/ + +void +putdegseq_sg(FILE *f, sparsegraph *sg, int linelength) +{ + int i; +#if !MAXN + DYNALLOC1(int,workperm,workperm_sz,sg->nv,"putdegs"); +#endif + + for (i = 0; i < sg->nv; ++i) + workperm[i] = sg->d[i]; + + sort1int(workperm,sg->nv); + putnumbers(f,workperm,linelength,sg->nv); +} + +/***************************************************************************** +* * +* complement(g,m,n) replaces the graph g by its complement * +* No loops are created unless there are loops present, in which case the * +* loops are also complemented. * +* * +*****************************************************************************/ + +void +complement(graph *g, int m, int n) +{ + boolean loops; + int i,j; + graph *gp; + +#if !MAXN + DYNALLOC1(set,workset,workset_sz,m,"complement"); +#endif + + loops = FALSE; + for (i = 0, gp = g; i < n && !loops; ++i, gp += M) + if (ISELEMENT(gp,i)) loops = TRUE; + + EMPTYSET(workset,m); + for (i = 0; i < n; ++ i) ADDELEMENT(workset,i); + + for (i = 0, gp = g; i < n; ++i, gp += M) + { + for (j = 0; j < M; ++j) gp[j] = workset[j] & ~gp[j]; + if (!loops) DELELEMENT(gp,i); + } +} + +/***************************************************************************** +* * +* converse(g,m,n) replaces the digraph g by its converse. * +* There is no effect on an undirected graph. * +* * +*****************************************************************************/ + +void +converse(graph *g, int m, int n) +{ + int i,j; + graph *gi,*gj; + + for (i = 0, gi = g; i < n; ++i, gi += M) + for (j = i+1, gj = gi+M; j < n; ++j, gj += M) + if ((ISELEMENT(gi,j)!=0) + (ISELEMENT(gj,i)!=0) == 1) + { + FLIPELEMENT(gi,j); + FLIPELEMENT(gj,i); + } +} + +/***************************************************************************** +* * +* naututil_check() checks that this file is compiled compatibly with the * +* given parameters. If not, call exit(1). * +* * +*****************************************************************************/ + +void +naututil_check(int wordsize, int m, int n, int version) +{ + if (wordsize != WORDSIZE) + { + fprintf(ERRFILE,"Error: WORDSIZE mismatch in naututil.c\n"); + exit(1); + } + +#if MAXN + if (m > MAXM) + { + fprintf(ERRFILE,"Error: MAXM inadequate in naututil.c\n"); + exit(1); + } + + if (n > MAXN) + { + fprintf(ERRFILE,"Error: MAXN inadequate in naututil.c\n"); + exit(1); + } +#endif + + if (version < NAUTYREQUIRED) + { + fprintf(ERRFILE,"Error: naututil.c version mismatch\n"); + exit(1); + } +} + +/***************************************************************************** +* * +* naututil_freedyn() - free the dynamic memory in this module * +* * +*****************************************************************************/ + +void +naututil_freedyn(void) +{ + echunk *ec1,*ec2; + +#if !MAXN + DYNFREE(workperm,workperm_sz); + DYNFREE(workset,workset_sz); +#endif + ec1 = first_echunk.next; + + while (ec1) + { + ec2 = ec1->next; + FREES(ec1); + ec1 = ec2; + } +} diff --git a/graph-checker/nauty/naututil.h b/graph-checker/nauty/naututil.h new file mode 100644 index 0000000..29cc3e7 --- /dev/null +++ b/graph-checker/nauty/naututil.h @@ -0,0 +1,343 @@ +/***************************************************************************** +* This is the header file for versions 2.8 of naututil.c and dreadnaut.c. * +* naututil.h. Generated from naututil-h.in by configure. +*****************************************************************************/ + +/* The parts between the ==== lines are modified by configure when +creating naututil.h out of naututil-h.in. If configure is not being +used, it is necessary to check they are correct. +====================================================================*/ + +#ifndef _NAUTUTIL_H_ /* only process this file once */ +#define _NAUTUTIL_H_ + +/* Check whether various headers are available */ + +#define HAVE_ISATTY 1 /* if isatty() is available */ +#define HAVE_TIMES 1 /* if times() is available */ +#define HAVE_TIME 1 /* if time() is available */ +#define HAVE_GETRUSAGE 1 /* if getrusage() is available */ +#define HAVE_GETTIMEOFDAY 1 /* if gettimeofday() */ +#define HAVE_CLOCK_GETTIME 1 /* if clock_gettime() */ +#define HAVE_CLOCK 1 /* if clock() is available */ + +/*==================================================================*/ + +/***************************************************************************** +* * +* Copyright (1984-2022) Brendan McKay. All rights reserved. * +* Subject to the waivers and disclaimers in nauty.h. * +* * +* CHANGE HISTORY * +* 10-Nov-87 : final changes for version 1.2 * +* 5-Dec-87 : changes for version 1.3 : * +* - added declarations of readinteger() and readstring() * +* - added definition of DEFEXT : default file-name * +* extension for dreadnaut input files * +* 28-Sep-88 : changes for version 1.4 : * +* - added support for PC Turbo C * +* 29-Nov-88 : - added getc macro for AZTEC C on MAC * +* 23-Mar-89 : changes for version 1.5 : * +* - added DREADVERSION macro * +* - added optional ANSI function prototypes * +* - changed file name to naututil.h * +* - moved ALLOCS to nauty.h and defined DYNALLOC * +* 25-Mar-89 : - added declaration of twopaths() * +* 29-Mar-89 : - added declaration of putmapping() * +* 4-Apr-89 : - added declarations of triples, quadruples, adjtriang * +* - only define ERRFILE if not in nauty.h * +* 25-Apr-89 : - added declarations of cellquads,distances,getbigcells * +* 26-Apr-89 : - added declarations of indsets,cliques,cellquins * +* - removed declarations of ptncode and equitable * +* 27-Apr-89 : - added declaration of putquotient * +* 18-Aug-89 : - added new arg to putset, and changed mathon * +* 2-Mar-90 : - added declarations of celltrips, cellcliq, cellind * +* - changed declarations to use EXTPROC * +* 12-Mar-90 : - added changes for Cray version * +* 20-Mar-90 : - added changes for THINK version * +* 27-Mar-90 : - split SYS_MSDOS into SYS_PCMS4 and SYS_PCMS5 * +* 13-Oct-90 : changes for version 1.6 : * +* - changed CPUTIME to use HZ on Unix for times() * +* 14-Oct-90 : - added SYS_APOLLO variant * +* 19-Oct-90 : - changed CPUTIME defs for BSDUNIX to avoid conficting * +* declarations of size_t and ptrdiff_t in gcc * +* 27-Aug-92 : changes for version 1.7 : * +* - added SYS_IBMC variant * +* - removed workaround for bad gcc installation * +* 5-Jun-93 : changes for version 1.8 : * +* - changed CRAY version of CPUTIME to use CLK_TCK * +* if HZ could not be found (making 1.7+) * +* 30-Jul-93 : - added SYS_ALPHA variant * +* 17-Sep-93 : changes for version 1.9 : * +* - declared adjacencies() * +* 24-Feb-94 : changes for version 1.10 : * +* - added version SYS_AMIGAAZT (making 1.9+) * +* 19-Apr-95 : - added C++ prototype wrapper * +* 6-Mar-96 : - added SYS_ALPHA32 code * +* 23-Jul-96 : changes for version 2.0 : * +* - changed readstring() declaration * +* - removed DYNALLOC definition * +* - added sublabel() definition * +* 15-Aug-96 : - added sethash() definition * +* 30-Aug-96 : - added KRAN and D. Knuth routines * +* 16-Sep-96 : - fixed the above! * +* 7-Feb-96 : - declared nautinv_null() and setnbhd() * +* 4-Sep-97 : - arg of time() is type time_t*, was long* * +* 22-Sep-97 : - defined fileno() and time_t for SYS_PCTURBO * +* 10-Dec-97 : - revised KRAN for new rng.c from Knuth * +* 18-Feb-98 : - changed time() to time_t for Unix * +* 21-Oct-98 : - changed short to shortish as needed * +* 9-Jan-00 : - declared nautinv_check() and naututil_check() * +* 16-Nov-00 : - applied changes logged in nauty.h * +* 22-Apr-01 : changes for version 2.1 : * +* - prototypes for nautinv.c are now in nautinv.h * +* - CPUTIME for UNIX uses CLK_TCK (needs revision!) * +* 2-Jun-01 : - prototype for converse() * +* 18-Oct-01 : - complete revision; sysdeps in separate files * +* 28-Aug-02 : changes for version 2.2 : * +* - revised for autoconf * +* 17-Nov-02 : added explicit "extern" where it was implicit before * +* 11-Apr-02 : added rangraph2() * +* 10-Sep-07 : Define CPUTIME=0.0 for hosts that don't provide it * +* 4-Nov-09 : added readgraph_sg(), putgraph_sg(), putcanon_sg() * +* 10-Nov-09 : removed types shortish and permutation * +* 14-Nov-09 : added relabel_sg(), copy_sg(), putdegs_sg(), * +* sublabel_sg() * +* 19-Nov-09 : added individualise() * +* 20-Nov-09 : added hashgraph_sg(), listhash(), hashgraph() * +* 19-Dec-09 : added ranreg(), rangraph2_sg() * +* 5-Jun-10 : added mathon_sg() and converse_sg() * +* 10-Jun-10 : added putquotient_sg() and complement_sg() * +* 15-Jan-12 : added TLS_ATTR to static declarations * +* 3-Mar-12 : added putorbitsplus() and putset_firstbold() * +* 17-Mar-12 : include naurng.h and remove redundant lines * +* 1-Nov-15 : changes for version 2.6 : * +* - prototypes for putdegseq(), putdegseq_sg() * +* 17-Dec-15 : prototype for readgraph_swg() * +* 6-Apr-16 : prototype for countcells() * +* 27-Aug-16 : added REALTIMEDEFS and NAUTYREALTIME * +* 14-Oct-22 : prototypes for settolist() and listtoset() * +* * +* ++++++ This file is automatically generated, don't edit it by hand! ++++++ +* * +*****************************************************************************/ + +#include "nauty.h" /* which includes stdio.h */ +#include "nausparse.h" +#include "naurng.h" +/* At this point we can assume that <sys/types.h>, <unistd.h>, <stddef.h>, + <stdlib.h>, <string.h> or <strings.h> and <malloc.h> if necessary have + been included if they exist. */ + +#ifdef __cplusplus +extern "C" { +#endif + +extern void complement(graph*,int,int); +extern void converse(graph*,int,int); +extern void converse_sg(sparsegraph*, sparsegraph*); +extern void copycomment(FILE*,FILE*,int); +extern void complement_sg(sparsegraph*, sparsegraph*); +extern int countcells(int*,int,int); +extern void flushline(FILE*); +extern void fixit(int*,int*,int*,int,int); +extern int getint(FILE*); +extern int getint_sl(FILE*); +extern long hash(set*,long,int); +extern long hashgraph(graph*,int,int,long); +extern long hashgraph_sg(sparsegraph*,long); +extern void individualise(int*,int*,int,int,int*,int*,int); +extern long listhash(int*,int,long); +extern void listtoset(int*,int,set*,int); +extern void mathon(graph*,int,int,graph*,int,int); +extern void mathon_sg(sparsegraph*,sparsegraph*); +extern void naututil_check(int,int,int,int); +extern void naututil_freedyn(void); +extern void putcanon(FILE*,int*,graph*,int,int,int); +extern void putcanon_sg(FILE*,int*,sparsegraph*,int); +extern void putdegs(FILE*,graph*,int,int,int); +extern void putdegs_sg(FILE*,sparsegraph*,int); +extern void putdegseq(FILE*,graph*,int,int,int); +extern void putdegseq_sg(FILE*,sparsegraph*,int); +extern void putgraph(FILE*,graph*,int,int,int); +extern void putgraph_sg(FILE*,sparsegraph*,int); +extern void putmapping(FILE*,int*,int,int*,int,int,int); +extern void putorbits(FILE*,int*,int,int); +extern void putorbitsplus(FILE*,int*,int,int); +extern void putptn(FILE*,int*,int*,int,int,int); +extern void putquotient(FILE*,graph*,int*,int*,int,int,int,int); +extern void putquotient_sg(FILE*,sparsegraph*,int*,int*,int,int); +extern void putset(FILE*,set*,int*,int,int,boolean); +extern void putset_firstbold(FILE*,set*,int*,int,int,boolean); +extern void rangraph(graph*,boolean,int,int,int); +extern void rangraph2(graph*,boolean,int,int,int,int); +extern void rangraph2_sg(sparsegraph*,boolean,int,int,int); +extern void ranreg_sg(sparsegraph *sg, int degree, int n); +extern void ranperm(int*,int); +extern void readgraph(FILE*,graph*,boolean,boolean,boolean,int,int,int); +extern void readgraph_sg(FILE*,sparsegraph*,boolean,boolean,int,int); +extern void readgraph_swg(FILE*,sparsegraph*,boolean,boolean,int,int); +extern boolean readinteger(FILE*,int*); +extern boolean readinteger_sl(FILE*,int*); +extern void readperm(FILE*,int*,boolean,int); +extern void readptn(FILE*,int*,int*,int*,boolean,int); +extern void readvperm(FILE*,int*,boolean,int,int*); +extern boolean readstring(FILE*,char*,int); +extern void relabel(graph*,int*,int*,graph*,int,int); +extern void relabel_sg(sparsegraph*,int*,int*,sparsegraph*); +extern long sethash(set*,int,long,int); +extern int setinter(set*,set*,int); +extern int setsize(set*,int); +extern int settolist(set *set1, int m, int *list); +extern void sublabel(graph*,int*,int,graph*,int,int); +extern void sublabel_sg(sparsegraph*,int*,int,sparsegraph*); +extern int subpartition(int*,int*,int,int*,int); +extern void unitptn(int*,int*,int*,int); + +#ifdef __cplusplus +} +#endif + +#define MAXREG 8 /* Used to limit ranreg_sg() degree */ + +#define PROMPTFILE stdout /* where to write prompts */ +#ifndef ERRFILE +#define ERRFILE stderr /* where to write error messages */ +#endif +#define MAXIFILES 10 /* how many input files can be open at once */ +#define EXIT exit(0) /* how to stop normally */ +#define DEFEXT ".dre" /* extension for dreadnaut files */ + +/************************************************************************* + The following macros may represent differences between system. This + file contains the UNIX/POSIX editions. For other systems, a separate + file of definitions is read in first. That file should define the + variables NAUTY_*_DEFINED for sections that are to replace the UNIX + versions. See the provided examples for more details. + + If your system does not have a predefined macro you can use to cause + a definitions file to be read, you have to make up one and arrange for + it to be defined when this file is read. + + The system-dependent files can also redefine the macros just ahead of + this comment. +**************************************************************************/ + +#ifdef __weirdmachine__ +#include "weird.h" /* Some weird machine (ILLUSTRATION ONLY) */ +#endif + +/*************************************************************************/ + +#ifndef NAUTY_PROMPT_DEFINED +#if HAVE_ISATTY +#define DOPROMPT(fp) (isatty(fileno(fp)) && isatty(fileno(PROMPTFILE))) +#else +#define DOPROMPT(fp) (curfile==0) +#endif +#endif /*NAUTY_PROMPT_DEFINED*/ + +/*************************************************************************/ + +#ifndef NAUTY_OPEN_DEFINED +#define OPENOUT(fp,name,append) fp = fopen(name,(append)?"a":"w") +#endif /*NAUTY_OPEN_DEFINED*/ + +/*************************************************************************/ + +#if !defined(NAUTY_CPU_DEFINED) && HAVE_CLOCK +#include <time.h> +#ifdef CLOCKS_PER_SEC +#define CPUDEFS +#define CPUTIME ((double)clock()/CLOCKS_PER_SEC) +#define NAUTY_CPU_DEFINED +#endif +#endif + +#if !defined(NAUTY_CPU_DEFINED) && HAVE_TIMES +#include <sys/times.h> +#define CPUDEFS static TLS_ATTR struct tms timebuffer; +#ifndef CLK_TCK +#include <time.h> +#endif +#if !defined(CLK_TCK) && defined(_SC_CLK_TCK) +#define CLK_TCK sysconf(_SC_CLK_TCK) +#endif +#ifndef CLK_TCK +#define CLK_TCK 60 +#endif +#define CPUTIME (times(&timebuffer),\ + (double)(timebuffer.tms_utime + timebuffer.tms_stime) / CLK_TCK) +#define NAUTY_CPU_DEFINED +#endif + +#if !defined(NAUTY_CPU_DEFINED) && HAVE_GETRUSAGE +#include <sys/time.h> +#include <sys/resource.h> +#define CPUDEFS struct rusage ruse; +#define CPUTIME (getrusage(RUSAGE_SELF,&ruse),\ + ruse.ru_utime.tv_sec + ruse.ru_stime.tv_sec + \ + 1e-6 * (ruse.ru_utime.tv_usec + ruse.ru_stime.tv_usec)) +#define NAUTY_CPU_DEFINED +#endif + +#ifndef NAUTY_CPU_DEFINED +#define CPUDEFS +#define CPUTIME 0.0 +#endif + +/*************************************************************************/ + +/* INITSEED is present for backwards compatibility. For new code, + initialise the seed using INITRANBYTIME defined in naurng.h. */ + +#ifndef NAUTY_SEED_DEFINED +#if HAVE_GETTIMEOFDAY +#include <sys/time.h> +#define INITSEED \ +{struct timeval nauty_tv; \ + gettimeofday(&nauty_tv,NULL); \ + seed = ((nauty_tv.tv_sec<<10) + (nauty_tv.tv_usec>>10)) & 0x7FFFFFFFL;} +#else +#if HAVE_TIME +#include <time.h> +#define INITSEED seed = ((time((time_t*)NULL)<<1) | 1) & 0x7FFFFFFFL +#endif +#endif +#endif /*NAUTY_SEED_DEFINED*/ + +/*************************************************************************/ + +#if !defined(NAUTY_REALTIME_DEFINED) && HAVE_GETTIMEOFDAY +#include <sys/time.h> +#define REALTIMEDEFS struct timeval nauty_rtv; +#define NAUTYREALTIME (gettimeofday(&nauty_rtv,NULL), \ + (double)(nauty_rtv.tv_sec + 1e-6*nauty_rtv.tv_usec)) +#define NAUTY_REALTIME_DEFINED +#endif + +#if !defined(NAUTY_REALTIME_DEFINED) && HAVE_CLOCK_GETTIME +#include <time.h> +#ifdef CLOCK_REALTIME +#define REALTIMEDEFS struct timespec nauty_rtv; +#define NAUTYREALTIME (clock_gettime(CLOCK_REALTIME,&nauty_rtv), \ + (double)(nauty_rtv.tv_sec + 1e-9*nauty_rtv.tv_nsec)) +#define NAUTY_REALTIME_DEFINED +#endif +#endif + +#if !defined(NAUTY_REALTIME_DEFINED) && HAVE_TIME +#include <time.h> +#define REALTIMEDEFS +#define NAUTYREALTIME ((double)time(NULL)) +#define NAUTY_REALTIME_DEFINED +#endif + +#ifndef NAUTY_REALTIME_DEFINED +#define REALTIMEDEFS +#define NAUTYREALTIME 0.0 +#endif + +#endif + +/* ++++++ This file is automatically generated, don't edit it by hand! ++++++ */ diff --git a/graph-checker/nauty/nauty.c b/graph-checker/nauty/nauty.c new file mode 100644 index 0000000..790a197 --- /dev/null +++ b/graph-checker/nauty/nauty.c @@ -0,0 +1,1255 @@ +/***************************************************************************** +* * +* Main source file for version 2.7 of nauty. * +* * +* Copyright (1984-2018) Brendan McKay. All rights reserved. Permission * +* Subject to the 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 : * +* - add use of refine1 instead of refine for m==1 * +* - changes for new optionblk syntax * +* - disable tc_level use for digraphs * +* - interposed doref() interface to refine() so that * +* options.invarproc can be supported * +* - declared local routines static * +* 28-Mar-89 : - implemented mininvarlevel/maxinvarlevel < 0 options * +* 2-Apr-89 : - added invarproc fields in stats * +* 5-Apr-89 : - modified error returns from nauty() * +* - added error message to ERRFILE * +* - changed MAKEEMPTY uses to EMPTYSET * +* 18-Apr-89 : - added MTOOBIG and CANONGNIL * +* 8-May-89 : - changed firstcode[] and canoncode[] to short * +* 10-Nov-90 : changes for version 1.6 : * +* - added dummy routine nauty_null (see dreadnaut.c) * +* 2-Sep-91 : changes for version 1.7 : * +* - moved MULTIPLY into nauty.h * +* 27-Mar-92 : - changed 'n' into 'm' in error message in nauty() * +* 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) * +* 13-Jul-96 : changes for version 2.0 : * +* - added dynamic allocation * +* 21-Oct-98 : - made short into shortish for BIGNAUTY as needed * +* 7-Jan-00 : - allowed n=0 * +* - added nauty_check() and a call to it * +* 12-Feb-00 : - used better method for target cell memory allocation * +* - did a little formating of the code * +* 27-May-00 : - fixed error introduced on Feb 12. * +* - dynamic allocations in nauty() are now deallocated * +* before return if n >= 320. * +* 16-Nov-00 : - use function prototypes, change UPROC to void. * +* - added argument to tcellproc(), removed nvector * +* - now use options.dispatch, options.groupopts is gone. * +* 22-Apr-01 : - Added code for compilation into Magma * +* - Removed nauty_null() and EXTDEFS * +* 2-Oct-01 : - Improved error message for bad dispatch vector * +* 21-Nov-01 : - use NAUTYREQUIRED in nauty_check() * +* 20-Dec-02 : changes for version 2.2: * +* - made tcnode0 global * +* - added nauty_freedyn() * +* 17-Nov-03 : changed INFINITY to NAUTY_INFINITY * +* 14-Sep-04 : extended prototypes even to recursive functions * +* 16-Oct-04 : disallow NULL dispatch vector * +* 11-Nov-05 : changes for version 2.3: * +* - init() and cleanup() optional calls * +* 23-Nov-06 : changes for version 2.4: * +* - use maketargetcell() instead of tcellproc() * +* 29-Nov-06 : add extra_autom, extra_level, extra_options * +* 10-Dec-06 : remove BIGNAUTY * +* 10-Nov-09 : remove shortish and permutation types * +* 16-Nov-11 : added Shreier option * +* 15-Jan-12 : added TLS_ATTR to static declarations * +* 18-Jan-13 : added signal aborting * +* 19-Jan-13 : added usercanonproc() * +* 14-Oct-17 : corrected code for n=0 * +* * +*****************************************************************************/ + +#define ONE_WORD_SETS +#include "nauty.h" +#include "schreier.h" + +#ifdef NAUTY_IN_MAGMA +#include "cleanup.e" +#endif + +#define NAUTY_ABORTED (-11) +#define NAUTY_KILLED (-12) + +typedef struct tcnode_struct +{ + struct tcnode_struct *next; + set *tcellptr; +} tcnode; + +/* aproto: header new_nauty_protos.h */ + +#ifndef NAUTY_IN_MAGMA +#if !MAXN +static int firstpathnode0(int*, int*, int, int, tcnode*); +static int othernode0(int*, int*, int, int, tcnode*); +#else +static int firstpathnode(int*, int*, int, int); +static int othernode(int*, int*, int, int); +#endif +static void firstterminal(int*, int); +static int processnode(int*, int*, int, int); +static void recover(int*, int); +static void writemarker(int, int, int, int, int, int); +#endif + +#if MAXM==1 +#define M 1 +#else +#define M m +#endif + +#define OPTCALL(proc) if (proc != NULL) (*proc) + + /* copies of some of the options: */ +static TLS_ATTR + boolean getcanon,digraph,writeautoms,domarkers,cartesian,doschreier; +static TLS_ATTR int linelength,tc_level,mininvarlevel,maxinvarlevel,invararg; +static TLS_ATTR void (*usernodeproc)(graph*,int*,int*,int,int,int,int,int,int); +static TLS_ATTR void (*userautomproc)(int,int*,int*,int,int,int); +static TLS_ATTR void (*userlevelproc) + (int*,int*,int,int*,statsblk*,int,int,int,int,int,int); +static TLS_ATTR int (*usercanonproc) + (graph*,int*,graph*,unsigned long,int,int,int); +static TLS_ATTR void (*invarproc) + (graph*,int*,int*,int,int,int,int*,int,boolean,int,int); +static TLS_ATTR FILE *outfile; +static TLS_ATTR dispatchvec dispatch; + + /* local versions of some of the arguments: */ +static TLS_ATTR int m,n; +static TLS_ATTR graph *g,*canong; +static TLS_ATTR int *orbits; +static TLS_ATTR statsblk *stats; + /* temporary versions of some stats: */ +static TLS_ATTR unsigned long invapplics,invsuccesses; +static TLS_ATTR int invarsuclevel; + + /* working variables: <the "bsf leaf" is the leaf which is best guess so + far at the canonical leaf> */ +static TLS_ATTR int gca_first, /* level of greatest common ancestor of + current node and first leaf */ + gca_canon, /* ditto for current node and bsf leaf */ + noncheaplevel, /* level of greatest ancestor for which cheapautom==FALSE */ + allsamelevel, /* level of least ancestor of first leaf for + which all descendant leaves are known to be + equivalent */ + eqlev_first, /* level to which codes for this node match those + for first leaf */ + eqlev_canon, /* level to which codes for this node match those + for the bsf leaf. */ + comp_canon, /* -1,0,1 according as code at eqlev_canon+1 is + <,==,> that for bsf leaf. Also used for + similar purpose during leaf processing */ + samerows, /* number of rows of canong which are correct for + the bsf leaf BDM:correct description? */ + canonlevel, /* level of bsf leaf */ + stabvertex, /* point fixed in ancestor of first leaf at level + gca_canon */ + cosetindex; /* the point being fixed at level gca_first */ + +static TLS_ATTR boolean needshortprune; /* used to flag calls to shortprune */ + +#if !MAXN +DYNALLSTAT(set,defltwork,defltwork_sz); +DYNALLSTAT(int,workperm,workperm_sz); +DYNALLSTAT(set,fixedpts,fixedpts_sz); +DYNALLSTAT(int,firstlab,firstlab_sz); +DYNALLSTAT(int,canonlab,canonlab_sz); +DYNALLSTAT(short,firstcode,firstcode_sz); +DYNALLSTAT(short,canoncode,canoncode_sz); +DYNALLSTAT(int,firsttc,firsttc_sz); +DYNALLSTAT(set,active,active_sz); + +/* In the dynamically allocated case (MAXN=0), each level of recursion + needs one set (tcell) to represent the target cell. This is + implemented by using a linked list of tcnode anchored at the root + of the search tree. Each node points to its child (if any) and to + the dynamically allocated tcell. Apart from the first node of + the list, each node always has a tcell good for m up to alloc_m. + tcnodes and tcells are kept between calls to nauty, except that + they are freed and reallocated if m gets bigger than alloc_m. */ + +static TLS_ATTR tcnode tcnode0 = {NULL,NULL}; +static TLS_ATTR int alloc_m = 0; + +#else +static TLS_ATTR set defltwork[2*MAXM]; /* workspace in case none provided */ +static TLS_ATTR int workperm[MAXN]; /* various scratch uses */ +static TLS_ATTR set fixedpts[MAXM]; /* points which were explicitly + fixed to get current node */ +static TLS_ATTR int firstlab[MAXN], /* label from first leaf */ + canonlab[MAXN]; /* label from bsf leaf */ +static TLS_ATTR short firstcode[MAXN+2], /* codes for first leaf */ + canoncode[MAXN+2]; /* codes for bsf leaf */ +static TLS_ATTR int firsttc[MAXN+2]; /* index of target cell for left path */ +static TLS_ATTR set active[MAXM]; /* used to contain index to cells now + active for refinement purposes */ +#endif + +static TLS_ATTR set *workspace,*worktop; /* first and just-after-last + addresses of work area to hold automorphism data */ +static TLS_ATTR set *fmptr; /* pointer into workspace */ + +static TLS_ATTR schreier *gp; /* These two for Schreier computations */ +static TLS_ATTR permnode *gens; + +/***************************************************************************** +* * +* This procedure finds generators for the automorphism group of a * +* vertex-coloured graph and optionally finds a canonically labelled * +* isomorph. A description of the data structures can be found in * +* nauty.h and in the "nauty User's Guide". The Guide also gives * +* many more details about its use, and implementation notes. * +* * +* Parameters - <r> means read-only, <w> means write-only, <wr> means both: * +* g <r> - the graph * +* lab,ptn <rw> - used for the partition nest which defines the colouring * +* of g. The initial colouring will be set by the program, * +* using the same colour for every vertex, if * +* options->defaultptn!=FALSE. Otherwise, you must set it * +* yourself (see the Guide). If options->getcanon!=FALSE, * +* the contents of lab on return give the labelling of g * +* corresponding to canong. This does not change the * +* initial colouring of g as defined by (lab,ptn), since * +* the labelling is consistent with the colouring. * +* active <r> - If this is not NULL and options->defaultptn==FALSE, * +* it is a set indicating the initial set of active colours. * +* See the Guide for details. * +* orbits <w> - On return, orbits[i] contains the number of the * +* least-numbered vertex in the same orbit as i, for * +* i=0,1,...,n-1. * +* options <r> - A list of options. See nauty.h and/or the Guide * +* for details. * +* stats <w> - A list of statistics produced by the procedure. See * +* nauty.h and/or the Guide for details. * +* workspace <w> - A chunk of memory for working storage. * +* worksize <r> - The number of setwords in workspace. See the Guide * +* for guidance. * +* m <r> - The number of setwords in sets. This must be at * +* least ceil(n / WORDSIZE) and at most MAXM. * +* n <r> - The number of vertices. This must be at least 1 and * +* at most MAXN. * +* canong <w> - The canononically labelled isomorph of g. This is * +* only produced if options->getcanon!=FALSE, and can be * +* given as NULL otherwise. * +* * +* FUNCTIONS CALLED: firstpathnode(),updatecan() * +* * +*****************************************************************************/ + +void +nauty(graph *g_arg, int *lab, int *ptn, set *active_arg, + int *orbits_arg, optionblk *options, statsblk *stats_arg, + set *ws_arg, int worksize, int m_arg, int n_arg, graph *canong_arg) +{ + int i; + int numcells; + int retval; + int initstatus; +#if !MAXN + tcnode *tcp,*tcq; +#endif + + /* determine dispatch vector */ + + if (options->dispatch == NULL) + { + fprintf(ERRFILE,">E nauty: null dispatch vector\n"); + fprintf(ERRFILE,"Maybe you need to recompile\n"); + exit(1); + } + else + dispatch = *(options->dispatch); + + if (options->userrefproc) + dispatch.refine = options->userrefproc; + else if (dispatch.refine1 && m_arg == 1) + dispatch.refine = dispatch.refine1; + + if (dispatch.refine == NULL || dispatch.updatecan == NULL + || dispatch.targetcell == NULL || dispatch.cheapautom == NULL) + { + fprintf(ERRFILE,">E bad dispatch vector\n"); + exit(1); + } + + /* check for excessive sizes: */ + +#if !MAXN + if (m_arg > NAUTY_INFINITY/WORDSIZE+1) + { + stats_arg->errstatus = MTOOBIG; + fprintf(ERRFILE,"nauty: need m <= %d, but m=%d\n\n", + NAUTY_INFINITY/WORDSIZE+1,m_arg); + return; + } + if (n_arg > NAUTY_INFINITY-2 || n_arg > WORDSIZE * m_arg) + { + stats_arg->errstatus = NTOOBIG; + fprintf(ERRFILE,"nauty: need n <= min(%d,%d*m), but n=%d\n\n", + NAUTY_INFINITY-2,WORDSIZE,n_arg); + return; + } +#else + if (m_arg > MAXM) + { + stats_arg->errstatus = MTOOBIG; + fprintf(ERRFILE,"nauty: need m <= %d\n\n",MAXM); + return; + } + if (n_arg > MAXN || n_arg > WORDSIZE * m_arg) + { + stats_arg->errstatus = NTOOBIG; + fprintf(ERRFILE, + "nauty: need n <= min(%d,%d*m)\n\n",MAXM,WORDSIZE); + return; + } +#endif + if (n_arg == 0) /* Special code for zero-sized graph */ + { + stats_arg->grpsize1 = 1.0; + stats_arg->grpsize2 = 0; + stats_arg->numorbits = 0; + stats_arg->numgenerators = 0; + stats_arg->errstatus = 0; + stats_arg->numnodes = 1; + stats_arg->numbadleaves = 0; + stats_arg->maxlevel = 1; + stats_arg->tctotal = 0; + stats_arg->canupdates = (options->getcanon != 0); + stats_arg->invapplics = 0; + stats_arg->invsuccesses = 0; + stats_arg->invarsuclevel = 0; + + g = canong = NULL; + initstatus = 0; + OPTCALL(dispatch.init)(g_arg,&g,canong_arg,&canong, + lab,ptn,active,options,&initstatus,m,n); + if (initstatus) stats->errstatus = initstatus; + + if (g == NULL) g = g_arg; + if (canong == NULL) canong = canong_arg; + OPTCALL(dispatch.cleanup)(g_arg,&g,canong_arg,&canong, + lab,ptn,options,stats_arg,m,n); + return; + } + + /* take copies of some args, and options: */ + m = m_arg; + n = n_arg; + + nautil_check(WORDSIZE,m,n,NAUTYVERSIONID); + OPTCALL(dispatch.check)(WORDSIZE,m,n,NAUTYVERSIONID); + +#if !MAXN + DYNALLOC1(set,defltwork,defltwork_sz,2*m,"nauty"); + DYNALLOC1(set,fixedpts,fixedpts_sz,m,"nauty"); + DYNALLOC1(set,active,active_sz,m,"nauty"); + DYNALLOC1(int,workperm,workperm_sz,n,"nauty"); + DYNALLOC1(int,firstlab,firstlab_sz,n,"nauty"); + DYNALLOC1(int,canonlab,canonlab_sz,n,"nauty"); + DYNALLOC1(short,firstcode,firstcode_sz,n+2,"nauty"); + DYNALLOC1(short,canoncode,canoncode_sz,n+2,"nauty"); + DYNALLOC1(int,firsttc,firsttc_sz,n+2,"nauty"); + if (m > alloc_m) + { + tcp = tcnode0.next; + while (tcp != NULL) + { + tcq = tcp->next; + FREES(tcp->tcellptr); + FREES(tcp); + tcp = tcq; + } + alloc_m = m; + tcnode0.next = NULL; + } +#endif + + /* OLD g = g_arg; */ + orbits = orbits_arg; + stats = stats_arg; + + getcanon = options->getcanon; + digraph = options->digraph; + writeautoms = options->writeautoms; + domarkers = options->writemarkers; + cartesian = options->cartesian; + doschreier = options->schreier; + if (doschreier) schreier_check(WORDSIZE,m,n,NAUTYVERSIONID); + linelength = options->linelength; + if (digraph) tc_level = 0; + else tc_level = options->tc_level; + outfile = (options->outfile == NULL ? stdout : options->outfile); + usernodeproc = options->usernodeproc; + userautomproc = options->userautomproc; + userlevelproc = options->userlevelproc; + usercanonproc = options->usercanonproc; + + invarproc = options->invarproc; + if (options->mininvarlevel < 0 && options->getcanon) + mininvarlevel = -options->mininvarlevel; + else + mininvarlevel = options->mininvarlevel; + if (options->maxinvarlevel < 0 && options->getcanon) + maxinvarlevel = -options->maxinvarlevel; + else + maxinvarlevel = options->maxinvarlevel; + invararg = options->invararg; + + if (getcanon) + if (canong_arg == NULL) + { + stats_arg->errstatus = CANONGNIL; + fprintf(ERRFILE, + "nauty: canong=NULL but options.getcanon=TRUE\n\n"); + return; + } + + /* initialize everything: */ + + if (options->defaultptn) + { + for (i = 0; i < n; ++i) /* give all verts same colour */ + { + lab[i] = i; + ptn[i] = NAUTY_INFINITY; + } + ptn[n-1] = 0; + EMPTYSET(active,m); + ADDELEMENT(active,0); + numcells = 1; + } + else + { + ptn[n-1] = 0; + numcells = 0; + for (i = 0; i < n; ++i) + if (ptn[i] != 0) ptn[i] = NAUTY_INFINITY; + else ++numcells; + if (active_arg == NULL) + { + EMPTYSET(active,m); + for (i = 0; i < n; ++i) + { + ADDELEMENT(active,i); + while (ptn[i]) ++i; + } + } + else + for (i = 0; i < M; ++i) active[i] = active_arg[i]; + } + + g = canong = NULL; + initstatus = 0; + OPTCALL(dispatch.init)(g_arg,&g,canong_arg,&canong, + lab,ptn,active,options,&initstatus,m,n); + if (initstatus) + { + stats->errstatus = initstatus; + return; + } + + if (g == NULL) g = g_arg; + if (canong == NULL) canong = canong_arg; + + if (doschreier) newgroup(&gp,&gens,n); + + for (i = 0; i < n; ++i) orbits[i] = i; + stats->grpsize1 = 1.0; + stats->grpsize2 = 0; + stats->numgenerators = 0; + stats->numnodes = 0; + stats->numbadleaves = 0; + stats->tctotal = 0; + stats->canupdates = 0; + stats->numorbits = n; + EMPTYSET(fixedpts,m); + noncheaplevel = 1; + eqlev_canon = -1; /* needed even if !getcanon */ + + if (worksize >= 2 * m) + workspace = ws_arg; + else + { + workspace = defltwork; + worksize = 2 * m; + } + worktop = workspace + (worksize - worksize % (2 * m)); + fmptr = workspace; + + /* here goes: */ + stats->errstatus = 0; + needshortprune = FALSE; + invarsuclevel = NAUTY_INFINITY; + invapplics = invsuccesses = 0; + +#if !MAXN + retval = firstpathnode0(lab,ptn,1,numcells,&tcnode0); +#else + retval = firstpathnode(lab,ptn,1,numcells); +#endif + + if (retval == NAUTY_ABORTED) + stats->errstatus = NAUABORTED; + else if (retval == NAUTY_KILLED) + stats->errstatus = NAUKILLED; + else + { + if (getcanon) + { + (*dispatch.updatecan)(g,canong,canonlab,samerows,M,n); + for (i = 0; i < n; ++i) lab[i] = canonlab[i]; + } + stats->invarsuclevel = + (invarsuclevel == NAUTY_INFINITY ? 0 : invarsuclevel); + stats->invapplics = invapplics; + stats->invsuccesses = invsuccesses; + } + +#if !MAXN +#ifndef NAUTY_IN_MAGMA + if (n >= 320) +#endif + { + nautil_freedyn(); + OPTCALL(dispatch.freedyn)(); + nauty_freedyn(); + } +#endif + OPTCALL(dispatch.cleanup)(g_arg,&g,canong_arg,&canong, + lab,ptn,options,stats,m,n); + + if (doschreier) + { + freeschreier(&gp,&gens); + if (n >= 320) schreier_freedyn(); + } +} + +/***************************************************************************** +* * +* firstpathnode(lab,ptn,level,numcells) produces a node on the leftmost * +* path down the tree. The parameters describe the level and the current * +* colour partition. The set of active cells is taken from the global set * +* 'active'. If the refined partition is not discrete, the leftmost child * +* is produced by calling firstpathnode, and the other children by calling * +* othernode. * +* For MAXN=0 there is an extra parameter: the address of the parent tcell * +* structure. * +* The value returned is the level to return to. * +* * +* FUNCTIONS CALLED: (*usernodeproc)(),doref(),cheapautom(), * +* firstterminal(),nextelement(),breakout(), * +* firstpathnode(),othernode(),recover(),writestats(), * +* (*userlevelproc)(),(*tcellproc)(),shortprune() * +* * +*****************************************************************************/ + +static int +#if !MAXN +firstpathnode0(int *lab, int *ptn, int level, int numcells, + tcnode *tcnode_parent) +#else +firstpathnode(int *lab, int *ptn, int level, int numcells) +#endif +{ + int tv; + int tv1,index,rtnlevel,tcellsize,tc,childcount,qinvar,refcode; +#if !MAXN + set *tcell; + tcnode *tcnode_this; + + tcnode_this = tcnode_parent->next; + if (tcnode_this == NULL) + { + if ((tcnode_this = (tcnode*)ALLOCS(1,sizeof(tcnode))) == NULL || + (tcnode_this->tcellptr + = (set*)ALLOCS(alloc_m,sizeof(set))) == NULL) + alloc_error("tcell"); + tcnode_parent->next = tcnode_this; + tcnode_this->next = NULL; + } + tcell = tcnode_this->tcellptr; +#else + set tcell[MAXM]; +#endif + + ++stats->numnodes; + + /* refine partition : */ + doref(g,lab,ptn,level,&numcells,&qinvar,workperm, + active,&refcode,dispatch.refine,invarproc, + mininvarlevel,maxinvarlevel,invararg,digraph,M,n); + firstcode[level] = (short)refcode; + if (qinvar > 0) + { + ++invapplics; + if (qinvar == 2) + { + ++invsuccesses; + if (mininvarlevel < 0) mininvarlevel = level; + if (maxinvarlevel < 0) maxinvarlevel = level; + if (level < invarsuclevel) invarsuclevel = level; + } + } + + tc = -1; + if (numcells != n) + { + /* locate new target cell, setting tc to its position in lab, tcell + to its contents, and tcellsize to its size: */ + maketargetcell(g,lab,ptn,level,tcell,&tcellsize, + &tc,tc_level,digraph,-1,dispatch.targetcell,M,n); + stats->tctotal += tcellsize; + } + firsttc[level] = tc; + + /* optionally call user-defined node examination procedure: */ + OPTCALL(usernodeproc) + (g,lab,ptn,level,numcells,tc,(int)firstcode[level],M,n); + + if (numcells == n) /* found first leaf? */ + { + firstterminal(lab,level); + OPTCALL(userlevelproc)(lab,ptn,level,orbits,stats,0,1,1,n,0,n); + if (getcanon && usercanonproc != NULL) + { + (*dispatch.updatecan)(g,canong,canonlab,samerows,M,n); + samerows = n; + if ((*usercanonproc)(g,canonlab,canong,stats->canupdates, + (int)canoncode[level],M,n)) + return NAUTY_ABORTED; + } + return level-1; + } + +#ifdef NAUTY_IN_MAGMA + if (main_seen_interrupt) return NAUTY_KILLED; +#else + if (nauty_kill_request) return NAUTY_KILLED; +#endif + + if (noncheaplevel >= level + && !(*dispatch.cheapautom)(ptn,level,digraph,n)) + noncheaplevel = level + 1; + + /* use the elements of the target cell to produce the children: */ + index = 0; + for (tv1 = tv = nextelement(tcell,M,-1); tv >= 0; + tv = nextelement(tcell,M,tv)) + { + if (orbits[tv] == tv) /* ie, not equiv to previous child */ + { + breakout(lab,ptn,level+1,tc,tv,active,M); + ADDELEMENT(fixedpts,tv); + cosetindex = tv; + if (tv == tv1) + { +#if !MAXN + rtnlevel = firstpathnode0(lab,ptn,level+1,numcells+1, + tcnode_this); +#else + rtnlevel = firstpathnode(lab,ptn,level+1,numcells+1); +#endif + childcount = 1; + gca_first = level; + stabvertex = tv1; + } + else + { +#if !MAXN + rtnlevel = othernode0(lab,ptn,level+1,numcells+1, + tcnode_this); +#else + rtnlevel = othernode(lab,ptn,level+1,numcells+1); +#endif + ++childcount; + } + DELELEMENT(fixedpts,tv); + if (rtnlevel < level) + return rtnlevel; + if (needshortprune) + { + needshortprune = FALSE; + shortprune(tcell,fmptr-M,M); + } + recover(ptn,level); + } + if (orbits[tv] == tv1) /* ie, in same orbit as tv1 */ + ++index; + } + MULTIPLY(stats->grpsize1,stats->grpsize2,index); + + if (tcellsize == index && allsamelevel == level + 1) + --allsamelevel; + + if (domarkers) + writemarker(level,tv1,index,tcellsize,stats->numorbits,numcells); + OPTCALL(userlevelproc)(lab,ptn,level,orbits,stats,tv1,index,tcellsize, + numcells,childcount,n); + return level-1; +} + +/***************************************************************************** +* * +* othernode(lab,ptn,level,numcells) produces a node other than an ancestor * +* of the first leaf. The parameters describe the level and the colour * +* partition. The list of active cells is found in the global set 'active'. * +* The value returned is the level to return to. * +* * +* FUNCTIONS CALLED: (*usernodeproc)(),doref(),refine(),recover(), * +* processnode(),cheapautom(),(*tcellproc)(),shortprune(), * +* nextelement(),breakout(),othernode(),longprune() * +* * +*****************************************************************************/ + +static int +#if !MAXN +othernode0(int *lab, int *ptn, int level, int numcells, + tcnode *tcnode_parent) +#else +othernode(int *lab, int *ptn, int level, int numcells) +#endif +{ + int tv; + int tv1,refcode,rtnlevel,tcellsize,tc,qinvar; + short code; +#if !MAXN + set *tcell; + tcnode *tcnode_this; + + tcnode_this = tcnode_parent->next; + if (tcnode_this == NULL) + { + if ((tcnode_this = (tcnode*)ALLOCS(1,sizeof(tcnode))) == NULL || + (tcnode_this->tcellptr + = (set*)ALLOCS(alloc_m,sizeof(set))) == NULL) + alloc_error("tcell"); + tcnode_parent->next = tcnode_this; + tcnode_this->next = NULL; + } + tcell = tcnode_this->tcellptr; +#else + set tcell[MAXM]; +#endif + +#ifdef NAUTY_IN_MAGMA + if (main_seen_interrupt) return NAUTY_KILLED; +#else + if (nauty_kill_request) return NAUTY_KILLED; +#endif + + ++stats->numnodes; + + /* refine partition : */ + doref(g,lab,ptn,level,&numcells,&qinvar,workperm,active, + &refcode,dispatch.refine,invarproc,mininvarlevel,maxinvarlevel, + invararg,digraph,M,n); + code = (short)refcode; + if (qinvar > 0) + { + ++invapplics; + if (qinvar == 2) + { + ++invsuccesses; + if (level < invarsuclevel) invarsuclevel = level; + } + } + + if (eqlev_first == level - 1 && code == firstcode[level]) + eqlev_first = level; + if (getcanon) + { + if (eqlev_canon == level - 1) + { + if (code < canoncode[level]) + comp_canon = -1; + else if (code > canoncode[level]) + comp_canon = 1; + else + { + comp_canon = 0; + eqlev_canon = level; + } + } + if (comp_canon > 0) canoncode[level] = code; + } + + tc = -1; + /* If children will be required, find new target cell and set tc to its + position in lab, tcell to its contents, and tcellsize to its size: */ + + if (numcells < n && (eqlev_first == level || + (getcanon && comp_canon >= 0))) + { + if (!getcanon || comp_canon < 0) + { + maketargetcell(g,lab,ptn,level,tcell,&tcellsize,&tc, + tc_level,digraph,firsttc[level],dispatch.targetcell,M,n); + if (tc != firsttc[level]) eqlev_first = level - 1; + } + else + maketargetcell(g,lab,ptn,level,tcell,&tcellsize,&tc, + tc_level,digraph,-1,dispatch.targetcell,M,n); + stats->tctotal += tcellsize; + } + + /* optionally call user-defined node examination procedure: */ + OPTCALL(usernodeproc)(g,lab,ptn,level,numcells,tc,(int)code,M,n); + + /* call processnode to classify the type of this node: */ + + rtnlevel = processnode(lab,ptn,level,numcells); + if (rtnlevel < level) /* keep returning if necessary */ + return rtnlevel; + if (needshortprune) + { + needshortprune = FALSE; + shortprune(tcell,fmptr-M,M); + } + + if (!(*dispatch.cheapautom)(ptn,level,digraph,n)) + noncheaplevel = level + 1; + + /* use the elements of the target cell to produce the children: */ + for (tv1 = tv = nextelement(tcell,M,-1); tv >= 0; + tv = nextelement(tcell,M,tv)) + { + breakout(lab,ptn,level+1,tc,tv,active,M); + ADDELEMENT(fixedpts,tv); +#if !MAXN + rtnlevel = othernode0(lab,ptn,level+1,numcells+1,tcnode_this); +#else + rtnlevel = othernode(lab,ptn,level+1,numcells+1); +#endif + DELELEMENT(fixedpts,tv); + + if (rtnlevel < level) return rtnlevel; + /* use stored automorphism data to prune target cell: */ + if (needshortprune) + { + needshortprune = FALSE; + shortprune(tcell,fmptr-M,M); + } + if (tv == tv1) + { + longprune(tcell,fixedpts,workspace,fmptr,M); + if (doschreier) pruneset(fixedpts,gp,&gens,tcell,M,n); + } + + recover(ptn,level); + } + + return level-1; +} + +/***************************************************************************** +* * +* Process the first leaf of the tree. * +* * +* FUNCTIONS CALLED: NONE * +* * +*****************************************************************************/ + +static void +firstterminal(int *lab, int level) +{ + int i; + + stats->maxlevel = level; + gca_first = allsamelevel = eqlev_first = level; + firstcode[level+1] = 077777; + firsttc[level+1] = -1; + + for (i = 0; i < n; ++i) firstlab[i] = lab[i]; + + if (getcanon) + { + canonlevel = eqlev_canon = gca_canon = level; + comp_canon = 0; + samerows = 0; + for (i = 0; i < n; ++i) canonlab[i] = lab[i]; + for (i = 0; i <= level; ++i) canoncode[i] = firstcode[i]; + canoncode[level+1] = 077777; + stats->canupdates = 1; + } +} + +/***************************************************************************** +* * +* Process a node other than the first leaf or its ancestors. It is first * +* classified into one of five types and then action is taken appropriate * +* to that type. The types are * +* * +* 0: Nothing unusual. This is just a node internal to the tree whose * +* children need to be generated sometime. * +* 1: This is a leaf equivalent to the first leaf. The mapping from * +* firstlab to lab is thus an automorphism. After processing the * +* automorphism, we can return all the way to the closest invocation * +* of firstpathnode. * +* 2: This is a leaf equivalent to the bsf leaf. Again, we have found an * +* automorphism, but it may or may not be as useful as one from a * +* type-1 node. Return as far up the tree as possible. * +* 3: This is a new bsf node, provably better than the previous bsf node. * +* After updating canonlab etc., treat it the same as type 4. * +* 4: This is a leaf for which we can prove that no descendant is * +* equivalent to the first or bsf leaf or better than the bsf leaf. * +* Return up the tree as far as possible, but this may only be by * +* one level. * +* * +* Types 2 and 3 can't occur if getcanon==FALSE. * +* The value returned is the level in the tree to return to, which can be * +* anywhere up to the closest invocation of firstpathnode. * +* * +* FUNCTIONS CALLED: isautom(),updatecan(),testcanlab(),fmperm(), * +* writeperm(),(*userautomproc)(),orbjoin(), * +* shortprune(),fmptn() * +* * +*****************************************************************************/ + +static int +processnode(int *lab, int *ptn, int level, int numcells) +{ + int i,code,save,newlevel; + boolean ispruneok; + int sr; + + code = 0; + if (eqlev_first != level && (!getcanon || comp_canon < 0)) + code = 4; + else if (numcells == n) + { + if (eqlev_first == level) + { + for (i = 0; i < n; ++i) workperm[firstlab[i]] = lab[i]; + + if (gca_first >= noncheaplevel || + (*dispatch.isautom)(g,workperm,digraph,M,n)) + code = 1; + } + if (code == 0) + { + if (getcanon) + { + sr = 0; + if (comp_canon == 0) + { + if (level < canonlevel) + comp_canon = 1; + else + { + (*dispatch.updatecan) + (g,canong,canonlab,samerows,M,n); + samerows = n; + comp_canon + = (*dispatch.testcanlab)(g,canong,lab,&sr,M,n); + } + } + if (comp_canon == 0) + { + for (i = 0; i < n; ++i) workperm[canonlab[i]] = lab[i]; + code = 2; + } + else if (comp_canon > 0) + code = 3; + else + code = 4; + } + else + code = 4; + } + } + + if (code != 0 && level > stats->maxlevel) stats->maxlevel = level; + + switch (code) + { + case 0: /* nothing unusual noticed */ + return level; + + case 1: /* lab is equivalent to firstlab */ + if (fmptr == worktop) fmptr -= 2 * M; + fmperm(workperm,fmptr,fmptr+M,M,n); + fmptr += 2 * M; + if (writeautoms) + writeperm(outfile,workperm,cartesian,linelength,n); + stats->numorbits = orbjoin(orbits,workperm,n); + ++stats->numgenerators; + OPTCALL(userautomproc)(stats->numgenerators,workperm,orbits, + stats->numorbits,stabvertex,n); + if (doschreier) addgenerator(&gp,&gens,workperm,n); + return gca_first; + + case 2: /* lab is equivalent to canonlab */ + if (fmptr == worktop) fmptr -= 2 * M; + fmperm(workperm,fmptr,fmptr+M,M,n); + fmptr += 2 * M; + save = stats->numorbits; + stats->numorbits = orbjoin(orbits,workperm,n); + if (stats->numorbits == save) + { + if (gca_canon != gca_first) needshortprune = TRUE; + return gca_canon; + } + if (writeautoms) + writeperm(outfile,workperm,cartesian,linelength,n); + ++stats->numgenerators; + OPTCALL(userautomproc)(stats->numgenerators,workperm,orbits, + stats->numorbits,stabvertex,n); + if (doschreier) addgenerator(&gp,&gens,workperm,n); + if (orbits[cosetindex] < cosetindex) + return gca_first; + if (gca_canon != gca_first) + needshortprune = TRUE; + return gca_canon; + + case 3: /* lab is better than canonlab */ + ++stats->canupdates; + for (i = 0; i < n; ++i) canonlab[i] = lab[i]; + canonlevel = eqlev_canon = gca_canon = level; + comp_canon = 0; + canoncode[level+1] = 077777; + samerows = sr; + if (getcanon && usercanonproc != NULL) + { + (*dispatch.updatecan)(g,canong,canonlab,samerows,M,n); + samerows = n; + if ((*usercanonproc)(g,canonlab,canong,stats->canupdates, + (int)canoncode[level],M,n)) + return NAUTY_ABORTED; + } + break; + + case 4: /* non-automorphism terminal node */ + ++stats->numbadleaves; + break; + } /* end of switch statement */ + + /* only cases 3 and 4 get this far: */ + if (level != noncheaplevel) + { + ispruneok = TRUE; + if (fmptr == worktop) fmptr -= 2 * M; + fmptn(lab,ptn,noncheaplevel,fmptr,fmptr+M,M,n); + fmptr += 2 * M; + } + else + ispruneok = FALSE; + + save = (allsamelevel > eqlev_canon ? allsamelevel-1 : eqlev_canon); + newlevel = (noncheaplevel <= save ? noncheaplevel-1 : save); + + if (ispruneok && newlevel != gca_first) needshortprune = TRUE; + return newlevel; + } + +/***************************************************************************** +* * +* Recover the partition nest at level 'level' and update various other * +* parameters. * +* * +* FUNCTIONS CALLED: NONE * +* * +*****************************************************************************/ + +static void +recover(int *ptn, int level) +{ + int i; + + for (i = 0; i < n; ++i) + if (ptn[i] > level) ptn[i] = NAUTY_INFINITY; + + if (level < noncheaplevel) noncheaplevel = level + 1; + if (level < eqlev_first) eqlev_first = level; + if (getcanon) + { + if (level < gca_canon) gca_canon = level; + if (level <= eqlev_canon) + { + eqlev_canon = level; + comp_canon = 0; + } + } +} + +/***************************************************************************** +* * +* Write statistics concerning an ancestor of the first leaf. * +* * +* level = its level * +* tv = the vertex fixed to get the first child = the smallest-numbered * +* vertex in the target cell * +* cellsize = the size of the target cell * +* index = the number of vertices in the target cell which were equivalent * +* to tv = the index of the stabiliser of tv in the group * +* fixing the colour partition at this level * +* * +* numorbits = the number of orbits of the group generated by all the * +* automorphisms so far discovered * +* * +* numcells = the total number of cells in the equitable partition at this * +* level * +* * +* FUNCTIONS CALLED: itos(),putstring() * +* * +*****************************************************************************/ + +static void +writemarker(int level, int tv, int index, int tcellsize, + int numorbits, int numcells) +{ + char s[30]; + +#define PUTINT(i) itos(i,s); putstring(outfile,s) +#define PUTSTR(x) putstring(outfile,x) + + PUTSTR("level "); + PUTINT(level); + PUTSTR(": "); + if (numcells != numorbits) + { + PUTINT(numcells); + PUTSTR(" cell"); + if (numcells == 1) PUTSTR("; "); + else PUTSTR("s; "); + } + PUTINT(numorbits); + PUTSTR(" orbit"); + if (numorbits == 1) PUTSTR("; "); + else PUTSTR("s; "); + PUTINT(tv+labelorg); + PUTSTR(" fixed; index "); + PUTINT(index); + if (tcellsize != index) + { + PUTSTR("/"); + PUTINT(tcellsize); + } + PUTSTR("\n"); +} + +/***************************************************************************** +* * +* nauty_check() checks that this file is compiled compatibly with the * +* given parameters. If not, call exit(1). * +* * +*****************************************************************************/ + +void +nauty_check(int wordsize, int m, int n, int version) +{ + if (wordsize != WORDSIZE) + { + fprintf(ERRFILE,"Error: WORDSIZE mismatch in nauty.c\n"); + exit(1); + } + +#if MAXN + if (m > MAXM) + { + fprintf(ERRFILE,"Error: MAXM inadequate in nauty.c\n"); + exit(1); + } + + if (n > MAXN) + { + fprintf(ERRFILE,"Error: MAXN inadequate in nauty.c\n"); + exit(1); + } +#endif + + if (version < NAUTYREQUIRED) + { + fprintf(ERRFILE,"Error: nauty.c version mismatch\n"); + exit(1); + } + +#if !HAVE_TLS + if ((version & 1)) + { + fprintf(ERRFILE, + "*** Warning: program with TLS calling nauty without TLS ***\n"); + } +#endif +} + +/***************************************************************************** +* * +* extra_autom(p,n) - add an extra automorphism, hard to do correctly * +* * +*****************************************************************************/ + +void +extra_autom(int *p, int n) +{ + if (writeautoms) + writeperm(outfile,p,cartesian,linelength,n); + stats->numorbits = orbjoin(orbits,p,n); + ++stats->numgenerators; + OPTCALL(userautomproc)(stats->numgenerators,p,orbits, + stats->numorbits,stabvertex,n); +} + +/***************************************************************************** +* * +* extra_level(level,lab,ptn,numcells,tv1,index,tcellsize,childcount) * +* creates an artificial level in the search. This is dangerous. * +* * +*****************************************************************************/ + +void +extra_level(int level, int *lab, int *ptn, int numcells, int tv1, int index, + int tcellsize, int childcount, int n) +{ + MULTIPLY(stats->grpsize1,stats->grpsize2,index); + if (domarkers) + writemarker(level,tv1,index,tcellsize,stats->numorbits,numcells); + OPTCALL(userlevelproc)(lab,ptn,level,orbits,stats,tv1,index,tcellsize, + numcells,childcount,n); +} + +/***************************************************************************** +* * +* nauty_freedyn() frees all the dynamic memory used in this module. * +* * +*****************************************************************************/ + +void +nauty_freedyn(void) +{ +#if !MAXN + tcnode *tcp,*tcq; + + tcp = tcnode0.next; + while (tcp != NULL) + { + tcq = tcp->next; + FREES(tcp->tcellptr); + FREES(tcp); + tcp = tcq; + } + alloc_m = 0; + tcnode0.next = NULL; + DYNFREE(firsttc,firsttc_sz); + DYNFREE(canoncode,canoncode_sz); + DYNFREE(firstcode,firstcode_sz); + DYNFREE(workperm,workperm_sz); + DYNFREE(canonlab,canonlab_sz); + DYNFREE(firstlab,firstlab_sz); + DYNFREE(defltwork,defltwork_sz); + DYNFREE(fixedpts,fixedpts_sz); + DYNFREE(active,active_sz); +#endif +} diff --git a/graph-checker/nauty/nauty.h b/graph-checker/nauty/nauty.h new file mode 100644 index 0000000..176eee8 --- /dev/null +++ b/graph-checker/nauty/nauty.h @@ -0,0 +1,1407 @@ +/************************************************************************** +* This is the header file for Version 2.8.6 of nauty(). +* nauty.h. Generated from nauty-h.in by configure. +**************************************************************************/ + +#ifndef _NAUTY_H_ /* only process this file once */ +#define _NAUTY_H_ + +/* The parts between the ==== lines are modified by configure when +creating nauty.h out of nauty-h.in. If configure is not being used, +it is necessary to check they are correct. +====================================================================*/ + +/* Check whether various headers or options are available */ +#define HAVE_UNISTD_H 1 /* <unistd.h> */ +#define HAVE_SYSTYPES_H 1 /* <sys/types.h> */ +#define HAVE_STDDEF_H 1 /* <stddef.h> */ +#define HAVE_STDLIB_H 1 /* <stdlib.h> */ +#define HAVE_STRING_H 1 /* <string.h> */ +#define HAVE_LIMITS_H 1 /* <limits.h> */ +#define HAVE_STDINT_H 1 /* <stdint.h> */ +#define MALLOC_DEC 1 /* 1 = malloc() is declared in stdlib.h, */ + /* 2 = in malloc.h, 0 = in neither place */ +#define HAS_MATH_INF 1 /* INFINITY is defined in math.h or */ + /* some system header likely to be used */ +#define HAVE_FLOCKFILE 1 /* Whether flockfile() is available */ +#define HAS_STDIO_UNLOCK 1 /* Whether there are getc_unlocked, */ + /* putc_unlocked,flockfile and funlockfile */ + +#define DEFAULT_WORDSIZE 0 + +/* Note that thread-local storage (TLS) is only useful for running nauty + in multiple threads and will slow it down a little otherwise. */ +#define TLS_SUPPORTED 1 /* Compiler supports thread-local */ + +/* If USE_TLS is defined, define TLS_ATTR to be the attribute name + for TLS and define HAVE_TLS=1. Otherwise define TLS_ATTR to be empty + and HAVE_TLS=0. USE_TLS can be defined on the command line or by + configuring with --enable-tls. */ +#ifndef USE_TLS + +#endif +#ifdef USE_TLS +#if !TLS_SUPPORTED + #error "TLS is requested but not available" +#else +#define TLS_ATTR _Thread_local +#define HAVE_TLS 1 +#endif +#else +#define TLS_ATTR +#define HAVE_TLS 0 +#endif + +#define USE_ANSICONTROLS 0 + /* whether --enable-ansicontrols is used */ +#define FLEX_ARRAY_OK 1 + /* whether the compiler supports flexible array members in structures */ + +#define _FILE_OFFSET_BITS 0 +#if _FILE_OFFSET_BITS == 64 +#define _LARGEFILE_SOURCE +#else +#undef _FILE_OFFSET_BITS +#endif + +/* Support of gcc extensions __builtin_clz, __builtin_clzl, __builtin_clzll */ +#ifndef HAVE_HWLZCNT +#define HAVE_HWLZCNT 1 +#endif +#define HAVE_CLZ 1 +#define HAVE_CLZL 1 +#define HAVE_CLZLL 1 + +/* Support of gcc extensions + __builtin_popcount, __builtin_popcountl, __builtin_popcountll + Note that these may only be fast if the compiler switch -mpopcnt is used. + + Also the intrinsics + _mm_popcnt_u32, _mm_popcnt_u64 + for the Intel compiler icc. These need no compiler switch. +*/ +#ifndef HAVE_HWPOPCNT +#define HAVE_HWPOPCNT 1 +#endif +#define HAVE_POPCNT 1 +#define HAVE_POPCNTL 1 +#define HAVE_POPCNTLL 1 +#define HAVE_MMPOP32 0 +#define HAVE_MMPOP64 0 + +/*==================================================================*/ + +/* The following line must be uncommented for compiling into Magma. */ +/* #define NAUTY_IN_MAGMA */ + +#ifdef NAUTY_IN_MAGMA +#include "defs.h" +#include "system.h" +#include "bs.h" +#define OLDEXTDEFS +#else +#include <stdio.h> +#define P_(x) x +#endif + +#if defined(__unix) || defined(__unix__) || defined(unix) +#define SYS_UNIX +#endif + +#define IS_ARM64 1 + +/***************************************************************************** +* * +* AUTHOR: Brendan D. McKay * +* Research School of Computer Science * +* Australian National University * +* Canberra, ACT 2601, Australia * +* phone: +61 2 6125 3845 * +* email: Brendan.McKay@anu.edu.au * +* * +* This software is subject to copyright as detailed in the file COPYRIGHT. * +* * +* Reference manual: * +* B. D. McKay and A. Piperno, nauty User's Guide (Version 2.5), * +* http://pallini.di.uniroma1.it * +* http://cs.anu.edu.au/~bdm/nauty/ * +* * +* 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 : added PC Turbo C support, making version 1.4 * +* 23-Mar-89 : changes for version 1.5 : * +* - reworked M==1 code * +* - defined NAUTYVERSION string * +* - made NAUTYH_READ to allow this file to be read twice * +* - added optional ANSI function prototypes * +* - added validity check for WORDSIZE * +* - added new fields to optionblk structure * +* - updated DEFAULTOPTIONS to add invariants fields * +* - added (set*) cast to definition of GRAPHROW * +* - added definition of ALLOCS and FREES * +* 25-Mar-89 : - added declaration of new function doref() * +* - added UNION macro * +* 29-Mar-89 : - reduced the default MAXN for small machines * +* - removed OUTOFSPACE (no longer used) * +* - added SETDIFF and XOR macros * +* 2-Apr-89 : - extended statsblk structure * +* 4-Apr-89 : - added IS_* macros * +* - added ERRFILE definition * +* - replaced statsblk.outofspace by statsblk.errstatus * +* 5-Apr-89 : - deleted definition of np2vector (no longer used) * +* - introduced EMPTYSET macro * +* 12-Apr-89 : - eliminated MARK, UNMARK and ISMARKED (no longer used) * +* 18-Apr-89 : - added MTOOBIG and CANONGNIL * +* 12-May-89 : - made ISELEM1 and ISELEMENT return 0 or 1 * +* 2-Mar-90 : - added EXTPROC macro and used it * +* 12-Mar-90 : - added SYS_CRAY, with help from N. Sloane and A. Grosky * +* - added dummy groupopts field to optionblk * +* - select some ANSI things if __STDC__ exists * +* 20-Mar-90 : - changed default MAXN for Macintosh versions * +* - created SYS_MACTHINK for Macintosh THINK compiler * +* 27-Mar-90 : - split SYS_MSDOS into SYS_PCMS4 and SYS_PCMS5 * +* 13-Oct-90 : changes for version 1.6: * +* - fix definition of setword for WORDSIZE==64 * +* 14-Oct-90 : - added SYS_APOLLO version to avoid compiler bug * +* 15-Oct-90 : - improve detection of ANSI conformance * +* 17-Oct-90 : - changed temp name in EMPTYSET to avoid A/UX bug * +* 16-Apr-91 : changes for version 1.7: * +* - made version SYS_PCTURBO use free(), not cfree() * +* 2-Sep-91 : - noted that SYS_PCMS5 also works for Quick C * +* - moved MULTIPLY to here from nauty.c * +* 12-Jun-92 : - changed the top part of this comment * +* 27-Aug-92 : - added version SYS_IBMC, thanks to Ivo Duentsch * +* 5-Jun-93 : - renamed to version 1.7+, only change in naututil.h * +* 29-Jul-93 : changes for version 1.8: * +* - fixed error in default 64-bit version of FIRSTBIT * +* (not used in any version before ALPHA) * +* - installed ALPHA version (thanks to Gordon Royle) * +* - defined ALLOCS,FREES for SYS_IBMC * +* 3-Sep-93 : - make calloc void* in ALPHA version * +* 17-Sep-93 : - renamed to version 1.9, * +* changed only dreadnaut.c and nautinv.c * +* 24-Feb-94 : changes for version 1.10: * +* - added version SYS_AMIGAAZT, thanks to Carsten Saager * +* (making 1.9+) * +* 19-Apr-95 : - added prototype wrapper for C++, * +* thanks to Daniel Huson * +* 5-Mar-96 : - added SYS_ALPHA32 version (32-bit setwords on Alpha) * +* 13-Jul-96 : changes for version 2.0: * +* - added dynamic allocation * +* - ERRFILE must be defined * +* - added FLIPELEM1 and FLIPELEMENT macros * +* 13-Aug-96 : - added SWCHUNK? macros * +* - added TAKEBIT macro * +* 28-Nov-96 : - include sys/types.h if not ANSI (tentative!) * +* 24-Jan-97 : - and stdlib.h if ANSI * +* - removed use of cfree() from UNIX variants * +* 25-Jan-97 : - changed options.getcanon from boolean to int * +* Backwards compatibility is ok, as boolean and int * +* are the same. Now getcanon=2 means to get the label * +* and not care about the group. Sometimes faster. * +* 6-Feb-97 : - Put in #undef for FALSE and TRUE to cope with * +* compilers that illegally predefine them. * +* - declared nauty_null and nautil_null * +* 2-Jul-98 : - declared ALLBITS * +* 21-Oct-98 : - allow WORDSIZE==64 using unsigned long long * +* - added BIGNAUTY option for really big graphs * +* 11-Dec-99 : - made bit, leftbit and bytecount static in each file * +* 9-Jan-00 : - declared nauty_check() and nautil_check() * +* 12-Feb-00 : - Used #error for compile-time checks * +* - Added DYNREALLOC * +* 4-Mar-00 : - declared ALLMASK(n) * +* 27-May-00 : - declared CONDYNFREE * +* 28-May-00 : - declared nautil_freedyn() * +* 16-Aug-00 : - added OLDNAUTY and changed canonical labelling * +* 16-Nov-00 : - function prototypes are now default and unavoidable * +* - removed UPROC, now assume all compilers know void * +* - removed nvector, now just int (as it always was) * +* - added extra parameter to targetcell() * +* - removed old versions which were only to skip around * +* bugs that should have been long fixed: * +* SYS_APOLLO and SYS_VAXBSD. * +* - DEFAULTOPIONS now specifies no output * +* - Removed obsolete SYS_MACLSC version * +* 21-Apr-01 : - Added code to satisfy compilation into Magma. This * +* is activated by defining NAUTY_IN_MAGMA above. * +* - The *_null routines no longer exist * +* - Default maxinvarlevel is now 1. (This has no effect * +* unless an invariant is specified.) * +* - Now labelorg has a concrete declaration in nautil.c * +* and EXTDEFS is not needed * +* 5-May-01 : - NILFUNCTION, NILSET, NILGRAPH now obsolete. Use NULL. * +* 11-Sep-01 : - setword is unsigned int in the event that UINT_MAX * +* is defined and indicates it is big enough * +* 17-Oct-01 : - major rewrite for 2.1. SYS_* variables gone! * +* Some modernity assumed, eg size_t * +* 8-Aug-02 : - removed MAKEEMPTY (use EMPTYSET instead) * +* - deleted OLDNAUTY everywhere * +* 27-Aug-02 : - converted to use autoconf. Now the original of this * +* file is nauty-h.in. Run configure to make nauty.h. * +* 20-Dec-02 : - increased INFINITY * +* some reorganization to please Magma * +* - declared nauty_freedyn() * +* 17-Nov-03 : - renamed INFINITY to NAUTY_INFINITY * +* 29-May-04 : - added definition of SETWORD_FORMAT * +* 14-Sep-04 : - extended prototypes even to recursive functions * +* 16-Oct-04 : - added DEFAULTOPTIONS_GRAPH * +* 24-Oct-04 : Starting 2.3 * +* - remove register declarations as modern compilers * +* tend to find them a nuisance * +* - Don't define the obsolete symbol INFINITY if it is * +* defined already * +* 17-Nov-04 : - make 6 counters in statsblk unsigned long * +* 17-Jan-04 : - add init() and cleanup() to dispatchvec * +* 12-Nov-05 : - Changed NAUTY_INFINITY to 2^30+2 in BIGNAUTY case * +* 22-Nov-06 : Starting 2.4 * +* - removed usertcellproc from options * +* changed bestcell to targetcell in dispatch vector * +* declare targetcell and maketargetcell * +* 29-Nov-06 : - add extraoptions to optionblk * +* - add declarations of extra_autom and extra_level * +* 10-Dec-06 : - BIGNAUTY is gone! Now permutation=shortish=int. * +* NAUTY_INFINITY only depends on whether sizeof(int)=2. * +* 27-Jun-08 : - define nauty_counter and LONG_LONG_COUNTERS * +* 30-Jun-08 : - declare version 2.4 * +* 8-Nov-09 : - final release of version 2.4; * +* 10-Nov-10 : Starting 2.5 * +* - declare shortish and permutation obsolete, now int * +* 14-Nov-10 : - SETWORDSNEEDED(n) * +* 23-May-10 : - declare densenauty() * +* 29-Jun-10 : - add PRINT_COUNTER(f,x) * +* - add DEFAULTOPTIONS_DIGRAPH() * +* 27-Mar-11 : - declare writegroupsize() * +* 14-Jan-12 : - add HAVE_TLS and TLS_ATTR * +* 21-Feb-12 : - add ENABLE_ANSI * +* 18-Mar-12 : - add COUNTER_FMT * +* 18-Aug-12 : - add ADDONEARC, ADDONEEDGE, EMPTYGRAPH * +* 29-Aug-12 : - add CLZ macros and FIRSTBITNZ * +* 19-Oct-12 : - add DEFAULT_WORDSIZE * +* 3-Jan-12 : Released 2.5rc1 * +* 18-Jan-12 : Froze 2.5 * +* 18-Jan-12 : - add NAUABORTED and NAUKILLED * +* - add nauty_kill_request * +* - add usercanonproc * +* 1-Oct-15 : - add COUNTER_FMT_RAW * +* 10-Jan-16 : - defined POPCOUNTMAC, optionally use popcnt * +* - remove SYS_CRAY, let's hope it is long obsolete * +* - add Intel popcount intrinsics for icc * +* 12-Jan-16 : - DYNFREE and CONDYNFREE now set the pointer to NULL * +* 16-Jan-16 : - Change NAUTY_INFINITY to 2 billion + 2 * +* 12-Mar-16 : - Add const to alloc_error() * +* : Froze 2.6 * +* 29-Aug-16 : - Add SWHIBIT, TAKEHIBIT and ATMOSTONEBIT * +* 10-Mar-18 : - Add SETWORD_DEC_FORMAT for decimal output * +* - Fix 64-bit SETWORD_FORMAT to use 0 padding. * +* 28-Feb-19 : - Use intrinsics for WORDSIZE=16 * +* - Macro versions of FIRSTBIT and FIRSTBITNZ are always * +* available as FIRSTBITMAC and FIRSTBITNZMAC * +* 1-Mar-19 : - Add AVOID* tests for non-POSIX header files * +* 31-Aug-19 : - Revise type size determinations to be more robust if * +* configuration wasn't done. * +* - HAVE_HWLZCNT and HAVE_HWPOPCNT can be defined at * +* compile time * +* - FIRSTBITNZ, FIRSTBIT and POPCOUNT can be defined at * +* compile time * +* 11-Oct-19 : - Move labelorg and nauty_kill_request into the * +* "C" block for C++ compatibiliy * +* 23-Mar-20 : - Don't define INFINITY even if it is absent from math.h * +* 17-Nov-20 : - Use USE_TLS for thread-local storage * +* - Define FLEX_ARRAY_OK * +* 26-Aug-21 : - Add WITHOUTHIBIT, REMOVEHIBIT * +* : Froze 2.7 * +* 9-Jan-21 : - add IS_ARM64 and change __POPCNT__ test. That * +* architecture has a vector CNT instruction that gcc * +* uses with __builtin_popcount() * +* ++++++ This file is automatically generated, don't edit it by hand! ++++++ +* * +*****************************************************************************/ + +/***************************************************************************** +* * +* 16-bit, 32-bit and 64-bit versions can be selected by defining WORDSIZE. * +* The largest graph that can be handled has MAXN vertices. * +* Both WORDSIZE and MAXN can be defined on the command line. * +* WORDSIZE must be 16, 32 or 64; MAXN must be <= NAUTY_INFINITY-2; * +* * +* With a very slight loss of efficiency (depending on platform), nauty * +* can be compiled to dynamically allocate arrays. Predefine MAXN=0 to * +* achieve this effect, which is default behaviour from version 2.0. * +* In that case, graphs of size up to NAUTY_INFINITY-2 can be handled * +* if the memory is available. * +* * +* If only very small graphs need to be processed, use MAXN<=WORDSIZE * +* since this causes substantial code optimizations. * +* * +* Conventions and Assumptions: * +* * +* A 'setword' is the chunk of memory that is occupied by one part of * +* a set. This is assumed to be >= WORDSIZE bits in size. * +* * +* The rightmost (loworder) WORDSIZE bits of setwords are numbered * +* 0..WORDSIZE-1, left to right. It is necessary that the 2^WORDSIZE * +* setwords with the other bits zero are totally ordered under <,=,>. * +* This needs care on a 1's-complement machine. * +* * +* The int variables m and n have consistent meanings throughout. * +* Graphs have n vertices always, and sets have m setwords always. * +* * +* A 'set' consists of m contiguous setwords, whose bits are numbered * +* 0,1,2,... from left (high-order) to right (low-order), using only * +* the rightmost WORDSIZE bits of each setword. It is used to * +* represent a subset of {0,1,...,n-1} in the usual way - bit number x * +* is 1 iff x is in the subset. Bits numbered n or greater, and * +* unnumbered bits, are assumed permanently zero. * +* * +* A 'graph' consists of n contiguous sets. The i-th set represents * +* the vertices adjacent to vertex i, for i = 0,1,...,n-1. * +* * +* A 'permutation' is an array of n ints repesenting a permutation of * +* the set {0,1,...,n-1}. The value of the i-th entry is the number to * +* which i is mapped. * +* * +* If g is a graph and p is a permutation, then g^p is the graph in * +* which vertex i is adjacent to vertex j iff vertex p[i] is adjacent * +* to vertex p[j] in g. * +* * +* A partition nest is represented by a pair (lab,ptn), where lab and ptn * +* are int arrays. The "partition at level x" is the partition whose * +* cells are {lab[i],lab[i+1],...,lab[j]}, where [i,j] is a maximal * +* subinterval of [0,n-1] such that ptn[k] > x for i <= k < j and * +* ptn[j] <= x. The partition at level 0 is given to nauty by the user. * +* This is refined for the root of the tree, which has level 1. * +* * +*****************************************************************************/ + +#ifndef NAUTY_IN_MAGMA +#if HAVE_SYSTYPES_H && !defined(AVOID_SYS_TYPES_H) +#include <sys/types.h> +#endif +#if HAVE_UNISTD_H && !defined(AVOID_UNISTD_H) +#include <unistd.h> +#endif +#if HAVE_STDDEF_H +#include <stddef.h> +#endif +#if HAVE_STDLIB_H +#include <stdlib.h> +#endif +#if HAVE_LIMITS_H +#include <limits.h> +#endif +#if HAVE_STDINT_H +#include <stdint.h> +#endif +#if HAVE_STRING_H +#include <string.h> +#elif !defined(AVOID_STRINGS_H) +#include <strings.h> +#endif +#endif + +/* Now we determine some sizes, relying on limits.h and + stdint.h first in case configuration was not done. + None of these tests are perfect, but sizeof() is not + allowed in preprocessor tests. The program nautest.c + will check these. */ + +#if defined(INT_MAX) +#if INT_MAX == 65535 +#define SIZEOF_INT 2 +#else +#define SIZEOF_INT 4 +#endif +#else +#define SIZEOF_INT 4 +#endif + +#if defined(LONG_MAX) +#if LONG_MAX == 2147483647L +#define SIZEOF_LONG 4 +#else +#define SIZEOF_LONG 8 +#endif +#else +#define SIZEOF_LONG 8 +#endif + +#if defined(LLONG_MAX) +#define SIZEOF_LONG_LONG 8 +#else +#define SIZEOF_LONG_LONG 8 /* 0 if nonexistent */ +#endif + +#if defined(_MSC_VER) +#define SIZEOF_INT128_T 0 +#define SIZEOF_INT128 0 +#else +#define SIZEOF_INT128_T 16 /* 0 if nonexistent */ +#define SIZEOF_INT128 16 /* 0 if nonexistent */ +#endif + +#if defined(_WIN64) +#define SIZEOF_POINTER 8 +#elif defined(_WIN32) +#define SIZEOF_POINTER 4 +#elif defined(UINTPTR_MAX) && UINTPTRMAX == 4294967295UL +#define SIZEOF_POINTER 4 +#else +#define SIZEOF_POINTER 8 +#endif + +/* WORDSIZE is the number of set elements per setword (16, 32 or 64). + WORDSIZE and setword are defined as follows: + + DEFAULT_WORDSIZE is usually 0 but is set by the configure script + to NN if --enable-wordsize=NN is used, where NN is 16, 32 or 64. + + If WORDSIZE is not defined, but DEFAULT_WORDSIZE > 0, then set + WORDSIZE to the same value as DEFAULT_WORDSIZE. + If WORDSIZE is so far undefined, use 32 unless longs have more + than 32 bits, in which case use 64. + Define setword thus: + WORDSIZE==16 : unsigned short + WORDSIZE==32 : unsigned int unless it is too small, + in which case unsigned long + WORDSIZE==64 : the first of unsigned int, unsigned long, + unsigned long long which is large enough. +*/ + +#ifdef NAUTY_IN_MAGMA +#undef WORDSIZE +#define WORDSIZE WORDBITS +#endif + +#ifndef WORDSIZE +#if DEFAULT_WORDSIZE > 0 +#define WORDSIZE DEFAULT_WORDSIZE +#endif +#endif + +#ifdef WORDSIZE + +#if (WORDSIZE != 16) && (WORDSIZE != 32) && (WORDSIZE != 64) + #error "WORDSIZE must be 16, 32 or 64" +#endif + +#else /* WORDSIZE undefined */ + +#if SIZEOF_LONG>4 +#define WORDSIZE 64 +#else +#define WORDSIZE 32 +#endif + +#endif /* WORDSIZE */ + +#ifdef NAUTY_IN_MAGMA +typedef t_uint setword; +#define SETWORD_INT /* Don't assume this is correct in Magma. */ + +#else /* NAUTY_IN_MAGMA */ + +#if WORDSIZE==16 +typedef unsigned short setword; +#define SETWORD_SHORT +#endif + +#if WORDSIZE==32 +#if SIZEOF_INT>=4 +typedef unsigned int setword; +#define SETWORD_INT +#else +typedef unsigned long setword; +#define SETWORD_LONG +#endif +#endif + +#if WORDSIZE==64 +#if SIZEOF_INT>=8 +typedef unsigned int setword; +#define SETWORD_INT +#else +#if SIZEOF_LONG>=8 +typedef unsigned long setword; +#define SETWORD_LONG +#else +typedef unsigned long long setword; +#define SETWORD_LONGLONG +#endif +#endif +#endif + +#endif /* NAUTY_IN_MAGMA else */ + +#if SIZEOF_LONG_LONG>=8 && SIZEOF_LONG==4 +typedef unsigned long long nauty_counter; +#define LONG_LONG_COUNTERS 1 +#define COUNTER_FMT "%llu" +#define COUNTER_FMT_RAW "llu" +#else +typedef unsigned long nauty_counter; +#define LONG_LONG_COUNTERS 0 +#define COUNTER_FMT "%lu" +#define COUNTER_FMT_RAW "lu" +#endif +#define PRINT_COUNTER(f,x) fprintf(f,COUNTER_FMT,x) + +#define NAUTYVERSIONID (28000+HAVE_TLS) /* 10000*version + HAVE_TLS */ +#define NAUTYREQUIRED NAUTYVERSIONID /* Minimum compatible version */ + +#if WORDSIZE==16 +#define NAUTYVERSION "2.8.6 (16 bits)" +#endif +#if WORDSIZE==32 +#define NAUTYVERSION "2.8.6 (32 bits)" +#endif +#if WORDSIZE==64 +#define NAUTYVERSION "2.8.6 (64 bits)" +#endif + +#ifndef MAXN /* maximum allowed n value; use 0 for dynamic sizing. */ +#define MAXN 0 +#define MAXM 0 +#else +#define MAXM ((MAXN+WORDSIZE-1)/WORDSIZE) /* max setwords in a set */ +#endif /* MAXN */ + +/* Starting at version 2.2, set operations work for all set sizes unless + ONE_WORD_SETS is defined. In the latter case, if MAXM=1, set ops + work only for single-setword sets. In any case, macro versions + ending with 1 work for single-setword sets and versions ending with + 0 work for all set sizes. +*/ + +#if WORDSIZE==16 +#define SETWD(pos) ((pos)>>4) /* number of setword containing bit pos */ +#define SETBT(pos) ((pos)&0xF) /* position within setword of bit pos */ +#define TIMESWORDSIZE(w) ((w)<<4) +#define SETWORDSNEEDED(n) ((((n)-1)>>4)+1) /* setwords needed for n bits */ +#endif + +#if WORDSIZE==32 +#define SETWD(pos) ((pos)>>5) +#define SETBT(pos) ((pos)&0x1F) +#define TIMESWORDSIZE(w) ((w)<<5) +#define SETWORDSNEEDED(n) ((((n)-1)>>5)+1) +#endif + +#if WORDSIZE==64 +#define SETWD(pos) ((pos)>>6) +#define SETBT(pos) ((pos)&0x3F) +#define TIMESWORDSIZE(w) ((w)<<6) /* w*WORDSIZE */ +#define SETWORDSNEEDED(n) ((((n)-1)>>6)+1) +#endif + +#ifdef NAUTY_IN_MAGMA +#define BITT bs_bit +#else +#define BITT bit +#endif + +#define ADDELEMENT1(setadd,pos) (*(setadd) |= BITT[pos]) +#define DELELEMENT1(setadd,pos) (*(setadd) &= ~BITT[pos]) +#define FLIPELEMENT1(setadd,pos) (*(setadd) ^= BITT[pos]) +#define ISELEMENT1(setadd,pos) ((*(setadd) & BITT[pos]) != 0) +#define EMPTYSET1(setadd,m) *(setadd) = 0; +#define GRAPHROW1(g,v,m) ((set*)(g)+(v)) +#define ADDONEARC1(g,v,w,m) (g)[v] |= BITT[w] +#define ADDONEEDGE1(g,v,w,m) { ADDONEARC1(g,v,w,m); ADDONEARC1(g,w,v,m); } +#define DELONEARC1(g,v,w,m) (g)[v] &= ~BITT[w] +#define DELONEEDGE1(g,v,w,m) { DELONEARC1(g,v,w,m); DELONEARC1(g,w,v,m); } +#define EMPTYGRAPH1(g,m,n) EMPTYSET0(g,n) /* really EMPTYSET0 */ + +#define ADDELEMENT0(setadd,pos) ((setadd)[SETWD(pos)] |= BITT[SETBT(pos)]) +#define DELELEMENT0(setadd,pos) ((setadd)[SETWD(pos)] &= ~BITT[SETBT(pos)]) +#define FLIPELEMENT0(setadd,pos) ((setadd)[SETWD(pos)] ^= BITT[SETBT(pos)]) +#define ISELEMENT0(setadd,pos) (((setadd)[SETWD(pos)] & BITT[SETBT(pos)]) != 0) +#define EMPTYSET0(setadd,m) \ + {setword *es; \ + for (es = (setword*)(setadd)+(m); --es >= (setword*)(setadd);) *es=0;} +#define GRAPHROW0(g,v,m) ((set*)(g) + (m)*(size_t)(v)) +#define ADDONEARC0(g,v,w,m) ADDELEMENT0(GRAPHROW0(g,v,m),w) +#define ADDONEEDGE0(g,v,w,m) { ADDONEARC0(g,v,w,m); ADDONEARC0(g,w,v,m); } +#define DELONEARC0(g,v,w,m) DELELEMENT0(GRAPHROW0(g,v,m),w) +#define DELONEEDGE0(g,v,w,m) { DELONEARC0(g,v,w,m); DELONEARC0(g,w,v,m); } +#define EMPTYGRAPH0(g,m,n) EMPTYSET0(g,(m)*(size_t)(n)) + +#if (MAXM==1) && defined(ONE_WORD_SETS) +#define ADDELEMENT ADDELEMENT1 +#define DELELEMENT DELELEMENT1 +#define FLIPELEMENT FLIPELEMENT1 +#define ISELEMENT ISELEMENT1 +#define EMPTYSET EMPTYSET1 +#define GRAPHROW GRAPHROW1 +#define ADDONEARC ADDONEARC1 +#define ADDONEEDGE ADDONEEDGE1 +#define DELONEARC DELONEARC1 +#define DELONEEDGE DELONEEDGE1 +#define EMPTYGRAPH EMPTYGRAPH1 +#else +#define ADDELEMENT ADDELEMENT0 +#define DELELEMENT DELELEMENT0 +#define FLIPELEMENT FLIPELEMENT0 +#define ISELEMENT ISELEMENT0 +#define EMPTYSET EMPTYSET0 +#define GRAPHROW GRAPHROW0 +#define ADDONEARC ADDONEARC0 +#define ADDONEEDGE ADDONEEDGE0 +#define DELONEARC DELONEARC0 +#define DELONEEDGE DELONEEDGE0 +#define EMPTYGRAPH EMPTYGRAPH0 +#endif + + +#ifdef NAUTY_IN_MAGMA +#undef EMPTYSET +#define EMPTYSET(setadd,m) {t_int _i; bsp_makeempty(setadd,m,_i);} +#endif + +#define NOTSUBSET(word1,word2) ((word1) & ~(word2)) /* test if the 1-bits + in setword word1 do not form a subset of those in word2 */ +#define INTERSECT(word1,word2) ((word1) &= (word2)) /* AND word2 into word1 */ +#define UNION(word1,word2) ((word1) |= (word2)) /* OR word2 into word1 */ +#define SETDIFF(word1,word2) ((word1) &= ~(word2)) /* - word2 into word1 */ +#define XOR(word1,word2) ((word1) ^= (word2)) /* XOR word2 into word1 */ +#define ZAPBIT(word,x) ((word) &= ~BITT[x]) /* delete bit x in setword */ +#define TAKEBIT(iw,w) {(iw) = FIRSTBITNZ(w); (w) ^= BITT[iw];} + +#define SWHIBIT(w) ((w)&(-(w))) /* Lowest order bit of unsigned type */ +#define REMOVEHIBIT(bit,w) {(bit) = SWHIBIT(w); (w) ^= (bit);} +#define ATMOSTONEBIT(w) (((w)&(-(w)))==(w)) /* True if |w| <= 1 */ +#define WITHOUTHIBIT(w) ((w)&((w)-1)) /* w without the lowest order bit */ + +#ifdef SETWORD_LONGLONG +#define MSK3232 0xFFFFFFFF00000000ULL +#define MSK1648 0xFFFF000000000000ULL +#define MSK0856 0xFF00000000000000ULL +#define MSK1632 0x0000FFFF00000000ULL +#define MSK0840 0xFF0000000000ULL +#define MSK1616 0xFFFF0000ULL +#define MSK0824 0xFF000000ULL +#define MSK0808 0xFF00ULL +#define MSK63C 0x7FFFFFFFFFFFFFFFULL +#define MSK31C 0x7FFFFFFFULL +#define MSK15C 0x7FFFULL +#define MSK64 0xFFFFFFFFFFFFFFFFULL +#define MSK32 0xFFFFFFFFULL +#define MSK16 0xFFFFULL +#define MSK8 0xFFULL +#endif + +#ifdef SETWORD_LONG +#define MSK3232 0xFFFFFFFF00000000UL +#define MSK1648 0xFFFF000000000000UL +#define MSK0856 0xFF00000000000000UL +#define MSK1632 0x0000FFFF00000000UL +#define MSK0840 0xFF0000000000UL +#define MSK1616 0xFFFF0000UL +#define MSK0824 0xFF000000UL +#define MSK0808 0xFF00UL +#define MSK63C 0x7FFFFFFFFFFFFFFFUL +#define MSK31C 0x7FFFFFFFUL +#define MSK15C 0x7FFFUL +#define MSK64 0xFFFFFFFFFFFFFFFFUL +#define MSK32 0xFFFFFFFFUL +#define MSK16 0xFFFFUL +#define MSK8 0xFFUL +#endif + +#if defined(SETWORD_INT) || defined(SETWORD_SHORT) +#define MSK3232 0xFFFFFFFF00000000U +#define MSK1648 0xFFFF000000000000U +#define MSK0856 0xFF00000000000000U +#define MSK1632 0x0000FFFF00000000U +#define MSK0840 0xFF0000000000U +#define MSK1616 0xFFFF0000U +#define MSK0824 0xFF000000U +#define MSK0808 0xFF00U +#define MSK63C 0x7FFFFFFFFFFFFFFFU +#define MSK31C 0x7FFFFFFFU +#define MSK15C 0x7FFFU +#define MSK64 0xFFFFFFFFFFFFFFFFU +#define MSK32 0xFFFFFFFFU +#define MSK16 0xFFFFU +#define MSK8 0xFFU +#endif + +#if defined(SETWORD_LONGLONG) +#define SETWORD_DEC_FORMAT "%llu" +#if WORDSIZE==16 +#define SETWORD_FORMAT "%04llx" +#endif +#if WORDSIZE==32 +#define SETWORD_FORMAT "%08llx" +#endif +#if WORDSIZE==64 +#define SETWORD_FORMAT "%016llx" +#endif +#endif + +#if defined(SETWORD_LONG) +#define SETWORD_DEC_FORMAT "%lu" +#if WORDSIZE==16 +#define SETWORD_FORMAT "%04lx" +#endif +#if WORDSIZE==32 +#define SETWORD_FORMAT "%08lx" +#endif +#if WORDSIZE==64 +#define SETWORD_FORMAT "%016lx" +#endif +#endif + +#if defined(SETWORD_INT) +#define SETWORD_DEC_FORMAT "%u" +#if WORDSIZE==16 +#define SETWORD_FORMAT "%04x" +#endif +#if WORDSIZE==32 +#define SETWORD_FORMAT "%08x" +#endif +#if WORDSIZE==64 +#define SETWORD_FORMAT "%016x" +#endif +#endif + +#if defined(SETWORD_SHORT) +#define SETWORD_DEC_FORMAT "%hu" +#if WORDSIZE==16 +#define SETWORD_FORMAT "%04hx" +#endif +#if WORDSIZE==32 +#define SETWORD_FORMAT "%08hx" +#endif +#if WORDSIZE==64 +#define SETWORD_FORMAT "%016hx" +#endif +#endif + +/* POPCOUNT(x) = number of 1-bits in a setword x + POPCOUNTMAC(x) = Macro version of POPCOUNT + FIRSTBIT(x) = number of first 1-bit in non-zero setword (0..WORDSIZE-1) + or WORDSIZE if x == 0 + FIRSTBITNZ(x) = as FIRSTBIT(x) but assumes x is not zero + BITMASK(x) = setword whose rightmost WORDSIZE-x-1 (numbered) bits + are 1 and the rest 0 (0 <= x < WORDSIZE) + (I.e., bits 0..x are unselected and the rest selected.) + Note: BITMASK(WORDSIZE) is erroneous! + ALLBITS = all (numbered) bits in a setword */ + +#if WORDSIZE==64 +#define POPCOUNTMAC(x) (bytecount[(x)>>56 & 0xFF] + bytecount[(x)>>48 & 0xFF] \ + + bytecount[(x)>>40 & 0xFF] + bytecount[(x)>>32 & 0xFF] \ + + bytecount[(x)>>24 & 0xFF] + bytecount[(x)>>16 & 0xFF] \ + + bytecount[(x)>>8 & 0xFF] + bytecount[(x) & 0xFF]) +#define FIRSTBITMAC(x) ((x) & MSK3232 ? \ + (x) & MSK1648 ? \ + (x) & MSK0856 ? \ + 0+leftbit[((x)>>56) & MSK8] : \ + 8+leftbit[(x)>>48] \ + : (x) & MSK0840 ? \ + 16+leftbit[(x)>>40] : \ + 24+leftbit[(x)>>32] \ + : (x) & MSK1616 ? \ + (x) & MSK0824 ? \ + 32+leftbit[(x)>>24] : \ + 40+leftbit[(x)>>16] \ + : (x) & MSK0808 ? \ + 48+leftbit[(x)>>8] : \ + 56+leftbit[x]) +#define BITMASK(x) (MSK63C >> (x)) +#define ALLBITS MSK64 +#define SWCHUNK0(w) ((long)((w)>>48)&0xFFFFL) +#define SWCHUNK1(w) ((long)((w)>>32)&0xFFFFL) +#define SWCHUNK2(w) ((long)((w)>>16)&0xFFFFL) +#define SWCHUNK3(w) ((long)(w)&0xFFFFL) +#endif + +#if WORDSIZE==32 +#define POPCOUNTMAC(x) (bytecount[(x)>>24 & 0xFF] + bytecount[(x)>>16 & 0xFF] \ + + bytecount[(x)>>8 & 0xFF] + bytecount[(x) & 0xFF]) +#define FIRSTBITMAC(x) ((x) & MSK1616 ? ((x) & MSK0824 ? \ + leftbit[((x)>>24) & MSK8] : 8+leftbit[(x)>>16]) \ + : ((x) & MSK0808 ? 16+leftbit[(x)>>8] : 24+leftbit[x])) +#define BITMASK(x) (MSK31C >> (x)) +#define ALLBITS MSK32 +#define SWCHUNK0(w) ((long)((w)>>16)&0xFFFFL) +#define SWCHUNK1(w) ((long)(w)&0xFFFFL) +#endif + +#if WORDSIZE==16 +#define POPCOUNTMAC(x) (bytecount[(x)>>8 & 0xFF] + bytecount[(x) & 0xFF]) +#define FIRSTBITMAC(x) ((x) & MSK0808 ? leftbit[((x)>>8) & MSK8] : 8+leftbit[x]) +#define BITMASK(x) ((setword)(MSK15C >> (x))) +#define ALLBITS ((setword)MSK16) +#define SWCHUNK0(w) ((long)(w)&0xFFFFL) +#endif + +#define FIRSTBITNZMAC FIRSTBITMAC + +/* Use clz instructions if available */ + +#ifndef FIRSTBITNZ /* Can be defined outside */ + +#ifdef NAUTY_IN_MAGMA +#define FIRSTBITNZ(x) bs_firstbit(x) + +#elif defined(_MSC_VER) +#if _MSC_VER >= 1800 +#include <intrin.h> +#else +#include <x86intrin.h> +#endif + +#if HAVE_HWLZCNT + +#if WORDSIZE==64 && defined(_WIN64) +#define FIRSTBITNZ(x) (int)_lzcnt_u64(x) +#elif WORDSIZE==32 +#define FIRSTBITNZ(x) (int)_lzcnt_u32(x) +#else +#define FIRSTBITNZ(x) (int)_lzcnt16(x) +#endif + +#else /* not HAVE_HWLZCNT */ + +#if WORDSIZE==64 && defined(_WIN64) +static int msc_bsr_64(setword x) \ + { unsigned long *p; \ + _BitScanReverse64(&p,x); return 63 - *p; } +#define FIRSTBITNZ(x) (msc_bsr_64(x)) +#elif WORDSIZE==32 +#pragma intrinsic(_BitScanReverse) +static int msc_bsr_32(setword x) \ + { unsigned *p; \ + _BitScanReverse(&p,x); return 31 - *p; } +#define FIRSTBITNZ(x) (msc_bsr_32(x)) +#elif WORDSIZE==16 +#pragma intrinsic(_BitScanReverse) +static int msc_bsr_16(setword x) \ + { unsigned long *p; \ + _BitScanReverse(&p,(unsigned long)x); return 15 - *p; } +#define FIRSTBITNZ(x) (msc_bsr_16(x)) +#endif /* WORDSIZE choice */ + +#endif /* HAVE_HWLZCNT choice */ + +#else /* Not MAGMA or WIN */ + +#if HAVE_HWLZCNT +#if defined(SETWORD_LONGLONG) && HAVE_CLZLL +#define FIRSTBITNZ(x) __builtin_clzll(x) +#elif defined(SETWORD_LONG) && HAVE_CLZL +#define FIRSTBITNZ(x) __builtin_clzl(x) +#elif defined(SETWORD_INT) && HAVE_CLZ +#define FIRSTBITNZ(x) __builtin_clz(x) +#elif defined(SETWORD_SHORT) && HAVE_CLZ +#define FIRSTBITNZ(x) (__builtin_clz((unsigned int)(x)) - 16) +#endif /* size choice */ +#endif /* HAVE_HWLZCNT */ + +#endif /* MAGMA-WIN-OTHER choice */ + +#endif /* ifndef FIRSTBITNZ */ + +/* Now fall back on macros for things not defined */ +#if defined(FIRSTBITNZ) && !defined(FIRSTBIT) +#define FIRSTBIT(x) ((x) ? FIRSTBITNZ(x) : WORDSIZE) +#else +#ifndef FIRSTBITNZ +#define FIRSTBITNZ FIRSTBITNZMAC +#endif +#ifndef FIRSTBIT +#define FIRSTBIT FIRSTBITMAC +#endif +#endif + +/* Use popcount instructions if available */ + +#ifndef POPCOUNT /* Can be defined outside */ + +#ifdef NAUTY_IN_MAGMA +#undef BITMASK +#define POPCOUNT(x) bs_popcount(x) +#define BITMASK(x) bs_bitmask(x) + +#elif defined(_MSC_VER) +#if _MSC_VER >= 1800 +#include <intrin.h> +#else +#include <x86intrin.h> +#endif +#if WORDSIZE==64 +#pragma instrinsic(_mm_popcnt_u64) +#define POPCOUNT(x) ((int)_mm_popcnt_u64(x)) +#elif WORDSIZE==32 +#pragma instrinsic(_mm_popcnt_u32) +#define POPCOUNT(x) _mm_popcnt_u32(x) +#elif WORDSIZE==16 +#pragma instrinsic(_mm_popcnt_u32) +#define POPCOUNT(x) _mm_popcnt_u32((unsigned int)(x)) +#endif + +#elif defined(__INTEL_COMPILER) +#include <nmmintrin.h> +#if WORDSIZE==64 && HAVE_MMPOP64 +#define POPCOUNT(x) ((int)_mm_popcnt_u64(x)) +#elif WORDSIZE==32 && HAVE_MMPOP32 +#define POPCOUNT(x) _mm_popcnt_u32(x) +#elif WORDSIZE==16 && HAVE_MMPOP32 +#define POPCOUNT(x) _mm_popcnt_u32((unsigned int)(x)) +#endif + +/* Note that, unlike icc, gcc will not use the POPCNT instruction + without permission, in which case it defines __POPCNT__ + except on Apple M1 hardware. */ +#elif defined(__POPCNT__) || IS_ARM64 +#if defined(SETWORD_LONGLONG) && HAVE_POPCNTLL +#define POPCOUNT(x) __builtin_popcountll(x) +#elif defined(SETWORD_LONG) && HAVE_POPCNTL +#define POPCOUNT(x) __builtin_popcountl(x) +#elif defined(SETWORD_INT) && HAVE_POPCNT +#define POPCOUNT(x) __builtin_popcount(x) +#elif defined(SETWORD_SHORT) && HAVE_POPCNT +#define POPCOUNT(x) __builtin_popcount((unsigned int)(x)) +#endif +#endif + +/* Fall-back position */ +#ifndef POPCOUNT +#define POPCOUNT POPCOUNTMAC +#endif + +#endif /* ifndef POPCOUNT */ + +#define ALLMASK(n) ((setword)((n)?~BITMASK((n)-1):0)) /* First n bits */ + +/* various constants: */ +#undef FALSE +#undef TRUE +#define FALSE 0 +#define TRUE 1 + +#if SIZEOF_INT>=4 +#define NAUTY_INFINITY 2000000002 /* Max graph size is 2 billion */ +#else +#define NAUTY_INFINITY 0x7FFF +#endif + +/* The following four types are obsolete, use int in new code. */ +typedef int shortish; +typedef shortish permutation; +typedef int nvector,np2vector; + +#if MAXN > NAUTY_INFINITY-2 + #error MAXN must be at most NAUTY_INFINITY-2 +#endif + + /* typedefs for sets, graphs, permutations, etc.: */ + +typedef int boolean; /* boolean MUST be the same as int */ + +#define UPROC void /* obsolete */ + +typedef setword set,graph; +#ifdef NAUTY_IN_MAGMA +typedef graph nauty_graph; +typedef set nauty_set; +#endif + +typedef struct +{ + double grpsize1; /* size of group is */ + int grpsize2; /* grpsize1 * 10^grpsize2 */ +#define groupsize1 grpsize1 /* for backwards compatibility */ +#define groupsize2 grpsize2 + int numorbits; /* number of orbits in group */ + int numgenerators; /* number of generators found */ + int errstatus; /* if non-zero : an error code */ +#define outofspace errstatus; /* for backwards compatibility */ + unsigned long numnodes; /* total number of nodes */ + unsigned long numbadleaves; /* number of leaves of no use */ + int maxlevel; /* maximum depth of search */ + unsigned long tctotal; /* total size of all target cells */ + unsigned long canupdates; /* number of updates of best label */ + unsigned long invapplics; /* number of applications of invarproc */ + unsigned long invsuccesses; /* number of successful uses of invarproc() */ + int invarsuclevel; /* least level where invarproc worked */ +} statsblk; + +/* codes for errstatus field (see nauty.c for more accurate descriptions): */ +/* 0 is normal - no error */ +#define NTOOBIG 1 /* n > MAXN or n > WORDSIZE*m */ +#define MTOOBIG 2 /* m > MAXM */ +#define CANONGNIL 3 /* canong = NULL, but getcanon = TRUE */ +#define NAUABORTED 4 /* nauty is terminated early under program control */ +#define NAUKILLED 5 /* nauty is terminated early by caught signal */ + +/* manipulation of real approximation to group size */ +#define MULTIPLY(s1,s2,i) if ((s1 *= i) >= 1e10) {s1 /= 1e10; s2 += 10;} + +struct optionstruct; /* incomplete definition */ + +typedef struct +{ + boolean (*isautom) /* test for automorphism */ + (graph*,int*,boolean,int,int); + int (*testcanlab) /* test for better labelling */ + (graph*,graph*,int*,int*,int,int); + void (*updatecan) /* update canonical object */ + (graph*,graph*,int*,int,int,int); + void (*refine) /* refine partition */ + (graph*,int*,int*,int,int*,int*,set*,int*,int,int); + void (*refine1) /* refine partition, MAXM==1 */ + (graph*,int*,int*,int,int*,int*,set*,int*,int,int); + boolean (*cheapautom) /* test for easy automorphism */ + (int*,int,boolean,int); + int (*targetcell) /* decide which cell to split */ + (graph*,int*,int*,int,int,boolean,int,int,int); + void (*freedyn)(void); /* free dynamic memory */ + void (*check) /* check compilation parameters */ + (int,int,int,int); + void (*init)(graph*,graph**,graph*,graph**,int*,int*,set*, + struct optionstruct*,int*,int,int); + void (*cleanup)(graph*,graph**,graph*,graph**,int*,int*, + struct optionstruct*,statsblk*,int,int); +} dispatchvec; + +typedef struct optionstruct +{ + int getcanon; /* make canong and canonlab? */ +#define LABELONLY 2 /* new value UNIMPLEMENTED */ + boolean digraph; /* multiple edges or loops? */ + boolean writeautoms; /* write automorphisms? */ + boolean writemarkers; /* write stats on pts fixed, etc.? */ + boolean defaultptn; /* set lab,ptn,active for single cell? */ + boolean cartesian; /* use cartesian rep for writing automs? */ + int linelength; /* max chars/line (excl. '\n') for output */ + FILE *outfile; /* file for output, if any */ + void (*userrefproc) /* replacement for usual refine procedure */ + (graph*,int*,int*,int,int*,int*,set*,int*,int,int); + void (*userautomproc) /* procedure called for each automorphism */ + (int,int*,int*,int,int,int); + void (*userlevelproc) /* procedure called for each level */ + (int*,int*,int,int*,statsblk*,int,int,int,int,int,int); + void (*usernodeproc) /* procedure called for each node */ + (graph*,int*,int*,int,int,int,int,int,int); + int (*usercanonproc) /* procedure called for better labellings */ + (graph*,int*,graph*,unsigned long,int,int,int); + void (*invarproc) /* procedure to compute vertex-invariant */ + (graph*,int*,int*,int,int,int,int*,int,boolean,int,int); + int tc_level; /* max level for smart target cell choosing */ + int mininvarlevel; /* min level for invariant computation */ + int maxinvarlevel; /* max level for invariant computation */ + int invararg; /* value passed to (*invarproc)() */ + dispatchvec *dispatch; /* vector of object-specific routines */ + boolean schreier; /* use random schreier method */ + void *extra_options; /* arbitrary extra options */ +#ifdef NAUTY_IN_MAGMA + boolean print_stats; /* CAYLEY specfic - GYM Sep 1990 */ + char *invarprocname; /* Magma - no longer global sjc 1994 */ + int lab_h; /* Magma - no longer global sjc 1994 */ + int ptn_h; /* Magma - no longer global sjc 1994 */ + int orbitset_h; /* Magma - no longer global sjc 1994 */ +#endif +} optionblk; + +#ifndef CONSOLWIDTH +#define CONSOLWIDTH 78 +#endif + +/* The following are obsolete. Just use NULL. */ +#define NILFUNCTION ((void(*)())NULL) /* nil pointer to user-function */ +#define NILSET ((set*)NULL) /* nil pointer to set */ +#define NILGRAPH ((graph*)NULL) /* nil pointer to graph */ + +#define DEFAULTOPTIONS_GRAPH(options) optionblk options = \ + {0,FALSE,FALSE,FALSE,TRUE,FALSE,CONSOLWIDTH, \ + NULL,NULL,NULL,NULL,NULL,NULL,NULL,100,0,1,0,&dispatch_graph,FALSE,NULL} +#define DEFAULTOPTIONS_DIGRAPH(options) optionblk options = \ + {0,TRUE,FALSE,FALSE,TRUE,FALSE,CONSOLWIDTH, \ + NULL,NULL,NULL,NULL,NULL,NULL,adjacencies,100,0,999,0,&dispatch_graph,FALSE,NULL} + +#ifndef DEFAULTOPTIONS +#define DEFAULTOPTIONS DEFAULTOPTIONS_GRAPH +#endif +#ifndef DEFAULTOPTIONS_DENSEGRAPH +#define DEFAULTOPTIONS_DENSEGRAPH DEFAULTOPTIONS_GRAPH +#endif + +#ifdef NAUTY_IN_MAGMA +#define PUTC(c,f) io_putchar(c) +#else +#ifdef IS_JAVA +extern void javastream(FILE* f,char c); +#define PUTC(c,f) javastream(f,c) +#else +#define PUTC(c,f) putc(c,f) +#endif +#endif + +/* We hope that malloc, free, realloc are declared either in <stdlib.h> + or <malloc.h>. Otherwise we will define them. We also assume that + size_t has been defined by the time we get to define malloc(). */ +#ifndef NAUTY_IN_MAGMA +#if MALLOC_DEC==2 +#include <malloc.h> +#endif +#if MALLOC_DEC==0 +extern void *malloc(size_t); +extern void *realloc(void*,size_t); +extern void free(void*); +#endif +#endif + +/* ALLOCS(x,y) should return a pointer (any pointer type) to x*y units of new + storage, not necessarily initialised. A "unit" of storage is defined by + the sizeof operator. x and y are integer values of type int or larger, + but x*y may well be too large for an int. The macro should cast to the + correct type for the call. On failure, ALLOCS(x,y) should return a NULL + pointer. FREES(p) should free storage previously allocated by ALLOCS, + where p is the value that ALLOCS returned. */ + +#ifdef NAUTY_IN_MAGMA +#define ALLOCS(x,y) mem_malloc((size_t)(x)*(size_t)(y)) +#define REALLOCS(p,x) mem_realloc(p,(size_t)(x)) +#define FREES(p) mem_free(p) +#else +#define ALLOCS(x,y) malloc((size_t)(x)*(size_t)(y)) +#define REALLOCS(p,x) realloc(p,(size_t)(x)) +#define FREES(p) free(p) +#endif + +/* The following macros are used by nauty if MAXN=0. They dynamically + allocate arrays of size dependent on m or n. For each array there + should be two static variables: + type *name; + size_t name_sz; + "name" will hold a pointer to an allocated array. "name_sz" will hold + the size of the allocated array in units of sizeof(type). DYNALLSTAT + declares both variables and initialises name_sz=0. DYNALLOC1 and + DYNALLOC2 test if there is enough space allocated, and if not free + the existing space and allocate a bigger space. The allocated space + is not initialised. + + In the case of DYNALLOC1, the space is allocated using + ALLOCS(sz,sizeof(type)). + In the case of DYNALLOC2, the space is allocated using + ALLOCS(sz1,sz2*sizeof(type)). + + DYNREALLOC is like DYNALLOC1 except that the old contents are copied + into the new space. Availability of realloc() is assumed. + + DYNFREE frees any allocated array and sets name_sz back to 0. + CONDYNFREE does the same, but only if name_sz exceeds some limit. +*/ + +#define DYNALLSTAT(type,name,name_sz) \ + static TLS_ATTR type *name; static TLS_ATTR size_t name_sz=0 +#define DYNALLOC1(type,name,name_sz,sz,msg) \ + if ((size_t)(sz) > name_sz) \ + { if (name_sz) FREES(name); name_sz = (sz); \ + if ((name=(type*)ALLOCS(sz,sizeof(type))) == NULL) {alloc_error(msg);}} +#define DYNALLOC2(type,name,name_sz,sz1,sz2,msg) \ + if ((size_t)(sz1)*(size_t)(sz2) > name_sz) \ + { if (name_sz) FREES(name); name_sz = (size_t)(sz1)*(size_t)(sz2); \ + if ((name=(type*)ALLOCS((sz1),(sz2)*sizeof(type))) == NULL) \ + {alloc_error(msg);}} +#define DYNREALLOC(type,name,name_sz,sz,msg) \ + {if ((size_t)(sz) > name_sz) \ + { if ((name = (type*)REALLOCS(name,(sz)*sizeof(type))) == NULL) \ + {alloc_error(msg);} else name_sz = (sz);}} +#define DYNFREE(name,name_sz) \ + { if (name) FREES(name); name = NULL; name_sz = 0;} +#define CONDYNFREE(name,name_sz,minsz) \ + if (name_sz > (size_t)(minsz)) {DYNFREE(name,name_sz);} + +/* File to write error messages to (used as first argument to fprintf()). */ +#define ERRFILE stderr + +/* Don't use OLDEXTDEFS, it is only still here for Magma. */ +#ifdef OLDEXTDEFS +#define EXTDEF_CLASS +#ifdef EXTDEFS +#define EXTDEF_TYPE 1 +#else +#define EXTDEF_TYPE 2 +#endif +#else +#define EXTDEF_CLASS static +#define EXTDEF_TYPE 2 +#endif + +#ifndef NAUTY_IN_MAGMA + /* Things equivalent to bit, bytecount, leftbit are defined + in bs.h for Magma. */ +#if EXTDEF_TYPE==1 +extern setword bit[]; +extern int bytecount[]; +extern int leftbit[]; + +#else + /* array giving setwords with single 1-bit */ +#if WORDSIZE==64 +#ifdef SETWORD_LONGLONG +EXTDEF_CLASS const +setword bit[] = {01000000000000000000000LL,0400000000000000000000LL, + 0200000000000000000000LL,0100000000000000000000LL, + 040000000000000000000LL,020000000000000000000LL, + 010000000000000000000LL,04000000000000000000LL, + 02000000000000000000LL,01000000000000000000LL, + 0400000000000000000LL,0200000000000000000LL, + 0100000000000000000LL,040000000000000000LL, + 020000000000000000LL,010000000000000000LL, + 04000000000000000LL,02000000000000000LL, + 01000000000000000LL,0400000000000000LL,0200000000000000LL, + 0100000000000000LL,040000000000000LL,020000000000000LL, + 010000000000000LL,04000000000000LL,02000000000000LL, + 01000000000000LL,0400000000000LL,0200000000000LL, + 0100000000000LL,040000000000LL,020000000000LL,010000000000LL, + 04000000000LL,02000000000LL,01000000000LL,0400000000LL, + 0200000000LL,0100000000LL,040000000LL,020000000LL, + 010000000LL,04000000LL,02000000LL,01000000LL,0400000LL, + 0200000LL,0100000LL,040000LL,020000LL,010000LL,04000LL, + 02000LL,01000LL,0400LL,0200LL,0100LL,040LL,020LL,010LL, + 04LL,02LL,01LL}; +#else +EXTDEF_CLASS const +setword bit[] = {01000000000000000000000,0400000000000000000000, + 0200000000000000000000,0100000000000000000000, + 040000000000000000000,020000000000000000000, + 010000000000000000000,04000000000000000000, + 02000000000000000000,01000000000000000000, + 0400000000000000000,0200000000000000000, + 0100000000000000000,040000000000000000,020000000000000000, + 010000000000000000,04000000000000000,02000000000000000, + 01000000000000000,0400000000000000,0200000000000000, + 0100000000000000,040000000000000,020000000000000, + 010000000000000,04000000000000,02000000000000, + 01000000000000,0400000000000,0200000000000,0100000000000, + 040000000000,020000000000,010000000000,04000000000, + 02000000000,01000000000,0400000000,0200000000,0100000000, + 040000000,020000000,010000000,04000000,02000000,01000000, + 0400000,0200000,0100000,040000,020000,010000,04000, + 02000,01000,0400,0200,0100,040,020,010,04,02,01}; +#endif +#endif + +#if WORDSIZE==32 +EXTDEF_CLASS const +setword bit[] = {020000000000,010000000000,04000000000,02000000000, + 01000000000,0400000000,0200000000,0100000000,040000000, + 020000000,010000000,04000000,02000000,01000000,0400000, + 0200000,0100000,040000,020000,010000,04000,02000,01000, + 0400,0200,0100,040,020,010,04,02,01}; +#endif + +#if WORDSIZE==16 +EXTDEF_CLASS const +setword bit[] = {0100000,040000,020000,010000,04000,02000,01000,0400,0200, + 0100,040,020,010,04,02,01}; +#endif + + /* array giving number of 1-bits in bytes valued 0..255: */ +EXTDEF_CLASS const +int bytecount[] = {0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4, + 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5, + 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5, + 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, + 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5, + 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, + 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, + 3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, + 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5, + 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, + 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, + 3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, + 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, + 3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, + 3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, + 4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8}; + + /* array giving position (1..7) of high-order 1-bit in byte: */ +EXTDEF_CLASS const +int leftbit[] = {8,7,6,6,5,5,5,5,4,4,4,4,4,4,4,4, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; +#endif /* EXTDEFS */ + +#endif /* not NAUTY_IN_MAGMA */ + +#define ANSIPROT 1 +#define EXTPROC(func,args) extern func args; /* obsolete */ + +/* The following is for C++ programs that read nauty.h. Compile nauty + itself using C, not C++. */ + +#ifdef __cplusplus +extern "C" { +#endif + +extern void alloc_error(const char*); +extern void breakout(int*,int*,int,int,int,set*,int); +extern boolean cheapautom(int*,int,boolean,int); +extern void doref(graph*,int*,int*,int,int*,int*,int*,set*,int*, + void(*)(graph*,int*,int*,int,int*,int*,set*,int*,int,int), + void(*)(graph*,int*,int*,int,int,int,int*,int,boolean,int,int), + int,int,int,boolean,int,int); +extern void extra_autom(int*,int); +extern void extra_level(int,int*,int*,int,int,int,int,int,int); +extern boolean isautom(graph*,int*,boolean,int,int); +extern dispatchvec dispatch_graph; +extern int itos(int,char*); +extern void fmperm(const int*,set*,set*,int,int); +extern void fmptn(const int*,const int*,int,set*,set*,int,int); +extern void longprune(set*,set*,set*,set*,int); +extern void nauty(graph*,int*,int*,set*,int*,optionblk*, + statsblk*,set*,int,int,int,graph*); +extern void maketargetcell(graph*,int*,int*,int, + set*,int*,int*,int,boolean,int, + int (*)(graph*,int*,int*,int,int,boolean,int,int,int),int,int); +extern int nextelement(const set*,int,int); +extern int orbjoin(int*,const int*,int); +extern void permset(const set*,set*,int,int*); +extern void putstring(FILE*,const char*); +extern void refine(graph*,int*,int*,int,int*,int*,set*,int*,int,int); +extern void refine1(graph*,int*,int*,int,int*,int*,set*,int*,int,int); +extern void shortprune(set*,const set*,int); +extern int targetcell(graph*,int*,int*,int,int,boolean,int,int,int); +extern int testcanlab(graph*,graph*,int*,int*,int,int); +extern void updatecan(graph*,graph*,int*,int,int,int); +extern void writeperm(FILE*,const int*,boolean,int,int); +extern void nauty_freedyn(void); +extern void nauty_check(int,int,int,int); +extern void naugraph_check(int,int,int,int); +extern void nautil_check(int,int,int,int); +extern void nautil_freedyn(void); +extern void naugraph_freedyn(void); +extern void densenauty(graph*,int*,int*,int*, + optionblk*,statsblk*,int,int,graph*); +extern void writegroupsize(FILE*,double,int); + +extern int labelorg; /* Declared in nautil.c */ +extern volatile int nauty_kill_request; /* Also declared in nautil.c */ + +#ifdef __cplusplus +} +#endif + +/* ++++++ This file is automatically generated, don't edit it by hand! ++++++ */ + + +#endif /* _NAUTY_H_ */ diff --git a/graph-checker/nauty/nautycliquer.c b/graph-checker/nauty/nautycliquer.c new file mode 100644 index 0000000..9c8d531 --- /dev/null +++ b/graph-checker/nauty/nautycliquer.c @@ -0,0 +1,2665 @@ +/* This file is a concatenation of the files cliquer.c, graph.c + and reorder.c from cliquer-1.21 except the routines for + reading and writing dimacs files. + + Also some timing code is commented out because it is not used + by nauty and causes portability problem on non-Unix systems + (thanks to Isuru Fernando). + + Some procedures which call cliquer with nauty-format graph + are added. Apart from removing DIMACS, the cliquer procedures + have not been changed except to include nautycliquer.h in + place of the previously included files. + + cliquer was kindly provided by Sampo Nisjkanen and Patric Ostergard. + + Brendan McKay. Aug 27, 2016 +*/ + +#include "nautycliquer.h" + +/* + * This file contains the clique searching routines. + * + * Copyright (C) 2002 Sampo Niskanen, Patric Östergård. + * Licensed under the GNU GPL, read the file LICENSE for details. + * This version covered by nauty&Traces licence, see file COPYRIGHT. + */ + +/* +#include <stdio.h> +#include <stdlib.h> +#include <limits.h> +#include <unistd.h> +#include <sys/time.h> +#include <sys/times.h> + +#include "cliquer.h" +*/ + + +/* Default cliquer options; no TLS_ATTR on this one */ +static clique_options clique_default_options_struct = { + reorder_by_default, NULL, clique_print_time, NULL, NULL, NULL, NULL, 0 +}; +clique_options *clique_default_options=&clique_default_options_struct; + + +/* Calculate d/q, rounding result upwards/downwards. */ +#define DIV_UP(d,q) (((d)+(q)-1)/(q)) +#define DIV_DOWN(d,q) ((int)((d)/(q))) + + +/* Global variables used: */ +/* These must be saved and restored in re-entrance. */ +static TLS_ATTR int *clique_size; /* c[i] == max. clique size in {0,1,...,i-1} */ +static TLS_ATTR set_t current_clique; /* Current clique being searched. */ +static TLS_ATTR set_t best_clique; /* Largest/heaviest clique found so far. */ +#if 0 +static struct tms cputimer; /* Timer for opts->time_function() */ +static struct timeval realtimer; /* Timer for opts->time_function() */ +#endif +static TLS_ATTR int clique_list_count=0; /* No. of cliques in opts->clique_list[] */ +static TLS_ATTR int weight_multiplier=1; /* Weights multiplied by this when passing + * to time_function(). */ + +/* List cache (contains memory blocks of size g->n * sizeof(int)) */ +static TLS_ATTR int **temp_list=NULL; +static TLS_ATTR int temp_count=0; + + +/* + * Macros for re-entrance. ENTRANCE_SAVE() must be called immediately + * after variable definitions, ENTRANCE_RESTORE() restores global + * variables to original values. entrance_level should be increased + * and decreased accordingly. + */ +static int entrance_level=0; /* How many levels for entrance have occurred? */ + +#define ENTRANCE_SAVE() \ +int *old_clique_size = clique_size; \ +set_t old_current_clique = current_clique; \ +set_t old_best_clique = best_clique; \ +int old_clique_list_count = clique_list_count; \ +int old_weight_multiplier = weight_multiplier; \ +int **old_temp_list = temp_list; \ +int old_temp_count = temp_count; +/* +struct tms old_cputimer; \ +struct timeval old_realtimer; \ +memcpy(&old_cputimer,&cputimer,sizeof(struct tms)); \ +memcpy(&old_realtimer,&realtimer,sizeof(struct timeval)) +*/ + +#define ENTRANCE_RESTORE() \ +clique_size = old_clique_size; \ +current_clique = old_current_clique; \ +best_clique = old_best_clique; \ +clique_list_count = old_clique_list_count; \ +weight_multiplier = old_weight_multiplier; \ +temp_list = old_temp_list; +/* +temp_count = old_temp_count; \ +memcpy(&cputimer,&old_cputimer,sizeof(struct tms)); \ +memcpy(&realtimer,&old_realtimer,sizeof(struct timeval)); +temp_count = old_temp_count; +*/ + + +/* Number of clock ticks per second (as returned by sysconf(_SC_CLK_TCK)) */ +static int clocks_per_sec=0; + + + +/* Recursion and helper functions */ +static boolean sub_unweighted_single(int *table, int size, int min_size, + graph_t *g); +static int sub_unweighted_all(int *table, int size, int min_size, int max_size, + boolean maximal, graph_t *g, + clique_options *opts); +static int sub_weighted_all(int *table, int size, int weight, + int current_weight, int prune_low, int prune_high, + int min_weight, int max_weight, boolean maximal, + graph_t *g, clique_options *opts); + + +static boolean store_clique(set_t clique, graph_t *g, clique_options *opts); +static boolean is_maximal(set_t clique, graph_t *g); +static boolean false_function(set_t clique,graph_t *g,clique_options *opts); + +/***** Unweighted searches *****/ +/* + * Unweighted searches are done separately from weighted searches because + * some effective pruning methods can be used when the vertex weights + * are all 1. Single and all clique finding routines are separated, + * because the single clique finding routine can store the found clique + * while it is returning from the recursion, thus requiring no implicit + * storing of the current clique. When searching for all cliques the + * current clique must be stored. + */ + + +/* + * unweighted_clique_search_single() + * + * Searches for a single clique of size min_size. Stores maximum clique + * sizes into clique_size[]. + * + * table - the order of the vertices in g to use + * min_size - minimum size of clique to search for. If min_size==0, + * searches for a maximum clique. + * g - the graph + * opts - time printing options + * + * opts->time_function is called after each base-level recursion, if + * non-NULL. (NOT IN THIS VERSION) + * + * Returns the size of the clique found, or 0 if min_size>0 and a clique + * of that size was not found (or if time_function aborted the search). + * The largest clique found is stored in current_clique. + * + * Note: Does NOT use opts->user_function of opts->clique_list. + */ +static int unweighted_clique_search_single(int *table, int min_size, + graph_t *g, clique_options *opts) { +#if 0 + struct tms tms; + struct timeval timeval; +#endif + int i,j; + int v,w; + int *newtable; + int newsize; + + v=table[0]; + clique_size[v]=1; + set_empty(current_clique); + SET_ADD_ELEMENT(current_clique,v); + if (min_size==1) + return 1; + + if (temp_count) { + temp_count--; + newtable=temp_list[temp_count]; + } else { + newtable=malloc(g->n * sizeof(int)); + } + for (i=1; i < g->n; i++) { + w=v; + v=table[i]; + + newsize=0; + for (j=0; j<i; j++) { + if (GRAPH_IS_EDGE(g, v, table[j])) { + newtable[newsize]=table[j]; + newsize++; + } + } + + if (sub_unweighted_single(newtable,newsize,clique_size[w],g)) { + SET_ADD_ELEMENT(current_clique,v); + clique_size[v]=clique_size[w]+1; + } else { + clique_size[v]=clique_size[w]; + } + +#if 0 + if (opts && opts->time_function) { + gettimeofday(&timeval,NULL); + times(&tms); + if (!opts->time_function(entrance_level, + i+1,g->n,clique_size[v] * + weight_multiplier, + (double)(tms.tms_utime- + cputimer.tms_utime)/ + clocks_per_sec, + timeval.tv_sec- + realtimer.tv_sec+ + (double)(timeval.tv_usec- + realtimer.tv_usec)/ + 1000000,opts)) { + temp_list[temp_count++]=newtable; + return 0; + } + } +#endif + + if (min_size) { + if (clique_size[v]>=min_size) { + temp_list[temp_count++]=newtable; + return clique_size[v]; + } + if (clique_size[v]+g->n-i-1 < min_size) { + temp_list[temp_count++]=newtable; + return 0; + } + } + } + + temp_list[temp_count++]=newtable; + + if (min_size) + return 0; + return clique_size[v]; +} + +/* + * sub_unweighted_single() + * + * Recursion function for searching for a single clique of size min_size. + * + * table - subset of the vertices in graph + * size - size of table + * min_size - size of clique to look for within the subgraph + * (decreased with every recursion) + * g - the graph + * + * Returns TRUE if a clique of size min_size is found, FALSE otherwise. + * If a clique of size min_size is found, it is stored in current_clique. + * + * clique_size[] for all values in table must be defined and correct, + * otherwise inaccurate results may occur. + */ +static boolean sub_unweighted_single(int *table, int size, int min_size, + graph_t *g) { + int i; + int v; + int *newtable; + int *p1, *p2; + + /* Zero or one vertices needed anymore. */ + if (min_size <= 1) { + if (size>0 && min_size==1) { + set_empty(current_clique); + SET_ADD_ELEMENT(current_clique,table[0]); + return TRUE; + } + if (min_size==0) { + set_empty(current_clique); + return TRUE; + } + return FALSE; + } + if (size < min_size) + return FALSE; + + /* Dynamic memory allocation with cache */ + if (temp_count) { + temp_count--; + newtable=temp_list[temp_count]; + } else { + newtable=malloc(g->n * sizeof(int)); + } + + for (i = size-1; i >= 0; i--) { + v = table[i]; + + if (clique_size[v] < min_size) + break; + /* This is faster when compiling with gcc than placing + * this in the for-loop condition. */ + if (i+1 < min_size) + break; + + /* Very ugly code, but works faster than "for (i=...)" */ + p1 = newtable; + for (p2=table; p2 < table+i; p2++) { + int w = *p2; + if (GRAPH_IS_EDGE(g, v, w)) { + *p1 = w; + p1++; + } + } + + /* Avoid unneccessary loops (next size == p1-newtable) */ + if (p1-newtable < min_size-1) + continue; + /* Now p1-newtable >= min_size-1 >= 2-1 == 1, so we can use + * p1-newtable-1 safely. */ + if (clique_size[newtable[p1-newtable-1]] < min_size-1) + continue; + + if (sub_unweighted_single(newtable,p1-newtable, + min_size-1,g)) { + /* Clique found. */ + SET_ADD_ELEMENT(current_clique,v); + temp_list[temp_count++]=newtable; + return TRUE; + } + } + temp_list[temp_count++]=newtable; + return FALSE; +} + + +/* + * unweighted_clique_search_all() + * + * Searches for all cliques with size at least min_size and at most + * max_size. Stores the cliques as opts declares. + * + * table - the order of the vertices in g to search + * start - first index where the subgraph table[0], ..., table[start] + * might include a requested kind of clique + * min_size - minimum size of clique to search for. min_size > 0 ! + * max_size - maximum size of clique to search for. If no upper limit + * is desired, use eg. INT_MAX + * maximal - requires cliques to be maximal + * g - the graph + * opts - time printing and clique storage options + * + * Cliques found are stored as defined by opts->user_function and + * opts->clique_list. opts->time_function is called after each + * base-level recursion, if non-NULL. + * + * clique_size[] must be defined and correct for all values of + * table[0], ..., table[start-1]. + * + * Returns the number of cliques stored (not neccessarily number of cliques + * in graph, if user/time_function aborts). + */ +static int unweighted_clique_search_all(int *table, int start, + int min_size, int max_size, + boolean maximal, graph_t *g, + clique_options *opts) { +#if 0 + struct timeval timeval; + struct tms tms; +#endif + int i,j; + int v; + int *newtable; + int newsize; + int count=0; + + if (temp_count) { + temp_count--; + newtable=temp_list[temp_count]; + } else { + newtable=malloc(g->n * sizeof(int)); + } + + clique_list_count=0; + set_empty(current_clique); + for (i=start; i < g->n; i++) { + v=table[i]; + clique_size[v]=min_size; /* Do not prune here. */ + + newsize=0; + for (j=0; j<i; j++) { + if (GRAPH_IS_EDGE(g,v,table[j])) { + newtable[newsize]=table[j]; + newsize++; + } + } + + SET_ADD_ELEMENT(current_clique,v); + j=sub_unweighted_all(newtable,newsize,min_size-1,max_size-1, + maximal,g,opts); + SET_DEL_ELEMENT(current_clique,v); + if (j<0) { + /* Abort. */ + count-=j; + break; + } + count+=j; + +#if 0 + if (opts->time_function) { + gettimeofday(&timeval,NULL); + times(&tms); + if (!opts->time_function(entrance_level, + i+1,g->n,min_size * + weight_multiplier, + (double)(tms.tms_utime- + cputimer.tms_utime)/ + clocks_per_sec, + timeval.tv_sec- + realtimer.tv_sec+ + (double)(timeval.tv_usec- + realtimer.tv_usec)/ + 1000000,opts)) { + /* Abort. */ + break; + } + } +#endif + } + temp_list[temp_count++]=newtable; + return count; +} + +/* + * sub_unweighted_all() + * + * Recursion function for searching for all cliques of given size. + * + * table - subset of vertices of graph g + * size - size of table + * min_size - minimum size of cliques to search for (decreased with + * every recursion) + * max_size - maximum size of cliques to search for (decreased with + * every recursion). If no upper limit is desired, use + * eg. INT_MAX + * maximal - require cliques to be maximal (passed through) + * g - the graph + * opts - storage options + * + * All cliques of suitable size found are stored according to opts. + * + * Returns the number of cliques found. If user_function returns FALSE, + * then the number of cliques is returned negative. + * + * Uses current_clique to store the currently-being-searched clique. + * clique_size[] for all values in table must be defined and correct, + * otherwise inaccurate results may occur. + */ +static int sub_unweighted_all(int *table, int size, int min_size, int max_size, + boolean maximal, graph_t *g, + clique_options *opts) { + int i; + int v; + int n; + int *newtable; + int *p1, *p2; + int count=0; /* Amount of cliques found */ + + if (min_size <= 0) { + if ((!maximal) || is_maximal(current_clique,g)) { + /* We've found one. Store it. */ + count++; + if (!store_clique(current_clique,g,opts)) { + return -count; + } + } + if (max_size <= 0) { + /* If we add another element, size will be too big. */ + return count; + } + } + + if (size < min_size) { + return count; + } + + /* Dynamic memory allocation with cache */ + if (temp_count) { + temp_count--; + newtable=temp_list[temp_count]; + } else { + newtable=malloc(g->n * sizeof(int)); + } + + for (i=size-1; i>=0; i--) { + v = table[i]; + if (clique_size[v] < min_size) { + break; + } + if (i+1 < min_size) { + break; + } + + /* Very ugly code, but works faster than "for (i=...)" */ + p1 = newtable; + for (p2=table; p2 < table+i; p2++) { + int w = *p2; + if (GRAPH_IS_EDGE(g, v, w)) { + *p1 = w; + p1++; + } + } + + /* Avoid unneccessary loops (next size == p1-newtable) */ + if (p1-newtable < min_size-1) { + continue; + } + + SET_ADD_ELEMENT(current_clique,v); + n=sub_unweighted_all(newtable,p1-newtable, + min_size-1,max_size-1,maximal,g,opts); + SET_DEL_ELEMENT(current_clique,v); + if (n < 0) { + /* Abort. */ + count -= n; + count = -count; + break; + } + count+=n; + } + temp_list[temp_count++]=newtable; + return count; +} + + + + +/***** Weighted clique searches *****/ +/* + * Weighted clique searches can use the same recursive routine, because + * in both cases (single/all) they have to search through all potential + * permutations searching for heavier cliques. + */ + + +/* + * weighted_clique_search_single() + * + * Searches for a single clique of weight at least min_weight, and at + * most max_weight. Stores maximum clique sizes into clique_size[] + * (or min_weight-1, whichever is smaller). + * + * table - the order of the vertices in g to use + * min_weight - minimum weight of clique to search for. If min_weight==0, + * then searches for a maximum weight clique + * max_weight - maximum weight of clique to search for. If no upper limit + * is desired, use eg. INT_MAX + * g - the graph + * opts - time printing options + * + * opts->time_function is called after each base-level recursion, if + * non-NULL. + * + * Returns 0 if a clique of requested weight was not found (also if + * time_function requested an abort), otherwise returns >= 1. + * If min_weight==0 (search for maximum-weight clique), then the return + * value is the weight of the clique found. The found clique is stored + * in best_clique. + * + * Note: Does NOT use opts->user_function of opts->clique_list. + */ +static int weighted_clique_search_single(int *table, int min_weight, + int max_weight, graph_t *g, + clique_options *opts) { +#if 0 + struct timeval timeval; + struct tms tms; +#endif + int i,j; + int v; + int *newtable; + int newsize; + int newweight; + int search_weight; + int min_w; + clique_options localopts; + + if (min_weight==0) + min_w=INT_MAX; + else + min_w=min_weight; + + + if (min_weight==1) { + /* min_weight==1 may cause trouble in the routine, and + * it's trivial to check as it's own case. + * We write nothing to clique_size[]. */ + for (i=0; i < g->n; i++) { + if (g->weights[table[i]] <= max_weight) { + set_empty(best_clique); + SET_ADD_ELEMENT(best_clique,table[i]); + return g->weights[table[i]]; + } + } + return 0; + } + + localopts.time_function=NULL; + localopts.reorder_function=NULL; + localopts.reorder_map=NULL; + localopts.user_function=false_function; + localopts.user_data=NULL; + localopts.clique_list=&best_clique; + localopts.clique_list_length=1; + clique_list_count=0; + + v=table[0]; + set_empty(best_clique); + SET_ADD_ELEMENT(best_clique,v); + search_weight=g->weights[v]; + if (min_weight && (search_weight >= min_weight)) { + if (search_weight <= max_weight) { + /* Found suitable clique. */ + return search_weight; + } + search_weight=min_weight-1; + } + clique_size[v]=search_weight; + set_empty(current_clique); + + if (temp_count) { + temp_count--; + newtable=temp_list[temp_count]; + } else { + newtable=malloc(g->n * sizeof(int)); + } + + for (i = 1; i < g->n; i++) { + v=table[i]; + + newsize=0; + newweight=0; + for (j=0; j<i; j++) { + if (GRAPH_IS_EDGE(g,v,table[j])) { + newweight += g->weights[table[j]]; + newtable[newsize]=table[j]; + newsize++; + } + } + + + SET_ADD_ELEMENT(current_clique,v); + search_weight=sub_weighted_all(newtable,newsize,newweight, + g->weights[v],search_weight, + clique_size[table[i-1]] + + g->weights[v], + min_w,max_weight,FALSE, + g,&localopts); + SET_DEL_ELEMENT(current_clique,v); + if (search_weight < 0) { + break; + } + + clique_size[v]=search_weight; + +#if 0 + if (opts->time_function) { + gettimeofday(&timeval,NULL); + times(&tms); + if (!opts->time_function(entrance_level, + i+1,g->n,clique_size[v] * + weight_multiplier, + (double)(tms.tms_utime- + cputimer.tms_utime)/ + clocks_per_sec, + timeval.tv_sec- + realtimer.tv_sec+ + (double)(timeval.tv_usec- + realtimer.tv_usec)/ + 1000000,opts)) { + set_free(current_clique); + current_clique=NULL; + break; + } + } +#endif + } + temp_list[temp_count++]=newtable; + if (min_weight && (search_weight > 0)) { + /* Requested clique has not been found. */ + return 0; + } + return clique_size[table[i-1]]; +} + + +/* + * weighted_clique_search_all() + * + * Searches for all cliques with weight at least min_weight and at most + * max_weight. Stores the cliques as opts declares. + * + * table - the order of the vertices in g to search + * start - first index where the subgraph table[0], ..., table[start] + * might include a requested kind of clique + * min_weight - minimum weight of clique to search for. min_weight > 0 ! + * max_weight - maximum weight of clique to search for. If no upper limit + * is desired, use eg. INT_MAX + * maximal - search only for maximal cliques + * g - the graph + * opts - time printing and clique storage options + * + * Cliques found are stored as defined by opts->user_function and + * opts->clique_list. opts->time_function is called after each + * base-level recursion, if non-NULL. + * + * clique_size[] must be defined and correct for all values of + * table[0], ..., table[start-1]. + * + * Returns the number of cliques stored (not neccessarily number of cliques + * in graph, if user/time_function aborts). + */ +static int weighted_clique_search_all(int *table, int start, + int min_weight, int max_weight, + boolean maximal, graph_t *g, + clique_options *opts) { +#if 0 + struct timeval timeval; + struct tms tms; +#endif + int i,j; + int v; + int *newtable; + int newsize; + int newweight; + + if (temp_count) { + temp_count--; + newtable=temp_list[temp_count]; + } else { + newtable=malloc(g->n * sizeof(int)); + } + + clique_list_count=0; + set_empty(current_clique); + for (i=start; i < g->n; i++) { + v=table[i]; + clique_size[v]=min_weight; /* Do not prune here. */ + + newsize=0; + newweight=0; + for (j=0; j<i; j++) { + if (GRAPH_IS_EDGE(g,v,table[j])) { + newtable[newsize]=table[j]; + newweight+=g->weights[table[j]]; + newsize++; + } + } + + SET_ADD_ELEMENT(current_clique,v); + j=sub_weighted_all(newtable,newsize,newweight, + g->weights[v],min_weight-1,INT_MAX, + min_weight,max_weight,maximal,g,opts); + SET_DEL_ELEMENT(current_clique,v); + + if (j<0) { + /* Abort. */ + break; + } + +#if 0 + if (opts->time_function) { + gettimeofday(&timeval,NULL); + times(&tms); + if (!opts->time_function(entrance_level, + i+1,g->n,clique_size[v] * + weight_multiplier, + (double)(tms.tms_utime- + cputimer.tms_utime)/ + clocks_per_sec, + timeval.tv_sec- + realtimer.tv_sec+ + (double)(timeval.tv_usec- + realtimer.tv_usec)/ + 1000000,opts)) { + set_free(current_clique); + current_clique=NULL; + break; + } + } +#endif + } + temp_list[temp_count++]=newtable; + + return clique_list_count; +} + +/* + * sub_weighted_all() + * + * Recursion function for searching for all cliques of given weight. + * + * table - subset of vertices of graph g + * size - size of table + * weight - total weight of vertices in table + * current_weight - weight of clique found so far + * prune_low - ignore all cliques with weight less or equal to this value + * (often heaviest clique found so far) (passed through) + * prune_high - maximum weight possible for clique in this subgraph + * (passed through) + * min_size - minimum weight of cliques to search for (passed through) + * Must be greater than 0. + * max_size - maximum weight of cliques to search for (passed through) + * If no upper limit is desired, use eg. INT_MAX + * maximal - search only for maximal cliques + * g - the graph + * opts - storage options + * + * All cliques of suitable weight found are stored according to opts. + * + * Returns weight of heaviest clique found (prune_low if a heavier clique + * hasn't been found); if a clique with weight at least min_size is found + * then min_size-1 is returned. If clique storage failed, -1 is returned. + * + * The largest clique found smaller than max_weight is stored in + * best_clique, if non-NULL. + * + * Uses current_clique to store the currently-being-searched clique. + * clique_size[] for all values in table must be defined and correct, + * otherwise inaccurate results may occur. + * + * To search for a single maximum clique, use min_weight==max_weight==INT_MAX, + * with best_clique non-NULL. To search for a single given-weight clique, + * use opts->clique_list and opts->user_function=false_function. When + * searching for all cliques, min_weight should be given the minimum weight + * desired. + */ +static int sub_weighted_all(int *table, int size, int weight, + int current_weight, int prune_low, int prune_high, + int min_weight, int max_weight, boolean maximal, + graph_t *g, clique_options *opts) { + int i; + int v,w; + int *newtable; + int *p1, *p2; + int newweight; + + if (current_weight >= min_weight) { + if ((current_weight <= max_weight) && + ((!maximal) || is_maximal(current_clique,g))) { + /* We've found one. Store it. */ + if (!store_clique(current_clique,g,opts)) { + return -1; + } + } + if (current_weight >= max_weight) { + /* Clique too heavy. */ + return min_weight-1; + } + } + if (size <= 0) { + /* current_weight < min_weight, prune_low < min_weight, + * so return value is always < min_weight. */ + if (current_weight>prune_low) { + if (best_clique) + set_copy(best_clique,current_clique); + if (current_weight < min_weight) + return current_weight; + else + return min_weight-1; + } else { + return prune_low; + } + } + + /* Dynamic memory allocation with cache */ + if (temp_count) { + temp_count--; + newtable=temp_list[temp_count]; + } else { + newtable=malloc(g->n * sizeof(int)); + } + + for (i = size-1; i >= 0; i--) { + v = table[i]; + if (current_weight+clique_size[v] <= prune_low) { + /* Dealing with subset without heavy enough clique. */ + break; + } + if (current_weight+weight <= prune_low) { + /* Even if all elements are added, won't do. */ + break; + } + + /* Very ugly code, but works faster than "for (i=...)" */ + p1 = newtable; + newweight = 0; + for (p2=table; p2 < table+i; p2++) { + w = *p2; + if (GRAPH_IS_EDGE(g, v, w)) { + *p1 = w; + newweight += g->weights[w]; + p1++; + } + } + + w=g->weights[v]; + weight-=w; + /* Avoid a few unneccessary loops */ + if (current_weight+w+newweight <= prune_low) { + continue; + } + + SET_ADD_ELEMENT(current_clique,v); + prune_low=sub_weighted_all(newtable,p1-newtable, + newweight, + current_weight+w, + prune_low,prune_high, + min_weight,max_weight,maximal, + g,opts); + SET_DEL_ELEMENT(current_clique,v); + if ((prune_low<0) || (prune_low>=prune_high)) { + /* Impossible to find larger clique. */ + break; + } + } + temp_list[temp_count++]=newtable; + return prune_low; +} + + + + +/***** Helper functions *****/ + + +/* + * store_clique() + * + * Stores a clique according to given user options. + * + * clique - the clique to store + * opts - storage options + * + * Returns FALSE if opts->user_function() returned FALSE; otherwise + * returns TRUE. + */ +static boolean store_clique(set_t clique, graph_t *g, clique_options *opts) { + + clique_list_count++; + + /* clique_list[] */ + if (opts->clique_list) { + /* + * This has been a major source of bugs: + * Has clique_list_count been set to 0 before calling + * the recursions? + */ + if (clique_list_count <= 0) { + fprintf(stderr,"CLIQUER INTERNAL ERROR: " + "clique_list_count has negative value!\n"); + fprintf(stderr,"Please report as a bug.\n"); + abort(); + } + if (clique_list_count <= opts->clique_list_length) + opts->clique_list[clique_list_count-1] = + set_duplicate(clique); + } + + /* user_function() */ + if (opts->user_function) { + if (!opts->user_function(clique,g,opts)) { + /* User function requested abort. */ + return FALSE; + } + } + + return TRUE; +} + +/* + * maximalize_clique() + * + * Adds greedily all possible vertices in g to set s to make it a maximal + * clique. + * + * s - clique of vertices to make maximal + * g - graph + * + * Note: Not very optimized (uses a simple O(n^2) routine), but is called + * at maximum once per clique_xxx() call, so it shouldn't matter. + */ +static void maximalize_clique(set_t s,graph_t *g) { + int i,j; + boolean add; + + for (i=0; i < g->n; i++) { + add=TRUE; + for (j=0; j < g->n; j++) { + if (SET_CONTAINS_FAST(s,j) && !GRAPH_IS_EDGE(g,i,j)) { + add=FALSE; + break; + } + } + if (add) { + SET_ADD_ELEMENT(s,i); + } + } + return; +} + + +/* + * is_maximal() + * + * Check whether a clique is maximal or not. + * + * clique - set of vertices in clique + * g - graph + * + * Returns TRUE is clique is a maximal clique of g, otherwise FALSE. + */ +static boolean is_maximal(set_t clique, graph_t *g) { + int i,j; + int *table; + int len; + boolean addable; + + if (temp_count) { + temp_count--; + table=temp_list[temp_count]; + } else { + table=malloc(g->n * sizeof(int)); + } + + len=0; + for (i=0; i < g->n; i++) + if (SET_CONTAINS_FAST(clique,i)) + table[len++]=i; + + for (i=0; i < g->n; i++) { + addable=TRUE; + for (j=0; j<len; j++) { + if (!GRAPH_IS_EDGE(g,i,table[j])) { + addable=FALSE; + break; + } + } + if (addable) { + temp_list[temp_count++]=table; + return FALSE; + } + } + temp_list[temp_count++]=table; + return TRUE; +} + + +/* + * false_function() + * + * Returns FALSE. Can be used as user_function. + */ +static boolean false_function(set_t clique,graph_t *g,clique_options *opts) { + return FALSE; +} + + + + +/***** API-functions *****/ + +/* + * clique_unweighted_max_weight() + * + * Returns the size of the maximum (sized) clique in g (or 0 if search + * was aborted). + * + * g - the graph + * opts - time printing options + * + * Note: As we don't have an algorithm faster than actually finding + * a maximum clique, we use clique_unweighted_find_single(). + * This incurs only very small overhead. + */ +int clique_unweighted_max_weight(graph_t *g, clique_options *opts) { + set_t s; + int size; + + ASSERT((sizeof(setelement)*8)==ELEMENTSIZE); + ASSERT(g!=NULL); + + s=clique_unweighted_find_single(g,0,0,FALSE,opts); + if (s==NULL) { + /* Search was aborted. */ + return 0; + } + size=set_size(s); + set_free(s); + return size; +} + + +/* + * clique_unweighted_find_single() + * + * Returns a clique with size at least min_size and at most max_size. + * + * g - the graph + * min_size - minimum size of clique to search for. If min_size==0, + * searches for maximum clique. + * max_size - maximum size of clique to search for. If max_size==0, no + * upper limit is used. If min_size==0, this must also be 0. + * maximal - require returned clique to be maximal + * opts - time printing options + * + * Returns the set of vertices forming the clique, or NULL if a clique + * of requested size/maximality does not exist in the graph (or if + * opts->time_function() requests abort). + * + * The returned clique is newly allocated and can be freed by set_free(). + * + * Note: Does NOT use opts->user_function() or opts->clique_list[]. + */ +set_t clique_unweighted_find_single(graph_t *g,int min_size,int max_size, + boolean maximal, clique_options *opts) { + int i; + int *table; + set_t s; + + ENTRANCE_SAVE(); + entrance_level++; + + if (opts==NULL) + opts=clique_default_options; + + ASSERT((sizeof(setelement)*8)==ELEMENTSIZE); + ASSERT(g!=NULL); + ASSERT(min_size>=0); + ASSERT(max_size>=0); + ASSERT((max_size==0) || (min_size <= max_size)); + ASSERT(!((min_size==0) && (max_size>0))); + ASSERT((opts->reorder_function==NULL) || (opts->reorder_map==NULL)); + + if ((max_size>0) && (min_size>max_size)) { + /* state was not changed */ + entrance_level--; + return NULL; + } + +#if 0 + if (clocks_per_sec==0) + clocks_per_sec=sysconf(_SC_CLK_TCK); + ASSERT(clocks_per_sec>0); +#endif + + /* Dynamic allocation */ + current_clique=set_new(g->n); + clique_size=malloc(g->n * sizeof(int)); + /* table allocated later */ + temp_list=malloc((g->n+2)*sizeof(int *)); + temp_count=0; + +#if 0 + /* "start clock" */ + gettimeofday(&realtimer,NULL); + times(&cputimer); +#endif + + /* reorder */ + if (opts->reorder_function) { + table=opts->reorder_function(g,FALSE); + } else if (opts->reorder_map) { + table=reorder_duplicate(opts->reorder_map,g->n); + } else { + table=reorder_ident(g->n); + } + ASSERT(reorder_is_bijection(table,g->n)); + + + if (unweighted_clique_search_single(table,min_size,g,opts)==0) { + set_free(current_clique); + current_clique=NULL; + goto cleanreturn; + } + if (maximal && (min_size>0)) { + maximalize_clique(current_clique,g); + + if ((max_size > 0) && (set_size(current_clique) > max_size)) { + clique_options localopts; + + s = set_new(g->n); + localopts.time_function = opts->time_function; + localopts.output = opts->output; + localopts.user_function = false_function; + localopts.clique_list = &s; + localopts.clique_list_length = 1; + + for (i=0; i < g->n-1; i++) + if (clique_size[table[i]]>=min_size) + break; + if (unweighted_clique_search_all(table,i,min_size, + max_size,maximal, + g,&localopts)) { + set_free(current_clique); + current_clique=s; + } else { + set_free(current_clique); + current_clique=NULL; + } + } + } + + cleanreturn: + s=current_clique; + + /* Free resources */ + for (i=0; i < temp_count; i++) + free(temp_list[i]); + free(temp_list); + free(table); + free(clique_size); + + ENTRANCE_RESTORE(); + entrance_level--; + + return s; +} + + +/* + * clique_unweighted_find_all() + * + * Find all cliques with size at least min_size and at most max_size. + * + * g - the graph + * min_size - minimum size of cliques to search for. If min_size==0, + * searches for maximum cliques. + * max_size - maximum size of cliques to search for. If max_size==0, no + * upper limit is used. If min_size==0, this must also be 0. + * maximal - require cliques to be maximal cliques + * opts - time printing and clique storage options + * + * Returns the number of cliques found. This can be less than the number + * of cliques in the graph iff opts->time_function() or opts->user_function() + * returns FALSE (request abort). + * + * The cliques found are stored in opts->clique_list[] and + * opts->user_function() is called with them (if non-NULL). The cliques + * stored in opts->clique_list[] are newly allocated, and can be freed + * by set_free(). + */ +int clique_unweighted_find_all(graph_t *g, int min_size, int max_size, + boolean maximal, clique_options *opts) { + int i; + int *table; + int count; + + ENTRANCE_SAVE(); + entrance_level++; + + if (opts==NULL) + opts=clique_default_options; + + ASSERT((sizeof(setelement)*8)==ELEMENTSIZE); + ASSERT(g!=NULL); + ASSERT(min_size>=0); + ASSERT(max_size>=0); + ASSERT((max_size==0) || (min_size <= max_size)); + ASSERT(!((min_size==0) && (max_size>0))); + ASSERT((opts->reorder_function==NULL) || (opts->reorder_map==NULL)); + + if ((max_size>0) && (min_size>max_size)) { + /* state was not changed */ + entrance_level--; + return 0; + } + +#if 0 + if (clocks_per_sec==0) + clocks_per_sec=sysconf(_SC_CLK_TCK); + ASSERT(clocks_per_sec>0); +#endif + + /* Dynamic allocation */ + current_clique=set_new(g->n); + clique_size=malloc(g->n * sizeof(int)); + /* table allocated later */ + temp_list=malloc((g->n+2)*sizeof(int *)); + temp_count=0; + + clique_list_count=0; + memset(clique_size,0,g->n * sizeof(int)); + +#if 0 + /* "start clock" */ + gettimeofday(&realtimer,NULL); + times(&cputimer); +#endif + + /* reorder */ + if (opts->reorder_function) { + table=opts->reorder_function(g,FALSE); + } else if (opts->reorder_map) { + table=reorder_duplicate(opts->reorder_map,g->n); + } else { + table=reorder_ident(g->n); + } + ASSERT(reorder_is_bijection(table,g->n)); + + + /* Search as normal until there is a chance to find a suitable + * clique. */ + if (unweighted_clique_search_single(table,min_size,g,opts)==0) { + count=0; + goto cleanreturn; + } + + if (min_size==0 && max_size==0) { + min_size=max_size=clique_size[table[g->n-1]]; + maximal=FALSE; /* No need to test, since we're searching + * for maximum cliques. */ + } + if (max_size==0) { + max_size=INT_MAX; + } + + for (i=0; i < g->n-1; i++) + if (clique_size[table[i]] >= min_size) + break; + count=unweighted_clique_search_all(table,i,min_size,max_size, + maximal,g,opts); + + cleanreturn: + /* Free resources */ + for (i=0; i<temp_count; i++) + free(temp_list[i]); + free(temp_list); + free(table); + free(clique_size); + set_free(current_clique); + + ENTRANCE_RESTORE(); + entrance_level--; + + return count; +} + + + + +/* + * clique_max_weight() + * + * Returns the weight of the maximum weight clique in the graph (or 0 if + * the search was aborted). + * + * g - the graph + * opts - time printing options + * + * Note: As we don't have an algorithm faster than actually finding + * a maximum weight clique, we use clique_find_single(). + * This incurs only very small overhead. + */ +int clique_max_weight(graph_t *g,clique_options *opts) { + set_t s; + int weight; + + ASSERT((sizeof(setelement)*8)==ELEMENTSIZE); + ASSERT(g!=NULL); + + s=clique_find_single(g,0,0,FALSE,opts); + if (s==NULL) { + /* Search was aborted. */ + return 0; + } + weight=graph_subgraph_weight(g,s); + set_free(s); + return weight; +} + + +/* + * clique_find_single() + * + * Returns a clique with weight at least min_weight and at most max_weight. + * + * g - the graph + * min_weight - minimum weight of clique to search for. If min_weight==0, + * searches for a maximum weight clique. + * max_weight - maximum weight of clique to search for. If max_weight==0, + * no upper limit is used. If min_weight==0, max_weight must + * also be 0. + * maximal - require returned clique to be maximal + * opts - time printing options + * + * Returns the set of vertices forming the clique, or NULL if a clique + * of requested weight/maximality does not exist in the graph (or if + * opts->time_function() requests abort). + * + * The returned clique is newly allocated and can be freed by set_free(). + * + * Note: Does NOT use opts->user_function() or opts->clique_list[]. + * Note: Automatically uses clique_unweighted_find_single if all vertex + * weights are the same. + */ +set_t clique_find_single(graph_t *g,int min_weight,int max_weight, + boolean maximal, clique_options *opts) { + int i; + int *table; + set_t s; + + ENTRANCE_SAVE(); + entrance_level++; + + if (opts==NULL) + opts=clique_default_options; + + ASSERT((sizeof(setelement)*8)==ELEMENTSIZE); + ASSERT(g!=NULL); + ASSERT(min_weight>=0); + ASSERT(max_weight>=0); + ASSERT((max_weight==0) || (min_weight <= max_weight)); + ASSERT(!((min_weight==0) && (max_weight>0))); + ASSERT((opts->reorder_function==NULL) || (opts->reorder_map==NULL)); + + if ((max_weight>0) && (min_weight>max_weight)) { + /* state was not changed */ + entrance_level--; + return NULL; + } + +#if 0 + if (clocks_per_sec==0) + clocks_per_sec=sysconf(_SC_CLK_TCK); + ASSERT(clocks_per_sec>0); +#endif + + /* Check whether we can use unweighted routines. */ + if (!graph_weighted(g)) { + min_weight=DIV_UP(min_weight,g->weights[0]); + if (max_weight) { + max_weight=DIV_DOWN(max_weight,g->weights[0]); + if (max_weight < min_weight) { + /* state was not changed */ + entrance_level--; + return NULL; + } + } + + weight_multiplier = g->weights[0]; + entrance_level--; + s=clique_unweighted_find_single(g,min_weight,max_weight, + maximal,opts); + ENTRANCE_RESTORE(); + return s; + } + + /* Dynamic allocation */ + current_clique=set_new(g->n); + best_clique=set_new(g->n); + clique_size=malloc(g->n * sizeof(int)); + memset(clique_size, 0, g->n * sizeof(int)); + /* table allocated later */ + temp_list=malloc((g->n+2)*sizeof(int *)); + temp_count=0; + + clique_list_count=0; + +#if 0 + /* "start clock" */ + gettimeofday(&realtimer,NULL); + times(&cputimer); +#endif + + /* reorder */ + if (opts->reorder_function) { + table=opts->reorder_function(g,TRUE); + } else if (opts->reorder_map) { + table=reorder_duplicate(opts->reorder_map,g->n); + } else { + table=reorder_ident(g->n); + } + ASSERT(reorder_is_bijection(table,g->n)); + + if (max_weight==0) + max_weight=INT_MAX; + + if (weighted_clique_search_single(table,min_weight,max_weight, + g,opts)==0) { + /* Requested clique has not been found. */ + set_free(best_clique); + best_clique=NULL; + goto cleanreturn; + } + if (maximal && (min_weight>0)) { + maximalize_clique(best_clique,g); + if (graph_subgraph_weight(g,best_clique) > max_weight) { + clique_options localopts; + + localopts.time_function = opts->time_function; + localopts.output = opts->output; + localopts.user_function = false_function; + localopts.clique_list = &best_clique; + localopts.clique_list_length = 1; + + for (i=0; i < g->n-1; i++) + if ((clique_size[table[i]] >= min_weight) || + (clique_size[table[i]] == 0)) + break; + if (!weighted_clique_search_all(table,i,min_weight, + max_weight,maximal, + g,&localopts)) { + set_free(best_clique); + best_clique=NULL; + } + } + } + + cleanreturn: + s=best_clique; + + /* Free resources */ + for (i=0; i < temp_count; i++) + free(temp_list[i]); + free(temp_list); + temp_list=NULL; + temp_count=0; + free(table); + set_free(current_clique); + current_clique=NULL; + free(clique_size); + clique_size=NULL; + + ENTRANCE_RESTORE(); + entrance_level--; + + return s; +} + + + + + +/* + * clique_find_all() + * + * Find all cliques with weight at least min_weight and at most max_weight. + * + * g - the graph + * min_weight - minimum weight of cliques to search for. If min_weight==0, + * searches for maximum weight cliques. + * max_weight - maximum weight of cliques to search for. If max_weight==0, + * no upper limit is used. If min_weight==0, max_weight must + * also be 0. + * maximal - require cliques to be maximal cliques + * opts - time printing and clique storage options + * + * Returns the number of cliques found. This can be less than the number + * of cliques in the graph iff opts->time_function() or opts->user_function() + * returns FALSE (request abort). + * + * The cliques found are stored in opts->clique_list[] and + * opts->user_function() is called with them (if non-NULL). The cliques + * stored in opts->clique_list[] are newly allocated, and can be freed + * by set_free(). + * + * Note: Automatically uses clique_unweighted_find_all if all vertex + * weights are the same. + */ +int clique_find_all(graph_t *g, int min_weight, int max_weight, + boolean maximal, clique_options *opts) { + int i,n; + int *table; + + ENTRANCE_SAVE(); + entrance_level++; + + if (opts==NULL) + opts=clique_default_options; + + ASSERT((sizeof(setelement)*8)==ELEMENTSIZE); + ASSERT(g!=NULL); + ASSERT(min_weight>=0); + ASSERT(max_weight>=0); + ASSERT((max_weight==0) || (min_weight <= max_weight)); + ASSERT(!((min_weight==0) && (max_weight>0))); + ASSERT((opts->reorder_function==NULL) || (opts->reorder_map==NULL)); + + if ((max_weight>0) && (min_weight>max_weight)) { + /* state was not changed */ + entrance_level--; + return 0; + } + +#if 0 + if (clocks_per_sec==0) + clocks_per_sec=sysconf(_SC_CLK_TCK); + ASSERT(clocks_per_sec>0); +#endif + + if (!graph_weighted(g)) { + min_weight=DIV_UP(min_weight,g->weights[0]); + if (max_weight) { + max_weight=DIV_DOWN(max_weight,g->weights[0]); + if (max_weight < min_weight) { + /* state was not changed */ + entrance_level--; + return 0; + } + } + + weight_multiplier = g->weights[0]; + entrance_level--; + i=clique_unweighted_find_all(g,min_weight,max_weight,maximal, + opts); + ENTRANCE_RESTORE(); + return i; + } + + /* Dynamic allocation */ + current_clique=set_new(g->n); + best_clique=set_new(g->n); + clique_size=malloc(g->n * sizeof(int)); + memset(clique_size, 0, g->n * sizeof(int)); + /* table allocated later */ + temp_list=malloc((g->n+2)*sizeof(int *)); + temp_count=0; + +#if 0 + /* "start clock" */ + gettimeofday(&realtimer,NULL); + times(&cputimer); +#endif + + /* reorder */ + if (opts->reorder_function) { + table=opts->reorder_function(g,TRUE); + } else if (opts->reorder_map) { + table=reorder_duplicate(opts->reorder_map,g->n); + } else { + table=reorder_ident(g->n); + } + ASSERT(reorder_is_bijection(table,g->n)); + + /* First phase */ + n=weighted_clique_search_single(table,min_weight,INT_MAX,g,opts); + if (n==0) { + /* Requested clique has not been found. */ + goto cleanreturn; + } + + if (min_weight==0) { + min_weight=n; + max_weight=n; + maximal=FALSE; /* They're maximum cliques already. */ + } + if (max_weight==0) + max_weight=INT_MAX; + + for (i=0; i < g->n; i++) + if ((clique_size[table[i]] >= min_weight) || + (clique_size[table[i]] == 0)) + break; + + /* Second phase */ + n=weighted_clique_search_all(table,i,min_weight,max_weight,maximal, + g,opts); + + cleanreturn: + /* Free resources */ + for (i=0; i < temp_count; i++) + free(temp_list[i]); + free(temp_list); + free(table); + set_free(current_clique); + set_free(best_clique); + free(clique_size); + + ENTRANCE_RESTORE(); + entrance_level--; + + return n; +} + + + + + + + + + + + + + + + + + +/* + * clique_print_time() + * + * Reports current running information every 0.1 seconds or when values + * change. + * + * level - re-entrance level + * i - current recursion level + * n - maximum recursion level + * max - weight of heaviest clique found + * cputime - CPU time used in algorithm so far + * realtime - real time used in algorithm so far + * opts - prints information to (FILE *)opts->output (or stdout if NULL) + * + * Returns always TRUE (ie. never requests abort). + */ +boolean clique_print_time(int level, int i, int n, int max, + double cputime, double realtime, + clique_options *opts) { + static float prev_time=100; + static int prev_i=100; + static int prev_max=100; + static int prev_level=0; + FILE *fp=opts->output; + int j; + + if (fp==NULL) + fp=stdout; + + if (ABS(prev_time-realtime)>0.1 || i==n || i<prev_i || max!=prev_max || + level!=prev_level) { + for (j=1; j<level; j++) + fprintf(fp," "); + if (realtime-prev_time < 0.01 || i<=prev_i) + fprintf(fp,"%3d/%d (max %2d) %2.2f s " + "(0.00 s/round)\n",i,n,max, + realtime); + else + fprintf(fp,"%3d/%d (max %2d) %2.2f s " + "(%2.2f s/round)\n", + i,n,max,realtime, + (realtime-prev_time)/(i-prev_i)); + prev_time=realtime; + prev_i=i; + prev_max=max; + prev_level=level; + } + return TRUE; +} + +/* + * clique_print_time_always() + * + * Reports current running information. + * + * level - re-entrance level + * i - current recursion level + * n - maximum recursion level + * max - largest clique found + * cputime - CPU time used in algorithm so far + * realtime - real time used in algorithm so far + * opts - prints information to (FILE *)opts->output (or stdout if NULL) + * + * Returns always TRUE (ie. never requests abort). + */ +boolean clique_print_time_always(int level, int i, int n, int max, + double cputime, double realtime, + clique_options *opts) { + static float prev_time=100; + static int prev_i=100; + FILE *fp=opts->output; + int j; + + if (fp==NULL) + fp=stdout; + + for (j=1; j<level; j++) + fprintf(fp," "); + + if (realtime-prev_time < 0.01 || i<=prev_i) + fprintf(fp,"%3d/%d (max %2d) %2.2f s (0.00 s/round)\n", + i,n,max,realtime); + else + fprintf(fp,"%3d/%d (max %2d) %2.2f s (%2.2f s/round)\n", + i,n,max,realtime,(realtime-prev_time)/(i-prev_i)); + prev_time=realtime; + prev_i=i; + + return TRUE; +} + + +/* + * This file contains the graph handling routines. + * + * Copyright (C) 2002 Sampo Niskanen, Patric Östergård. + * Licensed under the GNU GPL, read the file LICENSE for details. + */ + +/* +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include "graph.h" +*/ + + +/* + * graph_new() + * + * Returns a newly allocated graph with n vertices all with weight 1, + * and no edges. + */ +graph_t *graph_new(int n) { + graph_t *g; + int i; + + ASSERT((sizeof(setelement)*8)==ELEMENTSIZE); + ASSERT(n>0); + + g=malloc(sizeof(graph_t)); + g->n=n; + g->edges=malloc(g->n * sizeof(set_t)); + g->weights=malloc(g->n * sizeof(int)); + for (i=0; i < g->n; i++) { + g->edges[i]=set_new(n); + g->weights[i]=1; + } + return g; +} + +/* + * graph_free() + * + * Frees the memory associated with the graph g. + */ +void graph_free(graph_t *g) { + int i; + + ASSERT((sizeof(setelement)*8)==ELEMENTSIZE); + ASSERT(g!=NULL); + ASSERT(g->n > 0); + + for (i=0; i < g->n; i++) { + set_free(g->edges[i]); + } + free(g->weights); + free(g->edges); + free(g); + return; +} + + +/* + * graph_resize() + * + * Resizes graph g to given size. If size > g->n, the new vertices are + * not connected to any others and their weights are set to 1. + * If size < g->n, the last g->n - size vertices are removed. + */ +void graph_resize(graph_t *g, int size) { + int i; + + ASSERT(g!=NULL); + ASSERT(g->n > 0); + ASSERT(size > 0); + + if (g->n == size) + return; + + /* Free/alloc extra edge-sets */ + for (i=size; i < g->n; i++) + set_free(g->edges[i]); + g->edges=realloc(g->edges, size * sizeof(set_t)); + for (i=g->n; i < size; i++) + g->edges[i]=set_new(size); + + /* Resize original sets */ + for (i=0; i < MIN(g->n,size); i++) { + g->edges[i]=set_resize(g->edges[i],size); + } + + /* Weights */ + g->weights=realloc(g->weights,size * sizeof(int)); + for (i=g->n; i<size; i++) + g->weights[i]=1; + + g->n=size; + return; +} + +/* + * graph_crop() + * + * Resizes the graph so as to remove all highest-valued isolated vertices. + */ +void graph_crop(graph_t *g) { + int i; + + for (i=g->n-1; i>=1; i--) + if (set_size(g->edges[i])>0) + break; + graph_resize(g,i+1); + return; +} + + +/* + * graph_weighted() + * + * Returns TRUE if all vertex weights of graph g are all the same. + * + * Note: Does NOT require weights to be 1. + */ +boolean graph_weighted(graph_t *g) { + int i,w; + + w=g->weights[0]; + for (i=1; i < g->n; i++) + if (g->weights[i] != w) + return TRUE; + return FALSE; +} + +/* + * graph_edge_count() + * + * Returns the number of edges in graph g. + */ +int graph_edge_count(graph_t *g) { + int i; + int count=0; + + for (i=0; i < g->n; i++) { + count += set_size(g->edges[i]); + } + return count/2; +} + +/* + * graph_print() + * + * Prints a representation of the graph g to stdout (along with any errors + * noticed). Mainly useful for debugging purposes and trivial output. + * + * The output consists of a first line describing the dimensions and then + * one line per vertex containing the vertex number (numbered 0,...,n-1), + * the vertex weight (if the graph is weighted), "->" and then a list + * of all vertices it is adjacent to. + */ +void graph_print(graph_t *g) { + int i,j; + int asymm=0; + int refl=0; + int nonpos=0; + int extra=0; + unsigned int weight=0; + boolean weighted; + + ASSERT((sizeof(setelement)*8)==ELEMENTSIZE); + + if (g==NULL) { + printf(" WARNING: Graph pointer is NULL!\n"); + return; + } + if (g->n <= 0) { + printf(" WARNING: Graph has %d vertices " + "(should be positive)!\n",g->n); + return; + } + + weighted=graph_weighted(g); + + printf("%s graph has %d vertices, %d edges (density %.2f).\n", + weighted?"Weighted":((g->weights[0]==1)? + "Unweighted":"Semi-weighted"), + g->n,graph_edge_count(g), + (float)graph_edge_count(g)/((float)(g->n - 1)*(g->n)/2)); + + for (i=0; i < g->n; i++) { + printf("%2d",i); + if (weighted) { + printf(" w=%d",g->weights[i]); + if (g->weights[i] <= 0) { + printf("*NON-POSITIVE*"); + nonpos++; + } + } + if (weight < INT_MAX) + weight+=g->weights[i]; + printf(" ->"); + for (j=0; j < g->n; j++) { + if (SET_CONTAINS_FAST(g->edges[i],j)) { + printf(" %d",j); + if (i==j) { + printf("*REFLEXIVE*"); + refl++; + } + if (!SET_CONTAINS_FAST(g->edges[j],i)) { + printf("*ASYMMERTIC*"); + asymm++; + } + } + } + for (j=g->n; j < SET_ARRAY_LENGTH(g->edges[i])*ELEMENTSIZE; + j++) { + if (SET_CONTAINS_FAST(g->edges[i],j)) { + printf(" %d*NON-EXISTENT*",j); + extra++; + } + } + printf("\n"); + } + + if (asymm) + printf(" WARNING: Graph contained %d asymmetric edges!\n", + asymm); + if (refl) + printf(" WARNING: Graph contained %d reflexive edges!\n", + refl); + if (nonpos) + printf(" WARNING: Graph contained %d non-positive vertex " + "weights!\n",nonpos); + if (extra) + printf(" WARNING: Graph contained %d edges to " + "non-existent vertices!\n",extra); + if (weight>=INT_MAX) + printf(" WARNING: Total graph weight >= INT_MAX!\n"); + return; +} + + +/* + * graph_test() + * + * Tests graph g to be valid. Checks that g is non-NULL, the edges are + * symmetric and anti-reflexive, and that all vertex weights are positive. + * If output is non-NULL, prints a few lines telling the status of the graph + * to file descriptor output. + * + * Returns TRUE if the graph is valid, FALSE otherwise. + */ +boolean graph_test(graph_t *g,FILE *output) { + int i,j; + int edges=0; + int asymm=0; + int nonpos=0; + int refl=0; + int extra=0; + unsigned int weight=0; + boolean weighted; + + ASSERT((sizeof(setelement)*8)==ELEMENTSIZE); + + if (g==NULL) { + if (output) + fprintf(output," WARNING: Graph pointer is NULL!\n"); + return FALSE; + } + + weighted=graph_weighted(g); + + for (i=0; i < g->n; i++) { + if (g->edges[i]==NULL) { + if (output) + fprintf(output," WARNING: Graph edge set " + "NULL!\n" + " (further warning suppressed)\n"); + return FALSE; + } + if (SET_MAX_SIZE(g->edges[i]) < g->n) { + if (output) + fprintf(output," WARNING: Graph edge set " + "too small!\n" + " (further warnings suppressed)\n"); + return FALSE; + } + for (j=0; j < g->n; j++) { + if (SET_CONTAINS_FAST(g->edges[i],j)) { + edges++; + if (i==j) { + refl++; + } + if (!SET_CONTAINS_FAST(g->edges[j],i)) { + asymm++; + } + } + } + for (j=g->n; j < SET_ARRAY_LENGTH(g->edges[i])*ELEMENTSIZE; + j++) { + if (SET_CONTAINS_FAST(g->edges[i],j)) + extra++; + } + if (g->weights[i] <= 0) + nonpos++; + if (weight<INT_MAX) + weight += g->weights[i]; + } + + edges/=2; /* Each is counted twice. */ + + if (output) { + /* Semi-weighted means all weights are equal, but not 1. */ + fprintf(output,"%s graph has %d vertices, %d edges " + "(density %.2f).\n", + weighted?"Weighted": + ((g->weights[0]==1)?"Unweighted":"Semi-weighted"), + g->n,edges,(float)edges/((float)(g->n - 1)*(g->n)/2)); + + if (asymm) + fprintf(output," WARNING: Graph contained %d " + "asymmetric edges!\n",asymm); + if (refl) + fprintf(output," WARNING: Graph contained %d " + "reflexive edges!\n",refl); + if (nonpos) + fprintf(output," WARNING: Graph contained %d " + "non-positive vertex weights!\n",nonpos); + if (extra) + fprintf(output," WARNING: Graph contained %d edges " + "to non-existent vertices!\n",extra); + if (weight>=INT_MAX) + fprintf(output," WARNING: Total graph weight >= " + "INT_MAX!\n"); + if (asymm==0 && refl==0 && nonpos==0 && extra==0 && + weight<INT_MAX) + fprintf(output,"Graph OK.\n"); + } + + if (asymm || refl || nonpos || extra || weight>=INT_MAX) + return FALSE; + + return TRUE; +} + + +/* + * graph_test_regular() + * + * Returns the vertex degree for regular graphs, or -1 if the graph is + * not regular. + */ +int graph_test_regular(graph_t *g) { + int i,n; + + n=set_size(g->edges[0]); + + for (i=1; i < g->n; i++) { + if (set_size(g->edges[i]) != n) + return -1; + } + return n; +} + + + +/* + * This file contains the vertex reordering routines. + * + * Copyright (C) 2002 Sampo Niskanen, Patric Östergård. + * Licensed under the GNU GPL, read the file LICENSE for details. + */ + +/* +#include "reorder.h" + +#include <time.h> +#include <sys/times.h> +#include <stdlib.h> + +#include <limits.h> +*/ + + +/* + * reorder_set() + * + * Reorders the set s with a function i -> order[i]. + * + * Note: Assumes that order is the same size as SET_MAX_SIZE(s). + */ +void reorder_set(set_t s,int *order) { + set_t tmp; + int i,j; + setelement e; + + ASSERT(reorder_is_bijection(order,SET_MAX_SIZE(s))); + + tmp=set_new(SET_MAX_SIZE(s)); + + for (i=0; i<(SET_MAX_SIZE(s)/ELEMENTSIZE); i++) { + e=s[i]; + if (e==0) + continue; + for (j=0; j<ELEMENTSIZE; j++) { + if (e&1) { + SET_ADD_ELEMENT(tmp,order[i*ELEMENTSIZE+j]); + } + e = e>>1; + } + } + if (SET_MAX_SIZE(s)%ELEMENTSIZE) { + e=s[i]; + for (j=0; j<(SET_MAX_SIZE(s)%ELEMENTSIZE); j++) { + if (e&1) { + SET_ADD_ELEMENT(tmp,order[i*ELEMENTSIZE+j]); + } + e = e>>1; + } + } + set_copy(s,tmp); + set_free(tmp); + return; +} + + +/* + * reorder_graph() + * + * Reorders the vertices in the graph with function i -> order[i]. + * + * Note: Assumes that order is of size g->n. + */ +void reorder_graph(graph_t *g, int *order) { + int i; + set_t *tmp_e; + int *tmp_w; + + ASSERT(reorder_is_bijection(order,g->n)); + + tmp_e=malloc(g->n * sizeof(set_t)); + tmp_w=malloc(g->n * sizeof(int)); + for (i=0; i<g->n; i++) { + reorder_set(g->edges[i],order); + tmp_e[order[i]]=g->edges[i]; + tmp_w[order[i]]=g->weights[i]; + } + for (i=0; i<g->n; i++) { + g->edges[i]=tmp_e[i]; + g->weights[i]=tmp_w[i]; + } + free(tmp_e); + free(tmp_w); + return; +} + + + +/* + * reorder_duplicate() + * + * Returns a newly allocated duplicate of the given ordering. + */ +int *reorder_duplicate(int *order,int n) { + int *new; + + new=malloc(n*sizeof(int)); + memcpy(new,order,n*sizeof(int)); + return new; +} + +/* + * reorder_invert() + * + * Inverts the given ordering so that new[old[i]]==i. + * + * Note: Asserts that order is a bijection. + */ +void reorder_invert(int *order,int n) { + int *new; + int i; + + ASSERT(reorder_is_bijection(order,n)); + + new=malloc(n*sizeof(int)); + for (i=0; i<n; i++) + new[order[i]]=i; + for (i=0; i<n; i++) + order[i]=new[i]; + free(new); + return; +} + +/* + * reorder_reverse() + * + * Reverses the given ordering so that new[i] == n-1 - old[i]. + */ +void reorder_reverse(int *order,int n) { + int i; + + for (i=0; i<n; i++) + order[i] = n-1 - order[i]; + return; +} + +/* + * reorder_is_bijection + * + * Checks that an ordering is a bijection {0,...,n-1} -> {0,...,n-1}. + * + * Returns TRUE if it is a bijection, FALSE otherwise. + */ +boolean reorder_is_bijection(int *order,int n) { + boolean *used; + int i; + + used=calloc(n,sizeof(boolean)); + for (i=0; i<n; i++) { + if (order[i]<0 || order[i]>=n) { + free(used); + return FALSE; + } + if (used[order[i]]) { + free(used); + return FALSE; + } + used[order[i]]=TRUE; + } + for (i=0; i<n; i++) { + if (!used[i]) { + free(used); + return FALSE; + } + } + free(used); + return TRUE; +} + +/* + * reorder_ident() + * + * Returns a newly allocated identity ordering of size n, ie. order[i]==i. + */ +int *reorder_ident(int n) { + int i; + int *order; + + order=malloc(n*sizeof(int)); + for (i=0; i<n; i++) + order[i]=i; + return order; +} + + + +/*** Reordering functions for use in clique_options ***/ + +/* + * reorder_by_ident() + * + * Returns an identity ordering. + */ +int *reorder_by_ident(graph_t *g,boolean weighted) { + return reorder_ident(g->n); +} + +/* + * reorder_by_reverse() + * + * Returns a reverse identity ordering. + */ +int *reorder_by_reverse(graph_t *g,boolean weighted) { + int i; + int *order; + + order=malloc(g->n * sizeof(int)); + for (i=0; i < g->n; i++) + order[i]=g->n-i-1; + return order; +} + +/* + * reorder_by_greedy_coloring() + * + * Equivalent to reorder_by_weighted_greedy_coloring or + * reorder_by_unweighted_greedy_coloring according to the value of weighted. + */ +int *reorder_by_greedy_coloring(graph_t *g,boolean weighted) { + if (weighted) + return reorder_by_weighted_greedy_coloring(g,weighted); + else + return reorder_by_unweighted_greedy_coloring(g,weighted); +} + + +/* + * reorder_by_unweighted_greedy_coloring() + * + * Returns an ordering for the graph g by coloring the clique one + * color at a time, always adding the vertex of largest degree within + * the uncolored graph, and numbering these vertices 0, 1, ... + * + * Experimentally efficient for use with unweighted graphs. + */ +int *reorder_by_unweighted_greedy_coloring(graph_t *g,boolean weighted) { + int i,j,v; + boolean *tmp_used; + int *degree; /* -1 for used vertices */ + int *order; + int maxdegree,maxvertex=0; + boolean samecolor; + + tmp_used=calloc(g->n,sizeof(boolean)); + degree=calloc(g->n,sizeof(int)); + order=calloc(g->n,sizeof(int)); + + for (i=0; i < g->n; i++) { + for (j=0; j < g->n; j++) { + ASSERT(!((i==j) && GRAPH_IS_EDGE(g,i,j))); + if (GRAPH_IS_EDGE(g,i,j)) + degree[i]++; + } + } + + v=0; + while (v < g->n) { + /* Reset tmp_used. */ + memset(tmp_used,0,g->n * sizeof(boolean)); + + do { + /* Find vertex to be colored. */ + maxdegree=0; + samecolor=FALSE; + for (i=0; i < g->n; i++) { + if (!tmp_used[i] && degree[i] >= maxdegree) { + maxvertex=i; + maxdegree=degree[i]; + samecolor=TRUE; + } + } + if (samecolor) { + order[v]=maxvertex; + degree[maxvertex]=-1; + v++; + + /* Mark neighbors not to color with same + * color and update neighbor degrees. */ + for (i=0; i < g->n; i++) { + if (GRAPH_IS_EDGE(g,maxvertex,i)) { + tmp_used[i]=TRUE; + degree[i]--; + } + } + } + } while (samecolor); + } + + free(tmp_used); + free(degree); + return order; +} + +/* + * reorder_by_weighted_greedy_coloring() + * + * Returns an ordering for the graph g by coloring the clique one + * color at a time, always adding the vertex that (in order of importance): + * 1. has the minimum weight in the remaining graph + * 2. has the largest sum of weights surrounding the vertex + * + * Experimentally efficient for use with weighted graphs. + */ +int *reorder_by_weighted_greedy_coloring(graph_t *g, boolean weighted) { + int i,j,p=0; + int cnt; + int *nwt; /* Sum of surrounding vertices' weights */ + int min_wt,max_nwt; + boolean *used; + int *order; + + nwt=malloc(g->n * sizeof(int)); + order=malloc(g->n * sizeof(int)); + used=calloc(g->n,sizeof(boolean)); + + for (i=0; i < g->n; i++) { + nwt[i]=0; + for (j=0; j < g->n; j++) + if (GRAPH_IS_EDGE(g, i, j)) + nwt[i] += g->weights[j]; + } + + for (cnt=0; cnt < g->n; cnt++) { + min_wt=INT_MAX; + max_nwt=-1; + for (i=g->n-1; i>=0; i--) + if ((!used[i]) && (g->weights[i] < min_wt)) + min_wt=g->weights[i]; + for (i=g->n-1; i>=0; i--) { + if (used[i] || (g->weights[i] > min_wt)) + continue; + if (nwt[i] > max_nwt) { + max_nwt=nwt[i]; + p=i; + } + } + order[cnt]=p; + used[p]=TRUE; + for (j=0; j < g->n; j++) + if ((!used[j]) && (GRAPH_IS_EDGE(g, p, j))) + nwt[j] -= g->weights[p]; + } + + free(nwt); + free(used); + + ASSERT(reorder_is_bijection(order,g->n)); + + return order; +} + +/* + * reorder_by_degree() + * + * Returns a reordering of the graph g so that the vertices with largest + * degrees (most neighbors) are first. + */ +int *reorder_by_degree(graph_t *g, boolean weighted) { + int i,j,v; + int *degree; + int *order; + int maxdegree,maxvertex=0; + + degree=calloc(g->n,sizeof(int)); + order=calloc(g->n,sizeof(int)); + + for (i=0; i < g->n; i++) { + for (j=0; j < g->n; j++) { + ASSERT(!((i==j) && GRAPH_IS_EDGE(g,i,j))); + if (GRAPH_IS_EDGE(g,i,j)) + degree[i]++; + } + } + + for (v=0; v < g->n; v++) { + maxdegree=0; + for (i=0; i < g->n; i++) { + if (degree[i] >= maxdegree) { + maxvertex=i; + maxdegree=degree[i]; + } + } + order[v]=maxvertex; + degree[maxvertex]=-1; /* used */ +/*** Max. degree withing unselected graph: + for (i=0; i < g->n; i++) { + if (GRAPH_IS_EDGE(g,maxvertex,i)) + degree[i]--; + } +***/ + } + + free(degree); + return order; +} + +/* + * reorder_by_random() + * + * Returns a random reordering for graph g. + * Note: Used the functions rand() and srand() to generate the random + * numbers. srand() is re-initialized every time reorder_by_random() + * is called using the system time. + */ +int *reorder_by_random(graph_t *g, boolean weighted) { + /* struct tms t; */ + int i,r; + int *new; + boolean *used; + +/* + srand(times(&t)+time(NULL)); +*/ + INITRANBYTIME; + + new=calloc(g->n, sizeof(int)); + used=calloc(g->n, sizeof(boolean)); + for (i=0; i < g->n; i++) { + do { + r=NEXTRAN % g->n; + } while (used[r]); + new[i]=r; + used[r]=TRUE; + } + free(used); + return new; +} + +/************************************************************************/ + +/* This is an interface between nauty and cliquer for finding + cliques of a given size in an undirected graph. */ + +int +find_clique(graph *g, int m, int n, int min, int max, boolean maximal) +/* If there is a clique of size [min,max], perhaps required to be + maximal, then return its size. If there is none, return 0. + It is required that min <= max. Use min=max=0 to ask for + maximum cliques. */ +{ + graph_t *gg; + set_t cliq; + set *gi; + int i,j,size; + + gg = graph_new(n); + + for (i = 0, gi = g; i < n; ++i, gi += m) + for (j = i; (j = nextelement(gi,m,j)) >= 0; ) + GRAPH_ADD_EDGE(gg,i,j); + + cliq = clique_unweighted_find_single(gg,min,max,maximal,NULL); + + if (cliq) + { + size = set_size(cliq); + set_free(cliq); + } + else + size = 0; + + graph_free(gg); + + return size; +} + + +int +find_indset(graph *g, int m, int n, int min, int max, boolean maximal) +/* If there is an independent set of size [min,max], perhaps required + to be maximal, then return its size. If there is none, return 0. + It is required that min <= max. Use min=max=0 to ask for + maximum independent sets. */ +{ + graph_t *gg; + set_t cliq; + set *gi; + int i,j,jj,size; + + gg = graph_new(n); + + /* Make gg the complement of g */ + for (i = 0, gi = g; i < n; ++i, gi += m) + { + for (j = jj = i; (j = nextelement(gi,m,j)) >= 0; ) + { + while (++jj < j) GRAPH_ADD_EDGE(gg,i,jj); + } + while (++jj < n) GRAPH_ADD_EDGE(gg,i,jj); + } + + cliq = clique_unweighted_find_single(gg,min,max,maximal,NULL); + + if (cliq) + { + size = set_size(cliq); + set_free(cliq); + } + else + size = 0; + + graph_free(gg); + + return size; +} + diff --git a/graph-checker/nauty/nautycliquer.h b/graph-checker/nauty/nautycliquer.h new file mode 100644 index 0000000..ff232e5 --- /dev/null +++ b/graph-checker/nauty/nautycliquer.h @@ -0,0 +1,647 @@ +/* This file has all the included material used by cliquer-1.21 + and prototypes for the interface procedures in nautycliquer.c */ + +#ifndef NAUTYCLIQUER_H +#define NAUTYCLIQUER_H + +#include "nauty.h" +#include "gtools.h" +#include <limits.h> + +/********************************************************************** +#include "cliquerconf.h" +*/ + +/* + * setelement is the basic memory type used in sets. It is often fastest + * to be as large as can fit into the CPU registers. + * + * ELEMENTSIZE is the size of one setelement, measured in bits. It must + * be either 16, 32 or 64 (otherwise additional changes must be made to + * the source). + * + * The default is to use "unsigned long int" and attempt to guess the + * size using <limits.h>, which should work pretty well. Check functioning + * with "make test". + */ + +/* typedef unsigned long int setelement; */ +/* #define ELEMENTSIZE 64 */ + +/* + * INLINE is a command prepended to function declarations to instruct the + * compiler to inline the function. If inlining is not desired, define blank. + * + * The default is to use "inline", which is recognized by most compilers. + */ + +/* #define INLINE */ +/* #define INLINE __inline__ */ + + +/* + * Set handling functions are defined as static functions in set.h for + * performance reasons. This may cause unnecessary warnings from the + * compiler. Some compilers (such as GCC) have the possibility to turn + * off the warnings on a per-function basis using a flag prepended to + * the function declaration. + * + * The default is to use the correct attribute when compiling with GCC, + * or no flag otherwise. + */ + +/* #define UNUSED_FUNCTION __attribute__((unused)) */ +/* #define UNUSED_FUNCTION */ + +/* + * Uncommenting the following will disable all assertions (checks that + * function arguments and other variables are correct). This is highly + * discouraged, as it allows bugs to go unnoticed easier. The assertions + * are set so that they do not slow down programs notably. + */ + +/* #define ASSERT(x) */ + +/********************************************************************** +#include "misc.h" +*/ + +/* + * We #define boolean instead of using a typedef because nauty.h uses it + * also. AFAIK, there is no way to check for an existing typedef, and + * re-typedefing is illegal (even when using exactly the same datatype!). +#ifndef boolean +#define boolean int +#endif + +BDM: In nauty's version we will use nauty's boolean (which is int anyway). + */ + +/* + * Default value for UNUSED_FUNCTION: use "__attribute__((unused))" for + * GCC versions that support it, otherwise leave blank. + */ +#ifndef UNUSED_FUNCTION +# if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4) +# define UNUSED_FUNCTION __attribute__((unused)) +# else +# define UNUSED_FUNCTION +# endif +#endif /* !UNUSED_FUNCTION */ + +/* + * Default inlining directive: "inline" + */ +#ifndef INLINE +#define INLINE inline +#endif + +#ifndef ASSERT +#define ASSERT(expr) \ + if (!(expr)) { \ + fprintf(stderr,"cliquer file %s: line %d: assertion failed: " \ + "(%s)\n",__FILE__,__LINE__,#expr); \ + abort(); \ + } +#endif /* !ASSERT */ + + +#ifndef FALSE +#define FALSE (0) +#endif +#ifndef TRUE +#define TRUE (!FALSE) +#endif + + +#ifndef MIN +#define MIN(a,b) (((a)<(b))?(a):(b)) +#endif +#ifndef MAX +#define MAX(a,b) (((a)>(b))?(a):(b)) +#endif +#ifndef ABS +#define ABS(v) (((v)<0)?(-(v)):(v)) +#endif + +/********************************************************************** +#include "set.h" +*/ + +/* + * This file contains the set handling routines. + * + * Copyright (C) 2002 Sampo Niskanen, Patric ÖstergÃ¥rd. + * Licensed under the GNU GPL, read the file LICENSE for details. + */ + +/* + * Sets are arrays of setelement's (typically unsigned long int's) with + * representative bits for each value they can contain. The values + * are numbered 0,...,n-1. + */ + + +/*** Variable types and constants. ***/ + + +/* + * If setelement hasn't been declared: + * - use "unsigned long int" as setelement + * - try to deduce size from ULONG_MAX + */ + +#ifndef ELEMENTSIZE +typedef unsigned long int setelement; +# if (ULONG_MAX == 65535) +# define ELEMENTSIZE 16 +# elif (ULONG_MAX == 4294967295) +# define ELEMENTSIZE 32 +# else +# define ELEMENTSIZE 64 +# endif +#endif /* !ELEMENTSIZE */ + +typedef setelement * set_t; + + +/*** Counting amount of 1 bits in a setelement ***/ + +/* Array for amount of 1 bits in a byte. */ +static int set_bit_count[256] = { + 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4, + 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5, + 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5, + 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, + 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5, + 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, + 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, + 3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, + 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5, + 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, + 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, + 3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, + 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, + 3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, + 3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, + 4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8 }; + +/* The following macros assume that all higher bits are 0. + * They may in some cases be useful also on with other ELEMENTSIZE's, + * so we define them all. */ +#define SET_ELEMENT_BIT_COUNT_8(a) (set_bit_count[(a)]) +#define SET_ELEMENT_BIT_COUNT_16(a) (set_bit_count[(a)>>8] + \ + set_bit_count[(a)&0xFF]) +#define SET_ELEMENT_BIT_COUNT_32(a) (set_bit_count[(a)>>24] + \ + set_bit_count[((a)>>16)&0xFF] + \ + set_bit_count[((a)>>8)&0xFF] + \ + set_bit_count[(a)&0xFF]) +#define SET_ELEMENT_BIT_COUNT_64(a) (set_bit_count[(a)>>56] + \ + set_bit_count[((a)>>48)&0xFF] + \ + set_bit_count[((a)>>40)&0xFF] + \ + set_bit_count[((a)>>32)&0xFF] + \ + set_bit_count[((a)>>24)&0xFF] + \ + set_bit_count[((a)>>16)&0xFF] + \ + set_bit_count[((a)>>8)&0xFF] + \ + set_bit_count[(a)&0xFF]) +#if (ELEMENTSIZE==64) +# define SET_ELEMENT_BIT_COUNT(a) SET_ELEMENT_BIT_COUNT_64(a) +# define FULL_ELEMENT ((setelement)0xFFFFFFFFFFFFFFFF) +#elif (ELEMENTSIZE==32) +# define SET_ELEMENT_BIT_COUNT(a) SET_ELEMENT_BIT_COUNT_32(a) +# define FULL_ELEMENT ((setelement)0xFFFFFFFF) +#elif (ELEMENTSIZE==16) +# define SET_ELEMENT_BIT_COUNT(a) SET_ELEMENT_BIT_COUNT_16(a) +# define FULL_ELEMENT ((setelement)0xFFFF) +#else +# error "SET_ELEMENT_BIT_COUNT(a) not defined for current ELEMENTSIZE" +#endif + +/*** Macros and functions ***/ + +/* + * Gives a value with bit x (counting from lsb up) set. + * + * Making this as a table might speed up things on some machines + * (though on most modern machines it's faster to shift instead of + * using memory). Making it a macro makes it easy to change. + */ +#define SET_BIT_MASK(x) ((setelement)1<<(x)) + +/* Set element handling macros */ + +#define SET_ELEMENT_INTERSECT(a,b) ((a)&(b)) +#define SET_ELEMENT_UNION(a,b) ((a)|(b)) +#define SET_ELEMENT_DIFFERENCE(a,b) ((a)&(~(b))) +#define SET_ELEMENT_CONTAINS(e,v) ((e)&SET_BIT_MASK(v)) + +/* Set handling macros */ + +#define SET_ADD_ELEMENT(s,a) \ + ((s)[(a)/ELEMENTSIZE] |= SET_BIT_MASK((a)%ELEMENTSIZE)) +#define SET_DEL_ELEMENT(s,a) \ + ((s)[(a)/ELEMENTSIZE] &= ~SET_BIT_MASK((a)%ELEMENTSIZE)) +#define SET_CONTAINS_FAST(s,a) (SET_ELEMENT_CONTAINS((s)[(a)/ELEMENTSIZE], \ + (a)%ELEMENTSIZE)) +#define SET_CONTAINS(s,a) (((a)<SET_MAX_SIZE(s))?SET_CONTAINS_FAST(s,a):FALSE) + +/* Sets can hold values between 0,...,SET_MAX_SIZE(s)-1 */ +#define SET_MAX_SIZE(s) ((s)[-1]) +/* Sets consist of an array of SET_ARRAY_LENGTH(s) setelements */ +#define SET_ARRAY_LENGTH(s) (((s)[-1]+ELEMENTSIZE-1)/ELEMENTSIZE) + +/* + * set_new() + * + * Create a new set that can hold values in the range 0,...,size-1. + */ +UNUSED_FUNCTION +static set_t set_new(int size) { + int n; + set_t s; + + ASSERT(size>0); + + n=(size/ELEMENTSIZE+1)+1; + s=calloc(n,sizeof(setelement)); + s[0]=size; + + return &(s[1]); +} + +/* + * set_free() + * + * Free the memory associated with set s. + */ +UNUSED_FUNCTION INLINE +static void set_free(set_t s) { + ASSERT(s!=NULL); + free(&(s[-1])); +} + +/* + * set_resize() + * + * Resizes set s to given size. If the size is less than SET_MAX_SIZE(s), + * the last elements are dropped. + * + * Returns a pointer to the new set. + */ +UNUSED_FUNCTION INLINE +static set_t set_resize(set_t s, int size) { + int n; + + ASSERT(size>0); + + n=(size/ELEMENTSIZE+1); + s=((setelement *)realloc(s-1,(n+1)*sizeof(setelement)))+1; + + if (n>SET_ARRAY_LENGTH(s)) + memset(s+SET_ARRAY_LENGTH(s),0, + (n-SET_ARRAY_LENGTH(s))*sizeof(setelement)); + if (size < SET_MAX_SIZE(s)) + s[(size-1)/ELEMENTSIZE] &= (FULL_ELEMENT >> + (ELEMENTSIZE-size%ELEMENTSIZE)); + s[-1]=size; + + return s; +} + +/* + * set_size() + * + * Returns the number of elements in set s. + */ +UNUSED_FUNCTION INLINE +static int set_size(set_t s) { + int count=0; + setelement *c; + + for (c=s; c < s+SET_ARRAY_LENGTH(s); c++) + count+=SET_ELEMENT_BIT_COUNT(*c); + return count; +} + +/* + * set_duplicate() + * + * Returns a newly allocated duplicate of set s. + */ +UNUSED_FUNCTION INLINE +static set_t set_duplicate(set_t s) { + set_t new; + + new=set_new(SET_MAX_SIZE(s)); + memcpy(new,s,SET_ARRAY_LENGTH(s)*sizeof(setelement)); + return new; +} + +/* + * set_copy() + * + * Copies set src to dest. If dest is NULL, is equal to set_duplicate. + * If dest smaller than src, it is freed and a new set of the same size as + * src is returned. + */ +UNUSED_FUNCTION INLINE +static set_t set_copy(set_t dest,set_t src) { + if (dest==NULL) + return set_duplicate(src); + if (SET_MAX_SIZE(dest)<SET_MAX_SIZE(src)) { + set_free(dest); + return set_duplicate(src); + } + memcpy(dest,src,SET_ARRAY_LENGTH(src)*sizeof(setelement)); + memset(dest+SET_ARRAY_LENGTH(src),0,((SET_ARRAY_LENGTH(dest) - + SET_ARRAY_LENGTH(src)) * + sizeof(setelement))); + return dest; +} + +/* + * set_empty() + * + * Removes all elements from the set s. + */ +UNUSED_FUNCTION INLINE +static void set_empty(set_t s) { + memset(s,0,SET_ARRAY_LENGTH(s)*sizeof(setelement)); + return; +} + +/* + * set_intersection() + * + * Store the intersection of sets a and b into res. If res is NULL, + * a new set is created and the result is written to it. If res is + * smaller than the larger one of a and b, it is freed and a new set + * is created and the result is returned. + * + * Returns either res or a new set that has been allocated in its stead. + * + * Note: res may not be a or b. + */ +UNUSED_FUNCTION INLINE +static set_t set_intersection(set_t res,set_t a,set_t b) { + int i,max; + + if (res==NULL) { + res = set_new(MAX(SET_MAX_SIZE(a),SET_MAX_SIZE(b))); + } else if (SET_MAX_SIZE(res) < MAX(SET_MAX_SIZE(a),SET_MAX_SIZE(b))) { + set_free(res); + res = set_new(MAX(SET_MAX_SIZE(a),SET_MAX_SIZE(b))); + } else { + set_empty(res); + } + + max=MIN(SET_ARRAY_LENGTH(a),SET_ARRAY_LENGTH(b)); + for (i=0; i<max; i++) { + res[i]=SET_ELEMENT_INTERSECT(a[i],b[i]); + } + + return res; +} + +/* + * set_union() + * + * Store the union of sets a and b into res. If res is NULL, a new set + * is created and the result is written to it. If res is smaller than + * the larger one of a and b, it is freed and a new set is created and + * the result is returned. + * + * Returns either res or a new set that has been allocated in its stead. + * + * Note: res may not be a or b. + */ +UNUSED_FUNCTION INLINE +static set_t set_union(set_t res,set_t a,set_t b) { + int i,max; + + if (res==NULL) { + res = set_new(MAX(SET_MAX_SIZE(a),SET_MAX_SIZE(b))); + } else if (SET_MAX_SIZE(res) < MAX(SET_MAX_SIZE(a),SET_MAX_SIZE(b))) { + set_free(res); + res = set_new(MAX(SET_MAX_SIZE(a),SET_MAX_SIZE(b))); + } else { + set_empty(res); + } + + max=MAX(SET_ARRAY_LENGTH(a),SET_ARRAY_LENGTH(b)); + for (i=0; i<max; i++) { + res[i]=SET_ELEMENT_UNION(a[i],b[i]); + } + + return res; +} + + +/* + * set_return_next() + * + * Returns the smallest value in set s which is greater than n, or -1 if + * such a value does not exist. + * + * Can be used to iterate through all values of s: + * + * int i=-1; + * while ((i=set_return_next(s,i))>=0) { + * // i is in set s + * } + */ +UNUSED_FUNCTION INLINE +static int set_return_next(set_t s, int n) { + if (n<0) + n=0; + else + n++; + if (n >= SET_MAX_SIZE(s)) + return -1; + + while (n%ELEMENTSIZE) { + if (SET_CONTAINS(s,n)) + return n; + n++; + if (n >= SET_MAX_SIZE(s)) + return -1; + } + + while (s[n/ELEMENTSIZE]==0) { + n+=ELEMENTSIZE; + if (n >= SET_MAX_SIZE(s)) + return -1; + } + while (!SET_CONTAINS(s,n)) { + n++; + if (n >= SET_MAX_SIZE(s)) + return -1; + } + return n; +} + + +/* + * set_print() + * + * Prints the size and contents of set s to stdout. + * Mainly useful for debugging purposes and trivial output. + */ +UNUSED_FUNCTION +static void set_print(set_t s) { + int i; + printf("size=%d(max %d)",set_size(s),(int)SET_MAX_SIZE(s)); + for (i=0; i<SET_MAX_SIZE(s); i++) + if (SET_CONTAINS(s,i)) + printf(" %d",i); + printf("\n"); + return; +} + +/******************************************************************** +#include "graph.h" +*/ + +typedef struct _graph_t graph_t; +struct _graph_t { + int n; /* Vertices numbered 0...n-1 */ + set_t *edges; /* A list of n sets (the edges). */ + int *weights; /* A list of n vertex weights. */ +}; + + +#define GRAPH_IS_EDGE_FAST(g,i,j) (SET_CONTAINS_FAST((g)->edges[(i)],(j))) +#define GRAPH_IS_EDGE(g,i,j) (((i)<((g)->n))?SET_CONTAINS((g)->edges[(i)], \ + (j)):FALSE) +#define GRAPH_ADD_EDGE(g,i,j) do { \ + SET_ADD_ELEMENT((g)->edges[(i)],(j)); \ + SET_ADD_ELEMENT((g)->edges[(j)],(i)); \ +} while (FALSE) +#define GRAPH_DEL_EDGE(g,i,j) do { \ + SET_DEL_ELEMENT((g)->edges[(i)],(j)); \ + SET_DEL_ELEMENT((g)->edges[(j)],(i)); \ +} while (FALSE) + + +extern graph_t *graph_new(int n); +extern void graph_free(graph_t *g); +extern void graph_resize(graph_t *g, int size); +extern void graph_crop(graph_t *g); + +extern boolean graph_weighted(graph_t *g); +extern int graph_edge_count(graph_t *g); + +extern graph_t *graph_read_dimacs(FILE *fp); +extern graph_t *graph_read_dimacs_file(char *file); +extern boolean graph_write_dimacs_ascii(graph_t *g, char *comment,FILE *fp); +extern boolean graph_write_dimacs_ascii_file(graph_t *g,char *comment, + char *file); +extern boolean graph_write_dimacs_binary(graph_t *g, char *comment,FILE *fp); +extern boolean graph_write_dimacs_binary_file(graph_t *g, char *comment, + char *file); + +extern void graph_print(graph_t *g); +extern boolean graph_test(graph_t *g, FILE *output); +extern int graph_test_regular(graph_t *g); + +UNUSED_FUNCTION INLINE +static int graph_subgraph_weight(graph_t *g,set_t s) { + int i,j; + int count=0; + setelement e; + + for (i=0; i<SET_ARRAY_LENGTH(s); i++) { + if (s[i]) { + e=s[i]; + for (j=0; j<ELEMENTSIZE; j++) { + if (e&1) + count+=g->weights[i*ELEMENTSIZE+j]; + e = e>>1; + } + } + } + return count; +} + +UNUSED_FUNCTION INLINE +static int graph_vertex_degree(graph_t *g, int v) { + return set_size(g->edges[v]); +} + +/******************************************************************** +#include "reorder.h" +*/ + +extern void reorder_set(set_t s,int *order); +extern void reorder_graph(graph_t *g, int *order); +extern int *reorder_duplicate(int *order,int n); +extern void reorder_invert(int *order,int n); +extern void reorder_reverse(int *order,int n); +extern int *reorder_ident(int n); +extern boolean reorder_is_bijection(int *order,int n); + + +#define reorder_by_default reorder_by_greedy_coloring +extern int *reorder_by_greedy_coloring(graph_t *g, boolean weighted); +extern int *reorder_by_weighted_greedy_coloring(graph_t *g, boolean weighted); +extern int *reorder_by_unweighted_greedy_coloring(graph_t *g,boolean weighted); +extern int *reorder_by_degree(graph_t *g, boolean weighted); +extern int *reorder_by_random(graph_t *g, boolean weighted); +extern int *reorder_by_ident(graph_t *g, boolean weighted); +extern int *reorder_by_reverse(graph_t *g, boolean weighted); + + +typedef struct _clique_options clique_options; +struct _clique_options { + int *(*reorder_function)(graph_t *, boolean); + int *reorder_map; + + /* arguments: level, n, max, user_time, system_time, opts */ + boolean (*time_function)(int,int,int,int,double,double, + clique_options *); + FILE *output; + + boolean (*user_function)(set_t,graph_t *,clique_options *); + void *user_data; + set_t *clique_list; + int clique_list_length; +}; + +extern clique_options *clique_default_options; + +/* Weighted clique functions */ +extern int clique_max_weight(graph_t *g,clique_options *opts); +extern set_t clique_find_single(graph_t *g,int min_weight,int max_weight, + boolean maximal, clique_options *opts); +extern int clique_find_all(graph_t *g, int req_weight, boolean exact, + boolean maximal, clique_options *opts); + +/* Unweighted clique functions */ +#define clique_unweighted_max_size clique_unweighted_max_weight +extern int clique_unweighted_max_weight(graph_t *g, clique_options *opts); +extern set_t clique_unweighted_find_single(graph_t *g,int min_size, + int max_size,boolean maximal, + clique_options *opts); +extern int clique_unweighted_find_all(graph_t *g, int min_size, int max_size, + boolean maximal, clique_options *opts); + +/* Time printing functions */ +extern boolean clique_print_time(int level, int i, int n, int max, + double cputime, double realtime, + clique_options *opts); +extern boolean clique_print_time_always(int level, int i, int n, int max, + double cputime, double realtime, + clique_options *opts); + + +/* Alternate spelling (let's be a little forgiving): */ +#define cliquer_options clique_options +#define cliquer_default_options clique_default_options + +/* Procedures defined in nautycliquer.c */ +extern int find_clique(graph *g, int m, int n, + int min, int max, boolean maximal); +extern int find_indset(graph *g, int m, int n, + int min, int max, boolean maximal); + +#endif /* !NAUTYCLIQUER_H */ diff --git a/graph-checker/nauty/planarity.h b/graph-checker/nauty/planarity.h new file mode 100644 index 0000000..babb370 --- /dev/null +++ b/graph-checker/nauty/planarity.h @@ -0,0 +1,457 @@ +/* + data structures and stuff for the planarity algorithm + + Paulette Lieby, Brendan McKay + October 2001 +*/ + +#ifndef _PLANARITY_H_ +#define _PLANARITY_H_ + + + +/* The following line must be uncommented for compiling into Magma. */ +/* #define PLANAR_IN_MAGMA */ + + +#ifdef PLANAR_IN_MAGMA +#include "defs.h" +#include "system.h" /* includes <stdio.h> <signal.h> "system_math.h" + <setjmp.h> <ctype.h> and more + */ +#else +/* not PLANAR_IN_MAGMA */ +#include <stdio.h> +#include <assert.h> +#include <stdlib.h> +#include <time.h> + +#undef FALSE +#undef TRUE +#define FALSE 0 +#define TRUE 1 + +#define NP NULL + +#define ASSERT(x) assert(x) +#define DIE() exit(0) + +#define mem_malloc malloc +#define mem_realloc realloc +#define mem_free free + +#include "naututil.h" +#ifdef CPUDEFS +CPUDEFS +#endif +#define time_current_user() CPUTIME + +#endif /* not PLANAR_IN_MAGMA */ + +#include "nauty.h" + + + + + +/* + max number of edges (and directed edges) for the embed_graph + data structure: + 1 more than for a (possibly) planar graph to allow search for obstructions + + 1. if the graph is planar the embedding cannot possibly contain + more than 3*n - 6 edges (including the short cut edges) + 2. if the graph is non planar, when retrieving and marking the + obstruction, we introduce EXACTLY one edge crossing +*/ +#define MAXE(n) ((n) > 1 ? 3*(n) - 5 : 0) +#define MAXDE(n) (6*(n) - 10) + +#define NIL (-1) +#define CUTV (-2) /* obviously used in diff. circ. than NILSIGN */ +#define NILSIGN (-2) +#define CCLOCKW 1 +#define CLOCKW -1 +#define TE 1 +#define BE 2 +#define SCE 3 + +/* + various "marks" for various purposes, ONLY for the t_ver_edge str + + note: do NOT use a mark in {0,..,n} since those values are + used either while initialising or in the walkup procedure +*/ +#define MARK_EMBED(n) ((n)+1) +#define MARK_EXT_FACE(n) ((n)+2) +#define MARK_EXT_FACE_L(n) ((n)+3) +#define MARK_EXT_FACE_R(n) ((n)+4) +#define MARK_X_Y_PATH(n) ((n)+5) +#define MARK_MINORS(n) ((n)+6) +#define MIN_EMBED_MARK 0 /* ONLY for the t_embed_sparse_rep str */ + + +typedef enum +{ + MINOR_A, + MINOR_B, + MINOR_C, + MINOR_D, + MINOR_E, + MINOR_E1, + MINOR_E2, + MINOR_E3, + MINOR_E4, + MINOR_E5, + NBR_MINORS +} minor; + + +/* + a basic doubly linked circular list storing + vertices/edges from the "big" 2*n + 2(3*n-5) array of vertices/edges + + only used internally in the planarity tester: especially + where ordering of the vertices is important +*/ +typedef struct dlcl { + int info; + /* + info is: + - position in "big" array + */ + int in_adjl; /* if relevant, the pos in the adjl. list + of this edge + */ + int twin_in_adjl; /* if relevant, the pos in the adjl. list + of the twin of this edge + */ + int mult; /* if relevant, #occurences if this edge + (when graph is not simple + */ + struct dlcl * right; + struct dlcl * left; +} t_dlcl; + + +/* + a common structure for both (virtual) vertex & edge +*/ +typedef struct ver_edge { + /* vertex data */ + int label; + int DFS_parent; + int least_ancestor; + int lowpoint; + t_dlcl * separated_DFS_child_list; + t_dlcl * rep_in_parent_list; + t_dlcl * pertinent_bicomp_list; + int adjacent_to; + int visited; + /* edge data */ + int neighbour; + int in_adjl; + int twin_in_adjl; + int mult; + int type; + int sign; + /* link the lot in a doubly linked circular list */ + /* links indicate positions in the array of vertices/edges */ + int link[2]; +} t_ver_edge; + + +/* + data structure for the merge queue +*/ +typedef struct merge_queue { + int start, end; + int *b; +} t_merge_queue; + + + +/* + data structure for the sparse graph representation: + the array of vertices +*/ +typedef struct ver_sparse_rep { + int first_edge; /* can be index into an adj. list + or an embedding */ +} t_ver_sparse_rep; + +/* + data structure for the sparse graph representation: + a record in the adjacency list +*/ +typedef struct adjl_sparse_rep { + int end_vertex; + int next; /* next in list as an index in the adj. list */ +} t_adjl_sparse_rep; + +/* + data structure for the sparse graph representation: + a record in the embedding +*/ +typedef struct embed_sparse_rep { + int in_adjl;/* index of edge in adj. list */ + int next; /* next edge in embedding */ + int prev; /* previous edge in embedding */ + int inv; /* inverse edge */ + int mark; /* a spot for marking */ +} t_embed_sparse_rep; + +/* + data structure for the sparse graph representation: + a record an individual edge +*/ +typedef struct edge_sparse_rep { + int ends[2]; +} t_edge_sparse_rep; + +/* + data structure for the sparse graph representation: + a record for a component +*/ +typedef struct comp_sparse_rep { + int nbr_v; /* nbr of vertices */ + int *v; /* the actual vertices */ +} t_comp_sparse_rep; + +typedef struct graph_sparse_rep { + t_ver_sparse_rep *V; + int n; + t_adjl_sparse_rep *A; + int size_A; + int pos_A; + int nbr_e; /* ALWAYS # directed edges */ +} t_graph_sparse_rep; + +#ifdef __cplusplus +extern "C" { +#endif +/* + * embed_graph_protos.h + */ + +/* aproto: file embed_graph/sparseg_adjl_pred.c */ +extern boolean sparseg_adjl_dir_edge_exists (t_ver_sparse_rep *, int, t_adjl_sparse_rep *, int, int); +extern boolean sparseg_adjl_u_adj_v (t_ver_sparse_rep *, int, t_adjl_sparse_rep *, int, int); +extern boolean sparseg_adjl_sub (t_ver_sparse_rep *, int, t_adjl_sparse_rep *, t_ver_sparse_rep *, int, t_adjl_sparse_rep *); +extern boolean sparseg_adjl_eq (t_ver_sparse_rep *, int, t_adjl_sparse_rep *, t_ver_sparse_rep *, int, t_adjl_sparse_rep *); +/* aproto: endfile */ +/* aproto: file embed_graph/VES_misc.c */ +extern boolean embedg_VES_is_vertex (int, int); +extern boolean embedg_VES_is_virtual_vertex (int, int); +extern boolean embedg_VES_is_edge (int, int); +extern boolean embedg_VES_is_tree_edge (t_ver_edge *, int, int); +extern boolean embedg_VES_is_back_edge (t_ver_edge *, int, int); +extern boolean embedg_VES_is_short_cut_edge (t_ver_edge *, int, int); +extern void embedg_VES_print_vertex (int, int); +extern void embedg_VES_print_virtual_vertex (t_ver_edge *, int, int); +extern void embedg_VES_print_any_vertex (t_ver_edge *, int, int); +extern void embedg_VES_print_any_rec (t_ver_edge *, int, int); +extern void embedg_VES_print_edge (t_ver_edge *, int, int); +extern void embedg_VES_print_flipped_edges (t_ver_edge *, int, int); +extern int embedg_VES_get_edge_from_ver (t_ver_edge *, int, int); +extern int embedg_VES_get_ver_from_edge (t_ver_edge *, int, int); +extern int embedg_VES_get_twin_edge (t_ver_edge *, int, int); +extern int embedg_VES_get_ver_from_virtual (t_ver_edge *, int, int); +extern int embedg_VES_get_ver (t_ver_edge *, int, int); +extern int embedg_VES_get_next_in_dlcl (t_ver_edge *, int, int, int); +extern void embedg_VES_walk_bicomp (t_ver_edge *, int, int, int); +extern void embedg_VES_print_adj_list (t_ver_edge *, int, int, boolean); +extern boolean embedg_VES_is_adj_list_consistent (t_ver_edge *, int, int); +extern boolean embedg_VES_are_adj_lists_consistent (t_ver_edge *, int); +extern void embedg_VES_remove_edge (t_ver_edge *, int, int); +extern void embedg_VES_set_orientation (t_ver_edge *, int, int *); +/* aproto: endfile */ +/* aproto: file embed_graph/embed_edge.c */ +extern void embedg_VES_embed_edge (t_ver_edge *, int, int *, int, int, int, int, int); +extern void embedg_VES_add_edge (t_ver_edge *, int, int *, int, int, boolean, int); +/* aproto: endfile */ +/* aproto: file embed_graph/embedg_misc.c */ +extern void embedg_VES_delete (t_ver_edge *, int); +extern void embedg_VES_print (t_ver_edge *, int); +extern void embedg_VES_print_bigcomps (t_ver_edge *, int); +/* aproto: endfile */ +/* aproto: file embed_graph/ext_face_walk.c */ +extern void embedg_VES_get_succ_on_ext_face (t_ver_edge *, int, int, int, boolean, int, int *, int *); +extern void embedg_VES_get_succ_active_on_ext_face (t_ver_edge *, int, int, int, int, boolean, int, int *, int *); +extern void embedg_VES_get_succ_ext_active_on_ext_face (t_ver_edge *, int, int, int, int, boolean, int, int *, int *); +extern void embedg_VES_get_succ_pertinent_on_ext_face (t_ver_edge *, int, int, int, int, boolean, int, int *, int *); +/* aproto: endfile */ +/* aproto: file embed_graph/isolator.c */ +extern int embedg_iso_get_c_of_v (t_ver_edge *, int, int, int); +extern boolean embedg_iso_is_minor_A (t_ver_edge *, int, int *, int, int, int *); +extern void embedg_iso_get_x_y_w (t_ver_edge *, int, int, int, int, int, int, int, int *, int *, int *); +extern boolean embedg_iso_is_minor_B (t_ver_edge *, int, int *, int, int, int *, int *, int *); +extern void embedg_iso_get_highest_x_y_path (t_ver_edge *, int, int, int, int, int, int, int, int, int, int **, int **, int *, int *, boolean *, boolean *, boolean *); +/* aproto: endfile */ +/* aproto: file embed_graph/mark_kur.c */ +extern boolean embedg_VES_is_ext_face_marked (t_ver_edge *, int, int, int); +extern void embedg_mark_minor_A (t_dlcl **, t_dlcl **, t_ver_edge *, int, int *, int, int, int); +extern void embedg_mark_minor_B (t_dlcl **, t_dlcl **, t_ver_edge *, int, int *, int, int, int, int, int); +extern void embedg_mark_minor_C (t_dlcl **, t_dlcl **, t_ver_edge *, int, int *, int, int, int, int, int, int *, int *, int, boolean, boolean); +extern void embedg_mark_minor_D (t_dlcl **, t_dlcl **, t_ver_edge *, int, int *, int, int, int, int, int, int *, int *, int, int); +extern minor embedg_mark_minor_E (t_dlcl **, t_dlcl **, t_ver_edge *, int, int *, int, int, int, int, int, int *, int *, int); +/* aproto: endfile */ +/* aproto: file embed_graph/merge_bicomps.c */ +extern void embedg_VES_merge_simple_bicomps (t_ver_edge *, int, int, int, int, int); +extern void embedg_VES_merge_pertinent_bicomps (t_ver_edge *, int, int, int, int, int); +/* aproto: endfile */ +/* aproto: file embed_graph/merge_queue_misc.c */ +extern t_merge_queue embedg_merge_queue_new (int); +extern void embedg_merge_queue_delete (t_merge_queue); +extern boolean embedg_merge_queue_empty (t_merge_queue); +extern void embedg_merge_queue_print (t_merge_queue); +extern void embedg_merge_queue_append (t_merge_queue *, t_ver_edge *, int, int, int, int, int); +extern void embedg_merge_queue_append_vertex (t_merge_queue *, t_ver_edge *, int, int, int); +extern void embedg_merge_queue_append_virtual_vertex (t_merge_queue *, t_ver_edge *, int, int, int); +extern void embedg_merge_queue_get (t_merge_queue *, int *, int *, int *, int *); +extern void embedg_merge_queue_prune (t_merge_queue *, int *, int *, int *, int *); +/* aproto: endfile */ +/* aproto: file embed_graph/obstruction.c */ +extern void embedg_obstruction (t_ver_sparse_rep *, t_adjl_sparse_rep *, t_dlcl **, t_dlcl **, t_ver_edge *, int, int *, int, int, t_ver_sparse_rep **, t_adjl_sparse_rep **, int *); +extern minor embedg_mark_obstruction (t_dlcl **, t_dlcl **, t_ver_edge *, int, int *, int, int); +/* aproto: endfile */ +/* aproto: file embed_graph/post_dfs_preproc.c */ +extern t_dlcl *sparseg_order_wrt_lowpoint (int, int *, int *, t_dlcl *); +extern int *sparseg_find_least_ancestor (int, t_dlcl **); +extern int *sparseg_find_dfs_parent (int, t_dlcl **); +/* aproto: endfile */ +/* aproto: file embed_graph/proper_face_walk.c */ +extern boolean embedg_VES_get_succ_on_proper_face_with_avoidance (t_ver_edge *, int, int, int, int, boolean, int, int *, int *, int *); +extern void embedg_VES_get_succ_on_proper_face (t_ver_edge *, int, int, int, int, int, int *, int *, int *); +extern void embedg_VES_walk_proper_face (t_ver_edge *, int, int, int, boolean, int); +/* aproto: endfile */ +/* aproto: file embed_graph/vertex_activity.c */ +extern boolean embedg_VES_is_ver_pertinent (t_ver_edge *, int, int, int); +extern boolean embedg_VES_is_ver_ext_active (t_ver_edge *, int, int, int); +extern boolean embedg_VES_is_ver_int_active (t_ver_edge *, int, int, int); +extern boolean embedg_VES_is_ver_inactive (t_ver_edge *, int, int, int); +/* aproto: endfile */ +/* aproto: file embed_graph/walkdown.c */ +extern t_merge_queue embedg_walkdown (t_ver_edge *, int, int *, int); +/* aproto: endfile */ +/* aproto: file embed_graph/sparseg_dlcl_misc.c */ +extern void sparseg_dlcl_delete (t_dlcl **, int); +extern void sparseg_dlcl_print (t_dlcl **, int); +extern boolean sparseg_dlcl_is_adjacent (t_dlcl **, int, int, int, t_dlcl **); +extern void sparseg_dlcl_append_to_neigh_list (t_dlcl **, int, int, int, int); +extern void sparseg_dlcl_to_sparseg (t_dlcl **, int, int, t_ver_sparse_rep **, t_adjl_sparse_rep **); +extern boolean sparseg_dlcl_sub (t_dlcl **, int, t_dlcl **, int); +/* aproto: endfile */ +/* aproto: file embed_graph/walkup.c */ +extern void embedg_walkup (t_ver_edge *, int, int, t_dlcl *); +/* aproto: endfile */ +/* aproto: file embed_graph/dfs_preprocessing.c */ +extern void sparseg_adjl_dfs_preprocessing (t_ver_sparse_rep *, int, t_adjl_sparse_rep *, int *, int **, int **, int **, t_dlcl ***, t_dlcl ***, int **, int **, t_dlcl ***); +/* aproto: endfile */ +/* aproto: file embed_graph/planar_by_edge_addition.c */ +extern boolean sparseg_adjl_is_planar (t_ver_sparse_rep *, int, t_adjl_sparse_rep *, int *, t_dlcl ***, t_dlcl ***, t_dlcl ***, t_ver_edge **, int *, int *, int *); +/* aproto: endfile */ +/* aproto: file embed_graph/recover.c */ +extern void embedg_recover_embedding (t_ver_sparse_rep *, t_adjl_sparse_rep *, t_ver_edge *, int, int, t_dlcl **, t_ver_sparse_rep **, t_embed_sparse_rep **); +extern void embedg_recov_embed_walk_proper_face (int, int, t_adjl_sparse_rep *, t_embed_sparse_rep *, boolean, int); +extern boolean embedg_check_recov_embedding (int, int, int, t_ver_sparse_rep *, t_adjl_sparse_rep *, t_embed_sparse_rep *); +extern t_dlcl **embedg_recover_obstruction (t_ver_edge *, int, minor, int *); +extern boolean embedg_check_recov_obs (t_dlcl **, int, minor); +/* aproto: endfile */ +/* aproto: file embed_graph/planar_alg_init.c */ +extern t_ver_edge *embedg_planar_alg_init (t_ver_sparse_rep *, int, t_adjl_sparse_rep *, int *, int *, t_dlcl ***, t_dlcl ***, t_dlcl ***); +/* aproto: endfile */ +/* aproto: file embed_graph/dlcl_misc.c */ +extern t_dlcl *embedg_dlcl_rec_new (int); +extern void embedg_dlcl_rec_print (t_dlcl *); +extern void embedg_dlcl_print (t_dlcl *); +extern t_dlcl *embedg_dlcl_rec_append (t_dlcl *, t_dlcl *); +extern t_dlcl *embedg_dlcl_rec_prepend (t_dlcl *, t_dlcl *); +extern t_dlcl *embedg_dlcl_cat (t_dlcl *, t_dlcl *); +extern t_dlcl *embedg_dlcl_find (t_dlcl *, int); +extern t_dlcl *embedg_dlcl_find_with_NIL_twin_in_adjl (t_dlcl *, int); +extern t_dlcl *embedg_dlcl_delete_first (t_dlcl *); +extern t_dlcl *embedg_dlcl_delete_rec (t_dlcl *, t_dlcl *); +extern boolean embedg_dlcl_is_empty (t_dlcl *); +extern t_dlcl *embedg_dlcl_list_next (t_dlcl *); +extern t_dlcl *embedg_dlcl_list_prev (t_dlcl *); +extern t_dlcl *embedg_dlcl_list_last (t_dlcl *); +extern void embedg_dlcl_delete (t_dlcl *); +extern t_dlcl *embedg_dlcl_copy (t_dlcl *); +extern int embedg_dlcl_length (t_dlcl *); +/* aproto: endfile */ +/* aproto: file embed_graph/sparseg_adjl.c */ +extern boolean sparseg_adjl_plan_and_iso (t_ver_sparse_rep *, int, t_adjl_sparse_rep *, int, int *, t_ver_sparse_rep **, t_adjl_sparse_rep **, t_embed_sparse_rep **, int *); +extern int *sparseg_adjl_footprint (t_ver_sparse_rep *, int, t_adjl_sparse_rep *, int); +extern void sparseg_adjl_print (t_ver_sparse_rep *, int, t_adjl_sparse_rep *, boolean); +extern void sparseg_adjl_embed_print (t_ver_sparse_rep *, int, t_adjl_sparse_rep *, t_embed_sparse_rep *, boolean); +extern graph *sparseg_adjl_to_nauty_graph (t_ver_sparse_rep *, int, t_adjl_sparse_rep *); +extern t_edge_sparse_rep *sparseg_adjl_edges (t_ver_sparse_rep *, int, t_adjl_sparse_rep *, int, boolean); +/* aproto: endfile */ +/* aproto: file embed_graph/embedding.c */ +extern void embedg_embedding (t_ver_sparse_rep *, t_adjl_sparse_rep *, t_ver_edge *, int, int, int, int, t_dlcl **, t_ver_sparse_rep **, t_embed_sparse_rep **); +extern void embedg_remove_SCE (t_ver_edge *, int, int); +extern int *embedg_vertices_orientation (t_ver_edge *, int); +extern int embedg_merge_remaining_virtual (t_ver_edge *, int); +extern int embedg_nbr_faces (t_ver_edge *, int, int, int *, int *); +extern boolean embedg_is_embed_valid (t_ver_edge *, int, int, int, int *, int *); +/* aproto: endfile */ +/* aproto: file embed_graph/sparseg_adjl_modify.c */ +extern boolean sparseg_adjl_add_edge (t_ver_sparse_rep *, int, t_adjl_sparse_rep **, int *, int *, int, int, boolean); +extern boolean sparseg_adjl_add_edge_no_extend (t_ver_sparse_rep *, int, t_adjl_sparse_rep *, int, int *, int, int, boolean); +extern boolean sparseg_adjl_add_dir_edge (t_ver_sparse_rep *, int, t_adjl_sparse_rep **, int *, int *, int, int, boolean); +extern boolean sparseg_adjl_add_dir_edge_no_extend (t_ver_sparse_rep *, int, t_adjl_sparse_rep *, int, int *, int, int, boolean); +extern boolean sparseg_adjl_remove_edge_no_red (t_ver_sparse_rep *, t_adjl_sparse_rep *, int, int); +extern boolean sparseg_adjl_remove_dir_edge_no_red (t_ver_sparse_rep *, t_adjl_sparse_rep *, int, int); +extern int sparseg_adjl_remove_all_dir_edge_no_red (t_ver_sparse_rep *, t_adjl_sparse_rep *, int, int); +extern void sparseg_adjl_add_vertices (t_ver_sparse_rep **, int, int); +extern void sparseg_adjl_add_vertices_no_extend (t_ver_sparse_rep *, int, int); +extern void sparseg_adjl_remove_vertex (t_ver_sparse_rep **, int, t_adjl_sparse_rep *, int, int, int *); +extern void sparseg_adjl_remove_vertex_no_red (t_ver_sparse_rep *, int, t_adjl_sparse_rep *, int, int *); +extern void sparseg_adjl_relabel_vertex (t_adjl_sparse_rep *, int, int); +/* aproto: endfile */ +/* aproto: file embed_graph/sparseg_adjl_misc.c */ +extern void sparseg_adjl_assign_V (t_ver_sparse_rep *, t_ver_sparse_rep *, int); +extern t_ver_sparse_rep *sparseg_adjl_dup_V (t_ver_sparse_rep *, int); +extern void sparseg_adjl_assign_A (t_adjl_sparse_rep *, t_adjl_sparse_rep *, int); +extern t_adjl_sparse_rep *sparseg_adjl_dup_A (t_adjl_sparse_rep *, int); +extern void sparseg_embed_assign_E (t_embed_sparse_rep *, t_embed_sparse_rep *, int); +extern t_embed_sparse_rep *sparseg_embed_dup_E (t_embed_sparse_rep *, int); +extern void sparseg_adjl_underlying_undir (t_ver_sparse_rep *, int, t_adjl_sparse_rep *, int, t_ver_sparse_rep **, t_adjl_sparse_rep **, int *, int *); +extern void sparseg_adjl_edge_union (t_ver_sparse_rep *, int, t_adjl_sparse_rep *, int, t_ver_sparse_rep *, t_adjl_sparse_rep *, boolean, t_ver_sparse_rep **, t_adjl_sparse_rep **, int *, int *); +extern void sparseg_adjl_add_edges (t_ver_sparse_rep *, int, t_adjl_sparse_rep *, int, int *, t_ver_sparse_rep *, t_adjl_sparse_rep *, boolean, int *); +/* aproto: endfile */ +/* aproto: file embed_graph/degree.c */ +extern int *sparseg_adjl_degree_seq (t_ver_sparse_rep *, int, t_adjl_sparse_rep *); +/* aproto: endfile */ +/* aproto: file embed_graph/neighbours.c */ +extern void sparseg_adjl_vertex_out_neighbours (t_ver_sparse_rep *, int, t_adjl_sparse_rep *, int, int **, int *); +extern void sparseg_adjl_vertex_in_neighbours (t_ver_sparse_rep *, int, t_adjl_sparse_rep *, int, int **, int *); +/* aproto: endfile */ +/* aproto: file embed_graph/misc.c */ +extern void sparseg_comp_delete (t_comp_sparse_rep *, int); +extern void sparseg_comp_print (t_comp_sparse_rep *, int); +/* aproto: endfile */ +/* aproto: file embed_graph/dfs_things.c */ +extern void sparseg_adjl_dfs_things (t_ver_sparse_rep *, int, t_adjl_sparse_rep *, int, boolean, int *, t_ver_sparse_rep **, t_adjl_sparse_rep **, int *, int **, int **, int **, int **, int **, int *, int **, int *, t_comp_sparse_rep **); +extern void sparseg_adjl_dfs_add_vertex_to_comp (t_comp_sparse_rep **, int *, int); +extern void sparseg_adjl_dfs_create_new_comp (t_comp_sparse_rep **, int *); +extern void sparseg_adjl_from_comps_to_sparseg_adjl (t_ver_sparse_rep *, int, t_adjl_sparse_rep *, t_comp_sparse_rep *, int, t_graph_sparse_rep **); +extern void sparseg_adjl_from_start_comp_to_sparseg_adjl (t_ver_sparse_rep *, int, t_adjl_sparse_rep *, int *, t_graph_sparse_rep **); +/* aproto: endfile */ +/* aproto: file embed_graph/dfs_for_digraph.c */ +extern void sparseg_adjl_dfs_digraph (t_ver_sparse_rep *, int, t_adjl_sparse_rep *, int, int, t_ver_sparse_rep **, t_adjl_sparse_rep **, int *, int **, int **, int **, int **, int **, int *, t_comp_sparse_rep **); +/* aproto: endfile */ +/* aproto: file embed_graph/bfs.c */ +extern void sparseg_adjl_bfs (t_ver_sparse_rep *, int, t_adjl_sparse_rep *, boolean, int, t_ver_sparse_rep **, t_adjl_sparse_rep **, int *); +/* aproto: endfile */ +/* aproto: file embed_graph/faces.c */ +extern void sparseg_adjl_walk_proper_face (int, t_adjl_sparse_rep *, t_embed_sparse_rep *, int, boolean, int, int *, t_edge_sparse_rep *); +extern void sparseg_adjl_get_face_edges (int, t_adjl_sparse_rep *, int, t_embed_sparse_rep *, int, t_edge_sparse_rep **, int *, boolean, int); +/* aproto: endfile */ + +#ifdef __cplusplus +} +#endif + +#endif /* _PLANARITY_H_ */ diff --git a/graph-checker/nauty/quarticirred28.h b/graph-checker/nauty/quarticirred28.h new file mode 100644 index 0000000..14ca82e --- /dev/null +++ b/graph-checker/nauty/quarticirred28.h @@ -0,0 +1,316 @@ +static char *irred[] = { +"D~{\n", +"I~{?GKF@w\n", +"I^}A?KF@w\n", +"G~`HW{\n", +"N~{?GKF@w??@?B?B_@w\n", +"N^{?GKF@{?G??B?B_@w\n", +"L~?GW[N_A?cBCF\n", +"M^{??KF@{?G@@B?b_\n", +"N^}??KF@y??O?B?B_@w\n", +"K~`GW[_CGD_N\n", +"L~`HW[O?O@_F?N\n", +"S~{?GKF@w??@?B?B_@w????C??W??w??{\n", +"S^{?GKF@w??@?B?B_@{??O????W??w??{\n", +"Q~?GW[N???_B?F?Fo?A?@G?KO?w\n", +"R^{??KF@w??@?B?B_@{??O?CC?WC?w\n", +"S^{??KF@{?G??B?B_@wG??_???W??w??{\n", +"Q~??W[N_A?cBCFA??_??B??[?@w\n", +"O~?GW]?OH@aFA?@?_OWAF\n", +"Q~??W[N_A??B?F?Fc??_@A?KC?w\n", +"S^{?GKF@{????B?B_@y???@???W??w??{\n", +"P~?GW[N_A?_B?FG?A?G?J??{\n", +"Q~?GW[N_A?cB?FC???O?B??[?@w\n", +"R^{??KF@{????B?B_@y??@?CA?W?Gw\n", +"P~??W[N_A?cB?FC?@?GGB??{\n", +"S^{??KF@{??_?B?B_@y???_???W??w??{\n", +"O~?GW]?OGP_fG?C?_OWAF\n", +"Q~??W[N_A?cBAFC??_??B??[?@w\n", +"O~?GW]?OH@`FC?@?_OWAF\n", +"Q^{??KF@{??`?B?Bg??C@??k?@w\n", +"P~??W[N_A?`B?FG?A?GGB??{\n", +"O~`GW[_?g@_FC??O_@W?N\n", +"X~{?GKF@w??@?B?B_@w????C??W??w??{??????G??@_??F???N\n", +"X^{?GKF@w??@?B?B_@w????C??W??w??}???O?????@_??F???N\n", +"V~?GW[N???_B?F?F_???@??K??w?@{??@??@G??W_?B_\n", +"W^{??KF@w??@?B?B_@w????C??W??w??}???O??GG?@_O?F\n", +"X^{??KF@w??@?B?B_@{??O????W??w??{C???_????@_??F???N\n", +"V~??W[N???_B?F?Fo?A?@G?KO?wO??G????B???w??F_\n", +"T~?GW[??G@_F?N_?G?H?BC?[G??G?GC?K@?F\n", +"V~??W[N???_B?F?Fo?A????K??w?@x???O?@A??WG?B_\n", +"W^{??KF@w????B?B_@{??O????W??w??{C???_?G?O@_?_F\n", +"U~??W[N????B?F?Fo?A?@G?KO?wO??G?C?OB?A?w\n", +"X^{?GKF@w??@?B?B_@{???????W??w??|?????A???@_??F???N\n", +"U~?GW[N???_B?F?Fo?A?@??K??x???_?C??J??@w\n", +"V~?GW[N???_B?F?Fo?A?@G?K??w_????G??B???w??F_\n", +"W^{??KF@w??@?B?B_@{???????W??w??|???@??GC?@_?@F\n", +"U~??W[N???_B?F?Fo?A?@G?K??w_??O?CC?B??@w\n", +"X^{??KF@w??@?B?B_@{??@????W??w??|????_????@_??F???N\n", +"T~?GW[??G@_F?N_?G?GOB@?[_??_?GC?K@?F\n", +"V~??W[N???_B?F?Fo?A?@G?KG?w_??G????B???w??F_\n", +"T~?GW[??G@_F?N_?G?H?BA?[O??G?GC?K@?F\n", +"W^{??KF@w????B?B_@{??@????W??w??|????_?G?O@_?_F\n", +"U~??W[N????B?F?Fo?A?@G?KG?w_??G?C?OB?A?w\n", +"V^{??KF@w??@?B?B_@{??@?C??W??y???A?@??@W??F_\n", +"U~??W[N???_B?F?Fo?A?@A?K??x???_?CC?B??@w\n", +"V^{??KF@w????B?B_@{??@?CA?W??y????G@?@?W??F_\n", +"U~??W[N????B?F?Fo?A?@A?K?Ox???_?CC?B?A?w\n", +"V^{??KF@w????B?B_@{??@?C?GW??y???A?@?@?W??F_\n", +"U~?GW]?O?@_F?NA??_??B??[?@x???_?CA?B?O?w\n", +"X^{??KF@{????B?B_@wG??_???W??w??|????@????@_??F???N\n", +"V^{?GKF_???B?F?F__?C@?OK?_y????O???B???w??F_\n", +"U~??W[N_A?_B?FA??_??B??[?@x???_?C?CB??_w\n", +"S~?GW]?OG@_FA?@?_OWAFG??O?G?OW?G[\n", +"V~??W[N_A?cB?FA??_??B??[?@w_???C???B???w??F_\n", +"T~?GW]?OH@_FA?@?_OWAFC???C???W??[??N\n", +"V^{?GKF_???B?F?F__?C???K??w?@y???@?@?G?W?OB_\n", +"T~?GW]?OH@_FA?@???W?F??{O??C?GA?K?CF\n", +"W^{??KF@{????B?B_@wG??????W??w??|????_?G?G@_?@F\n", +"U~??W[N_A?cB?FA?????B??[?@w_??G?C?AB??Gw\n", +"S~?GW]?OH@_FA?@?_OW?FC??@?G?GW??{\n", +"X^{??KF@{????B?B_@y??@????W??w??{A???@????@_??F???N\n", +"S~?GW]?OG@_FG?C?`?WGF?_?@?G?OW?G[\n", +"V~??W[N_A?cB?FC?@???B??[?@wG???C???B???w??F_\n", +"T~?GW]?OH@_FC?A?__WCF?O??C???W??[??N\n", +"V~??W[N_???B?F?Fg??O???K??w?@x???O?@@??W?OB_\n", +"T~?GW]???@_F?NO?@?GGB?_[_??_?GA?K?OF\n", +"T~?GW]???@_F?NO?C?GOB@?[O??C?GA?K?OF\n", +"U~??W[N_A?_B?FG?@???B??[?@w_??G?C?CB??_w\n", +"S~?GW]?OG@_FG?A?__WCFC??@?G?OW?G[\n", +"U~??W[N_???B?F?Fg?@?@A?K??w_??G?C?GB??@w\n", +"T~??W[N_??_B?FO?C?G?R?@[O??O?GG?K?AF\n", +"U~??W[N_A?_B?FG???_?B??[?@w_??O?CC?B??_w\n", +"T~?GW[N_A?_B?FG???g?B??[O???CG??k??N\n", +"T~??W[N_A?_B?FG?@?G?J??[O??G?G?CK??N\n", +"S~?GW]?AG@_FO?G?__WCFC??@?G?OW?G[\n", +"T~?GW]?OGP_FG?@???W?F??{O??C?GA?K?CF\n", +"S~?GW]?OGP_FG?@?_OW?FC??@?G?GW??{\n", +"S~_GW\\?@G@_FG??__@W?FC??@?G?GW??{\n", +"[~?GW[N???_B?F?F_???@??K??w?@w?????@???W??B_??N_???O???c???W_??F\n", +"[~??W[N???_B?F?F_???@??K??w?@{??@??@G??W_?B`???@??????@_???w???N\n", +"Y~?GW[??G@_F?N????G?B??[?@{??A??C_?BC??wO???_?@?_?B?O?B_\n", +"[~??W[N???_B?F?F_???@??K??w?@{??@??????W??B_??NG???C???`???WG??F\n", +"Z~??W[N????B?F?F_???@??K??w?@{??@??@G??W_?B`???@??@?C?@_@??w\n", +"Z~?GW[N???_B?F?F_???@??K??w?@{??@??@???W??Bc???C??@???D_??@w\n", +"[~?GW[N???_B?F?F_???@??K??w?@{??@??@G??W??Ba??????A???@_???w???N\n", +"Z~??W[N???_B?F?F_???@??K??w?@{??@??@G??W??Ba???A??@@??@_??@w\n", +"Y~?GW[??G@_F?N????G?B??[?@{??A??CG?B@??x???A??@?_?B?O?B_\n", +"[~??W[N???_B?F?F_???@??K??w?@{??@??@G??WO?Ba???@??????@_???w???N\n", +"Y~?GW[??G@_F?N????G?B??[?@{??A??C_?BA??w_???_?@?_?B?O?B_\n", +"[^{??KF@w????B?B_@w???????W??w??}???O??GG?@_O?F?A???@??_?A?W??OF\n", +"Z~??W[N????B?F?F_???@??K??w?@{??@??@G??WO?Ba???@??@?C?@_@??w\n", +"[^{??KF@w??@?B?B_@w????C??W??w??}???@??G??@_??FO????_??_??@W???N\n", +"Z~??W[N???_B?F?F_???@??K??w?@{??@??@A??W??Bc???C??@@??@_??@w\n", +"[^{??KF@w????B?B_@w????C??W??w??}???@??GC?@_??FO????A??_?_?W???N\n", +"Z~??W[N????B?F?F_???@??K??w?@{??@??@A??W?_Bc???C??@@??@_@??w\n", +"[^{??KF@w????B?B_@w???????W??w??}???@??G?O@_?_FO????_??_?A?W??OF\n", +"[^{??KF@w????B?B_@w????C??W??w??}???@??G?O@_??FO????_??_?_?W???N\n", +"[^{??KF@w????B?B_@w???????W??w??}???@??G?O@_?AFO????_??_?_?W??OF\n", +"[~??W[N????B?F?Fo?A?@G?KO?wO??G????B???w??F_A???A?????@_???w???N\n", +"Y~?GW[???@_F?N_?G?H?BC?[G??G?GC?K@?F?G???_????B???F???F_\n", +"W~?GW[??G@_F_?O?c?W_FA??C?GC?WA?[?_??C?G?O@_?_F\n", +"Z~?GW[??G@_F?N_?G???B??[?@wO??G????B???w??Fc???C??@?_?@_G??w\n", +"[~??W[N????B?F?Fo?A????K??w?@wO??C?????W??B_??NG???C???_A??W?O?F\n", +"Y~?GW[???@_F?N_?G???B??[?@wO??G?CA?B?O?x???A??@?G?B?C?B_\n", +"[^{?GKF???_B?F?Fo??????K??w?@wG??A?@?O?W@?Bg?????C????@_???w???N\n", +"Z~??W[N???_B?F?Fo?A?@??K??wO??G????B???w??Fc???C??@??A@_??_w\n", +"X~?GW[??G@_F?N_?G?G?B??[G??G?GC?K@?FG???_??_?AB??AF\n", +"[~??W[N???_B?F?Fo?A?@G?K??wO??G????B???w??Fa?????@????@_???w???N\n", +"Y~?GW[??G@_F?N_?G?H?B??[G??G?GC?K@?FC?????O???B???F???F_\n", +"[^{?GKF???_B?F?Fo??????K??w?@wG??A?????W??B_??NO????O??_C??W??_F\n", +"Y~?GW[??G@_F?N_?G?H?B??[G??G????K??F??@w_???O?@?O?B??AB_\n", +"Z^{?GKF????B?F?Fo??????K??w?@wG??A?@?O?W@?Bg????C?@?A?@_?A?w\n", +"Z~??W[N????B?F?Fo?A?@G?K??wO??G????B???w??Fa????C?@?A?@_??_w\n", +"X~?GW[???@_F?N_?G?H?B??[G??G?GC?K@?FC???@??_A?B??AF\n", +"Z~??W[N???_B?F?Fo?A?@G?K??wO???????B???w??Fa???@??@??@@_??Gw\n", +"X~?GW[??G@_F?N_?G?H?B??[G??G?GC?K??FC???A??_?@B???N\n", +"X~?GW[??G@_F?N_?G?G?B??[_??_?GO?KC?F?_??A??_?AB??AF\n", +"[~??W[N???_B?F?Fo?A?@G?K??w_??O????B???w??F__????@????@_???w???N\n", +"Y~?GW[??G@_F?N_?G?H?B??[O??O?GG?KA?F?O????O???B???F???F_\n", +"[~??W[N????B?F?Fo?A????K??w?@x???G?????W??B_??NC???@???_A??W?O?F\n", +"Y~?GW[???@_F?N_?G???B??[?@x???O?CC?B?_?w_???G?@?G?B?C?B_\n", +"[~??W[N???_B?F?Fo??????K??w?@y???G?????W??B_??NG???C???__??W??_F\n", +"Y~?GW[??G@_F?N_?????B??[?@y???O?CC?B?_?x???A??@?O?B??GB_\n", +"Y~?GW[??G@_F?N_?????B??[?@y??@??CG?B@??w_???O?@?O?B??GB_\n", +"Z~??W[N???_B?F?Fo?A?@??K??x???O????B???w??Fa???@??@??A@_??_w\n", +"X~?GW[??G@_F?N_?G?G?B??[_??O?GG?KA?FC???A??_?AB??AF\n", +"Z~??W[N????B?F?Fo??????K??w?@y???_?@C??WO?B__???C?@?A?@_?A?w\n", +"Z~??W[N????B?F?Fo?A?@G?K??w_??O????B???w??F__???C?@?A?@_??_w\n", +"X~?GW[???@_F?N_?G?H?B??[O??O?GG?KA?F?O??@??_A?B??AF\n", +"Z~??W[N???_B?F?Fo?A????K??w?@x???G?@???W??Ba???@??@???D_??@w\n", +"Y~?GW[??G@_F?N_?G???B??[?@x???O?CC?B???w_???O?@?O?B???F_\n", +"Z~??W[N????B?F?Fo?A????K??w?@x???G?@@??W??Ba????C?@?A?@_??@w\n", +"Z~??W[N???_B?F?Fo??????K??w?@y???_?@A??W??Ba???@??@??C@_??@w\n", +"Y~?GW[???@_F?N_?G???B??[?@wO??G?C?_B?C?x???A??@?_?B?O?B_\n", +"Y^{?GKF???_B?F_?G???B??[?@wG??C?C?OB?A?wA???C?@?@?B??_B_\n", +"[^{?GKF????B?F?Fo?A????K??w?@wG??A?@?O?W?_B_C???A?????@_???w???N\n", +"Y^{?GKF???_B?F_?G???B??[?@wG??C?C@?B?C?w@???@?@?@?B??_B_\n", +"[~??W[N????B?F?Fo?A?@G?KO?wO???_???B???w??F__???A?????@_???w???N\n", +"W~?GW[??G@_F_?O?c?W_FA??C?G@?W?_[A???O?G?O@_?_F\n", +"Y~?GW[???@_F?N_?G?H?BC?[G??G?GC?K?_F?O???_????B???F???F_\n", +"W~?GW[??G@_F_?O?c?W_FA??C?GC?W@?[@???C?G?O@_?_F\n", +"Y~??W[N????B?F?Fo?A?@??K??wO??G?C?OB?A?x???A??@??AB??@B_\n", +"Z~??W[N????B?F?Fo?A?@G?K??wO??G?C?OB?A?w_?????_???B???B_??@w\n", +"[~??W[N????B?F?Fo?A????K??w?@wO???O????W??B_??NG???C???__??W?O?F\n", +"Y~?GW[???@_F?N_?G???B??[?@wO??G?CA?B?G?x???A??@?O?B?C?B_\n", +"Z^{?GKF????B?F?Fo??????K??w?@wG??A?@?C?W?OBg????O?@?G?@_?A?w\n", +"X~?GW[???@_F?N_?G?H?B??[G??G?G@?K?OFC???C??_G?B??AF\n", +"Z~??W[N????B?F?Fo?A????K??w?@wO???O@?A?W??Bc???C??@@??@_??@w\n", +"[^{??KF@w????B?B_@{??O????W??w??{C???A?G??@_??F?_???@??_??@W???N\n", +"Z^{?GKF????B?F?Fo?A????K??w?@wG??A?@?C?W??B_G???G?@?A?@_??@w\n", +"Y~??W[N????B?F?Fo?A?@G?KO?wO???_C??B???wG???@?@???J???F_\n", +"X~?GW[???@_F?N_?G?H?BC?[G??G?G@?K??F?_??A??_A?B???N\n", +"Z^{?GKF????B?F?Fo??????K??w?@wG??A?@?O?W?_Bg????G?@?A?@_?A?w\n", +"Z~??W[N????B?F?Fo?A?@G?K??wO???_???B???w??Fa???@??@?A?@_??_w\n", +"X~?GW[???@_F?N_?G?H?B??[G??G?GC?K?_FC???A??_A?B??AF\n", +"[^{??KF@w????B?B_@{???????W??w??{C???A?G?G@_??FO????_??_?@?W???N\n", +"Y~??W[N????B?F?Fo?A?@G?K??wO???_C?GB???w_???_?@??@B???F_\n", +"Y~?GW[N???_B?F?Fo?A?@??K??x?????S??B???w_?????`???J???F_\n", +"Z^{?GKF????B?F?Fo??????K??w?@y???C?@?_?W?_B_G???G?@?A?@_?A?w\n", +"Y~??W[N????B?F?Fo?A?@??K??x???_?CG?B?C?wG???@?@??AB??@B_\n", +"X~?GW[???@_F?N_?G?H?B??[O??O?GG?K?_F?_??A??_A?B??AF\n", +"Z~??W[N????B?F?Fo??????K??w?@y???_?@A??W?_Ba???@??@?A?@_?A?w\n", +"Z~??W[N????B?F?Fo?A????K??w?@x???G?@?C?W??Ba???@??@?A?@_??@w\n", +"[^{??KF@w????B?B_@{???????W??w??|???@??G?O@_??F?_???@??_?@?W???N\n", +"Y~??W[N????B?F?Fo?A?@G?K??w_??O?C?OB???wG???@?@??@B???F_\n", +"Y~??W[N???_B?F?Fo?A?@??K??x???O?C??J???w_???_?@??@B???F_\n", +"Y~??W[N????B?F?Fo?A?@??K??x???O?C?OB??Aw_???_?@?A?B??@B_\n", +"Y~?GW[???@_F?N_?G?GOB@?[_??C????K??F??@w_???G?@?G?B?C?B_\n", +"X~?GW[??G@_F?N_?@?G?B??\\??@??GG?KA?FC???A??_?AB??AF\n", +"Y~?GW[??G@_F?N_?G?GOB??[_??G????K??F??@w_???O?@?O?B??AB_\n", +"X~?GW[??G@_F?N_?G?GOB??[_??G?GC?K??FC???A??_?@B???N\n", +"W~?GW[??G@_F_?O?`?WGFG??O?G@?W?_[A???O?G?O@_?_F\n", +"[~??W[N????B?F?Fo?A?@G?KG?w_???_???B???w??F__???A?????@_???w???N\n", +"Y~??W[N???_B?F_?G?H?BA?[O???_???K??F??@wG???@?@?@?B??_B_\n", +"Y~?GW[???@_F?N_?G?H?BA?[O??@????K??F??@wG???O?@?O?B?C?B_\n", +"W~?GW[??G@_F_?O?c?WOFC???_G?_W?O[C???_?GA?@_?_F\n", +"[~??W[N????B?F?Fo??O???K??w?@y????O????W??B_??NG???C???__??W?O?F\n", +"Y~?GW[???@_F?N_?@?GGB?_\\???@????K??F??@x???A??@?O?B?C?B_\n", +"W~?GW[??G@_F_?A?__WCFO???_G?_W?O[_??C??GA?@_?_F\n", +"Y~?GW[???@_F?N_?G?GOB@?[_??@????K??F??@w_???O?@?O?B?C?B_\n", +"W~?GW[??G@_F_?O?`?WGFG???_G?_W?O[O???_?GA?@_?_F\n", +"Z~??W[N????B?F?Fo??O@@?K??y????_???B???w??Fc???C??@?A?@_??_w\n", +"X~??W[N???_B?F_?@?GGB??\\????_G?OK?CFG???_??_?OB??AF\n", +"X~??W[N???_B?F_?@?GGB??\\??@??G?_K?GFC????G?_?OB??AF\n", +"[^{??KF@w????B?B_@{??@?C??W??y????G????W??B_??N?_???@??_??_W??CF\n", +"Z^{?GKF????B?F?Fo??G@?_K??y????_???B???w??F_G???G?@?A?@_??_w\n", +"Y~??W[N????B?F?Fo??O@??K??y??@??CO?B?C?wG???@?@??AB??@B_\n", +"X~?GW[???@_F?N_?@?GGB??\\??@??G_?K?_F?_??A??_A?B??AF\n", +"X~??W[N???_B?F_?G?GOB??[_??_?G?_K?GF@????G?_?OB??AF\n", +"Y~??W[N????B?F?Fo??O@@?K??y????_C?GB???x???A??@??@B???F_\n", +"Z^{??KF@w????B?B_@{??@?C??W??y????G@?@?W??B_O????@@???`_??@w\n", +"Y^{?GKF????B?F?Fo??G@?_K??y????_C?GB???wA???C?@??@B???F_\n", +"Z~??W[N????B?F?Fo?A?@A?K??x????_???B???w??Fa???@??@?A?@_??_w\n", +"X~??W[N???_B?F_?G?GOB??[_???_G?OK?CFC???G??_?OB??AF\n", +"Y~??W[N????B?F?Fo?A?@A?K??x????_C?GB???w_???_?@??@B???F_\n", +"Y~??W[N????B?F?Fo?A?@A?K??x???_?C?OB???wG???@?@??@B???F_\n", +"Y~?GW[???@_F?N_?@???B??[?@y??@??CC?B?G?w_???O?@?O?B?C?B_\n", +"W~?GW[??G@_F_?O?`?WGFG??A?G@?W?_[O???O?G?O@_?_F\n", +"X~?GW[???@_F?N_?G?GOB??[_??G?G@?K?OFC???C??_G?B??AF\n", +"X~?GW[???@_F?N_?G?GOB??[_??G?GC?K?_FC???A??_A?B??AF\n", +"X~?GW[???@_F?N_?G?GOB@?[_??C?G@?K??FC???A??_A?B???N\n", +"Y~??W[N????B?F?Fo??O@??K??y??@??C?OB??Aw_???_?@?A?B??@B_\n", +"Z^{??KF@w????B?B_@{??@?C??W??y????G@??@W??B_O???@?@???`_??@w\n", +"W~?GW[??G@_F_?O?`?W@FG??C?GC?W?_[O???O?G?O@_?_F\n", +"Z~??W[N_A?_B?FA?????B??[?@x???_?C?CB??_wG????C????B???B_??@w\n", +"W~?GW]?OG@_FA?@?_?W?FG??O?G?OW?G[A???O?G?@@_?AF\n", +"[~??W[N_A?cB?FA?????B??[?@w_???C???B???w??F__????G????@_???w???N\n", +"X~?GW]?OH@_FA?@?_?W?FC???C???W??[??N?_??A??_?CB??CF\n", +"Y~?GW]?OH@_FA?@?_OW?FC???C???W??[??N?O????_???B???F???F_\n", +"X~?GW]??G@_FA?@???W?F??|??@??G?_K?GFC???C??_G?B?@?F\n", +"Y~?GW]?OG@_FA?@???W?F??{_???O???K??F??@w_???O?@?O?B?@?B_\n", +"[~??W[N_???B?F?F`??????K??w?@y????G????W??B_??NG???C???__??W??_F\n", +"Y~?GW]???@_F?NA??_GCB??\\????_???K??F??@x???A??@?O?B??AB_\n", +"W~?GW]??G@_FA?@?_OW?FO???OG?OW?G[_??C??GA?@_?AF\n", +"Y~??W[N_A?_B?FA??_??B??[?@x????GC??B???w_????O@???J???F_\n", +"W~?GW]?OG@_FA?@?_OWAFG???GG??W??[O???@?G??D_??N\n", +"Y~??W[N_??_B?FA?????B??[?@y??@??C?GB?@?w_???_?@??_B??GB_\n", +"W~?GW]??G@_FA?@?_OW?FO??_?G?_W?O[O???O?G?G@_?AF\n", +"Z~??W[N_A?_B?FA?????B??[?@x????G???B???w??Fa???@??@??_@_?C?w\n", +"X~?GW]?OG@_FA?@?_OW?FG???G???W??[??NC???A??_?_B??CF\n", +"Y~?GW]???@_F?NA?????B??[?@y???G?CA?B?C?x???A??@?O?B??GB_\n", +"Y~?GW]???@_F?NA?????B??[?@y??@??CC?B?C?w_???O?@?O?B??GB_\n", +"X~?GW]?OG@_FA?????W?F??{_??G?G?OK?CFC???C??_G?B??OF\n", +"W~?GW]?OG@_FA?@?_?W?FG??A?G?OW?G[O???O?G?@@_?AF\n", +"X~?GW]?OG@_FA?????W?F??{_??G?GC?K?GFC???A??_?_B??OF\n", +"X~?GW]?OG@_FA?@???W?F??{_??C?G?OK??FC???A??_?_B???N\n", +"Y~??W[N_A?_B?FA?????B??[?@x???G?C?CB???w_????O@??GB???F_\n", +"W~?GW]?OG@_FA?@?_OW?FG??@?G?OW??[O???@?G??`_??N\n", +"W~?GW]?OG@_FA?@?_?W?FG??A?G?OW?@[O???O?G?G@_?AF\n", +"X~?GW]??G@_FO?G?`?WGF?_??O???W??[??NC???A??_@?B?@?F\n", +"Y~?GW]?OG@_FG?A???W?F??{C???O???K??F??@w_???O?@?O?B?@?B_\n", +"Y~?GW]???@_F?NO?C?GOB??[C???_???K??F??@w_???O?@?O?B??AB_\n", +"W~?GW]??G@_FO?G?`?W?F@???OG?OW?G[O???_?GA?@_?AF\n", +"W~?GW]?OG@_FG?A?_?W?F@??A?G?OW?G[O???O?G?@@_?AF\n", +"W~?GW]??G@_FO?G?`?W?F@??A?G?_W?O[O???O?G?G@_?AF\n", +"X~?GW]?OG@_FG?A?__W?F?_??G???W??[??NC???A??_?_B??CF\n", +"W~?GW]??G@_FO?G?`?W?FC??C?GC?W?_[@???A?G?G@_?AF\n", +"W~?GW]?OG@_FG?A?_?W?FC??C?GC?W?O[@???@?G?@@_?AF\n", +"W~?GW]??G@_FO?A?__W?FG??A?G?_W?O[O???O?G?G@_?AF\n", +"X~?GW]???@_F?NO?@?GGB??[_??C?G?_K??FC???A??_?@B???N\n", +"Y~??W[N_??_B?FO??@??B??[?@x???O?C?CB???w_???_?@??_B???F_\n", +"X~?GW[N_??_B?FO??@G?B??[_???GG??k??FC????C?_?@B???N\n", +"X~?GW]?AG@_FO?@???W?F??{_??C?G?OK??FC???A??_?_B???N\n", +"W~?GW]?AG@_FO?@?_OW?FG??@?G?OW??[O???@?G??`_??N\n", +"W~?GW]?AG@_FO?@?_?W?FG??A?G?OW?@[O???O?G?G@_?AF\n", +"[~?GW[??G@_F_?O?_?W?FA??C?GC?WA?[?_??C?G?O@_?_FG???C???_?C?W??_F\n", +"[~?GW[??G@_F_?O?c?W?FA??C?GC?W??[?_??C?G?O@_?_FC????O??_?A?W??AF\n", +"[~?GW[??G@_F_?O?_?W?FA??C?GC?WA?[_??C??G@?@_A?F?A???@??_?C?W??_F\n", +"[~?GW[??G@_F_?O?_?W?FA??C?GC?WA?[_???G?G?_@_@?FC????@??_?C?W??_F\n", +"[~?GW[??G@_F_?O?c?W?FA??C?GC?W??[O???G?G?_@_@?F?O???@??_?A?W??AF\n", +"[~?GW[??G@_F_?O?c?W?FA??C?GC?W??[O???O?G@?@_A?F?A???@??_?A?W??AF\n", +"[~?GW[??G@_F_?O?c?W?FA??C?GC?W??[O???G?G?_@_?AF?O???A??_?_?W??AF\n", +"[~?GW[??G@_F_?O?_?W?FG??O?GO?WG?[A???O?G@?@_A?F?A???@??_?C?W??_F\n", +"[~?GW[??G@_F_?O?_?W?FG??G?GG?WC?[@???G?G?_@_@?FC????@??_?C?W??_F\n", +"[~?GW[??G@_F_?O?_?W?FG??O?GO?WG?[A???G?G?_@_@?F?O???@??_?C?W??_F\n", +"[~?GW[??G@_F_?O?c?W?FC??G?GG?W??[A???G?G?_@_@?F?O???@??_?A?W??AF\n", +"[~?GW[??G@_F_?O?c?W_FA???_G??W??[C???_?G?_@_@?F?O???@??_??OW??AF\n", +"[~?GW[??G@_F_?O?c?W?FA???_G?_W??[C???_?G?O@_?_FC????O??_?A?W??AF\n", +"[~?GW[??G@_F_?O?c?W?FA??C?G@?W??[A???C?G?O@_?_FC????O??_?A?W??AF\n", +"[~?GW[??G@_F_?O?c?W_FA??C?G@?W??[A???C?G?O@_??F?O???@??_??GW???N\n", +"[~?GW[??G@_F_?O?_?W?FA??C?G@?W?_[_??C??GC?@_@?F?O???@??_?C?W??_F\n", +"[~?GW[??G@_F_?O?c?W?FA??C?G@?W??[O???_?G?_@_@?F?O???@??_?A?W??AF\n", +"[~?GW[??G@_F_?O?c?W?FA??C?G@?W?_[O???_?G?O@_??F?O???@??_?A?W???N\n", +"[~?GW[??G@_F_?O?_?W?FA???_G?_W?O[_??@??G?@@_?AFC????_??_G??W?G?F\n", +"[~?GW[??G@_F_?O?_?W?FA??C?G@?W?_[_???_?G?@@_?AFC????O??_@??W?G?F\n", +"[~?GW[??G@_F_?O?_?W?FA???_G?_W?O[_??@??GC?@_?CFC????O??_?_?W??_F\n", +"[~?GW[??G@_F_?O?c?W?FA??C?G@?W??[A???C?G?O@_??NC????O??_?_?W??_F\n", +"[~?GW[??G@_F_?O?_?W?FA??C?G@?W?_[_???_?G?O@_?CFC????O??_?_?W??_F\n", +"[~?GW[??G@_F_?O?_?W?FG??O?G?@W??{G??@??G@?@_A?F?_???O??_@??W?G?F\n", +"[~?GW[??G@_F_?O?_?W?FG??O?G?@W??{G??@??GC?@_C?F?O???C??_@??W?G?F\n", +"[~?GW[??G@_F_?O?_?W?FG??G?GG?W@?[A???C?G?@@_?AFC????O??_@??W?G?F\n", +"[~?GW[??G@_F_?O?_?W?FG??G?GG?W@?[A???C?G?O@_?CFC????O??_?_?W??_F\n", +"[~?GW[??G@_F_?A?_?W?FO??_?G@?W?_[C???_?G?O@_?_FC????O??_?C?W??_F\n", +"[~?GW[??G@_F_?A?_?W?FO??_?G@?W?_[C???_?GA?@_@?FC????@??_?C?W??_F\n", +"[~?GW[??G@_F_?A?__W?FO??_?G@?W??[A???C?G?O@_?_FC????O??_?A?W??AF\n", +"[~?GW[??G@_F_?O?`?W?FG???_G?_W??[C???_?G?O@_?_FC????O??_?A?W??AF\n", +"[~?GW[??G@_F_?O?`?W?FG??O?G@?W??[C???_?G?_@_@?F?O???@??_?A?W??AF\n", +"[~?GW[??G@_F_?A?__W?FO???_G?_W??[_???_?G?O@_?_FC????O??_?A?W??AF\n", +"[~?GW[??G@_F_?A?__W?FO???_G?_W??[_??C??GC?@_@?F?O???@??_?A?W??AF\n", +"[~?GW[??G@_F_?O?`?W?FG???_G?_W??[O??@??GC?@_@?F?O???@??_?A?W??AF\n", +"[~?GW[??G@_F_?A?__W?FO???_G?_W?O[_???_?G?G@_??FC????O??_?A?W???N\n", +"[~?GW[??G@_F_?A?_?W?FO??_?G@?W?_[C???A?G?@@_?AFC????_??_G??W?G?F\n", +"[~?GW[??G@_F_?A?__W?FO???_G?_W??[A???A?G?G@_?AFG???C???_G??W??AF\n", +"[~?GW[??G@_F_?O?`?W?FG???_G?_W??[C???A?G?G@_?AFC????_??_G??W??AF\n", +"[~?GW[??G@_F_?O?`?W?FG???_G?_W??[C???_?G?O@_?AFC????O??_?_?W??AF\n", +"[~?GW[??G@_F_?A?_?W?FO???_G?_W?@[_??@??GC?@_@?FC????O??_?_?W??_F\n", +"[~?GW[??G@_F_?A?_CW?FO??C?G?_W??[_???_?G?O@_?_FC????O??_?A?W??AF\n", +"[~?GW[??G@_F_?A?_CW?FO??C?G?_W??[_???_?G?O@_?AFC????O??_?_?W??AF\n", +"[~?GW]??G@_FA???_?W?FO??_?G?_W?O[C????OG?@@_?AFC????_??_G??W?G?F\n", +"[~?GW]?OG@_FA?@?_?W?FG???GG??W??[A???O?G?@@_?AFC????@??_??OW??AF\n", +"[~?GW]??G@_FA???_?W?FO??_?G?_W?O[C???_?G?A@_?CFC????O??_?_?W??_F\n", +"[~?GW]??G@_FA???_?W?FO??_?G?_W?O[O??@??GC?@_?GF?O???@??_?C?W??_F\n", +"[~?GW]??G@_FA?@?_?W?FO???OG?OW??[_???_?G?@@_?AFC????O??_?_?W??AF\n", +"[~?GW]??G@_FA?@?_?W?FO???OG?OW??[_??C??GC?@_?CF?O???@??_?A?W??AF\n", +"[~?GW]??G@_FA?@?_?W?FO???OG?OW?G[_???_?G?@@_??FC????O??_?A?W???N\n", +"[~?GW]??G@_FA?@?_OWAFO???OG??W??[_???A?G??D_??FC????@??_??GW???N\n", +"[~?GW]??G@_FA???_?W?FO??C?G?_W?O[_???_?G?A@_?CFC????O??_?_?W??_F\n", +"[~?GW]??G@_FA???_?W?FO??C?GC?W?_[_???A?G?A@_?CFC????O??_?_?W??_F\n", +"[~?GW]??G@_FA?@?_?W?FO??A?G?_W??[_???A?G?@@_?AFC????O??_?_?W??AF\n", +"[~?GW]??G@_FA?@?_?W?FO??A?G?_W??[_???A?G?G@_?CFC????O??_?A?W??AF\n", +"[~?GW]??G@_FA?@?_OW?FO??@?G?_W??[_???A?G??`_??FC????@??_??GW???N\n", +"[~?GW]?OG@_FA?@?_?W?FG??A?G?OW??[O???@?G?@@_??F?O????C?_??GW???N\n", +"[~?GW]??G@_FO?A?_?W?FG??C?G?_W??[A???A?G?@@_?AFC????O??_?_?W??AF\n", +"[~?GW]??G@_FO?A?_?W?FG??C?G?_W??[A???A?G?G@_?CFC????O??_?A?W??AF\n", +"[~?GW]??G@_FO?A?_?W?FG??C?G?_W??[O???_?G?O@_?CF?O???@??_?A?W??AF\n"}; +#define NUMIRRED (sizeof(irred)/sizeof(char*)) diff --git a/graph-checker/nauty/schreier.c b/graph-checker/nauty/schreier.c new file mode 100644 index 0000000..d2a68f0 --- /dev/null +++ b/graph-checker/nauty/schreier.c @@ -0,0 +1,1128 @@ +/* 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(); +} diff --git a/graph-checker/nauty/schreier.h b/graph-checker/nauty/schreier.h new file mode 100644 index 0000000..fad9606 --- /dev/null +++ b/graph-checker/nauty/schreier.h @@ -0,0 +1,72 @@ +/* schreier.h - Version 1.3 (November 2020) */ + +#ifndef _SCHREIER_H_ /* only process this file once */ +#define _SCHREIER_H_ + +#include "nauty.h" +#include "naurng.h" + +typedef struct permnodestruct +{ + struct permnodestruct *prev,*next; /* prev&next in circular list */ + unsigned long refcount; /* number of references */ + int nalloc; /* size of p[] in ints, + <= 0 for a perm marker */ + int mark; /* a mark, 0 unless changed */ +#if FLEX_ARRAY_OK + int p[]; +#else + int p[2]; /* actual vector, extended to + nalloc enties */ +#endif +} permnode; + +typedef struct schreierlevel +{ + struct schreierlevel *next; /* down one level, if any */ + int fixed; /* fixed at next level, -1 if none */ + /* Invariant: next=NULL => fixed = -1 */ + int nalloc; /* size of vec[] and orbits[] */ + permnode **vec; /* vec[i]^pwr[i] is edge label, */ + int *pwr; /* transitive closure maps i->fixed */ + int *orbits; /* vector of orbits */ + permnode *marker; /* points to marker for this level */ +} schreier; + +#define SCHREIERFAILS 10 + /* Default number of Schreier failures before giving up. */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* See separate file schreier.txt for a description of usage. */ + +extern void freeschreier(schreier **gp, permnode **gens); +extern void addpermutation(permnode **ring, int *p, int n); +extern permnode *findpermutation(permnode *gens, int *p, int n); +extern boolean addgenerator(schreier **gp, permnode **gens, int *p, int n); +extern boolean + condaddgenerator(schreier **gp, permnode **gens, int *p, int n); +extern boolean expandschreier(schreier *gp, permnode **gens, int n); +extern int *getorbits(int *fix, int nfix, + schreier *gp, permnode **gens, int n); +extern int getorbitsmin(int *fix, int nfix, schreier *gp, permnode **gens, + int **orbits, int *cell, int ncell, int n, boolean changed); +extern void pruneset(set *fixset, schreier *gp, permnode **gens, + set *x, int m, int n); +extern void newgroup(schreier **gp, permnode **gens, int n); +extern void schreier_freedyn(void); +extern int schreier_fails(int nfails); +extern void dumpschreier(FILE *f, schreier *gp, permnode *gens, int n); +extern int schreier_gens(permnode *gens); +extern void deleteunmarked(permnode **gens); +extern void grouporder(int *fix, int nfix, schreier *gp, permnode **gens, + double *grpsize1, int *grpsize2, int n); +extern void schreier_check(int wordsize, int m, int n, int version); + +#ifdef __cplusplus +} +#endif + +#endif /* _SCHREIER_H_ */ diff --git a/graph-checker/nauty/sorttemplates.c b/graph-checker/nauty/sorttemplates.c new file mode 100644 index 0000000..5f7e100 --- /dev/null +++ b/graph-checker/nauty/sorttemplates.c @@ -0,0 +1,580 @@ +/* sorttemplates.c version 2.0, Mar 13, 2018. + * Author: Brendan McKay; brendan.mckay@anu.edu.au + * + * This file contains templates for creating in-place sorting procedures + * for different data types. It cannot be compiled separately but + * should be #included after defining a few preprocessor variables. + * SORT_OF_SORT, SORT_NAME and SORT_TYPE1 are always required, and + * SORT_TYPE2 is needed for SORT_OF_SORT = 3, + * SORT_COMPARE is needed for SORT_OF_SORT = 4. + * + * SORT_OF_SORT = 1: Creates a procedure + * static void SORT_NAME(SORT_TYPE1 *x, int n) + * which permutes x[0..n-1] so that x[0] <= ... <= x[n-1]. + * SORT_OF_SORT = 2: Creates a procedure + * static void SORT_NAME(SORT_TYPE1 *x, SORT_TYPE2 *y, int n) + * which permutes x[0..n-1] so that x[0] <= ... <= x[n-1] + * and also permutes y[0..n-1] by the same permutation. + * SORT_OF_SORT = 3: Creates a procedure + * static void SORT_NAME(SORT_TYPE1 *x, SORT_TYPE2 *y, int n) + * which permutes x[0..n-1] so that y[x[0]] <= ... <= y[x[n-1]]. + * SORT_OF_SORT = 4: Creates a procedure + * static void SORT_NAME(SORT_TYPE1 *x, int n) + * which permutes x[0..n-1] so that + * SORT_COMPARE(x[i],x[j]) <= 0 for i<j. + * SORT_COMPARE(x,y) is an expression <0,0,>0 for x<y,x=y,x>y. + * + * SORT_NAME = the name of the procedure to be created + * + * SORT_TYPE1 = type of the first or only array (no default) + * This can be any numeric type for SORT_OF_SORT=1,2, but + * should be an integer type for SORT_OF_SORT=3. + * For SORT_OF_SORT=4, SORT_TYPE1 should be assignable; + * such as a simple type or a structure. + * SORT_TYPE2 = type of the second array if needed (no default) + * This can be any assignable type (including a structure) for + * SORT_OF_SORT=2, but must be a numeric type for SORT_OF_SORT=3. + * + * Note that C doesn't like SORT_TYPE1 and SORT_TYPE2 to be defined + * as explicit pointer types like int*. The simplest work-around + * is to define a type, e.g. typedef intptr int* then use that. + * + * SORT_MINPARTITION = least number of elements for using quicksort + * partitioning, otherwise insertion sort is used (default "11") + * SORT_MINMEDIAN9 = least number of elements for using the median of 3 + * medians of 3 for partitioning (default "320") + * SORT_FUNCTYPE = type of sort function (default "static void") + * + * This file can be included any number of times provided the value + * of SORT_NAME is different each time. + */ + +#define SORT_MEDIAN_OF_3(a,b,c) \ + ((a) <= (b) ? ((b) <= (c) ? (b) : (c) <= (a) ? (a) : (c)) \ + : ((a) <= (c) ? (a) : (c) <= (b) ? (b) : (c))) +#define F_SORT_MEDIAN_OF_3(f,a,b,c) \ + (f(a,b) <= 0 ? (f(b,c) <= 0 ? (b) : f(c,a) <= 0 ? (a) : (c)) \ + : (f(a,c) <= 0 ? (a) : f(c,b) <= 0 ? (b) : (c))) + +#if !defined(SORT_OF_SORT) || !defined(SORT_NAME) + #error Either SORT_OF_SORT or SORT_NAME is undefined +#endif + +#if (SORT_OF_SORT < 1) || (SORT_OF_SORT > 4) + #error Unknown value of SORT_OF_SORT +#endif + +#ifndef SORT_TYPE1 + #error "SORT_TYPE1 must be defined before including sorttemplates.c" +#endif + +#ifndef SORT_MINPARTITION +#define SORT_MINPARTITION 11 +#endif + +#ifndef SORT_MINMEDIAN9 +#define SORT_MINMEDIAN9 320 +#endif + +#ifndef SORT_FUNCTYPE +#define SORT_FUNCTYPE static void +#endif + +#define SORT_SWAP1(x,y) tmp1 = x; x = y; y = tmp1; +#define SORT_SWAP2(x,y) tmp2 = x; x = y; y = tmp2; + +/*******************************************************************/ + +#if SORT_OF_SORT == 1 +SORT_FUNCTYPE +SORT_NAME(SORT_TYPE1 *x, int n) +{ + int i,j; + int a,d,ba,dc,s,nn; + SORT_TYPE1 tmp1,v,v1,v2,v3; + SORT_TYPE1 *x0,*xa,*xb,*xc,*xd,*xh,*xl; + struct {SORT_TYPE1 *addr; int len;} stack[40]; + int top; + + top = 0; + if (n > 1) + { + stack[top].addr = x; + stack[top].len = n; + ++top; + } + + while (top > 0) + { + --top; + x0 = stack[top].addr; + nn = stack[top].len; + + if (nn < SORT_MINPARTITION) + { + for (i = 1; i < nn; ++i) + { + tmp1 = x0[i]; + for (j = i; x0[j-1] > tmp1; ) + { + x0[j] = x0[j-1]; + if (--j == 0) break; + } + x0[j] = tmp1; + } + continue; + } + + if (nn < SORT_MINMEDIAN9) + v = SORT_MEDIAN_OF_3(x0[0],x0[nn/2],x0[nn-1]); + else + { + v1 = SORT_MEDIAN_OF_3(x0[0],x0[1],x0[2]); + v2 = SORT_MEDIAN_OF_3(x0[nn/2-1],x0[nn/2],x0[nn/2+1]); + v3 = SORT_MEDIAN_OF_3(x0[nn-3],x0[nn-2],x0[nn-1]); + v = SORT_MEDIAN_OF_3(v1,v2,v3); + } + + xa = xb = x0; xc = xd = x0+(nn-1); + for (;;) + { + while (xb <= xc && *xb <= v) + { + if (*xb == v) + { + *xb = *xa; *xa = v; ++xa; + } + ++xb; + } + while (xc >= xb && *xc >= v) + { + if (*xc == v) + { + *xc = *xd; *xd = v; --xd; + } + --xc; + } + if (xb > xc) break; + SORT_SWAP1(*xb,*xc); + ++xb; + --xc; + } + + a = xa - x0; + ba = xb - xa; + if (ba > a) s = a; else s = ba; + for (xl = x0, xh = xb-s; s > 0; --s) + { + *xl = *xh; *xh = v; ++xl; ++xh; + } + d = xd - x0; + dc = xd - xc; + if (dc > nn-1-d) s = nn-1-d; else s = dc; + for (xl = xb, xh = x0 + (nn-s); s > 0; --s) + { + *xh = *xl; *xl = v; ++xl; ++xh; + } + + if (ba > dc) + { + if (ba > 1) + { + stack[top].addr = x0; stack[top].len = ba; ++top; + } + if (dc > 1) + { + stack[top].addr = x0+(nn-dc); stack[top].len = dc; ++top; + } + } + else + { + if (dc > 1) + { + stack[top].addr = x0+(nn-dc); stack[top].len = dc; ++top; + } + if (ba > 1) + { + stack[top].addr = x0; stack[top].len = ba; ++top; + } + } + } +} +#endif + +#if SORT_OF_SORT == 2 +#ifndef SORT_TYPE2 + #error "SORT_TYPE2 must be defined before including sorttemplates.c" +#endif + +SORT_FUNCTYPE +SORT_NAME(SORT_TYPE1 *x, SORT_TYPE2 *y, int n) +{ + int i,j; + int a,d,ba,dc,s,nn; + SORT_TYPE2 tmp2,*y0,*ya,*yb,*yc,*yd,*yl,*yh; + SORT_TYPE1 tmp1,v,v1,v2,v3; + SORT_TYPE1 *x0,*xa,*xb,*xc,*xd,*xh,*xl; + struct {SORT_TYPE1 *addr; int len;} stack[40]; + int top; + + top = 0; + if (n > 1) + { + stack[top].addr = x; + stack[top].len = n; + ++top; + } + + while (top > 0) + { + --top; + x0 = stack[top].addr; + y0 = y + (x0-x); + nn = stack[top].len; + + if (nn < SORT_MINPARTITION) + { + for (i = 1; i < nn; ++i) + { + tmp1 = x0[i]; + tmp2 = y0[i]; + for (j = i; x0[j-1] > tmp1; ) + { + x0[j] = x0[j-1]; + y0[j] = y0[j-1]; + if (--j == 0) break; + } + x0[j] = tmp1; + y0[j] = tmp2; + } + continue; + } + + if (nn < SORT_MINMEDIAN9) + v = SORT_MEDIAN_OF_3(x0[0],x0[nn/2],x0[nn-1]); + else + { + v1 = SORT_MEDIAN_OF_3(x0[0],x0[1],x0[2]); + v2 = SORT_MEDIAN_OF_3(x0[nn/2-1],x0[nn/2],x0[nn/2+1]); + v3 = SORT_MEDIAN_OF_3(x0[nn-3],x0[nn-2],x0[nn-1]); + v = SORT_MEDIAN_OF_3(v1,v2,v3); + } + + xa = xb = x0; xc = xd = x0+(nn-1); + ya = yb = y0; yc = yd = y0+(nn-1); + for (;;) + { + while (xb <= xc && *xb <= v) + { + if (*xb == v) + { + *xb = *xa; *xa = v; ++xa; + SORT_SWAP2(*ya,*yb); ++ya; + } + ++xb; ++yb; + } + while (xc >= xb && *xc >= v) + { + if (*xc == v) + { + *xc = *xd; *xd = v; --xd; + SORT_SWAP2(*yc,*yd); --yd; + } + --xc; --yc; + } + if (xb > xc) break; + SORT_SWAP1(*xb,*xc); + SORT_SWAP2(*yb,*yc); + ++xb; ++yb; + --xc; --yc; + } + + a = xa - x0; + ba = xb - xa; + if (ba > a) s = a; else s = ba; + for (xl = x0, xh = xb-s, yl = y0, yh = yb-s; s > 0; --s) + { + *xl = *xh; *xh = v; ++xl; ++xh; + SORT_SWAP2(*yl,*yh); ++yl; ++yh; + } + d = xd - x0; + dc = xd - xc; + if (dc > nn-1-d) s = nn-1-d; else s = dc; + for (xl = xb, xh = x0+(nn-s), yl = yb, yh = y0+(nn-s); s > 0; --s) + { + *xh = *xl; *xl = v; ++xl; ++xh; + SORT_SWAP2(*yl,*yh); ++yl; ++yh; + } + + if (ba > dc) + { + if (ba > 1) + { + stack[top].addr = x0; stack[top].len = ba; ++top; + } + if (dc > 1) + { + stack[top].addr = x0+(nn-dc); stack[top].len = dc; ++top; + } + } + else + { + if (dc > 1) + { + stack[top].addr = x0+(nn-dc); stack[top].len = dc; ++top; + } + if (ba > 1) + { + stack[top].addr = x0; stack[top].len = ba; ++top; + } + } + } +} +#endif + +#if SORT_OF_SORT == 3 +#ifndef SORT_TYPE2 + #error "SORT_TYPE2 must be defined before including sorttemplates.c" +#endif + +SORT_FUNCTYPE +SORT_NAME(SORT_TYPE1 *x, SORT_TYPE2 *y, int n) +{ + int i,j; + int a,d,ba,dc,s,nn; + SORT_TYPE2 tmp2,v,v1,v2,v3; + SORT_TYPE1 tmp1,*x0,*xa,*xb,*xc,*xd,*xh,*xl; + struct {SORT_TYPE1 *addr; int len;} stack[40]; + int top; + + top = 0; + if (n > 1) + { + stack[top].addr = x; + stack[top].len = n; + ++top; + } + + while (top > 0) + { + --top; + x0 = stack[top].addr; + nn = stack[top].len; + + if (nn < SORT_MINPARTITION) + { + for (i = 1; i < nn; ++i) + { + tmp1 = x0[i]; + tmp2 = y[tmp1]; + for (j = i; y[x0[j-1]] > tmp2; ) + { + x0[j] = x0[j-1]; + if (--j == 0) break; + } + x0[j] = tmp1; + } + continue; + } + + if (nn < SORT_MINMEDIAN9) + v = SORT_MEDIAN_OF_3(y[x0[0]],y[x0[nn/2]],y[x0[nn-1]]); + else + { + v1 = SORT_MEDIAN_OF_3(y[x0[0]],y[x0[1]],y[x0[2]]); + v2 = SORT_MEDIAN_OF_3(y[x0[nn/2-1]],y[x0[nn/2]],y[x0[nn/2+1]]); + v3 = SORT_MEDIAN_OF_3(y[x0[nn-3]],y[x0[nn-2]],y[x0[nn-1]]); + v = SORT_MEDIAN_OF_3(v1,v2,v3); + } + + xa = xb = x0; xc = xd = x0+(nn-1); + for (;;) + { + while (xb <= xc && y[*xb] <= v) + { + if (y[*xb] == v) + { + SORT_SWAP1(*xa,*xb); ++xa; + } + ++xb; + } + while (xc >= xb && y[*xc] >= v) + { + if (y[*xc] == v) + { + SORT_SWAP1(*xc,*xd); --xd; + } + --xc; + } + if (xb > xc) break; + SORT_SWAP1(*xb,*xc); + ++xb; + --xc; + } + + a = xa - x0; + ba = xb - xa; + if (ba > a) s = a; else s = ba; + for (xl = x0, xh = xb-s; s > 0; --s) + { + SORT_SWAP1(*xl,*xh); ++xl; ++xh; + } + d = xd - x0; + dc = xd - xc; + if (dc > nn-1-d) s = nn-1-d; else s = dc; + for (xl = xb, xh = x0 + (nn-s); s > 0; --s) + { + SORT_SWAP1(*xl,*xh); ++xl; ++xh; + } + + if (ba > dc) + { + if (ba > 1) + { + stack[top].addr = x0; stack[top].len = ba; ++top; + } + if (dc > 1) + { + stack[top].addr = x0+(nn-dc); stack[top].len = dc; ++top; + } + } + else + { + if (dc > 1) + { + stack[top].addr = x0+(nn-dc); stack[top].len = dc; ++top; + } + if (ba > 1) + { + stack[top].addr = x0; stack[top].len = ba; ++top; + } + } + } +} +#endif + +#if SORT_OF_SORT == 4 +#ifndef SORT_COMPARE + #error "SORT_COMPARE must be defined before including sorttemplates.c" +#endif + +SORT_FUNCTYPE +SORT_NAME(SORT_TYPE1 *x, int n) +{ + int i,j; + int a,d,ba,dc,s,nn; + SORT_TYPE1 tmp2,v,v1,v2,v3; + SORT_TYPE1 tmp1,*x0,*xa,*xb,*xc,*xd,*xh,*xl; + struct {SORT_TYPE1 *addr; int len;} stack[40]; + int top; + + top = 0; + if (n > 1) + { + stack[top].addr = x; + stack[top].len = n; + ++top; + } + + while (top > 0) + { + --top; + x0 = stack[top].addr; + nn = stack[top].len; + + if (nn < SORT_MINPARTITION) + { + for (i = 1; i < nn; ++i) + { + tmp1 = x0[i]; + for (j = i; SORT_COMPARE(tmp1,x0[j-1]) < 0; ) + { + x0[j] = x0[j-1]; + if (--j == 0) break; + } + x0[j] = tmp1; + } + continue; + } + + if (nn < SORT_MINMEDIAN9) + v = F_SORT_MEDIAN_OF_3(SORT_COMPARE,x0[0],x0[nn/2],x0[nn-1]); + else + { + v1 = F_SORT_MEDIAN_OF_3(SORT_COMPARE,x0[0],x0[1],x0[2]); + v2 = F_SORT_MEDIAN_OF_3(SORT_COMPARE,x0[nn/2-1],x0[nn/2],x0[nn/2+1]); + v3 = F_SORT_MEDIAN_OF_3(SORT_COMPARE,x0[nn-3],x0[nn-2],x0[nn-1]); + v = F_SORT_MEDIAN_OF_3(SORT_COMPARE,v1,v2,v3); + } + + xa = xb = x0; xc = xd = x0+(nn-1); + for (;;) + { + while (xb <= xc && SORT_COMPARE(*xb,v) <= 0) + { + if (SORT_COMPARE(*xb,v) == 0) + { + SORT_SWAP1(*xa,*xb); ++xa; + } + ++xb; + } + while (xc >= xb && SORT_COMPARE(*xc,v) >= 0) + { + if (SORT_COMPARE(*xc,v) == 0) + { + SORT_SWAP1(*xc,*xd); --xd; + } + --xc; + } + if (xb > xc) break; + SORT_SWAP1(*xb,*xc); + ++xb; + --xc; + } + + a = xa - x0; + ba = xb - xa; + if (ba > a) s = a; else s = ba; + for (xl = x0, xh = xb-s; s > 0; --s) + { + SORT_SWAP1(*xl,*xh); ++xl; ++xh; + } + d = xd - x0; + dc = xd - xc; + if (dc > nn-1-d) s = nn-1-d; else s = dc; + for (xl = xb, xh = x0 + (nn-s); s > 0; --s) + { + SORT_SWAP1(*xl,*xh); ++xl; ++xh; + } + + if (ba > dc) + { + if (ba > 1) + { + stack[top].addr = x0; stack[top].len = ba; ++top; + } + if (dc > 1) + { + stack[top].addr = x0+(nn-dc); stack[top].len = dc; ++top; + } + } + else + { + if (dc > 1) + { + stack[top].addr = x0+(nn-dc); stack[top].len = dc; ++top; + } + if (ba > 1) + { + stack[top].addr = x0; stack[top].len = ba; ++top; + } + } + } +} +#endif + +#undef SORT_NAME +#undef SORT_OF_SORT +#undef SORT_TYPE1 +#undef SORT_TYPE2 +#undef SORT_COMPARE diff --git a/graph-checker/nauty/traces.c b/graph-checker/nauty/traces.c new file mode 100644 index 0000000..78b397d --- /dev/null +++ b/graph-checker/nauty/traces.c @@ -0,0 +1,10490 @@ +/****************************************************************************** + * * + * This is the main file for traces() version 2.2, which is included into * + * nauty() version 2.8.6. * + * * + * nauty is Copyright (1984-2018) Brendan McKay. All rights reserved. * + * Traces is Copyright Adolfo Piperno, 2008-2018. All rights reserved. * + * See the file COPYRIGHT for the details of the software license. * + * * + * CHANGE HISTORY * + * 28-Dec-12 : final changes for version 2.0 * + * 20-Jan-13 : add code for ^C catching in Traces * + * 29-Mar-13 : bug correction in automorphism mode * + * 02-Apr-13 : add preprocessing * + * 21-May-13 : bug correction (coloured lists) * + * 29-Jun-13 : bug correction (coloured lists and cycles) * + * 07-Dec-13 : bug correction in automorphism mode (wrong group size * + * due to randomness in Schreier-Sims orbit computation) * + * bug correction (discrete initial partition) * + * 15-Feb-14 : CPUDEFS removed (already declared in gtools.h) * + * 01-Sep-15 : add weighted edges (not active) * + * 28-Jan-16 : version ready for nauty and Traces v.2.6 distribution * + * 12-Jul-16 : bug correction (reaching degree 2 vertices) * + * 07-Jun-18 : bug correction (finalnumcells, thanks R.Kralovic) * + * 07-Jun-18 : bug correction (index computation when findperm) * + * 10-Nov-22 : bug correction (cycles in degree 2 subgraphs) * + *****************************************************************************/ + +#include "traces.h" + +#ifdef NAUTY_IN_MAGMA +#include "cleanup.e" +#endif + +#define SORT_OF_SORT 2 +#define SORT_NAME sort2ints +#define SORT_TYPE1 int +#define SORT_TYPE2 int +#include "sorttemplates.c" + +typedef struct weightwhere { + int weight; + int *ref; +} weightwhere; + +#define SORT_OF_SORT 2 +#define SORT_NAME sortweights +#undef SORT_TYPE2 +#define SORT_TYPE1 int +#define SORT_TYPE2 weightwhere +#include "sorttemplates.c" + +#define NAUTY_ABORTED (-11) +#define NAUTY_KILLED (-12) + +typedef struct Candidate { + boolean sortedlab; + int *invlab; + int *lab; + int code; + int do_it; + int indnum; + int name; + int vertex; + struct Candidate *next; + struct searchtrie *stnode; + unsigned int firstsingcode; + unsigned int pathsingcode; + unsigned int singcode; +} Candidate; + +typedef struct Partition { + int *cls; + int *inv; + int active; + int cells; + int code; +} Partition; + +typedef struct trielist { + struct searchtrie *triearray; + struct trielist *prev; + struct trielist *next; +} trielist; + +typedef struct TracesVars { + char digstring[25]; + double autchk; + double expaths; + double schreier1; + double schreier2; + double schreier3; + int augmented_cells; + int build_autom; + int *currorbit; + int *orbits; + int answ; + int brkstpcount; + int compstage; + int cand_level; + int canlist; + int digits; + int expathlength; + int firstpathlength; + int fromlevel; + int group_level; + int indivend; + int indivstart; + int indiv_vtx; + int lastcell; + int lastlev; + int lev_of_lastauto; + int levelfromCS0; + int linelgth; + int mark; + int treemark; + int autmark; + int markcell1; + int markcell2; + int maxdeg; + int maxtreelevel; + int maxspineorblevel; + int mindeg; + int name; + struct searchtrie *gotonode; + struct searchtrie *newgotonode; + struct searchtrie *newst_stage1; + int newindex; + int nextlevel; + int nfix; + int finalnumcells; + int permInd; + int preprocessed; + int samepref; + int specialgens; + int stackmark; + int steps; + int strategy; + trielist *strielist; + int strienext; + int tcell_sz; + int tcell; + int tcellevel; + int tcellexpath_sz; + int tcellexpath; + int tolevel_tl; + int tolevel; + int treedepth; + int trienext; + int triepos; + TracesOptions *options; + TracesStats *stats; + unsigned int singlongcode; + sparsegraph *graph; + sparsegraph *cangraph; + sparsegraph *input_graph; + int conta0; + int conta1; + int conta2; + int conta3; + int conta4; + int conta5; + int conta6; + int conta7; + int contatc; +} TracesVars; + +typedef struct TracesInfo { + boolean autofound; + boolean deg_one; + boolean first_matching; + boolean regular; + boolean exitfromref; + boolean identitygroup; + boolean minimalinorbits; + boolean thegraphisparse; + boolean thegrouphaschanged; + boolean thereisnextlevel; + boolean useTempOrbits1; + boolean useTempOrbits2; +} TracesInfo; + +typedef struct TracesSpine { + boolean thetracexists; + Candidate *listend; + Candidate *liststart; + int ccend; + int ccstart; + int listcounter; + int stpend; + int stpstart; + int tgtcell; + int tgtend; + int tgtfrom; + int tgtpos; + int tgtsize; + int trcend; + int trcstart; + int singend; + int singstart; + int updates; + unsigned long keptcounter; + unsigned long levelcounter; + Partition *part; + unsigned int singcode; +} TracesSpine; + +typedef struct trie { + int value; + struct trie *first_child; + struct trie *next_sibling; +} trie; + +typedef struct searchtrie { + int index; + int name; + int vtx; + int level; + struct searchtrie *father; + struct searchtrie *first_child; + struct searchtrie *last_child; + struct searchtrie *next_sibling; + struct searchtrie *goes_to; +} searchtrie; + +typedef struct pair { + int arg; + int val; +} pair; + +typedef struct grph_strct { + int *e; + int *w; + int d; + boolean one; +} grph_strct; + +typedef struct ExpPathInfo { + int code; + int cell; + int info; +} ExpPathInfo; + +static boolean traces_degree_refine(sparsegraph*, Candidate*, int, Partition*, + struct TracesVars*, struct TracesInfo*, int, int*); +static int traces_vertexclass_refine (int, int*, int*, Candidate*, Partition*, int*); +static int traces_refine(Candidate*, int, Partition*, + struct TracesVars*, struct TracesInfo*, int, boolean); +static void traces_refine_notrace(Candidate*, int, Partition*, + struct TracesVars*, struct TracesInfo*); +static void traces_refine_maketrie(Candidate*, int, Partition*, + struct TracesVars*, struct TracesInfo*); +static int traces_refine_comptrie(Candidate*, int, Partition*, + struct TracesVars*, struct TracesInfo*); +static int traces_refine_sametrace(Candidate*, int, Partition*, + struct TracesVars*, struct TracesInfo*); +static int traces_refine_refine(sparsegraph*, Candidate*, int, Partition*, + struct TracesVars*, struct TracesInfo*); +static void refine_tr_refine(Candidate*, int, Partition*, + struct TracesVars*, struct TracesInfo*); +static int given_gens(sparsegraph*, permnode*, int*, boolean); +static void quickSort(int*, int); +static struct Partition* NewPartition(int); +static struct Candidate* NewCandidate(int, Candidate**, int); +static void NewPartSpine(int, int); +static int FreeList(Candidate*, int); +static int FixBase(int*, struct TracesVars*, Candidate*, int, int); +static boolean FixedBase(int*, struct TracesVars*, Candidate*, int, int); +static void factorial(double*, int*, int); +static void factorial2(double*, int*, int); +static int CheckForAutomorphisms(Candidate*, Candidate*, struct TracesVars*, struct TracesInfo*, int, int, Partition*); +static int CheckForSingAutomorphisms(Candidate*, Partition*, Candidate*, struct TracesVars*, struct TracesInfo*, int, int); +static int CheckForMatching(Candidate*, Candidate*, Partition*, struct TracesVars*, struct TracesInfo*, int, int); +static void Individualize(Partition*, Candidate*, int, int, int, int); +static boolean TreeFyTwo(int, Candidate*, Candidate*, Partition*, int, struct TracesVars*, struct TracesInfo*); +static void ExperimentalStep(Partition*, Candidate*, struct TracesVars*, struct TracesInfo*, int, int); +static boolean TargetCell(Candidate*, Partition*, int, struct TracesVars*, int); +static boolean TargetCellFirstPath(Candidate*, Partition*, struct TracesVars*); +static int TargetCellExpPath(Candidate*, Partition*, struct TracesVars*); +static boolean TargetCellSmall(Candidate*, Partition*, int, struct TracesVars*, int); +static boolean TargetCellFirstPathSmall(Candidate*, Partition*, struct TracesVars*); +static int TargetCellExpPathSmall(Candidate*, Partition*, struct TracesVars*); +static boolean SelectNextLevel(int, struct TracesVars*, struct TracesInfo*); +static void CopyCand(Candidate*, Candidate*, int, int*, int*); +static struct trie* trie_new(int, struct TracesVars*); +static struct trie* trie_make(trie*, int, int, struct TracesVars*); +static struct trie* trie_comp(trie*, int); +static void trie_dump(trie*); +static void trie_class(trie*, int*); +static void RemoveFromLevel(int, int, int, boolean); +static int CompStage0(Partition*, Partition*, Candidate*, Candidate*, int, int, struct TracesVars*, struct TracesInfo*); +static int CompStage1(Partition*, Partition*, Candidate*, Candidate*, int, int, struct TracesVars*, struct TracesInfo*); +static int CompStage2(Partition*, Partition*, Candidate*, Candidate*, int, int, struct TracesVars*, struct TracesInfo*); +static void grouporderplus(sparsegraph*, Candidate*, Partition*, permnode**, double*, int*, int, struct TracesVars*, struct TracesInfo*); +static boolean Prefix(Candidate*, Candidate*, int); +static boolean findperm(permnode*, int*, int); +static int spinelementorbsize(int*, int*, int, int); +static trielist* searchtrie_new(int, struct TracesVars*); +static searchtrie* searchtrie_make(Candidate*, Candidate*, int, struct TracesVars*); +static boolean lookup(searchtrie*); +static int* findcurrorbits(schreier*, int); +static int Preprocess(sparsegraph*, permnode**, Candidate*, int, Partition*, struct TracesVars*); +static void MakeTree(int, int, sparsegraph*, int, struct TracesVars*, boolean); +static void MakeCanTree(int, sparsegraph*, int, Candidate*, Partition*, struct TracesVars*); +static int maxint(int, int); +static int minint(int, int); +static void orbjoin_sp_perm(int*, int*, int*, int, int*); +static void orbjoin_sp_pair(int*, int*, int, int, int, int*); +static boolean isautom_sg_pair(graph*, int*, boolean, int, int, struct TracesVars*); +static void SetAutom(int, int, struct TracesVars*); +static void ResetAutom(int, int, struct TracesVars*); +static void PrintVect(int*, int, int, int); +static void putgraphplus_sg(FILE*, sparsegraph*, int); +static boolean VerifyId(int *p, int n); +static void PrintPartition(int*, int*, int, int, int); +static void Place(int, Candidate*, Partition*); +static int NonSingDeg(int, Candidate*, Partition*); +static int NonSingDegPlus1(Candidate*, Partition*, int, TracesVars*); +static void NonSingDegPlus2(Candidate*, Partition*, int, TracesVars*); +static void Edge_Delete(int, int, Candidate*, TracesVars*); +static boolean VerifyPart(Partition*, int, int); +static int VerifyPerm(int*, int,int); +static boolean VerifyCand(Candidate*, int, int); +static int FirstNeighbour(int, Candidate*, Partition*, int*, int, int*, int); +static int NextNeighbour(int, Candidate*, Partition*, int*, int, int*, int); +static sparsegraph* copy_sg_structure(sparsegraph*, sparsegraph*); +static void WeightCodes (int); +static void PrintWeightedGraph1(sparsegraph*, int, char[30]); +static void PrintWeightedGraph2(int n, char msg[30]); +static void MakeDiscrete(Partition*, int); +static void Complete(sparsegraph*, Candidate*, Partition*, int, TracesVars*, double*, int*,permnode**, int); +static void Allocate_Traces_Structures(int); +static void Allocate_refine_Structures(int); +static void Initialize_Traces_Variables(TracesVars*, TracesOptions*, TracesStats*, int*, sparsegraph*, sparsegraph*, int); +static void Initialize_Traces_Statistics (TracesStats*, int); +static void Initialize_Traces_Time_Variables (TracesVars*); +static int trie_classify(int, TracesVars*); +static int Check_degree_one(sparsegraph*, Candidate*, Partition*, int); +static void sort_Split_Array(int*, int); +static const unsigned int fuzz1[] = {037541, 061532, 005257, 026416}; +static const unsigned int fuzz2[] = {006532, 070236, 035523, 062437}; +static int Select_from_CStack(int*, int); +static void PrintBlissGraph(int); +static void CodeClassify(int, int, int); +static void Adjust_Cycles(Candidate*, Partition*, int, TracesVars*); + +#define FUZZ1(x) ((x) ^ fuzz1[(x)&3]) +#define FUZZ2(x) ((x) ^ fuzz2[(x)&3]) + +#define MASHCOMM(l, i) ((l) + FUZZ1(i)) +#define MASHNONCOMM(l, i) (FUZZ2(l) + (i)) +#define MASH(l, i) ((((l) ^ 065435) + (i)) & 077777) +#define MASH1(l, i) ((l + (i*i)) & 077777) +#define CLEANUP(l) ((int)((l) % 0x7FFF)) +#define SS(n, sing, plur) (n), ((n) == 1?(sing):(plur)) + +#define SETMARK(Arr, Mrk) if (Mrk > (NAUTY_INFINITY-2)) { memset(Arr, 0, n*sizeof(int)); Mrk = 0; } Mrk++; + +#define COPYNODE(W, V) { \ +memcpy(W->lab, V->lab, n*sizeof(int)); \ +memcpy(W->invlab, V->invlab, n*sizeof(int)); \ +W->code = V->code; \ +W->singcode = V->singcode; \ +W->do_it = V->do_it; } + +#define NEXTLINE fprintf(outfile, "\n"); + +#define PRINTCHAR(c) fprintf(outfile, "%s", c); + +#define PRINTCAND(V, Lev) PRINTCHAR(" ") for (tmp=1; tmp<=Lev; tmp++) {fprintf(outfile, tv->digstring, V->lab[Spine[tmp].tgtpos]+labelorg);} + +#define PRINTCANDF(V, Lev) { NEXTLINE for (tmp=1; tmp<=Lev; tmp++) {fprintf(outfile, "F%di", V->lab[Spine[tmp].tgtpos]+labelorg);} NEXTLINE } + +#define PRINTCANDBIG(V, Lev) { PRINTCHAR(" ") \ +for (tmp=1; tmp<=5; tmp++) {fprintf(outfile, tv->digstring, V->lab[Spine[tmp].tgtpos]+labelorg);} \ +fprintf(outfile, "... "); \ +for (tmp=Lev-4; tmp<=Lev; tmp++) {fprintf(outfile, tv->digstring, V->lab[Spine[tmp].tgtpos]+labelorg);} } + +#define LINE(K, c) { PRINTCHAR(c) for (tmp=1; tmp<=K; tmp++) {fprintf(outfile, c);} } + +#define TRACE_CHECK(Tr, Ind, Arg, End) { TracePos = Tr+Ind; \ +if (newtrace) { \ +*TracePos = Arg; \ +} \ +else { \ +if (Ind < *End) { \ +if (*TracePos != Arg) { \ +if (*TracePos > Arg) { \ +return FALSE; \ +} \ +else { \ +*TracePos = Arg; \ +newtrace = TRUE; \ +} \ +} \ +} \ +else { \ +*TracePos = Arg; \ +newtrace = TRUE; \ +} \ +} \ +Ind++; } + +#define SAMETRACE_CHECK(Tr, Ind, Arg, End) { TracePos = Tr+Ind; \ +if (Ind < *End) { \ +if (*TracePos != Arg) { \ +return FALSE; \ +} \ +} \ +else { \ +return FALSE; \ +} \ +Ind++; } + +#define NEWPARTSPINE(Lev) { if (Lev > 3) { \ +Spine[Lev].part = malloc(sizeof(*(Spine[Lev].part))); \ +if (Spine[Lev].part == NULL) { \ +fprintf(ERRFILE, "\nError, memory not allocated.\n"); \ +exit(1); \ +} \ +Spine[Lev].part->cls = Spine[Lev-3].part->cls; \ +Spine[Lev].part->inv = Spine[Lev-3].part->inv; \ +Spine[Lev-3].part->cls = Spine[Lev-3].part->inv = NULL; \ +Spine[Lev].part->code = -1; \ +Spine[Lev].part->cells = 0; \ +} \ +else { \ +Spine[Lev].part = NewPartition(n); \ +} } + +#define FIND_SPLIT_CELLS SplInd = 0; \ +for (j = 0; j < HitClsInd; j++) { \ +ind1 = HitCls[j]; \ +ElmHitCll[ind1] -= ind1; \ +if ((ElmHitCll[ind1] > 0) && (ElmHitCll[ind1] < cls[ind1])) { \ +SplCls[SplInd++] = ind1; \ +} \ +} + +#define FREEPART(Part) { if (Part) { \ +if (Part->cls) free(Part->cls); \ +if (Part->inv) free(Part->inv); \ +free(Part); } \ +} + +#define FREECAND(Cand) { if (Cand) { \ +if (Cand->lab) free(Cand->lab); \ +if (Cand->invlab) free(Cand->invlab); \ +free(Cand); \ +} } + +#define COPYPART(P, Q) { memcpy(P->cls, Q->cls, n*sizeof(int)); \ +memcpy(P->inv, Q->inv, n*sizeof(int)); \ +P->cells = Q->cells; \ +P->code = Q->code; } \ + +#define ADDTONEXTLEVEL { if (SpineTL->listend) { \ +(SpineTL->listend)->next = NewCandidate(n, &GarbList, TRUE); \ +if ((tv->compstage < 2) && (SpineTL->listcounter <= (NAUTY_INFINITY-2))) SpineTL->listcounter++; \ +SpineTL->listend = (SpineTL->listend)->next; \ +CopyCand(SpineTL->listend, NextCand, n, NULL, NULL); \ +} \ +else { \ +SpineTL->liststart = NewCandidate(n, &GarbList, TRUE); \ +if (tv->compstage < 2) SpineTL->listcounter = 1; \ +SpineTL->listend = SpineTL->liststart; \ +CopyCand(SpineTL->liststart, NextCand, n, NULL, NULL); \ +} } + +#define ORBITSIZES { memset(OrbSize, 0, n*sizeof(int)); \ +for (i=0; i<n; i++) { \ +OrbSize[tv->orbits[i]]++; \ +} } + +#define CURRORBITSIZES { memset(CurrOrbSize, 0, n*sizeof(int)); \ +for (i=SpineTL->tgtcell; i<SpineTL->tgtend; i++) { \ +CurrOrbSize[tv->currorbit[CurrCand->lab[i]]]++; \ +} } + +#define EXITFROMSTAGE0REFINE { PRINT_LINE_PLUS(tv->tolevel) \ +if (tv->options->verbosity >= 2) fprintf(outfile, "-=="); \ +CurrCand->indnum--; \ +RemoveFromLevel(tv->tolevel, tv->treedepth, tv->strategy, FALSE); \ +tv->compstage = 1; \ +TempOrbits = NULL; \ +trieroot = trie_new(n, tv); \ +trieref = trieroot; \ +tv->nextlevel = tv->maxtreelevel = tv->fromlevel; \ +ti->thereisnextlevel = TRUE; \ +ti->exitfromref = TRUE; \ +return 0; } + +#define EXITFROMSTAGE0EXPATH2 { PRINT_LINE_PLUS(tv->tolevel) \ +if (tv->options->verbosity >= 2) fprintf(outfile, "=-="); \ +tv->compstage = 1; \ +TempOrbits = NULL; \ +trieroot = trie_new(n, tv); \ +trieref = trieroot; \ +tv->nextlevel = tv->maxtreelevel = tv->tolevel; \ +ti->thereisnextlevel = TRUE; \ +ti->exitfromref = FALSE; \ +return 0; } + +#define EXITFROMSTAGE0EXPATH1 { PRINT_RETURN PRINT_LINE_PLUS(tv->tolevel) \ +if (tv->options->verbosity >= 2) fprintf(outfile, "==-"); \ +if (SpineTL->liststart) { \ +AuxCand = SpineTL->liststart; \ +SpineTL->liststart = NewCandidate(n, &GarbList, TRUE); \ +CopyCand(SpineTL->liststart, NextCand, n, NULL, NULL); \ +SpineTL->liststart->next = AuxCand; \ +} \ +else { \ +SpineTL->liststart = NewCandidate(n, &GarbList, TRUE); \ +SpineTL->listend = SpineTL->liststart; \ +SpineTL->liststart->next = NULL; \ +CopyCand(SpineTL->liststart, NextCand, n, NULL, NULL); \ +} \ +tv->compstage = 1; \ +TempOrbits = NULL; \ +trieroot = trie_new(n, tv); \ +trieref = trieroot; \ +tv->nextlevel = tv->maxtreelevel = tv->tolevel; \ +ti->thereisnextlevel = TRUE; \ +ti->exitfromref = FALSE; \ +return 0; } + +#define UPDATE_LINELGTH { if (tv->options->verbosity >= 2) { \ +if (tv->tolevel < 12) { \ +tv->linelgth = (tv->digits+1)*tv->tolevel+16; \ +} \ +else { \ +tv->linelgth = (tv->digits+1)*10+20; \ +} \ +} } + +#define PRINT_LINE { if ((tv->options->verbosity >= 1) && (tv->strategy == 0)) { \ +if (tv->options->verbosity >= 2) { LINE(tv->linelgth, "-"); \ +NEXTLINE} \ +} \ +} + +#define PRINT_LINE_PLUS(Lev) { if ((tv->options->verbosity >= 1) && (tv->strategy == 0)) { \ +if (tv->options->verbosity >= 2) { LINE(16+tv->digits+1, "-");} \ +fprintf(outfile, " Level %d: %d cell%s; %d singleton%s; target cell: %d; %d orbit%s; %lu node%s (%lu kept); %d update%s;", \ +Lev, SS(Spine[Lev].part->cells, "", "s"), SS((Spine[Lev].part->cells == n) ? n : Spine[Lev].singend, "", "s"), Spine[Lev].tgtsize, SS(tv->stats->numorbits, "", "s"), \ +SS(Spine[Lev].levelcounter, "", "s"), Spine[Lev].keptcounter, SS(Spine[Lev].updates, "", "s")); \ +if (Lev <= tv->group_level) fprintf(outfile, " group_level: %d", tv->group_level); \ +NEXTLINE \ +} \ +} + +#define PRINT_CANDIDATE(Cand, Lev) { \ +for (tmp = Cand->name, cu = 0; tmp > 0; tmp /= 10, ++cu) {} \ +for (tmp = Lev, cu1 = 0; tmp > 0; tmp /= 10, ++cu1) {} \ +cu = 14-cu-cu1; \ +LINE(cu, "-") \ +fprintf(outfile, " %d, %d) ", Lev % 10000, Cand->name % 10000000); \ +if (Lev < 12) { \ +PRINTCAND(Cand, Lev) \ +} \ +else { \ +PRINTCANDBIG(Cand, Lev) \ +} \ +PRINTCHAR("| ") \ +fprintf(outfile, "{%x, %x} ", Cand->code, Cand->singcode); \ +} + +#define PRINT_CANDIDATEPLUS(PrevCand, Cand, Lev) { \ +for (tmp = Cand->name, cu = 0; tmp > 0; tmp /= 10, ++cu) {} \ +for (tmp = Lev, cu1 = 0; tmp > 0; tmp /= 10, ++cu1) {} \ +cu = 14-cu-cu1; \ +LINE(cu, "-") \ +fprintf(outfile, " %d, %d, %d) ", Lev % 10000, Cand->name % 10000000, PrevCand->name); \ +if (Lev < 12) { \ +PRINTCAND(Cand, Lev) \ +} \ +else { \ +PRINTCANDBIG(Cand, Lev) \ +} \ +PRINTCHAR("| ") \ +fprintf(outfile, "{%x, %x} ", Cand->code, Cand->singcode); \ +} + +#define PRINT_EXPPATHSTEP(Cand, Boo) { \ +if (tv->options->verbosity >= 2) { \ +if ((tv->tolevel_tl-tv->tolevel < 6) || !has_nexttcell) { \ +fprintf(outfile, "%d ", tv->indiv_vtx+labelorg); \ +if (Boo) { \ +if (tv->options->verbosity >= 2) fprintf(outfile, "{%d:%x} ", tv->tcellexpath, Cand->code); \ +} \ +else fprintf(outfile, "{interr.(%d)} ", NextPart->cells); \ +if ((!has_nexttcell) && (tv->compstage == 0)) { \ +fprintf(outfile, "(%d) ", tv->tolevel_tl); \ +} \ +} \ +else { \ +if (tv->tolevel_tl-tv->tolevel == 6) { \ +fprintf(outfile, "... "); \ +} \ +} \ +} \ +} + +#define PRINT_RETURN { if (tv->options->verbosity >= 2) { \ +fprintf(outfile, "\n"); \ +} } + +#define PRINT_FROM_VERB(Verb,Lev) { if (tv->options->verbosity >= Verb) { \ +fprintf(outfile, "FROM: "); \ +if (Lev < 12) { \ +PRINTCAND(CurrCand, tv->fromlevel) \ +} \ +else { \ +PRINTCANDBIG(CurrCand, tv->fromlevel) \ +} \ +fprintf(outfile, " do_it: %d, indnum: %d, stnode->index: %d tcell @ %d: %d", CurrCand->do_it, CurrCand->indnum, CurrCand->stnode->index,tv->tcellevel,Spine[tv->tcellevel].tgtcell); \ +PRINT_RETURN \ +} } + +#define PRINT_NOTMIN_VERB(Verb) { if (tv->options->verbosity >= Verb) { \ +fprintf(outfile, " is NOT minimal in orbits (1, %d) [%d]; ", gom_level, CurrCand->lab[Spine[gom_level+1].tgtpos]+labelorg); \ +fprintf(outfile, "at lev %d, orb[%d] = %d.\n", gom_level+1, CurrCand->lab[Spine[gom_level+1].tgtpos]+labelorg, tv->currorbit[CurrCand->lab[Spine[gom_level+1].tgtpos]]+labelorg); } } + +#define PRINT_SKIPPED_VERB(Verb) { if (tv->options->verbosity >= Verb) \ +fprintf(outfile, " skipped (0) (orbit[%d] = %d)\n", \ +NextCand->vertex+labelorg, tv->currorbit[NextCand->vertex]+labelorg); } + +#define PRINT_REFINE_VERB(Verb,where) { if (tv->options->verbosity >= Verb) \ +fprintf(outfile, " REFINE(%c) (orbit[%d] = %d)\n",where, NextCand->vertex+labelorg, tv->currorbit[NextCand->vertex]+labelorg); } + +#define PRINT_INDIV_VERB(Verb,Lev) { if (tv->options->verbosity >= Verb) { \ +if (Lev < 12) { \ +PRINTCAND(CurrCand, tv->fromlevel) \ +} \ +else { \ +PRINTCANDBIG(CurrCand, tv->fromlevel) \ +} \ +fprintf(outfile, "| "); \ +fprintf(outfile, tv->digstring, NextCand->vertex+labelorg); \ +} } + +#define PRINT_INDEX(V,Verb,where) if (tv->options->verbosity >= Verb) \ +fprintf(outfile,"Set index @ %d: Name %d, index %d\n",where,V->name,V->index); + +#define SPECIALGENERATORS { if (tv->options->generators) addpermutation(ring, AUTPERM, n); \ +tv->stats->numgenerators++; \ +tv->specialgens++; \ +if (tv->options->writeautoms) { \ +fprintf(outfile, "Gen #%d: ", tv->stats->numgenerators); \ +writeperm(outfile, AUTPERM, tv->options->cartesian, tv->options->linelength, n); \ +} \ +if (tv->options->userautomproc) { \ +(*tv->options->userautomproc)(tv->stats->numgenerators, AUTPERM, n); \ +} } + +#define UPDATEMIN(A, B) if (B < A) A = B; + +#define PAIRORBJOIN(A, V) { if (A != V) { \ +PrmPairs[tv->permInd].arg = A; \ +PrmPairs[tv->permInd].val = V; \ +tv->permInd++; \ +orbjoin_sp_pair(tv->orbits, OrbList, n, A, V, &tv->stats->numorbits); \ +MakeTree(A, V, sg, n, tv, FALSE); \ +} } + +#define SETPAIRS(A, V) { if (A != V) { \ +PrmPairs[tv->permInd].arg = A; \ +PrmPairs[tv->permInd].val = V; \ +tv->permInd++; \ +} } + +#define SETPAIRSAUT(A, V) { if ((A != V) && (AUTPERM[A] != V)) { \ +AUTPERM[A] = V; \ +PrmPairs[tv->permInd].arg = A; \ +PrmPairs[tv->permInd].val = V; \ +tv->permInd++; \ +} } + +#define SETPAIRSAUTANDTREE(arg, val) { if (tv->build_autom) { SETPAIRSAUT(arg, val) } \ +if (arg != val) orbjoin_sp_pair(tv->orbits, OrbList, n, arg, val, &tv->stats->numorbits); \ +MakeTree(arg, val, sg_orig, n, tv, FALSE); } + +#define PRINTF2(A, B) if (tv->options->verbosity > 3) printf(A, B) +#define PRINTF2_2(A, B, C) if (tv->options->verbosity > 3) printf(A, B, C) +#define PRINTF2_3(A, B, C, D) if (tv->options->verbosity > 3) printf(A, B, C, D) +#define PRINTF2_4(A, B, C, D, E) if (tv->options->verbosity > 3) printf(A, B, C, D, E) + +#define VERB_PRINT(V,Verb,R) if (tv->options->verbosity >= Verb) { \ +fprintf(outfile,"\033[0;32m%s\033[0m ",V); \ +if (R) fprintf(outfile,"\n"); } + +/* data decls. for CPUTIME */ +#ifdef CPUDEFS +CPUDEFS +#endif + +#if !MAXN +DYNALLSTAT(int, AUTPERM, AUTPERM_sz); +DYNALLSTAT(int, BreakSteps, BreakSteps_sz); +DYNALLSTAT(int, CStack, CStack_sz); +DYNALLSTAT(int, CurrOrbSize, CurrOrbSize_sz); +DYNALLSTAT(int, CurrRefCells, CurrRefCells_sz); +DYNALLSTAT(boolean, Diff, Diff_sz); +DYNALLSTAT(boolean, Factorials, Factorials_sz); +DYNALLSTAT(int, fix, fix_sz); +DYNALLSTAT(int, IDENTITY_PERM, IDENTITY_PERM_sz); +DYNALLSTAT(int, Markers, Markers_sz); +DYNALLSTAT(int, TreeMarkers, TreeMarkers_sz); +DYNALLSTAT(int, AutMarkers, AutMarkers_sz); +DYNALLSTAT(int, MarkHitVtx, MarkHitVtx_sz); +DYNALLSTAT(int, MultRefCells, MultRefCells_sz); +DYNALLSTAT(int, NghCounts, NghCounts_sz); +DYNALLSTAT(int, OrbSize, OrbSize_sz); +DYNALLSTAT(int, OrbList, OrbList_sz); +DYNALLSTAT(pair, PrmPairs, PrmPairs_sz); +DYNALLSTAT(int, TempOrbList, TempOrbList_sz); +DYNALLSTAT(int, RefCells, RefCells_sz); +DYNALLSTAT(searchtrie*, RefPath, RefPath_sz); +DYNALLSTAT(int, Singletons, Singletons_sz); +DYNALLSTAT(int, SplCls, SplCls_sz); +DYNALLSTAT(int, SplCnt, SplCnt_sz); +DYNALLSTAT(int, SplPos, SplPos_sz); +DYNALLSTAT(int, StackMarkers, StackMarkers_sz); +DYNALLSTAT(int, TheTrace, TheTrace_sz); +DYNALLSTAT(int, TheTraceCC, TheTraceCC_sz); +DYNALLSTAT(int, TheTraceSplNum, TheTraceSplNum_sz); +DYNALLSTAT(int, TheTraceSteps, TheTraceSteps_sz); +DYNALLSTAT(int, TEMPLAB, TEMPLAB_sz); +DYNALLSTAT(int, TEMPINVLAB, TEMPINVLAB_sz); +DYNALLSTAT(int, WeightsSeq, WeightsSeq_sz); +DYNALLSTAT(int, WorkArray, WorkArray_sz); +DYNALLSTAT(int, WorkArray0, WorkArray0_sz); +DYNALLSTAT(int, WorkArray1, WorkArray1_sz); +DYNALLSTAT(int, WorkArray2, WorkArray2_sz); +DYNALLSTAT(int, WorkArray3, WorkArray3_sz); +DYNALLSTAT(int, WorkArray4, WorkArray4_sz); +DYNALLSTAT(int, WorkArray5, WorkArray5_sz); +DYNALLSTAT(int, WorkArray6, WorkArray6_sz); +DYNALLSTAT(int, WorkArray7, WorkArray7_sz); +DYNALLSTAT(int, Neighbs1, Neighbs1_sz); +DYNALLSTAT(int, Neighbs2, Neighbs2_sz); +DYNALLSTAT(int, TreeStack, TreeStack_sz); +DYNALLSTAT(TracesSpine, Spine, Spine_sz); +DYNALLSTAT(trie*, TrieArray, TrieArray_sz); +DYNALLSTAT(grph_strct, TheGraph, TheGraph_sz); +DYNALLSTAT(ExpPathInfo, EPCodes, EPCodes_sz); +DYNALLSTAT(int, CyclesPart, CyclesPart_sz); +DYNALLSTAT(int, CyclesLength, CyclesLength_sz); +#else +static TLS_ATTR int CStack[MAXN]; +static TLS_ATTR int AUTPERM[MAXN]; +static TLS_ATTR int BreakSteps[MAXN]; +static TLS_ATTR int CurrOrbSize[MAXN]; +static TLS_ATTR int CurrRefCells[MAXN]; +static TLS_ATTR boolean Diff[MAXN]; +static TLS_ATTR boolean Factorials[MAXN]; +static TLS_ATTR int fix[MAXN]; +static TLS_ATTR int IDENTITY_PERM[MAXN]; +static TLS_ATTR int Markers[MAXN]; +static TLS_ATTR int TreeMarkers[MAXN]; +static TLS_ATTR int AutMarkers[MAXN]; +static TLS_ATTR int MarkHitVtx[MAXN]; +static TLS_ATTR int MultRefCells[MAXN]; +static TLS_ATTR int NghCounts[MAXN]; +static TLS_ATTR int OrbSize[MAXN]; +static TLS_ATTR int OrbList[MAXN]; +static TLS_ATTR pair PrmPairs[MAXN]; +static TLS_ATTR int TempOrbList[MAXN]; +static TLS_ATTR int RefCells[MAXN]; +static TLS_ATTR searchtrie* RefPath[MAXN]; +static TLS_ATTR int Singletons[MAXN]; +static TLS_ATTR int SplCls[MAXN]; +static TLS_ATTR int SplCnt[MAXN]; +static TLS_ATTR int SplPos[MAXN]; +static TLS_ATTR int StackMarkers[MAXN]; +static TLS_ATTR int TheTrace[MAXN]; +static TLS_ATTR int TheTraceCC[MAXN]; +static TLS_ATTR int TheTraceSplNum[MAXN]; +static TLS_ATTR int TheTraceSteps[MAXN]; +static TLS_ATTR int TEMPLAB[MAXN]; +static TLS_ATTR int TEMPINVLAB[MAXN]; +static TLS_ATTR int WeightsSeq[MAXN]; +static TLS_ATTR int WorkArray[MAXN]; +static TLS_ATTR int WorkArray0[MAXN]; +static TLS_ATTR int WorkArray1[MAXN]; +static TLS_ATTR int WorkArray2[MAXN]; +static TLS_ATTR int WorkArray3[MAXN]; +static TLS_ATTR int WorkArray4[MAXN]; +static TLS_ATTR int WorkArray5[MAXN]; +static TLS_ATTR int WorkArray6[MAXN]; +static TLS_ATTR int WorkArray7[MAXN]; +static TLS_ATTR int TreeStack[MAXN]; +static TLS_ATTR TracesSpine Spine[MAXN]; +static TLS_ATTR trie* TrieArray[MAXN]; +static TLS_ATTR grph_strct TheGraph[MAXN]; +static TLS_ATTR int Neighbs1[MAXN]; +static TLS_ATTR int Neighbs2[MAXN]; +static TLS_ATTR ExpPathInfo EPCodes[MAXN]; +static TLS_ATTR int CyclesPart[MAXN]; +static TLS_ATTR int CyclesLength[MAXN]; +#endif + +#define PERMSTACK WorkArray1 +#define CYCLES WorkArray1 +#define HitCls WorkArray2 +#define CYCOLR WorkArray2 +#define HitVtx WorkArray3 +#define CYLGTH WorkArray3 +#define CYMULT WorkArray4 +#define HitCount WorkArray5 +#define ElmHitCll WorkArray5 +#define CYCPOS WorkArray5 +#define CYCHIT TempOrbList +#define LGHATTR RefCells +#define CYCREP MultRefCells +#define TempOrbSize TEMPLAB +#define AutomCount TEMPINVLAB +#define CanonIndices MarkHitVtx +#define NSFCells NghCounts +#define TreeNodes AutMarkers +#define CellMarkers1 WorkArray6 +#define CellMarkers2 WorkArray7 +#define SingNonSing Singletons + +static TLS_ATTR FILE *outfile; + +/* Brendan's SCHREIER */ +static TLS_ATTR schreier *gpB; /* This will point to the Schreier structure */ +static TLS_ATTR permnode *gensB; /* This will point to the stored generators */ + +static TLS_ATTR Candidate *GarbList, *SpOrd, *SpCyc, *SpSwp; +static TLS_ATTR Partition *SpPart1, *SpPart2; +static TLS_ATTR TracesSpine *SpineTL, *SpineFL, *SpineTL_tl; +static TLS_ATTR trie *trieroot, *trieref; +static TLS_ATTR int *TempOrbits = NULL; +static TLS_ATTR sparsegraph redgraph; + + +void +Traces(sparsegraph *g_arg, int *lab, int *ptn, + int *orbits_arg, TracesOptions *options_arg, TracesStats *stats_arg, + sparsegraph *canong_arg) { + int i, j; + int tmp; + int deg, vtx1, vtx2, *ngh1, *ngh2, *wgh1, *wgh2, ord; + size_t j1; + + trielist *STStart, *STAux; + searchtrie *TrieNode; + int retval; + Partition *CurrPart, *NextPart; + Candidate *CurrCand, *NextCand, *BestCand, *AuxCand; + + const int n = g_arg->nv; + const int m = SETWORDSNEEDED(n); + + if (g_arg->nv > (NAUTY_INFINITY-2)) + { + fprintf(ERRFILE, "Traces: need n <= %d, but n=%d\n\n", + NAUTY_INFINITY-2, g_arg->nv); + return; + } + + Allocate_Traces_Structures(n); + + struct TracesVars *tv = malloc(sizeof(struct TracesVars)); + if (tv == NULL) { + fprintf(ERRFILE, "\nError, memory not allocated.\n"); + exit(1); + } + struct TracesInfo *ti = malloc(sizeof(struct TracesInfo)); + if (ti == NULL) { + fprintf(ERRFILE, "\nError, memory not allocated.\n"); + exit(1); + } + + trieroot = NULL; + NextCand = GarbList = NULL; + DYNFREE(g_arg->w,g_arg->wlen); /* to be removed in presence of weightd edges */ + + Initialize_Traces_Variables(tv, options_arg, stats_arg, orbits_arg, g_arg, canong_arg, n); + + outfile = (tv->options->outfile == NULL ? stdout : tv->options->outfile); + + SpOrd = SpCyc = SpSwp = NULL; + SpPart1 = SpPart2 = NULL; + + if (tv->options->verbosity >= 2) { + for (i = n, tv->digits = 0; i > 0; i /= 10, ++tv->digits) {} + snprintf(tv->digstring, 25, "%s%dd ", "%", tv->digits); + } + + /* Initialize group and statistics */ + Initialize_Traces_Statistics(stats_arg,n); + + if (tv->options->verbosity >= 2) { + Initialize_Traces_Time_Variables(tv); + } + + /* Initialize lab and ptn when in the unit partition case */ + if (tv->options->defaultptn) { + for (i = 0; i < n; i++) { + IDENTITY_PERM[i] = i; + ptn[i] = NAUTY_INFINITY; + } + ptn[n-1] = 0; + memcpy(lab, IDENTITY_PERM, n*sizeof(int)); + } else { + for (i = 0; i < n; i++) { + IDENTITY_PERM[i] = i; + } + } + + memcpy(orbits_arg, IDENTITY_PERM, n*sizeof(int)); + + if (tv->options->generators) { + tv->stats->numorbits = given_gens(g_arg, *tv->options->generators, + orbits_arg, tv->options->digraph); + newgroup(&gpB, NULL, n); + gensB = *tv->options->generators; + expandschreier(gpB, &gensB, n); + ti->thegrouphaschanged = TRUE; + ti->identitygroup = FALSE; + memcpy(OrbList, gpB->orbits, n*sizeof(int)); + } + else { + newgroup(&gpB, &gensB, n); + memcpy(OrbList, IDENTITY_PERM, n*sizeof(int)); + tv->stats->numorbits = n; + ti->thegrouphaschanged = FALSE; + ti->identitygroup = TRUE; + } + + copy_sg_structure(&redgraph, g_arg); + + tv->graph = &redgraph; + if (g_arg->w) memcpy(tv->graph->w, g_arg->w, tv->graph->wlen*sizeof(int)); + if (g_arg->e) memcpy(tv->graph->e, g_arg->e, tv->graph->elen*sizeof(int)); + + for (i=0; i<n; i++) { + EPCodes[i].info = 0; + TheGraph[i].d = g_arg->d[i]; + if (TheGraph[i].d > tv->maxdeg) { + tv->maxdeg = TheGraph[i].d; + } + if (TheGraph[i].d < tv->mindeg) { + tv->mindeg = TheGraph[i].d; + } + if (g_arg->e) { + TheGraph[i].e = tv->graph->e + g_arg->v[i]; + } + else { + TheGraph[i].e = NULL; + } + if (g_arg->w) { + TheGraph[i].w = tv->graph->w + g_arg->v[i]; + } + else { + TheGraph[i].w = NULL; + } + TheGraph[i].one = FALSE; + } + + ord = 0; + + /*----------- WEIGHTS --------------*/ + if (tv->options->weighted) { + WeightCodes(n); + ord = trie_classify(n,tv); + } + /*----------------------------------*/ + + if ((tv->maxdeg == tv->mindeg) && (ord == 0)) ti->regular = TRUE; else ti->regular = FALSE; + + tv->currorbit = gpB->orbits; + + memcpy(AUTPERM, IDENTITY_PERM, n*sizeof(int)); + tv->permInd = 0; + + memset(fix, 0, n*sizeof(int)); + memset(TheTraceCC, 0, n*sizeof(int)); + memset(Factorials, 0, n*sizeof(int)); + /* ran_init(1234); any long int as an argument */ + + /* The graph is sparse? */ + if (g_arg->nde < n || g_arg->nde / n < n / (g_arg->nde / n)) { + ti->thegraphisparse = TRUE; + } + else { + ti->thegraphisparse = FALSE; + } + + tv->preprocessed = 0; + ti->deg_one = FALSE; + ti->first_matching = FALSE; + retval = 0; + + /* Initialize candidate, partition, cells, orbits */ + Spine[0].part = NewPartition(n); + CurrPart = Spine[0].part; + memset(CurrPart->inv, 0, n*sizeof(int)); + + NextPart = NewPartition(n); + CurrCand = NewCandidate(n, &GarbList, TRUE); + + CurrCand->singcode = 0; + TempOrbits = NULL; + STStart = NULL; + + if (ti->regular) { + if (tv->options->defaultptn) { + memcpy(CurrCand->lab, IDENTITY_PERM, n*sizeof(int)); + memcpy(CurrCand->invlab, IDENTITY_PERM, n*sizeof(int)); + CurrPart->cells = 1; + CurrPart->cls[0] = n; + TheTrace[0] = 0; + } + else { + memcpy(CurrCand->lab, lab, n*sizeof(int)); + CurrPart->cells = 0; + j = 0; + for (i = 0; i < n; i++) { + if (j) CurrPart->inv[i] = j; + CurrCand->invlab[CurrCand->lab[i]] = i; + if (!ptn[i]) { + CurrPart->cls[j] = i-j+1; + if (CurrPart->cls[j] == 1) { + CurrCand->singcode = MASHCOMM(CurrCand->singcode, CurrCand->lab[j]); + } + TheTrace[CurrPart->cells++] = j; + j = i+1; + } + } + } + } else { + if (tv->options->weighted) { + CurrPart->cells = traces_vertexclass_refine(n, lab, ptn, + CurrCand, CurrPart, WeightsSeq); + } + else { + CurrPart->cells = traces_vertexclass_refine (n, lab, ptn, + CurrCand, CurrPart, g_arg->d); + } + } + + memset(NghCounts,0,n*sizeof(int)); + if (tv->options->verbosity == 7) PrintPartition(CurrCand->lab,CurrPart->cls,n,labelorg,1323); + + /* Check for deg 1 vertices */ + ti->deg_one = Check_degree_one(g_arg, CurrCand, CurrPart, n); + +#if !MAXN + DYNALLOC1(int, Neighbs1, Neighbs1_sz, tv->maxdeg, "Traces"); + DYNALLOC1(int, Neighbs2, Neighbs2_sz, tv->maxdeg, "Traces"); +#endif + + if (ti->deg_one) { + tv->preprocessed = Preprocess(g_arg, &gensB, CurrCand, n, CurrPart, tv); + } + + if (tv->preprocessed) { + memset(Diff,0,n*sizeof(boolean)); + for (i=0; i<n; i++) { + if ((TheGraph[i].d > 1) && (tv->input_graph->d[i] != TheGraph[i].d)) + Diff[i] = TRUE; + } + } + + /* Initialization of Spine structure */ + SpineFL = Spine; + SpineFL->tgtcell = SpineFL->tgtpos = 0; + SpineFL->tgtend = n; + SpineFL->tgtfrom = -1; + SpineFL->trcstart = 0; + SpineFL->trcend = CurrPart->active = CurrPart->cells; + SpineFL->ccstart = SpineFL->ccend = 0; + SpineFL->stpstart = SpineFL->stpend = 0; + SpineFL->singstart = SpineFL->singend = 0; + SpineFL->thetracexists = FALSE; + SpineFL->liststart = SpineFL->listend = CurrCand; + SpineFL->levelcounter = 1; + SpineFL->keptcounter = 1; + SpineFL->updates = 1; + + /* Further initializations */ + tv->maxtreelevel = 0; + tv->tolevel = 0; + tv->tcell = 0; + UPDATE_LINELGTH + + /* First refinement */ + if (tv->preprocessed < 2) + traces_refine(CurrCand, n, CurrPart, tv, ti, 0, FALSE); + + CurrCand->name = 0; + + if (CurrPart->cells == n) { + tv->stats->canupdates++; + + /* CANONICAL FORM ? */ + if (tv->options->getcanon) { + memcpy(lab, CurrCand->lab, n*sizeof(int)); + if (canong_arg) memcpy(CurrPart->inv, CurrCand->invlab, n*sizeof(int)); + if (tv->options->usercanonproc != NULL) + { + (*tv->options->usercanonproc)((graph*)g_arg, lab, (graph*)canong_arg, tv->stats->canupdates, CurrCand->code, m, n); + } + } + } + else { + STStart = searchtrie_new(n, tv); + CurrCand->stnode = tv->strielist->triearray; + NextCand = NewCandidate(n, &GarbList, TRUE); + SpineFL->listcounter = 1; + tv->tcellevel = 0; + ti->exitfromref = FALSE; + Spine[1].levelcounter = 0; + Spine[1].updates = 0; + Spine[1].tgtfrom = 0; + + memset(WorkArray, 0, n*sizeof(int)); + + do { + tv->fromlevel = tv->tolevel; + SpineFL = Spine+tv->fromlevel; + + if (CurrCand) { + switch (tv->compstage) { + case 0: retval = CompStage0(CurrPart, NextPart, CurrCand, NextCand, m, n, tv, ti); + break; + case 1: + if (!TempOrbits) { + memcpy(WorkArray1, tv->currorbit, n*sizeof(int)); + TempOrbits = WorkArray1; + } + memset(TempOrbSize, 0, n*sizeof(int)); + for (i=SpineTL->tgtcell; i<SpineTL->tgtend; i++) { + TempOrbSize[TempOrbits[CurrCand->lab[i]]]++; + } + retval = CompStage1(CurrPart, NextPart, CurrCand, NextCand, m, n, tv, ti); + break; + case 2: + retval = CompStage2(CurrPart, NextPart, CurrCand, NextCand, m, n, tv, ti); + break; + default: + break; + } + if (retval == NAUTY_ABORTED) + tv->stats->errstatus = NAUABORTED; + else if (retval == NAUTY_KILLED) { + tv->stats->errstatus = NAUKILLED; + return; + } + } + /* NEXT CANDIDATE */ + if (ti->thereisnextlevel) { + if (tv->nextlevel != tv->fromlevel) { + UPDATE_LINELGTH + } + tv->tolevel = tv->nextlevel; + CurrCand = Spine[tv->nextlevel].liststart; + CurrPart = Spine[tv->nextlevel].part; + } + } + while (ti->thereisnextlevel); + + if (!retval) { + if (tv->compstage) { + memset(CurrOrbSize, 0, n*sizeof(int)); + for (i=0; i<n; i++) { + CurrOrbSize[TempOrbits[i]]++; + } + } + + if (!tv->options->getcanon) { + if (tv->compstage) { + tv->maxtreelevel++; + TrieNode = Spine[tv->maxtreelevel].liststart->stnode; + if (TrieNode->father) { + TrieNode = TrieNode->father; + } + if (!ti->exitfromref) { + TrieNode->index = 0; + for (i=1; i<AutomCount[0]; i++) { + TrieNode->index += CurrOrbSize[AutomCount[i]]; + PRINT_INDEX(TrieNode,4,30) + } + } + } + } + + if (tv->maxtreelevel) PRINT_LINE_PLUS(tv->maxtreelevel); + + AuxCand = Spine[tv->maxtreelevel].liststart; + while (!AuxCand->do_it) { + AuxCand = AuxCand->next; + } + + /* CANONICAL FORM ? */ + if (tv->options->getcanon) { + BestCand = AuxCand; + AuxCand = AuxCand->next; + while (AuxCand) { + if (AuxCand->do_it) { + if (comparelab_tr(g_arg, BestCand->lab, BestCand->invlab, AuxCand->lab, AuxCand->invlab, Spine[tv->maxtreelevel].part->cls, Spine[tv->maxtreelevel].part->inv) == 1) { + BestCand = AuxCand; + tv->stats->canupdates++; + if (tv->options->usercanonproc != NULL) + { + (*tv->options->usercanonproc)((graph*)g_arg, lab, (graph*)canong_arg, tv->stats->canupdates, CurrCand->code, m, n); + } + } + } + AuxCand = AuxCand->next; + } + + grouporderplus(g_arg, BestCand, Spine[tv->maxtreelevel].part, &gensB, &(tv->stats->grpsize1), &(tv->stats->grpsize2), n, tv, ti); + + if (tv->options->verbosity >= 2) { + LINE(32, "-") + NEXTLINE + fprintf(outfile, "Canonical:"); + PRINTCAND(BestCand, tv->maxtreelevel) + PRINT_RETURN + } + memcpy(lab, BestCand->lab, n*sizeof(int)); + if (canong_arg) memcpy(CurrPart->inv, BestCand->invlab, n*sizeof(int)); + } + else { + grouporderplus(g_arg, AuxCand, Spine[tv->maxtreelevel].part, &gensB, &(tv->stats->grpsize1), &(tv->stats->grpsize2), n, tv, ti); + } + + if (tv->options->verbosity >= 2) { + if (tv->linelgth < 40) { + tv->linelgth = 40; + } + LINE(32, "-") + NEXTLINE + } + + } + } + tv->stats->treedepth = tv->treedepth; + if (Spine[tv->treedepth].part->code == -1) { + tv->stats->treedepth--; + } + + if (tv->options->verbosity >= 2) { + fprintf(outfile, "group time: %.2f, %.2f, %.2f; total: %.2f; exp_paths time: %.2f; aut_check time: %.2f\n%lu refinement%s interrupted by trace comparison (%s); special cells: %d\n------", tv->schreier1, tv->schreier2, tv->schreier3, tv->schreier1+tv->schreier2+tv->schreier3, + tv->expaths, tv->autchk, SS(tv->stats->interrupted, "", "s"), (tv->options->strategy == 0 ? "breadth-first" : "depth-first"), tv->specialgens); + PRINT_RETURN + } + if (tv->options->verbosity >= 3) fprintf(outfile, "CPYCAND(0): %d, ID<-TMPORB(1): %d, LAB(2): %d, PART(3): %d, TEMP(4)->: %d, TEMP(5)<-: %d, CHKFORAUT(6): %d, ISAUT(7): %d, ContaTC: %d\n", tv->conta0, tv->conta1, tv->conta2, tv->conta3, tv->conta4, tv->conta5, tv->conta6, tv->conta7, tv->contatc); + + if (tv->options->getcanon && canong_arg) { + canong_arg->nv = g_arg->nv; + canong_arg->nde = g_arg->nde; + SG_ALLOC(*canong_arg, g_arg->nv, g_arg->nde, "traces canong"); + updatecan_tr(g_arg, canong_arg, lab, CurrPart->inv, 0); + } + + if (tv->options->generators) { + deleteunmarked(&gensB); + *tv->options->generators = gensB; + freeschreier(&gpB, NULL); + } + else { + freeschreier(&gpB, &gensB); + schreier_freedyn(); + } + + while (STStart) { + STAux = STStart; + free(STAux->triearray); + STStart = STStart->next; + free(STAux); + } + + tv->canlist = 0; + for (i=0; i<=tv->treedepth; i++) { + if (Spine[i].liststart) { + tv->canlist += FreeList(Spine[i].liststart, TRUE); + Spine[i].liststart = Spine[i].listend = NULL; + } + } + + if (GarbList) { + tv->stats->peaknodes = FreeList(GarbList, FALSE); + } + + FREECAND(NextCand) + FREECAND(SpOrd) + FREECAND(SpCyc) + FREECAND(SpSwp) + FREEPART(NextPart) + FREEPART(SpPart1) + FREEPART(SpPart2) + + if (!tv->options->getcanon && trieroot) { + for (i=0; i<=tv->triepos; i++) { + free(TrieArray[i]); + } + } + + for (i=0; i <= tv->treedepth; i++) { + FREEPART(Spine[i].part) + } + + CurrCand = GarbList = NULL; + tv->stats->peaknodes += tv->canlist; + + if (tv->graph != g_arg) { + SG_FREE(redgraph); + } + free(tv); + free(ti); + traces_freedyn(); + + return; +} + +int traces_vertexclass_refine (int n, int *lab, int *ptn, Candidate *Cand, Partition *Part, int *RefArray) { + + int i, j, k, aux, cells, end; + + memcpy(Cand->lab, lab, n*sizeof(int)); + + cells = 0; + j = 0; + + for (i = 0; i < n; i++) { + WorkArray1[i] = RefArray[Cand->lab[i]]; + if (!ptn[i]) { + TheTrace[cells++] = j; + + sort2ints(WorkArray1+j,Cand->lab+j,i-j+1); + + aux = WorkArray1[j]; + Part->cls[j] = 1; + Part->inv[j] = j; + Cand->invlab[Cand->lab[j]] = j; + + if (i == j) { + Cand->singcode = MASHCOMM(Cand->singcode, Cand->lab[j]); + j++; + } else { + end = i+1; + for (k=j+1; k<end; k++) { + if (WorkArray1[k] == aux) { + (Part->cls[j])++; + Part->inv[k] = j; + Cand->invlab[Cand->lab[k]] = k; + } else { + if (Part->cls[j] == 1) { + Cand->singcode = MASHCOMM(Cand->singcode, Cand->lab[j]); + } + + TheTrace[cells++] = k; + j = k; + aux = WorkArray1[j]; + Part->cls[j] = 1; + Part->inv[j] = j; + Cand->invlab[Cand->lab[j]] = j; + } + } + j = end; + } + } + } + return cells; +} + +int traces_refine(Candidate *Cand, + int n, + Partition *Part, + struct TracesVars* tv, + struct TracesInfo *ti, + int num_indv, + boolean make_code) { + + int i, j, k, jk, sc, ind0, ind1, ind2, ind3, ind4, tlp1, labi; + int value, iend, newcell; + int HitClsInd, SplInd, SplCntInd, CStackInd, TraceInd, TraceCCInd, TraceStepsInd, SingInd; + int j1int, iend1int; + unsigned int longcode; + int newtrace = FALSE; + int Sparse = TRUE; + int *lab, *cls, *InvLab, *TracePos, *SplitCell, *LabCell, *TraceEnd, Traceccend, *Tracestpend; + int BigCell, BigCellPos, BigCellSize; + boolean TraceCell = FALSE; + int *nghb; + int conta; + const int variation = 0; + int currentweight, weightstart, weightend, currentcell, currentsize; + + VERB_PRINT("RFLT",3,FALSE) + + HitClsInd = 0; + if (tv->stackmark > (NAUTY_INFINITY-2)) { + memset(StackMarkers, 0, n*sizeof(int)); + tv->stackmark = 0; + } + tv->stackmark++; + tv->augmented_cells = Part->cells; + + SpineTL = Spine+tv->tolevel; + TraceEnd = &(SpineTL->trcend); + Traceccend = SpineTL->ccend; + Tracestpend = &(SpineTL->stpend); + TraceCCInd = SpineTL->ccstart; + TraceStepsInd = SpineTL->stpstart; + + SingInd = SpineTL->singstart + num_indv; + + lab = Cand->lab; + InvLab = Cand->invlab; + cls = Part->cls; + + UPDATEMIN(Part->active, n-1); + memcpy(CStack+1, TheTrace+SpineTL->trcstart, (Part->active)*sizeof(int)); + CStackInd = Part->active; + for (i = 1; i <= CStackInd; i++) { + StackMarkers[CStack[i]] = tv->stackmark; + } + + longcode = Part->cells; + TraceInd = SpineTL->trcstart+Part->active; + if (!SpineTL->thetracexists) { + newtrace = TRUE; + } + conta=0; + + while (CStackInd > 0) { + + weightend = 0; + + if (tv->mark > (NAUTY_INFINITY-2)) { + memset(Markers, 0, n*sizeof(int)); + memset(MarkHitVtx, 0, n*sizeof(int)); + tv->mark = 0; + } + tv->mark++; + + if (Part->cells == n) break; + + k = Select_from_CStack(cls, CStackInd); + + currentcell = CStack[k]; + currentsize = currentcell+cls[currentcell]; + CStack[k] = CStack[CStackInd--]; + StackMarkers[currentcell] = 0; + + labi = lab[currentcell]; + iend1int = TheGraph[labi].d; + nghb = TheGraph[labi].e; + + do { + ind0 = currentcell; + ind2 = currentsize; + weightstart = weightend; + + if (tv->options->weighted) { + currentweight = (TheGraph[labi].w)[weightstart]; + while ((iend1int > weightend) && ((TheGraph[labi].w)[weightend] == currentweight)) { + weightend++; + } + } else { + weightend = TheGraph[labi].d; + } + + if (!newtrace) { + TraceCell = ((ind0 == TheTraceCC[TraceCCInd]) && (TraceCCInd < Traceccend)); + } + + /* Analysis of occurrences of neighbors of the current cell */ + /* The list of cells with neighbors in the current cell is built */ + if (cls[ind0] == 1) { /* SINGLETON CURRENT CELL CASE */ + + /* NEIGHCOUNT_SING_MULT */ + HitClsInd = 0; + for (j1int = weightstart; j1int < weightend; ++j1int) { + k = nghb[j1int]; + value = Part->inv[InvLab[k]]; + if (cls[value] > 1) { + if (Markers[value] != tv->mark) { + HitCls[HitClsInd++] = value; + Markers[value] = tv->mark; + ElmHitCll[value] = value; + } + HitVtx[ElmHitCll[value]++] = k; + } else { + switch (variation) { + case 1: + longcode = MASHCOMM(longcode, value); + break; + default: + break; + } + } + } + /* end NEIGHCOUNT_SING_MULT */ + + tv->mark++; + FIND_SPLIT_CELLS; + + /* SINGLETON CC CASE */ + if (SplInd) { + if (newtrace) { + TheTraceCC[TraceCCInd] = ind0; + } + if (!TraceCell) { + TheTraceCC[TraceCCInd] = ind0; + newtrace = TRUE; + } + + TRACE_CHECK(TheTraceSplNum, TraceCCInd, SplInd, &Traceccend) + + /* Sorting the cells to be split */ + sort_Split_Array(SplCls, SplInd); + + for (j = 0; j < SplInd; j++) { + ind1 = SplCls[j]; + i = ind1+cls[ind1]-ElmHitCll[ind1]; + TRACE_CHECK(TheTrace, TraceInd, i, TraceEnd) + } + + for (j = 0; j < SplInd; j++) { + /* REARRANGE_CELLS */ + ind1 = SplCls[j]; + cls[ind1] = cls[ind1]-ElmHitCll[ind1]; + newcell = ind1+cls[ind1]; + cls[newcell] = ElmHitCll[ind1]; + Part->cells++; + if (StackMarkers[ind1] != tv->stackmark) { + if (cls[newcell] < cls[ind1]) { + CStack[++CStackInd] = newcell; + StackMarkers[newcell] = tv->stackmark; + } + else { + CStack[++CStackInd] = ind1; + StackMarkers[ind1] = tv->stackmark; + } + } + else { + CStack[++CStackInd] = newcell; + StackMarkers[newcell] = tv->stackmark; + } + SplitCell = HitVtx+ind1; + ind3 = cls[newcell]; + LabCell = lab+newcell; + for (jk = 0; jk < ind3; jk++) { + k = SplitCell[jk]; + i = LabCell[jk]; + Part->inv[newcell+jk] = newcell; + lab[InvLab[k]] = i; + InvLab[i] = InvLab[k]; + LabCell[jk] = k; + InvLab[k] = newcell+jk; + } + /* END REARRANGE_CELLS */ + + if (cls[ind1] == 1) { + Cand->singcode = MASHCOMM(Cand->singcode, Cand->lab[ind1]); + if (newtrace) Singletons[SingInd] = ind1; + SingInd++; + } + if (cls[newcell] == 1) { + Cand->singcode = MASHCOMM(Cand->singcode, Cand->lab[newcell]); + if (newtrace) Singletons[SingInd] = newcell; + SingInd++; + } + } + } + else { + if ((!newtrace) && TraceCell) { + if (!tv->options->weighted) return 0; + } + } + } + else { + if (ti->thegraphisparse) { + + /* NEIGHCOUNT_SPARSE_MULT */ + HitClsInd = 0; + if (cls[ind0] != n) { + for (i = ind0; i < ind2; i++) { + labi = lab[i]; + nghb = TheGraph[labi].e; + for (j = weightstart; j < weightend; j++) { + k = nghb[j]; + if (MarkHitVtx[k] == tv->mark) { + NghCounts[k]++; + } + else { + value = Part->inv[InvLab[k]]; + if (cls[value] > 1) { + MarkHitVtx[k] = tv->mark; + NghCounts[k] = 1; + if (Markers[value] != tv->mark) { + HitCls[HitClsInd++] = value; + Markers[value] = tv->mark; + HitVtx[value] = k; + ElmHitCll[value] = 1; + } + else { + HitVtx[value+ElmHitCll[value]++] = k; + } + } + else { + switch (variation) { + case 1: + longcode = MASHCOMM(longcode, value); + break; + default: + break; + } + } + } + } + } + } + /* End NEIGHCOUNT_SPARSE_MULT */ + + tv->mark++; + + SplInd = 0; + SplCls[0] = n; + for (j = 0; j < HitClsInd; j++) { + ind1 = HitCls[j]; + if ((ElmHitCll[ind1] > 0) && (ElmHitCll[ind1] < cls[ind1])) { + SplCls[SplInd++] = ind1; + } + else { + ind2 = ind1+cls[ind1]; + value = NghCounts[lab[ind1++]]; + for (i = ind1; i < ind2; i++) + { + if (NghCounts[lab[i]] != value) + { + SplCls[SplInd++] = HitCls[j]; + break; + } + } + } + } + + /* SPARSE CASE */ + if (SplInd) { + if (newtrace) { + TheTraceCC[TraceCCInd] = ind0; + } + if (!TraceCell) { + TheTraceCC[TraceCCInd] = ind0; + newtrace = TRUE; + } + TRACE_CHECK(TheTraceSplNum, TraceCCInd, SplInd+n, &Traceccend) + + /* Sorting the cells to be split */ + sort_Split_Array(SplCls, SplInd); + + for (sc = 0; sc < SplInd; sc++) { /* For each cell C to be split */ + ind0 = SplCls[sc]; + ind1 = ind0 + cls[ind0]; + SplCntInd = 0; + if (ElmHitCll[ind0] < cls[ind0]) { + SplCnt[SplCntInd++] = 0; + SplPos[0] = cls[ind0] - ElmHitCll[ind0]; + } + + /* According to the numbers of neighbors of C into the current cell */ + /* compute how many vertices in C will be placed into the same new cell */ + iend = ind0 + ElmHitCll[ind0]; + for (i = ind0; i < iend; i++) { + value = NghCounts[HitVtx[i]]; + if (Markers[value] != tv->mark) { + Markers[value] = tv->mark; + SplCnt[SplCntInd++] = value; + SplPos[value] = 1; + } + else { + SplPos[value]++; + } + } + tv->mark++; + + if (SplCntInd) { + TRACE_CHECK(TheTraceSteps, TraceStepsInd, SplCntInd+n, Tracestpend) + } + + /* Sort the values deriving from the previous step */ + sort_Split_Array(SplCnt,SplCntInd); + Part->cells += SplCntInd-1; + + /* Split the cell C and update the information for sizes of new cells */ + /* Put the new cells into the stack */ + i = ind0; + if (StackMarkers[i] != tv->stackmark) { + BigCellSize = 0; + } + for (k = 0; k < SplCntInd; k++) { + value = SplPos[SplCnt[k]]; + cls[i] = value; + if ((StackMarkers[ind0] != tv->stackmark) && (value > BigCellSize)) { + BigCell = i; + BigCellPos = CStackInd; + BigCellSize = cls[i]; + } + SplPos[SplCnt[k]] = i; + i += value; + if (i < ind1) { + CStack[++CStackInd] = i; + StackMarkers[i] = tv->stackmark; + TRACE_CHECK(TheTrace, TraceInd, i, TraceEnd) + } + } + + if ((StackMarkers[ind0] != tv->stackmark) && (BigCell != ind0)) { + CStack[BigCellPos] = ind0; + StackMarkers[BigCell] = 0; + StackMarkers[ind0] = tv->stackmark; + } + /* Permute elements of the cell C */ + iend = ind0 + ElmHitCll[ind0]; + for (i = ind0; i < iend; i++) { + value = HitVtx[i]; + j = SplPos[NghCounts[value]]++; /* where HitVtx[i] goes */ + k = InvLab[value]; /* where HitVtx[i] is in lab */ + lab[k] = lab[j]; + lab[j] = value; + InvLab[value] = j; + InvLab[lab[k]] = k; + NghCounts[value] = 0; + } + + /* Reconstruct the cell C and update the inverse partition */ + newcell = ind1 - ElmHitCll[ind0]; + i = newcell; + ind2 = newcell+cls[newcell]-1; + do { + Part->inv[i] = newcell; + if (i == ind2) { + newcell = i+1; + if (newcell < n) ind2 = newcell+cls[newcell]-1; + } + } + while (++i < ind1); + + for (i = ind0, k = 0; k < SplCntInd; i+=cls[i], k++) { + if (cls[i] == 1) { + Cand->singcode = MASHCOMM(Cand->singcode, Cand->lab[i]); + if (newtrace) Singletons[SingInd] = i; + SingInd++; + } + } + + } + } + else { + if ((!newtrace) && TraceCell) { + return 0; + } + } + + } + else { + if (TheGraph[lab[ind0]].d > n/cls[ind0]) { + Sparse = FALSE; + } + else { + Sparse = TRUE; + } + Sparse = TRUE; + if (Sparse) { + /* Counting occurrences of neighbors of the current cell */ + /* The list of cells with neighbors in the current cell is also built */ + if (cls[ind0] == n) { + for (i = 0; i < n; i++) { + NghCounts[i] = TheGraph[i].d; + } + HitCls[0] = 0; + HitClsInd = 1; + } + else { + memset(NghCounts, 0, n*sizeof(int)); + + /* NEIGHCOUNT_DENSE_SPARSE_MULT */ + HitClsInd = 0; + for (i = ind0; i < ind2; i++) { + labi = lab[i]; + nghb = TheGraph[labi].e; + for (j1int = weightstart; j1int < weightend; ++j1int) { + k = nghb[j1int]; + (NghCounts[k])++; + value = Part->inv[InvLab[k]]; + if (Markers[value] != tv->mark) { + if (cls[value] > 1) HitCls[HitClsInd++] = value; + Markers[value] = tv->mark; + } + } + } + /* End NEIGHCOUNT_DENSE_SPARSE_MULT */ + + } + + tv->mark++; + + + SplInd = 0; + for (j = 0; j < HitClsInd; j++) { + ind1 = HitCls[j]; + ind2 = ind1+cls[ind1]; + value = NghCounts[lab[ind1++]]; + for (i = ind1; i < ind2; i++) + { + if (NghCounts[lab[i]] != value) + { + SplCls[SplInd++] = HitCls[j]; + break; + } + } + } + + /* DENSE-SPARSE CASE */ + if (SplInd) { + if (newtrace) { + TheTraceCC[TraceCCInd] = ind0; + } + if (!TraceCell) { + TheTraceCC[TraceCCInd] = ind0; + newtrace = TRUE; + } + TRACE_CHECK(TheTraceSplNum, TraceCCInd, SplInd+2*n, &Traceccend) + + /* Sorting the cells to be split */ + sort_Split_Array(SplCls, SplInd); + + for (j = 0; j < SplInd; j++) { /* For each cell C to be split */ + ind0 = SplCls[j]; + ind1 = ind0+cls[ind0]; + SplCntInd = 0; + + /* According to the numbers of neighbors of C into the current cell */ + /* compute how many vertices in C will be placed into the same new cell */ + for (i = ind0; i < ind1; i++) { + value = NghCounts[lab[i]]; + if (Markers[value] != tv->mark) { + Markers[value] = tv->mark; + SplCnt[SplCntInd++] = value; + SplPos[value] = 1; + } + else { + SplPos[value]++; + } + } + tv->mark++; + + if (SplCntInd) { + TRACE_CHECK(TheTraceSteps, TraceStepsInd, SplCntInd+2*n, Tracestpend) + } + + /* Sort the values deriving from the previous step */ + sort_Split_Array(SplCnt, SplCntInd); + + Part->cells += SplCntInd-1; + + /* Split the cell C and update the information for sizes of new cells */ + /* Put the new cells into the stack */ + i = ind0; + if (StackMarkers[i] != tv->stackmark) { + BigCellSize = 0; + } + for (k = 0; k < SplCntInd; k++) { + value = SplPos[SplCnt[k]]; + cls[i] = value; + + if ((StackMarkers[ind0] != tv->stackmark) && (value > BigCellSize)) { + BigCell = i; + BigCellPos = CStackInd; + BigCellSize = cls[i]; + } + SplPos[SplCnt[k]] = i; + i += value; + if (i < ind1) { + CStack[++CStackInd] = i; + StackMarkers[i] = tv->stackmark; + TRACE_CHECK(TheTrace, TraceInd, i, TraceEnd) + } + } + + if ((StackMarkers[ind0] != tv->stackmark) && (BigCell != ind0)) { + CStack[BigCellPos] = ind0; + StackMarkers[BigCell] = 0; + StackMarkers[ind0] = tv->stackmark; + } + + /* Permute elements of the cell C */ + i = ind0; + do { + SplCnt[SplPos[NghCounts[lab[i]]]++] = lab[i]; + } + while(++i < ind1); + + /* Reconstruct the cell C and update the inverse partition */ + newcell = ind0; + i = ind0; + ind2 = newcell+cls[newcell]-1; + do { + lab[i] = SplCnt[i]; + InvLab[lab[i]] = i; + + Part->inv[i] = newcell; + if (i == ind2) { + newcell = i+1; + if (newcell < n) ind2 = newcell+cls[newcell]-1; + } + } + while (++i < ind1); + + for (i = ind0, k = 0; k < SplCntInd; i+=cls[i], k++) { + if (cls[i] == 1) { + Cand->singcode = MASHCOMM(Cand->singcode, Cand->lab[i]); + if (newtrace) Singletons[SingInd] = i; + SingInd++; + } + } + + } + } + else { + if ((!newtrace) && TraceCell) { + return 0; + } + } + + } + else { + if (cls[ind0] == n) { + for (i = 0; i < n; i++) { + NghCounts[i] = TheGraph[i].d; + } + } + else { + memset(NghCounts, 0, n*sizeof(int)); + + /* NEIGHCOUNT_DENSE_DENSE_MULT */ + for (i = ind0; i < ind2; i++) { + labi = lab[i]; + nghb = TheGraph[labi].e; + for (j1int = weightstart; j1int < weightend; ++j1int) { + k = nghb[j1int]; + (NghCounts[k])++; + } + } + /* End NEIGHCOUNT_DENSE_DENSE_MULT */ + + } + SplInd = 0; + ind4 = 0; + while (ind4 < n) { /* For each cell C with size(C) > 1 */ + ind1 = ind4+cls[ind4]; + if (cls[ind4] > 1) { + + + /* Determine whether C must be split */ + SplCntInd = 0; + value = NghCounts[lab[ind4]]; + for (i = ind4+1; i < ind1; i++) { + if (NghCounts[lab[i]] != value) + { + SplInd++; + Markers[value] = tv->mark; + SplCnt[SplCntInd++] = value; + SplPos[value] = i-ind4; + do { + value = NghCounts[lab[i++]]; + if (Markers[value] != tv->mark) { + Markers[value] = tv->mark; + SplCnt[SplCntInd++] = value; + SplPos[value] = 1; + } + else { + SplPos[value]++; + } + } + while(i != ind1); + break; + } + } + tv->mark++; + + if (SplInd && !TraceCell) newtrace = TRUE; + + if (SplCntInd) { + TRACE_CHECK(TheTraceSteps, TraceStepsInd, SplCntInd+3*n, Tracestpend) + + /* Sort the values deriving from the previous step */ + sort_Split_Array(SplCnt, SplCntInd); + + Part->cells += SplCntInd-1; + + /* Split the cell C and update the information for sizes of new cells */ + /* Put the new cells into the stack */ + i = ind4; + if (StackMarkers[i] != tv->stackmark) { + BigCellSize = 0; + } + for (k = 0; k < SplCntInd; k++) { + value = SplPos[SplCnt[k]]; + cls[i] = value; + + if ((StackMarkers[ind4] != tv->stackmark) && (value > BigCellSize)) { + BigCell = i; + BigCellPos = CStackInd; + BigCellSize = cls[i]; + } + SplPos[SplCnt[k]] = i; + i += value; + if (i < ind1) { + CStack[++CStackInd] = i; + StackMarkers[i] = tv->stackmark; + TRACE_CHECK(TheTrace, TraceInd, i, TraceEnd) + } + } + if ((StackMarkers[ind4] != tv->stackmark) && (BigCell != ind4)) { + CStack[BigCellPos] = ind4; + StackMarkers[BigCell] = 0; + StackMarkers[ind4] = tv->stackmark; + } + + /* Permute elements of the cell C */ + i = ind4; + do { + SplCnt[SplPos[NghCounts[lab[i]]]++] = lab[i]; + } + while(++i < ind1); + + /* Reconstruct the cell C and update the inverse partition */ + newcell = ind4; + i = ind4; + ind2 = newcell+cls[newcell]-1; + do { + lab[i] = SplCnt[i]; + InvLab[lab[i]] = i; + Part->inv[i] = newcell; + if (i == ind2) { + newcell = i+1; + if (newcell < n) ind2 = newcell+cls[newcell]-1; + } + } + while (++i < ind1); + + for (i = ind4; i < ind1; i+=cls[i]) { + if (cls[i] == 1) { + Cand->singcode = MASHCOMM(Cand->singcode, Cand->lab[i]); + if (newtrace) Singletons[SingInd] = i; + SingInd++; + } + } + } + } + ind4 = ind1; + } + + /* DENSE-DENSE CASE */ + if (SplInd) { + if (!TraceCell) { + newtrace = TRUE; + } + TRACE_CHECK(TheTraceSplNum, TraceCCInd, SplInd+3*n, &Traceccend) + if (newtrace) { + TheTraceCC[TraceCCInd-1] = ind0; + } + } + else { + if ((!newtrace) && TraceCell) { + return 0; + } + } + + } + } + } + } + while (weightend < iend1int); + } /* end while (CStackInd > 0) */ + + tv->augmented_cells = Part->cells - tv->augmented_cells; + + if (make_code) { + for (i=SpineTL->trcstart; i < TraceInd; i++) { + ind0 = TheTrace[i]; + longcode = MASHNONCOMM(longcode, Part->inv[ind0]); + labi = lab[ind0]; + iend1int = TheGraph[labi].d; + nghb = TheGraph[labi].e; + for (j1int = 0; j1int < iend1int; ++j1int) { + k = nghb[j1int]; + value = Part->inv[InvLab[k]]; + longcode = MASHCOMM(longcode, value); + } + } + } + Part->code = Cand->code = CLEANUP(longcode); + tlp1 = tv->tolevel+1; + if (newtrace) { + if ((tlp1 < n) && (tlp1 > tv->treedepth)) { + tv->treedepth = tlp1; + if (tv->strategy) { + Spine[tlp1].part = NewPartition(n); + } + else { + NewPartSpine(tlp1,n); + } + Spine[tlp1].liststart = Spine[tlp1].listend = NULL; + Spine[tlp1].listcounter = 0; + } + *TraceEnd = TraceInd; + SpineTL->ccend = TraceCCInd; + SpineTL->singend = SingInd; + *Tracestpend = TraceStepsInd; + + SpineTL->thetracexists = TRUE; + if (tlp1 < n) { + Spine[tlp1].ccstart = TraceCCInd; + Spine[tlp1].singstart = Spine[tlp1].singend = SingInd; + Spine[tlp1].stpstart = TraceStepsInd; + Spine[tlp1].thetracexists = FALSE; + Spine[tlp1].part->code = -1; + } + return 2; + } + else { + if (TraceInd < *TraceEnd) { + return 0; + } + if (Cand->code > SpineTL->part->code) { + return 2; + } + else { + if (Cand->code < SpineTL->part->code) { + return 0; + } + } + return 1; + } +} + +void traces_refine_notrace(Candidate *Cand, + int n, + Partition *Part, + struct TracesVars* tv, + struct TracesInfo *ti) { + int i, j, k, jk, sc, ind0, ind1, ind2, ind3, labi, auxcells; + int value, iend, newcell; + int HitClsInd, SplInd, SplCntInd, CStackInd; + int iend1int, j1int; + unsigned int longcode; + int Split = 0; + int Sparse = TRUE; + int *lab, *cls, *InvLab, *SplitCell, *LabCell; + int BigCell, BigCellPos, BigCellSize; + int *nghb; + const int variation = 1; + int currentweight, weightstart, weightend, currentcell, currentsize; + + if (tv->stackmark > (NAUTY_INFINITY-2)) { + memset(StackMarkers, 0, n*sizeof(int)); + tv->stackmark = 0; + } + tv->stackmark++; + + tv->augmented_cells = Part->cells; + + lab = Cand->lab; + InvLab = Cand->invlab; + cls = Part->cls; + + CStackInd = 1; + CStack[1] = tv->tcellexpath+cls[tv->tcellexpath]; + + for (i = 1; i <= CStackInd; i++) { + StackMarkers[CStack[i]] = tv->stackmark; + } + + longcode = Part->cells; + + while (CStackInd > 0) { + + weightend = 0; + + if (tv->mark > (NAUTY_INFINITY-2)) { + memset(Markers, 0, n*sizeof(int)); + memset(MarkHitVtx, 0, n*sizeof(int)); + tv->mark = 0; + } + tv->mark++; + + k = Select_from_CStack(cls, CStackInd); + + currentcell = CStack[k]; + currentsize = currentcell+cls[currentcell]; + CStack[k] = CStack[CStackInd--]; /* Current Cell */ + longcode = MASHNONCOMM(longcode, currentcell); + StackMarkers[currentcell] = 0; + + labi = lab[currentcell]; + iend1int = TheGraph[labi].d; + nghb = TheGraph[labi].e; + + do { + ind0 = currentcell; + ind2 = currentsize; + weightstart = weightend; + + if (tv->options->weighted) { + currentweight = (TheGraph[labi].w)[weightstart]; + while ((iend1int > weightend) && ((TheGraph[labi].w)[weightend] == currentweight)) { + weightend++; + } + } else { + weightend = TheGraph[labi].d; + } + + /* Analysis of occurrences of neighbors of the current cell */ + /* The list of cells with neighbors in the current cell is built */ + + if (cls[ind0] == 1) { /* SINGLETON CURRENT CELL CASE */ + + /* NEIGHCOUNT_SING_MULT */ + HitClsInd = 0; + for (j1int = weightstart; j1int < weightend; ++j1int) { + k = nghb[j1int]; + value = Part->inv[InvLab[k]]; + if (cls[value] > 1) { + if (Markers[value] != tv->mark) { + HitCls[HitClsInd++] = value; + Markers[value] = tv->mark; + ElmHitCll[value] = value; + } + HitVtx[ElmHitCll[value]++] = k; + } else { + switch (variation) { + case 1: + longcode = MASHCOMM(longcode, value); + break; + default: + break; + } + } + } + /* end NEIGHCOUNT_SING_MULT */ + + tv->mark++; + FIND_SPLIT_CELLS; + + /* Sorting the cells to be split */ + sort_Split_Array(SplCls, SplInd); + + /* REARRANGE THE CELLS */ + for (j = 0; j < SplInd; j++) { + /* REARRANGE_CELLS */ + ind1 = SplCls[j]; + cls[ind1] = cls[ind1]-ElmHitCll[ind1]; + newcell = ind1+cls[ind1]; + cls[newcell] = ElmHitCll[ind1]; + Part->cells++; + if (StackMarkers[ind1] != tv->stackmark) { + if (cls[newcell] < cls[ind1]) { + CStack[++CStackInd] = newcell; + StackMarkers[newcell] = tv->stackmark; + } + else { + CStack[++CStackInd] = ind1; + StackMarkers[ind1] = tv->stackmark; + } + } + else { + CStack[++CStackInd] = newcell; + StackMarkers[newcell] = tv->stackmark; + } + SplitCell = HitVtx+ind1; + ind3 = cls[newcell]; + LabCell = lab+newcell; + for (jk = 0; jk < ind3; jk++) { + k = SplitCell[jk]; + i = LabCell[jk]; + Part->inv[newcell+jk] = newcell; + lab[InvLab[k]] = i; + InvLab[i] = InvLab[k]; + LabCell[jk] = k; + InvLab[k] = newcell+jk; + } + /* END REARRANGE_CELLS */ + + if (cls[ind1] == 1) { + Cand->pathsingcode = MASHCOMM(Cand->pathsingcode, lab[ind1]); + } + if (cls[newcell] == 1) { + Cand->pathsingcode = MASHCOMM(Cand->pathsingcode, lab[newcell]); + } + } + } + else { + if (ti->thegraphisparse) { + + /* NEIGHCOUNT_SPARSE_MULT */ + HitClsInd = 0; + if (cls[ind0] != n) { + for (i = ind0; i < ind2; i++) { + labi = lab[i]; + nghb = TheGraph[labi].e; + for (j = weightstart; j < weightend; j++) { + k = nghb[j]; + if (MarkHitVtx[k] == tv->mark) { + NghCounts[k]++; + } + else { + value = Part->inv[InvLab[k]]; + if (cls[value] > 1) { + MarkHitVtx[k] = tv->mark; + NghCounts[k] = 1; + if (Markers[value] != tv->mark) { + HitCls[HitClsInd++] = value; + Markers[value] = tv->mark; + HitVtx[value] = k; + ElmHitCll[value] = 1; + } + else { + HitVtx[value+ElmHitCll[value]++] = k; + } + } + else { + switch (variation) { + case 1: + longcode = MASHCOMM(longcode, value); + break; + default: + break; + } + } + } + } + } + } + /* End NEIGHCOUNT_SPARSE_MULT */ + + tv->mark++; + + SplInd = 0; + SplCls[0] = n; + for (j = 0; j < HitClsInd; j++) { + ind1 = HitCls[j]; + if ((ElmHitCll[ind1] > 0) && (ElmHitCll[ind1] < cls[ind1])) { + SplCls[SplInd++] = ind1; + } + else { + ind2 = ind1+cls[ind1]; + Split = FALSE; + value = NghCounts[lab[ind1++]]; + for (i = ind1; i < ind2; i++) + { + if (NghCounts[lab[i]] != value) + { + SplCls[SplInd++] = HitCls[j]; + Split = TRUE; + break; + } + } + if (!Split) { + longcode = MASHCOMM(longcode, ind1); + } + } + } + + /* Sorting the cells to be split */ + sort_Split_Array(SplCls, SplInd); + + for (sc = 0; sc < SplInd; sc++) { /* For each cell C to be split */ + ind0 = SplCls[sc]; + ind1 = ind0 + cls[ind0]; + SplCntInd = 0; + if (ElmHitCll[ind0] < cls[ind0]) { + SplCnt[SplCntInd++] = 0; + SplPos[0] = cls[ind0] - ElmHitCll[ind0]; + } + + /* According to the numbers of neighbors of C into the current cell */ + /* compute how many vertices in C will be placed into the same new cell */ + iend = ind0 + ElmHitCll[ind0]; + for (i = ind0; i < iend; i++) { + value = NghCounts[HitVtx[i]]; + if (Markers[value] != tv->mark) { + Markers[value] = tv->mark; + SplCnt[SplCntInd++] = value; + SplPos[value] = 1; + } + else { + SplPos[value]++; + } + } + tv->mark++; + + /* Sort the values deriving from the previous step */ + sort_Split_Array(SplCnt, SplCntInd); + Part->cells += SplCntInd-1; + + /* Split the cell C and update the information for sizes of new cells */ + /* Put the new cells into the stack */ + i = ind0; + if (StackMarkers[i] != tv->stackmark) { + BigCellSize = 0; + } + for (k = 0; k < SplCntInd; k++) { + value = SplPos[SplCnt[k]]; + cls[i] = value; + if ((StackMarkers[ind0] != tv->stackmark) && (value > BigCellSize)) { + BigCell = i; + BigCellPos = CStackInd; + BigCellSize = cls[i]; + } + SplPos[SplCnt[k]] = i; + i += value; + if (i < ind1) { + CStack[++CStackInd] = i; + StackMarkers[i] = tv->stackmark; + } + } + + if ((StackMarkers[ind0] != tv->stackmark) && (BigCell != ind0)) { + CStack[BigCellPos] = ind0; + StackMarkers[BigCell] = 0; + StackMarkers[ind0] = tv->stackmark; + } + + /* Permute elements of the cell C */ + iend = ind0 + ElmHitCll[ind0]; + for (i = ind0; i < iend; i++) { + value = HitVtx[i]; + j = SplPos[NghCounts[value]]++; /* where HitVtx[i] goes */ + k = InvLab[value]; /* where HitVtx[i] is in lab */ + lab[k] = lab[j]; + lab[j] = value; + InvLab[value] = j; + InvLab[lab[k]] = k; + NghCounts[value] = 0; + } + + /* Reconstruct the cell C and update the inverse partition */ + newcell = ind1 - ElmHitCll[ind0]; + i = newcell; + ind2 = newcell+cls[newcell]-1; + do { + Part->inv[i] = newcell; + if (i == ind2) { + newcell = i+1; + if (newcell < n) ind2 = newcell+cls[newcell]-1; + } + } + while (++i < ind1); + + for (i = ind0, k = 0; k < SplCntInd; i+=cls[i], k++) { + if (cls[i] == 1) { + Cand->pathsingcode = MASHCOMM(Cand->pathsingcode, lab[i]); + } + } + + } + } + else { + if (TheGraph[lab[ind0]].d > n/cls[ind0]) { + Sparse = FALSE; + } + else { + Sparse = TRUE; + } + Sparse = TRUE; + if (Sparse) { + /* Counting occurrences of neighbors of the current cell */ + /* The list of cells with neighbors in the current cell is also built */ + if (cls[ind0] == n) { + for (i = 0; i < n; i++) { + NghCounts[i] = TheGraph[i].d; + } + HitCls[0] = 0; + HitClsInd = 1; + } + else { + memset(NghCounts, 0, n*sizeof(int)); + + /* NEIGHCOUNT_DENSE_SPARSE_MULT */ + HitClsInd = 0; + for (i = ind0; i < ind2; i++) { + labi = lab[i]; + nghb = TheGraph[labi].e; + for (j1int = weightstart; j1int < weightend; ++j1int) { + k = nghb[j1int]; + (NghCounts[k])++; + value = Part->inv[InvLab[k]]; + if (Markers[value] != tv->mark) { + if (cls[value] > 1) HitCls[HitClsInd++] = value; + Markers[value] = tv->mark; + } + } + } + /* End NEIGHCOUNT_DENSE_SPARSE_MULT */ + + } + + tv->mark++; + + SplInd = 0; + for (j = 0; j < HitClsInd; j++) { + ind1 = HitCls[j]; + ind2 = ind1+cls[ind1]; + value = NghCounts[lab[ind1++]]; + for (i = ind1; i < ind2; i++) + { + if (NghCounts[lab[i]] != value) + { + SplCls[SplInd++] = HitCls[j]; + break; + } + } + } + + /* Sorting the cells to be split */ + sort_Split_Array(SplCls, SplInd); + + for (j = 0; j < SplInd; j++) { /* For each cell C to be split */ + ind0 = SplCls[j]; + ind1 = ind0+cls[ind0]; + SplCntInd = 0; + + /* According to the numbers of neighbors of C into the current cell */ + /* compute how many vertices in C will be placed into the same new cell */ + for (i = ind0; i < ind1; i++) { + value = NghCounts[lab[i]]; + if (Markers[value] != tv->mark) { + Markers[value] = tv->mark; + SplCnt[SplCntInd++] = value; + SplPos[value] = 1; + } + else { + SplPos[value]++; + } + } + + tv->mark++; + + /* Sort the values deriving from the previous step */ + sort_Split_Array(SplCnt, SplCntInd); + + Part->cells += SplCntInd-1; + + /* Split the cell C and update the information for sizes of new cells */ + /* Put the new cells into the stack */ + i = ind0; + if (StackMarkers[i] != tv->stackmark) { + BigCellSize = 0; + } + for (k = 0; k < SplCntInd; k++) { + value = SplPos[SplCnt[k]]; + cls[i] = value; + if ((StackMarkers[ind0] != tv->stackmark) && (value > BigCellSize)) { + BigCell = i; + BigCellPos = CStackInd; + BigCellSize = cls[i]; + } + SplPos[SplCnt[k]] = i; + i += value; + if (i < ind1) { + CStack[++CStackInd] = i; + StackMarkers[i] = tv->stackmark; + } + } + if ((StackMarkers[ind0] != tv->stackmark) && (BigCell != ind0)) { + CStack[BigCellPos] = ind0; + StackMarkers[BigCell] = 0; + StackMarkers[ind0] = tv->stackmark; + } + + /* Permute elements of the cell C */ + i = ind0; + do { + SplCnt[SplPos[NghCounts[lab[i]]]++] = lab[i]; + } + while(++i < ind1); + + /* Reconstruct the cell C and update the inverse partition */ + newcell = ind0; + i = ind0; + ind2 = newcell+cls[newcell]-1; + do { + lab[i] = SplCnt[i]; + InvLab[lab[i]] = i; + Part->inv[i] = newcell; + + if (i == ind2) { + newcell = i+1; + if (newcell < n) ind2 = newcell+cls[newcell]-1; + } + } + while (++i < ind1); + + for (i = ind0, k = 0; k < SplCntInd; i+=cls[i], k++) { + if (cls[i] == 1) { + Cand->pathsingcode = MASHCOMM(Cand->pathsingcode, lab[i]); + } + } + + } + } + else { + if (cls[ind0] == n) { + for (i = 0; i < n; i++) { + NghCounts[i] = TheGraph[i].d; + } + } + else { + memset(NghCounts, 0, n*sizeof(int)); + + /* NEIGHCOUNT_DENSE_DENSE_MULT */ + for (i = ind0; i < ind2; i++) { + labi = lab[i]; + nghb = TheGraph[labi].e; + for (j1int = weightstart; j1int < weightend; ++j1int) { + k = nghb[j1int]; + (NghCounts[k])++; + } + } + /* End NEIGHCOUNT_DENSE_DENSE_MULT */ + + } + + ind0 = 0; + while (ind0 < n) { /* For each cell C with size(C) > 1 */ + ind1 = ind0+cls[ind0]; + if (cls[ind0] > 1) { + + /* Determine whether C must be split */ + SplCntInd = 0; + value = NghCounts[lab[ind0]]; + for (i = ind0+1; i < ind1; i++) + { + if (NghCounts[lab[i]] != value) + { + Markers[value] = tv->mark; + SplCnt[SplCntInd++] = value; + SplPos[value] = i-ind0; + do { + value = NghCounts[lab[i++]]; + if (Markers[value] != tv->mark) { + Markers[value] = tv->mark; + SplCnt[SplCntInd++] = value; + SplPos[value] = 1; + } + else { + SplPos[value]++; + } + } + while(i != ind1); + break; + } + } + + if (SplCntInd) { + tv->mark++; + + /* Sort the values deriving from the previous step */ + sort_Split_Array(SplCnt, SplCntInd); + Part->cells += SplCntInd-1; + + /* Split the cell C and update the information for sizes of new cells */ + /* Put the new cells into the stack */ + i = ind0; + if (StackMarkers[i] != tv->stackmark) { + BigCellSize = 0; + } + for (k = 0; k < SplCntInd; k++) { + value = SplPos[SplCnt[k]]; + cls[i] = value; + if ((StackMarkers[ind0] != tv->stackmark) && (value > BigCellSize)) { + BigCell = i; + BigCellPos = CStackInd; + BigCellSize = cls[i]; + } + SplPos[SplCnt[k]] = i; + i += value; + if (i < ind1) { + CStack[++CStackInd] = i; + StackMarkers[i] = tv->stackmark; + } + } + if ((StackMarkers[ind0] != tv->stackmark) && (BigCell != ind0)) { + CStack[BigCellPos] = ind0; + StackMarkers[BigCell] = 0; + StackMarkers[ind0] = tv->stackmark; + } + + /* Permute elements of the cell C */ + i = ind0; + do { + SplCnt[SplPos[NghCounts[lab[i]]]++] = lab[i]; + } + while(++i < ind1); + + /* Reconstruct the cell C and update the inverse partition */ + newcell = ind0; + i = ind0; + ind2 = newcell+cls[newcell]-1; + do { + lab[i] = SplCnt[i]; + InvLab[lab[i]] = i; + Part->inv[i] = newcell; + if (i == ind2) { + newcell = i+1; + if (newcell < n) ind2 = newcell+cls[newcell]-1; + } + } + while (++i < ind1); + + for (i = ind0; i < ind1; i+=cls[i]) { + if (cls[i] == 1) { + Cand->pathsingcode = MASHCOMM(Cand->pathsingcode, lab[i]); + } + } + + } + } + ind0 = ind1; + } + } + } + } + } + while (weightend < iend1int); + } /* end while (CStackInd > 0) */ + + tv->augmented_cells = Part->cells - tv->augmented_cells; + Cand->code = CLEANUP(longcode); + return; +} + +void traces_refine_maketrie(Candidate *Cand, + int n, + Partition *Part, + struct TracesVars* tv, + struct TracesInfo *ti) { + int i, j, k, jk, sc, ind0, ind1, ind2, ind3, labi; + int value, iend, newcell; + int HitClsInd, SplInd, SplCntInd, CStackInd; + int j1int, iend1int; + unsigned int longcode; + int Split = 0; + int Sparse = TRUE; + int *lab, *cls, *InvLab, *SplitCell, *LabCell; + int BigCell, BigCellPos, BigCellSize; + int *nghb; + const int variation = 1; + int currentweight, weightstart, weightend, currentcell, currentsize; + + if (tv->stackmark > (NAUTY_INFINITY-2)) { + memset(StackMarkers, 0, n*sizeof(int)); + tv->stackmark = 0; + } + tv->stackmark++; + + tv->augmented_cells = Part->cells; + + lab = Cand->lab; + InvLab = Cand->invlab; + cls = Part->cls; + + CStack[1] = Spine[tv->tolevel_tl].tgtpos; + CStackInd = 1; + for (i = 1; i <= CStackInd; i++) { + StackMarkers[CStack[i]] = tv->stackmark; + } + + longcode = Part->cells; + + while (CStackInd > 0) + { + weightend = 0; + + if (tv->mark > (NAUTY_INFINITY-2)) { + memset(Markers, 0, n*sizeof(int)); + memset(MarkHitVtx, 0, n*sizeof(int)); + tv->mark = 0; + } + tv->mark++; + + if (Part->cells == n) break; + + k = Select_from_CStack(cls, CStackInd); + + currentcell = CStack[k]; + currentsize = currentcell+cls[currentcell]; + CStack[k] = CStack[CStackInd--]; + longcode = MASHNONCOMM(longcode, currentcell); + StackMarkers[currentcell] = 0; + + labi = lab[currentcell]; + iend1int = TheGraph[labi].d; + nghb = TheGraph[labi].e; + + do { + ind0 = currentcell; + ind2 = currentsize; + weightstart = weightend; + + if (tv->options->weighted) { + currentweight = (TheGraph[labi].w)[weightstart]; + while ((iend1int > weightend) && ((TheGraph[labi].w)[weightend] == currentweight)) { + weightend++; + } + } else { + weightend = TheGraph[labi].d; + } + + /* Analysis of occurrences of neighbors of the current cell */ + /* The list of cells with neighbors in the current cell is built */ + if (cls[ind0] == 1) { /* SINGLETON CURRENT CELL CASE */ + + /* NEIGHCOUNT_SING_MULT */ + HitClsInd = 0; + for (j1int = weightstart; j1int < weightend; ++j1int) { + k = nghb[j1int]; + value = Part->inv[InvLab[k]]; + if (cls[value] > 1) { + if (Markers[value] != tv->mark) { + HitCls[HitClsInd++] = value; + Markers[value] = tv->mark; + ElmHitCll[value] = value; + } + HitVtx[ElmHitCll[value]++] = k; + } else { + switch (variation) { + case 1: + longcode = MASHCOMM(longcode, value); + break; + default: + break; + } + } + } + /* end NEIGHCOUNT_SING_MULT */ + + tv->mark++; + FIND_SPLIT_CELLS; + + /* Sorting the cells to be split */ + sort_Split_Array(SplCls, SplInd); + + for (j = 0; j < SplInd; j++) { + ind1 = SplCls[j]; + i = ind1+cls[ind1]-ElmHitCll[ind1]; + trieref = trie_make(trieref, i, n, tv); + } + + /* REARRANGE THE CELLS */ + for (j = 0; j < SplInd; j++) { + /* REARRANGE_CELLS */ + ind1 = SplCls[j]; + cls[ind1] = cls[ind1]-ElmHitCll[ind1]; + newcell = ind1+cls[ind1]; + cls[newcell] = ElmHitCll[ind1]; + Part->cells++; + if (StackMarkers[ind1] != tv->stackmark) { + if (cls[newcell] < cls[ind1]) { + CStack[++CStackInd] = newcell; + StackMarkers[newcell] = tv->stackmark; + } + else { + CStack[++CStackInd] = ind1; + StackMarkers[ind1] = tv->stackmark; + } + } + else { + CStack[++CStackInd] = newcell; + StackMarkers[newcell] = tv->stackmark; + } + SplitCell = HitVtx+ind1; + ind3 = cls[newcell]; + LabCell = lab+newcell; + for (jk = 0; jk < ind3; jk++) { + k = SplitCell[jk]; + i = LabCell[jk]; + Part->inv[newcell+jk] = newcell; + lab[InvLab[k]] = i; + InvLab[i] = InvLab[k]; + LabCell[jk] = k; + InvLab[k] = newcell+jk; + } + /* END REARRANGE_CELLS */ + } + } + else { + if (ti->thegraphisparse) { + + /* NEIGHCOUNT_SPARSE_MULT */ + HitClsInd = 0; + if (cls[ind0] != n) { + for (i = ind0; i < ind2; i++) { + labi = lab[i]; + nghb = TheGraph[labi].e; + for (j = weightstart; j < weightend; j++) { + k = nghb[j]; + if (MarkHitVtx[k] == tv->mark) { + NghCounts[k]++; + } + else { + value = Part->inv[InvLab[k]]; + if (cls[value] > 1) { + MarkHitVtx[k] = tv->mark; + NghCounts[k] = 1; + if (Markers[value] != tv->mark) { + HitCls[HitClsInd++] = value; + Markers[value] = tv->mark; + HitVtx[value] = k; + ElmHitCll[value] = 1; + } + else { + HitVtx[value+ElmHitCll[value]++] = k; + } + } + else { + switch (variation) { + case 1: + longcode = MASHCOMM(longcode, value); + break; + default: + break; + } + } + } + } + } + } + /* End NEIGHCOUNT_SPARSE_MULT */ + + tv->mark++; + + SplInd = 0; + SplCls[0] = n; + for (j = 0; j < HitClsInd; j++) { + ind1 = HitCls[j]; + if ((ElmHitCll[ind1] > 0) && (ElmHitCll[ind1] < cls[ind1])) { + SplCls[SplInd++] = ind1; + } + else { + ind2 = ind1+cls[ind1]; + Split = FALSE; + value = NghCounts[lab[ind1++]]; + for (i = ind1; i < ind2; i++) + { + if (NghCounts[lab[i]] != value) + { + SplCls[SplInd++] = HitCls[j]; + Split = TRUE; + break; + } + } + if (!Split) { + longcode = MASHCOMM(longcode, ind1); + } + } + } + + /* Sorting the cells to be split */ + sort_Split_Array(SplCls, SplInd); + + for (sc = 0; sc < SplInd; sc++) { /* For each cell C to be split */ + ind0 = SplCls[sc]; + ind1 = ind0 + cls[ind0]; + SplCntInd = 0; + if (ElmHitCll[ind0] < cls[ind0]) { + SplCnt[SplCntInd++] = 0; + SplPos[0] = cls[ind0] - ElmHitCll[ind0]; + } + + /* According to the numbers of neighbors of C into the current cell */ + /* compute how many vertices in C will be placed into the same new cell */ + iend = ind0 + ElmHitCll[ind0]; + for (i = ind0; i < iend; i++) { + value = NghCounts[HitVtx[i]]; + if (Markers[value] != tv->mark) { + Markers[value] = tv->mark; + SplCnt[SplCntInd++] = value; + SplPos[value] = 1; + } + else { + SplPos[value]++; + } + } + + tv->mark++; + + /* Sort the values deriving from the previous step */ + sort_Split_Array(SplCnt, SplCntInd); + Part->cells += SplCntInd-1; + + /* Split the cell C and update the information for sizes of new cells */ + /* Put the new cells into the stack */ + i = ind0; + if (StackMarkers[i] != tv->stackmark) { + BigCellSize = 0; + } + for (k = 0; k < SplCntInd; k++) { + value = SplPos[SplCnt[k]]; + cls[i] = value; + if ((StackMarkers[ind0] != tv->stackmark) && (value > BigCellSize)) { + BigCell = i; + BigCellPos = CStackInd; + BigCellSize = cls[i]; + } + SplPos[SplCnt[k]] = i; + i += value; + if (i < ind1) { + CStack[++CStackInd] = i; + StackMarkers[i] = tv->stackmark; + trieref = trie_make(trieref, i, n, tv); + } + } + + if ((StackMarkers[ind0] != tv->stackmark) && (BigCell != ind0)) { + CStack[BigCellPos] = ind0; + StackMarkers[BigCell] = 0; + StackMarkers[ind0] = tv->stackmark; + } + /* Permute elements of the cell C */ + iend = ind0 + ElmHitCll[ind0]; + for (i = ind0; i < iend; i++) { + value = HitVtx[i]; + j = SplPos[NghCounts[value]]++; /* where HitVtx[i] goes */ + k = InvLab[value]; /* where HitVtx[i] is in lab */ + lab[k] = lab[j]; + lab[j] = value; + InvLab[value] = j; + InvLab[lab[k]] = k; + NghCounts[value] = 0; + } + + /* Reconstruct the cell C and update the inverse partition */ + newcell = ind1 - ElmHitCll[ind0]; + i = newcell; + ind2 = newcell+cls[newcell]-1; + do { + Part->inv[i] = newcell; + if (i == ind2) { + newcell = i+1; + if (newcell < n) ind2 = newcell+cls[newcell]-1; + } + } + while (++i < ind1); + } + } + else { + if (TheGraph[lab[ind0]].d > n/cls[ind0]) { + Sparse = FALSE; + } + else { + Sparse = TRUE; + } + Sparse = TRUE; + if (Sparse) { + /* Counting occurrences of neighbors of the current cell */ + /* The list of cells with neighbors in the current cell is also built */ + if (cls[ind0] == n) { + for (i = 0; i < n; i++) { + NghCounts[i] = TheGraph[i].d; + } + HitCls[0] = 0; + HitClsInd = 1; + } + else { + memset(NghCounts, 0, n*sizeof(int)); + + /* NEIGHCOUNT_DENSE_SPARSE_MULT */ + HitClsInd = 0; + for (i = ind0; i < ind2; i++) { + labi = lab[i]; + nghb = TheGraph[labi].e; + for (j1int = weightstart; j1int < weightend; ++j1int) { + k = nghb[j1int]; + (NghCounts[k])++; + value = Part->inv[InvLab[k]]; + if (Markers[value] != tv->mark) { + if (cls[value] > 1) HitCls[HitClsInd++] = value; + Markers[value] = tv->mark; + } + } + } + /* End NEIGHCOUNT_DENSE_SPARSE_MULT */ + + } + + tv->mark++; + + SplInd = 0; + for (j = 0; j < HitClsInd; j++) { + ind1 = HitCls[j]; + ind2 = ind1+cls[ind1]; + value = NghCounts[lab[ind1++]]; + for (i = ind1; i < ind2; i++) + { + if (NghCounts[lab[i]] != value) + { + SplCls[SplInd++] = HitCls[j]; + break; + } + } + } + + /* Sorting the cells to be split */ + sort_Split_Array(SplCls, SplInd); + + for (j = 0; j < SplInd; j++) { /* For each cell C to be split */ + ind0 = SplCls[j]; + ind1 = ind0+cls[ind0]; + SplCntInd = 0; + + /* According to the numbers of neighbors of C into the current cell */ + /* compute how many vertices in C will be placed into the same new cell */ + for (i = ind0; i < ind1; i++) { + value = NghCounts[lab[i]]; + if (Markers[value] != tv->mark) { + Markers[value] = tv->mark; + SplCnt[SplCntInd++] = value; + SplPos[value] = 1; + } + else { + SplPos[value]++; + } + } + + tv->mark++; + + /* Sort the values deriving from the previous step */ + sort_Split_Array(SplCnt, SplCntInd); + Part->cells += SplCntInd-1; + + /* Split the cell C and update the information for sizes of new cells */ + /* Put the new cells into the stack */ + i = ind0; + if (StackMarkers[i] != tv->stackmark) { + BigCellSize = 0; + } + for (k = 0; k < SplCntInd; k++) { + value = SplPos[SplCnt[k]]; + cls[i] = value; + if ((StackMarkers[ind0] != tv->stackmark) && (value > BigCellSize)) { + BigCell = i; + BigCellPos = CStackInd; + BigCellSize = cls[i]; + } + SplPos[SplCnt[k]] = i; + i += value; + if (i < ind1) { + CStack[++CStackInd] = i; + StackMarkers[i] = tv->stackmark; + trieref = trie_make(trieref, i, n, tv); + } + } + if ((StackMarkers[ind0] != tv->stackmark) && (BigCell != ind0)) { + CStack[BigCellPos] = ind0; + StackMarkers[BigCell] = 0; + StackMarkers[ind0] = tv->stackmark; + } + + /* Permute elements of the cell C */ + i = ind0; + do { + SplCnt[SplPos[NghCounts[lab[i]]]++] = lab[i]; + } + while(++i < ind1); + + /* Reconstruct the cell C and update the inverse partition */ + newcell = ind0; + i = ind0; + ind2 = newcell+cls[newcell]-1; + do { + lab[i] = SplCnt[i]; + InvLab[lab[i]] = i; + Part->inv[i] = newcell; + if (i == ind2) { + newcell = i+1; + if (newcell < n) ind2 = newcell+cls[newcell]-1; + } + } + while (++i < ind1); + } + } + else { + if (cls[ind0] == n) { + for (i = 0; i < n; i++) { + NghCounts[i] = TheGraph[i].d; + } + } + else { + memset(NghCounts, 0, n*sizeof(int)); + + /* NEIGHCOUNT_DENSE_DENSE_MULT */ + for (i = ind0; i < ind2; i++) { + labi = lab[i]; + nghb = TheGraph[labi].e; + for (j1int = weightstart; j1int < weightend; ++j1int) { + k = nghb[j1int]; + (NghCounts[k])++; + } + } + /* End NEIGHCOUNT_DENSE_DENSE_MULT */ + + } + + ind0 = 0; + while (ind0 < n) { /* For each cell C with size(C) > 1 */ + ind1 = ind0+cls[ind0]; + if (cls[ind0] > 1) { + + /* Determine whether C must be split */ + SplCntInd = 0; + value = NghCounts[lab[ind0]]; + for (i = ind0+1; i < ind1; i++) + { + if (NghCounts[lab[i]] != value) + { + Markers[value] = tv->mark; + SplCnt[SplCntInd++] = value; + SplPos[value] = i-ind0; + do { + value = NghCounts[lab[i++]]; + if (Markers[value] != tv->mark) { + Markers[value] = tv->mark; + SplCnt[SplCntInd++] = value; + SplPos[value] = 1; + } + else { + SplPos[value]++; + } + } + while(i != ind1); + break; + } + } + + if (SplCntInd) { + tv->mark++; + + /* Sort the values deriving from the previous step */ + sort_Split_Array(SplCnt, SplCntInd); + + Part->cells += SplCntInd-1; + + /* Split the cell C and update the information for sizes of new cells */ + /* Put the new cells into the stack */ + i = ind0; + if (StackMarkers[i] != tv->stackmark) { + BigCellSize = 0; + } + for (k = 0; k < SplCntInd; k++) { + value = SplPos[SplCnt[k]]; + cls[i] = value; + if ((StackMarkers[ind0] != tv->stackmark) && (value > BigCellSize)) { + BigCell = i; + BigCellPos = CStackInd; + BigCellSize = cls[i]; + } + SplPos[SplCnt[k]] = i; + i += value; + if (i < ind1) { + CStack[++CStackInd] = i; + StackMarkers[i] = tv->stackmark; + } + } + if ((StackMarkers[ind0] != tv->stackmark) && (BigCell != ind0)) { + CStack[BigCellPos] = ind0; + StackMarkers[BigCell] = 0; + StackMarkers[ind0] = tv->stackmark; + trieref = trie_make(trieref, i, n, tv); + } + + /* Permute elements of the cell C */ + i = ind0; + do { + SplCnt[SplPos[NghCounts[lab[i]]]++] = lab[i]; + } + while(++i < ind1); + + /* Reconstruct the cell C and update the inverse partition */ + newcell = ind0; + i = ind0; + ind2 = newcell+cls[newcell]-1; + do { + lab[i] = SplCnt[i]; + InvLab[lab[i]] = i; + Part->inv[i] = newcell; + if (i == ind2) { + newcell = i+1; + if (newcell < n) ind2 = newcell+cls[newcell]-1; + } + } + while (++i < ind1); + } + } + ind0 = ind1; + } + } + } + } + } + while (weightend < iend1int); + } /* end while (CStackInd > 0) */ + + tv->augmented_cells = Part->cells - tv->augmented_cells; + + Cand->code = CLEANUP(longcode); + return; +} + +int traces_refine_comptrie(Candidate *Cand, + int n, + Partition *Part, + struct TracesVars* tv, + struct TracesInfo *ti) { + int i, j, k, jk, sc, ind0, ind1, ind2, ind3, labi; + int value, iend, newcell; + int HitClsInd, SplInd, SplCntInd, CStackInd; + int j1int, iend1int; + unsigned int longcode; + int Split = 0; + int Sparse = TRUE; + int *lab, *cls, *InvLab, *SplitCell, *LabCell; + int BigCell, BigCellPos, BigCellSize; + int *nghb; + const int variation = 1; + int currentweight, weightstart, weightend, currentcell, currentsize; + + if (tv->stackmark > (NAUTY_INFINITY-2)) { + memset(StackMarkers, 0, n*sizeof(int)); + tv->stackmark = 0; + } + tv->stackmark++; + + tv->augmented_cells = Part->cells; + + lab = Cand->lab; + InvLab = Cand->invlab; + cls = Part->cls; + + CStack[1] = Spine[tv->tolevel_tl].tgtpos; + CStackInd = 1; + for (i = 1; i <= CStackInd; i++) { + StackMarkers[CStack[i]] = tv->stackmark; + } + + longcode = Part->cells; + while (CStackInd > 0) + { + + weightend = 0; + + if (tv->mark > (NAUTY_INFINITY-2)) { + memset(Markers, 0, n*sizeof(int)); + memset(MarkHitVtx, 0, n*sizeof(int)); + tv->mark = 0; + } + tv->mark++; + + if (Part->cells == n) break; + + k = Select_from_CStack(cls, CStackInd); + + currentcell = CStack[k]; + currentsize = currentcell+cls[currentcell]; + CStack[k] = CStack[CStackInd--]; /* Current Cell */ + longcode = MASHNONCOMM(longcode, currentcell); + StackMarkers[currentcell] = 0; + + labi = lab[currentcell]; + iend1int = TheGraph[labi].d; + nghb = TheGraph[labi].e; + + do { + ind0 = currentcell; + ind2 = currentsize; + weightstart = weightend; + + if (tv->options->weighted) { + currentweight = (TheGraph[labi].w)[weightstart]; + while ((iend1int > weightend) && ((TheGraph[labi].w)[weightend] == currentweight)) { + weightend++; + } + } else { + weightend = TheGraph[labi].d; + } + + /* Analysis of occurrences of neighbors of the current cell */ + /* The list of cells with neighbors in the current cell is built */ + if (cls[ind0] == 1) { /* SINGLETON CURRENT CELL CASE */ + + /* NEIGHCOUNT_SING_MULT */ + HitClsInd = 0; + for (j1int = weightstart; j1int < weightend; ++j1int) { + k = nghb[j1int]; + value = Part->inv[InvLab[k]]; + if (cls[value] > 1) { + if (Markers[value] != tv->mark) { + HitCls[HitClsInd++] = value; + Markers[value] = tv->mark; + ElmHitCll[value] = value; + } + HitVtx[ElmHitCll[value]++] = k; + } else { + switch (variation) { + case 1: + longcode = MASHCOMM(longcode, value); + break; + default: + break; + } + } + } + /* end NEIGHCOUNT_SING_MULT */ + + tv->mark++; + FIND_SPLIT_CELLS; + + /* Sorting the cells to be split */ + sort_Split_Array(SplCls, SplInd); + + for (j = 0; j < SplInd; j++) { + ind1 = SplCls[j]; + i = ind1+cls[ind1]-ElmHitCll[ind1]; + trieref = trie_comp(trieref, i); + if (trieref == NULL) return 0; + } + + /* REARRANGE THE CELLS */ + for (j = 0; j < SplInd; j++) { + /* REARRANGE_CELLS */ + ind1 = SplCls[j]; + cls[ind1] = cls[ind1]-ElmHitCll[ind1]; + newcell = ind1+cls[ind1]; + cls[newcell] = ElmHitCll[ind1]; + Part->cells++; + if (StackMarkers[ind1] != tv->stackmark) { + if (cls[newcell] < cls[ind1]) { + CStack[++CStackInd] = newcell; + StackMarkers[newcell] = tv->stackmark; + } + else { + CStack[++CStackInd] = ind1; + StackMarkers[ind1] = tv->stackmark; + } + } + else { + CStack[++CStackInd] = newcell; + StackMarkers[newcell] = tv->stackmark; + } + SplitCell = HitVtx+ind1; + ind3 = cls[newcell]; + LabCell = lab+newcell; + for (jk = 0; jk < ind3; jk++) { + k = SplitCell[jk]; + i = LabCell[jk]; + Part->inv[newcell+jk] = newcell; + lab[InvLab[k]] = i; + InvLab[i] = InvLab[k]; + LabCell[jk] = k; + InvLab[k] = newcell+jk; + } + /* END REARRANGE_CELLS */ + } + } + else { + if (ti->thegraphisparse) { + + /* NEIGHCOUNT_SPARSE_MULT */ + HitClsInd = 0; + if (cls[ind0] != n) { + for (i = ind0; i < ind2; i++) { + labi = lab[i]; + nghb = TheGraph[labi].e; + for (j = weightstart; j < weightend; j++) { + k = nghb[j]; + if (MarkHitVtx[k] == tv->mark) { + NghCounts[k]++; + } + else { + value = Part->inv[InvLab[k]]; + if (cls[value] > 1) { + MarkHitVtx[k] = tv->mark; + NghCounts[k] = 1; + if (Markers[value] != tv->mark) { + HitCls[HitClsInd++] = value; + Markers[value] = tv->mark; + HitVtx[value] = k; + ElmHitCll[value] = 1; + } + else { + HitVtx[value+ElmHitCll[value]++] = k; + } + } + else { + switch (variation) { + case 1: + longcode = MASHCOMM(longcode, value); + break; + default: + break; + } + } + } + } + } + } + /* End NEIGHCOUNT_SPARSE_MULT */ + + tv->mark++; + + SplInd = 0; + SplCls[0] = n; + for (j = 0; j < HitClsInd; j++) { + ind1 = HitCls[j]; + if ((ElmHitCll[ind1] > 0) && (ElmHitCll[ind1] < cls[ind1])) { + SplCls[SplInd++] = ind1; + } + else { + ind2 = ind1+cls[ind1]; + Split = FALSE; + value = NghCounts[lab[ind1++]]; + for (i = ind1; i < ind2; i++) + { + if (NghCounts[lab[i]] != value) + { + SplCls[SplInd++] = HitCls[j]; + Split = TRUE; + break; + } + } + if (!Split) { + longcode = MASHCOMM(longcode, ind1); + } + } + } + + /* Sorting the cells to be split */ + sort_Split_Array(SplCls, SplInd); + + for (sc = 0; sc < SplInd; sc++) { /* For each cell C to be split */ + ind0 = SplCls[sc]; + ind1 = ind0 + cls[ind0]; + SplCntInd = 0; + if (ElmHitCll[ind0] < cls[ind0]) { + SplCnt[SplCntInd++] = 0; + SplPos[0] = cls[ind0] - ElmHitCll[ind0]; + } + + /* According to the numbers of neighbors of C into the current cell */ + /* compute how many vertices in C will be placed into the same new cell */ + iend = ind0 + ElmHitCll[ind0]; + for (i = ind0; i < iend; i++) { + value = NghCounts[HitVtx[i]]; + if (Markers[value] != tv->mark) { + Markers[value] = tv->mark; + SplCnt[SplCntInd++] = value; + SplPos[value] = 1; + } + else { + SplPos[value]++; + } + } + + tv->mark++; + + /* Sort the values deriving from the previous step */ + sort_Split_Array(SplCnt, SplCntInd); + Part->cells += SplCntInd-1; + + /* Split the cell C and update the information for sizes of new cells */ + /* Put the new cells into the stack */ + i = ind0; + if (StackMarkers[i] != tv->stackmark) { + BigCellSize = 0; + } + for (k = 0; k < SplCntInd; k++) { + value = SplPos[SplCnt[k]]; + cls[i] = value; + if ((StackMarkers[ind0] != tv->stackmark) && (value > BigCellSize)) { + BigCell = i; + BigCellPos = CStackInd; + BigCellSize = cls[i]; + } + SplPos[SplCnt[k]] = i; + i += value; + if (i < ind1) { + CStack[++CStackInd] = i; + StackMarkers[i] = tv->stackmark; + trieref = trie_comp(trieref, i); + if (trieref == NULL) return 0; + } + } + + if ((StackMarkers[ind0] != tv->stackmark) && (BigCell != ind0)) { + CStack[BigCellPos] = ind0; + StackMarkers[BigCell] = 0; + StackMarkers[ind0] = tv->stackmark; + } + /* Permute elements of the cell C */ + iend = ind0 + ElmHitCll[ind0]; + for (i = ind0; i < iend; i++) { + value = HitVtx[i]; + j = SplPos[NghCounts[value]]++; /* where HitVtx[i] goes */ + k = InvLab[value]; /* where HitVtx[i] is in lab */ + lab[k] = lab[j]; + lab[j] = value; + InvLab[value] = j; + InvLab[lab[k]] = k; + NghCounts[value] = 0; + } + + /* Reconstruct the cell C and update the inverse partition */ + newcell = ind1 - ElmHitCll[ind0]; + i = newcell; + ind2 = newcell+cls[newcell]-1; + do { + Part->inv[i] = newcell; + if (i == ind2) { + newcell = i+1; + if (newcell < n) ind2 = newcell+cls[newcell]-1; + } + } + while (++i < ind1); + } + } + else { + if (TheGraph[lab[ind0]].d > n/cls[ind0]) { + Sparse = FALSE; + } + else { + Sparse = TRUE; + } + Sparse = TRUE; + if (Sparse) { + /* Counting occurrences of neighbors of the current cell */ + /* The list of cells with neighbors in the current cell is also built */ + if (cls[ind0] == n) { + for (i = 0; i < n; i++) { + NghCounts[i] = TheGraph[i].d; + } + HitCls[0] = 0; + HitClsInd = 1; + } + else { + memset(NghCounts, 0, n*sizeof(int)); + + /* NEIGHCOUNT_DENSE_SPARSE_MULT */ + HitClsInd = 0; + for (i = ind0; i < ind2; i++) { + labi = lab[i]; + nghb = TheGraph[labi].e; + for (j1int = weightstart; j1int < weightend; ++j1int) { + k = nghb[j1int]; + (NghCounts[k])++; + value = Part->inv[InvLab[k]]; + if (Markers[value] != tv->mark) { + if (cls[value] > 1) HitCls[HitClsInd++] = value; + Markers[value] = tv->mark; + } + } + } + /* End NEIGHCOUNT_DENSE_SPARSE_MULT */ + ; + } + + tv->mark++; + + SplInd = 0; + for (j = 0; j < HitClsInd; j++) { + ind1 = HitCls[j]; + ind2 = ind1+cls[ind1]; + value = NghCounts[lab[ind1++]]; + for (i = ind1; i < ind2; i++) + { + if (NghCounts[lab[i]] != value) + { + SplCls[SplInd++] = HitCls[j]; + break; + } + } + } + + /* Sorting the cells to be split */ + sort_Split_Array(SplCls, SplInd); + + for (j = 0; j < SplInd; j++) { /* For each cell C to be split */ + ind0 = SplCls[j]; + ind1 = ind0+cls[ind0]; + SplCntInd = 0; + + /* According to the numbers of neighbors of C into the current cell */ + /* compute how many vertices in C will be placed into the same new cell */ + for (i = ind0; i < ind1; i++) { + value = NghCounts[lab[i]]; + if (Markers[value] != tv->mark) { + Markers[value] = tv->mark; + SplCnt[SplCntInd++] = value; + SplPos[value] = 1; + } + else { + SplPos[value]++; + } + } + + tv->mark++; + + /* Sort the values deriving from the previous step */ + sort_Split_Array(SplCnt, SplCntInd); + + Part->cells += SplCntInd-1; + + /* Split the cell C and update the information for sizes of new cells */ + /* Put the new cells into the stack */ + i = ind0; + if (StackMarkers[i] != tv->stackmark) { + BigCellSize = 0; + } + for (k = 0; k < SplCntInd; k++) { + value = SplPos[SplCnt[k]]; + cls[i] = value; + if ((StackMarkers[ind0] != tv->stackmark) && (value > BigCellSize)) { + BigCell = i; + BigCellPos = CStackInd; + BigCellSize = cls[i]; + } + SplPos[SplCnt[k]] = i; + i += value; + if (i < ind1) { + CStack[++CStackInd] = i; + StackMarkers[i] = tv->stackmark; + trieref = trie_comp(trieref, i); + if (trieref == NULL) return 0; + } + } + if ((StackMarkers[ind0] != tv->stackmark) && (BigCell != ind0)) { + CStack[BigCellPos] = ind0; + StackMarkers[BigCell] = 0; + StackMarkers[ind0] = tv->stackmark; + } + + /* Permute elements of the cell C */ + i = ind0; + do { + SplCnt[SplPos[NghCounts[lab[i]]]++] = lab[i]; + } + while(++i < ind1); + + /* Reconstruct the cell C and update the inverse partition */ + newcell = ind0; + i = ind0; + ind2 = newcell+cls[newcell]-1; + do { + lab[i] = SplCnt[i]; + InvLab[lab[i]] = i; + Part->inv[i] = newcell; + + if (i == ind2) { + newcell = i+1; + if (newcell < n) ind2 = newcell+cls[newcell]-1; + } + } + while (++i < ind1); + } + } + else { + if (cls[ind0] == n) { + for (i = 0; i < n; i++) { + NghCounts[i] = TheGraph[i].d; + } + } + else { + memset(NghCounts, 0, n*sizeof(int)); + + /* NEIGHCOUNT_DENSE_DENSE_MULT */ + for (i = ind0; i < ind2; i++) { + labi = lab[i]; + nghb = TheGraph[labi].e; + for (j1int = weightstart; j1int < weightend; ++j1int) { + k = nghb[j1int]; + (NghCounts[k])++; + } + } + /* End NEIGHCOUNT_DENSE_DENSE_MULT */ + + } + + ind0 = 0; + while (ind0 < n) { /* For each cell C with size(C) > 1 */ + ind1 = ind0+cls[ind0]; + if (cls[ind0] > 1) { + + /* Determine whether C must be split */ + SplCntInd = 0; + value = NghCounts[lab[ind0]]; + for (i = ind0+1; i < ind1; i++) + { + if (NghCounts[lab[i]] != value) + { + Markers[value] = tv->mark; + SplCnt[SplCntInd++] = value; + SplPos[value] = i-ind0; + do { + value = NghCounts[lab[i++]]; + if (Markers[value] != tv->mark) { + Markers[value] = tv->mark; + SplCnt[SplCntInd++] = value; + SplPos[value] = 1; + } + else { + SplPos[value]++; + } + } + while(i != ind1); + break; + } + } + + if (SplCntInd) { + tv->mark++; + + /* Sort the values deriving from the previous step */ + sort_Split_Array(SplCnt, SplCntInd); + + Part->cells += SplCntInd-1; + + /* Split the cell C and update the information for sizes of new cells */ + /* Put the new cells into the stack */ + i = ind0; + if (StackMarkers[i] != tv->stackmark) { + BigCellSize = 0; + } + for (k = 0; k < SplCntInd; k++) { + value = SplPos[SplCnt[k]]; + cls[i] = value; + if ((StackMarkers[ind0] != tv->stackmark) && (value > BigCellSize)) { + BigCell = i; + BigCellPos = CStackInd; + BigCellSize = cls[i]; + } + SplPos[SplCnt[k]] = i; + i += value; + if (i < ind1) { + CStack[++CStackInd] = i; + StackMarkers[i] = tv->stackmark; + } + } + if ((StackMarkers[ind0] != tv->stackmark) && (BigCell != ind0)) { + CStack[BigCellPos] = ind0; + StackMarkers[BigCell] = 0; + StackMarkers[ind0] = tv->stackmark; + trieref = trie_comp(trieref, i); + if (trieref == NULL) return 0; + } + + /* Permute elements of the cell C */ + i = ind0; + do { + SplCnt[SplPos[NghCounts[lab[i]]]++] = lab[i]; + } + while(++i < ind1); + + /* Reconstruct the cell C and update the inverse partition */ + newcell = ind0; + i = ind0; + ind2 = newcell+cls[newcell]-1; + do { + lab[i] = SplCnt[i]; + InvLab[lab[i]] = i; + Part->inv[i] = newcell; + if (i == ind2) { + newcell = i+1; + if (newcell < n) ind2 = newcell+cls[newcell]-1; + } + } + while (++i < ind1); + } + } + ind0 = ind1; + } + } + } + } + } + while (weightend < iend1int); + } /* end while (CStackInd > 0) */ + + tv->augmented_cells = Part->cells - tv->augmented_cells; + + Cand->code = CLEANUP(longcode); + return 1; +} + +int traces_refine_sametrace(Candidate *Cand, + int n, + Partition *Part, + struct TracesVars* tv, + struct TracesInfo *ti) { + int i, j, k, jk, sc, ind0, ind1, ind2, ind3, ind4, labi; + int value, iend, newcell; + int HitClsInd, SplInd, SplCntInd, CStackInd, TraceInd, TraceCCInd, TraceStepsInd; + int j1int, iend1int; + unsigned int longcode; + int Sparse = TRUE; + int *lab, *cls, *InvLab, *TracePos, *SplitCell, *LabCell, *TraceEnd, Traceccend, *Tracestpend; + int BigCell, BigCellPos, BigCellSize; + boolean TraceCell = FALSE; + int *nghb; + const int variation = 0; + int currentweight, weightstart, weightend, currentcell, currentsize; + + if (tv->stackmark > (NAUTY_INFINITY-2)) { + memset(StackMarkers, 0, n*sizeof(int)); + tv->stackmark = 0; + } + tv->stackmark++; + + tv->augmented_cells = Part->cells; + + SpineTL = Spine+tv->tolevel; + TraceEnd = &(SpineTL->trcend); + Traceccend = SpineTL->ccend; + Tracestpend = &(SpineTL->stpend); + TraceCCInd = SpineTL->ccstart; + TraceStepsInd = SpineTL->stpstart; + + lab = Cand->lab; + InvLab = Cand->invlab; + cls = Part->cls; + + UPDATEMIN(Part->active, n-1); + memcpy(CStack+1, TheTrace+SpineTL->trcstart, (Part->active)*sizeof(int)); + CStackInd = Part->active; + for (i = 1; i <= CStackInd; i++) { + StackMarkers[CStack[i]] = tv->stackmark; + } + + longcode = Part->cells; + TraceInd = SpineTL->trcstart+Part->active; + + while (CStackInd > 0) + { + + weightend = 0; + + if (tv->mark > (NAUTY_INFINITY-2)) { + memset(Markers, 0, n*sizeof(int)); + memset(MarkHitVtx, 0, n*sizeof(int)); + tv->mark = 0; + } + tv->mark++; + + if (Part->cells == n) break; + + k = Select_from_CStack(cls, CStackInd); + + currentcell = CStack[k]; + currentsize = currentcell+cls[currentcell]; + CStack[k] = CStack[CStackInd--]; /* Current Cell */ + StackMarkers[currentcell] = 0; + + labi = lab[currentcell]; + iend1int = TheGraph[labi].d; + nghb = TheGraph[labi].e; + + do { + ind0 = currentcell; + ind2 = currentsize; + weightstart = weightend; + + if (tv->options->weighted) { + currentweight = (TheGraph[labi].w)[weightstart]; + while ((iend1int > weightend) && ((TheGraph[labi].w)[weightend] == currentweight)) { + weightend++; + } + } else { + weightend = TheGraph[labi].d; + } + + TraceCell = ((ind0 == TheTraceCC[TraceCCInd]) && (TraceCCInd < Traceccend)); + + /* Analysis of occurrences of neighbors of the current cell */ + /* The list of cells with neighbors in the current cell is built */ + if (cls[ind0] == 1) { /* SINGLETON CURRENT CELL CASE */ + + /* NEIGHCOUNT_SING_MULT */ + HitClsInd = 0; + for (j1int = weightstart; j1int < weightend; ++j1int) { + k = nghb[j1int]; + value = Part->inv[InvLab[k]]; + if (cls[value] > 1) { + if (Markers[value] != tv->mark) { + HitCls[HitClsInd++] = value; + Markers[value] = tv->mark; + ElmHitCll[value] = value; + } + HitVtx[ElmHitCll[value]++] = k; + } else { + switch (variation) { + case 1: + longcode = MASHCOMM(longcode, value); + break; + default: + break; + } + } + } + /* end NEIGHCOUNT_SING_MULT */ + + tv->mark++; + FIND_SPLIT_CELLS; + + /* SINGLETON CC CASE */ + if (SplInd) { + SAMETRACE_CHECK(TheTraceSplNum, TraceCCInd, SplInd, &Traceccend) + + /* Sorting the cells to be split */ + sort_Split_Array(SplCls, SplInd); + + for (j = 0; j < SplInd; j++) { + ind1 = SplCls[j]; + i = ind1+cls[ind1]-ElmHitCll[ind1]; + SAMETRACE_CHECK(TheTrace, TraceInd, i, TraceEnd) + } + + /* REARRANGE THE CELLS */ + for (j = 0; j < SplInd; j++) { + /* REARRANGE_CELLS */ + ind1 = SplCls[j]; + cls[ind1] = cls[ind1]-ElmHitCll[ind1]; + newcell = ind1+cls[ind1]; + cls[newcell] = ElmHitCll[ind1]; + Part->cells++; + if (StackMarkers[ind1] != tv->stackmark) { + if (cls[newcell] < cls[ind1]) { + CStack[++CStackInd] = newcell; + StackMarkers[newcell] = tv->stackmark; + } + else { + CStack[++CStackInd] = ind1; + StackMarkers[ind1] = tv->stackmark; + } + } + else { + CStack[++CStackInd] = newcell; + StackMarkers[newcell] = tv->stackmark; + } + SplitCell = HitVtx+ind1; + ind3 = cls[newcell]; + LabCell = lab+newcell; + for (jk = 0; jk < ind3; jk++) { + k = SplitCell[jk]; + i = LabCell[jk]; + Part->inv[newcell+jk] = newcell; + lab[InvLab[k]] = i; + InvLab[i] = InvLab[k]; + LabCell[jk] = k; + InvLab[k] = newcell+jk; + } + /* END REARRANGE_CELLS */ + + if (cls[ind1] == 1) { + Cand->singcode = MASHCOMM(Cand->singcode, Cand->lab[ind1]); + } + if (cls[newcell] == 1) { + Cand->singcode = MASHCOMM(Cand->singcode, Cand->lab[newcell]); + } + } + } + else { + if (TraceCell) { + return 0; + } + } + + } + else { + if (ti->thegraphisparse) { + + /* NEIGHCOUNT_SPARSE_MULT */ + HitClsInd = 0; + if (cls[ind0] != n) { + for (i = ind0; i < ind2; i++) { + labi = lab[i]; + nghb = TheGraph[labi].e; + for (j = weightstart; j < weightend; j++) { + k = nghb[j]; + if (MarkHitVtx[k] == tv->mark) { + NghCounts[k]++; + } + else { + value = Part->inv[InvLab[k]]; + if (cls[value] > 1) { + MarkHitVtx[k] = tv->mark; + NghCounts[k] = 1; + if (Markers[value] != tv->mark) { + HitCls[HitClsInd++] = value; + Markers[value] = tv->mark; + HitVtx[value] = k; + ElmHitCll[value] = 1; + } + else { + HitVtx[value+ElmHitCll[value]++] = k; + } + } + else { + switch (variation) { + case 1: + longcode = MASHCOMM(longcode, value); + break; + default: + break; + } + } + } + } + } + } + /* End NEIGHCOUNT_SPARSE_MULT */ + + tv->mark++; + + SplInd = 0; + SplCls[0] = n; + for (j = 0; j < HitClsInd; j++) { + ind1 = HitCls[j]; + if ((ElmHitCll[ind1] > 0) && (ElmHitCll[ind1] < cls[ind1])) { + SplCls[SplInd++] = ind1; + } + else { + ind2 = ind1+cls[ind1]; + value = NghCounts[lab[ind1++]]; + for (i = ind1; i < ind2; i++) + { + if (NghCounts[lab[i]] != value) + { + SplCls[SplInd++] = HitCls[j]; + break; + } + } + } + } + + /* SPARSE CASE */ + if (SplInd) { + SAMETRACE_CHECK(TheTraceSplNum, TraceCCInd, SplInd+n, &Traceccend) + + /* Sorting the cells to be split */ + sort_Split_Array(SplCls, SplInd); + + for (sc = 0; sc < SplInd; sc++) { /* For each cell C to be split */ + ind0 = SplCls[sc]; + ind1 = ind0 + cls[ind0]; + SplCntInd = 0; + if (ElmHitCll[ind0] < cls[ind0]) { + SplCnt[SplCntInd++] = 0; + SplPos[0] = cls[ind0] - ElmHitCll[ind0]; + } + + /* According to the numbers of neighbors of C into the current cell */ + /* compute how many vertices in C will be placed into the same new cell */ + iend = ind0 + ElmHitCll[ind0]; + for (i = ind0; i < iend; i++) { + value = NghCounts[HitVtx[i]]; + if (Markers[value] != tv->mark) { + Markers[value] = tv->mark; + SplCnt[SplCntInd++] = value; + SplPos[value] = 1; + } + else { + SplPos[value]++; + } + } + tv->mark++; + + if (SplCntInd) { + SAMETRACE_CHECK(TheTraceSteps, TraceStepsInd, SplCntInd+n, Tracestpend) + } + + /* Sort the values deriving from the previous step */ + sort_Split_Array(SplCnt, SplCntInd); + + Part->cells += SplCntInd-1; + + /* Split the cell C and update the information for sizes of new cells */ + /* Put the new cells into the stack */ + i = ind0; + if (StackMarkers[i] != tv->stackmark) { + BigCellSize = 0; + } + for (k = 0; k < SplCntInd; k++) { + value = SplPos[SplCnt[k]]; + cls[i] = value; + if ((StackMarkers[ind0] != tv->stackmark) && (value > BigCellSize)) { + BigCell = i; + BigCellPos = CStackInd; + BigCellSize = cls[i]; + } + SplPos[SplCnt[k]] = i; + i += value; + if (i < ind1) { + CStack[++CStackInd] = i; + StackMarkers[i] = tv->stackmark; + SAMETRACE_CHECK(TheTrace, TraceInd, i, TraceEnd) + } + } + + if ((StackMarkers[ind0] != tv->stackmark) && (BigCell != ind0)) { + CStack[BigCellPos] = ind0; + StackMarkers[BigCell] = 0; + StackMarkers[ind0] = tv->stackmark; + } + /* Permute elements of the cell C */ + iend = ind0 + ElmHitCll[ind0]; + for (i = ind0; i < iend; i++) { + value = HitVtx[i]; + j = SplPos[NghCounts[value]]++; /* where HitVtx[i] goes */ + k = InvLab[value]; /* where HitVtx[i] is in lab */ + lab[k] = lab[j]; + lab[j] = value; + InvLab[value] = j; + InvLab[lab[k]] = k; + NghCounts[value] = 0; + } + + /* Reconstruct the cell C and update the inverse partition */ + newcell = ind1 - ElmHitCll[ind0]; + i = newcell; + ind2 = newcell+cls[newcell]-1; + do { + Part->inv[i] = newcell; + if (i == ind2) { + newcell = i+1; + if (newcell < n) ind2 = newcell+cls[newcell]-1; + } + } + while (++i < ind1); + + for (i = ind0, k = 0; k < SplCntInd; i+=cls[i], k++) { + if (cls[i] == 1) { + Cand->singcode = MASHCOMM(Cand->singcode, Cand->lab[i]); + } + } + + } + } + else { + if (TraceCell) { + return 0; + } + } + + } + else { + if (TheGraph[lab[ind0]].d > n/cls[ind0]) { + Sparse = FALSE; + } + else { + Sparse = TRUE; + } + Sparse = TRUE; + if (Sparse) { + /* Counting occurrences of neighbors of the current cell */ + /* The list of cells with neighbors in the current cell is also built */ + if (cls[ind0] == n) { + for (i = 0; i < n; i++) { + NghCounts[i] = TheGraph[i].d; + } + HitCls[0] = 0; + HitClsInd = 1; + } + else { + memset(NghCounts, 0, n*sizeof(int)); + + /* NEIGHCOUNT_DENSE_SPARSE_MULT */ + HitClsInd = 0; + for (i = ind0; i < ind2; i++) { + labi = lab[i]; + nghb = TheGraph[labi].e; + for (j1int = weightstart; j1int < weightend; ++j1int) { + k = nghb[j1int]; + (NghCounts[k])++; + value = Part->inv[InvLab[k]]; + if (Markers[value] != tv->mark) { + if (cls[value] > 1) HitCls[HitClsInd++] = value; + Markers[value] = tv->mark; + } + } + } + /* End NEIGHCOUNT_DENSE_SPARSE_MULT */ + + } + + tv->mark++; + + SplInd = 0; + for (j = 0; j < HitClsInd; j++) { + ind1 = HitCls[j]; + ind2 = ind1+cls[ind1]; + value = NghCounts[lab[ind1++]]; + for (i = ind1; i < ind2; i++) + { + if (NghCounts[lab[i]] != value) + { + SplCls[SplInd++] = HitCls[j]; + break; + } + } + } + + /* DENSE-SPARSE CASE */ + if (SplInd) { + SAMETRACE_CHECK(TheTraceSplNum, TraceCCInd, SplInd+2*n, &Traceccend) + + /* Sorting the cells to be split */ + sort_Split_Array(SplCls, SplInd); + + for (j = 0; j < SplInd; j++) { /* For each cell C to be split */ + ind0 = SplCls[j]; + ind1 = ind0+cls[ind0]; + SplCntInd = 0; + + /* According to the numbers of neighbors of C into the current cell */ + /* compute how many vertices in C will be placed into the same new cell */ + for (i = ind0; i < ind1; i++) { + value = NghCounts[lab[i]]; + if (Markers[value] != tv->mark) { + Markers[value] = tv->mark; + SplCnt[SplCntInd++] = value; + SplPos[value] = 1; + } + else { + SplPos[value]++; + } + } + tv->mark++; + + if (SplCntInd) { + SAMETRACE_CHECK(TheTraceSteps, TraceStepsInd, SplCntInd+2*n, Tracestpend) + } + + /* Sort the values deriving from the previous step */ + sort_Split_Array(SplCnt, SplCntInd); + Part->cells += SplCntInd-1; + + /* Split the cell C and update the information for sizes of new cells */ + /* Put the new cells into the stack */ + i = ind0; + if (StackMarkers[i] != tv->stackmark) { + BigCellSize = 0; + } + for (k = 0; k < SplCntInd; k++) { + value = SplPos[SplCnt[k]]; + cls[i] = value; + if ((StackMarkers[ind0] != tv->stackmark) && (value > BigCellSize)) { + BigCell = i; + BigCellPos = CStackInd; + BigCellSize = cls[i]; + } + SplPos[SplCnt[k]] = i; + i += value; + if (i < ind1) { + CStack[++CStackInd] = i; + StackMarkers[i] = tv->stackmark; + SAMETRACE_CHECK(TheTrace, TraceInd, i, TraceEnd) + } + } + + if ((StackMarkers[ind0] != tv->stackmark) && (BigCell != ind0)) { + CStack[BigCellPos] = ind0; + StackMarkers[BigCell] = 0; + StackMarkers[ind0] = tv->stackmark; + } + + /* Permute elements of the cell C */ + i = ind0; + do { + SplCnt[SplPos[NghCounts[lab[i]]]++] = lab[i]; + } + while(++i < ind1); + + /* Reconstruct the cell C and update the inverse partition */ + newcell = ind0; + i = ind0; + ind2 = newcell+cls[newcell]-1; + do { + lab[i] = SplCnt[i]; + InvLab[lab[i]] = i; + Part->inv[i] = newcell; + if (i == ind2) { + newcell = i+1; + if (newcell < n) ind2 = newcell+cls[newcell]-1; + } + } + while (++i < ind1); + + for (i = ind0, k = 0; k < SplCntInd; i+=cls[i], k++) { + if (cls[i] == 1) { + Cand->singcode = MASHCOMM(Cand->singcode, Cand->lab[i]); + } + } + + } + } + else { + if (TraceCell) { + return 0; + } + } + + } + else { + if (cls[ind0] == n) { + for (i = 0; i < n; i++) { + NghCounts[i] = TheGraph[i].d; + } + } + else { + memset(NghCounts, 0, n*sizeof(int)); + + /* NEIGHCOUNT_DENSE_DENSE_MULT */ + for (i = ind0; i < ind2; i++) { + labi = lab[i]; + nghb = TheGraph[labi].e; + for (j1int = weightstart; j1int < weightend; ++j1int) { + k = nghb[j1int]; + (NghCounts[k])++; + } + } + /* End NEIGHCOUNT_DENSE_DENSE_MULT */ + + } + SplInd = 0; + ind4 = 0; + while (ind4 < n) { /* For each cell C with size(C) > 1 */ + ind1 = ind4+cls[ind4]; + if (cls[ind4] > 1) { + + /* Determine whether C must be split */ + SplCntInd = 0; + value = NghCounts[lab[ind4]]; + for (i = ind4+1; i < ind1; i++) { + if (NghCounts[lab[i]] != value) + { + SplInd++; + Markers[value] = tv->mark; + SplCnt[SplCntInd++] = value; + SplPos[value] = i-ind4; + do { + value = NghCounts[lab[i++]]; + if (Markers[value] != tv->mark) { + Markers[value] = tv->mark; + SplCnt[SplCntInd++] = value; + SplPos[value] = 1; + } + else { + SplPos[value]++; + } + } + while(i != ind1); + break; + } + } + tv->mark++; + + if (SplCntInd) { + SAMETRACE_CHECK(TheTraceSteps, TraceStepsInd, SplCntInd+3*n, Tracestpend) + + /* Sort the values deriving from the previous step */ + sort_Split_Array(SplCnt, SplCntInd); + + Part->cells += SplCntInd-1; + + /* Split the cell C and update the information for sizes of new cells */ + /* Put the new cells into the stack */ + i = ind4; + if (StackMarkers[i] != tv->stackmark) { + BigCellSize = 0; + } + for (k = 0; k < SplCntInd; k++) { + value = SplPos[SplCnt[k]]; + cls[i] = value; + if ((StackMarkers[ind4] != tv->stackmark) && (value > BigCellSize)) { + BigCell = i; + BigCellPos = CStackInd; + BigCellSize = cls[i]; + } + SplPos[SplCnt[k]] = i; + i += value; + if (i < ind1) { + CStack[++CStackInd] = i; + StackMarkers[i] = tv->stackmark; + SAMETRACE_CHECK(TheTrace, TraceInd, i, TraceEnd) + } + } + if ((StackMarkers[ind4] != tv->stackmark) && (BigCell != ind4)) { + CStack[BigCellPos] = ind4; + StackMarkers[BigCell] = 0; + StackMarkers[ind4] = tv->stackmark; + } + + /* Permute elements of the cell C */ + i = ind4; + do { + SplCnt[SplPos[NghCounts[lab[i]]]++] = lab[i]; + } + while(++i < ind1); + + /* Reconstruct the cell C and update the inverse partition */ + newcell = ind4; + i = ind4; + ind2 = newcell+cls[newcell]-1; + do { + lab[i] = SplCnt[i]; + InvLab[lab[i]] = i; + Part->inv[i] = newcell; + if (i == ind2) { + newcell = i+1; + if (newcell < n) ind2 = newcell+cls[newcell]-1; + } + } + while (++i < ind1); + + for (i = ind4, k = 0; k < SplCntInd; i+=cls[i], k++) { + if (cls[i] == 1) { + Cand->singcode = MASHCOMM(Cand->singcode, Cand->lab[i]); + } + } + + } + } + ind4 = ind1; + } + + /* DENSE-DENSE CASE */ + if (SplInd) { + SAMETRACE_CHECK(TheTraceSplNum, TraceCCInd, SplInd+3*n, &Traceccend) + } + else { + if (TraceCell) { + return 0; + } + } + + } + } + } + } + while (weightend < iend1int); + } /* end while (CStackInd > 0) */ + + for (i=SpineTL->trcstart; i < TraceInd; i++) { + ind0 = TheTrace[i]; + longcode = MASHNONCOMM(longcode, Part->inv[ind0]); + labi = lab[ind0]; + iend1int = TheGraph[labi].d; + nghb = TheGraph[labi].e; + for (j1int = 0; j1int < iend1int; ++j1int) { + k = nghb[j1int]; + value = Part->inv[InvLab[k]]; + longcode = MASHCOMM(longcode, value); + } + } + + tv->augmented_cells = Part->cells - tv->augmented_cells; + + Part->code = Cand->code = CLEANUP(longcode); + if ((Cand->code != SpineTL->part->code) || (TraceInd != *TraceEnd)) return FALSE; + return TRUE; +} + +void refine_tr(sparsegraph *sg, int *lab, int *ptn, int *numcells, int *code, TracesOptions *options_arg) { + + const int n = sg->nv; + const int m = SETWORDSNEEDED(n); + + int i, j, ord; + Partition *CurrPart; + Candidate *CurrCand; + struct TracesVars tvar, *tv; + struct TracesInfo tinf, *ti; + + if (n > (NAUTY_INFINITY-2)) + { + fprintf(ERRFILE, "Traces: need n <= %d, but n=%d\n\n", + NAUTY_INFINITY-2, n); + return; + } + + Allocate_refine_Structures(n); + + tv = &tvar; + ti = &tinf; + + tv->options = options_arg; + tv->mark = tv->stackmark = NAUTY_INFINITY-1; + tv->maxdeg = 0; + tv->mindeg = n; + + outfile = (tv->options->outfile == NULL ? stdout : tv->options->outfile); + + for (i = 0; i < n; i++) { + IDENTITY_PERM[i] = i; + } + + copy_sg_structure(&redgraph, sg); + + tv->graph = &redgraph; + if (tv->options->weighted) { + tv->graph->w = malloc(tv->graph->wlen*sizeof(int)); + if (tv->graph->w == NULL) { + fprintf(ERRFILE, "\nError, memory not allocated.\n"); + exit(1); + } + memcpy(tv->graph->w, sg->w, tv->graph->wlen*sizeof(int)); + } + memcpy(tv->graph->e, sg->e, tv->graph->elen*sizeof(int)); + + for (i=0; i<n; i++) { + TheGraph[i].d = sg->d[i]; + if (TheGraph[i].d > tv->maxdeg) { + tv->maxdeg = TheGraph[i].d; + } + if (TheGraph[i].d < tv->mindeg) { + tv->mindeg = TheGraph[i].d; + } + TheGraph[i].e = tv->graph->e + sg->v[i]; + if (sg->w) + TheGraph[i].w = tv->graph->w + sg->v[i]; + else + TheGraph[i].w = NULL; + TheGraph[i].one = FALSE; + } + + ord = 0; + + /*----------- WEIGHTS --------------*/ + if (tv->options->weighted) { + WeightCodes(n); + ord = trie_classify(n,tv); + } + /*----------------------------------*/ + + if ((tv->maxdeg == tv->mindeg) && (ord == 0)) ti->regular = TRUE; else ti->regular = FALSE; + + /* The graph is sparse? */ + if (sg->nde < n || sg->nde / n < n / (sg->nde / n)) { + ti->thegraphisparse = TRUE; + } + else { + ti->thegraphisparse = FALSE; + } + + /* Initialize candidate, partition, cells, orbits */ + CurrCand = NewCandidate(n, &GarbList, TRUE); + CurrPart = NewPartition(n); + memset(CurrPart->inv, 0, n*sizeof(int)); + + CurrCand->singcode = 0; + + if (ti->regular) { + memcpy(CurrCand->lab, lab, n*sizeof(int)); + CurrPart->cells = 0; + j = 0; + for (i = 0; i < n; i++) { + if (j) CurrPart->inv[i] = j; + CurrCand->invlab[CurrCand->lab[i]] = i; + if (!ptn[i]) { + CurrPart->cls[j] = i-j+1; + if (CurrPart->cls[j] == 1) { + CurrCand->singcode = MASHCOMM(CurrCand->singcode, CurrCand->lab[j]); + } + TheTrace[CurrPart->cells++] = j; + j = i+1; + } + } + } else { + if (tv->options->weighted) { + CurrPart->cells = traces_vertexclass_refine (n, lab, ptn, CurrCand, CurrPart, WeightsSeq); + } + else { + CurrPart->cells = traces_vertexclass_refine (n, lab, ptn, CurrCand, CurrPart, sg->d); + } + } + + /* First refinement */ + refine_tr_refine(CurrCand, n, CurrPart, tv, ti); + + for (i = CurrPart->cls[0]; i < n; i+=CurrPart->cls[i]) { + ptn[i-1] = 0; + } + ptn[n-1] = 0; + + memcpy(lab, CurrCand->lab, n*sizeof(int)); + *code = CurrCand->code; + *numcells = CurrPart->cells; + + FREECAND(CurrCand) + FREEPART(CurrPart) + + if (tv->graph != sg) { + SG_FREE(redgraph); + } + +#if !MAXN + DYNFREE(CStack, CStack_sz); + DYNFREE(IDENTITY_PERM, IDENTITY_PERM_sz); + DYNFREE(Markers, Markers_sz); + DYNFREE(MarkHitVtx, MarkHitVtx_sz); + DYNFREE(TreeMarkers, TreeMarkers_sz); + DYNFREE(NghCounts, NghCounts_sz); + DYNFREE(Singletons, Singletons_sz); + DYNFREE(SplPos, SplPos_sz); + DYNFREE(SplCls, SplCls_sz); + DYNFREE(SplCnt, SplCnt_sz); + DYNFREE(StackMarkers, StackMarkers_sz); + DYNFREE(TheTrace, TheTrace_sz); + DYNFREE(TheTraceSteps, TheTraceSteps_sz); + DYNFREE(TheTraceCC, TheTraceCC_sz); + DYNFREE(TheTraceSplNum, TheTraceSplNum_sz); + DYNFREE(WeightsSeq, WeightsSeq_sz); + DYNFREE(WorkArray1, WorkArray1_sz); + DYNFREE(WorkArray2, WorkArray2_sz); + DYNFREE(WorkArray3, WorkArray3_sz); + DYNFREE(WorkArray4, WorkArray4_sz); + DYNFREE(WorkArray5, WorkArray5_sz); + DYNFREE(TreeStack, TreeStack_sz); + DYNFREE(Spine, Spine_sz); + DYNFREE(TrieArray, TrieArray_sz); + DYNFREE(TheGraph, TheGraph_sz); +#endif +} + +void refine_tr_refine(Candidate *Cand, + int n, + Partition *Part, + struct TracesVars* tv, + struct TracesInfo *ti) { + + int i, j, k, jk, sc, ind0, ind1, ind2, ind3, ind4, labi; + int value, iend, newcell; + int HitClsInd, SplInd, SplCntInd, CStackInd, TraceInd, TraceCCInd, TraceStepsInd; + int j1int, iend1int; + unsigned int longcode; + int newtrace = FALSE; + int Sparse = TRUE; + int *lab, *cls, *InvLab, *TracePos, *SplitCell, *LabCell, TraceEnd, Traceccend, Tracestpend; + int BigCell, BigCellPos, BigCellSize; + boolean TraceCell = FALSE; + int *nghb; + const int variation = 0; + int currentweight, weightstart, weightend, currentcell, currentsize; + + HitClsInd = 0; + if (tv->stackmark > (NAUTY_INFINITY-2)) { + memset(StackMarkers, 0, n*sizeof(int)); + tv->stackmark = 0; + } + tv->stackmark++; + + lab = Cand->lab; + InvLab = Cand->invlab; + cls = Part->cls; + + UPDATEMIN(Part->active, n-1); + memcpy(CStack+1, TheTrace, (Part->cells)*sizeof(int)); + CStackInd = Part->cells; + + for (i = 1; i <= CStackInd; i++) { + StackMarkers[CStack[i]] = tv->stackmark; + } + + longcode = Part->cells; + newtrace = TRUE; + while (CStackInd > 0) { + + weightend = 0; + + if (tv->mark > (NAUTY_INFINITY-2)) { + memset(Markers, 0, n*sizeof(int)); + memset(MarkHitVtx, 0, n*sizeof(int)); + tv->mark = 0; + } + tv->mark++; + + if (Part->cells == n) break; + + k = Select_from_CStack(cls, CStackInd); + + currentcell = CStack[k]; + currentsize = currentcell+cls[currentcell]; + CStack[k] = CStack[CStackInd--]; + StackMarkers[currentcell] = 0; + + labi = lab[currentcell]; + iend1int = TheGraph[labi].d; + nghb = TheGraph[labi].e; + + do { + + ind0 = currentcell; + ind2 = currentsize; + weightstart = weightend; + + if (tv->options->weighted) { + currentweight = (TheGraph[labi].w)[weightstart]; + while ((iend1int > weightend) && ((TheGraph[labi].w)[weightend] == currentweight)) { + weightend++; + } + } else { + weightend = TheGraph[labi].d; + } + + /* Analysis of occurrences of neighbors of the current cell */ + /* The list of cells with neighbors in the current cell is built */ + if (cls[ind0] == 1) { /* SINGLETON CURRENT CELL CASE */ + + /* NEIGHCOUNT_SING_MULT */ + HitClsInd = 0; + for (j1int = weightstart; j1int < weightend; ++j1int) { + k = nghb[j1int]; + value = Part->inv[InvLab[k]]; + if (cls[value] > 1) { + if (Markers[value] != tv->mark) { + HitCls[HitClsInd++] = value; + Markers[value] = tv->mark; + ElmHitCll[value] = value; + } + HitVtx[ElmHitCll[value]++] = k; + } else { + switch (variation) { + case 1: + longcode = MASHCOMM(longcode, value); + break; + default: + break; + } + } + } + /* end NEIGHCOUNT_SING_MULT */ + + tv->mark++; + FIND_SPLIT_CELLS; + + /* SINGLETON CC CASE */ + if (SplInd) { + + /* Sorting the cells to be split */ + sort_Split_Array(SplCls, SplInd); + + for (j = 0; j < SplInd; j++) { + ind1 = SplCls[j]; + i = ind1+cls[ind1]-ElmHitCll[ind1]; + } + + /* REARRANGE THE CELLS */ + for (j = 0; j < SplInd; j++) { + /* REARRANGE_CELLS */ + ind1 = SplCls[j]; + cls[ind1] = cls[ind1]-ElmHitCll[ind1]; + newcell = ind1+cls[ind1]; + cls[newcell] = ElmHitCll[ind1]; + Part->cells++; + if (StackMarkers[ind1] != tv->stackmark) { + if (cls[newcell] < cls[ind1]) { + CStack[++CStackInd] = newcell; + StackMarkers[newcell] = tv->stackmark; + } + else { + CStack[++CStackInd] = ind1; + StackMarkers[ind1] = tv->stackmark; + } + } + else { + CStack[++CStackInd] = newcell; + StackMarkers[newcell] = tv->stackmark; + } + SplitCell = HitVtx+ind1; + ind3 = cls[newcell]; + LabCell = lab+newcell; + for (jk = 0; jk < ind3; jk++) { + k = SplitCell[jk]; + i = LabCell[jk]; + Part->inv[newcell+jk] = newcell; + lab[InvLab[k]] = i; + InvLab[i] = InvLab[k]; + LabCell[jk] = k; + InvLab[k] = newcell+jk; + } + /* END REARRANGE_CELLS */ + + } + } + else { + } + } + else { + if (ti->thegraphisparse) { + + /* NEIGHCOUNT_SPARSE_MULT */ + HitClsInd = 0; + if (cls[ind0] != n) { + for (i = ind0; i < ind2; i++) { + labi = lab[i]; + nghb = TheGraph[labi].e; + for (j = weightstart; j < weightend; j++) { + k = nghb[j]; + if (MarkHitVtx[k] == tv->mark) { + NghCounts[k]++; + } + else { + value = Part->inv[InvLab[k]]; + if (cls[value] > 1) { + MarkHitVtx[k] = tv->mark; + NghCounts[k] = 1; + if (Markers[value] != tv->mark) { + HitCls[HitClsInd++] = value; + Markers[value] = tv->mark; + HitVtx[value] = k; + ElmHitCll[value] = 1; + } + else { + HitVtx[value+ElmHitCll[value]++] = k; + } + } + else { + switch (variation) { + case 1: + longcode = MASHCOMM(longcode, value); + break; + default: + break; + } + } + } + } + } + } + /* End NEIGHCOUNT_SPARSE_MULT */ + + tv->mark++; + + SplInd = 0; + SplCls[0] = n; + for (j = 0; j < HitClsInd; j++) { + ind1 = HitCls[j]; + if ((ElmHitCll[ind1] > 0) && (ElmHitCll[ind1] < cls[ind1])) { + SplCls[SplInd++] = ind1; + } + else { + ind2 = ind1+cls[ind1]; + value = NghCounts[lab[ind1++]]; + for (i = ind1; i < ind2; i++) + { + if (NghCounts[lab[i]] != value) + { + SplCls[SplInd++] = HitCls[j]; + break; + } + } + } + } + + /* SPARSE CASE */ + if (SplInd) { + + /* Sorting the cells to be split */ + sort_Split_Array(SplCls, SplInd); + + for (sc = 0; sc < SplInd; sc++) { /* For each cell C to be split */ + ind0 = SplCls[sc]; + ind1 = ind0 + cls[ind0]; + SplCntInd = 0; + if (ElmHitCll[ind0] < cls[ind0]) { + SplCnt[SplCntInd++] = 0; + SplPos[0] = cls[ind0] - ElmHitCll[ind0]; + } + + /* According to the numbers of neighbors of C into the current cell */ + /* compute how many vertices in C will be placed into the same new cell */ + iend = ind0 + ElmHitCll[ind0]; + for (i = ind0; i < iend; i++) { + value = NghCounts[HitVtx[i]]; + if (Markers[value] != tv->mark) { + Markers[value] = tv->mark; + SplCnt[SplCntInd++] = value; + SplPos[value] = 1; + } + else { + SplPos[value]++; + } + } + tv->mark++; + + sort_Split_Array(SplCnt,SplCntInd); + Part->cells += SplCntInd-1; + + /* Split the cell C and update the information for sizes of new cells */ + /* Put the new cells into the stack */ + i = ind0; + if (StackMarkers[i] != tv->stackmark) { + BigCellSize = 0; + } + for (k = 0; k < SplCntInd; k++) { + value = SplPos[SplCnt[k]]; + cls[i] = value; + if ((StackMarkers[ind0] != tv->stackmark) && (value > BigCellSize)) { + BigCell = i; + BigCellPos = CStackInd; + BigCellSize = cls[i]; + } + SplPos[SplCnt[k]] = i; + i += value; + if (i < ind1) { + CStack[++CStackInd] = i; + StackMarkers[i] = tv->stackmark; + } + } + + if ((StackMarkers[ind0] != tv->stackmark) && (BigCell != ind0)) { + CStack[BigCellPos] = ind0; + StackMarkers[BigCell] = 0; + StackMarkers[ind0] = tv->stackmark; + } + /* Permute elements of the cell C */ + iend = ind0 + ElmHitCll[ind0]; + for (i = ind0; i < iend; i++) { + value = HitVtx[i]; + j = SplPos[NghCounts[value]]++; /* where HitVtx[i] goes */ + k = InvLab[value]; /* where HitVtx[i] is in lab */ + lab[k] = lab[j]; + lab[j] = value; + InvLab[value] = j; + InvLab[lab[k]] = k; + NghCounts[value] = 0; + } + + /* Reconstruct the cell C and update the inverse partition */ + newcell = ind1 - ElmHitCll[ind0]; + i = newcell; + ind2 = newcell+cls[newcell]-1; + do { + Part->inv[i] = newcell; + if (i == ind2) { + newcell = i+1; + if (newcell < n) ind2 = newcell+cls[newcell]-1; + } + } + while (++i < ind1); + } + } + } + else { + if (TheGraph[lab[ind0]].d > n/cls[ind0]) { + Sparse = FALSE; + } + else { + Sparse = TRUE; + } + Sparse = TRUE; + if (Sparse) { + /* Counting occurrences of neighbors of the current cell */ + /* The list of cells with neighbors in the current cell is also built */ + if (cls[ind0] == n) { + for (i = 0; i < n; i++) { + NghCounts[i] = TheGraph[i].d; + } + HitCls[0] = 0; + HitClsInd = 1; + } + else { + memset(NghCounts, 0, n*sizeof(int)); + + /* NEIGHCOUNT_DENSE_SPARSE_MULT */ + HitClsInd = 0; + for (i = ind0; i < ind2; i++) { + labi = lab[i]; + nghb = TheGraph[labi].e; + for (j1int = weightstart; j1int < weightend; ++j1int) { + k = nghb[j1int]; + (NghCounts[k])++; + value = Part->inv[InvLab[k]]; + if (Markers[value] != tv->mark) { + if (cls[value] > 1) HitCls[HitClsInd++] = value; + Markers[value] = tv->mark; + } + } + } + /* End NEIGHCOUNT_DENSE_SPARSE_MULT */ + + } + + tv->mark++; + + + SplInd = 0; + for (j = 0; j < HitClsInd; j++) { + ind1 = HitCls[j]; + ind2 = ind1+cls[ind1]; + value = NghCounts[lab[ind1++]]; + for (i = ind1; i < ind2; i++) + { + if (NghCounts[lab[i]] != value) + { + SplCls[SplInd++] = HitCls[j]; + break; + } + } + } + + /* DENSE-SPARSE CASE */ + if (SplInd) { + + /* Sorting the cells to be split */ + sort_Split_Array(SplCls, SplInd); + + for (j = 0; j < SplInd; j++) { /* For each cell C to be split */ + ind0 = SplCls[j]; + ind1 = ind0+cls[ind0]; + SplCntInd = 0; + + /* According to the numbers of neighbors of C into the current cell */ + /* compute how many vertices in C will be placed into the same new cell */ + for (i = ind0; i < ind1; i++) { + value = NghCounts[lab[i]]; + if (Markers[value] != tv->mark) { + Markers[value] = tv->mark; + SplCnt[SplCntInd++] = value; + SplPos[value] = 1; + } + else { + SplPos[value]++; + } + } + tv->mark++; + + /* Sort the values deriving from the previous step */ + sort_Split_Array(SplCnt, SplCntInd); + Part->cells += SplCntInd-1; + + /* Split the cell C and update the information for sizes of new cells */ + /* Put the new cells into the stack */ + i = ind0; + if (StackMarkers[i] != tv->stackmark) { + BigCellSize = 0; + } + for (k = 0; k < SplCntInd; k++) { + value = SplPos[SplCnt[k]]; + cls[i] = value; + + if ((StackMarkers[ind0] != tv->stackmark) && (value > BigCellSize)) { + BigCell = i; + BigCellPos = CStackInd; + BigCellSize = cls[i]; + } + SplPos[SplCnt[k]] = i; + i += value; + if (i < ind1) { + CStack[++CStackInd] = i; + StackMarkers[i] = tv->stackmark; + } + } + + if ((StackMarkers[ind0] != tv->stackmark) && (BigCell != ind0)) { + CStack[BigCellPos] = ind0; + StackMarkers[BigCell] = 0; + StackMarkers[ind0] = tv->stackmark; + } + + /* Permute elements of the cell C */ + i = ind0; + do { + SplCnt[SplPos[NghCounts[lab[i]]]++] = lab[i]; + } + while(++i < ind1); + + /* Reconstruct the cell C and update the inverse partition */ + newcell = ind0; + i = ind0; + ind2 = newcell+cls[newcell]-1; + do { + lab[i] = SplCnt[i]; + InvLab[lab[i]] = i; + + Part->inv[i] = newcell; + if (i == ind2) { + newcell = i+1; + if (newcell < n) ind2 = newcell+cls[newcell]-1; + } + } + while (++i < ind1); + } + } + } + else { + if (cls[ind0] == n) { + for (i = 0; i < n; i++) { + NghCounts[i] = TheGraph[i].d; + } + } + else { + memset(NghCounts, 0, n*sizeof(int)); + + /* NEIGHCOUNT_DENSE_DENSE_MULT */ + for (i = ind0; i < ind2; i++) { + labi = lab[i]; + nghb = TheGraph[labi].e; + for (j1int = weightstart; j1int < weightend; ++j1int) { + k = nghb[j1int]; + (NghCounts[k])++; + } + } + /* End NEIGHCOUNT_DENSE_DENSE_MULT */ + + } + SplInd = 0; + ind4 = 0; + while (ind4 < n) { /* For each cell C with size(C) > 1 */ + ind1 = ind4+cls[ind4]; + if (cls[ind4] > 1) { + + + /* Determine whether C must be split */ + SplCntInd = 0; + value = NghCounts[lab[ind4]]; + for (i = ind4+1; i < ind1; i++) { + if (NghCounts[lab[i]] != value) + { + SplInd++; + Markers[value] = tv->mark; + SplCnt[SplCntInd++] = value; + SplPos[value] = i-ind4; + do { + value = NghCounts[lab[i++]]; + if (Markers[value] != tv->mark) { + Markers[value] = tv->mark; + SplCnt[SplCntInd++] = value; + SplPos[value] = 1; + } + else { + SplPos[value]++; + } + } + while(i != ind1); + break; + } + } + tv->mark++; + + if (SplCntInd) { + + /* Sort the values deriving from the previous step */ + sort_Split_Array(SplCnt, SplCntInd); + Part->cells += SplCntInd-1; + + /* Split the cell C and update the information for sizes of new cells */ + /* Put the new cells into the stack */ + i = ind4; + if (StackMarkers[i] != tv->stackmark) { + BigCellSize = 0; + } + for (k = 0; k < SplCntInd; k++) { + value = SplPos[SplCnt[k]]; + cls[i] = value; + + if ((StackMarkers[ind4] != tv->stackmark) && (value > BigCellSize)) { + BigCell = i; + BigCellPos = CStackInd; + BigCellSize = cls[i]; + } + SplPos[SplCnt[k]] = i; + i += value; + if (i < ind1) { + CStack[++CStackInd] = i; + StackMarkers[i] = tv->stackmark; + } + } + if ((StackMarkers[ind4] != tv->stackmark) && (BigCell != ind4)) { + CStack[BigCellPos] = ind4; + StackMarkers[BigCell] = 0; + StackMarkers[ind4] = tv->stackmark; + } + + /* Permute elements of the cell C */ + i = ind4; + do { + SplCnt[SplPos[NghCounts[lab[i]]]++] = lab[i]; + } + while(++i < ind1); + + /* Reconstruct the cell C and update the inverse partition */ + newcell = ind4; + i = ind4; + ind2 = newcell+cls[newcell]-1; + do { + lab[i] = SplCnt[i]; + InvLab[lab[i]] = i; + Part->inv[i] = newcell; + if (i == ind2) { + newcell = i+1; + if (newcell < n) ind2 = newcell+cls[newcell]-1; + } + } + while (++i < ind1); + } + } + ind4 = ind1; + } + + /* DENSE-DENSE CASE */ + } + } + } + } + while (weightend < iend1int); + } /* end while (CStackInd > 0) */ + + Cand->code = CLEANUP(longcode); + for (ind0=Part->cls[0]; ind0 < n; ind0+=Part->cls[ind0]) { + longcode = MASHNONCOMM(longcode, Part->inv[ind0]); + labi = lab[ind0]; + iend1int = TheGraph[labi].d; + nghb = TheGraph[labi].e; + for (j1int = 0; j1int < iend1int; ++j1int) { + k = nghb[j1int]; + value = Part->inv[InvLab[k]]; + longcode = MASHCOMM(longcode, value); + } + } + Part->code = Cand->code = CLEANUP(longcode); + return; +} + +void Adjust_Cycles(Candidate *Cand, Partition *Part, int n, struct TracesVars *tv) { + int i, j, i1, j1, k; + int tmp, arg, cyclength; + + if (Part->cells < n) { + memset(CyclesLength,0,n*sizeof(int)); + SETMARK(CellMarkers1, tv->markcell1) + SETMARK(CellMarkers2, tv->markcell2) + i1 = j1 = 0; + for (i=0; i<n; i+=Part->cls[i]) { + if (Part->cls[i] > 1) { + for (j=i; j<i+Part->cls[i]; j++) { + arg = Cand->lab[j]; + if (CellMarkers1[arg] != tv->markcell1) { + CellMarkers1[arg] = tv->markcell1; + cyclength = 1; + CyclesPart[i1++] = Cand->invlab[arg]; + do { + tmp = NextNeighbour(arg, Cand, Part, CellMarkers1, tv->markcell1, &arg, n); + if (tmp) { + CellMarkers1[arg] = tv->markcell1; + cyclength++; + CyclesPart[i1++] = Cand->invlab[arg]; + } + } while (tmp); + for (k=j1; k<i1; k++) { + CyclesLength[CyclesPart[k]] = cyclength; + } + j1 = i1; + } + } + } + } + } + for (i=0; i<n; i+=Part->cls[i]) { + if (Part->cls[i] > 1) { + sort2ints(CyclesLength+i, Cand->lab+i, Part->cls[i]); + } + for (j=i; j<i+Part->cls[i]; j++) { + Cand->invlab[Cand->lab[j]] = j; + } + } +} + +void Allocate_Traces_Structures(int n) { +#if !MAXN + DYNALLOC1(int, AUTPERM, AUTPERM_sz, n, "Traces"); + DYNALLOC1(int, BreakSteps, BreakSteps_sz, n, "Traces"); + DYNALLOC1(int, CurrOrbSize, CurrOrbSize_sz, n, "Traces"); + DYNALLOC1(int, CurrRefCells, CurrRefCells_sz, n, "Traces"); + DYNALLOC1(boolean, Diff, Diff_sz, n, "Traces"); + DYNALLOC1(int, CStack, CStack_sz, n, "Traces"); + DYNALLOC1(boolean, Factorials, Factorials_sz, n, "Traces"); + DYNALLOC1(int, fix, fix_sz, n, "Traces"); + DYNALLOC1(int, IDENTITY_PERM, IDENTITY_PERM_sz, n, "Traces"); + DYNALLOC1(int, Markers, Markers_sz, n, "Traces"); + DYNALLOC1(int, TreeMarkers, TreeMarkers_sz, n, "Traces"); + DYNALLOC1(int, AutMarkers, AutMarkers_sz, n, "Traces"); + DYNALLOC1(int, MarkHitVtx, MarkHitVtx_sz, n, "Traces"); + DYNALLOC1(int, MultRefCells, MultRefCells_sz, n, "Traces"); + DYNALLOC1(int, NghCounts, NghCounts_sz, n, "Traces"); + DYNALLOC1(int, OrbSize, OrbSize_sz, n, "Traces"); + DYNALLOC1(int, OrbList, OrbList_sz, n, "Traces"); + DYNALLOC1(pair, PrmPairs, PrmPairs_sz, n, "Traces"); + DYNALLOC1(int, TempOrbList, TempOrbList_sz, n, "Traces"); + DYNALLOC1(int, RefCells, RefCells_sz, n, "Traces"); + DYNALLOC1(int, Singletons, Singletons_sz, n, "Traces"); + DYNALLOC1(int, SplCls, SplCls_sz, n, "Traces"); + DYNALLOC1(int, SplCnt, SplCnt_sz, n, "Traces"); + DYNALLOC1(int, SplPos, SplPos_sz, n, "Traces"); + DYNALLOC1(int, StackMarkers, StackMarkers_sz, n, "Traces"); + DYNALLOC1(int, TheTrace, TheTrace_sz, n+10, "Traces"); + DYNALLOC1(int, TheTraceCC, TheTraceCC_sz, n, "Traces"); + DYNALLOC1(int, TheTraceSplNum, TheTraceSplNum_sz, n, "Traces"); + DYNALLOC1(int, TheTraceSteps, TheTraceSteps_sz, n+10, "Traces"); + DYNALLOC1(int, TEMPLAB, TEMPLAB_sz, n, "Traces"); + DYNALLOC1(int, TEMPINVLAB, TEMPINVLAB_sz, n, "Traces"); + DYNALLOC1(int, WeightsSeq, WeightsSeq_sz, n, "Traces"); + DYNALLOC1(int, WorkArray, WorkArray_sz, n, "Traces"); + DYNALLOC1(int, WorkArray0, WorkArray0_sz, n, "Traces"); + DYNALLOC1(int, WorkArray1, WorkArray1_sz, n, "Traces"); + DYNALLOC1(int, WorkArray2, WorkArray2_sz, n, "Traces"); + DYNALLOC1(int, WorkArray3, WorkArray3_sz, n, "Traces"); + DYNALLOC1(int, WorkArray4, WorkArray4_sz, n, "Traces"); + DYNALLOC1(int, WorkArray5, WorkArray5_sz, n, "Traces"); + DYNALLOC1(int, WorkArray6, WorkArray6_sz, n, "Traces"); + DYNALLOC1(int, WorkArray7, WorkArray7_sz, n, "Traces"); + DYNALLOC1(int, TreeStack, TreeStack_sz, n, "Traces"); + DYNALLOC1(TracesSpine, Spine, Spine_sz, n, "Traces"); + DYNALLOC1(trie*, TrieArray, TrieArray_sz, n, "Traces"); + DYNALLOC1(grph_strct, TheGraph, TheGraph_sz, n, "Traces"); + DYNALLOC1(ExpPathInfo, EPCodes, EPCodes_sz, n, "Traces"); + DYNALLOC1(int, CyclesPart, CyclesPart_sz, n, "Traces"); + DYNALLOC1(int, CyclesLength, CyclesLength_sz, n, "Traces"); +#endif + return; +} + +void Allocate_refine_Structures(int n) { +#if !MAXN + DYNALLOC1(int, CStack, CStack_sz, n, "refine_tr"); + DYNALLOC1(int, IDENTITY_PERM, IDENTITY_PERM_sz, n, "refine_tr"); + DYNALLOC1(int, Markers, Markers_sz, n, "refine_tr"); + DYNALLOC1(int, MarkHitVtx, MarkHitVtx_sz, n, "refine_tr"); + DYNALLOC1(int, TreeMarkers, TreeMarkers_sz, n, "refine_tr"); + DYNALLOC1(int, NghCounts, NghCounts_sz, n, "refine_tr"); + DYNALLOC1(int, Singletons, Singletons_sz, n, "refine_tr"); + DYNALLOC1(int, SplPos, SplPos_sz, n, "refine_tr"); + DYNALLOC1(int, SplCls, SplCls_sz, n, "refine_tr"); + DYNALLOC1(int, SplCnt, SplCnt_sz, n, "refine_tr"); + DYNALLOC1(int, StackMarkers, StackMarkers_sz, n, "refine_tr"); + DYNALLOC1(int, TheTrace, TheTrace_sz, n+10, "refine_tr"); + DYNALLOC1(int, TheTraceSteps, TheTraceSteps_sz, n+10, "refine_tr"); + DYNALLOC1(int, TheTraceCC, TheTraceCC_sz, n, "refine_tr"); + DYNALLOC1(int, TheTraceSplNum, TheTraceSplNum_sz, n, "refine_tr"); + DYNALLOC1(int, WeightsSeq, WeightsSeq_sz, n, "refine_tr"); + DYNALLOC1(int, WorkArray1, WorkArray1_sz, n, "refine_tr"); + DYNALLOC1(int, WorkArray2, WorkArray2_sz, n, "refine_tr"); + DYNALLOC1(int, WorkArray3, WorkArray3_sz, n, "refine_tr"); + DYNALLOC1(int, WorkArray4, WorkArray4_sz, n, "refine_tr"); + DYNALLOC1(int, WorkArray5, WorkArray5_sz, n, "refine_tr"); + DYNALLOC1(int, TreeStack, TreeStack_sz, n, "refine_tr"); + DYNALLOC1(TracesSpine, Spine, Spine_sz, n, "refine_tr"); + DYNALLOC1(trie*, TrieArray, TrieArray_sz, n, "refine_tr"); + DYNALLOC1(grph_strct, TheGraph, TheGraph_sz, n, "refine_tr"); +#endif + +#define HitCls WorkArray2 +#define HitVtx WorkArray3 +#define ElmHitCll WorkArray5 + return; +} + +struct Candidate *NewCandidate(int n, Candidate **GarbList, int Mrk) { + struct Candidate *Cand; + + if (*GarbList) { + Cand = *GarbList; + *GarbList = (*GarbList)->next; + } + else { + Cand = malloc(sizeof(*Cand)); + if (Cand == NULL) { + fprintf(ERRFILE, "\nError, memory not allocated.\n"); + exit(1); + } + Cand->lab = malloc(n*sizeof(*Cand->lab)); + if (Cand->lab == NULL) { + fprintf(ERRFILE, "\nError, memory not allocated.\n"); + exit(1); + } + Cand->invlab = malloc(n*sizeof(*Cand->invlab)); + if (Cand->invlab == NULL) { + fprintf(ERRFILE, "\nError, memory not allocated.\n"); + exit(1); + } + } + Cand->do_it = Mrk; + Cand->indnum = 0; + Cand->code = 0; + Cand->next = NULL; + Cand->stnode = NULL; + Cand->sortedlab = FALSE; + return Cand; +} + +int Check_degree_one(sparsegraph *sg, Candidate *Cand, Partition *Part, int n) { + + int i; + + for (i=0; i<n; i += Part->cls[i]) { + if (sg->d[Cand->lab[i]] == 1) { + return TRUE; + } + } + return FALSE; +} + +int CheckForAutomorphisms(Candidate *CurrCand, Candidate *NextCand, + struct TracesVars* tv, struct TracesInfo* ti, + int m, int n, Partition* Part) { + Candidate *CheckAutList; + int i, j, k, tgt_level, numtemporbits; + int CheckLevel, CheckLevelEnd; + int temp, tmp, tmp1, arg, arg1, val, val1; + searchtrie *TrieCandFrom, *TrieCheckFrom; + + VERB_PRINT("CFA",3,FALSE) + + CheckLevel = 0; + CheckLevelEnd = 0; + temp = 0; + tv->gotonode = NULL; + tv->conta6++; + + switch (tv->compstage) { + case 0: + if (tv->strategy) { + CheckLevel = CheckLevelEnd = tv->maxtreelevel; + } + else { + if ((Spine[tv->tolevel].part)->cells == tv->finalnumcells) { + CheckLevel = CheckLevelEnd = tv->maxtreelevel; + } + else { + CheckLevel = 1; + if ((Spine[tv->maxtreelevel].part)->cells == tv->finalnumcells) { + CheckLevelEnd = tv->maxtreelevel - 1; + } + else { + CheckLevelEnd = tv->maxtreelevel; + } + } + } + break; + case 1: + CheckLevel = CheckLevelEnd = tv->tolevel; + break; + case 2: + if (m || (tv->tolevel == tv->maxtreelevel+1)) { + CheckLevel = CheckLevelEnd = tv->maxtreelevel+1; + } + else { + CheckLevel = 1; + if ((Spine[tv->maxtreelevel].part)->cells == tv->finalnumcells) { + CheckLevelEnd = tv->maxtreelevel - 1; + } + else { + CheckLevelEnd = tv->maxtreelevel; + } + } + break; + default: + break; + } + + + Adjust_Cycles(NextCand, Part, n, tv); + + while (CheckLevel <= CheckLevelEnd) { + CheckAutList = Spine[CheckLevel].liststart; + while (CheckAutList) { + if (CheckAutList->do_it && lookup(CheckAutList->stnode) && (CheckAutList != NextCand) && (CheckAutList != CurrCand)) { + if (CheckAutList->code == NextCand->code) { + SETMARK(Markers, tv->mark) + if (Part->cells == n) { + if (tv->permInd) ResetAutom(tv->permInd, n, tv); + for (i = 0; i < n; i++) { + arg = NextCand->lab[i]; + val = CheckAutList->lab[i]; + SETPAIRSAUT(arg, val) + } + } + else { + Adjust_Cycles(CheckAutList, Part, n, tv); + if (tv->permInd) ResetAutom(tv->permInd, n, tv); + SETMARK(CellMarkers1, tv->markcell1) + SETMARK(CellMarkers2, tv->markcell2) + for (i=0; i<n; i+=Part->cls[i]) { + if (Part->cls[i] == 1) { + arg = NextCand->lab[i]; + val = CheckAutList->lab[i]; + SETPAIRSAUT(arg, val) + if (tv->preprocessed && Diff[arg]) + MakeTree(arg, val, tv->input_graph, n, tv, TRUE); + } + else { + k = i; + for (j=i; j<i+Part->cls[i]; j++) { + arg = arg1 = NextCand->lab[j]; + if (CellMarkers1[arg] != tv->markcell1) { + CellMarkers1[arg] = tv->markcell1; + while ((CellMarkers2[CheckAutList->lab[k]] == tv->markcell2) && (k < i+Part->cls[i])) { + k++; + } + if (k < i+Part->cls[i]) { + val = val1 = CheckAutList->lab[k]; + CellMarkers2[val] = tv->markcell2; + SETPAIRSAUT(arg, val) + if (tv->preprocessed && Diff[arg]) + MakeTree(arg, val, tv->input_graph, n, tv, TRUE); + tmp = FirstNeighbour(arg, NextCand, Part, CellMarkers1, tv->markcell1, &arg, n); + if (tmp) { + CellMarkers1[arg] = tv->markcell1; + tmp = FirstNeighbour(val, CheckAutList, Part, CellMarkers2, tv->markcell2, &val, n); + CellMarkers2[val] = tv->markcell2; + SETPAIRSAUT(arg, val) + if (tv->preprocessed && Diff[arg]) + MakeTree(arg, val, tv->input_graph, n, tv, TRUE); + while (tmp) { + tmp = NextNeighbour(arg, NextCand, Part, CellMarkers1, tv->markcell1, &arg, n); + if (tmp) { + CellMarkers1[arg] = tv->markcell1; + tmp = NextNeighbour(val, CheckAutList, Part, CellMarkers2, tv->markcell2, &val, n); + if (tmp) { + CellMarkers2[val] = tv->markcell2; + SETPAIRSAUT(arg, val) + if (tv->preprocessed && Diff[arg]) + MakeTree(arg, val, tv->input_graph, n, tv, TRUE); + } + } + } + arg = arg1; + val = val1; + do { + tmp = NextNeighbour(arg, NextCand, Part, CellMarkers1, tv->markcell1, &arg, n); + if (tmp) { + CellMarkers1[arg] = tv->markcell1; + tmp = NextNeighbour(val, CheckAutList, Part, CellMarkers2, tv->markcell2, &val, n); + if (tmp) { + CellMarkers2[val] = tv->markcell2; + SETPAIRSAUT(arg, val) + if (tv->preprocessed && Diff[arg]) + MakeTree(arg, val, tv->input_graph, n, tv, TRUE); + } + } + } while (tmp); + } + } + } + } + } + } + } + + if (isautom_sg_pair((graph*)tv->input_graph, AUTPERM, tv->options->digraph, m, n, tv)) { + if (!findperm(gensB, AUTPERM, n)) { + if (tv->options->verbosity >= 2) tv->schreier3 -= CPUTIME; + addgenerator(&gpB, &gensB, AUTPERM, n); + if (tv->options->verbosity >= 2) tv->schreier3 += CPUTIME; + if (tv->options->verbosity >= 2) { + fprintf(outfile, "[A(%d,%d)] ", CheckLevel, CheckAutList->name); + } + tv->lev_of_lastauto = tv->tolevel; + tv->stats->numgenerators++; + orbjoin_sp_perm(tv->orbits, AUTPERM, OrbList, n, &tv->stats->numorbits); + ti->thegrouphaschanged = TRUE; + ti->identitygroup = FALSE; + if (tv->options->verbosity >= 2 && tv->options->writeautoms) { + PRINT_RETURN + } + if (tv->options->writeautoms) { + fprintf(outfile, "Gen(A) #%d: ", tv->stats->numgenerators); + writeperm(outfile, AUTPERM, tv->options->cartesian, tv->options->linelength, n); + } + if (tv->options->userautomproc) { + (*tv->options->userautomproc)(tv->stats->numgenerators, AUTPERM, n); + } + } + else { + orbjoin_sp_perm(tv->orbits, AUTPERM, OrbList, n, &tv->stats->numorbits); + if (tv->options->verbosity >= 2) { + fprintf(outfile, "[A*(%d,%d)] ", CheckLevel, CheckAutList->name); + } + } + TrieCandFrom = NULL; + TrieCheckFrom = CheckAutList->stnode; + if (CurrCand->stnode->level <= 1) { + tgt_level = CurrCand->stnode->level + 1; + while (TrieCheckFrom->level > tgt_level) { + TrieCheckFrom = TrieCheckFrom->father; + } + } + else { + if (tv->tolevel <= TrieCheckFrom->level) { + tgt_level = tv->tolevel; + while (TrieCheckFrom->level != tgt_level) { + TrieCheckFrom = TrieCheckFrom->father; + } + } + else { + TrieCandFrom = CurrCand->stnode; + tgt_level = TrieCheckFrom->level; + while (TrieCandFrom->level != tgt_level) { + TrieCandFrom = TrieCandFrom->father; + } + } + } + if (TrieCandFrom) { + while (TrieCandFrom->father != TrieCheckFrom->father) { + TrieCandFrom = TrieCandFrom->father; + TrieCheckFrom = TrieCheckFrom->father; + } + } + else { + if ((TrieCheckFrom->level > 1) && (TrieCheckFrom->father != CurrCand->stnode)) { + TrieCandFrom = CurrCand->stnode; + TrieCheckFrom = TrieCheckFrom->father; + while (TrieCandFrom->father != TrieCheckFrom->father) { + TrieCandFrom = TrieCandFrom->father; + TrieCheckFrom = TrieCheckFrom->father; + } + } + } + + while (TrieCheckFrom->goes_to) { + TrieCheckFrom = TrieCheckFrom->goes_to; + } + + for (temp=1; temp<=tv->tolevel; temp++) { + if (CheckAutList->lab[Spine[temp].tgtpos] != NextCand->lab[Spine[temp].tgtpos]) { + break; + } + } + + if (temp == tv->tolevel) { + if (TempOrbits) { + if (tv->compstage == 0) { + for (j=0; j<tv->permInd; j++) { + orbjoin_sp_pair(TempOrbits, TempOrbList, n, + PrmPairs[j].arg, PrmPairs[j].val, &numtemporbits); + } + } + else { + orbjoin(TempOrbits, AUTPERM, n); + } + } else { + orbjoin(tv->currorbit, AUTPERM, n); + } + } + + switch (tv->compstage) { + case 0: + if (tv->strategy && (tv->steps == 1)) { + RemoveFromLevel(temp, tv->maxtreelevel-1, tv->strategy, FALSE); + if (TrieCandFrom) { + TrieCheckFrom->index += TrieCandFrom->index; + TrieCandFrom->goes_to = TrieCheckFrom; + PRINT_INDEX(TrieCheckFrom,4,1) + } + else { + TrieCheckFrom->index++; + PRINT_INDEX(TrieCheckFrom,4,2) + } + NextCand->do_it = FALSE; + } + else { + if (CheckAutList->lab[Spine[temp].tgtpos] >= NextCand->lab[Spine[temp].tgtpos]) { + CheckAutList->do_it = FALSE; + if (TrieCandFrom) { + TrieCandFrom->index += TrieCheckFrom->index; + tv->newindex = 0; + TrieCheckFrom->goes_to = TrieCandFrom; + PRINT_INDEX(TrieCandFrom,4,3) + } + else { + if (CurrCand->stnode->level > 1) { + tv->newgotonode = TrieCheckFrom; + tv->newindex = TrieCheckFrom->index; + } + else { + tv->newgotonode = NULL; + tv->newindex = 0; + } + } + } + else { + if (TrieCandFrom) { + TrieCheckFrom->index += TrieCandFrom->index; + TrieCandFrom->goes_to = TrieCheckFrom; + PRINT_INDEX(TrieCheckFrom,4,4) + } + else { + TrieCheckFrom->index++; + PRINT_INDEX(TrieCheckFrom,4,5) + } + NextCand->do_it = FALSE; + } + } + break; + case 1: + TrieCheckFrom->index ++; + PRINT_INDEX(TrieCheckFrom,4,6) + tv->gotonode = TrieCheckFrom; + break; + case 2: + if (TrieCandFrom) { + TrieCheckFrom->index += TrieCandFrom->index; + TrieCandFrom->goes_to = TrieCheckFrom; + PRINT_INDEX(TrieCheckFrom,4,7) + } + else { + TrieCheckFrom->index++; + PRINT_INDEX(TrieCheckFrom,4,8) + } + if (temp == tv->maxtreelevel) { + tmp1 = TempOrbits[NextCand->lab[Spine[temp].tgtpos]]; + + if (CheckAutList->lab[Spine[temp].tgtpos] == AutomCount[1]) { + for (i=1; i<AutomCount[0]; i++) if (AutomCount[i] == tmp1) break; + if (i == AutomCount[0]) { + AutomCount[AutomCount[0]++] = TempOrbits[NextCand->lab[Spine[temp].tgtpos]]; + } + } + + } + break; + default: + break; + } + return temp; + } + } + } + CheckAutList = CheckAutList->next; + } + CheckLevel++; + } + return FALSE; +} + + +int CheckForSingAutomorphisms(Candidate *CurrCand, Partition *NextPart, Candidate *NextCand, + struct TracesVars* tv, struct TracesInfo* ti, + int m, int n) { + int i, j, temp, tmp, tmp1, result, tgt_level, numtemporbits; + TracesSpine *SpineTL; + Candidate *CheckAutList; + searchtrie *TrieCandFrom, *TrieCheckFrom; + + SpineTL = Spine+tv->tolevel; + CheckAutList = SpineTL->liststart; + tv->gotonode = NULL; + + VERB_PRINT("CFSA",3,FALSE) + + result = 0; + while (CheckAutList != NULL) { + if (CheckAutList->do_it && (CheckAutList->stnode->father == CurrCand->stnode)) { + if (CheckAutList->firstsingcode == NextCand->firstsingcode) { + if (tv->permInd) ResetAutom(tv->permInd, n, tv); + if ((tv->tolevel == 1) && (Spine[0].part->cells == 1)) tmp = 2; else tmp = tv->tolevel; + if (TreeFyTwo(tmp, CheckAutList, NextCand, NextPart, n, tv, ti)) { + if (isautom_sg((graph*)tv->input_graph, AUTPERM, tv->options->digraph, m, n)) { + if (!findperm(gensB, AUTPERM, n)) { + if (tv->options->verbosity >= 2) tv->schreier3 -= CPUTIME; + addgenerator(&gpB, &gensB, AUTPERM, n); + if (tv->options->verbosity >= 2) tv->schreier3 += CPUTIME; + result = CheckAutList->name; + if (TempOrbits) { + if (tv->compstage == 0) { + for (j=0; j<tv->permInd; j++) { + orbjoin_sp_pair(TempOrbits, TempOrbList, n, + PrmPairs[j].arg, PrmPairs[j].val, &numtemporbits); + } + } + else { + orbjoin(TempOrbits, AUTPERM, n); + } + } + tv->stats->numgenerators++; + orbjoin_sp_perm(tv->orbits, AUTPERM, OrbList, n, &tv->stats->numorbits); + + ti->thegrouphaschanged = TRUE; + ti->identitygroup = FALSE; + tv->lev_of_lastauto = tv->tolevel; + if (tv->options->verbosity >= 2) fprintf(outfile, "[a(%d)] ", CheckAutList->name); + if (tv->options->verbosity >= 2 && tv->options->writeautoms) { + PRINT_RETURN + } + if (tv->options->writeautoms) { + fprintf(outfile, "Gen(a) #%d: ", tv->stats->numgenerators); + writeperm(outfile, AUTPERM, tv->options->cartesian, tv->options->linelength, n); + } + if (tv->options->userautomproc) { + (*tv->options->userautomproc)(tv->stats->numgenerators, AUTPERM, n); + } + } + else { + if (tv->options->verbosity >= 2) { + fprintf(outfile, "[a*]"); + } + if (TempOrbits) { + if (tv->compstage == 0) { + for (j=0; j<tv->permInd; j++) { + orbjoin_sp_pair(TempOrbits, TempOrbList, n, + PrmPairs[j].arg, PrmPairs[j].val, &numtemporbits); + } + } + else { + orbjoin(TempOrbits, AUTPERM, n); + } + } + else { + orbjoin(tv->currorbit, AUTPERM, n); + } + orbjoin_sp_perm(tv->orbits, AUTPERM, OrbList, n, &tv->stats->numorbits); + + result = -CheckAutList->name; + } + + TrieCandFrom = NULL; + TrieCheckFrom = CheckAutList->stnode; + if (CurrCand->stnode->level <= 1) { + tgt_level = CurrCand->stnode->level + 1; + while (TrieCheckFrom->level > tgt_level) { + TrieCheckFrom = TrieCheckFrom->father; + } + } + else { + if (tv->tolevel <= TrieCheckFrom->level) { + tgt_level = tv->tolevel; + while (TrieCheckFrom->level != tgt_level) { + TrieCheckFrom = TrieCheckFrom->father; + } + } + else { + TrieCandFrom = CurrCand->stnode; + tgt_level = TrieCheckFrom->level; + while (TrieCandFrom->level != tgt_level) { + TrieCandFrom = TrieCandFrom->father; + } + } + } + if (TrieCandFrom) { + while (TrieCandFrom->father != TrieCheckFrom->father) { + TrieCandFrom = TrieCandFrom->father; + TrieCheckFrom = TrieCheckFrom->father; + } + } + else { + if ((TrieCheckFrom->level > 1) && (TrieCheckFrom->father != CurrCand->stnode)) { + TrieCandFrom = CurrCand->stnode; + TrieCheckFrom = TrieCheckFrom->father; + while (TrieCandFrom->father != TrieCheckFrom->father) { + TrieCandFrom = TrieCandFrom->father; + TrieCheckFrom = TrieCheckFrom->father; + } + } + } + + while (TrieCheckFrom->goes_to) { + TrieCheckFrom = TrieCheckFrom->goes_to; + } + + for (temp=1; temp<=tv->tolevel; temp++) { + if (CheckAutList->lab[Spine[temp].tgtpos] != NextCand->lab[Spine[temp].tgtpos]) { + break; + } + } + + switch (tv->compstage) { + case 0: + if (tv->strategy && (tv->steps == 1)) { + RemoveFromLevel(temp, tv->maxtreelevel-1, tv->strategy, FALSE); + if (TrieCandFrom) { + TrieCheckFrom->index += TrieCandFrom->index; + TrieCandFrom->goes_to = TrieCheckFrom; + PRINT_INDEX(TrieCheckFrom,4,9) + } + else { + TrieCheckFrom->index++; + PRINT_INDEX(TrieCheckFrom,4,10) + } + NextCand->do_it = FALSE; + } + else { + if (CheckAutList->lab[Spine[temp].tgtpos] >= NextCand->lab[Spine[temp].tgtpos]) { + CheckAutList->do_it = FALSE; + if (TrieCandFrom) { + TrieCandFrom->index += TrieCheckFrom->index; + tv->newindex = 0; + TrieCheckFrom->goes_to = TrieCandFrom; + PRINT_INDEX(TrieCandFrom,4,11) + } + else { + if (CurrCand->stnode->level > 1) { + tv->newgotonode = TrieCheckFrom; + tv->newindex = TrieCheckFrom->index; + } + else { + tv->newgotonode = NULL; + tv->newindex = 0; + } + } + } + else { + if (TrieCandFrom) { + TrieCheckFrom->index += TrieCandFrom->index; + TrieCandFrom->goes_to = TrieCheckFrom; + PRINT_INDEX(TrieCheckFrom,4,12) + } + else { + TrieCheckFrom->index++; + PRINT_INDEX(TrieCheckFrom,4,13) + } + NextCand->do_it = FALSE; + } + } + break; + case 1: + TrieCheckFrom->index++; + PRINT_INDEX(TrieCheckFrom,4,14) + tv->gotonode = TrieCheckFrom; + break; + case 2: + if (TrieCandFrom) { + TrieCheckFrom->index += TrieCandFrom->index; + TrieCandFrom->goes_to = TrieCheckFrom; + PRINT_INDEX(TrieCheckFrom,4,15) + } + else { + TrieCheckFrom->index++; + PRINT_INDEX(TrieCheckFrom,4,16) + } + if ((temp == tv->maxtreelevel) && (CheckAutList->lab[Spine[temp].tgtpos] == AutomCount[1])) { + tmp1 = TempOrbits[NextCand->lab[Spine[temp].tgtpos]]; + for (i=1; i<AutomCount[0]; i++) if (AutomCount[i] == tmp1) break; + if (i == AutomCount[0]) { + AutomCount[AutomCount[0]++] = TempOrbits[NextCand->lab[Spine[temp].tgtpos]]; + } + } + break; + default: + break; + } + return result; + } + } + } + } + CheckAutList = CheckAutList->next; + } + return result; +} + +int CheckForMatching(Candidate *CurrCand, Candidate *NextCand, Partition *Part, struct TracesVars* tv, struct TracesInfo* ti, int m, int n) { + int i, j, vtx, vtx1, temp, tmp1, tgt_level, numtemporbits, pos; + TracesSpine *SpineTL; + Candidate *CheckAutList; + int *cls; + searchtrie *TrieCandFrom, *TrieCheckFrom; + boolean CodeVerify; + + VERB_PRINT("CFM",3,FALSE) + + SpineTL = Spine+tv->tolevel; + CheckAutList = SpineTL->liststart; + cls = Part->cls; + numtemporbits = 0; + tv->gotonode = NULL; + + while (CheckAutList != NULL) { + if (CheckAutList->do_it && (CheckAutList->singcode == NextCand->singcode)) { + TrieCheckFrom = CheckAutList->stnode->father; + TrieCandFrom = CurrCand->stnode; + while (TrieCandFrom != TrieCheckFrom) { + TrieCandFrom = TrieCandFrom->father; + TrieCheckFrom = TrieCheckFrom->father; + } + + if (tv->permInd) ResetAutom(tv->permInd, n, tv); + + CodeVerify = TRUE; + for (i=Spine[TrieCheckFrom->level+1].singstart; i<SpineTL->singend; i++) { + Markers[NextCand->lab[Singletons[i]]] = tv->mark; + } + for (i=Spine[TrieCheckFrom->level+1].singstart; i<SpineTL->singend; i++) { + pos = Singletons[i]; + vtx1 = CheckAutList->lab[pos]; + if (Markers[vtx1] != tv->mark) { + CodeVerify = FALSE; + break; + } + vtx = NextCand->lab[pos]; + SETPAIRSAUT(vtx, vtx1) + + if (tv->preprocessed && Diff[vtx1]) { + MakeTree(vtx, vtx1, tv->input_graph, n, tv, TRUE); + } + } + tv->conta7++; + + if (CodeVerify) { + if (isautom_sg_pair((graph*)tv->input_graph, AUTPERM, tv->options->digraph, m, n, tv)) { + + if (!findperm(gensB, AUTPERM, n)) { + if (tv->options->verbosity >= 2) tv->schreier3 -= CPUTIME; + if (tv->options->generators) addpermutation(&gensB, AUTPERM, n); + + if (tv->options->verbosity >= 2) tv->schreier3 += CPUTIME; + + if (CheckAutList->stnode->father == CurrCand->stnode) { + if (TempOrbits) { + if (tv->compstage == 0) { + for (j=0; j<tv->permInd; j++) { + orbjoin_sp_pair(TempOrbits, TempOrbList, n, PrmPairs[j].arg, PrmPairs[j].val, &numtemporbits); + } + } + else { + orbjoin(TempOrbits, AUTPERM, n); + } + } else { + orbjoin(tv->currorbit, AUTPERM, n); + } + } + + tv->stats->numgenerators++; + + for (j=0; j<tv->permInd; j++) { + orbjoin_sp_pair(tv->orbits, OrbList, n, PrmPairs[j].arg, PrmPairs[j].val, &tv->stats->numorbits); + } + + ti->thegrouphaschanged = TRUE; + ti->first_matching = TRUE; + tv->lev_of_lastauto = tv->tolevel; + if (tv->options->verbosity >= 2) fprintf(outfile, "[M(%d)] ", CheckAutList->name); + if (tv->options->verbosity >= 2 && tv->options->writeautoms) { + PRINT_RETURN + } + if (tv->options->writeautoms) { + fprintf(outfile, "Gen(M) #%d: ", tv->stats->numgenerators); + writeperm(outfile, AUTPERM, tv->options->cartesian, tv->options->linelength, n); + } + if (tv->options->userautomproc) { + (*tv->options->userautomproc)(tv->stats->numgenerators, AUTPERM, n); + } + + } + else { + if (tv->options->verbosity >= 2) { + fprintf(outfile, "[M*]"); + } + if (CheckAutList->stnode->father == CurrCand->stnode) { + if (TempOrbits) { + if (tv->compstage == 0) { + for (j=0; j<tv->permInd; j++) { + orbjoin_sp_pair(TempOrbits, TempOrbList, n, PrmPairs[j].arg, PrmPairs[j].val, &numtemporbits); + } + } + else { + orbjoin(TempOrbits, AUTPERM, n); + } + } else { + orbjoin(tv->currorbit, AUTPERM, n); + } + } + + for (j=0; j<tv->permInd; j++) { + orbjoin_sp_pair(tv->orbits, OrbList, n, PrmPairs[j].arg, PrmPairs[j].val, &tv->stats->numorbits); + } + } + + TrieCandFrom = NULL; + TrieCheckFrom = CheckAutList->stnode; + if (CurrCand->stnode->level <= 1) { + tgt_level = CurrCand->stnode->level + 1; + while (TrieCheckFrom->level > tgt_level) { + TrieCheckFrom = TrieCheckFrom->father; + } + } + else { + if (tv->tolevel <= TrieCheckFrom->level) { + tgt_level = tv->tolevel; + while (TrieCheckFrom->level != tgt_level) { + TrieCheckFrom = TrieCheckFrom->father; + } + } + else { + TrieCandFrom = CurrCand->stnode; + tgt_level = TrieCheckFrom->level; + while (TrieCandFrom->level != tgt_level) { + TrieCandFrom = TrieCandFrom->father; + } + } + } + if (TrieCandFrom) { + while (TrieCandFrom->father != TrieCheckFrom->father) { + TrieCandFrom = TrieCandFrom->father; + TrieCheckFrom = TrieCheckFrom->father; + } + } + else { + if ((TrieCheckFrom->level > 1) && (TrieCheckFrom->father != CurrCand->stnode)) { + TrieCandFrom = CurrCand->stnode; + TrieCheckFrom = TrieCheckFrom->father; + while (TrieCandFrom->father != TrieCheckFrom->father) { + TrieCandFrom = TrieCandFrom->father; + TrieCheckFrom = TrieCheckFrom->father; + } + } + } + + while (TrieCheckFrom->goes_to) { + TrieCheckFrom = TrieCheckFrom->goes_to; + } + + for (temp=1; temp<=tv->tolevel; temp++) { + if (CheckAutList->lab[Spine[temp].tgtpos] != NextCand->lab[Spine[temp].tgtpos]) { + break; + } + } + switch (tv->compstage) { + case 0: + if (tv->strategy && (tv->steps == 1)) { + RemoveFromLevel(temp, tv->maxtreelevel-1, tv->strategy, FALSE); + if (TrieCandFrom) { + TrieCheckFrom->index += TrieCandFrom->index; + TrieCandFrom->goes_to = TrieCheckFrom; + PRINT_INDEX(TrieCheckFrom,4,17) + } + else { + TrieCheckFrom->index++; + PRINT_INDEX(TrieCheckFrom,4,18) + } + NextCand->do_it = FALSE; + } + else { + if (CheckAutList->lab[Spine[temp].tgtpos] >= NextCand->lab[Spine[temp].tgtpos]) { + CheckAutList->do_it = FALSE; + if (TrieCandFrom) { + TrieCandFrom->index += TrieCheckFrom->index; + tv->newindex = 0; + TrieCheckFrom->goes_to = TrieCandFrom; + PRINT_INDEX(TrieCandFrom,4,19) + } + else { + if (CurrCand->stnode->level > 1) { + tv->newgotonode = TrieCheckFrom; + tv->newindex = TrieCheckFrom->index; + } + else { + tv->newgotonode = NULL; + tv->newindex = 0; + } + } + } + else { + if (TrieCandFrom) { + TrieCheckFrom->index += TrieCandFrom->index; + TrieCandFrom->goes_to = TrieCheckFrom; + PRINT_INDEX(TrieCheckFrom,4,20) + } + else { + TrieCheckFrom->index++; + PRINT_INDEX(TrieCheckFrom,4,21) + } + NextCand->do_it = FALSE; + } + } + break; + case 1: + TrieCheckFrom->index ++; + tv->gotonode = TrieCheckFrom; + PRINT_INDEX(TrieCheckFrom,4,22) + break; + case 2: + if (TrieCandFrom) { + TrieCheckFrom->index += TrieCandFrom->index; + TrieCandFrom->goes_to = TrieCheckFrom; + PRINT_INDEX(TrieCheckFrom,4,23) + } + else { + TrieCheckFrom->index++; + PRINT_INDEX(TrieCheckFrom,4,24) + } + if ((temp == tv->maxtreelevel) && (CheckAutList->lab[Spine[temp].tgtpos] == AutomCount[1])) { + tmp1 = TempOrbits[NextCand->lab[Spine[temp].tgtpos]]; + for (i=1; i<AutomCount[0]; i++) if (AutomCount[i] == tmp1) break; + if (i == AutomCount[0]) { + AutomCount[AutomCount[0]++] = TempOrbits[NextCand->lab[Spine[temp].tgtpos]]; + } + } + break; + default: + break; + } + return temp; + } + } + } + CheckAutList = CheckAutList->next; + } + return FALSE; +} + +void CodeClassify(int Level, int code, int cell) { + switch (EPCodes[Level].info) { + case 0: + EPCodes[Level].code = code; + EPCodes[Level].cell = cell; + EPCodes[Level].info = 1; + break; + case 1: + if (EPCodes[Level].cell != cell) { + EPCodes[Level].info = 3; + } else { + if (EPCodes[Level].code != code) { + EPCodes[Level].info = 2; + } + } + break; + case 2: + if (EPCodes[Level].cell != cell) { + EPCodes[Level].info = 3; + } + break; + default: + break; + } +} + +void Complete(sparsegraph *sg_orig, Candidate *Cand, Partition *Part, int cell, TracesVars *tv, + double *grpsize1, int *grpsize2, permnode **ring, int n) { + int i, j, k; + int arg, val; + int numtemporbits; + k = cell + Part->cls[cell]; + + if (tv->permInd) ResetAutom(tv->permInd, n, tv); + for (i = cell; i < k; i++) { + tv->currorbit[Cand->lab[i]] = Cand->lab[k]; + arg = Cand->lab[i]; + val = Cand->lab[i+1]; + SETPAIRSAUTANDTREE(arg, val) + } + arg = Cand->lab[i]; + val = Cand->lab[cell]; + SETPAIRSAUTANDTREE(arg, val) + SPECIALGENERATORS + + if (Part->cls[cell] > 1) { + if (tv->permInd) ResetAutom(tv->permInd, n, tv); + arg = Cand->lab[cell]; + val = Cand->lab[cell+1]; + SETPAIRSAUTANDTREE(arg, val) + arg = Cand->lab[cell+1]; + val = Cand->lab[cell]; + SETPAIRSAUTANDTREE(arg, val) + SPECIALGENERATORS + } +} + +int CompStage0(Partition *CurrPart, Partition *NextPart, Candidate *CurrCand, Candidate *NextCand, + int m, int n, struct TracesVars* tv, struct TracesInfo *ti) { + int i, j, i1, j2, k, cu, cu1, num_indv; + int temp, tmp, auxcode, search_vtx, gom_level; + boolean closeloop, firstsing, has_nexttcell; + Candidate *SpTLliststart, *AuxCand; + searchtrie *TreeNode, *TreeNode1, *TreeNode2; + +#ifdef NAUTY_IN_MAGMA + if (main_seen_interrupt) return NAUTY_KILLED; +#else + if (nauty_kill_request) return NAUTY_KILLED; +#endif + + PRINT_FROM_VERB(4,tv->tolevel) + if (TargetCell(CurrCand, CurrPart, n, tv, tv->tolevel)) { + ++tv->tolevel; + SpineTL = Spine+tv->tolevel; + SpineTL->tgtcell = tv->tcell; + SpineTL->tgtsize = CurrPart->cls[tv->tcell]; + SpineTL->tgtend = tv->tcell+SpineTL->tgtsize; + SpineTL->tgtpos = SpineTL->tgtend - 1; + } + else { + tv->finalnumcells = CurrPart->cells; + ti->thereisnextlevel = SelectNextLevel(n, tv, ti); + return 0; + } + + tv->newgotonode = NULL; + + /* CANDIDATE */ + temp = CurrCand->lab[Spine[1].tgtpos]; + k = SpineTL->tgtend; + + TreeNode = CurrCand->stnode; + while (TreeNode) { + if (TreeNode->goes_to) { + CurrCand->do_it = FALSE; + break; + } + TreeNode = TreeNode->father; + } + + if (CurrCand->do_it) { + if ((tv->orbits[temp] == temp) || tv->tolevel == 1) { + ti->minimalinorbits = TRUE; + + if ((tv->group_level >= tv->tolevel) && (FixedBase(fix, tv, CurrCand, 0, tv->fromlevel))) { + tv->nfix = tv->fromlevel; + tv->currorbit = findcurrorbits(gpB, tv->nfix); + } else { + if ((!ti->identitygroup) && + (((Spine[tv->fromlevel].liststart != Spine[tv->fromlevel].listend) + && (CurrPart->cls[tv->tcell] > 10)) + || tv->strategy + || (tv->expathlength <=10) + )) { + + TempOrbits = NULL; + tv->samepref = FixBase(fix, tv, CurrCand, 0, tv->fromlevel); + if ((tv->samepref != tv->nfix) || ti->thegrouphaschanged) { + if (tv->options->verbosity >= 2) tv->schreier1 -= CPUTIME; + gom_level = getorbitsmin(fix, tv->nfix, gpB, &gensB, &tv->currorbit, CurrCand->lab+tv->tcell, CurrPart->cls[tv->tcell], n, TRUE); + if (tv->options->verbosity >= 2) tv->schreier1 += CPUTIME; + ti->thegrouphaschanged = FALSE; + + if (gom_level < tv->nfix) { + PRINT_NOTMIN_VERB(4) + + TreeNode = CurrCand->stnode; + j2 = CurrCand->lab[Spine[gom_level+1].tgtpos]; + i1 = tv->currorbit[j2]; + for (j=0; j < tv->nfix - gom_level; j++) { + TreeNode = TreeNode->father; + } + TreeNode1 = TreeNode->first_child; + while (TreeNode1) { + if (TreeNode1->vtx == i1) { + break; + } + TreeNode1 = TreeNode1->next_sibling; + } + if (TreeNode1) { + while (TreeNode1->goes_to) { + TreeNode1 = TreeNode1->goes_to; + } + TreeNode2 = TreeNode1->next_sibling; + while (TreeNode2->vtx != j2) { + TreeNode2 = TreeNode2->next_sibling; + } + TreeNode1->index += TreeNode2->index; + TreeNode2->goes_to = TreeNode1; + PRINT_INDEX(TreeNode1,4,25) + + ti->minimalinorbits = FALSE; + } + else { + tv->currorbit = getorbits(fix, tv->nfix, gpB, &gensB, n); + } + } + } + else { + tv->currorbit = findcurrorbits(gpB, tv->nfix); + } + } + else { + TempOrbits = WorkArray1; + memcpy(TempOrbits, IDENTITY_PERM, n*sizeof(int)); + memcpy(TempOrbList, IDENTITY_PERM, n*sizeof(int)); + + tv->conta1++; + tv->currorbit = TempOrbits; + } + } + if (ti->minimalinorbits) { + memcpy(NextCand->lab, CurrCand->lab, n*sizeof(int)); + memcpy(NextCand->invlab, CurrCand->invlab, n*sizeof(int)); + tv->conta2++; + auxcode = CurrCand->code; + SpineTL->trcstart = CurrPart->cells; + TheTrace[SpineTL->trcstart] = SpineTL->tgtpos; + if (!CurrCand->sortedlab) { + quickSort(CurrCand->lab+tv->tcell, CurrPart->cls[tv->tcell]); + for (i=tv->tcell; i<tv->tcell+CurrPart->cls[tv->tcell]; i++) { + CurrCand->invlab[CurrCand->lab[i]] = i; + } + CurrCand->sortedlab = TRUE; + } + + tv->indivstart = tv->tcell+CurrCand->indnum; + tv->indivend = tv->indivstart+tv->steps; + if (tv->indivend > SpineTL->tgtend) { + tv->indivend = SpineTL->tgtend; + } + + temp = CurrCand->lab[tv->indivstart]; + for (k = tv->indivstart; k < tv->indivend; k++) { + CurrCand->indnum++; + NextCand->singcode = CurrCand->singcode; + NextCand->vertex = CurrCand->lab[k]; + NextCand->name = ++tv->name; + if (NextCand->name == (NAUTY_INFINITY-2)) { + NextCand->name = tv->name = 1; + } + + PRINT_INDIV_VERB(4,tv->tolevel) + if (tv->currorbit[NextCand->vertex] != NextCand->vertex) { + PRINT_SKIPPED_VERB(4) + + search_vtx = tv->currorbit[NextCand->vertex]; + TreeNode = CurrCand->stnode; + if (TreeNode->first_child) { + TreeNode = TreeNode->first_child; + while (TreeNode) { + if (TreeNode->vtx == search_vtx) { + break; + } + TreeNode = TreeNode->next_sibling; + } + if (TreeNode) { + while (TreeNode->goes_to) { + TreeNode = TreeNode->goes_to; + } + TreeNode->index++; + PRINT_INDEX(TreeNode,4,26) + continue; + } + } + } + else { + PRINT_REFINE_VERB(4,'a') + + memcpy(NextPart->cls, CurrPart->cls, n*sizeof(int)); + memcpy(NextPart->inv, CurrPart->inv, n*sizeof(int)); + + tv->conta3++; + + if (NextPart->cls[tv->tcell] == 2) { + num_indv = 2; + NextCand->singcode = MASHCOMM(NextCand->singcode, CurrCand->lab[tv->tcell]); + NextCand->singcode = MASHCOMM(NextCand->singcode, CurrCand->lab[tv->tcell+1]); + if (SpineTL->singstart == SpineTL->singend) { + Singletons[SpineTL->singend++] = tv->tcell; + Singletons[SpineTL->singend++] = tv->tcell+1; + } + } + else { + num_indv = 1; + NextCand->singcode = MASHCOMM(NextCand->singcode, NextCand->vertex); + if (SpineTL->singstart == SpineTL->singend) { + Singletons[SpineTL->singend++] = tv->tcell + NextPart->cls[tv->tcell] - 1; + } + } + + Individualize(NextPart, NextCand, NextCand->vertex, tv->tcell, CurrPart->cells, SpineTL->tgtpos); + tv->stats->numnodes++; + tv->answ = traces_refine(NextCand, + n, + NextPart, tv, ti, num_indv, TRUE); + switch (tv->answ) { + case 0: /* Interrupted refinement: do not add to the list */ + tv->stats->interrupted++; + SpineTL->levelcounter++; + break; + case 1 : /* The same trace has been found once more : add to the list */ + SpineTL->levelcounter++; + + NextCand->do_it = TRUE; + if (tv->options->verbosity >= 2) PRINT_CANDIDATE(NextCand, tv->tolevel); + + tv->tolevel_tl = tv->tolevel; + NextCand->pathsingcode = NextCand->singcode; + NextCand->firstsingcode = 0; + + if (tv->steps > 1) { + if (tv->fromlevel <= tv->lev_of_lastauto) { + closeloop = CheckForMatching(CurrCand, NextCand, NextPart, tv, ti, m, n); + } + if (NextCand->do_it) { + firstsing = TRUE; + + /* EXPERIMENTAL PATH */ + if (NextPart->cells != tv->finalnumcells) { /* 160712 */ + if (tv->options->verbosity >= 2) tv->expaths -= CPUTIME; + while (NextPart->cells < n) { + if (firstsing && BreakSteps[tv->tolevel]) { + firstsing = FALSE; + NextCand->firstsingcode = NextCand->pathsingcode; + if (CheckForSingAutomorphisms(CurrCand, NextPart, NextCand, tv, ti, m, n)) + if (!NextCand->do_it) { + break; + } + } + + has_nexttcell = TargetCellExpPath(NextCand, NextPart, tv); + + if (!has_nexttcell) { + NextCand->firstsingcode = NextCand->pathsingcode; + if (tv->options->verbosity >= 2) { + if (tv->tolevel_tl-tv->tolevel >= 6) { + PRINT_EXPPATHSTEP(NextCand, TRUE) + } + else { + fprintf(outfile, "(%d) ", tv->tolevel_tl); + } + } + break; + } + ExperimentalStep(NextPart, NextCand, tv, ti, m, n); + if (NextPart->cells == n) { + has_nexttcell = FALSE; + } + PRINT_EXPPATHSTEP(NextCand, TRUE) + } + if (tv->options->verbosity >= 2) tv->expaths += CPUTIME; + } + } + else { + if (closeloop < tv->tolevel) k = SpineTL->tgtend; + PRINT_RETURN + break; + } + + if (!tv->strategy && !tv->options->getcanon && (tv->tolevel_tl == tv->tolevel + 1) && ((NextPart->cells > tv->finalnumcells) || (NextPart->cells == n))) { /* 160717 */ + tv->levelfromCS0 = tv->tolevel; + tv->maxtreelevel = tv->tolevel_tl; + tv->finalnumcells = NextPart->cells; + if (tv->tolevel == 1) { + tv->newst_stage1 = searchtrie_make(CurrCand, NextCand, n, tv); + EXITFROMSTAGE0EXPATH1 + } + else { + temp = 0; + for (i=0; i<tv->tolevel; i++) { + temp += Spine[i].listcounter; + } + if (temp > 5) { + tv->newst_stage1 = searchtrie_make(CurrCand, NextCand, n, tv); + EXITFROMSTAGE0EXPATH1 + } + } + } + + /* ANY AUTOMORPHISM? */ + if (tv->options->verbosity >= 2) tv->autchk -= CPUTIME; + tv->newindex = 0; + if (NextCand->do_it) { + closeloop = CheckForAutomorphisms(CurrCand, NextCand, tv, ti, m, n, NextPart); + if (!NextCand->do_it && closeloop < tv->tolevel) k = SpineTL->tgtend; + } + if (tv->options->verbosity >= 2) tv->autchk += CPUTIME; + + if (NextCand->do_it) { + ADDTONEXTLEVEL; + SpineTL->keptcounter++; + searchtrie_make(CurrCand, SpineTL->listend, n, tv); + } + } + else { + if (BreakSteps[tv->tolevel]) { + NextCand->firstsingcode = NextCand->pathsingcode; + if (CheckForSingAutomorphisms(CurrCand, NextPart, NextCand, tv, ti, m, n)) + if (!NextCand->do_it) { + PRINT_RETURN + break; + } + } + + /* ANY AUTOMORPHISM? */ + if (tv->options->verbosity >= 2) tv->autchk -= CPUTIME; + tv->newindex = 0; + if (NextCand->do_it) { + closeloop = CheckForAutomorphisms(CurrCand, NextCand, tv, ti, m, n, NextPart); + if (!NextCand->do_it && closeloop < tv->tolevel) k = SpineTL->tgtend; + } + if (tv->options->verbosity >= 2) tv->autchk += CPUTIME; + + if (NextCand->do_it) { + ADDTONEXTLEVEL; + SpineTL->keptcounter++; + searchtrie_make(CurrCand, SpineTL->listend, n, tv); + } + } + PRINT_RETURN + break; + case 2 : /* Delete the old list and start a new one: a better trace has been found */ + + if (NextPart->cells > tv->finalnumcells) { + tv->finalnumcells = NextPart->cells; + } + tv->tolevel_tl = tv->tolevel; + has_nexttcell = FALSE; + if (NextPart->cells == n) { + tv->stats->canupdates++; + if (tv->options->usercanonproc != NULL) + { + (*tv->options->usercanonproc)((graph*)tv->input_graph, NextCand->lab, (graph*)tv->cangraph, tv->stats->canupdates, NextCand->code, m, n); + } + } + + if (tv->tolevel > tv->treedepth) { + tv->treedepth = tv->tolevel; + if (tv->strategy) { + SpineTL->part = NewPartition(n); + } + else { + NewPartSpine(tv->tolevel,n); + } + } + + if (!tv->strategy && (tv->tolevel > 1) && !SpineTL->liststart) { + /* First Candidate at current level */ + tv->maxtreelevel = tv->tolevel; + + SpineTL->liststart = NewCandidate(n, &GarbList, TRUE); + SpineTL->listend = SpineTL->liststart; + + tv->conta0++; + CopyCand(SpineTL->liststart, NextCand, n, TEMPLAB, TEMPINVLAB); + if (NextPart->cells < tv->finalnumcells) SpineTL->liststart->code = auxcode; + COPYPART(SpineTL->part, NextPart); + tv->newindex = 0; + tv->newst_stage1 = searchtrie_make(CurrCand, SpineTL->listend, n, tv); + + SpineTL->listcounter = 1; + SpTLliststart = SpineTL->liststart; + + i = tv->tolevel; + if (tv->brkstpcount) { + while ((i<n) && !BreakSteps[i]) { + i++; + } + if (i<n) SpineTL->liststart->firstsingcode = Spine[i].singcode; + } + + SpineTL->updates = 1; + SpineTL->levelcounter = 1; + SpineTL->keptcounter = 1; + + PRINT_LINE_PLUS(tv->fromlevel) + + if (tv->options->verbosity >= 2) PRINT_CANDIDATE(SpineTL->liststart, tv->tolevel); + PRINT_RETURN; + + if (!tv->strategy && !tv->options->getcanon && (tv->tolevel+1 == tv->firstpathlength) && ((NextPart->cells > tv->finalnumcells) || (NextPart->cells == n))) { + tv->finalnumcells = NextPart->cells; + if ((tv->tolevel == 1) && (CurrPart->cls[tv->tcell] > 5)) { + EXITFROMSTAGE0EXPATH2; + } + else { + temp = 0; + for (i=0; i<tv->tolevel; i++) { + temp += Spine[i].listcounter; + } + if (temp > 5) { + EXITFROMSTAGE0EXPATH2; + } + } + } + } + else { + memset(WorkArray, 0, n*sizeof(int)); + + tv->lastcell = tv->lastlev = -1; + has_nexttcell = TargetCellFirstPath(NextCand, NextPart, tv); + + if (!has_nexttcell) { + tv->stats->canupdates++; + if (tv->options->usercanonproc != NULL) { + (*tv->options->usercanonproc)((graph*)tv->input_graph, NextCand->lab, (graph*)tv->cangraph, tv->stats->canupdates, NextCand->code, m, n); + } + } + + tv->tcellevel = tv->maxtreelevel = tv->tolevel; + SpineTL->levelcounter++; + SpineTL->updates++; + SpineTL->keptcounter = 1; + + RemoveFromLevel(tv->tolevel, tv->treedepth, tv->strategy, TRUE); + SpineTL->liststart = NewCandidate(n, &GarbList, TRUE); + SpineTL->listend = SpineTL->liststart; + + tv->conta0++; + CopyCand(SpineTL->liststart, NextCand, n, NULL, NULL); + COPYPART(SpineTL->part, NextPart); + + tv->newindex = 0; + + tv->newst_stage1 = searchtrie_make(CurrCand, SpineTL->listend, n, tv); + + SpineTL->listcounter = 1; + SpTLliststart = SpineTL->liststart; + + SpTLliststart->pathsingcode = SpineTL->singcode = SpTLliststart->singcode; + SpTLliststart->firstsingcode = 0; + + PRINT_LINE + if (tv->options->verbosity >= 2) PRINT_CANDIDATE(SpTLliststart, tv->tolevel); + + memset(BreakSteps, 0, n*sizeof(int)); + tv->brkstpcount = 0; + + if (tv->steps > 1) { + + /* EXPERIMENTAL PATH */ + if (tv->options->verbosity >= 2) tv->expaths -= CPUTIME; + PRINTF2("CStage0 2: %d\n", tv->finalnumcells); + tv->finalnumcells = n; + + while (has_nexttcell) { + ExperimentalStep(NextPart, SpTLliststart, tv, ti, m, n); + + Spine[tv->tolevel_tl].singcode = SpTLliststart->pathsingcode; + has_nexttcell = TargetCellFirstPath(SpTLliststart, NextPart, tv); + PRINT_EXPPATHSTEP(SpTLliststart, TRUE) + } + if (NextPart->cells < n) { + PRINTF2("CStage0 3: %d\n", tv->finalnumcells); + tv->finalnumcells = minint(NextPart->cells,tv->finalnumcells); /* 160712 */ + tv->finalnumcells = NextPart->cells; + + PRINTF2("CStage0 3<: %d\n", tv->finalnumcells); + } + + PRINTF2("CS0 2?: finalnumcells: %d\n", tv->finalnumcells); + if (NextPart->cells == tv->finalnumcells) { + UPDATEMIN(tv->expathlength, tv->tolevel_tl); + } + + if (tv->options->verbosity >= 2) tv->expaths += CPUTIME; + + tv->firstpathlength = tv->tolevel_tl; + PRINT_RETURN + if (!tv->strategy && !tv->options->getcanon && (NextPart->cells == tv->finalnumcells) && (tv->tolevel_tl == tv->tolevel + 1) && ((NextPart->cells > tv->finalnumcells) || (NextPart->cells == n))) { + tv->maxtreelevel = tv->tolevel_tl; + tv->finalnumcells = NextPart->cells; + if ((tv->tolevel == 1) && (CurrPart->cls[tv->tcell] > 5)) { + EXITFROMSTAGE0EXPATH2 + } + else { + temp = 0; + for (i=0; i<tv->tolevel; i++) { + temp += Spine[i].listcounter; + } + if (temp > 5) { + EXITFROMSTAGE0EXPATH2 + } + } + } + memcpy(TEMPLAB, SpTLliststart->lab, n*sizeof(int)); + memcpy(TEMPINVLAB, SpTLliststart->invlab, n*sizeof(int)); + tv->conta5++; + } + else { + PRINT_RETURN + } + } + + break; + default: + break; + } + } + } /* end for */ + } + } + } + + /* REMOVE CURRENT CANDIDATE */ + if (SpineFL->liststart && (k >= SpineTL->tgtend)) { + SpineFL->liststart = CurrCand->next; + if (CurrCand->next == NULL) { + SpineFL->listend = NULL; + } + SpineFL->listcounter--; + CurrCand->next = GarbList; + GarbList = CurrCand; + } + ti->thereisnextlevel = SelectNextLevel(n, tv, ti); + return 0; +} + +int CompStage1(Partition *CurrPart, Partition *NextPart, Candidate *CurrCand, Candidate *NextCand, + int m, int n, + struct TracesVars* tv, struct TracesInfo *ti) { + int i, k, cu, cu1, tmp, gom_level, search_vtx, temp; + searchtrie *TreeNode, *TrieNode; + +#ifdef NAUTY_IN_MAGMA + if (main_seen_interrupt) return NAUTY_KILLED; +#else + if (nauty_kill_request) return NAUTY_KILLED; +#endif + + CurrCand->stnode = tv->newst_stage1; + + tv->tolevel++; + SpineTL = Spine+tv->tolevel; + tv->tcell = SpineTL->tgtcell; + SpineTL->levelcounter = 0; + SpineTL->keptcounter = 0; + SpineTL->updates = 1; + + + if (tv->options->verbosity >= 2) { + LINE(32, "=") + NEXTLINE + } + + memset(RefCells, 0, n*sizeof(int)); + memset(MultRefCells, 0, n*sizeof(int)); + ti->thegrouphaschanged = TRUE; + + /* CANDIDATE */ + memcpy(NextCand->lab, CurrCand->lab, n*sizeof(int)); + memcpy(NextCand->invlab, CurrCand->invlab, n*sizeof(int)); + NextCand->do_it = TRUE; + SpineTL->trcstart = CurrPart->cells; + + tv->indivstart = tv->tcell; + tv->indivend = SpineTL->tgtend; + if (TheGraph[CurrCand->lab[tv->indivstart]].d == 1) { + tv->indivstart = SpineTL->tgtend-1; + } + + FixBase(fix, tv, NextCand, 0, tv->fromlevel); + + if (!ti->identitygroup) { + if (tv->options->verbosity >= 2) tv->schreier2 -= CPUTIME; + tv->currorbit = getorbits(fix, tv->nfix, gpB, &gensB, n); + if (tv->options->verbosity >= 2) tv->schreier2 += CPUTIME; + } + else { + memcpy(tv->currorbit, IDENTITY_PERM, n*sizeof(int)); + } + + if (!CurrCand->sortedlab) { + quickSort(CurrCand->lab+tv->tcell, CurrPart->cls[tv->tcell]); + for (i=tv->tcell; i<tv->tcell+CurrPart->cls[tv->tcell]; i++) { + CurrCand->invlab[CurrCand->lab[i]] = i; + } + CurrCand->sortedlab = TRUE; + } + for (k = tv->indivstart; k < tv->indivend; k++) { + NextCand->vertex = CurrCand->lab[k]; + NextCand->name = ++tv->name; + if (NextCand->name == (NAUTY_INFINITY-2)) { + NextCand->name = tv->name = 1; + } + if (tv->currorbit[CurrCand->lab[k]] != CurrCand->lab[k]) { + search_vtx = tv->currorbit[NextCand->vertex]; + TreeNode = CurrCand->stnode; + if (TreeNode->first_child) { + TreeNode = TreeNode->first_child; + while (TreeNode) { + if (TreeNode->vtx == search_vtx) { + break; + } + TreeNode = TreeNode->next_sibling; + } + if (TreeNode) { + while (TreeNode->goes_to) { + TreeNode = TreeNode->goes_to; + } + TreeNode->index++; + PRINT_INDEX(TreeNode,4,27) + } + } + continue; + } + PRINT_REFINE_VERB(4,'b') + memcpy(NextPart->cls, CurrPart->cls, n*sizeof(int)); + memcpy(NextPart->inv, CurrPart->inv, n*sizeof(int)); + + Individualize(NextPart, NextCand, CurrCand->lab[k], tv->tcell, CurrPart->cells, SpineTL->tgtpos); + + tv->stats->numnodes++; + SpineTL->levelcounter++; + tv->tolevel_tl = tv->tolevel; + trieref = trieroot; + SpineTL->levelcounter++; + + traces_refine_maketrie(NextCand, + n, + NextPart, tv, ti); + + RefCells[CurrCand->lab[k]] = NextPart->cells; + PRINTF2("CS1 1?: finalnumcells: %d\n", tv->finalnumcells); + if ((NextPart->cells == tv->finalnumcells) || (NextPart->cells == n)) { + if (tv->options->verbosity >= 2) PRINT_CANDIDATE(NextCand, tv->tolevel); + + /* ANY AUTOMORPHISM? */ + if (tv->options->verbosity >= 2) tv->autchk -= CPUTIME; + + PRINTF2("CS1 2?: finalnumcells: %d\n", tv->finalnumcells); + CheckForAutomorphisms(CurrCand, NextCand, tv, ti, m, n, NextPart); + if (tv->options->verbosity >= 2) tv->autchk += CPUTIME; + + PRINT_RETURN + + /* ADD TO NEXT LEVEL */ + SpineTL->keptcounter++; + if (!Spine[tv->tolevel].listend) COPYPART(Spine[tv->tolevel].part, NextPart); + ADDTONEXTLEVEL; + searchtrie_make(CurrCand, SpineTL->listend, n, tv); + } + } /* end for */ + PRINTF2("CS1 3: finalnumcells: %d\n", tv->finalnumcells); + for (k = tv->indivstart; k < tv->indivend; k++) { + MultRefCells[RefCells[tv->currorbit[CurrCand->lab[k]]] % n]++; + } + + if (tv->options->verbosity >= 2) { + if (MultRefCells[0]) { + fprintf(outfile, tv->digstring, n); + fprintf(outfile, "cells: %d; ", MultRefCells[0]); + } + for (k=1; k<n; k++) { + if (MultRefCells[k]) { + fprintf(outfile, tv->digstring, k); + fprintf(outfile, "cells: %d; ", MultRefCells[k]); + } + } + NEXTLINE + + } + +#if !MAXN + DYNALLOC1(searchtrie*, RefPath, RefPath_sz, tv->tolevel, "Traces-CS1"); +#endif + + TreeNode = CurrCand->stnode; + while (TreeNode) { + RefPath[TreeNode->level] = TreeNode; + TreeNode = TreeNode->father; + } + + /* REMOVE CURRENT CANDIDATE */ + SpineFL->liststart = CurrCand->next; + if (CurrCand->next == NULL) { + SpineFL->listend = NULL; + SpineFL->listcounter = 1; + } + SpineFL->listcounter--; + CurrCand->next = GarbList; + GarbList = CurrCand; + + if (tv->options->verbosity >= 2) { + LINE(32, "=") + NEXTLINE + } + tv->compstage = 2; + tv->steps = n; + + if (tv->options->verbosity >= 2) tv->schreier1 -= CPUTIME; + + gom_level = getorbitsmin(fix, tv->nfix, gpB, &gensB, &tv->currorbit, + CurrCand->lab+tv->tcell, CurrPart->cls[tv->tcell], n, TRUE); + if (tv->options->verbosity >= 2) tv->schreier1 += CPUTIME; + ORBITSIZES + ti->thereisnextlevel = SelectNextLevel(n, tv, ti); + PRINTF2("CS1 4: finalnumcells: %d\n", tv->finalnumcells); + SpineTL->part->cells = tv->finalnumcells; + + AutomCount[0] = 2; + AutomCount[1] = CurrCand->vertex; + + return 0; +} + +int CompStage2(Partition *CurrPart, Partition *NextPart, Candidate *CurrCand, Candidate *NextCand, + int m, int n, + struct TracesVars* tv, struct TracesInfo *ti) { + int i, j, i1, j2, k, cu, cu1, vertex, gom_level; + int temp, tmp, autom; + Candidate *AuxCand; + searchtrie *TreeNode, *TreeNode1, *TreeNode2; + int *CuOrb,*AuxOrb; + boolean has_nexttcell = FALSE; + searchtrie *TrieNode; + boolean schreierwrong; + +#ifdef NAUTY_IN_MAGMA + if (main_seen_interrupt) return NAUTY_KILLED; +#else + if (nauty_kill_request) return NAUTY_KILLED; +#endif + + autom = 0; + schreierwrong = FALSE; + + TreeNode = CurrCand->stnode; + tv->cand_level = 0; + + while (TreeNode) { + if (TreeNode->goes_to) { + CurrCand->do_it = FALSE; + } + if (!tv->cand_level && TreeNode == RefPath[TreeNode->level]) { + tv->cand_level = TreeNode->level; + } + TreeNode = TreeNode->father; + } + if (tv->cand_level+1 == tv->maxtreelevel) { + ti->useTempOrbits1 = TRUE; + } + else { + ti->useTempOrbits1 = FALSE; + } + if (tv->cand_level == tv->fromlevel) { + ti->useTempOrbits2 = TRUE; + } + else { + ti->useTempOrbits2 = FALSE; + } + + PRINT_FROM_VERB(4,tv->tolevel) + + if (CurrCand->do_it) { + if (tv->tolevel == 0) { + vertex = Spine[tv->maxtreelevel+1].liststart->lab[Spine[1].tgtpos]; + k = n; + + tv->fromlevel = tv->tolevel++; + SpineFL = Spine+tv->fromlevel; + SpineTL = Spine+tv->tolevel; + tv->tcell = Spine[tv->tolevel].tgtcell; + + memcpy(NextCand->lab, CurrCand->lab, n*sizeof(int)); + memcpy(NextCand->invlab, CurrCand->invlab, n*sizeof(int)); + SpineTL->trcstart = CurrPart->cells; + TheTrace[SpineTL->trcstart] = SpineTL->tgtpos; + + tv->indivstart = tv->tcell+CurrCand->indnum; + tv->indivend = tv->indivstart+tv->steps; + if (tv->indivend > SpineTL->tgtend) { + tv->indivend = SpineTL->tgtend; + } + memset(CurrRefCells, 0, n*sizeof(int)); + ti->thegrouphaschanged = TRUE; + + if (!CurrCand->sortedlab) { + quickSort(CurrCand->lab+tv->tcell, CurrPart->cls[tv->tcell]); + for (i=tv->tcell; i<tv->tcell+CurrPart->cls[tv->tcell]; i++) { + CurrCand->invlab[CurrCand->lab[i]] = i; + } + CurrCand->sortedlab = TRUE; + } + + for (k = tv->indivstart; k < tv->indivend; k++) { + if ((tv->orbits[CurrCand->lab[k]] == CurrCand->lab[k]) && ((tv->finalnumcells < n) || (OrbSize[tv->orbits[CurrCand->lab[k]]] >= OrbSize[tv->orbits[vertex]]))) { + + CurrCand->indnum++; + NextCand->singcode = CurrCand->singcode; + NextCand->vertex = CurrCand->lab[k]; + NextCand->name = ++tv->name; + if (NextCand->name == (NAUTY_INFINITY-2)) { + NextCand->name = tv->name = 1; + } + + if (ti->thegrouphaschanged) { + if (tv->fromlevel == tv->maxtreelevel) { + CURRORBITSIZES + } + ti->thegrouphaschanged = FALSE; + } + + if (tv->currorbit[CurrCand->lab[k]] != CurrCand->lab[k]) { + continue; + } + + memcpy(NextPart->cls, CurrPart->cls, n*sizeof(int)); + memcpy(NextPart->inv, CurrPart->inv, n*sizeof(int)); + if (NextPart->cls[tv->tcell] == 2) { + NextCand->singcode = MASHCOMM(NextCand->singcode, CurrCand->lab[tv->tcell]); + NextCand->singcode = MASHCOMM(NextCand->singcode, CurrCand->lab[tv->tcell+1]); + } + else { + NextCand->singcode = MASHCOMM(NextCand->singcode, CurrCand->lab[k]+labelorg); + } + + Individualize(NextPart, NextCand, CurrCand->lab[k], tv->tcell, CurrPart->cells, SpineTL->tgtpos); + + tv->stats->numnodes++; + Spine[tv->tolevel+1].levelcounter++; + if (tv->fromlevel == tv->maxtreelevel) { + tv->tolevel_tl = tv->tolevel; + trieref = trieroot; + + tv->answ = traces_refine_comptrie(NextCand, + n, + NextPart, tv, ti); + if (tv->answ) { + if (NextPart->cells != tv->finalnumcells) { + CurrRefCells[NextPart->cells % n] += CurrOrbSize[CurrCand->lab[k]]; + if (CurrRefCells[NextPart->cells % n] > MultRefCells[NextPart->cells % n]) { + k = n; + break; + } + continue; + } + if (tv->options->verbosity >= 2) PRINT_CANDIDATE(NextCand, tv->tolevel) + } + } + else { + tv->answ = traces_refine_sametrace(NextCand, + n, + NextPart, tv, ti); + + if (tv->answ) { + if (tv->options->verbosity >= 2) PRINT_CANDIDATE(NextCand, tv->tolevel) + if (tv->tolevel == tv->maxtreelevel) { + tv->tolevel_tl = tv->tolevel; + if (tv->options->verbosity >= 2) tv->expaths -= CPUTIME; + TargetCellExpPath(NextCand, NextPart, tv); + ExperimentalStep(NextPart, NextCand, tv, ti, m, n); + PRINT_EXPPATHSTEP(NextCand, tv->answ) + PRINTF2("CS2 1?: finalnumcells: %d\n", tv->finalnumcells); + if ((NextPart->cells == tv->finalnumcells) || (NextPart->cells == n)) { + UPDATEMIN(tv->expathlength, tv->tolevel_tl); + } + + if (tv->options->verbosity >= 2) tv->expaths += CPUTIME; + if (!tv->answ) { + PRINT_RETURN + } + } + } + } + if (tv->answ) { + PRINTF2("CS2 2?: finalnumcells: %d\n", tv->finalnumcells); + if ((NextPart->cells == tv->finalnumcells) || (NextPart->cells == n)) { + if (tv->options->verbosity >= 2) tv->autchk -= CPUTIME; + temp = (tv->tolevel_tl == tv->tolevel+1); + autom = CheckForAutomorphisms(CurrCand, NextCand, + tv, ti, temp, n, NextPart); + if (tv->options->verbosity >= 2) tv->autchk += CPUTIME; + + if (ti->thegrouphaschanged) { + ORBITSIZES + } + } + PRINT_RETURN + + /* ADD TO NEXT LEVEL */ + PRINTF2_2("CS2 3?: cells: %d, finalnumcells: %d\n", NextPart->cells, tv->finalnumcells); + if ((NextPart->cells != tv->finalnumcells) || (tv->tolevel != tv->maxtreelevel) || (tv->tolevel_tl != tv->tolevel+1)) { + ADDTONEXTLEVEL; + searchtrie_make(CurrCand, SpineTL->listend, n, tv); + } + } + else { + tv->stats->interrupted++; + } + if (tv->fromlevel == tv->maxtreelevel) { + k = n; + break; + } + } + } /* end for */ + } + else { + + temp = CurrCand->lab[Spine[1].tgtpos]; + vertex = Spine[tv->maxtreelevel+1].liststart->lab[Spine[1].tgtpos]; + k = n; + + if (tv->cand_level || + ((tv->orbits[temp] == temp) && ((tv->finalnumcells < n) || (OrbSize[tv->orbits[temp]] >= OrbSize[tv->orbits[vertex]])))) { + tv->fromlevel = tv->tolevel++; + SpineFL = Spine+tv->fromlevel; + SpineTL = Spine+tv->tolevel; + tv->tcell = Spine[tv->tolevel].tgtcell; + + ti->minimalinorbits = TRUE; + + if (!ti->identitygroup) { + + if (ti->useTempOrbits1 && ti->useTempOrbits2) { + CuOrb = TempOrbits; + } + else { + FixBase(fix, tv, CurrCand, 0, tv->fromlevel); + if (ti->useTempOrbits1 && tv->fromlevel == tv->maxtreelevel) { + tv->currorbit = getorbits(fix, tv->nfix, gpB, &gensB, n); + CuOrb = tv->currorbit; + } + else { + if (tv->options->verbosity >= 2) tv->schreier1 -= CPUTIME; + + gom_level = getorbitsmin(fix, tv->nfix, gpB, &gensB, &tv->currorbit, + CurrCand->lab+tv->tcell, CurrPart->cls[tv->tcell], n, TRUE); + if (tv->options->verbosity >= 2) tv->schreier1 += CPUTIME; + + CuOrb = tv->currorbit; + if (gom_level < tv->nfix) { + PRINT_NOTMIN_VERB(4) + if (ti->useTempOrbits1) { + for (i=1; i<AutomCount[0]; i++) if (AutomCount[i] == CuOrb[tv->currorbit[CurrCand->vertex]]) break; + if (i < AutomCount[0]) { + AutomCount[AutomCount[0]++] = CurrCand->vertex; + } + ti->minimalinorbits = FALSE; + } + else { + TreeNode = CurrCand->stnode; + j2 = CurrCand->lab[Spine[gom_level+1].tgtpos]; + i1 = tv->currorbit[j2]; + for (j=0; j < tv->nfix - gom_level; j++) { + TreeNode = TreeNode->father; + } + TreeNode1 = TreeNode->first_child; + while (TreeNode1) { + if (TreeNode1->vtx == i1) { + break; + } + TreeNode1 = TreeNode1->next_sibling; + } + schreierwrong = FALSE; + if (TreeNode1) { + while (TreeNode1->goes_to) { + TreeNode1 = TreeNode1->goes_to; + } + TreeNode2 = TreeNode->first_child; + while (TreeNode2->vtx != j2) { + TreeNode2 = TreeNode2->next_sibling; + } + + TreeNode1->index += TreeNode2->index; + TreeNode2->goes_to = TreeNode1; + PRINT_INDEX(TreeNode1,4,28) + PRINT_INDEX(TreeNode2,4,29) + ti->minimalinorbits = FALSE; + } + else { + tv->currorbit = getorbits(fix, tv->nfix, gpB, &gensB, n); + schreierwrong = TRUE; + } + } + } + } + } + ti->thegrouphaschanged = FALSE; + } + else { + CuOrb = IDENTITY_PERM; + } + + if (ti->minimalinorbits) { + memcpy(NextCand->lab, CurrCand->lab, n*sizeof(int)); + memcpy(NextCand->invlab, CurrCand->invlab, n*sizeof(int)); + SpineTL->trcstart = CurrPart->cells; + TheTrace[SpineTL->trcstart] = SpineTL->tgtpos; + + tv->indivstart = tv->tcell+CurrCand->indnum; + tv->indivend = tv->indivstart+tv->steps; + if (tv->indivend > SpineTL->tgtend) { + tv->indivend = SpineTL->tgtend; + } + memset(CurrRefCells, 0, n*sizeof(int)); + ti->thegrouphaschanged = TRUE; + + if (!CurrCand->sortedlab) { + quickSort(CurrCand->lab+tv->tcell, CurrPart->cls[tv->tcell]); + for (i=tv->tcell; i<tv->tcell+CurrPart->cls[tv->tcell]; i++) { + CurrCand->invlab[CurrCand->lab[i]] = i; + } + CurrCand->sortedlab = TRUE; + } + + for (k = tv->indivstart; k < tv->indivend; k++) { + CurrCand->indnum++; + NextCand->singcode = CurrCand->singcode; + NextCand->vertex = CurrCand->lab[k]; + NextCand->name = ++tv->name; + if (NextCand->name == (NAUTY_INFINITY-2)) { + NextCand->name = tv->name = 1; + } + + if (ti->thegrouphaschanged) { + if (tv->fromlevel == tv->maxtreelevel) { + CURRORBITSIZES + } + ti->thegrouphaschanged = FALSE; + } + + if (!schreierwrong) { + if (CuOrb[CurrCand->lab[k]] != CurrCand->lab[k]) { + continue; + } + } + + memcpy(NextPart->cls, CurrPart->cls, n*sizeof(int)); + memcpy(NextPart->inv, CurrPart->inv, n*sizeof(int)); + if (NextPart->cls[tv->tcell] == 2) { + NextCand->singcode = MASHCOMM(NextCand->singcode, CurrCand->lab[tv->tcell]); + NextCand->singcode = MASHCOMM(NextCand->singcode, CurrCand->lab[tv->tcell+1]); + } + else { + NextCand->singcode = MASHCOMM(NextCand->singcode, CurrCand->lab[k]); + } + + Individualize(NextPart, NextCand, CurrCand->lab[k], tv->tcell, CurrPart->cells, SpineTL->tgtpos); + + tv->stats->numnodes++; + Spine[tv->tolevel+1].levelcounter++; + if (tv->fromlevel == tv->maxtreelevel) { + tv->tolevel_tl = tv->tolevel; + trieref = trieroot; + + tv->answ = traces_refine_comptrie(NextCand, + n, + NextPart, tv, ti); + + if (tv->answ) { + PRINTF2("CS2 4?: finalnumcells: %d\n", tv->finalnumcells); + if (NextPart->cells != tv->finalnumcells) { + CurrRefCells[NextPart->cells % n] += CurrOrbSize[CurrCand->lab[k]]; + if (CurrRefCells[NextPart->cells % n] > MultRefCells[NextPart->cells % n]) { + k = n; + break; + } + continue; + } + if (tv->options->verbosity >= 2) PRINT_CANDIDATE(NextCand, tv->tolevel); + } + } + else + { + tv->answ = traces_refine_sametrace(NextCand, + n, + NextPart, tv, ti); + + if (tv->answ) { + if (tv->options->verbosity >= 2) PRINT_CANDIDATE(NextCand, tv->tolevel) + if (tv->tolevel == tv->maxtreelevel) { + tv->tolevel_tl = tv->tolevel; + if (tv->options->verbosity >= 2) tv->expaths -= CPUTIME; + if (TargetCellExpPath(NextCand, NextPart, tv)) { + ExperimentalStep(NextPart, NextCand, tv, ti, m, n); + PRINT_EXPPATHSTEP(NextCand, tv->answ) + PRINTF2("CS2 5?: finalnumcells: %d\n", tv->finalnumcells); + if ((NextPart->cells == tv->finalnumcells) || (NextPart->cells == n)) { + UPDATEMIN(tv->expathlength, tv->tolevel_tl); + } + + } + if (tv->options->verbosity >= 2) tv->expaths += CPUTIME; + if (!tv->answ) { + PRINT_RETURN + } + } + } + } + if (tv->answ) { + PRINTF2("CS2 6?: finalnumcells: %d\n", tv->finalnumcells); + if ((NextPart->cells == tv->finalnumcells) || (NextPart->cells == n)) { + if (tv->options->verbosity >= 2) tv->autchk -= CPUTIME; + temp = (tv->tolevel_tl == tv->tolevel+1); + autom = CheckForAutomorphisms(CurrCand, NextCand, + tv, ti, temp, n, NextPart); + if (tv->options->verbosity >= 2) tv->autchk += CPUTIME; + if (autom) { + for (i=autom; i<=tv->maxtreelevel; i++) { + AuxCand = Spine[i].liststart; + while (AuxCand && Prefix(AuxCand, NextCand, autom)) { + AuxCand->do_it = FALSE; + AuxCand = AuxCand->next; + } + } + if (autom == tv->tolevel) { + autom = 0; + } + } + + if (ti->thegrouphaschanged) { + ORBITSIZES + } + } + PRINT_RETURN + + /* ADD TO NEXT LEVEL */ + PRINTF2("CS2 7?: finalnumcells: %d\n", tv->finalnumcells); + if ((NextPart->cells != tv->finalnumcells) || (tv->tolevel != tv->maxtreelevel) || (tv->tolevel_tl != tv->tolevel+1)) { + ADDTONEXTLEVEL; + searchtrie_make(CurrCand, SpineTL->listend, n, tv); + } + } + else { + tv->stats->interrupted++; + } + if (autom) { + k = n; + autom = 0; + break; + } + if (tv->fromlevel == tv->maxtreelevel) { + k = n; + break; + } + } /* end for */ + TreeNode = RefPath[tv->maxtreelevel]; + } + } + else SpineTL = &Spine[tv->tolevel+1]; + } + + } + + /* REMOVE CURRENT CANDIDATE */ + if (!CurrCand->do_it || k >= SpineTL->tgtend) { + SpineFL->liststart = CurrCand->next; + if (CurrCand->next == NULL) { + SpineFL->listend = NULL; + } + CurrCand->next = GarbList; + GarbList = CurrCand; + } + ti->thereisnextlevel = SelectNextLevel(n, tv, ti); + return 0; +} + +void CopyCand(Candidate *W, Candidate *V,int n, int *lab, int *invlab) { + + if (lab) { + memcpy(W->lab, lab, n*sizeof(int)); + memcpy(W->invlab, invlab, n*sizeof(int)); + } + else { + memcpy(W->lab, V->lab, n*sizeof(int)); + memcpy(W->invlab, V->invlab, n*sizeof(int)); + } + W->name = V->name; + W->vertex = V->vertex; + W->code = V->code; + W->singcode = V->singcode; + W->firstsingcode = V->firstsingcode; + W->do_it = V->do_it; + W->sortedlab = FALSE; +} + +sparsegraph* copy_sg_structure(sparsegraph *sg2, sparsegraph *sg1) { + int *d1, *e1, *d2, *e2; + int i, n; + size_t *v1, *v2, k; + + if (!sg2) + { + if ((sg2 = (sparsegraph*)ALLOCS(1, sizeof(sparsegraph))) == NULL) + { + fprintf(ERRFILE, "copy_sg: malloc failed\n"); + exit(1); + } + SG_INIT(*sg2); + } + + SG_VDE(sg1, v1, d1, e1); + + n = sg1->nv; + + k = 0; + for (i = 0; i < n; ++i) + if (v1[i]+d1[i]>k) k = v1[i] + d1[i]; + SG_ALLOC(*sg2, n, k, "copy_sg malloc"); + + sg2->nv = n; + sg2->nde = sg1->nde; + sg2->elen = k; + /* sg2->wlen = k; */ + return sg2; +} + +void Edge_Delete(int vertex, int sons, Candidate *Cand, TracesVars *tv) { + int d_vtx, j1, temp; + int *sge, *sgw; + + if (TheGraph[vertex].d <= 1) { + return; + } + + d_vtx = TheGraph[vertex].d = TheGraph[vertex].d - sons; + sge = TheGraph[vertex].e; + sgw = TheGraph[vertex].w; + + for (j1=0; j1<d_vtx; j1++) { + if (TheGraph[sge[j1]].one) { + while (TheGraph[sge[TheGraph[vertex].d]].d == -1) { + (TheGraph[vertex].d)++; + } + temp = sge[j1]; + sge[j1] = sge[TheGraph[vertex].d]; + sge[TheGraph[vertex].d] = temp; + if (sgw) { + temp = sgw[j1]; + sgw[j1] = sgw[TheGraph[vertex].d]; + sgw[TheGraph[vertex].d] = temp; + } + } + } + TheGraph[vertex].d = d_vtx; +} + +void ExperimentalStep(Partition *NextPart, Candidate *NextCand, + TracesVars *tv, TracesInfo *ti, int m, int n) { + int i, iend, min, tmp; + + SpineTL_tl = Spine+tv->tolevel_tl; + NextPart->active = 1; + VERB_PRINT("EXSTP ",3,FALSE) + if (SpineTL_tl) { + } + + /* EXPERIMENTAL PATH INDIVIDUALIZATION AND REFINEMENT */ + if (tv->answ == 2) { + min = NextCand->lab[tv->tcellexpath]; + tmp = tv->tcellexpath; + iend = tv->tcellexpath + NextPart->cls[tv->tcellexpath]; + for (i=tv->tcellexpath + 1; i<iend ; i++) { + if (NextCand->lab[i] < min) { + min = NextCand->lab[i]; + tmp = i; + } + } + } + else { + tmp = tv->tcellexpath+KRAN(NextPart->cls[tv->tcellexpath]); + } + if (NextPart->cls[tv->tcellexpath] == 2) { + NextCand->pathsingcode = MASHCOMM(NextCand->pathsingcode, NextCand->lab[tv->tcellexpath]); + NextCand->pathsingcode = MASHCOMM(NextCand->pathsingcode, NextCand->lab[tv->tcellexpath+1]); + } + else { + NextCand->pathsingcode = MASHCOMM(NextCand->pathsingcode, NextCand->lab[tmp]); + } + + tv->indiv_vtx = NextCand->lab[tmp]; + Individualize(NextPart, NextCand, NextCand->lab[tmp], tv->tcellexpath, NextPart->cells, tv->tcellexpath + NextPart->cls[tv->tcellexpath]-1); + + tv->stats->numnodes++; + if (tv->compstage == 0) { + traces_refine_notrace(NextCand, + n, + NextPart, tv, ti); + } + else { + if (tv->tolevel_tl == tv->maxtreelevel+1) { + trieref = trieroot; + tv->answ = traces_refine_comptrie(NextCand, + n, + NextPart, tv, ti); + if (tv->answ == 0 ) { + tv->stats->interrupted++; + } + } + else { + traces_refine_notrace(NextCand, + n, + NextPart, tv, ti); + } + } + + CodeClassify(tv->tolevel_tl, NextCand->code, tv->tcellexpath); + +} + +void factorial(double *size1, int *size2, int k) { + int i; + + for(i = k; i; i--) { + MULTIPLY(*size1, *size2, i); + } +} + +void factorial2(double *size1, int *size2, int k) { + int i; + + for(i = k; i > 0; i -= 2) { + MULTIPLY(*size1, *size2, i); + } +} + +boolean findperm(permnode *pn, int *p, int n) { + permnode *rn; + + if (!pn) { + return FALSE; + } + rn = pn; + do { + if (!memcmp(rn->p, p, n*sizeof(int))) { + return TRUE; + } + rn = rn->next; + } while (rn != pn); + return FALSE; +} + +int *findcurrorbits(schreier *gp, int k) { + int i; + schreier *sh; + + sh = gp; + for (i = 0; i < k; i++) { + sh = sh->next; + } + return sh->orbits; +} + +int FirstNeighbour(int vtx, Candidate *Cand, Partition *Part, int* Markers, int mark, int *ngh, int n) { + int *e_vtx; + int i, k, deg; + int ngh1, ngh2, cell1, cell2; + + k = 0; + + deg = TheGraph[vtx].d; + e_vtx = TheGraph[vtx].e; + + if (deg == n-1) { + return 0; + } + + for (i=0; i<deg; i++) { + if (Markers[e_vtx[i]] != mark) { + cell1 = Part->inv[Cand->invlab[e_vtx[i]]]; + if (Part->cls[cell1] > 1) { + ngh1 = e_vtx[i++]; + k++; + break; + } + } + } + for (; i<deg; i++) { + if (Markers[e_vtx[i]] != mark) { + cell2 = Part->inv[Cand->invlab[e_vtx[i]]]; + if (Part->cls[cell2] > 1) { + ngh2 = e_vtx[i]; + k++; + break; + } + } + } + switch (k) { + case 0: + break; + + case 1: + *ngh = ngh1; + break; + + case 2: + if (cell1 < cell2) { + *ngh = ngh1; + } + else { + *ngh = ngh2; + } + break; + + default: + break; + } + return k; +} + +int FixBase(int *fix, struct TracesVars *tv, Candidate *Cand, int from, int to) { + int i, j, k, go, nfix; + + nfix = j = 0; + go = TRUE; + for (i = from; i < to; i++) { + k = Cand->lab[Spine[i+1].tgtpos]; + if (go && (nfix < tv->nfix) && (fix[nfix] == k)) { + j++; + } + else { + fix[nfix] = k; + if (go) go = FALSE; + } + nfix++; + } + tv->nfix = nfix; + return j; +} + +boolean FixedBase(int *fix, struct TracesVars *tv, Candidate *Cand, int from, int to) { + int i, k, nfix; + + nfix = 0; + for (i = from; i < to; i++) { + k = Cand->lab[Spine[i+1].tgtpos]; + if (fix[nfix] != k) { + return FALSE; + } + nfix++; + } + return TRUE; +} + +int FreeList(Candidate *List, int cond) { + Candidate *Temp; + int conta = 0; + int conta1 = 0; + + while (List) { + if (List->do_it == cond) { + conta1++; + } + conta++; + Temp = List; + if (List->lab) free(List->lab); + if (List->invlab) free(List->invlab); + List = List->next; + free(Temp); + } + + if (cond) { + return conta1; + } + else { + return conta; + } +} + +/* Check if the permutations in the list gens are automorphisms, + * also set mark and refcount fields and initialise orbits. */ +int given_gens(sparsegraph *g, permnode *gens, int *orbits, boolean digraph) { + int i, m, n, norbs; + permnode *pn; + + n = g->nv; + for (i = 0; i < n; ++i) orbits[i] = i; + memcpy(IDENTITY_PERM, orbits, n*sizeof(int)); + norbs = n; + + if (!gens) return norbs; + + m = SETWORDSNEEDED(n); + pn = gens; + do { + if (!isautom_sg((graph*)g, pn->p, digraph, m, n)) { + fprintf(ERRFILE, "Input permutation is not an automorphism\n"); + exit(1); + } + norbs = orbjoin(orbits, pn->p, n); + pn->mark = 1; + pn->refcount = 0; + pn = pn->next; + } while (pn != gens); + + return norbs; +} + +void grouporderplus(sparsegraph *sg_orig, Candidate *Cand, Partition *Part, permnode **ring, + double *grpsize1, int *grpsize2, int n, TracesVars *tv, TracesInfo *ti) { + + int i, i1, j, j0, j2, k, k1, k2, w, w1, w2, c, c1, c2, n1, n2; + int prev, step, start, counts, StInd, CyInd, cycnum; + int tmp, temp, halfsize, nghcell, numvertices; + int arg, val; + + searchtrie *TrieNode; + int NSFCInd, ind; + boolean do_ngh = FALSE; + + numvertices = n; + memcpy(CanonIndices, IDENTITY_PERM, n*sizeof(int)); + memset(TreeNodes, 0, n*sizeof(int)); + + TrieNode = Spine[tv->maxtreelevel].liststart->stnode; + if (TrieNode->father) { + if (tv->options->verbosity >= 2) { + LINE(32, "-") + NEXTLINE + fprintf(outfile, "group structure: "); + while (TrieNode->father) { + if (Factorials[TrieNode->level]) { + fprintf(outfile, "%d (%d!), ", TrieNode->name, TrieNode->index); + factorial(grpsize1, grpsize2, TrieNode->index); + } else { + if (TrieNode->father->name) { + fprintf(outfile, "%d (%d), ", TrieNode->name, TrieNode->index); + MULTIPLY(tv->stats->grpsize1, tv->stats->grpsize2, TrieNode->index); + } + else { + temp = spinelementorbsize(tv->orbits, Spine[tv->maxtreelevel].liststart->lab+Spine[1].tgtcell, Spine[1].tgtsize, TrieNode->vtx); + MULTIPLY(*grpsize1, *grpsize2, temp); + fprintf(outfile, "%d (%d)\n", + TrieNode->name, temp); + } + } + TrieNode = TrieNode->father; + } + } + else { + while (TrieNode->father) { + if (Factorials[TrieNode->level]) { + factorial(grpsize1, grpsize2, TrieNode->index); + } else { + if (TrieNode->father->name) { + MULTIPLY(tv->stats->grpsize1, tv->stats->grpsize2, TrieNode->index); + } + else { + temp = spinelementorbsize(tv->orbits, Spine[tv->maxtreelevel].liststart->lab+Spine[1].tgtcell, Spine[1].tgtsize, TrieNode->vtx); + MULTIPLY(*grpsize1, *grpsize2, temp); + } + } + TrieNode = TrieNode->father; + } + } + } + + if (Part->cells < n) { + if (!ti->deg_one) { + if (sg_orig->e) { + memcpy(tv->graph->e, sg_orig->e, tv->graph->elen*sizeof(int)); + for (i=0; i<n; i++) { + TheGraph[i].e = tv->graph->e + sg_orig->v[i]; + } + } + } + NSFCInd = 0; + + /* Trees */ + if (tv->options->getcanon && tv->preprocessed) { + for (i = 0; i < n; i += Part->cls[i]) { + if (Part->cls[i] == 1) { + tmp = Cand->lab[i]; + if ((TheGraph[tmp].d >= 0) && (TheGraph[tmp].d < sg_orig->d[tmp])) { + for (j=i; j<i+Part->cls[i]; j++) { + MakeCanTree(Cand->lab[j], sg_orig, n, Cand, Part, tv); + } + } + } + } + } + + memset(SingNonSing, 0, n*sizeof(int)); + + for (i = 0; i < n; i += Part->cls[i]) { + if (Part->cls[i] > 1) { + if (TheGraph[Cand->lab[i]].d > 2) { + for (j=i; j<i+Part->cls[i]; j++) { + SingNonSing[Cand->lab[j]] = 2; + } + } + } + else { + SingNonSing[Cand->lab[i]] = 1; + } + } + + for (i = 0; i < n; i += Part->cls[i]) { + if (Part->cls[i] > 1) { + if (TheGraph[Cand->lab[i]].d > 2) NonSingDegPlus1(Cand, Part, i, tv); + NSFCells[NSFCInd++] = i; + } + else { + NonSingDegPlus2(Cand, Part, i, tv); + numvertices--; + } + } + + /* Degree 2 and at least one nghb with deg > 2 */ + SETMARK(StackMarkers, tv->stackmark) + for (ind = 0; ind < NSFCInd; ind++) { + i = NSFCells[ind]; + SETMARK(Markers, tv->mark) + if (Part->cls[i] > 1) { + tmp = Cand->lab[i]; + if ((TheGraph[tmp].d == 2) && ((TheGraph[TheGraph[tmp].e[0]].d > 2) || ((TheGraph[TheGraph[tmp].e[1]].d > 2)))) { + n1 = TheGraph[tmp].e[0]; + n2 = TheGraph[tmp].e[1]; + if (TheGraph[n1].d > 2) { + if (TheGraph[n2].d > 2) { + if (Cand->invlab[n1] < Cand->invlab[n2]) { + start = n1; + } + else { + start = n2; + } + } + else { + start = n1; + } + } + else { + start = n2; + } + counts = 0; + StInd = 0; + for (j=i; j<i+Part->cls[i]; j++) { + step = Cand->lab[j]; + if (Markers[step] != tv->mark) { + prev = start; + counts++; + do { + Markers[step] = tv->mark; + PERMSTACK[StInd++] = step; + if (TheGraph[step].e[0] != prev) { + prev = step; + step = TheGraph[step].e[0]; + } + else { + prev = step; + step = TheGraph[step].e[1]; + } + } while (TheGraph[step].d == 2); + + if (TheGraph[step].d == 1) { + PERMSTACK[StInd++] = step; + } + } + } + + if (counts == Part->cls[i]) { + factorial(grpsize1, grpsize2, Part->cls[i]); + if (tv->permInd) ResetAutom(tv->permInd, n, tv); + for (k=0; k<StInd/counts; k++) { + i1 = PERMSTACK[k]; + for (j0=0; j0<counts-1; j0++) { + SETPAIRSAUTANDTREE(PERMSTACK[j0*(StInd/counts)+k], PERMSTACK[(j0+1)*(StInd/counts)+k]) + } + SETPAIRSAUTANDTREE(PERMSTACK[j0*(StInd/counts)+k], i1) + } + SPECIALGENERATORS + if (counts > 2) { + if (tv->permInd) ResetAutom(tv->permInd, n, tv); + for (k=0; k<StInd/counts; k++) { + i1 = PERMSTACK[k]; + for (j0=0; j0<1; j0++) { + SETPAIRSAUTANDTREE(PERMSTACK[j0*(StInd/counts)+k], PERMSTACK[(j0+1)*(StInd/counts)+k]) + } + SETPAIRSAUTANDTREE(PERMSTACK[j0*(StInd/counts)+k], i1) + } + SPECIALGENERATORS + } + } + else { + factorial2(grpsize1, grpsize2, Part->cls[i]); + for (j=0; j<counts; j++) { + j0 = j*(StInd/counts); + k1 = (j+1)*(StInd/counts); + if (tv->permInd) ResetAutom(tv->permInd, n, tv); + for (k=j0, i1=k1-1; k<k1; k++, i1--) { + SETPAIRSAUTANDTREE(PERMSTACK[k], PERMSTACK[i1]) + } + SPECIALGENERATORS + } + if (counts > 1) { + if (tv->permInd) ResetAutom(tv->permInd, n, tv); + for (k=0; k<StInd/counts; k++) { + i1 = PERMSTACK[k]; + for (j0=0; j0<counts-1; j0++) { + SETPAIRSAUTANDTREE(PERMSTACK[j0*(StInd/counts)+k], PERMSTACK[(j0+1)*(StInd/counts)+k]) + } + SETPAIRSAUTANDTREE(PERMSTACK[j0*(StInd/counts)+k], i1) + } + SPECIALGENERATORS + } + if (counts > 2) { + if (tv->permInd) ResetAutom(tv->permInd, n, tv); + for (k=0; k<StInd/counts; k++) { + i1 = PERMSTACK[k]; + for (j0=0; j0<1; j0++) { + SETPAIRSAUTANDTREE(PERMSTACK[j0*(StInd/counts)+k], PERMSTACK[(j0+1)*(StInd/counts)+k]) + } + SETPAIRSAUTANDTREE(PERMSTACK[j0*(StInd/counts)+k], i1) + } + SPECIALGENERATORS + } + } + for (j=0; j<StInd; j++) { + Place(PERMSTACK[j], Cand, Part); + if ((TheGraph[PERMSTACK[j]].d >= 0) && (TheGraph[PERMSTACK[j]].d < sg_orig->d[PERMSTACK[j]])) { + MakeCanTree(PERMSTACK[j], sg_orig, n, Cand, Part, tv); + } + } + } + } + } + + /* Degree 2 and at least one nghb with == 1 */ + for (ind = 0; ind < NSFCInd; ind++) { + SETMARK(Markers, tv->mark) + i = NSFCells[ind]; + if (Part->cls[i] > 1) { + tmp = Cand->lab[i]; + if ((TheGraph[tmp].d == 2) && ((TheGraph[TheGraph[tmp].e[0]].d == 1) || ((TheGraph[TheGraph[tmp].e[1]].d == 1)))) { + counts = 0; + StInd = 0; + for (j=i; j<i+Part->cls[i]; j++) { + step = Cand->lab[j]; + if (Markers[step] != tv->mark) { + n1 = TheGraph[step].e[0]; + n2 = TheGraph[step].e[1]; + if (TheGraph[n1].d == 1) { + if (TheGraph[n2].d == 1) { + if (Cand->invlab[n1] < Cand->invlab[n2]) { + start = n1; + } + else { + start = n2; + } + } + else { + start = n1; + } + } + else { + start = n2; + } + PERMSTACK[StInd++] = start; + prev = start; + counts++; + + do { + Markers[step] = tv->mark; + PERMSTACK[StInd++] = step; + if (TheGraph[step].e[0] != prev) { + prev = step; + step = TheGraph[step].e[0]; + } else { + prev = step; + step = TheGraph[step].e[1]; + } + } while (TheGraph[step].d == 2); + PERMSTACK[StInd++] = step; + } + } + + if (counts == Part->cls[i]) { + if (Part->inv[Cand->invlab[PERMSTACK[0]]] != Part->inv[Cand->invlab[PERMSTACK[StInd/counts-1]]]) { + factorial(grpsize1, grpsize2, Part->cls[i]); + } + else { + factorial2(grpsize1, grpsize2, 2*Part->cls[i]); + for (j=0; j<counts; j++) { + j0 = j*(StInd/counts); + k1 = (j+1)*(StInd/counts); + if (tv->permInd) ResetAutom(tv->permInd, n, tv); + for (k=j0, i1=k1-1; k<k1; k++, i1--) { + SETPAIRSAUTANDTREE(PERMSTACK[k], PERMSTACK[i1]) + } + SPECIALGENERATORS + } + } + if (tv->permInd) ResetAutom(tv->permInd, n, tv); + for (k=0; k<StInd/counts; k++) { + i1 = PERMSTACK[k]; + for (j0=0; j0<counts-1; j0++) { + SETPAIRSAUTANDTREE(PERMSTACK[j0*(StInd/counts)+k], PERMSTACK[(j0+1)*(StInd/counts)+k]) + } + SETPAIRSAUTANDTREE(PERMSTACK[j0*(StInd/counts)+k], i1) + } + SPECIALGENERATORS + if (counts > 2) { + if (tv->permInd) ResetAutom(tv->permInd, n, tv); + for (k=0; k<StInd/counts; k++) { + i1 = PERMSTACK[k]; + for (j0=0; j0<1; j0++) { + SETPAIRSAUTANDTREE(PERMSTACK[j0*(StInd/counts)+k], PERMSTACK[(j0+1)*(StInd/counts)+k]) + } + SETPAIRSAUTANDTREE(PERMSTACK[j0*(StInd/counts)+k], i1) + } + SPECIALGENERATORS + } + } + else { + factorial2(grpsize1, grpsize2, Part->cls[i]); + for (j=0; j<counts; j++) { + j0 = j*(StInd/counts); + k1 = (j+1)*(StInd/counts); + if (tv->permInd) ResetAutom(tv->permInd, n, tv); + for (k=j0, i1=k1-1; k<k1; k++, i1--) { + SETPAIRSAUTANDTREE(PERMSTACK[k], PERMSTACK[i1]) + } + SPECIALGENERATORS + } + if (counts > 1) { + if (tv->permInd) ResetAutom(tv->permInd, n, tv); + for (k=0; k<StInd/counts; k++) { + i1 = PERMSTACK[k]; + for (j0=0; j0<counts-1; j0++) { + SETPAIRSAUTANDTREE(PERMSTACK[j0*(StInd/counts)+k], PERMSTACK[(j0+1)*(StInd/counts)+k]) + } + SETPAIRSAUTANDTREE(PERMSTACK[j0*(StInd/counts)+k], i1) + } + SPECIALGENERATORS + } + if (counts > 2) { + if (tv->permInd) ResetAutom(tv->permInd, n, tv); + for (k=0; k<StInd/counts; k++) { + i1 = PERMSTACK[k]; + for (j0=0; j0<1; j0++) { + SETPAIRSAUTANDTREE(PERMSTACK[j0*(StInd/counts)+k], PERMSTACK[(j0+1)*(StInd/counts)+k]) + } + SETPAIRSAUTANDTREE(PERMSTACK[j0*(StInd/counts)+k], i1) + } + SPECIALGENERATORS + } + } + for (j=0; j<StInd; j++) { + Place(PERMSTACK[j], Cand, Part); + if ((TheGraph[PERMSTACK[j]].d >= 0) && (TheGraph[PERMSTACK[j]].d < sg_orig->d[PERMSTACK[j]])) { + MakeCanTree(PERMSTACK[j], sg_orig, n, Cand, Part, tv); + } + } + } + } + } + + /* Cycles */ + for (ind = 0; ind < NSFCInd; ind++) { + i = NSFCells[ind]; + SETMARK(Markers, tv->mark) + if (Part->cls[i] > 1) { + tmp = Cand->lab[i]; + if (TheGraph[tmp].d == 2) { + CyInd = StInd = cycnum = 0; + for (j=i; j<i+Part->cls[i]; j++) { + start = Cand->lab[j]; + if (Markers[start] != tv->mark) { + counts = 1; + CYCLES[StInd] = start; + CYCOLR[StInd++] = Part->inv[Cand->invlab[start]]; + Markers[start] = tv->mark; + k = Cand->invlab[TheGraph[start].e[0]]; + k1 = Cand->invlab[TheGraph[start].e[1]]; + if (Part->inv[k] < Part->inv[k1]) { + step = TheGraph[start].e[0]; + } + else { + step = TheGraph[start].e[1]; + } + prev = start; + do { + counts++; + Markers[step] = tv->mark; + CYCLES[StInd] = step; + CYCOLR[StInd++] = Part->inv[Cand->invlab[step]]; + + if (TheGraph[step].e[0] != prev) { + prev = step; + step = TheGraph[step].e[0]; + } + else { + prev = step; + step = TheGraph[step].e[1]; + } + } while (step != start); + CYLGTH[CyInd++] = counts; + cycnum++; + } + } + + CYCPOS[0] = 0; + for (j=1; j<CyInd; j++) { + CYCPOS[j] = CYCPOS[j-1]+CYLGTH[j-1]; + } + memcpy(WorkArray, CYLGTH, CyInd*sizeof(int)); + sort2ints(WorkArray, CYCPOS, CyInd); + + k = 0; + for (i1=0; i1<CyInd; i1++) { + k1 = CYCOLR[k]; + k2 = CYCOLR[k+1]; + for (j=1; j<=CYLGTH[i1]/2; j++) { + w1 = CYCOLR[j+k]; + w2 = CYCOLR[j+1+k]; + if ((w1 == k1) && (w2 == k2)) { + if (tv->permInd) ResetAutom(tv->permInd, n, tv); + for (w=0; w<CYLGTH[i1]; w++) { + if (CYCOLR[w+k] == CYCOLR[((w+j) % CYLGTH[i1]) + k]) { + SETPAIRSAUTANDTREE(CYCLES[w+k], CYCLES[((w+j) % CYLGTH[i1]) + k]) + } + else { + break; + } + } + if (w == CYLGTH[i1]) { SPECIALGENERATORS } + if (w == CYLGTH[i1]) { + MULTIPLY(*grpsize1, *grpsize2, CYLGTH[i1]/j); + break; + } + } + } + + if (Part->cls[k1] >= Part->cls[k2]) { + for (j=CYLGTH[i1]-1; j>0; j--) { + w1 = CYCOLR[j % CYLGTH[i1] + k]; + w2 = CYCOLR[(j-1) % CYLGTH[i1] + k]; + if ((w1 == k1) && (w2 == k2)) { + if (tv->permInd) ResetAutom(tv->permInd, n, tv); + for (w=0; w<CYLGTH[i1]; w++) { + SETPAIRSAUTANDTREE(CYCLES[w+k], CYCLES[((j-w+(w>j)*CYLGTH[i1]) % CYLGTH[i1]) + k]) + } + SPECIALGENERATORS + MULTIPLY(*grpsize1, *grpsize2, 2); + break; + } + } + } + else { + j=CYLGTH[i1]-1; + w2 = CYCOLR[j % CYLGTH[i1] + k]; + if (w2 == k2) { + if (tv->permInd) ResetAutom(tv->permInd, n, tv); + for (w=1; w<CYLGTH[i1]; w++) { + SETPAIRSAUTANDTREE(CYCLES[w+k], CYCLES[CYLGTH[i1]-w+k]) + } + SPECIALGENERATORS + MULTIPLY(*grpsize1, *grpsize2, 2); + } + } + k += CYLGTH[i1]; + } + k = 0; + for (i1=0; i1<CyInd; i1++) { + if (CYLGTH[i1] > 0) { + CYMULT[0] = k; + k1 = k; + counts = 1; + for (j0=i1+1; j0<CyInd; j0++) { + k1 += abs(CYLGTH[j0]); + if (CYLGTH[j0] == CYLGTH[i1]) { + CYMULT[counts++] = k1; + CYLGTH[j0] = -CYLGTH[j0]; + } + } + if (counts > 1) { + if (tv->permInd) ResetAutom(tv->permInd, n, tv); + for (j0=0; j0<CYLGTH[i1]; j0++) { + for (j2 = 0; j2<counts-1; j2++) { + SETPAIRSAUTANDTREE(CYCLES[CYMULT[j2]+j0], CYCLES[CYMULT[j2+1]+j0]) + } + SETPAIRSAUTANDTREE(CYCLES[CYMULT[j2]+j0], CYCLES[CYMULT[0]+j0]) + } + SPECIALGENERATORS + if (counts > 2) { + if (tv->permInd) ResetAutom(tv->permInd, n, tv); + for (j0=0; j0<CYLGTH[i1]; j0++) { + SETPAIRSAUTANDTREE(CYCLES[CYMULT[1]+j0], CYCLES[CYMULT[0]+j0]) + if (tv->build_autom) { + SETPAIRSAUT(CYCLES[CYMULT[0]+j0], CYCLES[CYMULT[1]+j0]) + } + MakeTree(CYCLES[CYMULT[0]+j0], CYCLES[CYMULT[1]+j0], sg_orig, n, tv, FALSE); + } + SPECIALGENERATORS + } + factorial(grpsize1, grpsize2, counts); + } + } + k += abs(CYLGTH[i1]); + CYLGTH[i1] = -CYLGTH[i1]; + } + + for (c1=0; c1<CyInd; c1++) { + c = CYCPOS[c1]+WorkArray[c1]; + for (c2=CYCPOS[c1]; c2<c; c2++) { + Place(CYCLES[c2], Cand, Part); + if ((TheGraph[CYCLES[c2]].d >= 0) && (TheGraph[CYCLES[c2]].d < sg_orig->d[CYCLES[c2]])) { + MakeCanTree(CYCLES[c2], sg_orig, n, Cand, Part, tv); + } + } + } + } + } + } + + /* Degree 1, and nghb too */ + SETMARK(Markers, tv->mark) + for (ind = 0; ind < NSFCInd; ind++) { + i = NSFCells[ind]; + if (Part->cls[i] > 1) { + tmp = Cand->lab[i]; + if ((TheGraph[tmp].d == 1) && (TheGraph[TheGraph[tmp].e[0]].d == 1) && (i == Part->inv[Cand->invlab[TheGraph[tmp].e[0]]])) { + factorial2(grpsize1, grpsize2, Part->cls[i]); + /* the cell has size two */ + if (Part->cls[i] == 2) { + val = Cand->lab[i+1]; + if (tv->permInd) ResetAutom(tv->permInd, n, tv); + arg = tmp; + SETPAIRSAUTANDTREE(arg, val) + SETPAIRSAUTANDTREE(val, arg) + SPECIALGENERATORS + } + else { + /* the cell has size greater than two */ + if (tv->permInd) ResetAutom(tv->permInd, n, tv); + SETMARK(Markers, tv->mark) + halfsize = Part->cls[i]/2; + i1 = 0; + for (j=i; j<i+Part->cls[i]; j++) { + if (Markers[Cand->lab[j]] != tv->mark) { + Markers[TheGraph[Cand->lab[j]].e[0]] = tv->mark; + PERMSTACK[i1] = Cand->lab[j]; + PERMSTACK[i1+halfsize] = TheGraph[Cand->lab[j]].e[0]; + i1++; + } + } + temp = PERMSTACK[0]; + for (j=0; j<Part->cls[i]-1; j++) { + SETPAIRSAUTANDTREE(PERMSTACK[j], PERMSTACK[j+1]) + } + SETPAIRSAUTANDTREE(PERMSTACK[j], temp) + SPECIALGENERATORS + if (tv->permInd) ResetAutom(tv->permInd, n, tv); + memmove(PERMSTACK+halfsize, PERMSTACK+halfsize+1, (halfsize-1)*sizeof(int)); + temp = PERMSTACK[1]; + for (j=1; j<Part->cls[i]-2; j++) { + SETPAIRSAUTANDTREE(PERMSTACK[j], PERMSTACK[j+1]) + } + SETPAIRSAUTANDTREE(PERMSTACK[j], temp) + SPECIALGENERATORS + } + + SETMARK(Markers, tv->mark) + for (j=i; j<i+Part->cls[i]; j++) { + temp = Cand->lab[j]; + if (Markers[temp] != tv->mark) { + if ((TheGraph[temp].d >= 0) && (TheGraph[temp].d < sg_orig->d[temp])) { + MakeCanTree(temp, sg_orig, n, Cand, Part, tv); + } + tmp = Cand->lab[j+1]; + Markers[TheGraph[temp].e[0]] = tv->mark; + i1 = Cand->invlab[TheGraph[temp].e[0]]; + Cand->lab[j+1] = TheGraph[temp].e[0]; + + if ((TheGraph[TheGraph[temp].e[0]].d >= 0) && (TheGraph[TheGraph[temp].e[0]].d < sg_orig->d[TheGraph[temp].e[0]])) { + MakeCanTree(TheGraph[temp].e[0], sg_orig, n, Cand, Part, tv); + } + Cand->invlab[TheGraph[temp].e[0]] = j+1; + Cand->lab[i1] = tmp; + Cand->invlab[tmp] = i1; + } + } + } + } + } + + /* Degree 0 */ + for (ind = 0; ind < NSFCInd; ind++) { + i = NSFCells[ind]; + if (Part->cls[i] > 1) { + tmp = Cand->lab[i]; + if ((TheGraph[0].e != NULL) && (TheGraph[tmp].d != 0) && (TheGraph[tmp].d != numvertices-1)) + nghcell = Part->inv[Cand->invlab[TheGraph[tmp].e[0]]]; else nghcell = i; + if ((TheGraph[tmp].d == 0) || + ((TheGraph[tmp].d == numvertices-1) && (TheGraph[tmp].d > 2)) || + ((TheGraph[tmp].d == 1) && (TheGraph[TheGraph[tmp].e[0]].d == 1) && (i < nghcell))) { + do_ngh = FALSE; + if ((TheGraph[tmp].d == 1) && (TheGraph[TheGraph[tmp].e[0]].d == 1) && (i != nghcell)) { + do_ngh = TRUE; + } + if (tv->permInd) ResetAutom(tv->permInd, n, tv); + for (j=i; j<i+Part->cls[i]-1; j++) { + arg = Cand->lab[j]; + val = Cand->lab[j+1]; + SETPAIRSAUTANDTREE(arg, val) + if (do_ngh) { + SETPAIRSAUTANDTREE(TheGraph[arg].e[0], TheGraph[val].e[0]) + + } + } + arg = Cand->lab[j]; + val = tmp; + SETPAIRSAUTANDTREE(arg, val) + if (do_ngh) { + SETPAIRSAUTANDTREE(TheGraph[arg].e[0], TheGraph[val].e[0]) + } + SPECIALGENERATORS + if (Part->cls[i] > 2) { + if (tv->permInd) ResetAutom(tv->permInd, n, tv); + arg = tmp; + val = Cand->lab[i+1]; + SETPAIRSAUTANDTREE(arg, val) + if (do_ngh) { + SETPAIRSAUTANDTREE(TheGraph[arg].e[0], TheGraph[val].e[0]) + } + arg = Cand->lab[i+1]; + val = tmp; + SETPAIRSAUTANDTREE(arg, val) + if (do_ngh) { + SETPAIRSAUTANDTREE(TheGraph[arg].e[0], TheGraph[val].e[0]) + } + SPECIALGENERATORS + } + factorial(grpsize1, grpsize2, Part->cls[i]); + if (do_ngh) { + for (j=i; j<i+Part->cls[i]; j++) { + temp = TheGraph[Cand->lab[j]].e[0]; + Cand->lab[nghcell] = temp; + Cand->invlab[temp] = nghcell; + nghcell++; + } + } + + k = i+Part->cls[i]; + for (j=i; j<k; j++) { + Place(Cand->lab[j], Cand, Part); + if ((TheGraph[Cand->lab[j]].d >= 0) && (TheGraph[Cand->lab[j]].d < sg_orig->d[Cand->lab[j]])) { + MakeCanTree(Cand->lab[j], sg_orig, n, Cand, Part, tv); + if (do_ngh) { + MakeCanTree(TheGraph[Cand->lab[j]].e[0], sg_orig, n, Cand, Part, tv); + } + } + } + } + } + } + + } + + /* Orbit Count */ + SETMARK(Markers, tv->mark) + i1=0; + for (c1=0; c1<n; c1++) { + if (Markers[tv->orbits[c1]] != tv->mark) { + i1++; + Markers[tv->orbits[c1]] = tv->mark; + } + } + tv->stats->numorbits = i1; + return; +} + +void Individualize(Partition *NextPart, Candidate *NextCand, int K, int Tc, int Cl, int Pos) { + int i, j; + + NextCand->do_it = TRUE; + if (NextPart->cls[Tc] > 1) { + NextPart->cells = Cl+1; + NextPart->active = 1; + NextPart->cls[Tc]--; + NextPart->cls[Pos] = 1; + } + NextPart->inv[Pos] = Pos; + + j = NextCand->lab[Pos]; + i = NextCand->invlab[K]; + NextCand->lab[Pos] = K; + NextCand->invlab[K] = Pos; + NextCand->lab[i] = j; + NextCand->invlab[j] = i; + return; +} + +void Initialize_Traces_Variables(TracesVars *tv, TracesOptions *options_arg, + TracesStats *stats_arg, int *orbits_arg, + sparsegraph *g_arg, sparsegraph *canong_arg, + int n) { + tv->augmented_cells = n; + tv->canlist = 0; + tv->compstage = 0; + tv->expathlength = n; + tv->finalnumcells = n; + tv->firstpathlength = 0; + tv->group_level = 0; + tv->lev_of_lastauto = 0; + tv->linelgth = 0; + tv->name = 0; + tv->maxspineorblevel = 0; + tv->nfix = 0; + tv->options = options_arg; + tv->orbits = orbits_arg; + tv->permInd = 0; + tv->maxdeg = 0; + tv->mindeg = n; + + if (tv->options->generators || tv->options->writeautoms || tv->options->userautomproc) + tv->build_autom = TRUE; + else + tv->build_autom = FALSE; + + tv->specialgens = 0; + tv->stats = stats_arg; + tv->treedepth = 0; + tv->gotonode = NULL; + tv->input_graph = tv->graph = g_arg; + tv->cangraph = canong_arg; + tv->mark = tv->stackmark = tv->treemark = tv->autmark = tv->markcell1 = tv->markcell2 = NAUTY_INFINITY-1; + tv->conta0 = tv->conta1 = tv->conta2 = tv->conta3 = tv->conta4 = tv->conta5 = tv->conta6 = tv->conta7 = tv->contatc = 0; + + if (tv->options->strategy == 0) { + tv->steps = n; + tv->strategy = 0; + } + else { + tv->strategy = 1; + tv->steps = tv->options->strategy; + if (tv->steps > n) { + tv->steps = n; + } + } +} + +void Initialize_Traces_Statistics (TracesStats *stats_arg, int n) { + stats_arg->grpsize1 = 1; + stats_arg->grpsize2 = 0; + stats_arg->numorbits = n; + stats_arg->treedepth= 0; + stats_arg->numgenerators = 0; + stats_arg->numnodes = 1; + stats_arg->errstatus = 0; + stats_arg->interrupted = 0; + stats_arg->canupdates = 0; + stats_arg->peaknodes = 0; +} + +void Initialize_Traces_Time_Variables (TracesVars *tv) { + tv->autchk = 0; + tv->expaths = 0; + tv->schreier1 = 0; + tv->schreier2 = 0; + tv->schreier3 = 0; +} + +boolean isautom_sg_pair(graph *g, int *p, boolean digraph, int m, int n, struct TracesVars *tv) { + int *d, *e; + size_t *v; + int i, k, pi, di; + size_t vi, vpi, j; + + SG_VDE(g, v, d, e); + + for (k = 0; k < tv->permInd; ++k) + { + i = PrmPairs[k].arg; + pi = p[i]; + di = d[i]; + if (d[pi] != di) return FALSE; + + vi = v[i]; + vpi = v[pi]; + SETMARK(AutMarkers, tv->autmark) + for (j = 0; j < di; ++j) AutMarkers[p[e[vi+j]]] = tv->autmark; + for (j = 0; j < di; ++j) if (AutMarkers[e[vpi+j]] != tv->autmark) { + return FALSE; + } + } + + return TRUE; +} + +boolean lookup(searchtrie *t) { + searchtrie *TreeNode; + + TreeNode = t; + while (TreeNode->level >= 1) { + if (TreeNode->goes_to) { + return FALSE; + } + TreeNode = TreeNode->father; + } + return TRUE; +} + +void MakeCanTree(int v1, sparsegraph *sg_orig, int n, Candidate *Cand, Partition *Part, struct TracesVars* tv) { + int ind, vtx, ngh, trind, deg0, deg1; + size_t j1; + int *sge1; + + trind = 1; + ind = 0; + TreeStack[0] = v1; + SETMARK(TreeMarkers, tv->treemark); + + while (ind < trind) { + vtx = TreeStack[ind++]; + if (TreeNodes[vtx]) { + return; + } + + if (TheGraph[vtx].d == -1) { + Place(vtx, Cand, Part); + TreeNodes[vtx] = TRUE; + } + + TreeMarkers[vtx] = tv->treemark; + deg0 = maxint(TheGraph[vtx].d, 0); + deg1 = sg_orig->d[vtx]; + sge1 = TheGraph[vtx].e; + + for (j1 = deg0; j1 < deg1; j1++) { + ngh = sge1[j1]; + if ((TheGraph[ngh].d == -1) && (TreeMarkers[ngh] != tv->treemark)) { + TreeStack[trind++] = ngh; + } + } + } + return; +} + +void MakeDiscrete(Partition *Part, int cell) { + int i, k; + + Part->cells += (Part->cls[cell] - 1); + k = cell + Part->cls[cell]; + + for (i = cell; i < k; i++) { + Part->cls[i] = 1; + Part->inv[i] = i; + } +} + +void MakeTree(int v1, int v2, sparsegraph *sg, int n, struct TracesVars* tv, boolean forceautom) { + int ind, vtx1, vtx2, ngh1, ngh2, trind, deg0, deg1; + size_t j1; + int *sge1, *sge2; + boolean build_autom; + + if (v1 == v2) return; + build_autom = tv->build_autom || forceautom; + trind = 2; + ind = 0; + TreeStack[0] = v1; + TreeStack[1] = v2; + SETMARK(TreeMarkers, tv->treemark); + + while (ind < trind) { + vtx1 = TreeStack[ind++]; + vtx2 = TreeStack[ind++]; + + TreeMarkers[vtx1] = tv->treemark; + TreeMarkers[vtx2] = tv->treemark; + + deg0 = maxint(TheGraph[vtx1].d, 0); + deg1 = sg->d[vtx1]; + sge1 = TheGraph[vtx1].e; + sge2 = TheGraph[vtx2].e; + for (j1 = deg0; j1 < deg1; j1++) { + ngh1 = sge1[j1]; + ngh2 = sge2[j1]; + if ((TreeMarkers[ngh1] != tv->treemark) && (ngh1 != ngh2)) { + TreeStack[trind++] = ngh1; + TreeStack[trind++] = ngh2; + if (ngh1 != ngh2) { + if (build_autom) { + AUTPERM[ngh1] = ngh2; + PrmPairs[tv->permInd].arg = ngh1; + PrmPairs[tv->permInd].val = ngh2; + tv->permInd++; + } + orbjoin_sp_pair(tv->orbits, OrbList, n, + ngh1, ngh2, &tv->stats->numorbits); + } + } + } + } + return; +} + +int maxint(int u, int v) { + if (u > v) { + return u; + } + else { + return v; + } +} + +int minint(int u, int v) { + if (u < v) { + return u; + } + else { + return v; + } +} + +int NextNeighbour(int vtx, Candidate *Cand, Partition *Part, int* Markers, int mark, int *ngh, int n) { + int *e_vtx; + int i, j, deg, cell1, nghi; + int cell[2]; + int vert[2]; + + deg = TheGraph[vtx].d; + e_vtx = TheGraph[vtx].e; + + if (deg == n-1) { + return 0; + } + j = 0; + cell[0] = cell[1] = n; + for (i=0; i<deg; i++) { + nghi = e_vtx[i]; + if (Markers[nghi] != mark) { + cell1 = Part->inv[Cand->invlab[nghi]]; + if (Part->cls[cell1] > 1) { + cell[j] = cell1; + vert[j] = nghi; + j++; + if (j==2) break; + } + } + } + if (j>0) { + if (cell[0] < cell[1]) { + *ngh = vert[0]; + } else { + *ngh = vert[1]; + } + return 1; + } + else return 0; +} + +int NonSingDeg(int vtx, Candidate *Cand, Partition *Part) { + int *e_vtx; + int i, deg, retdeg; + + retdeg = TheGraph[vtx].d; + deg = retdeg; + e_vtx = TheGraph[vtx].e; + for (i=0; i<deg; i++) { + if (Part->cls[Part->inv[Cand->invlab[e_vtx[i]]]] == 1) { + retdeg--; + } + } + return retdeg; +} + +int NonSingDegPlus1(Candidate *Cand, Partition *Part, int cell, TracesVars *tv) { + + int *e_vtx; + int vtx, sing; + int i, j, deg, retdeg, n, singcount; + + n = tv->input_graph->nv; + singcount = 0; + + SETMARK(StackMarkers, tv->stackmark) + + for (j=cell; j<cell+Part->cls[cell]; j++) { + vtx = Cand->lab[j]; + deg = TheGraph[vtx].d; + retdeg = 0; + e_vtx = TheGraph[vtx].e; + + for (i=0; i<deg; i++) { + if (SingNonSing[e_vtx[i]] != 1) { + e_vtx[retdeg++] = e_vtx[i]; + } + else { + if (StackMarkers[e_vtx[i]] != tv->stackmark) { + sing = e_vtx[i]; + WorkArray2[singcount] = Part->inv[Cand->invlab[sing]]; + WorkArray[singcount++] = sing; + + StackMarkers[e_vtx[i]] = tv->stackmark; + } + } + } + if (j == cell) { + sort2ints(WorkArray2, WorkArray, singcount); + } + if (deg != retdeg) { + memcpy(e_vtx+retdeg, WorkArray, singcount*sizeof(int)); + TheGraph[vtx].d = retdeg; + } + } + return retdeg; +} + +void NonSingDegPlus2(Candidate *Cand, Partition *Part, int cell, TracesVars *tv) { + + int *e_sing; + int sing; + int k, deg1, singdeg, singcount; + + singcount = 0; + + sing = Cand->lab[cell]; + singdeg = 0; + deg1 = TheGraph[sing].d; + e_sing = TheGraph[sing].e; + + for (k=0; k<deg1; k++) { + if (SingNonSing[e_sing[k]] != 2) { + e_sing[singdeg++] = e_sing[k]; + } + } + TheGraph[sing].d = singdeg; +} + +void orbjoin_sp_pair(int *orbits, int *list, int n, int u, int v, int *numorbs) { + int j1, j2, k1, k2; + + j1 = orbits[u]; + while (orbits[j1] != j1) j1 = orbits[j1]; + j2 = orbits[v]; + while (orbits[j2] != j2) j2 = orbits[j2]; + + if (j1 != j2) { + k1 = j1; + k2 = j2; + if (k1 < k2) { + (*numorbs)--; + while (list[j2] != k2) { + orbits[j2] = k1; + j2 = list[j2]; + } + orbits[j2] = k1; + k1 = list[k1]; + list[j2] = k1; + list[j1] = k2; + } + else if (k1 > k2) { + (*numorbs)--; + while (list[j1] != k1) { + orbits[j1] = k2; + j1 = list[j1]; + } + orbits[j1] = k2; + k2 = list[k2]; + list[j1] = k2; + list[j2] = k1; + } + } + return; +} + +void orbjoin_sp_perm(int *orbits, int *map, int *list, int n, int *numorbs) { + int i, j1, j2, k1, k2; + + 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]; + k1 = j1; + k2 = j2; + if (k1 < k2) { + (*numorbs)--; + while (OrbList[j2] != k2) { + orbits[j2] = k1; + j2 = OrbList[j2]; + } + orbits[j2] = k1; + k1 = OrbList[k1]; + OrbList[j2] = k1; + OrbList[j1] = k2; + } + else if (k1 > k2) { + (*numorbs)--; + while (OrbList[j1] != k1) { + orbits[j1] = k2; + j1 = OrbList[j1]; + } + orbits[j1] = k2; + k2 = OrbList[k2]; + OrbList[j1] = k2; + OrbList[j2] = k1; + } + } +} + +struct Partition *NewPartition(int n) { + struct Partition *P; + + P = malloc(sizeof(*(P))); + if (P == NULL) { + fprintf(ERRFILE, "\nError, memory not allocated.\n"); + exit(1); + } + P->cls = malloc(n*sizeof(int)); + if (P->cls == NULL) { + fprintf(ERRFILE, "\nError, memory not allocated.\n"); + exit(1); + } + P->inv = malloc(n*sizeof(int)); + if (P->inv == NULL) { + fprintf(ERRFILE, "\nError, memory not allocated.\n"); + exit(1); + } + P->code = -1; + P->cells = 0; + return P; +} + +void NewPartSpine(int Lev, int n) { + + if (Lev > 3) { + Spine[Lev].part = malloc(sizeof(*(Spine[Lev].part))); + if (Spine[Lev].part == NULL) { + fprintf(ERRFILE, "\nError, memory not allocated.\n"); + exit(1); + } + Spine[Lev].part->cls = Spine[Lev-3].part->cls; + Spine[Lev].part->inv = Spine[Lev-3].part->inv; + Spine[Lev-3].part->cls = Spine[Lev-3].part->inv = NULL; + Spine[Lev].part->code = -1; + Spine[Lev].part->cells = 0; + } + else { + Spine[Lev].part = NewPartition(n); + } +} + +void Place(int vtx, Candidate *Cand, Partition *Part) { + int vtxto, vtxpos; + + vtxpos = Cand->invlab[vtx]; + vtxto = CanonIndices[Part->inv[vtxpos]]++; + if (Cand->lab[vtxpos] != Cand->lab[vtxto]) { + Cand->lab[vtxpos] = Cand->lab[vtxto]; + Cand->lab[vtxto] = vtx; + Cand->invlab[Cand->lab[vtxpos]] = vtxpos; + Cand->invlab[Cand->lab[vtxto]] = vtxto; + } + if (Part->cls[vtxto] > 1) { + Part->cls[vtxto+1] = Part->cls[vtxto]-1; + Part->cls[vtxto] = 1; + } +} + +boolean Prefix(Candidate *Cand1, Candidate *Cand2, int k) { + int i; + + for (i=1; i<=k; i++) { + if (Cand1->lab[Spine[k].tgtpos] != Cand2->lab[Spine[k].tgtpos]) { + break; + } + } + return (i>k); +} + +int Preprocess(sparsegraph *sg, + permnode **ring, + Candidate *Cand, + int n, + Partition *Part, + struct TracesVars* tv) { + + int i, j, j0, k, curr_cell, ind, ind0, ind1, ind2; + int *sge; + int HitClsInd, labi, nghb, value, SplInd, SplCntInd, sc, iend, CStackInd, newcell, TraceInd; + +#define SETPAIRSAUTANDTREE_PREPROC(arg, val) \ +if (tv->build_autom) SETPAIRSAUT(arg, val) \ +if (arg != val) \ +orbjoin_sp_pair(tv->orbits, OrbList, n, arg, val, &tv->stats->numorbits); \ +MakeTree(arg, val, sg, n, tv, FALSE); + + CStackInd = 0; + for (i = 0; i < n; i += Part->cls[i]) { + if (TheGraph[Cand->lab[i]].d == 1) { + CStack[CStackInd++] = i; + } + } + + TraceInd = Part->cells; + + if (CStackInd > 0) { + ind = 0; + while (ind < CStackInd) { + + if (tv->mark > (NAUTY_INFINITY-2)) { + memset(Markers, 0, n*sizeof(int)); + memset(MarkHitVtx, 0, n*sizeof(int)); + tv->mark = 0; + } + tv->mark++; + + curr_cell = CStack[ind++]; + ind2 = curr_cell+Part->cls[curr_cell]; + HitClsInd = 0; + for (i = curr_cell; i < ind2; i++) { + labi = Cand->lab[i]; + nghb = *(TheGraph[labi].e); + + if (TheGraph[nghb].d != 1) { + TheGraph[labi].d = -1; + TheGraph[labi].one = TRUE; + } + + if (MarkHitVtx[nghb] == tv->mark) { + NghCounts[nghb]++; + } + else { + value = Part->inv[Cand->invlab[nghb]]; + MarkHitVtx[nghb] = tv->mark; + NghCounts[nghb] = 1; + if (Markers[value] != tv->mark) { + HitCls[HitClsInd++] = value; + Markers[value] = tv->mark; + HitVtx[value] = nghb; + ElmHitCll[value] = 1; + } + else { + HitVtx[value+ElmHitCll[value]++] = nghb; + } + } + } + tv->mark++; + + sort_Split_Array(HitCls,HitClsInd); + SplInd = 0; + SplCls[0] = n; + for (j = 0; j < HitClsInd; j++) { + ind1 = HitCls[j]; + if ((ElmHitCll[ind1] > 0) && (ElmHitCll[ind1] < Part->cls[ind1])) { + SplCls[SplInd++] = ind1; + } + else { + ind2 = ind1+Part->cls[ind1]; + value = NghCounts[Cand->lab[ind1++]]; + for (i = ind1; i < ind2; i++) { + if (NghCounts[Cand->lab[i]] != value) { + SplCls[SplInd++] = HitCls[j]; + break; + } + } + if (i == ind2) { + ind1 = HitCls[j]; + if (TheGraph[Cand->lab[ind1]].d != 1) { + for (i = ind1; i < ind2; i++) { + value = Cand->lab[i]; + Edge_Delete(value, NghCounts[value], Cand, tv); + sge = TheGraph[value].e+TheGraph[value].d; + if (NghCounts[value]>1) { + factorial(&(tv->stats->grpsize1), &(tv->stats->grpsize2), NghCounts[value]); + if (tv->permInd) ResetAutom(tv->permInd, n, tv); + for (j0=0; j0<NghCounts[value]-1; j0++) { + SETPAIRSAUTANDTREE_PREPROC(sge[j0], sge[j0+1]) + } + SETPAIRSAUTANDTREE_PREPROC(sge[j0], sge[0]) + SPECIALGENERATORS + if (NghCounts[value] > 2) { + if (tv->permInd) ResetAutom(tv->permInd, n, tv); + SETPAIRSAUTANDTREE_PREPROC(sge[0], sge[1]) + if (tv->build_autom) { + SETPAIRSAUT(sge[1], sge[0]) + } + MakeTree(sge[1], sge[0], sg, n, tv, FALSE); + SPECIALGENERATORS + } + } + } + if (TheGraph[Cand->lab[ind1]].d == 1) { + CStack[CStackInd++] = ind1; + } + } + } + } + } + + if (SplInd) { + + for (sc = 0; sc < SplInd; sc++) { /* For each cell C to be split */ + ind0 = SplCls[sc]; + ind1 = ind0 + Part->cls[ind0]; + SplCntInd = 0; + if (ElmHitCll[ind0] < Part->cls[ind0]) { + SplCnt[SplCntInd++] = 0; + SplPos[0] = Part->cls[ind0] - ElmHitCll[ind0]; + } + + /* According to the numbers of neighbors of C into the current cell */ + /* compute how many vertices in C will be placed into the same new cell */ + iend = ind0 + ElmHitCll[ind0]; + for (i = ind0; i < iend; i++) { + value = NghCounts[HitVtx[i]]; + if (Markers[value] != tv->mark) { + Markers[value] = tv->mark; + SplCnt[SplCntInd++] = value; + SplPos[value] = 1; + } + else { + SplPos[value]++; + } + } + tv->mark++; + + /* Sort the values deriving from the previous step */ + sort_Split_Array(SplCnt, SplCntInd); + + Part->cells += SplCntInd-1; + + /* Split the cell C and update the information for sizes of new cells */ + /* Put the new cells into the stack */ + i = ind0; + for (k = 0; k < SplCntInd; k++) { + value = SplPos[SplCnt[k]]; + Part->cls[i] = value; + SplPos[SplCnt[k]] = i; + i += value; + if (i < ind1) { + TheTrace[TraceInd++] = i; + } + } + + /* Permute elements of the cell C */ + iend = ind0 + ElmHitCll[ind0]; + + for (i = ind0; i < iend; i++) { + value = HitVtx[i]; + Edge_Delete(value, NghCounts[value], Cand, tv); + sge = TheGraph[value].e+TheGraph[value].d; + if (NghCounts[value] > 1) { + factorial(&(tv->stats->grpsize1), &(tv->stats->grpsize2), NghCounts[value]); + + if (tv->permInd) ResetAutom(tv->permInd, n, tv); + for (j0=0; j0<NghCounts[value]-1; j0++) { + SETPAIRSAUTANDTREE_PREPROC(sge[j0], sge[j0+1]) + } + SETPAIRSAUTANDTREE_PREPROC(sge[j0], sge[0]) + SPECIALGENERATORS + if (NghCounts[value] > 2) { + if (tv->permInd) ResetAutom(tv->permInd, n, tv); + SETPAIRSAUTANDTREE_PREPROC(sge[0], sge[1]) + if (tv->build_autom) { + SETPAIRSAUT(sge[1], sge[0]) + } + MakeTree(sge[1], sge[0], sg, n, tv, FALSE); + SPECIALGENERATORS + } + } + + j = SplPos[NghCounts[value]]++; /* where HitVtx[i] goes */ + k = Cand->invlab[value]; /* where HitVtx[i] is in lab */ + Cand->lab[k] = Cand->lab[j]; + Cand->lab[j] = value; + Cand->invlab[value] = j; + Cand->invlab[Cand->lab[k]] = k; + NghCounts[value] = 0; + } + + /* Reconstruct the cell C and update the inverse partition */ + newcell = ind1 - ElmHitCll[ind0]; + i = newcell; + ind2 = newcell+Part->cls[newcell]-1; + do { + Part->inv[i] = newcell; + if (i == ind2) { + newcell = i+1; + if (newcell < n) ind2 = newcell+Part->cls[newcell]-1; + } + } + while (++i < ind1); + + for (i = ind0, k = 0; k < SplCntInd; i+=Part->cls[i], k++) { + if ((k > 0) || (SplCnt[0] > 0)) { + if (TheGraph[Cand->lab[i]].d == 1) { + CStack[CStackInd++] = i; + } + } + if (Part->cls[i] == 1) { + Cand->singcode = MASHCOMM(Cand->singcode, Cand->lab[i]); + } + } + + } + } + } + return 1; + } + else { + return 0; + } +} + +void PrintPartition(int *v, int *cls, int n, int l, int line) { + int i, j, k; + + k=0; + fprintf(outfile, "[ "); + for (i=0; i<n; i+=cls[i]) { + if ((cls[i]<=0) || i>=n) { + printf("WRONG"); + break; + } + for (j=i; j<i+cls[i]; j++) { + fprintf(outfile, "%d ", v[j]+l); + if (k++ > 50) { + fprintf(outfile,"\n"); + k=0; + } + } + if ((i+cls[i])<n) fprintf(outfile, "| "); + } + fprintf(outfile, "] at line %d\n", line); + return; +} + +void PrintVect(int *v, int z, int n, int l) { + int i; + printf("["); + for (i = z; i<n; i++) + printf(" %2d", v[i]+l); + printf(" ]\n"); + return; +} + +void PrintWeightedGraph1(sparsegraph *g_arg, int n, char msg[30]) { + int i, j; + int *ngh1, *wgh1; + + printf("%s\n",msg); + for (i=0; i<n; i++) { + ngh1 = g_arg->e+g_arg->v[i]; + wgh1 = g_arg->w+g_arg->v[i]; + printf("%2d: ",i+labelorg); + for (j=0; j<g_arg->d[i]; j++) { + printf("%2d ",ngh1[j]); + printf("(%d) ",wgh1[j]); + } + printf("\n"); + } + printf("\n"); +} + +void PrintWeightedGraph2(int n, char msg[30]) { + int i, j; + int *ngh1, *wgh1; + + printf("%s\n",msg); + for (i=0; i<n; i++) { + ngh1 = TheGraph[i].e; + printf("%2d: ",i+labelorg); + for (j=0; j<TheGraph[i].d; j++) { + printf("%2d ",ngh1[j]+labelorg); + } + printf(";\n"); + } + printf("\n"); +} + +void PrintBlissGraph(int n) { + int i, j; + int *ngh1, *wgh1; + + fprintf(outfile,"p edge %d\n",n); + for (i=0; i<n; i++) { + ngh1 = TheGraph[i].e; + for (j=0; j<TheGraph[i].d; j++) { + if (i < ngh1[j]) { + fprintf(outfile, "e %d %d\n",i+labelorg,ngh1[j]+labelorg); + } + } + } + printf("\n"); +} + +void putgraphplus_sg(FILE *f, sparsegraph *sg, int linelength) +{ + int i,n,curlen,slen; + int *d,*e; + size_t *v,j; + char s[60]; + + n = sg->nv; + SG_VDE(sg,v,d,e); + + for (i = 0; i < n; ++i) + { + fprintf(f,"%3d : ",i+labelorg); + curlen = 7; + + for (j = v[i]; j < v[i]+d[i]; ++j) + { + if (sg->w) { + if (sg->w[j] != 1) { + slen = itos(sg->w[j],s); + if (linelength > 0 && curlen + slen + 1 > linelength) + { + putstring(f,"\n "); + curlen = 2; + } + PUTC(' ',f); + PUTC('w',f); + putstring(f,s); + curlen += slen + 3; + } + } + + slen = itos(e[j]+labelorg,s); + if (linelength > 0 && curlen + slen + 1 > linelength) + { + putstring(f,"\n "); + curlen = 2; + } + PUTC(' ',f); + putstring(f,s); + curlen += slen + 1; + } + putstring(f,";\n"); + } +} + +void quickSort(int *arr, int elements) { + +#define MAX_LEVELS 300 + + int piv, beg[MAX_LEVELS], end[MAX_LEVELS], i = 0, L, R, swap; + int k, value; + + beg[0] = 0; + end[0] = elements; + while (i>= 0) { + L = beg[i]; + R = end[i]-1; + if (L<R-8) { + piv = arr[(L+R)/2]; + arr[(L+R)/2] = arr[L]; + arr[L] = piv; + while (L<R) { + while (arr[R]>= piv && L<R) R--; + if (L<R) arr[L++] = arr[R]; + while (arr[L]<= piv && L<R) L++; + if (L<R) arr[R--] = arr[L]; + } + arr[L] = piv; + beg[i+1] = L+1; + end[i+1] = end[i]; end[i++] = L; + if (end[i]-beg[i]>end[i-1]-beg[i-1]) { + swap = beg[i]; + beg[i] = beg[i-1]; + beg[i-1] = swap; + swap = end[i]; + end[i] = end[i-1]; + end[i-1] = swap; + } + } + else { + i--; + } + } + for (k = 1; k < elements; ++k) { + value = arr[k]; + i = k - 1; + while ((i >= 0) && (value < arr[i])) { + arr[i + 1] = arr[i]; + --i; + } + arr[i + 1] = value; + } +} + +void RemoveFromLevel(int from, int to, int strategy, boolean reinit) { + int i; + + for (i=from; i<=to; i++) { + if (Spine[i].listend) { + (Spine[i].listend)->next = GarbList; + GarbList = Spine[i].liststart; + Spine[i].liststart = Spine[i].listend = NULL; + } + if (strategy == 0 || reinit) { + Spine[i].listcounter = 0; + if (i>from) { + Spine[i].thetracexists = FALSE; + Spine[i].part->code = -1; + } + } + } +} + +searchtrie *searchtrie_make(Candidate *CurrCand, Candidate *NextCand, int n, struct TracesVars *tv) { + + searchtrie *st; + if (tv->strienext == n) { + tv->strienext = 0; + tv->strielist->next = malloc(sizeof(struct trielist)); + if (tv->strielist->next == NULL) { + fprintf(ERRFILE, "\nError, memory not allocated.\n"); + exit(1); + } + tv->strielist->next->prev = tv->strielist; + tv->strielist = tv->strielist->next; + tv->strielist->next = NULL; + tv->strielist->triearray = malloc(n*sizeof(searchtrie)); + if (tv->strielist->triearray == NULL) { + fprintf(ERRFILE, "\nError, memory not allocated.\n"); + exit(1); + } + } + st = &(tv->strielist->triearray[tv->strienext]); + st->father = CurrCand->stnode; + st->name = NextCand->name; + st->index = tv->newindex+1; + st->vtx = NextCand->vertex; + st->level = tv->tolevel; + st->first_child = st->next_sibling = st->last_child = st->goes_to = NULL; + if (st->father) { + if (st->father->first_child) { + st->father->last_child->next_sibling = st; + st->father->last_child = st; + } + else { + st->father->first_child = st->father->last_child = st; + } + } + NextCand->stnode = st; + if (tv->newgotonode) { + tv->newgotonode->goes_to = st; + } + if (tv->gotonode) { + st->goes_to = tv->gotonode; + tv->gotonode = NULL; + } + tv->strienext++; + return st; +} + +trielist *searchtrie_new(int n, struct TracesVars *tv) { + + tv->strielist = malloc(sizeof(struct trielist)); + if (tv->strielist == NULL) { + fprintf(ERRFILE, "\nError, memory not allocated.\n"); + exit(1); + } + tv->strielist->prev = tv->strielist->next = NULL; + tv->strielist->triearray = malloc(n*sizeof(searchtrie)); + if (tv->strielist->triearray == NULL) { + fprintf(ERRFILE, "\nError, memory not allocated.\n"); + exit(1); + } + tv->strielist->triearray[0].father = tv->strielist->triearray[0].first_child = NULL; + tv->strielist->triearray[0].next_sibling = tv->strielist->triearray[0].last_child = NULL; + tv->strielist->triearray[0].goes_to = NULL; + tv->strielist->triearray[0].index = 1; + tv->strielist->triearray[0].name = tv->strielist->triearray[0].level = 0; + tv->strielist->triearray[0].vtx = n; + + tv->strienext = 1; + return tv->strielist; +} + +int Select_from_CStack(int *cls, int CStackInd) { + int j, k; + + j = CStackInd; + k = CStackInd; + while (--j > 0) { + if (cls[CStack[j]] < cls[CStack[k]]) { + k = j; + } + if ((cls[CStack[k]] == 1) || (j < CStackInd - 12)) { + break; + } + } + return k; +} + +boolean SelectNextLevel(int n, struct TracesVars *tv, struct TracesInfo *ti) { + int i, j, val; + Candidate *FirstCand; + boolean orbitcell; + VERB_PRINT("SelNxtLev",3,FALSE) + + switch (tv->compstage) { + case 2: + tv->nextlevel = tv->maxtreelevel; + while (tv->nextlevel >=0) { + if (Spine[tv->nextlevel].liststart) { + break; + } + tv->nextlevel--; + } + if (tv->nextlevel < 0) { + return FALSE; + } + break; + default: + switch (tv->strategy) { + case 0: + tv->nextlevel = tv->fromlevel; + while (!Spine[tv->nextlevel].liststart) { + (tv->nextlevel)++; + } + PRINTF2("SelectNextLevel 1?: finalnumcells: %d; ", tv->finalnumcells); + PRINTF2("Spine[tv->nextlevel].part->cells: %d; ", Spine[tv->nextlevel].part->cells); + PRINTF2("tv->maxtreelevel: %d; ", tv->maxtreelevel); + PRINTF2("tv->nextlevel: %d\n", tv->nextlevel); + if ((Spine[tv->nextlevel].part->cells == tv->finalnumcells) || (tv->nextlevel > tv->maxtreelevel)) { + return FALSE; + } + else { + /* Check the whole group */ + if ((tv->group_level < tv->tolevel) && !ti->first_matching && ti->thegrouphaschanged) { + + FirstCand = Spine[tv->nextlevel].liststart; + val = tv->orbits[FirstCand->lab[Spine[1].tgtcell]]; + for (i=Spine[1].tgtcell; i<Spine[1].tgtend; i++) { + if (tv->orbits[FirstCand->lab[i]] != val) { + break; + } + } + if (i<Spine[1].tgtend) { + orbitcell = FALSE; + } else { + orbitcell = TRUE; + } + if (orbitcell) { + FirstCand = Spine[tv->nextlevel].liststart; + FixBase(fix, tv, FirstCand, 0, tv->firstpathlength); + if (tv->options->verbosity >= 2) tv->schreier1 -= CPUTIME; + getorbitsmin(fix, tv->nfix, gpB, &gensB, &tv->currorbit, NULL, n, n, TRUE); + if (tv->options->verbosity >= 2) tv->schreier1 += CPUTIME; + for (j=2; j<=tv->firstpathlength; j++) { + tv->currorbit = findcurrorbits(gpB, j-1); + val = tv->currorbit[FirstCand->lab[Spine[j].tgtcell]]; + for (i=Spine[j].tgtcell; i<Spine[j].tgtend; i++) { + if (tv->currorbit[FirstCand->lab[i]] != val) { + break; + } + } + if (i<Spine[j].tgtend) { + break; + } + } + tv->group_level = j-1; + if (tv->group_level >= tv->tolevel) { + ti->thegrouphaschanged = FALSE; + } + } + /* End check the whole group */ + } + + } + break; + case 1: + tv->nextlevel = tv->maxtreelevel; + PRINTF2("SelectNextLevel 2?: finalnumcells: %d; ", tv->finalnumcells); + PRINTF2("Spine[tv->nextlevel].part->cells: %d; ", Spine[tv->nextlevel].part->cells); + if (Spine[tv->nextlevel].part->cells == tv->finalnumcells) { + (tv->nextlevel)--; + } + while (tv->nextlevel >= 0) { + if (Spine[tv->nextlevel].liststart) { + break; + } + tv->nextlevel--; + } + if (tv->nextlevel < 0) { + return FALSE; + } + break; + default: + break; + } + break; + } + return TRUE; +} + +void SetAutom(int q, int n, struct TracesVars *tv) { + int i; + + for (i=0; i<q; i++) { + AUTPERM[PrmPairs[i].arg] = PrmPairs[i].val; + } + return; +} + +void ResetAutom(int q, int n, struct TracesVars *tv) { + int i; + + if (n/q < 256) { + memcpy(AUTPERM, IDENTITY_PERM, n*sizeof(int)); + } + else { + for (i=0; i<q; i++) { + AUTPERM[PrmPairs[i].arg] = PrmPairs[i].arg; + } + } + tv->permInd = 0; + return; +} + +void sort_Split_Array(int *Array, int Ind){ + int i, k, value; + + switch (Ind) { + case 0: + case 1: + break; + case 2: + if (Array[0] > Array[1]) { + value = Array[0]; + Array[0] = Array[1]; + Array[1] = value; + } + break; + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + for (k = 1; k < Ind; ++k) { + value = Array[k]; + i = k - 1; + while ((i >= 0) && (value < Array[i])) { + Array[i + 1] = Array[i]; + --i; + } + Array[i + 1] = value; + } + break; + default: + quickSort(Array, Ind); + break; + } +} + +int spinelementorbsize(int *orbits, int *lab, int size, int elem) { + int i, j, val; + + j = 0; + val = orbits[elem]; + for (i = 0; i < size; ++i) { + if (orbits[lab[i]] == val) ++j; + } + return j; +} + +boolean TargetCell(Candidate *TargCand, Partition *Part, int n, struct TracesVars* tv, int Lv) { + int TCell = -1, TCSize = 1; + int i; + VERB_PRINT("TCELL",3,FALSE) + if (Part->cells == n) { + tv->finalnumcells = n; + return FALSE; + } + if (tv->maxdeg <=2) { + return FALSE; + } + + if (Lv < tv->tcellevel) { + tv->tcell = Spine[Lv+1].tgtcell; + return TRUE; + } + else { + if (Part->cls[0] == n) { + tv->tcell = 0; + return TRUE; + } + while (TCell < 0) { + for (i = Spine[Lv].tgtcell; i < Spine[Lv].tgtend; i += Part->cls[i]) { + if (Part->cls[i] > TCSize) { + if (NonSingDeg(TargCand->lab[i], TargCand, Part) > 2) { + TCSize = Part->cls[i]; + TCell = i; + } + } + } + Lv--; + if ((Lv < 0) && (TCell < 0)) return FALSE; + } + tv->tcell = TCell; + return TRUE; + } +} + +int TargetCellExpPath(Candidate *TargCand, Partition *Part, struct TracesVars* tv) { + int Lv, n; + VERB_PRINT("TCEP",3,FALSE) + + n = tv->input_graph->nv; + if (Part->cells == n) { + return 0; + } + + Lv = tv->tolevel_tl+1; + SpineTL_tl = Spine+Lv; + if (tv->tolevel_tl < tv->tcellevel) { + tv->tcellexpath = Part->inv[SpineTL_tl->tgtcell]; + tv->tolevel_tl++; + if (Part->cls[tv->tcellexpath] == 1) { + if (tv->options->verbosity >= 2) { + if (tv->tolevel_tl-tv->tolevel == 6) { + fprintf(outfile, "... "); + } + } + return TargetCellExpPath(TargCand, Part, tv); + } else { + return 1+((Spine[tv->tolevel_tl].tgtcell >= Spine[tv->tolevel_tl-1].tgtcell) && (Spine[tv->tolevel_tl].tgtend <= Spine[tv->tolevel_tl-1].tgtend)); + } + } + else { + if (TargetCellFirstPath(TargCand, Part, tv)) { + return 1+((Spine[tv->tolevel_tl].tgtcell >= Spine[tv->tolevel_tl-1].tgtcell) && (Spine[tv->tolevel_tl].tgtend <= Spine[tv->tolevel_tl-1].tgtend)); + } + else { + return 0; + } + } +} + +boolean TargetCellFirstPath(Candidate *TargCand, Partition *Part, struct TracesVars* tv) { + int n, TCell, TCSize, TCell1, TCSize1; + int Lv, i, Lev, vtx, vtx_d; + int loopstart, loopend; + boolean divided; + VERB_PRINT("TCFP",3,FALSE) + + n = tv->input_graph->nv; + + if (Part->cells == n) { + return 0; + } + + Lev = tv->tolevel_tl; + Lv = tv->tolevel_tl; + + TCell = TCell1 = -1; + TCSize = TCSize1 = 1; + + while (TCell < 0) { + + if (tv->compstage == 2) { + loopstart = Spine[Lv].tgtcell; + divided = FALSE; + } else { + loopstart = Part->inv[Spine[Lv].tgtcell]; + divided = FALSE; + + if (Lv == tv->lastlev) { + loopstart = Part->inv[tv->lastcell]; + divided = TRUE; + } + } + + i = loopstart; + loopend = Spine[Lv].tgtend; + while (i < loopend) { + if (Part->cls[i] > TCSize) { + vtx = TargCand->lab[i]; + vtx_d = TheGraph[vtx].d; + if (vtx_d > 2) { + if (NonSingDeg(vtx, TargCand, Part) > 2) { + TCSize = Part->cls[i]; + TCell = i; + if (TCSize == WorkArray[Lv]) { + break; + } + } + } + } + i += Part->cls[i]; + if (divided && (i == loopend)) { + i = loopstart = Part->inv[Spine[Lv].tgtcell]; + loopend = tv->lastcell; + divided = FALSE; + TCSize1 = TCSize; + TCell1 = TCell; + TCell = -1; + TCSize = 1; + } + } + + if (TCSize1 > TCSize) { + TCell = TCell1; + TCSize = TCSize1; + } + + if (TCell < 0) { + if (Lv == 0) { + if (tv->answ == 2) { + tv->finalnumcells = minint(Part->cells,tv->finalnumcells); /* 160712 */ + tv->finalnumcells = Part->cells; + } + return FALSE; + } else { + Lv = Spine[Lv].tgtfrom; + } + } + } + + tv->tcellexpath = tv->lastcell = TCell; + tv->tolevel_tl++; + + Spine[tv->tolevel_tl].tgtfrom = tv->lastlev = Lv; + Spine[tv->tolevel_tl].tgtcell = tv->tcellexpath; + Spine[tv->tolevel_tl].tgtsize = WorkArray[Lv] = TCSize; + Spine[tv->tolevel_tl].tgtend = Spine[tv->tolevel_tl].tgtcell + TCSize; + Spine[tv->tolevel_tl].tgtpos = Spine[tv->tolevel_tl].tgtend - 1; + tv->tcellevel = tv->tolevel_tl; + + if (Lv != Lev) { + BreakSteps[Lev] = ++tv->brkstpcount; + if (Spine[tv->tolevel].liststart) { + if (!Spine[tv->tolevel].liststart->firstsingcode) { + Spine[tv->tolevel].liststart->firstsingcode = Spine[tv->tolevel].liststart->pathsingcode; + } + } + } + return TRUE; +} + +void traces_freedyn(void) { + /* Free the static dynamic memory used by Traces */ +#if !MAXN + DYNFREE(AUTPERM, AUTPERM_sz); + DYNFREE(BreakSteps, BreakSteps_sz); + DYNFREE(CStack, CStack_sz); + DYNFREE(CurrOrbSize, CurrOrbSize_sz); + DYNFREE(CurrRefCells, CurrRefCells_sz); + DYNFREE(Diff, Diff_sz); + DYNFREE(Factorials, Factorials_sz); + DYNFREE(fix, fix_sz); + DYNFREE(IDENTITY_PERM, IDENTITY_PERM_sz); + DYNFREE(Markers, Markers_sz); + DYNFREE(TreeMarkers, TreeMarkers_sz); + DYNFREE(AutMarkers, AutMarkers_sz); + DYNFREE(MarkHitVtx, MarkHitVtx_sz); + DYNFREE(MultRefCells, MultRefCells_sz); + DYNFREE(NghCounts, NghCounts_sz); + DYNFREE(OrbSize, OrbSize_sz); + DYNFREE(OrbList, OrbList_sz); + DYNFREE(PrmPairs, PrmPairs_sz); + DYNFREE(TempOrbList, TempOrbList_sz); + DYNFREE(RefCells, RefCells_sz); + DYNFREE(RefPath, RefPath_sz); + DYNFREE(Singletons, Singletons_sz); + DYNFREE(SplCls, SplCls_sz); + DYNFREE(SplCnt, SplCnt_sz); + DYNFREE(SplPos, SplPos_sz); + DYNFREE(StackMarkers, StackMarkers_sz); + DYNFREE(TheTrace, TheTrace_sz); + DYNFREE(TheTraceCC, TheTraceCC_sz); + DYNFREE(TheTraceSplNum, TheTraceSplNum_sz); + DYNFREE(TheTraceSteps, TheTraceSteps_sz); + DYNFREE(TEMPLAB, TEMPLAB_sz); + DYNFREE(TEMPINVLAB, TEMPINVLAB_sz); + DYNFREE(WeightsSeq, WeightsSeq_sz); + DYNFREE(WorkArray, WorkArray_sz); + DYNFREE(WorkArray0, WorkArray0_sz); + DYNFREE(WorkArray1, WorkArray1_sz); + DYNFREE(WorkArray2, WorkArray2_sz); + DYNFREE(WorkArray3, WorkArray3_sz); + DYNFREE(WorkArray4, WorkArray4_sz); + DYNFREE(WorkArray5, WorkArray5_sz); + DYNFREE(WorkArray6, WorkArray6_sz); + DYNFREE(WorkArray7, WorkArray7_sz); + DYNFREE(Neighbs1, Neighbs1_sz); + DYNFREE(Neighbs2, Neighbs2_sz); + DYNFREE(TreeStack, TreeStack_sz); + DYNFREE(Spine, Spine_sz); + DYNFREE(TrieArray, TrieArray_sz); + DYNFREE(TheGraph, TheGraph_sz); + DYNFREE(EPCodes, EPCodes_sz); + DYNFREE(CyclesPart, CyclesPart_sz); + DYNFREE(CyclesLength, CyclesLength_sz); +#endif +} + +boolean TreeFyTwo(int From, Candidate *Cand1, Candidate *Cand2, Partition *Part, int n, + struct TracesVars* tv, struct TracesInfo *ti) { + int i, i1, i2, j1, j2, k; + int vtx1, vtx2, ngh1, ngh2, arg, val; + int *tgtc1, *tgtc2, *adj1, *adj2; + int iend; + + SETMARK(Markers, tv->mark) + + i2=0; + + if (tv->permInd) ResetAutom(tv->permInd, n, tv); + i1 = Spine[From].tgtsize; + tgtc1 = Cand1->lab+Spine[From].tgtcell; + tgtc2 = Cand2->lab+Spine[From].tgtcell; + for (i=0; i<i1; i++) { + arg = tgtc1[i]; + val = tgtc2[i]; + if ((Markers[arg] != tv->mark) && (Markers[val] != tv->mark)) { + SETPAIRSAUT(arg, val) + SETPAIRSAUT(val, arg) + Markers[arg] = tv->mark; + Markers[val] = tv->mark; + } else { + return FALSE; /* 160715 */ + } + } + + while (i2 < tv->permInd) { + vtx1 = PrmPairs[i2].arg; + vtx2 = PrmPairs[i2++].val; + adj1 = TheGraph[vtx1].e; + adj2 = TheGraph[vtx2].e; + iend = TheGraph[vtx1].d; + j1 = j2 = 0; + for (k=0; k < iend; k++) { + ngh1 = adj1[k]; + if (Markers[ngh1] != tv->mark) { + Neighbs1[j1++] = Cand1->invlab[ngh1]; + } + ngh2 = adj2[k]; + if (Markers[ngh2] != tv->mark) { + Neighbs2[j2++] = Cand2->invlab[ngh2]; + } + } + + k = tv->permInd; + if (j1 == j2) { + quickSort(Neighbs1, j1); + quickSort(Neighbs2, j2); + for (i=0; i<j1; i++) { + arg = Cand1->lab[Neighbs1[i]]; + val = Cand2->lab[Neighbs2[i]]; + if ((Markers[arg] != tv->mark) && (Markers[val] != tv->mark)) { + SETPAIRSAUT(arg, val) + SETPAIRSAUT(val, arg) + Markers[arg] = tv->mark; + Markers[val] = tv->mark; + } + } + } + } + return TRUE; +} + +/***************************************************************************** + * * + * trie_class(t,c) classifies vertices according to weights of their edges * + * * + *****************************************************************************/ + +void trie_class(trie *t, int *count) { + + if (t->first_child == NULL) { + WeightsSeq[t->value] = *count; + if (t->next_sibling == NULL) (*count)++; + return; + } + else { + t = t->first_child; + while (t) { + trie_class(t,count); + t = t->next_sibling; + } + } +} + +int trie_classify(int n, TracesVars *tv) { + + int i, j, ord; + int *ngh1, *wgh1; + + trieroot = trie_new(n, tv); + ord = 0; + + for (i=0; i<n; i++) { + ngh1 = TheGraph[i].e; + wgh1 = TheGraph[i].w; + sort2ints(wgh1, ngh1, TheGraph[i].d); + + trieref = trieroot; + for (j=0; j<TheGraph[i].d; j++) { + trieref = trie_make(trieref, wgh1[j], n, tv); + } + trieref = trie_make(trieref, n, n, tv); + trie_make(trieref, i, n, tv); + } + trie_class(trieroot,&ord); + + for (i=0; i<=tv->triepos; i++) { + free(TrieArray[i]); + } + trieroot = NULL; + return ord-1; +} + +struct trie *trie_comp(trie *t, int value) { + + if (t->first_child) { + t = t->first_child; + while (t) { + if (value != t->value) { + t = t->next_sibling; + } + else { + break; + } + } + return t; + } + else { + return NULL; + } +} + +void trie_dump(trie *t) { + if (t->first_child == NULL) return; + else { + printf("( "); + t = t->first_child; + while (t) { + printf("%d ",t->value); + trie_dump(t); + t = t->next_sibling; + } + printf(") "); + } +} + +/***************************************************************************** + * * + * trie_make(t,v,n,tv) places the value v into the trie t * + * * + *****************************************************************************/ + +struct trie *trie_make(trie *t, int value, int n, struct TracesVars* tv) { + trie *t1; + + t1 = t; + if (tv->trienext == n) { + tv->trienext = 0; + tv->triepos++; + TrieArray[tv->triepos] = malloc(n*sizeof(trie)); + if (TrieArray[tv->triepos] == NULL) { + fprintf(ERRFILE, "\nError, memory not allocated.\n"); + exit(1); + } + } + if (t->first_child) { + t = t->first_child; + if (value < t->value) { + t1->first_child = &TrieArray[tv->triepos][tv->trienext++]; + t1->first_child->next_sibling = t; + t1->first_child->first_child = NULL; + t = t1->first_child; + t->value = value; + return t; + } + while (value > t->value) { + t1 = t; + if (t->next_sibling) { + t = t->next_sibling; + } + else break; + } + if (value == t->value) { + return t; + } + t1->next_sibling = &TrieArray[tv->triepos][tv->trienext++]; + t1->next_sibling->first_child = t1->next_sibling->next_sibling = NULL; + if (t != t1) { + t1->next_sibling->next_sibling = t; + } + t = t1->next_sibling; + } + else { + t->first_child = &TrieArray[tv->triepos][tv->trienext++]; + t = t->first_child; + t->first_child = t->next_sibling = NULL; + } + t->value = value; + return t; +} + +struct trie *trie_new(int n, struct TracesVars* tv) { + + TrieArray[0] = malloc(n*sizeof(trie)); + if (TrieArray[0] == NULL) { + fprintf(ERRFILE, "\nError, memory not allocated.\n"); + exit(1); + } + TrieArray[0][0].first_child = TrieArray[0][0].next_sibling = NULL; + tv->triepos = 0; + tv->trienext = 1; + return TrieArray[0]; +} + +boolean VerifyCand(Candidate *Cand, int n, int line) { + int i, k; + + for (i=0; i<n; i++) { + k=Cand->lab[i]; + if (Cand->invlab[k] != i) { + printf("Cand->invlab wrong at %d (vtx: %d), line %d\n", i, k, line); + PrintVect(Cand->lab, 0, n, 0); + PrintVect(Cand->invlab, 0, n, 0); + return FALSE; + } + } + return TRUE; +} + +boolean VerifyId(int *p, int n) { + int i, r; + + r = TRUE; + for (i=0; i<n; i++) { + if (p[i] != i) { + printf("p[%d] = %d\n", i, p[i]); + r = FALSE; + } + } + return r; +} + +boolean VerifyPart(Partition *Part, int start, int end) { + int i,j; + + for (i=start; i<end; i+=Part->cls[i]) { + if (Part->cls[i] == 0 || i>=end) { + printf("WRONG cls\n"); + return FALSE; + } + for (j=0; j<Part->cls[i]; j++) { + if (Part->inv[i+j] != i) { + printf("WRONG inv\n"); + return FALSE; + } + } + } + printf("OK\n"); + return TRUE; +} + +int VerifyPerm(int *perm, int n,int where) { + int i; + + memset(Markers, 0, n*sizeof(int)); + + for (i=0; i<n; i++) { + if ((perm[i] >= n) || (Markers[perm[i]])) { + fprintf(stderr,"wrong permutation @ %d\n",where); + PrintVect(perm,0,i+1,labelorg); + } + Markers[perm[i]] = TRUE; + } + return TRUE; +} + +/***************************************************************************** + * * + * WeightCodes(n) transforms the weight w(u,v) of an edge into a new weight * + * W(u,v) such that W(a,b) = W(c,d) iff w(a,b) = w(c,d) and w(b,a) = w(d,c). * + * * + *****************************************************************************/ + +void WeightCodes(int n) { + int i,j,aux; + int sumdegs; + int deg, vtx1, vtx2, *ngh1, *ngh2, *wgh1, *wgh2, ord; + + sumdegs = 0; + for (i=0; i<n; i++) { + sumdegs += TheGraph[i].d; + } + + DYNALLSTAT(int, VArray, VArray_sz); + DYNALLSTAT(weightwhere, WArray, WArray_sz); + DYNALLSTAT(grph_strct, TheAuxGraph, TheAuxGraph_sz); + + DYNALLOC1(int, VArray, VArray_sz, sumdegs, "WeightCodes"); + DYNALLOC1(weightwhere, WArray, WArray_sz, sumdegs, "WeightCodes"); + DYNALLOC1(grph_strct, TheAuxGraph, TheAuxGraph_sz, n, "WeightCodes"); + + memcpy(TheAuxGraph,TheGraph,n*sizeof(grph_strct)); + + ord = 0; + for (vtx1=0; vtx1<n; vtx1++) { + ngh1 = (TheAuxGraph[vtx1].e)++; + wgh1 = TheAuxGraph[vtx1].w; + deg = TheAuxGraph[vtx1].d; + for (i=0; i<deg; i++) { + vtx2 = ngh1[i]; + ngh2 = (TheAuxGraph[vtx2].e)++; + wgh2 = (TheAuxGraph[vtx2].w)++; + (TheAuxGraph[vtx1].d)--; + (TheAuxGraph[vtx2].d)--; + VArray[ord] = wgh1[i]; + WArray[ord].weight = wgh2[0]; + WArray[ord++].ref = (TheAuxGraph[vtx1].w)++; + VArray[ord] = wgh2[0]; + WArray[ord].weight = wgh1[i]; + WArray[ord++].ref = wgh2; + } + } + + sortweights(VArray,WArray,ord); + + /* swap VArray and WArray.weight */ + for (i=0; i<sumdegs; i++) { + aux = VArray[i]; + VArray[i] = WArray[i].weight; + WArray[i].weight = aux; + } + + i = j = 0; + do { + if (WArray[i].weight == WArray[j].weight) { + j++; + } else { + sortweights(VArray+i,WArray+i,j-i); + i = j; + } + } while (j<sumdegs); + sortweights(VArray+i,WArray+i,j-i); + + /* weight class */ + ord = 0; + *(WArray[0].ref) = 0; + for (i=1; i<sumdegs; i++) { + if ((WArray[i].weight != WArray[i-1].weight) || (VArray[i] != VArray[i-1])) { + ord++; + } + *(WArray[i].ref) = ord; + } + + DYNFREE(VArray, VArray_sz); + DYNFREE(WArray, WArray_sz); + DYNFREE(TheAuxGraph, TheAuxGraph_sz); + +} + + + +boolean TargetCellSmall(Candidate *TargCand, Partition *Part, int n, struct TracesVars* tv, int Lv) { + int TCell = -1, TCSize = n; + int i; + + if (tv->maxdeg <=2) { + return FALSE; + } + + if (Lv < tv->tcellevel) { + tv->tcell = Spine[Lv+1].tgtcell; + return TRUE; + } + else { + if (Part->cls[0] == n) { + tv->tcell = 0; + return TRUE; + } + while (TCell < 0) { + for (i = Spine[Lv].tgtcell; i < Spine[Lv].tgtend; i += Part->cls[i]) { + if (Part->cls[i] < TCSize) { + if (NonSingDeg(TargCand->lab[i], TargCand, Part) > 2) { + TCSize = Part->cls[i]; + TCell = i; + } + } + } + Lv--; + if ((Lv < 0) && (TCell < 0)) return FALSE; + } + tv->tcell = TCell; + return TRUE; + } +} + +int TargetCellExpPathSmall(Candidate *TargCand, Partition *Part, struct TracesVars* tv) { + int Lv, n; + + n = tv->input_graph->nv; + if (Part->cells == n) { + return 0; + } + + Lv = tv->tolevel_tl+1; + SpineTL_tl = Spine+Lv; + if (tv->tolevel_tl < tv->tcellevel) { + tv->tcellexpath = Part->inv[SpineTL_tl->tgtcell]; + tv->tolevel_tl++; + if (Part->cls[tv->tcellexpath] == 1) { + if (tv->options->verbosity >= 2) { + if (tv->tolevel_tl-tv->tolevel == 6) { + fprintf(outfile, "... "); + } + } + return TargetCellExpPath(TargCand, Part, tv); + } else { + return 1+((Spine[tv->tolevel_tl].tgtcell >= Spine[tv->tolevel_tl-1].tgtcell) && (Spine[tv->tolevel_tl].tgtend <= Spine[tv->tolevel_tl-1].tgtend)); + } + } + else { + if (TargetCellFirstPath(TargCand, Part, tv)) { + return 1+((Spine[tv->tolevel_tl].tgtcell >= Spine[tv->tolevel_tl-1].tgtcell) && (Spine[tv->tolevel_tl].tgtend <= Spine[tv->tolevel_tl-1].tgtend)); + } + else { + return 0; + } + } +} + +boolean TargetCellFirstPathSmall(Candidate *TargCand, Partition *Part, struct TracesVars* tv) { + int n, TCell, TCSize, TCell1, TCSize1; + int Lv, i, Lev, vtx, vtx_d; + int loopstart, loopend; + boolean divided; + + n = tv->input_graph->nv; + if (Part->cells == n) { + return 0; + } + Lev = tv->tolevel_tl; + Lv = tv->tolevel_tl; + TCell = TCell1 = -1; + TCSize = TCSize1 = n; + + while (TCell < 0) { + loopstart = Part->inv[Spine[Lv].tgtcell]; + divided = FALSE; + + if (Lv == tv->lastlev) { + loopstart = Part->inv[tv->lastcell]; + divided = TRUE; + } + + i = loopstart; + loopend = Spine[Lv].tgtend; + + while (i < loopend) { + if ((Part->cls[i] > 1) && (Part->cls[i] < TCSize)) { + vtx = TargCand->lab[i]; + vtx_d = TheGraph[vtx].d; + + if (vtx_d > 2) { + if (NonSingDeg(vtx, TargCand, Part) > 2) { + TCSize = Part->cls[i]; + TCell = i; + if (TCSize == WorkArray[Lv]) { + break; + } + } + } + } + i += Part->cls[i]; + if (divided && (i == loopend)) { + i = loopstart = Spine[Lv].tgtcell; + loopend = tv->lastcell; + divided = FALSE; + TCSize1 = TCSize; + TCell1 = TCell; + TCell = -1; + TCSize = n; + } + } + + if (TCSize1 < TCSize) { + TCell = TCell1; + TCSize = TCSize1; + } + + if (TCell < 0) { + if (Lv == 0) { + tv->finalnumcells = minint(Part->cells,tv->finalnumcells); /* 160712 */ + tv->finalnumcells = Part->cells; + return FALSE; + } else { + Lv = Spine[Lv].tgtfrom; + } + } + } + tv->tcellexpath = tv->lastcell = TCell; + tv->tolevel_tl++; + + Spine[tv->tolevel_tl].tgtfrom = tv->lastlev = Lv; + Spine[tv->tolevel_tl].tgtcell = tv->tcellexpath; + Spine[tv->tolevel_tl].tgtsize = WorkArray[Lv] = TCSize; + Spine[tv->tolevel_tl].tgtend = Spine[tv->tolevel_tl].tgtcell + TCSize; + Spine[tv->tolevel_tl].tgtpos = Spine[tv->tolevel_tl].tgtend - 1; + tv->tcellevel = tv->tolevel_tl; + + if (Lv != Lev) { + BreakSteps[Lev] = ++tv->brkstpcount; + if (Spine[tv->tolevel].liststart) { + if (!Spine[tv->tolevel].liststart->firstsingcode) { + Spine[tv->tolevel].liststart->firstsingcode = Spine[tv->tolevel].liststart->pathsingcode; + } + } + } + return TRUE; +} diff --git a/graph-checker/nauty/traces.h b/graph-checker/nauty/traces.h new file mode 100644 index 0000000..a78738e --- /dev/null +++ b/graph-checker/nauty/traces.h @@ -0,0 +1,73 @@ +/****************************************************************************** + * * + * This is the header file for traces() version 2.2, which is included into * + * nauty() version 2.8.6. * + * * + * nauty is Copyright (1984-2018) Brendan McKay. All rights reserved. * + * Traces is Copyright Adolfo Piperno, 2008-2018. All rights reserved. * + * See the file COPYRIGHT for the details of the software license. * + * * + * CHANGE HISTORY * + * 28-Dec-12 : final changes for version 2.0 * + * 20-Jan-13 : add code for ^C catching in Traces * + * 29-Mar-13 : bug correction in automorphism mode * + * 02-Apr-13 : add preprocessing * + * 21-May-13 : bug correction (coloured lists) * + * 29-Jun-13 : bug correction (coloured lists and cycles) * + * 07-Dec-13 : bug correction in automorphism mode (wrong group size * + * due to randomness in Schreier-Sims orbit computation) * + * bug correction (discrete initial partition) * + * 15-Feb-14 : CPUDEFS removed (already declared in gtools.h) * + * 01-Sep-15 : add weighted edges (not active) * + * 28-Jan-16 : version ready for nauty and Traces v.2.6 distribution * + * 12-Jul-16 : bug correction (reaching degree 2 vertices) * + * 07-Jun-18 : bug correction (finalnumcells, thanks R.Kralovic) * + * 07-Jun-18 : bug correction (index computation when findperm) * + * 10-Nov-22 : bug correction (cycles in degree 2 subgraphs) * +******************************************************************************/ + +#include "gtools.h" +#include "schreier.h" + +typedef struct TracesOptions { + boolean getcanon; + boolean writeautoms; + boolean cartesian; + boolean digraph; + boolean defaultptn; + int linelength; + FILE* outfile; + int strategy; /* Only the value 0 is supported in this version. */ + int verbosity; + permnode **generators; + void (*userautomproc)(int,int*,int); + int (*usercanonproc)(graph*,int*,graph*,int,int,int,int); + boolean weighted; +} TracesOptions; + +#define DEFAULTOPTIONS_TRACES(opts) TracesOptions opts \ += { FALSE, FALSE, FALSE, FALSE, TRUE, 0, NULL, 0, 0, NULL, NULL, NULL, FALSE } + +typedef struct TracesStats { + double grpsize1; + int grpsize2; + int numgenerators; + int numorbits; + int treedepth; + int canupdates; + int errstatus; + unsigned long numnodes; + unsigned long interrupted; + unsigned long peaknodes; +} TracesStats; + +#ifdef __cplusplus +extern "C" { +#endif +extern void Traces(sparsegraph*,int*,int*,int*,TracesOptions*, + TracesStats*,sparsegraph*); +extern void refine_tr(sparsegraph*,int*,int*,int*,int*,TracesOptions*); +extern void traces_freedyn(void); +#ifdef __cplusplus +} +#endif diff --git a/graph-checker/src/geng.rs b/graph-checker/src/geng.rs new file mode 100644 index 0000000..a998b28 --- /dev/null +++ b/graph-checker/src/geng.rs @@ -0,0 +1,65 @@ +use std::iter::Iterator; +use std::ptr; + +#[allow(non_camel_case_types)] +enum geng_iterator {} + +extern "C" { + fn geng_iterator_create( + iter: *const *mut geng_iterator, + graph_size: usize, + batch_size: usize, + ); + fn geng_iterator_next(iter: *const geng_iterator, g: *mut u32) -> bool; + fn geng_iterator_destroy(iter: *const geng_iterator); + fn printgraph(g: *const u32, n: usize); +} + +fn print_graph(g: Vec<u32>, n: usize) { + unsafe { + printgraph(g.as_ptr(), n); + } +} + +pub struct GengIterator { + pub size: usize, + iter: Box<geng_iterator>, +} + +impl GengIterator { + fn new(n: usize) -> GengIterator { + let iter = unsafe { + let iter: *mut geng_iterator = ptr::null_mut(); + geng_iterator_create(&iter, n, 10000); + Box::from_raw(iter) + }; + GengIterator { size: n, iter } + } +} + +impl Iterator for &GengIterator { + type Item = Vec<u32>; + + fn next(&mut self) -> Option<Self::Item> { + let mut g = vec![0; self.size]; + let res; + unsafe { + let ptr: *const geng_iterator = &*self.iter; + res = geng_iterator_next(ptr, g.as_mut_ptr()) + } + if res { + Some(g) + } else { + None + } + } +} + +impl Drop for GengIterator { + fn drop(&mut self) { + unsafe { + let ptr: *const geng_iterator = &*self.iter; + geng_iterator_destroy(ptr); + } + } +} diff --git a/graph-checker/src/graph.rs b/graph-checker/src/graph.rs index 581c522..dd8a6f4 100644 --- a/graph-checker/src/graph.rs +++ b/graph-checker/src/graph.rs @@ -1,7 +1,6 @@ use std::collections::HashSet; use std::fmt; -// TODO: manual Debug impl #[derive(Clone, PartialEq, Eq)] pub struct Graph { pub size: usize, @@ -32,7 +31,6 @@ impl fmt::Display for Graph { } } -// TODO: manual Debug impl impl fmt::Debug for Graph { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "\n")?; @@ -51,7 +49,6 @@ impl fmt::Debug for Graph { } } -// TODO: impl Cutset fn trim_cutset(cutset: &Cutset) -> Graph { let mut mat = vec![vec![0; cutset.cardinality]; cutset.cardinality]; @@ -112,7 +109,6 @@ impl Graph { for character in line.chars() { chars.push((character as i32 - 63) as u8); } - // TODO: spec allows multi-byte vector size let size = chars[0] as usize; let bytes = &chars[1..]; @@ -265,7 +261,6 @@ impl Graph { self.count_components() == 1 } - // TODO: replace with is_n_connected pub fn is_2_connected(&self) -> bool { let mut vertices = vec![1; self.size]; for i in 0..self.size { @@ -280,7 +275,6 @@ impl Graph { return true; } - // TODO: replace with is_n_connected pub fn is_3_connected(&self) -> bool { let mut vertices = vec![1; self.size]; for i in 0..self.size { @@ -302,7 +296,6 @@ impl Graph { return true; } - // TODO: replace with is_n_connected pub fn is_4_connected(&self) -> bool { let mut vertices = vec![1; self.size]; for i in 0..self.size { @@ -328,11 +321,6 @@ impl Graph { return true; } - pub fn is_n_connected(&self, n: usize) -> bool { - // TODO: implement - todo!(); - } - pub fn neighbors(&self, vert: i32) -> Vec<i32> { let mut res = Vec::new(); @@ -378,7 +366,7 @@ impl Graph { pub fn get_toughness(&self) -> f64 { let mut left: f64 = 0.0; - let mut right: f64 = 1024.0; // Reasonable limit + let mut right: f64 = 1024.0; let eps: f64 = 1e-9; while (right - left).abs() > eps { diff --git a/graph-checker/src/main.rs b/graph-checker/src/main.rs index 58b6a1c..628ce92 100644 --- a/graph-checker/src/main.rs +++ b/graph-checker/src/main.rs @@ -17,6 +17,16 @@ struct Counters { dirac_hamiltonian: i32, ore_hamiltonian: i32, posa_hamiltonian: i32, + t3_1: i32, + t3_2: i32, + c3_5: i32, + t85: i32, + t86: i32, + t87: i32, + t88: i32, + t96: i32, + conj17: i32, + all_2c: i32, } impl Counters { @@ -31,6 +41,16 @@ impl Counters { dirac_hamiltonian: 0, ore_hamiltonian: 0, posa_hamiltonian: 0, + t3_1: 0, + t3_2: 0, + c3_5: 0, + t85: 0, + t86: 0, + t87: 0, + t88: 0, + t96: 0, + conj17: 0, + all_2c: 0, }; } } @@ -50,121 +70,115 @@ fn main() { let start = Instant::now(); let closure = g.get_closure(); let is_complete = closure.is_complete(); - println!("Graph:\n{g}"); - println!("Closure:\n{closure}"); - println!("{is_complete}"); - - // let toughness = if g.is_complete() { - // f64::MAX - // } else { - // g.get_toughness() - // }; - // println!("{toughness}"); - - // if toughness >= 1.0 { - // counters.tough_1 += 1; - // } - // if toughness >= 2.0 { - // counters.tough_2 += 1; - // } - - // let min_degree = g.min_degree() as f64; - // println!("{min_degree}"); - - // TODO: add counter - // if forbidden::theorem3_1(&g) { - // println!("g6:t3_1:{}", line); - // } - - // TODO: add counter - // if forbidden::theorem3_2(&g) { - // println!("g6:t3_2:{}", line); - // } - - // TODO: add counter - // if forbidden::corollary3_5(&g) { - // println!("g6:c3_5:{}", line); - // } - - // if forbidden::theorem85(&g) { - // println!("g6:t85:{}", line); - // } - // if forbidden::theorem86(&g) { - // println!("g6:t86:{}", line); - // } - // if forbidden::theorem87(&g) { - // println!("g6:t87:{}", line); - // } - // if forbidden::theorem88(&g) { - // println!("g6:t88:{}", line); - // } - // if forbidden::theorem96(&g) { - // println!("g6:t96:{}", line); - // } - // if forbidden::conjecture17(&g) { - // println!("g6:conj17:{}", line); - // } - // if forbidden::theorem_all_2con(&g) { - // println!("g6:all_2c:{}", line); - // } - - // if toughness::theorem15(&g, toughness, min_degree) { - // counters.t15_hamiltonian += 1; - // println!("g6:bigalke-jung:{}", line); - // } - - // if toughness::theorem25(&g, toughness, min_degree) { - // counters.t25_hamiltonian += 1; - // println!("g6:bauer:{}", line); - // } - - // if basic::dirac_theorem(&g) { - // counters.dirac_hamiltonian += 1; - // println!("g6:dirac:{}", line); - // } - - // if basic::ore_theorem(&g) { - // counters.ore_hamiltonian += 1; - // println!("g6:ore:{}", line); - // } - - // if basic::posa_theorem(&g) { - // counters.posa_hamiltonian += 1; - // println!("g6:posa:{}", line); - // } - - // if is_complete { - // counters.bch_hamiltonian += 1; - // println!("g6:bondy-chvatal:{}", line); - // } - - // if is_complete && false { - // println!("Graph: {line}\n{g}"); - - // let components_count = g.count_components(); - // println!("Components count: {components_count}"); - // let min_degree = g.min_degree(); - // println!("Minimal degree: {min_degree}"); - - // let cutset = g.max_independent_cutset(); - // println!("Maximal independent cutset:\n{}", cutset.graph); - // println!("Included vertices: {:?}", cutset.vertices); - // println!("Cutset cardinality: {}", cutset.cardinality); - - // let cycle = basic::bondy_chvatal_hamiltonian_cycle(&g); - // print!("Hamiltonian cycle: "); - // let start = 0; - // let mut current = start; - // loop { - // print!("{}", current + 1); - // print!(" -> "); - // current = cycle[current]; - // if current == start { - // println!("{}", current + 1); - // break; - // } - // } - // } + + let toughness = if g.is_complete() { + f64::MAX + } else { + g.get_toughness() + }; + + if toughness >= 1.0 { + counters.tough_1 += 1; + } + if toughness >= 2.0 { + counters.tough_2 += 1; + } + let min_degree = g.min_degree() as f64; + + if forbidden::theorem3_1(&g) { + counters.t3_1 += 1; + println!("g6:t3_1:{}", line); + } + + if forbidden::theorem3_2(&g) { + counters.t3_2 += 1; + println!("g6:t3_2:{}", line); + } + + if forbidden::corollary3_5(&g) { + counters.c3_5 += 1; + println!("g6:c3_5:{}", line); + } + if forbidden::theorem85(&g) { + counters.t85 += 1; + println!("g6:t85:{}", line); + } + if forbidden::theorem86(&g) { + counters.t86 += 1; + println!("g6:t86:{}", line); + } + if forbidden::theorem87(&g) { + counters.t87 += 1; + println!("g6:t87:{}", line); + } + if forbidden::theorem88(&g) { + counters.t88 += 1; + println!("g6:t88:{}", line); + } + if forbidden::theorem96(&g) { + counters.t96 += 1; + println!("g6:t96:{}", line); + } + if forbidden::conjecture17(&g) { + counters.conj17 += 1; + println!("g6:conj17:{}", line); + } + if forbidden::theorem_all_2con(&g) { + counters.all_2c += 1; + println!("g6:all_2c:{}", line); + } + if toughness::theorem15(&g, toughness, min_degree) { + counters.t15_hamiltonian += 1; + println!("g6:bigalke-jung:{}", line); + } + if toughness::theorem25(&g, toughness, min_degree) { + counters.t25_hamiltonian += 1; + println!("g6:bauer:{}", line); + } + if basic::dirac_theorem(&g) { + counters.dirac_hamiltonian += 1; + println!("g6:dirac:{}", line); + } + if basic::ore_theorem(&g) { + counters.ore_hamiltonian += 1; + println!("g6:ore:{}", line); + } + if basic::posa_theorem(&g) { + counters.posa_hamiltonian += 1; + println!("g6:posa:{}", line); + } + if is_complete { + counters.bch_hamiltonian += 1; + println!("g6:bondy-chvatal:{}", line); + } + + if is_complete && false { + println!("Graph: {line}\n{g}"); + + let components_count = g.count_components(); + println!("Components count: {components_count}"); + let min_degree = g.min_degree(); + println!("Minimal degree: {min_degree}"); + + let cutset = g.max_independent_cutset(); + println!("Maximal independent cutset:\n{}", cutset.graph); + println!("Included vertices: {:?}", cutset.vertices); + println!("Cutset cardinality: {}", cutset.cardinality); + + let cycle = basic::bondy_chvatal_hamiltonian_cycle(&g); + print!("Hamiltonian cycle: "); + let start = 0; + let mut current = start; + loop { + print!("{}", current + 1); + print!(" -> "); + current = cycle[current]; + if current == start { + println!("{}", current + 1); + break; + } + } + } let elapsed = start.elapsed(); time += elapsed.as_nanos(); diff --git a/graph-checker/src/theorems/basic.rs b/graph-checker/src/theorems/basic.rs index 41d5221..94c1f16 100644 --- a/graph-checker/src/theorems/basic.rs +++ b/graph-checker/src/theorems/basic.rs @@ -81,7 +81,6 @@ pub fn bondy_chvatal_hamiltonian_cycle(graph: &Graph) -> Vec<usize> { loop { let next = cycle[current]; let val = closure.matrix[current][next]; - // println!("{current} -> {next} = {val}"); if val > m { m = val; end = current; @@ -95,7 +94,6 @@ pub fn bondy_chvatal_hamiltonian_cycle(graph: &Graph) -> Vec<usize> { if m == 1 { break; } - // println!("New start: {new_start}, end: {end}, m = {m}"); start = new_start; let mut s = start; @@ -110,7 +108,6 @@ pub fn bondy_chvatal_hamiltonian_cycle(graph: &Graph) -> Vec<usize> { } current = next; } - // println!("s = {s}"); let mut new_cycle = Vec::new(); new_cycle.push(start); diff --git a/graph-checker/src/theorems/forbidden.rs b/graph-checker/src/theorems/forbidden.rs index 9450b07..925b6f1 100644 --- a/graph-checker/src/theorems/forbidden.rs +++ b/graph-checker/src/theorems/forbidden.rs @@ -82,8 +82,6 @@ pub fn corollary3_5(g: &Graph) -> bool { || (is_2 && claw_free && g.is_free_of(&vec![ag])); } -// 2003 Gould - pub fn theorem85(g: &Graph) -> bool { let claw = Graph { size: 4, |