pub fn cosine_similarity(a: &[f32], b: &[f32]) -> f32 {
if a.len() != b.len() {
return 0.0;
}
let dot_product: f32 = a.iter().zip(b.iter()).map(|(x, y)| x * y).sum();
let magnitude_a: f32 = a.iter().map(|x| x * x).sum::<f32>().sqrt();
let magnitude_b: f32 = b.iter().map(|x| x * x).sum::<f32>().sqrt();
if magnitude_a == 0.0 || magnitude_b == 0.0 {
return 0.0;
}
dot_product / (magnitude_a * magnitude_b)
}
pub fn euclidean_distance(a: &[f32], b: &[f32]) -> f32 {
if a.len() != b.len() {
return f32::INFINITY;
}
a.iter()
.zip(b.iter())
.map(|(x, y)| (x - y).powi(2))
.sum::<f32>()
.sqrt()
}
pub fn dot_product(a: &[f32], b: &[f32]) -> f32 {
if a.len() != b.len() {
return 0.0;
}
a.iter().zip(b.iter()).map(|(x, y)| x * y).sum()
}
pub fn normalize(v: &[f32]) -> Vec<f32> {
let magnitude: f32 = v.iter().map(|x| x * x).sum::<f32>().sqrt();
if magnitude == 0.0 {
return v.to_vec();
}
v.iter().map(|x| x / magnitude).collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cosine_similarity_identical() {
let a = vec![1.0, 2.0, 3.0];
let b = vec![1.0, 2.0, 3.0];
let sim = cosine_similarity(&a, &b);
assert!((sim - 1.0).abs() < 1e-6);
}
#[test]
fn test_cosine_similarity_orthogonal() {
let a = vec![1.0, 0.0, 0.0];
let b = vec![0.0, 1.0, 0.0];
let sim = cosine_similarity(&a, &b);
assert!((sim - 0.0).abs() < 1e-6);
}
#[test]
fn test_cosine_similarity_opposite() {
let a = vec![1.0, 0.0];
let b = vec![-1.0, 0.0];
let sim = cosine_similarity(&a, &b);
assert!((sim - (-1.0)).abs() < 1e-6);
}
#[test]
fn test_cosine_similarity_different_lengths() {
let a = vec![1.0, 2.0, 3.0];
let b = vec![1.0, 2.0];
let sim = cosine_similarity(&a, &b);
assert_eq!(sim, 0.0);
}
#[test]
fn test_cosine_similarity_zero_vector() {
let a = vec![1.0, 2.0, 3.0];
let b = vec![0.0, 0.0, 0.0];
let sim = cosine_similarity(&a, &b);
assert_eq!(sim, 0.0);
}
#[test]
fn test_cosine_similarity_normalized() {
let a = normalize(&[1.0, 1.0]);
let b = normalize(&[1.0, 1.0]);
let sim = cosine_similarity(&a, &b);
assert!((sim - 1.0).abs() < 1e-6);
}
#[test]
fn test_euclidean_distance_identical() {
let a = vec![1.0, 2.0, 3.0];
let b = vec![1.0, 2.0, 3.0];
let dist = euclidean_distance(&a, &b);
assert!((dist - 0.0).abs() < 1e-6);
}
#[test]
fn test_euclidean_distance_3_4_5() {
let a = vec![0.0, 0.0];
let b = vec![3.0, 4.0];
let dist = euclidean_distance(&a, &b);
assert!((dist - 5.0).abs() < 1e-6);
}
#[test]
fn test_euclidean_distance_different_lengths() {
let a = vec![1.0, 2.0, 3.0];
let b = vec![1.0, 2.0];
let dist = euclidean_distance(&a, &b);
assert!(dist.is_infinite());
}
#[test]
fn test_euclidean_distance_unit() {
let a = vec![0.0];
let b = vec![1.0];
let dist = euclidean_distance(&a, &b);
assert!((dist - 1.0).abs() < 1e-6);
}
#[test]
fn test_dot_product_basic() {
let a = vec![1.0, 2.0, 3.0];
let b = vec![4.0, 5.0, 6.0];
let result = dot_product(&a, &b);
assert!((result - 32.0).abs() < 1e-6); }
#[test]
fn test_dot_product_orthogonal() {
let a = vec![1.0, 0.0];
let b = vec![0.0, 1.0];
let result = dot_product(&a, &b);
assert!((result - 0.0).abs() < 1e-6);
}
#[test]
fn test_dot_product_different_lengths() {
let a = vec![1.0, 2.0, 3.0];
let b = vec![1.0, 2.0];
let result = dot_product(&a, &b);
assert_eq!(result, 0.0);
}
#[test]
fn test_normalize_unit() {
let v = vec![3.0, 4.0];
let normalized = normalize(&v);
assert!((normalized[0] - 0.6).abs() < 1e-6);
assert!((normalized[1] - 0.8).abs() < 1e-6);
let mag: f32 = normalized.iter().map(|x| x * x).sum::<f32>().sqrt();
assert!((mag - 1.0).abs() < 1e-6);
}
#[test]
fn test_normalize_already_unit() {
let v = vec![1.0, 0.0, 0.0];
let normalized = normalize(&v);
assert!((normalized[0] - 1.0).abs() < 1e-6);
assert!((normalized[1] - 0.0).abs() < 1e-6);
}
#[test]
fn test_normalize_zero_vector() {
let v = vec![0.0, 0.0, 0.0];
let normalized = normalize(&v);
assert_eq!(normalized, vec![0.0, 0.0, 0.0]);
}
#[test]
fn test_normalize_preserves_direction() {
let v = vec![2.0, 2.0];
let normalized = normalize(&v);
let ratio = normalized[0] / normalized[1];
assert!((ratio - 1.0).abs() < 1e-6);
}
}