ax-rnd 0.1.3

AxRng is a fast, small random number generator (rng) library and CLI tool written in Rust.
Documentation
use ax_rnd::{AxRng, rng};

const SAMPLES: usize = 200_000;

#[test]
fn bit_histogram_uniform() {
    let mut rnd = rng(42);
    let mut counts = [0u64; 64];

    for _ in 0..SAMPLES {
        let v = rnd.next_u64();
        for i in 0..64 {
            counts[i] += (v >> i) & 1;
        }
    }

    let expected = SAMPLES as f64 / 2.0;
    let tolerance = expected * 0.015;

    for i in 0..64 {
        let diff = (counts[i] as f64 - expected).abs();
        assert!(
            diff < tolerance,
            "bit {} count {} deviates too far from expected {}",
            i,
            counts[i],
            expected
        );
    }
}

#[test]
fn avalanche_effect() {
    let mut rnd = rng(0xAABBCCDDEEFF0011);

    let mut total_flips = 0u64;
    let trials = 50_000;

    for _ in 0..trials {
        let base = rnd.next_u64();
        let flipped = base ^ 1;

        let out_base = {
            let mut r = AxRng::new(base);
            r.next_u64()
        };
        let out_flipped = {
            let mut r = AxRng::new(flipped);
            r.next_u64()
        };

        total_flips += (out_base ^ out_flipped).count_ones() as u64;
    }

    let avg = total_flips as f64 / trials as f64;
    let expected = 32.0;
    let diff = (avg - expected).abs();

    assert!(
        diff < 2.0,
        "avalanche average {:.2} deviates too far from expected {} (diff={:.2})",
        avg,
        expected,
        diff
    );
}

#[test]
fn stream_correlation_near_zero() {
    let mut a = rng(123456);
    let mut b = rng(654321);

    let n = 100_000usize;
    let mut greater = 0usize;
    let mut equal = 0usize;

    for _ in 0..n {
        let av = a.next_u64();
        let bv = b.next_u64();

        if av > bv {
            greater += 1;
        } else if av == bv {
            equal += 1;
        }
    }

    let ratio = greater as f64 / n as f64;

    assert!(
        (ratio - 0.5).abs() < 0.01,
        "stream win ratio {:.4} deviates from 0.5 (equal collisions={})",
        ratio,
        equal
    );
}

#[test]
fn low_byte_uniformity() {
    let mut rnd = rng(777);
    let mut low_counts = [0u64; 256];
    let mut high_counts = [0u64; 256];

    let samples = 256_000usize;

    for _ in 0..samples {
        let v = rnd.next_u64();
        low_counts[(v & 0xFF) as usize] += 1;
        high_counts[((v >> 56) & 0xFF) as usize] += 1;
    }

    let expected = samples as f64 / 256.0;
    let tolerance = expected * 0.15;

    for i in 0..256 {
        let low_diff = (low_counts[i] as f64 - expected).abs();
        let high_diff = (high_counts[i] as f64 - expected).abs();

        assert!(
            low_diff < tolerance,
            "low byte {} count {} deviates from expected {}",
            i,
            low_counts[i],
            expected
        );
        assert!(
            high_diff < tolerance,
            "high byte {} count {} deviates from expected {}",
            i,
            high_counts[i],
            expected
        );
    }
}

#[test]
#[cfg(feature = "std")]
fn practrand_stream_quality() {
    use std::fs::File;
    use std::io::Write;

    let mut rnd = rng(0xDEADBEEFCAFEBABE);
    let bytes = 8 * 1024 * 1024usize;

    let mut buf = vec![0u8; bytes];
    rnd.fill_bytes(&mut buf);

    let path = std::env::temp_dir().join("axrnd_practrand_8mb.bin");
    let mut file = File::create(&path).expect("create temp file");
    file.write_all(&buf).expect("write temp file");

    assert!(
        path.metadata().unwrap().len() as usize == bytes,
        "PractRand prep file size mismatch"
    );

    let ones = buf.iter().map(|b| b.count_ones() as u64).sum::<u64>();
    let total_bits = (bytes * 8) as u64;
    let ratio = ones as f64 / total_bits as f64;

    assert!(
        (ratio - 0.5).abs() < 0.001,
        "overall bit balance {:.4} too far from 0.5",
        ratio
    );

    eprintln!(
        "PractRand 8MB sample written to: {} (bit balance={:.4})",
        path.display(),
        ratio
    );
}