#![allow(unused_unsafe)]
use std::sync::OnceLock;
static HAS_AVX2: OnceLock<bool> = OnceLock::new();
#[inline]
pub fn dot_product_scalar(a: &[f32], b: &[f32]) -> f32 {
assert_eq!(a.len(), b.len(), "Vectors must have the same length");
a.iter().zip(b.iter()).map(|(x, y)| x * y).sum()
}
#[cfg(target_arch = "x86_64")]
#[target_feature(enable = "avx2")]
#[inline]
unsafe fn dot_product_avx2(a: &[f32], b: &[f32]) -> f32 {
use std::arch::x86_64::*;
assert_eq!(a.len(), b.len(), "Vectors must have the same length");
let len = a.len();
let mut result = 0.0f32;
let simd_len = len & !7; let mut i = 0;
if simd_len > 0 {
let mut sum0 = unsafe { _mm256_setzero_ps() };
while i < simd_len {
let a_vec = unsafe { _mm256_loadu_ps(a.as_ptr().add(i)) };
let b_vec = unsafe { _mm256_loadu_ps(b.as_ptr().add(i)) };
#[cfg(target_feature = "fma")]
{
sum0 = unsafe { _mm256_fmadd_ps(a_vec, b_vec, sum0) };
}
#[cfg(not(target_feature = "fma"))]
{
let mul = unsafe { _mm256_mul_ps(a_vec, b_vec) };
sum0 = unsafe { _mm256_add_ps(mul, sum0) };
}
i += 8;
}
let high = unsafe { _mm256_extractf128_ps(sum0, 1) }; let low = _mm256_castps256_ps128(sum0); let sum128 = unsafe { _mm_add_ps(high, low) };
let shuffle = unsafe { _mm_shuffle_ps(sum128, sum128, 0b01_00_11_10) };
let sum2 = unsafe { _mm_add_ps(sum128, shuffle) };
let shuffle2 = unsafe { _mm_shuffle_ps(sum2, sum2, 0b00_00_11_11) };
let sum3 = unsafe { _mm_add_ps(sum2, shuffle2) };
result = unsafe { _mm_cvtss_f32(sum3) };
}
while i < len {
result += a[i] * b[i];
i += 1;
}
result
}
#[inline]
pub fn dot_product(a: &[f32], b: &[f32]) -> f32 {
#[cfg(target_arch = "x86_64")]
{
let has_avx2 = HAS_AVX2.get_or_init(|| std::arch::is_x86_feature_detected!("avx2"));
if *has_avx2 {
unsafe { dot_product_avx2(a, b) }
} else {
dot_product_scalar(a, b)
}
}
#[cfg(not(target_arch = "x86_64"))]
{
dot_product_scalar(a, b)
}
}
#[inline]
pub fn compute_norm_squared_scalar(v: &[f32]) -> f32 {
v.iter().map(|x| x * x).sum()
}
#[cfg(target_arch = "x86_64")]
#[target_feature(enable = "avx2")]
#[inline]
unsafe fn compute_norm_squared_avx2(v: &[f32]) -> f32 {
unsafe {
use std::arch::x86_64::*;
let len = v.len();
let mut result = 0.0f32;
let simd_len = len & !7; let mut i = 0;
if simd_len > 0 {
let mut sum0 = unsafe { _mm256_setzero_ps() };
while i < simd_len {
let v_vec = _mm256_loadu_ps(v.as_ptr().add(i));
let squared = _mm256_mul_ps(v_vec, v_vec);
sum0 = _mm256_add_ps(squared, sum0);
i += 8;
}
let high = unsafe { _mm256_extractf128_ps(sum0, 1) };
let low = _mm256_castps256_ps128(sum0);
let sum128 = unsafe { _mm_add_ps(high, low) };
let shuffle = unsafe { _mm_shuffle_ps(sum128, sum128, 0b01_00_11_10) };
let sum2 = unsafe { _mm_add_ps(sum128, shuffle) };
let shuffle2 = unsafe { _mm_shuffle_ps(sum2, sum2, 0b00_00_11_11) };
let sum3 = unsafe { _mm_add_ps(sum2, shuffle2) };
result = unsafe { _mm_cvtss_f32(sum3) };
}
while i < len {
let val = v[i];
result += val * val;
i += 1;
}
result
}
}
#[inline]
pub fn compute_norm_squared(v: &[f32]) -> f32 {
#[cfg(target_arch = "x86_64")]
{
let has_avx2 = HAS_AVX2.get_or_init(|| std::arch::is_x86_feature_detected!("avx2"));
if *has_avx2 {
unsafe { compute_norm_squared_avx2(v) }
} else {
compute_norm_squared_scalar(v)
}
}
#[cfg(not(target_arch = "x86_64"))]
{
compute_norm_squared_scalar(v)
}
}
#[inline]
pub fn cosine_similarity_scalar(a: &[f32], b: &[f32]) -> f32 {
assert!(!a.is_empty(), "Vectors cannot be empty");
let dot = dot_product_scalar(a, b);
let norm_a = compute_norm_squared_scalar(a).sqrt();
let norm_b = compute_norm_squared_scalar(b).sqrt();
assert!(norm_a > f32::EPSILON, "First vector has zero magnitude");
assert!(norm_b > f32::EPSILON, "Second vector has zero magnitude");
dot / (norm_a * norm_b)
}
#[cfg(target_arch = "x86_64")]
#[target_feature(enable = "avx2")]
#[inline]
unsafe fn cosine_similarity_avx2(a: &[f32], b: &[f32]) -> f32 {
unsafe {
assert!(!a.is_empty(), "Vectors cannot be empty");
let dot = dot_product_avx2(a, b);
let norm_a = compute_norm_squared_avx2(a).sqrt();
let norm_b = compute_norm_squared_avx2(b).sqrt();
assert!(norm_a > f32::EPSILON, "First vector has zero magnitude");
assert!(norm_b > f32::EPSILON, "Second vector has zero magnitude");
dot / (norm_a * norm_b)
}
}
#[inline]
pub fn cosine_similarity(a: &[f32], b: &[f32]) -> f32 {
assert_eq!(a.len(), b.len(), "Vectors must have the same length");
#[cfg(target_arch = "x86_64")]
{
let has_avx2 = HAS_AVX2.get_or_init(|| std::arch::is_x86_feature_detected!("avx2"));
if *has_avx2 {
unsafe { cosine_similarity_avx2(a, b) }
} else {
cosine_similarity_scalar(a, b)
}
}
#[cfg(not(target_arch = "x86_64"))]
{
cosine_similarity_scalar(a, b)
}
}
#[inline]
pub fn euclidean_distance_scalar(a: &[f32], b: &[f32]) -> f32 {
assert_eq!(a.len(), b.len(), "Vectors must have the same length");
a.iter()
.zip(b.iter())
.map(|(x, y)| {
let diff = x - y;
diff * diff
})
.sum::<f32>()
.sqrt()
}
#[cfg(target_arch = "x86_64")]
#[target_feature(enable = "avx2")]
#[inline]
unsafe fn euclidean_distance_avx2(a: &[f32], b: &[f32]) -> f32 {
unsafe {
use std::arch::x86_64::*;
assert_eq!(a.len(), b.len(), "Vectors must have the same length");
let len = a.len();
let mut sum = 0.0_f32;
let chunks = len / 8;
let remainder = len % 8;
let mut i = 0;
for _ in 0..chunks {
let av = _mm256_loadu_ps(a.as_ptr().add(i));
let bv = _mm256_loadu_ps(b.as_ptr().add(i));
let diff = _mm256_sub_ps(av, bv);
let squared = _mm256_mul_ps(diff, diff);
let high = _mm256_extractf128_ps(squared, 1);
let low = _mm256_castps256_ps128(squared);
let sum2 = _mm_add_ps(low, high);
let mut tmp = [0.0_f32; 4];
_mm_storeu_ps(tmp.as_mut_ptr(), sum2);
sum += tmp[0] + tmp[1] + tmp[2] + tmp[3];
i += 8;
}
for j in 0..remainder {
let diff = a[i + j] - b[i + j];
sum += diff * diff;
}
sum.sqrt()
}
}
#[inline]
pub fn euclidean_distance(a: &[f32], b: &[f32]) -> f32 {
assert_eq!(a.len(), b.len(), "Vectors must have the same length");
#[cfg(target_arch = "x86_64")]
{
let has_avx2 = HAS_AVX2.get_or_init(|| std::arch::is_x86_feature_detected!("avx2"));
if *has_avx2 {
unsafe { euclidean_distance_avx2(a, b) }
} else {
euclidean_distance_scalar(a, b)
}
}
#[cfg(not(target_arch = "x86_64"))]
{
euclidean_distance_scalar(a, b)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_dot_product_correctness() {
let a: Vec<f32> = (1..=100).map(|i| i as f32).collect();
let b: Vec<f32> = (101..=200).map(|i| i as f32).collect();
let result = dot_product(&a, &b);
let expected: f32 = a.iter().zip(b.iter()).map(|(x, y)| x * y).sum();
assert!((result - expected).abs() < f32::EPSILON);
}
#[test]
fn test_dot_product_empty_vectors() {
let a: Vec<f32> = vec![];
let b: Vec<f32> = vec![];
let result = dot_product(&a, &b);
assert_eq!(result, 0.0);
}
#[test]
fn test_dot_product_single_element() {
let a = vec![5.0];
let b = vec![3.0];
let result = dot_product(&a, &b);
assert_eq!(result, 15.0);
}
#[test]
fn test_dot_product_non_aligned_size() {
for size in [1, 3, 5, 7, 9, 13, 17, 25] {
let a: Vec<f32> = (1..=size).map(|i| i as f32).collect();
let b: Vec<f32> = (1..=size).map(|i| (i * 2) as f32).collect();
let result = dot_product(&a, &b);
let expected: f32 = a.iter().zip(b.iter()).map(|(x, y)| x * y).sum();
assert!(
(result - expected).abs() < f32::EPSILON,
"Failed for size {}",
size
);
}
}
#[test]
fn test_dot_product_aligned_size() {
for size in [8, 16, 32, 64, 128] {
let a: Vec<f32> = (1..=size).map(|i| i as f32).collect();
let b: Vec<f32> = (1..=size).map(|i| (i * 2) as f32).collect();
let result = dot_product(&a, &b);
let expected: f32 = a.iter().zip(b.iter()).map(|(x, y)| x * y).sum();
assert!(
(result - expected).abs() < f32::EPSILON,
"Failed for size {}",
size
);
}
}
#[test]
fn test_dot_product_negative_values() {
let a = [-1.0, 2.0, -3.0, 4.0];
let b = [5.0, -6.0, 7.0, -8.0];
let result = dot_product(&a, &b);
let expected: f32 = a.iter().zip(b.iter()).map(|(x, y)| x * y).sum();
assert_eq!(result, expected);
assert_eq!(result, -70.0);
}
#[test]
fn test_dot_product_zeros() {
let a = [0.0, 0.0, 0.0];
let b = [1.0, 2.0, 3.0];
let result = dot_product(&a, &b);
assert_eq!(result, 0.0);
}
#[test]
fn test_dot_product_large_vectors() {
let a: Vec<f32> = (1..=1024).map(|i| i as f32).collect();
let b: Vec<f32> = (1..=1024).map(|i| (i * 2) as f32).collect();
let result = dot_product(&a, &b);
let expected: f32 = a.iter().zip(b.iter()).map(|(x, y)| x * y).sum();
let abs_diff = (result - expected).abs();
let rel_error = abs_diff / expected.abs();
assert!(
rel_error < 1e-5 || abs_diff < f32::EPSILON,
"Relative error {} too large (abs diff: {}, result: {}, expected: {})",
rel_error,
abs_diff,
result,
expected
);
}
#[test]
fn test_dot_product_scalar_matches_simd() {
let sizes = [1, 3, 7, 8, 15, 16, 31, 32, 100, 256];
for size in sizes {
let a: Vec<f32> = (0..size).map(|i| (i as f32 * 0.1).fract()).collect();
let b: Vec<f32> = (0..size).map(|i| (i as f32 * 0.13).fract()).collect();
let scalar_result = dot_product_scalar(&a, &b);
let simd_result = dot_product(&a, &b);
let abs_diff = (scalar_result - simd_result).abs();
let rel_error = if scalar_result.abs() > f32::EPSILON {
abs_diff / scalar_result.abs()
} else {
abs_diff
};
assert!(
rel_error < 1e-5 || abs_diff < f32::EPSILON,
"Scalar and SIMD differ for size {}: scalar={}, simd={}, diff={}, rel_error={}",
size,
scalar_result,
simd_result,
abs_diff,
rel_error
);
}
}
#[test]
#[should_panic(expected = "Vectors must have the same length")]
fn test_dot_product_different_lengths_panic() {
let a = vec![1.0, 2.0, 3.0];
let b = vec![1.0, 2.0];
dot_product(&a, &b);
}
#[cfg(target_arch = "x86_64")]
#[test]
fn test_avx2_availability() {
let has_avx2 = std::arch::is_x86_feature_detected!("avx2");
println!("AVX2 available: {}", has_avx2);
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_eq!(result, 32.0);
}
#[test]
fn test_compute_norm_squared_basic() {
let v = vec![3.0, 4.0];
let norm_sq = compute_norm_squared(&v);
assert_eq!(norm_sq, 25.0); }
#[test]
fn test_compute_norm_squared_unit_vector() {
let v = vec![1.0, 0.0, 0.0];
let norm_sq = compute_norm_squared(&v);
assert_eq!(norm_sq, 1.0);
}
#[test]
fn test_compute_norm_squared_zero_vector() {
let v = vec![0.0, 0.0, 0.0];
let norm_sq = compute_norm_squared(&v);
assert_eq!(norm_sq, 0.0);
}
#[test]
fn test_compute_norm_squared_pythagorean_triple() {
let v = vec![5.0, 12.0];
let norm_sq = compute_norm_squared(&v);
assert_eq!(norm_sq, 169.0); let norm = norm_sq.sqrt();
assert_eq!(norm, 13.0);
}
#[test]
fn test_compute_norm_squared_non_aligned() {
for size in [1, 3, 5, 7, 9, 13, 17] {
let v: Vec<f32> = (1..=size).map(|i| i as f32).collect();
let result = compute_norm_squared(&v);
let expected: f32 = v.iter().map(|x| x * x).sum();
assert!(
(result - expected).abs() < f32::EPSILON,
"Failed for size {}",
size
);
}
}
#[test]
fn test_compute_norm_squared_large_vector() {
let v: Vec<f32> = (1..=1000).map(|i| i as f32 * 0.1).collect();
let result = compute_norm_squared(&v);
let expected: f32 = v.iter().map(|x| x * x).sum();
let abs_diff = (result - expected).abs();
let rel_error = if expected.abs() > f32::EPSILON {
abs_diff / expected.abs()
} else {
abs_diff
};
assert!(
rel_error < 1e-5 || abs_diff < 1e-3,
"Norm squared differs: result={}, expected={}, diff={}, rel_error={}",
result,
expected,
abs_diff,
rel_error
);
}
#[test]
fn test_compute_norm_squared_matches_scalar() {
let v: Vec<f32> = (1..=100).map(|i| i as f32 * 0.1).collect();
let scalar_result = compute_norm_squared_scalar(&v);
let simd_result = compute_norm_squared(&v);
let abs_diff = (scalar_result - simd_result).abs();
assert!(
abs_diff < 1e-3,
"Norm squared differs: scalar={}, simd={}, diff={}",
scalar_result,
simd_result,
abs_diff
);
}
#[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 similarity = cosine_similarity(&a, &b);
assert!((similarity - 1.0).abs() < f32::EPSILON);
}
#[test]
fn test_cosine_similarity_opposite() {
let a = vec![1.0, 0.0];
let b = vec![-1.0, 0.0];
let similarity = cosine_similarity(&a, &b);
assert!((similarity + 1.0).abs() < f32::EPSILON);
}
#[test]
fn test_cosine_similarity_orthogonal() {
let a = vec![1.0, 0.0];
let b = vec![0.0, 1.0];
let similarity = cosine_similarity(&a, &b);
assert!((similarity - 0.0).abs() < f32::EPSILON);
}
#[test]
fn test_cosine_similarity_normalized() {
let a = vec![1.0, 0.0, 0.0];
let b = vec![
std::f32::consts::FRAC_1_SQRT_2,
std::f32::consts::FRAC_1_SQRT_2,
0.0,
];
let similarity = cosine_similarity(&a, &b);
assert!((similarity - std::f32::consts::FRAC_1_SQRT_2).abs() < 0.0001);
}
#[test]
fn test_cosine_similarity_non_zero() {
let a = vec![1.0, 2.0, 3.0];
let b = vec![4.0, 5.0, 6.0];
let similarity = cosine_similarity(&a, &b);
assert!(similarity >= -1.0 && similarity <= 1.0);
}
#[test]
fn test_cosine_matches_manual_calculation() {
let a = vec![1.0, 2.0, 3.0];
let b = vec![4.0, 5.0, 6.0];
let result = cosine_similarity(&a, &b);
let dot: f32 = a.iter().zip(b.iter()).map(|(x, y)| x * y).sum();
let norm_a: f32 = a.iter().map(|x| x * x).sum::<f32>().sqrt();
let norm_b: f32 = b.iter().map(|x| x * x).sum::<f32>().sqrt();
let expected = dot / (norm_a * norm_b);
assert!((result - expected).abs() < f32::EPSILON);
}
#[test]
fn test_cosine_similarity_matches_scalar() {
let a: Vec<f32> = (1..=100).map(|i| i as f32 * 0.1).collect();
let b: Vec<f32> = (101..=200).map(|i| i as f32 * 0.1).collect();
let scalar_result = cosine_similarity_scalar(&a, &b);
let simd_result = cosine_similarity(&a, &b);
assert!((scalar_result - simd_result).abs() < f32::EPSILON * 10.0);
}
#[test]
fn test_cosine_similarity_large_vectors() {
let a: Vec<f32> = (1..=1000).map(|i| i as f32).collect();
let b: Vec<f32> = (1001..=2000).map(|i| i as f32).collect();
let similarity = cosine_similarity(&a, &b);
assert!(similarity >= -1.0 && similarity <= 1.0);
}
#[test]
#[should_panic(expected = "Vectors must have the same length")]
fn test_cosine_similarity_different_lengths_panic() {
let a = vec![1.0, 2.0];
let b = vec![1.0, 2.0, 3.0];
cosine_similarity(&a, &b);
}
#[test]
#[should_panic(expected = "Vectors cannot be empty")]
fn test_cosine_similarity_empty_vectors_panic() {
let a: Vec<f32> = vec![];
let b: Vec<f32> = vec![];
cosine_similarity(&a, &b);
}
#[test]
#[should_panic(expected = "First vector has zero magnitude")]
fn test_cosine_similarity_zero_magnitude_panic() {
let a = vec![0.0, 0.0, 0.0];
let b = vec![1.0, 0.0, 0.0];
cosine_similarity(&a, &b);
}
#[test]
#[should_panic(expected = "Second vector has zero magnitude")]
fn test_cosine_similarity_zero_magnitude_b_panic() {
let a = vec![1.0, 0.0, 0.0];
let b = vec![0.0, 0.0, 0.0];
cosine_similarity(&a, &b);
}
#[test]
fn test_dot_norm_cosine_integration() {
let a: Vec<f32> = (1..=50).map(|i| i as f32 * 0.1).collect();
let b: Vec<f32> = (51..=100).map(|i| i as f32 * 0.1).collect();
let dot = dot_product(&a, &b);
let norm_a = compute_norm_squared(&a).sqrt();
let norm_b = compute_norm_squared(&b).sqrt();
let cosine = cosine_similarity(&a, &b);
let expected_cosine = dot / (norm_a * norm_b);
assert!((cosine - expected_cosine).abs() < f32::EPSILON);
}
#[test]
fn test_euclidean_distance_scalar_identical() {
let a = [1.0, 2.0, 3.0];
let b = [1.0, 2.0, 3.0];
let distance = euclidean_distance_scalar(&a, &b);
assert_eq!(distance, 0.0);
}
#[test]
fn test_euclidean_distance_scalar_basic() {
let a = [0.0, 0.0];
let b = [1.0, 0.0];
let distance = euclidean_distance_scalar(&a, &b);
assert_eq!(distance, 1.0);
}
#[test]
fn test_euclidean_distance_scalar_diagonal() {
let a = [0.0, 0.0];
let b = [1.0, 1.0];
let distance = euclidean_distance_scalar(&a, &b);
assert!((distance - std::f32::consts::SQRT_2).abs() < f32::EPSILON);
}
#[test]
fn test_euclidean_distance_scalar_high_dimensional() {
let a: Vec<f32> = (1..=100).map(|i| i as f32).collect();
let b: Vec<f32> = (101..=200).map(|i| i as f32).collect();
let distance = euclidean_distance_scalar(&a, &b);
assert!(distance > 0.0);
assert!(distance.is_finite());
}
#[test]
fn test_euclidean_distance_scalar_negative_values() {
let a = [-1.0, 2.0, -3.0];
let b = [4.0, -5.0, 6.0];
let distance = euclidean_distance_scalar(&a, &b);
assert!((distance - 12.4499).abs() < 0.001);
}
#[test]
fn test_euclidean_distance_scalar_non_aligned() {
let a: Vec<f32> = (1..=13).map(|i| i as f32).collect();
let b: Vec<f32> = (14..=26).map(|i| i as f32).collect();
let distance = euclidean_distance_scalar(&a, &b);
assert!(distance > 0.0);
assert!(distance.is_finite());
}
#[test]
fn test_euclidean_distance_dispatch_identical() {
let a = [1.0, 2.0, 3.0];
let b = [1.0, 2.0, 3.0];
let distance = euclidean_distance(&a, &b);
assert_eq!(distance, 0.0);
}
#[test]
fn test_euclidean_distance_dispatch_basic() {
let a = [0.0, 0.0];
let b = [1.0, 0.0];
let distance = euclidean_distance(&a, &b);
assert_eq!(distance, 1.0);
}
#[test]
fn test_euclidean_distance_dispatch_matches_scalar() {
let a: Vec<f32> = (1..=50).map(|i| i as f32 * 0.1).collect();
let b: Vec<f32> = (51..=100).map(|i| i as f32 * 0.1).collect();
let scalar_result = euclidean_distance_scalar(&a, &b);
let dispatch_result = euclidean_distance(&a, &b);
assert!((dispatch_result - scalar_result).abs() < f32::EPSILON);
}
#[test]
fn test_euclidean_distance_large_vector() {
let a: Vec<f32> = (1..=1000).map(|i| i as f32).collect();
let b: Vec<f32> = (1001..=2000).map(|i| i as f32).collect();
let distance = euclidean_distance(&a, &b);
assert!(distance > 0.0);
assert!(distance.is_finite());
}
#[cfg(target_arch = "x86_64")]
#[test]
fn test_euclidean_distance_avx2_matches_scalar() {
if std::arch::is_x86_feature_detected!("avx2") {
let a: Vec<f32> = (1..=100).map(|i| i as f32 * 0.73).collect();
let b: Vec<f32> = (101..=200).map(|i| i as f32 * 1.23).collect();
let scalar_result = euclidean_distance_scalar(&a, &b);
let avx2_result = unsafe { euclidean_distance_avx2(&a, &b) };
assert!((avx2_result - scalar_result).abs() < f32::EPSILON);
}
}
#[cfg(target_arch = "x86_64")]
#[test]
fn test_euclidean_distance_avx2_remainder() {
if std::arch::is_x86_feature_detected!("avx2") {
for size in [1, 7, 8, 9, 15, 16, 17, 23, 24, 25].iter() {
let a: Vec<f32> = (1..=*size).map(|i| i as f32).collect();
let b: Vec<f32> = (*size + 1..=*size * 2).map(|i| i as f32).collect();
let scalar_result = euclidean_distance_scalar(&a, &b);
let avx2_result = unsafe { euclidean_distance_avx2(&a, &b) };
assert!(
(avx2_result - scalar_result).abs() < f32::EPSILON,
"Mismatch for size {}: scalar={}, avx2={}",
size,
scalar_result,
avx2_result
);
}
}
}
#[test]
fn test_euclidean_distance_zero_vector() {
let a = [0.0, 0.0, 0.0];
let b = [0.0, 0.0, 0.0];
let distance = euclidean_distance(&a, &b);
assert_eq!(distance, 0.0);
}
#[test]
fn test_euclidean_distance_symmetry() {
let a = [1.0, 2.0, 3.0];
let b = [4.0, 5.0, 6.0];
let distance_ab = euclidean_distance(&a, &b);
let distance_ba = euclidean_distance(&b, &a);
assert_eq!(distance_ab, distance_ba);
}
#[test]
#[should_panic(expected = "Vectors must have the same length")]
fn test_euclidean_distance_different_lengths_panic() {
let a = vec![1.0, 2.0, 3.0];
let b = vec![1.0, 2.0];
euclidean_distance(&a, &b);
}
}