aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakub Kotur <qtr@google.com>2021-03-16 19:18:10 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2021-03-16 19:18:10 +0000
commitb02d3be986002ad26ecb917611c2ad7a248d717d (patch)
tree6a459df0d175f36d0969c5ea6077ef65fe9f8ae2
parentbd13213149d538f4448011b3dbdb511b164d9846 (diff)
parent9e2f87b95499a1650759d8055fdd4db4e93771aa (diff)
downloadoorandom-b02d3be986002ad26ecb917611c2ad7a248d717d.tar.gz
Initial import of oorandom-11.1.3. am: 2ea66b38cc am: 9e2f87b954
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/oorandom/+/1621405 Change-Id: Ia3d3e88c8c6cf146a72e39f7438e56f4419dd892
-rw-r--r--Cargo.toml38
-rw-r--r--Cargo.toml.orig22
-rw-r--r--README.md196
-rw-r--r--src/lib.rs586
4 files changed, 842 insertions, 0 deletions
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..48044bb
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,38 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies
+#
+# If you believe there's an error in this file please file an
+# issue against the rust-lang/cargo repository. If you're
+# editing this file be aware that the upstream Cargo.toml
+# will likely look very different (and much more reasonable)
+
+[package]
+edition = "2018"
+name = "oorandom"
+version = "11.1.3"
+authors = ["Simon Heath <icefox@dreamquest.io>"]
+description = "A tiny, robust PRNG implementation."
+readme = "README.md"
+keywords = ["rng", "prng", "random", "pcg"]
+categories = ["algorithms", "embedded", "no-std"]
+license = "MIT"
+repository = "https://sr.ht/~icefox/oorandom/"
+
+[dependencies]
+[dev-dependencies.rand_core]
+version = "0.5"
+
+[dev-dependencies.rand_pcg]
+version = "0.2"
+
+[dev-dependencies.random-fast-rng]
+version = "0.1"
+
+[dev-dependencies.randomize]
+version = "3.0.0"
+[badges.maintenance]
+status = "passively-maintained"
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
new file mode 100644
index 0000000..4b30b6f
--- /dev/null
+++ b/Cargo.toml.orig
@@ -0,0 +1,22 @@
+[package]
+name = "oorandom"
+version = "11.1.3"
+authors = ["Simon Heath <icefox@dreamquest.io>"]
+edition = "2018"
+description = "A tiny, robust PRNG implementation."
+repository = "https://sr.ht/~icefox/oorandom/"
+readme = "README.md"
+keywords = ["rng", "prng", "random", "pcg",]
+categories = ["algorithms", "embedded", "no-std"]
+license = "MIT"
+
+[badges]
+maintenance = { status = "passively-maintained" }
+
+[dependencies]
+
+[dev-dependencies]
+randomize = "3.0.0"
+rand_pcg = "0.2"
+rand_core = "0.5"
+random-fast-rng = "0.1"
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..c71378f
--- /dev/null
+++ b/README.md
@@ -0,0 +1,196 @@
+# oorandom
+
+[![Crates.io](https://img.shields.io/crates/v/oorandom.svg)](https://crates.io/crates/oorandom)
+[![Docs](https://docs.rs/oorandom/badge.svg)](https://docs.rs/oorandom)
+[![builds.sr.ht status](https://builds.sr.ht/~icefox/oorandom.svg)](https://builds.sr.ht/~icefox/oorandom?)
+
+# What is this?
+
+`oorandom` is a minimalistic pseudorandom number generator in Rust. For
+those times when the `rand` crate is just too big and you want something
+a bit dumber.
+
+More specifically, it implements ONE prng, which is currently a permuted
+congruential generator (PCG). It may change if something better comes
+along, but that seems unlikely and will probably be a major version
+bump. It will give you `u32` or `u64`, and signed or floating-point
+equivalents. It is also `#[no_std]`. Anything else is gravy.
+
+Thanks to Lokathor for making
+[`randomize`](https://github.com/Lokathor/randomize), which inspired me
+to do my own equivalent.
+
+The name comes from my attempts to find a good way to pronounce
+`/dev/urandom`.
+
+Please direct questions, discussions and bugs to the [issue
+tracker](https://todo.sr.ht/~icefox/oorandom).
+
+# Why use `oorandom` instead of...
+
+ * `rand` -- `oorandom` is simpler and has zero choices you need to
+ make. It also compiles in 1/10th the time and has a stable API.
+ * `getrandom` -- They solve different problems; `getrandom` gives you
+ whatever secure randomness the OS decides to give you, not a
+ deterministic and seedable PRNG. It's generally a good idea to use
+ `getrandom` to seed this RNG though.
+ * `randomize` -- `randomize` used to be more complicated, but
+ `randomize` 3.x is quite similar to `oorandom` in functionality and
+ design. Go for it.
+ * `rand_pcg` and `rand_core` -- Yes you can take `rand` apart into its
+ pieces and use those individually, if you want to abandon having an
+ all-in-one solution, still deal with the lack of stability in
+ `rand_core` and actually figure out which pieces you need. It works
+ just fine. Seems more complicated than it needs to be though.
+ * `nanorand` -- `nanorand` uses the
+ [WyRand](https://github.com/wangyi-fudan/wyhash) PRNG algorithm,
+ which is supposedly faster than PCG and at least as good quality. I
+ haven't verified these claims, and I don't know of any *really*
+ thorough 3rd party investigation into them, though it apparently
+ passes [Dr. Lemire's tests](https://github.com/lemire/testingRNG).
+ So for now I personally consider WyRand to be in the "trust but
+ verify" level of quality. It's probably fine.
+
+# This is not...
+
+This is not cryptographically secure, and if you use it for crypto you
+will get what you deserve. You are also in charge of choosing a useful
+seed; the `getrandom` crate might be useful for that.
+
+This is also not optimized to be stupidly fast, but is basically just
+as fast as `rustc` feels like making it. This means it is safe and robust
+and portable and involves absolutely zero clever tricks.
+
+# Usage
+
+```rust
+use oorandom;
+fn main() {
+ let some_seed = 4;
+ let mut rng = oorandom::Rand32::new(some_seed);
+ println!("Your random number is: {}", rng.rand_float());
+}
+```
+
+If you want a nondeterministic seed, I recommend using the `getrandom` crate to produce one.
+
+# License
+
+MIT
+
+# A brief history of random numbers
+
+The usefulness of random numbers has been known for a long, long time
+to people who also knew how to use slide rules. If you wanted to do
+some math without the bother of coming up with all that pesky input
+data from the real world, you might as well just use any ol' random numbers,
+as long as there weren't any patterns in them to heck up the patterns you were
+trying to look at. So in the first half
+of the 20th century you had little old ladies named Edith spinning
+roulette wheels or pulling bingo balls out of baskets and writing the
+results down, which got assembled into giant tomes and published so
+that engineering schools could buy them and have giant tomes sitting
+on their shelves. Anyone who wanted some meaningless numbers could
+pull the tome down, flip it open to a presumably-random page, and
+there were all the random numbers anyone could want. The problem was
+solved, and life was good.
+
+In late 1940's computers were invented, but they were far too big and
+expensive to be put on the task of *intentionally* generating
+nonsense, and things carried on as before. If you needed random
+numbers in a computer program, you just got a pretty young lady named
+Mary to transcribe part of the book to punch cards for you.
+
+Around the early 1960's computers got fast enough that Edith and Mary
+couldn't keep up with them, so they got downsized and replaced with
+more computers. To do this people came up with Linear Congruential
+Generators (LCG's), which could generate lots of numbers numbers that
+weren't really random, but sure looked random. LCG's worked well on
+computers that even a second-rate university could afford, and so the
+problem was solved, and life was good.
+
+At some unknown point in here, presumably sometime in the 60's or 70's,
+someone seems to have invented Linear Feedback Shift Registers (LFSR's)
+as well. These made random-looking numbers and were really easy to
+implement in hardware compared to the LCG, which needed to do
+complicated things like multiply numbers. The random-looking numbers
+made by LFSR's were good enough for hardware people, so they started
+using LFSR's whenever they needed to and never looked back.
+
+Anyway, by the late 60's people who knew how to use slide rules had
+realized that using numbers that only *looked* random could really heck
+up their math pretty bad, and one of the more common LCG implmentations,
+RANDU, was actually about as bad as possible. So, just using any old
+LCG wasn't good enough, you had to use one made by someone with a PhD in
+mathematics. Donald Knuth shook his fist at the world and shouted "Hah!
+I told you so!", published a book on how to do it Right that most people
+didn't read, and then went back into his Fortress of Solitude to write
+TeX. Because it was created by IBM, RANDU's awfulness is now enshrined
+forever in history documents like this one, and because the people
+writing OS's and programming languages at the time weren't actually
+doing much slide-rule stuff anymore and didn't actually *need* very good
+random-looking numbers, everyone went back to using whatever old crap
+RNG they were using anyway. The problem was solved, or at least not
+terribly problematic, and life was good.
+
+Also, sometime in the 70's or 80's the arts of cryptography started
+leaking from classified government works into the real world. People
+started thinking about how much money they could make from scrambling
+satellite TV so that plebs with HAM radio licenses couldn't watch it,
+and these people started giving more money to people who had PhD's in
+mathematics to figure out how to make this work. It was quickly
+determined that neither LCG's nor LFSR's made numbers that were
+random-looking enough to really get in the way of someone who knew how
+to use a slide rule, and since Edith had long ago retired to a small
+beach house in New Jersey, they needed to figure out how to get
+computers to make better random-looking numbers. But making numbers
+look random enough that someone couldn't undo the process and get free
+pay-per-view was slow and involved lots of details that nobody else
+really cared about, so that topic went off on its own adventures and
+will not be further mentioned.
+
+Things more or less trundled along this way until the late 90's, when
+suddenly computers were everywhere and there was a new generation of
+people who had grown up too late to know how to use slide rules, so they
+did all their math with computers. They were doing a LOT of math by
+now, and they looked around and realized that their random-looking
+numbers really weren't very random-looking at all, and this was actually
+something of a problem by now. So the Mersenne Twister got invented.
+It was pretty slow and used a lot of memory and made kinda mediocre
+random numbers, but it was way better than a bad LCG, and most
+importantly, it had a cool name. Most people didn't want to read Knuth's
+book and figure out how to make a non-bad LCG, so everyone started using
+the Mersenne Twister whenever possible. The problem was solved, and
+life was good.
+
+This is where things stood until the early 2010's, when I finished my MS
+and started paying attention again. People suddenly realized it was
+possible to make random-looking numbers better than the Mersenne Twister
+using an algorithm called xorshift. Xorshift was fast, it made good
+pretty random-looking numbers, and it didn't need a whole 3 kilobytes of
+state just sitting around taking up space and causing comment among the
+neighbors at church. It did sometimes have problems with some of its
+numbers not looking random enough in a few select circumstances, but
+people were gun-shy about their randomness by now so a few people with
+PhD's in mathematics slowly and patiently spent years figuring out ways
+to work around these problems, leading to a whole confusing family of
+related things such as xoshiro, xoroshiro, xoroshiro+, xoroshiro*, and
+so on. Nobody else could really tell the difference between them, but
+everyone agreed they were better than Mersenne Twister, easier to
+implement, and the name was nearly as cool. Many papers were published,
+the problem was solved, and life was good.
+
+However, at about the same time some bright young spark figured out that
+it actually wasn't too hard, if you read Knuth's book and thought real
+hard about what you were doing, to take the old LCG and hop it up on
+cocaine and moon juice. The result got called the Permuted Congruential
+Generator, or PCG. This quite miffed the people working on xorshift
+generators by being almost as small and fast, and producing
+random-looking numbers that satisfied even the people who had learned to
+use slide rules for fun in this latter age. It also used xor's and bit
+shifts, and that's xorshift's turf, dammit, it's right in the name!
+Since nobody had figured out any downsides to PCG's yet, everyone
+shrugged and said "might as well just go with that then", and that is
+where, as of 2019, the art currently stands. The problem is solved, and
+life is good.
+
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..8196950
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,586 @@
+//! 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());
+ }
+ }
+ }
+}