summaryrefslogtreecommitdiff
path: root/graph-checker
diff options
context:
space:
mode:
authorAndrew Guschin <guschin@altlinux.org>2024-03-31 18:36:27 +0500
committerAndrew Guschin <guschin@altlinux.org>2024-03-31 18:36:27 +0500
commitf7aa97e10a2fbddb76e1893b7deb193ad56e7192 (patch)
treedab29cd1166edee5c096bdfc45d1c6ab509107f8 /graph-checker
parentb294692a8251eb9c4ea8f3e78651d88fc6efd792 (diff)
latest version
Diffstat (limited to 'graph-checker')
-rw-r--r--graph-checker/Cargo.lock61
-rw-r--r--graph-checker/Cargo.toml5
-rw-r--r--graph-checker/build.rs26
-rw-r--r--graph-checker/nauty/.gitignore5
-rw-r--r--graph-checker/nauty/COPYRIGHT41
-rw-r--r--graph-checker/nauty/README107
-rw-r--r--graph-checker/nauty/README_24292
-rw-r--r--graph-checker/nauty/geng-iter.c128
-rw-r--r--graph-checker/nauty/geng-iter.h23
-rw-r--r--graph-checker/nauty/geng.c2238
-rw-r--r--graph-checker/nauty/geng.h13
-rw-r--r--graph-checker/nauty/gtnauty.c830
-rw-r--r--graph-checker/nauty/gtools.c2924
-rw-r--r--graph-checker/nauty/gtools.h326
-rw-r--r--graph-checker/nauty/gutil1.c1222
-rw-r--r--graph-checker/nauty/gutil2.c762
-rw-r--r--graph-checker/nauty/gutils.h65
-rw-r--r--graph-checker/nauty/makefile53
-rw-r--r--graph-checker/nauty/naugraph.c717
-rw-r--r--graph-checker/nauty/naugroup.c527
-rw-r--r--graph-checker/nauty/naugroup.h55
-rw-r--r--graph-checker/nauty/naugstrings.h20
-rw-r--r--graph-checker/nauty/naurng.c153
-rw-r--r--graph-checker/nauty/naurng.h41
-rw-r--r--graph-checker/nauty/nausparse.c1757
-rw-r--r--graph-checker/nauty/nausparse.h130
-rw-r--r--graph-checker/nauty/nautil.c750
-rw-r--r--graph-checker/nauty/nautinv.c1752
-rw-r--r--graph-checker/nauty/nautinv.h42
-rw-r--r--graph-checker/nauty/naututil.c3233
-rw-r--r--graph-checker/nauty/naututil.h343
-rw-r--r--graph-checker/nauty/nauty.c1255
-rw-r--r--graph-checker/nauty/nauty.h1407
-rw-r--r--graph-checker/nauty/nautycliquer.c2665
-rw-r--r--graph-checker/nauty/nautycliquer.h647
-rw-r--r--graph-checker/nauty/planarity.h457
-rw-r--r--graph-checker/nauty/quarticirred28.h316
-rw-r--r--graph-checker/nauty/schreier.c1128
-rw-r--r--graph-checker/nauty/schreier.h72
-rw-r--r--graph-checker/nauty/sorttemplates.c580
-rw-r--r--graph-checker/nauty/traces.c10490
-rw-r--r--graph-checker/nauty/traces.h73
-rw-r--r--graph-checker/src/geng.rs65
-rw-r--r--graph-checker/src/graph.rs14
-rw-r--r--graph-checker/src/main.rs244
-rw-r--r--graph-checker/src/theorems/basic.rs3
-rw-r--r--graph-checker/src/theorems/forbidden.rs2
47 files changed, 37926 insertions, 133 deletions
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,