use crate::{
CellIndex, DirectedEdgeIndex, Direction, Edge, IndexMode, Resolution,
index::bits,
};
use core::{
iter::FusedIterator,
ops::{Index, IndexMut},
};
#[rustfmt::skip]
const WALK_DIGIT: WalkDigits = WalkDigits([
Direction::K, Direction::K, Direction::K, Direction::IK, Direction::IK, Direction::IK, Direction::I, Direction::I, Direction::I, Direction::IJ, Direction::IJ, Direction::IJ, Direction::J, Direction::J, Direction::J, Direction::JK, Direction::JK, Direction::JK ]);
const EDGE_DIR: EdgeDirections = EdgeDirections([
Direction::JK,
Direction::K,
Direction::IK,
Direction::I,
Direction::IJ,
Direction::J,
]);
#[derive(Clone)]
pub struct Gosper {
scratchpad: u64,
count: u64,
edge_idx: EdgeDirectionsIndex,
walk_idx: WalkIndices,
parent_resolution: Resolution,
child_resolution: Resolution,
is_pentagon: bool,
}
impl Gosper {
pub fn new(index: CellIndex, child_resolution: Resolution) -> Self {
let parent_resolution = index.resolution();
let is_pentagon = index.is_pentagon();
let nb_sides = 6 - u64::from(is_pentagon);
let mut iter = Self {
scratchpad: 0,
count: 0,
edge_idx: EdgeDirectionsIndex::default(),
walk_idx: WalkIndices::default(),
parent_resolution,
child_resolution,
is_pentagon,
};
if child_resolution < parent_resolution {
return iter;
}
let mut scratchpad =
bits::set_mode(u64::from(index), IndexMode::DirectedEdge);
if child_resolution == parent_resolution {
iter.scratchpad = bits::set_edge(scratchpad, Direction::JK.into());
iter.count = nb_sides;
return iter;
}
let delta = u8::from(child_resolution) - u8::from(parent_resolution);
iter.count = nb_sides * 3_u64.pow(delta.into());
scratchpad = bits::set_resolution(scratchpad, child_resolution);
let first_child = parent_resolution.succ().expect("parent < child");
let direction = Direction::K.into();
scratchpad = bits::set_direction(scratchpad, direction, first_child);
if let Some(next_child) = first_child.succ() {
for res in Resolution::range(next_child, child_resolution) {
iter.walk_idx[res] = WalkDigitsIndex::new(res.is_class3());
let direction = WALK_DIGIT[iter.walk_idx[res]].into();
scratchpad = bits::set_direction(scratchpad, direction, res);
}
}
let edge = EDGE_DIR.edge_at(iter.edge_idx);
iter.scratchpad = bits::set_edge(scratchpad, edge);
iter.skip_pentagon_edges();
iter
}
fn advance_edge(&mut self) {
let cell_changed = self.advance_origin_cell();
if cell_changed {
self.edge_idx.next_cell();
}
self.edge_idx.next_edge();
let edge = EDGE_DIR.edge_at(self.edge_idx);
self.scratchpad = bits::set_edge(self.scratchpad, edge);
}
fn skip_pentagon_edges(&mut self) {
if !self.is_pentagon {
return;
}
debug_assert!(self.parent_resolution < self.child_resolution);
while bits::get_direction(
self.scratchpad,
self.parent_resolution.succ().expect("parent < child"),
) == Direction::K.into()
{
self.advance_edge();
}
}
fn advance_origin_cell(&mut self) -> bool {
let mut stack = [0; 16];
let mut depth = 0;
let mut curr_res = self.child_resolution;
let mut curr_dir = bits::get_direction(self.scratchpad, curr_res);
while let Some(prev_resolution) = curr_res.pred()
&& prev_resolution > self.parent_resolution
&& need_to_move_to_next(self.walk_idx[curr_res], curr_res)
{
stack[depth] = curr_dir;
depth += 1;
curr_res = prev_resolution;
curr_dir = bits::get_direction(self.scratchpad, curr_res);
}
self.walk_idx[curr_res].next_position();
let next_dir = WALK_DIGIT[self.walk_idx[curr_res]].into();
self.scratchpad =
bits::set_direction(self.scratchpad, next_dir, curr_res);
let mut parent_changed = next_dir != curr_dir;
while depth > 0 {
depth -= 1;
let curr_dir = stack[depth];
curr_res = curr_res.succ().expect("backtrack");
if parent_changed {
self.walk_idx[curr_res].next_parent();
}
self.walk_idx[curr_res].next_position();
let next_dir = WALK_DIGIT[self.walk_idx[curr_res]].into();
self.scratchpad =
bits::set_direction(self.scratchpad, next_dir, curr_res);
parent_changed = next_dir != curr_dir;
}
parent_changed
}
}
impl Iterator for Gosper {
type Item = DirectedEdgeIndex;
fn next(&mut self) -> Option<Self::Item> {
if self.scratchpad == 0 {
return None;
}
let res = DirectedEdgeIndex::new_unchecked(self.scratchpad);
self.count -= 1;
if self.count == 0 {
self.scratchpad = 0;
return Some(res);
}
if self.parent_resolution == self.child_resolution {
self.edge_idx.next_edge();
if self.is_pentagon && EDGE_DIR[self.edge_idx] == Direction::K {
self.edge_idx.next_edge();
}
let edge = EDGE_DIR.edge_at(self.edge_idx);
self.scratchpad = bits::set_edge(self.scratchpad, edge);
} else {
self.advance_edge();
self.skip_pentagon_edges();
}
Some(res)
}
fn size_hint(&self) -> (usize, Option<usize>) {
let count = usize::try_from(self.count).unwrap_or(usize::MAX);
(count, Some(count))
}
}
impl ExactSizeIterator for Gosper {}
impl FusedIterator for Gosper {}
#[inline]
fn need_to_move_to_next(
walk_idx: WalkDigitsIndex,
curr_resolution: Resolution,
) -> bool {
const MOD3_LUT: u64 = 0xfffffffd34d34d34;
let shift = walk_idx.0 << 1;
let class = u8::from(curr_resolution) & 1;
(MOD3_LUT >> shift) & 0b11 == u64::from(class)
}
struct EdgeDirections([Direction; 6]);
impl EdgeDirections {
fn edge_at(&self, index: EdgeDirectionsIndex) -> Edge {
self[index].into()
}
}
impl Index<EdgeDirectionsIndex> for EdgeDirections {
type Output = Direction;
fn index(&self, index: EdgeDirectionsIndex) -> &Self::Output {
&self.0[usize::from(index.0)]
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, Ord, PartialOrd)]
struct EdgeDirectionsIndex(u8);
impl EdgeDirectionsIndex {
const fn next_cell(&mut self) {
if self.0 >= 2 {
self.0 -= 2;
} else {
self.0 += 4;
}
}
const fn next_edge(&mut self) {
if self.0 == 5 {
self.0 = 0;
} else {
self.0 += 1;
}
}
}
struct WalkDigits([Direction; 18]);
impl Index<WalkDigitsIndex> for WalkDigits {
type Output = Direction;
fn index(&self, index: WalkDigitsIndex) -> &Self::Output {
&self.0[usize::from(index.0)]
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, Ord, PartialOrd)]
struct WalkDigitsIndex(u8);
impl WalkDigitsIndex {
const fn new(is_class3: bool) -> Self {
Self(if is_class3 { 14 } else { 16 })
}
const fn next_parent(&mut self) {
if self.0 < 6 {
self.0 += 12;
} else {
self.0 -= 6;
}
}
const fn next_position(&mut self) {
if self.0 == 17 {
self.0 = 0;
} else {
self.0 += 1;
}
}
}
#[derive(Clone, Default)]
struct WalkIndices([WalkDigitsIndex; 16]);
impl Index<Resolution> for WalkIndices {
type Output = WalkDigitsIndex;
fn index(&self, index: Resolution) -> &Self::Output {
&self.0[usize::from(index)]
}
}
impl IndexMut<Resolution> for WalkIndices {
fn index_mut(&mut self, index: Resolution) -> &mut Self::Output {
&mut self.0[usize::from(index)]
}
}
#[cfg(test)]
#[path = "./gosper_tests.rs"]
mod tests;