const UNROLL_FACTOR: usize = 4;
const VECTOR_UNROLL_FACTOR: usize = 8;
#[inline]
pub fn batch_squared_distances(points: &[(f64, f64)], center: (f64, f64)) -> Vec<f64> {
let n = points.len();
let mut result = vec![0.0; n];
if n == 0 {
return result;
}
let cx = center.0;
let cy = center.1;
let chunks = n / UNROLL_FACTOR;
for i in 0..chunks {
let base = i * UNROLL_FACTOR;
let (x0, y0) = points[base];
let (x1, y1) = points[base + 1];
let (x2, y2) = points[base + 2];
let (x3, y3) = points[base + 3];
let dx0 = x0 - cx;
let dy0 = y0 - cy;
let dx1 = x1 - cx;
let dy1 = y1 - cy;
let dx2 = x2 - cx;
let dy2 = y2 - cy;
let dx3 = x3 - cx;
let dy3 = y3 - cy;
result[base] = dx0 * dx0 + dy0 * dy0;
result[base + 1] = dx1 * dx1 + dy1 * dy1;
result[base + 2] = dx2 * dx2 + dy2 * dy2;
result[base + 3] = dx3 * dx3 + dy3 * dy3;
}
for i in (chunks * UNROLL_FACTOR)..n {
let (x, y) = points[i];
let dx = x - cx;
let dy = y - cy;
result[i] = dx * dx + dy * dy;
}
result
}
#[inline]
pub fn batch_in_range(points: &[(f64, f64)], center: (f64, f64), range_sq: f64) -> Vec<bool> {
let n = points.len();
let mut result = vec![false; n];
if n == 0 {
return result;
}
let cx = center.0;
let cy = center.1;
let chunks = n / UNROLL_FACTOR;
for i in 0..chunks {
let base = i * UNROLL_FACTOR;
let (x0, y0) = points[base];
let (x1, y1) = points[base + 1];
let (x2, y2) = points[base + 2];
let (x3, y3) = points[base + 3];
let dx0 = x0 - cx;
let dy0 = y0 - cy;
let dx1 = x1 - cx;
let dy1 = y1 - cy;
let dx2 = x2 - cx;
let dy2 = y2 - cy;
let dx3 = x3 - cx;
let dy3 = y3 - cy;
result[base] = dx0 * dx0 + dy0 * dy0 <= range_sq;
result[base + 1] = dx1 * dx1 + dy1 * dy1 <= range_sq;
result[base + 2] = dx2 * dx2 + dy2 * dy2 <= range_sq;
result[base + 3] = dx3 * dx3 + dy3 * dy3 <= range_sq;
}
for i in (chunks * UNROLL_FACTOR)..n {
let (x, y) = points[i];
let dx = x - cx;
let dy = y - cy;
result[i] = dx * dx + dy * dy <= range_sq;
}
result
}
#[inline]
pub fn vector_coherence(v1: &[f64], v2: &[f64]) -> f64 {
assert_eq!(v1.len(), v2.len(), "Vectors must have same length");
let n = v1.len();
if n == 0 {
return 0.0;
}
let mut dot = 0.0;
let mut mag1_sq = 0.0;
let mut mag2_sq = 0.0;
let chunks = n / VECTOR_UNROLL_FACTOR;
for i in 0..chunks {
let base = i * VECTOR_UNROLL_FACTOR;
let a0 = v1[base];
let a1 = v1[base + 1];
let a2 = v1[base + 2];
let a3 = v1[base + 3];
let a4 = v1[base + 4];
let a5 = v1[base + 5];
let a6 = v1[base + 6];
let a7 = v1[base + 7];
let b0 = v2[base];
let b1 = v2[base + 1];
let b2 = v2[base + 2];
let b3 = v2[base + 3];
let b4 = v2[base + 4];
let b5 = v2[base + 5];
let b6 = v2[base + 6];
let b7 = v2[base + 7];
dot += a0 * b0 + a1 * b1 + a2 * b2 + a3 * b3;
dot += a4 * b4 + a5 * b5 + a6 * b6 + a7 * b7;
mag1_sq += a0 * a0 + a1 * a1 + a2 * a2 + a3 * a3;
mag1_sq += a4 * a4 + a5 * a5 + a6 * a6 + a7 * a7;
mag2_sq += b0 * b0 + b1 * b1 + b2 * b2 + b3 * b3;
mag2_sq += b4 * b4 + b5 * b5 + b6 * b6 + b7 * b7;
}
let remaining_start = chunks * VECTOR_UNROLL_FACTOR;
let remaining = n - remaining_start;
let small_chunks = remaining / UNROLL_FACTOR;
for i in 0..small_chunks {
let base = remaining_start + i * UNROLL_FACTOR;
let a0 = v1[base];
let a1 = v1[base + 1];
let a2 = v1[base + 2];
let a3 = v1[base + 3];
let b0 = v2[base];
let b1 = v2[base + 1];
let b2 = v2[base + 2];
let b3 = v2[base + 3];
dot += a0 * b0 + a1 * b1 + a2 * b2 + a3 * b3;
mag1_sq += a0 * a0 + a1 * a1 + a2 * a2 + a3 * a3;
mag2_sq += b0 * b0 + b1 * b1 + b2 * b2 + b3 * b3;
}
for i in (remaining_start + small_chunks * UNROLL_FACTOR)..n {
let a = v1[i];
let b = v2[i];
dot += a * b;
mag1_sq += a * a;
mag2_sq += b * b;
}
let denominator = (mag1_sq * mag2_sq).sqrt();
if denominator < f64::EPSILON {
return 0.0;
}
dot / denominator
}
#[inline]
pub fn normalize_vectors(vectors: &mut [Vec<f64>]) {
for vec in vectors.iter_mut() {
normalize_vector_inplace(vec);
}
}
#[inline]
pub fn normalize_vector_inplace(vec: &mut [f64]) {
let n = vec.len();
if n == 0 {
return;
}
let mut mag_sq = 0.0;
let chunks = n / VECTOR_UNROLL_FACTOR;
for i in 0..chunks {
let base = i * VECTOR_UNROLL_FACTOR;
let a0 = vec[base];
let a1 = vec[base + 1];
let a2 = vec[base + 2];
let a3 = vec[base + 3];
let a4 = vec[base + 4];
let a5 = vec[base + 5];
let a6 = vec[base + 6];
let a7 = vec[base + 7];
mag_sq += a0 * a0 + a1 * a1 + a2 * a2 + a3 * a3;
mag_sq += a4 * a4 + a5 * a5 + a6 * a6 + a7 * a7;
}
let remaining_start = chunks * VECTOR_UNROLL_FACTOR;
let remaining = n - remaining_start;
let small_chunks = remaining / UNROLL_FACTOR;
for i in 0..small_chunks {
let base = remaining_start + i * UNROLL_FACTOR;
let a0 = vec[base];
let a1 = vec[base + 1];
let a2 = vec[base + 2];
let a3 = vec[base + 3];
mag_sq += a0 * a0 + a1 * a1 + a2 * a2 + a3 * a3;
}
for i in (remaining_start + small_chunks * UNROLL_FACTOR)..n {
mag_sq += vec[i] * vec[i];
}
if mag_sq < f64::EPSILON {
return;
}
let inv_mag = 1.0 / mag_sq.sqrt();
for i in 0..chunks {
let base = i * VECTOR_UNROLL_FACTOR;
vec[base] *= inv_mag;
vec[base + 1] *= inv_mag;
vec[base + 2] *= inv_mag;
vec[base + 3] *= inv_mag;
vec[base + 4] *= inv_mag;
vec[base + 5] *= inv_mag;
vec[base + 6] *= inv_mag;
vec[base + 7] *= inv_mag;
}
for i in 0..small_chunks {
let base = remaining_start + i * UNROLL_FACTOR;
vec[base] *= inv_mag;
vec[base + 1] *= inv_mag;
vec[base + 2] *= inv_mag;
vec[base + 3] *= inv_mag;
}
for i in (remaining_start + small_chunks * UNROLL_FACTOR)..n {
vec[i] *= inv_mag;
}
}
#[inline]
pub fn vector_magnitude(v: &[f64]) -> f64 {
vector_magnitude_squared(v).sqrt()
}
#[inline]
pub fn vector_magnitude_squared(v: &[f64]) -> f64 {
let n = v.len();
if n == 0 {
return 0.0;
}
let mut sum = 0.0;
let chunks = n / VECTOR_UNROLL_FACTOR;
for i in 0..chunks {
let base = i * VECTOR_UNROLL_FACTOR;
let a0 = v[base];
let a1 = v[base + 1];
let a2 = v[base + 2];
let a3 = v[base + 3];
let a4 = v[base + 4];
let a5 = v[base + 5];
let a6 = v[base + 6];
let a7 = v[base + 7];
sum += a0 * a0 + a1 * a1 + a2 * a2 + a3 * a3;
sum += a4 * a4 + a5 * a5 + a6 * a6 + a7 * a7;
}
for i in (chunks * VECTOR_UNROLL_FACTOR)..n {
sum += v[i] * v[i];
}
sum
}
#[inline]
pub fn vector_dot(v1: &[f64], v2: &[f64]) -> f64 {
assert_eq!(v1.len(), v2.len(), "Vectors must have same length");
let n = v1.len();
if n == 0 {
return 0.0;
}
let mut sum = 0.0;
let chunks = n / VECTOR_UNROLL_FACTOR;
for i in 0..chunks {
let base = i * VECTOR_UNROLL_FACTOR;
sum += v1[base] * v2[base];
sum += v1[base + 1] * v2[base + 1];
sum += v1[base + 2] * v2[base + 2];
sum += v1[base + 3] * v2[base + 3];
sum += v1[base + 4] * v2[base + 4];
sum += v1[base + 5] * v2[base + 5];
sum += v1[base + 6] * v2[base + 6];
sum += v1[base + 7] * v2[base + 7];
}
for i in (chunks * VECTOR_UNROLL_FACTOR)..n {
sum += v1[i] * v2[i];
}
sum
}
#[inline]
pub fn batch_squared_distances_3d(
points: &[(f64, f64, f64)],
center: (f64, f64, f64),
) -> Vec<f64> {
let n = points.len();
let mut result = vec![0.0; n];
if n == 0 {
return result;
}
let (cx, cy, cz) = center;
let chunks = n / UNROLL_FACTOR;
for i in 0..chunks {
let base = i * UNROLL_FACTOR;
let (x0, y0, z0) = points[base];
let (x1, y1, z1) = points[base + 1];
let (x2, y2, z2) = points[base + 2];
let (x3, y3, z3) = points[base + 3];
let dx0 = x0 - cx;
let dy0 = y0 - cy;
let dz0 = z0 - cz;
let dx1 = x1 - cx;
let dy1 = y1 - cy;
let dz1 = z1 - cz;
let dx2 = x2 - cx;
let dy2 = y2 - cy;
let dz2 = z2 - cz;
let dx3 = x3 - cx;
let dy3 = y3 - cy;
let dz3 = z3 - cz;
result[base] = dx0 * dx0 + dy0 * dy0 + dz0 * dz0;
result[base + 1] = dx1 * dx1 + dy1 * dy1 + dz1 * dz1;
result[base + 2] = dx2 * dx2 + dy2 * dy2 + dz2 * dz2;
result[base + 3] = dx3 * dx3 + dy3 * dy3 + dz3 * dz3;
}
for i in (chunks * UNROLL_FACTOR)..n {
let (x, y, z) = points[i];
let dx = x - cx;
let dy = y - cy;
let dz = z - cz;
result[i] = dx * dx + dy * dy + dz * dz;
}
result
}
#[inline]
pub fn count_in_range(points: &[(f64, f64)], center: (f64, f64), range_sq: f64) -> usize {
let n = points.len();
if n == 0 {
return 0;
}
let cx = center.0;
let cy = center.1;
let mut count = 0;
let chunks = n / UNROLL_FACTOR;
for i in 0..chunks {
let base = i * UNROLL_FACTOR;
let (x0, y0) = points[base];
let (x1, y1) = points[base + 1];
let (x2, y2) = points[base + 2];
let (x3, y3) = points[base + 3];
let dx0 = x0 - cx;
let dy0 = y0 - cy;
let dx1 = x1 - cx;
let dy1 = y1 - cy;
let dx2 = x2 - cx;
let dy2 = y2 - cy;
let dx3 = x3 - cx;
let dy3 = y3 - cy;
if dx0 * dx0 + dy0 * dy0 <= range_sq {
count += 1;
}
if dx1 * dx1 + dy1 * dy1 <= range_sq {
count += 1;
}
if dx2 * dx2 + dy2 * dy2 <= range_sq {
count += 1;
}
if dx3 * dx3 + dy3 * dy3 <= range_sq {
count += 1;
}
}
for i in (chunks * UNROLL_FACTOR)..n {
let (x, y) = points[i];
let dx = x - cx;
let dy = y - cy;
if dx * dx + dy * dy <= range_sq {
count += 1;
}
}
count
}
#[cfg(test)]
mod tests {
use super::*;
const EPSILON: f64 = 1e-10;
#[test]
fn test_batch_squared_distances_empty() {
let result = batch_squared_distances(&[], (0.0, 0.0));
assert!(result.is_empty());
}
#[test]
fn test_batch_squared_distances_single() {
let points = [(3.0, 4.0)];
let result = batch_squared_distances(&points, (0.0, 0.0));
assert_eq!(result.len(), 1);
assert!((result[0] - 25.0).abs() < EPSILON);
}
#[test]
fn test_batch_squared_distances_multiple() {
let points = [
(3.0, 0.0),
(0.0, 4.0),
(3.0, 4.0),
(1.0, 1.0),
(2.0, 2.0),
];
let center = (0.0, 0.0);
let result = batch_squared_distances(&points, center);
assert!((result[0] - 9.0).abs() < EPSILON);
assert!((result[1] - 16.0).abs() < EPSILON);
assert!((result[2] - 25.0).abs() < EPSILON);
assert!((result[3] - 2.0).abs() < EPSILON);
assert!((result[4] - 8.0).abs() < EPSILON);
}
#[test]
fn test_batch_squared_distances_with_offset_center() {
let points = [(4.0, 5.0)];
let center = (1.0, 1.0);
let result = batch_squared_distances(&points, center);
assert!((result[0] - 25.0).abs() < EPSILON);
}
#[test]
fn test_batch_in_range_empty() {
let result = batch_in_range(&[], (0.0, 0.0), 10.0);
assert!(result.is_empty());
}
#[test]
fn test_batch_in_range_all_inside() {
let points = [(1.0, 0.0), (0.0, 1.0), (0.5, 0.5)];
let result = batch_in_range(&points, (0.0, 0.0), 10.0);
assert!(result.iter().all(|&x| x));
}
#[test]
fn test_batch_in_range_all_outside() {
let points = [(10.0, 0.0), (0.0, 10.0), (7.0, 7.0)];
let result = batch_in_range(&points, (0.0, 0.0), 10.0);
assert!(result.iter().all(|&x| !x));
}
#[test]
fn test_batch_in_range_mixed() {
let points = [(1.0, 0.0), (5.0, 0.0)];
let result = batch_in_range(&points, (0.0, 0.0), 10.0); assert!(result[0]); assert!(!result[1]); }
#[test]
fn test_batch_in_range_boundary() {
let points = [(3.0, 0.0)];
let result = batch_in_range(&points, (0.0, 0.0), 9.0);
assert!(result[0]); }
#[test]
fn test_vector_coherence_identical() {
let v = vec![1.0, 2.0, 3.0];
let coherence = vector_coherence(&v, &v);
assert!((coherence - 1.0).abs() < EPSILON);
}
#[test]
fn test_vector_coherence_opposite() {
let v1 = vec![1.0, 0.0, 0.0];
let v2 = vec![-1.0, 0.0, 0.0];
let coherence = vector_coherence(&v1, &v2);
assert!((coherence + 1.0).abs() < EPSILON);
}
#[test]
fn test_vector_coherence_orthogonal() {
let v1 = vec![1.0, 0.0, 0.0];
let v2 = vec![0.0, 1.0, 0.0];
let coherence = vector_coherence(&v1, &v2);
assert!(coherence.abs() < EPSILON);
}
#[test]
fn test_vector_coherence_45_degrees() {
let v1 = vec![1.0, 0.0];
let v2 = vec![1.0, 1.0];
let coherence = vector_coherence(&v1, &v2);
assert!((coherence - 1.0 / 2.0_f64.sqrt()).abs() < EPSILON);
}
#[test]
fn test_vector_coherence_zero_vector() {
let v1 = vec![1.0, 2.0, 3.0];
let v2 = vec![0.0, 0.0, 0.0];
let coherence = vector_coherence(&v1, &v2);
assert!(coherence.abs() < EPSILON);
}
#[test]
fn test_vector_coherence_high_dimensional() {
let mut v1 = vec![0.0; 100];
let mut v2 = vec![0.0; 100];
v1[0] = 1.0;
v2[0] = 1.0;
let coherence = vector_coherence(&v1, &v2);
assert!((coherence - 1.0).abs() < EPSILON);
}
#[test]
fn test_normalize_vectors_empty() {
let mut vectors: Vec<Vec<f64>> = vec![];
normalize_vectors(&mut vectors);
assert!(vectors.is_empty());
}
#[test]
fn test_normalize_vectors_single() {
let mut vectors = vec![vec![3.0, 4.0]];
normalize_vectors(&mut vectors);
assert!((vectors[0][0] - 0.6).abs() < EPSILON);
assert!((vectors[0][1] - 0.8).abs() < EPSILON);
}
#[test]
fn test_normalize_vectors_multiple() {
let mut vectors = vec![
vec![3.0, 4.0],
vec![5.0, 12.0],
vec![1.0, 0.0],
];
normalize_vectors(&mut vectors);
for v in &vectors {
let mag: f64 = v.iter().map(|x| x * x).sum::<f64>().sqrt();
assert!((mag - 1.0).abs() < EPSILON);
}
}
#[test]
fn test_normalize_vectors_zero_vector() {
let mut vectors = vec![vec![0.0, 0.0, 0.0]];
normalize_vectors(&mut vectors);
assert_eq!(vectors[0], vec![0.0, 0.0, 0.0]);
}
#[test]
fn test_normalize_vectors_high_dimensional() {
let mut vectors = vec![vec![1.0; 100]];
normalize_vectors(&mut vectors);
let mag: f64 = vectors[0].iter().map(|x| x * x).sum::<f64>().sqrt();
assert!((mag - 1.0).abs() < EPSILON);
}
#[test]
fn test_vector_magnitude() {
let v = vec![3.0, 4.0];
assert!((vector_magnitude(&v) - 5.0).abs() < EPSILON);
}
#[test]
fn test_vector_magnitude_squared() {
let v = vec![3.0, 4.0];
assert!((vector_magnitude_squared(&v) - 25.0).abs() < EPSILON);
}
#[test]
fn test_vector_dot() {
let v1 = vec![1.0, 2.0, 3.0];
let v2 = vec![4.0, 5.0, 6.0];
assert!((vector_dot(&v1, &v2) - 32.0).abs() < EPSILON);
}
#[test]
fn test_batch_squared_distances_3d() {
let points = [(1.0, 0.0, 0.0), (0.0, 2.0, 0.0), (0.0, 0.0, 3.0)];
let center = (0.0, 0.0, 0.0);
let dists = batch_squared_distances_3d(&points, center);
assert!((dists[0] - 1.0).abs() < EPSILON);
assert!((dists[1] - 4.0).abs() < EPSILON);
assert!((dists[2] - 9.0).abs() < EPSILON);
}
#[test]
fn test_count_in_range() {
let points = [(1.0, 0.0), (3.0, 0.0), (5.0, 0.0), (7.0, 0.0)];
let center = (0.0, 0.0);
assert_eq!(count_in_range(&points, center, 1.0), 1); assert_eq!(count_in_range(&points, center, 10.0), 2); assert_eq!(count_in_range(&points, center, 26.0), 3); assert_eq!(count_in_range(&points, center, 50.0), 4); }
#[test]
fn test_unroll_remainder_handling() {
for n in 0..20 {
let points: Vec<(f64, f64)> = (0..n).map(|i| (i as f64, 0.0)).collect();
let result = batch_squared_distances(&points, (0.0, 0.0));
assert_eq!(result.len(), n);
for (i, &d) in result.iter().enumerate() {
let expected = (i as f64) * (i as f64);
assert!(
(d - expected).abs() < EPSILON,
"Mismatch at index {} for n={}: got {}, expected {}",
i,
n,
d,
expected
);
}
}
}
#[test]
fn test_vector_coherence_remainder_handling() {
for n in 0..20 {
let v1: Vec<f64> = (0..n).map(|i| (i + 1) as f64).collect();
let v2: Vec<f64> = (0..n).map(|i| (i + 1) as f64).collect();
if n == 0 {
assert!((vector_coherence(&v1, &v2)).abs() < EPSILON);
} else {
assert!((vector_coherence(&v1, &v2) - 1.0).abs() < EPSILON);
}
}
}
}