use crate::Error;
pub fn nist_freq_monobit(data: impl Iterator<Item = bool>) -> Result<f32, Error> {
let mut v = 0isize;
let mut n = 0usize;
for d in data {
n += 1;
match d {
true => v += 1,
false => v -= 1,
}
}
if n < 100 {
return Err(Error::InsufficientSampleSize(n));
}
let s = v.abs() as f32 / libm::sqrtf(n as f32);
let p = libm::erfcf(s / libm::sqrtf(2.0));
if p < 0.01 {
return Err(Error::BadPValue(p));
}
Ok(p)
}
pub fn nist_freq_block(
mut data: impl Iterator<Item = bool>,
block_len: usize,
) -> Result<f32, Error> {
let mut num_blocks = 0;
let mut x2_partial = 0.0;
loop {
let block = (&mut data).take(block_len);
let mut block_n = 0;
let mut block_v = 0;
for v in block {
block_n += 1;
if v {
block_v += 1;
}
}
if block_n < block_len {
break;
}
let block_p = (block_v as f32 / block_n as f32) - 0.5;
let block_x2 = libm::powf(block_p, 2.0);
x2_partial += block_x2;
num_blocks += 1;
}
let x2 = 4f32 * block_len as f32 * x2_partial;
let p = 1.0 - nist_igamma(num_blocks as f32 / 2.0, x2 / 2.0);
if p < 0.01 {
return Err(Error::BadPValue(p));
}
Ok(p)
}
fn nist_igamma(a: f32, x: f32) -> f32 {
let epsilon = 1e-8;
let mut sum = 1.0f32;
let mut term = 1.0f32;
let mut n = 0;
let max_iterations = 100;
while libm::fabsf(term) >= epsilon * libm::fabsf(sum) && n < max_iterations {
term *= x / (a + n as f32 + 1.0);
sum += term;
n += 1;
}
let gamma = sum * libm::powf(x, a) * libm::expf(-x) / libm::tgammaf(a + 1.0);
gamma
}
#[cfg(test)]
mod tests {
use assert_approx_eq::assert_approx_eq;
use bitvec::prelude::*;
use rand::{rngs::OsRng, RngCore};
use super::*;
use crate::helpers::BitIter;
#[test]
fn nist_monobit_ok() {
let mut rng = OsRng {};
let mut buff = [0u8; 100];
rng.fill_bytes(&mut buff);
nist_freq_monobit(BitIter::new(&buff)).expect("Monobit test failed");
}
#[test]
fn nist_monobit_spec() {
let buff = bits![
1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0,
0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0,
1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1,
0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0
];
let p = nist_freq_monobit(buff.iter().by_vals()).expect("Monobit test failed");
assert_approx_eq!(p, 0.109599);
}
#[test]
fn nist_monobit_fail() {
nist_freq_monobit(BitIter::from([0xffu8; 128])).expect_err("Monobit p > threshold");
nist_freq_monobit(BitIter::from([0x00u8; 128])).expect_err("Monobit p > threshold");
}
#[test]
fn nist_block_ok() {
let mut rng = OsRng {};
let mut buff = [0u8; 100];
rng.fill_bytes(&mut buff);
nist_freq_block(BitIter::new(&buff), 10).expect("Monobit test failed");
}
#[test]
fn nist_block_ex() {
let buff = [0b01100110, 0b00000010];
let data = BitIter::new(&buff).take(10);
let p = nist_freq_block(data, 3).expect("Block frequency test failed");
assert_approx_eq!(p, 0.801252);
}
#[test]
fn nist_block_spec() {
let buff = bits![
1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0,
0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0,
1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1,
0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0
];
let p = nist_freq_block(buff.iter().by_vals(), 10).expect("Block frequency test failed");
assert_approx_eq!(p, 0.706438);
}
#[test]
fn nist_block_fail() {
let buff = bits![1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
nist_freq_block(buff.iter().by_vals(), 10).expect_err("Block frequency test failed");
}
#[test]
fn igamma() {
let tests = &[
(1.0, 1.0, 0.6321205588),
(1.0, 2.0, 0.8646647167),
];
for (a, x, g) in tests {
let v = nist_igamma(*a, *x);
assert_approx_eq!(v, *g, 1e-6f32);
}
}
}