#![doc(html_root_url = "https://docs.rs/nauty-Traces-sys/0.3.0")]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
mod bindings;
use ::std::os::raw::c_int;
pub use bindings::*;
use num_integer::Integer;
pub const FALSE: boolean = bindings::FALSE as boolean;
pub const TRUE: boolean = bindings::TRUE as boolean;
pub const CONSOLWIDTH: c_int = bindings::CONSOLWIDTH as c_int;
pub const MTOOBIG: c_int = bindings::MTOOBIG as c_int;
pub const NTOOBIG: c_int = bindings::NTOOBIG as c_int;
pub const CANONGNIL: c_int = bindings::CANONGNIL as c_int;
pub const NAUABORTED: c_int = bindings::NAUABORTED as c_int;
pub const NAUKILLED: c_int = bindings::NAUKILLED as c_int;
#[allow(non_upper_case_globals)]
pub const bit: [set; set::BITS as usize] = {
let mut arr = [0; set::BITS as usize];
let mut val = 1;
let mut num = 0;
while num < arr.len() {
let idx = arr.len() - num - 1;
arr[idx] = val;
val <<= 1;
num += 1;
}
arr
};
pub fn SETWORDSNEEDED(n: usize) -> usize {
Integer::div_ceil(&n, &(WORDSIZE as usize))
}
impl std::default::Default for optionblk {
fn default() -> Self {
optionblk {
getcanon: 0,
digraph: FALSE,
writeautoms: FALSE,
writemarkers: FALSE,
defaultptn: TRUE,
cartesian: FALSE,
linelength: CONSOLWIDTH,
outfile: std::ptr::null_mut(),
userrefproc: None,
userautomproc: None,
userlevelproc: None,
usernodeproc: None,
usercanonproc: None,
invarproc: None,
tc_level: 100,
mininvarlevel: 0,
maxinvarlevel: 1,
invararg: 0,
dispatch: unsafe { &mut dispatch_graph },
schreier: FALSE,
extra_options: std::ptr::null_mut(),
}
}
}
impl optionblk {
pub fn default_sparse() -> Self {
optionblk {
getcanon: 0,
digraph: FALSE,
writeautoms: FALSE,
writemarkers: FALSE,
defaultptn: TRUE,
cartesian: FALSE,
linelength: CONSOLWIDTH,
outfile: std::ptr::null_mut(),
userrefproc: None,
userautomproc: None,
userlevelproc: None,
usernodeproc: None,
usercanonproc: None,
invarproc: None,
tc_level: 100,
mininvarlevel: 0,
maxinvarlevel: 1,
invararg: 0,
dispatch: unsafe { &mut dispatch_sparse },
schreier: FALSE,
extra_options: std::ptr::null_mut(),
}
}
}
impl std::default::Default for TracesOptions {
fn default() -> Self {
TracesOptions {
getcanon: FALSE,
writeautoms: FALSE,
cartesian: FALSE,
digraph: FALSE,
defaultptn: TRUE,
linelength: 0,
outfile: std::ptr::null_mut(),
strategy: 0,
verbosity: 0,
generators: std::ptr::null_mut(),
userautomproc: None,
usercanonproc: None,
weighted: FALSE,
}
}
}
#[allow(clippy::derivable_impls)]
impl std::default::Default for statsblk {
fn default() -> Self {
statsblk {
canupdates: Default::default(),
errstatus: Default::default(),
grpsize1: Default::default(),
grpsize2: Default::default(),
invapplics: Default::default(),
invarsuclevel: Default::default(),
invsuccesses: Default::default(),
maxlevel: Default::default(),
numbadleaves: Default::default(),
numgenerators: Default::default(),
numnodes: Default::default(),
numorbits: Default::default(),
tctotal: Default::default(),
}
}
}
#[allow(clippy::derivable_impls)]
impl std::default::Default for TracesStats {
fn default() -> Self {
TracesStats {
grpsize1: Default::default(),
grpsize2: Default::default(),
numgenerators: Default::default(),
numorbits: Default::default(),
treedepth: Default::default(),
canupdates: Default::default(),
errstatus: Default::default(),
numnodes: Default::default(),
interrupted: Default::default(),
peaknodes: Default::default(),
}
}
}
pub fn empty_graph(m: usize, n: usize) -> Vec<graph> {
vec![0; m * n]
}
pub fn uninit_graph(m: usize, n: usize) -> Vec<graph> {
Vec::with_capacity(m * n)
}
pub fn ADDONEEDGE(g: &mut [graph], v: usize, w: usize, m: usize) {
ADDONEARC(g, v, w, m);
ADDONEARC(g, w, v, m);
}
pub fn ADDONEARC(g: &mut [graph], v: usize, w: usize, m: usize) {
ADDELEMENT(GRAPHROW(g as &mut [set], v, m), w);
}
pub fn GRAPHROW(g: &mut [set], v: usize, m: usize) -> &mut [set] {
&mut g[m * v..]
}
pub fn ADDELEMENT(setadd: &mut [set], pos: usize) {
setadd[SETWD(pos)] |= bit[SETBT(pos)]
}
pub fn SETWD(pos: usize) -> usize {
pos / (WORDSIZE as usize)
}
pub fn SETBT(pos: usize) -> usize {
pos & (WORDSIZE as usize - 1)
}
#[cfg(feature = "libc")]
pub fn DYNFREE<T>(ptr: &mut *mut T, len: &mut size_t) {
unsafe {
libc::free(*ptr as *mut libc::c_void);
}
*ptr = std::ptr::null_mut();
*len = 0;
}
#[cfg(feature = "libc")]
pub fn SG_FREE(sg: &mut sparsegraph) {
DYNFREE(&mut sg.v, &mut sg.vlen);
DYNFREE(&mut sg.d, &mut sg.dlen);
DYNFREE(&mut sg.e, &mut sg.elen);
}
impl std::default::Default for sparsegraph {
fn default() -> Self {
sparsegraph {
nde: Default::default(),
v: std::ptr::null_mut(),
nv: Default::default(),
d: std::ptr::null_mut(),
e: std::ptr::null_mut(),
w: std::ptr::null_mut(),
vlen: Default::default(),
dlen: Default::default(),
elen: Default::default(),
wlen: Default::default(),
}
}
}
#[derive(Debug, Default, Clone)]
pub struct SparseGraph {
pub v: Vec<size_t>,
pub d: Vec<c_int>,
pub e: Vec<c_int>,
}
impl SparseGraph {
pub fn new(vertices: usize, edges: usize) -> Self {
SparseGraph {
v: vec![0; vertices],
d: vec![0; vertices],
e: vec![0; edges],
}
}
}
impl<'a> std::convert::From<&'a mut SparseGraph> for sparsegraph {
fn from(g: &'a mut SparseGraph) -> Self {
sparsegraph {
nv: g.v.len() as c_int,
nde: g.e.len() as size_t,
v: g.v.as_mut_ptr(),
d: g.d.as_mut_ptr(),
e: g.e.as_mut_ptr(),
w: std::ptr::null_mut(),
vlen: g.v.len() as size_t,
dlen: g.d.len() as size_t,
elen: g.e.len() as size_t,
wlen: 0,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use ::std::os::raw::c_int;
#[test]
fn nautyex2() {
let orders = [1., 2., 6., 8., 10., 12., 14., 16., 18., 20.];
let mut options = optionblk::default();
let mut stats = statsblk::default();
for (n, order) in orders.iter().enumerate() {
let n = n + 1;
let m = SETWORDSNEEDED(n);
unsafe {
nauty_check(
WORDSIZE as c_int,
m as c_int,
n as c_int,
NAUTYVERSIONID as c_int,
);
}
let mut lab = vec![0; n];
let mut ptn = vec![0; n];
let mut orbits = vec![0; n];
let mut g = empty_graph(m, n);
for v in 0..n {
ADDONEEDGE(&mut g, v, (v + 1) % n, m);
}
unsafe {
densenauty(
g.as_mut_ptr(),
lab.as_mut_ptr(),
ptn.as_mut_ptr(),
orbits.as_mut_ptr(),
&mut options,
&mut stats,
m as c_int,
n as c_int,
std::ptr::null_mut(),
);
}
assert_eq!(stats.grpsize1, *order);
assert_eq!(stats.grpsize2, 0);
}
}
extern "C" fn writeautom(p: *mut i32, n: i32) {
for i in 0..n {
print!(" {:2}", unsafe { *p.offset(i as isize) })
}
println!()
}
#[test]
fn nautyex3() {
let mut options = optionblk::default();
let mut stats = statsblk::default();
options.userautomproc = Some(groupautomproc);
options.userlevelproc = Some(grouplevelproc);
for n in 1..20 {
let m = SETWORDSNEEDED(n);
unsafe {
nauty_check(
WORDSIZE as c_int,
m as c_int,
n as c_int,
NAUTYVERSIONID as c_int,
);
}
let mut g = empty_graph(n, m);
let mut lab = vec![0; n];
let mut ptn = vec![0; n];
let mut orbits = vec![0; n];
for v in 0..n {
ADDONEEDGE(&mut g, v, (v + 1) % n, m)
}
println!("Automorphisms of C[{}]:", n);
unsafe {
densenauty(
g.as_mut_ptr(),
lab.as_mut_ptr(),
ptn.as_mut_ptr(),
orbits.as_mut_ptr(),
&mut options,
&mut stats,
m as c_int,
n as c_int,
std::ptr::null_mut(),
);
let group = groupptr(FALSE);
makecosetreps(group);
allgroup(group, Some(writeautom));
}
}
}
#[test]
fn nautyex4() {
let n_range = 1..20;
use ::std::os::raw::c_int;
let mut options = optionblk::default_sparse();
let mut stats = statsblk::default();
for n in n_range {
let m = SETWORDSNEEDED(n);
unsafe {
nauty_check(
WORDSIZE as c_int,
m as c_int,
n as c_int,
NAUTYVERSIONID as c_int,
);
}
let mut lab = vec![0; n];
let mut ptn = vec![0; n];
let mut orbits = vec![0; n];
let mut sg = SparseGraph::new(
n,
2 * n,
);
for i in 0..n {
sg.v[i] = 2 * i as size_t;
sg.d[i] = 2;
sg.e[2 * i] = ((i + n - 1) % n) as c_int;
sg.e[2 * i + 1] = ((i + n + 1) % n) as c_int;
}
println!("Generators for Aut(C[{}]):", n);
unsafe {
sparsenauty(
&mut (&mut sg).into(),
lab.as_mut_ptr(),
ptn.as_mut_ptr(),
orbits.as_mut_ptr(),
&mut options,
&mut stats,
std::ptr::null_mut(),
);
}
println!("Automorphism group size = {}", stats.grpsize1);
if n < 3 {
assert_eq!(stats.grpsize1, n as f64)
} else {
assert_eq!(stats.grpsize1, 2. * n as f64)
}
println!();
}
}
#[test]
fn nautyex5() {
let n_range = (2..20).step_by(2);
let mut options = optionblk::default_sparse();
let mut stats = statsblk::default();
let mut cg1 = sparsegraph::default();
let mut cg2 = sparsegraph::default();
options.getcanon = TRUE;
for n in n_range {
let m = SETWORDSNEEDED(n);
unsafe {
nauty_check(
WORDSIZE as c_int,
m as c_int,
n as c_int,
NAUTYVERSIONID as c_int,
);
}
let mut lab1 = vec![0; n];
let mut lab2 = vec![0; n];
let mut ptn = vec![0; n];
let mut orbits = vec![0; n];
let mut map = vec![0; n];
let mut sg1 = SparseGraph::new(
n,
3 * n,
);
for i in 0..n {
sg1.v[i] = (3 * i) as size_t;
sg1.d[i] = 3;
}
for i in (0..n).step_by(2) {
sg1.e[sg1.v[i] as usize] = (i + 1) as c_int;
sg1.e[sg1.v[i + 1] as usize] = i as c_int;
}
for i in 0..n - 2 {
sg1.e[(sg1.v[i] + 1) as usize] = (i + 2) as c_int;
}
sg1.e[(sg1.v[n - 2] + 1) as usize] = 1;
sg1.e[(sg1.v[n - 1] + 1) as usize] = 0;
for i in 2..n {
sg1.e[(sg1.v[i] + 2) as usize] = (i - 2) as c_int;
}
sg1.e[(sg1.v[1] + 2) as usize] = (n - 2) as c_int;
sg1.e[(sg1.v[0] + 2) as usize] = (n - 1) as c_int;
let mut sg2 = SparseGraph::new(
n,
3 * n,
);
for i in 0..n {
sg2.v[i] = (3 * i) as size_t;
sg2.d[i] = 3;
sg2.e[sg2.v[i] as usize] = ((i + 1) % n) as c_int;
sg2.e[(sg2.v[i] + 1) as usize] = ((i + n - 1) % n) as c_int;
sg2.e[(sg2.v[i] + 2) as usize] = ((i + n / 2) % n) as c_int;
}
unsafe {
sparsenauty(
&mut (&mut sg1).into(),
lab1.as_mut_ptr(),
ptn.as_mut_ptr(),
orbits.as_mut_ptr(),
&mut options,
&mut stats,
&mut cg1,
);
sparsenauty(
&mut (&mut sg2).into(),
lab2.as_mut_ptr(),
ptn.as_mut_ptr(),
orbits.as_mut_ptr(),
&mut options,
&mut stats,
&mut cg2,
);
}
let are_same = unsafe { aresame_sg(&mut cg1, &mut cg2) } == TRUE;
assert!(are_same);
if are_same {
println!("Isomorphic");
if n <= 1000 {
for i in 0..n {
map[lab1[i] as usize] = lab2[i];
}
for i in 0..n {
print!(" {}-{}", i, map[i])
}
println!()
}
} else {
println!("Not isomorphic.");
}
}
}
#[test]
fn nautyex6() {
let n_range = (2..20).step_by(2);
let mut options = optionblk::default();
let mut stats = statsblk::default();
options.getcanon = TRUE;
for n in n_range {
let m = SETWORDSNEEDED(n);
unsafe {
nauty_check(
WORDSIZE as c_int,
m as c_int,
n as c_int,
NAUTYVERSIONID as c_int,
);
}
let mut lab1 = vec![0; n];
let mut lab2 = vec![0; n];
let mut ptn = vec![0; n];
let mut orbits = vec![0; n];
let mut map = vec![0; n];
let mut cg1 = empty_graph(n, m);
let mut cg2 = empty_graph(n, m);
let mut g1 = empty_graph(m, n);
for i in 0..n - 2 {
ADDONEEDGE(&mut g1, i, i + 2, m)
}
ADDONEEDGE(&mut g1, n - 2, 1, m);
ADDONEEDGE(&mut g1, n - 1, 0, m);
for i in (0..n).step_by(2) {
ADDONEEDGE(&mut g1, i, i + 1, m)
}
let mut g2 = empty_graph(m, n);
for i in 0..n - 1 {
ADDONEEDGE(&mut g2, i, i + 1, m)
}
ADDONEEDGE(&mut g2, n - 1, 0, m);
for i in 0..n / 2 {
ADDONEEDGE(&mut g2, i, i + n / 2, m);
}
unsafe {
densenauty(
g1.as_mut_ptr(),
lab1.as_mut_ptr(),
ptn.as_mut_ptr(),
orbits.as_mut_ptr(),
&mut options,
&mut stats,
m as c_int,
n as c_int,
cg1.as_mut_ptr(),
);
densenauty(
g2.as_mut_ptr(),
lab2.as_mut_ptr(),
ptn.as_mut_ptr(),
orbits.as_mut_ptr(),
&mut options,
&mut stats,
m as c_int,
n as c_int,
cg2.as_mut_ptr(),
);
}
assert_eq!(cg1, cg2);
if cg1 == cg2 {
println!("Isomorphic.");
if n <= 1000 {
for i in 0..n {
map[lab1[i] as usize] = lab2[i];
}
for i in 0..n {
print!(" {}-{}", i, map[i]);
}
println!()
}
}
}
}
#[test]
fn nautyex7() {
let n_range = (2..20).step_by(2);
let mut options = TracesOptions::default();
let mut stats = TracesStats::default();
let mut cg1 = sparsegraph::default();
let mut cg2 = sparsegraph::default();
options.getcanon = TRUE;
for n in n_range {
let m = SETWORDSNEEDED(n);
unsafe {
nauty_check(
WORDSIZE as c_int,
m as c_int,
n as c_int,
NAUTYVERSIONID as c_int,
);
}
let mut lab1 = vec![0; n];
let mut lab2 = vec![0; n];
let mut ptn = vec![0; n];
let mut orbits = vec![0; n];
let mut map = vec![0; n];
let mut sg1 = SparseGraph::new(
n,
3 * n,
);
for i in 0..n {
sg1.v[i] = (3 * i) as size_t;
sg1.d[i] = 3;
}
for i in (0..n).step_by(2) {
sg1.e[sg1.v[i] as usize] = (i + 1) as c_int;
sg1.e[sg1.v[i + 1] as usize] = i as c_int;
}
for i in 0..n - 2 {
sg1.e[(sg1.v[i] + 1) as usize] = (i + 2) as c_int;
}
sg1.e[(sg1.v[n - 2] + 1) as usize] = 1;
sg1.e[(sg1.v[n - 1] + 1) as usize] = 0;
for i in 2..n {
sg1.e[(sg1.v[i] + 2) as usize] = (i - 2) as c_int;
}
sg1.e[(sg1.v[1] + 2) as usize] = (n - 2) as c_int;
sg1.e[(sg1.v[0] + 2) as usize] = (n - 1) as c_int;
let mut sg2 = SparseGraph::new(
n,
3 * n,
);
for i in 0..n {
sg2.v[i] = (3 * i) as size_t;
sg2.d[i] = 3;
sg2.e[(sg2.v[i]) as usize] = ((i + 1) % n) as c_int;
sg2.e[(sg2.v[i] + 1) as usize] = ((i + n - 1) % n) as c_int;
sg2.e[(sg2.v[i] + 2) as usize] = ((i + n / 2) % n) as c_int;
}
unsafe {
Traces(
&mut (&mut sg1).into(),
lab1.as_mut_ptr(),
ptn.as_mut_ptr(),
orbits.as_mut_ptr(),
&mut options,
&mut stats,
&mut cg1,
);
Traces(
&mut (&mut sg2).into(),
lab2.as_mut_ptr(),
ptn.as_mut_ptr(),
orbits.as_mut_ptr(),
&mut options,
&mut stats,
&mut cg2,
);
}
let are_same = unsafe { aresame_sg(&mut cg1, &mut cg2) } == TRUE;
assert!(are_same);
if are_same {
println!("Isomorphic.");
if n <= 1000 {
for i in 0..n {
map[lab1[i] as usize] = lab2[i]
}
for i in 0..n {
print!(" {}-{}", i, map[i]);
}
println!();
}
} else {
println!("Not isomorphic.");
}
}
}
#[test]
fn nautyex8() {
let n_range = (2..20).step_by(2);
let mut options = optionblk::default();
let mut stats = statsblk::default();
options.getcanon = TRUE;
for n in n_range {
let m = SETWORDSNEEDED(n);
unsafe {
nauty_check(
WORDSIZE as c_int,
m as c_int,
n as c_int,
NAUTYVERSIONID as c_int,
);
}
let mut lab1 = vec![0; n];
let mut lab2 = vec![0; n];
let mut ptn = vec![0; n];
let mut orbits = vec![0; n];
let mut map = vec![0; n];
let mut g1 = empty_graph(m, n);
for i in (0..n).step_by(2) {
ADDONEEDGE(&mut g1, i, i + 1, m);
}
for i in 0..n - 2 {
ADDONEEDGE(&mut g1, i, i + 2, m);
}
ADDONEEDGE(&mut g1, 1, n - 2, m);
ADDONEEDGE(&mut g1, 0, n - 1, m);
let mut g2 = empty_graph(m, n);
for i in 0..n {
ADDONEEDGE(&mut g2, i, (i + 1) % n, m);
ADDONEEDGE(&mut g2, i, (i + n / 2) % n, m);
}
let mut cg1 = empty_graph(m, n);
let mut cg2 = empty_graph(m, n);
unsafe {
densenauty(
g1.as_mut_ptr(),
lab1.as_mut_ptr(),
ptn.as_mut_ptr(),
orbits.as_mut_ptr(),
&mut options,
&mut stats,
m as c_int,
n as c_int,
cg1.as_mut_ptr(),
);
densenauty(
g2.as_mut_ptr(),
lab2.as_mut_ptr(),
ptn.as_mut_ptr(),
orbits.as_mut_ptr(),
&mut options,
&mut stats,
m as c_int,
n as c_int,
cg2.as_mut_ptr(),
);
}
assert_eq!(cg1, cg2);
if cg1 == cg2 {
println!("Isomorphic.");
for i in 0..n {
map[lab1[i] as usize] = lab2[i];
}
for (i, m) in map.iter().enumerate() {
print!(" {}-{}", i, m)
}
println!()
} else {
println!("Not isomorphic.")
}
}
}
#[test]
fn nautyex9() {
let mut auto_group_size = std::collections::HashMap::new();
for i in &[3, 4, 6, 7, 8, 9, 11, 12, 14, 15, 16, 18, 19] {
auto_group_size.insert(*i, None);
}
auto_group_size.insert(5, Some(10.));
auto_group_size.insert(10, Some(320.));
auto_group_size.insert(13, Some(78.));
auto_group_size.insert(17, Some(136.));
let n_range = 3..20;
let mut options = TracesOptions::default();
let mut stats = TracesStats::default();
let mut gens = std::ptr::null_mut();
options.generators = &mut gens;
for n in n_range {
let m = SETWORDSNEEDED(n);
unsafe {
nauty_check(
WORDSIZE as c_int,
m as c_int,
n as c_int,
NAUTYVERSIONID as c_int,
);
}
let mut lab = vec![0; n];
let mut ptn = vec![0; n];
let mut orbits = vec![0; n];
let mut p = vec![0; n];
let mut issquare = vec![FALSE; n];
gens = std::ptr::null_mut();
for i in 0..n {
issquare[(i * i) % n] = TRUE;
}
if issquare.last() != Some(&TRUE) {
assert_eq!(auto_group_size[&n], None);
println!("-1 must be a square mod n; try again");
continue;
}
let mut deg = 0;
for i in 1..n {
if issquare[i] == TRUE {
deg += 1
}
}
let mut sg = SparseGraph::new(
n,
n * deg,
);
for i in 0..n {
sg.v[i] = (i * deg) as size_t;
sg.d[i] = deg as c_int;
}
for i in 0..n {
let mut k = sg.v[i] as usize;
for j in 1..n {
if issquare[j] == TRUE {
sg.e[k] = ((i + j) % n) as c_int;
k += 1;
}
}
}
unsafe {
freeschreier(std::ptr::null_mut(), &mut gens);
}
for i in 0..n {
p[i] = ((i + 1) % n) as c_int;
}
unsafe {
addpermutation(&mut gens, p.as_mut_ptr(), n as c_int);
}
for i in 0..n {
p[i] = ((n - i) % n) as c_int;
}
unsafe {
addpermutation(&mut gens, p.as_mut_ptr(), n as c_int);
}
unsafe {
Traces(
&mut (&mut sg).into(),
lab.as_mut_ptr(),
ptn.as_mut_ptr(),
orbits.as_mut_ptr(),
&mut options,
&mut stats,
std::ptr::null_mut(),
);
}
assert_eq!(auto_group_size[&n], Some(stats.grpsize1));
print!("Automorphism group size = ");
unsafe {
writegroupsize(stdout, stats.grpsize1, stats.grpsize2);
}
println!();
}
}
#[test]
fn nautyex10() {
let n_range = (2..20).step_by(2);
let mut options = TracesOptions::default();
let mut stats = TracesStats::default();
let mut cg1 = sparsegraph::default();
let mut cg2 = sparsegraph::default();
for n in n_range {
let m = SETWORDSNEEDED(n);
unsafe {
nauty_check(
WORDSIZE as c_int,
m as c_int,
n as c_int,
NAUTYVERSIONID as c_int,
);
}
let mut lab1 = vec![0; n];
let mut lab2 = vec![0; n];
let mut ptn = vec![0; n];
let mut orbits = vec![0; n];
let mut map = vec![0; n];
let mut sg1 = SparseGraph::new(
n,
3 * n,
);
for i in 0..n {
sg1.v[i] = (3 * i) as size_t;
sg1.d[i] = 3;
}
for i in (0..n).step_by(2) {
sg1.e[sg1.v[i] as usize] = (i + 1) as c_int;
sg1.e[sg1.v[i + 1] as usize] = i as c_int;
}
for i in 0..n - 2 {
sg1.e[(sg1.v[i] + 1) as usize] = (i + 2) as c_int;
}
sg1.e[(sg1.v[n - 2] + 1) as usize] = 1;
sg1.e[(sg1.v[n - 1] + 1) as usize] = 0;
for i in 2..n {
sg1.e[(sg1.v[i] + 2) as usize] = (i - 2) as c_int;
}
sg1.e[(sg1.v[1] + 2) as usize] = (n - 2) as c_int;
sg1.e[(sg1.v[0] + 2) as usize] = (n - 1) as c_int;
let mut sg2 = SparseGraph::new(
n,
3 * n,
);
for i in 0..n {
sg2.v[i] = (3 * i) as size_t;
sg2.d[i] = 3;
sg2.e[sg2.v[i] as usize] = ((i + 1) % n) as c_int;
sg2.e[(sg2.v[i] + 1) as usize] = ((i + n - 1) % n) as c_int;
sg2.e[(sg2.v[i] + 2) as usize] = ((i + n / 2) % n) as c_int;
}
let mut generators = std::ptr::null_mut();
options.generators = &mut generators;
options.getcanon = FALSE;
unsafe {
Traces(
&mut (&mut sg1).into(),
lab1.as_mut_ptr(),
ptn.as_mut_ptr(),
orbits.as_mut_ptr(),
&mut options,
&mut stats,
std::ptr::null_mut(),
);
}
options.getcanon = TRUE;
unsafe {
Traces(
&mut (&mut sg1).into(),
lab1.as_mut_ptr(),
ptn.as_mut_ptr(),
orbits.as_mut_ptr(),
&mut options,
&mut stats,
&mut cg1,
);
freeschreier(std::ptr::null_mut(), &mut generators);
}
options.getcanon = FALSE;
unsafe {
Traces(
&mut (&mut sg2).into(),
lab2.as_mut_ptr(),
ptn.as_mut_ptr(),
orbits.as_mut_ptr(),
&mut options,
&mut stats,
std::ptr::null_mut(),
);
}
options.getcanon = TRUE;
unsafe {
Traces(
&mut (&mut sg2).into(),
lab2.as_mut_ptr(),
ptn.as_mut_ptr(),
orbits.as_mut_ptr(),
&mut options,
&mut stats,
&mut cg2,
);
freeschreier(std::ptr::null_mut(), &mut generators);
}
let are_same = unsafe { aresame_sg(&mut cg1, &mut cg2) } == TRUE;
assert!(are_same);
if are_same {
println!("Isomorphic");
if n <= 1000 {
for i in 0..n {
map[lab1[i] as usize] = lab2[i];
}
for i in 0..n {
print!(" {}-{}", i, map[i])
}
println!()
}
} else {
println!("Not isomorphic.");
}
}
}
#[cfg(feature = "libc")]
#[test]
fn sg_fun() {
let n = 10;
let mut sg = SparseGraph::new(
n,
2 * n,
);
for i in 0..n {
sg.v[i] = 2 * i as size_t;
sg.d[i] = 2;
sg.e[2 * i] = ((i + n - 1) % n) as c_int;
sg.e[2 * i + 1] = ((i + n + 1) % n) as c_int;
}
unsafe { test_copy_sg(&mut (&mut sg).into()) }
let m = SETWORDSNEEDED(n);
let mut g = empty_graph(m, n);
for v in 0..n {
ADDONEEDGE(&mut g, v, (v + 1) % n, m)
}
unsafe { test_nauty_to_sg(&mut g, &mut (&mut sg).into(), n, m) }
unsafe { test_sg_to_nauty(&mut (&mut sg).into(), &mut g, n) }
test_sortlists_sg(&mut sg);
}
#[cfg(feature = "libc")]
unsafe fn test_copy_sg(sg: &mut sparsegraph) {
let sg_cp = copy_sg(sg, std::ptr::null_mut());
assert!(aresame_sg(sg, sg_cp) != 0);
SG_FREE(&mut *sg_cp);
}
#[cfg(feature = "libc")]
unsafe fn test_nauty_to_sg(
g: &mut [graph],
sg: &mut sparsegraph,
n: usize,
m: usize,
) {
let sg_from_g = nauty_to_sg(
g.as_mut_ptr(),
std::ptr::null_mut(),
m as c_int,
n as c_int,
);
assert!(aresame_sg(sg, sg_from_g) != 0);
SG_FREE(&mut *sg_from_g);
}
#[cfg(feature = "libc")]
unsafe fn test_sg_to_nauty(
sg: &mut sparsegraph,
g: &mut [graph],
n: usize,
) {
let mut m = 0;
let mut g_from_sg = sg_to_nauty(sg, std::ptr::null_mut(), 0, &mut m);
let g2 = std::slice::from_raw_parts(
g_from_sg as *const graph,
n * m as usize,
);
assert_eq!(&*g, g2);
std::mem::drop(g2);
let mut dummy = 0;
DYNFREE(&mut g_from_sg, &mut dummy);
}
#[cfg(feature = "libc")]
fn test_sortlists_sg(sg: &mut SparseGraph) {
let orig_sg = sg.clone();
unsafe { sortlists_sg(&mut sg.into()) }
assert_eq!(orig_sg.v, sg.v);
assert_eq!(orig_sg.d, sg.d);
assert!(orig_sg.e != sg.e);
}
}