use crate::types::Float;
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
#[cfg(feature = "std")]
use std::vec::Vec;
#[inline]
pub fn sum(data: &[Float]) -> Float {
data.iter().sum()
}
#[inline]
pub fn dot_product(a: &[Float], b: &[Float]) -> Float {
assert_eq!(
a.len(),
b.len(),
"Dot product requires vectors of equal length"
);
a.iter().zip(b.iter()).map(|(x, y)| x * y).sum()
}
#[inline]
pub(crate) fn first_non_finite(values: &[Float]) -> Option<usize> {
values.iter().position(|value| !value.is_finite())
}
#[cfg_attr(all(feature = "std", target_arch = "aarch64"), allow(dead_code))]
#[inline]
pub(crate) fn typical_price(high: &[Float], low: &[Float], close: &[Float], output: &mut [Float]) {
for index in 0..high.len() {
output[index] = (high[index] + low[index] + close[index]) / 3.0 as Float;
}
}
#[inline]
pub fn rolling_sum(data: &[Float], window_size: usize) -> Vec<Float> {
assert!(window_size >= 1, "Window size must be at least 1");
assert!(
data.len() >= window_size,
"Data length must be at least window size"
);
let n = data.len();
let result_len = n - window_size + 1;
let mut result = Vec::with_capacity(result_len);
let mut current_sum: Float = data[..window_size].iter().sum();
result.push(current_sum);
for i in window_size..n {
current_sum -= data[i - window_size];
current_sum += data[i];
result.push(current_sum);
}
result
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sum_empty() {
let data: Vec<Float> = vec![];
assert_eq!(sum(&data), Float::from(0.0));
}
#[test]
fn test_sum_single() {
let data = vec![Float::from(5.0)];
assert_eq!(sum(&data), Float::from(5.0));
}
#[test]
fn test_sum_multiple() {
let data = vec![
Float::from(1.0),
Float::from(2.0),
Float::from(3.0),
Float::from(4.0),
Float::from(5.0),
];
assert_eq!(sum(&data), Float::from(15.0));
}
#[test]
fn test_sum_with_negatives() {
let data = vec![
Float::from(1.0),
Float::from(-2.0),
Float::from(3.0),
Float::from(-4.0),
Float::from(5.0),
];
assert_eq!(sum(&data), Float::from(3.0));
}
#[test]
fn test_sum_with_zeros() {
let data = vec![
Float::from(0.0),
Float::from(1.0),
Float::from(0.0),
Float::from(2.0),
Float::from(0.0),
];
assert_eq!(sum(&data), Float::from(3.0));
}
#[test]
fn test_dot_product_empty() {
let a: Vec<Float> = vec![];
let b: Vec<Float> = vec![];
assert_eq!(dot_product(&a, &b), Float::from(0.0));
}
#[test]
fn test_dot_product_single() {
let a = vec![Float::from(3.0)];
let b = vec![Float::from(4.0)];
assert_eq!(dot_product(&a, &b), Float::from(12.0));
}
#[test]
fn test_dot_product_multiple() {
let a = vec![Float::from(1.0), Float::from(2.0), Float::from(3.0)];
let b = vec![Float::from(4.0), Float::from(5.0), Float::from(6.0)];
assert_eq!(dot_product(&a, &b), Float::from(32.0));
}
#[test]
fn test_dot_product_with_negatives() {
let a = vec![Float::from(1.0), Float::from(-2.0), Float::from(3.0)];
let b = vec![Float::from(4.0), Float::from(5.0), Float::from(-6.0)];
assert_eq!(dot_product(&a, &b), Float::from(-24.0));
}
#[test]
fn test_rolling_sum_basic() {
let data = vec![
Float::from(1.0),
Float::from(2.0),
Float::from(3.0),
Float::from(4.0),
Float::from(5.0),
];
let result = rolling_sum(&data, 3);
assert_eq!(
result,
vec![Float::from(6.0), Float::from(9.0), Float::from(12.0)]
);
}
#[test]
fn test_rolling_sum_window_size_1() {
let data = vec![
Float::from(1.0),
Float::from(2.0),
Float::from(3.0),
Float::from(4.0),
];
let result = rolling_sum(&data, 1);
assert_eq!(
result,
vec![
Float::from(1.0),
Float::from(2.0),
Float::from(3.0),
Float::from(4.0)
]
);
}
#[test]
fn test_rolling_sum_full_window() {
let data = vec![Float::from(1.0), Float::from(2.0), Float::from(3.0)];
let result = rolling_sum(&data, 3);
assert_eq!(result, vec![Float::from(6.0)]);
}
#[test]
fn test_rolling_sum_with_negatives() {
let data = vec![
Float::from(1.0),
Float::from(-1.0),
Float::from(1.0),
Float::from(-1.0),
Float::from(1.0),
Float::from(1.0),
];
let result = rolling_sum(&data, 3);
assert_eq!(
result,
vec![
Float::from(1.0),
Float::from(-1.0),
Float::from(1.0),
Float::from(1.0)
]
);
}
#[test]
#[should_panic(expected = "Window size must be at least 1")]
fn test_rolling_sum_zero_window() {
let data = vec![Float::from(1.0), Float::from(2.0), Float::from(3.0)];
let _ = rolling_sum(&data, 0);
}
#[test]
#[should_panic(expected = "Data length must be at least window size")]
fn test_rolling_sum_window_too_large() {
let data = vec![Float::from(1.0), Float::from(2.0), Float::from(3.0)];
let _ = rolling_sum(&data, 5);
}
#[test]
fn test_rolling_sum_large_window() {
let data: Vec<Float> = (1..=100).map(|i| i as Float).collect();
let result = rolling_sum(&data, 10);
assert_eq!(result.len(), 91);
assert_eq!(result[0], Float::from(55.0));
assert_eq!(result[90], Float::from(955.0));
}
#[test]
fn test_rolling_sum_consistency_with_sum() {
let data: Vec<Float> = vec![
Float::from(1.0),
Float::from(2.0),
Float::from(3.0),
Float::from(4.0),
Float::from(5.0),
Float::from(6.0),
];
let result = rolling_sum(&data, data.len());
assert_eq!(result.len(), 1);
assert_eq!(result[0], sum(&data));
}
}