diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-18 02:49:50 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-18 02:49:50 +0000 |
commit | 9835e2ae736235810b4ea1c162ca5e65c547e770 (patch) | |
tree | 3fcebf40ed70e581d776a8a4c65923e8ec20e026 /vendor/oorandom | |
parent | Releasing progress-linux version 1.70.0+dfsg2-1~progress7.99u1. (diff) | |
download | rustc-9835e2ae736235810b4ea1c162ca5e65c547e770.tar.xz rustc-9835e2ae736235810b4ea1c162ca5e65c547e770.zip |
Merging upstream version 1.71.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/oorandom')
-rw-r--r-- | vendor/oorandom/.cargo-checksum.json | 1 | ||||
-rw-r--r-- | vendor/oorandom/Cargo.toml | 38 | ||||
-rw-r--r-- | vendor/oorandom/LICENSE-MIT | 21 | ||||
-rw-r--r-- | vendor/oorandom/README.md | 196 | ||||
-rw-r--r-- | vendor/oorandom/src/lib.rs | 586 |
5 files changed, 842 insertions, 0 deletions
diff --git a/vendor/oorandom/.cargo-checksum.json b/vendor/oorandom/.cargo-checksum.json new file mode 100644 index 000000000..885ab9f44 --- /dev/null +++ b/vendor/oorandom/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.toml":"496e2a1d44bacd9e3459193579d40f52d4e9608cf3aae7d74579a1b6db0ac344","LICENSE-MIT":"e3f96201e81ea40ed151ec06694dd5b1d9e6b94a74f47a1c45779c4b8a293888","README.md":"5cc80c52e26b3a17c70782d9e3c7af8c8605d798471fce3c44492211de910382","src/lib.rs":"14615fda0995ac1e98ead0db2d3b1e52e2c3790ae41b61daf84992b04c21270c"},"package":"0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"}
\ No newline at end of file diff --git a/vendor/oorandom/Cargo.toml b/vendor/oorandom/Cargo.toml new file mode 100644 index 000000000..48044bb5a --- /dev/null +++ b/vendor/oorandom/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/vendor/oorandom/LICENSE-MIT b/vendor/oorandom/LICENSE-MIT new file mode 100644 index 000000000..7f19fc273 --- /dev/null +++ b/vendor/oorandom/LICENSE-MIT @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2019 Simon Heath + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE.
\ No newline at end of file diff --git a/vendor/oorandom/README.md b/vendor/oorandom/README.md new file mode 100644 index 000000000..c71378f89 --- /dev/null +++ b/vendor/oorandom/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/vendor/oorandom/src/lib.rs b/vendor/oorandom/src/lib.rs new file mode 100644 index 000000000..819695008 --- /dev/null +++ b/vendor/oorandom/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()); + } + } + } +} |