use wide::f64x4;
use super::SIMD_THRESHOLD;
#[cfg(feature = "simd")]
#[inline]
pub fn rolling_sum(data: &[f64], window: usize) -> Vec<f64> {
rolling_sum_simd(data, window)
}
#[cfg(not(feature = "simd"))]
#[inline]
pub fn rolling_sum(data: &[f64], window: usize) -> Vec<f64> {
rolling_sum_scalar(data, window)
}
#[cfg(feature = "simd")]
#[inline]
pub fn rolling_mean(data: &[f64], window: usize) -> Vec<f64> {
rolling_mean_simd(data, window)
}
#[cfg(not(feature = "simd"))]
#[inline]
pub fn rolling_mean(data: &[f64], window: usize) -> Vec<f64> {
rolling_mean_scalar(data, window)
}
fn rolling_sum_simd(data: &[f64], window: usize) -> Vec<f64> {
let n = data.len();
if window == 0 || window > n {
return vec![f64::NAN; n];
}
if n < SIMD_THRESHOLD {
return rolling_sum_scalar(data, window);
}
let mut result = vec![f64::NAN; n];
let mut sum: f64 = data[..window].iter().sum();
result[window - 1] = sum;
let remaining = n - window;
let simd_chunks = remaining / 4;
for chunk_idx in 0..simd_chunks {
let base = window + chunk_idx * 4;
let add_vals = f64x4::new([data[base], data[base + 1], data[base + 2], data[base + 3]]);
let sub_vals = f64x4::new([
data[base - window],
data[base - window + 1],
data[base - window + 2],
data[base - window + 3],
]);
let deltas = add_vals - sub_vals;
let delta_arr = deltas.to_array();
for j in 0..4 {
sum += delta_arr[j];
result[base + j] = sum;
}
}
let processed = window + simd_chunks * 4;
for i in processed..n {
sum = sum - data[i - window] + data[i];
result[i] = sum;
}
result
}
fn rolling_sum_scalar(data: &[f64], window: usize) -> Vec<f64> {
let n = data.len();
let mut result = vec![f64::NAN; n];
if window == 0 || window > n {
return result;
}
let mut sum: f64 = data[..window].iter().sum();
result[window - 1] = sum;
for i in window..n {
sum = sum - data[i - window] + data[i];
result[i] = sum;
}
result
}
#[cfg(not(feature = "simd"))]
fn rolling_mean_scalar(data: &[f64], window: usize) -> Vec<f64> {
let sums = rolling_sum_scalar(data, window);
sums.iter()
.map(|&s| {
if s.is_nan() {
f64::NAN
} else {
s / window as f64
}
})
.collect()
}
fn rolling_mean_simd(data: &[f64], window: usize) -> Vec<f64> {
let sums = rolling_sum_simd(data, window);
let window_f64 = window as f64;
let mut result = vec![f64::NAN; sums.len()];
let chunks = sums.len() / 4;
for chunk in 0..chunks {
let i = chunk * 4;
let sum_vec = f64x4::new([sums[i], sums[i + 1], sums[i + 2], sums[i + 3]]);
let window_vec = f64x4::splat(window_f64);
let mean = sum_vec / window_vec;
let arr = mean.to_array();
result[i..i + 4].copy_from_slice(&arr);
}
for i in (chunks * 4)..sums.len() {
result[i] = sums[i] / window_f64;
}
result
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_rolling_sum_simd() {
let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
let result = rolling_sum_simd(&data, 3);
assert!(result[0].is_nan());
assert!(result[1].is_nan());
assert_eq!(result[2], 6.0); assert_eq!(result[3], 9.0); assert_eq!(result[4], 12.0); assert_eq!(result[5], 15.0); assert_eq!(result[6], 18.0); assert_eq!(result[7], 21.0); }
#[test]
fn test_rolling_mean_simd() {
let data = vec![2.0, 4.0, 6.0, 8.0, 10.0];
let result = rolling_mean_simd(&data, 3);
assert!(result[0].is_nan());
assert!(result[1].is_nan());
assert_eq!(result[2], 4.0); assert_eq!(result[3], 6.0); assert_eq!(result[4], 8.0); }
}