use std::collections::HashSet;
pub use rand_core;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Graph(Box<[(u32, u32)]>);
impl Graph {
pub fn new(rng: &mut impl rand_core::RngCore, n: u8) -> Self {
assert!(n <= 32, "graph n param must be lower or equal to 32");
let edges_number = 1_usize << n;
let mut nodes = Vec::with_capacity(edges_number);
for _ in 0..edges_number {
let from_node = rng.next_u32() % edges_number as u32;
let to_node = rng.next_u32() % edges_number as u32;
nodes.push((from_node, to_node));
}
Self(nodes.into_boxed_slice())
}
pub fn solve(
&self,
max_depth: usize,
mut handler: impl FnMut(&[u32]) -> bool
) -> Option<Box<[u32]>> {
let edges_number = self.0.len();
let mut top_nodes = vec![vec![]; edges_number];
let mut bottom_nodes = vec![vec![]; edges_number];
for (top_node, bottom_node) in &self.0 {
if !top_nodes[*top_node as usize].contains(bottom_node) {
top_nodes[*top_node as usize].push(*bottom_node);
}
if !bottom_nodes[*bottom_node as usize].contains(top_node) {
bottom_nodes[*bottom_node as usize].push(*top_node);
}
}
#[allow(clippy::needless_range_loop)]
for top_node in 0..edges_number {
if top_nodes[top_node].len() < 2 {
for bottom_node in &top_nodes[top_node] {
bottom_nodes[*bottom_node as usize].retain(|node| {
node != &(top_node as u32)
});
}
top_nodes[top_node].clear();
}
}
#[allow(clippy::needless_range_loop)]
for bottom_node in 0..edges_number {
if bottom_nodes[bottom_node].len() < 2 {
for top_node in &bottom_nodes[bottom_node] {
top_nodes[*top_node as usize].retain(|node| {
node != &(bottom_node as u32)
});
}
bottom_nodes[bottom_node].clear();
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
enum GraphSide {
Top(u32),
Bottom(u32)
}
for target_top_node in 0..edges_number as u32 {
if top_nodes[target_top_node as usize].is_empty() {
continue;
}
let mut stack: Vec<(GraphSide, Box<[u32]>)> = Vec::new();
let mut visited = HashSet::new();
stack.push((
GraphSide::Top(target_top_node),
Box::new([target_top_node])
));
while let Some((node, path)) = stack.pop() {
if visited.insert(node) && path.len() < max_depth {
match node {
GraphSide::Top(top_node) => {
for bottom_node in &top_nodes[top_node as usize] {
let mut path = path.to_vec();
path.push(*bottom_node);
stack.push((
GraphSide::Bottom(*bottom_node),
path.into_boxed_slice()
));
}
}
GraphSide::Bottom(bottom_node) => {
for top_node in &bottom_nodes[bottom_node as usize] {
let mut path = path.to_vec();
path.push(*top_node);
stack.push((
GraphSide::Top(*top_node),
path.into_boxed_slice()
));
}
}
}
}
else if node == GraphSide::Top(target_top_node)
&& path.len() > 3
&& handler(&path)
{
return Some(path);
}
}
}
None
}
pub fn solve_for(&self, diff: usize)-> Option<Box<[u32]>> {
if diff % 2 == 0 || diff < 5 {
return None;
}
self.solve(diff, |cycle| cycle.len() == diff)
}
pub fn verify(&self, cycle: &[u32]) -> bool {
let n = cycle.len();
if n % 2 == 0 {
return false;
}
let mut i = 1;
while i < n {
let mut found = false;
for (top_node, bottom_node) in &self.0 {
if (i % 2 != 0 && top_node == &cycle[i - 1] && bottom_node == &cycle[i])
|| (i % 2 == 0 && bottom_node == &cycle[i - 1] && top_node == &cycle[i])
{
found = true;
break;
}
}
if !found {
return false;
}
i += 1;
}
true
}
}
#[test]
fn test() {
use rand_core::SeedableRng;
let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(123);
let graph = Graph::new(&mut rng, 16);
assert!(graph.solve_for(9).is_some());
assert!(graph.verify(&[
1981,
19107,
3084,
24653,
6267,
46608,
34728,
11923,
1981
]));
}