use crate::smoothing::{sma, smooth};
use crate::statistics::Metrics;
#[test]
fn test_sma_basic() {
let data = vec![1.0, 2.0, 3.0, 4.0, 5.0];
let result = sma(&data, 3, 3);
assert_eq!(result.len(), 2, "Expected 2 values from SMA");
assert!((result[0] - 2.0).abs() < 1e-10, "First value should be 2.0, got {}", result[0]);
assert!((result[1] - 4.5).abs() < 1e-10, "Second value should be 4.5, got {}", result[1]);
}
#[test]
fn test_sma_slide_one() {
let data = vec![1.0, 2.0, 3.0, 4.0, 5.0];
let result = sma(&data, 3, 1);
assert_eq!(result.len(), 3);
assert!((result[0] - 2.0).abs() < 1e-10);
assert!((result[1] - 3.0).abs() < 1e-10);
assert!((result[2] - 4.0).abs() < 1e-10);
}
#[test]
fn test_sma_with_nan() {
let data = vec![1.0, f64::NAN, 3.0, 4.0];
let result = sma(&data, 2, 2);
assert_eq!(result.len(), 2);
assert!((result[0] - 0.5).abs() < 1e-10, "First value should be 0.5, got {}", result[0]);
assert!((result[1] - 3.5).abs() < 1e-10, "Second value should be 3.5, got {}", result[1]);
}
#[test]
fn test_sma_window_equals_slide() {
let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0];
let result = sma(&data, 2, 2);
assert_eq!(result.len(), 3);
assert!((result[0] - 1.5).abs() < 1e-10);
assert!((result[1] - 3.5).abs() < 1e-10);
assert!((result[2] - 5.5).abs() < 1e-10);
}
#[test]
fn test_sma_window_greater_than_data() {
let data = vec![1.0, 2.0, 3.0];
let result = sma(&data, 5, 1);
assert!(result.is_empty());
}
#[test]
fn test_sma_last_incomplete_window() {
let data = vec![1.0, 2.0, 3.0, 4.0, 5.0];
let result = sma(&data, 3, 3);
assert_eq!(result.len(), 2);
assert!((result[0] - 2.0).abs() < 1e-10);
assert!((result[1] - 4.5).abs() < 1e-10);
}
#[test]
fn test_smooth_small_dataset() {
let data = vec![1.0, 3.0, 2.0, 4.0, 3.0, 5.0, 4.0, 2.0];
let result = smooth(&data, 2);
assert!(!result.is_empty());
assert!(result.len() <= data.len());
}
#[test]
fn test_smooth_empty() {
let data: Vec<f64> = vec![];
let result = smooth(&data, 2);
assert!(result.is_empty());
}
#[test]
fn test_smooth_single_element() {
let data = vec![42.0];
let result = smooth(&data, 2);
assert!(result.len() <= 1);
if !result.is_empty() {
assert_eq!(result[0], 42.0);
}
}
#[test]
fn test_smooth_preserves_mean() {
let data: Vec<f64> = (1..101).map(|x| x as f64).collect();
let original_mean = Metrics::mean(&data);
let smoothed = smooth(&data, 10);
if smoothed.is_empty() {
return;
}
let smoothed_mean = Metrics::mean(&smoothed);
assert!((original_mean - smoothed_mean).abs() < original_mean * 0.1,
"Means should be close: original {}, smoothed {}",
original_mean, smoothed_mean);
}
#[test]
fn test_smooth_reduces_roughness() {
let mut data = Vec::with_capacity(100);
for i in 0..100 {
data.push(if i % 2 == 0 { 10.0 } else { 0.0 });
}
let original_metrics = Metrics::new(data.clone());
let smoothed = smooth(&data, 10);
if smoothed.is_empty() {
return;
}
let smoothed_metrics = Metrics::new(smoothed);
assert!(smoothed_metrics.roughness() < original_metrics.roughness(),
"Smoothed roughness ({}) should be less than original roughness ({})",
smoothed_metrics.roughness(), original_metrics.roughness());
}
#[test]
fn test_smooth_with_nan_values() {
let mut data = vec![1.0, 2.0, 3.0, 4.0, 5.0];
data.push(f64::NAN);
let result = smooth(&data, 2);
assert!(!result.is_empty());
for (i, &val) in result.iter().enumerate() {
assert!(!val.is_nan(), "Result contains NaN at position {}", i);
}
}
#[test]
fn test_smooth_seasonal_data() {
let data: Vec<f64> = (0..200)
.map(|i| {
let i_f64 = i as f64;
(i_f64 * 0.1).sin() * 10.0 + (i_f64 * 0.6).sin() * 2.0
})
.collect();
let result = smooth(&data, 10);
assert!(!result.is_empty());
}
#[test]
fn test_smooth_with_different_resolutions() {
let data: Vec<f64> = (0..100).map(|i| (i as f64 * 0.1).sin() + i as f64 * 0.01).collect();
for &res in &[5, 10, 20] {
let smoothed = smooth(&data, res);
assert!(!smoothed.is_empty(),
"Resolution {} should produce non-empty output", res);
}
}