#![cfg_attr(docsrs, doc = include_str!("../README.md"))]
#![cfg_attr(not(docsrs), doc = "# Rapidrand")]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(docsrs, doc(auto_cfg(hide(docsrs))))]
#![no_std]
#![deny(missing_docs)]
#![deny(unused_must_use)]
#[cfg(feature = "rand")]
use rand_core::{SeedableRng, TryRng, utils::fill_bytes_via_next_word};
const RAPID_SECRET_ADD: u64 = 0x2d358dccaa6c78a5;
const RAPID_SECRET_XOR: u64 = 0x8bb84b93962eacc9;
#[inline(always)]
#[must_use]
const fn rapid_mix(a: u64, b: u64) -> u64 {
let r = (a as u128).wrapping_mul(b as u128);
(r as u64) ^ (r >> 64) as u64
}
#[inline(always)]
#[must_use]
pub const fn rapidrng(state: &mut u64) -> u64 {
*state = state.wrapping_add(RAPID_SECRET_ADD);
rapid_mix(*state, *state ^ RAPID_SECRET_XOR)
}
#[inline(always)]
#[must_use]
pub const fn rapidrng_single(state: &mut u64) -> u64 {
*state = state.wrapping_add(RAPID_SECRET_ADD);
rapid_mix(*state, *state ^ RAPID_SECRET_ADD)
}
#[cfg(feature = "rand")]
#[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub struct RapidRng {
state: u64,
}
#[cfg(feature = "rand")]
impl TryRng for RapidRng {
type Error = core::convert::Infallible;
#[inline]
fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
Ok(rapidrng(&mut self.state) as u32)
}
#[inline]
fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
Ok(rapidrng(&mut self.state))
}
#[inline]
fn try_fill_bytes(&mut self, dst: &mut [u8]) -> Result<(), Self::Error> {
fill_bytes_via_next_word(dst, || self.try_next_u64())
}
}
#[cfg(feature = "rand")]
impl SeedableRng for RapidRng {
type Seed = [u8; 8];
#[inline]
fn from_seed(seed: Self::Seed) -> Self {
Self {
state: u64::from_le_bytes(seed),
}
}
#[inline]
fn seed_from_u64(mut state: u64) -> Self {
Self {
state: rapidrng(&mut state),
}
}
}
#[cfg(test)]
mod tests {
extern crate std;
#[cfg(feature = "rand")]
use rand_core::Rng;
use super::*;
#[cfg(feature = "rand")]
#[test]
fn test_rapidrng() {
let mut rng = RapidRng::seed_from_u64(0);
let x = rng.next_u64();
let y = rng.next_u64();
assert_ne!(x, 0);
assert_ne!(x, y);
}
#[test]
fn bit_flip_trial_fast() {
let cycles = 100_000;
let mut seen = std::collections::HashSet::with_capacity(cycles);
let mut flips = std::vec::Vec::with_capacity(cycles);
let mut prev = 0;
for _ in 0..cycles {
let next = rapidrng(&mut prev);
let xor = prev ^ next;
let flipped = xor.count_ones() as u64;
assert!(
xor.count_ones() >= 10,
"Flipping bit changed only {} bits",
flipped
);
flips.push(flipped);
assert!(!seen.contains(&next), "rapidrng produced a duplicate value");
seen.insert(next);
prev = next;
}
let average = flips.iter().sum::<u64>() as f64 / flips.len() as f64;
assert!(
average > 31.95 && average < 32.05,
"Did not flip an average of half the bits. average: {}, expected: 32.0",
average
);
}
#[cfg(feature = "rand")]
#[test]
fn bit_flip_trial() {
use rand_core::Rng;
let cycles = 100_000;
let mut seen = std::collections::HashSet::with_capacity(cycles);
let mut flips = std::vec::Vec::with_capacity(cycles);
let mut rng = RapidRng::seed_from_u64(0);
let mut prev = 0;
for _ in 0..cycles {
let next = rng.next_u64();
let xor = prev ^ next;
let flipped = xor.count_ones() as u64;
assert!(
xor.count_ones() >= 10,
"Flipping bit changed only {} bits",
flipped
);
flips.push(flipped);
assert!(!seen.contains(&next), "RapidRng produced a duplicate value");
seen.insert(next);
prev = next;
}
let average = flips.iter().sum::<u64>() as f64 / flips.len() as f64;
assert!(
average > 31.95 && average < 32.05,
"Did not flip an average of half the bits. average: {}, expected: 32.0",
average
);
}
#[cfg(feature = "rand")]
#[test]
fn test_seedable() {
let mut base = RapidRng::seed_from_u64(0x1);
let mut same = RapidRng::seed_from_u64(0x1);
assert_eq!(base.next_u64(), same.next_u64());
assert_eq!(base.next_u64(), same.next_u64());
assert_eq!(base.next_u64(), same.next_u64());
let mut base = RapidRng::seed_from_u64(0x1);
let mut diff = RapidRng::seed_from_u64(0x2);
assert_ne!(base.next_u64(), diff.next_u64());
}
}