use crate::fit_linear_fixed;
use alice_codec::Wavelet1D;
#[must_use]
pub fn fit_denoised(data: &[i32], threshold: i32) -> (i32, i32) {
if data.len() < 4 {
return fit_linear_fixed(data);
}
let denoised = wavelet_denoise(data, threshold);
fit_linear_fixed(&denoised)
}
#[must_use]
pub fn wavelet_denoise(data: &[i32], threshold: i32) -> Vec<i32> {
let n = data.len();
if n < 2 {
return data.to_vec();
}
let padded_len = n.next_power_of_two();
let mut buf = Vec::with_capacity(padded_len);
buf.extend_from_slice(data);
for i in 0..(padded_len - n) {
let idx = n - 1 - (i % n);
buf.push(data[idx]);
}
let wavelet = Wavelet1D::cdf53();
wavelet.forward(&mut buf);
let half = padded_len / 2;
let abs_threshold = threshold.unsigned_abs();
for coeff in &mut buf[half..] {
if (*coeff as i64).unsigned_abs() < abs_threshold as u64 {
*coeff = 0;
}
}
wavelet.inverse(&mut buf);
buf.truncate(n);
buf
}
#[must_use]
pub fn denoising_benefit(data: &[i32], threshold: i32) -> (i64, i64) {
let (slope_raw, intercept_raw) = fit_linear_fixed(data);
let error_raw = crate::compute_residual_error(data, slope_raw, intercept_raw);
let denoised = wavelet_denoise(data, threshold);
let (slope_dn, intercept_dn) = fit_linear_fixed(&denoised);
let error_dn = crate::compute_residual_error(&denoised, slope_dn, intercept_dn);
(error_raw, error_dn)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_wavelet_denoise_preserves_length() {
let data = [100, 200, 300, 400, 500, 600, 700, 800];
let denoised = wavelet_denoise(&data, 10);
assert_eq!(denoised.len(), data.len());
}
#[test]
fn test_fit_denoised_short_data() {
let data = [100, 200];
let (slope, intercept) = fit_denoised(&data, 10);
let (slope_raw, intercept_raw) = fit_linear_fixed(&data);
assert_eq!(slope, slope_raw);
assert_eq!(intercept, intercept_raw);
}
#[test]
fn test_denoising_benefit() {
let data = [100, 210, 290, 410, 490, 610, 690, 810];
let (err_raw, err_dn) = denoising_benefit(&data, 30);
assert!(err_dn <= err_raw);
}
#[test]
fn test_wavelet_denoise_single_element() {
let data = [42];
let denoised = wavelet_denoise(&data, 10);
assert_eq!(denoised.len(), 1);
assert_eq!(denoised[0], 42);
}
#[test]
fn test_wavelet_denoise_two_elements() {
let data = [10, 20];
let denoised = wavelet_denoise(&data, 5);
assert_eq!(denoised.len(), 2);
}
#[test]
fn test_wavelet_denoise_power_of_two_length() {
let data = [
10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160,
];
let denoised = wavelet_denoise(&data, 5);
assert_eq!(denoised.len(), data.len());
}
#[test]
fn test_wavelet_denoise_non_power_of_two_length() {
let data = [10, 20, 30, 40, 50, 60];
let denoised = wavelet_denoise(&data, 5);
assert_eq!(denoised.len(), 6);
}
#[test]
fn test_wavelet_denoise_zero_threshold() {
let data = [100, 200, 150, 250, 300, 200, 350, 400];
let denoised = wavelet_denoise(&data, 0);
assert_eq!(denoised.len(), data.len());
}
#[test]
fn test_fit_denoised_fallback_for_three_elements() {
let data = [0, 50, 100];
let (slope_dn, intercept_dn) = fit_denoised(&data, 10);
let (slope_raw, intercept_raw) = fit_linear_fixed(&data);
assert_eq!(slope_dn, slope_raw);
assert_eq!(intercept_dn, intercept_raw);
}
#[test]
fn test_fit_denoised_processes_four_elements() {
let data = [100, 200, 300, 400];
let (slope, intercept) = fit_denoised(&data, 5);
assert!(slope != i32::MIN);
assert!(intercept != i32::MIN);
}
#[test]
fn test_denoising_benefit_constant_data() {
let data = [500, 500, 500, 500, 500, 500, 500, 500];
let (err_raw, err_dn) = denoising_benefit(&data, 10);
assert!(err_dn <= err_raw + 1); }
#[test]
fn test_denoising_benefit_returns_two_values() {
let data = [1, 2, 3, 4, 5, 6, 7, 8];
let (err_raw, err_dn) = denoising_benefit(&data, 5);
assert!(err_raw >= 0);
assert!(err_dn >= 0);
}
}