blob: 819695008514c28fa70f0f68fa2713ab5988011f [file] [log] [blame]
//! A tiny, robust PRNG implementation.
//!
//! More specifically, it implements a single GOOD PRNG algorithm,
//! which is currently a permuted congruential generator. It has two
//! implementations, one that returns `u32` and one that returns
//! `u64`. It also has functions that return floats or integer
//! ranges. And that's it. What more do you need?
//!
//! For more info on PCG generators, see http://www.pcg-random.org/
//!
//! This was designed as a minimalist utility for video games. No
//! promises are made about its quality, and if you use it for
//! cryptography you will get what you deserve.
//!
//! Works with `#![no_std]`, has no global state, no dependencies
//! apart from some in the unit tests, and is generally neato.
#![forbid(unsafe_code)]
#![forbid(missing_docs)]
#![forbid(missing_debug_implementations)]
#![forbid(unused_results)]
#![no_std]
use core::ops::Range;
/// A PRNG producing a 32-bit output.
///
/// The current implementation is `PCG-XSH-RR`.
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Rand32 {
state: u64,
inc: u64,
}
impl Rand32 {
/// The default value for `increment`.
/// This is basically arbitrary, it comes from the
/// PCG reference C implementation:
/// https://github.com/imneme/pcg-c/blob/master/include/pcg_variants.h#L284
pub const DEFAULT_INC: u64 = 1442695040888963407;
/// This is the number that you have to Really Get Right.
///
/// The value used here is from the PCG C implementation:
/// https://github.com/imneme/pcg-c/blob/master/include/pcg_variants.h#L278
pub(crate) const MULTIPLIER: u64 = 6364136223846793005;
/// Creates a new PRNG with the given seed and a default increment.
pub fn new(seed: u64) -> Self {
Self::new_inc(seed, Self::DEFAULT_INC)
}
/// Creates a new PRNG. The two inputs, `seed` and `increment`,
/// determine what you get; `increment` basically selects which
/// sequence of all those possible the PRNG will produce, and the
/// `seed` selects where in that sequence you start.
///
/// Both are arbitrary; increment must be an odd number but this
/// handles that for you
pub fn new_inc(seed: u64, increment: u64) -> Self {
let mut rng = Self {
state: 0,
inc: increment.wrapping_shl(1) | 1,
};
// This initialization song-and-dance is a little odd,
// but seems to be just how things go.
let _ = rng.rand_u32();
rng.state = rng.state.wrapping_add(seed);
let _ = rng.rand_u32();
rng
}
/// Returns the internal state of the PRNG. This allows
/// you to save a PRNG and create a new one that will resume
/// from the same spot in the sequence.
pub fn state(&self) -> (u64, u64) {
(self.state, self.inc)
}
/// Creates a new PRNG from a saved state from `Rand32::state()`.
/// This is NOT quite the same as `new_inc()` because `new_inc()` does
/// a little extra setup work to initialize the state.
pub fn from_state(state: (u64, u64)) -> Self {
let (state, inc) = state;
Self { state, inc }
}
/// Produces a random `u32` in the range `[0, u32::MAX]`.
pub fn rand_u32(&mut self) -> u32 {
let oldstate: u64 = self.state;
self.state = oldstate
.wrapping_mul(Self::MULTIPLIER)
.wrapping_add(self.inc);
let xorshifted: u32 = (((oldstate >> 18) ^ oldstate) >> 27) as u32;
let rot: u32 = (oldstate >> 59) as u32;
xorshifted.rotate_right(rot)
}
/// Produces a random `i32` in the range `[i32::MIN, i32::MAX]`.
pub fn rand_i32(&mut self) -> i32 {
self.rand_u32() as i32
}
/// Produces a random `f32` in the range `[0.0, 1.0)`.
pub fn rand_float(&mut self) -> f32 {
// This impl was taken more or less from `rand`, see
// <https://docs.rs/rand/0.7.0/src/rand/distributions/float.rs.html#104-117>
// There MAY be better ways to do this, see:
// https://mumble.net/~campbell/2014/04/28/uniform-random-float
// https://mumble.net/~campbell/2014/04/28/random_real.c
// https://github.com/Lokathor/randomize/issues/34
const TOTAL_BITS: u32 = 32;
const PRECISION: u32 = core::f32::MANTISSA_DIGITS + 1;
const MANTISSA_SCALE: f32 = 1.0 / ((1u32 << PRECISION) as f32);
let mut u = self.rand_u32();
u >>= TOTAL_BITS - PRECISION;
u as f32 * MANTISSA_SCALE
}
/// Produces a random within the given bounds. Like any `Range`,
/// it includes the lower bound and excludes the upper one.
///
/// This should be faster than `Self::rand() % end + start`, but the
/// real advantage is it's more convenient. Requires that
/// `range.end <= range.start`.
pub fn rand_range(&mut self, range: Range<u32>) -> u32 {
// This is harder to do well than it looks, it seems. I don't
// trust Lokathor's implementation 'cause I don't understand
// it, so I went to numpy's, which points me to "Lemire's
// rejection algorithm": http://arxiv.org/abs/1805.10941
//
// Algorithms 3, 4 and 5 in that paper all seem fine modulo
// minor performance differences, so this is algorithm 5.
// It uses numpy's implementation, `buffered_bounded_lemire_uint32()`
debug_assert!(range.start < range.end);
let range_starting_from_zero = 0..(range.end - range.start);
let s: u32 = range_starting_from_zero.end;
let mut m: u64 = u64::from(self.rand_u32()) * u64::from(s);
let mut leftover: u32 = (m & 0xFFFF_FFFF) as u32;
if leftover < s {
// TODO: verify the wrapping_neg() here
let threshold: u32 = s.wrapping_neg() % s;
while leftover < threshold {
m = u64::from(self.rand_u32()).wrapping_mul(u64::from(s));
leftover = (m & 0xFFFF_FFFF) as u32;
}
}
(m >> 32) as u32 + range.start
}
}
/// A PRNG producing a 64-bit output.
///
/// The current implementation is `PCG-XSH-RR`.
// BUGGO: The recommended algorithm is PCG-XSL-RR?
// See https://github.com/imneme/pcg-c/blob/master/include/pcg_variants.h#L2405
// Not sure if it matters?
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Rand64 {
state: u128,
inc: u128,
}
impl Rand64 {
/// The default value for `increment`.
///
/// The value used here is from the PCG default C implementation: http://www.pcg-random.org/download.html
pub const DEFAULT_INC: u128 = 0x2FE0E169_FFBD06E3_5BC307BD_4D2F814F;
/// This is the number that you have to Really Get Right.
///
/// The value used here is from the PCG C implementation:
/// https://github.com/imneme/pcg-c/blob/master/include/pcg_variants.h#L288
pub(crate) const MULTIPLIER: u128 = 47026247687942121848144207491837523525;
/// Creates a new PRNG with the given seed and a default increment.
pub fn new(seed: u128) -> Self {
Self::new_inc(seed, Self::DEFAULT_INC)
}
/// Same as `Rand32::new_inc()`
pub fn new_inc(seed: u128, increment: u128) -> Self {
let mut rng = Self {
state: 0,
inc: increment.wrapping_shl(1) | 1,
};
let _ = rng.rand_u64();
rng.state = rng.state.wrapping_add(seed);
let _ = rng.rand_u64();
rng
}
/// Returns the internal state of the PRNG. This allows
/// you to save a PRNG and create a new one that will resume
/// from the same spot in the sequence.
pub fn state(&self) -> (u128, u128) {
(self.state, self.inc)
}
/// Creates a new PRNG from a saved state from `Rand32::state()`.
/// This is NOT quite the same as `new_inc()` because `new_inc()` does
/// a little extra setup work to initialize the state.
pub fn from_state(state: (u128, u128)) -> Self {
let (state, inc) = state;
Self { state, inc }
}
/// Produces a random `u64` in the range`[0, u64::MAX]`.
pub fn rand_u64(&mut self) -> u64 {
let oldstate: u128 = self.state;
self.state = oldstate
.wrapping_mul(Self::MULTIPLIER)
.wrapping_add(self.inc);
let xorshifted: u64 = (((oldstate >> 29) ^ oldstate) >> 58) as u64;
let rot: u32 = (oldstate >> 122) as u32;
xorshifted.rotate_right(rot)
}
/// Produces a random `i64` in the range `[i64::MIN, i64::MAX]`.
pub fn rand_i64(&mut self) -> i64 {
self.rand_u64() as i64
}
/// Produces a random `f64` in the range `[0.0, 1.0)`.
pub fn rand_float(&mut self) -> f64 {
const TOTAL_BITS: u32 = 64;
const PRECISION: u32 = core::f64::MANTISSA_DIGITS + 1;
const MANTISSA_SCALE: f64 = 1.0 / ((1u64 << PRECISION) as f64);
let mut u = self.rand_u64();
u >>= TOTAL_BITS - PRECISION;
u as f64 * MANTISSA_SCALE
}
/// Produces a random within the given bounds. Like any `Range`,
/// it includes the lower bound and excludes the upper one.
///
/// This should be faster than `Self::rand() % end + start`, but the
/// real advantage is it's more convenient. Requires that
/// `range.end <= range.start`.
pub fn rand_range(&mut self, range: Range<u64>) -> u64 {
// Same as `Rand32::rand_range()`
debug_assert!(range.start < range.end);
let range_starting_from_zero = 0..(range.end - range.start);
let s: u64 = range_starting_from_zero.end;
let mut m: u128 = u128::from(self.rand_u64()) * u128::from(s);
let mut leftover: u64 = (m & 0xFFFFFFFF_FFFFFFFF) as u64;
if leftover < s {
// TODO: Verify the wrapping_negate() here
let threshold: u64 = s.wrapping_neg() % s;
while leftover < threshold {
m = u128::from(self.rand_u64()) * u128::from(s);
leftover = (m & 0xFFFFFFFF_FFFFFFFF) as u64;
}
}
(m.wrapping_shr(64)) as u64 + range.start
}
}
#[cfg(test)]
mod tests {
use super::*;
use randomize::{self, PCG32, PCG64};
#[test]
fn test_rand32_vs_randomize() {
// Generate some random numbers and validate them against
// a known-good generator.
{
let seed = 54321;
let mut r1 = Rand32::new(seed);
let mut r2 = PCG32::seed(seed, Rand32::DEFAULT_INC);
for _ in 0..1000 {
assert_eq!(r1.rand_u32(), r2.next_u32());
assert_eq!(r1.rand_i32(), r2.next_u32() as i32);
}
}
{
let seed = 3141592653;
let inc = 0xDEADBEEF;
let mut r1 = Rand32::new_inc(seed, inc);
let mut r2 = PCG32::seed(seed, inc);
for _ in 0..1000 {
assert_eq!(r1.rand_u32(), r2.next_u32());
assert_eq!(r1.rand_i32(), r2.next_u32() as i32);
}
}
}
#[test]
fn test_rand64_vs_randomize() {
// Generate some random numbers and validate them against
// a known-good generator.
{
let seed = 54321;
let mut r1 = Rand64::new(seed);
let mut r2 = PCG64::seed(seed, Rand64::DEFAULT_INC);
for _ in 0..1000 {
assert_eq!(r1.rand_u64(), r2.next_u64());
assert_eq!(r1.rand_i64(), r2.next_u64() as i64);
}
}
{
let seed = 3141592653;
let inc = 0xDEADBEEF;
let mut r1 = Rand64::new_inc(seed, inc);
let mut r2 = PCG64::seed(seed, inc);
for _ in 0..1000 {
assert_eq!(r1.rand_u64(), r2.next_u64());
assert_eq!(r1.rand_i64(), r2.next_u64() as i64);
}
}
}
#[test]
fn test_float32() {
{
let seed = 2718281828;
let mut r1 = Rand32::new(seed);
let mut r2 = PCG32::seed(seed, Rand32::DEFAULT_INC);
for _ in 0..1000 {
// First just make sure they both work with randomize's
// f32 conversion function -- sanity checks.
let i1 = r1.rand_u32();
let i2 = r2.next_u32();
assert_eq!(i1, i2);
let f1 = randomize::f32_half_open_right(i1);
let f2 = randomize::f32_half_open_right(i2);
// We can directly compare floats 'cause we do no math, it's
// literally the same bitwise algorithm with the same inputs.
assert_eq!(f1, f2);
// Make sure result is in [0.0, 1.0)
assert!(f1 >= 0.0);
assert!(f1 < 1.0);
}
// At least make sure our float's from rand_float() are in the valid range.
for _ in 0..1000 {
let f1 = r1.rand_float();
assert!(f1 >= 0.0);
assert!(f1 < 1.0);
}
/*
TODO: Randomize changed its int-to-float conversion functions and now they don't
match ours.
for _ in 0..1000 {
// Now make sure our own float conversion function works.
let f1 = r1.rand_float();
//let f2 = randomize::f32_half_open_right(r2.next_u32());
let f2 = randomize::f32_open(r2.next_u32());
assert_eq!(f1, f2);
assert!(f1 >= 0.0);
assert!(f1 < 1.0);
}
*/
}
}
#[test]
fn test_float64() {
{
let seed = 2718281828;
let mut r1 = Rand64::new(seed);
let mut r2 = PCG64::seed(seed, Rand64::DEFAULT_INC);
for _ in 0..1000 {
// First just make sure they both work with randomize's
// f64 conversion function -- sanity checks.
let i1 = r1.rand_u64();
let i2 = r2.next_u64();
assert_eq!(i1, i2);
let f1 = randomize::f64_half_open_right(i1);
let f2 = randomize::f64_half_open_right(i2);
// We can directly compare floats 'cause we do no math, it's
// literally the same bitwise algorithm with the same inputs.
assert_eq!(f1, f2);
// Make sure result is in [0.0, 1.0)
assert!(f1 >= 0.0);
assert!(f1 < 1.0);
}
// At least make sure our float's from rand_float() are in the valid range.
for _ in 0..1000 {
let f1 = r1.rand_float();
assert!(f1 >= 0.0);
assert!(f1 < 1.0);
}
/*
TODO: Randomize changed its int-to-float conversion functions and now they don't
match ours.
for _ in 0..1000 {
// Now make sure our own float conversion function works.
let f1 = r1.rand_float();
//let f2 = randomize::f32_half_open_right(r2.next_u32());
let f2 = randomize::f32_open(r2.next_u32());
assert_eq!(f1, f2);
assert!(f1 >= 0.0);
assert!(f1 < 1.0);
}
*/
}
}
#[test]
fn test_randrange32() {
// Make sure ranges are valid and in the given range
let seed = 2342_3141;
let mut r1 = Rand32::new(seed);
for _ in 0..1000 {
// Generate our bounds at random
let a = r1.rand_u32();
let b = r1.rand_u32();
if a == b {
continue;
}
let (low, high) = if a < b { (a, b) } else { (b, a) };
// Get a number in that range
let in_range = r1.rand_range(low..high);
assert!(in_range >= low);
assert!(in_range < high);
}
}
#[test]
fn test_randrange64() {
// Make sure ranges are valid and in the given range
let seed = 2342_2718;
let mut r1 = Rand64::new(seed);
for _ in 0..1000 {
// Generate our bounds at random
let a = r1.rand_u64();
let b = r1.rand_u64();
if a == b {
continue;
}
let (low, high) = if a < b { (a, b) } else { (b, a) };
// Get a number in that range
let in_range = r1.rand_range(low..high);
assert!(in_range >= low);
assert!(in_range < high);
}
}
#[test]
fn test_rand32_vs_rand() {
use rand_core::RngCore;
use rand_pcg;
{
let seed = 54321;
let mut r1 = Rand32::new(seed);
let mut r2 = rand_pcg::Pcg32::new(seed, Rand32::DEFAULT_INC);
for _ in 0..1000 {
assert_eq!(r1.rand_u32(), r2.next_u32());
}
}
{
let seed = 3141592653;
let inc = 0xDEADBEEF;
let mut r1 = Rand32::new_inc(seed, inc);
let mut r2 = rand_pcg::Pcg32::new(seed, inc);
for _ in 0..1000 {
assert_eq!(r1.rand_u32(), r2.next_u32());
}
}
}
// This doesn't work 'cause for 64-bit output `rand` uses
// PCG-XSL-RR
// and we use
// PCG-XSH-RR
/*
#[test]
fn test_rand64_vs_rand() {
use rand_pcg;
use rand_core::RngCore;
{
let seed = 54321;
let mut r1 = Rand64::new(seed);
let mut r2 = rand_pcg::Pcg64::new(seed, Rand64::DEFAULT_INC);
for _ in 0..1000 {
assert_eq!(r1.rand(), r2.next_u64());
}
}
{
let seed = 3141592653;
let inc = 0xDEADBEEF;
let mut r1 = Rand64::new_inc(seed, inc);
let mut r2 = rand_pcg::Pcg64::new(seed, inc);
for _ in 0..1000 {
assert_eq!(r1.rand(), r2.next_u64());
}
}
}
*/
// Test vs. random-fast-rng, which I will call rfr
// rfr's float conversion uses yet a different algorithm
// than ours, so we can't really check that.
#[test]
fn test_rand32_vs_rfr() {
use random_fast_rng as rfr;
use rfr::Random;
{
let seed = 54321;
let mut r1 = Rand32::new(seed);
let mut r2 = rfr::FastRng::seed(seed, Rand32::DEFAULT_INC);
for _ in 0..1000 {
assert_eq!(r1.rand_u32(), r2.get_u32());
}
}
{
let seed = 3141592653;
let inc = 0xDEADBEEF;
let mut r1 = Rand32::new_inc(seed, inc);
let mut r2 = rfr::FastRng::seed(seed, inc);
for _ in 0..1000 {
assert_eq!(r1.rand_u32(), r2.get_u32());
}
}
}
/// Make sure that saving a RNG state and restoring
/// it works.
/// See https://todo.sr.ht/~icefox/oorandom/1
#[test]
fn test_save_restore() {
{
let seed = 54321;
let mut r1 = Rand32::new(seed);
let s1 = r1.state();
let mut r2 = Rand32::from_state(s1);
assert_eq!(r1, r2);
for _ in 0..1000 {
assert_eq!(r1.rand_u32(), r2.rand_u32());
}
}
{
let seed = 3141592653;
let inc = 0xDEADBEEF;
let mut r1 = Rand32::new_inc(seed, inc);
let s1 = r1.state();
let mut r2 = Rand32::from_state(s1);
assert_eq!(r1, r2);
for _ in 0..1000 {
assert_eq!(r1.rand_u32(), r2.rand_u32());
}
}
{
let seed = 54321;
let mut r1 = Rand64::new(seed);
let s1 = r1.state();
let mut r2 = Rand64::from_state(s1);
assert_eq!(r1, r2);
for _ in 0..1000 {
assert_eq!(r1.rand_u64(), r2.rand_u64());
}
}
{
let seed = 3141592653;
let inc = 0xDEADBEEF;
let mut r1 = Rand64::new_inc(seed, inc);
let s1 = r1.state();
let mut r2 = Rand64::from_state(s1);
assert_eq!(r1, r2);
for _ in 0..1000 {
assert_eq!(r1.rand_u64(), r2.rand_u64());
}
}
}
}