pub mod avx2;
pub mod neon;
pub mod scalar;
#[inline]
pub fn sum_f64_filtered(values: &[f64], mask: &[bool]) -> f64 {
#[cfg(target_arch = "aarch64")]
{
unsafe { neon::sum_f64_filtered(values, mask) }
}
#[cfg(target_arch = "x86_64")]
{
if std::arch::is_x86_feature_detected!("avx2") {
unsafe { avx2::sum_f64_filtered(values, mask) }
} else {
scalar::sum_f64_filtered(values, mask)
}
}
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
{
scalar::sum_f64_filtered(values, mask)
}
}
#[inline]
pub fn sum_f64_masked(values: &[f64], mask: &[bool]) -> f64 {
sum_f64_filtered(values, mask)
}
#[inline]
pub fn sum_f64(values: &[f64]) -> f64 {
#[cfg(target_arch = "aarch64")]
{
unsafe { neon::sum_f64(values) }
}
#[cfg(target_arch = "x86_64")]
{
if std::arch::is_x86_feature_detected!("avx2") {
unsafe { avx2::sum_f64(values) }
} else {
scalar::sum_f64(values)
}
}
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
{
scalar::sum_f64(values)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_dispatch_sum_f64_filtered_basic() {
let values = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
let mask = vec![true, false, true, false, true, false, true, false];
let result = sum_f64_filtered(&values, &mask);
assert!((result - 16.0).abs() < 1e-10, "got {}", result);
}
#[test]
fn test_dispatch_sum_f64_filtered_all_true() {
let values = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
let mask = vec![true; 8];
let result = sum_f64_filtered(&values, &mask);
assert!((result - 36.0).abs() < 1e-10);
}
#[test]
fn test_dispatch_sum_f64_filtered_all_false() {
let values = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
let mask = vec![false; 8];
assert_eq!(sum_f64_filtered(&values, &mask), 0.0);
}
#[test]
fn test_dispatch_sum_f64_filtered_empty() {
assert_eq!(sum_f64_filtered(&[], &[]), 0.0);
}
#[test]
fn test_dispatch_sum_f64_filtered_single() {
assert!((sum_f64_filtered(&[42.0], &[true]) - 42.0).abs() < 1e-10);
assert_eq!(sum_f64_filtered(&[42.0], &[false]), 0.0);
}
#[test]
fn test_dispatch_sum_f64_filtered_sub_vector_width() {
let values = vec![10.0, 20.0, 30.0];
let mask = vec![true, false, true];
assert!((sum_f64_filtered(&values, &mask) - 40.0).abs() < 1e-10);
}
#[test]
fn test_dispatch_sum_f64_filtered_remainder() {
let values: Vec<f64> = (1..=5).map(|x| x as f64).collect();
let mask = vec![true, true, true, true, true];
assert!((sum_f64_filtered(&values, &mask) - 15.0).abs() < 1e-10);
}
#[test]
fn test_dispatch_sum_f64_filtered_large() {
let values: Vec<f64> = (1..=1024).map(|x| x as f64).collect();
let mask: Vec<bool> = (0..1024).map(|i| i % 2 == 0).collect();
let result = sum_f64_filtered(&values, &mask);
let expected: f64 = (0..1024).filter(|i| i % 2 == 0).map(|i| (i + 1) as f64).sum();
assert!((result - expected).abs() < 1e-6);
}
#[test]
fn test_dispatch_matches_scalar() {
for len in [0, 1, 2, 3, 4, 5, 7, 8, 9, 15, 16, 17, 31, 32, 33, 63, 64, 65, 100, 1024] {
let values: Vec<f64> = (0..len).map(|i| (i as f64) * 1.1 + 0.7).collect();
let mask: Vec<bool> = (0..len).map(|i| i % 3 != 0).collect();
let dispatched = sum_f64_filtered(&values, &mask);
let scalar = scalar::sum_f64_filtered(&values, &mask);
assert!(
(dispatched - scalar).abs() < 1e-10,
"len={}: dispatched {} != scalar {}",
len,
dispatched,
scalar
);
}
}
#[test]
fn test_dispatch_sum_f64_unfiltered() {
let values: Vec<f64> = (1..=100).map(|x| x as f64).collect();
assert!((sum_f64(&values) - 5050.0).abs() < 1e-10);
assert_eq!(sum_f64(&[]), 0.0);
}
#[test]
fn test_dispatch_sum_f64_unfiltered_matches_scalar() {
for len in [0, 1, 3, 4, 5, 7, 8, 9, 16, 17, 100, 1024] {
let values: Vec<f64> = (0..len).map(|i| (i as f64) * 2.3 + 0.1).collect();
let dispatched = sum_f64(&values);
let scalar = scalar::sum_f64(&values);
assert!(
(dispatched - scalar).abs() < 1e-10,
"len={}: dispatched {} != scalar {}",
len,
dispatched,
scalar
);
}
}
}