pub fn dot(a: &[f32], b: &[f32]) -> f32 {
assert_eq!(
a.len(),
b.len(),
"dot requires equal-length vectors (got {} and {})",
a.len(),
b.len(),
);
let mut sum = 0.0f32;
for (x, y) in a.iter().zip(b.iter()) {
sum += x * y;
}
sum
}
pub fn squared_l2(a: &[f32], b: &[f32]) -> f32 {
assert_eq!(
a.len(),
b.len(),
"squared_l2 requires equal-length vectors (got {} and {})",
a.len(),
b.len(),
);
let mut sum = 0.0f32;
for (x, y) in a.iter().zip(b.iter()) {
let d = x - y;
sum += d * d;
}
sum
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn squared_l2_is_zero_for_identical_vectors() {
assert_eq!(squared_l2(&[1.0, 2.0, 3.0], &[1.0, 2.0, 3.0]), 0.0);
}
#[test]
fn squared_l2_matches_manual_calculation() {
assert_eq!(squared_l2(&[0.0, 0.0], &[3.0, 4.0]), 25.0);
}
#[test]
fn squared_l2_is_symmetric() {
let a = [1.0_f32, -2.0, 0.5];
let b = [4.0_f32, 0.0, -1.5];
assert_eq!(squared_l2(&a, &b), squared_l2(&b, &a));
}
#[test]
fn squared_l2_handles_empty_vectors() {
assert_eq!(squared_l2(&[], &[]), 0.0);
}
#[test]
#[should_panic(expected = "equal-length vectors")]
fn squared_l2_panics_on_length_mismatch() {
let _ = squared_l2(&[1.0, 2.0], &[1.0]);
}
#[test]
fn dot_is_zero_for_orthogonal_vectors() {
assert_eq!(dot(&[1.0, 0.0, 0.0], &[0.0, 1.0, 0.0]), 0.0);
}
#[test]
fn dot_matches_manual_calculation() {
assert_eq!(dot(&[1.0, 2.0, 3.0], &[4.0, 5.0, 6.0]), 32.0);
}
#[test]
fn dot_is_symmetric() {
let a = [1.0_f32, -2.0, 0.5];
let b = [4.0_f32, 0.0, -1.5];
assert_eq!(dot(&a, &b), dot(&b, &a));
}
#[test]
fn dot_of_unit_vector_with_itself_is_one() {
let v = [0.6_f32, 0.8];
assert!((dot(&v, &v) - 1.0).abs() < 1e-6);
}
#[test]
fn dot_handles_empty_vectors() {
assert_eq!(dot(&[], &[]), 0.0);
}
#[test]
#[should_panic(expected = "equal-length vectors")]
fn dot_panics_on_length_mismatch() {
let _ = dot(&[1.0, 2.0], &[1.0]);
}
}