use crate::{math::igamc, result::TestResult, rng::Rng};
const N_TRIALS: usize = 100_000;
const N_CELLS: usize = 43;
const SDATA: [f64; N_CELLS] = [
0.00002103, 0.00005779, 0.00017554, 0.00046732, 0.00110783, 0.00236784, 0.00460944, 0.00824116,
0.01362781, 0.02096849, 0.03017612, 0.04080197, 0.05204203, 0.06283828, 0.07205637, 0.07869451,
0.08206755, 0.08191935, 0.07844008, 0.07219412, 0.06398679, 0.05470931, 0.04519852, 0.03613661,
0.02800028, 0.02105567, 0.01538652, 0.01094020, 0.00757796, 0.00511956, 0.00337726, 0.00217787,
0.00137439, 0.00084970, 0.00051518, 0.00030666, 0.00017939, 0.00010324, 0.00005851, 0.00003269,
0.00001803, 0.00000982, 0.00001121,
];
pub fn squeeze(rng: &mut impl Rng) -> TestResult {
let mut counts = [0u32; N_CELLS];
for _ in 0..N_TRIALS {
let mut k: i64 = 2_147_483_647; let mut j: usize = 0;
while k != 1 && j < 48 {
let u = (rng.next_u32() as f64 + 0.5) / 4_294_967_296.0;
k = (k as f64 * u).ceil() as i64;
j += 1;
}
let j = j.max(6);
let idx = (j - 6).min(N_CELLS - 1);
counts[idx] += 1;
}
let n = N_TRIALS as f64;
let chi_sq: f64 = counts
.iter()
.zip(SDATA.iter())
.filter(|(_, &p)| p * n >= 5.0)
.map(|(&c, &p)| (c as f64 - n * p).powi(2) / (n * p))
.sum();
let df = counts
.iter()
.zip(SDATA.iter())
.filter(|(_, &p)| p * n >= 5.0)
.count()
.saturating_sub(1);
let p_value = igamc(df as f64 / 2.0, chi_sq / 2.0);
TestResult::with_note(
"diehard::squeeze",
p_value,
format!("trials={N_TRIALS}, cells={N_CELLS}, df={df}, χ²={chi_sq:.4}"),
)
}