use crate::lattice::{Lattice, ORDER3};
#[must_use]
pub fn ring(depth: usize) -> Vec<(i64, i64, i64)> {
ORDER3
.iter()
.copied()
.filter(|o| depth > 1 || o.2 == 0)
.collect()
}
fn adjacent(a: (i64, i64, i64), b: (i64, i64, i64)) -> bool {
let (dx, dy, dz) = (a.0 - b.0, a.1 - b.1, a.2 - b.2);
dx.abs() <= 1 && dy.abs() <= 1 && dz.abs() <= 1
}
#[must_use]
pub fn locally_connected(
lattice: &Lattice,
ring: &[(i64, i64, i64)],
target: usize,
label: u32,
) -> bool {
let (x, y, z) = lattice.coords(target);
let mut members: [(i64, i64, i64); 26] = [(0, 0, 0); 26];
let mut count = 0usize;
for &offset in ring {
let site = lattice.index(x + offset.0, y + offset.1, z + offset.2);
if site != target && lattice.labels[site] == label {
members[count] = offset;
count += 1;
}
}
if count <= 1 {
return true;
}
let mut seen = [false; 26];
let mut stack = [0usize; 26];
let mut top = 1usize;
stack[0] = 0;
seen[0] = true;
let mut reached = 1usize;
while top > 0 {
top -= 1;
let here = stack[top];
for other in 0..count {
if !seen[other] && adjacent(members[here], members[other]) {
seen[other] = true;
stack[top] = other;
top += 1;
reached += 1;
}
}
}
reached == count
}
#[cfg(test)]
mod tests {
use super::*;
fn plane_with(pattern: &[(i64, i64)]) -> Lattice {
let mut lattice = Lattice::medium(9, 9, 1, 2);
for &(x, y) in pattern {
let index = lattice.index(4 + x, 4 + y, 0);
lattice.labels[index] = 1;
}
lattice
}
#[test]
fn a_solid_neighbourhood_is_one_piece() {
let lattice = plane_with(&[
(-1, -1),
(0, -1),
(1, -1),
(-1, 0),
(1, 0),
(-1, 1),
(0, 1),
(1, 1),
]);
let target = lattice.index(4, 4, 0);
assert!(locally_connected(&lattice, &ring(1), target, 1));
}
#[test]
fn two_opposite_neighbours_are_two_pieces() {
let lattice = plane_with(&[(-1, 0), (1, 0)]);
let target = lattice.index(4, 4, 0);
assert!(!locally_connected(&lattice, &ring(1), target, 1));
}
#[test]
fn one_neighbour_is_never_disconnected() {
let lattice = plane_with(&[(1, 0)]);
let target = lattice.index(4, 4, 0);
assert!(locally_connected(&lattice, &ring(1), target, 1));
}
#[test]
fn a_diagonal_pair_touches_and_stays_one_piece() {
let lattice = plane_with(&[(1, 0), (1, 1)]);
let target = lattice.index(4, 4, 0);
assert!(locally_connected(&lattice, &ring(1), target, 1));
}
#[test]
fn a_bridge_through_the_third_axis_is_read_in_a_volume() {
let mut lattice = Lattice::medium(9, 9, 9, 2);
for &(x, y, z) in &[(4i64, 4, 3), (4, 4, 5)] {
let index = lattice.index(x, y, z);
lattice.labels[index] = 1;
}
let target = lattice.index(4, 4, 4);
assert!(!locally_connected(&lattice, &ring(9), target, 1));
let side = lattice.index(5, 4, 4);
lattice.labels[side] = 1;
assert!(locally_connected(&lattice, &ring(9), target, 1));
}
}