extern crate alloc;
use alloc::vec::Vec;
use crate::math;
pub fn lag1_autocorrelation(data: &[f64]) -> f64 {
compute_lag_autocorrelation(data, 1)
}
pub fn lag2_autocorrelation(data: &[f64]) -> f64 {
compute_lag_autocorrelation(data, 2)
}
pub fn compute_lag_autocorrelation(data: &[f64], lag: usize) -> f64 {
let n = data.len();
if n <= lag {
return 0.0;
}
let mean: f64 = data.iter().sum::<f64>() / n as f64;
let variance: f64 = data.iter().map(|&x| math::sq(x - mean)).sum();
if variance == 0.0 {
return 0.0;
}
let mut lagged_cov = 0.0;
for t in 0..(n - lag) {
lagged_cov += (data[t] - mean) * (data[t + lag] - mean);
}
lagged_cov / variance
}
pub fn estimate_dependence_length(data: &[f64], max_lag: usize) -> usize {
let n = data.len();
if n == 0 {
return 1;
}
let threshold = 2.0 / math::sqrt(n as f64);
for h in 1..=max_lag.min(n - 1) {
let rho = compute_lag_autocorrelation(data, h);
if rho.abs() < threshold {
return h;
}
}
max_lag
}
#[cfg(test)]
pub fn interleaved_autocorrelation(fixed: &[f64], random: &[f64], lag: usize) -> f64 {
let n = fixed.len().min(random.len());
let mut interleaved = Vec::with_capacity(2 * n);
for i in 0..n {
interleaved.push(fixed[i]);
interleaved.push(random[i]);
}
compute_lag_autocorrelation(&interleaved, lag)
}
#[allow(dead_code)]
pub fn autocorrelation_function(data: &[f64], max_lag: usize) -> Vec<f64> {
(1..=max_lag)
.map(|lag| compute_lag_autocorrelation(data, lag))
.collect()
}
#[cfg(test)]
pub fn effective_sample_size(n: usize, rho1: f64) -> f64 {
let rho1 = rho1.clamp(-0.99, 0.99);
let n = n as f64;
n * (1.0 - rho1) / (1.0 + rho1)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_lag1_independent_data() {
let data: Vec<f64> = (0..1000)
.map(|i| {
let mut x = i as u64;
x = x
.wrapping_mul(0x5851F42D4C957F2D)
.wrapping_add(0x14057B7EF767814F);
x ^= x >> 33;
x = x.wrapping_mul(0xC4CEB9FE1A85EC53);
(x as f64) / (u64::MAX as f64)
})
.collect();
let acf = lag1_autocorrelation(&data);
assert!(acf.abs() < 0.3, "Expected low autocorrelation, got {}", acf);
}
#[test]
fn test_lag1_correlated_data() {
let mut data = vec![0.0];
for i in 1..1000 {
data.push(data[i - 1] + ((i % 10) as f64 - 5.0) * 0.01);
}
let acf = lag1_autocorrelation(&data);
assert!(acf > 0.9, "Expected high autocorrelation, got {}", acf);
}
#[test]
fn test_constant_data() {
let data = vec![5.0; 100];
let acf = lag1_autocorrelation(&data);
assert_eq!(acf, 0.0); }
#[test]
fn test_alternating_data() {
let data: Vec<f64> = (0..100)
.map(|i| if i % 2 == 0 { 1.0 } else { -1.0 })
.collect();
let acf = lag1_autocorrelation(&data);
assert!(acf < -0.9, "Expected negative autocorrelation, got {}", acf);
}
#[test]
fn test_short_data() {
assert_eq!(lag1_autocorrelation(&[1.0]), 0.0);
assert_eq!(lag2_autocorrelation(&[1.0, 2.0]), 0.0);
}
#[test]
fn test_effective_sample_size() {
assert!((effective_sample_size(100, 0.0) - 100.0).abs() < 1e-10);
let ess_pos = effective_sample_size(100, 0.5);
assert!(ess_pos < 100.0);
assert!((ess_pos - 100.0 * (1.0 - 0.5) / (1.0 + 0.5)).abs() < 1e-10);
let ess_neg = effective_sample_size(100, -0.5);
assert!(ess_neg > 100.0);
}
#[test]
fn test_interleaved_autocorrelation() {
let fixed: Vec<f64> = (0..50).map(|x| x as f64).collect();
let random: Vec<f64> = (0..50).map(|x| (x + 50) as f64).collect();
let acf = interleaved_autocorrelation(&fixed, &random, 2);
assert!(
acf > 0.5,
"Expected high lag-2 autocorrelation, got {}",
acf
);
}
#[test]
fn test_acf_function() {
let data: Vec<f64> = (0..100).map(|x| x as f64).collect();
let acf = autocorrelation_function(&data, 5);
assert_eq!(acf.len(), 5);
for (i, &r) in acf.iter().enumerate() {
assert!(r > 0.0, "Expected positive ACF at lag {}, got {}", i + 1, r);
}
}
}