use nauty_Traces_sys::{sparsegraph, size_t, optionblk, statsblk, sparsenauty, sortlists_sg, TRUE, SG_FREE, FALSE};
use crate::dbs::{NimbersProvider, NimbersStorer, HasLen};
use std::collections::HashMap;
use crate::games::cram::Cram;
use std::mem::MaybeUninit;
#[derive(Debug, Default, Clone)]
pub struct SparseGraph {
pub v: Vec<size_t>,
pub d: Vec<::std::os::raw::c_int>,
pub e: Vec<::std::os::raw::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],
}
}
pub fn with_max_neighbours_per_vertex<I: IntoIterator<Item=usize>>(max_neighbours: I) -> Self {
let mut edges = 0usize;
let v: Vec<_> = max_neighbours.into_iter().map(|n| {
let result = edges;
edges += n;
result as _
}).collect();
let d = vec![0; v.len()];
Self { v, d, e: vec![0; edges] }
}
pub fn with_max_neighbours(number_of_vertices: usize, max_neighbours_per_vertex: usize) -> Self {
Self::with_max_neighbours_per_vertex((0..number_of_vertices).map(|_| max_neighbours_per_vertex))
}
fn add_directed_edge(&mut self, from_vertex: usize, to_vertex: usize) {
self.e[self.v[from_vertex] as usize + self.d[from_vertex] as usize] = to_vertex as _;
self.d[from_vertex] += 1;
}
pub fn add_undirected_edge(&mut self, vertex_a: usize, vertex_b: usize) {
self.add_directed_edge(vertex_a, vertex_b);
self.add_directed_edge(vertex_b, vertex_a);
}
}
impl<'a> std::convert::From<&'a mut SparseGraph> for sparsegraph {
fn from(g: &'a mut SparseGraph) -> Self {
sparsegraph {
nv: g.v.len() as ::std::os::raw::c_int,
nde: g.d.iter().map(|v| *v as size_t).sum(),
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,
}
}
}
fn position_to_graph(cram: &Cram, bitboard: u64) -> SparseGraph {
let mut bitboard_to_graph_index = vec![0u8; cram.board_size() as usize];
let mut graph_size = 0u8;
let mut rest = bitboard;
while rest != 0 {
let bitboard_index = rest.trailing_zeros();
rest ^= 1u64 << bitboard_index;
bitboard_to_graph_index[bitboard_index as usize] = graph_size;
graph_size += 1;
}
let graph_size = graph_size as usize;
let mut result = SparseGraph::with_max_neighbours(graph_size, 4);
rest = bitboard;
while rest != 0 {
let current_bitboard_index = rest.trailing_zeros() as usize;
let current_bitboard_bit = 1u64 << current_bitboard_index;
rest ^= current_bitboard_bit;
if cram.shifted_right(current_bitboard_bit) & bitboard != 0 {
result.add_undirected_edge(
bitboard_to_graph_index[current_bitboard_index] as usize,
bitboard_to_graph_index[current_bitboard_index + 1] as usize);
}
if cram.shifted_down(current_bitboard_bit) & bitboard != 0 {
result.add_undirected_edge(
bitboard_to_graph_index[current_bitboard_index] as usize,
bitboard_to_graph_index[current_bitboard_index + cram.number_of_cols as usize] as usize);
}
}
result
}
pub struct GraphCanonTT<'c> {
cram: &'c Cram,
db: HashMap<Box<[u8]>, u8>
}
impl<'c> GraphCanonTT<'c> {
pub fn new(cram: &'c Cram) -> Self {
Self { cram, db: HashMap::new() }
}
fn key(&self, position: u64) -> Box<[u8]> {
let mut options = optionblk::default_sparse();
let mut stats = statsblk::default();
options.getcanon = TRUE;
options.digraph = FALSE;
options.tc_level = 0;
let mut lab_to_ignore: [MaybeUninit<::std::os::raw::c_int>; 64] = unsafe { MaybeUninit::uninit().assume_init() };
let mut ptn_to_ignore: [MaybeUninit<::std::os::raw::c_int>; 64] = unsafe { MaybeUninit::uninit().assume_init() };
let mut orbits_to_ignore: [MaybeUninit<::std::os::raw::c_int>; 64] = unsafe { MaybeUninit::uninit().assume_init() };
let mut canonical_graph = sparsegraph::default();
unsafe {
sparsenauty(
&mut (&mut position_to_graph(self.cram, position)).into(),
lab_to_ignore.as_mut_ptr() as *mut ::std::os::raw::c_int,
ptn_to_ignore.as_mut_ptr() as *mut ::std::os::raw::c_int,
orbits_to_ignore.as_mut_ptr() as *mut ::std::os::raw::c_int,
&mut options,
&mut stats,
&mut canonical_graph,
);
sortlists_sg(&mut canonical_graph);
}
let code_len = (canonical_graph.nde / 2) as u8 + canonical_graph.nv as u8;
let mut result = Vec::with_capacity(code_len as usize);
result.push(code_len);
let nv = canonical_graph.nv as usize;
let v = unsafe { std::slice::from_raw_parts_mut(canonical_graph.v, canonical_graph.vlen as usize) };
let e = unsafe { std::slice::from_raw_parts_mut(canonical_graph.e, canonical_graph.elen as usize) };
let d = unsafe { std::slice::from_raw_parts_mut(canonical_graph.d, canonical_graph.dlen as usize) };
for vertex in 0..nv {
let beg = v[vertex] as usize;
result.extend(e[beg..(beg+d[vertex] as usize)].iter().map(|v|*v as u8));
if vertex+1 != nv {
result.push(u8::MAX);
}
}
SG_FREE(&mut canonical_graph);
result.into_boxed_slice()
}
}
impl<'c> NimbersProvider<u64> for GraphCanonTT<'c> {
#[inline(always)] fn get_nimber(&self, position: &u64) -> Option<u8> {
self.db.get_nimber(&self.key(*position))
}
}
impl<'c> NimbersStorer<u64> for GraphCanonTT<'c> {
#[inline(always)] fn store_nimber(&mut self, position: u64, nimber: u8) {
self.db.store_nimber(self.key(position), nimber)
}
}
impl<'c> HasLen for GraphCanonTT<'c> {
#[inline(always)] fn len(&self) -> usize { self.db.len() }
}