extern crate test; use crate::common::Inc; use std::{ fmt::Debug, hash::Hash, iter::Step, ops::{Add, AddAssign, Index, IndexMut}, }; #[derive(Hash, PartialEq, Eq, Debug, Clone, Copy)] #[repr(transparent)] pub struct PositionND(pub [I; DIMS]); pub type Position2D = PositionND; pub const fn num_neighbors(d: usize) -> usize { 3usize.pow(d as u32) - 1 } impl + AddAssign + Debug, const DIMS: usize> PositionND { pub fn zero() -> Self { PositionND([I::default(); DIMS]) } pub fn from_padded(slice: &[I]) -> PositionND { let mut points = [I::default(); DIMS]; #[allow(clippy::manual_memcpy)] for i in 0..(DIMS.min(slice.len())) { points[i] = slice[i]; } PositionND(points) } pub fn neighbors(&self) -> [PositionND; num_neighbors(DIMS)] where [PositionND; num_neighbors(DIMS) + 1]: Sized { let ns = neighbor_vectors::(); let mut out = [*self; num_neighbors(DIMS)]; for (out, dir) in out.iter_mut().zip(IntoIterator::into_iter(ns).filter(|n| n != &[I::default(); DIMS])) { *out += PositionND(dir); } out } } impl Add> for PositionND where I: AddAssign + Copy { type Output = PositionND; fn add(mut self, rhs: PositionND) -> Self::Output { for (x, y) in self.0.iter_mut().zip(rhs.0) { *x += y; } self } } impl AddAssign> for PositionND where I: AddAssign + Copy { fn add_assign(&mut self, rhs: PositionND) { for i in 0..D { self[i] += rhs[i]; } } } impl Index for PositionND { type Output = I; fn index(&self, index: usize) -> &Self::Output { &self.0[index] } } impl IndexMut for PositionND { fn index_mut(&mut self, index: usize) -> &mut Self::Output { &mut self.0[index] } } impl PositionND { pub fn neighbors_no_diagonals(&self) -> [PositionND; 4] { let PositionND([x, y]) = *self; [PositionND([x.inc(), y]), PositionND([x, y.inc()]), PositionND([x.dec(), y]), PositionND([x, y.dec()])] } } macro_rules! dim { ($d: expr, $i:ty) => {{ let zero: $i = Default::default(); let mut out = [[zero; D]; num_neighbors(D) + 1]; let mut i = 0; for offset in zero.dec()..=zero.inc() { for inner in neighbor_vectors::<$i, $d>() { out[i][0] = offset; let mut j = 1; for e in inner { out[i][j] = e; j += 1; } i += 1; } } out }}; } fn neighbor_vectors() -> [[I; D]; num_neighbors(D) + 1] { // I would love to just call neighbor_vectors::(), but it seems to be impossible to get the // correct constraints for that. match D { 0 => unreachable!(), 1 => { let zero = I::default(); let mut out = [[zero; D]; num_neighbors(D) + 1]; out[0] = [zero.dec(); D]; out[1] = [zero; D]; out[2] = [zero.inc(); D]; out } 2 => dim!(1, I), 3 => dim!(2, I), 4 => dim!(3, I), 5 => dim!(4, I), 6 => dim!(5, I), 7 => dim!(6, I), // Adding more causes a stackoverflow. How curious. _ => unimplemented!(), } } #[cfg(test)] mod tests { use super::*; #[test] fn test_neighbors_2d() { let p = PositionND([0, 0]); let n = p.neighbors(); assert_eq!( n, [ PositionND([-1, -1]), PositionND([-1, 0]), PositionND([-1, 1]), PositionND([0, -1]), PositionND([0, 1]), PositionND([1, -1]), PositionND([1, 0]), PositionND([1, 1]), ] ); let p = PositionND([1, 1]); let n = p.neighbors(); assert_eq!( n, [ PositionND([0, 0]), PositionND([0, 1]), PositionND([0, 2]), PositionND([1, 0]), PositionND([1, 2]), PositionND([2, 0]), PositionND([2, 1]), PositionND([2, 2]), ] ) } #[test] fn test_neighbors_3d() { let p = PositionND([0, 0, 0]); let n = p.neighbors(); assert_eq!( n, [ PositionND([-1, -1, -1]), PositionND([-1, -1, 0]), PositionND([-1, -1, 1]), PositionND([-1, 0, -1]), PositionND([-1, 0, 0]), PositionND([-1, 0, 1]), PositionND([-1, 1, -1]), PositionND([-1, 1, 0]), PositionND([-1, 1, 1]), PositionND([0, -1, -1]), PositionND([0, -1, 0]), PositionND([0, -1, 1]), PositionND([0, 0, -1]), PositionND([0, 0, 1]), PositionND([0, 1, -1]), PositionND([0, 1, 0]), PositionND([0, 1, 1]), PositionND([1, -1, -1]), PositionND([1, -1, 0]), PositionND([1, -1, 1]), PositionND([1, 0, -1]), PositionND([1, 0, 0]), PositionND([1, 0, 1]), PositionND([1, 1, -1]), PositionND([1, 1, 0]), PositionND([1, 1, 1]), ] ); } #[test] fn test_neighbor_vectors() { let n = neighbor_vectors::(); assert_eq!(n, [[-1, -1], [-1, 0], [-1, 1], [0, -1], [0, 0], [0, 1], [1, -1], [1, 0], [1, 1],]); } #[bench] fn bench_neighbor_vectors_2d(b: &mut test::Bencher) { b.iter(|| test::black_box(neighbor_vectors::())) } #[bench] fn bench_neighbor_vectors_3d(b: &mut test::Bencher) { b.iter(|| test::black_box(neighbor_vectors::())) } #[bench] fn bench_neighbor_vectors_4d(b: &mut test::Bencher) { b.iter(|| test::black_box(neighbor_vectors::())) } #[bench] fn bench_neighbor_vectors_5d(b: &mut test::Bencher) { b.iter(|| test::black_box(neighbor_vectors::())) } }