diff --git a/Cargo.toml b/Cargo.toml index 5390fd9d970..616a3cdd35f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,7 @@ members = ["rand_core", "rand_isaac", "rand_chacha", "rand_hc128", "rand_pcg", " [dependencies] rand_core = { path = "rand_core", version = "0.3", default-features = false } +rand_pcg = { path = "rand_pcg", version = "0.1" } # only for deprecations and benches: rand_isaac = { path = "rand_isaac", version = "0.1" } rand_chacha = { path = "rand_chacha", version = "0.1" } diff --git a/benches/generators.rs b/benches/generators.rs index 977a1beed78..608c64eac01 100644 --- a/benches/generators.rs +++ b/benches/generators.rs @@ -13,6 +13,7 @@ extern crate rand; extern crate rand_isaac; extern crate rand_chacha; extern crate rand_hc128; +extern crate rand_pcg; extern crate rand_xorshift; const RAND_BENCH_N: u64 = 1000; @@ -27,6 +28,7 @@ use rand::rngs::{OsRng, JitterRng, EntropyRng}; use rand_isaac::{IsaacRng, Isaac64Rng}; use rand_chacha::ChaChaRng; use rand_hc128::{Hc128Rng, Hc128Core}; +use rand_pcg::{Lcg64Xsh32, Mcg128Xsl64}; use rand_xorshift::XorShiftRng; macro_rules! gen_bytes { @@ -47,6 +49,8 @@ macro_rules! gen_bytes { } gen_bytes!(gen_bytes_xorshift, XorShiftRng::from_entropy()); +gen_bytes!(gen_bytes_lcg64_xsh32, Lcg64Xsh32::from_entropy()); +gen_bytes!(gen_bytes_mcg128_xsh64, Mcg128Xsl64::from_entropy()); gen_bytes!(gen_bytes_chacha20, ChaChaRng::from_entropy()); gen_bytes!(gen_bytes_hc128, Hc128Rng::from_entropy()); gen_bytes!(gen_bytes_isaac, IsaacRng::from_entropy()); @@ -73,6 +77,8 @@ macro_rules! gen_uint { } gen_uint!(gen_u32_xorshift, u32, XorShiftRng::from_entropy()); +gen_uint!(gen_u32_lcg64_xsh32, u32, Lcg64Xsh32::from_entropy()); +gen_uint!(gen_u32_mcg128_xsh64, u32, Mcg128Xsl64::from_entropy()); gen_uint!(gen_u32_chacha20, u32, ChaChaRng::from_entropy()); gen_uint!(gen_u32_hc128, u32, Hc128Rng::from_entropy()); gen_uint!(gen_u32_isaac, u32, IsaacRng::from_entropy()); @@ -82,6 +88,8 @@ gen_uint!(gen_u32_small, u32, SmallRng::from_entropy()); gen_uint!(gen_u32_os, u32, OsRng::new().unwrap()); gen_uint!(gen_u64_xorshift, u64, XorShiftRng::from_entropy()); +gen_uint!(gen_u64_lcg64_xsh32, u64, Lcg64Xsh32::from_entropy()); +gen_uint!(gen_u64_mcg128_xsh64, u64, Mcg128Xsl64::from_entropy()); gen_uint!(gen_u64_chacha20, u64, ChaChaRng::from_entropy()); gen_uint!(gen_u64_hc128, u64, Hc128Rng::from_entropy()); gen_uint!(gen_u64_isaac, u64, IsaacRng::from_entropy()); @@ -115,6 +123,8 @@ macro_rules! init_gen { } init_gen!(init_xorshift, XorShiftRng); +init_gen!(init_lcg64_xsh32, Lcg64Xsh32); +init_gen!(init_mcg128_xsh64, Mcg128Xsl64); init_gen!(init_hc128, Hc128Rng); init_gen!(init_isaac, IsaacRng); init_gen!(init_isaac64, Isaac64Rng); diff --git a/src/lib.rs b/src/lib.rs index 13f52153367..19dbd639cbf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -245,6 +245,7 @@ extern crate rand_core; extern crate rand_isaac; // only for deprecations extern crate rand_chacha; // only for deprecations extern crate rand_hc128; +extern crate rand_pcg; extern crate rand_xorshift; #[cfg(feature = "log")] #[macro_use] extern crate log; diff --git a/src/prng/mod.rs b/src/prng/mod.rs index 7c8c69dbb10..672b1264a6b 100644 --- a/src/prng/mod.rs +++ b/src/prng/mod.rs @@ -45,18 +45,18 @@ //! //! | name | full name | performance | memory | quality | period | features | //! |------|-----------|-------------|--------|---------|--------|----------| -//! | [`XorShiftRng`] | Xorshift 32/128 | ★★★☆☆ | 16 bytes | ★☆☆☆☆ | `u32` * 2128 - 1 | — | +//! | [`Pcg32`] | PCG XSH RR 64/32 (LCG) | ★★★☆☆ | 16 bytes | ★★★☆☆ | `u32` * 264 | — | +//! | [`Pcg64Mcg`] | PCG XSL 128/64 (MCG) | ★★★★☆ | 16 bytes | ★★★☆☆ | `u64` * 2126 | — | +//! | [`XorShiftRng`] | Xorshift 32/128 | ★★★★☆ | 16 bytes | ★☆☆☆☆ | `u32` * 2128 - 1 | — | //! // Quality stars [not rendered in documentation]: -// 5. reserved for crypto-level (e.g. ChaCha8, ISAAC) -// 4. good performance on TestU01 and PractRand, good theory -// (e.g. PCG, truncated Xorshift*) -// 3. good performance on TestU01 and PractRand, but "falling through the -// cracks" or insufficient theory (e.g. SFC, Xoshiro) -// 2. imperfect performance on tests or other limiting properties, but not -// terrible (e.g. Xoroshiro128+) +// 5. proven cryptographic quality (e.g. ChaCha20) +// 4. potentially cryptographic, but low margin or lack of theory (e.g. ChaCha8, ISAAC) +// 3. good performance on TestU01 and PractRand, good theory +// 2. imperfect performance on tests or other limiting properties, or +// insufficient theory, but not terrible // 1. clear deficiencies in test results, cycle length, theory, or other -// properties (e.g. Xorshift) +// properties // // Performance stars [not rendered in documentation]: // Meant to give an indication of relative performance. Roughly follows a log @@ -297,6 +297,8 @@ //! [`rngs` module]: ../rngs/index.html //! [basic PRNGs]: #basic-pseudo-random-number-generators-prngs //! [CSPRNGs]: #cryptographically-secure-pseudo-random-number-generators-csprngs +//! [`Pcg32`]: ../../rand_pcg/type.Pcg32.html +//! [`Pcg64Mcg`]: ../../rand_pcg/type.Pcg64Mcg.html //! [`XorShiftRng`]: ../../rand_xorshift/struct.XorShiftRng.html //! [`ChaChaRng`]: ../../rand_chacha/struct.ChaChaRng.html //! [`Hc128Rng`]: ../../rand_hc128/struct.Hc128Rng.html diff --git a/src/rngs/mod.rs b/src/rngs/mod.rs index 6888517d98e..70c45069827 100644 --- a/src/rngs/mod.rs +++ b/src/rngs/mod.rs @@ -48,9 +48,6 @@ //! //! - [`SmallRng`] is a PRNG chosen for low memory usage, high performance and //! good statistical quality. -//! The current algorithm (plain Xorshift) unfortunately performs -//! poorly in statistical quality test suites (TestU01 and PractRand) and will -//! be replaced in the next major release. //! - [`StdRng`] is a CSPRNG chosen for good performance and trust of security //! (based on reviews, maturity and usage). The current algorithm is HC-128, //! which is one of the recommendations by ECRYPT's eSTREAM project. diff --git a/src/rngs/small.rs b/src/rngs/small.rs index 8197a64f7ac..c7b28fce702 100644 --- a/src/rngs/small.rs +++ b/src/rngs/small.rs @@ -9,7 +9,11 @@ //! A small fast RNG use {RngCore, SeedableRng, Error}; -use ::rand_xorshift::XorShiftRng; + +#[cfg(all(rust_1_26, target_pointer_width = "64"))] +type Rng = ::rand_pcg::Pcg64Mcg; +#[cfg(not(all(rust_1_26, target_pointer_width = "64")))] +type Rng = ::rand_pcg::Pcg32; /// An RNG recommended when small state, cheap initialization and good /// performance are required. The PRNG algorithm in `SmallRng` is chosen to be @@ -20,9 +24,11 @@ use ::rand_xorshift::XorShiftRng; /// future library versions may use a different internal generator with /// different output. Further, this generator may not be portable and can /// produce different output depending on the architecture. If you require -/// reproducible output, use a named RNG, for example [`XorShiftRng`]. +/// reproducible output, use a named RNG. Refer to the documentation on the +/// [`prng` module](../prng/index.html). /// -/// The current algorithm used on all platforms is [Xorshift]. +/// The current algorithm is [`Pcg64Mcg`] on 64-bit platforms with Rust version +/// 1.26 and later, or [`Pcg32`] otherwise. /// /// # Examples /// @@ -61,10 +67,10 @@ use ::rand_xorshift::XorShiftRng; /// [`FromEntropy`]: ../trait.FromEntropy.html /// [`StdRng`]: struct.StdRng.html /// [`thread_rng`]: ../fn.thread_rng.html -/// [Xorshift]: ../../rand_xorshift/struct.XorShiftRng.html -/// [`XorShiftRng`]: ../../rand_xorshift/struct.XorShiftRng.html +/// [`Pcg64Mcg`]: https://docs.rs/rand_pcg/0.1.0/rand_pcg/type.Pcg64Mcg.html +/// [`Pcg32`]: https://docs.rs/rand_pcg/0.1.0/rand_pcg/type.Pcg32.html #[derive(Clone, Debug)] -pub struct SmallRng(XorShiftRng); +pub struct SmallRng(Rng); impl RngCore for SmallRng { #[inline(always)] @@ -87,13 +93,13 @@ impl RngCore for SmallRng { } impl SeedableRng for SmallRng { - type Seed = ::Seed; + type Seed = ::Seed; fn from_seed(seed: Self::Seed) -> Self { - SmallRng(XorShiftRng::from_seed(seed)) + SmallRng(Rng::from_seed(seed)) } fn from_rng(rng: R) -> Result { - XorShiftRng::from_rng(rng).map(SmallRng) + Rng::from_rng(rng).map(SmallRng) } } diff --git a/src/seq/mod.rs b/src/seq/mod.rs index 89d2417d666..99596028a63 100644 --- a/src/seq/mod.rs +++ b/src/seq/mod.rs @@ -512,7 +512,7 @@ pub fn sample_slice_ref<'a, R, T>(rng: &mut R, slice: &'a [T], amount: usize) -> mod test { use super::*; #[cfg(feature = "alloc")] use {Rng, SeedableRng}; - #[cfg(feature = "alloc")] use ::rand_xorshift::XorShiftRng; + #[cfg(feature = "alloc")] use rngs::SmallRng; #[cfg(all(feature="alloc", not(feature="std")))] use alloc::vec::Vec; @@ -753,7 +753,7 @@ mod test { #[cfg(feature = "alloc")] #[allow(deprecated)] fn test_sample_slice() { - let xor_rng = XorShiftRng::from_seed; + let seeded_rng = SmallRng::from_seed; let mut r = ::test::rng(403); @@ -764,16 +764,16 @@ mod test { r.fill(&mut seed); // assert the basics work - let regular = index::sample(&mut xor_rng(seed), length, amount); + let regular = index::sample(&mut seeded_rng(seed), length, amount); assert_eq!(regular.len(), amount); assert!(regular.iter().all(|e| e < length)); // also test that sampling the slice works let vec: Vec = (0..(length as u32)).collect(); - let result = sample_slice(&mut xor_rng(seed), &vec, amount); + let result = sample_slice(&mut seeded_rng(seed), &vec, amount); assert_eq!(result, regular.iter().map(|i| i as u32).collect::>()); - let result = sample_slice_ref(&mut xor_rng(seed), &vec, amount); + let result = sample_slice_ref(&mut seeded_rng(seed), &vec, amount); assert!(result.iter().zip(regular.iter()).all(|(i,j)| **i == j as u32)); } }