use alloc::vec::Vec;
use super::*;
use crate::marching_cubes::table::{NO_EDGE, segment_links};
fn every_configuration() -> impl Iterator<Item = (u16, u16)> {
(0..512u16).flat_map(|case| {
let ambiguous = AMBIGUOUS_FACES[case as usize];
let mut masks = Vec::new();
let mut sub = ambiguous;
loop {
masks.push(sub);
if sub == 0 {
break;
}
sub = (sub - 1) & ambiguous;
}
masks.into_iter().map(move |joined| (case, joined))
})
}
#[test]
fn every_edge_is_shared_by_two_faces_in_opposite_directions() {
let mut directed: Vec<(u8, u8, u8)> = Vec::new();
for face in &FACES {
for k in 0..face.corners {
let edge = face.edge[k];
assert_ne!(edge, NO_EDGE, "a walked corner has no edge");
let from = face.sample[k];
let to = face.sample[(k + 1) % face.corners];
directed.push((edge, from, to));
}
}
assert_eq!(directed.len(), 4 * 4 + 4 + 3 * 4, "boundary corner count");
for edge in 0..EDGE_COUNT as u8 {
let uses: Vec<&(u8, u8, u8)> = directed.iter().filter(|d| d.0 == edge).collect();
assert_eq!(uses.len(), 2, "edge {edge} is used {} times", uses.len());
let (_, a_from, a_to) = *uses[0];
let (_, b_from, b_to) = *uses[1];
assert_eq!(
(a_from, a_to),
(b_to, b_from),
"edge {edge} runs {a_from}->{a_to} and {b_from}->{b_to}; it must reverse"
);
let mut named = EDGE_SAMPLES[edge as usize];
named.sort_unstable();
let mut walked = [a_from, a_to];
walked.sort_unstable();
assert_eq!(named, walked, "edge {edge} joins the wrong samples");
}
}
#[test]
fn only_quadrants_and_the_back_face_can_be_ambiguous() {
for (f, face) in FACES.iter().enumerate() {
if face.corners == 3 {
for case in 0..512u16 {
assert_eq!(
AMBIGUOUS_FACES[case as usize] & (1 << f),
0,
"lateral face {f} ambiguous at case {case:#011b}"
);
}
}
}
assert!(
(0..512u16).any(|c| AMBIGUOUS_FACES[c as usize] != 0),
"no case is ambiguous anywhere"
);
}
#[test]
fn the_half_resolution_edges_are_the_corner_pairs() {
let half: Vec<u8> = (0..EDGE_COUNT as u8)
.filter(|e| is_half_resolution(*e))
.collect();
assert_eq!(half, alloc::vec![12, 13, 14, 15]);
for e in half {
let [a, b] = EDGE_SAMPLES[e as usize];
assert!(
matches!(a, 0 | 2 | 6 | 8) && matches!(b, 0 | 2 | 6 | 8),
"half-resolution edge {e} joins {a} and {b}, which are not both corners"
);
}
}
#[test]
fn the_links_cover_the_cut_edges_and_close() {
for (case, joined) in every_configuration() {
let next = transition_links(case, joined);
let cut = cut_edges(case);
let mut linked = 0u16;
let mut targeted = 0u16;
for (e, &t) in next.iter().enumerate() {
if t != NO_EDGE {
linked |= 1 << e;
assert_eq!(
targeted & (1 << t),
0,
"case {case:#011b}/{joined:#b}: edge {t} is the target of two segments"
);
targeted |= 1 << t;
}
}
assert_eq!(
linked, cut,
"case {case:#011b}/{joined:#b}: linked {linked:#018b}, cut {cut:#018b}"
);
assert_eq!(
targeted, cut,
"case {case:#011b}/{joined:#b}: targeted {targeted:#018b}, cut {cut:#018b}"
);
}
}
#[test]
fn the_cycles_partition_the_cut_edges() {
let mut longest = 0usize;
let mut most_cycles = 0usize;
for (case, joined) in every_configuration() {
let next = transition_links(case, joined);
let mut visited = 0u16;
let mut cycles = 0usize;
for start in 0..EDGE_COUNT as u8 {
if next[start as usize] == NO_EDGE || visited & (1 << start) != 0 {
continue;
}
cycles += 1;
let mut len = 0usize;
let mut current = start;
while visited & (1 << current) == 0 {
visited |= 1 << current;
len += 1;
current = next[current as usize];
}
assert_eq!(
current, start,
"case {case:#011b}/{joined:#b}: a walk closed on {current}, not {start}"
);
assert!(len >= 3, "case {case:#011b}: a cycle of length {len}");
longest = longest.max(len);
}
most_cycles = most_cycles.max(cycles);
assert_eq!(
visited,
cut_edges(case),
"case {case:#011b}: cycles missed an edge"
);
}
std::println!("longest cycle {longest}, most cycles in one cell {most_cycles}");
}
#[test]
fn each_quadrant_pairs_as_marching_cubes_would() {
let cube_face = crate::cube::face_corners(2, 0);
let cube_edges = [
crate::cube::edge_index(cube_face[0], cube_face[1]),
crate::cube::edge_index(cube_face[1], cube_face[2]),
crate::cube::edge_index(cube_face[2], cube_face[3]),
crate::cube::edge_index(cube_face[3], cube_face[0]),
];
let mut compared = 0usize;
for (q, face) in FACES.iter().enumerate().take(4) {
for signs in 0..16u8 {
let mut case = 0u16;
let mut cube_case = 0u8;
for (k, corner) in cube_face.iter().enumerate() {
if signs & (1 << k) != 0 {
case |= 1 << face.sample[k];
cube_case |= 1 << corner;
}
}
let t = transition_links(case, 0);
let c = segment_links(cube_case, 0);
let own = |links: &[u8], edges: &[u8; 4]| -> Vec<(usize, usize)> {
let mut pairs = Vec::new();
for (k, e) in edges.iter().enumerate() {
let to = links[*e as usize];
if let Some(m) = edges.iter().position(|x| *x == to) {
pairs.push((k, m));
}
}
pairs.sort_unstable();
pairs
};
let quadrant_edges = [face.edge[0], face.edge[1], face.edge[2], face.edge[3]];
assert_eq!(
own(&t, &quadrant_edges),
own(&c, &cube_edges),
"quadrant {q}, signs {signs:#06b}"
);
compared += 1;
}
}
assert_eq!(compared, 64);
}
#[test]
fn a_lateral_link_stitches_the_seam_or_caps_a_feature_the_coarse_grid_cannot_see() {
let mut stitched = 0usize;
let mut capped = 0usize;
for (case, joined) in every_configuration() {
let next = transition_links(case, joined);
for face in FACES.iter().filter(|f| f.corners == 3) {
let edges = [face.edge[0], face.edge[1], face.edge[2]];
let coarse = edges
.iter()
.copied()
.find(|e| is_half_resolution(*e))
.expect("every lateral has exactly one half-resolution edge");
assert_eq!(
edges.iter().filter(|e| is_half_resolution(**e)).count(),
1,
"a lateral must have exactly one half-resolution edge"
);
let coarse_cut = cut_edges(case) & (1 << coarse) != 0;
for e in edges {
let to = next[e as usize];
if to == NO_EDGE || !edges.contains(&to) {
continue;
}
let crosses = is_half_resolution(e) != is_half_resolution(to);
assert_eq!(
crosses,
coarse_cut,
"case {case:#011b}/{joined:#b}: lateral {:?} links {e} to {to}, \
coarse edge {coarse} cut = {coarse_cut}",
&face.sample[..3]
);
if crosses {
stitched += 1;
} else {
capped += 1;
}
}
}
}
std::println!("{stitched} lateral links stitch the seam, {capped} cap a sub-coarse feature");
assert!(stitched > 0, "no lateral ever stitched the seam");
assert!(
capped > 0,
"no lateral ever capped a feature — the counterexample is gone"
);
}
#[test]
fn the_dihedral_group_is_closed_and_has_order_eight() {
let mut seen: Vec<Perm> = Vec::new();
for p in &DIHEDRAL {
assert!(!seen.contains(p), "D8 has a repeated element");
seen.push(*p);
}
assert_eq!(seen.len(), 8);
for p in &DIHEDRAL {
let mut sorted = p.to_vec();
sorted.sort_unstable();
assert_eq!(sorted, (0..SAMPLE_COUNT as u8).collect::<Vec<_>>());
}
for a in &DIHEDRAL {
for b in &DIHEDRAL {
let mut c = [0u8; SAMPLE_COUNT];
for s in 0..SAMPLE_COUNT {
c[s] = b[a[s] as usize];
}
assert!(DIHEDRAL.contains(&c), "D8 is not closed under composition");
}
}
for p in &DIHEDRAL {
assert_eq!(p[4], 4, "the centre must be fixed");
for corner in [0u8, 2, 6, 8] {
assert!(matches!(p[corner as usize], 0 | 2 | 6 | 8));
}
}
}
#[test]
fn the_orbit_count_is_lengyels_seventy_three() {
let canonical = |case: u16| -> u16 {
let inversion_applies = AMBIGUOUS_FACES[case as usize] == 0;
let mut best = case;
for p in &DIHEDRAL {
let image = permute(case, p);
best = best.min(image);
if inversion_applies {
best = best.min(invert(image));
}
}
best
};
for case in 0..512u16 {
let ambiguous = AMBIGUOUS_FACES[case as usize] != 0;
for p in &DIHEDRAL {
assert_eq!(
AMBIGUOUS_FACES[permute(case, p) as usize] != 0,
ambiguous,
"case {case:#011b} changes ambiguity under a symmetry"
);
}
assert_eq!(
AMBIGUOUS_FACES[invert(case) as usize] != 0,
ambiguous,
"case {case:#011b} changes ambiguity under inversion"
);
}
let mut classes: Vec<u16> = (0..512u16).map(canonical).collect();
classes.sort_unstable();
classes.dedup();
std::println!("{} equivalence classes over 512 cases", classes.len());
assert_eq!(
classes.len(),
73,
"Lengyel 2010 section 4.3 says exactly 73; got {}",
classes.len()
);
assert_eq!(
canonical(0),
canonical(0x1ff),
"the trivial class must be one class"
);
}
#[test]
fn every_dihedral_orbit_size_divides_eight() {
for case in 0..512u16 {
let mut orbit: Vec<u16> = DIHEDRAL.iter().map(|p| permute(case, p)).collect();
orbit.sort_unstable();
orbit.dedup();
assert_eq!(
8 % orbit.len(),
0,
"case {case:#011b} has orbit size {}",
orbit.len()
);
}
}
#[test]
fn the_class_split_is_fifty_one_and_twenty_two() {
let canonical = |case: u16| -> u16 {
let inversion_applies = AMBIGUOUS_FACES[case as usize] == 0;
let mut best = case;
for p in &DIHEDRAL {
let image = permute(case, p);
best = best.min(image);
if inversion_applies {
best = best.min(invert(image));
}
}
best
};
let mut classes: Vec<u16> = (0..512u16).map(canonical).collect();
classes.sort_unstable();
classes.dedup();
let mut light = 0usize;
let mut heavy = 0usize;
for &rep in &classes {
let members: Vec<u16> = (0..512u16).filter(|c| canonical(*c) == rep).collect();
if members.iter().any(|m| m.count_ones() <= 4) {
light += 1;
} else {
heavy += 1;
}
}
std::println!("{light} classes reach four-or-fewer inside, {heavy} do not");
assert_eq!(light, 51);
assert_eq!(heavy, 22);
}