const LANES: usize = 8;
#[must_use]
pub(crate) fn sqdist(a: &[f32], b: &[f32]) -> f32 {
let n = a.len().min(b.len());
let (xs, x_tail) = a[..n].as_chunks::<LANES>();
let (ys, _) = b[..n].as_chunks::<LANES>();
let mut totals = [0.0f32; LANES];
for (x, y) in xs.iter().zip(ys) {
for k in 0..LANES {
let d = x[k] - y[k];
totals[k] += d * d;
}
}
let mut sum = 0.0f32;
for total in totals {
sum += total;
}
for (x, y) in x_tail.iter().zip(&b[n - x_tail.len()..]) {
let d = x - y;
sum += d * d;
}
sum
}
#[cfg(test)]
mod tests {
use super::*;
use yo_common::Rng;
fn unit(rng: &mut Rng) -> f32 {
(rng.next_u64() >> 40) as f32 / (1u32 << 24) as f32
}
fn plain(a: &[f32], b: &[f32]) -> f32 {
a.iter().zip(b).map(|(x, y)| (x - y) * (x - y)).sum()
}
#[test]
fn it_agrees_with_the_obvious_loop_at_every_length() {
let mut rng = Rng::new(0x51D1);
let a: Vec<f32> = (0..200).map(|_| unit(&mut rng) * 4.0 - 2.0).collect();
let b: Vec<f32> = (0..200).map(|_| unit(&mut rng) * 4.0 - 2.0).collect();
for n in 0..=200 {
let want = plain(&a[..n], &b[..n]);
let got = sqdist(&a[..n], &b[..n]);
assert!(
(got - want).abs() <= want.abs() * 1e-5 + 1e-6,
"at {n} dimensions, {got} against {want}"
);
}
}
#[test]
fn a_vector_is_no_distance_from_itself() {
let mut rng = Rng::new(7);
let a: Vec<f32> = (0..128).map(|_| unit(&mut rng)).collect();
assert_eq!(sqdist(&a, &a), 0.0);
}
#[test]
fn different_lengths_are_measured_over_the_shorter_one() {
let a = [1.0f32; 20];
let b = [0.0f32; 9];
assert_eq!(sqdist(&a, &b), 9.0);
assert_eq!(sqdist(&b, &a), 9.0);
}
}