pub mod philox;
use philox::philox4x32_10;
pub const STREAM_TAG_RESAMPLE: u64 = 0x5245_5341_4D50_4C45;
#[inline]
fn splitmix64(mut z: u64) -> u64 {
z = (z ^ (z >> 30)).wrapping_mul(0xbf58_476d_1ce4_e5b9);
z = (z ^ (z >> 27)).wrapping_mul(0x94d0_49bb_1331_11eb);
z ^ (z >> 31)
}
#[derive(Debug, Clone)]
pub struct CommonStatsRng {
key: [u32; 2],
draw_lo: u32,
draw_hi: u32,
block: u64, buf: [u32; 4],
buf_pos: usize, }
impl CommonStatsRng {
pub fn new(seed: u64, draw_id: u64) -> Self {
let k = splitmix64(seed);
let eff = draw_id ^ STREAM_TAG_RESAMPLE;
Self {
key: [k as u32, (k >> 32) as u32],
draw_lo: eff as u32,
draw_hi: (eff >> 32) as u32,
block: 0,
buf: [0; 4],
buf_pos: 4,
}
}
#[inline]
pub fn next_u32(&mut self) -> u32 {
if self.buf_pos == 4 {
let b = self.block;
self.buf = philox4x32_10([b as u32, (b >> 32) as u32, self.draw_lo, self.draw_hi], self.key);
self.block = self.block.wrapping_add(1);
self.buf_pos = 0;
}
let w = self.buf[self.buf_pos];
self.buf_pos += 1;
w
}
#[inline]
pub fn bounded(&mut self, n: u32) -> u32 {
debug_assert!(n >= 1, "bounded: n must be >= 1");
let n64 = n as u64;
let mut m = self.next_u32() as u64 * n64;
let mut lo = m as u32; if lo < n {
let t = (1u64 << 32).wrapping_rem(n64) as u32; while lo < t {
m = self.next_u32() as u64 * n64;
lo = m as u32;
}
}
(m >> 32) as u32
}
}
#[cfg(feature = "dist")]
impl CommonStatsRng {
pub fn uniform(&mut self) -> f64 {
(self.next_u32() as f64 + 0.5) / 4_294_967_296.0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn same_key_reproduces_words() {
let mut a = CommonStatsRng::new(42, 7);
let mut b = CommonStatsRng::new(42, 7);
for _ in 0..1000 {
assert_eq!(a.next_u32(), b.next_u32());
}
}
#[test]
fn reproduces_across_block_boundaries() {
let words: Vec<u32> = {
let mut r = CommonStatsRng::new(1, 0);
(0..37).map(|_| r.next_u32()).collect()
};
let mut r2 = CommonStatsRng::new(1, 0);
for &w in &words {
assert_eq!(r2.next_u32(), w);
}
}
#[test]
fn different_draw_ids_diverge() {
let mut a = CommonStatsRng::new(42, 0);
let mut b = CommonStatsRng::new(42, 1);
let mut diff = 0usize;
for _ in 0..100 {
if a.next_u32() != b.next_u32() {
diff += 1;
}
}
assert!(diff > 90, "different draw_ids must give independent streams");
}
#[test]
fn different_seeds_diverge() {
let mut a = CommonStatsRng::new(0, 5);
let mut b = CommonStatsRng::new(1, 5);
let mut diff = 0usize;
for _ in 0..100 {
if a.next_u32() != b.next_u32() {
diff += 1;
}
}
assert!(diff > 90, "different seeds must give independent streams");
}
#[test]
fn bounded_one_is_always_zero() {
let mut r = CommonStatsRng::new(99, 3);
for _ in 0..1000 {
assert_eq!(r.bounded(1), 0);
}
}
#[test]
fn bounded_stays_in_range() {
let mut r = CommonStatsRng::new(7, 11);
for &n in &[2u32, 3, 7, 10, 100, 1000] {
for _ in 0..5000 {
assert!(r.bounded(n) < n, "bounded({n}) out of range");
}
}
}
#[test]
fn bounded_is_approximately_uniform() {
let n = 7u32;
let draws = 700_000usize;
let mut counts = [0u64; 7];
let mut r = CommonStatsRng::new(2024, 1);
for _ in 0..draws {
counts[r.bounded(n) as usize] += 1;
}
let expected = draws as f64 / n as f64;
for (i, &c) in counts.iter().enumerate() {
let rel = (c as f64 - expected).abs() / expected;
assert!(rel < 0.02, "bucket {i} count {c} deviates {rel:.4} from uniform");
}
}
#[cfg(feature = "dist")]
#[test]
fn uniform_in_open_unit_interval() {
let mut rng = CommonStatsRng::new(42, 0);
for _ in 0..100_000 {
let u = rng.uniform();
assert!(u > 0.0 && u < 1.0, "uniform out of (0,1): {u}");
}
let mut rng = CommonStatsRng::new(7, 1);
let n = 200_000;
let mean: f64 = (0..n).map(|_| rng.uniform()).sum::<f64>() / n as f64;
assert!((mean - 0.5).abs() < 1e-2, "mean {mean} far from 0.5");
}
}