use wide::f64x4;
use super::SIMD_THRESHOLD;
#[cfg(feature = "simd")]
#[inline]
pub fn diff(data: &[f64]) -> Vec<f64> {
diff_simd(data)
}
#[cfg(not(feature = "simd"))]
#[inline]
pub fn diff(data: &[f64]) -> Vec<f64> {
diff_scalar(data)
}
#[cfg(feature = "simd")]
#[inline]
pub fn pct_change(data: &[f64]) -> Vec<f64> {
pct_change_simd(data)
}
#[cfg(not(feature = "simd"))]
#[inline]
pub fn pct_change(data: &[f64]) -> Vec<f64> {
pct_change_scalar(data)
}
#[cfg(not(feature = "simd"))]
fn diff_scalar(data: &[f64]) -> Vec<f64> {
let n = data.len();
if n < 2 {
return vec![f64::NAN; n];
}
let mut result = vec![f64::NAN; n];
for i in 1..n {
result[i] = data[i] - data[i - 1];
}
result
}
#[cfg(not(feature = "simd"))]
fn pct_change_scalar(data: &[f64]) -> Vec<f64> {
let n = data.len();
if n < 2 {
return vec![f64::NAN; n];
}
let mut result = vec![f64::NAN; n];
for i in 1..n {
if data[i - 1] != 0.0 {
result[i] = (data[i] - data[i - 1]) / data[i - 1];
} else {
result[i] = f64::NAN;
}
}
result
}
fn diff_simd(data: &[f64]) -> Vec<f64> {
let n = data.len();
if n < 2 {
return vec![f64::NAN; n];
}
let mut result = vec![f64::NAN; n];
if n < SIMD_THRESHOLD {
for i in 1..n {
result[i] = data[i] - data[i - 1];
}
return result;
}
let chunks = (n - 1) / 4;
for chunk in 0..chunks {
let i = 1 + chunk * 4;
let curr = f64x4::new([data[i], data[i + 1], data[i + 2], data[i + 3]]);
let prev = f64x4::new([data[i - 1], data[i], data[i + 1], data[i + 2]]);
let diff = curr - prev;
let arr = diff.to_array();
result[i..i + 4].copy_from_slice(&arr);
}
for i in (1 + chunks * 4)..n {
result[i] = data[i] - data[i - 1];
}
result
}
fn pct_change_simd(data: &[f64]) -> Vec<f64> {
let n = data.len();
if n < 2 {
return vec![f64::NAN; n];
}
let mut result = vec![f64::NAN; n];
if n < SIMD_THRESHOLD {
for i in 1..n {
result[i] = (data[i] - data[i - 1]) / data[i - 1];
}
return result;
}
let chunks = (n - 1) / 4;
for chunk in 0..chunks {
let i = 1 + chunk * 4;
let curr = f64x4::new([data[i], data[i + 1], data[i + 2], data[i + 3]]);
let prev = f64x4::new([data[i - 1], data[i], data[i + 1], data[i + 2]]);
let diff = curr - prev;
let pct = diff / prev;
let arr = pct.to_array();
result[i..i + 4].copy_from_slice(&arr);
}
for i in (1 + chunks * 4)..n {
result[i] = (data[i] - data[i - 1]) / data[i - 1];
}
result
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_diff_simd() {
let data = vec![100.0, 102.0, 101.0, 105.0, 103.0];
let result = diff(&data);
assert!(result[0].is_nan());
assert_eq!(result[1], 2.0);
assert_eq!(result[2], -1.0);
assert_eq!(result[3], 4.0);
assert_eq!(result[4], -2.0);
}
#[test]
fn test_pct_change_simd() {
let data = vec![100.0, 110.0, 99.0, 105.0];
let result = pct_change(&data);
assert!(result[0].is_nan());
assert!((result[1] - 0.10).abs() < 1e-10); assert!((result[2] - (-0.1)).abs() < 1e-10); assert!((result[3] - 0.06060606).abs() < 1e-6); }
}