oorandom-11.1.3/Cargo.toml0000644000000021471375624412500110060ustar # 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 "] 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" oorandom-11.1.3/Cargo.toml.orig010064400017500001750000000010071375623755200145000ustar 00000000000000[package] name = "oorandom" version = "11.1.3" authors = ["Simon Heath "] 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" oorandom-11.1.3/LICENSE-MIT010064400017500001750000000020651352724001100132260ustar 00000000000000The 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.oorandom-11.1.3/README.md010064400017500001750000000241011375624400600130600ustar 00000000000000# 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. oorandom-11.1.3/src/lib.rs010064400017500001750000000504301375623753700135230ustar 00000000000000//! 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 // // 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 { // 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 { // 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()); } } } }