use std::cell::Cell;
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::{SystemTime, UNIX_EPOCH};
const GOLDEN_GAMMA: u64 = 0x9E37_79B9_7F4A_7C15;
thread_local! {
static STATE: Cell<u64> = Cell::new(seed());
}
static SEED_COUNTER: AtomicU64 = AtomicU64::new(0);
fn seed() -> u64 {
let nanos = SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_nanos() as u64)
.unwrap_or(0);
let counter = SEED_COUNTER.fetch_add(1, Ordering::Relaxed);
mix(nanos ^ counter.wrapping_mul(GOLDEN_GAMMA))
}
fn mix(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)
}
fn next_u64() -> u64 {
STATE.with(|s| {
let z = s.get().wrapping_add(GOLDEN_GAMMA);
s.set(z);
mix(z)
})
}
pub(crate) fn below(n: u32) -> u32 {
debug_assert!(n > 0);
let n = n as u64;
let mut m = (next_u64() as u128).wrapping_mul(n as u128);
let mut l = m as u64; if l < n {
let threshold = n.wrapping_neg() % n; while l < threshold {
m = (next_u64() as u128).wrapping_mul(n as u128);
l = m as u64;
}
}
(m >> 64) as u32
}
pub(crate) fn two_distinct_below(n: u32) -> (u32, u32) {
debug_assert!(n >= 2);
let i = below(n);
let mut j = below(n - 1);
if j >= i {
j += 1;
}
(i, j)
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashSet;
#[test]
fn below_stays_in_range() {
for n in [1u32, 2, 3, 7, 10, 1000] {
for _ in 0..10_000 {
assert!(below(n) < n, "below({n}) must be < {n}");
}
}
}
#[test]
fn two_distinct_are_distinct_and_in_range() {
for n in [2u32, 3, 5, 10] {
for _ in 0..10_000 {
let (i, j) = two_distinct_below(n);
assert!(i < n && j < n, "both indices in [0,{n})");
assert_ne!(i, j, "the two picks must differ");
}
}
}
#[test]
fn below_covers_the_range_roughly_uniformly() {
let n = 8u32;
let mut counts = [0u32; 8];
let trials = 80_000;
for _ in 0..trials {
counts[below(n) as usize] += 1;
}
let expected = trials / n;
for (b, &c) in counts.iter().enumerate() {
assert!(c > 0, "bucket {b} was never hit");
assert!(
c < expected * 2 && c > expected / 2,
"bucket {b} count {c} is far from the expected {expected}"
);
}
}
#[test]
fn two_distinct_covers_both_orders() {
let mut seen = HashSet::new();
for _ in 0..1000 {
seen.insert(two_distinct_below(2));
}
assert!(seen.contains(&(0, 1)) && seen.contains(&(1, 0)));
}
}