use crate::{math::igamc, result::TestResult};
pub fn overlapping_template(bits: &[u8], m: usize) -> TestResult {
if m != 9 {
return TestResult::insufficient(
"nist::overlapping_template",
"only m = 9 is supported; pi table is valid for m = 9, M = 1032 only",
);
}
let n = bits.len();
let big_m = 1032_usize; let num_blocks = n / big_m;
if num_blocks < 5 {
return TestResult::insufficient(
"nist::overlapping_template",
"n too small — need ≥ 5 blocks",
);
}
let k = 5usize;
let pi: [f64; 6] = [0.364091, 0.185659, 0.139381, 0.100571, 0.070432, 0.139865];
let template: Vec<u8> = vec![1u8; m];
let mut nu = [0usize; 6];
for block in bits.chunks_exact(big_m).take(num_blocks) {
let w = count_overlapping(block, &template);
let idx = w.min(k);
nu[idx] += 1;
}
let chi_sq: f64 = nu
.iter()
.zip(pi.iter())
.map(|(&count, &p)| {
let exp = num_blocks as f64 * p;
(count as f64 - exp).powi(2) / exp
})
.sum();
let p_value = igamc(k as f64 / 2.0, chi_sq / 2.0);
TestResult::with_note(
"nist::overlapping_template",
p_value,
format!("n={n}, m={m}, N={num_blocks}, ν={nu:?}, χ²={chi_sq:.4}"),
)
}
fn count_overlapping(block: &[u8], template: &[u8]) -> usize {
let m = template.len();
block.windows(m).filter(|&w| w == template).count()
}