use crate::{CellIndex, Direction};
use alloc::collections::VecDeque;
#[cfg(feature = "std")]
use ahash::{HashSet, HashSetExt};
#[cfg(not(feature = "std"))]
use alloc::collections::BTreeSet;
#[cfg(not(feature = "std"))]
type Set<K> = BTreeSet<K>;
#[cfg(feature = "std")]
type Set<K> = HashSet<K>;
const NEXT_RING_DIRECTION: Direction = Direction::I;
const DIRECTIONS: [Direction; 6] = [
Direction::J,
Direction::JK,
Direction::K,
Direction::IK,
Direction::I,
Direction::IJ,
];
pub struct DiskDistancesSafe {
k: u32,
seen: Set<CellIndex>,
candidates: VecDeque<(CellIndex, u32)>,
}
impl DiskDistancesSafe {
pub fn new(origin: CellIndex, k: u32) -> Self {
let size = usize::try_from(crate::max_grid_disk_size(k))
.expect("grid too large");
let mut candidates = VecDeque::with_capacity(size * 5 / 2);
candidates.push_back((origin, 0));
Self {
k,
#[cfg(feature = "std")]
seen: Set::with_capacity(size),
#[cfg(not(feature = "std"))]
seen: Set::new(),
candidates,
}
}
}
impl Iterator for DiskDistancesSafe {
type Item = (CellIndex, u32);
fn next(&mut self) -> Option<Self::Item> {
while let Some((cell, ring)) = self.candidates.pop_front() {
if ring > self.k || self.seen.contains(&cell) {
continue;
}
if ring < self.k {
self.candidates.extend((0..6).filter_map(|i| {
super::neighbor_rotations(cell, DIRECTIONS[i], 0)
.map(|(origin, _)| (origin, ring + 1))
}));
}
self.seen.insert(cell);
return Some((cell, ring));
}
None
}
}
pub struct DiskDistancesUnsafe {
origin: CellIndex,
k: u32,
is_failed: bool,
ring: u32,
side: u8,
position: u32,
rotations: u8,
}
impl DiskDistancesUnsafe {
pub const fn new(origin: CellIndex, k: u32) -> Self {
Self {
origin,
k,
is_failed: false,
ring: 0,
side: 0,
position: 0,
rotations: 0,
}
}
}
impl Iterator for DiskDistancesUnsafe {
type Item = Option<(CellIndex, u32)>;
fn next(&mut self) -> Option<Self::Item> {
if self.is_failed || self.ring > self.k {
return None;
}
if self.side == 0 && self.position == 0 {
if self.ring == 0 {
self.ring += 1;
return Some(if self.origin.is_pentagon() {
self.is_failed = true;
None
} else {
Some((self.origin, 0))
});
}
let Some((new_origin, new_rotations)) = super::neighbor_rotations(
self.origin,
NEXT_RING_DIRECTION,
self.rotations,
) else {
self.is_failed = true;
return Some(None);
};
self.origin = new_origin;
self.rotations = new_rotations;
}
let Some((new_origin, new_rotations)) = super::neighbor_rotations(
self.origin,
DIRECTIONS[usize::from(self.side)],
self.rotations,
) else {
self.is_failed = true;
return Some(None);
};
self.origin = new_origin;
self.rotations = new_rotations;
let distance = self.ring;
self.position += 1;
if self.position == self.ring {
self.position = 0;
self.side += 1;
if self.side == 6 {
self.side = 0;
self.ring += 1;
}
}
Some(if self.origin.is_pentagon() {
self.is_failed = true;
None
} else {
Some((self.origin, distance))
})
}
}
pub struct RingUnsafe {
k: u32,
rotations: u8,
direction: u8,
position: u32,
origin: CellIndex,
last_index: CellIndex,
}
impl RingUnsafe {
pub fn new(mut origin: CellIndex, k: u32) -> Option<Self> {
let mut rotations = 0;
if origin.is_pentagon() {
return None;
}
for _ in 0..k {
(origin, rotations) = super::neighbor_rotations(
origin,
NEXT_RING_DIRECTION,
rotations,
)?;
if origin.is_pentagon() {
return None;
}
}
Some(Self {
k,
rotations,
direction: 0,
position: 0,
origin,
last_index: origin,
})
}
}
impl Iterator for RingUnsafe {
type Item = Option<CellIndex>;
fn next(&mut self) -> Option<Self::Item> {
if self.direction == 6 {
if self.origin != self.last_index {
return Some(None);
}
return None;
}
let item = self.origin;
(self.origin, self.rotations) = super::neighbor_rotations(
self.origin,
DIRECTIONS[usize::from(self.direction)],
self.rotations,
)
.expect("ring neighbor");
if self.origin.is_pentagon() {
return Some(None);
}
self.position += 1;
if self.position == self.k {
self.position = 0;
self.direction += 1;
}
Some(Some(item))
}
fn size_hint(&self) -> (usize, Option<usize>) {
let count = usize::try_from(crate::max_grid_disk_size(self.k))
.unwrap_or(usize::MAX);
(count, Some(count))
}
}
#[cfg(test)]
#[path = "./iterator_tests.rs"]
mod tests;