pub fn abs_energy(series: &[f64]) -> f64 {
crate::simd::sum_of_squares(series)
}
pub fn absolute_maximum(series: &[f64]) -> f64 {
series
.iter()
.map(|x| x.abs())
.fold(f64::NEG_INFINITY, f64::max)
}
pub fn absolute_sum_of_changes(series: &[f64]) -> f64 {
if series.len() < 2 {
return 0.0;
}
series.windows(2).map(|w| (w[1] - w[0]).abs()).sum()
}
pub fn length(series: &[f64]) -> f64 {
series.len() as f64
}
pub fn maximum(series: &[f64]) -> f64 {
series.iter().copied().fold(f64::NEG_INFINITY, f64::max)
}
pub fn mean(series: &[f64]) -> f64 {
if series.is_empty() {
return f64::NAN;
}
crate::simd::mean(series)
}
pub fn mean_abs_change(series: &[f64]) -> f64 {
if series.len() < 2 {
return f64::NAN;
}
absolute_sum_of_changes(series) / (series.len() - 1) as f64
}
pub fn mean_change(series: &[f64]) -> f64 {
if series.len() < 2 {
return f64::NAN;
}
(series[series.len() - 1] - series[0]) / (series.len() - 1) as f64
}
pub fn mean_second_derivative_central(series: &[f64]) -> f64 {
if series.len() < 3 {
return f64::NAN;
}
let sum: f64 = series
.windows(3)
.map(|w| (w[2] - 2.0 * w[1] + w[0]) / 2.0)
.sum();
sum / (series.len() - 2) as f64
}
pub fn mean_n_absolute_max(series: &[f64], n: usize) -> f64 {
if series.is_empty() || n == 0 {
return f64::NAN;
}
let mut abs_values: Vec<f64> = series.iter().map(|x| x.abs()).collect();
abs_values.sort_by(|a, b| b.partial_cmp(a).unwrap_or(std::cmp::Ordering::Equal));
let count = n.min(abs_values.len());
abs_values[..count].iter().sum::<f64>() / count as f64
}
pub fn median(series: &[f64]) -> f64 {
if series.is_empty() {
return f64::NAN;
}
let mut sorted = series.to_vec();
sorted.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
let n = sorted.len();
if n % 2 == 0 {
(sorted[n / 2 - 1] + sorted[n / 2]) / 2.0
} else {
sorted[n / 2]
}
}
pub fn minimum(series: &[f64]) -> f64 {
series.iter().copied().fold(f64::INFINITY, f64::min)
}
pub fn root_mean_square(series: &[f64]) -> f64 {
if series.is_empty() {
return f64::NAN;
}
(abs_energy(series) / series.len() as f64).sqrt()
}
pub fn standard_deviation(series: &[f64]) -> f64 {
variance(series).sqrt()
}
pub fn sum_values(series: &[f64]) -> f64 {
crate::simd::sum(series)
}
pub fn variance(series: &[f64]) -> f64 {
if series.is_empty() {
return f64::NAN;
}
if series.len() == 1 {
return 0.0;
}
crate::simd::variance(series)
}
pub fn variance_sample(series: &[f64]) -> f64 {
if series.len() < 2 {
return f64::NAN;
}
crate::simd::variance_sample(series)
}
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_relative_eq;
#[test]
fn abs_energy_known_values() {
let series = vec![1.0, 2.0, 3.0, 4.0, 5.0];
assert_relative_eq!(abs_energy(&series), 55.0, epsilon = 1e-10);
}
#[test]
fn abs_energy_empty() {
let series: Vec<f64> = vec![];
assert_relative_eq!(abs_energy(&series), 0.0, epsilon = 1e-10);
}
#[test]
fn abs_energy_single() {
assert_relative_eq!(abs_energy(&[3.0]), 9.0, epsilon = 1e-10);
}
#[test]
fn abs_energy_negative_values() {
let series = vec![-1.0, -2.0, 3.0];
assert_relative_eq!(abs_energy(&series), 14.0, epsilon = 1e-10);
}
#[test]
fn absolute_maximum_positive() {
let series = vec![1.0, 5.0, 3.0, 2.0];
assert_relative_eq!(absolute_maximum(&series), 5.0, epsilon = 1e-10);
}
#[test]
fn absolute_maximum_negative_larger() {
let series = vec![1.0, -10.0, 3.0, 2.0];
assert_relative_eq!(absolute_maximum(&series), 10.0, epsilon = 1e-10);
}
#[test]
fn absolute_maximum_empty() {
let series: Vec<f64> = vec![];
assert_eq!(absolute_maximum(&series), f64::NEG_INFINITY);
}
#[test]
fn absolute_sum_of_changes_known() {
let series = vec![1.0, 3.0, 7.0, 4.0];
assert_relative_eq!(absolute_sum_of_changes(&series), 9.0, epsilon = 1e-10);
}
#[test]
fn absolute_sum_of_changes_empty() {
assert_relative_eq!(absolute_sum_of_changes(&[]), 0.0, epsilon = 1e-10);
}
#[test]
fn absolute_sum_of_changes_single() {
assert_relative_eq!(absolute_sum_of_changes(&[5.0]), 0.0, epsilon = 1e-10);
}
#[test]
fn absolute_sum_of_changes_constant() {
let series = vec![5.0; 10];
assert_relative_eq!(absolute_sum_of_changes(&series), 0.0, epsilon = 1e-10);
}
#[test]
fn length_various() {
assert_relative_eq!(length(&[1.0, 2.0, 3.0]), 3.0, epsilon = 1e-10);
assert_relative_eq!(length(&[]), 0.0, epsilon = 1e-10);
assert_relative_eq!(length(&[1.0]), 1.0, epsilon = 1e-10);
}
#[test]
fn maximum_known() {
let series = vec![1.0, 5.0, 3.0, 2.0];
assert_relative_eq!(maximum(&series), 5.0, epsilon = 1e-10);
}
#[test]
fn maximum_negative() {
let series = vec![-5.0, -2.0, -10.0];
assert_relative_eq!(maximum(&series), -2.0, epsilon = 1e-10);
}
#[test]
fn maximum_empty() {
assert_eq!(maximum(&[]), f64::NEG_INFINITY);
}
#[test]
fn mean_known() {
let series = vec![1.0, 2.0, 3.0, 4.0, 5.0];
assert_relative_eq!(mean(&series), 3.0, epsilon = 1e-10);
}
#[test]
fn mean_empty() {
assert!(mean(&[]).is_nan());
}
#[test]
fn mean_single() {
assert_relative_eq!(mean(&[7.0]), 7.0, epsilon = 1e-10);
}
#[test]
fn mean_abs_change_known() {
let series = vec![1.0, 3.0, 7.0, 4.0];
assert_relative_eq!(mean_abs_change(&series), 3.0, epsilon = 1e-10);
}
#[test]
fn mean_abs_change_short() {
assert!(mean_abs_change(&[]).is_nan());
assert!(mean_abs_change(&[1.0]).is_nan());
}
#[test]
fn mean_change_known() {
let series = vec![1.0, 2.0, 3.0, 4.0, 5.0];
assert_relative_eq!(mean_change(&series), 1.0, epsilon = 1e-10);
}
#[test]
fn mean_change_decreasing() {
let series = vec![10.0, 8.0, 6.0, 4.0, 2.0];
assert_relative_eq!(mean_change(&series), -2.0, epsilon = 1e-10);
}
#[test]
fn mean_change_short() {
assert!(mean_change(&[]).is_nan());
assert!(mean_change(&[1.0]).is_nan());
}
#[test]
fn mean_second_derivative_central_linear() {
let series = vec![1.0, 2.0, 3.0, 4.0, 5.0];
assert_relative_eq!(
mean_second_derivative_central(&series),
0.0,
epsilon = 1e-10
);
}
#[test]
fn mean_second_derivative_central_quadratic() {
let series = vec![0.0, 1.0, 4.0, 9.0, 16.0];
let result = mean_second_derivative_central(&series);
assert_relative_eq!(result, 1.0, epsilon = 1e-10);
}
#[test]
fn mean_second_derivative_central_short() {
assert!(mean_second_derivative_central(&[]).is_nan());
assert!(mean_second_derivative_central(&[1.0]).is_nan());
assert!(mean_second_derivative_central(&[1.0, 2.0]).is_nan());
}
#[test]
fn mean_n_absolute_max_known() {
let series = vec![1.0, -5.0, 3.0, -2.0, 4.0];
assert_relative_eq!(mean_n_absolute_max(&series, 3), 4.0, epsilon = 1e-10);
}
#[test]
fn mean_n_absolute_max_all() {
let series = vec![1.0, 2.0, 3.0];
assert_relative_eq!(mean_n_absolute_max(&series, 10), 2.0, epsilon = 1e-10);
}
#[test]
fn mean_n_absolute_max_empty() {
assert!(mean_n_absolute_max(&[], 3).is_nan());
}
#[test]
fn mean_n_absolute_max_zero_n() {
assert!(mean_n_absolute_max(&[1.0, 2.0], 0).is_nan());
}
#[test]
fn median_odd() {
let series = vec![1.0, 3.0, 2.0, 5.0, 4.0];
assert_relative_eq!(median(&series), 3.0, epsilon = 1e-10);
}
#[test]
fn median_even() {
let series = vec![1.0, 2.0, 3.0, 4.0];
assert_relative_eq!(median(&series), 2.5, epsilon = 1e-10);
}
#[test]
fn median_empty() {
assert!(median(&[]).is_nan());
}
#[test]
fn median_single() {
assert_relative_eq!(median(&[7.0]), 7.0, epsilon = 1e-10);
}
#[test]
fn minimum_known() {
let series = vec![3.0, 1.0, 4.0, 1.0, 5.0];
assert_relative_eq!(minimum(&series), 1.0, epsilon = 1e-10);
}
#[test]
fn minimum_negative() {
let series = vec![-5.0, -2.0, -10.0];
assert_relative_eq!(minimum(&series), -10.0, epsilon = 1e-10);
}
#[test]
fn minimum_empty() {
assert_eq!(minimum(&[]), f64::INFINITY);
}
#[test]
fn root_mean_square_known() {
let series = vec![1.0, 2.0, 3.0];
let expected = (14.0_f64 / 3.0).sqrt();
assert_relative_eq!(root_mean_square(&series), expected, epsilon = 1e-10);
}
#[test]
fn root_mean_square_constant() {
let series = vec![5.0; 10];
assert_relative_eq!(root_mean_square(&series), 5.0, epsilon = 1e-10);
}
#[test]
fn root_mean_square_empty() {
assert!(root_mean_square(&[]).is_nan());
}
#[test]
fn standard_deviation_known() {
let series = vec![1.0, 2.0, 3.0, 4.0, 5.0];
assert_relative_eq!(standard_deviation(&series), 2.0_f64.sqrt(), epsilon = 1e-10);
}
#[test]
fn standard_deviation_constant() {
let series = vec![5.0; 10];
assert_relative_eq!(standard_deviation(&series), 0.0, epsilon = 1e-10);
}
#[test]
fn standard_deviation_short() {
assert!(standard_deviation(&[]).is_nan());
}
#[test]
fn standard_deviation_single() {
assert_relative_eq!(standard_deviation(&[5.0]), 0.0, epsilon = 1e-10);
}
#[test]
fn sum_values_known() {
let series = vec![1.0, 2.0, 3.0, 4.0, 5.0];
assert_relative_eq!(sum_values(&series), 15.0, epsilon = 1e-10);
}
#[test]
fn sum_values_empty() {
assert_relative_eq!(sum_values(&[]), 0.0, epsilon = 1e-10);
}
#[test]
fn sum_values_negative() {
let series = vec![-1.0, -2.0, 3.0];
assert_relative_eq!(sum_values(&series), 0.0, epsilon = 1e-10);
}
#[test]
fn variance_known() {
let series = vec![1.0, 2.0, 3.0, 4.0, 5.0];
assert_relative_eq!(variance(&series), 2.0, epsilon = 1e-10);
}
#[test]
fn variance_constant() {
let series = vec![5.0; 10];
assert_relative_eq!(variance(&series), 0.0, epsilon = 1e-10);
}
#[test]
fn variance_two_values() {
let series = vec![0.0, 2.0];
assert_relative_eq!(variance(&series), 1.0, epsilon = 1e-10);
}
#[test]
fn variance_short() {
assert!(variance(&[]).is_nan());
}
#[test]
fn variance_single() {
assert_relative_eq!(variance(&[5.0]), 0.0, epsilon = 1e-10);
}
#[test]
fn variance_sample_known() {
let series = vec![1.0, 2.0, 3.0, 4.0, 5.0];
assert_relative_eq!(variance_sample(&series), 2.5, epsilon = 1e-10);
}
#[test]
fn variance_sample_short() {
assert!(variance_sample(&[]).is_nan());
assert!(variance_sample(&[1.0]).is_nan());
}
}