use crate::error::TreeError;
use crate::topology::{LeafNumber, Location};
use crate::tree::Coord;
const LEAF_EDGE: i32 = 8;
pub const DIRECTIONS: usize = 26;
const CENTRE: u8 = 13;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct Offset(u8);
impl Offset {
pub fn all() -> impl Iterator<Item = Offset> {
(0..26).map(Offset)
}
pub fn index(self) -> usize {
usize::from(self.0)
}
fn cell(self) -> u8 {
if self.0 < CENTRE { self.0 } else { self.0 + 1 }
}
pub fn delta(self) -> [i32; 3] {
let c = i32::from(self.cell());
[c % 3 - 1, (c / 3) % 3 - 1, c / 9 - 1]
}
pub fn opposite(self) -> Offset {
let c = 26 - self.cell();
Offset(if c < CENTRE { c } else { c - 1 })
}
}
#[derive(Debug, Clone)]
pub struct LeafManifest {
neighbours: Vec<[Location; DIRECTIONS]>,
}
fn step(origin: Coord, d: [i32; 3]) -> Option<Coord> {
let mut out = origin;
for (o, k) in out.iter_mut().zip(d) {
*o = o.checked_add(k * LEAF_EDGE)?;
}
Some(out)
}
impl LeafManifest {
pub(crate) const fn empty() -> Self {
Self { neighbours: Vec::new() }
}
pub(crate) fn build(origins: &[Coord], mut locate: impl FnMut(Coord) -> Location) -> Self {
let neighbours = origins
.iter()
.map(|o| {
let mut row = [Location::Background; DIRECTIONS];
for (cell, d) in row.iter_mut().zip(Offset::all()) {
*cell = step(*o, d.delta()).map_or(Location::Background, &mut locate);
}
row
})
.collect();
Self { neighbours }
}
pub fn neighbours(&self, n: LeafNumber) -> Result<&[Location; DIRECTIONS], TreeError> {
self.neighbours.get(n.get() as usize).ok_or(TreeError::UnknownLeaf(n.get()))
}
pub fn neighbour(&self, n: LeafNumber, d: Offset) -> Result<Location, TreeError> {
let row = self.neighbours(n)?;
row.get(d.index()).copied().ok_or(TreeError::UnknownLeaf(n.get()))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn directions_cover_the_block_but_its_centre() {
let deltas: Vec<[i32; 3]> = Offset::all().map(Offset::delta).collect();
assert_eq!(deltas.len(), 26);
assert_eq!(deltas[0], [-1, -1, -1]);
assert_eq!(deltas[1], [0, -1, -1]);
assert_eq!(deltas[12], [-1, 0, 0]);
assert_eq!(deltas[13], [1, 0, 0]);
assert_eq!(deltas[25], [1, 1, 1]);
assert!(!deltas.contains(&[0, 0, 0]));
let mut sorted = deltas.clone();
sorted.sort_unstable();
sorted.dedup();
assert_eq!(sorted.len(), 26);
for d in Offset::all() {
assert_eq!(d.opposite().delta(), d.delta().map(|c| -c));
assert_eq!(d.opposite().opposite(), d);
}
}
#[test]
fn a_step_beyond_the_coordinate_range_is_nothing() {
assert_eq!(step([i32::MAX - 7, 0, 0], [1, 0, 0]), None);
assert_eq!(step([i32::MIN, 0, 0], [-1, 0, 0]), None);
assert_eq!(step([0, 0, 0], [-1, 1, 0]), Some([-8, 8, 0]));
}
#[test]
fn a_location_is_eight_bytes() {
assert_eq!(size_of::<Location>(), 8);
assert_eq!(size_of::<[Location; DIRECTIONS]>(), 208);
}
}