use crate::linalg::ols;
use crate::{check_input, Error, Result};
#[derive(Debug, Clone, Copy)]
pub struct EacfConfig {
pub max_ar: usize,
pub max_ma: usize,
pub band: f64,
}
impl Default for EacfConfig {
fn default() -> Self {
EacfConfig {
max_ar: 7,
max_ma: 13,
band: 2.0,
}
}
}
#[derive(Debug, Clone)]
pub struct EacfResult {
pub order: Option<(usize, usize)>,
pub table: Vec<Vec<f64>>,
pub significant: Vec<Vec<bool>>,
}
pub fn eacf(x: &[f64], config: EacfConfig) -> Result<EacfResult> {
let n = x.len();
let kmax = config.max_ar;
let jmax = config.max_ma;
let base_max = kmax + jmax + 1;
check_input(x, 2 * base_max + 2)?;
let mean = x.iter().sum::<f64>() / n as f64;
let xc: Vec<f64> = x.iter().map(|v| v - mean).collect();
let mut layers: Vec<Vec<Vec<f64>>> = Vec::with_capacity(jmax + 2);
let mut base: Vec<Vec<f64>> = vec![Vec::new()]; for k in 1..=base_max {
let rows = n - k;
let mut design = vec![0.0; rows * k];
let mut y = vec![0.0; rows];
for t in 0..rows {
y[t] = xc[k + t];
for l in 0..k {
design[t * k + l] = xc[k + t - l - 1];
}
}
let (beta, _s2, _se) = ols(&design, rows, k, &y)?;
base.push(beta);
}
layers.push(base);
for j in 1..=jmax + 1 {
let prev = &layers[j - 1];
let mut layer: Vec<Vec<f64>> = vec![Vec::new()];
for k in 1..=base_max - j {
let pk1 = &prev[k + 1]; let pk = &prev[k]; let denom = pk[k - 1];
if denom.abs() < 1e-300 {
return Err(Error::Singular);
}
let ratio = pk1[k] / denom;
let mut cur = Vec::with_capacity(k);
for l in 1..=k {
let prev_lm1 = if l == 1 { -1.0 } else { pk[l - 2] };
cur.push(pk1[l - 1] - prev_lm1 * ratio);
}
layer.push(cur);
}
layers.push(layer);
}
let mut table = vec![vec![0.0; jmax + 1]; kmax + 1];
let mut significant = vec![vec![false; jmax + 1]; kmax + 1];
let mut strong = vec![vec![false; jmax + 1]; kmax + 1];
for (k, row) in table.iter_mut().enumerate() {
for (j, cell) in row.iter_mut().enumerate() {
let lag = j + 1;
let r = if k == 0 {
acf_lag(&xc, lag)
} else {
let phi = &layers[lag][k];
let w: Vec<f64> = (k..n)
.map(|t| {
let mut v = xc[t];
for (l, p) in phi.iter().enumerate() {
v -= p * xc[t - l - 1];
}
v
})
.collect();
acf_lag(&w, lag)
};
*cell = r;
let se = 1.0 / ((n - k - lag) as f64).sqrt();
significant[k][j] = r.abs() > config.band * se;
strong[k][j] = r.abs() > (3.0 * config.band * se).min(0.2);
}
}
let mut order = None;
'outer: for total in 0..=kmax + jmax {
for p in 0..=total.min(kmax) {
let q = total - p;
if q > jmax {
continue;
}
let mut cells = 0usize;
let mut clean = true;
'tri: for d in 0..=kmax - p {
let start = (q + d).min(jmax + 1);
for cell in strong[p + d].iter().skip(start) {
cells += 1;
if *cell {
clean = false;
break 'tri;
}
}
}
let q_contrast = q == 0 || significant[p][q - 1];
let p_contrast = p == 0 || (0..=jmax).any(|j| strong[p - 1][j]);
if cells >= 3 && clean && q_contrast && p_contrast {
order = Some((p, q));
break 'outer;
}
}
}
Ok(EacfResult {
order,
table,
significant,
})
}
fn acf_lag(w: &[f64], lag: usize) -> f64 {
let n = w.len();
let mean = w.iter().sum::<f64>() / n as f64;
let mut num = 0.0;
let mut den = 0.0;
for t in 0..n {
let d = w[t] - mean;
den += d * d;
if t + lag < n {
num += d * (w[t + lag] - mean);
}
}
if den <= 0.0 {
0.0
} else {
num / den
}
}