use num_traits::{FromPrimitive, PrimInt, WrappingAdd, WrappingSub};
pub trait Int: PrimInt + FromPrimitive + WrappingAdd + WrappingSub {}
impl<I: PrimInt + FromPrimitive + WrappingAdd + WrappingSub> Int for I {}
#[derive(Debug, Copy, Clone)]
enum Leg {
Center,
Right,
Top,
Left,
Bottom,
}
#[derive(Debug, Clone)]
pub struct ChebyshevIterator<I: Int> {
max_distance: I,
start_x: I,
start_y: I,
x: I,
y: I,
layer: I,
leg: Leg,
}
impl<I: Int> ChebyshevIterator<I> {
pub fn new(x: I, y: I, max_distance: I) -> Self {
ChebyshevIterator {
max_distance,
start_x: x,
start_y: y,
x: I::zero(),
y: I::zero(),
layer: I::one(),
leg: Leg::Center,
}
}
}
impl<I: Int> Iterator for ChebyshevIterator<I> {
type Item = (I, I);
fn next(&mut self) -> Option<(I, I)> {
match self.leg {
Leg::Center => {
self.leg = Leg::Right;
}
Leg::Right => {
self.x = self.x.wrapping_add(&I::one());
if self.x == self.layer {
self.leg = Leg::Top;
if self.layer == self.max_distance {
return None;
}
}
}
Leg::Top => {
self.y = self.y.wrapping_add(&I::one());
if self.y == self.layer {
self.leg = Leg::Left;
}
}
Leg::Left => {
self.x = self.x.wrapping_sub(&I::one());
if self.x.wrapping_add(&self.layer).is_zero() {
self.leg = Leg::Bottom;
}
}
Leg::Bottom => {
self.y = self.y.wrapping_sub(&I::one());
if self.y.wrapping_add(&self.layer).is_zero() {
self.leg = Leg::Right;
self.layer = self.layer.add(I::one());
}
}
}
Some((
self.start_x.wrapping_add(&self.x),
self.start_y.wrapping_add(&self.y),
))
}
}
#[derive(Debug, Clone)]
pub struct ManhattanIterator<I: Int> {
max_distance: I,
start_x: I,
start_y: I,
x: I,
y: I,
layer: I,
leg: Leg,
}
impl<I: Int> ManhattanIterator<I> {
pub fn new(x: I, y: I, max_distance: I) -> Self {
ManhattanIterator {
max_distance,
start_x: x,
start_y: y,
x: I::from_u8(2).expect("Could not instantiate number"),
y: I::zero().wrapping_sub(&I::one()),
layer: I::one(),
leg: Leg::Center,
}
}
}
impl<I: Int> Iterator for ManhattanIterator<I> {
type Item = (I, I);
fn next(&mut self) -> Option<(I, I)> {
match self.leg {
Leg::Center => {
self.leg = Leg::Right;
return Some((self.start_x, self.start_y));
}
Leg::Right => {
if self.max_distance == I::one() {
return None;
}
self.x = self.x.wrapping_sub(&I::one());
self.y = self.y.wrapping_add(&I::one());
if self.x.is_zero() {
self.leg = Leg::Top;
}
}
Leg::Top => {
self.x = self.x.wrapping_sub(&I::one());
self.y = self.y.wrapping_sub(&I::one());
if self.y.is_zero() {
self.leg = Leg::Left;
}
}
Leg::Left => {
self.x = self.x.wrapping_add(&I::one());
self.y = self.y.wrapping_sub(&I::one());
if self.x.is_zero() {
self.leg = Leg::Bottom;
}
}
Leg::Bottom => {
self.x = self.x.wrapping_add(&I::one());
self.y = self.y.wrapping_add(&I::one());
if self.y.is_zero() {
self.x = self.x.wrapping_add(&I::one());
self.leg = Leg::Right;
self.layer = self.layer.wrapping_add(&I::one());
if self.layer == self.max_distance {
return None;
}
}
}
}
Some((self.start_x + self.x, self.start_y + self.y))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn output() {
const SIZE: usize = 7;
println!("Manhattan");
let mut output: [i32; SIZE * SIZE] = [0; SIZE * SIZE];
let mut current = 0;
let spiral = ManhattanIterator::new(3, 3, 4);
for (x, y) in spiral {
current += 1;
let index = x as usize + (y as usize * SIZE);
output[index] = current;
}
for y in 0..SIZE {
for x in 0..SIZE {
let index = x as usize + (y as usize * SIZE);
let output_val = output[index];
print!("{:3} ", output_val);
}
println!();
}
println!("Chebyshev");
output = [0; SIZE * SIZE];
current = 0;
let spiral = ChebyshevIterator::new(3, 3, 4);
for (x, y) in spiral {
current += 1;
let index = x as usize + (y as usize * SIZE);
output[index] = current;
}
for y in 0..SIZE {
for x in 0..SIZE {
let index = x as usize + (y as usize * SIZE);
let output_val = output[index];
print!("{:3} ", output_val);
}
println!();
}
}
#[test]
fn manhattan_bounds() {
for size in 1..100 {
let max_distance = size + 1;
for (x, y) in ManhattanIterator::new(0i16, 0i16, size) {
let distance = x.abs() + y.abs();
assert!(
distance <= max_distance,
"spiral was out of bounds: distance {}, size: {}, x: {}, y: {}",
distance,
size,
x,
y
);
}
}
}
#[test]
fn chebyshev_bounds() {
for size in 1..100 {
let max_distance = (size + 1) as i32;
for (x, y) in ChebyshevIterator::new(0i32, 0i32, size) {
let distance = std::cmp::max(x.abs(), y.abs());
assert!(
distance <= max_distance,
"spiral was out of bounds: distance {}, size: {}, x: {}, y: {}",
distance,
size,
x,
y
);
}
}
}
#[test]
fn manhattan() {
let expected: [i32; 3 * 3] = [0, 5, 0, 4, 1, 2, 0, 3, 0];
let mut current = 0;
for (x, y) in ManhattanIterator::new(1, 1, 2) {
current += 1;
let index = x + y * 3;
assert_eq!(expected[index as usize], current);
}
let expected: [i32; 5 * 5] = [
0, 0, 12, 0, 0, 0, 11, 5, 13, 0, 10, 4, 1, 2, 6, 0, 9, 3, 7, 0, 0, 0, 8, 0, 0,
];
current = 0;
for (x, y) in ManhattanIterator::new(2, 2, 3) {
current += 1;
let index = x + y * 5;
assert_eq!(expected[index as usize], current);
}
}
#[test]
fn manhattan_unsigned() {
ManhattanIterator::new(0u32, 0u32, 4u32).for_each(drop);
}
#[test]
fn chebyshev() {
let expected: [i32; 5 * 5] = [
21, 22, 23, 24, 25, 20, 7, 8, 9, 10, 19, 6, 1, 2, 11, 18, 5, 4, 3, 12, 17, 16, 15, 14,
13,
];
let mut current = 0;
for (x, y) in ChebyshevIterator::new(2i32, 2i32, 3i32) {
current += 1;
let index = x + y * 5;
assert_eq!(expected[index as usize], current);
}
}
#[test]
fn chebyshev_unsigned() {
let expected: [u32; 5 * 5] = [
21, 22, 23, 24, 25, 20, 7, 8, 9, 10, 19, 6, 1, 2, 11, 18, 5, 4, 3, 12, 17, 16, 15, 14,
13,
];
let mut current = 0;
for (x, y) in ChebyshevIterator::new(2u32, 2u32, 3u32) {
current += 1;
let index = x + y * 5;
assert_eq!(expected[index as usize], current);
}
}
}