use alloc::vec::Vec;
use num::signum;
#[must_use]
pub fn naive(delta: [isize; 2]) -> Vec<[isize; 2]> {
let [dx, dy] = delta;
let mut ans: Vec<[isize; 2]> = vec![];
for dx_block in -8..=8 {
for dy_block in -8..=8 {
if dx * dy_block - dy * dx_block != 0 {
continue;
} if dx * dx_block + dy * dy_block <= 0 {
continue;
} if dx_block * dx_block + dy_block * dy_block >= dx * dx + dy * dy {
continue;
}
ans.push([dx_block, dy_block]);
}
}
ans
}
#[must_use]
pub fn fast(delta: [isize; 2]) -> Vec<[isize; 2]> {
if let [0, 0] = delta {
return vec![];
}
let [dx, dy] = delta;
let d_length = dx * dx + dy * dy;
let g = num::integer::gcd(dx, dy);
let qx = dx / g;
let qy = dy / g;
let mut ans: Vec<[isize; 2]> = vec![];
for mult in 1.. {
let dx_block = mult * qx;
let dy_block = mult * qy;
let d_block_length = dx_block * dx_block + dy_block * dy_block;
if core::cmp::max(dx_block.abs(), dy_block.abs()) > 8 || d_block_length >= d_length {
break;
}
ans.push([dx_block, dy_block]);
}
ans
}
pub struct Blocker {
d_length: isize,
qx: isize,
qy: isize,
mult: isize,
}
impl Iterator for Blocker {
type Item = [isize; 2];
#[inline]
fn next(&mut self) -> Option<[isize; 2]> {
let dx_block = self.mult * self.qx;
let dy_block = self.mult * self.qy;
let d_block_length = dx_block * dx_block + dy_block * dy_block;
if d_block_length >= self.d_length {
return None;
}
self.mult += 1;
Some([dx_block, dy_block])
}
}
pub fn ultrafast(delta: [isize; 2]) -> impl Iterator<Item = [isize; 2]> {
let [dx, dy] = delta;
let d_length = dx * dx + dy * dy;
let qx = signum(dx);
let qy = signum(dy);
Blocker {
d_length,
qx,
qy,
mult: 1,
}
}
#[test]
fn naive_vs_fast() {
use std::collections::HashSet;
fn assert_eq_ignoring_order<T>(a: &[T], b: &[T])
where
T: Eq + core::hash::Hash + std::fmt::Debug,
{
let a: HashSet<_> = a.iter().collect();
let b: HashSet<_> = b.iter().collect();
assert_eq!(a, b);
}
for dx in -8..=8 {
for dy in -8..=8 {
assert_eq_ignoring_order(&naive([dx, dy]), &fast([dx, dy]));
}
}
}
#[test]
fn naive_vs_ultrafast() {
use std::collections::HashSet;
fn assert_eq_ignoring_order<T>(a: &[T], b: &[T])
where
T: Eq + core::hash::Hash + std::fmt::Debug,
{
let a: HashSet<_> = a.iter().collect();
let b: HashSet<_> = b.iter().collect();
assert_eq!(a, b);
}
for [dx, dy] in [
[1, 0],
[2, 0],
[3, 0],
[4, 0],
[5, 0],
[6, 0],
[7, 0],
[8, 0],
[9, 0],
[-1, 0],
[-2, 0],
[-3, 0],
[-4, 0],
[-5, 0],
[-6, 0],
[-7, 0],
[-8, 0],
[-9, 0],
[0, 1],
[0, 2],
[0, 3],
[0, 4],
[0, 5],
[0, 6],
[0, 7],
[0, 8],
[0, 9],
[0, -1],
[0, -2],
[0, -3],
[0, -4],
[0, -5],
[0, -6],
[0, -7],
[0, -8],
[0, -9],
[1, 1],
[2, 2],
[3, 3],
[4, 4],
[5, 5],
[6, 6],
[7, 7],
[8, 8],
[9, 9],
[-1, 1],
[-2, 2],
[-3, 3],
[-4, 4],
[-5, 5],
[-6, 6],
[-7, 7],
[-8, 8],
[-9, 9],
[1, -1],
[2, -2],
[3, -3],
[4, -4],
[5, -5],
[6, -6],
[7, -7],
[8, -8],
[9, -9],
[-1, -1],
[-2, -2],
[-3, -3],
[-4, -4],
[-5, -5],
[-6, -6],
[-7, -7],
[-8, -8],
[-9, -9],
] {
assert_eq_ignoring_order(&naive([dx, dy]), &ultrafast([dx, dy]).collect::<Vec<_>>());
}
}