#![allow(
clippy::float_cmp,
clippy::unreadable_literal,
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
clippy::cast_precision_loss,
clippy::cast_possible_wrap,
clippy::too_many_lines,
clippy::needless_range_loop,
clippy::explicit_iter_loop,
clippy::bool_to_int_with_if,
clippy::approx_constant,
clippy::cast_lossless,
clippy::redundant_clone,
clippy::format_collect,
clippy::similar_names,
clippy::needless_collect,
clippy::iter_cloned_collect,
clippy::suboptimal_flops,
clippy::should_panic_without_expect,
clippy::manual_range_contains
)]
use crate::adaptive_polyfit::*;
use crate::constant_fit::*;
#[cfg(feature = "std")]
use crate::delta::*;
#[cfg(feature = "std")]
use crate::piecewise::*;
use crate::q16_linear::*;
#[cfg(feature = "std")]
use crate::robust::*;
use crate::simd_fit::*;
#[test]
fn test_fit_linear_perfect() {
let data: [i32; 5] = [50, 150, 250, 350, 450];
let (slope, intercept) = fit_linear_fixed(&data);
assert!((slope - 6_553_600).abs() < 100);
assert!((intercept - 3_276_800).abs() < 100);
}
#[test]
fn test_fit_constant() {
let data: [i32; 4] = [100, 100, 100, 100];
let (slope, intercept) = fit_linear_fixed(&data);
assert!(slope.abs() < 100);
assert!((intercept - 6_553_600).abs() < 100);
}
#[test]
fn test_evaluate() {
let slope = 6_553_600; let intercept = 3_276_800;
let y = evaluate_linear_fixed(slope, intercept, 3);
assert!((y - 22_937_600).abs() < 1000);
}
#[test]
fn test_q16_conversion() {
assert_eq!(int_to_q16(100), 6_553_600);
assert_eq!(q16_to_int(6_553_600), 100);
}
#[test]
fn test_single_point() {
let data: [i32; 1] = [42];
let (slope, intercept) = fit_linear_fixed(&data);
assert_eq!(slope, 0);
assert_eq!(q16_to_int(intercept), 42);
}
#[test]
fn test_empty() {
let data: [i32; 0] = [];
let (slope, intercept) = fit_linear_fixed(&data);
assert_eq!(slope, 0);
assert_eq!(intercept, 0);
}
#[test]
fn test_loop_unrolling_boundary() {
let data4: [i32; 4] = [10, 20, 30, 40];
let (slope4, _) = fit_linear_fixed(&data4);
assert!((slope4 - 655_360).abs() < 100);
let data5: [i32; 5] = [10, 20, 30, 40, 50];
let (slope5, _) = fit_linear_fixed(&data5);
assert!((slope5 - 655_360).abs() < 100);
let data7: [i32; 7] = [10, 20, 30, 40, 50, 60, 70];
let (slope7, _) = fit_linear_fixed(&data7);
assert!((slope7 - 655_360).abs() < 100); }
#[test]
fn test_two_element_data() {
let data: [i32; 2] = [0, 100];
let (slope, intercept) = fit_linear_fixed(&data);
assert!((slope - int_to_q16(100)).abs() < 200);
assert!(intercept.abs() < 200);
}
#[test]
fn test_negative_slope() {
let data: [i32; 5] = [1000, 950, 900, 850, 800];
let (slope, _intercept) = fit_linear_fixed(&data);
assert!(slope < 0);
assert!((slope + 3_276_800).abs() < 500);
}
#[test]
fn test_negative_intercept() {
let data: [i32; 5] = [-100, -90, -80, -70, -60];
let (slope, intercept) = fit_linear_fixed(&data);
assert!((slope - int_to_q16(10)).abs() < 500);
assert!(intercept < 0);
assert!((intercept + int_to_q16(100)).abs() < 500);
}
#[test]
fn test_three_element_data() {
let data: [i32; 3] = [0, 5, 10];
let (slope, intercept) = fit_linear_fixed(&data);
assert!((slope - int_to_q16(5)).abs() < 200);
assert!(intercept.abs() < 200);
}
#[test]
fn test_evaluate_at_zero() {
let slope = int_to_q16(50);
let intercept = int_to_q16(200);
let y = evaluate_linear_fixed(slope, intercept, 0);
assert_eq!(y, intercept);
}
#[test]
fn test_evaluate_negative_x() {
let slope = int_to_q16(10);
let intercept = int_to_q16(100);
let y = evaluate_linear_fixed(slope, intercept, -2);
let expected = int_to_q16(80);
assert!((y - expected).abs() < 10);
}
#[test]
fn test_q16_to_int_negative() {
let q = int_to_q16(-100);
assert_eq!(q16_to_int(q), -100);
}
#[test]
fn test_q16_to_int_zero() {
assert_eq!(q16_to_int(0), 0);
assert_eq!(int_to_q16(0), 0);
}
#[test]
fn test_fit_constant_fixed_empty() {
let result = fit_constant_fixed(&[]);
assert_eq!(result, 0);
}
#[test]
fn test_fit_constant_fixed_single() {
let data = [500];
let mean = fit_constant_fixed(&data);
assert_eq!(q16_to_int(mean), 500);
}
#[test]
fn test_fit_constant_fixed_multiple() {
let data = [100, 200, 300];
let mean = fit_constant_fixed(&data);
assert!((q16_to_int(mean) - 200).abs() <= 1);
}
#[test]
fn test_fit_constant_fixed_unrolled() {
let data = [10, 20, 30, 40, 50, 60, 70, 80];
let mean = fit_constant_fixed(&data);
assert!((q16_to_int(mean) - 45).abs() <= 1);
}
#[test]
fn test_compute_residual_error_perfect_fit() {
let data = [0, 10, 20, 30, 40];
let (slope, intercept) = fit_linear_fixed(&data);
let err = compute_residual_error(&data, slope, intercept);
assert!(err < 1000, "residual error for perfect fit: {err}");
}
#[test]
fn test_compute_residual_error_constant_vs_linear() {
let data = [0, 100, 200, 300, 400];
let constant_mean = fit_constant_fixed(&data);
let (slope, intercept) = fit_linear_fixed(&data);
let err_const = compute_residual_error(&data, 0, constant_mean);
let err_linear = compute_residual_error(&data, slope, intercept);
assert!(err_linear < err_const);
}
#[test]
fn test_should_use_linear_for_trending_data() {
let data = [0, 100, 200, 300, 400, 500, 600];
assert!(should_use_linear(&data));
}
#[test]
fn test_should_use_linear_for_constant_data() {
let data = [100, 100, 100, 100, 100, 100];
assert!(!should_use_linear(&data));
}
#[test]
fn test_should_use_linear_too_short() {
assert!(!should_use_linear(&[]));
assert!(!should_use_linear(&[1]));
assert!(!should_use_linear(&[1, 2]));
}
#[cfg(feature = "std")]
#[test]
fn test_q16_to_f32_positive() {
let q = Q16_ONE;
let f = q16_to_f32(q);
assert!((f - 1.0).abs() < 1e-5);
}
#[allow(clippy::float_cmp)]
#[cfg(feature = "std")]
#[test]
fn test_q16_to_f32_zero() {
assert_eq!(q16_to_f32(0), 0.0);
}
#[cfg(feature = "std")]
#[test]
fn test_q16_to_f32_half() {
let q = Q16_ONE >> 1;
let f = q16_to_f32(q);
assert!((f - 0.5).abs() < 1e-5);
}
#[cfg(feature = "std")]
#[test]
fn test_q16_to_f32_negative() {
let q = -Q16_ONE;
let f = q16_to_f32(q);
assert!((f + 1.0).abs() < 1e-5);
}
#[cfg(feature = "std")]
#[test]
fn test_large_dataset_consistency() {
let data: Vec<i32> = (0..100).collect();
let (slope, intercept) = fit_linear_fixed(&data);
assert!((slope - 65536).abs() < 500);
assert!(intercept.abs() < 500);
}
#[test]
fn test_fit_linear_all_zeros() {
let data = [0i32; 8];
let (slope, intercept) = fit_linear_fixed(&data);
assert_eq!(slope, 0);
assert_eq!(intercept, 0);
}
#[test]
fn test_evaluate_linear_fixed_large_x() {
let slope = int_to_q16(1);
let intercept = int_to_q16(0);
let y = evaluate_linear_fixed(slope, intercept, 1000);
assert!((q16_to_int(y) - 1000).abs() <= 1);
}
#[test]
fn test_fit_quadratic_perfect() {
let data = [0, 1, 4, 9, 16, 25];
let (a, b, c) = fit_quadratic_fixed(&data);
assert!((a - int_to_q16(1)).abs() < 500, "a = {a} (expected ~65536)");
assert!(b.abs() < 500, "b = {b} (expected ~0)");
assert!(c.abs() < 500, "c = {c} (expected ~0)");
}
#[test]
fn test_fit_quadratic_with_linear() {
let data = [10, 15, 24, 37, 54];
let (a, b, c) = fit_quadratic_fixed(&data);
assert!((a - int_to_q16(2)).abs() < 1000, "a = {a}");
assert!((b - int_to_q16(3)).abs() < 1000, "b = {b}");
assert!((c - int_to_q16(10)).abs() < 1000, "c = {c}");
}
#[test]
fn test_evaluate_quadratic() {
let a = int_to_q16(1); let b = int_to_q16(2); let c = int_to_q16(3); let y = evaluate_quadratic_fixed(a, b, c, 3);
assert!((q16_to_int(y) - 18).abs() <= 1);
}
#[test]
fn test_fit_quadratic_fallback_2pts() {
let data = [10, 20];
let (a, b, _c) = fit_quadratic_fixed(&data);
assert_eq!(a, 0); assert!((b - int_to_q16(10)).abs() < 500);
}
#[test]
fn test_fit_quadratic_empty() {
let data: [i32; 0] = [];
let (a, b, c) = fit_quadratic_fixed(&data);
assert_eq!(a, 0);
assert_eq!(b, 0);
assert_eq!(c, 0);
}
#[test]
fn test_fit_cubic_perfect() {
let data = [0, 1, 8, 27, 64, 125];
let (a, b, _c, _d) = fit_cubic_fixed(&data);
assert!(
(a - int_to_q16(1)).abs() < 2000,
"a = {a} (expected ~65536)"
);
assert!(b.abs() < 2000, "b = {b} (expected ~0)");
}
#[test]
fn test_fit_cubic_fallback_3pts() {
let data = [0, 1, 4];
let (a, _b, _c, _d) = fit_cubic_fixed(&data);
assert_eq!(a, 0);
}
#[test]
fn test_evaluate_cubic() {
let a = int_to_q16(1);
let b = 0;
let c = 0;
let d = int_to_q16(5);
let y = evaluate_cubic_fixed(a, b, c, d, 2);
assert!((q16_to_int(y) - 13).abs() <= 1);
}
#[cfg(feature = "std")]
#[test]
fn test_piecewise_linear_single_segment() {
let data: Vec<i32> = (0..20).map(|x| x * 10).collect();
let segments = fit_piecewise_linear(&data, i64::MAX, 4);
assert_eq!(segments.len(), 1);
assert_eq!(segments[0].start, 0);
assert_eq!(segments[0].end, 20);
}
#[cfg(feature = "std")]
#[test]
fn test_piecewise_linear_split() {
let mut data = vec![0i32; 10];
data.extend(vec![1000i32; 10]);
let segments = fit_piecewise_linear(&data, 100, 4);
assert!(segments.len() >= 2, "segments: {}", segments.len());
}
#[cfg(feature = "std")]
#[test]
fn test_piecewise_linear_empty() {
let segments = fit_piecewise_linear(&[], 100, 4);
assert!(segments.is_empty());
}
#[cfg(feature = "std")]
#[test]
fn test_piecewise_linear_short() {
let data = [1, 2, 3];
let segments = fit_piecewise_linear(&data, 0, 4);
assert_eq!(segments.len(), 1);
}
#[cfg(feature = "std")]
#[test]
fn test_filter_outliers_basic() {
let data = [100, 101, 99, 100, 102, 100, 9999, 100, 101, 99];
let filtered = filter_outliers_mad(&data, 3);
assert_eq!(filtered.len(), 10);
assert!(filtered[6] < 200, "outlier was replaced: {}", filtered[6]);
}
#[cfg(feature = "std")]
#[test]
fn test_filter_outliers_no_outliers() {
let data = [10, 11, 10, 11, 10];
let filtered = filter_outliers_mad(&data, 3);
assert_eq!(filtered, data);
}
#[cfg(feature = "std")]
#[test]
fn test_filter_outliers_short() {
let data = [1, 2];
let filtered = filter_outliers_mad(&data, 3);
assert_eq!(filtered, data);
}
#[cfg(feature = "std")]
#[test]
fn test_fit_linear_robust() {
let mut data: Vec<i32> = (0..20).map(|x| x * 10).collect();
data[10] = 99999; let (slope, _intercept) = fit_linear_robust(&data, 3);
assert!((slope - int_to_q16(10)).abs() < 20000, "slope = {slope}");
}
#[test]
fn test_fit_linear_simd_matches_scalar() {
let data = [50, 150, 250, 350, 450, 550, 650, 750, 850, 950];
let (slope_scalar, intercept_scalar) = fit_linear_fixed(&data);
let (slope_simd, intercept_simd) = fit_linear_simd(&data);
assert!(
(slope_simd - slope_scalar).abs() < 100,
"slope: simd={slope_simd} scalar={slope_scalar}"
);
assert!(
(intercept_simd - intercept_scalar).abs() < 100,
"intercept: simd={intercept_simd} scalar={intercept_scalar}"
);
}
#[test]
fn test_fit_linear_simd_large() {
let data: [i32; 16] = [
10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160,
];
let (slope_scalar, intercept_scalar) = fit_linear_fixed(&data);
let (slope_simd, intercept_simd) = fit_linear_simd(&data);
assert!(
(slope_simd - slope_scalar).abs() < 100,
"slope: simd={slope_simd} scalar={slope_scalar}"
);
assert!(
(intercept_simd - intercept_scalar).abs() < 100,
"intercept: simd={intercept_simd} scalar={intercept_scalar}"
);
}
#[test]
fn test_fit_linear_simd_small_fallback() {
let data = [100, 200, 300];
let (slope, intercept) = fit_linear_simd(&data);
assert!((slope - int_to_q16(100)).abs() < 500);
assert!((intercept - int_to_q16(100)).abs() < 500);
}
#[cfg(feature = "std")]
#[test]
fn test_delta_encode_decode_roundtrip() {
let coeffs = vec![(100, 200), (110, 210), (120, 220), (130, 230)];
let encoded = delta_encode_coefficients(&coeffs);
let decoded = delta_decode_coefficients(&encoded);
assert_eq!(decoded, coeffs);
}
#[cfg(feature = "std")]
#[test]
fn test_delta_encode_first_preserved() {
let coeffs = vec![(1000, 2000), (1005, 2010)];
let encoded = delta_encode_coefficients(&coeffs);
assert_eq!(encoded[0], (1000, 2000)); assert_eq!(encoded[1], (5, 10)); }
#[cfg(feature = "std")]
#[test]
fn test_delta_encode_empty() {
let encoded = delta_encode_coefficients(&[]);
assert!(encoded.is_empty());
}
#[cfg(feature = "std")]
#[test]
fn test_delta_encoding_savings() {
let coeffs: Vec<(i32, i32)> = (0..100)
.map(|i| (int_to_q16(100 + i), int_to_q16(50 + i)))
.collect();
let (original, delta) = delta_encoding_savings(&coeffs);
assert!(delta <= original, "delta={delta} original={original}");
}
#[test]
fn test_q16_roundtrip_positive() {
for v in [1, 10, 100, 1000, 32767] {
let q = int_to_q16(v);
assert_eq!(q16_to_int(q), v, "roundtrip failed for {v}");
}
}
#[test]
fn test_q16_roundtrip_negative() {
for v in [-1, -10, -100, -1000, -32768] {
let q = int_to_q16(v);
assert_eq!(q16_to_int(q), v, "roundtrip failed for {v}");
}
}
#[cfg(feature = "std")]
#[test]
fn test_q16_to_f32_roundtrip() {
for v in [0, 1, -1, 100, -100] {
let q = int_to_q16(v);
let f = q16_to_f32(q);
assert!(
(f - v as f32).abs() < 1e-3,
"f32 roundtrip failed for {v}: got {f}",
);
}
}
#[cfg(feature = "std")]
#[test]
fn test_q16_to_f32_fractional() {
let q = Q16_ONE / 4;
let f = q16_to_f32(q);
assert!((f - 0.25).abs() < 1e-5);
}
#[test]
fn test_compute_residual_error_empty() {
let err = compute_residual_error(&[], 0, 0);
assert_eq!(err, 0);
}
#[test]
fn test_compute_residual_error_single() {
let data = [100];
let slope = 0;
let intercept = int_to_q16(100);
let err = compute_residual_error(&data, slope, intercept);
assert!(err < 100, "err = {err}");
}
#[test]
fn test_q16_shift_and_one_constants() {
assert_eq!(Q16_SHIFT, 16);
assert_eq!(Q16_ONE, 65536);
assert_eq!(Q16_ONE, 1 << Q16_SHIFT);
}
#[test]
fn test_fit_linear_exactly_8_elements() {
let data = [0i32, 10, 20, 30, 40, 50, 60, 70];
let (slope, intercept) = fit_linear_fixed(&data);
assert!((slope - int_to_q16(10)).abs() < 500, "slope={slope}");
assert!(intercept.abs() < 500, "intercept={intercept}");
}
#[test]
fn test_fit_linear_exactly_9_elements() {
let data = [0i32, 5, 10, 15, 20, 25, 30, 35, 40];
let (slope, intercept) = fit_linear_fixed(&data);
assert!((slope - int_to_q16(5)).abs() < 300);
assert!(intercept.abs() < 300);
}
#[test]
fn test_fit_linear_large_values() {
let data = [1000i32, 2000, 3000, 4000, 5000];
let (slope, _intercept) = fit_linear_fixed(&data);
assert!(slope > 0, "slope must be positive, got {slope}");
}
#[test]
fn test_fit_linear_temperature_sensor() {
let data = [2500i32, 2510, 2520, 2530, 2540, 2550, 2560, 2570];
let (slope, intercept) = fit_linear_fixed(&data);
assert!((slope - int_to_q16(10)).abs() < 500, "slope={slope}");
assert!(
(intercept - int_to_q16(2500)).abs() < 1000,
"intercept={intercept}"
);
}
#[test]
fn test_fit_linear_pressure_sensor() {
let data = [10132i32, 10130, 10128, 10126, 10124, 10122];
let (slope, _intercept) = fit_linear_fixed(&data);
assert!(slope < 0, "pressure drop: slope should be negative");
}
#[test]
fn test_fit_linear_noisy_linear() {
let data = [52i32, 148, 253, 347, 452, 548, 653, 747];
let (slope, _intercept) = fit_linear_fixed(&data);
assert!(
(slope - int_to_q16(100)).abs() < int_to_q16(15),
"slope={slope}"
);
}
#[test]
fn test_evaluate_linear_fixed_symmetry() {
let slope = int_to_q16(10);
let intercept = 0;
let y_pos = evaluate_linear_fixed(slope, intercept, 5);
let y_neg = evaluate_linear_fixed(slope, intercept, -5);
assert_eq!(y_pos, int_to_q16(50));
assert_eq!(y_neg, -int_to_q16(50));
}
#[test]
fn test_evaluate_linear_fixed_zero_slope() {
let intercept = int_to_q16(999);
for x in [-10, 0, 10, 100] {
let y = evaluate_linear_fixed(0, intercept, x);
assert_eq!(y, intercept, "x={x}");
}
}
#[test]
fn test_fit_constant_fixed_four_elements() {
let data = [10i32, 20, 30, 40];
let mean = fit_constant_fixed(&data);
assert!((q16_to_int(mean) - 25).abs() <= 1);
}
#[test]
fn test_fit_constant_fixed_five_elements() {
let data = [10i32, 20, 30, 40, 50];
let mean = fit_constant_fixed(&data);
assert!((q16_to_int(mean) - 30).abs() <= 1);
}
#[test]
fn test_fit_constant_fixed_negative_values() {
let data = [-100i32, -50, 0, 50, 100];
let mean = fit_constant_fixed(&data);
assert!(q16_to_int(mean).abs() <= 1);
}
#[test]
fn test_fit_quadratic_negative_a() {
let data = [100i32, 99, 96, 91, 84];
let (a, _b, c) = fit_quadratic_fixed(&data);
assert!(a < 0, "a should be negative, got {a}");
assert!((c - int_to_q16(100)).abs() < 1000, "c={c}");
}
#[test]
fn test_fit_quadratic_single_point() {
let data = [42i32];
let (a, b, c) = fit_quadratic_fixed(&data);
assert_eq!(a, 0);
assert_eq!(b, 0);
assert_eq!(q16_to_int(c), 42);
}
#[test]
fn test_fit_quadratic_all_same() {
let data = [7i32; 6];
let (a, b, c) = fit_quadratic_fixed(&data);
assert_eq!(a, 0, "a={a}");
assert!(b.abs() < 500, "b={b}");
assert!((c - int_to_q16(7)).abs() < 500, "c={c}");
}
#[test]
fn test_evaluate_quadratic_at_zero() {
let a = int_to_q16(5);
let b = int_to_q16(3);
let c = int_to_q16(7);
let y = evaluate_quadratic_fixed(a, b, c, 0);
assert_eq!(y, c);
}
#[test]
fn test_evaluate_quadratic_at_one() {
let a = int_to_q16(2);
let b = int_to_q16(3);
let c = int_to_q16(4);
let y = evaluate_quadratic_fixed(a, b, c, 1);
assert!((q16_to_int(y) - 9).abs() <= 1);
}
#[test]
fn test_evaluate_quadratic_negative_x() {
let a = int_to_q16(1);
let b = int_to_q16(2);
let c = int_to_q16(1);
let y = evaluate_quadratic_fixed(a, b, c, -2);
assert!((q16_to_int(y) - 1).abs() <= 1);
}
#[test]
fn test_fit_cubic_fallback_1pt() {
let data = [99i32];
let (a, b, _c, d) = fit_cubic_fixed(&data);
assert_eq!(a, 0);
assert_eq!(b, 0);
assert_eq!(q16_to_int(d), 99);
}
#[test]
fn test_fit_cubic_fallback_2pts() {
let data = [0i32, 10];
let (a, b, _c, _d) = fit_cubic_fixed(&data);
assert_eq!(a, 0, "cubic term must be 0 for 2 pts");
assert_eq!(b, 0, "quadratic term must be 0 for 2 pts");
}
#[test]
fn test_fit_cubic_constant_data() {
let data = [5i32; 5];
let (a, _b, _c, d) = fit_cubic_fixed(&data);
assert_eq!(a, 0, "a={a}");
assert!((d - int_to_q16(5)).abs() < 500, "d={d}");
}
#[test]
fn test_evaluate_cubic_at_zero() {
let a = int_to_q16(1);
let b = int_to_q16(2);
let c = int_to_q16(3);
let d = int_to_q16(10);
let y = evaluate_cubic_fixed(a, b, c, d, 0);
assert_eq!(y, d);
}
#[test]
fn test_evaluate_cubic_at_negative_x() {
let a = int_to_q16(1);
let b = 0;
let c = 0;
let d = 0;
let y = evaluate_cubic_fixed(a, b, c, d, -2);
assert!((q16_to_int(y) - (-8)).abs() <= 1);
}
#[test]
fn test_evaluate_cubic_consistency_with_quadratic() {
let b = int_to_q16(2);
let c = int_to_q16(3);
let d = int_to_q16(5);
for x in [0i32, 1, 2, -1, -2] {
let yq = evaluate_quadratic_fixed(b, c, d, x);
let yc = evaluate_cubic_fixed(0, b, c, d, x);
assert_eq!(yc, yq, "mismatch at x={x}");
}
}
#[test]
fn test_should_use_linear_noisy_constant() {
let data = [100i32, 101, 100, 99, 100, 101, 100, 99];
assert!(!should_use_linear(&data));
}
#[test]
fn test_should_use_linear_exactly_3_elements() {
let data = [0i32, 100, 200];
assert!(should_use_linear(&data));
}
#[test]
fn test_compute_residual_error_noisy() {
let perfect = [0i32, 10, 20, 30, 40];
let noisy = [0i32, 12, 18, 33, 37];
let (sp, ip) = fit_linear_fixed(&perfect);
let (sn, inn) = fit_linear_fixed(&noisy);
let err_perfect = compute_residual_error(&perfect, sp, ip);
let err_noisy = compute_residual_error(&noisy, sn, inn);
assert!(err_noisy >= err_perfect);
}
#[cfg(feature = "std")]
#[test]
fn test_filter_outliers_mad_all_same() {
let data = [42i32; 8];
let filtered = filter_outliers_mad(&data, 3);
assert_eq!(filtered, data);
}
#[cfg(feature = "std")]
#[test]
fn test_filter_outliers_mad_multiple_outliers() {
let data = [100i32, 101, 9999, 99, 100, -9999, 101, 100];
let filtered = filter_outliers_mad(&data, 3);
assert!(filtered[2] < 200, "upper outlier replaced: {}", filtered[2]);
assert!(
filtered[5] > -200,
"lower outlier replaced: {}",
filtered[5]
);
}
#[cfg(feature = "std")]
#[test]
fn test_filter_outliers_mad_k1_aggressive() {
let data = [100i32, 101, 102, 200, 99, 100];
let filtered = filter_outliers_mad(&data, 1);
assert!(filtered[3] < 200, "filtered[3]={}", filtered[3]);
}
#[cfg(feature = "std")]
#[test]
fn test_filter_outliers_mad_exact_3_elements() {
let data = [10i32, 100, 10];
let filtered = filter_outliers_mad(&data, 2);
assert_eq!(filtered.len(), 3);
}
#[cfg(feature = "std")]
#[test]
fn test_fit_linear_robust_no_outliers() {
let data: Vec<i32> = (0..10).map(|x: i32| x * 50).collect();
let (s_robust, i_robust) = fit_linear_robust(&data, 3);
let (s_plain, i_plain) = fit_linear_fixed(&data);
assert!((s_robust - s_plain).abs() < 200);
assert!((i_robust - i_plain).abs() < 200);
}
#[cfg(feature = "std")]
#[test]
fn test_piecewise_linear_coverage_fields() {
let data: Vec<i32> = (0..10).map(|x| x * 5).collect();
let segments = fit_piecewise_linear(&data, i64::MAX, 2);
assert!(!segments.is_empty());
let seg = &segments[0];
assert_eq!(seg.start, 0);
assert_eq!(seg.end, 10);
}
#[cfg(feature = "std")]
#[test]
fn test_piecewise_linear_coverage_ordering() {
let mut data = vec![0i32; 10];
data.extend(vec![500i32; 10]);
let segments = fit_piecewise_linear(&data, 0, 2);
for w in segments.windows(2) {
assert!(w[0].end <= w[1].start, "segments overlap or unordered");
}
}
#[cfg(feature = "std")]
#[test]
fn test_piecewise_linear_single_element() {
let data = [42i32];
let segments = fit_piecewise_linear(&data, 100, 4);
assert_eq!(segments.len(), 1);
assert_eq!(segments[0].start, 0);
assert_eq!(segments[0].end, 1);
}
#[cfg(feature = "std")]
#[test]
fn test_delta_encode_single_element() {
let coeffs = vec![(500i32, 1000i32)];
let encoded = delta_encode_coefficients(&coeffs);
assert_eq!(encoded.len(), 1);
assert_eq!(encoded[0], (500, 1000));
let decoded = delta_decode_coefficients(&encoded);
assert_eq!(decoded, coeffs);
}
#[cfg(feature = "std")]
#[test]
fn test_delta_encode_negative_values() {
let coeffs = vec![(-100i32, -200i32), (-90, -180), (-80, -160)];
let encoded = delta_encode_coefficients(&coeffs);
assert_eq!(encoded[0], (-100, -200));
assert_eq!(encoded[1].0, 10); let decoded = delta_decode_coefficients(&encoded);
assert_eq!(decoded, coeffs);
}
#[cfg(feature = "std")]
#[test]
fn test_delta_decode_empty() {
let decoded = delta_decode_coefficients(&[]);
assert!(decoded.is_empty());
}
#[cfg(feature = "std")]
#[test]
fn test_delta_encoding_savings_constant_coefficients() {
let coeffs: Vec<(i32, i32)> = vec![(int_to_q16(10), int_to_q16(5)); 20];
let (original, delta) = delta_encoding_savings(&coeffs);
assert!(delta <= original, "delta={delta} original={original}");
}
#[cfg(feature = "std")]
#[test]
fn test_delta_encoding_savings_single() {
let coeffs = vec![(int_to_q16(50), int_to_q16(25))];
let (original, delta) = delta_encoding_savings(&coeffs);
assert_eq!(original, delta); }
#[cfg(feature = "std")]
#[test]
fn test_fit_linear_simd_zero_data() {
let data = [0i32; 8];
let (slope, intercept) = fit_linear_simd(&data);
assert_eq!(slope, 0);
assert_eq!(intercept, 0);
}
#[cfg(feature = "std")]
#[test]
fn test_large_dataset_q16_accuracy() {
let data: Vec<i32> = (0..1000).map(|x: i32| x * 3 + 7).collect();
let (slope, intercept) = fit_linear_fixed(&data);
assert!((slope - int_to_q16(3)).abs() < 1000, "slope={slope}");
assert!(
(intercept - int_to_q16(7)).abs() < 1000,
"intercept={intercept}"
);
}
#[cfg(feature = "std")]
#[test]
fn test_model_selection_sensor_window() {
let window: Vec<i32> = (0..16).map(|x: i32| 2000 + x * 5).collect();
assert!(should_use_linear(&window));
}
#[cfg(feature = "std")]
#[test]
fn test_model_selection_stable_sensor() {
let window: Vec<i32> = (0..16).map(|_| 2500).collect();
assert!(!should_use_linear(&window));
}