From 854bcbdc95e66ea725a94de0c46414bbc24bfb5d Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Fri, 28 Jul 2017 12:34:06 +0100 Subject: [PATCH 001/247] =?UTF-8?q?Remove=20distributions::Sample;=20renam?= =?UTF-8?q?e=20IndependentSample=20=E2=86=92=20Sample?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- benches/distributions/gamma.rs | 6 +-- src/distributions/exponential.rs | 16 +++--- src/distributions/gamma.rs | 85 ++++++++++++-------------------- src/distributions/mod.rs | 75 ++++++++++------------------ src/distributions/normal.rs | 30 +++++------ src/distributions/range.rs | 24 +++------ src/lib.rs | 16 +++--- 7 files changed, 93 insertions(+), 159 deletions(-) diff --git a/benches/distributions/gamma.rs b/benches/distributions/gamma.rs index bf3fd367a9b..e365a1ac0ce 100644 --- a/benches/distributions/gamma.rs +++ b/benches/distributions/gamma.rs @@ -1,7 +1,7 @@ use std::mem::size_of; use test::Bencher; use rand; -use rand::distributions::IndependentSample; +use rand::distributions::Sample; use rand::distributions::gamma::Gamma; #[bench] @@ -11,7 +11,7 @@ fn bench_gamma_large_shape(b: &mut Bencher) { b.iter(|| { for _ in 0..::RAND_BENCH_N { - gamma.ind_sample(&mut rng); + gamma.sample(&mut rng); } }); b.bytes = size_of::() as u64 * ::RAND_BENCH_N; @@ -24,7 +24,7 @@ fn bench_gamma_small_shape(b: &mut Bencher) { b.iter(|| { for _ in 0..::RAND_BENCH_N { - gamma.ind_sample(&mut rng); + gamma.sample(&mut rng); } }); b.bytes = size_of::() as u64 * ::RAND_BENCH_N; diff --git a/src/distributions/exponential.rs b/src/distributions/exponential.rs index c3c924c6b7e..1d14b2d563f 100644 --- a/src/distributions/exponential.rs +++ b/src/distributions/exponential.rs @@ -11,7 +11,7 @@ //! The exponential distribution. use {Rng, Rand}; -use distributions::{ziggurat, ziggurat_tables, Sample, IndependentSample}; +use distributions::{ziggurat, ziggurat_tables, Sample}; /// A wrapper around an `f64` to generate Exp(1) random numbers. /// @@ -65,10 +65,10 @@ impl Rand for Exp1 { /// # Example /// /// ```rust -/// use rand::distributions::{Exp, IndependentSample}; +/// use rand::distributions::{Exp, Sample}; /// /// let exp = Exp::new(2.0); -/// let v = exp.ind_sample(&mut rand::thread_rng()); +/// let v = exp.sample(&mut rand::thread_rng()); /// println!("{} is from a Exp(2) distribution", v); /// ``` #[derive(Clone, Copy, Debug)] @@ -88,10 +88,7 @@ impl Exp { } impl Sample for Exp { - fn sample(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) } -} -impl IndependentSample for Exp { - fn ind_sample(&self, rng: &mut R) -> f64 { + fn sample(&self, rng: &mut R) -> f64 { let Exp1(n) = rng.gen::(); n * self.lambda_inverse } @@ -99,16 +96,15 @@ impl IndependentSample for Exp { #[cfg(test)] mod test { - use distributions::{Sample, IndependentSample}; + use distributions::{Sample}; use super::Exp; #[test] fn test_exp() { - let mut exp = Exp::new(10.0); + let exp = Exp::new(10.0); let mut rng = ::test::rng(); for _ in 0..1000 { assert!(exp.sample(&mut rng) >= 0.0); - assert!(exp.ind_sample(&mut rng) >= 0.0); } } #[test] diff --git a/src/distributions/gamma.rs b/src/distributions/gamma.rs index e6a9d77d885..a31708997e3 100644 --- a/src/distributions/gamma.rs +++ b/src/distributions/gamma.rs @@ -17,7 +17,7 @@ use self::ChiSquaredRepr::*; use {Rng, Open01}; use super::normal::StandardNormal; -use super::{IndependentSample, Sample, Exp}; +use super::{Sample, Exp}; /// The Gamma distribution `Gamma(shape, scale)` distribution. /// @@ -38,10 +38,10 @@ use super::{IndependentSample, Sample, Exp}; /// # Example /// /// ```rust -/// use rand::distributions::{IndependentSample, Gamma}; +/// use rand::distributions::{Sample, Gamma}; /// /// let gamma = Gamma::new(2.0, 5.0); -/// let v = gamma.ind_sample(&mut rand::thread_rng()); +/// let v = gamma.sample(&mut rand::thread_rng()); /// println!("{} is from a Gamma(2, 5) distribution", v); /// ``` /// @@ -133,34 +133,25 @@ impl GammaLargeShape { } } -impl Sample for Gamma { - fn sample(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) } -} -impl Sample for GammaSmallShape { - fn sample(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) } -} -impl Sample for GammaLargeShape { - fn sample(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) } -} -impl IndependentSample for Gamma { - fn ind_sample(&self, rng: &mut R) -> f64 { +impl Sample for Gamma { + fn sample(&self, rng: &mut R) -> f64 { match self.repr { - Small(ref g) => g.ind_sample(rng), - One(ref g) => g.ind_sample(rng), - Large(ref g) => g.ind_sample(rng), + Small(ref g) => g.sample(rng), + One(ref g) => g.sample(rng), + Large(ref g) => g.sample(rng), } } } -impl IndependentSample for GammaSmallShape { - fn ind_sample(&self, rng: &mut R) -> f64 { +impl Sample for GammaSmallShape { + fn sample(&self, rng: &mut R) -> f64 { let Open01(u) = rng.gen::>(); - self.large_shape.ind_sample(rng) * u.powf(self.inv_shape) + self.large_shape.sample(rng) * u.powf(self.inv_shape) } } -impl IndependentSample for GammaLargeShape { - fn ind_sample(&self, rng: &mut R) -> f64 { +impl Sample for GammaLargeShape { + fn sample(&self, rng: &mut R) -> f64 { loop { let StandardNormal(x) = rng.gen::(); let v_cbrt = 1.0 + self.c * x; @@ -191,10 +182,10 @@ impl IndependentSample for GammaLargeShape { /// # Example /// /// ```rust -/// use rand::distributions::{ChiSquared, IndependentSample}; +/// use rand::distributions::{ChiSquared, Sample}; /// /// let chi = ChiSquared::new(11.0); -/// let v = chi.ind_sample(&mut rand::thread_rng()); +/// let v = chi.sample(&mut rand::thread_rng()); /// println!("{} is from a χ²(11) distribution", v) /// ``` #[derive(Clone, Copy, Debug)] @@ -225,17 +216,14 @@ impl ChiSquared { } } impl Sample for ChiSquared { - fn sample(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) } -} -impl IndependentSample for ChiSquared { - fn ind_sample(&self, rng: &mut R) -> f64 { + fn sample(&self, rng: &mut R) -> f64 { match self.repr { DoFExactlyOne => { // k == 1 => N(0,1)^2 let StandardNormal(norm) = rng.gen::(); norm * norm } - DoFAnythingElse(ref g) => g.ind_sample(rng) + DoFAnythingElse(ref g) => g.sample(rng) } } } @@ -249,10 +237,10 @@ impl IndependentSample for ChiSquared { /// # Example /// /// ```rust -/// use rand::distributions::{FisherF, IndependentSample}; +/// use rand::distributions::{FisherF, Sample}; /// /// let f = FisherF::new(2.0, 32.0); -/// let v = f.ind_sample(&mut rand::thread_rng()); +/// let v = f.sample(&mut rand::thread_rng()); /// println!("{} is from an F(2, 32) distribution", v) /// ``` #[derive(Clone, Copy, Debug)] @@ -279,11 +267,8 @@ impl FisherF { } } impl Sample for FisherF { - fn sample(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) } -} -impl IndependentSample for FisherF { - fn ind_sample(&self, rng: &mut R) -> f64 { - self.numer.ind_sample(rng) / self.denom.ind_sample(rng) * self.dof_ratio + fn sample(&self, rng: &mut R) -> f64 { + self.numer.sample(rng) / self.denom.sample(rng) * self.dof_ratio } } @@ -293,10 +278,10 @@ impl IndependentSample for FisherF { /// # Example /// /// ```rust -/// use rand::distributions::{StudentT, IndependentSample}; +/// use rand::distributions::{StudentT, Sample}; /// /// let t = StudentT::new(11.0); -/// let v = t.ind_sample(&mut rand::thread_rng()); +/// let v = t.sample(&mut rand::thread_rng()); /// println!("{} is from a t(11) distribution", v) /// ``` #[derive(Clone, Copy, Debug)] @@ -317,45 +302,39 @@ impl StudentT { } } impl Sample for StudentT { - fn sample(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) } -} -impl IndependentSample for StudentT { - fn ind_sample(&self, rng: &mut R) -> f64 { + fn sample(&self, rng: &mut R) -> f64 { let StandardNormal(norm) = rng.gen::(); - norm * (self.dof / self.chi.ind_sample(rng)).sqrt() + norm * (self.dof / self.chi.sample(rng)).sqrt() } } #[cfg(test)] mod test { - use distributions::{Sample, IndependentSample}; + use distributions::{Sample}; use super::{ChiSquared, StudentT, FisherF}; #[test] fn test_chi_squared_one() { - let mut chi = ChiSquared::new(1.0); + let chi = ChiSquared::new(1.0); let mut rng = ::test::rng(); for _ in 0..1000 { chi.sample(&mut rng); - chi.ind_sample(&mut rng); } } #[test] fn test_chi_squared_small() { - let mut chi = ChiSquared::new(0.5); + let chi = ChiSquared::new(0.5); let mut rng = ::test::rng(); for _ in 0..1000 { chi.sample(&mut rng); - chi.ind_sample(&mut rng); } } #[test] fn test_chi_squared_large() { - let mut chi = ChiSquared::new(30.0); + let chi = ChiSquared::new(30.0); let mut rng = ::test::rng(); for _ in 0..1000 { chi.sample(&mut rng); - chi.ind_sample(&mut rng); } } #[test] @@ -366,21 +345,19 @@ mod test { #[test] fn test_f() { - let mut f = FisherF::new(2.0, 32.0); + let f = FisherF::new(2.0, 32.0); let mut rng = ::test::rng(); for _ in 0..1000 { f.sample(&mut rng); - f.ind_sample(&mut rng); } } #[test] fn test_t() { - let mut t = StudentT::new(11.0); + let t = StudentT::new(11.0); let mut rng = ::test::rng(); for _ in 0..1000 { t.sample(&mut rng); - t.ind_sample(&mut rng); } } } diff --git a/src/distributions/mod.rs b/src/distributions/mod.rs index f128b75c1ce..82987205d4f 100644 --- a/src/distributions/mod.rs +++ b/src/distributions/mod.rs @@ -10,12 +10,10 @@ //! Sampling from random distributions. //! -//! This is a generalization of `Rand` to allow parameters to control the -//! exact properties of the generated values, e.g. the mean and standard -//! deviation of a normal distribution. The `Sample` trait is the most -//! general, and allows for generating values that change some state -//! internally. The `IndependentSample` trait is for generating values -//! that do not need to record state. +//! A distribution may have internal state describing the distribution of +//! generated values; for example `Range` needs to know its upper and lower +//! bounds. Distributions use the `Sample` trait to yield values: call +//! `dist.sample(&mut rng)` to get a random variable. use std::marker; @@ -31,50 +29,34 @@ pub mod gamma; pub mod normal; pub mod exponential; -/// Types that can be used to create a random instance of `Support`. -pub trait Sample { - /// Generate a random value of `Support`, using `rng` as the +/// Types (distributions) that can be used to create a random instance of `T`. +pub trait Sample { + /// Generate a random value of `T`, using `rng` as the /// source of randomness. - fn sample(&mut self, rng: &mut R) -> Support; + fn sample(&self, rng: &mut R) -> T; } -/// `Sample`s that do not require keeping track of state. -/// -/// Since no state is recorded, each sample is (statistically) -/// independent of all others, assuming the `Rng` used has this -/// property. -// FIXME maybe having this separate is overkill (the only reason is to -// take &self rather than &mut self)? or maybe this should be the -// trait called `Sample` and the other should be `DependentSample`. -pub trait IndependentSample: Sample { - /// Generate a random value. - fn ind_sample(&self, &mut R) -> Support; -} /// A wrapper for generating types that implement `Rand` via the -/// `Sample` & `IndependentSample` traits. +/// `Sample` trait. #[derive(Debug)] -pub struct RandSample { - _marker: marker::PhantomData Sup>, +pub struct RandSample { + _marker: marker::PhantomData T>, } -impl Copy for RandSample {} -impl Clone for RandSample { +impl Copy for RandSample {} +impl Clone for RandSample { fn clone(&self) -> Self { *self } } -impl Sample for RandSample { - fn sample(&mut self, rng: &mut R) -> Sup { self.ind_sample(rng) } -} - -impl IndependentSample for RandSample { - fn ind_sample(&self, rng: &mut R) -> Sup { +impl Sample for RandSample { + fn sample(&self, rng: &mut R) -> T { rng.gen() } } -impl RandSample { - pub fn new() -> RandSample { +impl RandSample { + pub fn new() -> RandSample { RandSample { _marker: marker::PhantomData } } } @@ -93,15 +75,15 @@ pub struct Weighted { /// Each item has an associated weight that influences how likely it /// is to be chosen: higher weight is more likely. /// -/// The `Clone` restriction is a limitation of the `Sample` and -/// `IndependentSample` traits. Note that `&T` is (cheaply) `Clone` for +/// The `Clone` restriction is a limitation of the `Sample` trait. +/// Note that `&T` is (cheaply) `Clone` for /// all `T`, as is `u32`, so one can store references or indices into /// another vector. /// /// # Example /// /// ```rust -/// use rand::distributions::{Weighted, WeightedChoice, IndependentSample}; +/// use rand::distributions::{Weighted, WeightedChoice, Sample}; /// /// let mut items = vec!(Weighted { weight: 2, item: 'a' }, /// Weighted { weight: 4, item: 'b' }, @@ -110,7 +92,7 @@ pub struct Weighted { /// let mut rng = rand::thread_rng(); /// for _ in 0..16 { /// // on average prints 'a' 4 times, 'b' 8 and 'c' twice. -/// println!("{}", wc.ind_sample(&mut rng)); +/// println!("{}", wc.sample(&mut rng)); /// } /// ``` #[derive(Debug)] @@ -156,17 +138,13 @@ impl<'a, T: Clone> WeightedChoice<'a, T> { } impl<'a, T: Clone> Sample for WeightedChoice<'a, T> { - fn sample(&mut self, rng: &mut R) -> T { self.ind_sample(rng) } -} - -impl<'a, T: Clone> IndependentSample for WeightedChoice<'a, T> { - fn ind_sample(&self, rng: &mut R) -> T { + fn sample(&self, rng: &mut R) -> T { // we want to find the first element that has cumulative // weight > sample_weight, which we do by binary since the // cumulative weights of self.items are sorted. // choose a weight in [0, total_weight) - let sample_weight = self.weight_range.ind_sample(rng); + let sample_weight = self.weight_range.sample(rng); // short circuit when it's the first item if sample_weight < self.items[0].weight { @@ -272,7 +250,7 @@ fn ziggurat( mod tests { use {Rng, Rand}; - use super::{RandSample, WeightedChoice, Weighted, Sample, IndependentSample}; + use super::{RandSample, WeightedChoice, Weighted, Sample}; #[derive(PartialEq, Debug)] struct ConstRand(usize); @@ -296,10 +274,9 @@ mod tests { #[test] fn test_rand_sample() { - let mut rand_sample = RandSample::::new(); + let rand_sample = RandSample::::new(); assert_eq!(rand_sample.sample(&mut ::test::rng()), ConstRand(0)); - assert_eq!(rand_sample.ind_sample(&mut ::test::rng()), ConstRand(0)); } #[test] fn test_weighted_choice() { @@ -317,7 +294,7 @@ mod tests { let mut rng = CountingRng { i: 0 }; for &val in expected.iter() { - assert_eq!(wc.ind_sample(&mut rng), val) + assert_eq!(wc.sample(&mut rng), val) } }} } diff --git a/src/distributions/normal.rs b/src/distributions/normal.rs index 280613d8595..ce43172a651 100644 --- a/src/distributions/normal.rs +++ b/src/distributions/normal.rs @@ -11,7 +11,7 @@ //! The normal and derived distributions. use {Rng, Rand, Open01}; -use distributions::{ziggurat, ziggurat_tables, Sample, IndependentSample}; +use distributions::{ziggurat, ziggurat_tables, Sample}; /// A wrapper around an `f64` to generate N(0, 1) random numbers /// (a.k.a. a standard normal, or Gaussian). @@ -81,11 +81,11 @@ impl Rand for StandardNormal { /// # Example /// /// ```rust -/// use rand::distributions::{Normal, IndependentSample}; +/// use rand::distributions::{Normal, Sample}; /// /// // mean 2, standard deviation 3 /// let normal = Normal::new(2.0, 3.0); -/// let v = normal.ind_sample(&mut rand::thread_rng()); +/// let v = normal.sample(&mut rand::thread_rng()); /// println!("{} is from a N(2, 9) distribution", v) /// ``` #[derive(Clone, Copy, Debug)] @@ -111,10 +111,7 @@ impl Normal { } } impl Sample for Normal { - fn sample(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) } -} -impl IndependentSample for Normal { - fn ind_sample(&self, rng: &mut R) -> f64 { + fn sample(&self, rng: &mut R) -> f64 { let StandardNormal(n) = rng.gen::(); self.mean + self.std_dev * n } @@ -129,11 +126,11 @@ impl IndependentSample for Normal { /// # Example /// /// ```rust -/// use rand::distributions::{LogNormal, IndependentSample}; +/// use rand::distributions::{LogNormal, Sample}; /// /// // mean 2, standard deviation 3 /// let log_normal = LogNormal::new(2.0, 3.0); -/// let v = log_normal.ind_sample(&mut rand::thread_rng()); +/// let v = log_normal.sample(&mut rand::thread_rng()); /// println!("{} is from an ln N(2, 9) distribution", v) /// ``` #[derive(Clone, Copy, Debug)] @@ -155,26 +152,22 @@ impl LogNormal { } } impl Sample for LogNormal { - fn sample(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) } -} -impl IndependentSample for LogNormal { - fn ind_sample(&self, rng: &mut R) -> f64 { - self.norm.ind_sample(rng).exp() + fn sample(&self, rng: &mut R) -> f64 { + self.norm.sample(rng).exp() } } #[cfg(test)] mod tests { - use distributions::{Sample, IndependentSample}; + use distributions::{Sample}; use super::{Normal, LogNormal}; #[test] fn test_normal() { - let mut norm = Normal::new(10.0, 10.0); + let norm = Normal::new(10.0, 10.0); let mut rng = ::test::rng(); for _ in 0..1000 { norm.sample(&mut rng); - norm.ind_sample(&mut rng); } } #[test] @@ -186,11 +179,10 @@ mod tests { #[test] fn test_log_normal() { - let mut lnorm = LogNormal::new(10.0, 10.0); + let lnorm = LogNormal::new(10.0, 10.0); let mut rng = ::test::rng(); for _ in 0..1000 { lnorm.sample(&mut rng); - lnorm.ind_sample(&mut rng); } } #[test] diff --git a/src/distributions/range.rs b/src/distributions/range.rs index 7206941d0dc..77547219cbe 100644 --- a/src/distributions/range.rs +++ b/src/distributions/range.rs @@ -15,7 +15,7 @@ use std::num::Wrapping as w; use Rng; -use distributions::{Sample, IndependentSample}; +use distributions::{Sample}; /// Sample values uniformly between two bounds. /// @@ -34,14 +34,14 @@ use distributions::{Sample, IndependentSample}; /// # Example /// /// ```rust -/// use rand::distributions::{IndependentSample, Range}; +/// use rand::distributions::{Sample, Range}; /// /// fn main() { /// let between = Range::new(10, 10000); /// let mut rng = rand::thread_rng(); /// let mut sum = 0; /// for _ in 0..1000 { -/// sum += between.ind_sample(&mut rng); +/// sum += between.sample(&mut rng); /// } /// println!("{}", sum); /// } @@ -62,12 +62,8 @@ impl Range { } } -impl Sample for Range { - #[inline] - fn sample(&mut self, rng: &mut R) -> Sup { self.ind_sample(rng) } -} -impl IndependentSample for Range { - fn ind_sample(&self, rng: &mut R) -> Sup { +impl Sample for Range { + fn sample(&self, rng: &mut R) -> T { SampleRange::sample_range(self, rng) } } @@ -165,7 +161,7 @@ float_impl! { f64 } #[cfg(test)] mod tests { - use distributions::{Sample, IndependentSample}; + use distributions::{Sample}; use super::Range as Range; #[should_panic] @@ -189,12 +185,10 @@ mod tests { (10, 127), (::std::$ty::MIN, ::std::$ty::MAX)]; for &(low, high) in v.iter() { - let mut sampler: Range<$ty> = Range::new(low, high); + let sampler: Range<$ty> = Range::new(low, high); for _ in 0..1000 { let v = sampler.sample(&mut rng); assert!(low <= v && v < high); - let v = sampler.ind_sample(&mut rng); - assert!(low <= v && v < high); } } )* @@ -215,12 +209,10 @@ mod tests { (1e-35, 1e-25), (-1e35, 1e35)]; for &(low, high) in v.iter() { - let mut sampler: Range<$ty> = Range::new(low, high); + let sampler: Range<$ty> = Range::new(low, high); for _ in 0..1000 { let v = sampler.sample(&mut rng); assert!(low <= v && v < high); - let v = sampler.ind_sample(&mut rng); - assert!(low <= v && v < high); } } )* diff --git a/src/lib.rs b/src/lib.rs index 2e76c5a5649..2ac141fe3de 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -110,7 +110,7 @@ //! and multiply this fraction by 4. //! //! ``` -//! use rand::distributions::{IndependentSample, Range}; +//! use rand::distributions::{Sample, Range}; //! //! fn main() { //! let between = Range::new(-1f64, 1.); @@ -120,8 +120,8 @@ //! let mut in_circle = 0; //! //! for _ in 0..total { -//! let a = between.ind_sample(&mut rng); -//! let b = between.ind_sample(&mut rng); +//! let a = between.sample(&mut rng); +//! let b = between.sample(&mut rng); //! if a*a + b*b <= 1. { //! in_circle += 1; //! } @@ -153,7 +153,7 @@ //! //! ``` //! use rand::Rng; -//! use rand::distributions::{IndependentSample, Range}; +//! use rand::distributions::{Sample, Range}; //! //! struct SimulationResult { //! win: bool, @@ -163,10 +163,10 @@ //! // Run a single simulation of the Monty Hall problem. //! fn simulate(random_door: &Range, rng: &mut R) //! -> SimulationResult { -//! let car = random_door.ind_sample(rng); +//! let car = random_door.sample(rng); //! //! // This is our initial choice -//! let mut choice = random_door.ind_sample(rng); +//! let mut choice = random_door.sample(rng); //! //! // The game host opens a door //! let open = game_host_open(car, choice, rng); @@ -265,7 +265,7 @@ use IsaacRng as IsaacWordRng; #[cfg(target_pointer_width = "64")] use Isaac64Rng as IsaacWordRng; -use distributions::{Range, IndependentSample}; +use distributions::{Range, Sample}; use distributions::range::SampleRange; pub mod distributions; @@ -501,7 +501,7 @@ pub trait Rng { /// ``` fn gen_range(&mut self, low: T, high: T) -> T where Self: Sized { assert!(low < high, "Rng.gen_range called with low >= high"); - Range::new(low, high).ind_sample(self) + Range::new(low, high).sample(self) } /// Return a bool with a 1 in n chance of true From e1eb512c30d28899cf43b35a1d04c54d976be086 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Fri, 28 Jul 2017 13:51:29 +0100 Subject: [PATCH 002/247] Add prng module; move XorShiftRng to it --- src/lib.rs | 90 ++------------------------------------ src/prng/mod.rs | 45 +++++++++++++++++++ src/prng/xorshift.rs | 101 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 149 insertions(+), 87 deletions(-) create mode 100644 src/prng/mod.rs create mode 100644 src/prng/xorshift.rs diff --git a/src/lib.rs b/src/lib.rs index 2ac141fe3de..0df683b0181 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -268,6 +268,8 @@ use Isaac64Rng as IsaacWordRng; use distributions::{Range, Sample}; use distributions::range::SampleRange; +use prng::XorShiftRng; + pub mod distributions; pub mod isaac; pub mod chacha; @@ -275,6 +277,7 @@ pub mod reseeding; mod rand_impls; pub mod os; pub mod read; +pub mod prng; #[allow(bad_style)] type w64 = w; @@ -713,93 +716,6 @@ pub trait SeedableRng: Rng { fn from_seed(seed: Seed) -> Self; } -/// An Xorshift[1] random number -/// generator. -/// -/// The Xorshift algorithm is not suitable for cryptographic purposes -/// but is very fast. If you do not know for sure that it fits your -/// requirements, use a more secure one such as `IsaacRng` or `OsRng`. -/// -/// [1]: Marsaglia, George (July 2003). ["Xorshift -/// RNGs"](http://www.jstatsoft.org/v08/i14/paper). *Journal of -/// Statistical Software*. Vol. 8 (Issue 14). -#[allow(missing_copy_implementations)] -#[derive(Clone, Debug)] -pub struct XorShiftRng { - x: w32, - y: w32, - z: w32, - w: w32, -} - -impl XorShiftRng { - /// Creates a new XorShiftRng instance which is not seeded. - /// - /// The initial values of this RNG are constants, so all generators created - /// by this function will yield the same stream of random numbers. It is - /// highly recommended that this is created through `SeedableRng` instead of - /// this function - pub fn new_unseeded() -> XorShiftRng { - XorShiftRng { - x: w(0x193a6754), - y: w(0xa8a7d469), - z: w(0x97830e05), - w: w(0x113ba7bb), - } - } -} - -impl Rng for XorShiftRng { - #[inline] - fn next_u32(&mut self) -> u32 { - let x = self.x; - let t = x ^ (x << 11); - self.x = self.y; - self.y = self.z; - self.z = self.w; - let w_ = self.w; - self.w = w_ ^ (w_ >> 19) ^ (t ^ (t >> 8)); - self.w.0 - } -} - -impl SeedableRng<[u32; 4]> for XorShiftRng { - /// Reseed an XorShiftRng. This will panic if `seed` is entirely 0. - fn reseed(&mut self, seed: [u32; 4]) { - assert!(!seed.iter().all(|&x| x == 0), - "XorShiftRng.reseed called with an all zero seed."); - - self.x = w(seed[0]); - self.y = w(seed[1]); - self.z = w(seed[2]); - self.w = w(seed[3]); - } - - /// Create a new XorShiftRng. This will panic if `seed` is entirely 0. - fn from_seed(seed: [u32; 4]) -> XorShiftRng { - assert!(!seed.iter().all(|&x| x == 0), - "XorShiftRng::from_seed called with an all zero seed."); - - XorShiftRng { - x: w(seed[0]), - y: w(seed[1]), - z: w(seed[2]), - w: w(seed[3]), - } - } -} - -impl Rand for XorShiftRng { - fn rand(rng: &mut R) -> XorShiftRng { - let mut tuple: (u32, u32, u32, u32) = rng.gen(); - while tuple == (0, 0, 0, 0) { - tuple = rng.gen(); - } - let (x, y, z, w_) = tuple; - XorShiftRng { x: w(x), y: w(y), z: w(z), w: w(w_) } - } -} - /// A wrapper for generating floating point numbers uniformly in the /// open interval `(0,1)` (not including either endpoint). /// diff --git a/src/prng/mod.rs b/src/prng/mod.rs new file mode 100644 index 00000000000..0586eaed0e3 --- /dev/null +++ b/src/prng/mod.rs @@ -0,0 +1,45 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Pseudo random number generators are algorithms to produce *apparently +//! random* numbers deterministically, and usually fairly quickly. +//! +//! So long as the algorithm is computationally secure, is initialised with +//! sufficient entropy (i.e. unknown by an attacker), and its internal state is +//! also protected (unknown to an attacker), the output will also be +//! *computationally secure*. Computationally Secure Pseudo Random Number +//! Generators (CSPRNGs) are thus suitable sources of random numbers for +//! cryptography. There are a couple of gotchas here, however. First, the seed +//! used for initialisation must be unknown. Usually this should be provided by +//! the operating system and should usually be secure, however this may not +//! always be the case (especially soon after startup). Second, user-space +//! memory may be vulnerable, for example when written to swap space, and after +//! forking a child process should reinitialise any user-space PRNGs. For this +//! reason it may be preferable to source random numbers directly from the OS +//! for cryptographic applications. +//! +//! PRNGs are also widely used for non-cryptographic uses: randomised +//! algorithms, simulations, games. In these applications it is usually not +//! important for numbers to be cryptographically *unguessable*, but even +//! distribution and independence from other samples (from the point of view +//! of someone unaware of the algorithm used, at least) may still be important. +//! Good PRNGs should satisfy these properties, but do not take them for +//! granted; Wikipedia's article on +//! [Pseudorandom number generators](https://en.wikipedia.org/wiki/Pseudorandom_number_generator) +//! provides some background on this topic. +//! +//! Care should be taken when seeding (initialising) PRNGs. Some PRNGs have +//! short periods for some seeds. If one PRNG is seeded from another using the +//! same algorithm, it is possible that both will yield the same sequence of +//! values (with some lag). + +mod xorshift; + +pub use self::xorshift::XorShiftRng; diff --git a/src/prng/xorshift.rs b/src/prng/xorshift.rs new file mode 100644 index 00000000000..a8c3e667a88 --- /dev/null +++ b/src/prng/xorshift.rs @@ -0,0 +1,101 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Xorshift generators + +use std::num::Wrapping as w; +use {Rng, SeedableRng, Rand}; + +/// An Xorshift[1] random number +/// generator. +/// +/// The Xorshift algorithm is not suitable for cryptographic purposes +/// but is very fast. If you do not know for sure that it fits your +/// requirements, use a more secure one such as `IsaacRng` or `OsRng`. +/// +/// [1]: Marsaglia, George (July 2003). ["Xorshift +/// RNGs"](http://www.jstatsoft.org/v08/i14/paper). *Journal of +/// Statistical Software*. Vol. 8 (Issue 14). +#[allow(missing_copy_implementations)] +#[derive(Clone, Debug)] +pub struct XorShiftRng { + x: w, + y: w, + z: w, + w: w, +} + +impl XorShiftRng { + /// Creates a new XorShiftRng instance which is not seeded. + /// + /// The initial values of this RNG are constants, so all generators created + /// by this function will yield the same stream of random numbers. It is + /// highly recommended that this is created through `SeedableRng` instead of + /// this function + pub fn new_unseeded() -> XorShiftRng { + XorShiftRng { + x: w(0x193a6754), + y: w(0xa8a7d469), + z: w(0x97830e05), + w: w(0x113ba7bb), + } + } +} + +impl Rng for XorShiftRng { + #[inline] + fn next_u32(&mut self) -> u32 { + let x = self.x; + let t = x ^ (x << 11); + self.x = self.y; + self.y = self.z; + self.z = self.w; + let w_ = self.w; + self.w = w_ ^ (w_ >> 19) ^ (t ^ (t >> 8)); + self.w.0 + } +} + +impl SeedableRng<[u32; 4]> for XorShiftRng { + /// Reseed an XorShiftRng. This will panic if `seed` is entirely 0. + fn reseed(&mut self, seed: [u32; 4]) { + assert!(!seed.iter().all(|&x| x == 0), + "XorShiftRng.reseed called with an all zero seed."); + + self.x = w(seed[0]); + self.y = w(seed[1]); + self.z = w(seed[2]); + self.w = w(seed[3]); + } + + /// Create a new XorShiftRng. This will panic if `seed` is entirely 0. + fn from_seed(seed: [u32; 4]) -> XorShiftRng { + assert!(!seed.iter().all(|&x| x == 0), + "XorShiftRng::from_seed called with an all zero seed."); + + XorShiftRng { + x: w(seed[0]), + y: w(seed[1]), + z: w(seed[2]), + w: w(seed[3]), + } + } +} + +impl Rand for XorShiftRng { + fn rand(rng: &mut R) -> XorShiftRng { + let mut tuple: (u32, u32, u32, u32) = rng.gen(); + while tuple == (0, 0, 0, 0) { + tuple = rng.gen(); + } + let (x, y, z, w_) = tuple; + XorShiftRng { x: w(x), y: w(y), z: w(z), w: w(w_) } + } +} From 6387536360df4baa48876c7c691bae1dfd9ad50a Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Fri, 28 Jul 2017 13:55:48 +0100 Subject: [PATCH 003/247] Remove unnecessary FIXME (see also Rng::next_u32) --- src/isaac.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/isaac.rs b/src/isaac.rs index b70a8e61d3f..210d428a750 100644 --- a/src/isaac.rs +++ b/src/isaac.rs @@ -441,7 +441,6 @@ impl Clone for Isaac64Rng { } impl Rng for Isaac64Rng { - // FIXME #7771: having next_u32 like this should be unnecessary #[inline] fn next_u32(&mut self) -> u32 { self.next_u64() as u32 From 482eeba2d41f99d9d9c72c8f28664a4f55dae653 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Fri, 28 Jul 2017 13:57:53 +0100 Subject: [PATCH 004/247] Move w32/w64 types out of lib.rs --- src/chacha.rs | 5 ++++- src/isaac.rs | 7 ++++++- src/lib.rs | 6 ------ 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/chacha.rs b/src/chacha.rs index 1acec5e9bf5..c11b3076429 100644 --- a/src/chacha.rs +++ b/src/chacha.rs @@ -11,7 +11,10 @@ //! The ChaCha random number generator. use std::num::Wrapping as w; -use {Rng, SeedableRng, Rand, w32}; +use {Rng, SeedableRng, Rand}; + +#[allow(bad_style)] +type w32 = w; const KEY_WORDS : usize = 8; // 8 words for the 256-bit key const STATE_WORDS : usize = 16; diff --git a/src/isaac.rs b/src/isaac.rs index 210d428a750..a9ae30e493b 100644 --- a/src/isaac.rs +++ b/src/isaac.rs @@ -17,7 +17,12 @@ use std::iter::repeat; use std::num::Wrapping as w; use std::fmt; -use {Rng, SeedableRng, Rand, w32, w64}; +use {Rng, SeedableRng, Rand}; + +#[allow(bad_style)] +type w64 = w; +#[allow(bad_style)] +type w32 = w; const RAND_SIZE_LEN: usize = 8; const RAND_SIZE: u32 = 1 << RAND_SIZE_LEN; diff --git a/src/lib.rs b/src/lib.rs index 0df683b0181..d2cb6ce98a3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -253,7 +253,6 @@ use std::marker; use std::mem; use std::io; use std::rc::Rc; -use std::num::Wrapping as w; pub use os::OsRng; @@ -279,11 +278,6 @@ pub mod os; pub mod read; pub mod prng; -#[allow(bad_style)] -type w64 = w; -#[allow(bad_style)] -type w32 = w; - /// A type that can be randomly generated using an `Rng`. /// /// ## Built-in Implementations From 4b1903a23f50f49f2345674982fd4209c3a60148 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Fri, 28 Jul 2017 14:02:49 +0100 Subject: [PATCH 005/247] Move chacha and isaac to prng module --- src/lib.rs | 9 ++------- src/{ => prng}/chacha.rs | 6 ++++-- src/{ => prng}/isaac.rs | 0 src/prng/mod.rs | 4 ++++ 4 files changed, 10 insertions(+), 9 deletions(-) rename src/{ => prng}/chacha.rs (98%) rename src/{ => prng}/isaac.rs (100%) diff --git a/src/lib.rs b/src/lib.rs index d2cb6ce98a3..1a74c12b66d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -256,13 +256,10 @@ use std::rc::Rc; pub use os::OsRng; -pub use isaac::{IsaacRng, Isaac64Rng}; -pub use chacha::ChaChaRng; - #[cfg(target_pointer_width = "32")] -use IsaacRng as IsaacWordRng; +use prng::IsaacRng as IsaacWordRng; #[cfg(target_pointer_width = "64")] -use Isaac64Rng as IsaacWordRng; +use prng::Isaac64Rng as IsaacWordRng; use distributions::{Range, Sample}; use distributions::range::SampleRange; @@ -270,8 +267,6 @@ use distributions::range::SampleRange; use prng::XorShiftRng; pub mod distributions; -pub mod isaac; -pub mod chacha; pub mod reseeding; mod rand_impls; pub mod os; diff --git a/src/chacha.rs b/src/prng/chacha.rs similarity index 98% rename from src/chacha.rs rename to src/prng/chacha.rs index c11b3076429..bda5b84fa0c 100644 --- a/src/chacha.rs +++ b/src/prng/chacha.rs @@ -88,7 +88,8 @@ impl ChaChaRng { /// # Examples /// /// ```rust - /// use rand::{Rng, ChaChaRng}; + /// use rand::Rng; + /// use rand::prng::ChaChaRng; /// /// let mut ra = ChaChaRng::new_unseeded(); /// println!("{:?}", ra.next_u32()); @@ -119,7 +120,8 @@ impl ChaChaRng { /// # Examples /// /// ```rust - /// use rand::{Rng, ChaChaRng}; + /// use rand::Rng; + /// use rand::prng::ChaChaRng; /// /// let mut ra = ChaChaRng::new_unseeded(); /// ra.set_counter(0u64, 1234567890u64); diff --git a/src/isaac.rs b/src/prng/isaac.rs similarity index 100% rename from src/isaac.rs rename to src/prng/isaac.rs diff --git a/src/prng/mod.rs b/src/prng/mod.rs index 0586eaed0e3..1c7845875a0 100644 --- a/src/prng/mod.rs +++ b/src/prng/mod.rs @@ -40,6 +40,10 @@ //! same algorithm, it is possible that both will yield the same sequence of //! values (with some lag). +mod chacha; +mod isaac; mod xorshift; +pub use self::chacha::ChaChaRng; +pub use self::isaac::{IsaacRng, Isaac64Rng}; pub use self::xorshift::XorShiftRng; From aa5423b5ac5dd423bfca285e17f7b07547a555f3 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Fri, 28 Jul 2017 14:09:03 +0100 Subject: [PATCH 006/247] Make read and os modules private --- src/lib.rs | 8 +++++--- src/read.rs | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 1a74c12b66d..ae36bd24cee 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -254,6 +254,7 @@ use std::mem; use std::io; use std::rc::Rc; +pub use read::ReadRng; pub use os::OsRng; #[cfg(target_pointer_width = "32")] @@ -267,11 +268,12 @@ use distributions::range::SampleRange; use prng::XorShiftRng; pub mod distributions; +pub mod prng; pub mod reseeding; + mod rand_impls; -pub mod os; -pub mod read; -pub mod prng; +mod read; +mod os; /// A type that can be randomly generated using an `Rng`. /// diff --git a/src/read.rs b/src/read.rs index c7351b75937..4cc2717e2e3 100644 --- a/src/read.rs +++ b/src/read.rs @@ -24,10 +24,10 @@ use Rng; /// # Example /// /// ```rust -/// use rand::{read, Rng}; +/// use rand::{ReadRng, Rng}; /// /// let data = vec![1, 2, 3, 4, 5, 6, 7, 8]; -/// let mut rng = read::ReadRng::new(&data[..]); +/// let mut rng = ReadRng::new(&data[..]); /// println!("{:x}", rng.gen::()); /// ``` #[derive(Debug)] From b899667509bf8b591a0885ed9dd1d2445915fa81 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Fri, 28 Jul 2017 14:29:15 +0100 Subject: [PATCH 007/247] Export IsaacWordRng; don't use StdRng in seeded examples Reproducibility requires fixed algorithm as well as fixed seed --- src/lib.rs | 16 +++++++--------- src/prng/isaac.rs | 6 ++++++ src/prng/mod.rs | 2 +- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ae36bd24cee..a3bd308566a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -257,14 +257,10 @@ use std::rc::Rc; pub use read::ReadRng; pub use os::OsRng; -#[cfg(target_pointer_width = "32")] -use prng::IsaacRng as IsaacWordRng; -#[cfg(target_pointer_width = "64")] -use prng::Isaac64Rng as IsaacWordRng; - use distributions::{Range, Sample}; use distributions::range::SampleRange; +use prng::IsaacWordRng; use prng::XorShiftRng; pub mod distributions; @@ -683,10 +679,11 @@ pub trait SeedableRng: Rng { /// # Example /// /// ```rust - /// use rand::{Rng, SeedableRng, StdRng}; + /// use rand::{Rng, SeedableRng}; + /// use rand::prng::IsaacWordRng; /// /// let seed: &[_] = &[1, 2, 3, 4]; - /// let mut rng: StdRng = SeedableRng::from_seed(seed); + /// let mut rng: IsaacWordRng = SeedableRng::from_seed(seed); /// println!("{}", rng.gen::()); /// rng.reseed(&[5, 6, 7, 8]); /// println!("{}", rng.gen::()); @@ -698,10 +695,11 @@ pub trait SeedableRng: Rng { /// # Example /// /// ```rust - /// use rand::{Rng, SeedableRng, StdRng}; + /// use rand::{Rng, SeedableRng}; + /// use rand::prng::ChaChaRng; /// /// let seed: &[_] = &[1, 2, 3, 4]; - /// let mut rng: StdRng = SeedableRng::from_seed(seed); + /// let mut rng: ChaChaRng = SeedableRng::from_seed(seed); /// println!("{}", rng.gen::()); /// ``` fn from_seed(seed: Seed) -> Self; diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index a9ae30e493b..7592e31b1ff 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -19,6 +19,12 @@ use std::fmt; use {Rng, SeedableRng, Rand}; +/// Select 32- or 64-bit variant dependent on pointer size. +#[cfg(target_pointer_width = "32")] +pub use prng::IsaacRng as IsaacWordRng; +#[cfg(target_pointer_width = "64")] +pub use prng::Isaac64Rng as IsaacWordRng; + #[allow(bad_style)] type w64 = w; #[allow(bad_style)] diff --git a/src/prng/mod.rs b/src/prng/mod.rs index 1c7845875a0..eeb9f8fce2c 100644 --- a/src/prng/mod.rs +++ b/src/prng/mod.rs @@ -45,5 +45,5 @@ mod isaac; mod xorshift; pub use self::chacha::ChaChaRng; -pub use self::isaac::{IsaacRng, Isaac64Rng}; +pub use self::isaac::{IsaacRng, Isaac64Rng, IsaacWordRng}; pub use self::xorshift::XorShiftRng; From 8bde67d2f07725bc4b968565859a95b9745678cc Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Fri, 28 Jul 2017 14:42:15 +0100 Subject: [PATCH 008/247] Rename distributions module to dist This module should be heavily used, hence the short name for convenience --- benches/bench.rs | 2 +- benches/distributions/exponential.rs | 4 ++-- benches/distributions/gamma.rs | 4 ++-- benches/distributions/normal.rs | 4 ++-- src/{distributions => dist}/exponential.rs | 8 ++++---- src/{distributions => dist}/gamma.rs | 10 +++++----- src/{distributions => dist}/mod.rs | 2 +- src/{distributions => dist}/normal.rs | 10 +++++----- src/{distributions => dist}/range.rs | 6 +++--- src/{distributions => dist}/ziggurat_tables.rs | 0 src/lib.rs | 14 +++++++------- 11 files changed, 32 insertions(+), 32 deletions(-) rename src/{distributions => dist}/exponential.rs (94%) rename src/{distributions => dist}/gamma.rs (97%) rename src/{distributions => dist}/mod.rs (99%) rename src/{distributions => dist}/normal.rs (95%) rename src/{distributions => dist}/range.rs (98%) rename src/{distributions => dist}/ziggurat_tables.rs (100%) diff --git a/benches/bench.rs b/benches/bench.rs index 5fa92bdbea0..08b7fb6f181 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -5,7 +5,7 @@ extern crate rand; const RAND_BENCH_N: u64 = 1000; -mod distributions; +mod dist; use std::mem::size_of; use test::{black_box, Bencher}; diff --git a/benches/distributions/exponential.rs b/benches/distributions/exponential.rs index 152615d7ba3..f9b6dcb6bbf 100644 --- a/benches/distributions/exponential.rs +++ b/benches/distributions/exponential.rs @@ -1,8 +1,8 @@ use std::mem::size_of; use test::Bencher; use rand; -use rand::distributions::exponential::Exp; -use rand::distributions::Sample; +use rand::dist::exponential::Exp; +use rand::dist::Sample; #[bench] fn rand_exp(b: &mut Bencher) { diff --git a/benches/distributions/gamma.rs b/benches/distributions/gamma.rs index e365a1ac0ce..19b63795115 100644 --- a/benches/distributions/gamma.rs +++ b/benches/distributions/gamma.rs @@ -1,8 +1,8 @@ use std::mem::size_of; use test::Bencher; use rand; -use rand::distributions::Sample; -use rand::distributions::gamma::Gamma; +use rand::dist::Sample; +use rand::dist::gamma::Gamma; #[bench] fn bench_gamma_large_shape(b: &mut Bencher) { diff --git a/benches/distributions/normal.rs b/benches/distributions/normal.rs index 1c858b19b39..7c53c8f85ac 100644 --- a/benches/distributions/normal.rs +++ b/benches/distributions/normal.rs @@ -1,8 +1,8 @@ use std::mem::size_of; use test::Bencher; use rand; -use rand::distributions::Sample; -use rand::distributions::normal::Normal; +use rand::dist::Sample; +use rand::dist::normal::Normal; #[bench] fn rand_normal(b: &mut Bencher) { diff --git a/src/distributions/exponential.rs b/src/dist/exponential.rs similarity index 94% rename from src/distributions/exponential.rs rename to src/dist/exponential.rs index 1d14b2d563f..080468bbcc1 100644 --- a/src/distributions/exponential.rs +++ b/src/dist/exponential.rs @@ -11,7 +11,7 @@ //! The exponential distribution. use {Rng, Rand}; -use distributions::{ziggurat, ziggurat_tables, Sample}; +use dist::{ziggurat, ziggurat_tables, Sample}; /// A wrapper around an `f64` to generate Exp(1) random numbers. /// @@ -29,7 +29,7 @@ use distributions::{ziggurat, ziggurat_tables, Sample}; /// # Example /// /// ```rust -/// use rand::distributions::exponential::Exp1; +/// use rand::dist::exponential::Exp1; /// /// let Exp1(x) = rand::random(); /// println!("{}", x); @@ -65,7 +65,7 @@ impl Rand for Exp1 { /// # Example /// /// ```rust -/// use rand::distributions::{Exp, Sample}; +/// use rand::dist::{Exp, Sample}; /// /// let exp = Exp::new(2.0); /// let v = exp.sample(&mut rand::thread_rng()); @@ -96,7 +96,7 @@ impl Sample for Exp { #[cfg(test)] mod test { - use distributions::{Sample}; + use dist::{Sample}; use super::Exp; #[test] diff --git a/src/distributions/gamma.rs b/src/dist/gamma.rs similarity index 97% rename from src/distributions/gamma.rs rename to src/dist/gamma.rs index a31708997e3..69a243d20a0 100644 --- a/src/distributions/gamma.rs +++ b/src/dist/gamma.rs @@ -38,7 +38,7 @@ use super::{Sample, Exp}; /// # Example /// /// ```rust -/// use rand::distributions::{Sample, Gamma}; +/// use rand::dist::{Sample, Gamma}; /// /// let gamma = Gamma::new(2.0, 5.0); /// let v = gamma.sample(&mut rand::thread_rng()); @@ -182,7 +182,7 @@ impl Sample for GammaLargeShape { /// # Example /// /// ```rust -/// use rand::distributions::{ChiSquared, Sample}; +/// use rand::dist::{ChiSquared, Sample}; /// /// let chi = ChiSquared::new(11.0); /// let v = chi.sample(&mut rand::thread_rng()); @@ -237,7 +237,7 @@ impl Sample for ChiSquared { /// # Example /// /// ```rust -/// use rand::distributions::{FisherF, Sample}; +/// use rand::dist::{FisherF, Sample}; /// /// let f = FisherF::new(2.0, 32.0); /// let v = f.sample(&mut rand::thread_rng()); @@ -278,7 +278,7 @@ impl Sample for FisherF { /// # Example /// /// ```rust -/// use rand::distributions::{StudentT, Sample}; +/// use rand::dist::{StudentT, Sample}; /// /// let t = StudentT::new(11.0); /// let v = t.sample(&mut rand::thread_rng()); @@ -310,7 +310,7 @@ impl Sample for StudentT { #[cfg(test)] mod test { - use distributions::{Sample}; + use dist::{Sample}; use super::{ChiSquared, StudentT, FisherF}; #[test] diff --git a/src/distributions/mod.rs b/src/dist/mod.rs similarity index 99% rename from src/distributions/mod.rs rename to src/dist/mod.rs index 82987205d4f..84fa400a2fc 100644 --- a/src/distributions/mod.rs +++ b/src/dist/mod.rs @@ -83,7 +83,7 @@ pub struct Weighted { /// # Example /// /// ```rust -/// use rand::distributions::{Weighted, WeightedChoice, Sample}; +/// use rand::dist::{Weighted, WeightedChoice, Sample}; /// /// let mut items = vec!(Weighted { weight: 2, item: 'a' }, /// Weighted { weight: 4, item: 'b' }, diff --git a/src/distributions/normal.rs b/src/dist/normal.rs similarity index 95% rename from src/distributions/normal.rs rename to src/dist/normal.rs index ce43172a651..6349c41d845 100644 --- a/src/distributions/normal.rs +++ b/src/dist/normal.rs @@ -11,7 +11,7 @@ //! The normal and derived distributions. use {Rng, Rand, Open01}; -use distributions::{ziggurat, ziggurat_tables, Sample}; +use dist::{ziggurat, ziggurat_tables, Sample}; /// A wrapper around an `f64` to generate N(0, 1) random numbers /// (a.k.a. a standard normal, or Gaussian). @@ -28,7 +28,7 @@ use distributions::{ziggurat, ziggurat_tables, Sample}; /// # Example /// /// ```rust -/// use rand::distributions::normal::StandardNormal; +/// use rand::dist::normal::StandardNormal; /// /// let StandardNormal(x) = rand::random(); /// println!("{}", x); @@ -81,7 +81,7 @@ impl Rand for StandardNormal { /// # Example /// /// ```rust -/// use rand::distributions::{Normal, Sample}; +/// use rand::dist::{Normal, Sample}; /// /// // mean 2, standard deviation 3 /// let normal = Normal::new(2.0, 3.0); @@ -126,7 +126,7 @@ impl Sample for Normal { /// # Example /// /// ```rust -/// use rand::distributions::{LogNormal, Sample}; +/// use rand::dist::{LogNormal, Sample}; /// /// // mean 2, standard deviation 3 /// let log_normal = LogNormal::new(2.0, 3.0); @@ -159,7 +159,7 @@ impl Sample for LogNormal { #[cfg(test)] mod tests { - use distributions::{Sample}; + use dist::{Sample}; use super::{Normal, LogNormal}; #[test] diff --git a/src/distributions/range.rs b/src/dist/range.rs similarity index 98% rename from src/distributions/range.rs rename to src/dist/range.rs index 77547219cbe..07b376a9722 100644 --- a/src/distributions/range.rs +++ b/src/dist/range.rs @@ -15,7 +15,7 @@ use std::num::Wrapping as w; use Rng; -use distributions::{Sample}; +use dist::{Sample}; /// Sample values uniformly between two bounds. /// @@ -34,7 +34,7 @@ use distributions::{Sample}; /// # Example /// /// ```rust -/// use rand::distributions::{Sample, Range}; +/// use rand::dist::{Sample, Range}; /// /// fn main() { /// let between = Range::new(10, 10000); @@ -161,7 +161,7 @@ float_impl! { f64 } #[cfg(test)] mod tests { - use distributions::{Sample}; + use dist::{Sample}; use super::Range as Range; #[should_panic] diff --git a/src/distributions/ziggurat_tables.rs b/src/dist/ziggurat_tables.rs similarity index 100% rename from src/distributions/ziggurat_tables.rs rename to src/dist/ziggurat_tables.rs diff --git a/src/lib.rs b/src/lib.rs index a3bd308566a..f7662ec229e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,7 +16,7 @@ //! suffice, but sometimes an annotation is required, e.g. //! `rand::random::()`. //! -//! See the `distributions` submodule for sampling random numbers from +//! See the `dist` submodule for sampling random numbers from //! distributions like normal and exponential. //! //! # Usage @@ -110,7 +110,7 @@ //! and multiply this fraction by 4. //! //! ``` -//! use rand::distributions::{Sample, Range}; +//! use rand::dist::{Sample, Range}; //! //! fn main() { //! let between = Range::new(-1f64, 1.); @@ -153,7 +153,7 @@ //! //! ``` //! use rand::Rng; -//! use rand::distributions::{Sample, Range}; +//! use rand::dist::{Sample, Range}; //! //! struct SimulationResult { //! win: bool, @@ -257,13 +257,13 @@ use std::rc::Rc; pub use read::ReadRng; pub use os::OsRng; -use distributions::{Range, Sample}; -use distributions::range::SampleRange; +use dist::{Range, Sample}; +use dist::range::SampleRange; use prng::IsaacWordRng; use prng::XorShiftRng; -pub mod distributions; +pub mod dist; pub mod prng; pub mod reseeding; @@ -469,7 +469,7 @@ pub trait Rng { /// Generate a random value in the range [`low`, `high`). /// /// This is a convenience wrapper around - /// `distributions::Range`. If this function will be called + /// `dist::Range`. If this function will be called /// repeatedly with the same arguments, one should use `Range`, as /// that will amortize the computations that allow for perfect /// uniformity, as they only happen on initialization. From 01e28acf7bd7f852146bc6006299c553cdbdeb38 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Fri, 28 Jul 2017 14:48:57 +0100 Subject: [PATCH 009/247] Move WeightedChoice to a submodule This was taking up 3/4 of dist/mod.rs --- src/dist/mod.rs | 250 +--------------------------------------- src/dist/weighted.rs | 268 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 269 insertions(+), 249 deletions(-) create mode 100644 src/dist/weighted.rs diff --git a/src/dist/mod.rs b/src/dist/mod.rs index 84fa400a2fc..9277e0d9c9b 100644 --- a/src/dist/mod.rs +++ b/src/dist/mod.rs @@ -28,6 +28,7 @@ pub mod range; pub mod gamma; pub mod normal; pub mod exponential; +pub mod weighted; /// Types (distributions) that can be used to create a random instance of `T`. pub trait Sample { @@ -61,125 +62,6 @@ impl RandSample { } } -/// A value with a particular weight for use with `WeightedChoice`. -#[derive(Copy, Clone, Debug)] -pub struct Weighted { - /// The numerical weight of this item - pub weight: u32, - /// The actual item which is being weighted - pub item: T, -} - -/// A distribution that selects from a finite collection of weighted items. -/// -/// Each item has an associated weight that influences how likely it -/// is to be chosen: higher weight is more likely. -/// -/// The `Clone` restriction is a limitation of the `Sample` trait. -/// Note that `&T` is (cheaply) `Clone` for -/// all `T`, as is `u32`, so one can store references or indices into -/// another vector. -/// -/// # Example -/// -/// ```rust -/// use rand::dist::{Weighted, WeightedChoice, Sample}; -/// -/// let mut items = vec!(Weighted { weight: 2, item: 'a' }, -/// Weighted { weight: 4, item: 'b' }, -/// Weighted { weight: 1, item: 'c' }); -/// let wc = WeightedChoice::new(&mut items); -/// let mut rng = rand::thread_rng(); -/// for _ in 0..16 { -/// // on average prints 'a' 4 times, 'b' 8 and 'c' twice. -/// println!("{}", wc.sample(&mut rng)); -/// } -/// ``` -#[derive(Debug)] -pub struct WeightedChoice<'a, T:'a> { - items: &'a mut [Weighted], - weight_range: Range -} - -impl<'a, T: Clone> WeightedChoice<'a, T> { - /// Create a new `WeightedChoice`. - /// - /// Panics if: - /// - `v` is empty - /// - the total weight is 0 - /// - the total weight is larger than a `u32` can contain. - pub fn new(items: &'a mut [Weighted]) -> WeightedChoice<'a, T> { - // strictly speaking, this is subsumed by the total weight == 0 case - assert!(!items.is_empty(), "WeightedChoice::new called with no items"); - - let mut running_total: u32 = 0; - - // we convert the list from individual weights to cumulative - // weights so we can binary search. This *could* drop elements - // with weight == 0 as an optimisation. - for item in items.iter_mut() { - running_total = match running_total.checked_add(item.weight) { - Some(n) => n, - None => panic!("WeightedChoice::new called with a total weight \ - larger than a u32 can contain") - }; - - item.weight = running_total; - } - assert!(running_total != 0, "WeightedChoice::new called with a total weight of 0"); - - WeightedChoice { - items: items, - // we're likely to be generating numbers in this range - // relatively often, so might as well cache it - weight_range: Range::new(0, running_total) - } - } -} - -impl<'a, T: Clone> Sample for WeightedChoice<'a, T> { - fn sample(&self, rng: &mut R) -> T { - // we want to find the first element that has cumulative - // weight > sample_weight, which we do by binary since the - // cumulative weights of self.items are sorted. - - // choose a weight in [0, total_weight) - let sample_weight = self.weight_range.sample(rng); - - // short circuit when it's the first item - if sample_weight < self.items[0].weight { - return self.items[0].item.clone(); - } - - let mut idx = 0; - let mut modifier = self.items.len(); - - // now we know that every possibility has an element to the - // left, so we can just search for the last element that has - // cumulative weight <= sample_weight, then the next one will - // be "it". (Note that this greatest element will never be the - // last element of the vector, since sample_weight is chosen - // in [0, total_weight) and the cumulative weight of the last - // one is exactly the total weight.) - while modifier > 1 { - let i = idx + modifier / 2; - if self.items[i].weight <= sample_weight { - // we're small, so look to the right, but allow this - // exact element still. - idx = i; - // we need the `/ 2` to round up otherwise we'll drop - // the trailing elements when `modifier` is odd. - modifier += 1; - } else { - // otherwise we're too big, so go left. (i.e. do - // nothing) - } - modifier /= 2; - } - return self.items[idx + 1].item.clone(); - } -} - mod ziggurat_tables; /// Sample a random number using the Ziggurat method (specifically the @@ -245,133 +127,3 @@ fn ziggurat( } } } - -#[cfg(test)] -mod tests { - - use {Rng, Rand}; - use super::{RandSample, WeightedChoice, Weighted, Sample}; - - #[derive(PartialEq, Debug)] - struct ConstRand(usize); - impl Rand for ConstRand { - fn rand(_: &mut R) -> ConstRand { - ConstRand(0) - } - } - - // 0, 1, 2, 3, ... - struct CountingRng { i: u32 } - impl Rng for CountingRng { - fn next_u32(&mut self) -> u32 { - self.i += 1; - self.i - 1 - } - fn next_u64(&mut self) -> u64 { - self.next_u32() as u64 - } - } - - #[test] - fn test_rand_sample() { - let rand_sample = RandSample::::new(); - - assert_eq!(rand_sample.sample(&mut ::test::rng()), ConstRand(0)); - } - #[test] - fn test_weighted_choice() { - // this makes assumptions about the internal implementation of - // WeightedChoice, specifically: it doesn't reorder the items, - // it doesn't do weird things to the RNG (so 0 maps to 0, 1 to - // 1, internally; modulo a modulo operation). - - macro_rules! t { - ($items:expr, $expected:expr) => {{ - let mut items = $items; - let wc = WeightedChoice::new(&mut items); - let expected = $expected; - - let mut rng = CountingRng { i: 0 }; - - for &val in expected.iter() { - assert_eq!(wc.sample(&mut rng), val) - } - }} - } - - t!(vec!(Weighted { weight: 1, item: 10}), [10]); - - // skip some - t!(vec!(Weighted { weight: 0, item: 20}, - Weighted { weight: 2, item: 21}, - Weighted { weight: 0, item: 22}, - Weighted { weight: 1, item: 23}), - [21,21, 23]); - - // different weights - t!(vec!(Weighted { weight: 4, item: 30}, - Weighted { weight: 3, item: 31}), - [30,30,30,30, 31,31,31]); - - // check that we're binary searching - // correctly with some vectors of odd - // length. - t!(vec!(Weighted { weight: 1, item: 40}, - Weighted { weight: 1, item: 41}, - Weighted { weight: 1, item: 42}, - Weighted { weight: 1, item: 43}, - Weighted { weight: 1, item: 44}), - [40, 41, 42, 43, 44]); - t!(vec!(Weighted { weight: 1, item: 50}, - Weighted { weight: 1, item: 51}, - Weighted { weight: 1, item: 52}, - Weighted { weight: 1, item: 53}, - Weighted { weight: 1, item: 54}, - Weighted { weight: 1, item: 55}, - Weighted { weight: 1, item: 56}), - [50, 51, 52, 53, 54, 55, 56]); - } - - #[test] - fn test_weighted_clone_initialization() { - let initial : Weighted = Weighted {weight: 1, item: 1}; - let clone = initial.clone(); - assert_eq!(initial.weight, clone.weight); - assert_eq!(initial.item, clone.item); - } - - #[test] #[should_panic] - fn test_weighted_clone_change_weight() { - let initial : Weighted = Weighted {weight: 1, item: 1}; - let mut clone = initial.clone(); - clone.weight = 5; - assert_eq!(initial.weight, clone.weight); - } - - #[test] #[should_panic] - fn test_weighted_clone_change_item() { - let initial : Weighted = Weighted {weight: 1, item: 1}; - let mut clone = initial.clone(); - clone.item = 5; - assert_eq!(initial.item, clone.item); - - } - - #[test] #[should_panic] - fn test_weighted_choice_no_items() { - WeightedChoice::::new(&mut []); - } - #[test] #[should_panic] - fn test_weighted_choice_zero_weight() { - WeightedChoice::new(&mut [Weighted { weight: 0, item: 0}, - Weighted { weight: 0, item: 1}]); - } - #[test] #[should_panic] - fn test_weighted_choice_weight_overflows() { - let x = ::std::u32::MAX / 2; // x + x + 2 is the overflow - WeightedChoice::new(&mut [Weighted { weight: x, item: 0 }, - Weighted { weight: 1, item: 1 }, - Weighted { weight: x, item: 2 }, - Weighted { weight: 1, item: 3 }]); - } -} diff --git a/src/dist/weighted.rs b/src/dist/weighted.rs new file mode 100644 index 00000000000..8794e5a42f3 --- /dev/null +++ b/src/dist/weighted.rs @@ -0,0 +1,268 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Weighted choices +//! +//! TODO: evaluate whether this functionality should be stabilised as-is, +//! adapted, or removed entirely. + +use Rng; +use dist::{Range, Sample}; + +/// A value with a particular weight for use with `WeightedChoice`. +#[derive(Copy, Clone, Debug)] +pub struct Weighted { + /// The numerical weight of this item + pub weight: u32, + /// The actual item which is being weighted + pub item: T, +} + +/// A distribution that selects from a finite collection of weighted items. +/// +/// Each item has an associated weight that influences how likely it +/// is to be chosen: higher weight is more likely. +/// +/// The `Clone` restriction is a limitation of the `Sample` trait. +/// Note that `&T` is (cheaply) `Clone` for +/// all `T`, as is `u32`, so one can store references or indices into +/// another vector. +/// +/// # Example +/// +/// ```rust +/// use rand::dist::Sample; +/// use rand::dist::weighted::{Weighted, WeightedChoice}; +/// +/// let mut items = vec!(Weighted { weight: 2, item: 'a' }, +/// Weighted { weight: 4, item: 'b' }, +/// Weighted { weight: 1, item: 'c' }); +/// let wc = WeightedChoice::new(&mut items); +/// let mut rng = rand::thread_rng(); +/// for _ in 0..16 { +/// // on average prints 'a' 4 times, 'b' 8 and 'c' twice. +/// println!("{}", wc.sample(&mut rng)); +/// } +/// ``` +#[derive(Debug)] +pub struct WeightedChoice<'a, T:'a> { + items: &'a mut [Weighted], + weight_range: Range +} + +impl<'a, T: Clone> WeightedChoice<'a, T> { + /// Create a new `WeightedChoice`. + /// + /// Panics if: + /// - `v` is empty + /// - the total weight is 0 + /// - the total weight is larger than a `u32` can contain. + pub fn new(items: &'a mut [Weighted]) -> WeightedChoice<'a, T> { + // strictly speaking, this is subsumed by the total weight == 0 case + assert!(!items.is_empty(), "WeightedChoice::new called with no items"); + + let mut running_total: u32 = 0; + + // we convert the list from individual weights to cumulative + // weights so we can binary search. This *could* drop elements + // with weight == 0 as an optimisation. + for item in items.iter_mut() { + running_total = match running_total.checked_add(item.weight) { + Some(n) => n, + None => panic!("WeightedChoice::new called with a total weight \ + larger than a u32 can contain") + }; + + item.weight = running_total; + } + assert!(running_total != 0, "WeightedChoice::new called with a total weight of 0"); + + WeightedChoice { + items: items, + // we're likely to be generating numbers in this range + // relatively often, so might as well cache it + weight_range: Range::new(0, running_total) + } + } +} + +impl<'a, T: Clone> Sample for WeightedChoice<'a, T> { + fn sample(&self, rng: &mut R) -> T { + // we want to find the first element that has cumulative + // weight > sample_weight, which we do by binary since the + // cumulative weights of self.items are sorted. + + // choose a weight in [0, total_weight) + let sample_weight = self.weight_range.sample(rng); + + // short circuit when it's the first item + if sample_weight < self.items[0].weight { + return self.items[0].item.clone(); + } + + let mut idx = 0; + let mut modifier = self.items.len(); + + // now we know that every possibility has an element to the + // left, so we can just search for the last element that has + // cumulative weight <= sample_weight, then the next one will + // be "it". (Note that this greatest element will never be the + // last element of the vector, since sample_weight is chosen + // in [0, total_weight) and the cumulative weight of the last + // one is exactly the total weight.) + while modifier > 1 { + let i = idx + modifier / 2; + if self.items[i].weight <= sample_weight { + // we're small, so look to the right, but allow this + // exact element still. + idx = i; + // we need the `/ 2` to round up otherwise we'll drop + // the trailing elements when `modifier` is odd. + modifier += 1; + } else { + // otherwise we're too big, so go left. (i.e. do + // nothing) + } + modifier /= 2; + } + return self.items[idx + 1].item.clone(); + } +} + +#[cfg(test)] +mod tests { + + use {Rng, Rand}; + use dist::{RandSample, Sample}; + use super::{WeightedChoice, Weighted}; + + #[derive(PartialEq, Debug)] + struct ConstRand(usize); + impl Rand for ConstRand { + fn rand(_: &mut R) -> ConstRand { + ConstRand(0) + } + } + + // 0, 1, 2, 3, ... + struct CountingRng { i: u32 } + impl Rng for CountingRng { + fn next_u32(&mut self) -> u32 { + self.i += 1; + self.i - 1 + } + fn next_u64(&mut self) -> u64 { + self.next_u32() as u64 + } + } + + #[test] + fn test_rand_sample() { + let rand_sample = RandSample::::new(); + + assert_eq!(rand_sample.sample(&mut ::test::rng()), ConstRand(0)); + } + #[test] + fn test_weighted_choice() { + // this makes assumptions about the internal implementation of + // WeightedChoice, specifically: it doesn't reorder the items, + // it doesn't do weird things to the RNG (so 0 maps to 0, 1 to + // 1, internally; modulo a modulo operation). + + macro_rules! t { + ($items:expr, $expected:expr) => {{ + let mut items = $items; + let wc = WeightedChoice::new(&mut items); + let expected = $expected; + + let mut rng = CountingRng { i: 0 }; + + for &val in expected.iter() { + assert_eq!(wc.sample(&mut rng), val) + } + }} + } + + t!(vec!(Weighted { weight: 1, item: 10}), [10]); + + // skip some + t!(vec!(Weighted { weight: 0, item: 20}, + Weighted { weight: 2, item: 21}, + Weighted { weight: 0, item: 22}, + Weighted { weight: 1, item: 23}), + [21,21, 23]); + + // different weights + t!(vec!(Weighted { weight: 4, item: 30}, + Weighted { weight: 3, item: 31}), + [30,30,30,30, 31,31,31]); + + // check that we're binary searching + // correctly with some vectors of odd + // length. + t!(vec!(Weighted { weight: 1, item: 40}, + Weighted { weight: 1, item: 41}, + Weighted { weight: 1, item: 42}, + Weighted { weight: 1, item: 43}, + Weighted { weight: 1, item: 44}), + [40, 41, 42, 43, 44]); + t!(vec!(Weighted { weight: 1, item: 50}, + Weighted { weight: 1, item: 51}, + Weighted { weight: 1, item: 52}, + Weighted { weight: 1, item: 53}, + Weighted { weight: 1, item: 54}, + Weighted { weight: 1, item: 55}, + Weighted { weight: 1, item: 56}), + [50, 51, 52, 53, 54, 55, 56]); + } + + #[test] + fn test_weighted_clone_initialization() { + let initial : Weighted = Weighted {weight: 1, item: 1}; + let clone = initial.clone(); + assert_eq!(initial.weight, clone.weight); + assert_eq!(initial.item, clone.item); + } + + #[test] #[should_panic] + fn test_weighted_clone_change_weight() { + let initial : Weighted = Weighted {weight: 1, item: 1}; + let mut clone = initial.clone(); + clone.weight = 5; + assert_eq!(initial.weight, clone.weight); + } + + #[test] #[should_panic] + fn test_weighted_clone_change_item() { + let initial : Weighted = Weighted {weight: 1, item: 1}; + let mut clone = initial.clone(); + clone.item = 5; + assert_eq!(initial.item, clone.item); + + } + + #[test] #[should_panic] + fn test_weighted_choice_no_items() { + WeightedChoice::::new(&mut []); + } + #[test] #[should_panic] + fn test_weighted_choice_zero_weight() { + WeightedChoice::new(&mut [Weighted { weight: 0, item: 0}, + Weighted { weight: 0, item: 1}]); + } + #[test] #[should_panic] + fn test_weighted_choice_weight_overflows() { + let x = ::std::u32::MAX / 2; // x + x + 2 is the overflow + WeightedChoice::new(&mut [Weighted { weight: x, item: 0 }, + Weighted { weight: 1, item: 1 }, + Weighted { weight: x, item: 2 }, + Weighted { weight: 1, item: 3 }]); + } +} From d858e9415b6e6d77d313329b6b48266c430fcf38 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Fri, 28 Jul 2017 15:27:18 +0100 Subject: [PATCH 010/247] Add dist::uniform; implement for integer types --- src/dist/mod.rs | 4 + src/dist/uniform.rs | 182 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 186 insertions(+) create mode 100644 src/dist/uniform.rs diff --git a/src/dist/mod.rs b/src/dist/mod.rs index 9277e0d9c9b..9a0d15e28ba 100644 --- a/src/dist/mod.rs +++ b/src/dist/mod.rs @@ -14,16 +14,20 @@ //! generated values; for example `Range` needs to know its upper and lower //! bounds. Distributions use the `Sample` trait to yield values: call //! `dist.sample(&mut rng)` to get a random variable. +//! +//! TODO: is it worth exposing both submodules and re-exporting their members? use std::marker; use {Rng, Rand}; +pub use self::uniform::{Uniform, uniform}; pub use self::range::Range; pub use self::gamma::{Gamma, ChiSquared, FisherF, StudentT}; pub use self::normal::{Normal, LogNormal}; pub use self::exponential::Exp; +pub mod uniform; pub mod range; pub mod gamma; pub mod normal; diff --git a/src/dist/uniform.rs b/src/dist/uniform.rs new file mode 100644 index 00000000000..8662c0bc9f0 --- /dev/null +++ b/src/dist/uniform.rs @@ -0,0 +1,182 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Generating uniformly distributed numbers +//! +//! TODO: is there value in having both `uniform` and `Uniform`? + +use std::mem; +use std::marker::PhantomData; + +use Rng; +use dist::Sample; + +/// Sample values uniformly over the whole range supported by the type +pub fn uniform(rng: &mut R) -> T { + T::sample_uniform(rng) +} + +/// Sample values uniformly over the whole range supported by the type +pub trait SampleUniform: Sized { + /// Sample a value from a RNG + fn sample_uniform(rng: &mut R) -> Self; +} + +impl SampleUniform for isize { + #[inline] + fn sample_uniform(rng: &mut R) -> isize { + if mem::size_of::() == 4 { + rng.gen::() as isize + } else { + rng.gen::() as isize + } + } +} + +impl SampleUniform for i8 { + #[inline] + fn sample_uniform(rng: &mut R) -> i8 { + rng.next_u32() as i8 + } +} + +impl SampleUniform for i16 { + #[inline] + fn sample_uniform(rng: &mut R) -> i16 { + rng.next_u32() as i16 + } +} + +impl SampleUniform for i32 { + #[inline] + fn sample_uniform(rng: &mut R) -> i32 { + rng.next_u32() as i32 + } +} + +impl SampleUniform for i64 { + #[inline] + fn sample_uniform(rng: &mut R) -> i64 { + rng.next_u64() as i64 + } +} + +#[cfg(feature = "i128_support")] +impl SampleUniform for i128 { + #[inline] + fn sample_uniform(rng: &mut R) -> i128 { + rng.gen::() as i128 + } +} + +impl SampleUniform for usize { + #[inline] + fn sample_uniform(rng: &mut R) -> usize { + if mem::size_of::() == 4 { + rng.gen::() as usize + } else { + rng.gen::() as usize + } + } +} + +impl SampleUniform for u8 { + #[inline] + fn sample_uniform(rng: &mut R) -> u8 { + rng.next_u32() as u8 + } +} + +impl SampleUniform for u16 { + #[inline] + fn sample_uniform(rng: &mut R) -> u16 { + rng.next_u32() as u16 + } +} + +impl SampleUniform for u32 { + #[inline] + fn sample_uniform(rng: &mut R) -> u32 { + rng.next_u32() + } +} + +impl SampleUniform for u64 { + #[inline] + fn sample_uniform(rng: &mut R) -> u64 { + rng.next_u64() + } +} + +#[cfg(feature = "i128_support")] +impl SampleUniform for u128 { + #[inline] + fn sample_uniform(rng: &mut R) -> u128 { + ((rng.next_u64() as u128) << 64) | (rng.next_u64() as u128) + } +} + +/// Sample values uniformly over the whole range supported by the type. +/// +/// No internal state. +#[derive(Clone, Copy, Debug, Default)] +pub struct Uniform { + _marker: PhantomData, +} + +impl Uniform { + /// Create an instance. Should optimise to nothing, since there is no + /// internal state. + pub fn new() -> Self { + Uniform { + _marker: PhantomData + } + } +} + +impl Sample for Uniform { + fn sample(&self, rng: &mut R) -> T { + T::sample_uniform(rng) + } +} + + +#[cfg(test)] +mod tests { + use dist::{uniform, Uniform, Sample}; + use dist::uniform::SampleUniform; + + #[test] + fn test_integers() { + let mut rng = ::test::rng(); + + let _: i32 = SampleUniform::sample_uniform(&mut rng); + let _: i32 = i32::sample_uniform(&mut rng); + + let _: isize = uniform(&mut rng); + let _: i8 = uniform(&mut rng); + let _: i16 = uniform(&mut rng); + let _: i32 = uniform(&mut rng); + let _: i64 = uniform(&mut rng); + #[cfg(feature = "i128_support")] + let _: i128 = uniform(&mut rng); + + let _: usize = uniform(&mut rng); + let _: u8 = uniform(&mut rng); + let _: u16 = uniform(&mut rng); + let _: u32 = uniform(&mut rng); + let _: u64 = uniform(&mut rng); + #[cfg(feature = "i128_support")] + let _: u128 = uniform(&mut rng); + + let dist = Uniform::::new(); + let _ = dist.sample(&mut rng); + } +} From b08bd97f82a670e7bce98d8a98893ca862593e7a Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Fri, 28 Jul 2017 16:20:11 +0100 Subject: [PATCH 011/247] Replace Rng::gen_iter() with Rng::iter_map(Fn(&mut R) -> T) --- src/lib.rs | 60 ++++++++++++++++++++++++++-------------------- src/prng/chacha.rs | 4 ++-- src/prng/isaac.rs | 8 +++---- 3 files changed, 40 insertions(+), 32 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index f7662ec229e..729802a2acb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -249,7 +249,6 @@ use std::cell::RefCell; -use std::marker; use std::mem; use std::io; use std::rc::Rc; @@ -448,22 +447,32 @@ pub trait Rng { Rand::rand(self) } - /// Return an iterator that will yield an infinite number of randomly - /// generated items. - /// + /// Yield an iterator applying some function to self. + /// + /// Unfortunately this is only possible with static dispatch (i.e. where + /// `Self: Sized`). [Why? Because the method must be generic, to support + /// the lifetime bound on the `&mut Rng` field of the returned object, if + /// not also for types `T` and `F`; representing this field requires that + /// `Rng` trait objects can be constructed, but trait objects cannot be + /// constructed for traits with generic methods without a `Sized` bound. + /// But the starting point was wanting a dynamic version of this method, + /// i.e. not requiring the `Sized` bound. + /// See `rustc --explain E0038` for more.] + /// /// # Example /// /// ``` /// use rand::{thread_rng, Rng}; + /// use rand::dist::uniform; /// /// let mut rng = thread_rng(); - /// let x = rng.gen_iter::().take(10).collect::>(); + /// let x = rng.iter_map(|rng| uniform(rng)).take(10).collect::>(); /// println!("{:?}", x); - /// println!("{:?}", rng.gen_iter::<(f64, bool)>().take(5) - /// .collect::>()); /// ``` - fn gen_iter<'a, T: Rand>(&'a mut self) -> Generator<'a, T, Self> where Self: Sized { - Generator { rng: self, _marker: marker::PhantomData } + fn iter_map<'a, T, F>(&'a mut self, f: F) -> RngIterator<'a, Self, T, F> + where Self: Sized, F: Fn(&mut Self) -> T + { + RngIterator { rng: self, f: f } } /// Generate a random value in the range [`low`, `high`). @@ -628,23 +637,21 @@ impl Rng for Box where R: Rng { } } -/// Iterator which will generate a stream of random items. -/// -/// This iterator is created via the [`gen_iter`] method on [`Rng`]. -/// -/// [`gen_iter`]: trait.Rng.html#method.gen_iter -/// [`Rng`]: trait.Rng.html +/// Pseudo-Iterator encapsulating a random number generator. +/// See [`Rng::iter`](trait.Rng.html#method.iter). +/// +/// This only implements a [`map`](struct.RngIterator.html#method.map) method. #[derive(Debug)] -pub struct Generator<'a, T, R:'a> { +pub struct RngIterator<'a, R:'a, T, F: Fn(&mut R) -> T> { rng: &'a mut R, - _marker: marker::PhantomData T>, + f: F } -impl<'a, T: Rand, R: Rng> Iterator for Generator<'a, T, R> { +impl<'a, R:'a, T, F: Fn(&mut R) -> T> Iterator for RngIterator<'a, R, T, F> { type Item = T; - + fn next(&mut self) -> Option { - Some(self.rng.gen()) + Some((self.f)(self.rng)) } } @@ -946,7 +953,8 @@ pub fn sample(rng: &mut R, iterable: I, amount: usize) -> Vec #[cfg(test)] mod test { - use super::{Rng, thread_rng, random, SeedableRng, StdRng, sample}; + use {Rng, thread_rng, random, SeedableRng, StdRng, sample}; + use dist::uniform; use std::iter::repeat; pub struct MyRng { inner: R } @@ -1068,9 +1076,9 @@ mod test { #[test] fn test_gen_vec() { let mut r = thread_rng(); - assert_eq!(r.gen_iter::().take(0).count(), 0); - assert_eq!(r.gen_iter::().take(10).count(), 10); - assert_eq!(r.gen_iter::().take(16).count(), 16); + assert_eq!(r.iter_map(|rng| rng.next_u32()).take(0).count(), 0); + assert_eq!(r.iter_map(|rng| uniform::(rng)).take(10).count(), 10); + assert_eq!(r.iter_map(|rng| rng.gen::()).take(16).count(), 16); } #[test] @@ -1172,7 +1180,7 @@ mod test { #[test] fn test_std_rng_seeded() { - let s = thread_rng().gen_iter::().take(256).collect::>(); + let s = thread_rng().iter_map(|rng| uniform(rng)).take(256).collect::>(); let mut ra: StdRng = SeedableRng::from_seed(&s[..]); let mut rb: StdRng = SeedableRng::from_seed(&s[..]); assert!(iter_eq(ra.gen_ascii_chars().take(100), @@ -1181,7 +1189,7 @@ mod test { #[test] fn test_std_rng_reseed() { - let s = thread_rng().gen_iter::().take(256).collect::>(); + let s = thread_rng().iter_map(|rng| uniform(rng)).take(256).collect::>(); let mut r: StdRng = SeedableRng::from_seed(&s[..]); let string1 = r.gen_ascii_chars().take(100).collect::(); diff --git a/src/prng/chacha.rs b/src/prng/chacha.rs index bda5b84fa0c..a2275607922 100644 --- a/src/prng/chacha.rs +++ b/src/prng/chacha.rs @@ -241,7 +241,7 @@ mod test { #[test] fn test_rng_rand_seeded() { - let s = ::test::rng().gen_iter::().take(8).collect::>(); + let s = ::test::rng().iter_map(|rng| rng.next_u32()).take(8).collect::>(); let mut ra: ChaChaRng = SeedableRng::from_seed(&s[..]); let mut rb: ChaChaRng = SeedableRng::from_seed(&s[..]); assert!(::test::iter_eq(ra.gen_ascii_chars().take(100), @@ -259,7 +259,7 @@ mod test { #[test] fn test_rng_reseed() { - let s = ::test::rng().gen_iter::().take(8).collect::>(); + let s = ::test::rng().iter_map(|rng| rng.next_u32()).take(8).collect::>(); let mut r: ChaChaRng = SeedableRng::from_seed(&s[..]); let string1: String = r.gen_ascii_chars().take(100).collect(); diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index 7592e31b1ff..f80f2a58670 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -533,7 +533,7 @@ mod test { #[test] fn test_rng_32_rand_seeded() { - let s = ::test::rng().gen_iter::().take(256).collect::>(); + let s = ::test::rng().iter_map(|rng| rng.next_u32()).take(256).collect::>(); let mut ra: IsaacRng = SeedableRng::from_seed(&s[..]); let mut rb: IsaacRng = SeedableRng::from_seed(&s[..]); assert!(::test::iter_eq(ra.gen_ascii_chars().take(100), @@ -541,7 +541,7 @@ mod test { } #[test] fn test_rng_64_rand_seeded() { - let s = ::test::rng().gen_iter::().take(256).collect::>(); + let s = ::test::rng().iter_map(|rng| rng.next_u64()).take(256).collect::>(); let mut ra: Isaac64Rng = SeedableRng::from_seed(&s[..]); let mut rb: Isaac64Rng = SeedableRng::from_seed(&s[..]); assert!(::test::iter_eq(ra.gen_ascii_chars().take(100), @@ -567,7 +567,7 @@ mod test { #[test] fn test_rng_32_reseed() { - let s = ::test::rng().gen_iter::().take(256).collect::>(); + let s = ::test::rng().iter_map(|rng| rng.next_u32()).take(256).collect::>(); let mut r: IsaacRng = SeedableRng::from_seed(&s[..]); let string1: String = r.gen_ascii_chars().take(100).collect(); @@ -578,7 +578,7 @@ mod test { } #[test] fn test_rng_64_reseed() { - let s = ::test::rng().gen_iter::().take(256).collect::>(); + let s = ::test::rng().iter_map(|rng| rng.next_u64()).take(256).collect::>(); let mut r: Isaac64Rng = SeedableRng::from_seed(&s[..]); let string1: String = r.gen_ascii_chars().take(100).collect(); From 75f6c0ff42508f9b5d5378291cfcb166089021e9 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Fri, 28 Jul 2017 17:02:07 +0100 Subject: [PATCH 012/247] Migrate dist and prng modules away from using Rand on integer types This is the first of several commits to remove Rand --- src/dist/mod.rs | 2 +- src/dist/range.rs | 3 ++- src/dist/uniform.rs | 8 ++++---- src/prng/chacha.rs | 2 +- src/prng/xorshift.rs | 9 ++++++--- 5 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/dist/mod.rs b/src/dist/mod.rs index 9a0d15e28ba..05d87ca81ad 100644 --- a/src/dist/mod.rs +++ b/src/dist/mod.rs @@ -107,7 +107,7 @@ fn ziggurat( // efficiently and overload next_f32/f64, so by not calling it // this may be slower than it would be otherwise.) // FIXME: investigate/optimise for the above. - let bits: u64 = rng.gen(); + let bits: u64 = uniform(rng); let i = (bits & 0xff) as usize; let f = (bits >> 11) as f64 / SCALE; diff --git a/src/dist/range.rs b/src/dist/range.rs index 07b376a9722..4c139b2a413 100644 --- a/src/dist/range.rs +++ b/src/dist/range.rs @@ -112,9 +112,10 @@ macro_rules! integer_impl { #[inline] fn sample_range(r: &Range<$ty>, rng: &mut R) -> $ty { + use $crate::dist::uniform; loop { // rejection sample - let v = rng.gen::<$unsigned>(); + let v: $unsigned = uniform(rng); // until we find something that fits into the // region which r.range evenly divides (this will // be uniformly distributed) diff --git a/src/dist/uniform.rs b/src/dist/uniform.rs index 8662c0bc9f0..c2439ab24fb 100644 --- a/src/dist/uniform.rs +++ b/src/dist/uniform.rs @@ -33,9 +33,9 @@ impl SampleUniform for isize { #[inline] fn sample_uniform(rng: &mut R) -> isize { if mem::size_of::() == 4 { - rng.gen::() as isize + i32::sample_uniform(rng) as isize } else { - rng.gen::() as isize + i64::sample_uniform(rng) as isize } } } @@ -80,9 +80,9 @@ impl SampleUniform for usize { #[inline] fn sample_uniform(rng: &mut R) -> usize { if mem::size_of::() == 4 { - rng.gen::() as usize + u32::sample_uniform(rng) as usize } else { - rng.gen::() as usize + u64::sample_uniform(rng) as usize } } } diff --git a/src/prng/chacha.rs b/src/prng/chacha.rs index a2275607922..e3c9fd8e5e1 100644 --- a/src/prng/chacha.rs +++ b/src/prng/chacha.rs @@ -227,7 +227,7 @@ impl Rand for ChaChaRng { fn rand(other: &mut R) -> ChaChaRng { let mut key : [u32; KEY_WORDS] = [0; KEY_WORDS]; for word in key.iter_mut() { - *word = other.gen(); + *word = other.next_u32(); } SeedableRng::from_seed(&key[..]) } diff --git a/src/prng/xorshift.rs b/src/prng/xorshift.rs index a8c3e667a88..6df5a3cd9eb 100644 --- a/src/prng/xorshift.rs +++ b/src/prng/xorshift.rs @@ -91,9 +91,12 @@ impl SeedableRng<[u32; 4]> for XorShiftRng { impl Rand for XorShiftRng { fn rand(rng: &mut R) -> XorShiftRng { - let mut tuple: (u32, u32, u32, u32) = rng.gen(); - while tuple == (0, 0, 0, 0) { - tuple = rng.gen(); + let mut tuple: (u32, u32, u32, u32); + loop { + tuple = (rng.next_u32(), rng.next_u32(), rng.next_u32(), rng.next_u32()); + if tuple != (0, 0, 0, 0) { + break; + } } let (x, y, z, w_) = tuple; XorShiftRng { x: w(x), y: w(y), z: w(z), w: w(w_) } From 3a5fab2d2052bba071891e2faf7d12c9678d8bdf Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Fri, 28 Jul 2017 17:12:59 +0100 Subject: [PATCH 013/247] Replace struct Exp1 impl Rand with fn exp1 --- src/dist/exponential.rs | 42 ++++++++++++++++++----------------------- 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/src/dist/exponential.rs b/src/dist/exponential.rs index 080468bbcc1..bd1fecc4698 100644 --- a/src/dist/exponential.rs +++ b/src/dist/exponential.rs @@ -10,10 +10,10 @@ //! The exponential distribution. -use {Rng, Rand}; +use {Rng}; use dist::{ziggurat, ziggurat_tables, Sample}; -/// A wrapper around an `f64` to generate Exp(1) random numbers. +/// Generates Exp(1) random numbers. /// /// See `Exp` for the general exponential distribution. /// @@ -29,32 +29,27 @@ use dist::{ziggurat, ziggurat_tables, Sample}; /// # Example /// /// ```rust -/// use rand::dist::exponential::Exp1; +/// use rand::dist::exponential::exp1; /// -/// let Exp1(x) = rand::random(); +/// let x = exp1(&mut rand::thread_rng()); /// println!("{}", x); /// ``` -#[derive(Clone, Copy, Debug)] -pub struct Exp1(pub f64); - // This could be done via `-rng.gen::().ln()` but that is slower. -impl Rand for Exp1 { +#[inline] +pub fn exp1(rng: &mut R) -> f64 { #[inline] - fn rand(rng: &mut R) -> Exp1 { - #[inline] - fn pdf(x: f64) -> f64 { - (-x).exp() - } - #[inline] - fn zero_case(rng: &mut R, _u: f64) -> f64 { - ziggurat_tables::ZIG_EXP_R - rng.gen::().ln() - } - - Exp1(ziggurat(rng, false, - &ziggurat_tables::ZIG_EXP_X, - &ziggurat_tables::ZIG_EXP_F, - pdf, zero_case)) + fn pdf(x: f64) -> f64 { + (-x).exp() + } + #[inline] + fn zero_case(rng: &mut R, _u: f64) -> f64 { + ziggurat_tables::ZIG_EXP_R - rng.gen::().ln() } + + (ziggurat(rng, false, + &ziggurat_tables::ZIG_EXP_X, + &ziggurat_tables::ZIG_EXP_F, + pdf, zero_case)) } /// The exponential distribution `Exp(lambda)`. @@ -89,8 +84,7 @@ impl Exp { impl Sample for Exp { fn sample(&self, rng: &mut R) -> f64 { - let Exp1(n) = rng.gen::(); - n * self.lambda_inverse + exp1(rng) * self.lambda_inverse } } From a10251ba5d0d3066de984c1ca40c1a366f209a41 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Fri, 28 Jul 2017 17:17:38 +0100 Subject: [PATCH 014/247] Replace struct StandardNormal impl Rand with fn standard_normal --- src/dist/gamma.rs | 8 ++--- src/dist/normal.rs | 78 +++++++++++++++++++++------------------------- 2 files changed, 40 insertions(+), 46 deletions(-) diff --git a/src/dist/gamma.rs b/src/dist/gamma.rs index 69a243d20a0..bcf455a2752 100644 --- a/src/dist/gamma.rs +++ b/src/dist/gamma.rs @@ -16,7 +16,7 @@ use self::GammaRepr::*; use self::ChiSquaredRepr::*; use {Rng, Open01}; -use super::normal::StandardNormal; +use super::normal::standard_normal; use super::{Sample, Exp}; /// The Gamma distribution `Gamma(shape, scale)` distribution. @@ -153,7 +153,7 @@ impl Sample for GammaSmallShape { impl Sample for GammaLargeShape { fn sample(&self, rng: &mut R) -> f64 { loop { - let StandardNormal(x) = rng.gen::(); + let x = standard_normal(rng); let v_cbrt = 1.0 + self.c * x; if v_cbrt <= 0.0 { // a^3 <= 0 iff a <= 0 continue @@ -220,7 +220,7 @@ impl Sample for ChiSquared { match self.repr { DoFExactlyOne => { // k == 1 => N(0,1)^2 - let StandardNormal(norm) = rng.gen::(); + let norm = standard_normal(rng); norm * norm } DoFAnythingElse(ref g) => g.sample(rng) @@ -303,7 +303,7 @@ impl StudentT { } impl Sample for StudentT { fn sample(&self, rng: &mut R) -> f64 { - let StandardNormal(norm) = rng.gen::(); + let norm = standard_normal(rng); norm * (self.dof / self.chi.sample(rng)).sqrt() } } diff --git a/src/dist/normal.rs b/src/dist/normal.rs index 6349c41d845..d43b3acccba 100644 --- a/src/dist/normal.rs +++ b/src/dist/normal.rs @@ -10,10 +10,10 @@ //! The normal and derived distributions. -use {Rng, Rand, Open01}; +use {Rng, Open01}; use dist::{ziggurat, ziggurat_tables, Sample}; -/// A wrapper around an `f64` to generate N(0, 1) random numbers +/// Generates N(0, 1) random numbers /// (a.k.a. a standard normal, or Gaussian). /// /// See `Normal` for the general normal distribution. @@ -28,55 +28,50 @@ use dist::{ziggurat, ziggurat_tables, Sample}; /// # Example /// /// ```rust -/// use rand::dist::normal::StandardNormal; +/// use rand::dist::normal::standard_normal; /// -/// let StandardNormal(x) = rand::random(); +/// let x = standard_normal(&mut rand::thread_rng()); /// println!("{}", x); /// ``` -#[derive(Clone, Copy, Debug)] -pub struct StandardNormal(pub f64); - -impl Rand for StandardNormal { - fn rand(rng: &mut R) -> StandardNormal { - #[inline] - fn pdf(x: f64) -> f64 { - (-x*x/2.0).exp() - } - #[inline] - fn zero_case(rng: &mut R, u: f64) -> f64 { - // compute a random number in the tail by hand - - // strange initial conditions, because the loop is not - // do-while, so the condition should be true on the first - // run, they get overwritten anyway (0 < 1, so these are - // good). - let mut x = 1.0f64; - let mut y = 0.0f64; - - while -2.0 * y < x * x { - let Open01(x_) = rng.gen::>(); - let Open01(y_) = rng.gen::>(); - - x = x_.ln() / ziggurat_tables::ZIG_NORM_R; - y = y_.ln(); - } - - if u < 0.0 { x - ziggurat_tables::ZIG_NORM_R } else { ziggurat_tables::ZIG_NORM_R - x } +pub fn standard_normal(rng: &mut R) -> f64 { + #[inline] + fn pdf(x: f64) -> f64 { + (-x*x/2.0).exp() + } + #[inline] + fn zero_case(rng: &mut R, u: f64) -> f64 { + // compute a random number in the tail by hand + + // strange initial conditions, because the loop is not + // do-while, so the condition should be true on the first + // run, they get overwritten anyway (0 < 1, so these are + // good). + let mut x = 1.0f64; + let mut y = 0.0f64; + + while -2.0 * y < x * x { + let Open01(x_) = rng.gen::>(); + let Open01(y_) = rng.gen::>(); + + x = x_.ln() / ziggurat_tables::ZIG_NORM_R; + y = y_.ln(); } - StandardNormal(ziggurat( - rng, - true, // this is symmetric - &ziggurat_tables::ZIG_NORM_X, - &ziggurat_tables::ZIG_NORM_F, - pdf, zero_case)) + if u < 0.0 { x - ziggurat_tables::ZIG_NORM_R } else { ziggurat_tables::ZIG_NORM_R - x } } + + ziggurat( + rng, + true, // this is symmetric + &ziggurat_tables::ZIG_NORM_X, + &ziggurat_tables::ZIG_NORM_F, + pdf, zero_case) } /// The normal distribution `N(mean, std_dev**2)`. /// /// This uses the ZIGNOR variant of the Ziggurat method, see -/// `StandardNormal` for more details. +/// `standard_normal` for more details. /// /// # Example /// @@ -112,8 +107,7 @@ impl Normal { } impl Sample for Normal { fn sample(&self, rng: &mut R) -> f64 { - let StandardNormal(n) = rng.gen::(); - self.mean + self.std_dev * n + self.mean + self.std_dev * standard_normal(rng) } } From f6ba51765080f903277e1154703fb18a89dc8efd Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sat, 29 Jul 2017 08:12:31 +0100 Subject: [PATCH 015/247] Replace f32/f64 Rand impl, Open01 and Closed01 with dist::uniform01 etc. --- Cargo.toml | 3 - src/dist/exponential.rs | 4 +- src/dist/gamma.rs | 10 +-- src/dist/mod.rs | 4 +- src/dist/normal.rs | 8 +- src/dist/range.rs | 4 +- src/dist/uniform.rs | 195 +++++++++++++++++++++++++++++++++++----- src/lib.rs | 85 +++++------------- src/rand_impls.rs | 94 ------------------- 9 files changed, 209 insertions(+), 198 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e7dd0f42ae1..084d477d15d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,9 +20,6 @@ nightly = ["i128_support"] [dependencies] libc = "0.2" -[dev-dependencies] -log = "0.3.0" - [workspace] members = ["rand-derive"] diff --git a/src/dist/exponential.rs b/src/dist/exponential.rs index bd1fecc4698..06eac82313e 100644 --- a/src/dist/exponential.rs +++ b/src/dist/exponential.rs @@ -11,7 +11,7 @@ //! The exponential distribution. use {Rng}; -use dist::{ziggurat, ziggurat_tables, Sample}; +use dist::{ziggurat, ziggurat_tables, Sample, uniform01}; /// Generates Exp(1) random numbers. /// @@ -43,7 +43,7 @@ pub fn exp1(rng: &mut R) -> f64 { } #[inline] fn zero_case(rng: &mut R, _u: f64) -> f64 { - ziggurat_tables::ZIG_EXP_R - rng.gen::().ln() + ziggurat_tables::ZIG_EXP_R - uniform01::(rng).ln() } (ziggurat(rng, false, diff --git a/src/dist/gamma.rs b/src/dist/gamma.rs index bcf455a2752..a98591a71d5 100644 --- a/src/dist/gamma.rs +++ b/src/dist/gamma.rs @@ -15,9 +15,9 @@ use self::GammaRepr::*; use self::ChiSquaredRepr::*; -use {Rng, Open01}; -use super::normal::standard_normal; -use super::{Sample, Exp}; +use {Rng}; +use dist::normal::standard_normal; +use dist::{Sample, Exp, open01}; /// The Gamma distribution `Gamma(shape, scale)` distribution. /// @@ -145,7 +145,7 @@ impl Sample for Gamma { } impl Sample for GammaSmallShape { fn sample(&self, rng: &mut R) -> f64 { - let Open01(u) = rng.gen::>(); + let u: f64 = open01(rng); self.large_shape.sample(rng) * u.powf(self.inv_shape) } @@ -160,7 +160,7 @@ impl Sample for GammaLargeShape { } let v = v_cbrt * v_cbrt * v_cbrt; - let Open01(u) = rng.gen::>(); + let u: f64 = open01(rng); let x_sqr = x * x; if u < 1.0 - 0.0331 * x_sqr * x_sqr || diff --git a/src/dist/mod.rs b/src/dist/mod.rs index 05d87ca81ad..b4ab96ca7cb 100644 --- a/src/dist/mod.rs +++ b/src/dist/mod.rs @@ -21,7 +21,7 @@ use std::marker; use {Rng, Rand}; -pub use self::uniform::{Uniform, uniform}; +pub use self::uniform::{uniform, uniform01, open01, closed01}; pub use self::range::Range; pub use self::gamma::{Gamma, ChiSquared, FisherF, StudentT}; pub use self::normal::{Normal, LogNormal}; @@ -126,7 +126,7 @@ fn ziggurat( return zero_case(rng, u); } // algebraically equivalent to f1 + DRanU()*(f0 - f1) < 1 - if f_tab[i + 1] + (f_tab[i] - f_tab[i + 1]) * rng.gen::() < pdf(x) { + if f_tab[i + 1] + (f_tab[i] - f_tab[i + 1]) * uniform01::(rng) < pdf(x) { return x; } } diff --git a/src/dist/normal.rs b/src/dist/normal.rs index d43b3acccba..2e21ad47c79 100644 --- a/src/dist/normal.rs +++ b/src/dist/normal.rs @@ -10,8 +10,8 @@ //! The normal and derived distributions. -use {Rng, Open01}; -use dist::{ziggurat, ziggurat_tables, Sample}; +use {Rng}; +use dist::{ziggurat, ziggurat_tables, Sample, open01}; /// Generates N(0, 1) random numbers /// (a.k.a. a standard normal, or Gaussian). @@ -50,8 +50,8 @@ pub fn standard_normal(rng: &mut R) -> f64 { let mut y = 0.0f64; while -2.0 * y < x * x { - let Open01(x_) = rng.gen::>(); - let Open01(y_) = rng.gen::>(); + let x_: f64 = open01(rng); + let y_: f64 = open01(rng); x = x_.ln() / ziggurat_tables::ZIG_NORM_R; y = y_.ln(); diff --git a/src/dist/range.rs b/src/dist/range.rs index 4c139b2a413..e80f5f317b2 100644 --- a/src/dist/range.rs +++ b/src/dist/range.rs @@ -15,7 +15,7 @@ use std::num::Wrapping as w; use Rng; -use dist::{Sample}; +use dist::{Sample, uniform01}; /// Sample values uniformly between two bounds. /// @@ -151,7 +151,7 @@ macro_rules! float_impl { } } fn sample_range(r: &Range<$ty>, rng: &mut R) -> $ty { - r.low + r.range * rng.gen::<$ty>() + r.low + r.range * uniform01::<$ty, _>(rng) } } } diff --git a/src/dist/uniform.rs b/src/dist/uniform.rs index c2439ab24fb..80696e89bc6 100644 --- a/src/dist/uniform.rs +++ b/src/dist/uniform.rs @@ -9,8 +9,6 @@ // except according to those terms. //! Generating uniformly distributed numbers -//! -//! TODO: is there value in having both `uniform` and `Uniform`? use std::mem; use std::marker::PhantomData; @@ -18,17 +16,98 @@ use std::marker::PhantomData; use Rng; use dist::Sample; +// ----- convenience functions ----- + /// Sample values uniformly over the whole range supported by the type +/// +/// This method has precisely two template parameters. To fix the output type, +/// use the syntax `uniform::(rng)`. pub fn uniform(rng: &mut R) -> T { T::sample_uniform(rng) } +/// Sample values uniformly over the half-open range [0, 1) +/// +/// This method has precisely two template parameters. To fix the output type, +/// use the syntax `uniform01::(rng)`. +pub fn uniform01(rng: &mut R) -> T { + T::sample_uniform01(rng) +} + +/// Sample values uniformly over the open range (0, 1) +/// +/// This method has precisely two template parameters. To fix the output type, +/// use the syntax `open01::(rng)`. +pub fn open01(rng: &mut R) -> T { + T::sample_open01(rng) +} + +/// Sample values uniformly over the closed range [0, 1] +/// +/// This method has precisely two template parameters. To fix the output type, +/// use the syntax `closed01::(rng)`. +pub fn closed01(rng: &mut R) -> T { + T::sample_closed01(rng) +} + + +// ----- Sample implementations ----- +// TODO: do we want these? If so, implement for other ranges. + +/// Sample values uniformly over the whole range supported by the type. +/// +/// No internal state. +#[derive(Clone, Copy, Debug, Default)] +pub struct Uniform { + _marker: PhantomData, +} + +impl Uniform { + /// Create an instance. Should optimise to nothing, since there is no + /// internal state. + pub fn new() -> Self { + Uniform { + _marker: PhantomData + } + } +} + +impl Sample for Uniform { + fn sample(&self, rng: &mut R) -> T { + T::sample_uniform(rng) + } +} + + +// ----- Sampling traits ----- + /// Sample values uniformly over the whole range supported by the type pub trait SampleUniform: Sized { /// Sample a value from a RNG fn sample_uniform(rng: &mut R) -> Self; } +/// Sample values uniformly over the half-open range [0, 1) +pub trait SampleUniform01: Sized { + /// Sample a value from a RNG + fn sample_uniform01(rng: &mut R) -> Self; +} + +/// Sample values uniformly over the open range (0, 1) +pub trait SampleOpen01: Sized { + /// Sample a value from a RNG + fn sample_open01(rng: &mut R) -> Self; +} + +/// Sample values uniformly over the closed range [0, 1] +pub trait SampleClosed01: Sized { + /// Sample a value from a RNG + fn sample_closed01(rng: &mut R) -> Self; +} + + +// ----- actual implementations ----- + impl SampleUniform for isize { #[inline] fn sample_uniform(rng: &mut R) -> isize { @@ -123,35 +202,47 @@ impl SampleUniform for u128 { } } -/// Sample values uniformly over the whole range supported by the type. -/// -/// No internal state. -#[derive(Clone, Copy, Debug, Default)] -pub struct Uniform { - _marker: PhantomData, -} -impl Uniform { - /// Create an instance. Should optimise to nothing, since there is no - /// internal state. - pub fn new() -> Self { - Uniform { - _marker: PhantomData +macro_rules! float_impls { + ($scale_name:ident, $ty:ty, $mantissa_bits:expr, $method_name:ident) => { + const $scale_name: $ty = (1u64 << $mantissa_bits) as $ty; + + impl SampleUniform01 for $ty { + #[inline] + fn sample_uniform01(rng: &mut R) -> $ty { + rng.$method_name() + } + } + impl SampleOpen01 for $ty { + #[inline] + fn sample_open01(rng: &mut R) -> $ty { + // add a small amount (specifically 2 bits below + // the precision of f64/f32 at 1.0), so that small + // numbers are larger than 0, but large numbers + // aren't pushed to/above 1. + uniform01::<$ty, _>(rng) + 0.25 / $scale_name + } + } + impl SampleClosed01 for $ty { + #[inline] + fn sample_closed01(rng: &mut R) -> $ty { + // rescale so that 1.0 - epsilon becomes 1.0 + // precisely. + uniform01::<$ty, _>(rng) * $scale_name / ($scale_name - 1.0) + } } } } - -impl Sample for Uniform { - fn sample(&self, rng: &mut R) -> T { - T::sample_uniform(rng) - } -} +float_impls! { SCALE_F64, f64, 53, next_f64 } +float_impls! { SCALE_F32, f32, 24, next_f32 } #[cfg(test)] mod tests { - use dist::{uniform, Uniform, Sample}; - use dist::uniform::SampleUniform; + use {Rng, thread_rng}; + use dist::{uniform, Sample}; + use dist::uniform::{SampleUniform, Uniform}; + use dist::{uniform01, open01, closed01}; #[test] fn test_integers() { @@ -179,4 +270,62 @@ mod tests { let dist = Uniform::::new(); let _ = dist.sample(&mut rng); } + + struct ConstantRng(u64); + impl Rng for ConstantRng { + fn next_u32(&mut self) -> u32 { + let ConstantRng(v) = *self; + v as u32 + } + fn next_u64(&mut self) -> u64 { + let ConstantRng(v) = *self; + v + } + } + + #[test] + fn test_f64() { + let mut r = thread_rng(); + let a: f64 = uniform01(&mut r); + let b = uniform01::(&mut r); + assert!(0.0 <= a && a < 1.0); + assert!(0.0 <= b && b < 1.0); + } + + #[test] + fn floating_point_edge_cases() { + // FIXME: this message and test predates this repo; message suggests + // test is supposed to be ==; using != is pretty useless + // the test for exact equality is correct here. + assert!(uniform01::(&mut ConstantRng(0xffff_ffff)) != 1.0); + assert!(uniform01::(&mut ConstantRng(0xffff_ffff_ffff_ffff)) != 1.0); + } + + #[test] + fn rand_open() { + // this is unlikely to catch an incorrect implementation that + // generates exactly 0 or 1, but it keeps it sane. + let mut rng = thread_rng(); + for _ in 0..1_000 { + // strict inequalities + let f = open01::(&mut rng); + assert!(0.0 < f && f < 1.0); + + let f = open01::(&mut rng); + assert!(0.0 < f && f < 1.0); + } + } + + #[test] + fn rand_closed() { + let mut rng = thread_rng(); + for _ in 0..1_000 { + // strict inequalities + let f = closed01::(&mut rng); + assert!(0.0 <= f && f <= 1.0); + + let f = closed01::(&mut rng); + assert!(0.0 <= f && f <= 1.0); + } + } } diff --git a/src/lib.rs b/src/lib.rs index 729802a2acb..ac57a63a70a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -88,7 +88,10 @@ //! ``` //! //! ```rust -//! let tuple = rand::random::<(f64, char)>(); +//! use rand::thread_rng; +//! use rand::dist::{uniform, uniform01}; +//! let mut rng = thread_rng(); +//! let tuple: (f64, u8) = (uniform01(&mut rng), uniform(&mut rng)); //! println!("{:?}", tuple) //! ``` //! @@ -245,8 +248,6 @@ #![cfg_attr(feature = "i128_support", feature(i128_type))] -#[cfg(test)] #[macro_use] extern crate log; - use std::cell::RefCell; use std::mem; @@ -351,8 +352,8 @@ pub trait Rng { /// the requirements directly can overload this for performance. /// It is required that the return value lies in `[0, 1)`. /// - /// See `Closed01` for the closed interval `[0,1]`, and - /// `Open01` for the open interval `(0,1)`. + /// See `closed01` for the closed interval `[0,1]`, and + /// `open01` for the open interval `(0,1)`. fn next_f32(&mut self) -> f32 { const UPPER_MASK: u32 = 0x3F800000; const LOWER_MASK: u32 = 0x7FFFFF; @@ -369,8 +370,8 @@ pub trait Rng { /// the requirements directly can overload this for performance. /// It is required that the return value lies in `[0, 1)`. /// - /// See `Closed01` for the closed interval `[0,1]`, and - /// `Open01` for the open interval `(0,1)`. + /// See `closed01` for the closed interval `[0,1]`, and + /// `open01` for the open interval `(0,1)`. fn next_f64(&mut self) -> f64 { const UPPER_MASK: u64 = 0x3FF0000000000000; const LOWER_MASK: u64 = 0xFFFFFFFFFFFFF; @@ -436,11 +437,14 @@ pub trait Rng { /// /// ```rust /// use rand::{thread_rng, Rng}; + /// use rand::dist::{uniform, uniform01}; /// /// let mut rng = thread_rng(); - /// let x: u32 = rng.gen(); + /// let x: u32 = uniform(&mut rng); + /// let y: f64 = uniform01(&mut rng); /// println!("{}", x); - /// println!("{:?}", rng.gen::<(f64, bool)>()); + /// println!("{}", y); + /// println!("{}", rng.gen::()); /// ``` #[inline(always)] fn gen(&mut self) -> T where Self: Sized { @@ -688,12 +692,13 @@ pub trait SeedableRng: Rng { /// ```rust /// use rand::{Rng, SeedableRng}; /// use rand::prng::IsaacWordRng; + /// use rand::dist::uniform01; /// /// let seed: &[_] = &[1, 2, 3, 4]; /// let mut rng: IsaacWordRng = SeedableRng::from_seed(seed); - /// println!("{}", rng.gen::()); + /// println!("{}", uniform01::(&mut rng)); /// rng.reseed(&[5, 6, 7, 8]); - /// println!("{}", rng.gen::()); + /// println!("{}", uniform01::(&mut rng)); /// ``` fn reseed(&mut self, Seed); @@ -704,49 +709,15 @@ pub trait SeedableRng: Rng { /// ```rust /// use rand::{Rng, SeedableRng}; /// use rand::prng::ChaChaRng; + /// use rand::dist::uniform01; /// /// let seed: &[_] = &[1, 2, 3, 4]; /// let mut rng: ChaChaRng = SeedableRng::from_seed(seed); - /// println!("{}", rng.gen::()); + /// println!("{}", uniform01::(&mut rng)); /// ``` fn from_seed(seed: Seed) -> Self; } -/// A wrapper for generating floating point numbers uniformly in the -/// open interval `(0,1)` (not including either endpoint). -/// -/// Use `Closed01` for the closed interval `[0,1]`, and the default -/// `Rand` implementation for `f32` and `f64` for the half-open -/// `[0,1)`. -/// -/// # Example -/// ```rust -/// use rand::{random, Open01}; -/// -/// let Open01(val) = random::>(); -/// println!("f32 from (0,1): {}", val); -/// ``` -#[derive(Debug)] -pub struct Open01(pub F); - -/// A wrapper for generating floating point numbers uniformly in the -/// closed interval `[0,1]` (including both endpoints). -/// -/// Use `Open01` for the closed interval `(0,1)`, and the default -/// `Rand` implementation of `f32` and `f64` for the half-open -/// `[0,1)`. -/// -/// # Example -/// -/// ```rust -/// use rand::{random, Closed01}; -/// -/// let Closed01(val) = random::>(); -/// println!("f32 from [0,1]: {}", val); -/// ``` -#[derive(Debug)] -pub struct Closed01(pub F); - /// The standard RNG. This is designed to be efficient on the current /// platform. #[derive(Copy, Clone, Debug)] @@ -889,9 +860,6 @@ impl Rng for ThreadRng { /// let x = rand::random::(); /// println!("{}", x); /// -/// let y = rand::random::(); -/// println!("{}", y); -/// /// if rand::random() { // generates a boolean /// println!("Better lucky than good!"); /// } @@ -954,7 +922,7 @@ pub fn sample(rng: &mut R, iterable: I, amount: usize) -> Vec #[cfg(test)] mod test { use {Rng, thread_rng, random, SeedableRng, StdRng, sample}; - use dist::uniform; + use dist::{uniform, uniform01}; use std::iter::repeat; pub struct MyRng { inner: R } @@ -1050,14 +1018,6 @@ mod test { r.gen_range(5, 2); } - #[test] - fn test_gen_f64() { - let mut r = thread_rng(); - let a = r.gen::(); - let b = r.gen::(); - debug!("{:?}", (a, b)); - } - #[test] fn test_gen_weighted_bool() { let mut r = thread_rng(); @@ -1078,7 +1038,7 @@ mod test { let mut r = thread_rng(); assert_eq!(r.iter_map(|rng| rng.next_u32()).take(0).count(), 0); assert_eq!(r.iter_map(|rng| uniform::(rng)).take(10).count(), 10); - assert_eq!(r.iter_map(|rng| rng.gen::()).take(16).count(), 16); + assert_eq!(r.iter_map(|rng| uniform01::(rng)).take(16).count(), 16); } #[test] @@ -1150,14 +1110,13 @@ mod test { fn test_random() { // not sure how to test this aside from just getting some values let _n : usize = random(); - let _f : f32 = random(); let _o : Option> = random(); let _many : ((), (usize, isize, Option<(u32, (bool,))>), - (u8, i8, u16, i16, u32, i32, u64, i64), - (f32, (f64, (f64,)))) = random(); + (u8, i8, u16, i16, u32, i32, u64, i64)) + = random(); } #[test] diff --git a/src/rand_impls.rs b/src/rand_impls.rs index a9cf5d9908b..60cc0e5bbf0 100644 --- a/src/rand_impls.rs +++ b/src/rand_impls.rs @@ -110,48 +110,6 @@ impl Rand for u128 { } -macro_rules! float_impls { - ($mod_name:ident, $ty:ty, $mantissa_bits:expr, $method_name:ident) => { - mod $mod_name { - use {Rand, Rng, Open01, Closed01}; - - const SCALE: $ty = (1u64 << $mantissa_bits) as $ty; - - impl Rand for $ty { - /// Generate a floating point number in the half-open - /// interval `[0,1)`. - /// - /// See `Closed01` for the closed interval `[0,1]`, - /// and `Open01` for the open interval `(0,1)`. - #[inline] - fn rand(rng: &mut R) -> $ty { - rng.$method_name() - } - } - impl Rand for Open01<$ty> { - #[inline] - fn rand(rng: &mut R) -> Open01<$ty> { - // add a small amount (specifically 2 bits below - // the precision of f64/f32 at 1.0), so that small - // numbers are larger than 0, but large numbers - // aren't pushed to/above 1. - Open01(rng.$method_name() + 0.25 / SCALE) - } - } - impl Rand for Closed01<$ty> { - #[inline] - fn rand(rng: &mut R) -> Closed01<$ty> { - // rescale so that 1.0 - epsilon becomes 1.0 - // precisely. - Closed01(rng.$method_name() * SCALE / (SCALE - 1.0)) - } - } - } - } -} -float_impls! { f64_rand_impls, f64, 53, next_f64 } -float_impls! { f32_rand_impls, f32, 24, next_f32 } - impl Rand for char { #[inline] fn rand(rng: &mut R) -> char { @@ -246,55 +204,3 @@ impl Rand for Option { } } } - -#[cfg(test)] -mod tests { - use {Rng, thread_rng, Open01, Closed01}; - - struct ConstantRng(u64); - impl Rng for ConstantRng { - fn next_u32(&mut self) -> u32 { - let ConstantRng(v) = *self; - v as u32 - } - fn next_u64(&mut self) -> u64 { - let ConstantRng(v) = *self; - v - } - } - - #[test] - fn floating_point_edge_cases() { - // the test for exact equality is correct here. - assert!(ConstantRng(0xffff_ffff).gen::() != 1.0); - assert!(ConstantRng(0xffff_ffff_ffff_ffff).gen::() != 1.0); - } - - #[test] - fn rand_open() { - // this is unlikely to catch an incorrect implementation that - // generates exactly 0 or 1, but it keeps it sane. - let mut rng = thread_rng(); - for _ in 0..1_000 { - // strict inequalities - let Open01(f) = rng.gen::>(); - assert!(0.0 < f && f < 1.0); - - let Open01(f) = rng.gen::>(); - assert!(0.0 < f && f < 1.0); - } - } - - #[test] - fn rand_closed() { - let mut rng = thread_rng(); - for _ in 0..1_000 { - // strict inequalities - let Closed01(f) = rng.gen::>(); - assert!(0.0 <= f && f <= 1.0); - - let Closed01(f) = rng.gen::>(); - assert!(0.0 <= f && f <= 1.0); - } - } -} From d2f6382f719bcad00b91e58ca46fdc6dc89b6f61 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sat, 29 Jul 2017 08:26:24 +0100 Subject: [PATCH 016/247] Remove `random()` convenience function --- src/lib.rs | 76 ++++++------------------------------------------------ 1 file changed, 8 insertions(+), 68 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ac57a63a70a..b1a20d70149 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,14 +10,11 @@ //! Utilities for random number generation //! -//! The key functions are `random()` and `Rng::gen()`. These are polymorphic and -//! so can be used to generate any type that implements `Rand`. Type inference -//! means that often a simple call to `rand::random()` or `rng.gen()` will -//! suffice, but sometimes an annotation is required, e.g. -//! `rand::random::()`. -//! -//! See the `dist` submodule for sampling random numbers from -//! distributions like normal and exponential. +//! The `Rng` trait covers random number generation, and can be used directly +//! to produce values of some core types (`u32, u64, f32, f64`, and byte +//! strings). To generate anything else, or to generate values of the above type +//! in generic code, use the `dist` (distributions) module. This includes +//! distributions like ranges, normal and exponential. //! //! # Usage //! @@ -38,8 +35,8 @@ //! # Thread-local RNG //! //! There is built-in support for a RNG associated with each thread stored -//! in thread-local storage. This RNG can be accessed via `thread_rng`, or -//! used implicitly via `random`. This RNG is normally randomly seeded +//! in thread-local storage. This RNG can be accessed via `thread_rng`. +//! This RNG is normally randomly seeded //! from an operating-system source of randomness, e.g. `/dev/urandom` on //! Unix systems, and will automatically reseed itself from this source //! after generating 32 KiB of random data. @@ -845,50 +842,6 @@ impl Rng for ThreadRng { } } -/// Generates a random value using the thread-local random number generator. -/// -/// `random()` can generate various types of random things, and so may require -/// type hinting to generate the specific type you want. -/// -/// This function uses the thread local random number generator. This means -/// that if you're calling `random()` in a loop, caching the generator can -/// increase performance. An example is shown below. -/// -/// # Examples -/// -/// ``` -/// let x = rand::random::(); -/// println!("{}", x); -/// -/// if rand::random() { // generates a boolean -/// println!("Better lucky than good!"); -/// } -/// ``` -/// -/// Caching the thread local random number generator: -/// -/// ``` -/// use rand::Rng; -/// -/// let mut v = vec![1, 2, 3]; -/// -/// for x in v.iter_mut() { -/// *x = rand::random() -/// } -/// -/// // would be faster as -/// -/// let mut rng = rand::thread_rng(); -/// -/// for x in v.iter_mut() { -/// *x = rng.gen(); -/// } -/// ``` -#[inline] -pub fn random() -> T { - thread_rng().gen() -} - /// Randomly sample up to `amount` elements from a finite iterator. /// The order of elements in the sample is not random. /// @@ -921,7 +874,7 @@ pub fn sample(rng: &mut R, iterable: I, amount: usize) -> Vec #[cfg(test)] mod test { - use {Rng, thread_rng, random, SeedableRng, StdRng, sample}; + use {Rng, thread_rng, SeedableRng, StdRng, sample}; use dist::{uniform, uniform01}; use std::iter::repeat; @@ -1106,19 +1059,6 @@ mod test { } } - #[test] - fn test_random() { - // not sure how to test this aside from just getting some values - let _n : usize = random(); - let _o : Option> = random(); - let _many : ((), - (usize, - isize, - Option<(u32, (bool,))>), - (u8, i8, u16, i16, u32, i32, u64, i64)) - = random(); - } - #[test] fn test_sample() { let min_val = 1; From c3535c2523d9f3b4e1d96af0d6d8ebd2ce05a5b8 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sat, 29 Jul 2017 08:29:17 +0100 Subject: [PATCH 017/247] Remove rand-derive sub-crate --- Cargo.toml | 3 - README.md | 28 -------- rand-derive/Cargo.toml | 23 ------- rand-derive/README.md | 51 --------------- rand-derive/src/lib.rs | 106 ------------------------------- rand-derive/tests/rand_macros.rs | 39 ------------ 6 files changed, 250 deletions(-) delete mode 100644 rand-derive/Cargo.toml delete mode 100644 rand-derive/README.md delete mode 100644 rand-derive/src/lib.rs delete mode 100644 rand-derive/tests/rand_macros.rs diff --git a/Cargo.toml b/Cargo.toml index 084d477d15d..1af45a864ff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,8 +20,5 @@ nightly = ["i128_support"] [dependencies] libc = "0.2" -[workspace] -members = ["rand-derive"] - [target.'cfg(target_os = "fuchsia")'.dependencies] magenta = "^0.1.1" diff --git a/README.md b/README.md index 0aea1a11ab1..5617bd28a5f 100644 --- a/README.md +++ b/README.md @@ -50,34 +50,6 @@ let mut rng = rand::ChaChaRng::new_unseeded(); println!("i32: {}, u32: {}", rng.gen::(), rng.gen::()) ``` -# `derive(Rand)` - -You can derive the `Rand` trait for your custom type via the `#[derive(Rand)]` -directive. To use this first add this to your Cargo.toml: - -```toml -rand = "0.3" -rand-derive = "0.3" -``` - -Next in your crate: - -```rust -extern crate rand; -#[macro_use] -extern crate rand_derive; - -#[derive(Rand, Debug)] -struct MyStruct { - a: i32, - b: u32, -} - -fn main() { - println!("{:?}", rand::random::()); -} -``` - # License diff --git a/rand-derive/Cargo.toml b/rand-derive/Cargo.toml deleted file mode 100644 index d04ad1e281d..00000000000 --- a/rand-derive/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] - -name = "rand-derive" -version = "0.3.0" -authors = ["The Rust Project Developers"] -license = "MIT/Apache-2.0" -readme = "README.md" -repository = "https://github.com/rust-lang-nursery/rand" -documentation = "https://docs.rs/rand-derive" -homepage = "https://github.com/rust-lang-nursery/rand" -description = """ -`#[derive(Rand)]` functionality for the `rand::Rand` trait. -""" - -[lib] -proc-macro = true - -[dependencies] -quote = "0.3" -syn = "0.11" - -[dev-dependencies] -rand = { path = "..", version = "0.3" } diff --git a/rand-derive/README.md b/rand-derive/README.md deleted file mode 100644 index 3b01cd926ea..00000000000 --- a/rand-derive/README.md +++ /dev/null @@ -1,51 +0,0 @@ - -rand_macros -==== - -`#[derive(Rand)]` functionality for the `rand::Rand` trait. - -## Usage -Add this to your `Cargo.toml`: - -```toml -[dependencies] -rand = "0.3" -rand_macros = "0.2" -``` - -and this to your crate root: - -```rust -extern crate rand; -#[macro_use] -extern crate rand_macros; -``` - -## Examples - -`#[derive(Rand)]` can be used on any `struct` or `enum` where all fields/variants implement `rand::Rand`. - -```rust -#[derive(Debug, Rand)] -struct Foo { - x: u16, - y: Option, -} - -#[derive(Debug, Rand)] -enum Bar { - X{x: u8, y: isize}, - Y([bool; 4]), - Z, -} -``` -Now you can call the `Rng::gen()` function on your custom types. - -```rust -use rand::Rng; - -let mut rng = rand::thread_rng(); - -println!("{:?}", rng.gen::()); -println!("{:?}", rng.gen::()); -``` diff --git a/rand-derive/src/lib.rs b/rand-derive/src/lib.rs deleted file mode 100644 index a710841f4b2..00000000000 --- a/rand-derive/src/lib.rs +++ /dev/null @@ -1,106 +0,0 @@ -//! Support for `#[derive(Rand)]` -//! -//! # Examples -//! -//! ``` -//! extern crate rand; -//! #[macro_use] -//! extern crate rand_derive; -//! -//! #[derive(Rand, Debug)] -//! struct MyStruct { -//! a: i32, -//! b: u32, -//! } -//! -//! fn main() { -//! println!("{:?}", rand::random::()); -//! } -//! ``` - -extern crate proc_macro; -#[macro_use] -extern crate quote; -extern crate syn; - -use proc_macro::TokenStream; - -#[proc_macro_derive(Rand)] -pub fn rand_derive(input: TokenStream) -> TokenStream { - let s = input.to_string(); - let ast = syn::parse_derive_input(&s).unwrap(); - let gen = impl_rand_derive(&ast); - gen.parse().unwrap() -} - -fn impl_rand_derive(ast: &syn::MacroInput) -> quote::Tokens { - let name = &ast.ident; - let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); - - let rand = match ast.body { - syn::Body::Struct(syn::VariantData::Struct(ref body)) => { - let fields = body - .iter() - .filter_map(|field| field.ident.as_ref()) - .map(|ident| quote! { #ident: __rng.gen() }) - .collect::>(); - - quote! { #name { #(#fields,)* } } - }, - syn::Body::Struct(syn::VariantData::Tuple(ref body)) => { - let fields = (0..body.len()) - .map(|_| quote! { __rng.gen() }) - .collect::>(); - - quote! { #name (#(#fields),*) } - }, - syn::Body::Struct(syn::VariantData::Unit) => { - quote! { #name } - }, - syn::Body::Enum(ref body) => { - if body.is_empty() { - panic!("`Rand` cannot be derived for enums with no variants"); - } - - let len = body.len(); - let mut variants = body - .iter() - .enumerate() - .map(|(index, variant)| { - let ident = &variant.ident; - let arm = match variant.data { - syn::VariantData::Struct(ref body) => { - let fields = body - .iter() - .filter_map(|field| field.ident.as_ref()) - .map(|ident| quote! { #ident: __rng.gen() }) - .collect::>(); - quote! { #name::#ident { #(#fields,)* } } - }, - syn::VariantData::Tuple(ref body) => { - let fields = (0..body.len()) - .map(|_| quote! { __rng.gen() }) - .collect::>(); - - quote! { #name::#ident (#(#fields),*) } - }, - syn::VariantData::Unit => quote! { #name::#ident } - }; - - quote! { #index => #arm } - }) - .collect::>(); - variants.push(quote! { _ => unreachable!() }); - quote! { match __rng.gen_range(0, #len) { #(#variants,)* } } - } - }; - - quote! { - impl #impl_generics ::rand::Rand for #name #ty_generics #where_clause { - #[inline] - fn rand<__R: ::rand::Rng>(__rng: &mut __R) -> Self { - #rand - } - } - } -} diff --git a/rand-derive/tests/rand_macros.rs b/rand-derive/tests/rand_macros.rs deleted file mode 100644 index 69c91f0e9ab..00000000000 --- a/rand-derive/tests/rand_macros.rs +++ /dev/null @@ -1,39 +0,0 @@ -#![allow(dead_code)] - -extern crate rand; -#[macro_use] -extern crate rand_derive; - -use rand::Rng; - -#[derive(Rand)] -struct Struct { - x: u16, - y: Option, -} - -#[derive(Rand)] -struct Tuple(i16, Option); - -#[derive(Rand)] -struct Unit; - -#[derive(Rand)] -enum Enum { - X { x: u8, y: isize }, - Y([bool; 4]), - Z, -} - -#[test] -fn smoke() { - let mut rng = rand::XorShiftRng::new_unseeded(); - - // check nothing horrible happens internally: - for _ in 0..100 { - let _ = rng.gen::(); - let _ = rng.gen::(); - let _ = rng.gen::(); - let _ = rng.gen::(); - } -} From d0cda063ca77ae2b175bdb0dd1628010904ed930 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sat, 29 Jul 2017 08:30:45 +0100 Subject: [PATCH 018/247] Remove Rand tuple, array and Option impls --- src/rand_impls.rs | 71 ----------------------------------------------- 1 file changed, 71 deletions(-) diff --git a/src/rand_impls.rs b/src/rand_impls.rs index 60cc0e5bbf0..6bb31a56b90 100644 --- a/src/rand_impls.rs +++ b/src/rand_impls.rs @@ -133,74 +133,3 @@ impl Rand for bool { rng.gen::() & 1 == 1 } } - -macro_rules! tuple_impl { - // use variables to indicate the arity of the tuple - ($($tyvar:ident),* ) => { - // the trailing commas are for the 1 tuple - impl< - $( $tyvar : Rand ),* - > Rand for ( $( $tyvar ),* , ) { - - #[inline] - fn rand(_rng: &mut R) -> ( $( $tyvar ),* , ) { - ( - // use the $tyvar's to get the appropriate number of - // repeats (they're not actually needed) - $( - _rng.gen::<$tyvar>() - ),* - , - ) - } - } - } -} - -impl Rand for () { - #[inline] - fn rand(_: &mut R) -> () { () } -} -tuple_impl!{A} -tuple_impl!{A, B} -tuple_impl!{A, B, C} -tuple_impl!{A, B, C, D} -tuple_impl!{A, B, C, D, E} -tuple_impl!{A, B, C, D, E, F} -tuple_impl!{A, B, C, D, E, F, G} -tuple_impl!{A, B, C, D, E, F, G, H} -tuple_impl!{A, B, C, D, E, F, G, H, I} -tuple_impl!{A, B, C, D, E, F, G, H, I, J} -tuple_impl!{A, B, C, D, E, F, G, H, I, J, K} -tuple_impl!{A, B, C, D, E, F, G, H, I, J, K, L} - -macro_rules! array_impl { - {$n:expr, $t:ident, $($ts:ident,)*} => { - array_impl!{($n - 1), $($ts,)*} - - impl Rand for [T; $n] where T: Rand { - #[inline] - fn rand(_rng: &mut R) -> [T; $n] { - [_rng.gen::<$t>(), $(_rng.gen::<$ts>()),*] - } - } - }; - {$n:expr,} => { - impl Rand for [T; $n] { - fn rand(_rng: &mut R) -> [T; $n] { [] } - } - }; -} - -array_impl!{32, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,} - -impl Rand for Option { - #[inline] - fn rand(rng: &mut R) -> Option { - if rng.gen() { - Some(rng.gen()) - } else { - None - } - } -} From c961a38b3e04b8c48d1559ec65315b4165a7c324 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sat, 29 Jul 2017 08:36:18 +0100 Subject: [PATCH 019/247] Replace char impl of Rand with dist::uniform::codepoint --- src/dist/mod.rs | 2 +- src/dist/uniform.rs | 20 ++++++++++++++++++++ src/rand_impls.rs | 18 ------------------ 3 files changed, 21 insertions(+), 19 deletions(-) diff --git a/src/dist/mod.rs b/src/dist/mod.rs index b4ab96ca7cb..224265ecd1c 100644 --- a/src/dist/mod.rs +++ b/src/dist/mod.rs @@ -21,7 +21,7 @@ use std::marker; use {Rng, Rand}; -pub use self::uniform::{uniform, uniform01, open01, closed01}; +pub use self::uniform::{uniform, uniform01, open01, closed01, codepoint}; pub use self::range::Range; pub use self::gamma::{Gamma, ChiSquared, FisherF, StudentT}; pub use self::normal::{Normal, LogNormal}; diff --git a/src/dist/uniform.rs b/src/dist/uniform.rs index 80696e89bc6..65efc7658ae 100644 --- a/src/dist/uniform.rs +++ b/src/dist/uniform.rs @@ -10,6 +10,7 @@ //! Generating uniformly distributed numbers +use std::char; use std::mem; use std::marker::PhantomData; @@ -50,6 +51,25 @@ pub fn closed01(rng: &mut R) -> T { T::sample_closed01(rng) } +/// Sample a `char`, uniformly distributed over all Unicode scalar values, +/// i.e. all code points in the range `0...0x10_FFFF`, except for the range +/// `0xD800...0xDFFF` (the surrogate code points). This includes +/// unassigned/reserved code points. +#[inline] +pub fn codepoint(rng: &mut R) -> char { + // a char is 21 bits + const CHAR_MASK: u32 = 0x001f_ffff; + loop { + // Rejection sampling. About 0.2% of numbers with at most + // 21-bits are invalid codepoints (surrogates), so this + // will succeed first go almost every time. + match char::from_u32(rng.next_u32() & CHAR_MASK) { + Some(c) => return c, + None => {} + } + } +} + // ----- Sample implementations ----- // TODO: do we want these? If so, implement for other ranges. diff --git a/src/rand_impls.rs b/src/rand_impls.rs index 6bb31a56b90..c3fa4526549 100644 --- a/src/rand_impls.rs +++ b/src/rand_impls.rs @@ -10,7 +10,6 @@ //! The implementations of `Rand` for the built-in types. -use std::char; use std::mem; use {Rand,Rng}; @@ -110,23 +109,6 @@ impl Rand for u128 { } -impl Rand for char { - #[inline] - fn rand(rng: &mut R) -> char { - // a char is 21 bits - const CHAR_MASK: u32 = 0x001f_ffff; - loop { - // Rejection sampling. About 0.2% of numbers with at most - // 21-bits are invalid codepoints (surrogates), so this - // will succeed first go almost every time. - match char::from_u32(rng.next_u32() & CHAR_MASK) { - Some(c) => return c, - None => {} - } - } - } -} - impl Rand for bool { #[inline] fn rand(rng: &mut R) -> bool { From 4d2766b3fc7986811b795086a40445e2004cf693 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sat, 29 Jul 2017 08:42:24 +0100 Subject: [PATCH 020/247] Replace Rand impl of bool with SampleUniform impl --- src/dist/uniform.rs | 7 +++++++ src/lib.rs | 15 +++++++++------ src/rand_impls.rs | 8 -------- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/dist/uniform.rs b/src/dist/uniform.rs index 65efc7658ae..80b88466b62 100644 --- a/src/dist/uniform.rs +++ b/src/dist/uniform.rs @@ -222,6 +222,13 @@ impl SampleUniform for u128 { } } +impl SampleUniform for bool { + #[inline] + fn sample_uniform(rng: &mut R) -> bool { + rng.next_u32() & 1 == 1 + } +} + macro_rules! float_impls { ($scale_name:ident, $ty:ty, $mantissa_bits:expr, $method_name:ident) => { diff --git a/src/lib.rs b/src/lib.rs index b1a20d70149..af0125248e2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -76,11 +76,13 @@ //! # Examples //! //! ```rust -//! use rand::Rng; +//! use rand::dist::uniform; //! //! let mut rng = rand::thread_rng(); -//! if rng.gen() { // random bool -//! println!("i32: {}, u32: {}", rng.gen::(), rng.gen::()) +//! if uniform(&mut rng) { // random bool +//! let a: i32 = uniform(&mut rng); +//! let b: u32 = uniform(&mut rng); +//! println!("i32: {}, u32: {}", a, b) //! } //! ``` //! @@ -153,7 +155,7 @@ //! //! ``` //! use rand::Rng; -//! use rand::dist::{Sample, Range}; +//! use rand::dist::{Sample, Range, uniform}; //! //! struct SimulationResult { //! win: bool, @@ -172,7 +174,7 @@ //! let open = game_host_open(car, choice, rng); //! //! // Shall we switch? -//! let switch = rng.gen(); +//! let switch = uniform(rng); //! if switch { //! choice = switch_door(choice, open); //! } @@ -439,9 +441,10 @@ pub trait Rng { /// let mut rng = thread_rng(); /// let x: u32 = uniform(&mut rng); /// let y: f64 = uniform01(&mut rng); + /// let z: bool = uniform(&mut rng); /// println!("{}", x); /// println!("{}", y); - /// println!("{}", rng.gen::()); + /// println!("{}", z); /// ``` #[inline(always)] fn gen(&mut self) -> T where Self: Sized { diff --git a/src/rand_impls.rs b/src/rand_impls.rs index c3fa4526549..6c3e401246b 100644 --- a/src/rand_impls.rs +++ b/src/rand_impls.rs @@ -107,11 +107,3 @@ impl Rand for u128 { ((rng.next_u64() as u128) << 64) | (rng.next_u64() as u128) } } - - -impl Rand for bool { - #[inline] - fn rand(rng: &mut R) -> bool { - rng.gen::() & 1 == 1 - } -} From 9ded40c6d71e74fe1370e5878103ba23c5ef3db9 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sat, 29 Jul 2017 08:45:15 +0100 Subject: [PATCH 021/247] Remove integer impls of Rand --- src/lib.rs | 7 ++- src/rand_impls.rs | 109 ---------------------------------------------- src/read.rs | 4 +- 3 files changed, 5 insertions(+), 115 deletions(-) delete mode 100644 src/rand_impls.rs diff --git a/src/lib.rs b/src/lib.rs index af0125248e2..7a1275dd233 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -266,7 +266,6 @@ pub mod dist; pub mod prng; pub mod reseeding; -mod rand_impls; mod read; mod os; @@ -1029,7 +1028,7 @@ mod test { #[test] fn test_thread_rng() { let mut r = thread_rng(); - r.gen::(); + uniform::(&mut r); let mut v = [1, 1, 1]; r.shuffle(&mut v); let b: &[_] = &[1, 1, 1]; @@ -1043,7 +1042,7 @@ mod test { { let mut r = &mut rng as &mut Rng; r.next_u32(); - (&mut r).gen::(); + uniform::(&mut r); let mut v = [1, 1, 1]; (&mut r).shuffle(&mut v); let b: &[_] = &[1, 1, 1]; @@ -1053,7 +1052,7 @@ mod test { { let mut r = Box::new(rng) as Box; r.next_u32(); - r.gen::(); + uniform::(&mut r); let mut v = [1, 1, 1]; r.shuffle(&mut v); let b: &[_] = &[1, 1, 1]; diff --git a/src/rand_impls.rs b/src/rand_impls.rs deleted file mode 100644 index 6c3e401246b..00000000000 --- a/src/rand_impls.rs +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! The implementations of `Rand` for the built-in types. - -use std::mem; - -use {Rand,Rng}; - -impl Rand for isize { - #[inline] - fn rand(rng: &mut R) -> isize { - if mem::size_of::() == 4 { - rng.gen::() as isize - } else { - rng.gen::() as isize - } - } -} - -impl Rand for i8 { - #[inline] - fn rand(rng: &mut R) -> i8 { - rng.next_u32() as i8 - } -} - -impl Rand for i16 { - #[inline] - fn rand(rng: &mut R) -> i16 { - rng.next_u32() as i16 - } -} - -impl Rand for i32 { - #[inline] - fn rand(rng: &mut R) -> i32 { - rng.next_u32() as i32 - } -} - -impl Rand for i64 { - #[inline] - fn rand(rng: &mut R) -> i64 { - rng.next_u64() as i64 - } -} - -#[cfg(feature = "i128_support")] -impl Rand for i128 { - #[inline] - fn rand(rng: &mut R) -> i128 { - rng.gen::() as i128 - } -} - -impl Rand for usize { - #[inline] - fn rand(rng: &mut R) -> usize { - if mem::size_of::() == 4 { - rng.gen::() as usize - } else { - rng.gen::() as usize - } - } -} - -impl Rand for u8 { - #[inline] - fn rand(rng: &mut R) -> u8 { - rng.next_u32() as u8 - } -} - -impl Rand for u16 { - #[inline] - fn rand(rng: &mut R) -> u16 { - rng.next_u32() as u16 - } -} - -impl Rand for u32 { - #[inline] - fn rand(rng: &mut R) -> u32 { - rng.next_u32() - } -} - -impl Rand for u64 { - #[inline] - fn rand(rng: &mut R) -> u64 { - rng.next_u64() - } -} - -#[cfg(feature = "i128_support")] -impl Rand for u128 { - #[inline] - fn rand(rng: &mut R) -> u128 { - ((rng.next_u64() as u128) << 64) | (rng.next_u64() as u128) - } -} diff --git a/src/read.rs b/src/read.rs index 4cc2717e2e3..fc8ea24fc4f 100644 --- a/src/read.rs +++ b/src/read.rs @@ -24,11 +24,11 @@ use Rng; /// # Example /// /// ```rust -/// use rand::{ReadRng, Rng}; +/// use rand::{ReadRng, dist}; /// /// let data = vec![1, 2, 3, 4, 5, 6, 7, 8]; /// let mut rng = ReadRng::new(&data[..]); -/// println!("{:x}", rng.gen::()); +/// println!("{:x}", dist::uniform::(&mut rng)); /// ``` #[derive(Debug)] pub struct ReadRng { From 92ee7782557d0ac3d31909ae9cfed23e11a97eca Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sat, 29 Jul 2017 09:17:18 +0100 Subject: [PATCH 022/247] Remove Rand, RandSample and gen(). Add comments and doc. --- src/dist/mod.rs | 28 +------------- src/dist/weighted.rs | 16 +------- src/lib.rs | 66 ++------------------------------- src/prng/chacha.rs | 28 +++++++------- src/prng/isaac.rs | 87 ++++++++++++++++++++++++-------------------- src/prng/mod.rs | 3 ++ src/prng/xorshift.rs | 34 +++++++++-------- 7 files changed, 91 insertions(+), 171 deletions(-) diff --git a/src/dist/mod.rs b/src/dist/mod.rs index 224265ecd1c..beed17e1fe0 100644 --- a/src/dist/mod.rs +++ b/src/dist/mod.rs @@ -17,9 +17,7 @@ //! //! TODO: is it worth exposing both submodules and re-exporting their members? -use std::marker; - -use {Rng, Rand}; +use Rng; pub use self::uniform::{uniform, uniform01, open01, closed01, codepoint}; pub use self::range::Range; @@ -42,30 +40,6 @@ pub trait Sample { } -/// A wrapper for generating types that implement `Rand` via the -/// `Sample` trait. -#[derive(Debug)] -pub struct RandSample { - _marker: marker::PhantomData T>, -} - -impl Copy for RandSample {} -impl Clone for RandSample { - fn clone(&self) -> Self { *self } -} - -impl Sample for RandSample { - fn sample(&self, rng: &mut R) -> T { - rng.gen() - } -} - -impl RandSample { - pub fn new() -> RandSample { - RandSample { _marker: marker::PhantomData } - } -} - mod ziggurat_tables; /// Sample a random number using the Ziggurat method (specifically the diff --git a/src/dist/weighted.rs b/src/dist/weighted.rs index 8794e5a42f3..9d9c43b9c20 100644 --- a/src/dist/weighted.rs +++ b/src/dist/weighted.rs @@ -138,18 +138,12 @@ impl<'a, T: Clone> Sample for WeightedChoice<'a, T> { #[cfg(test)] mod tests { - - use {Rng, Rand}; - use dist::{RandSample, Sample}; + use Rng; + use dist::Sample; use super::{WeightedChoice, Weighted}; #[derive(PartialEq, Debug)] struct ConstRand(usize); - impl Rand for ConstRand { - fn rand(_: &mut R) -> ConstRand { - ConstRand(0) - } - } // 0, 1, 2, 3, ... struct CountingRng { i: u32 } @@ -163,12 +157,6 @@ mod tests { } } - #[test] - fn test_rand_sample() { - let rand_sample = RandSample::::new(); - - assert_eq!(rand_sample.sample(&mut ::test::rng()), ConstRand(0)); - } #[test] fn test_weighted_choice() { // this makes assumptions about the internal implementation of diff --git a/src/lib.rs b/src/lib.rs index 7a1275dd233..65b2f7a6672 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -269,45 +269,6 @@ pub mod reseeding; mod read; mod os; -/// A type that can be randomly generated using an `Rng`. -/// -/// ## Built-in Implementations -/// -/// This crate implements `Rand` for various primitive types. Assuming the -/// provided `Rng` is well-behaved, these implementations generate values with -/// the following ranges and distributions: -/// -/// * Integers (`i32`, `u32`, `isize`, `usize`, etc.): Uniformly distributed -/// over all values of the type. -/// * `char`: Uniformly distributed over all Unicode scalar values, i.e. all -/// code points in the range `0...0x10_FFFF`, except for the range -/// `0xD800...0xDFFF` (the surrogate code points). This includes -/// unassigned/reserved code points. -/// * `bool`: Generates `false` or `true`, each with probability 0.5. -/// * Floating point types (`f32` and `f64`): Uniformly distributed in the -/// half-open range `[0, 1)`. (The [`Open01`], [`Closed01`], [`Exp1`], and -/// [`StandardNormal`] wrapper types produce floating point numbers with -/// alternative ranges or distributions.) -/// -/// [`Open01`]: struct.Open01.html -/// [`Closed01`]: struct.Closed01.html -/// [`Exp1`]: struct.Exp1.html -/// [`StandardNormal`]: struct.StandardNormal.html -/// -/// The following aggregate types also implement `Rand` as long as their -/// component types implement it: -/// -/// * Tuples and arrays: Each element of the tuple or array is generated -/// independently, using its own `Rand` implementation. -/// * `Option`: Returns `None` with probability 0.5; otherwise generates a -/// random `T` and returns `Some(T)`. - -pub trait Rand : Sized { - /// Generates a random instance of this type using the specified source of - /// randomness. - fn rand(rng: &mut R) -> Self; -} - /// A random number generator. pub trait Rng { /// Return the next random u32. @@ -429,27 +390,6 @@ pub trait Rng { } } - /// Return a random value of a `Rand` type. - /// - /// # Example - /// - /// ```rust - /// use rand::{thread_rng, Rng}; - /// use rand::dist::{uniform, uniform01}; - /// - /// let mut rng = thread_rng(); - /// let x: u32 = uniform(&mut rng); - /// let y: f64 = uniform01(&mut rng); - /// let z: bool = uniform(&mut rng); - /// println!("{}", x); - /// println!("{}", y); - /// println!("{}", z); - /// ``` - #[inline(always)] - fn gen(&mut self) -> T where Self: Sized { - Rand::rand(self) - } - /// Yield an iterator applying some function to self. /// /// Unfortunately this is only possible with static dispatch (i.e. where @@ -737,7 +677,7 @@ impl StdRng { /// Reading the randomness from the OS may fail, and any error is /// propagated via the `io::Result` return value. pub fn new() -> io::Result { - OsRng::new().map(|mut r| StdRng { rng: r.gen() }) + OsRng::new().map(|mut r| StdRng { rng: IsaacWordRng::new_from_rng(&mut r) }) } } @@ -774,9 +714,11 @@ impl<'a> SeedableRng<&'a [usize]> for StdRng { /// /// This will read randomness from the operating system to seed the /// generator. +// TODO: is this method useful? pub fn weak_rng() -> XorShiftRng { + // TODO: should this seed from `thread_rng()`? match OsRng::new() { - Ok(mut r) => r.gen(), + Ok(mut r) => XorShiftRng::new_from_rng(&mut r), Err(e) => panic!("weak_rng: failed to create seeded RNG: {:?}", e) } } diff --git a/src/prng/chacha.rs b/src/prng/chacha.rs index e3c9fd8e5e1..4bf6c349217 100644 --- a/src/prng/chacha.rs +++ b/src/prng/chacha.rs @@ -11,7 +11,7 @@ //! The ChaCha random number generator. use std::num::Wrapping as w; -use {Rng, SeedableRng, Rand}; +use {Rng, SeedableRng}; #[allow(bad_style)] type w32 = w; @@ -81,7 +81,6 @@ fn core(output: &mut [w32; STATE_WORDS], input: &[w32; STATE_WORDS]) { } impl ChaChaRng { - /// Create an ChaCha random number generator using the default /// fixed key of 8 zero words. /// @@ -108,6 +107,20 @@ impl ChaChaRng { rng } + /// Create an ChaCha random number generator, seeding from another generator. + /// + /// Care should be taken when seeding one RNG from another. There is no + /// free entropy gained. In some cases where the parent and child RNGs use + /// the same algorithm, both generate the same output sequences (possibly + /// with a small lag). + pub fn new_from_rng(other: &mut R) -> ChaChaRng { + let mut key : [u32; KEY_WORDS] = [0; KEY_WORDS]; + for word in key.iter_mut() { + *word = other.next_u32(); + } + SeedableRng::from_seed(&key[..]) + } + /// Sets the internal 128-bit ChaCha counter to /// a user-provided value. This permits jumping /// arbitrarily ahead (or backwards) in the pseudorandom stream. @@ -201,7 +214,6 @@ impl Rng for ChaChaRng { } impl<'a> SeedableRng<&'a [u32]> for ChaChaRng { - fn reseed(&mut self, seed: &'a [u32]) { // reset state self.init(&[0u32; KEY_WORDS]); @@ -223,16 +235,6 @@ impl<'a> SeedableRng<&'a [u32]> for ChaChaRng { } } -impl Rand for ChaChaRng { - fn rand(other: &mut R) -> ChaChaRng { - let mut key : [u32; KEY_WORDS] = [0; KEY_WORDS]; - for word in key.iter_mut() { - *word = other.next_u32(); - } - SeedableRng::from_seed(&key[..]) - } -} - #[cfg(test)] mod test { diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index f80f2a58670..0b6b100276a 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -17,7 +17,7 @@ use std::iter::repeat; use std::num::Wrapping as w; use std::fmt; -use {Rng, SeedableRng, Rand}; +use {Rng, SeedableRng}; /// Select 32- or 64-bit variant dependent on pointer size. #[cfg(target_pointer_width = "32")] @@ -61,7 +61,6 @@ static EMPTY: IsaacRng = IsaacRng { }; impl IsaacRng { - /// Create an ISAAC random number generator using the default /// fixed seed. pub fn new_unseeded() -> IsaacRng { @@ -70,6 +69,29 @@ impl IsaacRng { rng } + /// Create an ISAAC random number generator, seeding from another generator. + /// + /// Care should be taken when seeding one RNG from another. There is no + /// free entropy gained. In some cases where the parent and child RNGs use + /// the same algorithm, both generate the same output sequences (possibly + /// with a small lag). + pub fn new_from_rng(other: &mut R) -> IsaacRng { + let mut ret = EMPTY; + unsafe { + let ptr = ret.rsl.as_mut_ptr() as *mut u8; + + let slice = slice::from_raw_parts_mut(ptr, RAND_SIZE_USIZE * 4); + other.fill_bytes(slice); + } + ret.cnt = 0; + ret.a = w(0); + ret.b = w(0); + ret.c = w(0); + + ret.init(true); + return ret; + } + /// Initialises `self`. If `use_rsl` is true, then use the current value /// of `rsl` as a seed, otherwise construct one algorithmically (not /// randomly). @@ -253,25 +275,6 @@ impl<'a> SeedableRng<&'a [u32]> for IsaacRng { } } -impl Rand for IsaacRng { - fn rand(other: &mut R) -> IsaacRng { - let mut ret = EMPTY; - unsafe { - let ptr = ret.rsl.as_mut_ptr() as *mut u8; - - let slice = slice::from_raw_parts_mut(ptr, RAND_SIZE_USIZE * 4); - other.fill_bytes(slice); - } - ret.cnt = 0; - ret.a = w(0); - ret.b = w(0); - ret.c = w(0); - - ret.init(true); - return ret; - } -} - impl fmt::Debug for IsaacRng { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "IsaacRng {{}}") @@ -317,6 +320,29 @@ impl Isaac64Rng { rng } + /// Create an ISAAC random number generator, seeding from another generator. + /// + /// Care should be taken when seeding one RNG from another. There is no + /// free entropy gained. In some cases where the parent and child RNGs use + /// the same algorithm, both generate the same output sequences (possibly + /// with a small lag). + pub fn new_from_rng(other: &mut R) -> Isaac64Rng { + let mut ret = EMPTY_64; + unsafe { + let ptr = ret.rsl.as_mut_ptr() as *mut u8; + + let slice = slice::from_raw_parts_mut(ptr, RAND_SIZE_64 * 8); + other.fill_bytes(slice); + } + ret.cnt = 0; + ret.a = w(0); + ret.b = w(0); + ret.c = w(0); + + ret.init(true); + return ret; + } + /// Initialises `self`. If `use_rsl` is true, then use the current value /// of `rsl` as a seed, otherwise construct one algorithmically (not /// randomly). @@ -501,25 +527,6 @@ impl<'a> SeedableRng<&'a [u64]> for Isaac64Rng { } } -impl Rand for Isaac64Rng { - fn rand(other: &mut R) -> Isaac64Rng { - let mut ret = EMPTY_64; - unsafe { - let ptr = ret.rsl.as_mut_ptr() as *mut u8; - - let slice = slice::from_raw_parts_mut(ptr, RAND_SIZE_64 * 8); - other.fill_bytes(slice); - } - ret.cnt = 0; - ret.a = w(0); - ret.b = w(0); - ret.c = w(0); - - ret.init(true); - return ret; - } -} - impl fmt::Debug for Isaac64Rng { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Isaac64Rng {{}}") diff --git a/src/prng/mod.rs b/src/prng/mod.rs index eeb9f8fce2c..c07f09b7994 100644 --- a/src/prng/mod.rs +++ b/src/prng/mod.rs @@ -39,6 +39,9 @@ //! short periods for some seeds. If one PRNG is seeded from another using the //! same algorithm, it is possible that both will yield the same sequence of //! values (with some lag). +//! +//! TODO: add some guidance on selection of a PRNG: cryptographic approval, +//! statistical properties, performance. mod chacha; mod isaac; diff --git a/src/prng/xorshift.rs b/src/prng/xorshift.rs index 6df5a3cd9eb..116e1ec7b8b 100644 --- a/src/prng/xorshift.rs +++ b/src/prng/xorshift.rs @@ -11,7 +11,7 @@ //! Xorshift generators use std::num::Wrapping as w; -use {Rng, SeedableRng, Rand}; +use {Rng, SeedableRng}; /// An Xorshift[1] random number /// generator. @@ -47,6 +47,24 @@ impl XorShiftRng { w: w(0x113ba7bb), } } + + /// Create an ChaCha random number generator, seeding from another generator. + /// + /// Care should be taken when seeding one RNG from another. There is no + /// free entropy gained. In some cases where the parent and child RNGs use + /// the same algorithm, both generate the same output sequences (possibly + /// with a small lag). + pub fn new_from_rng(rng: &mut R) -> XorShiftRng { + let mut tuple: (u32, u32, u32, u32); + loop { + tuple = (rng.next_u32(), rng.next_u32(), rng.next_u32(), rng.next_u32()); + if tuple != (0, 0, 0, 0) { + break; + } + } + let (x, y, z, w_) = tuple; + XorShiftRng { x: w(x), y: w(y), z: w(z), w: w(w_) } + } } impl Rng for XorShiftRng { @@ -88,17 +106,3 @@ impl SeedableRng<[u32; 4]> for XorShiftRng { } } } - -impl Rand for XorShiftRng { - fn rand(rng: &mut R) -> XorShiftRng { - let mut tuple: (u32, u32, u32, u32); - loop { - tuple = (rng.next_u32(), rng.next_u32(), rng.next_u32(), rng.next_u32()); - if tuple != (0, 0, 0, 0) { - break; - } - } - let (x, y, z, w_) = tuple; - XorShiftRng { x: w(x), y: w(y), z: w(z), w: w(w_) } - } -} From 0df5fd199a60dd5f54f1c2d883782b58cb27897b Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sat, 29 Jul 2017 09:48:10 +0100 Subject: [PATCH 023/247] =?UTF-8?q?Rename=20Sample=20=E2=86=92=20Distribut?= =?UTF-8?q?ion.=20Make=20WeightedChoice=20own=20its=20arguments.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The change to WeightedChoice is debatable, but since the argument must normally be constructed specially anyway, the reference model makes little sense. See issues #90, #142 and PR #152. --- benches/distributions/exponential.rs | 2 +- benches/distributions/gamma.rs | 2 +- benches/distributions/normal.rs | 2 +- src/dist/exponential.rs | 8 +++--- src/dist/gamma.rs | 24 ++++++++--------- src/dist/mod.rs | 4 +-- src/dist/normal.rs | 12 ++++----- src/dist/range.rs | 8 +++--- src/dist/uniform.rs | 27 +++++++++++++++---- src/dist/weighted.rs | 39 ++++++++++++---------------- src/lib.rs | 6 ++--- 11 files changed, 73 insertions(+), 61 deletions(-) diff --git a/benches/distributions/exponential.rs b/benches/distributions/exponential.rs index f9b6dcb6bbf..20ecd245769 100644 --- a/benches/distributions/exponential.rs +++ b/benches/distributions/exponential.rs @@ -2,7 +2,7 @@ use std::mem::size_of; use test::Bencher; use rand; use rand::dist::exponential::Exp; -use rand::dist::Sample; +use rand::dist::Distribution; #[bench] fn rand_exp(b: &mut Bencher) { diff --git a/benches/distributions/gamma.rs b/benches/distributions/gamma.rs index 19b63795115..6b122a21ddf 100644 --- a/benches/distributions/gamma.rs +++ b/benches/distributions/gamma.rs @@ -1,7 +1,7 @@ use std::mem::size_of; use test::Bencher; use rand; -use rand::dist::Sample; +use rand::dist::Distribution; use rand::dist::gamma::Gamma; #[bench] diff --git a/benches/distributions/normal.rs b/benches/distributions/normal.rs index 7c53c8f85ac..e602124ce8c 100644 --- a/benches/distributions/normal.rs +++ b/benches/distributions/normal.rs @@ -1,7 +1,7 @@ use std::mem::size_of; use test::Bencher; use rand; -use rand::dist::Sample; +use rand::dist::Distribution; use rand::dist::normal::Normal; #[bench] diff --git a/src/dist/exponential.rs b/src/dist/exponential.rs index 06eac82313e..ea3320fc797 100644 --- a/src/dist/exponential.rs +++ b/src/dist/exponential.rs @@ -11,7 +11,7 @@ //! The exponential distribution. use {Rng}; -use dist::{ziggurat, ziggurat_tables, Sample, uniform01}; +use dist::{ziggurat, ziggurat_tables, Distribution, uniform01}; /// Generates Exp(1) random numbers. /// @@ -60,7 +60,7 @@ pub fn exp1(rng: &mut R) -> f64 { /// # Example /// /// ```rust -/// use rand::dist::{Exp, Sample}; +/// use rand::dist::{Exp, Distribution}; /// /// let exp = Exp::new(2.0); /// let v = exp.sample(&mut rand::thread_rng()); @@ -82,7 +82,7 @@ impl Exp { } } -impl Sample for Exp { +impl Distribution for Exp { fn sample(&self, rng: &mut R) -> f64 { exp1(rng) * self.lambda_inverse } @@ -90,7 +90,7 @@ impl Sample for Exp { #[cfg(test)] mod test { - use dist::{Sample}; + use dist::{Distribution}; use super::Exp; #[test] diff --git a/src/dist/gamma.rs b/src/dist/gamma.rs index a98591a71d5..1d76be8ae1a 100644 --- a/src/dist/gamma.rs +++ b/src/dist/gamma.rs @@ -17,7 +17,7 @@ use self::ChiSquaredRepr::*; use {Rng}; use dist::normal::standard_normal; -use dist::{Sample, Exp, open01}; +use dist::{Distribution, Exp, open01}; /// The Gamma distribution `Gamma(shape, scale)` distribution. /// @@ -38,7 +38,7 @@ use dist::{Sample, Exp, open01}; /// # Example /// /// ```rust -/// use rand::dist::{Sample, Gamma}; +/// use rand::dist::{Distribution, Gamma}; /// /// let gamma = Gamma::new(2.0, 5.0); /// let v = gamma.sample(&mut rand::thread_rng()); @@ -134,7 +134,7 @@ impl GammaLargeShape { } -impl Sample for Gamma { +impl Distribution for Gamma { fn sample(&self, rng: &mut R) -> f64 { match self.repr { Small(ref g) => g.sample(rng), @@ -143,14 +143,14 @@ impl Sample for Gamma { } } } -impl Sample for GammaSmallShape { +impl Distribution for GammaSmallShape { fn sample(&self, rng: &mut R) -> f64 { let u: f64 = open01(rng); self.large_shape.sample(rng) * u.powf(self.inv_shape) } } -impl Sample for GammaLargeShape { +impl Distribution for GammaLargeShape { fn sample(&self, rng: &mut R) -> f64 { loop { let x = standard_normal(rng); @@ -182,7 +182,7 @@ impl Sample for GammaLargeShape { /// # Example /// /// ```rust -/// use rand::dist::{ChiSquared, Sample}; +/// use rand::dist::{ChiSquared, Distribution}; /// /// let chi = ChiSquared::new(11.0); /// let v = chi.sample(&mut rand::thread_rng()); @@ -215,7 +215,7 @@ impl ChiSquared { ChiSquared { repr: repr } } } -impl Sample for ChiSquared { +impl Distribution for ChiSquared { fn sample(&self, rng: &mut R) -> f64 { match self.repr { DoFExactlyOne => { @@ -237,7 +237,7 @@ impl Sample for ChiSquared { /// # Example /// /// ```rust -/// use rand::dist::{FisherF, Sample}; +/// use rand::dist::{FisherF, Distribution}; /// /// let f = FisherF::new(2.0, 32.0); /// let v = f.sample(&mut rand::thread_rng()); @@ -266,7 +266,7 @@ impl FisherF { } } } -impl Sample for FisherF { +impl Distribution for FisherF { fn sample(&self, rng: &mut R) -> f64 { self.numer.sample(rng) / self.denom.sample(rng) * self.dof_ratio } @@ -278,7 +278,7 @@ impl Sample for FisherF { /// # Example /// /// ```rust -/// use rand::dist::{StudentT, Sample}; +/// use rand::dist::{StudentT, Distribution}; /// /// let t = StudentT::new(11.0); /// let v = t.sample(&mut rand::thread_rng()); @@ -301,7 +301,7 @@ impl StudentT { } } } -impl Sample for StudentT { +impl Distribution for StudentT { fn sample(&self, rng: &mut R) -> f64 { let norm = standard_normal(rng); norm * (self.dof / self.chi.sample(rng)).sqrt() @@ -310,7 +310,7 @@ impl Sample for StudentT { #[cfg(test)] mod test { - use dist::{Sample}; + use dist::{Distribution}; use super::{ChiSquared, StudentT, FisherF}; #[test] diff --git a/src/dist/mod.rs b/src/dist/mod.rs index beed17e1fe0..f3a36838528 100644 --- a/src/dist/mod.rs +++ b/src/dist/mod.rs @@ -12,7 +12,7 @@ //! //! A distribution may have internal state describing the distribution of //! generated values; for example `Range` needs to know its upper and lower -//! bounds. Distributions use the `Sample` trait to yield values: call +//! bounds. Distributions use the `Distribution` trait to yield values: call //! `dist.sample(&mut rng)` to get a random variable. //! //! TODO: is it worth exposing both submodules and re-exporting their members? @@ -33,7 +33,7 @@ pub mod exponential; pub mod weighted; /// Types (distributions) that can be used to create a random instance of `T`. -pub trait Sample { +pub trait Distribution { /// Generate a random value of `T`, using `rng` as the /// source of randomness. fn sample(&self, rng: &mut R) -> T; diff --git a/src/dist/normal.rs b/src/dist/normal.rs index 2e21ad47c79..386edca869c 100644 --- a/src/dist/normal.rs +++ b/src/dist/normal.rs @@ -11,7 +11,7 @@ //! The normal and derived distributions. use {Rng}; -use dist::{ziggurat, ziggurat_tables, Sample, open01}; +use dist::{ziggurat, ziggurat_tables, Distribution, open01}; /// Generates N(0, 1) random numbers /// (a.k.a. a standard normal, or Gaussian). @@ -76,7 +76,7 @@ pub fn standard_normal(rng: &mut R) -> f64 { /// # Example /// /// ```rust -/// use rand::dist::{Normal, Sample}; +/// use rand::dist::{Normal, Distribution}; /// /// // mean 2, standard deviation 3 /// let normal = Normal::new(2.0, 3.0); @@ -105,7 +105,7 @@ impl Normal { } } } -impl Sample for Normal { +impl Distribution for Normal { fn sample(&self, rng: &mut R) -> f64 { self.mean + self.std_dev * standard_normal(rng) } @@ -120,7 +120,7 @@ impl Sample for Normal { /// # Example /// /// ```rust -/// use rand::dist::{LogNormal, Sample}; +/// use rand::dist::{LogNormal, Distribution}; /// /// // mean 2, standard deviation 3 /// let log_normal = LogNormal::new(2.0, 3.0); @@ -145,7 +145,7 @@ impl LogNormal { LogNormal { norm: Normal::new(mean, std_dev) } } } -impl Sample for LogNormal { +impl Distribution for LogNormal { fn sample(&self, rng: &mut R) -> f64 { self.norm.sample(rng).exp() } @@ -153,7 +153,7 @@ impl Sample for LogNormal { #[cfg(test)] mod tests { - use dist::{Sample}; + use dist::{Distribution}; use super::{Normal, LogNormal}; #[test] diff --git a/src/dist/range.rs b/src/dist/range.rs index e80f5f317b2..cf00f7adeab 100644 --- a/src/dist/range.rs +++ b/src/dist/range.rs @@ -15,7 +15,7 @@ use std::num::Wrapping as w; use Rng; -use dist::{Sample, uniform01}; +use dist::{Distribution, uniform01}; /// Sample values uniformly between two bounds. /// @@ -34,7 +34,7 @@ use dist::{Sample, uniform01}; /// # Example /// /// ```rust -/// use rand::dist::{Sample, Range}; +/// use rand::dist::{Distribution, Range}; /// /// fn main() { /// let between = Range::new(10, 10000); @@ -62,7 +62,7 @@ impl Range { } } -impl Sample for Range { +impl Distribution for Range { fn sample(&self, rng: &mut R) -> T { SampleRange::sample_range(self, rng) } @@ -162,7 +162,7 @@ float_impl! { f64 } #[cfg(test)] mod tests { - use dist::{Sample}; + use dist::{Distribution}; use super::Range as Range; #[should_panic] diff --git a/src/dist/uniform.rs b/src/dist/uniform.rs index 80b88466b62..51d0c01f90f 100644 --- a/src/dist/uniform.rs +++ b/src/dist/uniform.rs @@ -11,11 +11,12 @@ //! Generating uniformly distributed numbers use std::char; +use std::fmt; use std::mem; use std::marker::PhantomData; use Rng; -use dist::Sample; +use dist::Distribution; // ----- convenience functions ----- @@ -71,13 +72,13 @@ pub fn codepoint(rng: &mut R) -> char { } -// ----- Sample implementations ----- +// ----- Distribution implementations ----- // TODO: do we want these? If so, implement for other ranges. /// Sample values uniformly over the whole range supported by the type. /// /// No internal state. -#[derive(Clone, Copy, Debug, Default)] +#[derive(Default)] pub struct Uniform { _marker: PhantomData, } @@ -92,7 +93,23 @@ impl Uniform { } } -impl Sample for Uniform { +impl Copy for Uniform {} + +// derive only auto-impls for types T: Clone, but we don't have that restriction +impl Clone for Uniform { + fn clone(&self) -> Self { + Uniform::new() + } +} + +// derive only auto-impls for types T: Debug, but we don't have that restriction +impl fmt::Debug for Uniform { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!(f, "Uniform {{}}") + } +} + +impl Distribution for Uniform { fn sample(&self, rng: &mut R) -> T { T::sample_uniform(rng) } @@ -267,7 +284,7 @@ float_impls! { SCALE_F32, f32, 24, next_f32 } #[cfg(test)] mod tests { use {Rng, thread_rng}; - use dist::{uniform, Sample}; + use dist::{uniform, Distribution}; use dist::uniform::{SampleUniform, Uniform}; use dist::{uniform01, open01, closed01}; diff --git a/src/dist/weighted.rs b/src/dist/weighted.rs index 9d9c43b9c20..f4c05eece98 100644 --- a/src/dist/weighted.rs +++ b/src/dist/weighted.rs @@ -14,7 +14,7 @@ //! adapted, or removed entirely. use Rng; -use dist::{Range, Sample}; +use dist::{Range, Distribution}; /// A value with a particular weight for use with `WeightedChoice`. #[derive(Copy, Clone, Debug)] @@ -30,41 +30,36 @@ pub struct Weighted { /// Each item has an associated weight that influences how likely it /// is to be chosen: higher weight is more likely. /// -/// The `Clone` restriction is a limitation of the `Sample` trait. -/// Note that `&T` is (cheaply) `Clone` for -/// all `T`, as is `u32`, so one can store references or indices into -/// another vector. -/// /// # Example /// /// ```rust -/// use rand::dist::Sample; +/// use rand::dist::Distribution; /// use rand::dist::weighted::{Weighted, WeightedChoice}; /// -/// let mut items = vec!(Weighted { weight: 2, item: 'a' }, +/// let items = vec!(Weighted { weight: 2, item: 'a' }, /// Weighted { weight: 4, item: 'b' }, /// Weighted { weight: 1, item: 'c' }); -/// let wc = WeightedChoice::new(&mut items); +/// let wc = WeightedChoice::new(items); /// let mut rng = rand::thread_rng(); /// for _ in 0..16 { /// // on average prints 'a' 4 times, 'b' 8 and 'c' twice. /// println!("{}", wc.sample(&mut rng)); /// } /// ``` -#[derive(Debug)] -pub struct WeightedChoice<'a, T:'a> { - items: &'a mut [Weighted], +#[derive(Clone, Debug)] +pub struct WeightedChoice { + items: Vec>, weight_range: Range } -impl<'a, T: Clone> WeightedChoice<'a, T> { +impl WeightedChoice { /// Create a new `WeightedChoice`. /// /// Panics if: /// - `v` is empty /// - the total weight is 0 /// - the total weight is larger than a `u32` can contain. - pub fn new(items: &'a mut [Weighted]) -> WeightedChoice<'a, T> { + pub fn new(mut items: Vec>) -> WeightedChoice { // strictly speaking, this is subsumed by the total weight == 0 case assert!(!items.is_empty(), "WeightedChoice::new called with no items"); @@ -73,7 +68,7 @@ impl<'a, T: Clone> WeightedChoice<'a, T> { // we convert the list from individual weights to cumulative // weights so we can binary search. This *could* drop elements // with weight == 0 as an optimisation. - for item in items.iter_mut() { + for ref mut item in items.iter_mut() { running_total = match running_total.checked_add(item.weight) { Some(n) => n, None => panic!("WeightedChoice::new called with a total weight \ @@ -93,7 +88,7 @@ impl<'a, T: Clone> WeightedChoice<'a, T> { } } -impl<'a, T: Clone> Sample for WeightedChoice<'a, T> { +impl Distribution for WeightedChoice { fn sample(&self, rng: &mut R) -> T { // we want to find the first element that has cumulative // weight > sample_weight, which we do by binary since the @@ -139,7 +134,7 @@ impl<'a, T: Clone> Sample for WeightedChoice<'a, T> { #[cfg(test)] mod tests { use Rng; - use dist::Sample; + use dist::Distribution; use super::{WeightedChoice, Weighted}; #[derive(PartialEq, Debug)] @@ -166,8 +161,8 @@ mod tests { macro_rules! t { ($items:expr, $expected:expr) => {{ - let mut items = $items; - let wc = WeightedChoice::new(&mut items); + let items = $items; + let wc = WeightedChoice::new(items); let expected = $expected; let mut rng = CountingRng { i: 0 }; @@ -238,17 +233,17 @@ mod tests { #[test] #[should_panic] fn test_weighted_choice_no_items() { - WeightedChoice::::new(&mut []); + WeightedChoice::::new(vec![]); } #[test] #[should_panic] fn test_weighted_choice_zero_weight() { - WeightedChoice::new(&mut [Weighted { weight: 0, item: 0}, + WeightedChoice::new(vec![Weighted { weight: 0, item: 0}, Weighted { weight: 0, item: 1}]); } #[test] #[should_panic] fn test_weighted_choice_weight_overflows() { let x = ::std::u32::MAX / 2; // x + x + 2 is the overflow - WeightedChoice::new(&mut [Weighted { weight: x, item: 0 }, + WeightedChoice::new(vec![Weighted { weight: x, item: 0 }, Weighted { weight: 1, item: 1 }, Weighted { weight: x, item: 2 }, Weighted { weight: 1, item: 3 }]); diff --git a/src/lib.rs b/src/lib.rs index 65b2f7a6672..ba4227628f5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -112,7 +112,7 @@ //! and multiply this fraction by 4. //! //! ``` -//! use rand::dist::{Sample, Range}; +//! use rand::dist::{Distribution, Range}; //! //! fn main() { //! let between = Range::new(-1f64, 1.); @@ -155,7 +155,7 @@ //! //! ``` //! use rand::Rng; -//! use rand::dist::{Sample, Range, uniform}; +//! use rand::dist::{Distribution, Range, uniform}; //! //! struct SimulationResult { //! win: bool, @@ -256,7 +256,7 @@ use std::rc::Rc; pub use read::ReadRng; pub use os::OsRng; -use dist::{Range, Sample}; +use dist::{Range, Distribution}; use dist::range::SampleRange; use prng::IsaacWordRng; From 686cdb18933fb03ea88b5e3f3f1f764734e3f506 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sat, 29 Jul 2017 10:29:11 +0100 Subject: [PATCH 024/247] Add DefaultDist distribution (is it useful?) Unfortunately implementation hits rust issue #23341 --- src/dist/default.rs | 125 ++++++++++++++++++++++++++++++++++++++++++++ src/dist/mod.rs | 2 + src/dist/uniform.rs | 62 +++------------------- 3 files changed, 133 insertions(+), 56 deletions(-) create mode 100644 src/dist/default.rs diff --git a/src/dist/default.rs b/src/dist/default.rs new file mode 100644 index 00000000000..d8f7b890f34 --- /dev/null +++ b/src/dist/default.rs @@ -0,0 +1,125 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Generic value creation + +use std::fmt; +use std::marker::PhantomData; + +use Rng; +use dist::Distribution; +use dist::uniform::{SampleUniform, /*SampleUniform01,*/ codepoint}; + +/// A generic random value creator. Generates values using what appears to be +/// an appropriate distribution for each type, but choice is arbitrary. +/// +/// Makes use of the following traits on the types implementing them: +/// +/// * [`SampleUniform`] for integer types +/// * [`SampleUniform01`] for floating point types (FIXME) +/// +/// Makes use of the following methods: +/// +/// * [`codepoint`] for `char` +/// +/// TODO: link +/// +/// TODO: is this useful? Personally I feel not, but it carries over previous +/// functionality. +#[derive(Default)] +pub struct DefaultDist { + _marker: PhantomData, +} + +impl DefaultDist { + /// Create an instance. Should optimise to nothing, since there is no + /// internal state. + pub fn new() -> Self { + DefaultDist { + _marker: PhantomData + } + } +} + +impl Copy for DefaultDist {} + +// derive only auto-impls for types T: Clone, but we don't have that restriction +impl Clone for DefaultDist { + fn clone(&self) -> Self { + DefaultDist::new() + } +} + +// derive only auto-impls for types T: Debug, but we don't have that restriction +impl fmt::Debug for DefaultDist { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!(f, "DefaultDist {{}}") + } +} + +impl Distribution for DefaultDist { + fn sample(&self, rng: &mut R) -> T { + T::sample_default(rng) + } +} + + +// ----- SampleDefault ----- + +/// Types supporting default sampling (see `DefaultDist`) +pub trait SampleDefault { + /// Sample a value using an RNG + fn sample_default(rng: &mut R) -> Self; +} + +impl SampleDefault for T { + fn sample_default(rng: &mut R) -> Self { + T::sample_uniform(rng) + } +} +// FIXME: https://github.com/rust-lang/rust/issues/23341 +// impl SampleDefault for T { +// fn sample_default(rng: &mut R) -> T { +// T::sample_uniform01(rng) +// } +// } +impl SampleDefault for char { + fn sample_default(rng: &mut R) -> char { + codepoint(rng) + } +} + + +#[cfg(test)] +mod tests { + use {Rng, thread_rng}; + use dist::Distribution; + use dist::default::{DefaultDist, SampleDefault}; + + + #[test] + fn test_types() { + let mut rng = thread_rng(); + fn do_test(rng: &mut R) -> T { + let dist = DefaultDist::::new(); + dist.sample(rng) + } + + do_test::(&mut rng); + do_test::(&mut rng); + // FIXME (see above) + // do_test::(&mut rng); + // do_test::(&mut rng); + #[cfg(feature = "i128_support")] + do_test::(&mut rng); + do_test::(&mut rng); + do_test::(&mut rng); + } +} diff --git a/src/dist/mod.rs b/src/dist/mod.rs index f3a36838528..0ecd1d152ce 100644 --- a/src/dist/mod.rs +++ b/src/dist/mod.rs @@ -19,12 +19,14 @@ use Rng; +pub use self::default::{DefaultDist, SampleDefault}; pub use self::uniform::{uniform, uniform01, open01, closed01, codepoint}; pub use self::range::Range; pub use self::gamma::{Gamma, ChiSquared, FisherF, StudentT}; pub use self::normal::{Normal, LogNormal}; pub use self::exponential::Exp; +pub mod default; pub mod uniform; pub mod range; pub mod gamma; diff --git a/src/dist/uniform.rs b/src/dist/uniform.rs index 51d0c01f90f..6dd06077067 100644 --- a/src/dist/uniform.rs +++ b/src/dist/uniform.rs @@ -11,12 +11,9 @@ //! Generating uniformly distributed numbers use std::char; -use std::fmt; use std::mem; -use std::marker::PhantomData; use Rng; -use dist::Distribution; // ----- convenience functions ----- @@ -72,73 +69,29 @@ pub fn codepoint(rng: &mut R) -> char { } -// ----- Distribution implementations ----- -// TODO: do we want these? If so, implement for other ranges. - -/// Sample values uniformly over the whole range supported by the type. -/// -/// No internal state. -#[derive(Default)] -pub struct Uniform { - _marker: PhantomData, -} - -impl Uniform { - /// Create an instance. Should optimise to nothing, since there is no - /// internal state. - pub fn new() -> Self { - Uniform { - _marker: PhantomData - } - } -} - -impl Copy for Uniform {} - -// derive only auto-impls for types T: Clone, but we don't have that restriction -impl Clone for Uniform { - fn clone(&self) -> Self { - Uniform::new() - } -} - -// derive only auto-impls for types T: Debug, but we don't have that restriction -impl fmt::Debug for Uniform { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - write!(f, "Uniform {{}}") - } -} - -impl Distribution for Uniform { - fn sample(&self, rng: &mut R) -> T { - T::sample_uniform(rng) - } -} - - // ----- Sampling traits ----- /// Sample values uniformly over the whole range supported by the type pub trait SampleUniform: Sized { - /// Sample a value from a RNG + /// Sample a value using an RNG fn sample_uniform(rng: &mut R) -> Self; } /// Sample values uniformly over the half-open range [0, 1) pub trait SampleUniform01: Sized { - /// Sample a value from a RNG + /// Sample a value using an RNG fn sample_uniform01(rng: &mut R) -> Self; } /// Sample values uniformly over the open range (0, 1) pub trait SampleOpen01: Sized { - /// Sample a value from a RNG + /// Sample a value using an RNG fn sample_open01(rng: &mut R) -> Self; } /// Sample values uniformly over the closed range [0, 1] pub trait SampleClosed01: Sized { - /// Sample a value from a RNG + /// Sample a value using an RNG fn sample_closed01(rng: &mut R) -> Self; } @@ -284,8 +237,8 @@ float_impls! { SCALE_F32, f32, 24, next_f32 } #[cfg(test)] mod tests { use {Rng, thread_rng}; - use dist::{uniform, Distribution}; - use dist::uniform::{SampleUniform, Uniform}; + use dist::{uniform}; + use dist::uniform::SampleUniform; use dist::{uniform01, open01, closed01}; #[test] @@ -310,9 +263,6 @@ mod tests { let _: u64 = uniform(&mut rng); #[cfg(feature = "i128_support")] let _: u128 = uniform(&mut rng); - - let dist = Uniform::::new(); - let _ = dist.sample(&mut rng); } struct ConstantRng(u64); From a754e8046268b4cfb6cc04562164608b5f6363e3 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sat, 29 Jul 2017 10:35:19 +0100 Subject: [PATCH 025/247] Update readme --- README.md | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 5617bd28a5f..d92cd161793 100644 --- a/README.md +++ b/README.md @@ -25,29 +25,36 @@ extern crate rand; ## Examples -There is built-in support for a random number generator (RNG) associated with each thread stored in thread-local storage. This RNG can be accessed via thread_rng, or used implicitly via random. This RNG is normally randomly seeded from an operating-system source of randomness, e.g. /dev/urandom on Unix systems, and will automatically reseed itself from this source after generating 32 KiB of random data. +There is built-in support for a random number generator (RNG) associated with +each thread stored in thread-local storage. This RNG can be accessed via +thread_rng. ```rust -let tuple = rand::random::<(f64, char)>(); -println!("{:?}", tuple) +use rand::thread_rng; + +let x: u32 = thread_rng().next_u32(); +println!("{}", u32) ``` ```rust +use rand::dist::{uniform}; use rand::Rng; let mut rng = rand::thread_rng(); -if rng.gen() { // random bool - println!("i32: {}, u32: {}", rng.gen::(), rng.gen::()) +if uniform(&mut rng) { // random bool + let x: i32 = uniform(&mut rng); + let y: u32 = uniform(&mut rng); + println!("i32: {}, u32: {}", x, y); } ``` -It is also possible to use other RNG types, which have a similar interface. The following uses the "ChaCha" algorithm instead of the default. +It is also possible to use other generators types, which have a similar interface. The following uses the "ChaCha" algorithm instead of the default. ```rust use rand::{Rng, ChaChaRng}; -let mut rng = rand::ChaChaRng::new_unseeded(); -println!("i32: {}, u32: {}", rng.gen::(), rng.gen::()) +let mut rng = rand::ChaChaRng::new_from_rng(&mut thread_rng()); +println!("random between 0-9: {}", Range::new(0, 10).sample(&mut rng)); ``` From 24f9891ec8d111a087a03343c0ae9765394d5d4f Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sat, 29 Jul 2017 10:55:05 +0100 Subject: [PATCH 026/247] Replace Rng::gen_weighted_bool and gen_range with dist::weighted_bool and dist::range --- README.md | 6 +-- src/dist/mod.rs | 33 ++++++++++++++- src/dist/range.rs | 62 +++++++++++++++++++++++++++- src/lib.rs | 101 +++++----------------------------------------- 4 files changed, 105 insertions(+), 97 deletions(-) diff --git a/README.md b/README.md index d92cd161793..f1939c297a8 100644 --- a/README.md +++ b/README.md @@ -51,10 +51,10 @@ if uniform(&mut rng) { // random bool It is also possible to use other generators types, which have a similar interface. The following uses the "ChaCha" algorithm instead of the default. ```rust -use rand::{Rng, ChaChaRng}; +use rand::{thread_rng, ChaChaRng, dist}; -let mut rng = rand::ChaChaRng::new_from_rng(&mut thread_rng()); -println!("random between 0-9: {}", Range::new(0, 10).sample(&mut rng)); +let mut rng = ChaChaRng::new_from_rng(&mut thread_rng()); +println!("random between 0-9: {}", dist::range(0, 10, &mut rng)); ``` diff --git a/src/dist/mod.rs b/src/dist/mod.rs index 0ecd1d152ce..93774bb9bfb 100644 --- a/src/dist/mod.rs +++ b/src/dist/mod.rs @@ -21,7 +21,7 @@ use Rng; pub use self::default::{DefaultDist, SampleDefault}; pub use self::uniform::{uniform, uniform01, open01, closed01, codepoint}; -pub use self::range::Range; +pub use self::range::{range, Range}; pub use self::gamma::{Gamma, ChiSquared, FisherF, StudentT}; pub use self::normal::{Normal, LogNormal}; pub use self::exponential::Exp; @@ -34,6 +34,24 @@ pub mod normal; pub mod exponential; pub mod weighted; +/// Return a bool with a 1 in n chance of being true +/// +/// This uses [`range`] internally, so for repeated uses it would be faster to +/// create a `Range` distribution and test its samples: +/// `range.sample(rng) == 0`. +/// +/// # Example +/// +/// ```rust +/// use rand::dist::weighted_bool; +/// +/// let mut rng = rand::thread_rng(); +/// println!("{}", weighted_bool(3, &mut rng)); +/// ``` +pub fn weighted_bool(n: u32, rng: &mut R) -> bool { + n <= 1 || range(0, n, rng) == 0 +} + /// Types (distributions) that can be used to create a random instance of `T`. pub trait Distribution { /// Generate a random value of `T`, using `rng` as the @@ -107,3 +125,16 @@ fn ziggurat( } } } + +#[cfg(test)] +mod test { + use thread_rng; + use dist::weighted_bool; + + #[test] + fn test_fn_weighted_bool() { + let mut r = thread_rng(); + assert_eq!(weighted_bool(0, &mut r), true); + assert_eq!(weighted_bool(1, &mut r), true); + } +} diff --git a/src/dist/range.rs b/src/dist/range.rs index cf00f7adeab..e39b10d97ae 100644 --- a/src/dist/range.rs +++ b/src/dist/range.rs @@ -17,6 +17,32 @@ use std::num::Wrapping as w; use Rng; use dist::{Distribution, uniform01}; +/// Generate a random value in the range [`low`, `high`). +/// +/// This is a convenience wrapper around `Range`. If this function will be called +/// repeatedly with the same arguments, one should use `Range`, as that will +/// amortize the computations that allow for perfect uniformity. +/// +/// # Panics +/// +/// Panics if `low >= high`. +/// +/// # Example +/// +/// ```rust +/// use rand::dist::range; +/// +/// let mut rng = rand::thread_rng(); +/// let n: u32 = range(0, 10, &mut rng); +/// println!("{}", n); +/// let m: f64 = range(-40.0f64, 1.3e5f64, &mut rng); +/// println!("{}", m); +/// ``` +pub fn range(low: T, high: T, rng: &mut R) -> T { + assert!(low < high, "dist::range called with low >= high"); + Range::new(low, high).sample(rng) +} + /// Sample values uniformly between two bounds. /// /// This gives a uniform distribution (assuming the RNG used to sample @@ -162,8 +188,42 @@ float_impl! { f64 } #[cfg(test)] mod tests { + use thread_rng; use dist::{Distribution}; - use super::Range as Range; + use dist::{Range, range}; + + #[test] + fn test_fn_range() { + let mut r = thread_rng(); + for _ in 0..1000 { + let a = range(-3, 42, &mut r); + assert!(a >= -3 && a < 42); + assert_eq!(range(0, 1, &mut r), 0); + assert_eq!(range(-12, -11, &mut r), -12); + } + + for _ in 0..1000 { + let a = range(10, 42, &mut r); + assert!(a >= 10 && a < 42); + assert_eq!(range(0, 1, &mut r), 0); + assert_eq!(range(3_000_000, 3_000_001, &mut r), 3_000_000); + } + + } + + #[test] + #[should_panic] + fn test_fn_range_panic_int() { + let mut r = thread_rng(); + range(5, -2, &mut r); + } + + #[test] + #[should_panic] + fn test_fn_range_panic_usize() { + let mut r = thread_rng(); + range(5, 2, &mut r); + } #[should_panic] #[test] diff --git a/src/lib.rs b/src/lib.rs index ba4227628f5..551a5e2643a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -256,8 +256,7 @@ use std::rc::Rc; pub use read::ReadRng; pub use os::OsRng; -use dist::{Range, Distribution}; -use dist::range::SampleRange; +use dist::range::range; use prng::IsaacWordRng; use prng::XorShiftRng; @@ -418,48 +417,6 @@ pub trait Rng { RngIterator { rng: self, f: f } } - /// Generate a random value in the range [`low`, `high`). - /// - /// This is a convenience wrapper around - /// `dist::Range`. If this function will be called - /// repeatedly with the same arguments, one should use `Range`, as - /// that will amortize the computations that allow for perfect - /// uniformity, as they only happen on initialization. - /// - /// # Panics - /// - /// Panics if `low >= high`. - /// - /// # Example - /// - /// ```rust - /// use rand::{thread_rng, Rng}; - /// - /// let mut rng = thread_rng(); - /// let n: u32 = rng.gen_range(0, 10); - /// println!("{}", n); - /// let m: f64 = rng.gen_range(-40.0f64, 1.3e5f64); - /// println!("{}", m); - /// ``` - fn gen_range(&mut self, low: T, high: T) -> T where Self: Sized { - assert!(low < high, "Rng.gen_range called with low >= high"); - Range::new(low, high).sample(self) - } - - /// Return a bool with a 1 in n chance of true - /// - /// # Example - /// - /// ```rust - /// use rand::{thread_rng, Rng}; - /// - /// let mut rng = thread_rng(); - /// println!("{}", rng.gen_weighted_bool(3)); - /// ``` - fn gen_weighted_bool(&mut self, n: u32) -> bool where Self: Sized { - n <= 1 || self.gen_range(0, n) == 0 - } - /// Return an iterator of random characters from the set A-Z,a-z,0-9. /// /// # Example @@ -492,7 +449,7 @@ pub trait Rng { if values.is_empty() { None } else { - Some(&values[self.gen_range(0, values.len())]) + Some(&values[range(0, values.len(), self)]) } } @@ -504,7 +461,7 @@ pub trait Rng { None } else { let len = values.len(); - Some(&mut values[self.gen_range(0, len)]) + Some(&mut values[range(0, len, self)]) } } @@ -531,7 +488,7 @@ pub trait Rng { // invariant: elements with index >= i have been locked in place. i -= 1; // lock element i in place. - values.swap(i, self.gen_range(0, i + 1)); + values.swap(i, range(0, i + 1, self)); } } } @@ -807,7 +764,7 @@ pub fn sample(rng: &mut R, iterable: I, amount: usize) -> Vec // continue unless the iterator was exhausted if reservoir.len() == amount { for (i, elem) in iter.enumerate() { - let k = rng.gen_range(0, i + 1 + amount); + let k = range(0, i + 1 + amount, rng); if let Some(spot) = reservoir.get_mut(k) { *spot = elem; } @@ -819,7 +776,7 @@ pub fn sample(rng: &mut R, iterable: I, amount: usize) -> Vec #[cfg(test)] mod test { use {Rng, thread_rng, SeedableRng, StdRng, sample}; - use dist::{uniform, uniform01}; + use dist::{uniform, uniform01, range}; use std::iter::repeat; pub struct MyRng { inner: R } @@ -882,46 +839,6 @@ mod test { } } - #[test] - fn test_gen_range() { - let mut r = thread_rng(); - for _ in 0..1000 { - let a = r.gen_range(-3, 42); - assert!(a >= -3 && a < 42); - assert_eq!(r.gen_range(0, 1), 0); - assert_eq!(r.gen_range(-12, -11), -12); - } - - for _ in 0..1000 { - let a = r.gen_range(10, 42); - assert!(a >= 10 && a < 42); - assert_eq!(r.gen_range(0, 1), 0); - assert_eq!(r.gen_range(3_000_000, 3_000_001), 3_000_000); - } - - } - - #[test] - #[should_panic] - fn test_gen_range_panic_int() { - let mut r = thread_rng(); - r.gen_range(5, -2); - } - - #[test] - #[should_panic] - fn test_gen_range_panic_usize() { - let mut r = thread_rng(); - r.gen_range(5, 2); - } - - #[test] - fn test_gen_weighted_bool() { - let mut r = thread_rng(); - assert_eq!(r.gen_weighted_bool(0), true); - assert_eq!(r.gen_weighted_bool(1), true); - } - #[test] fn test_gen_ascii_str() { let mut r = thread_rng(); @@ -975,7 +892,7 @@ mod test { r.shuffle(&mut v); let b: &[_] = &[1, 1, 1]; assert_eq!(v, b); - assert_eq!(r.gen_range(0, 1), 0); + assert_eq!(range(0, 1, &mut r), 0); } #[test] @@ -989,7 +906,7 @@ mod test { (&mut r).shuffle(&mut v); let b: &[_] = &[1, 1, 1]; assert_eq!(v, b); - assert_eq!((&mut r).gen_range(0, 1), 0); + assert_eq!(range(0, 1, &mut r), 0); } { let mut r = Box::new(rng) as Box; @@ -999,7 +916,7 @@ mod test { r.shuffle(&mut v); let b: &[_] = &[1, 1, 1]; assert_eq!(v, b); - assert_eq!(r.gen_range(0, 1), 0); + assert_eq!(range(0, 1, &mut r), 0); } } From 02f7345802c3a246cb1d056df1473a68d0bd7e6c Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sat, 29 Jul 2017 12:16:13 +0100 Subject: [PATCH 027/247] Replace iter_map() with iter() returning a custom iterator type --- src/iter.rs | 134 +++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 39 ++++--------- src/prng/chacha.rs | 4 +- src/prng/isaac.rs | 8 +-- 4 files changed, 150 insertions(+), 35 deletions(-) create mode 100644 src/iter.rs diff --git a/src/iter.rs b/src/iter.rs new file mode 100644 index 00000000000..81812139203 --- /dev/null +++ b/src/iter.rs @@ -0,0 +1,134 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Iterators attached to an `Rng` +//! +//! Lifetime restrictions prevent an `Rng` iterator from simply implementing +//! `std::iter::Iterator`. Instead, you get the simplified iterators below, +//! providing only a subset of functionality. + +use std::cmp::min; + +use Rng; + +/// Pseudo-iterator encapsulating a random number generator. +/// See [`Rng::iter`](trait.Rng.html#method.iter). +#[derive(Debug)] +pub struct RngIterator<'a, R: Rng+'a> { + pub(crate) rng: &'a mut R, + pub(crate) len: Option, +} + +impl<'a, R: Rng+'a> RngIterator<'a, R> { + /// Restrict number of generated items to at most `len` + pub fn take(self, len: usize) -> Self { + RngIterator { + rng: self.rng, + len: Some(self.len.map_or(len, |old| min(old, len))), + } + } + + /// Produce an iterator returning a mapped value + pub fn map(self, f: F) -> RngMap<'a, R, B, F> + where F: FnMut(&mut R) -> B + { + RngMap { + rng: self.rng, + len: self.len, + f: f, + } + } + + /// Produce an iterator returning a flat-mapped value + pub fn flat_map(self, f: F) -> RngFlatMap<'a, R, U, F> + where F: FnMut(&mut R) -> U, U: IntoIterator + { + RngFlatMap { + rng: self.rng, + len: self.len, + f: f, + frontiter: None, + } + } +} + +#[derive(Debug)] +pub struct RngMap<'a, R:'a, B, F> where F: FnMut(&mut R) -> B { + rng: &'a mut R, + len: Option, + f: F, +} +impl<'a, R:'a, B, F> Iterator for RngMap<'a, R, B, F> + where F: FnMut(&mut R) -> B +{ + type Item = B; + fn next(&mut self) -> Option { + match self.len { + Some(0) => return None, + Some(ref mut n) => { *n -= 1; } + None => {} + } + + Some((self.f)(self.rng)) + } +} + +#[derive(Debug)] +pub struct RngFlatMap<'a, R:'a, U, F> + where F: FnMut(&mut R) -> U, U: IntoIterator +{ + rng: &'a mut R, + len: Option, + f: F, + frontiter: Option, +} +impl<'a, R:'a, U, F> Iterator for RngFlatMap<'a, R, U, F> + where F: FnMut(&mut R) -> U, U: IntoIterator +{ + type Item = ::Item; + fn next(&mut self) -> Option { + loop { + if let Some(ref mut inner) = self.frontiter { + if let Some(x) = inner.by_ref().next() { + return Some(x) + } + } + + match self.len { + Some(0) => return None, + Some(ref mut n) => { *n -= 1; } + None => {} + } + + self.frontiter = Some(IntoIterator::into_iter((self.f)(self.rng))); + } + } +} + +#[cfg(test)] +mod tests { + use {Rng, thread_rng}; + use dist::uniform; + + #[test] + fn test_iter() { + let mut rng = thread_rng(); + + let x: Vec<()> = rng.iter().take(10).map(|_| ()).collect(); + assert_eq!(x.len(), 10); + let y: Vec = rng.iter().take(10).map(|rng| uniform(rng)).collect(); + assert_eq!(y.len(), 10); + let z: Vec = rng.iter().take(10).flat_map(|rng| + vec![uniform(rng), uniform(rng)].into_iter()).collect(); + assert_eq!(z.len(), 20); + let w: Vec = rng.iter().take(10).flat_map(|_| vec![].into_iter()).collect(); + assert_eq!(w.len(), 0); + } +} diff --git a/src/lib.rs b/src/lib.rs index 551a5e2643a..197c58724ea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -262,6 +262,7 @@ use prng::IsaacWordRng; use prng::XorShiftRng; pub mod dist; +pub mod iter; pub mod prng; pub mod reseeding; @@ -389,7 +390,7 @@ pub trait Rng { } } - /// Yield an iterator applying some function to self. + /// Yield an iterator. /// /// Unfortunately this is only possible with static dispatch (i.e. where /// `Self: Sized`). [Why? Because the method must be generic, to support @@ -408,13 +409,11 @@ pub trait Rng { /// use rand::dist::uniform; /// /// let mut rng = thread_rng(); - /// let x = rng.iter_map(|rng| uniform(rng)).take(10).collect::>(); + /// let x = rng.iter().take(10).map(|rng| uniform(rng)).collect::>(); /// println!("{:?}", x); /// ``` - fn iter_map<'a, T, F>(&'a mut self, f: F) -> RngIterator<'a, Self, T, F> - where Self: Sized, F: Fn(&mut Self) -> T - { - RngIterator { rng: self, f: f } + fn iter<'a>(&'a mut self) -> iter::RngIterator<'a, Self> where Self: Sized { + iter::RngIterator { rng: self, len: None } } /// Return an iterator of random characters from the set A-Z,a-z,0-9. @@ -537,24 +536,6 @@ impl Rng for Box where R: Rng { } } -/// Pseudo-Iterator encapsulating a random number generator. -/// See [`Rng::iter`](trait.Rng.html#method.iter). -/// -/// This only implements a [`map`](struct.RngIterator.html#method.map) method. -#[derive(Debug)] -pub struct RngIterator<'a, R:'a, T, F: Fn(&mut R) -> T> { - rng: &'a mut R, - f: F -} - -impl<'a, R:'a, T, F: Fn(&mut R) -> T> Iterator for RngIterator<'a, R, T, F> { - type Item = T; - - fn next(&mut self) -> Option { - Some((self.f)(self.rng)) - } -} - /// Iterator which will continuously generate random ascii characters. /// /// This iterator is created via the [`gen_ascii_chars`] method on [`Rng`]. @@ -850,9 +831,9 @@ mod test { #[test] fn test_gen_vec() { let mut r = thread_rng(); - assert_eq!(r.iter_map(|rng| rng.next_u32()).take(0).count(), 0); - assert_eq!(r.iter_map(|rng| uniform::(rng)).take(10).count(), 10); - assert_eq!(r.iter_map(|rng| uniform01::(rng)).take(16).count(), 16); + assert_eq!(r.iter().map(|rng| rng.next_u32()).take(0).count(), 0); + assert_eq!(r.iter().map(|rng| uniform::(rng)).take(10).count(), 10); + assert_eq!(r.iter().map(|rng| uniform01::(rng)).take(16).count(), 16); } #[test] @@ -940,7 +921,7 @@ mod test { #[test] fn test_std_rng_seeded() { - let s = thread_rng().iter_map(|rng| uniform(rng)).take(256).collect::>(); + let s = thread_rng().iter().map(|rng| uniform(rng)).take(256).collect::>(); let mut ra: StdRng = SeedableRng::from_seed(&s[..]); let mut rb: StdRng = SeedableRng::from_seed(&s[..]); assert!(iter_eq(ra.gen_ascii_chars().take(100), @@ -949,7 +930,7 @@ mod test { #[test] fn test_std_rng_reseed() { - let s = thread_rng().iter_map(|rng| uniform(rng)).take(256).collect::>(); + let s = thread_rng().iter().map(|rng| uniform(rng)).take(256).collect::>(); let mut r: StdRng = SeedableRng::from_seed(&s[..]); let string1 = r.gen_ascii_chars().take(100).collect::(); diff --git a/src/prng/chacha.rs b/src/prng/chacha.rs index 4bf6c349217..ef93d7baab2 100644 --- a/src/prng/chacha.rs +++ b/src/prng/chacha.rs @@ -243,7 +243,7 @@ mod test { #[test] fn test_rng_rand_seeded() { - let s = ::test::rng().iter_map(|rng| rng.next_u32()).take(8).collect::>(); + let s = ::test::rng().iter().map(|rng| rng.next_u32()).take(8).collect::>(); let mut ra: ChaChaRng = SeedableRng::from_seed(&s[..]); let mut rb: ChaChaRng = SeedableRng::from_seed(&s[..]); assert!(::test::iter_eq(ra.gen_ascii_chars().take(100), @@ -261,7 +261,7 @@ mod test { #[test] fn test_rng_reseed() { - let s = ::test::rng().iter_map(|rng| rng.next_u32()).take(8).collect::>(); + let s = ::test::rng().iter().map(|rng| rng.next_u32()).take(8).collect::>(); let mut r: ChaChaRng = SeedableRng::from_seed(&s[..]); let string1: String = r.gen_ascii_chars().take(100).collect(); diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index 0b6b100276a..b05f38f4707 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -540,7 +540,7 @@ mod test { #[test] fn test_rng_32_rand_seeded() { - let s = ::test::rng().iter_map(|rng| rng.next_u32()).take(256).collect::>(); + let s = ::test::rng().iter().map(|rng| rng.next_u32()).take(256).collect::>(); let mut ra: IsaacRng = SeedableRng::from_seed(&s[..]); let mut rb: IsaacRng = SeedableRng::from_seed(&s[..]); assert!(::test::iter_eq(ra.gen_ascii_chars().take(100), @@ -548,7 +548,7 @@ mod test { } #[test] fn test_rng_64_rand_seeded() { - let s = ::test::rng().iter_map(|rng| rng.next_u64()).take(256).collect::>(); + let s = ::test::rng().iter().map(|rng| rng.next_u64()).take(256).collect::>(); let mut ra: Isaac64Rng = SeedableRng::from_seed(&s[..]); let mut rb: Isaac64Rng = SeedableRng::from_seed(&s[..]); assert!(::test::iter_eq(ra.gen_ascii_chars().take(100), @@ -574,7 +574,7 @@ mod test { #[test] fn test_rng_32_reseed() { - let s = ::test::rng().iter_map(|rng| rng.next_u32()).take(256).collect::>(); + let s = ::test::rng().iter().map(|rng| rng.next_u32()).take(256).collect::>(); let mut r: IsaacRng = SeedableRng::from_seed(&s[..]); let string1: String = r.gen_ascii_chars().take(100).collect(); @@ -585,7 +585,7 @@ mod test { } #[test] fn test_rng_64_reseed() { - let s = ::test::rng().iter_map(|rng| rng.next_u64()).take(256).collect::>(); + let s = ::test::rng().iter().map(|rng| rng.next_u64()).take(256).collect::>(); let mut r: Isaac64Rng = SeedableRng::from_seed(&s[..]); let string1: String = r.gen_ascii_chars().take(100).collect(); From 9d50d1f88f67fe7de8ac3d67cdf1a81a1671bf27 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sat, 29 Jul 2017 12:24:46 +0100 Subject: [PATCH 028/247] Replace ra.gen_ascii_chars() and AsciiGenerator with ascii_word_char() --- src/dist/mod.rs | 3 ++- src/dist/uniform.rs | 25 ++++++++++++++++++++- src/lib.rs | 55 +++++---------------------------------------- src/prng/chacha.rs | 13 ++++++----- src/prng/isaac.rs | 25 +++++++++++---------- src/reseeding.rs | 16 +++++++------ 6 files changed, 60 insertions(+), 77 deletions(-) diff --git a/src/dist/mod.rs b/src/dist/mod.rs index 93774bb9bfb..3f5af1022aa 100644 --- a/src/dist/mod.rs +++ b/src/dist/mod.rs @@ -20,7 +20,8 @@ use Rng; pub use self::default::{DefaultDist, SampleDefault}; -pub use self::uniform::{uniform, uniform01, open01, closed01, codepoint}; +pub use self::uniform::{uniform, uniform01, open01, closed01, codepoint, + ascii_word_char}; pub use self::range::{range, Range}; pub use self::gamma::{Gamma, ChiSquared, FisherF, StudentT}; pub use self::normal::{Normal, LogNormal}; diff --git a/src/dist/uniform.rs b/src/dist/uniform.rs index 6dd06077067..2c2194b9c1a 100644 --- a/src/dist/uniform.rs +++ b/src/dist/uniform.rs @@ -68,6 +68,17 @@ pub fn codepoint(rng: &mut R) -> char { } } +/// Sample a `char`, uniformly distributed over ASCII letters and numbers: +/// a-z, A-Z and 0-9. +#[inline] +pub fn ascii_word_char(rng: &mut R) -> char { + const GEN_ASCII_STR_CHARSET: &'static [u8] = + b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\ + abcdefghijklmnopqrstuvwxyz\ + 0123456789"; + *rng.choose(GEN_ASCII_STR_CHARSET).unwrap() as char +} + // ----- Sampling traits ----- @@ -238,7 +249,7 @@ float_impls! { SCALE_F32, f32, 24, next_f32 } mod tests { use {Rng, thread_rng}; use dist::{uniform}; - use dist::uniform::SampleUniform; + use dist::uniform::{SampleUniform, codepoint, ascii_word_char}; use dist::{uniform01, open01, closed01}; #[test] @@ -264,6 +275,18 @@ mod tests { #[cfg(feature = "i128_support")] let _: u128 = uniform(&mut rng); } + + #[test] + fn test_chars() { + let mut rng = ::test::rng(); + + let _ = codepoint(&mut rng); + let c = ascii_word_char(&mut rng); + assert!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')); + + let word: String = rng.iter().take(5).map(|rng| ascii_word_char(rng)).collect(); + assert_eq!(word.len(), 5); + } struct ConstantRng(u64); impl Rng for ConstantRng { diff --git a/src/lib.rs b/src/lib.rs index 197c58724ea..62d1ec41f9f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -416,20 +416,6 @@ pub trait Rng { iter::RngIterator { rng: self, len: None } } - /// Return an iterator of random characters from the set A-Z,a-z,0-9. - /// - /// # Example - /// - /// ```rust - /// use rand::{thread_rng, Rng}; - /// - /// let s: String = thread_rng().gen_ascii_chars().take(10).collect(); - /// println!("{}", s); - /// ``` - fn gen_ascii_chars<'a>(&'a mut self) -> AsciiGenerator<'a, Self> where Self: Sized { - AsciiGenerator { rng: self } - } - /// Return a random element from `values`. /// /// Return `None` if `values` is empty. @@ -536,29 +522,6 @@ impl Rng for Box where R: Rng { } } -/// Iterator which will continuously generate random ascii characters. -/// -/// This iterator is created via the [`gen_ascii_chars`] method on [`Rng`]. -/// -/// [`gen_ascii_chars`]: trait.Rng.html#method.gen_ascii_chars -/// [`Rng`]: trait.Rng.html -#[derive(Debug)] -pub struct AsciiGenerator<'a, R:'a> { - rng: &'a mut R, -} - -impl<'a, R: Rng> Iterator for AsciiGenerator<'a, R> { - type Item = char; - - fn next(&mut self) -> Option { - const GEN_ASCII_STR_CHARSET: &'static [u8] = - b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\ - abcdefghijklmnopqrstuvwxyz\ - 0123456789"; - Some(*self.rng.choose(GEN_ASCII_STR_CHARSET).unwrap() as char) - } -} - /// A random number generator that can be explicitly seeded to produce /// the same stream of randomness multiple times. pub trait SeedableRng: Rng { @@ -757,7 +720,7 @@ pub fn sample(rng: &mut R, iterable: I, amount: usize) -> Vec #[cfg(test)] mod test { use {Rng, thread_rng, SeedableRng, StdRng, sample}; - use dist::{uniform, uniform01, range}; + use dist::{uniform, uniform01, range, ascii_word_char}; use std::iter::repeat; pub struct MyRng { inner: R } @@ -820,14 +783,6 @@ mod test { } } - #[test] - fn test_gen_ascii_str() { - let mut r = thread_rng(); - assert_eq!(r.gen_ascii_chars().take(0).count(), 0); - assert_eq!(r.gen_ascii_chars().take(10).count(), 10); - assert_eq!(r.gen_ascii_chars().take(16).count(), 16); - } - #[test] fn test_gen_vec() { let mut r = thread_rng(); @@ -924,19 +879,19 @@ mod test { let s = thread_rng().iter().map(|rng| uniform(rng)).take(256).collect::>(); let mut ra: StdRng = SeedableRng::from_seed(&s[..]); let mut rb: StdRng = SeedableRng::from_seed(&s[..]); - assert!(iter_eq(ra.gen_ascii_chars().take(100), - rb.gen_ascii_chars().take(100))); + assert!(iter_eq(ra.iter().map(|rng| ascii_word_char(rng)).take(100), + rb.iter().map(|rng| ascii_word_char(rng)).take(100))); } #[test] fn test_std_rng_reseed() { let s = thread_rng().iter().map(|rng| uniform(rng)).take(256).collect::>(); let mut r: StdRng = SeedableRng::from_seed(&s[..]); - let string1 = r.gen_ascii_chars().take(100).collect::(); + let string1 = r.iter().map(|rng| ascii_word_char(rng)).take(100).collect::(); r.reseed(&s); - let string2 = r.gen_ascii_chars().take(100).collect::(); + let string2 = r.iter().map(|rng| ascii_word_char(rng)).take(100).collect::(); assert_eq!(string1, string2); } } diff --git a/src/prng/chacha.rs b/src/prng/chacha.rs index ef93d7baab2..47bbb370184 100644 --- a/src/prng/chacha.rs +++ b/src/prng/chacha.rs @@ -239,6 +239,7 @@ impl<'a> SeedableRng<&'a [u32]> for ChaChaRng { #[cfg(test)] mod test { use {Rng, SeedableRng}; + use dist::ascii_word_char; use super::ChaChaRng; #[test] @@ -246,8 +247,8 @@ mod test { let s = ::test::rng().iter().map(|rng| rng.next_u32()).take(8).collect::>(); let mut ra: ChaChaRng = SeedableRng::from_seed(&s[..]); let mut rb: ChaChaRng = SeedableRng::from_seed(&s[..]); - assert!(::test::iter_eq(ra.gen_ascii_chars().take(100), - rb.gen_ascii_chars().take(100))); + assert!(::test::iter_eq(ra.iter().map(|rng| ascii_word_char(rng)).take(100), + rb.iter().map(|rng| ascii_word_char(rng)).take(100))); } #[test] @@ -255,19 +256,19 @@ mod test { let seed : &[_] = &[0,1,2,3,4,5,6,7]; let mut ra: ChaChaRng = SeedableRng::from_seed(seed); let mut rb: ChaChaRng = SeedableRng::from_seed(seed); - assert!(::test::iter_eq(ra.gen_ascii_chars().take(100), - rb.gen_ascii_chars().take(100))); + assert!(::test::iter_eq(ra.iter().map(|rng| ascii_word_char(rng)).take(100), + rb.iter().map(|rng| ascii_word_char(rng)).take(100))); } #[test] fn test_rng_reseed() { let s = ::test::rng().iter().map(|rng| rng.next_u32()).take(8).collect::>(); let mut r: ChaChaRng = SeedableRng::from_seed(&s[..]); - let string1: String = r.gen_ascii_chars().take(100).collect(); + let string1: String = r.iter().map(|rng| ascii_word_char(rng)).take(100).collect(); r.reseed(&s); - let string2: String = r.gen_ascii_chars().take(100).collect(); + let string2: String = r.iter().map(|rng| ascii_word_char(rng)).take(100).collect(); assert_eq!(string1, string2); } diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index b05f38f4707..88ddbe96d7d 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -536,6 +536,7 @@ impl fmt::Debug for Isaac64Rng { #[cfg(test)] mod test { use {Rng, SeedableRng}; + use dist::ascii_word_char; use super::{IsaacRng, Isaac64Rng}; #[test] @@ -543,16 +544,16 @@ mod test { let s = ::test::rng().iter().map(|rng| rng.next_u32()).take(256).collect::>(); let mut ra: IsaacRng = SeedableRng::from_seed(&s[..]); let mut rb: IsaacRng = SeedableRng::from_seed(&s[..]); - assert!(::test::iter_eq(ra.gen_ascii_chars().take(100), - rb.gen_ascii_chars().take(100))); + assert!(::test::iter_eq(ra.iter().map(|rng| ascii_word_char(rng)).take(100), + rb.iter().map(|rng| ascii_word_char(rng)).take(100))); } #[test] fn test_rng_64_rand_seeded() { let s = ::test::rng().iter().map(|rng| rng.next_u64()).take(256).collect::>(); let mut ra: Isaac64Rng = SeedableRng::from_seed(&s[..]); let mut rb: Isaac64Rng = SeedableRng::from_seed(&s[..]); - assert!(::test::iter_eq(ra.gen_ascii_chars().take(100), - rb.gen_ascii_chars().take(100))); + assert!(::test::iter_eq(ra.iter().map(|rng| ascii_word_char(rng)).take(100), + rb.iter().map(|rng| ascii_word_char(rng)).take(100))); } #[test] @@ -560,38 +561,38 @@ mod test { let seed: &[_] = &[1, 23, 456, 7890, 12345]; let mut ra: IsaacRng = SeedableRng::from_seed(seed); let mut rb: IsaacRng = SeedableRng::from_seed(seed); - assert!(::test::iter_eq(ra.gen_ascii_chars().take(100), - rb.gen_ascii_chars().take(100))); + assert!(::test::iter_eq(ra.iter().map(|rng| ascii_word_char(rng)).take(100), + rb.iter().map(|rng| ascii_word_char(rng)).take(100))); } #[test] fn test_rng_64_seeded() { let seed: &[_] = &[1, 23, 456, 7890, 12345]; let mut ra: Isaac64Rng = SeedableRng::from_seed(seed); let mut rb: Isaac64Rng = SeedableRng::from_seed(seed); - assert!(::test::iter_eq(ra.gen_ascii_chars().take(100), - rb.gen_ascii_chars().take(100))); + assert!(::test::iter_eq(ra.iter().map(|rng| ascii_word_char(rng)).take(100), + rb.iter().map(|rng| ascii_word_char(rng)).take(100))); } #[test] fn test_rng_32_reseed() { let s = ::test::rng().iter().map(|rng| rng.next_u32()).take(256).collect::>(); let mut r: IsaacRng = SeedableRng::from_seed(&s[..]); - let string1: String = r.gen_ascii_chars().take(100).collect(); + let string1: String = r.iter().map(|rng| ascii_word_char(rng)).take(100).collect(); r.reseed(&s[..]); - let string2: String = r.gen_ascii_chars().take(100).collect(); + let string2: String = r.iter().map(|rng| ascii_word_char(rng)).take(100).collect(); assert_eq!(string1, string2); } #[test] fn test_rng_64_reseed() { let s = ::test::rng().iter().map(|rng| rng.next_u64()).take(256).collect::>(); let mut r: Isaac64Rng = SeedableRng::from_seed(&s[..]); - let string1: String = r.gen_ascii_chars().take(100).collect(); + let string1: String = r.iter().map(|rng| ascii_word_char(rng)).take(100).collect(); r.reseed(&s[..]); - let string2: String = r.gen_ascii_chars().take(100).collect(); + let string2: String = r.iter().map(|rng| ascii_word_char(rng)).take(100).collect(); assert_eq!(string1, string2); } diff --git a/src/reseeding.rs b/src/reseeding.rs index 2fba9f4ad60..a5f6c5403c5 100644 --- a/src/reseeding.rs +++ b/src/reseeding.rs @@ -104,6 +104,7 @@ impl, Rsdr: Reseeder + Default> /// /// ```rust /// use rand::{Rng, SeedableRng, StdRng}; +/// use rand::dist::ascii_word_char; /// use rand::reseeding::{Reseeder, ReseedingRng}; /// /// struct TickTockReseeder { tick: bool } @@ -120,8 +121,8 @@ impl, Rsdr: Reseeder + Default> /// let inner = StdRng::new().unwrap(); /// let mut rng = ReseedingRng::new(inner, 10, rsdr); /// -/// // this will repeat, because it gets reseeded very regularly. -/// let s: String = rng.gen_ascii_chars().take(100).collect(); +/// // this will repeat, because it gets reseeded very frequently. +/// let s: String = rng.iter().map(|rng| ascii_word_char(rng)).take(100).collect(); /// println!("{}", s); /// } /// @@ -149,8 +150,9 @@ impl Default for ReseedWithDefault { mod test { use std::default::Default; use std::iter::repeat; - use super::{ReseedingRng, ReseedWithDefault}; use {SeedableRng, Rng}; + use dist::ascii_word_char; + use super::{ReseedingRng, ReseedWithDefault}; struct Counter { i: u32 @@ -193,18 +195,18 @@ mod test { fn test_rng_seeded() { let mut ra: MyRng = SeedableRng::from_seed((ReseedWithDefault, 2)); let mut rb: MyRng = SeedableRng::from_seed((ReseedWithDefault, 2)); - assert!(::test::iter_eq(ra.gen_ascii_chars().take(100), - rb.gen_ascii_chars().take(100))); + assert!(::test::iter_eq(ra.iter().map(|rng| ascii_word_char(rng)).take(100), + rb.iter().map(|rng| ascii_word_char(rng)).take(100))); } #[test] fn test_rng_reseed() { let mut r: MyRng = SeedableRng::from_seed((ReseedWithDefault, 3)); - let string1: String = r.gen_ascii_chars().take(100).collect(); + let string1: String = r.iter().map(|rng| ascii_word_char(rng)).take(100).collect(); r.reseed((ReseedWithDefault, 3)); - let string2: String = r.gen_ascii_chars().take(100).collect(); + let string2: String = r.iter().map(|rng| ascii_word_char(rng)).take(100).collect(); assert_eq!(string1, string2); } From 839cb06dc238b881a8778f85eb0f15e51d8ea5f3 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sat, 29 Jul 2017 13:03:00 +0100 Subject: [PATCH 029/247] Replace choose(), choose_mut() and shuffle() on Rng with Choose and Shuffle traits Small problem: this doesn't support dynamic dispatch --- src/dist/uniform.rs | 3 +- src/lib.rs | 106 ++++----------------------------- src/sequence.rs | 139 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 153 insertions(+), 95 deletions(-) create mode 100644 src/sequence.rs diff --git a/src/dist/uniform.rs b/src/dist/uniform.rs index 2c2194b9c1a..abb6c7c041f 100644 --- a/src/dist/uniform.rs +++ b/src/dist/uniform.rs @@ -72,11 +72,12 @@ pub fn codepoint(rng: &mut R) -> char { /// a-z, A-Z and 0-9. #[inline] pub fn ascii_word_char(rng: &mut R) -> char { + use Choose; const GEN_ASCII_STR_CHARSET: &'static [u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\ abcdefghijklmnopqrstuvwxyz\ 0123456789"; - *rng.choose(GEN_ASCII_STR_CHARSET).unwrap() as char + *GEN_ASCII_STR_CHARSET.choose(rng).unwrap() as char } diff --git a/src/lib.rs b/src/lib.rs index 62d1ec41f9f..5fa2286ec8e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -255,6 +255,7 @@ use std::rc::Rc; pub use read::ReadRng; pub use os::OsRng; +pub use self::sequence::{Choose, Shuffle}; use dist::range::range; @@ -265,6 +266,7 @@ pub mod dist; pub mod iter; pub mod prng; pub mod reseeding; +pub mod sequence; mod read; mod os; @@ -415,67 +417,6 @@ pub trait Rng { fn iter<'a>(&'a mut self) -> iter::RngIterator<'a, Self> where Self: Sized { iter::RngIterator { rng: self, len: None } } - - /// Return a random element from `values`. - /// - /// Return `None` if `values` is empty. - /// - /// # Example - /// - /// ``` - /// use rand::{thread_rng, Rng}; - /// - /// let choices = [1, 2, 4, 8, 16, 32]; - /// let mut rng = thread_rng(); - /// println!("{:?}", rng.choose(&choices)); - /// assert_eq!(rng.choose(&choices[..0]), None); - /// ``` - fn choose<'a, T>(&mut self, values: &'a [T]) -> Option<&'a T> where Self: Sized { - if values.is_empty() { - None - } else { - Some(&values[range(0, values.len(), self)]) - } - } - - /// Return a mutable pointer to a random element from `values`. - /// - /// Return `None` if `values` is empty. - fn choose_mut<'a, T>(&mut self, values: &'a mut [T]) -> Option<&'a mut T> where Self: Sized { - if values.is_empty() { - None - } else { - let len = values.len(); - Some(&mut values[range(0, len, self)]) - } - } - - /// Shuffle a mutable slice in place. - /// - /// This applies Durstenfeld's algorithm for the [Fisher–Yates shuffle](https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_modern_algorithm) - /// which produces an unbiased permutation. - /// - /// # Example - /// - /// ```rust - /// use rand::{thread_rng, Rng}; - /// - /// let mut rng = thread_rng(); - /// let mut y = [1, 2, 3]; - /// rng.shuffle(&mut y); - /// println!("{:?}", y); - /// rng.shuffle(&mut y); - /// println!("{:?}", y); - /// ``` - fn shuffle(&mut self, values: &mut [T]) where Self: Sized { - let mut i = values.len(); - while i >= 2 { - // invariant: elements with index >= i have been locked in place. - i -= 1; - // lock element i in place. - values.swap(i, range(0, i + 1, self)); - } - } } impl<'a, R: ?Sized> Rng for &'a mut R where R: Rng { @@ -719,7 +660,7 @@ pub fn sample(rng: &mut R, iterable: I, amount: usize) -> Vec #[cfg(test)] mod test { - use {Rng, thread_rng, SeedableRng, StdRng, sample}; + use {Rng, thread_rng, SeedableRng, StdRng, sample, Shuffle}; use dist::{uniform, uniform01, range, ascii_word_char}; use std::iter::repeat; @@ -791,41 +732,12 @@ mod test { assert_eq!(r.iter().map(|rng| uniform01::(rng)).take(16).count(), 16); } - #[test] - fn test_choose() { - let mut r = thread_rng(); - assert_eq!(r.choose(&[1, 1, 1]).map(|&x|x), Some(1)); - - let v: &[isize] = &[]; - assert_eq!(r.choose(v), None); - } - - #[test] - fn test_shuffle() { - let mut r = thread_rng(); - let empty: &mut [isize] = &mut []; - r.shuffle(empty); - let mut one = [1]; - r.shuffle(&mut one); - let b: &[_] = &[1]; - assert_eq!(one, b); - - let mut two = [1, 2]; - r.shuffle(&mut two); - assert!(two == [1, 2] || two == [2, 1]); - - let mut x = [1, 1, 1]; - r.shuffle(&mut x); - let b: &[_] = &[1, 1, 1]; - assert_eq!(x, b); - } - #[test] fn test_thread_rng() { let mut r = thread_rng(); uniform::(&mut r); let mut v = [1, 1, 1]; - r.shuffle(&mut v); + v.shuffle(&mut r); let b: &[_] = &[1, 1, 1]; assert_eq!(v, b); assert_eq!(range(0, 1, &mut r), 0); @@ -838,20 +750,26 @@ mod test { let mut r = &mut rng as &mut Rng; r.next_u32(); uniform::(&mut r); + // FIXME: dynamic dispatch? + /* let mut v = [1, 1, 1]; - (&mut r).shuffle(&mut v); + v[..].shuffle(r); let b: &[_] = &[1, 1, 1]; assert_eq!(v, b); + */ assert_eq!(range(0, 1, &mut r), 0); } { let mut r = Box::new(rng) as Box; r.next_u32(); uniform::(&mut r); + // FIXME: dynamic dispatch? + /* let mut v = [1, 1, 1]; - r.shuffle(&mut v); + v[..].shuffle(r); let b: &[_] = &[1, 1, 1]; assert_eq!(v, b); + */ assert_eq!(range(0, 1, &mut r), 0); } } diff --git a/src/sequence.rs b/src/sequence.rs new file mode 100644 index 00000000000..c22fa064298 --- /dev/null +++ b/src/sequence.rs @@ -0,0 +1,139 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Random operations on sequences + +use Rng; +use dist::range; + +/// This trait implements a `choose` operations on slices and sequences. +pub trait Choose { + /// Return one element from a sequence. + /// + /// Returns `None` only if the sequence is empty. + /// + /// # Example + /// + /// ``` + /// use rand::{thread_rng, Choose}; + /// + /// let choices = [1, 2, 4, 8, 16, 32]; + /// let mut rng = thread_rng(); + /// println!("{:?}", choices[..].choose(&mut rng)); + /// assert_eq!(choices[..0].choose(&mut rng), None); + /// ``` + fn choose(self, rng: &mut R) -> Option; +} + +impl<'a, T> Choose<&'a T> for &'a [T] { + fn choose(self, rng: &mut R) -> Option<&'a T> { + if self.is_empty() { + None + } else { + Some(&self[range(0, self.len(), rng)]) + } + } +} + +impl<'a, T> Choose<&'a mut T> for &'a mut [T] { + fn choose(self, rng: &mut R) -> Option<&'a mut T> { + if self.is_empty() { + None + } else { + let len = self.len(); + Some(&mut self[range(0, len, rng)]) + } + } +} + +impl Choose for Vec { + fn choose(mut self, rng: &mut R) -> Option { + if self.is_empty() { + None + } else { + let index = range(0, self.len(), rng); + self.drain(index..).next() + } + } +} + +/// This trait introduces a `shuffle` operations on slices. +pub trait Shuffle { + /// Shuffle a mutable sequence in place. + /// + /// This applies Durstenfeld's algorithm for the [Fisher–Yates shuffle](https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_modern_algorithm) + /// which produces an unbiased permutation. + /// + /// # Example + /// + /// ```rust + /// use rand::{thread_rng, Shuffle}; + /// + /// let mut rng = thread_rng(); + /// let mut y = [1, 2, 3]; + /// y[..].shuffle(&mut rng); + /// println!("{:?}", y); + /// y[..].shuffle(&mut rng); + /// println!("{:?}", y); + /// ``` + fn shuffle(self, rng: &mut R); +} + +impl<'a, T> Shuffle for &'a mut [T] { + fn shuffle(self, rng: &mut R) { + let mut i = self.len(); + while i >= 2 { + // invariant: elements with index >= i have been locked in place. + i -= 1; + // lock element i in place. + self.swap(i, range(0, i + 1, rng)); + } + } +} + +impl<'a, T> Shuffle for &'a mut Vec { + fn shuffle(self, rng: &mut R) { + (self[..]).shuffle(rng) + } +} + +#[cfg(test)] +mod test { + use {thread_rng, Choose, Shuffle}; + + #[test] + fn test_choose() { + let mut r = thread_rng(); + assert_eq!([1, 1, 1][..].choose(&mut r).map(|&x|x), Some(1)); + + let v: &[isize] = &[]; + assert_eq!(v.choose(&mut r), None); + } + + #[test] + fn test_shuffle() { + let mut r = thread_rng(); + let empty: &mut [isize] = &mut []; + empty.shuffle(&mut r); + let mut one = [1]; + one[..].shuffle(&mut r); + let b: &[_] = &[1]; + assert_eq!(one, b); + + let mut two = [1, 2]; + two[..].shuffle(&mut r); + assert!(two == [1, 2] || two == [2, 1]); + + let mut x = [1, 1, 1]; + x[..].shuffle(&mut r); + let b: &[_] = &[1, 1, 1]; + assert_eq!(x, b); + } +} From 2c5fabe25ca673f634a981bc58c78a7b1f11cf2b Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sat, 29 Jul 2017 13:08:20 +0100 Subject: [PATCH 030/247] Fix benchmarks --- benches/bench.rs | 24 +++++++++++++----------- benches/distributions/exponential.rs | 2 +- benches/distributions/normal.rs | 2 +- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/benches/bench.rs b/benches/bench.rs index 08b7fb6f181..6f2c0006d9e 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -5,19 +5,21 @@ extern crate rand; const RAND_BENCH_N: u64 = 1000; -mod dist; +mod distributions; use std::mem::size_of; use test::{black_box, Bencher}; -use rand::{XorShiftRng, StdRng, IsaacRng, Isaac64Rng, Rng}; -use rand::{OsRng, sample, weak_rng}; +use rand::{Rng, StdRng, OsRng, weak_rng}; +use rand::prng::{XorShiftRng, IsaacRng, Isaac64Rng}; +use rand::{sample, Shuffle}; +use rand::dist::uniform; #[bench] fn rand_xorshift(b: &mut Bencher) { - let mut rng: XorShiftRng = OsRng::new().unwrap().gen(); + let mut rng = XorShiftRng::new_from_rng(&mut OsRng::new().unwrap()); b.iter(|| { for _ in 0..RAND_BENCH_N { - black_box(rng.gen::()); + black_box(uniform::(&mut rng)); } }); b.bytes = size_of::() as u64 * RAND_BENCH_N; @@ -25,10 +27,10 @@ fn rand_xorshift(b: &mut Bencher) { #[bench] fn rand_isaac(b: &mut Bencher) { - let mut rng: IsaacRng = OsRng::new().unwrap().gen(); + let mut rng = IsaacRng::new_from_rng(&mut OsRng::new().unwrap()); b.iter(|| { for _ in 0..RAND_BENCH_N { - black_box(rng.gen::()); + black_box(uniform::(&mut rng)); } }); b.bytes = size_of::() as u64 * RAND_BENCH_N; @@ -36,10 +38,10 @@ fn rand_isaac(b: &mut Bencher) { #[bench] fn rand_isaac64(b: &mut Bencher) { - let mut rng: Isaac64Rng = OsRng::new().unwrap().gen(); + let mut rng = Isaac64Rng::new_from_rng(&mut OsRng::new().unwrap()); b.iter(|| { for _ in 0..RAND_BENCH_N { - black_box(rng.gen::()); + black_box(uniform::(&mut rng)); } }); b.bytes = size_of::() as u64 * RAND_BENCH_N; @@ -50,7 +52,7 @@ fn rand_std(b: &mut Bencher) { let mut rng = StdRng::new().unwrap(); b.iter(|| { for _ in 0..RAND_BENCH_N { - black_box(rng.gen::()); + black_box(uniform::(&mut rng)); } }); b.bytes = size_of::() as u64 * RAND_BENCH_N; @@ -83,7 +85,7 @@ fn rand_shuffle_100(b: &mut Bencher) { let mut rng = weak_rng(); let x : &mut [usize] = &mut [1; 100]; b.iter(|| { - rng.shuffle(x); + x.shuffle(&mut rng); }) } diff --git a/benches/distributions/exponential.rs b/benches/distributions/exponential.rs index 20ecd245769..aff4e203f47 100644 --- a/benches/distributions/exponential.rs +++ b/benches/distributions/exponential.rs @@ -7,7 +7,7 @@ use rand::dist::Distribution; #[bench] fn rand_exp(b: &mut Bencher) { let mut rng = rand::weak_rng(); - let mut exp = Exp::new(2.71828 * 3.14159); + let exp = Exp::new(2.71828 * 3.14159); b.iter(|| { for _ in 0..::RAND_BENCH_N { diff --git a/benches/distributions/normal.rs b/benches/distributions/normal.rs index e602124ce8c..f819dd1fdfb 100644 --- a/benches/distributions/normal.rs +++ b/benches/distributions/normal.rs @@ -7,7 +7,7 @@ use rand::dist::normal::Normal; #[bench] fn rand_normal(b: &mut Bencher) { let mut rng = rand::weak_rng(); - let mut normal = Normal::new(-2.71828, 3.14159); + let normal = Normal::new(-2.71828, 3.14159); b.iter(|| { for _ in 0..::RAND_BENCH_N { From 8d0e851ed7be1cb04f7d42cd099b7c0a1b4f7072 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sat, 29 Jul 2017 15:17:46 +0100 Subject: [PATCH 031/247] Fix readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f1939c297a8..0f9ea87a140 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ thread_rng. use rand::thread_rng; let x: u32 = thread_rng().next_u32(); -println!("{}", u32) +println!("{}", x) ``` ```rust From 4ac9c93b7bf49a14edcf0ab4932e1a93b352dd65 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 31 Jul 2017 08:53:05 +0100 Subject: [PATCH 032/247] Rng::iter: add String generation example --- src/lib.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 5fa2286ec8e..6db01a95848 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -408,11 +408,14 @@ pub trait Rng { /// /// ``` /// use rand::{thread_rng, Rng}; - /// use rand::dist::uniform; + /// use rand::dist::{uniform, ascii_word_char}; /// /// let mut rng = thread_rng(); - /// let x = rng.iter().take(10).map(|rng| uniform(rng)).collect::>(); + /// let x: Vec = rng.iter().take(10).map(|rng| uniform(rng)).collect(); /// println!("{:?}", x); + /// + /// let w: String = rng.iter().take(6).map(|rng| ascii_word_char(rng)).collect(); + /// println!("{}", w); /// ``` fn iter<'a>(&'a mut self) -> iter::RngIterator<'a, Self> where Self: Sized { iter::RngIterator { rng: self, len: None } From f6dbe28e74b68564df4b55c435b506fc48e6a0fb Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 31 Jul 2017 15:02:25 +0100 Subject: [PATCH 033/247] Support dynamic dispatch for most methods involving an Rng --- src/dist/default.rs | 26 ++++++++++----------- src/dist/exponential.rs | 6 ++--- src/dist/gamma.rs | 12 +++++----- src/dist/mod.rs | 11 +++++---- src/dist/normal.rs | 8 +++---- src/dist/range.rs | 10 ++++---- src/dist/uniform.rs | 52 ++++++++++++++++++++--------------------- src/dist/weighted.rs | 2 +- src/iter.rs | 29 +++++++++++++++++------ src/lib.rs | 6 ++--- src/prng/chacha.rs | 2 +- src/prng/isaac.rs | 4 ++-- src/prng/xorshift.rs | 2 +- src/reseeding.rs | 4 ++-- src/sequence.rs | 27 ++++++++++++++------- 15 files changed, 114 insertions(+), 87 deletions(-) diff --git a/src/dist/default.rs b/src/dist/default.rs index d8f7b890f34..133bf7fc1c5 100644 --- a/src/dist/default.rs +++ b/src/dist/default.rs @@ -65,7 +65,7 @@ impl fmt::Debug for DefaultDist { } impl Distribution for DefaultDist { - fn sample(&self, rng: &mut R) -> T { + fn sample(&self, rng: &mut R) -> T { T::sample_default(rng) } } @@ -76,11 +76,11 @@ impl Distribution for DefaultDist { /// Types supporting default sampling (see `DefaultDist`) pub trait SampleDefault { /// Sample a value using an RNG - fn sample_default(rng: &mut R) -> Self; + fn sample_default(rng: &mut R) -> Self; } impl SampleDefault for T { - fn sample_default(rng: &mut R) -> Self { + fn sample_default(rng: &mut R) -> Self { T::sample_uniform(rng) } } @@ -91,7 +91,7 @@ impl SampleDefault for T { // } // } impl SampleDefault for char { - fn sample_default(rng: &mut R) -> char { + fn sample_default(rng: &mut R) -> char { codepoint(rng) } } @@ -106,20 +106,20 @@ mod tests { #[test] fn test_types() { - let mut rng = thread_rng(); - fn do_test(rng: &mut R) -> T { + let mut rng: &mut Rng = &mut thread_rng(); + fn do_test(rng: &mut Rng) -> T { let dist = DefaultDist::::new(); dist.sample(rng) } - do_test::(&mut rng); - do_test::(&mut rng); + do_test::(rng); + do_test::(rng); // FIXME (see above) - // do_test::(&mut rng); - // do_test::(&mut rng); + // do_test::(rng); + // do_test::(rng); #[cfg(feature = "i128_support")] - do_test::(&mut rng); - do_test::(&mut rng); - do_test::(&mut rng); + do_test::(rng); + do_test::(rng); + do_test::(rng); } } diff --git a/src/dist/exponential.rs b/src/dist/exponential.rs index ea3320fc797..3ec62f4c9ae 100644 --- a/src/dist/exponential.rs +++ b/src/dist/exponential.rs @@ -36,13 +36,13 @@ use dist::{ziggurat, ziggurat_tables, Distribution, uniform01}; /// ``` // This could be done via `-rng.gen::().ln()` but that is slower. #[inline] -pub fn exp1(rng: &mut R) -> f64 { +pub fn exp1(rng: &mut R) -> f64 { #[inline] fn pdf(x: f64) -> f64 { (-x).exp() } #[inline] - fn zero_case(rng: &mut R, _u: f64) -> f64 { + fn zero_case(rng: &mut R, _u: f64) -> f64 { ziggurat_tables::ZIG_EXP_R - uniform01::(rng).ln() } @@ -83,7 +83,7 @@ impl Exp { } impl Distribution for Exp { - fn sample(&self, rng: &mut R) -> f64 { + fn sample(&self, rng: &mut R) -> f64 { exp1(rng) * self.lambda_inverse } } diff --git a/src/dist/gamma.rs b/src/dist/gamma.rs index 1d76be8ae1a..92e1101e601 100644 --- a/src/dist/gamma.rs +++ b/src/dist/gamma.rs @@ -135,7 +135,7 @@ impl GammaLargeShape { impl Distribution for Gamma { - fn sample(&self, rng: &mut R) -> f64 { + fn sample(&self, rng: &mut R) -> f64 { match self.repr { Small(ref g) => g.sample(rng), One(ref g) => g.sample(rng), @@ -144,14 +144,14 @@ impl Distribution for Gamma { } } impl Distribution for GammaSmallShape { - fn sample(&self, rng: &mut R) -> f64 { + fn sample(&self, rng: &mut R) -> f64 { let u: f64 = open01(rng); self.large_shape.sample(rng) * u.powf(self.inv_shape) } } impl Distribution for GammaLargeShape { - fn sample(&self, rng: &mut R) -> f64 { + fn sample(&self, rng: &mut R) -> f64 { loop { let x = standard_normal(rng); let v_cbrt = 1.0 + self.c * x; @@ -216,7 +216,7 @@ impl ChiSquared { } } impl Distribution for ChiSquared { - fn sample(&self, rng: &mut R) -> f64 { + fn sample(&self, rng: &mut R) -> f64 { match self.repr { DoFExactlyOne => { // k == 1 => N(0,1)^2 @@ -267,7 +267,7 @@ impl FisherF { } } impl Distribution for FisherF { - fn sample(&self, rng: &mut R) -> f64 { + fn sample(&self, rng: &mut R) -> f64 { self.numer.sample(rng) / self.denom.sample(rng) * self.dof_ratio } } @@ -302,7 +302,7 @@ impl StudentT { } } impl Distribution for StudentT { - fn sample(&self, rng: &mut R) -> f64 { + fn sample(&self, rng: &mut R) -> f64 { let norm = standard_normal(rng); norm * (self.dof / self.chi.sample(rng)).sqrt() } diff --git a/src/dist/mod.rs b/src/dist/mod.rs index 3f5af1022aa..44abf410e52 100644 --- a/src/dist/mod.rs +++ b/src/dist/mod.rs @@ -49,7 +49,7 @@ pub mod weighted; /// let mut rng = rand::thread_rng(); /// println!("{}", weighted_bool(3, &mut rng)); /// ``` -pub fn weighted_bool(n: u32, rng: &mut R) -> bool { +pub fn weighted_bool(n: u32, rng: &mut R) -> bool { n <= 1 || range(0, n, rng) == 0 } @@ -57,7 +57,7 @@ pub fn weighted_bool(n: u32, rng: &mut R) -> bool { pub trait Distribution { /// Generate a random value of `T`, using `rng` as the /// source of randomness. - fn sample(&self, rng: &mut R) -> T; + fn sample(&self, rng: &mut R) -> T; } @@ -79,7 +79,7 @@ mod ziggurat_tables; // the perf improvement (25-50%) is definitely worth the extra code // size from force-inlining. #[inline(always)] -fn ziggurat( +fn ziggurat( rng: &mut R, symmetric: bool, x_tab: ziggurat_tables::ZigTable, @@ -129,13 +129,14 @@ fn ziggurat( #[cfg(test)] mod test { - use thread_rng; + use {Rng, thread_rng}; use dist::weighted_bool; #[test] fn test_fn_weighted_bool() { let mut r = thread_rng(); assert_eq!(weighted_bool(0, &mut r), true); - assert_eq!(weighted_bool(1, &mut r), true); + let s: &mut Rng = &mut r; + assert_eq!(weighted_bool(1, s), true); } } diff --git a/src/dist/normal.rs b/src/dist/normal.rs index 386edca869c..be9845d3c2a 100644 --- a/src/dist/normal.rs +++ b/src/dist/normal.rs @@ -33,13 +33,13 @@ use dist::{ziggurat, ziggurat_tables, Distribution, open01}; /// let x = standard_normal(&mut rand::thread_rng()); /// println!("{}", x); /// ``` -pub fn standard_normal(rng: &mut R) -> f64 { +pub fn standard_normal(rng: &mut R) -> f64 { #[inline] fn pdf(x: f64) -> f64 { (-x*x/2.0).exp() } #[inline] - fn zero_case(rng: &mut R, u: f64) -> f64 { + fn zero_case(rng: &mut R, u: f64) -> f64 { // compute a random number in the tail by hand // strange initial conditions, because the loop is not @@ -106,7 +106,7 @@ impl Normal { } } impl Distribution for Normal { - fn sample(&self, rng: &mut R) -> f64 { + fn sample(&self, rng: &mut R) -> f64 { self.mean + self.std_dev * standard_normal(rng) } } @@ -146,7 +146,7 @@ impl LogNormal { } } impl Distribution for LogNormal { - fn sample(&self, rng: &mut R) -> f64 { + fn sample(&self, rng: &mut R) -> f64 { self.norm.sample(rng).exp() } } diff --git a/src/dist/range.rs b/src/dist/range.rs index e39b10d97ae..b038cbbd9b3 100644 --- a/src/dist/range.rs +++ b/src/dist/range.rs @@ -38,7 +38,7 @@ use dist::{Distribution, uniform01}; /// let m: f64 = range(-40.0f64, 1.3e5f64, &mut rng); /// println!("{}", m); /// ``` -pub fn range(low: T, high: T, rng: &mut R) -> T { +pub fn range(low: T, high: T, rng: &mut R) -> T { assert!(low < high, "dist::range called with low >= high"); Range::new(low, high).sample(rng) } @@ -89,7 +89,7 @@ impl Range { } impl Distribution for Range { - fn sample(&self, rng: &mut R) -> T { + fn sample(&self, rng: &mut R) -> T { SampleRange::sample_range(self, rng) } } @@ -106,7 +106,7 @@ pub trait SampleRange : Sized { /// Sample a value from the given `Range` with the given `Rng` as /// a source of randomness. - fn sample_range(r: &Range, rng: &mut R) -> Self; + fn sample_range(r: &Range, rng: &mut R) -> Self; } macro_rules! integer_impl { @@ -137,7 +137,7 @@ macro_rules! integer_impl { } #[inline] - fn sample_range(r: &Range<$ty>, rng: &mut R) -> $ty { + fn sample_range(r: &Range<$ty>, rng: &mut R) -> $ty { use $crate::dist::uniform; loop { // rejection sample @@ -176,7 +176,7 @@ macro_rules! float_impl { accept_zone: 0.0 // unused } } - fn sample_range(r: &Range<$ty>, rng: &mut R) -> $ty { + fn sample_range(r: &Range<$ty>, rng: &mut R) -> $ty { r.low + r.range * uniform01::<$ty, _>(rng) } } diff --git a/src/dist/uniform.rs b/src/dist/uniform.rs index abb6c7c041f..6d3117eb2ca 100644 --- a/src/dist/uniform.rs +++ b/src/dist/uniform.rs @@ -21,7 +21,7 @@ use Rng; /// /// This method has precisely two template parameters. To fix the output type, /// use the syntax `uniform::(rng)`. -pub fn uniform(rng: &mut R) -> T { +pub fn uniform(rng: &mut R) -> T { T::sample_uniform(rng) } @@ -29,7 +29,7 @@ pub fn uniform(rng: &mut R) -> T { /// /// This method has precisely two template parameters. To fix the output type, /// use the syntax `uniform01::(rng)`. -pub fn uniform01(rng: &mut R) -> T { +pub fn uniform01(rng: &mut R) -> T { T::sample_uniform01(rng) } @@ -37,7 +37,7 @@ pub fn uniform01(rng: &mut R) -> T { /// /// This method has precisely two template parameters. To fix the output type, /// use the syntax `open01::(rng)`. -pub fn open01(rng: &mut R) -> T { +pub fn open01(rng: &mut R) -> T { T::sample_open01(rng) } @@ -45,7 +45,7 @@ pub fn open01(rng: &mut R) -> T { /// /// This method has precisely two template parameters. To fix the output type, /// use the syntax `closed01::(rng)`. -pub fn closed01(rng: &mut R) -> T { +pub fn closed01(rng: &mut R) -> T { T::sample_closed01(rng) } @@ -54,7 +54,7 @@ pub fn closed01(rng: &mut R) -> T { /// `0xD800...0xDFFF` (the surrogate code points). This includes /// unassigned/reserved code points. #[inline] -pub fn codepoint(rng: &mut R) -> char { +pub fn codepoint(rng: &mut R) -> char { // a char is 21 bits const CHAR_MASK: u32 = 0x001f_ffff; loop { @@ -71,7 +71,7 @@ pub fn codepoint(rng: &mut R) -> char { /// Sample a `char`, uniformly distributed over ASCII letters and numbers: /// a-z, A-Z and 0-9. #[inline] -pub fn ascii_word_char(rng: &mut R) -> char { +pub fn ascii_word_char(rng: &mut R) -> char { use Choose; const GEN_ASCII_STR_CHARSET: &'static [u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\ @@ -86,25 +86,25 @@ pub fn ascii_word_char(rng: &mut R) -> char { /// Sample values uniformly over the whole range supported by the type pub trait SampleUniform: Sized { /// Sample a value using an RNG - fn sample_uniform(rng: &mut R) -> Self; + fn sample_uniform(rng: &mut R) -> Self; } /// Sample values uniformly over the half-open range [0, 1) pub trait SampleUniform01: Sized { /// Sample a value using an RNG - fn sample_uniform01(rng: &mut R) -> Self; + fn sample_uniform01(rng: &mut R) -> Self; } /// Sample values uniformly over the open range (0, 1) pub trait SampleOpen01: Sized { /// Sample a value using an RNG - fn sample_open01(rng: &mut R) -> Self; + fn sample_open01(rng: &mut R) -> Self; } /// Sample values uniformly over the closed range [0, 1] pub trait SampleClosed01: Sized { /// Sample a value using an RNG - fn sample_closed01(rng: &mut R) -> Self; + fn sample_closed01(rng: &mut R) -> Self; } @@ -112,7 +112,7 @@ pub trait SampleClosed01: Sized { impl SampleUniform for isize { #[inline] - fn sample_uniform(rng: &mut R) -> isize { + fn sample_uniform(rng: &mut R) -> isize { if mem::size_of::() == 4 { i32::sample_uniform(rng) as isize } else { @@ -123,28 +123,28 @@ impl SampleUniform for isize { impl SampleUniform for i8 { #[inline] - fn sample_uniform(rng: &mut R) -> i8 { + fn sample_uniform(rng: &mut R) -> i8 { rng.next_u32() as i8 } } impl SampleUniform for i16 { #[inline] - fn sample_uniform(rng: &mut R) -> i16 { + fn sample_uniform(rng: &mut R) -> i16 { rng.next_u32() as i16 } } impl SampleUniform for i32 { #[inline] - fn sample_uniform(rng: &mut R) -> i32 { + fn sample_uniform(rng: &mut R) -> i32 { rng.next_u32() as i32 } } impl SampleUniform for i64 { #[inline] - fn sample_uniform(rng: &mut R) -> i64 { + fn sample_uniform(rng: &mut R) -> i64 { rng.next_u64() as i64 } } @@ -152,14 +152,14 @@ impl SampleUniform for i64 { #[cfg(feature = "i128_support")] impl SampleUniform for i128 { #[inline] - fn sample_uniform(rng: &mut R) -> i128 { + fn sample_uniform(rng: &mut R) -> i128 { rng.gen::() as i128 } } impl SampleUniform for usize { #[inline] - fn sample_uniform(rng: &mut R) -> usize { + fn sample_uniform(rng: &mut R) -> usize { if mem::size_of::() == 4 { u32::sample_uniform(rng) as usize } else { @@ -170,28 +170,28 @@ impl SampleUniform for usize { impl SampleUniform for u8 { #[inline] - fn sample_uniform(rng: &mut R) -> u8 { + fn sample_uniform(rng: &mut R) -> u8 { rng.next_u32() as u8 } } impl SampleUniform for u16 { #[inline] - fn sample_uniform(rng: &mut R) -> u16 { + fn sample_uniform(rng: &mut R) -> u16 { rng.next_u32() as u16 } } impl SampleUniform for u32 { #[inline] - fn sample_uniform(rng: &mut R) -> u32 { + fn sample_uniform(rng: &mut R) -> u32 { rng.next_u32() } } impl SampleUniform for u64 { #[inline] - fn sample_uniform(rng: &mut R) -> u64 { + fn sample_uniform(rng: &mut R) -> u64 { rng.next_u64() } } @@ -199,14 +199,14 @@ impl SampleUniform for u64 { #[cfg(feature = "i128_support")] impl SampleUniform for u128 { #[inline] - fn sample_uniform(rng: &mut R) -> u128 { + fn sample_uniform(rng: &mut R) -> u128 { ((rng.next_u64() as u128) << 64) | (rng.next_u64() as u128) } } impl SampleUniform for bool { #[inline] - fn sample_uniform(rng: &mut R) -> bool { + fn sample_uniform(rng: &mut R) -> bool { rng.next_u32() & 1 == 1 } } @@ -218,13 +218,13 @@ macro_rules! float_impls { impl SampleUniform01 for $ty { #[inline] - fn sample_uniform01(rng: &mut R) -> $ty { + fn sample_uniform01(rng: &mut R) -> $ty { rng.$method_name() } } impl SampleOpen01 for $ty { #[inline] - fn sample_open01(rng: &mut R) -> $ty { + fn sample_open01(rng: &mut R) -> $ty { // add a small amount (specifically 2 bits below // the precision of f64/f32 at 1.0), so that small // numbers are larger than 0, but large numbers @@ -234,7 +234,7 @@ macro_rules! float_impls { } impl SampleClosed01 for $ty { #[inline] - fn sample_closed01(rng: &mut R) -> $ty { + fn sample_closed01(rng: &mut R) -> $ty { // rescale so that 1.0 - epsilon becomes 1.0 // precisely. uniform01::<$ty, _>(rng) * $scale_name / ($scale_name - 1.0) diff --git a/src/dist/weighted.rs b/src/dist/weighted.rs index f4c05eece98..a7d8c69dbe2 100644 --- a/src/dist/weighted.rs +++ b/src/dist/weighted.rs @@ -89,7 +89,7 @@ impl WeightedChoice { } impl Distribution for WeightedChoice { - fn sample(&self, rng: &mut R) -> T { + fn sample(&self, rng: &mut R) -> T { // we want to find the first element that has cumulative // weight > sample_weight, which we do by binary since the // cumulative weights of self.items are sorted. diff --git a/src/iter.rs b/src/iter.rs index 81812139203..62943cf036f 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -21,12 +21,18 @@ use Rng; /// Pseudo-iterator encapsulating a random number generator. /// See [`Rng::iter`](trait.Rng.html#method.iter). #[derive(Debug)] -pub struct RngIterator<'a, R: Rng+'a> { +pub struct RngIterator<'a, R: Rng+?Sized+'a> { pub(crate) rng: &'a mut R, pub(crate) len: Option, } -impl<'a, R: Rng+'a> RngIterator<'a, R> { +impl<'a, R: Rng+?Sized+'a> RngIterator<'a, R> { + /// Create an instance. Same as `Rng::iter()` but supports dynamic dispatch. + // TODO: remove `Rng::iter()`? + pub fn new(rng: &'a mut R) -> Self { + RngIterator { rng, len: None } + } + /// Restrict number of generated items to at most `len` pub fn take(self, len: usize) -> Self { RngIterator { @@ -60,12 +66,12 @@ impl<'a, R: Rng+'a> RngIterator<'a, R> { } #[derive(Debug)] -pub struct RngMap<'a, R:'a, B, F> where F: FnMut(&mut R) -> B { +pub struct RngMap<'a, R:?Sized+'a, B, F> where F: FnMut(&mut R) -> B { rng: &'a mut R, len: Option, f: F, } -impl<'a, R:'a, B, F> Iterator for RngMap<'a, R, B, F> +impl<'a, R:?Sized+'a, B, F> Iterator for RngMap<'a, R, B, F> where F: FnMut(&mut R) -> B { type Item = B; @@ -81,7 +87,7 @@ impl<'a, R:'a, B, F> Iterator for RngMap<'a, R, B, F> } #[derive(Debug)] -pub struct RngFlatMap<'a, R:'a, U, F> +pub struct RngFlatMap<'a, R:?Sized+'a, U, F> where F: FnMut(&mut R) -> U, U: IntoIterator { rng: &'a mut R, @@ -89,7 +95,7 @@ pub struct RngFlatMap<'a, R:'a, U, F> f: F, frontiter: Option, } -impl<'a, R:'a, U, F> Iterator for RngFlatMap<'a, R, U, F> +impl<'a, R:?Sized+'a, U, F> Iterator for RngFlatMap<'a, R, U, F> where F: FnMut(&mut R) -> U, U: IntoIterator { type Item = ::Item; @@ -115,7 +121,8 @@ impl<'a, R:'a, U, F> Iterator for RngFlatMap<'a, R, U, F> #[cfg(test)] mod tests { use {Rng, thread_rng}; - use dist::uniform; + use dist::{uniform, ascii_word_char}; + use iter::RngIterator; #[test] fn test_iter() { @@ -131,4 +138,12 @@ mod tests { let w: Vec = rng.iter().take(10).flat_map(|_| vec![].into_iter()).collect(); assert_eq!(w.len(), 0); } + + #[test] + fn test_dyn_dispatch() { + let mut r: &mut Rng = &mut thread_rng(); + + let x: String = RngIterator::new(r).take(10).map(|rng| ascii_word_char(rng)).collect(); + assert_eq!(x.len(), 10); + } } diff --git a/src/lib.rs b/src/lib.rs index 6db01a95848..cb67bac37fc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -667,11 +667,11 @@ mod test { use dist::{uniform, uniform01, range, ascii_word_char}; use std::iter::repeat; - pub struct MyRng { inner: R } + pub struct MyRng { inner: R } - impl Rng for MyRng { + impl Rng for MyRng { fn next_u32(&mut self) -> u32 { - fn next(t: &mut T) -> u32 { + fn next(t: &mut T) -> u32 { t.next_u32() } next(&mut self.inner) diff --git a/src/prng/chacha.rs b/src/prng/chacha.rs index 47bbb370184..e06c8f30545 100644 --- a/src/prng/chacha.rs +++ b/src/prng/chacha.rs @@ -113,7 +113,7 @@ impl ChaChaRng { /// free entropy gained. In some cases where the parent and child RNGs use /// the same algorithm, both generate the same output sequences (possibly /// with a small lag). - pub fn new_from_rng(other: &mut R) -> ChaChaRng { + pub fn new_from_rng(other: &mut R) -> ChaChaRng { let mut key : [u32; KEY_WORDS] = [0; KEY_WORDS]; for word in key.iter_mut() { *word = other.next_u32(); diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index 88ddbe96d7d..521a6aab312 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -75,7 +75,7 @@ impl IsaacRng { /// free entropy gained. In some cases where the parent and child RNGs use /// the same algorithm, both generate the same output sequences (possibly /// with a small lag). - pub fn new_from_rng(other: &mut R) -> IsaacRng { + pub fn new_from_rng(other: &mut R) -> IsaacRng { let mut ret = EMPTY; unsafe { let ptr = ret.rsl.as_mut_ptr() as *mut u8; @@ -326,7 +326,7 @@ impl Isaac64Rng { /// free entropy gained. In some cases where the parent and child RNGs use /// the same algorithm, both generate the same output sequences (possibly /// with a small lag). - pub fn new_from_rng(other: &mut R) -> Isaac64Rng { + pub fn new_from_rng(other: &mut R) -> Isaac64Rng { let mut ret = EMPTY_64; unsafe { let ptr = ret.rsl.as_mut_ptr() as *mut u8; diff --git a/src/prng/xorshift.rs b/src/prng/xorshift.rs index 116e1ec7b8b..aaa4246e988 100644 --- a/src/prng/xorshift.rs +++ b/src/prng/xorshift.rs @@ -54,7 +54,7 @@ impl XorShiftRng { /// free entropy gained. In some cases where the parent and child RNGs use /// the same algorithm, both generate the same output sequences (possibly /// with a small lag). - pub fn new_from_rng(rng: &mut R) -> XorShiftRng { + pub fn new_from_rng(rng: &mut R) -> XorShiftRng { let mut tuple: (u32, u32, u32, u32); loop { tuple = (rng.next_u32(), rng.next_u32(), rng.next_u32(), rng.next_u32()); diff --git a/src/reseeding.rs b/src/reseeding.rs index a5f6c5403c5..4167c9dc2bf 100644 --- a/src/reseeding.rs +++ b/src/reseeding.rs @@ -127,7 +127,7 @@ impl, Rsdr: Reseeder + Default> /// } /// /// ``` -pub trait Reseeder { +pub trait Reseeder { /// Reseed the given RNG. fn reseed(&mut self, rng: &mut R); } @@ -137,7 +137,7 @@ pub trait Reseeder { #[derive(Clone, Copy, Debug)] pub struct ReseedWithDefault; -impl Reseeder for ReseedWithDefault { +impl Reseeder for ReseedWithDefault { fn reseed(&mut self, rng: &mut R) { *rng = Default::default(); } diff --git a/src/sequence.rs b/src/sequence.rs index c22fa064298..6fbec3afdda 100644 --- a/src/sequence.rs +++ b/src/sequence.rs @@ -29,11 +29,11 @@ pub trait Choose { /// println!("{:?}", choices[..].choose(&mut rng)); /// assert_eq!(choices[..0].choose(&mut rng), None); /// ``` - fn choose(self, rng: &mut R) -> Option; + fn choose(self, rng: &mut R) -> Option; } impl<'a, T> Choose<&'a T> for &'a [T] { - fn choose(self, rng: &mut R) -> Option<&'a T> { + fn choose(self, rng: &mut R) -> Option<&'a T> { if self.is_empty() { None } else { @@ -43,7 +43,7 @@ impl<'a, T> Choose<&'a T> for &'a [T] { } impl<'a, T> Choose<&'a mut T> for &'a mut [T] { - fn choose(self, rng: &mut R) -> Option<&'a mut T> { + fn choose(self, rng: &mut R) -> Option<&'a mut T> { if self.is_empty() { None } else { @@ -54,7 +54,7 @@ impl<'a, T> Choose<&'a mut T> for &'a mut [T] { } impl Choose for Vec { - fn choose(mut self, rng: &mut R) -> Option { + fn choose(mut self, rng: &mut R) -> Option { if self.is_empty() { None } else { @@ -83,11 +83,11 @@ pub trait Shuffle { /// y[..].shuffle(&mut rng); /// println!("{:?}", y); /// ``` - fn shuffle(self, rng: &mut R); + fn shuffle(self, rng: &mut R); } impl<'a, T> Shuffle for &'a mut [T] { - fn shuffle(self, rng: &mut R) { + fn shuffle(self, rng: &mut R) { let mut i = self.len(); while i >= 2 { // invariant: elements with index >= i have been locked in place. @@ -99,14 +99,14 @@ impl<'a, T> Shuffle for &'a mut [T] { } impl<'a, T> Shuffle for &'a mut Vec { - fn shuffle(self, rng: &mut R) { + fn shuffle(self, rng: &mut R) { (self[..]).shuffle(rng) } } #[cfg(test)] mod test { - use {thread_rng, Choose, Shuffle}; + use {Rng, thread_rng, Choose, Shuffle}; #[test] fn test_choose() { @@ -136,4 +136,15 @@ mod test { let b: &[_] = &[1, 1, 1]; assert_eq!(x, b); } + + #[test] + fn dyn_dispatch() { + let mut r: &mut Rng = &mut thread_rng(); + + assert_eq!([7, 7][..].choose(r), Some(&7)); + + let mut x = [6, 2]; + x[..].shuffle(r); + assert!(x == [6, 2] || x == [2, 6]); + } } From c2a1ad14a6d6e6c2f02d77ba1575db1349159033 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 31 Jul 2017 15:07:10 +0100 Subject: [PATCH 034/247] =?UTF-8?q?Rename=20iterator=20types:=20RngIterato?= =?UTF-8?q?r=20=E2=86=92=20Iter,=20RngMap=20=E2=86=92=20Map,=20RngFlatMap?= =?UTF-8?q?=20=E2=86=92=20FlatMap?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/iter.rs | 28 ++++++++++++++-------------- src/lib.rs | 4 ++-- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/iter.rs b/src/iter.rs index 62943cf036f..6fdacd59bd8 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -21,31 +21,31 @@ use Rng; /// Pseudo-iterator encapsulating a random number generator. /// See [`Rng::iter`](trait.Rng.html#method.iter). #[derive(Debug)] -pub struct RngIterator<'a, R: Rng+?Sized+'a> { +pub struct Iter<'a, R: Rng+?Sized+'a> { pub(crate) rng: &'a mut R, pub(crate) len: Option, } -impl<'a, R: Rng+?Sized+'a> RngIterator<'a, R> { +impl<'a, R: Rng+?Sized+'a> Iter<'a, R> { /// Create an instance. Same as `Rng::iter()` but supports dynamic dispatch. // TODO: remove `Rng::iter()`? pub fn new(rng: &'a mut R) -> Self { - RngIterator { rng, len: None } + Iter { rng, len: None } } /// Restrict number of generated items to at most `len` pub fn take(self, len: usize) -> Self { - RngIterator { + Iter { rng: self.rng, len: Some(self.len.map_or(len, |old| min(old, len))), } } /// Produce an iterator returning a mapped value - pub fn map(self, f: F) -> RngMap<'a, R, B, F> + pub fn map(self, f: F) -> Map<'a, R, B, F> where F: FnMut(&mut R) -> B { - RngMap { + Map { rng: self.rng, len: self.len, f: f, @@ -53,10 +53,10 @@ impl<'a, R: Rng+?Sized+'a> RngIterator<'a, R> { } /// Produce an iterator returning a flat-mapped value - pub fn flat_map(self, f: F) -> RngFlatMap<'a, R, U, F> + pub fn flat_map(self, f: F) -> FlatMap<'a, R, U, F> where F: FnMut(&mut R) -> U, U: IntoIterator { - RngFlatMap { + FlatMap { rng: self.rng, len: self.len, f: f, @@ -66,12 +66,12 @@ impl<'a, R: Rng+?Sized+'a> RngIterator<'a, R> { } #[derive(Debug)] -pub struct RngMap<'a, R:?Sized+'a, B, F> where F: FnMut(&mut R) -> B { +pub struct Map<'a, R:?Sized+'a, B, F> where F: FnMut(&mut R) -> B { rng: &'a mut R, len: Option, f: F, } -impl<'a, R:?Sized+'a, B, F> Iterator for RngMap<'a, R, B, F> +impl<'a, R:?Sized+'a, B, F> Iterator for Map<'a, R, B, F> where F: FnMut(&mut R) -> B { type Item = B; @@ -87,7 +87,7 @@ impl<'a, R:?Sized+'a, B, F> Iterator for RngMap<'a, R, B, F> } #[derive(Debug)] -pub struct RngFlatMap<'a, R:?Sized+'a, U, F> +pub struct FlatMap<'a, R:?Sized+'a, U, F> where F: FnMut(&mut R) -> U, U: IntoIterator { rng: &'a mut R, @@ -95,7 +95,7 @@ pub struct RngFlatMap<'a, R:?Sized+'a, U, F> f: F, frontiter: Option, } -impl<'a, R:?Sized+'a, U, F> Iterator for RngFlatMap<'a, R, U, F> +impl<'a, R:?Sized+'a, U, F> Iterator for FlatMap<'a, R, U, F> where F: FnMut(&mut R) -> U, U: IntoIterator { type Item = ::Item; @@ -122,7 +122,7 @@ impl<'a, R:?Sized+'a, U, F> Iterator for RngFlatMap<'a, R, U, F> mod tests { use {Rng, thread_rng}; use dist::{uniform, ascii_word_char}; - use iter::RngIterator; + use iter::Iter; #[test] fn test_iter() { @@ -143,7 +143,7 @@ mod tests { fn test_dyn_dispatch() { let mut r: &mut Rng = &mut thread_rng(); - let x: String = RngIterator::new(r).take(10).map(|rng| ascii_word_char(rng)).collect(); + let x: String = Iter::new(r).take(10).map(|rng| ascii_word_char(rng)).collect(); assert_eq!(x.len(), 10); } } diff --git a/src/lib.rs b/src/lib.rs index cb67bac37fc..82e6289227f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -417,8 +417,8 @@ pub trait Rng { /// let w: String = rng.iter().take(6).map(|rng| ascii_word_char(rng)).collect(); /// println!("{}", w); /// ``` - fn iter<'a>(&'a mut self) -> iter::RngIterator<'a, Self> where Self: Sized { - iter::RngIterator { rng: self, len: None } + fn iter<'a>(&'a mut self) -> iter::Iter<'a, Self> where Self: Sized { + iter::Iter { rng: self, len: None } } } From cfe0b11df9a93754250f2ecdce438e2ce50a5d25 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 31 Jul 2017 15:23:10 +0100 Subject: [PATCH 035/247] Replace Rng::iter(&mut self) with iter::iter(&mut R) Why (1): to simplify Rng Why (2): Rng::iter cannot support dynamic dispatch --- src/dist/uniform.rs | 4 ++-- src/iter.rs | 38 ++++++++++++++++++++----------- src/lib.rs | 54 ++++++++------------------------------------- src/prng/chacha.rs | 18 +++++++-------- src/prng/isaac.rs | 34 ++++++++++++++-------------- src/reseeding.rs | 14 ++++++------ 6 files changed, 69 insertions(+), 93 deletions(-) diff --git a/src/dist/uniform.rs b/src/dist/uniform.rs index 6d3117eb2ca..7d4ad2348bb 100644 --- a/src/dist/uniform.rs +++ b/src/dist/uniform.rs @@ -248,7 +248,7 @@ float_impls! { SCALE_F32, f32, 24, next_f32 } #[cfg(test)] mod tests { - use {Rng, thread_rng}; + use {Rng, thread_rng, iter}; use dist::{uniform}; use dist::uniform::{SampleUniform, codepoint, ascii_word_char}; use dist::{uniform01, open01, closed01}; @@ -285,7 +285,7 @@ mod tests { let c = ascii_word_char(&mut rng); assert!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')); - let word: String = rng.iter().take(5).map(|rng| ascii_word_char(rng)).collect(); + let word: String = iter(&mut rng).take(5).map(|rng| ascii_word_char(rng)).collect(); assert_eq!(word.len(), 5); } diff --git a/src/iter.rs b/src/iter.rs index 6fdacd59bd8..dbbd7493be5 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -26,13 +26,26 @@ pub struct Iter<'a, R: Rng+?Sized+'a> { pub(crate) len: Option, } +/// Create an iterator on an `Rng`. +/// +/// # Example +/// +/// ``` +/// use rand::{thread_rng, Rng, iter}; +/// use rand::dist::{uniform, ascii_word_char}; +/// +/// let mut rng = thread_rng(); +/// let x: Vec = iter(&mut rng).take(10).map(|rng| uniform(rng)).collect(); +/// println!("{:?}", x); +/// +/// let w: String = iter(&mut rng).take(6).map(|rng| ascii_word_char(rng)).collect(); +/// println!("{}", w); +/// ``` +pub fn iter<'a, R: Rng+?Sized+'a>(rng: &'a mut R) -> Iter<'a, R> { + Iter { rng: rng, len: None } +} + impl<'a, R: Rng+?Sized+'a> Iter<'a, R> { - /// Create an instance. Same as `Rng::iter()` but supports dynamic dispatch. - // TODO: remove `Rng::iter()`? - pub fn new(rng: &'a mut R) -> Self { - Iter { rng, len: None } - } - /// Restrict number of generated items to at most `len` pub fn take(self, len: usize) -> Self { Iter { @@ -120,22 +133,21 @@ impl<'a, R:?Sized+'a, U, F> Iterator for FlatMap<'a, R, U, F> #[cfg(test)] mod tests { - use {Rng, thread_rng}; + use {Rng, thread_rng, iter}; use dist::{uniform, ascii_word_char}; - use iter::Iter; #[test] fn test_iter() { let mut rng = thread_rng(); - let x: Vec<()> = rng.iter().take(10).map(|_| ()).collect(); + let x: Vec<()> = iter(&mut rng).take(10).map(|_| ()).collect(); assert_eq!(x.len(), 10); - let y: Vec = rng.iter().take(10).map(|rng| uniform(rng)).collect(); + let y: Vec = iter(&mut rng).take(10).map(|rng| uniform(rng)).collect(); assert_eq!(y.len(), 10); - let z: Vec = rng.iter().take(10).flat_map(|rng| + let z: Vec = iter(&mut rng).take(10).flat_map(|rng| vec![uniform(rng), uniform(rng)].into_iter()).collect(); assert_eq!(z.len(), 20); - let w: Vec = rng.iter().take(10).flat_map(|_| vec![].into_iter()).collect(); + let w: Vec = iter(&mut rng).take(10).flat_map(|_| vec![].into_iter()).collect(); assert_eq!(w.len(), 0); } @@ -143,7 +155,7 @@ mod tests { fn test_dyn_dispatch() { let mut r: &mut Rng = &mut thread_rng(); - let x: String = Iter::new(r).take(10).map(|rng| ascii_word_char(rng)).collect(); + let x: String = iter(r).take(10).map(|rng| ascii_word_char(rng)).collect(); assert_eq!(x.len(), 10); } } diff --git a/src/lib.rs b/src/lib.rs index 82e6289227f..fea5fcd3cda 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -256,6 +256,7 @@ use std::rc::Rc; pub use read::ReadRng; pub use os::OsRng; pub use self::sequence::{Choose, Shuffle}; +pub use iter::iter; use dist::range::range; @@ -391,35 +392,6 @@ pub trait Rng { count -= 1; } } - - /// Yield an iterator. - /// - /// Unfortunately this is only possible with static dispatch (i.e. where - /// `Self: Sized`). [Why? Because the method must be generic, to support - /// the lifetime bound on the `&mut Rng` field of the returned object, if - /// not also for types `T` and `F`; representing this field requires that - /// `Rng` trait objects can be constructed, but trait objects cannot be - /// constructed for traits with generic methods without a `Sized` bound. - /// But the starting point was wanting a dynamic version of this method, - /// i.e. not requiring the `Sized` bound. - /// See `rustc --explain E0038` for more.] - /// - /// # Example - /// - /// ``` - /// use rand::{thread_rng, Rng}; - /// use rand::dist::{uniform, ascii_word_char}; - /// - /// let mut rng = thread_rng(); - /// let x: Vec = rng.iter().take(10).map(|rng| uniform(rng)).collect(); - /// println!("{:?}", x); - /// - /// let w: String = rng.iter().take(6).map(|rng| ascii_word_char(rng)).collect(); - /// println!("{}", w); - /// ``` - fn iter<'a>(&'a mut self) -> iter::Iter<'a, Self> where Self: Sized { - iter::Iter { rng: self, len: None } - } } impl<'a, R: ?Sized> Rng for &'a mut R where R: Rng { @@ -663,8 +635,8 @@ pub fn sample(rng: &mut R, iterable: I, amount: usize) -> Vec #[cfg(test)] mod test { - use {Rng, thread_rng, SeedableRng, StdRng, sample, Shuffle}; - use dist::{uniform, uniform01, range, ascii_word_char}; + use {Rng, thread_rng, SeedableRng, StdRng, sample, Shuffle, iter}; + use dist::{uniform, range, ascii_word_char}; use std::iter::repeat; pub struct MyRng { inner: R } @@ -727,14 +699,6 @@ mod test { } } - #[test] - fn test_gen_vec() { - let mut r = thread_rng(); - assert_eq!(r.iter().map(|rng| rng.next_u32()).take(0).count(), 0); - assert_eq!(r.iter().map(|rng| uniform::(rng)).take(10).count(), 10); - assert_eq!(r.iter().map(|rng| uniform01::(rng)).take(16).count(), 16); - } - #[test] fn test_thread_rng() { let mut r = thread_rng(); @@ -797,22 +761,22 @@ mod test { #[test] fn test_std_rng_seeded() { - let s = thread_rng().iter().map(|rng| uniform(rng)).take(256).collect::>(); + let s = iter(&mut thread_rng()).map(|rng| uniform(rng)).take(256).collect::>(); let mut ra: StdRng = SeedableRng::from_seed(&s[..]); let mut rb: StdRng = SeedableRng::from_seed(&s[..]); - assert!(iter_eq(ra.iter().map(|rng| ascii_word_char(rng)).take(100), - rb.iter().map(|rng| ascii_word_char(rng)).take(100))); + assert!(iter_eq(iter(&mut ra).map(|rng| ascii_word_char(rng)).take(100), + iter(&mut rb).map(|rng| ascii_word_char(rng)).take(100))); } #[test] fn test_std_rng_reseed() { - let s = thread_rng().iter().map(|rng| uniform(rng)).take(256).collect::>(); + let s = iter(&mut thread_rng()).map(|rng| uniform(rng)).take(256).collect::>(); let mut r: StdRng = SeedableRng::from_seed(&s[..]); - let string1 = r.iter().map(|rng| ascii_word_char(rng)).take(100).collect::(); + let string1 = iter(&mut r).map(|rng| ascii_word_char(rng)).take(100).collect::(); r.reseed(&s); - let string2 = r.iter().map(|rng| ascii_word_char(rng)).take(100).collect::(); + let string2 = iter(&mut r).map(|rng| ascii_word_char(rng)).take(100).collect::(); assert_eq!(string1, string2); } } diff --git a/src/prng/chacha.rs b/src/prng/chacha.rs index e06c8f30545..a26d43515a0 100644 --- a/src/prng/chacha.rs +++ b/src/prng/chacha.rs @@ -238,17 +238,17 @@ impl<'a> SeedableRng<&'a [u32]> for ChaChaRng { #[cfg(test)] mod test { - use {Rng, SeedableRng}; + use {Rng, SeedableRng, iter}; use dist::ascii_word_char; use super::ChaChaRng; #[test] fn test_rng_rand_seeded() { - let s = ::test::rng().iter().map(|rng| rng.next_u32()).take(8).collect::>(); + let s = iter(&mut ::test::rng()).map(|rng| rng.next_u32()).take(8).collect::>(); let mut ra: ChaChaRng = SeedableRng::from_seed(&s[..]); let mut rb: ChaChaRng = SeedableRng::from_seed(&s[..]); - assert!(::test::iter_eq(ra.iter().map(|rng| ascii_word_char(rng)).take(100), - rb.iter().map(|rng| ascii_word_char(rng)).take(100))); + assert!(::test::iter_eq(iter(&mut ra).map(|rng| ascii_word_char(rng)).take(100), + iter(&mut rb).map(|rng| ascii_word_char(rng)).take(100))); } #[test] @@ -256,19 +256,19 @@ mod test { let seed : &[_] = &[0,1,2,3,4,5,6,7]; let mut ra: ChaChaRng = SeedableRng::from_seed(seed); let mut rb: ChaChaRng = SeedableRng::from_seed(seed); - assert!(::test::iter_eq(ra.iter().map(|rng| ascii_word_char(rng)).take(100), - rb.iter().map(|rng| ascii_word_char(rng)).take(100))); + assert!(::test::iter_eq(iter(&mut ra).map(|rng| ascii_word_char(rng)).take(100), + iter(&mut rb).map(|rng| ascii_word_char(rng)).take(100))); } #[test] fn test_rng_reseed() { - let s = ::test::rng().iter().map(|rng| rng.next_u32()).take(8).collect::>(); + let s = iter(&mut ::test::rng()).map(|rng| rng.next_u32()).take(8).collect::>(); let mut r: ChaChaRng = SeedableRng::from_seed(&s[..]); - let string1: String = r.iter().map(|rng| ascii_word_char(rng)).take(100).collect(); + let string1: String = iter(&mut r).map(|rng| ascii_word_char(rng)).take(100).collect(); r.reseed(&s); - let string2: String = r.iter().map(|rng| ascii_word_char(rng)).take(100).collect(); + let string2: String = iter(&mut r).map(|rng| ascii_word_char(rng)).take(100).collect(); assert_eq!(string1, string2); } diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index 521a6aab312..dca556df569 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -535,25 +535,25 @@ impl fmt::Debug for Isaac64Rng { #[cfg(test)] mod test { - use {Rng, SeedableRng}; + use {Rng, SeedableRng, iter}; use dist::ascii_word_char; use super::{IsaacRng, Isaac64Rng}; #[test] fn test_rng_32_rand_seeded() { - let s = ::test::rng().iter().map(|rng| rng.next_u32()).take(256).collect::>(); + let s = iter(&mut ::test::rng()).map(|rng| rng.next_u32()).take(256).collect::>(); let mut ra: IsaacRng = SeedableRng::from_seed(&s[..]); let mut rb: IsaacRng = SeedableRng::from_seed(&s[..]); - assert!(::test::iter_eq(ra.iter().map(|rng| ascii_word_char(rng)).take(100), - rb.iter().map(|rng| ascii_word_char(rng)).take(100))); + assert!(::test::iter_eq(iter(&mut ra).map(|rng| ascii_word_char(rng)).take(100), + iter(&mut rb).map(|rng| ascii_word_char(rng)).take(100))); } #[test] fn test_rng_64_rand_seeded() { - let s = ::test::rng().iter().map(|rng| rng.next_u64()).take(256).collect::>(); + let s = iter(&mut ::test::rng()).map(|rng| rng.next_u64()).take(256).collect::>(); let mut ra: Isaac64Rng = SeedableRng::from_seed(&s[..]); let mut rb: Isaac64Rng = SeedableRng::from_seed(&s[..]); - assert!(::test::iter_eq(ra.iter().map(|rng| ascii_word_char(rng)).take(100), - rb.iter().map(|rng| ascii_word_char(rng)).take(100))); + assert!(::test::iter_eq(iter(&mut ra).map(|rng| ascii_word_char(rng)).take(100), + iter(&mut rb).map(|rng| ascii_word_char(rng)).take(100))); } #[test] @@ -561,38 +561,38 @@ mod test { let seed: &[_] = &[1, 23, 456, 7890, 12345]; let mut ra: IsaacRng = SeedableRng::from_seed(seed); let mut rb: IsaacRng = SeedableRng::from_seed(seed); - assert!(::test::iter_eq(ra.iter().map(|rng| ascii_word_char(rng)).take(100), - rb.iter().map(|rng| ascii_word_char(rng)).take(100))); + assert!(::test::iter_eq(iter(&mut ra).map(|rng| ascii_word_char(rng)).take(100), + iter(&mut rb).map(|rng| ascii_word_char(rng)).take(100))); } #[test] fn test_rng_64_seeded() { let seed: &[_] = &[1, 23, 456, 7890, 12345]; let mut ra: Isaac64Rng = SeedableRng::from_seed(seed); let mut rb: Isaac64Rng = SeedableRng::from_seed(seed); - assert!(::test::iter_eq(ra.iter().map(|rng| ascii_word_char(rng)).take(100), - rb.iter().map(|rng| ascii_word_char(rng)).take(100))); + assert!(::test::iter_eq(iter(&mut ra).map(|rng| ascii_word_char(rng)).take(100), + iter(&mut rb).map(|rng| ascii_word_char(rng)).take(100))); } #[test] fn test_rng_32_reseed() { - let s = ::test::rng().iter().map(|rng| rng.next_u32()).take(256).collect::>(); + let s = iter(&mut ::test::rng()).map(|rng| rng.next_u32()).take(256).collect::>(); let mut r: IsaacRng = SeedableRng::from_seed(&s[..]); - let string1: String = r.iter().map(|rng| ascii_word_char(rng)).take(100).collect(); + let string1: String = iter(&mut r).map(|rng| ascii_word_char(rng)).take(100).collect(); r.reseed(&s[..]); - let string2: String = r.iter().map(|rng| ascii_word_char(rng)).take(100).collect(); + let string2: String = iter(&mut r).map(|rng| ascii_word_char(rng)).take(100).collect(); assert_eq!(string1, string2); } #[test] fn test_rng_64_reseed() { - let s = ::test::rng().iter().map(|rng| rng.next_u64()).take(256).collect::>(); + let s = iter(&mut ::test::rng()).map(|rng| rng.next_u64()).take(256).collect::>(); let mut r: Isaac64Rng = SeedableRng::from_seed(&s[..]); - let string1: String = r.iter().map(|rng| ascii_word_char(rng)).take(100).collect(); + let string1: String = iter(&mut r).map(|rng| ascii_word_char(rng)).take(100).collect(); r.reseed(&s[..]); - let string2: String = r.iter().map(|rng| ascii_word_char(rng)).take(100).collect(); + let string2: String = iter(&mut r).map(|rng| ascii_word_char(rng)).take(100).collect(); assert_eq!(string1, string2); } diff --git a/src/reseeding.rs b/src/reseeding.rs index 4167c9dc2bf..1b189516a8a 100644 --- a/src/reseeding.rs +++ b/src/reseeding.rs @@ -103,7 +103,7 @@ impl, Rsdr: Reseeder + Default> /// # Example /// /// ```rust -/// use rand::{Rng, SeedableRng, StdRng}; +/// use rand::{Rng, SeedableRng, StdRng, iter}; /// use rand::dist::ascii_word_char; /// use rand::reseeding::{Reseeder, ReseedingRng}; /// @@ -122,7 +122,7 @@ impl, Rsdr: Reseeder + Default> /// let mut rng = ReseedingRng::new(inner, 10, rsdr); /// /// // this will repeat, because it gets reseeded very frequently. -/// let s: String = rng.iter().map(|rng| ascii_word_char(rng)).take(100).collect(); +/// let s: String = iter(&mut rng).map(|rng| ascii_word_char(rng)).take(100).collect(); /// println!("{}", s); /// } /// @@ -150,7 +150,7 @@ impl Default for ReseedWithDefault { mod test { use std::default::Default; use std::iter::repeat; - use {SeedableRng, Rng}; + use {SeedableRng, Rng, iter}; use dist::ascii_word_char; use super::{ReseedingRng, ReseedWithDefault}; @@ -195,18 +195,18 @@ mod test { fn test_rng_seeded() { let mut ra: MyRng = SeedableRng::from_seed((ReseedWithDefault, 2)); let mut rb: MyRng = SeedableRng::from_seed((ReseedWithDefault, 2)); - assert!(::test::iter_eq(ra.iter().map(|rng| ascii_word_char(rng)).take(100), - rb.iter().map(|rng| ascii_word_char(rng)).take(100))); + assert!(::test::iter_eq(iter(&mut ra).map(|rng| ascii_word_char(rng)).take(100), + iter(&mut rb).map(|rng| ascii_word_char(rng)).take(100))); } #[test] fn test_rng_reseed() { let mut r: MyRng = SeedableRng::from_seed((ReseedWithDefault, 3)); - let string1: String = r.iter().map(|rng| ascii_word_char(rng)).take(100).collect(); + let string1: String = iter(&mut r).map(|rng| ascii_word_char(rng)).take(100).collect(); r.reseed((ReseedWithDefault, 3)); - let string2: String = r.iter().map(|rng| ascii_word_char(rng)).take(100).collect(); + let string2: String = iter(&mut r).map(|rng| ascii_word_char(rng)).take(100).collect(); assert_eq!(string1, string2); } From e8fe340cf194187560f9a66126bb9ff2e17e5c57 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 31 Jul 2017 15:26:25 +0100 Subject: [PATCH 036/247] Move sample() to sequence module --- src/lib.rs | 54 ++----------------------------------------------- src/sequence.rs | 50 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 51 insertions(+), 53 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index fea5fcd3cda..2793837a41a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -255,11 +255,9 @@ use std::rc::Rc; pub use read::ReadRng; pub use os::OsRng; -pub use self::sequence::{Choose, Shuffle}; +pub use self::sequence::{Choose, sample, Shuffle}; pub use iter::iter; -use dist::range::range; - use prng::IsaacWordRng; use prng::XorShiftRng; @@ -603,39 +601,9 @@ impl Rng for ThreadRng { } } -/// Randomly sample up to `amount` elements from a finite iterator. -/// The order of elements in the sample is not random. -/// -/// # Example -/// -/// ```rust -/// use rand::{thread_rng, sample}; -/// -/// let mut rng = thread_rng(); -/// let sample = sample(&mut rng, 1..100, 5); -/// println!("{:?}", sample); -/// ``` -pub fn sample(rng: &mut R, iterable: I, amount: usize) -> Vec - where I: IntoIterator, - R: Rng, -{ - let mut iter = iterable.into_iter(); - let mut reservoir: Vec = iter.by_ref().take(amount).collect(); - // continue unless the iterator was exhausted - if reservoir.len() == amount { - for (i, elem) in iter.enumerate() { - let k = range(0, i + 1 + amount, rng); - if let Some(spot) = reservoir.get_mut(k) { - *spot = elem; - } - } - } - reservoir -} - #[cfg(test)] mod test { - use {Rng, thread_rng, SeedableRng, StdRng, sample, Shuffle, iter}; + use {Rng, thread_rng, SeedableRng, StdRng, Shuffle, iter}; use dist::{uniform, range, ascii_word_char}; use std::iter::repeat; @@ -741,24 +709,6 @@ mod test { } } - #[test] - fn test_sample() { - let min_val = 1; - let max_val = 100; - - let mut r = thread_rng(); - let vals = (min_val..max_val).collect::>(); - let small_sample = sample(&mut r, vals.iter(), 5); - let large_sample = sample(&mut r, vals.iter(), vals.len() + 5); - - assert_eq!(small_sample.len(), 5); - assert_eq!(large_sample.len(), vals.len()); - - assert!(small_sample.iter().all(|e| { - **e >= min_val && **e <= max_val - })); - } - #[test] fn test_std_rng_seeded() { let s = iter(&mut thread_rng()).map(|rng| uniform(rng)).take(256).collect::>(); diff --git a/src/sequence.rs b/src/sequence.rs index 6fbec3afdda..0dd1189ec7a 100644 --- a/src/sequence.rs +++ b/src/sequence.rs @@ -64,6 +64,36 @@ impl Choose for Vec { } } +/// Randomly sample up to `amount` elements from a finite iterator. +/// The order of elements in the sample is not random. +/// +/// # Example +/// +/// ```rust +/// use rand::{thread_rng, sample}; +/// +/// let mut rng = thread_rng(); +/// let sample = sample(&mut rng, 1..100, 5); +/// println!("{:?}", sample); +/// ``` +pub fn sample(rng: &mut R, iterable: I, amount: usize) -> Vec + where I: IntoIterator, + R: Rng, +{ + let mut iter = iterable.into_iter(); + let mut reservoir: Vec = iter.by_ref().take(amount).collect(); + // continue unless the iterator was exhausted + if reservoir.len() == amount { + for (i, elem) in iter.enumerate() { + let k = range(0, i + 1 + amount, rng); + if let Some(spot) = reservoir.get_mut(k) { + *spot = elem; + } + } + } + reservoir +} + /// This trait introduces a `shuffle` operations on slices. pub trait Shuffle { /// Shuffle a mutable sequence in place. @@ -106,7 +136,7 @@ impl<'a, T> Shuffle for &'a mut Vec { #[cfg(test)] mod test { - use {Rng, thread_rng, Choose, Shuffle}; + use {Rng, thread_rng, Choose, sample, Shuffle}; #[test] fn test_choose() { @@ -117,6 +147,24 @@ mod test { assert_eq!(v.choose(&mut r), None); } + #[test] + fn test_sample() { + let min_val = 1; + let max_val = 100; + + let mut r = thread_rng(); + let vals = (min_val..max_val).collect::>(); + let small_sample = sample(&mut r, vals.iter(), 5); + let large_sample = sample(&mut r, vals.iter(), vals.len() + 5); + + assert_eq!(small_sample.len(), 5); + assert_eq!(large_sample.len(), vals.len()); + + assert!(small_sample.iter().all(|e| { + **e >= min_val && **e <= max_val + })); + } + #[test] fn test_shuffle() { let mut r = thread_rng(); From 3d43bc96d746b3446d948a61996f3e31a27d2432 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 31 Jul 2017 15:48:32 +0100 Subject: [PATCH 037/247] Resurrect random() --- src/lib.rs | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 2793837a41a..4472a0e8e05 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -601,6 +601,56 @@ impl Rng for ThreadRng { } } +/// Generates a andom value using the thread-local random number generator. +/// +/// `random()` can generate various types of random things, and so may require +/// type hinting to generate the specific type you want. It uses +/// `dist::SampleDefault` to determine *how* values are generated for each type. +/// +/// This function uses the thread local random number generator. This means +/// that if you're calling `random()` in a loop, caching the generator can +/// increase performance. An example is shown below. +/// +/// # Examples +/// +/// ``` +/// let x = rand::random::(); +/// println!("{}", x); +/// +/// if rand::random() { // generates a boolean +/// println!("Better lucky than good!"); +/// } +/// ``` +/// +/// Caching the thread local random number generator: +/// +/// ``` +/// use rand::Rng; +/// use rand::dist::SampleDefault; +/// +/// let mut v = vec![1, 2, 3]; +/// +/// for x in v.iter_mut() { +/// *x = rand::random() +/// } +/// +/// // would be faster as +/// +/// let mut rng = rand::thread_rng(); +/// +/// for x in v.iter_mut() { +/// *x = SampleDefault::sample_default(&mut rng); +/// } +/// ``` +/// +/// Note that the above example uses `SampleDefault` which is a zero-sized +/// marker type. +#[inline] +pub fn random() -> T { + dist::SampleDefault::sample_default(&mut thread_rng()) +} + + #[cfg(test)] mod test { use {Rng, thread_rng, SeedableRng, StdRng, Shuffle, iter}; From bd10d7319bdb8da09d24fdd268fc31e6b0092d65 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 31 Jul 2017 17:29:14 +0100 Subject: [PATCH 038/247] Implement new trait Rand Unlike the one proposed by @aturon, this doesn't return a result. Overall this seems to work very nicely. --- src/dist/default.rs | 90 ++++++++------------------------ src/dist/mod.rs | 13 ++++- src/dist/uniform.rs | 124 +++++++++++++++++++++----------------------- src/lib.rs | 10 ++-- 4 files changed, 96 insertions(+), 141 deletions(-) diff --git a/src/dist/default.rs b/src/dist/default.rs index 133bf7fc1c5..c0619d8cbbc 100644 --- a/src/dist/default.rs +++ b/src/dist/default.rs @@ -10,88 +10,43 @@ //! Generic value creation -use std::fmt; -use std::marker::PhantomData; - use Rng; -use dist::Distribution; -use dist::uniform::{SampleUniform, /*SampleUniform01,*/ codepoint}; +use dist::{Distribution, Rand}; +use dist::uniform::{Uniform, /*Uniform01,*/ codepoint}; -/// A generic random value creator. Generates values using what appears to be -/// an appropriate distribution for each type, but choice is arbitrary. +/// A generic random value distribution. Generates values using what appears to +/// be "the best" distribution for each type, but ultimately the choice is arbitrary. /// -/// Makes use of the following traits on the types implementing them: +/// Makes use of the following distributions: /// -/// * [`SampleUniform`] for integer types -/// * [`SampleUniform01`] for floating point types (FIXME) +/// * [`Uniform`] for integer types +/// * [`Uniform01`] for floating point types (FIXME) /// /// Makes use of the following methods: /// /// * [`codepoint`] for `char` /// /// TODO: link -/// -/// TODO: is this useful? Personally I feel not, but it carries over previous -/// functionality. -#[derive(Default)] -pub struct DefaultDist { - _marker: PhantomData, -} +#[derive(Debug)] +pub struct Default; -impl DefaultDist { - /// Create an instance. Should optimise to nothing, since there is no - /// internal state. - pub fn new() -> Self { - DefaultDist { - _marker: PhantomData - } - } -} +// ----- implementations ----- -impl Copy for DefaultDist {} - -// derive only auto-impls for types T: Clone, but we don't have that restriction -impl Clone for DefaultDist { - fn clone(&self) -> Self { - DefaultDist::new() - } -} - -// derive only auto-impls for types T: Debug, but we don't have that restriction -impl fmt::Debug for DefaultDist { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - write!(f, "DefaultDist {{}}") - } -} - -impl Distribution for DefaultDist { +impl> Distribution for Default { fn sample(&self, rng: &mut R) -> T { - T::sample_default(rng) + T::rand(rng, Uniform) } } - -// ----- SampleDefault ----- - -/// Types supporting default sampling (see `DefaultDist`) -pub trait SampleDefault { - /// Sample a value using an RNG - fn sample_default(rng: &mut R) -> Self; -} - -impl SampleDefault for T { - fn sample_default(rng: &mut R) -> Self { - T::sample_uniform(rng) - } -} // FIXME: https://github.com/rust-lang/rust/issues/23341 -// impl SampleDefault for T { -// fn sample_default(rng: &mut R) -> T { -// T::sample_uniform01(rng) +// impl> Distribution for Default { +// fn sample(&self, rng: &mut R) -> T { +// T::rand(rng, Uniform01) // } // } -impl SampleDefault for char { - fn sample_default(rng: &mut R) -> char { + +impl Distribution for Default { + fn sample(&self, rng: &mut R) -> char { codepoint(rng) } } @@ -100,16 +55,13 @@ impl SampleDefault for char { #[cfg(test)] mod tests { use {Rng, thread_rng}; - use dist::Distribution; - use dist::default::{DefaultDist, SampleDefault}; - + use dist::{Rand, Default}; #[test] fn test_types() { let mut rng: &mut Rng = &mut thread_rng(); - fn do_test(rng: &mut Rng) -> T { - let dist = DefaultDist::::new(); - dist.sample(rng) + fn do_test>(rng: &mut Rng) -> T { + T::rand(rng, Default) } do_test::(rng); diff --git a/src/dist/mod.rs b/src/dist/mod.rs index 44abf410e52..6db9d4cf31a 100644 --- a/src/dist/mod.rs +++ b/src/dist/mod.rs @@ -19,9 +19,10 @@ use Rng; -pub use self::default::{DefaultDist, SampleDefault}; +pub use self::default::Default; pub use self::uniform::{uniform, uniform01, open01, closed01, codepoint, ascii_word_char}; +pub use self::uniform::{Uniform, Uniform01, Open01, Closed01}; pub use self::range::{range, Range}; pub use self::gamma::{Gamma, ChiSquared, FisherF, StudentT}; pub use self::normal::{Normal, LogNormal}; @@ -60,6 +61,16 @@ pub trait Distribution { fn sample(&self, rng: &mut R) -> T; } +/// Generic trait for sampling random values from some distribution +pub trait Rand { + fn rand(rng: &mut R, dist: Dist) -> Self; +} + +impl> Rand for T { + fn rand(rng: &mut R, dist: D) -> Self { + dist.sample(rng) + } +} mod ziggurat_tables; diff --git a/src/dist/uniform.rs b/src/dist/uniform.rs index 7d4ad2348bb..a85ef5cb61e 100644 --- a/src/dist/uniform.rs +++ b/src/dist/uniform.rs @@ -14,6 +14,7 @@ use std::char; use std::mem; use Rng; +use dist::{Distribution, Rand}; // ----- convenience functions ----- @@ -21,32 +22,32 @@ use Rng; /// /// This method has precisely two template parameters. To fix the output type, /// use the syntax `uniform::(rng)`. -pub fn uniform(rng: &mut R) -> T { - T::sample_uniform(rng) +pub fn uniform, R: Rng+?Sized>(rng: &mut R) -> T { + T::rand(rng, Uniform) } /// Sample values uniformly over the half-open range [0, 1) /// /// This method has precisely two template parameters. To fix the output type, /// use the syntax `uniform01::(rng)`. -pub fn uniform01(rng: &mut R) -> T { - T::sample_uniform01(rng) +pub fn uniform01, R: Rng+?Sized>(rng: &mut R) -> T { + T::rand(rng, Uniform01) } /// Sample values uniformly over the open range (0, 1) /// /// This method has precisely two template parameters. To fix the output type, /// use the syntax `open01::(rng)`. -pub fn open01(rng: &mut R) -> T { - T::sample_open01(rng) +pub fn open01, R: Rng+?Sized>(rng: &mut R) -> T { + T::rand(rng, Open01) } /// Sample values uniformly over the closed range [0, 1] /// /// This method has precisely two template parameters. To fix the output type, /// use the syntax `closed01::(rng)`. -pub fn closed01(rng: &mut R) -> T { - T::sample_closed01(rng) +pub fn closed01, R: Rng+?Sized>(rng: &mut R) -> T { + T::rand(rng, Closed01) } /// Sample a `char`, uniformly distributed over all Unicode scalar values, @@ -81,132 +82,123 @@ pub fn ascii_word_char(rng: &mut R) -> char { } -// ----- Sampling traits ----- +// ----- Sampling distributions ----- /// Sample values uniformly over the whole range supported by the type -pub trait SampleUniform: Sized { - /// Sample a value using an RNG - fn sample_uniform(rng: &mut R) -> Self; -} +#[derive(Debug)] +pub struct Uniform; /// Sample values uniformly over the half-open range [0, 1) -pub trait SampleUniform01: Sized { - /// Sample a value using an RNG - fn sample_uniform01(rng: &mut R) -> Self; -} +#[derive(Debug)] +pub struct Uniform01; /// Sample values uniformly over the open range (0, 1) -pub trait SampleOpen01: Sized { - /// Sample a value using an RNG - fn sample_open01(rng: &mut R) -> Self; -} +#[derive(Debug)] +pub struct Open01; /// Sample values uniformly over the closed range [0, 1] -pub trait SampleClosed01: Sized { - /// Sample a value using an RNG - fn sample_closed01(rng: &mut R) -> Self; -} +#[derive(Debug)] +pub struct Closed01; // ----- actual implementations ----- -impl SampleUniform for isize { - #[inline] - fn sample_uniform(rng: &mut R) -> isize { +impl Distribution for Uniform { + fn sample(&self, rng: &mut R) -> isize { if mem::size_of::() == 4 { - i32::sample_uniform(rng) as isize + i32::rand(rng, Uniform) as isize } else { - i64::sample_uniform(rng) as isize + i64::rand(rng, Uniform) as isize } } } -impl SampleUniform for i8 { +impl Distribution for Uniform { #[inline] - fn sample_uniform(rng: &mut R) -> i8 { + fn sample(&self, rng: &mut R) -> i8 { rng.next_u32() as i8 } } -impl SampleUniform for i16 { +impl Distribution for Uniform { #[inline] - fn sample_uniform(rng: &mut R) -> i16 { + fn sample(&self, rng: &mut R) -> i16 { rng.next_u32() as i16 } } -impl SampleUniform for i32 { +impl Distribution for Uniform { #[inline] - fn sample_uniform(rng: &mut R) -> i32 { + fn sample(&self, rng: &mut R) -> i32 { rng.next_u32() as i32 } } -impl SampleUniform for i64 { +impl Distribution for Uniform { #[inline] - fn sample_uniform(rng: &mut R) -> i64 { + fn sample(&self, rng: &mut R) -> i64 { rng.next_u64() as i64 } } #[cfg(feature = "i128_support")] -impl SampleUniform for i128 { +impl Distribution for Uniform { #[inline] - fn sample_uniform(rng: &mut R) -> i128 { + fn sample(&self, rng: &mut R) -> i128 { rng.gen::() as i128 } } -impl SampleUniform for usize { +impl Distribution for Uniform { #[inline] - fn sample_uniform(rng: &mut R) -> usize { + fn sample(&self, rng: &mut R) -> usize { if mem::size_of::() == 4 { - u32::sample_uniform(rng) as usize + u32::rand(rng, Uniform) as usize } else { - u64::sample_uniform(rng) as usize + u64::rand(rng, Uniform) as usize } } } -impl SampleUniform for u8 { +impl Distribution for Uniform { #[inline] - fn sample_uniform(rng: &mut R) -> u8 { + fn sample(&self, rng: &mut R) -> u8 { rng.next_u32() as u8 } } -impl SampleUniform for u16 { +impl Distribution for Uniform { #[inline] - fn sample_uniform(rng: &mut R) -> u16 { + fn sample(&self, rng: &mut R) -> u16 { rng.next_u32() as u16 } } -impl SampleUniform for u32 { +impl Distribution for Uniform { #[inline] - fn sample_uniform(rng: &mut R) -> u32 { + fn sample(&self, rng: &mut R) -> u32 { rng.next_u32() } } -impl SampleUniform for u64 { +impl Distribution for Uniform { #[inline] - fn sample_uniform(rng: &mut R) -> u64 { + fn sample(&self, rng: &mut R) -> u64 { rng.next_u64() } } #[cfg(feature = "i128_support")] -impl SampleUniform for u128 { +impl Distribution for Uniform { #[inline] - fn sample_uniform(rng: &mut R) -> u128 { + fn sample(&self, rng: &mut R) -> u128 { ((rng.next_u64() as u128) << 64) | (rng.next_u64() as u128) } } -impl SampleUniform for bool { +impl Distribution for Uniform { #[inline] - fn sample_uniform(rng: &mut R) -> bool { + fn sample(&self, rng: &mut R) -> bool { rng.next_u32() & 1 == 1 } } @@ -216,15 +208,15 @@ macro_rules! float_impls { ($scale_name:ident, $ty:ty, $mantissa_bits:expr, $method_name:ident) => { const $scale_name: $ty = (1u64 << $mantissa_bits) as $ty; - impl SampleUniform01 for $ty { + impl Distribution<$ty> for Uniform01 { #[inline] - fn sample_uniform01(rng: &mut R) -> $ty { + fn sample(&self, rng: &mut R) -> $ty { rng.$method_name() } } - impl SampleOpen01 for $ty { + impl Distribution<$ty> for Open01 { #[inline] - fn sample_open01(rng: &mut R) -> $ty { + fn sample(&self, rng: &mut R) -> $ty { // add a small amount (specifically 2 bits below // the precision of f64/f32 at 1.0), so that small // numbers are larger than 0, but large numbers @@ -232,9 +224,9 @@ macro_rules! float_impls { uniform01::<$ty, _>(rng) + 0.25 / $scale_name } } - impl SampleClosed01 for $ty { + impl Distribution<$ty> for Closed01 { #[inline] - fn sample_closed01(rng: &mut R) -> $ty { + fn sample(&self, rng: &mut R) -> $ty { // rescale so that 1.0 - epsilon becomes 1.0 // precisely. uniform01::<$ty, _>(rng) * $scale_name / ($scale_name - 1.0) @@ -249,16 +241,16 @@ float_impls! { SCALE_F32, f32, 24, next_f32 } #[cfg(test)] mod tests { use {Rng, thread_rng, iter}; - use dist::{uniform}; - use dist::uniform::{SampleUniform, codepoint, ascii_word_char}; + use dist::{Rand, uniform, Uniform}; + use dist::uniform::{codepoint, ascii_word_char}; use dist::{uniform01, open01, closed01}; #[test] fn test_integers() { let mut rng = ::test::rng(); - let _: i32 = SampleUniform::sample_uniform(&mut rng); - let _: i32 = i32::sample_uniform(&mut rng); + let _: i32 = Rand::rand(&mut rng, Uniform); + let _ = i32::rand(&mut rng, Uniform); let _: isize = uniform(&mut rng); let _: i8 = uniform(&mut rng); diff --git a/src/lib.rs b/src/lib.rs index 4472a0e8e05..21f42ee6ce5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -260,6 +260,7 @@ pub use iter::iter; use prng::IsaacWordRng; use prng::XorShiftRng; +use dist::{Default, Rand}; pub mod dist; pub mod iter; @@ -625,8 +626,7 @@ impl Rng for ThreadRng { /// Caching the thread local random number generator: /// /// ``` -/// use rand::Rng; -/// use rand::dist::SampleDefault; +/// use rand::dist::{Rand, Default}; /// /// let mut v = vec![1, 2, 3]; /// @@ -639,15 +639,15 @@ impl Rng for ThreadRng { /// let mut rng = rand::thread_rng(); /// /// for x in v.iter_mut() { -/// *x = SampleDefault::sample_default(&mut rng); +/// *x = Rand::rand(&mut rng, Default); /// } /// ``` /// /// Note that the above example uses `SampleDefault` which is a zero-sized /// marker type. #[inline] -pub fn random() -> T { - dist::SampleDefault::sample_default(&mut thread_rng()) +pub fn random>() -> T { + T::rand(&mut thread_rng(), Default) } From 44ee653230fe36a662de78ea23d5377e01a03a78 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 31 Jul 2017 17:39:07 +0100 Subject: [PATCH 039/247] Remove next_f32 / next_f64 from Rng (again) --- benches/bench.rs | 16 +++++------ src/dist/uniform.rs | 45 ++++++++++++++++++++++++------ src/lib.rs | 67 --------------------------------------------- 3 files changed, 44 insertions(+), 84 deletions(-) diff --git a/benches/bench.rs b/benches/bench.rs index 6f2c0006d9e..ff9a5f3876f 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -9,17 +9,17 @@ mod distributions; use std::mem::size_of; use test::{black_box, Bencher}; -use rand::{Rng, StdRng, OsRng, weak_rng}; +use rand::{StdRng, OsRng, weak_rng}; use rand::prng::{XorShiftRng, IsaacRng, Isaac64Rng}; use rand::{sample, Shuffle}; -use rand::dist::uniform; +use rand::dist::{Rand, Uniform, Uniform01}; #[bench] fn rand_xorshift(b: &mut Bencher) { let mut rng = XorShiftRng::new_from_rng(&mut OsRng::new().unwrap()); b.iter(|| { for _ in 0..RAND_BENCH_N { - black_box(uniform::(&mut rng)); + black_box(usize::rand(&mut rng, Uniform)); } }); b.bytes = size_of::() as u64 * RAND_BENCH_N; @@ -30,7 +30,7 @@ fn rand_isaac(b: &mut Bencher) { let mut rng = IsaacRng::new_from_rng(&mut OsRng::new().unwrap()); b.iter(|| { for _ in 0..RAND_BENCH_N { - black_box(uniform::(&mut rng)); + black_box(usize::rand(&mut rng, Uniform)); } }); b.bytes = size_of::() as u64 * RAND_BENCH_N; @@ -41,7 +41,7 @@ fn rand_isaac64(b: &mut Bencher) { let mut rng = Isaac64Rng::new_from_rng(&mut OsRng::new().unwrap()); b.iter(|| { for _ in 0..RAND_BENCH_N { - black_box(uniform::(&mut rng)); + black_box(usize::rand(&mut rng, Uniform)); } }); b.bytes = size_of::() as u64 * RAND_BENCH_N; @@ -52,7 +52,7 @@ fn rand_std(b: &mut Bencher) { let mut rng = StdRng::new().unwrap(); b.iter(|| { for _ in 0..RAND_BENCH_N { - black_box(uniform::(&mut rng)); + black_box(usize::rand(&mut rng, Uniform)); } }); b.bytes = size_of::() as u64 * RAND_BENCH_N; @@ -63,7 +63,7 @@ fn rand_f32(b: &mut Bencher) { let mut rng = StdRng::new().unwrap(); b.iter(|| { for _ in 0..RAND_BENCH_N { - black_box(rng.next_f32()); + black_box(f32::rand(&mut rng, Uniform01)); } }); b.bytes = size_of::() as u64 * RAND_BENCH_N; @@ -74,7 +74,7 @@ fn rand_f64(b: &mut Bencher) { let mut rng = StdRng::new().unwrap(); b.iter(|| { for _ in 0..RAND_BENCH_N { - black_box(rng.next_f64()); + black_box(f64::rand(&mut rng, Uniform01)); } }); b.bytes = size_of::() as u64 * RAND_BENCH_N; diff --git a/src/dist/uniform.rs b/src/dist/uniform.rs index a85ef5cb61e..bbdfe335417 100644 --- a/src/dist/uniform.rs +++ b/src/dist/uniform.rs @@ -203,17 +203,44 @@ impl Distribution for Uniform { } } +impl Distribution for Uniform01 { + /// This uses a technique described by Saito and Matsumoto at + /// MCQMC'08. Given that the IEEE floating point numbers are + /// uniformly distributed over [1,2), we generate a number in + /// this range and then offset it onto the range [0,1). Our + /// choice of bits (masking v. shifting) is arbitrary and + /// should be immaterial for high quality generators. For low + /// quality generators (ex. LCG), prefer bitshifting due to + /// correlation between sequential low order bits. + /// + /// See: + /// A PRNG specialized in double precision floating point numbers using + /// an affine transition + /// http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/ARTICLES/dSFMT.pdf + /// http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/dSFMT-slide-e.pdf + fn sample(&self, rng: &mut R) -> f32 { + const UPPER_MASK: u32 = 0x3F800000; + const LOWER_MASK: u32 = 0x7FFFFF; + let tmp = UPPER_MASK | (rng.next_u32() & LOWER_MASK); + let result: f32 = unsafe { mem::transmute(tmp) }; + result - 1.0 + } +} + +impl Distribution for Uniform01 { + fn sample(&self, rng: &mut R) -> f64 { + const UPPER_MASK: u64 = 0x3FF0000000000000; + const LOWER_MASK: u64 = 0xFFFFFFFFFFFFF; + let tmp = UPPER_MASK | (rng.next_u64() & LOWER_MASK); + let result: f64 = unsafe { mem::transmute(tmp) }; + result - 1.0 + } +} macro_rules! float_impls { - ($scale_name:ident, $ty:ty, $mantissa_bits:expr, $method_name:ident) => { + ($scale_name:ident, $ty:ty, $mantissa_bits:expr) => { const $scale_name: $ty = (1u64 << $mantissa_bits) as $ty; - impl Distribution<$ty> for Uniform01 { - #[inline] - fn sample(&self, rng: &mut R) -> $ty { - rng.$method_name() - } - } impl Distribution<$ty> for Open01 { #[inline] fn sample(&self, rng: &mut R) -> $ty { @@ -234,8 +261,8 @@ macro_rules! float_impls { } } } -float_impls! { SCALE_F64, f64, 53, next_f64 } -float_impls! { SCALE_F32, f32, 24, next_f32 } +float_impls! { SCALE_F64, f64, 53 } +float_impls! { SCALE_F32, f32, 24 } #[cfg(test)] diff --git a/src/lib.rs b/src/lib.rs index 21f42ee6ce5..ac51b0aef1d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -290,57 +290,6 @@ pub trait Rng { ((self.next_u32() as u64) << 32) | (self.next_u32() as u64) } - /// Return the next random f32 selected from the half-open - /// interval `[0, 1)`. - /// - /// This uses a technique described by Saito and Matsumoto at - /// MCQMC'08. Given that the IEEE floating point numbers are - /// uniformly distributed over [1,2), we generate a number in - /// this range and then offset it onto the range [0,1). Our - /// choice of bits (masking v. shifting) is arbitrary and - /// should be immaterial for high quality generators. For low - /// quality generators (ex. LCG), prefer bitshifting due to - /// correlation between sequential low order bits. - /// - /// See: - /// A PRNG specialized in double precision floating point numbers using - /// an affine transition - /// http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/ARTICLES/dSFMT.pdf - /// http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/dSFMT-slide-e.pdf - /// - /// By default this is implemented in terms of `next_u32`, but a - /// random number generator which can generate numbers satisfying - /// the requirements directly can overload this for performance. - /// It is required that the return value lies in `[0, 1)`. - /// - /// See `closed01` for the closed interval `[0,1]`, and - /// `open01` for the open interval `(0,1)`. - fn next_f32(&mut self) -> f32 { - const UPPER_MASK: u32 = 0x3F800000; - const LOWER_MASK: u32 = 0x7FFFFF; - let tmp = UPPER_MASK | (self.next_u32() & LOWER_MASK); - let result: f32 = unsafe { mem::transmute(tmp) }; - result - 1.0 - } - - /// Return the next random f64 selected from the half-open - /// interval `[0, 1)`. - /// - /// By default this is implemented in terms of `next_u64`, but a - /// random number generator which can generate numbers satisfying - /// the requirements directly can overload this for performance. - /// It is required that the return value lies in `[0, 1)`. - /// - /// See `closed01` for the closed interval `[0,1]`, and - /// `open01` for the open interval `(0,1)`. - fn next_f64(&mut self) -> f64 { - const UPPER_MASK: u64 = 0x3FF0000000000000; - const LOWER_MASK: u64 = 0xFFFFFFFFFFFFF; - let tmp = UPPER_MASK | (self.next_u64() & LOWER_MASK); - let result: f64 = unsafe { mem::transmute(tmp) }; - result - 1.0 - } - /// Fill `dest` with random data. /// /// This has a default implementation in terms of `next_u64` and @@ -402,14 +351,6 @@ impl<'a, R: ?Sized> Rng for &'a mut R where R: Rng { (**self).next_u64() } - fn next_f32(&mut self) -> f32 { - (**self).next_f32() - } - - fn next_f64(&mut self) -> f64 { - (**self).next_f64() - } - fn fill_bytes(&mut self, dest: &mut [u8]) { (**self).fill_bytes(dest) } @@ -424,14 +365,6 @@ impl Rng for Box where R: Rng { (**self).next_u64() } - fn next_f32(&mut self) -> f32 { - (**self).next_f32() - } - - fn next_f64(&mut self) -> f64 { - (**self).next_f64() - } - fn fill_bytes(&mut self, dest: &mut [u8]) { (**self).fill_bytes(dest) } From 0c40b806bf3fd80b71ac1499c8c7f705b2b28af0 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 31 Jul 2017 17:46:40 +0100 Subject: [PATCH 040/247] Add next_u8, next_u16 and next_u128 to Rng --- src/dist/uniform.rs | 6 +++--- src/lib.rs | 51 ++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 51 insertions(+), 6 deletions(-) diff --git a/src/dist/uniform.rs b/src/dist/uniform.rs index bbdfe335417..ff22500d342 100644 --- a/src/dist/uniform.rs +++ b/src/dist/uniform.rs @@ -163,14 +163,14 @@ impl Distribution for Uniform { impl Distribution for Uniform { #[inline] fn sample(&self, rng: &mut R) -> u8 { - rng.next_u32() as u8 + rng.next_u8() } } impl Distribution for Uniform { #[inline] fn sample(&self, rng: &mut R) -> u16 { - rng.next_u32() as u16 + rng.next_u16() } } @@ -192,7 +192,7 @@ impl Distribution for Uniform { impl Distribution for Uniform { #[inline] fn sample(&self, rng: &mut R) -> u128 { - ((rng.next_u64() as u128) << 64) | (rng.next_u64() as u128) + rng.next_u128() } } diff --git a/src/lib.rs b/src/lib.rs index ac51b0aef1d..98b67db20f3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -273,10 +273,21 @@ mod os; /// A random number generator. pub trait Rng { + /// Return the next random u8. + /// + /// By default this is implemented in terms of `next_u32`. + fn next_u8(&mut self) -> u8 { + self.next_u32() as u8 + } + + /// Return the next random u16. + /// + /// By default this is implemented in terms of `next_u32`. + fn next_u16(&mut self) -> u16 { + self.next_u32() as u16 + } + /// Return the next random u32. - /// - /// This rarely needs to be called directly, prefer `r.gen()` to - /// `r.next_u32()`. // FIXME #rust-lang/rfcs#628: Should be implemented in terms of next_u64 fn next_u32(&mut self) -> u32; @@ -290,6 +301,14 @@ pub trait Rng { ((self.next_u32() as u64) << 32) | (self.next_u32() as u64) } + #[cfg(feature = "i128_support")] + /// Return the next random u128. + /// + /// By default this is implemented in terms of `next_u64`. + fn next_u128(&mut self) -> u128 { + ((self.next_u64() as u128) << 64) | (self.next_u64() as u128) + } + /// Fill `dest` with random data. /// /// This has a default implementation in terms of `next_u64` and @@ -343,6 +362,14 @@ pub trait Rng { } impl<'a, R: ?Sized> Rng for &'a mut R where R: Rng { + fn next_u8(&mut self) -> u8 { + (**self).next_u8() + } + + fn next_u16(&mut self) -> u16 { + (**self).next_u16() + } + fn next_u32(&mut self) -> u32 { (**self).next_u32() } @@ -351,12 +378,25 @@ impl<'a, R: ?Sized> Rng for &'a mut R where R: Rng { (**self).next_u64() } + #[cfg(feature = "i128_support")] + fn next_u128(&mut self) -> u128 { + (**self).next_u128() + } + fn fill_bytes(&mut self, dest: &mut [u8]) { (**self).fill_bytes(dest) } } impl Rng for Box where R: Rng { + fn next_u8(&mut self) -> u8 { + (**self).next_u8() + } + + fn next_u16(&mut self) -> u16 { + (**self).next_u16() + } + fn next_u32(&mut self) -> u32 { (**self).next_u32() } @@ -365,6 +405,11 @@ impl Rng for Box where R: Rng { (**self).next_u64() } + #[cfg(feature = "i128_support")] + fn next_u128(&mut self) -> u128 { + (**self).next_u128() + } + fn fill_bytes(&mut self, dest: &mut [u8]) { (**self).fill_bytes(dest) } From 73843f954e426f204c5725c9f16c57c9fc42af4c Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 1 Aug 2017 08:23:41 +0100 Subject: [PATCH 041/247] pub use Default and Rand in lib root; hide default and uniform modules --- src/dist/mod.rs | 7 ++++--- src/dist/weighted.rs | 2 +- src/lib.rs | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/dist/mod.rs b/src/dist/mod.rs index 6db9d4cf31a..eec5726e5c8 100644 --- a/src/dist/mod.rs +++ b/src/dist/mod.rs @@ -27,14 +27,15 @@ pub use self::range::{range, Range}; pub use self::gamma::{Gamma, ChiSquared, FisherF, StudentT}; pub use self::normal::{Normal, LogNormal}; pub use self::exponential::Exp; +pub use self::weighted::{Weighted, WeightedChoice}; -pub mod default; -pub mod uniform; +mod default; +mod uniform; pub mod range; pub mod gamma; pub mod normal; pub mod exponential; -pub mod weighted; +mod weighted; /// Return a bool with a 1 in n chance of being true /// diff --git a/src/dist/weighted.rs b/src/dist/weighted.rs index a7d8c69dbe2..522ccc6d5b5 100644 --- a/src/dist/weighted.rs +++ b/src/dist/weighted.rs @@ -34,7 +34,7 @@ pub struct Weighted { /// /// ```rust /// use rand::dist::Distribution; -/// use rand::dist::weighted::{Weighted, WeightedChoice}; +/// use rand::dist::{Weighted, WeightedChoice}; /// /// let items = vec!(Weighted { weight: 2, item: 'a' }, /// Weighted { weight: 4, item: 'b' }, diff --git a/src/lib.rs b/src/lib.rs index 98b67db20f3..17f9b21680c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -257,10 +257,10 @@ pub use read::ReadRng; pub use os::OsRng; pub use self::sequence::{Choose, sample, Shuffle}; pub use iter::iter; +pub use dist::{Default, Rand}; use prng::IsaacWordRng; use prng::XorShiftRng; -use dist::{Default, Rand}; pub mod dist; pub mod iter; From 6b51b0bd3b04a6daab96b608e7ce2a728b4313f0 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 1 Aug 2017 08:28:49 +0100 Subject: [PATCH 042/247] Rename dist back to distributions --- README.md | 6 ++--- benches/bench.rs | 2 +- benches/distributions/exponential.rs | 4 ++-- benches/distributions/gamma.rs | 4 ++-- benches/distributions/normal.rs | 4 ++-- src/{dist => distributions}/default.rs | 6 ++--- src/{dist => distributions}/exponential.rs | 8 +++---- src/{dist => distributions}/gamma.rs | 14 +++++------ src/{dist => distributions}/mod.rs | 4 ++-- src/{dist => distributions}/normal.rs | 10 ++++---- src/{dist => distributions}/range.rs | 14 +++++------ src/{dist => distributions}/uniform.rs | 8 +++---- src/{dist => distributions}/weighted.rs | 8 +++---- .../ziggurat_tables.rs | 0 src/iter.rs | 4 ++-- src/lib.rs | 24 +++++++++---------- src/prng/chacha.rs | 2 +- src/prng/isaac.rs | 2 +- src/read.rs | 4 ++-- src/reseeding.rs | 4 ++-- src/sequence.rs | 2 +- 21 files changed, 67 insertions(+), 67 deletions(-) rename src/{dist => distributions}/default.rs (93%) rename src/{dist => distributions}/exponential.rs (93%) rename src/{dist => distributions}/gamma.rs (96%) rename src/{dist => distributions}/mod.rs (98%) rename src/{dist => distributions}/normal.rs (94%) rename src/{dist => distributions}/range.rs (96%) rename src/{dist => distributions}/uniform.rs (98%) rename src/{dist => distributions}/weighted.rs (97%) rename src/{dist => distributions}/ziggurat_tables.rs (100%) diff --git a/README.md b/README.md index 0f9ea87a140..7caa0fc9e1b 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ println!("{}", x) ``` ```rust -use rand::dist::{uniform}; +use rand::distributions::{uniform}; use rand::Rng; let mut rng = rand::thread_rng(); @@ -51,10 +51,10 @@ if uniform(&mut rng) { // random bool It is also possible to use other generators types, which have a similar interface. The following uses the "ChaCha" algorithm instead of the default. ```rust -use rand::{thread_rng, ChaChaRng, dist}; +use rand::{thread_rng, ChaChaRng, distributions}; let mut rng = ChaChaRng::new_from_rng(&mut thread_rng()); -println!("random between 0-9: {}", dist::range(0, 10, &mut rng)); +println!("random between 0-9: {}", distributions::range(0, 10, &mut rng)); ``` diff --git a/benches/bench.rs b/benches/bench.rs index ff9a5f3876f..960f9c2b959 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -12,7 +12,7 @@ use test::{black_box, Bencher}; use rand::{StdRng, OsRng, weak_rng}; use rand::prng::{XorShiftRng, IsaacRng, Isaac64Rng}; use rand::{sample, Shuffle}; -use rand::dist::{Rand, Uniform, Uniform01}; +use rand::distributions::{Rand, Uniform, Uniform01}; #[bench] fn rand_xorshift(b: &mut Bencher) { diff --git a/benches/distributions/exponential.rs b/benches/distributions/exponential.rs index aff4e203f47..57c581a810d 100644 --- a/benches/distributions/exponential.rs +++ b/benches/distributions/exponential.rs @@ -1,8 +1,8 @@ use std::mem::size_of; use test::Bencher; use rand; -use rand::dist::exponential::Exp; -use rand::dist::Distribution; +use rand::distributions::exponential::Exp; +use rand::distributions::Distribution; #[bench] fn rand_exp(b: &mut Bencher) { diff --git a/benches/distributions/gamma.rs b/benches/distributions/gamma.rs index 6b122a21ddf..47c501f35fd 100644 --- a/benches/distributions/gamma.rs +++ b/benches/distributions/gamma.rs @@ -1,8 +1,8 @@ use std::mem::size_of; use test::Bencher; use rand; -use rand::dist::Distribution; -use rand::dist::gamma::Gamma; +use rand::distributions::Distribution; +use rand::distributions::gamma::Gamma; #[bench] fn bench_gamma_large_shape(b: &mut Bencher) { diff --git a/benches/distributions/normal.rs b/benches/distributions/normal.rs index f819dd1fdfb..509f954857e 100644 --- a/benches/distributions/normal.rs +++ b/benches/distributions/normal.rs @@ -1,8 +1,8 @@ use std::mem::size_of; use test::Bencher; use rand; -use rand::dist::Distribution; -use rand::dist::normal::Normal; +use rand::distributions::Distribution; +use rand::distributions::normal::Normal; #[bench] fn rand_normal(b: &mut Bencher) { diff --git a/src/dist/default.rs b/src/distributions/default.rs similarity index 93% rename from src/dist/default.rs rename to src/distributions/default.rs index c0619d8cbbc..ef406ddff1b 100644 --- a/src/dist/default.rs +++ b/src/distributions/default.rs @@ -11,8 +11,8 @@ //! Generic value creation use Rng; -use dist::{Distribution, Rand}; -use dist::uniform::{Uniform, /*Uniform01,*/ codepoint}; +use distributions::{Distribution, Rand}; +use distributions::uniform::{Uniform, /*Uniform01,*/ codepoint}; /// A generic random value distribution. Generates values using what appears to /// be "the best" distribution for each type, but ultimately the choice is arbitrary. @@ -55,7 +55,7 @@ impl Distribution for Default { #[cfg(test)] mod tests { use {Rng, thread_rng}; - use dist::{Rand, Default}; + use distributions::{Rand, Default}; #[test] fn test_types() { diff --git a/src/dist/exponential.rs b/src/distributions/exponential.rs similarity index 93% rename from src/dist/exponential.rs rename to src/distributions/exponential.rs index 3ec62f4c9ae..e7856420335 100644 --- a/src/dist/exponential.rs +++ b/src/distributions/exponential.rs @@ -11,7 +11,7 @@ //! The exponential distribution. use {Rng}; -use dist::{ziggurat, ziggurat_tables, Distribution, uniform01}; +use distributions::{ziggurat, ziggurat_tables, Distribution, uniform01}; /// Generates Exp(1) random numbers. /// @@ -29,7 +29,7 @@ use dist::{ziggurat, ziggurat_tables, Distribution, uniform01}; /// # Example /// /// ```rust -/// use rand::dist::exponential::exp1; +/// use rand::distributions::exponential::exp1; /// /// let x = exp1(&mut rand::thread_rng()); /// println!("{}", x); @@ -60,7 +60,7 @@ pub fn exp1(rng: &mut R) -> f64 { /// # Example /// /// ```rust -/// use rand::dist::{Exp, Distribution}; +/// use rand::distributions::{Exp, Distribution}; /// /// let exp = Exp::new(2.0); /// let v = exp.sample(&mut rand::thread_rng()); @@ -90,7 +90,7 @@ impl Distribution for Exp { #[cfg(test)] mod test { - use dist::{Distribution}; + use distributions::{Distribution}; use super::Exp; #[test] diff --git a/src/dist/gamma.rs b/src/distributions/gamma.rs similarity index 96% rename from src/dist/gamma.rs rename to src/distributions/gamma.rs index 92e1101e601..7c29a1df6f2 100644 --- a/src/dist/gamma.rs +++ b/src/distributions/gamma.rs @@ -16,8 +16,8 @@ use self::GammaRepr::*; use self::ChiSquaredRepr::*; use {Rng}; -use dist::normal::standard_normal; -use dist::{Distribution, Exp, open01}; +use distributions::normal::standard_normal; +use distributions::{Distribution, Exp, open01}; /// The Gamma distribution `Gamma(shape, scale)` distribution. /// @@ -38,7 +38,7 @@ use dist::{Distribution, Exp, open01}; /// # Example /// /// ```rust -/// use rand::dist::{Distribution, Gamma}; +/// use rand::distributions::{Distribution, Gamma}; /// /// let gamma = Gamma::new(2.0, 5.0); /// let v = gamma.sample(&mut rand::thread_rng()); @@ -182,7 +182,7 @@ impl Distribution for GammaLargeShape { /// # Example /// /// ```rust -/// use rand::dist::{ChiSquared, Distribution}; +/// use rand::distributions::{ChiSquared, Distribution}; /// /// let chi = ChiSquared::new(11.0); /// let v = chi.sample(&mut rand::thread_rng()); @@ -237,7 +237,7 @@ impl Distribution for ChiSquared { /// # Example /// /// ```rust -/// use rand::dist::{FisherF, Distribution}; +/// use rand::distributions::{FisherF, Distribution}; /// /// let f = FisherF::new(2.0, 32.0); /// let v = f.sample(&mut rand::thread_rng()); @@ -278,7 +278,7 @@ impl Distribution for FisherF { /// # Example /// /// ```rust -/// use rand::dist::{StudentT, Distribution}; +/// use rand::distributions::{StudentT, Distribution}; /// /// let t = StudentT::new(11.0); /// let v = t.sample(&mut rand::thread_rng()); @@ -310,7 +310,7 @@ impl Distribution for StudentT { #[cfg(test)] mod test { - use dist::{Distribution}; + use distributions::{Distribution}; use super::{ChiSquared, StudentT, FisherF}; #[test] diff --git a/src/dist/mod.rs b/src/distributions/mod.rs similarity index 98% rename from src/dist/mod.rs rename to src/distributions/mod.rs index eec5726e5c8..d6bb1bb8351 100644 --- a/src/dist/mod.rs +++ b/src/distributions/mod.rs @@ -46,7 +46,7 @@ mod weighted; /// # Example /// /// ```rust -/// use rand::dist::weighted_bool; +/// use rand::distributions::weighted_bool; /// /// let mut rng = rand::thread_rng(); /// println!("{}", weighted_bool(3, &mut rng)); @@ -142,7 +142,7 @@ fn ziggurat( #[cfg(test)] mod test { use {Rng, thread_rng}; - use dist::weighted_bool; + use distributions::weighted_bool; #[test] fn test_fn_weighted_bool() { diff --git a/src/dist/normal.rs b/src/distributions/normal.rs similarity index 94% rename from src/dist/normal.rs rename to src/distributions/normal.rs index be9845d3c2a..3e207c2b5ef 100644 --- a/src/dist/normal.rs +++ b/src/distributions/normal.rs @@ -11,7 +11,7 @@ //! The normal and derived distributions. use {Rng}; -use dist::{ziggurat, ziggurat_tables, Distribution, open01}; +use distributions::{ziggurat, ziggurat_tables, Distribution, open01}; /// Generates N(0, 1) random numbers /// (a.k.a. a standard normal, or Gaussian). @@ -28,7 +28,7 @@ use dist::{ziggurat, ziggurat_tables, Distribution, open01}; /// # Example /// /// ```rust -/// use rand::dist::normal::standard_normal; +/// use rand::distributions::normal::standard_normal; /// /// let x = standard_normal(&mut rand::thread_rng()); /// println!("{}", x); @@ -76,7 +76,7 @@ pub fn standard_normal(rng: &mut R) -> f64 { /// # Example /// /// ```rust -/// use rand::dist::{Normal, Distribution}; +/// use rand::distributions::{Normal, Distribution}; /// /// // mean 2, standard deviation 3 /// let normal = Normal::new(2.0, 3.0); @@ -120,7 +120,7 @@ impl Distribution for Normal { /// # Example /// /// ```rust -/// use rand::dist::{LogNormal, Distribution}; +/// use rand::distributions::{LogNormal, Distribution}; /// /// // mean 2, standard deviation 3 /// let log_normal = LogNormal::new(2.0, 3.0); @@ -153,7 +153,7 @@ impl Distribution for LogNormal { #[cfg(test)] mod tests { - use dist::{Distribution}; + use distributions::{Distribution}; use super::{Normal, LogNormal}; #[test] diff --git a/src/dist/range.rs b/src/distributions/range.rs similarity index 96% rename from src/dist/range.rs rename to src/distributions/range.rs index b038cbbd9b3..c5a086b8bbe 100644 --- a/src/dist/range.rs +++ b/src/distributions/range.rs @@ -15,7 +15,7 @@ use std::num::Wrapping as w; use Rng; -use dist::{Distribution, uniform01}; +use distributions::{Distribution, uniform01}; /// Generate a random value in the range [`low`, `high`). /// @@ -30,7 +30,7 @@ use dist::{Distribution, uniform01}; /// # Example /// /// ```rust -/// use rand::dist::range; +/// use rand::distributions::range; /// /// let mut rng = rand::thread_rng(); /// let n: u32 = range(0, 10, &mut rng); @@ -39,7 +39,7 @@ use dist::{Distribution, uniform01}; /// println!("{}", m); /// ``` pub fn range(low: T, high: T, rng: &mut R) -> T { - assert!(low < high, "dist::range called with low >= high"); + assert!(low < high, "distributions::range called with low >= high"); Range::new(low, high).sample(rng) } @@ -60,7 +60,7 @@ pub fn range(low: T, high: T, rng: & /// # Example /// /// ```rust -/// use rand::dist::{Distribution, Range}; +/// use rand::distributions::{Distribution, Range}; /// /// fn main() { /// let between = Range::new(10, 10000); @@ -138,7 +138,7 @@ macro_rules! integer_impl { #[inline] fn sample_range(r: &Range<$ty>, rng: &mut R) -> $ty { - use $crate::dist::uniform; + use $crate::distributions::uniform; loop { // rejection sample let v: $unsigned = uniform(rng); @@ -189,8 +189,8 @@ float_impl! { f64 } #[cfg(test)] mod tests { use thread_rng; - use dist::{Distribution}; - use dist::{Range, range}; + use distributions::{Distribution}; + use distributions::{Range, range}; #[test] fn test_fn_range() { diff --git a/src/dist/uniform.rs b/src/distributions/uniform.rs similarity index 98% rename from src/dist/uniform.rs rename to src/distributions/uniform.rs index ff22500d342..f8a6dd28b25 100644 --- a/src/dist/uniform.rs +++ b/src/distributions/uniform.rs @@ -14,7 +14,7 @@ use std::char; use std::mem; use Rng; -use dist::{Distribution, Rand}; +use distributions::{Distribution, Rand}; // ----- convenience functions ----- @@ -268,9 +268,9 @@ float_impls! { SCALE_F32, f32, 24 } #[cfg(test)] mod tests { use {Rng, thread_rng, iter}; - use dist::{Rand, uniform, Uniform}; - use dist::uniform::{codepoint, ascii_word_char}; - use dist::{uniform01, open01, closed01}; + use distributions::{Rand, uniform, Uniform}; + use distributions::uniform::{codepoint, ascii_word_char}; + use distributions::{uniform01, open01, closed01}; #[test] fn test_integers() { diff --git a/src/dist/weighted.rs b/src/distributions/weighted.rs similarity index 97% rename from src/dist/weighted.rs rename to src/distributions/weighted.rs index 522ccc6d5b5..10d9893596a 100644 --- a/src/dist/weighted.rs +++ b/src/distributions/weighted.rs @@ -14,7 +14,7 @@ //! adapted, or removed entirely. use Rng; -use dist::{Range, Distribution}; +use distributions::{Range, Distribution}; /// A value with a particular weight for use with `WeightedChoice`. #[derive(Copy, Clone, Debug)] @@ -33,8 +33,8 @@ pub struct Weighted { /// # Example /// /// ```rust -/// use rand::dist::Distribution; -/// use rand::dist::{Weighted, WeightedChoice}; +/// use rand::distributions::Distribution; +/// use rand::distributions::{Weighted, WeightedChoice}; /// /// let items = vec!(Weighted { weight: 2, item: 'a' }, /// Weighted { weight: 4, item: 'b' }, @@ -134,7 +134,7 @@ impl Distribution for WeightedChoice { #[cfg(test)] mod tests { use Rng; - use dist::Distribution; + use distributions::Distribution; use super::{WeightedChoice, Weighted}; #[derive(PartialEq, Debug)] diff --git a/src/dist/ziggurat_tables.rs b/src/distributions/ziggurat_tables.rs similarity index 100% rename from src/dist/ziggurat_tables.rs rename to src/distributions/ziggurat_tables.rs diff --git a/src/iter.rs b/src/iter.rs index dbbd7493be5..282b151a6fa 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -32,7 +32,7 @@ pub struct Iter<'a, R: Rng+?Sized+'a> { /// /// ``` /// use rand::{thread_rng, Rng, iter}; -/// use rand::dist::{uniform, ascii_word_char}; +/// use rand::distributions::{uniform, ascii_word_char}; /// /// let mut rng = thread_rng(); /// let x: Vec = iter(&mut rng).take(10).map(|rng| uniform(rng)).collect(); @@ -134,7 +134,7 @@ impl<'a, R:?Sized+'a, U, F> Iterator for FlatMap<'a, R, U, F> #[cfg(test)] mod tests { use {Rng, thread_rng, iter}; - use dist::{uniform, ascii_word_char}; + use distributions::{uniform, ascii_word_char}; #[test] fn test_iter() { diff --git a/src/lib.rs b/src/lib.rs index 17f9b21680c..4c1b66f6b19 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,7 +13,7 @@ //! The `Rng` trait covers random number generation, and can be used directly //! to produce values of some core types (`u32, u64, f32, f64`, and byte //! strings). To generate anything else, or to generate values of the above type -//! in generic code, use the `dist` (distributions) module. This includes +//! in generic code, use the `distributions` module. This includes //! distributions like ranges, normal and exponential. //! //! # Usage @@ -76,7 +76,7 @@ //! # Examples //! //! ```rust -//! use rand::dist::uniform; +//! use rand::distributions::uniform; //! //! let mut rng = rand::thread_rng(); //! if uniform(&mut rng) { // random bool @@ -88,7 +88,7 @@ //! //! ```rust //! use rand::thread_rng; -//! use rand::dist::{uniform, uniform01}; +//! use rand::distributions::{uniform, uniform01}; //! let mut rng = thread_rng(); //! let tuple: (f64, u8) = (uniform01(&mut rng), uniform(&mut rng)); //! println!("{:?}", tuple) @@ -112,7 +112,7 @@ //! and multiply this fraction by 4. //! //! ``` -//! use rand::dist::{Distribution, Range}; +//! use rand::distributions::{Distribution, Range}; //! //! fn main() { //! let between = Range::new(-1f64, 1.); @@ -155,7 +155,7 @@ //! //! ``` //! use rand::Rng; -//! use rand::dist::{Distribution, Range, uniform}; +//! use rand::distributions::{Distribution, Range, uniform}; //! //! struct SimulationResult { //! win: bool, @@ -257,12 +257,12 @@ pub use read::ReadRng; pub use os::OsRng; pub use self::sequence::{Choose, sample, Shuffle}; pub use iter::iter; -pub use dist::{Default, Rand}; +pub use distributions::{Default, Rand}; use prng::IsaacWordRng; use prng::XorShiftRng; -pub mod dist; +pub mod distributions; pub mod iter; pub mod prng; pub mod reseeding; @@ -425,7 +425,7 @@ pub trait SeedableRng: Rng { /// ```rust /// use rand::{Rng, SeedableRng}; /// use rand::prng::IsaacWordRng; - /// use rand::dist::uniform01; + /// use rand::distributions::uniform01; /// /// let seed: &[_] = &[1, 2, 3, 4]; /// let mut rng: IsaacWordRng = SeedableRng::from_seed(seed); @@ -442,7 +442,7 @@ pub trait SeedableRng: Rng { /// ```rust /// use rand::{Rng, SeedableRng}; /// use rand::prng::ChaChaRng; - /// use rand::dist::uniform01; + /// use rand::distributions::uniform01; /// /// let seed: &[_] = &[1, 2, 3, 4]; /// let mut rng: ChaChaRng = SeedableRng::from_seed(seed); @@ -584,7 +584,7 @@ impl Rng for ThreadRng { /// /// `random()` can generate various types of random things, and so may require /// type hinting to generate the specific type you want. It uses -/// `dist::SampleDefault` to determine *how* values are generated for each type. +/// `distributions::SampleDefault` to determine *how* values are generated for each type. /// /// This function uses the thread local random number generator. This means /// that if you're calling `random()` in a loop, caching the generator can @@ -604,7 +604,7 @@ impl Rng for ThreadRng { /// Caching the thread local random number generator: /// /// ``` -/// use rand::dist::{Rand, Default}; +/// use rand::distributions::{Rand, Default}; /// /// let mut v = vec![1, 2, 3]; /// @@ -632,7 +632,7 @@ pub fn random>() -> T { #[cfg(test)] mod test { use {Rng, thread_rng, SeedableRng, StdRng, Shuffle, iter}; - use dist::{uniform, range, ascii_word_char}; + use distributions::{uniform, range, ascii_word_char}; use std::iter::repeat; pub struct MyRng { inner: R } diff --git a/src/prng/chacha.rs b/src/prng/chacha.rs index a26d43515a0..bdb39462fbc 100644 --- a/src/prng/chacha.rs +++ b/src/prng/chacha.rs @@ -239,7 +239,7 @@ impl<'a> SeedableRng<&'a [u32]> for ChaChaRng { #[cfg(test)] mod test { use {Rng, SeedableRng, iter}; - use dist::ascii_word_char; + use distributions::ascii_word_char; use super::ChaChaRng; #[test] diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index dca556df569..5d05f256b64 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -536,7 +536,7 @@ impl fmt::Debug for Isaac64Rng { #[cfg(test)] mod test { use {Rng, SeedableRng, iter}; - use dist::ascii_word_char; + use distributions::ascii_word_char; use super::{IsaacRng, Isaac64Rng}; #[test] diff --git a/src/read.rs b/src/read.rs index fc8ea24fc4f..bf0c2fccdf1 100644 --- a/src/read.rs +++ b/src/read.rs @@ -24,11 +24,11 @@ use Rng; /// # Example /// /// ```rust -/// use rand::{ReadRng, dist}; +/// use rand::{ReadRng, distributions}; /// /// let data = vec![1, 2, 3, 4, 5, 6, 7, 8]; /// let mut rng = ReadRng::new(&data[..]); -/// println!("{:x}", dist::uniform::(&mut rng)); +/// println!("{:x}", distributions::uniform::(&mut rng)); /// ``` #[derive(Debug)] pub struct ReadRng { diff --git a/src/reseeding.rs b/src/reseeding.rs index 1b189516a8a..052567e46c4 100644 --- a/src/reseeding.rs +++ b/src/reseeding.rs @@ -104,7 +104,7 @@ impl, Rsdr: Reseeder + Default> /// /// ```rust /// use rand::{Rng, SeedableRng, StdRng, iter}; -/// use rand::dist::ascii_word_char; +/// use rand::distributions::ascii_word_char; /// use rand::reseeding::{Reseeder, ReseedingRng}; /// /// struct TickTockReseeder { tick: bool } @@ -151,7 +151,7 @@ mod test { use std::default::Default; use std::iter::repeat; use {SeedableRng, Rng, iter}; - use dist::ascii_word_char; + use distributions::ascii_word_char; use super::{ReseedingRng, ReseedWithDefault}; struct Counter { diff --git a/src/sequence.rs b/src/sequence.rs index 0dd1189ec7a..2fce74e5d72 100644 --- a/src/sequence.rs +++ b/src/sequence.rs @@ -11,7 +11,7 @@ //! Random operations on sequences use Rng; -use dist::range; +use distributions::range; /// This trait implements a `choose` operations on slices and sequences. pub trait Choose { From 92ab890b8b2145c211ffe24553d721f94ac73936 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 1 Aug 2017 08:50:03 +0100 Subject: [PATCH 043/247] Add random_with(distribution) function --- src/lib.rs | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 4c1b66f6b19..ac4949fd4d6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -580,7 +580,7 @@ impl Rng for ThreadRng { } } -/// Generates a andom value using the thread-local random number generator. +/// Generates a random value using the thread-local random number generator. /// /// `random()` can generate various types of random things, and so may require /// type hinting to generate the specific type you want. It uses @@ -604,7 +604,7 @@ impl Rng for ThreadRng { /// Caching the thread local random number generator: /// /// ``` -/// use rand::distributions::{Rand, Default}; +/// use rand::{Rand, Default}; /// /// let mut v = vec![1, 2, 3]; /// @@ -628,6 +628,39 @@ pub fn random>() -> T { T::rand(&mut thread_rng(), Default) } +/// Generates a random value using the thread-local random number generator. +/// +/// This is a more flexible variant of `random()`, supporting selection of the +/// distribution used. For example: +/// +/// ``` +/// use rand::random_with; +/// use rand::distributions::{Rand, Default, Uniform01, Closed01, Range}; +/// +/// // identical to calling `random()`: +/// // FIXME let x: f64 = random_with(Default); +/// +/// // same distribution, since Default uses Uniform01 for floats: +/// let y: f64 = random_with(Uniform01); +/// +/// // use the closed range [0, 1] inseat of half-open [0, 1): +/// let z: f64 = random_with(Closed01); +/// +/// // use the half-open range [0, 2): +/// let w: f64 = random_with(Range::new(0.0, 2.0)); +/// +/// // Note that constructing a `Range` is non-trivial, so for the last example +/// // it might be better to do this if sampling a lot: +/// let mut rng = rand::thread_rng(); +/// let range = Range::new(0.0, 2.0); +/// // Do this bit many times: +/// let v = f64::rand(&mut rng, range); +/// ``` +#[inline] +pub fn random_with>(distribution: D) -> T { + T::rand(&mut thread_rng(), distribution) +} + #[cfg(test)] mod test { From 4522367b4b2c2c4dffcc901d1a8e1dc3ba815eb7 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 1 Aug 2017 09:39:36 +0100 Subject: [PATCH 044/247] Support f32, f64 for Default (workaround) --- src/distributions/default.rs | 20 +++++++++++++++----- src/lib.rs | 2 +- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/distributions/default.rs b/src/distributions/default.rs index ef406ddff1b..6cdc6513b02 100644 --- a/src/distributions/default.rs +++ b/src/distributions/default.rs @@ -12,7 +12,7 @@ use Rng; use distributions::{Distribution, Rand}; -use distributions::uniform::{Uniform, /*Uniform01,*/ codepoint}; +use distributions::uniform::{Uniform, Uniform01, codepoint}; /// A generic random value distribution. Generates values using what appears to /// be "the best" distribution for each type, but ultimately the choice is arbitrary. @@ -20,7 +20,7 @@ use distributions::uniform::{Uniform, /*Uniform01,*/ codepoint}; /// Makes use of the following distributions: /// /// * [`Uniform`] for integer types -/// * [`Uniform01`] for floating point types (FIXME) +/// * [`Uniform01`] for floating point types /// /// Makes use of the following methods: /// @@ -44,6 +44,17 @@ impl> Distribution for Default { // T::rand(rng, Uniform01) // } // } +// workaround above issue: +impl Distribution for Default { + fn sample(&self, rng: &mut R) -> f32 { + f32::rand(rng, Uniform01) + } +} +impl Distribution for Default { + fn sample(&self, rng: &mut R) -> f64 { + f64::rand(rng, Uniform01) + } +} impl Distribution for Default { fn sample(&self, rng: &mut R) -> char { @@ -66,9 +77,8 @@ mod tests { do_test::(rng); do_test::(rng); - // FIXME (see above) - // do_test::(rng); - // do_test::(rng); + do_test::(rng); + do_test::(rng); #[cfg(feature = "i128_support")] do_test::(rng); do_test::(rng); diff --git a/src/lib.rs b/src/lib.rs index ac4949fd4d6..1df0adbe62b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -638,7 +638,7 @@ pub fn random>() -> T { /// use rand::distributions::{Rand, Default, Uniform01, Closed01, Range}; /// /// // identical to calling `random()`: -/// // FIXME let x: f64 = random_with(Default); +/// let x: f64 = random_with(Default); /// /// // same distribution, since Default uses Uniform01 for floats: /// let y: f64 = random_with(Uniform01); From af372d72fb2e794977ea682ad1fdd466f30465a3 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 1 Aug 2017 09:43:43 +0100 Subject: [PATCH 045/247] Re-enable dynamic dispatch tests --- src/lib.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 1df0adbe62b..dcb06c89d17 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -746,26 +746,20 @@ mod test { let mut r = &mut rng as &mut Rng; r.next_u32(); uniform::(&mut r); - // FIXME: dynamic dispatch? - /* let mut v = [1, 1, 1]; v[..].shuffle(r); let b: &[_] = &[1, 1, 1]; assert_eq!(v, b); - */ assert_eq!(range(0, 1, &mut r), 0); } { let mut r = Box::new(rng) as Box; r.next_u32(); uniform::(&mut r); - // FIXME: dynamic dispatch? - /* let mut v = [1, 1, 1]; - v[..].shuffle(r); + v[..].shuffle(&mut *r); let b: &[_] = &[1, 1, 1]; assert_eq!(v, b); - */ assert_eq!(range(0, 1, &mut r), 0); } } From 3a92bc949fead35ad7c858c2741628bb5742a531 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 1 Aug 2017 09:19:19 +0100 Subject: [PATCH 046/247] Remove open01(rng) and closed01(rng) wrapper functions It turns out there's little need for them --- src/distributions/gamma.rs | 6 +++--- src/distributions/mod.rs | 2 +- src/distributions/normal.rs | 6 +++--- src/distributions/uniform.rs | 28 ++++++---------------------- 4 files changed, 13 insertions(+), 29 deletions(-) diff --git a/src/distributions/gamma.rs b/src/distributions/gamma.rs index 7c29a1df6f2..9a60c4492ec 100644 --- a/src/distributions/gamma.rs +++ b/src/distributions/gamma.rs @@ -17,7 +17,7 @@ use self::ChiSquaredRepr::*; use {Rng}; use distributions::normal::standard_normal; -use distributions::{Distribution, Exp, open01}; +use distributions::{Distribution, Exp, Rand, Open01}; /// The Gamma distribution `Gamma(shape, scale)` distribution. /// @@ -145,7 +145,7 @@ impl Distribution for Gamma { } impl Distribution for GammaSmallShape { fn sample(&self, rng: &mut R) -> f64 { - let u: f64 = open01(rng); + let u = f64::rand(rng, Open01); self.large_shape.sample(rng) * u.powf(self.inv_shape) } @@ -160,7 +160,7 @@ impl Distribution for GammaLargeShape { } let v = v_cbrt * v_cbrt * v_cbrt; - let u: f64 = open01(rng); + let u = f64::rand(rng, Open01); let x_sqr = x * x; if u < 1.0 - 0.0331 * x_sqr * x_sqr || diff --git a/src/distributions/mod.rs b/src/distributions/mod.rs index d6bb1bb8351..be1f9798b46 100644 --- a/src/distributions/mod.rs +++ b/src/distributions/mod.rs @@ -20,7 +20,7 @@ use Rng; pub use self::default::Default; -pub use self::uniform::{uniform, uniform01, open01, closed01, codepoint, +pub use self::uniform::{uniform, uniform01, codepoint, ascii_word_char}; pub use self::uniform::{Uniform, Uniform01, Open01, Closed01}; pub use self::range::{range, Range}; diff --git a/src/distributions/normal.rs b/src/distributions/normal.rs index 3e207c2b5ef..6cda325df46 100644 --- a/src/distributions/normal.rs +++ b/src/distributions/normal.rs @@ -11,7 +11,7 @@ //! The normal and derived distributions. use {Rng}; -use distributions::{ziggurat, ziggurat_tables, Distribution, open01}; +use distributions::{ziggurat, ziggurat_tables, Distribution, Rand, Open01}; /// Generates N(0, 1) random numbers /// (a.k.a. a standard normal, or Gaussian). @@ -50,8 +50,8 @@ pub fn standard_normal(rng: &mut R) -> f64 { let mut y = 0.0f64; while -2.0 * y < x * x { - let x_: f64 = open01(rng); - let y_: f64 = open01(rng); + let x_ = f64::rand(rng, Open01); + let y_ = f64::rand(rng, Open01); x = x_.ln() / ziggurat_tables::ZIG_NORM_R; y = y_.ln(); diff --git a/src/distributions/uniform.rs b/src/distributions/uniform.rs index f8a6dd28b25..008239daea9 100644 --- a/src/distributions/uniform.rs +++ b/src/distributions/uniform.rs @@ -34,22 +34,6 @@ pub fn uniform01, R: Rng+?Sized>(rng: &mut R) -> T { T::rand(rng, Uniform01) } -/// Sample values uniformly over the open range (0, 1) -/// -/// This method has precisely two template parameters. To fix the output type, -/// use the syntax `open01::(rng)`. -pub fn open01, R: Rng+?Sized>(rng: &mut R) -> T { - T::rand(rng, Open01) -} - -/// Sample values uniformly over the closed range [0, 1] -/// -/// This method has precisely two template parameters. To fix the output type, -/// use the syntax `closed01::(rng)`. -pub fn closed01, R: Rng+?Sized>(rng: &mut R) -> T { - T::rand(rng, Closed01) -} - /// Sample a `char`, uniformly distributed over all Unicode scalar values, /// i.e. all code points in the range `0...0x10_FFFF`, except for the range /// `0xD800...0xDFFF` (the surrogate code points). This includes @@ -268,9 +252,9 @@ float_impls! { SCALE_F32, f32, 24 } #[cfg(test)] mod tests { use {Rng, thread_rng, iter}; - use distributions::{Rand, uniform, Uniform}; + use distributions::{Rand, uniform, Uniform, Open01, Closed01}; use distributions::uniform::{codepoint, ascii_word_char}; - use distributions::{uniform01, open01, closed01}; + use distributions::{uniform01}; #[test] fn test_integers() { @@ -345,10 +329,10 @@ mod tests { let mut rng = thread_rng(); for _ in 0..1_000 { // strict inequalities - let f = open01::(&mut rng); + let f = f64::rand(&mut rng, Open01); assert!(0.0 < f && f < 1.0); - let f = open01::(&mut rng); + let f = f32::rand(&mut rng, Open01); assert!(0.0 < f && f < 1.0); } } @@ -358,10 +342,10 @@ mod tests { let mut rng = thread_rng(); for _ in 0..1_000 { // strict inequalities - let f = closed01::(&mut rng); + let f = f64::rand(&mut rng, Closed01); assert!(0.0 <= f && f <= 1.0); - let f = closed01::(&mut rng); + let f = f32::rand(&mut rng, Closed01); assert!(0.0 <= f && f <= 1.0); } } From 54487c63aeef3d71570fd4328265319b5dbb8976 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 1 Aug 2017 09:36:57 +0100 Subject: [PATCH 047/247] Remove uniform01(rng) wrapper function (as previous commit) --- src/distributions/exponential.rs | 5 +++-- src/distributions/mod.rs | 5 ++--- src/distributions/range.rs | 5 +++-- src/distributions/uniform.rs | 25 ++++++++--------------- src/lib.rs | 35 +++++++++++++++----------------- 5 files changed, 33 insertions(+), 42 deletions(-) diff --git a/src/distributions/exponential.rs b/src/distributions/exponential.rs index e7856420335..be8616391e2 100644 --- a/src/distributions/exponential.rs +++ b/src/distributions/exponential.rs @@ -11,7 +11,7 @@ //! The exponential distribution. use {Rng}; -use distributions::{ziggurat, ziggurat_tables, Distribution, uniform01}; +use distributions::{ziggurat, ziggurat_tables, Distribution, Uniform01, Rand}; /// Generates Exp(1) random numbers. /// @@ -43,7 +43,8 @@ pub fn exp1(rng: &mut R) -> f64 { } #[inline] fn zero_case(rng: &mut R, _u: f64) -> f64 { - ziggurat_tables::ZIG_EXP_R - uniform01::(rng).ln() + let x = f64::rand(rng, Uniform01); + ziggurat_tables::ZIG_EXP_R - x.ln() } (ziggurat(rng, false, diff --git a/src/distributions/mod.rs b/src/distributions/mod.rs index be1f9798b46..ae261e60b27 100644 --- a/src/distributions/mod.rs +++ b/src/distributions/mod.rs @@ -20,8 +20,7 @@ use Rng; pub use self::default::Default; -pub use self::uniform::{uniform, uniform01, codepoint, - ascii_word_char}; +pub use self::uniform::{uniform, codepoint, ascii_word_char}; pub use self::uniform::{Uniform, Uniform01, Open01, Closed01}; pub use self::range::{range, Range}; pub use self::gamma::{Gamma, ChiSquared, FisherF, StudentT}; @@ -133,7 +132,7 @@ fn ziggurat( return zero_case(rng, u); } // algebraically equivalent to f1 + DRanU()*(f0 - f1) < 1 - if f_tab[i + 1] + (f_tab[i] - f_tab[i + 1]) * uniform01::(rng) < pdf(x) { + if f_tab[i + 1] + (f_tab[i] - f_tab[i + 1]) * f64::rand(rng, Uniform01) < pdf(x) { return x; } } diff --git a/src/distributions/range.rs b/src/distributions/range.rs index c5a086b8bbe..717daaefd77 100644 --- a/src/distributions/range.rs +++ b/src/distributions/range.rs @@ -15,7 +15,7 @@ use std::num::Wrapping as w; use Rng; -use distributions::{Distribution, uniform01}; +use distributions::{Distribution, Uniform01, Rand}; /// Generate a random value in the range [`low`, `high`). /// @@ -177,7 +177,8 @@ macro_rules! float_impl { } } fn sample_range(r: &Range<$ty>, rng: &mut R) -> $ty { - r.low + r.range * uniform01::<$ty, _>(rng) + let x: $ty = Rand::rand(rng, Uniform01); + r.low + r.range * x } } } diff --git a/src/distributions/uniform.rs b/src/distributions/uniform.rs index 008239daea9..1ece83c445b 100644 --- a/src/distributions/uniform.rs +++ b/src/distributions/uniform.rs @@ -26,14 +26,6 @@ pub fn uniform, R: Rng+?Sized>(rng: &mut R) -> T { T::rand(rng, Uniform) } -/// Sample values uniformly over the half-open range [0, 1) -/// -/// This method has precisely two template parameters. To fix the output type, -/// use the syntax `uniform01::(rng)`. -pub fn uniform01, R: Rng+?Sized>(rng: &mut R) -> T { - T::rand(rng, Uniform01) -} - /// Sample a `char`, uniformly distributed over all Unicode scalar values, /// i.e. all code points in the range `0...0x10_FFFF`, except for the range /// `0xD800...0xDFFF` (the surrogate code points). This includes @@ -232,7 +224,8 @@ macro_rules! float_impls { // the precision of f64/f32 at 1.0), so that small // numbers are larger than 0, but large numbers // aren't pushed to/above 1. - uniform01::<$ty, _>(rng) + 0.25 / $scale_name + let x: $ty = Rand::rand(rng, Uniform01); + x + 0.25 / $scale_name } } impl Distribution<$ty> for Closed01 { @@ -240,7 +233,8 @@ macro_rules! float_impls { fn sample(&self, rng: &mut R) -> $ty { // rescale so that 1.0 - epsilon becomes 1.0 // precisely. - uniform01::<$ty, _>(rng) * $scale_name / ($scale_name - 1.0) + let x: $ty = Rand::rand(rng, Uniform01); + x * $scale_name / ($scale_name - 1.0) } } } @@ -252,9 +246,8 @@ float_impls! { SCALE_F32, f32, 24 } #[cfg(test)] mod tests { use {Rng, thread_rng, iter}; - use distributions::{Rand, uniform, Uniform, Open01, Closed01}; + use distributions::{Rand, uniform, Uniform, Uniform01, Open01, Closed01}; use distributions::uniform::{codepoint, ascii_word_char}; - use distributions::{uniform01}; #[test] fn test_integers() { @@ -307,8 +300,8 @@ mod tests { #[test] fn test_f64() { let mut r = thread_rng(); - let a: f64 = uniform01(&mut r); - let b = uniform01::(&mut r); + let a: f64 = Rand::rand(&mut r, Uniform01); + let b = f64::rand(&mut r, Uniform01); assert!(0.0 <= a && a < 1.0); assert!(0.0 <= b && b < 1.0); } @@ -318,8 +311,8 @@ mod tests { // FIXME: this message and test predates this repo; message suggests // test is supposed to be ==; using != is pretty useless // the test for exact equality is correct here. - assert!(uniform01::(&mut ConstantRng(0xffff_ffff)) != 1.0); - assert!(uniform01::(&mut ConstantRng(0xffff_ffff_ffff_ffff)) != 1.0); + assert!(f64::rand(&mut ConstantRng(0xffff_ffff), Uniform01) != 1.0); + assert!(f64::rand(&mut ConstantRng(0xffff_ffff_ffff_ffff), Uniform01) != 1.0); } #[test] diff --git a/src/lib.rs b/src/lib.rs index dcb06c89d17..566fbeec056 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -76,21 +76,20 @@ //! # Examples //! //! ```rust -//! use rand::distributions::uniform; +//! use rand::random; //! -//! let mut rng = rand::thread_rng(); -//! if uniform(&mut rng) { // random bool -//! let a: i32 = uniform(&mut rng); -//! let b: u32 = uniform(&mut rng); +//! if random() { // random bool +//! let a: i32 = random(); +//! let b: u32 = random(); //! println!("i32: {}, u32: {}", a, b) //! } //! ``` //! //! ```rust //! use rand::thread_rng; -//! use rand::distributions::{uniform, uniform01}; +//! use rand::distributions::{Rand, Uniform, Uniform01}; //! let mut rng = thread_rng(); -//! let tuple: (f64, u8) = (uniform01(&mut rng), uniform(&mut rng)); +//! let tuple = (f64::rand(&mut rng, Uniform01), u8::rand(&mut rng, Uniform)); //! println!("{:?}", tuple) //! ``` //! @@ -112,7 +111,7 @@ //! and multiply this fraction by 4. //! //! ``` -//! use rand::distributions::{Distribution, Range}; +//! use rand::distributions::{Rand, Range}; //! //! fn main() { //! let between = Range::new(-1f64, 1.); @@ -122,8 +121,8 @@ //! let mut in_circle = 0; //! //! for _ in 0..total { -//! let a = between.sample(&mut rng); -//! let b = between.sample(&mut rng); +//! let a = f64::rand(&mut rng, between); +//! let b = f64::rand(&mut rng, between); //! if a*a + b*b <= 1. { //! in_circle += 1; //! } @@ -423,15 +422,14 @@ pub trait SeedableRng: Rng { /// # Example /// /// ```rust - /// use rand::{Rng, SeedableRng}; - /// use rand::prng::IsaacWordRng; - /// use rand::distributions::uniform01; + /// use rand::{Rng, SeedableRng, Rand, Default}; + /// use rand::prng::ChaChaRng; /// /// let seed: &[_] = &[1, 2, 3, 4]; - /// let mut rng: IsaacWordRng = SeedableRng::from_seed(seed); - /// println!("{}", uniform01::(&mut rng)); + /// let mut rng: ChaChaRng = SeedableRng::from_seed(seed); + /// println!("{}", f64::rand(&mut rng, Default)); /// rng.reseed(&[5, 6, 7, 8]); - /// println!("{}", uniform01::(&mut rng)); + /// println!("{}", f64::rand(&mut rng, Default)); /// ``` fn reseed(&mut self, Seed); @@ -440,13 +438,12 @@ pub trait SeedableRng: Rng { /// # Example /// /// ```rust - /// use rand::{Rng, SeedableRng}; + /// use rand::{Rng, SeedableRng, Rand, Default}; /// use rand::prng::ChaChaRng; - /// use rand::distributions::uniform01; /// /// let seed: &[_] = &[1, 2, 3, 4]; /// let mut rng: ChaChaRng = SeedableRng::from_seed(seed); - /// println!("{}", uniform01::(&mut rng)); + /// println!("{}", f64::rand(&mut rng, Default)); /// ``` fn from_seed(seed: Seed) -> Self; } From 228f0f3d50b1637dfbf5c1e0d3b3c247b31fb616 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 1 Aug 2017 11:04:37 +0100 Subject: [PATCH 048/247] Move sequence.rs to sequences/mod.rs and distributions/weighted.rs under sequences/ --- src/distributions/mod.rs | 2 -- src/distributions/uniform.rs | 2 +- src/lib.rs | 9 +++++---- src/{sequence.rs => sequences/mod.rs} | 16 ++++++++++++---- src/{distributions => sequences}/weighted.rs | 2 +- 5 files changed, 19 insertions(+), 12 deletions(-) rename src/{sequence.rs => sequences/mod.rs} (94%) rename src/{distributions => sequences}/weighted.rs (99%) diff --git a/src/distributions/mod.rs b/src/distributions/mod.rs index ae261e60b27..1794ac490be 100644 --- a/src/distributions/mod.rs +++ b/src/distributions/mod.rs @@ -26,7 +26,6 @@ pub use self::range::{range, Range}; pub use self::gamma::{Gamma, ChiSquared, FisherF, StudentT}; pub use self::normal::{Normal, LogNormal}; pub use self::exponential::Exp; -pub use self::weighted::{Weighted, WeightedChoice}; mod default; mod uniform; @@ -34,7 +33,6 @@ pub mod range; pub mod gamma; pub mod normal; pub mod exponential; -mod weighted; /// Return a bool with a 1 in n chance of being true /// diff --git a/src/distributions/uniform.rs b/src/distributions/uniform.rs index 1ece83c445b..07d0c777471 100644 --- a/src/distributions/uniform.rs +++ b/src/distributions/uniform.rs @@ -49,7 +49,7 @@ pub fn codepoint(rng: &mut R) -> char { /// a-z, A-Z and 0-9. #[inline] pub fn ascii_word_char(rng: &mut R) -> char { - use Choose; + use sequences::Choose; const GEN_ASCII_STR_CHARSET: &'static [u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\ abcdefghijklmnopqrstuvwxyz\ diff --git a/src/lib.rs b/src/lib.rs index 566fbeec056..5cf65bc2111 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -155,6 +155,7 @@ //! ``` //! use rand::Rng; //! use rand::distributions::{Distribution, Range, uniform}; +//! use rand::sequences::Choose; //! //! struct SimulationResult { //! win: bool, @@ -185,7 +186,7 @@ //! // where the car is. The game host will never open the door with the car. //! fn game_host_open(car: u32, choice: u32, rng: &mut R) -> u32 { //! let choices = free_doors(&[car, choice]); -//! rand::sample(rng, choices.into_iter(), 1)[0] +//! *choices[..].choose(rng).unwrap() //! } //! //! // Returns the door we switch to, given our current choice and @@ -254,7 +255,6 @@ use std::rc::Rc; pub use read::ReadRng; pub use os::OsRng; -pub use self::sequence::{Choose, sample, Shuffle}; pub use iter::iter; pub use distributions::{Default, Rand}; @@ -265,7 +265,7 @@ pub mod distributions; pub mod iter; pub mod prng; pub mod reseeding; -pub mod sequence; +pub mod sequences; mod read; mod os; @@ -661,8 +661,9 @@ pub fn random_with>(distribution: D) -> T { #[cfg(test)] mod test { - use {Rng, thread_rng, SeedableRng, StdRng, Shuffle, iter}; + use {Rng, thread_rng, SeedableRng, StdRng, iter}; use distributions::{uniform, range, ascii_word_char}; + use sequences::Shuffle; use std::iter::repeat; pub struct MyRng { inner: R } diff --git a/src/sequence.rs b/src/sequences/mod.rs similarity index 94% rename from src/sequence.rs rename to src/sequences/mod.rs index 2fce74e5d72..5276a3313aa 100644 --- a/src/sequence.rs +++ b/src/sequences/mod.rs @@ -13,6 +13,10 @@ use Rng; use distributions::range; +pub use self::weighted::{Weighted, WeightedChoice}; + +mod weighted; + /// This trait implements a `choose` operations on slices and sequences. pub trait Choose { /// Return one element from a sequence. @@ -22,7 +26,8 @@ pub trait Choose { /// # Example /// /// ``` - /// use rand::{thread_rng, Choose}; + /// use rand::thread_rng; + /// use rand::sequences::Choose; /// /// let choices = [1, 2, 4, 8, 16, 32]; /// let mut rng = thread_rng(); @@ -70,7 +75,8 @@ impl Choose for Vec { /// # Example /// /// ```rust -/// use rand::{thread_rng, sample}; +/// use rand::thread_rng; +/// use rand::sequences::sample; /// /// let mut rng = thread_rng(); /// let sample = sample(&mut rng, 1..100, 5); @@ -104,7 +110,8 @@ pub trait Shuffle { /// # Example /// /// ```rust - /// use rand::{thread_rng, Shuffle}; + /// use rand::thread_rng; + /// use rand::sequences::Shuffle; /// /// let mut rng = thread_rng(); /// let mut y = [1, 2, 3]; @@ -136,7 +143,8 @@ impl<'a, T> Shuffle for &'a mut Vec { #[cfg(test)] mod test { - use {Rng, thread_rng, Choose, sample, Shuffle}; + use {Rng, thread_rng}; + use super::{Choose, sample, Shuffle}; #[test] fn test_choose() { diff --git a/src/distributions/weighted.rs b/src/sequences/weighted.rs similarity index 99% rename from src/distributions/weighted.rs rename to src/sequences/weighted.rs index 10d9893596a..0b7cf352d7a 100644 --- a/src/distributions/weighted.rs +++ b/src/sequences/weighted.rs @@ -34,7 +34,7 @@ pub struct Weighted { /// /// ```rust /// use rand::distributions::Distribution; -/// use rand::distributions::{Weighted, WeightedChoice}; +/// use rand::sequences::{Weighted, WeightedChoice}; /// /// let items = vec!(Weighted { weight: 2, item: 'a' }, /// Weighted { weight: 4, item: 'b' }, From 6b0450b3f0012ca1f6521d1ffc0e167dc7234468 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sat, 5 Aug 2017 16:25:06 +0100 Subject: [PATCH 049/247] Extra doc on iter() --- src/iter.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/iter.rs b/src/iter.rs index 282b151a6fa..23bae8c4846 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -28,6 +28,10 @@ pub struct Iter<'a, R: Rng+?Sized+'a> { /// Create an iterator on an `Rng`. /// +/// This does not create a "real" `Iterator` due to lifetime restrictions, but +/// the returned object supports the most important iterator functions, like +/// `take(number)` and `map(closure)`. +/// /// # Example /// /// ``` @@ -42,7 +46,7 @@ pub struct Iter<'a, R: Rng+?Sized+'a> { /// println!("{}", w); /// ``` pub fn iter<'a, R: Rng+?Sized+'a>(rng: &'a mut R) -> Iter<'a, R> { - Iter { rng: rng, len: None } + Iter { rng, len: None } } impl<'a, R: Rng+?Sized+'a> Iter<'a, R> { From 10d6eb3337dffa5a515de82937645eff72834b8d Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sat, 5 Aug 2017 17:10:29 +0100 Subject: [PATCH 050/247] Allow implementation of Range for user types T. This method is a little clumsy but does satisfy the goal. Alternatives should be considered. --- src/distributions/range.rs | 59 +++++++++++++++++++++++++++++++------- 1 file changed, 49 insertions(+), 10 deletions(-) diff --git a/src/distributions/range.rs b/src/distributions/range.rs index 717daaefd77..6d1f34d4556 100644 --- a/src/distributions/range.rs +++ b/src/distributions/range.rs @@ -40,7 +40,7 @@ use distributions::{Distribution, Uniform01, Rand}; /// ``` pub fn range(low: T, high: T, rng: &mut R) -> T { assert!(low < high, "distributions::range called with low >= high"); - Range::new(low, high).sample(rng) + SampleRange::construct_range(low, high).sample(rng) } /// Sample values uniformly between two bounds. @@ -56,6 +56,10 @@ pub fn range(low: T, high: T, rng: & /// including `high`, but this may be very difficult. All the /// primitive integer types satisfy this property, and the float types /// normally satisfy it, but rounding may mean `high` can occur. +/// +/// Internal details of this type are exposed so as to allow users to implement +/// `SampleRange` for their own types. Outside of `SampleRange` implementations, +/// accessing these members should not be necessary. /// /// # Example /// @@ -74,9 +78,16 @@ pub fn range(low: T, high: T, rng: & /// ``` #[derive(Clone, Copy, Debug)] pub struct Range { - low: X, - range: X, - accept_zone: X + /// The `low` of `Range::new(low, high)`. + pub low: X, + /// Usually it is more convenient to store `low` and `range = high - low` + /// internally. Custom implementations of `SampleRange` may however use + /// this differently. + pub range: X, + /// The integer implementation ensures uniform distribution by rejecting + /// parameters outside of a certain "acceptance zone", from `0` to `zone`. + /// Not all implementations use this parameter. + pub zone: X } impl Range { @@ -132,7 +143,7 @@ macro_rules! integer_impl { Range { low: low, range: range as $ty, - accept_zone: zone as $ty + zone: zone as $ty } } @@ -145,7 +156,7 @@ macro_rules! integer_impl { // until we find something that fits into the // region which r.range evenly divides (this will // be uniformly distributed) - if v < r.accept_zone as $unsigned { + if v < r.zone as $unsigned { // and return it, with some adjustments return (w(r.low) + w((v % r.range as $unsigned) as $ty)).0; } @@ -173,7 +184,7 @@ macro_rules! float_impl { Range { low: low, range: high - low, - accept_zone: 0.0 // unused + zone: 0.0 // unused } } fn sample_range(r: &Range<$ty>, rng: &mut R) -> $ty { @@ -189,9 +200,9 @@ float_impl! { f64 } #[cfg(test)] mod tests { - use thread_rng; - use distributions::{Distribution}; - use distributions::{Range, range}; + use {Rng, thread_rng}; + use distributions::{Distribution, Rand, Uniform01}; + use distributions::range::{Range, range, SampleRange}; #[test] fn test_fn_range() { @@ -284,4 +295,32 @@ mod tests { t!(f32, f64) } + #[test] + fn test_custom_range() { + #[derive(Clone, Copy, PartialEq, PartialOrd)] + struct MyF32 { + x: f32, + } + impl SampleRange for MyF32 { + fn construct_range(low: MyF32, high: MyF32) -> Range { + Range { + low: low, + range: MyF32 { x: high.x - low.x }, + zone: MyF32 { x: 0.0f32 } + } + } + fn sample_range(r: &Range, rng: &mut R) -> MyF32 { + let x = f32::rand(rng, Uniform01); + MyF32 { x: r.low.x + r.range.x * x } + } + } + + let (low, high) = (MyF32{ x: 17.0f32 }, MyF32{ x: 22.0f32 }); + let range = Range::new(low, high); + let mut rng = ::test::rng(); + for _ in 0..100 { + let x = MyF32::rand(&mut rng, range); + assert!(low <= x && x < high); + } + } } From 549febba05f633a122bb8b28eed7004f211c6fd3 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sat, 5 Aug 2017 17:58:21 +0100 Subject: [PATCH 051/247] Move PartialOrd bound to SampleRange, not uses --- src/distributions/range.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/distributions/range.rs b/src/distributions/range.rs index 6d1f34d4556..0cd0a20ddf5 100644 --- a/src/distributions/range.rs +++ b/src/distributions/range.rs @@ -38,7 +38,7 @@ use distributions::{Distribution, Uniform01, Rand}; /// let m: f64 = range(-40.0f64, 1.3e5f64, &mut rng); /// println!("{}", m); /// ``` -pub fn range(low: T, high: T, rng: &mut R) -> T { +pub fn range(low: T, high: T, rng: &mut R) -> T { assert!(low < high, "distributions::range called with low >= high"); SampleRange::construct_range(low, high).sample(rng) } @@ -90,7 +90,7 @@ pub struct Range { pub zone: X } -impl Range { +impl Range { /// Create a new `Range` instance that samples uniformly from /// `[low, high)`. Panics if `low >= high`. pub fn new(low: X, high: X) -> Range { @@ -108,7 +108,7 @@ impl Distribution for Range { /// The helper trait for types that have a sensible way to sample /// uniformly between two values. This should not be used directly, /// and is only to facilitate `Range`. -pub trait SampleRange : Sized { +pub trait SampleRange : PartialOrd+Sized { /// Construct the `Range` object that `sample_range` /// requires. This should not ever be called directly, only via /// `Range::new`, which will check that `low < high`, so this From 2d29e2ce852af7248f8eda1ba5aa824612ec6cfe Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sat, 5 Aug 2017 18:36:57 +0100 Subject: [PATCH 052/247] Black-magic alternative to Range supporting custom parameters. Props to @sgrif or this probably wouldn't have happened. https://github.com/rust-lang/rust/issues/20041#issuecomment-320455707 --- src/distributions/mod.rs | 1 + src/distributions/range2.rs | 360 ++++++++++++++++++++++++++++++++++++ 2 files changed, 361 insertions(+) create mode 100644 src/distributions/range2.rs diff --git a/src/distributions/mod.rs b/src/distributions/mod.rs index 1794ac490be..89bdf252c6e 100644 --- a/src/distributions/mod.rs +++ b/src/distributions/mod.rs @@ -30,6 +30,7 @@ pub use self::exponential::Exp; mod default; mod uniform; pub mod range; +pub mod range2; pub mod gamma; pub mod normal; pub mod exponential; diff --git a/src/distributions/range2.rs b/src/distributions/range2.rs new file mode 100644 index 00000000000..a6aee82cc95 --- /dev/null +++ b/src/distributions/range2.rs @@ -0,0 +1,360 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Alternative design for `Range`. +//! +//! TODO: decide whether to replace the old `range` module with this. +//! Advantage: float ranges don't have to store a "zone" parameter. +//! Advantage: custom implementations can store extra parameters. +//! Possible advantage: easier implementations for custom types written in +//! terms of implementations of other types. +//! Disadvantage: complex? +//! +//! This is *almost* like having separate `RangeInt`, `RangeFloat`, +//! etc. (or just `RangeI32`, etc.) types, each implementing `Distribution`, +//! but it adds some magic to support generic `range` and `new_range` methods. + +use std::num::Wrapping as w; + +use Rng; +use distributions::{Distribution, Uniform01, Rand}; + +/// Generate a random value in the range [`low`, `high`). +/// +/// This is a convenience wrapper around `Range`. If this function will be called +/// repeatedly with the same arguments, one should use `Range`, as that will +/// amortize the computations that allow for perfect uniformity. +/// +/// # Panics +/// +/// Panics if `low >= high`. +/// +/// # Example +/// +/// ```rust +/// use rand::distributions::range2::range; +/// +/// let mut rng = rand::thread_rng(); +/// let n: u32 = range(0, 10, &mut rng); +/// println!("{}", n); +/// let m: f64 = range(-40.0f64, 1.3e5f64, &mut rng); +/// println!("{}", m); +/// ``` +pub fn range(low: X, high: X, rng: &mut R) -> X { + assert!(low < high, "distributions::range called with low >= high"); + Range { inner: X::T::new(low, high) }.sample(rng) +} + +/// Convenience function to construct a `Range` +pub fn new_range(low: X, high: X) -> Range { + assert!(low < high, "new_range called with `low >= high`"); + Range { inner: RangeImpl::new(low, high) } +} + +/// Sample values uniformly between two bounds. +/// +/// This gives a uniform distribution (assuming the RNG used to sample +/// it is itself uniform & the `RangeImpl` implementation is correct), +/// even for edge cases like `low = 0u8`, +/// `high = 170u8`, for which a naive modulo operation would return +/// numbers less than 85 with double the probability to those greater +/// than 85. +/// +/// Types should attempt to sample in `[low, high)`, i.e., not +/// including `high`, but this may be very difficult. All the +/// primitive integer types satisfy this property, and the float types +/// normally satisfy it, but rounding may mean `high` can occur. +/// +/// # Example +/// +/// ```rust +/// use rand::distributions::Distribution; +/// use rand::distributions::range2::new_range; +/// +/// fn main() { +/// let between = new_range(10, 10000); +/// let mut rng = rand::thread_rng(); +/// let mut sum = 0; +/// for _ in 0..1000 { +/// sum += between.sample(&mut rng); +/// } +/// println!("{}", sum); +/// } +/// ``` +#[derive(Clone, Copy, Debug)] +pub struct Range { + inner: T, +} + +impl Range { + /// Create a new `Range` instance that samples uniformly from + /// `[low, high)`. Panics if `low >= high`. + pub fn new(low: T::X, high: T::X) -> Range { + assert!(low < high, "Range::new called with `low >= high`"); + Range { inner: RangeImpl::new(low, high) } + } +} + +impl Distribution for Range { + fn sample(&self, rng: &mut R) -> T::X { + self.inner.sample(rng) + } +} + +/// Helper trait for creating implementations of `RangeImpl`. +pub trait SampleRange: PartialOrd+Sized { + type T: RangeImpl; +} + +/// Helper trait handling actual range sampling. +pub trait RangeImpl { + /// The type sampled by this implementation. + type X: PartialOrd; + + /// Construct self. + /// + /// This should not be called directly. `Range::new` asserts that + /// `low < high` before calling this. + fn new(low: Self::X, high: Self::X) -> Self; + + /// Sample a value. + fn sample(&self, rng: &mut R) -> Self::X; +} + +/// Implementation of `RangeImpl` for integer types. +#[derive(Clone, Copy, Debug)] +pub struct RangeInt { + low: X, + range: X, + zone: X, +} + +macro_rules! range_int_impl { + ($ty:ty, $unsigned:ident) => { + impl SampleRange for $ty { + type T = RangeInt<$ty>; + } + + impl RangeImpl for RangeInt<$ty> { + // we play free and fast with unsigned vs signed here + // (when $ty is signed), but that's fine, since the + // contract of this macro is for $ty and $unsigned to be + // "bit-equal", so casting between them is a no-op & a + // bijection. + + type X = $ty; + + fn new(low: Self::X, high: Self::X) -> Self { + let range = (w(high as $unsigned) - w(low as $unsigned)).0; + let unsigned_max: $unsigned = ::std::$unsigned::MAX; + + // this is the largest number that fits into $unsigned + // that `range` divides evenly, so, if we've sampled + // `n` uniformly from this region, then `n % range` is + // uniform in [0, range) + let zone = unsigned_max - unsigned_max % range; + + RangeInt { + low: low, + range: range as $ty, + zone: zone as $ty + } + } + + fn sample(&self, rng: &mut R) -> Self::X { + use $crate::distributions::uniform; + loop { + // rejection sample + let v: $unsigned = uniform(rng); + // until we find something that fits into the + // region which self.range evenly divides (this will + // be uniformly distributed) + if v < self.zone as $unsigned { + // and return it, with some adjustments + return (w(self.low) + w((v % self.range as $unsigned) as $ty)).0; + } + } + } + } + } +} + +range_int_impl! { i8, u8 } +range_int_impl! { i16, u16 } +range_int_impl! { i32, u32 } +range_int_impl! { i64, u64 } +range_int_impl! { isize, usize } +range_int_impl! { u8, u8 } +range_int_impl! { u16, u16 } +range_int_impl! { u32, u32 } +range_int_impl! { u64, u64 } +range_int_impl! { usize, usize } + +/// Implementation of `RangeImpl` for float types. +#[derive(Clone, Copy, Debug)] +pub struct RangeFloat { + low: X, + range: X, +} + +macro_rules! range_float_impl { + ($ty:ty) => { + impl SampleRange for $ty { + type T = RangeFloat<$ty>; + } + + impl RangeImpl for RangeFloat<$ty> { + type X = $ty; + + fn new(low: Self::X, high: Self::X) -> Self { + RangeFloat { + low: low, + range: high - low, + } + } + + fn sample(&self, rng: &mut R) -> Self::X { + let x: $ty = Rand::rand(rng, Uniform01); + self.low + self.range * x + } + } + } +} + +range_float_impl! { f32 } +range_float_impl! { f64 } + +#[cfg(test)] +mod tests { + use {Rng, thread_rng}; + use distributions::Rand; + use distributions::range2::{Range, range, new_range, RangeImpl, RangeFloat}; + + #[test] + fn test_fn_range() { + let mut r = thread_rng(); + for _ in 0..1000 { + let a = range(-3, 42, &mut r); + assert!(a >= -3 && a < 42); + assert_eq!(range(0, 1, &mut r), 0); + assert_eq!(range(-12, -11, &mut r), -12); + } + + for _ in 0..1000 { + let a = range(10, 42, &mut r); + assert!(a >= 10 && a < 42); + assert_eq!(range(0, 1, &mut r), 0); + assert_eq!(range(3_000_000, 3_000_001, &mut r), 3_000_000); + } + } + + #[test] + #[should_panic] + fn test_fn_range_panic_int() { + let mut r = thread_rng(); + range(5, -2, &mut r); + } + + #[test] + #[should_panic] + fn test_fn_range_panic_usize() { + let mut r = thread_rng(); + range(5, 2, &mut r); + } + + #[should_panic] + #[test] + fn test_range_bad_limits_equal() { + new_range(10, 10); + } + #[should_panic] + #[test] + fn test_range_bad_limits_flipped() { + new_range(10, 5); + } + + #[test] + fn test_integers() { + let mut rng = ::test::rng(); + macro_rules! t { + ($($ty:ident),*) => {{ + $( + let v: &[($ty, $ty)] = &[(0, 10), + (10, 127), + (::std::$ty::MIN, ::std::$ty::MAX)]; + for &(low, high) in v.iter() { + let range = new_range(low, high); + for _ in 0..1000 { + let v: $ty = Rand::rand(&mut rng, range); + assert!(low <= v && v < high); + } + } + )* + }} + } + t!(i8, i16, i32, i64, isize, + u8, u16, u32, u64, usize) + } + + #[test] + fn test_floats() { + let mut rng = ::test::rng(); + macro_rules! t { + ($($ty:ty),*) => {{ + $( + let v: &[($ty, $ty)] = &[(0.0, 100.0), + (-1e35, -1e25), + (1e-35, 1e-25), + (-1e35, 1e35)]; + for &(low, high) in v.iter() { + let range = new_range(low, high); + for _ in 0..1000 { + let v: $ty = Rand::rand(&mut rng, range); + assert!(low <= v && v < high); + } + } + )* + }} + } + + t!(f32, f64) + } + + #[test] + fn test_custom_range() { + #[derive(Clone, Copy, PartialEq, PartialOrd)] + struct MyF32 { + x: f32, + } + #[derive(Clone, Copy, Debug)] + struct RangeMyF32 { + inner: RangeFloat, + } + impl RangeImpl for RangeMyF32 { + type X = MyF32; + fn new(low: Self::X, high: Self::X) -> Self { + RangeMyF32 { + inner: RangeFloat::::new(low.x, high.x), + } + } + fn sample(&self, rng: &mut R) -> Self::X { + MyF32 { x: self.inner.sample(rng) } + } + } + + let (low, high) = (MyF32{ x: 17.0f32 }, MyF32{ x: 22.0f32 }); + let range = Range::::new(low, high); + let mut rng = ::test::rng(); + for _ in 0..100 { + let x = MyF32::rand(&mut rng, range); + assert!(low <= x && x < high); + } + } +} From 21de6aec3c841b476f3a2056a205741d780237e0 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sun, 6 Aug 2017 14:07:58 +0100 Subject: [PATCH 053/247] Add no_std support by conditionally disabling many features This provides no source of entropy in no_std mode; possibly this should be fixed? --- Cargo.toml | 4 +++- src/distributions/mod.rs | 15 +++++++++++++-- src/distributions/range.rs | 4 ++-- src/distributions/range2.rs | 4 ++-- src/distributions/uniform.rs | 4 ++-- src/iter.rs | 2 +- src/lib.rs | 33 ++++++++++++++++++++++++++++++--- src/prng/chacha.rs | 2 +- src/prng/isaac.rs | 8 ++++---- src/prng/xorshift.rs | 2 +- src/reseeding.rs | 2 +- src/sequences/mod.rs | 5 +++++ 12 files changed, 65 insertions(+), 20 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1af45a864ff..991e781936e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,8 +14,10 @@ keywords = ["random", "rng"] categories = ["algorithms"] [features] -i128_support = [] +default = ["std"] nightly = ["i128_support"] +std = [] +i128_support = [] [dependencies] libc = "0.2" diff --git a/src/distributions/mod.rs b/src/distributions/mod.rs index 89bdf252c6e..6f60910724a 100644 --- a/src/distributions/mod.rs +++ b/src/distributions/mod.rs @@ -23,18 +23,30 @@ pub use self::default::Default; pub use self::uniform::{uniform, codepoint, ascii_word_char}; pub use self::uniform::{Uniform, Uniform01, Open01, Closed01}; pub use self::range::{range, Range}; + +#[cfg(feature="std")] pub use self::gamma::{Gamma, ChiSquared, FisherF, StudentT}; +#[cfg(feature="std")] pub use self::normal::{Normal, LogNormal}; +#[cfg(feature="std")] pub use self::exponential::Exp; mod default; mod uniform; +#[cfg(feature="std")] +mod ziggurat_tables; + pub mod range; pub mod range2; + +#[cfg(feature="std")] pub mod gamma; +#[cfg(feature="std")] pub mod normal; +#[cfg(feature="std")] pub mod exponential; + /// Return a bool with a 1 in n chance of being true /// /// This uses [`range`] internally, so for repeated uses it would be faster to @@ -71,8 +83,6 @@ impl> Rand for T { } } -mod ziggurat_tables; - /// Sample a random number using the Ziggurat method (specifically the /// ZIGNOR variant from Doornik 2005). Most of the arguments are /// directly from the paper: @@ -88,6 +98,7 @@ mod ziggurat_tables; // the perf improvement (25-50%) is definitely worth the extra code // size from force-inlining. +#[cfg(feature="std")] #[inline(always)] fn ziggurat( rng: &mut R, diff --git a/src/distributions/range.rs b/src/distributions/range.rs index 0cd0a20ddf5..13409885ef8 100644 --- a/src/distributions/range.rs +++ b/src/distributions/range.rs @@ -12,7 +12,7 @@ // this is surprisingly complicated to be both generic & correct -use std::num::Wrapping as w; +use core::num::Wrapping as w; use Rng; use distributions::{Distribution, Uniform01, Rand}; @@ -132,7 +132,7 @@ macro_rules! integer_impl { #[inline] fn construct_range(low: $ty, high: $ty) -> Range<$ty> { let range = (w(high as $unsigned) - w(low as $unsigned)).0; - let unsigned_max: $unsigned = ::std::$unsigned::MAX; + let unsigned_max: $unsigned = ::core::$unsigned::MAX; // this is the largest number that fits into $unsigned // that `range` divides evenly, so, if we've sampled diff --git a/src/distributions/range2.rs b/src/distributions/range2.rs index a6aee82cc95..e70665777d9 100644 --- a/src/distributions/range2.rs +++ b/src/distributions/range2.rs @@ -21,7 +21,7 @@ //! etc. (or just `RangeI32`, etc.) types, each implementing `Distribution`, //! but it adds some magic to support generic `range` and `new_range` methods. -use std::num::Wrapping as w; +use core::num::Wrapping as w; use Rng; use distributions::{Distribution, Uniform01, Rand}; @@ -153,7 +153,7 @@ macro_rules! range_int_impl { fn new(low: Self::X, high: Self::X) -> Self { let range = (w(high as $unsigned) - w(low as $unsigned)).0; - let unsigned_max: $unsigned = ::std::$unsigned::MAX; + let unsigned_max: $unsigned = ::core::$unsigned::MAX; // this is the largest number that fits into $unsigned // that `range` divides evenly, so, if we've sampled diff --git a/src/distributions/uniform.rs b/src/distributions/uniform.rs index 07d0c777471..e5c71466655 100644 --- a/src/distributions/uniform.rs +++ b/src/distributions/uniform.rs @@ -10,8 +10,8 @@ //! Generating uniformly distributed numbers -use std::char; -use std::mem; +use core::char; +use core::mem; use Rng; use distributions::{Distribution, Rand}; diff --git a/src/iter.rs b/src/iter.rs index 23bae8c4846..d26137d0b2c 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -14,7 +14,7 @@ //! `std::iter::Iterator`. Instead, you get the simplified iterators below, //! providing only a subset of functionality. -use std::cmp::min; +use core::cmp::min; use Rng; diff --git a/src/lib.rs b/src/lib.rs index 5cf65bc2111..26b57ae55ce 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -245,20 +245,30 @@ #![deny(missing_debug_implementations)] +#![cfg_attr(not(feature="std"), no_std)] #![cfg_attr(feature = "i128_support", feature(i128_type))] +// We need to use several items from "core" for no_std support. +#[cfg(feature="std")] +extern crate core; +#[cfg(feature="std")] use std::cell::RefCell; -use std::mem; -use std::io; +#[cfg(feature="std")] use std::rc::Rc; +#[cfg(feature="std")] +use core::mem; +#[cfg(feature="std")] pub use read::ReadRng; +#[cfg(feature="std")] pub use os::OsRng; pub use iter::iter; pub use distributions::{Default, Rand}; +#[cfg(feature="std")] use prng::IsaacWordRng; +#[cfg(feature="std")] use prng::XorShiftRng; pub mod distributions; @@ -267,7 +277,9 @@ pub mod prng; pub mod reseeding; pub mod sequences; +#[cfg(feature="std")] mod read; +#[cfg(feature="std")] mod os; /// A random number generator. @@ -387,6 +399,7 @@ impl<'a, R: ?Sized> Rng for &'a mut R where R: Rng { } } +#[cfg(feature="std")] impl Rng for Box where R: Rng { fn next_u8(&mut self) -> u8 { (**self).next_u8() @@ -450,11 +463,13 @@ pub trait SeedableRng: Rng { /// The standard RNG. This is designed to be efficient on the current /// platform. +#[cfg(feature="std")] #[derive(Copy, Clone, Debug)] pub struct StdRng { rng: IsaacWordRng, } +#[cfg(feature="std")] impl StdRng { /// Create a randomly seeded instance of `StdRng`. /// @@ -467,11 +482,12 @@ impl StdRng { /// /// Reading the randomness from the OS may fail, and any error is /// propagated via the `io::Result` return value. - pub fn new() -> io::Result { + pub fn new() -> ::std::io::Result { OsRng::new().map(|mut r| StdRng { rng: IsaacWordRng::new_from_rng(&mut r) }) } } +#[cfg(feature="std")] impl Rng for StdRng { #[inline] fn next_u32(&mut self) -> u32 { @@ -484,6 +500,7 @@ impl Rng for StdRng { } } +#[cfg(feature="std")] impl<'a> SeedableRng<&'a [usize]> for StdRng { fn reseed(&mut self, seed: &'a [usize]) { // the internal RNG can just be seeded from the above @@ -506,6 +523,7 @@ impl<'a> SeedableRng<&'a [usize]> for StdRng { /// This will read randomness from the operating system to seed the /// generator. // TODO: is this method useful? +#[cfg(feature="std")] pub fn weak_rng() -> XorShiftRng { // TODO: should this seed from `thread_rng()`? match OsRng::new() { @@ -515,9 +533,11 @@ pub fn weak_rng() -> XorShiftRng { } /// Controls how the thread-local RNG is reseeded. +#[cfg(feature="std")] #[derive(Debug)] struct ThreadRngReseeder; +#[cfg(feature="std")] impl reseeding::Reseeder for ThreadRngReseeder { fn reseed(&mut self, rng: &mut StdRng) { *rng = match StdRng::new() { @@ -526,10 +546,13 @@ impl reseeding::Reseeder for ThreadRngReseeder { } } } +#[cfg(feature="std")] const THREAD_RNG_RESEED_THRESHOLD: u64 = 32_768; +#[cfg(feature="std")] type ThreadRngInner = reseeding::ReseedingRng; /// The thread-local RNG. +#[cfg(feature="std")] #[derive(Clone, Debug)] pub struct ThreadRng { rng: Rc>, @@ -546,6 +569,7 @@ pub struct ThreadRng { /// if the operating system random number generator is rigged to give /// the same sequence always. If absolute consistency is required, /// explicitly select an RNG, e.g. `IsaacRng` or `Isaac64Rng`. +#[cfg(feature="std")] pub fn thread_rng() -> ThreadRng { // used to make space in TLS for a random number generator thread_local!(static THREAD_RNG_KEY: Rc> = { @@ -562,6 +586,7 @@ pub fn thread_rng() -> ThreadRng { ThreadRng { rng: THREAD_RNG_KEY.with(|t| t.clone()) } } +#[cfg(feature="std")] impl Rng for ThreadRng { fn next_u32(&mut self) -> u32 { self.rng.borrow_mut().next_u32() @@ -620,6 +645,7 @@ impl Rng for ThreadRng { /// /// Note that the above example uses `SampleDefault` which is a zero-sized /// marker type. +#[cfg(feature="std")] #[inline] pub fn random>() -> T { T::rand(&mut thread_rng(), Default) @@ -653,6 +679,7 @@ pub fn random>() -> T { /// // Do this bit many times: /// let v = f64::rand(&mut rng, range); /// ``` +#[cfg(feature="std")] #[inline] pub fn random_with>(distribution: D) -> T { T::rand(&mut thread_rng(), distribution) diff --git a/src/prng/chacha.rs b/src/prng/chacha.rs index bdb39462fbc..e66c4dc9145 100644 --- a/src/prng/chacha.rs +++ b/src/prng/chacha.rs @@ -10,7 +10,7 @@ //! The ChaCha random number generator. -use std::num::Wrapping as w; +use core::num::Wrapping as w; use {Rng, SeedableRng}; #[allow(bad_style)] diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index 5d05f256b64..c3abd141225 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -12,10 +12,10 @@ #![allow(non_camel_case_types)] -use std::slice; -use std::iter::repeat; -use std::num::Wrapping as w; -use std::fmt; +use core::slice; +use core::iter::repeat; +use core::num::Wrapping as w; +use core::fmt; use {Rng, SeedableRng}; diff --git a/src/prng/xorshift.rs b/src/prng/xorshift.rs index aaa4246e988..51431c5b810 100644 --- a/src/prng/xorshift.rs +++ b/src/prng/xorshift.rs @@ -10,7 +10,7 @@ //! Xorshift generators -use std::num::Wrapping as w; +use core::num::Wrapping as w; use {Rng, SeedableRng}; /// An Xorshift[1] random number diff --git a/src/reseeding.rs b/src/reseeding.rs index 052567e46c4..e17d8383920 100644 --- a/src/reseeding.rs +++ b/src/reseeding.rs @@ -11,7 +11,7 @@ //! A wrapper around another RNG that reseeds it after it //! generates a certain number of random bytes. -use std::default::Default; +use core::default::Default; use {Rng, SeedableRng}; diff --git a/src/sequences/mod.rs b/src/sequences/mod.rs index 5276a3313aa..3bd35fa0cd7 100644 --- a/src/sequences/mod.rs +++ b/src/sequences/mod.rs @@ -13,8 +13,10 @@ use Rng; use distributions::range; +#[cfg(feature="std")] pub use self::weighted::{Weighted, WeightedChoice}; +#[cfg(feature="std")] mod weighted; /// This trait implements a `choose` operations on slices and sequences. @@ -58,6 +60,7 @@ impl<'a, T> Choose<&'a mut T> for &'a mut [T] { } } +#[cfg(feature="std")] impl Choose for Vec { fn choose(mut self, rng: &mut R) -> Option { if self.is_empty() { @@ -82,6 +85,7 @@ impl Choose for Vec { /// let sample = sample(&mut rng, 1..100, 5); /// println!("{:?}", sample); /// ``` +#[cfg(feature="std")] pub fn sample(rng: &mut R, iterable: I, amount: usize) -> Vec where I: IntoIterator, R: Rng, @@ -135,6 +139,7 @@ impl<'a, T> Shuffle for &'a mut [T] { } } +#[cfg(feature="std")] impl<'a, T> Shuffle for &'a mut Vec { fn shuffle(self, rng: &mut R) { (self[..]).shuffle(rng) From 212e04e1e2eb7e06c8c70bf2c63acfa9945de03c Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sun, 6 Aug 2017 15:35:51 +0100 Subject: [PATCH 054/247] Integer ranges: reject fewer samples --- src/distributions/range.rs | 26 +++++++++++++++----------- src/distributions/range2.rs | 26 +++++++++++++++----------- 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/src/distributions/range.rs b/src/distributions/range.rs index 13409885ef8..de7618edf90 100644 --- a/src/distributions/range.rs +++ b/src/distributions/range.rs @@ -134,14 +134,21 @@ macro_rules! integer_impl { let range = (w(high as $unsigned) - w(low as $unsigned)).0; let unsigned_max: $unsigned = ::core::$unsigned::MAX; - // this is the largest number that fits into $unsigned - // that `range` divides evenly, so, if we've sampled - // `n` uniformly from this region, then `n % range` is - // uniform in [0, range) - let zone = unsigned_max - unsigned_max % range; + // We want to calculate type_range % range where type_range is + // pow(2, n_bits($ty)), but we can't represent type_range. + // (type_range - range) % range is equivalent, since we know + // type_range > range. Since range >= 1, + // type_range - range = (unsigned_max - range) + 1. + let ignore_zone = ((unsigned_max - range) + 1) % range; + // We want to sample from the zone + // [0, (type_range - ignore_zone)) + // however, ignore_zone may be zero. Instead use a closed range + // from zero to: + let zone = unsigned_max - ignore_zone; Range { low: low, + // These are really $unsigned values, but store as $ty: range: range as $ty, zone: zone as $ty } @@ -151,13 +158,10 @@ macro_rules! integer_impl { fn sample_range(r: &Range<$ty>, rng: &mut R) -> $ty { use $crate::distributions::uniform; loop { - // rejection sample let v: $unsigned = uniform(rng); - // until we find something that fits into the - // region which r.range evenly divides (this will - // be uniformly distributed) - if v < r.zone as $unsigned { - // and return it, with some adjustments + // Reject samples not between 0 and zone: + if v <= r.zone as $unsigned { + // Adjustment sample for range and low value: return (w(r.low) + w((v % r.range as $unsigned) as $ty)).0; } } diff --git a/src/distributions/range2.rs b/src/distributions/range2.rs index e70665777d9..2f12d0e9203 100644 --- a/src/distributions/range2.rs +++ b/src/distributions/range2.rs @@ -155,14 +155,21 @@ macro_rules! range_int_impl { let range = (w(high as $unsigned) - w(low as $unsigned)).0; let unsigned_max: $unsigned = ::core::$unsigned::MAX; - // this is the largest number that fits into $unsigned - // that `range` divides evenly, so, if we've sampled - // `n` uniformly from this region, then `n % range` is - // uniform in [0, range) - let zone = unsigned_max - unsigned_max % range; + // We want to calculate type_range % range where type_range is + // pow(2, n_bits($ty)), but we can't represent type_range. + // (type_range - range) % range is equivalent, since we know + // type_range > range. Since range >= 1, + // type_range - range = (unsigned_max - range) + 1. + let ignore_zone = ((unsigned_max - range) + 1) % range; + // We want to sample from the zone + // [0, (type_range - ignore_zone)) + // however, ignore_zone may be zero. Instead use a closed range + // from zero to: + let zone = unsigned_max - ignore_zone; RangeInt { low: low, + // These are really $unsigned values, but store as $ty: range: range as $ty, zone: zone as $ty } @@ -171,13 +178,10 @@ macro_rules! range_int_impl { fn sample(&self, rng: &mut R) -> Self::X { use $crate::distributions::uniform; loop { - // rejection sample let v: $unsigned = uniform(rng); - // until we find something that fits into the - // region which self.range evenly divides (this will - // be uniformly distributed) - if v < self.zone as $unsigned { - // and return it, with some adjustments + // Reject samples not between 0 and zone: + if v <= self.zone as $unsigned { + // Adjustment sample for range and low value: return (w(self.low) + w((v % self.range as $unsigned) as $ty)).0; } } From e386895add99a2759d580148aab350a88fbb5cc0 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 7 Aug 2017 08:54:58 +0100 Subject: [PATCH 055/247] Add SimpleRand (example) and improve Rand doc --- src/distributions/mod.rs | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/distributions/mod.rs b/src/distributions/mod.rs index 6f60910724a..0a65413315d 100644 --- a/src/distributions/mod.rs +++ b/src/distributions/mod.rs @@ -73,16 +73,55 @@ pub trait Distribution { } /// Generic trait for sampling random values from some distribution +/// +/// # Example +/// ```rust +/// use rand::distributions::{Rand, Default, Range}; +/// +/// let mut rng = rand::thread_rng(); +/// println!("Random byte: {}", u8::rand(&mut rng, Default)); +/// println!("Random range: {}", i32::rand(&mut rng, Range::new(-99, 100))); +/// ``` pub trait Rand { fn rand(rng: &mut R, dist: Dist) -> Self; } impl> Rand for T { + /// Generate a random value of the given type, according to the specified + /// distribution. + /// + /// The distributions `Default` (or `Uniform` and `Uniform01`) and `Range` + /// should cover most simpler usages; `Normal`, `LogNormal`, `Exp`, `Gamma` + /// and a few others are also available. fn rand(rng: &mut R, dist: D) -> Self { dist.sample(rng) } } +/// Simpler version of `Rand`, without support for alternative distributions. +/// +/// TODO: decide which version of `Rand` to keep. If this one, rename struct to +/// `Rand` and function to `rand`. +/// +/// # Example +/// ```rust +/// use rand::distributions::SimpleRand; +/// +/// let mut rng = rand::thread_rng(); +/// println!("Random byte: {}", u8::simple_rand(&mut rng)); +/// ``` +pub trait SimpleRand { + /// Generate a random value of the given type, using the `Default` + /// distribution. + fn simple_rand(rng: &mut R) -> Self; +} + +impl SimpleRand for T where Default: Distribution { + fn simple_rand(rng: &mut R) -> Self { + Default.sample(rng) + } +} + /// Sample a random number using the Ziggurat method (specifically the /// ZIGNOR variant from Doornik 2005). Most of the arguments are /// directly from the paper: From 88f73e0cf41acf3ce156dc037beb4a2d553d36cf Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 7 Aug 2017 11:01:08 +0100 Subject: [PATCH 056/247] Fix Rand doc and rename dist var to distr --- src/distributions/mod.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/distributions/mod.rs b/src/distributions/mod.rs index 0a65413315d..7144216a708 100644 --- a/src/distributions/mod.rs +++ b/src/distributions/mod.rs @@ -13,7 +13,7 @@ //! A distribution may have internal state describing the distribution of //! generated values; for example `Range` needs to know its upper and lower //! bounds. Distributions use the `Distribution` trait to yield values: call -//! `dist.sample(&mut rng)` to get a random variable. +//! `distr.sample(&mut rng)` to get a random variable. //! //! TODO: is it worth exposing both submodules and re-exporting their members? @@ -82,19 +82,19 @@ pub trait Distribution { /// println!("Random byte: {}", u8::rand(&mut rng, Default)); /// println!("Random range: {}", i32::rand(&mut rng, Range::new(-99, 100))); /// ``` -pub trait Rand { - fn rand(rng: &mut R, dist: Dist) -> Self; -} - -impl> Rand for T { +pub trait Rand { /// Generate a random value of the given type, according to the specified /// distribution. /// /// The distributions `Default` (or `Uniform` and `Uniform01`) and `Range` /// should cover most simpler usages; `Normal`, `LogNormal`, `Exp`, `Gamma` /// and a few others are also available. - fn rand(rng: &mut R, dist: D) -> Self { - dist.sample(rng) + fn rand(rng: &mut R, distr: D) -> Self; +} + +impl> Rand for T { + fn rand(rng: &mut R, distr: D) -> Self { + distr.sample(rng) } } From 3a4ace552dc684e3705280a3f116f6549a1f4e10 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Fri, 11 Aug 2017 11:41:27 +0100 Subject: [PATCH 057/247] Fix benchmarks; add ChaCha and LogNormal benchmarks --- benches/bench.rs | 15 +++++++++++++-- benches/distributions/normal.rs | 15 ++++++++++++++- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/benches/bench.rs b/benches/bench.rs index 960f9c2b959..1e054dc3a41 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -10,8 +10,8 @@ mod distributions; use std::mem::size_of; use test::{black_box, Bencher}; use rand::{StdRng, OsRng, weak_rng}; -use rand::prng::{XorShiftRng, IsaacRng, Isaac64Rng}; -use rand::{sample, Shuffle}; +use rand::prng::{XorShiftRng, IsaacRng, Isaac64Rng, ChaChaRng}; +use rand::sequences::{sample, Shuffle}; use rand::distributions::{Rand, Uniform, Uniform01}; #[bench] @@ -47,6 +47,17 @@ fn rand_isaac64(b: &mut Bencher) { b.bytes = size_of::() as u64 * RAND_BENCH_N; } +#[bench] +fn rand_chacha(b: &mut Bencher) { + let mut rng = ChaChaRng::new_from_rng(&mut OsRng::new().unwrap()); + b.iter(|| { + for _ in 0..RAND_BENCH_N { + black_box(usize::rand(&mut rng, Uniform)); + } + }); + b.bytes = size_of::() as u64 * RAND_BENCH_N; +} + #[bench] fn rand_std(b: &mut Bencher) { let mut rng = StdRng::new().unwrap(); diff --git a/benches/distributions/normal.rs b/benches/distributions/normal.rs index 509f954857e..4837f631bbe 100644 --- a/benches/distributions/normal.rs +++ b/benches/distributions/normal.rs @@ -2,7 +2,7 @@ use std::mem::size_of; use test::Bencher; use rand; use rand::distributions::Distribution; -use rand::distributions::normal::Normal; +use rand::distributions::normal::{Normal, LogNormal}; #[bench] fn rand_normal(b: &mut Bencher) { @@ -16,3 +16,16 @@ fn rand_normal(b: &mut Bencher) { }); b.bytes = size_of::() as u64 * ::RAND_BENCH_N; } + +#[bench] +fn rand_log_normal(b: &mut Bencher) { + let mut rng = rand::weak_rng(); + let log_normal = LogNormal::new(-2.71828, 3.14159); + + b.iter(|| { + for _ in 0..::RAND_BENCH_N { + log_normal.sample(&mut rng); + } + }); + b.bytes = size_of::() as u64 * ::RAND_BENCH_N; +} From 96c2a4242fbf5f6b104547e672fb0428bdb9d216 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Fri, 11 Aug 2017 12:23:41 +0100 Subject: [PATCH 058/247] Revise benchmarks into three separate ones; add comparators --- benches/bench.rs | 110 --------------------------- benches/distributions.rs | 96 +++++++++++++++++++++++ benches/distributions/exponential.rs | 18 ----- benches/distributions/gamma.rs | 31 -------- benches/distributions/mod.rs | 3 - benches/distributions/normal.rs | 31 -------- benches/misc.rs | 74 ++++++++++++++++++ 7 files changed, 170 insertions(+), 193 deletions(-) delete mode 100644 benches/bench.rs create mode 100644 benches/distributions.rs delete mode 100644 benches/distributions/exponential.rs delete mode 100644 benches/distributions/gamma.rs delete mode 100644 benches/distributions/mod.rs delete mode 100644 benches/distributions/normal.rs create mode 100644 benches/misc.rs diff --git a/benches/bench.rs b/benches/bench.rs deleted file mode 100644 index 1e054dc3a41..00000000000 --- a/benches/bench.rs +++ /dev/null @@ -1,110 +0,0 @@ -#![feature(test)] - -extern crate test; -extern crate rand; - -const RAND_BENCH_N: u64 = 1000; - -mod distributions; - -use std::mem::size_of; -use test::{black_box, Bencher}; -use rand::{StdRng, OsRng, weak_rng}; -use rand::prng::{XorShiftRng, IsaacRng, Isaac64Rng, ChaChaRng}; -use rand::sequences::{sample, Shuffle}; -use rand::distributions::{Rand, Uniform, Uniform01}; - -#[bench] -fn rand_xorshift(b: &mut Bencher) { - let mut rng = XorShiftRng::new_from_rng(&mut OsRng::new().unwrap()); - b.iter(|| { - for _ in 0..RAND_BENCH_N { - black_box(usize::rand(&mut rng, Uniform)); - } - }); - b.bytes = size_of::() as u64 * RAND_BENCH_N; -} - -#[bench] -fn rand_isaac(b: &mut Bencher) { - let mut rng = IsaacRng::new_from_rng(&mut OsRng::new().unwrap()); - b.iter(|| { - for _ in 0..RAND_BENCH_N { - black_box(usize::rand(&mut rng, Uniform)); - } - }); - b.bytes = size_of::() as u64 * RAND_BENCH_N; -} - -#[bench] -fn rand_isaac64(b: &mut Bencher) { - let mut rng = Isaac64Rng::new_from_rng(&mut OsRng::new().unwrap()); - b.iter(|| { - for _ in 0..RAND_BENCH_N { - black_box(usize::rand(&mut rng, Uniform)); - } - }); - b.bytes = size_of::() as u64 * RAND_BENCH_N; -} - -#[bench] -fn rand_chacha(b: &mut Bencher) { - let mut rng = ChaChaRng::new_from_rng(&mut OsRng::new().unwrap()); - b.iter(|| { - for _ in 0..RAND_BENCH_N { - black_box(usize::rand(&mut rng, Uniform)); - } - }); - b.bytes = size_of::() as u64 * RAND_BENCH_N; -} - -#[bench] -fn rand_std(b: &mut Bencher) { - let mut rng = StdRng::new().unwrap(); - b.iter(|| { - for _ in 0..RAND_BENCH_N { - black_box(usize::rand(&mut rng, Uniform)); - } - }); - b.bytes = size_of::() as u64 * RAND_BENCH_N; -} - -#[bench] -fn rand_f32(b: &mut Bencher) { - let mut rng = StdRng::new().unwrap(); - b.iter(|| { - for _ in 0..RAND_BENCH_N { - black_box(f32::rand(&mut rng, Uniform01)); - } - }); - b.bytes = size_of::() as u64 * RAND_BENCH_N; -} - -#[bench] -fn rand_f64(b: &mut Bencher) { - let mut rng = StdRng::new().unwrap(); - b.iter(|| { - for _ in 0..RAND_BENCH_N { - black_box(f64::rand(&mut rng, Uniform01)); - } - }); - b.bytes = size_of::() as u64 * RAND_BENCH_N; -} - -#[bench] -fn rand_shuffle_100(b: &mut Bencher) { - let mut rng = weak_rng(); - let x : &mut [usize] = &mut [1; 100]; - b.iter(|| { - x.shuffle(&mut rng); - }) -} - -#[bench] -fn rand_sample_10_of_100(b: &mut Bencher) { - let mut rng = weak_rng(); - let x : &[usize] = &[1; 100]; - b.iter(|| { - sample(&mut rng, x, 10); - }) -} diff --git a/benches/distributions.rs b/benches/distributions.rs new file mode 100644 index 00000000000..a141e0adfcf --- /dev/null +++ b/benches/distributions.rs @@ -0,0 +1,96 @@ +#![feature(test)] + +extern crate test; +extern crate rand; + +const RAND_BENCH_N: u64 = 1000; + +use std::mem::size_of; +use test::Bencher; + +use rand::{weak_rng, Default, Rand}; +use rand::distributions::Distribution; +use rand::distributions::exponential::Exp; +use rand::distributions::normal::{Normal, LogNormal}; +use rand::distributions::gamma::Gamma; + + +#[bench] +fn distr_baseline(b: &mut Bencher) { + let mut rng = weak_rng(); + + b.iter(|| { + for _ in 0..::RAND_BENCH_N { + f64::rand(&mut rng, Default); + } + }); + b.bytes = size_of::() as u64 * ::RAND_BENCH_N; +} + + +#[bench] +fn distr_exp(b: &mut Bencher) { + let mut rng = weak_rng(); + let distr = Exp::new(2.71828 * 3.14159); + + b.iter(|| { + for _ in 0..::RAND_BENCH_N { + distr.sample(&mut rng); + } + }); + b.bytes = size_of::() as u64 * ::RAND_BENCH_N; +} + + +#[bench] +fn distr_normal(b: &mut Bencher) { + let mut rng = weak_rng(); + let distr = Normal::new(-2.71828, 3.14159); + + b.iter(|| { + for _ in 0..::RAND_BENCH_N { + distr.sample(&mut rng); + } + }); + b.bytes = size_of::() as u64 * ::RAND_BENCH_N; +} + +#[bench] +fn distr_log_normal(b: &mut Bencher) { + let mut rng = weak_rng(); + let distr = LogNormal::new(-2.71828, 3.14159); + + b.iter(|| { + for _ in 0..::RAND_BENCH_N { + distr.sample(&mut rng); + } + }); + b.bytes = size_of::() as u64 * ::RAND_BENCH_N; +} + + +#[bench] +fn distr_gamma_large_shape(b: &mut Bencher) { + let mut rng = weak_rng(); + let distr = Gamma::new(10., 1.0); + + b.iter(|| { + for _ in 0..::RAND_BENCH_N { + distr.sample(&mut rng); + } + }); + b.bytes = size_of::() as u64 * ::RAND_BENCH_N; +} + +#[bench] +fn distr_gamma_small_shape(b: &mut Bencher) { + let mut rng = weak_rng(); + let distr = Gamma::new(0.1, 1.0); + + b.iter(|| { + for _ in 0..::RAND_BENCH_N { + distr.sample(&mut rng); + } + }); + b.bytes = size_of::() as u64 * ::RAND_BENCH_N; +} diff --git a/benches/distributions/exponential.rs b/benches/distributions/exponential.rs deleted file mode 100644 index 57c581a810d..00000000000 --- a/benches/distributions/exponential.rs +++ /dev/null @@ -1,18 +0,0 @@ -use std::mem::size_of; -use test::Bencher; -use rand; -use rand::distributions::exponential::Exp; -use rand::distributions::Distribution; - -#[bench] -fn rand_exp(b: &mut Bencher) { - let mut rng = rand::weak_rng(); - let exp = Exp::new(2.71828 * 3.14159); - - b.iter(|| { - for _ in 0..::RAND_BENCH_N { - exp.sample(&mut rng); - } - }); - b.bytes = size_of::() as u64 * ::RAND_BENCH_N; -} diff --git a/benches/distributions/gamma.rs b/benches/distributions/gamma.rs deleted file mode 100644 index 47c501f35fd..00000000000 --- a/benches/distributions/gamma.rs +++ /dev/null @@ -1,31 +0,0 @@ -use std::mem::size_of; -use test::Bencher; -use rand; -use rand::distributions::Distribution; -use rand::distributions::gamma::Gamma; - -#[bench] -fn bench_gamma_large_shape(b: &mut Bencher) { - let gamma = Gamma::new(10., 1.0); - let mut rng = rand::weak_rng(); - - b.iter(|| { - for _ in 0..::RAND_BENCH_N { - gamma.sample(&mut rng); - } - }); - b.bytes = size_of::() as u64 * ::RAND_BENCH_N; -} - -#[bench] -fn bench_gamma_small_shape(b: &mut Bencher) { - let gamma = Gamma::new(0.1, 1.0); - let mut rng = rand::weak_rng(); - - b.iter(|| { - for _ in 0..::RAND_BENCH_N { - gamma.sample(&mut rng); - } - }); - b.bytes = size_of::() as u64 * ::RAND_BENCH_N; -} diff --git a/benches/distributions/mod.rs b/benches/distributions/mod.rs deleted file mode 100644 index 49f6bd9c06c..00000000000 --- a/benches/distributions/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod exponential; -mod normal; -mod gamma; diff --git a/benches/distributions/normal.rs b/benches/distributions/normal.rs deleted file mode 100644 index 4837f631bbe..00000000000 --- a/benches/distributions/normal.rs +++ /dev/null @@ -1,31 +0,0 @@ -use std::mem::size_of; -use test::Bencher; -use rand; -use rand::distributions::Distribution; -use rand::distributions::normal::{Normal, LogNormal}; - -#[bench] -fn rand_normal(b: &mut Bencher) { - let mut rng = rand::weak_rng(); - let normal = Normal::new(-2.71828, 3.14159); - - b.iter(|| { - for _ in 0..::RAND_BENCH_N { - normal.sample(&mut rng); - } - }); - b.bytes = size_of::() as u64 * ::RAND_BENCH_N; -} - -#[bench] -fn rand_log_normal(b: &mut Bencher) { - let mut rng = rand::weak_rng(); - let log_normal = LogNormal::new(-2.71828, 3.14159); - - b.iter(|| { - for _ in 0..::RAND_BENCH_N { - log_normal.sample(&mut rng); - } - }); - b.bytes = size_of::() as u64 * ::RAND_BENCH_N; -} diff --git a/benches/misc.rs b/benches/misc.rs new file mode 100644 index 00000000000..6c83023782e --- /dev/null +++ b/benches/misc.rs @@ -0,0 +1,74 @@ +#![feature(test)] + +extern crate test; +extern crate rand; + +const RAND_BENCH_N: u64 = 1000; + +use std::mem::size_of; +use test::{black_box, Bencher}; +use rand::{StdRng, weak_rng}; +use rand::sequences::{sample, Shuffle}; +use rand::distributions::{Rand, Uniform, Uniform01}; + +#[bench] +fn misc_baseline_32(b: &mut Bencher) { + let mut rng = StdRng::new().unwrap(); + b.iter(|| { + for _ in 0..RAND_BENCH_N { + black_box(u32::rand(&mut rng, Uniform)); + } + }); + b.bytes = size_of::() as u64 * RAND_BENCH_N; +} + +#[bench] +fn misc_baseline_64(b: &mut Bencher) { + let mut rng = StdRng::new().unwrap(); + b.iter(|| { + for _ in 0..RAND_BENCH_N { + black_box(u64::rand(&mut rng, Uniform)); + } + }); + b.bytes = size_of::() as u64 * RAND_BENCH_N; +} + +#[bench] +fn misc_convert_f32(b: &mut Bencher) { + let mut rng = StdRng::new().unwrap(); + b.iter(|| { + for _ in 0..RAND_BENCH_N { + black_box(f32::rand(&mut rng, Uniform01)); + } + }); + b.bytes = size_of::() as u64 * RAND_BENCH_N; +} + +#[bench] +fn misc_convert_f64(b: &mut Bencher) { + let mut rng = StdRng::new().unwrap(); + b.iter(|| { + for _ in 0..RAND_BENCH_N { + black_box(f64::rand(&mut rng, Uniform01)); + } + }); + b.bytes = size_of::() as u64 * RAND_BENCH_N; +} + +#[bench] +fn misc_shuffle_100(b: &mut Bencher) { + let mut rng = weak_rng(); + let x : &mut [usize] = &mut [1; 100]; + b.iter(|| { + x.shuffle(&mut rng); + }) +} + +#[bench] +fn misc_sample_10_of_100(b: &mut Bencher) { + let mut rng = weak_rng(); + let x : &[usize] = &[1; 100]; + b.iter(|| { + sample(&mut rng, x, 10); + }) +} From dc8c1f4622d27abb34f2072a998e572c8c401be0 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Fri, 11 Aug 2017 16:25:28 +0100 Subject: [PATCH 059/247] Benches: add missing file from prev. commit --- benches/generators.rs | 78 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 benches/generators.rs diff --git a/benches/generators.rs b/benches/generators.rs new file mode 100644 index 00000000000..b2db489f897 --- /dev/null +++ b/benches/generators.rs @@ -0,0 +1,78 @@ +#![feature(test)] + +extern crate test; +extern crate rand; + +const RAND_BENCH_N: u64 = 1000; + +use std::mem::size_of; +use test::{black_box, Bencher}; + +use rand::{StdRng, OsRng, Rand, Default}; +use rand::prng::{XorShiftRng, IsaacRng, Isaac64Rng, ChaChaRng}; + +#[bench] +fn gen_usize_xorshift(b: &mut Bencher) { + let mut rng = XorShiftRng::new_from_rng(&mut OsRng::new().unwrap()); + b.iter(|| { + for _ in 0..RAND_BENCH_N { + black_box(usize::rand(&mut rng, Default)); + } + }); + b.bytes = size_of::() as u64 * RAND_BENCH_N; +} + +#[bench] +fn gen_usize_isaac(b: &mut Bencher) { + let mut rng = IsaacRng::new_from_rng(&mut OsRng::new().unwrap()); + b.iter(|| { + for _ in 0..RAND_BENCH_N { + black_box(usize::rand(&mut rng, Default)); + } + }); + b.bytes = size_of::() as u64 * RAND_BENCH_N; +} + +#[bench] +fn gen_usize_isaac64(b: &mut Bencher) { + let mut rng = Isaac64Rng::new_from_rng(&mut OsRng::new().unwrap()); + b.iter(|| { + for _ in 0..RAND_BENCH_N { + black_box(usize::rand(&mut rng, Default)); + } + }); + b.bytes = size_of::() as u64 * RAND_BENCH_N; +} + +#[bench] +fn gen_usize_chacha(b: &mut Bencher) { + let mut rng = ChaChaRng::new_from_rng(&mut OsRng::new().unwrap()); + b.iter(|| { + for _ in 0..RAND_BENCH_N { + black_box(usize::rand(&mut rng, Default)); + } + }); + b.bytes = size_of::() as u64 * RAND_BENCH_N; +} + +#[bench] +fn gen_usize_std(b: &mut Bencher) { + let mut rng = StdRng::new().unwrap(); + b.iter(|| { + for _ in 0..RAND_BENCH_N { + black_box(usize::rand(&mut rng, Default)); + } + }); + b.bytes = size_of::() as u64 * RAND_BENCH_N; +} + +#[bench] +fn gen_usize_os(b: &mut Bencher) { + let mut rng = OsRng::new().unwrap(); + b.iter(|| { + for _ in 0..RAND_BENCH_N { + black_box(usize::rand(&mut rng, Default)); + } + }); + b.bytes = size_of::() as u64 * RAND_BENCH_N; +} From 21a43755276984ed777020163cf058b76e656d69 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Fri, 11 Aug 2017 16:25:55 +0100 Subject: [PATCH 060/247] Move thread_local stuff to new module. Tweaks for no_std. --- src/lib.rs | 176 +++----------------------------------------- src/thread_local.rs | 162 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 171 insertions(+), 167 deletions(-) create mode 100644 src/thread_local.rs diff --git a/src/lib.rs b/src/lib.rs index 26b57ae55ce..f9d735521d9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -252,12 +252,7 @@ #[cfg(feature="std")] extern crate core; -#[cfg(feature="std")] -use std::cell::RefCell; -#[cfg(feature="std")] -use std::rc::Rc; -#[cfg(feature="std")] -use core::mem; +use core::mem::transmute; #[cfg(feature="std")] pub use read::ReadRng; @@ -265,10 +260,11 @@ pub use read::ReadRng; pub use os::OsRng; pub use iter::iter; pub use distributions::{Default, Rand}; - #[cfg(feature="std")] +pub use thread_local::{ThreadRng, thread_rng, random, random_with}; + use prng::IsaacWordRng; -#[cfg(feature="std")] +#[cfg(feature="std")] // available but unused without "std" use prng::XorShiftRng; pub mod distributions; @@ -277,10 +273,12 @@ pub mod prng; pub mod reseeding; pub mod sequences; +#[cfg(feature="std")] +mod os; #[cfg(feature="std")] mod read; #[cfg(feature="std")] -mod os; +mod thread_local; /// A random number generator. pub trait Rng { @@ -463,7 +461,6 @@ pub trait SeedableRng: Rng { /// The standard RNG. This is designed to be efficient on the current /// platform. -#[cfg(feature="std")] #[derive(Copy, Clone, Debug)] pub struct StdRng { rng: IsaacWordRng, @@ -487,7 +484,6 @@ impl StdRng { } } -#[cfg(feature="std")] impl Rng for StdRng { #[inline] fn next_u32(&mut self) -> u32 { @@ -500,16 +496,15 @@ impl Rng for StdRng { } } -#[cfg(feature="std")] impl<'a> SeedableRng<&'a [usize]> for StdRng { fn reseed(&mut self, seed: &'a [usize]) { // the internal RNG can just be seeded from the above // randomness. - self.rng.reseed(unsafe {mem::transmute(seed)}) + self.rng.reseed(unsafe {transmute(seed)}) } fn from_seed(seed: &'a [usize]) -> StdRng { - StdRng { rng: SeedableRng::from_seed(unsafe {mem::transmute(seed)}) } + StdRng { rng: SeedableRng::from_seed(unsafe {transmute(seed)}) } } } @@ -532,159 +527,6 @@ pub fn weak_rng() -> XorShiftRng { } } -/// Controls how the thread-local RNG is reseeded. -#[cfg(feature="std")] -#[derive(Debug)] -struct ThreadRngReseeder; - -#[cfg(feature="std")] -impl reseeding::Reseeder for ThreadRngReseeder { - fn reseed(&mut self, rng: &mut StdRng) { - *rng = match StdRng::new() { - Ok(r) => r, - Err(e) => panic!("could not reseed thread_rng: {}", e) - } - } -} -#[cfg(feature="std")] -const THREAD_RNG_RESEED_THRESHOLD: u64 = 32_768; -#[cfg(feature="std")] -type ThreadRngInner = reseeding::ReseedingRng; - -/// The thread-local RNG. -#[cfg(feature="std")] -#[derive(Clone, Debug)] -pub struct ThreadRng { - rng: Rc>, -} - -/// Retrieve the lazily-initialized thread-local random number -/// generator, seeded by the system. Intended to be used in method -/// chaining style, e.g. `thread_rng().gen::()`. -/// -/// The RNG provided will reseed itself from the operating system -/// after generating a certain amount of randomness. -/// -/// The internal RNG used is platform and architecture dependent, even -/// if the operating system random number generator is rigged to give -/// the same sequence always. If absolute consistency is required, -/// explicitly select an RNG, e.g. `IsaacRng` or `Isaac64Rng`. -#[cfg(feature="std")] -pub fn thread_rng() -> ThreadRng { - // used to make space in TLS for a random number generator - thread_local!(static THREAD_RNG_KEY: Rc> = { - let r = match StdRng::new() { - Ok(r) => r, - Err(e) => panic!("could not initialize thread_rng: {}", e) - }; - let rng = reseeding::ReseedingRng::new(r, - THREAD_RNG_RESEED_THRESHOLD, - ThreadRngReseeder); - Rc::new(RefCell::new(rng)) - }); - - ThreadRng { rng: THREAD_RNG_KEY.with(|t| t.clone()) } -} - -#[cfg(feature="std")] -impl Rng for ThreadRng { - fn next_u32(&mut self) -> u32 { - self.rng.borrow_mut().next_u32() - } - - fn next_u64(&mut self) -> u64 { - self.rng.borrow_mut().next_u64() - } - - #[inline] - fn fill_bytes(&mut self, bytes: &mut [u8]) { - self.rng.borrow_mut().fill_bytes(bytes) - } -} - -/// Generates a random value using the thread-local random number generator. -/// -/// `random()` can generate various types of random things, and so may require -/// type hinting to generate the specific type you want. It uses -/// `distributions::SampleDefault` to determine *how* values are generated for each type. -/// -/// This function uses the thread local random number generator. This means -/// that if you're calling `random()` in a loop, caching the generator can -/// increase performance. An example is shown below. -/// -/// # Examples -/// -/// ``` -/// let x = rand::random::(); -/// println!("{}", x); -/// -/// if rand::random() { // generates a boolean -/// println!("Better lucky than good!"); -/// } -/// ``` -/// -/// Caching the thread local random number generator: -/// -/// ``` -/// use rand::{Rand, Default}; -/// -/// let mut v = vec![1, 2, 3]; -/// -/// for x in v.iter_mut() { -/// *x = rand::random() -/// } -/// -/// // would be faster as -/// -/// let mut rng = rand::thread_rng(); -/// -/// for x in v.iter_mut() { -/// *x = Rand::rand(&mut rng, Default); -/// } -/// ``` -/// -/// Note that the above example uses `SampleDefault` which is a zero-sized -/// marker type. -#[cfg(feature="std")] -#[inline] -pub fn random>() -> T { - T::rand(&mut thread_rng(), Default) -} - -/// Generates a random value using the thread-local random number generator. -/// -/// This is a more flexible variant of `random()`, supporting selection of the -/// distribution used. For example: -/// -/// ``` -/// use rand::random_with; -/// use rand::distributions::{Rand, Default, Uniform01, Closed01, Range}; -/// -/// // identical to calling `random()`: -/// let x: f64 = random_with(Default); -/// -/// // same distribution, since Default uses Uniform01 for floats: -/// let y: f64 = random_with(Uniform01); -/// -/// // use the closed range [0, 1] inseat of half-open [0, 1): -/// let z: f64 = random_with(Closed01); -/// -/// // use the half-open range [0, 2): -/// let w: f64 = random_with(Range::new(0.0, 2.0)); -/// -/// // Note that constructing a `Range` is non-trivial, so for the last example -/// // it might be better to do this if sampling a lot: -/// let mut rng = rand::thread_rng(); -/// let range = Range::new(0.0, 2.0); -/// // Do this bit many times: -/// let v = f64::rand(&mut rng, range); -/// ``` -#[cfg(feature="std")] -#[inline] -pub fn random_with>(distribution: D) -> T { - T::rand(&mut thread_rng(), distribution) -} - #[cfg(test)] mod test { diff --git a/src/thread_local.rs b/src/thread_local.rs new file mode 100644 index 00000000000..9b3f5139de6 --- /dev/null +++ b/src/thread_local.rs @@ -0,0 +1,162 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Thread-local handle to a random number generator + +use std::cell::RefCell; +use std::rc::Rc; + +use {Rng, StdRng, Rand, Default}; +use reseeding::{Reseeder, ReseedingRng}; + +/// Controls how the thread-local RNG is reseeded. +#[derive(Debug)] +struct ThreadRngReseeder; + +impl Reseeder for ThreadRngReseeder { + fn reseed(&mut self, rng: &mut StdRng) { + *rng = match StdRng::new() { + Ok(r) => r, + Err(e) => panic!("could not reseed thread_rng: {}", e) + } + } +} + +const THREAD_RNG_RESEED_THRESHOLD: u64 = 32_768; +type ThreadRngInner = ReseedingRng; + +/// The thread-local RNG. +#[derive(Clone, Debug)] +pub struct ThreadRng { + rng: Rc>, +} + +/// Retrieve the lazily-initialized thread-local random number +/// generator, seeded by the system. Intended to be used in method +/// chaining style, e.g. `thread_rng().gen::()`. +/// +/// The RNG provided will reseed itself from the operating system +/// after generating a certain amount of randomness. +/// +/// The internal RNG used is platform and architecture dependent, even +/// if the operating system random number generator is rigged to give +/// the same sequence always. If absolute consistency is required, +/// explicitly select an RNG, e.g. `IsaacRng` or `Isaac64Rng`. +pub fn thread_rng() -> ThreadRng { + // used to make space in TLS for a random number generator + thread_local!(static THREAD_RNG_KEY: Rc> = { + let r = match StdRng::new() { + Ok(r) => r, + Err(e) => panic!("could not initialize thread_rng: {}", e) + }; + let rng = ReseedingRng::new(r, + THREAD_RNG_RESEED_THRESHOLD, + ThreadRngReseeder); + Rc::new(RefCell::new(rng)) + }); + + ThreadRng { rng: THREAD_RNG_KEY.with(|t| t.clone()) } +} + +impl Rng for ThreadRng { + fn next_u32(&mut self) -> u32 { + self.rng.borrow_mut().next_u32() + } + + fn next_u64(&mut self) -> u64 { + self.rng.borrow_mut().next_u64() + } + + #[inline] + fn fill_bytes(&mut self, bytes: &mut [u8]) { + self.rng.borrow_mut().fill_bytes(bytes) + } +} + +/// Generates a random value using the thread-local random number generator. +/// +/// `random()` can generate various types of random things, and so may require +/// type hinting to generate the specific type you want. It uses +/// `distributions::SampleDefault` to determine *how* values are generated for each type. +/// +/// This function uses the thread local random number generator. This means +/// that if you're calling `random()` in a loop, caching the generator can +/// increase performance. An example is shown below. +/// +/// # Examples +/// +/// ``` +/// let x = rand::random::(); +/// println!("{}", x); +/// +/// if rand::random() { // generates a boolean +/// println!("Better lucky than good!"); +/// } +/// ``` +/// +/// Caching the thread local random number generator: +/// +/// ``` +/// use rand::{Rand, Default}; +/// +/// let mut v = vec![1, 2, 3]; +/// +/// for x in v.iter_mut() { +/// *x = rand::random() +/// } +/// +/// // would be faster as +/// +/// let mut rng = rand::thread_rng(); +/// +/// for x in v.iter_mut() { +/// *x = Rand::rand(&mut rng, Default); +/// } +/// ``` +/// +/// Note that the above example uses `SampleDefault` which is a zero-sized +/// marker type. +#[inline] +pub fn random>() -> T { + T::rand(&mut thread_rng(), Default) +} + +/// Generates a random value using the thread-local random number generator. +/// +/// This is a more flexible variant of `random()`, supporting selection of the +/// distribution used. For example: +/// +/// ``` +/// use rand::random_with; +/// use rand::distributions::{Rand, Default, Uniform01, Closed01, Range}; +/// +/// // identical to calling `random()`: +/// let x: f64 = random_with(Default); +/// +/// // same distribution, since Default uses Uniform01 for floats: +/// let y: f64 = random_with(Uniform01); +/// +/// // use the closed range [0, 1] inseat of half-open [0, 1): +/// let z: f64 = random_with(Closed01); +/// +/// // use the half-open range [0, 2): +/// let w: f64 = random_with(Range::new(0.0, 2.0)); +/// +/// // Note that constructing a `Range` is non-trivial, so for the last example +/// // it might be better to do this if sampling a lot: +/// let mut rng = rand::thread_rng(); +/// let range = Range::new(0.0, 2.0); +/// // Do this bit many times: +/// let v = f64::rand(&mut rng, range); +/// ``` +#[inline] +pub fn random_with>(distribution: D) -> T { + T::rand(&mut thread_rng(), distribution) +} From 404e28246cddae4c56b9d5f6e9ca20d9275a5252 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 14 Aug 2017 10:50:34 +0100 Subject: [PATCH 061/247] Require Rng: Debug --- src/distributions/uniform.rs | 1 + src/lib.rs | 5 ++++- src/os.rs | 2 ++ src/read.rs | 7 ++++--- src/reseeding.rs | 11 +++++++---- src/sequences/weighted.rs | 1 + 6 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/distributions/uniform.rs b/src/distributions/uniform.rs index e5c71466655..0a891998a23 100644 --- a/src/distributions/uniform.rs +++ b/src/distributions/uniform.rs @@ -285,6 +285,7 @@ mod tests { assert_eq!(word.len(), 5); } + #[derive(Debug)] struct ConstantRng(u64); impl Rng for ConstantRng { fn next_u32(&mut self) -> u32 { diff --git a/src/lib.rs b/src/lib.rs index f9d735521d9..aa86e0568af 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -252,6 +252,7 @@ #[cfg(feature="std")] extern crate core; +use core::fmt::Debug; use core::mem::transmute; #[cfg(feature="std")] @@ -281,7 +282,7 @@ mod read; mod thread_local; /// A random number generator. -pub trait Rng { +pub trait Rng: Debug { /// Return the next random u8. /// /// By default this is implemented in terms of `next_u32`. @@ -535,6 +536,7 @@ mod test { use sequences::Shuffle; use std::iter::repeat; + #[derive(Debug)] pub struct MyRng { inner: R } impl Rng for MyRng { @@ -550,6 +552,7 @@ mod test { MyRng { inner: ::thread_rng() } } + #[derive(Debug)] struct ConstRng { i: u64 } impl Rng for ConstRng { fn next_u32(&mut self) -> u32 { self.i as u32 } diff --git a/src/os.rs b/src/os.rs index 82a09ad0347..2dafc1bc6df 100644 --- a/src/os.rs +++ b/src/os.rs @@ -171,10 +171,12 @@ mod imp { target_arch = "powerpc"))))] fn is_getrandom_available() -> bool { false } + #[derive(Debug)] pub struct OsRng { inner: OsRngInner, } + #[derive(Debug)] enum OsRngInner { OsGetrandomRng, OsReadRng(ReadRng), diff --git a/src/read.rs b/src/read.rs index bf0c2fccdf1..a1873a6bb2c 100644 --- a/src/read.rs +++ b/src/read.rs @@ -10,6 +10,7 @@ //! A wrapper around any Read to treat it as an RNG. +use std::fmt::Debug; use std::io::{self, Read}; use std::mem; use Rng; @@ -31,11 +32,11 @@ use Rng; /// println!("{:x}", distributions::uniform::(&mut rng)); /// ``` #[derive(Debug)] -pub struct ReadRng { +pub struct ReadRng { reader: R } -impl ReadRng { +impl ReadRng { /// Create a new `ReadRng` from a `Read`. pub fn new(r: R) -> ReadRng { ReadRng { @@ -44,7 +45,7 @@ impl ReadRng { } } -impl Rng for ReadRng { +impl Rng for ReadRng { fn next_u32(&mut self) -> u32 { // This is designed for speed: reading a LE integer on a LE // platform just involves blitting the bytes into the memory diff --git a/src/reseeding.rs b/src/reseeding.rs index e17d8383920..35498c4e3a4 100644 --- a/src/reseeding.rs +++ b/src/reseeding.rs @@ -12,6 +12,7 @@ //! generates a certain number of random bytes. use core::default::Default; +use core::fmt::Debug; use {Rng, SeedableRng}; @@ -22,7 +23,7 @@ const DEFAULT_GENERATION_THRESHOLD: u64 = 32 * 1024; /// A wrapper around any RNG which reseeds the underlying RNG after it /// has generated a certain number of random bytes. #[derive(Debug)] -pub struct ReseedingRng { +pub struct ReseedingRng { rng: R, generation_threshold: u64, bytes_generated: u64, @@ -30,7 +31,7 @@ pub struct ReseedingRng { pub reseeder: Rsdr, } -impl> ReseedingRng { +impl + Debug> ReseedingRng { /// Create a new `ReseedingRng` with the given parameters. /// /// # Arguments @@ -58,7 +59,7 @@ impl> ReseedingRng { } -impl> Rng for ReseedingRng { +impl + Debug> Rng for ReseedingRng { fn next_u32(&mut self) -> u32 { self.reseed_if_necessary(); self.bytes_generated += 4; @@ -78,7 +79,7 @@ impl> Rng for ReseedingRng { } } -impl, Rsdr: Reseeder + Default> +impl, Rsdr: Reseeder + Debug + Default> SeedableRng<(Rsdr, S)> for ReseedingRng { fn reseed(&mut self, (rsdr, seed): (Rsdr, S)) { self.rng.reseed(seed); @@ -107,6 +108,7 @@ impl, Rsdr: Reseeder + Default> /// use rand::distributions::ascii_word_char; /// use rand::reseeding::{Reseeder, ReseedingRng}; /// +/// #[derive(Debug)] /// struct TickTockReseeder { tick: bool } /// impl Reseeder for TickTockReseeder { /// fn reseed(&mut self, rng: &mut StdRng) { @@ -154,6 +156,7 @@ mod test { use distributions::ascii_word_char; use super::{ReseedingRng, ReseedWithDefault}; + #[derive(Debug)] struct Counter { i: u32 } diff --git a/src/sequences/weighted.rs b/src/sequences/weighted.rs index 0b7cf352d7a..395bbb19bcc 100644 --- a/src/sequences/weighted.rs +++ b/src/sequences/weighted.rs @@ -141,6 +141,7 @@ mod tests { struct ConstRand(usize); // 0, 1, 2, 3, ... + #[derive(Debug)] struct CountingRng { i: u32 } impl Rng for CountingRng { fn next_u32(&mut self) -> u32 { From 68bb5497fd6e77f9f5326cbf37da178613a5fd3d Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 14 Aug 2017 10:51:11 +0100 Subject: [PATCH 062/247] Make thread_rng use OsRng by default and allow override --- src/lib.rs | 3 +- src/thread_local.rs | 170 ++++++++++++++++++++++++++++++++------------ 2 files changed, 127 insertions(+), 46 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index aa86e0568af..a4cce709d49 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -262,7 +262,8 @@ pub use os::OsRng; pub use iter::iter; pub use distributions::{Default, Rand}; #[cfg(feature="std")] -pub use thread_local::{ThreadRng, thread_rng, random, random_with}; +pub use thread_local::{ThreadRng, thread_rng, set_thread_rng, set_new_thread_rng, + random, random_with}; use prng::IsaacWordRng; #[cfg(feature="std")] // available but unused without "std" diff --git a/src/thread_local.rs b/src/thread_local.rs index 9b3f5139de6..7b91acfef17 100644 --- a/src/thread_local.rs +++ b/src/thread_local.rs @@ -12,57 +12,40 @@ use std::cell::RefCell; use std::rc::Rc; +use std::sync::Mutex; -use {Rng, StdRng, Rand, Default}; -use reseeding::{Reseeder, ReseedingRng}; +use {Rng, OsRng, Rand, Default}; -/// Controls how the thread-local RNG is reseeded. -#[derive(Debug)] -struct ThreadRngReseeder; - -impl Reseeder for ThreadRngReseeder { - fn reseed(&mut self, rng: &mut StdRng) { - *rng = match StdRng::new() { - Ok(r) => r, - Err(e) => panic!("could not reseed thread_rng: {}", e) - } - } -} - -const THREAD_RNG_RESEED_THRESHOLD: u64 = 32_768; -type ThreadRngInner = ReseedingRng; +// use reseeding::{Reseeder, ReseedingRng}; +// +// /// Controls how the thread-local RNG is reseeded. +// #[derive(Debug)] +// struct ThreadRngReseeder; +// +// impl Reseeder for ThreadRngReseeder { +// fn reseed(&mut self, rng: &mut StdRng) { +// *rng = match StdRng::new() { +// Ok(r) => r, +// Err(e) => panic!("could not reseed thread_rng: {}", e) +// } +// } +// } +// +// const THREAD_RNG_RESEED_THRESHOLD: u64 = 32_768; +// type ReseedingStdRng = ReseedingRng; +// thread_local!(static THREAD_RNG_KEY: Rc>>> = { +// let r = match StdRng::new() { +// Ok(r) => r, +// Err(e) => panic!("could not initialize thread_rng: {}", e) +// }; +// let rng = ReseedingRng::new(r, +// THREAD_RNG_RESEED_THRESHOLD, +// ThreadRngReseeder); /// The thread-local RNG. #[derive(Clone, Debug)] pub struct ThreadRng { - rng: Rc>, -} - -/// Retrieve the lazily-initialized thread-local random number -/// generator, seeded by the system. Intended to be used in method -/// chaining style, e.g. `thread_rng().gen::()`. -/// -/// The RNG provided will reseed itself from the operating system -/// after generating a certain amount of randomness. -/// -/// The internal RNG used is platform and architecture dependent, even -/// if the operating system random number generator is rigged to give -/// the same sequence always. If absolute consistency is required, -/// explicitly select an RNG, e.g. `IsaacRng` or `Isaac64Rng`. -pub fn thread_rng() -> ThreadRng { - // used to make space in TLS for a random number generator - thread_local!(static THREAD_RNG_KEY: Rc> = { - let r = match StdRng::new() { - Ok(r) => r, - Err(e) => panic!("could not initialize thread_rng: {}", e) - }; - let rng = ReseedingRng::new(r, - THREAD_RNG_RESEED_THRESHOLD, - ThreadRngReseeder); - Rc::new(RefCell::new(rng)) - }); - - ThreadRng { rng: THREAD_RNG_KEY.with(|t| t.clone()) } + rng: Rc>>, } impl Rng for ThreadRng { @@ -80,6 +63,83 @@ impl Rng for ThreadRng { } } +thread_local!( + static THREAD_RNG_CREATOR: Mutex Box>>> = { + // TODO: should this panic? + Mutex::new(RefCell::new(Box::new(|| Box::new(OsRng::new().unwrap())))) + } +); + +thread_local!( + static THREAD_RNG_KEY: Rc>> = { + let rng = THREAD_RNG_CREATOR.with(|f| { + let creator = f.lock().unwrap(); + let rng = (creator.borrow())(); + rng + }); + Rc::new(RefCell::new(rng)) + } +); + +/// Retrieve the lazily-initialized thread-local random number +/// generator, seeded by the system. This is used by `random` and +/// `random_with` to generate new values, and may be used directly with other +/// distributions: `Range::new(0, 10).sample(&mut thread_rng())`. +/// +/// By default, this uses `OsRng` which pulls random numbers directly from the +/// operating system. This is intended to be a good secure default; for more +/// see `set_thread_rng` and `set_new_thread_rng`. +/// +/// This is intended to provide securely generated random numbers, but this +/// behaviour can be changed by `set_thread_rng` and `set_new_thread_rng`. +/// +/// `thread_rng` is not especially fast, since it uses dynamic dispatch and +/// `OsRng`, which requires a system call on each usage. Users wanting a fast +/// generator for games or simulations may prefer not to use `thread_rng` at +/// all, especially if reproducibility of results is important. +pub fn thread_rng() -> ThreadRng { + ThreadRng { rng: THREAD_RNG_KEY.with(|t| t.clone()) } +} + +/// Set the thread-local RNG used by `thread_rng`. Only affects the current +/// thread. See also `set_new_thread_rng`. +/// +/// If this is never called in a thread and `set_new_thread_rng` is never called +/// globally, the first call to `thread_rng()` in the thread will create a new +/// `OsRng` and use that as a source of numbers. +/// This method allows a different generator to be set, and can be called at +/// any time to set a new generator. +/// +/// Calling this affects all users of `thread_rng`, `random` and `random_with` +/// in the current thread, including libraries. Users should ensure the +/// generator used is secure enough for all users of `thread_rng`, including +/// other libraries. +pub fn set_thread_rng(rng: Box) { + THREAD_RNG_KEY.with(|t| *t.borrow_mut() = rng); +} + +/// Control how thread-local generators are created for new threads. +/// +/// The first time `thread_rng()` is called in each thread, this closure is +/// used to create a new generator. If this closure has not been set, the +/// built-in closure returning a new boxed `OsRng` is used. +/// +/// Calling this affects all users of `thread_rng`, `random` and `random_with` +/// in all new threads (as well as existing threads which haven't called +/// `thread_rng` yet), including libraries. Users should ensure the generator +/// used is secure enough for all users of `thread_rng`, including other +/// libraries. +/// +/// This closure should not panic; doing so would kill whichever thread is +/// calling `thread_rng` at the time, poison its mutex, and cause all future +/// threads initialising a `thread_rng` (or calling this function) to panic. +pub fn set_new_thread_rng(creator: Box Box>) { + THREAD_RNG_CREATOR.with(|f| { + let p = f.lock().unwrap(); + *p.borrow_mut() = creator; + }); +} + /// Generates a random value using the thread-local random number generator. /// /// `random()` can generate various types of random things, and so may require @@ -160,3 +220,23 @@ pub fn random>() -> T { pub fn random_with>(distribution: D) -> T { T::rand(&mut thread_rng(), distribution) } + +#[cfg(test)] +mod test { + use {set_thread_rng, random, Rng}; + + #[derive(Debug)] + struct ConstRng { i: u64 } + impl Rng for ConstRng { + fn next_u32(&mut self) -> u32 { self.i as u32 } + fn next_u64(&mut self) -> u64 { self.i } + } + + #[test] + fn test_set_thread_rng() { + random::(); + set_thread_rng(Box::new(ConstRng { i: 12u64 })); + assert_eq!(random::(), 12u64); + assert_eq!(random::(), 12u64); + } +} From d2b9f8e16aceacdf16114ff842dbb4ad98202f04 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 14 Aug 2017 11:16:40 +0100 Subject: [PATCH 063/247] Add ConstRng type; improve set_thread_rng doc --- src/lib.rs | 44 +++++++++++++++++++++++++++++++++----------- src/thread_local.rs | 41 +++++++++++++++++++++++++++++------------ 2 files changed, 62 insertions(+), 23 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index a4cce709d49..cb652ad18dc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -461,6 +461,37 @@ pub trait SeedableRng: Rng { fn from_seed(seed: Seed) -> Self; } +/// A very simple implementation of `Rng`, purely for testing. Returns the same +/// value each time. +/// +/// ```rust +/// use rand::{ConstRng, Rng}; +/// +/// let mut my_rng = ConstRng::new(2u32); +/// assert_eq!(my_rng.next_u32(), 2u32); +/// assert_eq!(my_rng.next_u64(), (2u64 << 32) + 2u64); +/// ``` +#[derive(Debug)] +pub struct ConstRng { + v: T +} + +impl ConstRng { + /// Create a `ConstRng`, yielding the given value + pub fn new(v: T) -> Self { + ConstRng { v } + } +} + +impl Rng for ConstRng { + fn next_u32(&mut self) -> u32 { self.v } +} + +impl Rng for ConstRng { + fn next_u32(&mut self) -> u32 { self.v as u32 } + fn next_u64(&mut self) -> u64 { self.v } +} + /// The standard RNG. This is designed to be efficient on the current /// platform. #[derive(Copy, Clone, Debug)] @@ -532,7 +563,7 @@ pub fn weak_rng() -> XorShiftRng { #[cfg(test)] mod test { - use {Rng, thread_rng, SeedableRng, StdRng, iter}; + use {Rng, thread_rng, SeedableRng, StdRng, ConstRng, iter}; use distributions::{uniform, range, ascii_word_char}; use sequences::Shuffle; use std::iter::repeat; @@ -553,15 +584,6 @@ mod test { MyRng { inner: ::thread_rng() } } - #[derive(Debug)] - struct ConstRng { i: u64 } - impl Rng for ConstRng { - fn next_u32(&mut self) -> u32 { self.i as u32 } - fn next_u64(&mut self) -> u64 { self.i } - - // no fill_bytes on purpose - } - pub fn iter_eq(i: I, j: J) -> bool where I: IntoIterator, J: IntoIterator, @@ -581,7 +603,7 @@ mod test { #[test] fn test_fill_bytes_default() { - let mut r = ConstRng { i: 0x11_22_33_44_55_66_77_88 }; + let mut r = ConstRng::new(0x11_22_33_44_55_66_77_88u64); // check every remainder mod 8, both in small and big vectors. let lengths = [0, 1, 2, 3, 4, 5, 6, 7, diff --git a/src/thread_local.rs b/src/thread_local.rs index 7b91acfef17..96c19afed43 100644 --- a/src/thread_local.rs +++ b/src/thread_local.rs @@ -114,6 +114,21 @@ pub fn thread_rng() -> ThreadRng { /// in the current thread, including libraries. Users should ensure the /// generator used is secure enough for all users of `thread_rng`, including /// other libraries. +/// +/// This may have some use in testing, by spawning a new thread, overriding the +/// generator with known values (a constant or a PRNG with known seed), then +/// asserting the exact output of "randomly" generated values. The use of a new +/// thread avoids affecting other users of `thread_rng`. +/// +/// ```rust +/// use rand::{ConstRng, random, set_thread_rng}; +/// use std::thread; +/// +/// thread::spawn(move || { +/// set_thread_rng(Box::new(ConstRng::new(123u32))); +/// assert_eq!(random::(), 123u32); +/// }); +/// ``` pub fn set_thread_rng(rng: Box) { THREAD_RNG_KEY.with(|t| *t.borrow_mut() = rng); } @@ -223,20 +238,22 @@ pub fn random_with>(distribution: D) -> T { #[cfg(test)] mod test { - use {set_thread_rng, random, Rng}; + use std::thread; + use {set_thread_rng, random, ConstRng}; - #[derive(Debug)] - struct ConstRng { i: u64 } - impl Rng for ConstRng { - fn next_u32(&mut self) -> u32 { self.i as u32 } - fn next_u64(&mut self) -> u64 { self.i } - } - #[test] fn test_set_thread_rng() { - random::(); - set_thread_rng(Box::new(ConstRng { i: 12u64 })); - assert_eq!(random::(), 12u64); - assert_eq!(random::(), 12u64); + let v = 12u64; + thread::spawn(move || { + random::(); + // affect this thread only: + set_thread_rng(Box::new(ConstRng::new(v))); + assert_eq!(random::(), v); + assert_eq!(random::(), v); + }); + + // The chance of 128 bits randomly equalling our value is miniscule: + let (x, y): (u64, u64) = (random(), random()); + assert!((x, y) != (v, v)); } } From adbbd9d877e3012d8e1001d723fc4cccdff594b2 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 14 Aug 2017 12:08:43 +0100 Subject: [PATCH 064/247] Replace ConstantRng in uniform tests with ConstRng --- src/distributions/uniform.rs | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/src/distributions/uniform.rs b/src/distributions/uniform.rs index 0a891998a23..ebd6c418c84 100644 --- a/src/distributions/uniform.rs +++ b/src/distributions/uniform.rs @@ -245,7 +245,7 @@ float_impls! { SCALE_F32, f32, 24 } #[cfg(test)] mod tests { - use {Rng, thread_rng, iter}; + use {ConstRng, thread_rng, iter}; use distributions::{Rand, uniform, Uniform, Uniform01, Open01, Closed01}; use distributions::uniform::{codepoint, ascii_word_char}; @@ -285,19 +285,6 @@ mod tests { assert_eq!(word.len(), 5); } - #[derive(Debug)] - struct ConstantRng(u64); - impl Rng for ConstantRng { - fn next_u32(&mut self) -> u32 { - let ConstantRng(v) = *self; - v as u32 - } - fn next_u64(&mut self) -> u64 { - let ConstantRng(v) = *self; - v - } - } - #[test] fn test_f64() { let mut r = thread_rng(); @@ -312,8 +299,8 @@ mod tests { // FIXME: this message and test predates this repo; message suggests // test is supposed to be ==; using != is pretty useless // the test for exact equality is correct here. - assert!(f64::rand(&mut ConstantRng(0xffff_ffff), Uniform01) != 1.0); - assert!(f64::rand(&mut ConstantRng(0xffff_ffff_ffff_ffff), Uniform01) != 1.0); + assert!(f64::rand(&mut ConstRng::new(0xffff_ffffu64), Uniform01) != 1.0); + assert!(f64::rand(&mut ConstRng::new(0xffff_ffff_ffff_ffffu64), Uniform01) != 1.0); } #[test] From 2511b272c935374d443ad0ffd5d80e1874412933 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 14 Aug 2017 12:10:26 +0100 Subject: [PATCH 065/247] Add fn new() -> Self for Rngs; remove weak_rng() --- benches/distributions.rs | 14 +++++++------- benches/misc.rs | 6 +++--- src/lib.rs | 21 --------------------- src/prng/chacha.rs | 10 +++++++++- src/prng/isaac.rs | 18 +++++++++++++++++- src/prng/xorshift.rs | 10 +++++++++- 6 files changed, 45 insertions(+), 34 deletions(-) diff --git a/benches/distributions.rs b/benches/distributions.rs index a141e0adfcf..352fe9de012 100644 --- a/benches/distributions.rs +++ b/benches/distributions.rs @@ -8,7 +8,7 @@ const RAND_BENCH_N: u64 = 1000; use std::mem::size_of; use test::Bencher; -use rand::{weak_rng, Default, Rand}; +use rand::{XorShiftRng, Default, Rand}; use rand::distributions::Distribution; use rand::distributions::exponential::Exp; use rand::distributions::normal::{Normal, LogNormal}; @@ -17,7 +17,7 @@ use rand::distributions::gamma::Gamma; #[bench] fn distr_baseline(b: &mut Bencher) { - let mut rng = weak_rng(); + let mut rng = XorShiftRng::new(); b.iter(|| { for _ in 0..::RAND_BENCH_N { @@ -30,7 +30,7 @@ fn distr_baseline(b: &mut Bencher) { #[bench] fn distr_exp(b: &mut Bencher) { - let mut rng = weak_rng(); + let mut rng = XorShiftRng::new(); let distr = Exp::new(2.71828 * 3.14159); b.iter(|| { @@ -44,7 +44,7 @@ fn distr_exp(b: &mut Bencher) { #[bench] fn distr_normal(b: &mut Bencher) { - let mut rng = weak_rng(); + let mut rng = XorShiftRng::new(); let distr = Normal::new(-2.71828, 3.14159); b.iter(|| { @@ -57,7 +57,7 @@ fn distr_normal(b: &mut Bencher) { #[bench] fn distr_log_normal(b: &mut Bencher) { - let mut rng = weak_rng(); + let mut rng = XorShiftRng::new(); let distr = LogNormal::new(-2.71828, 3.14159); b.iter(|| { @@ -71,7 +71,7 @@ fn distr_log_normal(b: &mut Bencher) { #[bench] fn distr_gamma_large_shape(b: &mut Bencher) { - let mut rng = weak_rng(); + let mut rng = XorShiftRng::new(); let distr = Gamma::new(10., 1.0); b.iter(|| { @@ -84,7 +84,7 @@ fn distr_gamma_large_shape(b: &mut Bencher) { #[bench] fn distr_gamma_small_shape(b: &mut Bencher) { - let mut rng = weak_rng(); + let mut rng = XorShiftRng::new(); let distr = Gamma::new(0.1, 1.0); b.iter(|| { diff --git a/benches/misc.rs b/benches/misc.rs index 6c83023782e..5c7e58036d4 100644 --- a/benches/misc.rs +++ b/benches/misc.rs @@ -7,7 +7,7 @@ const RAND_BENCH_N: u64 = 1000; use std::mem::size_of; use test::{black_box, Bencher}; -use rand::{StdRng, weak_rng}; +use rand::{StdRng, XorShiftRng}; use rand::sequences::{sample, Shuffle}; use rand::distributions::{Rand, Uniform, Uniform01}; @@ -57,7 +57,7 @@ fn misc_convert_f64(b: &mut Bencher) { #[bench] fn misc_shuffle_100(b: &mut Bencher) { - let mut rng = weak_rng(); + let mut rng = XorShiftRng::new(); let x : &mut [usize] = &mut [1; 100]; b.iter(|| { x.shuffle(&mut rng); @@ -66,7 +66,7 @@ fn misc_shuffle_100(b: &mut Bencher) { #[bench] fn misc_sample_10_of_100(b: &mut Bencher) { - let mut rng = weak_rng(); + let mut rng = XorShiftRng::new(); let x : &[usize] = &[1; 100]; b.iter(|| { sample(&mut rng, x, 10); diff --git a/src/lib.rs b/src/lib.rs index cb652ad18dc..299610e0762 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -266,8 +266,6 @@ pub use thread_local::{ThreadRng, thread_rng, set_thread_rng, set_new_thread_rng random, random_with}; use prng::IsaacWordRng; -#[cfg(feature="std")] // available but unused without "std" -use prng::XorShiftRng; pub mod distributions; pub mod iter; @@ -541,25 +539,6 @@ impl<'a> SeedableRng<&'a [usize]> for StdRng { } } -/// Create a weak random number generator with a default algorithm and seed. -/// -/// It returns the fastest `Rng` algorithm currently available in Rust without -/// consideration for cryptography or security. If you require a specifically -/// seeded `Rng` for consistency over time you should pick one algorithm and -/// create the `Rng` yourself. -/// -/// This will read randomness from the operating system to seed the -/// generator. -// TODO: is this method useful? -#[cfg(feature="std")] -pub fn weak_rng() -> XorShiftRng { - // TODO: should this seed from `thread_rng()`? - match OsRng::new() { - Ok(mut r) => XorShiftRng::new_from_rng(&mut r), - Err(e) => panic!("weak_rng: failed to create seeded RNG: {:?}", e) - } -} - #[cfg(test)] mod test { diff --git a/src/prng/chacha.rs b/src/prng/chacha.rs index e66c4dc9145..9f16dab7d92 100644 --- a/src/prng/chacha.rs +++ b/src/prng/chacha.rs @@ -11,7 +11,7 @@ //! The ChaCha random number generator. use core::num::Wrapping as w; -use {Rng, SeedableRng}; +use {OsRng, Rng, SeedableRng}; #[allow(bad_style)] type w32 = w; @@ -81,6 +81,14 @@ fn core(output: &mut [w32; STATE_WORDS], input: &[w32; STATE_WORDS]) { } impl ChaChaRng { + /// Creates a new `ChaChaRng`, automatically seeded via `OsRng`. + pub fn new() -> ChaChaRng { + match OsRng::new() { + Ok(mut r) => ChaChaRng::new_from_rng(&mut r), + Err(e) => panic!("ChaChaRng::new: failed to get seed from OS: {:?}", e) + } + } + /// Create an ChaCha random number generator using the default /// fixed key of 8 zero words. /// diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index c3abd141225..e0fc5de766a 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -17,7 +17,7 @@ use core::iter::repeat; use core::num::Wrapping as w; use core::fmt; -use {Rng, SeedableRng}; +use {OsRng, Rng, SeedableRng}; /// Select 32- or 64-bit variant dependent on pointer size. #[cfg(target_pointer_width = "32")] @@ -61,6 +61,14 @@ static EMPTY: IsaacRng = IsaacRng { }; impl IsaacRng { + /// Creates a new `IsaacRng`, automatically seeded via `OsRng`. + pub fn new() -> IsaacRng { + match OsRng::new() { + Ok(mut r) => IsaacRng::new_from_rng(&mut r), + Err(e) => panic!("IsaacRng::new: failed to get seed from OS: {:?}", e) + } + } + /// Create an ISAAC random number generator using the default /// fixed seed. pub fn new_unseeded() -> IsaacRng { @@ -312,6 +320,14 @@ static EMPTY_64: Isaac64Rng = Isaac64Rng { }; impl Isaac64Rng { + /// Creates a new `Isaac64Rng`, automatically seeded via `OsRng`. + pub fn new() -> Isaac64Rng { + match OsRng::new() { + Ok(mut r) => Isaac64Rng::new_from_rng(&mut r), + Err(e) => panic!("Isaac64Rng::new: failed to get seed from OS: {:?}", e) + } + } + /// Create a 64-bit ISAAC random number generator using the /// default fixed seed. pub fn new_unseeded() -> Isaac64Rng { diff --git a/src/prng/xorshift.rs b/src/prng/xorshift.rs index 51431c5b810..3c96c004c12 100644 --- a/src/prng/xorshift.rs +++ b/src/prng/xorshift.rs @@ -11,7 +11,7 @@ //! Xorshift generators use core::num::Wrapping as w; -use {Rng, SeedableRng}; +use {OsRng, Rng, SeedableRng}; /// An Xorshift[1] random number /// generator. @@ -33,6 +33,14 @@ pub struct XorShiftRng { } impl XorShiftRng { + /// Creates a new `XorShiftRng`, automatically seeded via `OsRng`. + pub fn new() -> XorShiftRng { + match OsRng::new() { + Ok(mut r) => XorShiftRng::new_from_rng(&mut r), + Err(e) => panic!("XorShiftRng::new: failed to get seed from OS: {:?}", e) + } + } + /// Creates a new XorShiftRng instance which is not seeded. /// /// The initial values of this RNG are constants, so all generators created From cde9f58da9b54066ee6c9a74fcde7b6740303e92 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 15 Aug 2017 20:20:14 +0100 Subject: [PATCH 066/247] Add Sample trait --- src/distributions/mod.rs | 8 ++++- src/lib.rs | 76 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 81 insertions(+), 3 deletions(-) diff --git a/src/distributions/mod.rs b/src/distributions/mod.rs index 7144216a708..ed54d697f3d 100644 --- a/src/distributions/mod.rs +++ b/src/distributions/mod.rs @@ -72,6 +72,12 @@ pub trait Distribution { fn sample(&self, rng: &mut R) -> T; } +impl<'a, T, D: Distribution> Distribution for &'a D { + fn sample(&self, rng: &mut R) -> T { + (*self).sample(rng) + } +} + /// Generic trait for sampling random values from some distribution /// /// # Example @@ -190,7 +196,7 @@ fn ziggurat( #[cfg(test)] mod test { use {Rng, thread_rng}; - use distributions::weighted_bool; + use distributions::{weighted_bool}; #[test] fn test_fn_weighted_bool() { diff --git a/src/lib.rs b/src/lib.rs index 299610e0762..f3cb4c1c50d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -260,7 +260,7 @@ pub use read::ReadRng; #[cfg(feature="std")] pub use os::OsRng; pub use iter::iter; -pub use distributions::{Default, Rand}; +pub use distributions::{Distribution, Default, Rand}; #[cfg(feature="std")] pub use thread_local::{ThreadRng, thread_rng, set_thread_rng, set_new_thread_rng, random, random_with}; @@ -459,6 +459,63 @@ pub trait SeedableRng: Rng { fn from_seed(seed: Seed) -> Self; } +use distributions::range::{Range, SampleRange}; + +pub trait Sample { + /// Sample a new value, using the given distribution. + /// + /// ### Example + /// + /// ```rust + /// use rand::{thread_rng, Sample}; + /// use rand::distributions::Uniform; + /// + /// let mut rng = thread_rng(); + /// let x: i32 = rng.sample(Uniform); + /// ``` + fn sample>(&mut self, distr: D) -> T; + + /// Sample a new value, using the [`Default`] distribution. + /// + /// ### Example + /// + /// ```rust + /// use rand::{thread_rng, Sample}; + /// + /// let mut rng = thread_rng(); + /// let x: i32 = rng.gen(); + /// ``` + fn gen(&mut self) -> T where Default: Distribution { + self.sample(Default) + } + + /// Sample a new value, using the [`Range`] distribution. + /// + /// ### Example + /// + /// ```rust + /// use rand::{thread_rng, Sample}; + /// + /// let mut rng = thread_rng(); + /// + /// // simulate dice roll + /// let x = rng.gen_range(1, 7); + /// assert!(1 <= x && x <= 6); + /// ``` + /// + /// If the same range is used repeatedly, some work can be saved by + /// constructing the `Range` once and using it with `sample`. + fn gen_range(&mut self, low: T, high: T) -> T { + self.sample(Range::new(low, high)) + } +} + +impl Sample for R { + fn sample>(&mut self, distr: D) -> T { + distr.sample(self) + } +} + /// A very simple implementation of `Rng`, purely for testing. Returns the same /// value each time. /// @@ -542,8 +599,9 @@ impl<'a> SeedableRng<&'a [usize]> for StdRng { #[cfg(test)] mod test { - use {Rng, thread_rng, SeedableRng, StdRng, ConstRng, iter}; + use {Rng, thread_rng, SeedableRng, StdRng, ConstRng, iter, Sample}; use distributions::{uniform, range, ascii_word_char}; + use distributions::{Uniform, Range, Exp}; use sequences::Shuffle; use std::iter::repeat; @@ -656,4 +714,18 @@ mod test { let string2 = iter(&mut r).map(|rng| ascii_word_char(rng)).take(100).collect::(); assert_eq!(string1, string2); } + + #[test] + fn test_sample_from_rng() { + // use a static Rng type: + let mut rng = thread_rng(); + + let _a: u32 = rng.sample(Uniform); + let _b = rng.sample(Range::new(-2, 15)); + + // use a dynamic Rng type: + let mut rng: &mut Rng = &mut thread_rng(); + + let _c = rng.sample(Exp::new(2.0)); + } } From 28972a16f9f0985b3ac5c2d8f1f557dc0b5dd6ce Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 15 Aug 2017 20:33:29 +0100 Subject: [PATCH 067/247] Add Sample::iter wrapper; add AsciiWordChar distribution --- src/distributions/mod.rs | 2 +- src/distributions/uniform.rs | 14 ++++++++++++++ src/lib.rs | 17 ++++++++++++++++- 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/distributions/mod.rs b/src/distributions/mod.rs index ed54d697f3d..816351a48e5 100644 --- a/src/distributions/mod.rs +++ b/src/distributions/mod.rs @@ -21,7 +21,7 @@ use Rng; pub use self::default::Default; pub use self::uniform::{uniform, codepoint, ascii_word_char}; -pub use self::uniform::{Uniform, Uniform01, Open01, Closed01}; +pub use self::uniform::{Uniform, Uniform01, Open01, Closed01, AsciiWordChar}; pub use self::range::{range, Range}; #[cfg(feature="std")] diff --git a/src/distributions/uniform.rs b/src/distributions/uniform.rs index ebd6c418c84..c2ba56af8f9 100644 --- a/src/distributions/uniform.rs +++ b/src/distributions/uniform.rs @@ -30,6 +30,8 @@ pub fn uniform, R: Rng+?Sized>(rng: &mut R) -> T { /// i.e. all code points in the range `0...0x10_FFFF`, except for the range /// `0xD800...0xDFFF` (the surrogate code points). This includes /// unassigned/reserved code points. +/// +/// TODO: should this be removed in favour of a distribution? #[inline] pub fn codepoint(rng: &mut R) -> char { // a char is 21 bits @@ -47,6 +49,8 @@ pub fn codepoint(rng: &mut R) -> char { /// Sample a `char`, uniformly distributed over ASCII letters and numbers: /// a-z, A-Z and 0-9. +/// +/// TODO: should this be removed in favour of `AsciiWordChar`? #[inline] pub fn ascii_word_char(rng: &mut R) -> char { use sequences::Choose; @@ -76,6 +80,10 @@ pub struct Open01; #[derive(Debug)] pub struct Closed01; +/// Sample values uniformly from the ASCII ranges z-a, A-Z, and 0-9. +#[derive(Debug)] +pub struct AsciiWordChar; + // ----- actual implementations ----- @@ -242,6 +250,12 @@ macro_rules! float_impls { float_impls! { SCALE_F64, f64, 53 } float_impls! { SCALE_F32, f32, 24 } +impl Distribution for AsciiWordChar { + fn sample(&self, rng: &mut R) -> char { + ascii_word_char(rng) + } +} + #[cfg(test)] mod tests { diff --git a/src/lib.rs b/src/lib.rs index f3cb4c1c50d..37bfe3408ad 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -461,7 +461,7 @@ pub trait SeedableRng: Rng { use distributions::range::{Range, SampleRange}; -pub trait Sample { +pub trait Sample: Rng { /// Sample a new value, using the given distribution. /// /// ### Example @@ -508,6 +508,21 @@ pub trait Sample { fn gen_range(&mut self, low: T, high: T) -> T { self.sample(Range::new(low, high)) } + + /// Construct an iterator on an `Rng`. + /// + /// ### Example + /// + /// ```rust + /// use rand::{thread_rng, Sample}; + /// use rand::distributions::AsciiWordChar; + /// + /// let mut rng = thread_rng(); + /// let x: String = rng.iter().map(|rng| rng.sample(AsciiWordChar)).take(6).collect(); + /// ``` + fn iter<'a>(&'a mut self) -> iter::Iter<'a, Self> { + iter(self) + } } impl Sample for R { From fe990d9e93009de24de43419fcf61a8252fc4882 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 15 Aug 2017 20:39:45 +0100 Subject: [PATCH 068/247] Update Sample doc --- src/distributions/mod.rs | 3 +++ src/distributions/range.rs | 3 +++ src/distributions/range2.rs | 3 +++ src/lib.rs | 21 +++++++++++++++++++-- 4 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/distributions/mod.rs b/src/distributions/mod.rs index 816351a48e5..3041cc62654 100644 --- a/src/distributions/mod.rs +++ b/src/distributions/mod.rs @@ -80,6 +80,9 @@ impl<'a, T, D: Distribution> Distribution for &'a D { /// Generic trait for sampling random values from some distribution /// +/// TODO: quite possibly remove both this and `SimpleRand` since `Sample` is +/// more convenient and distributions like `Default` handle all the real work. +/// /// # Example /// ```rust /// use rand::distributions::{Rand, Default, Range}; diff --git a/src/distributions/range.rs b/src/distributions/range.rs index de7618edf90..9e2199ac6b8 100644 --- a/src/distributions/range.rs +++ b/src/distributions/range.rs @@ -22,6 +22,9 @@ use distributions::{Distribution, Uniform01, Rand}; /// This is a convenience wrapper around `Range`. If this function will be called /// repeatedly with the same arguments, one should use `Range`, as that will /// amortize the computations that allow for perfect uniformity. +/// +/// TODO: probably remove this function, since `Range::new` is the explicit +/// version and `Sample::gen_range` is the convenience version. /// /// # Panics /// diff --git a/src/distributions/range2.rs b/src/distributions/range2.rs index 2f12d0e9203..978e647e186 100644 --- a/src/distributions/range2.rs +++ b/src/distributions/range2.rs @@ -31,6 +31,9 @@ use distributions::{Distribution, Uniform01, Rand}; /// This is a convenience wrapper around `Range`. If this function will be called /// repeatedly with the same arguments, one should use `Range`, as that will /// amortize the computations that allow for perfect uniformity. +/// +/// TODO: probably remove this function, since `new_range` is the explicit +/// version and `Sample::gen_range` is the convenience version. /// /// # Panics /// diff --git a/src/lib.rs b/src/lib.rs index 37bfe3408ad..46dcb6665d3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -461,6 +461,11 @@ pub trait SeedableRng: Rng { use distributions::range::{Range, SampleRange}; +/// Extension trait on [`Rng`] with some convenience methods. +/// +/// This trait exists to allow syntax like `rng.gen()` and +/// `rng.gen_range(1, 7)`. None of the methods in this trait are any more than +/// wrappers around functionality which exists elsewhere in the trait. pub trait Sample: Rng { /// Sample a new value, using the given distribution. /// @@ -500,11 +505,23 @@ pub trait Sample: Rng { /// /// // simulate dice roll /// let x = rng.gen_range(1, 7); - /// assert!(1 <= x && x <= 6); /// ``` /// /// If the same range is used repeatedly, some work can be saved by - /// constructing the `Range` once and using it with `sample`. + /// constructing the `Range` once and using it with `sample`: + /// + /// ```rust + /// use rand::{thread_rng, Sample}; + /// use rand::distributions::Range; + /// + /// let mut rng = thread_rng(); + /// let die_range = Range::new(1, 7); + /// + /// for _ in 0..100 { + /// let x = rng.sample(die_range); + /// assert!(1 <= x && x <= 6); + /// } + /// ``` fn gen_range(&mut self, low: T, high: T) -> T { self.sample(Range::new(low, high)) } From a8b2871c351e2e081a830f78ca8a2aaa3b158dc5 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 15 Aug 2017 21:09:00 +0100 Subject: [PATCH 069/247] Add size_hint() to Iterator impls --- src/iter.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/iter.rs b/src/iter.rs index d26137d0b2c..db62f6a47eb 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -15,6 +15,7 @@ //! providing only a subset of functionality. use core::cmp::min; +use core::usize; use Rng; @@ -92,6 +93,7 @@ impl<'a, R:?Sized+'a, B, F> Iterator for Map<'a, R, B, F> where F: FnMut(&mut R) -> B { type Item = B; + fn next(&mut self) -> Option { match self.len { Some(0) => return None, @@ -101,6 +103,13 @@ impl<'a, R:?Sized+'a, B, F> Iterator for Map<'a, R, B, F> Some((self.f)(self.rng)) } + + fn size_hint(&self) -> (usize, Option) { + // If len == None we have an infinite iterator; usize::MAX is nearest + // available lower bound. Probably this suffices to make the following equal: + // rng.iter().take(n).map(f).size_hint() == rng.iter().map(f).take(n).size_hint() + self.len.map_or((usize::MAX, None), |len| (len, Some(len))) + } } #[derive(Debug)] @@ -116,6 +125,7 @@ impl<'a, R:?Sized+'a, U, F> Iterator for FlatMap<'a, R, U, F> where F: FnMut(&mut R) -> U, U: IntoIterator { type Item = ::Item; + fn next(&mut self) -> Option { loop { if let Some(ref mut inner) = self.frontiter { @@ -133,6 +143,11 @@ impl<'a, R:?Sized+'a, U, F> Iterator for FlatMap<'a, R, U, F> self.frontiter = Some(IntoIterator::into_iter((self.f)(self.rng))); } } + + fn size_hint(&self) -> (usize, Option) { + // See impl for Map above + self.len.map_or((usize::MAX, None), |len| (len, Some(len))) + } } #[cfg(test)] From 57efe7eeb9767a7faf2cc1da7149d2e0993641c8 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Wed, 16 Aug 2017 10:41:26 +0100 Subject: [PATCH 070/247] =?UTF-8?q?Remove=20distributions::range*::range?= =?UTF-8?q?=20functions,=20rename=20new=5Frange=20=E2=86=92=20range?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/distributions/mod.rs | 6 ++-- src/distributions/range.rs | 47 +++++-------------------- src/distributions/range2.rs | 69 +++++++++++-------------------------- src/lib.rs | 13 +++---- src/sequences/mod.rs | 13 ++++--- 5 files changed, 46 insertions(+), 102 deletions(-) diff --git a/src/distributions/mod.rs b/src/distributions/mod.rs index 3041cc62654..4d01169a5a3 100644 --- a/src/distributions/mod.rs +++ b/src/distributions/mod.rs @@ -22,7 +22,7 @@ use Rng; pub use self::default::Default; pub use self::uniform::{uniform, codepoint, ascii_word_char}; pub use self::uniform::{Uniform, Uniform01, Open01, Closed01, AsciiWordChar}; -pub use self::range::{range, Range}; +pub use self::range::{Range}; #[cfg(feature="std")] pub use self::gamma::{Gamma, ChiSquared, FisherF, StudentT}; @@ -31,6 +31,8 @@ pub use self::normal::{Normal, LogNormal}; #[cfg(feature="std")] pub use self::exponential::Exp; +use Sample; + mod default; mod uniform; #[cfg(feature="std")] @@ -62,7 +64,7 @@ pub mod exponential; /// println!("{}", weighted_bool(3, &mut rng)); /// ``` pub fn weighted_bool(n: u32, rng: &mut R) -> bool { - n <= 1 || range(0, n, rng) == 0 + n <= 1 || rng.gen_range(0, n) == 0 } /// Types (distributions) that can be used to create a random instance of `T`. diff --git a/src/distributions/range.rs b/src/distributions/range.rs index 9e2199ac6b8..d1d2f936d2c 100644 --- a/src/distributions/range.rs +++ b/src/distributions/range.rs @@ -17,35 +17,6 @@ use core::num::Wrapping as w; use Rng; use distributions::{Distribution, Uniform01, Rand}; -/// Generate a random value in the range [`low`, `high`). -/// -/// This is a convenience wrapper around `Range`. If this function will be called -/// repeatedly with the same arguments, one should use `Range`, as that will -/// amortize the computations that allow for perfect uniformity. -/// -/// TODO: probably remove this function, since `Range::new` is the explicit -/// version and `Sample::gen_range` is the convenience version. -/// -/// # Panics -/// -/// Panics if `low >= high`. -/// -/// # Example -/// -/// ```rust -/// use rand::distributions::range; -/// -/// let mut rng = rand::thread_rng(); -/// let n: u32 = range(0, 10, &mut rng); -/// println!("{}", n); -/// let m: f64 = range(-40.0f64, 1.3e5f64, &mut rng); -/// println!("{}", m); -/// ``` -pub fn range(low: T, high: T, rng: &mut R) -> T { - assert!(low < high, "distributions::range called with low >= high"); - SampleRange::construct_range(low, high).sample(rng) -} - /// Sample values uniformly between two bounds. /// /// This gives a uniform distribution (assuming the RNG used to sample @@ -209,23 +180,23 @@ float_impl! { f64 } mod tests { use {Rng, thread_rng}; use distributions::{Distribution, Rand, Uniform01}; - use distributions::range::{Range, range, SampleRange}; + use distributions::range::{Range, SampleRange}; #[test] fn test_fn_range() { let mut r = thread_rng(); for _ in 0..1000 { - let a = range(-3, 42, &mut r); + let a = Range::new(-3, 42).sample(&mut r); assert!(a >= -3 && a < 42); - assert_eq!(range(0, 1, &mut r), 0); - assert_eq!(range(-12, -11, &mut r), -12); + assert_eq!(Range::new(0, 1).sample(&mut r), 0); + assert_eq!(Range::new(-12, -11).sample(&mut r), -12); } for _ in 0..1000 { - let a = range(10, 42, &mut r); + let a = Range::new(10, 42).sample(&mut r); assert!(a >= 10 && a < 42); - assert_eq!(range(0, 1, &mut r), 0); - assert_eq!(range(3_000_000, 3_000_001, &mut r), 3_000_000); + assert_eq!(Range::new(0, 1).sample(&mut r), 0); + assert_eq!(Range::new(3_000_000, 3_000_001).sample(&mut r), 3_000_000); } } @@ -234,14 +205,14 @@ mod tests { #[should_panic] fn test_fn_range_panic_int() { let mut r = thread_rng(); - range(5, -2, &mut r); + Range::new(5, -2).sample(&mut r); } #[test] #[should_panic] fn test_fn_range_panic_usize() { let mut r = thread_rng(); - range(5, 2, &mut r); + Range::new(5, 2).sample(&mut r); } #[should_panic] diff --git a/src/distributions/range2.rs b/src/distributions/range2.rs index 978e647e186..a7fc5e48862 100644 --- a/src/distributions/range2.rs +++ b/src/distributions/range2.rs @@ -26,38 +26,9 @@ use core::num::Wrapping as w; use Rng; use distributions::{Distribution, Uniform01, Rand}; -/// Generate a random value in the range [`low`, `high`). -/// -/// This is a convenience wrapper around `Range`. If this function will be called -/// repeatedly with the same arguments, one should use `Range`, as that will -/// amortize the computations that allow for perfect uniformity. -/// -/// TODO: probably remove this function, since `new_range` is the explicit -/// version and `Sample::gen_range` is the convenience version. -/// -/// # Panics -/// -/// Panics if `low >= high`. -/// -/// # Example -/// -/// ```rust -/// use rand::distributions::range2::range; -/// -/// let mut rng = rand::thread_rng(); -/// let n: u32 = range(0, 10, &mut rng); -/// println!("{}", n); -/// let m: f64 = range(-40.0f64, 1.3e5f64, &mut rng); -/// println!("{}", m); -/// ``` -pub fn range(low: X, high: X, rng: &mut R) -> X { - assert!(low < high, "distributions::range called with low >= high"); - Range { inner: X::T::new(low, high) }.sample(rng) -} - /// Convenience function to construct a `Range` -pub fn new_range(low: X, high: X) -> Range { - assert!(low < high, "new_range called with `low >= high`"); +pub fn range(low: X, high: X) -> Range { + assert!(low < high, "range called with `low >= high`"); Range { inner: RangeImpl::new(low, high) } } @@ -79,10 +50,10 @@ pub fn new_range(low: X, high: X) -> Range { /// /// ```rust /// use rand::distributions::Distribution; -/// use rand::distributions::range2::new_range; +/// use rand::distributions::range2::range; /// /// fn main() { -/// let between = new_range(10, 10000); +/// let between = range(10, 10000); /// let mut rng = rand::thread_rng(); /// let mut sum = 0; /// for _ in 0..1000 { @@ -241,24 +212,24 @@ range_float_impl! { f64 } #[cfg(test)] mod tests { use {Rng, thread_rng}; - use distributions::Rand; - use distributions::range2::{Range, range, new_range, RangeImpl, RangeFloat}; + use distributions::{Rand, Distribution}; + use distributions::range2::{Range, range, RangeImpl, RangeFloat}; #[test] fn test_fn_range() { let mut r = thread_rng(); for _ in 0..1000 { - let a = range(-3, 42, &mut r); + let a = range(-3, 42).sample(&mut r); assert!(a >= -3 && a < 42); - assert_eq!(range(0, 1, &mut r), 0); - assert_eq!(range(-12, -11, &mut r), -12); + assert_eq!(range(0, 1).sample(&mut r), 0); + assert_eq!(range(-12, -11).sample(&mut r), -12); } for _ in 0..1000 { - let a = range(10, 42, &mut r); + let a = range(10, 42).sample(&mut r); assert!(a >= 10 && a < 42); - assert_eq!(range(0, 1, &mut r), 0); - assert_eq!(range(3_000_000, 3_000_001, &mut r), 3_000_000); + assert_eq!(range(0, 1).sample(&mut r), 0); + assert_eq!(range(3_000_000, 3_000_001).sample(&mut r), 3_000_000); } } @@ -266,25 +237,25 @@ mod tests { #[should_panic] fn test_fn_range_panic_int() { let mut r = thread_rng(); - range(5, -2, &mut r); + range(5, -2).sample(&mut r); } #[test] #[should_panic] fn test_fn_range_panic_usize() { let mut r = thread_rng(); - range(5, 2, &mut r); + range(5, 2).sample(&mut r); } #[should_panic] #[test] fn test_range_bad_limits_equal() { - new_range(10, 10); + range(10, 10); } #[should_panic] #[test] fn test_range_bad_limits_flipped() { - new_range(10, 5); + range(10, 5); } #[test] @@ -297,9 +268,9 @@ mod tests { (10, 127), (::std::$ty::MIN, ::std::$ty::MAX)]; for &(low, high) in v.iter() { - let range = new_range(low, high); + let my_range = range(low, high); for _ in 0..1000 { - let v: $ty = Rand::rand(&mut rng, range); + let v: $ty = Rand::rand(&mut rng, my_range); assert!(low <= v && v < high); } } @@ -321,9 +292,9 @@ mod tests { (1e-35, 1e-25), (-1e35, 1e35)]; for &(low, high) in v.iter() { - let range = new_range(low, high); + let my_range = range(low, high); for _ in 0..1000 { - let v: $ty = Rand::rand(&mut rng, range); + let v: $ty = Rand::rand(&mut rng, my_range); assert!(low <= v && v < high); } } diff --git a/src/lib.rs b/src/lib.rs index 46dcb6665d3..5326237d108 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -459,7 +459,7 @@ pub trait SeedableRng: Rng { fn from_seed(seed: Seed) -> Self; } -use distributions::range::{Range, SampleRange}; +use distributions::range::SampleRange; /// Extension trait on [`Rng`] with some convenience methods. /// @@ -523,7 +523,8 @@ pub trait Sample: Rng { /// } /// ``` fn gen_range(&mut self, low: T, high: T) -> T { - self.sample(Range::new(low, high)) + assert!(low < high, "Sample::gen_range called with low >= high"); + SampleRange::construct_range(low, high).sample(self) } /// Construct an iterator on an `Rng`. @@ -632,7 +633,7 @@ impl<'a> SeedableRng<&'a [usize]> for StdRng { #[cfg(test)] mod test { use {Rng, thread_rng, SeedableRng, StdRng, ConstRng, iter, Sample}; - use distributions::{uniform, range, ascii_word_char}; + use distributions::{uniform, ascii_word_char}; use distributions::{Uniform, Range, Exp}; use sequences::Shuffle; use std::iter::repeat; @@ -698,7 +699,7 @@ mod test { v.shuffle(&mut r); let b: &[_] = &[1, 1, 1]; assert_eq!(v, b); - assert_eq!(range(0, 1, &mut r), 0); + assert_eq!(r.gen_range(0, 1), 0); } #[test] @@ -712,7 +713,7 @@ mod test { v[..].shuffle(r); let b: &[_] = &[1, 1, 1]; assert_eq!(v, b); - assert_eq!(range(0, 1, &mut r), 0); + assert_eq!(r.gen_range(0, 1), 0); } { let mut r = Box::new(rng) as Box; @@ -722,7 +723,7 @@ mod test { v[..].shuffle(&mut *r); let b: &[_] = &[1, 1, 1]; assert_eq!(v, b); - assert_eq!(range(0, 1, &mut r), 0); + assert_eq!(r.gen_range(0, 1), 0); } } diff --git a/src/sequences/mod.rs b/src/sequences/mod.rs index 3bd35fa0cd7..19be9be673f 100644 --- a/src/sequences/mod.rs +++ b/src/sequences/mod.rs @@ -10,8 +10,7 @@ //! Random operations on sequences -use Rng; -use distributions::range; +use {Rng, Sample}; #[cfg(feature="std")] pub use self::weighted::{Weighted, WeightedChoice}; @@ -44,7 +43,7 @@ impl<'a, T> Choose<&'a T> for &'a [T] { if self.is_empty() { None } else { - Some(&self[range(0, self.len(), rng)]) + Some(&self[rng.gen_range(0, self.len())]) } } } @@ -55,7 +54,7 @@ impl<'a, T> Choose<&'a mut T> for &'a mut [T] { None } else { let len = self.len(); - Some(&mut self[range(0, len, rng)]) + Some(&mut self[rng.gen_range(0, len)]) } } } @@ -66,7 +65,7 @@ impl Choose for Vec { if self.is_empty() { None } else { - let index = range(0, self.len(), rng); + let index = rng.gen_range(0, self.len()); self.drain(index..).next() } } @@ -95,7 +94,7 @@ pub fn sample(rng: &mut R, iterable: I, amount: usize) -> Vec // continue unless the iterator was exhausted if reservoir.len() == amount { for (i, elem) in iter.enumerate() { - let k = range(0, i + 1 + amount, rng); + let k = rng.gen_range(0, i + 1 + amount); if let Some(spot) = reservoir.get_mut(k) { *spot = elem; } @@ -134,7 +133,7 @@ impl<'a, T> Shuffle for &'a mut [T] { // invariant: elements with index >= i have been locked in place. i -= 1; // lock element i in place. - self.swap(i, range(0, i + 1, rng)); + self.swap(i, rng.gen_range(0, i + 1)); } } } From 697b83e82ece37b095d727af8bedcaa9d862e919 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Thu, 17 Aug 2017 11:17:02 +0100 Subject: [PATCH 071/247] Add no_std build test to travis; fix no_std build --- .travis.yml | 1 + src/prng/chacha.rs | 5 ++++- src/prng/isaac.rs | 6 +++++- src/prng/xorshift.rs | 5 ++++- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1cb2e68cbb8..480895bf0a1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,6 +15,7 @@ matrix: - cargo test - cargo test --features nightly - cargo test --manifest-path rand-derive/Cargo.toml + - cargo build --no-default-features - cargo doc --no-deps --features nightly script: - cargo test diff --git a/src/prng/chacha.rs b/src/prng/chacha.rs index 9f16dab7d92..567765850ec 100644 --- a/src/prng/chacha.rs +++ b/src/prng/chacha.rs @@ -11,7 +11,9 @@ //! The ChaCha random number generator. use core::num::Wrapping as w; -use {OsRng, Rng, SeedableRng}; +use {Rng, SeedableRng}; +#[cfg(feature="std")] +use OsRng; #[allow(bad_style)] type w32 = w; @@ -82,6 +84,7 @@ fn core(output: &mut [w32; STATE_WORDS], input: &[w32; STATE_WORDS]) { impl ChaChaRng { /// Creates a new `ChaChaRng`, automatically seeded via `OsRng`. + #[cfg(feature="std")] pub fn new() -> ChaChaRng { match OsRng::new() { Ok(mut r) => ChaChaRng::new_from_rng(&mut r), diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index e0fc5de766a..7d9a7bbbd03 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -17,7 +17,9 @@ use core::iter::repeat; use core::num::Wrapping as w; use core::fmt; -use {OsRng, Rng, SeedableRng}; +use {Rng, SeedableRng}; +#[cfg(feature="std")] +use OsRng; /// Select 32- or 64-bit variant dependent on pointer size. #[cfg(target_pointer_width = "32")] @@ -62,6 +64,7 @@ static EMPTY: IsaacRng = IsaacRng { impl IsaacRng { /// Creates a new `IsaacRng`, automatically seeded via `OsRng`. + #[cfg(feature="std")] pub fn new() -> IsaacRng { match OsRng::new() { Ok(mut r) => IsaacRng::new_from_rng(&mut r), @@ -321,6 +324,7 @@ static EMPTY_64: Isaac64Rng = Isaac64Rng { impl Isaac64Rng { /// Creates a new `Isaac64Rng`, automatically seeded via `OsRng`. + #[cfg(feature="std")] pub fn new() -> Isaac64Rng { match OsRng::new() { Ok(mut r) => Isaac64Rng::new_from_rng(&mut r), diff --git a/src/prng/xorshift.rs b/src/prng/xorshift.rs index 3c96c004c12..0c716b57e75 100644 --- a/src/prng/xorshift.rs +++ b/src/prng/xorshift.rs @@ -11,7 +11,9 @@ //! Xorshift generators use core::num::Wrapping as w; -use {OsRng, Rng, SeedableRng}; +use {Rng, SeedableRng}; +#[cfg(feature="std")] +use OsRng; /// An Xorshift[1] random number /// generator. @@ -34,6 +36,7 @@ pub struct XorShiftRng { impl XorShiftRng { /// Creates a new `XorShiftRng`, automatically seeded via `OsRng`. + #[cfg(feature="std")] pub fn new() -> XorShiftRng { match OsRng::new() { Ok(mut r) => XorShiftRng::new_from_rng(&mut r), From ecc8a38bf81c514712791b0c6996d4ad5b3bd5ed Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Thu, 17 Aug 2017 11:19:01 +0100 Subject: [PATCH 072/247] Add 128-bit integer support to range types Courtesy of @pjhades https://github.com/rust-lang-nursery/rand/pull/163 --- src/distributions/range.rs | 8 +++++++- src/distributions/range2.rs | 8 +++++++- src/distributions/uniform.rs | 2 +- src/lib.rs | 2 +- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/distributions/range.rs b/src/distributions/range.rs index d1d2f936d2c..b086df8ca43 100644 --- a/src/distributions/range.rs +++ b/src/distributions/range.rs @@ -148,11 +148,15 @@ integer_impl! { i8, u8 } integer_impl! { i16, u16 } integer_impl! { i32, u32 } integer_impl! { i64, u64 } +#[cfg(feature = "i128_support")] +integer_impl! { i128, u128 } integer_impl! { isize, usize } integer_impl! { u8, u8 } integer_impl! { u16, u16 } integer_impl! { u32, u32 } integer_impl! { u64, u64 } +#[cfg(feature = "i128_support")] +integer_impl! { u128, u128 } integer_impl! { usize, usize } macro_rules! float_impl { @@ -246,7 +250,9 @@ mod tests { }} } t!(i8, i16, i32, i64, isize, - u8, u16, u32, u64, usize) + u8, u16, u32, u64, usize); + #[cfg(feature = "i128_support")] + t!(i128, u128) } #[test] diff --git a/src/distributions/range2.rs b/src/distributions/range2.rs index a7fc5e48862..9ba055c48de 100644 --- a/src/distributions/range2.rs +++ b/src/distributions/range2.rs @@ -168,11 +168,15 @@ range_int_impl! { i8, u8 } range_int_impl! { i16, u16 } range_int_impl! { i32, u32 } range_int_impl! { i64, u64 } +#[cfg(feature = "i128_support")] +range_int_impl! { i128, u128 } range_int_impl! { isize, usize } range_int_impl! { u8, u8 } range_int_impl! { u16, u16 } range_int_impl! { u32, u32 } range_int_impl! { u64, u64 } +#[cfg(feature = "i128_support")] +range_int_impl! { u128, u128 } range_int_impl! { usize, usize } /// Implementation of `RangeImpl` for float types. @@ -278,7 +282,9 @@ mod tests { }} } t!(i8, i16, i32, i64, isize, - u8, u16, u32, u64, usize) + u8, u16, u32, u64, usize); + #[cfg(feature = "i128_support")] + t!(i128, u128) } #[test] diff --git a/src/distributions/uniform.rs b/src/distributions/uniform.rs index c2ba56af8f9..b65100ee2fb 100644 --- a/src/distributions/uniform.rs +++ b/src/distributions/uniform.rs @@ -129,7 +129,7 @@ impl Distribution for Uniform { impl Distribution for Uniform { #[inline] fn sample(&self, rng: &mut R) -> i128 { - rng.gen::() as i128 + rng.next_u128() as i128 } } diff --git a/src/lib.rs b/src/lib.rs index 5326237d108..547eefce796 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -246,7 +246,7 @@ #![deny(missing_debug_implementations)] #![cfg_attr(not(feature="std"), no_std)] -#![cfg_attr(feature = "i128_support", feature(i128_type))] +#![cfg_attr(feature = "i128_support", feature(i128_type, i128))] // We need to use several items from "core" for no_std support. #[cfg(feature="std")] From 831553c8d60daa40683e4019ba8650d244f5d6e0 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Thu, 17 Aug 2017 15:42:18 +0100 Subject: [PATCH 073/247] Add range benchmarks --- benches/distributions.rs | 58 +++++++++++++++++++++++++++++++++++++++- benches/misc.rs | 3 ++- 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/benches/distributions.rs b/benches/distributions.rs index 352fe9de012..ea797c630e9 100644 --- a/benches/distributions.rs +++ b/benches/distributions.rs @@ -8,8 +8,10 @@ const RAND_BENCH_N: u64 = 1000; use std::mem::size_of; use test::Bencher; -use rand::{XorShiftRng, Default, Rand}; +use rand::{Default, Rand}; +use rand::prng::XorShiftRng; use rand::distributions::Distribution; +use rand::distributions::{Range, range2}; use rand::distributions::exponential::Exp; use rand::distributions::normal::{Normal, LogNormal}; use rand::distributions::gamma::Gamma; @@ -28,6 +30,60 @@ fn distr_baseline(b: &mut Bencher) { } +#[bench] +fn distr_range_int(b: &mut Bencher) { + let mut rng = XorShiftRng::new(); + let distr = Range::new(3i64, 134217671i64); + + b.iter(|| { + for _ in 0..::RAND_BENCH_N { + distr.sample(&mut rng); + } + }); + b.bytes = size_of::() as u64 * ::RAND_BENCH_N; +} + +#[bench] +fn distr_range_float(b: &mut Bencher) { + let mut rng = XorShiftRng::new(); + let distr = Range::new(2.26f64, 2.319f64); + + b.iter(|| { + for _ in 0..::RAND_BENCH_N { + distr.sample(&mut rng); + } + }); + b.bytes = size_of::() as u64 * ::RAND_BENCH_N; +} + + +#[bench] +fn distr_range2_int(b: &mut Bencher) { + let mut rng = XorShiftRng::new(); + let distr = range2::range(3i64, 134217671i64); + + b.iter(|| { + for _ in 0..::RAND_BENCH_N { + distr.sample(&mut rng); + } + }); + b.bytes = size_of::() as u64 * ::RAND_BENCH_N; +} + +#[bench] +fn distr_range2_float(b: &mut Bencher) { + let mut rng = XorShiftRng::new(); + let distr = range2::range(2.26f64, 2.319f64); + + b.iter(|| { + for _ in 0..::RAND_BENCH_N { + distr.sample(&mut rng); + } + }); + b.bytes = size_of::() as u64 * ::RAND_BENCH_N; +} + + #[bench] fn distr_exp(b: &mut Bencher) { let mut rng = XorShiftRng::new(); diff --git a/benches/misc.rs b/benches/misc.rs index 5c7e58036d4..874b3758669 100644 --- a/benches/misc.rs +++ b/benches/misc.rs @@ -7,7 +7,8 @@ const RAND_BENCH_N: u64 = 1000; use std::mem::size_of; use test::{black_box, Bencher}; -use rand::{StdRng, XorShiftRng}; +use rand::StdRng; +use rand::prng::XorShiftRng; use rand::sequences::{sample, Shuffle}; use rand::distributions::{Rand, Uniform, Uniform01}; From 18e8dab0283600958d290fb424b34466bcf1823f Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Thu, 17 Aug 2017 15:48:37 +0100 Subject: [PATCH 074/247] Remove unnecessary mutability --- src/distributions/default.rs | 2 +- src/iter.rs | 2 +- src/lib.rs | 6 +++--- src/os.rs | 4 ++-- src/sequences/mod.rs | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/distributions/default.rs b/src/distributions/default.rs index 6cdc6513b02..054598e774f 100644 --- a/src/distributions/default.rs +++ b/src/distributions/default.rs @@ -70,7 +70,7 @@ mod tests { #[test] fn test_types() { - let mut rng: &mut Rng = &mut thread_rng(); + let rng: &mut Rng = &mut thread_rng(); fn do_test>(rng: &mut Rng) -> T { T::rand(rng, Default) } diff --git a/src/iter.rs b/src/iter.rs index db62f6a47eb..70bfd8e9b66 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -172,7 +172,7 @@ mod tests { #[test] fn test_dyn_dispatch() { - let mut r: &mut Rng = &mut thread_rng(); + let r: &mut Rng = &mut thread_rng(); let x: String = iter(r).take(10).map(|rng| ascii_word_char(rng)).collect(); assert_eq!(x.len(), 10); diff --git a/src/lib.rs b/src/lib.rs index 547eefce796..f7b829e1d0d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -706,9 +706,9 @@ mod test { fn test_rng_trait_object() { let mut rng = thread_rng(); { - let mut r = &mut rng as &mut Rng; + let r = &mut rng as &mut Rng; r.next_u32(); - uniform::(&mut r); + uniform::(r); let mut v = [1, 1, 1]; v[..].shuffle(r); let b: &[_] = &[1, 1, 1]; @@ -757,7 +757,7 @@ mod test { let _b = rng.sample(Range::new(-2, 15)); // use a dynamic Rng type: - let mut rng: &mut Rng = &mut thread_rng(); + let rng: &mut Rng = &mut thread_rng(); let _c = rng.sample(Exp::new(2.0)); } diff --git a/src/os.rs b/src/os.rs index 2dafc1bc6df..7bac4d3dcbf 100644 --- a/src/os.rs +++ b/src/os.rs @@ -53,13 +53,13 @@ impl fmt::Debug for OsRng { } } -fn next_u32(mut fill_buf: &mut FnMut(&mut [u8])) -> u32 { +fn next_u32(fill_buf: &mut FnMut(&mut [u8])) -> u32 { let mut buf: [u8; 4] = [0; 4]; fill_buf(&mut buf); unsafe { mem::transmute::<[u8; 4], u32>(buf) } } -fn next_u64(mut fill_buf: &mut FnMut(&mut [u8])) -> u64 { +fn next_u64(fill_buf: &mut FnMut(&mut [u8])) -> u64 { let mut buf: [u8; 8] = [0; 8]; fill_buf(&mut buf); unsafe { mem::transmute::<[u8; 8], u64>(buf) } diff --git a/src/sequences/mod.rs b/src/sequences/mod.rs index 19be9be673f..89ea650b2da 100644 --- a/src/sequences/mod.rs +++ b/src/sequences/mod.rs @@ -199,7 +199,7 @@ mod test { #[test] fn dyn_dispatch() { - let mut r: &mut Rng = &mut thread_rng(); + let r: &mut Rng = &mut thread_rng(); assert_eq!([7, 7][..].choose(r), Some(&7)); From e73622f440c7f5808b0c36f1bbfef7b06fb1e41a Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Thu, 17 Aug 2017 15:49:15 +0100 Subject: [PATCH 075/247] Update travis and appveyor: require Rust 1.18, don't test rand-derive --- .travis.yml | 4 +--- appveyor.yml | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 480895bf0a1..fb6a2df233d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ before_script: matrix: include: - - rust: 1.15.0 + - rust: 1.18.0 - rust: stable - rust: stable os: osx @@ -14,12 +14,10 @@ matrix: script: - cargo test - cargo test --features nightly - - cargo test --manifest-path rand-derive/Cargo.toml - cargo build --no-default-features - cargo doc --no-deps --features nightly script: - cargo test - - cargo test --manifest-path rand-derive/Cargo.toml after_success: - travis-cargo --only nightly doc-upload env: diff --git a/appveyor.yml b/appveyor.yml index 39c6a180be8..efaf5dacc11 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -34,4 +34,3 @@ build: false test_script: - cargo test - cargo test --features nightly - - cargo test --manifest-path rand-derive/Cargo.toml From de24228f59f1a62ee0b7702cb7e389f7f352a099 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 21 Aug 2017 12:27:06 +0100 Subject: [PATCH 076/247] Remove next_u8 and next_u16 functions from Rng --- src/distributions/uniform.rs | 4 ++-- src/lib.rs | 30 ------------------------------ 2 files changed, 2 insertions(+), 32 deletions(-) diff --git a/src/distributions/uniform.rs b/src/distributions/uniform.rs index b65100ee2fb..5694cd86e8b 100644 --- a/src/distributions/uniform.rs +++ b/src/distributions/uniform.rs @@ -147,14 +147,14 @@ impl Distribution for Uniform { impl Distribution for Uniform { #[inline] fn sample(&self, rng: &mut R) -> u8 { - rng.next_u8() + rng.next_u32() as u8 } } impl Distribution for Uniform { #[inline] fn sample(&self, rng: &mut R) -> u16 { - rng.next_u16() + rng.next_u32() as u16 } } diff --git a/src/lib.rs b/src/lib.rs index f7b829e1d0d..2540281812c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -282,20 +282,6 @@ mod thread_local; /// A random number generator. pub trait Rng: Debug { - /// Return the next random u8. - /// - /// By default this is implemented in terms of `next_u32`. - fn next_u8(&mut self) -> u8 { - self.next_u32() as u8 - } - - /// Return the next random u16. - /// - /// By default this is implemented in terms of `next_u32`. - fn next_u16(&mut self) -> u16 { - self.next_u32() as u16 - } - /// Return the next random u32. // FIXME #rust-lang/rfcs#628: Should be implemented in terms of next_u64 fn next_u32(&mut self) -> u32; @@ -371,14 +357,6 @@ pub trait Rng: Debug { } impl<'a, R: ?Sized> Rng for &'a mut R where R: Rng { - fn next_u8(&mut self) -> u8 { - (**self).next_u8() - } - - fn next_u16(&mut self) -> u16 { - (**self).next_u16() - } - fn next_u32(&mut self) -> u32 { (**self).next_u32() } @@ -399,14 +377,6 @@ impl<'a, R: ?Sized> Rng for &'a mut R where R: Rng { #[cfg(feature="std")] impl Rng for Box where R: Rng { - fn next_u8(&mut self) -> u8 { - (**self).next_u8() - } - - fn next_u16(&mut self) -> u16 { - (**self).next_u16() - } - fn next_u32(&mut self) -> u32 { (**self).next_u32() } From cca68bbd6404cd18fd0a8fc8487cad067958a367 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 21 Aug 2017 12:29:09 +0100 Subject: [PATCH 077/247] Remove Rng: Debug requirement --- src/lib.rs | 3 +-- src/thread_local.rs | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 2540281812c..e2c1afd3bb8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -252,7 +252,6 @@ #[cfg(feature="std")] extern crate core; -use core::fmt::Debug; use core::mem::transmute; #[cfg(feature="std")] @@ -281,7 +280,7 @@ mod read; mod thread_local; /// A random number generator. -pub trait Rng: Debug { +pub trait Rng { /// Return the next random u32. // FIXME #rust-lang/rfcs#628: Should be implemented in terms of next_u64 fn next_u32(&mut self) -> u32; diff --git a/src/thread_local.rs b/src/thread_local.rs index 96c19afed43..06eeb2ccef4 100644 --- a/src/thread_local.rs +++ b/src/thread_local.rs @@ -43,7 +43,8 @@ use {Rng, OsRng, Rand, Default}; // ThreadRngReseeder); /// The thread-local RNG. -#[derive(Clone, Debug)] +#[derive(Clone)] +#[allow(missing_debug_implementations)] pub struct ThreadRng { rng: Rc>>, } From 2a5937702017de13243868ad2ddd395dfec72f47 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 21 Aug 2017 13:34:23 +0100 Subject: [PATCH 078/247] =?UTF-8?q?Add=20CryptoError;=20make=20RNG::new=20?= =?UTF-8?q?return=20Result;=20rename=20new=5Ffrom=5Fr?= =?UTF-8?q?ng=20=E2=86=92=20from=5Frng?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- benches/generators.rs | 8 ++++---- src/lib.rs | 16 ++++++++++++++-- src/os.rs | 35 ++++++++++++++++++----------------- src/prng/chacha.rs | 12 +++++------- src/prng/isaac.rs | 22 +++++++++------------- src/prng/xorshift.rs | 12 +++++------- src/read.rs | 12 ++++++------ 8 files changed, 62 insertions(+), 57 deletions(-) diff --git a/README.md b/README.md index 7caa0fc9e1b..cdbdb046931 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ It is also possible to use other generators types, which have a similar interfac ```rust use rand::{thread_rng, ChaChaRng, distributions}; -let mut rng = ChaChaRng::new_from_rng(&mut thread_rng()); +let mut rng = ChaChaRng::from_rng(&mut thread_rng()).unwrap(); println!("random between 0-9: {}", distributions::range(0, 10, &mut rng)); ``` diff --git a/benches/generators.rs b/benches/generators.rs index b2db489f897..1b4f2276d1d 100644 --- a/benches/generators.rs +++ b/benches/generators.rs @@ -13,7 +13,7 @@ use rand::prng::{XorShiftRng, IsaacRng, Isaac64Rng, ChaChaRng}; #[bench] fn gen_usize_xorshift(b: &mut Bencher) { - let mut rng = XorShiftRng::new_from_rng(&mut OsRng::new().unwrap()); + let mut rng = XorShiftRng::new().unwrap(); b.iter(|| { for _ in 0..RAND_BENCH_N { black_box(usize::rand(&mut rng, Default)); @@ -24,7 +24,7 @@ fn gen_usize_xorshift(b: &mut Bencher) { #[bench] fn gen_usize_isaac(b: &mut Bencher) { - let mut rng = IsaacRng::new_from_rng(&mut OsRng::new().unwrap()); + let mut rng = IsaacRng::new().unwrap(); b.iter(|| { for _ in 0..RAND_BENCH_N { black_box(usize::rand(&mut rng, Default)); @@ -35,7 +35,7 @@ fn gen_usize_isaac(b: &mut Bencher) { #[bench] fn gen_usize_isaac64(b: &mut Bencher) { - let mut rng = Isaac64Rng::new_from_rng(&mut OsRng::new().unwrap()); + let mut rng = Isaac64Rng::new().unwrap(); b.iter(|| { for _ in 0..RAND_BENCH_N { black_box(usize::rand(&mut rng, Default)); @@ -46,7 +46,7 @@ fn gen_usize_isaac64(b: &mut Bencher) { #[bench] fn gen_usize_chacha(b: &mut Bencher) { - let mut rng = ChaChaRng::new_from_rng(&mut OsRng::new().unwrap()); + let mut rng = ChaChaRng::new().unwrap(); b.iter(|| { for _ in 0..RAND_BENCH_N { black_box(usize::rand(&mut rng, Default)); diff --git a/src/lib.rs b/src/lib.rs index e2c1afd3bb8..75836e6b259 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -279,6 +279,12 @@ mod read; #[cfg(feature="std")] mod thread_local; +/// Error type for cryptographic generators. Only operating system and hardware +/// generators should be able to fail. In such cases there is little that can +/// be done besides try again later. +#[derive(Debug)] +pub struct CryptoError; + /// A random number generator. pub trait Rng { /// Return the next random u32. @@ -569,8 +575,8 @@ impl StdRng { /// /// Reading the randomness from the OS may fail, and any error is /// propagated via the `io::Result` return value. - pub fn new() -> ::std::io::Result { - OsRng::new().map(|mut r| StdRng { rng: IsaacWordRng::new_from_rng(&mut r) }) + pub fn new() -> Result { + IsaacWordRng::new().map(|rng| StdRng { rng }) } } @@ -598,6 +604,12 @@ impl<'a> SeedableRng<&'a [usize]> for StdRng { } } +impl From<::std::io::Error> for CryptoError { + fn from(_: ::std::io::Error) -> CryptoError { + CryptoError + } +} + #[cfg(test)] mod test { diff --git a/src/os.rs b/src/os.rs index 7bac4d3dcbf..f00672f6592 100644 --- a/src/os.rs +++ b/src/os.rs @@ -11,8 +11,9 @@ //! Interfaces to the operating system provided random number //! generators. -use std::{io, mem, fmt}; -use Rng; +use std::{mem, fmt}; + +use {Rng, CryptoError}; /// A random number generator that retrieves randomness straight from /// the operating system. Platform sources: @@ -36,7 +37,7 @@ pub struct OsRng(imp::OsRng); impl OsRng { /// Create a new `OsRng`. - pub fn new() -> io::Result { + pub fn new() -> Result { imp::OsRng::new().map(OsRng) } } @@ -79,7 +80,7 @@ mod imp { use std::io; use std::fs::File; - use Rng; + use {Rng, CryptoError}; use read::ReadRng; #[cfg(all(target_os = "linux", @@ -183,12 +184,12 @@ mod imp { } impl OsRng { - pub fn new() -> io::Result { + pub fn new() -> Result { if is_getrandom_available() { return Ok(OsRng { inner: OsGetrandomRng }); } - let reader = try!(File::open("/dev/urandom")); + let reader = File::open("/dev/urandom")?; let reader_rng = ReadRng::new(reader); Ok(OsRng { inner: OsReadRng(reader_rng) }) @@ -242,7 +243,7 @@ mod imp { } impl OsRng { - pub fn new() -> io::Result { + pub fn new() -> Result { Ok(OsRng) } } @@ -278,7 +279,7 @@ mod imp { pub struct OsRng; impl OsRng { - pub fn new() -> io::Result { + pub fn new() -> Result { Ok(OsRng) } } @@ -322,7 +323,7 @@ mod imp { pub struct OsRng; impl OsRng { - pub fn new() -> io::Result { + pub fn new() -> Result { Ok(OsRng) } } @@ -362,8 +363,8 @@ mod imp { } impl OsRng { - pub fn new() -> io::Result { - let reader = try!(File::open("rand:")); + pub fn new() -> Result { + let reader = File::open("rand:")?; let reader_rng = ReadRng::new(reader); Ok(OsRng { inner: reader_rng }) @@ -396,7 +397,7 @@ mod imp { pub struct OsRng; impl OsRng { - pub fn new() -> io::Result { + pub fn new() -> Result { Ok(OsRng) } } @@ -442,7 +443,7 @@ mod imp { pub struct OsRng; impl OsRng { - pub fn new() -> io::Result { + pub fn new() -> Result { Ok(OsRng) } } @@ -501,7 +502,7 @@ mod imp { } impl OsRng { - pub fn new() -> io::Result { + pub fn new() -> Result { let mut iface = NaClIRTRandom { get_random_bytes: None, }; @@ -515,9 +516,9 @@ mod imp { let result = OsRng(iface.get_random_bytes.take().unwrap()); Ok(result) } else { - let error = io::ErrorKind::NotFound; - let error = io::Error::new(error, "IRT random interface missing"); - Err(error) + // let error = io::ErrorKind::NotFound; + // let error = io::Error::new(error, "IRT random interface missing"); + Err(CryptoError) } } } diff --git a/src/prng/chacha.rs b/src/prng/chacha.rs index 567765850ec..1ce1e7fe4d3 100644 --- a/src/prng/chacha.rs +++ b/src/prng/chacha.rs @@ -11,7 +11,7 @@ //! The ChaCha random number generator. use core::num::Wrapping as w; -use {Rng, SeedableRng}; +use {Rng, SeedableRng, CryptoError}; #[cfg(feature="std")] use OsRng; @@ -85,11 +85,9 @@ fn core(output: &mut [w32; STATE_WORDS], input: &[w32; STATE_WORDS]) { impl ChaChaRng { /// Creates a new `ChaChaRng`, automatically seeded via `OsRng`. #[cfg(feature="std")] - pub fn new() -> ChaChaRng { - match OsRng::new() { - Ok(mut r) => ChaChaRng::new_from_rng(&mut r), - Err(e) => panic!("ChaChaRng::new: failed to get seed from OS: {:?}", e) - } + pub fn new() -> Result { + let mut r = OsRng::new()?; + Ok(ChaChaRng::from_rng(&mut r)) } /// Create an ChaCha random number generator using the default @@ -124,7 +122,7 @@ impl ChaChaRng { /// free entropy gained. In some cases where the parent and child RNGs use /// the same algorithm, both generate the same output sequences (possibly /// with a small lag). - pub fn new_from_rng(other: &mut R) -> ChaChaRng { + pub fn from_rng(other: &mut R) -> ChaChaRng { let mut key : [u32; KEY_WORDS] = [0; KEY_WORDS]; for word in key.iter_mut() { *word = other.next_u32(); diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index 7d9a7bbbd03..c52c83e428b 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -17,7 +17,7 @@ use core::iter::repeat; use core::num::Wrapping as w; use core::fmt; -use {Rng, SeedableRng}; +use {Rng, SeedableRng, CryptoError}; #[cfg(feature="std")] use OsRng; @@ -65,11 +65,9 @@ static EMPTY: IsaacRng = IsaacRng { impl IsaacRng { /// Creates a new `IsaacRng`, automatically seeded via `OsRng`. #[cfg(feature="std")] - pub fn new() -> IsaacRng { - match OsRng::new() { - Ok(mut r) => IsaacRng::new_from_rng(&mut r), - Err(e) => panic!("IsaacRng::new: failed to get seed from OS: {:?}", e) - } + pub fn new() -> Result { + let mut r = OsRng::new()?; + Ok(IsaacRng::from_rng(&mut r)) } /// Create an ISAAC random number generator using the default @@ -86,7 +84,7 @@ impl IsaacRng { /// free entropy gained. In some cases where the parent and child RNGs use /// the same algorithm, both generate the same output sequences (possibly /// with a small lag). - pub fn new_from_rng(other: &mut R) -> IsaacRng { + pub fn from_rng(other: &mut R) -> IsaacRng { let mut ret = EMPTY; unsafe { let ptr = ret.rsl.as_mut_ptr() as *mut u8; @@ -325,11 +323,9 @@ static EMPTY_64: Isaac64Rng = Isaac64Rng { impl Isaac64Rng { /// Creates a new `Isaac64Rng`, automatically seeded via `OsRng`. #[cfg(feature="std")] - pub fn new() -> Isaac64Rng { - match OsRng::new() { - Ok(mut r) => Isaac64Rng::new_from_rng(&mut r), - Err(e) => panic!("Isaac64Rng::new: failed to get seed from OS: {:?}", e) - } + pub fn new() -> Result { + let mut r = OsRng::new()?; + Ok(Isaac64Rng::from_rng(&mut r)) } /// Create a 64-bit ISAAC random number generator using the @@ -346,7 +342,7 @@ impl Isaac64Rng { /// free entropy gained. In some cases where the parent and child RNGs use /// the same algorithm, both generate the same output sequences (possibly /// with a small lag). - pub fn new_from_rng(other: &mut R) -> Isaac64Rng { + pub fn from_rng(other: &mut R) -> Isaac64Rng { let mut ret = EMPTY_64; unsafe { let ptr = ret.rsl.as_mut_ptr() as *mut u8; diff --git a/src/prng/xorshift.rs b/src/prng/xorshift.rs index 0c716b57e75..9534717e9a3 100644 --- a/src/prng/xorshift.rs +++ b/src/prng/xorshift.rs @@ -11,7 +11,7 @@ //! Xorshift generators use core::num::Wrapping as w; -use {Rng, SeedableRng}; +use {Rng, SeedableRng, CryptoError}; #[cfg(feature="std")] use OsRng; @@ -37,11 +37,9 @@ pub struct XorShiftRng { impl XorShiftRng { /// Creates a new `XorShiftRng`, automatically seeded via `OsRng`. #[cfg(feature="std")] - pub fn new() -> XorShiftRng { - match OsRng::new() { - Ok(mut r) => XorShiftRng::new_from_rng(&mut r), - Err(e) => panic!("XorShiftRng::new: failed to get seed from OS: {:?}", e) - } + pub fn new() -> Result { + let mut r = OsRng::new()?; + Ok(XorShiftRng::from_rng(&mut r)) } /// Creates a new XorShiftRng instance which is not seeded. @@ -65,7 +63,7 @@ impl XorShiftRng { /// free entropy gained. In some cases where the parent and child RNGs use /// the same algorithm, both generate the same output sequences (possibly /// with a small lag). - pub fn new_from_rng(rng: &mut R) -> XorShiftRng { + pub fn from_rng(rng: &mut R) -> XorShiftRng { let mut tuple: (u32, u32, u32, u32); loop { tuple = (rng.next_u32(), rng.next_u32(), rng.next_u32(), rng.next_u32()); diff --git a/src/read.rs b/src/read.rs index a1873a6bb2c..fbf35d38df3 100644 --- a/src/read.rs +++ b/src/read.rs @@ -11,9 +11,10 @@ //! A wrapper around any Read to treat it as an RNG. use std::fmt::Debug; -use std::io::{self, Read}; +use std::io::Read; use std::mem; -use Rng; + +use {Rng, CryptoError}; /// An RNG that reads random bytes straight from a `Read`. This will /// work best with an infinite reader, but this is not required. @@ -66,11 +67,10 @@ impl Rng for ReadRng { } } -fn fill(r: &mut Read, mut buf: &mut [u8]) -> io::Result<()> { +fn fill(r: &mut Read, mut buf: &mut [u8]) -> Result<(), CryptoError> { while buf.len() > 0 { - match try!(r.read(buf)) { - 0 => return Err(io::Error::new(io::ErrorKind::Other, - "end of file reached")), + match r.read(buf)? { + 0 => return Err(CryptoError), n => buf = &mut mem::replace(&mut buf, &mut [])[n..], } } From a3f5bf11f3d5e322d5031379f7cfafb032890348 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sat, 26 Aug 2017 12:13:16 +0100 Subject: [PATCH 079/247] Remove useless impl (We have automatic derefs.) --- src/lib.rs | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 75836e6b259..6883dad1cb6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -361,27 +361,8 @@ pub trait Rng { } } -impl<'a, R: ?Sized> Rng for &'a mut R where R: Rng { - fn next_u32(&mut self) -> u32 { - (**self).next_u32() - } - - fn next_u64(&mut self) -> u64 { - (**self).next_u64() - } - - #[cfg(feature = "i128_support")] - fn next_u128(&mut self) -> u128 { - (**self).next_u128() - } - - fn fill_bytes(&mut self, dest: &mut [u8]) { - (**self).fill_bytes(dest) - } -} - #[cfg(feature="std")] -impl Rng for Box where R: Rng { +impl Rng for Box where R: Rng+?Sized { fn next_u32(&mut self) -> u32 { (**self).next_u32() } From 1f1db330e626af805d692dbdcd2dbb982968e533 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Wed, 30 Aug 2017 16:13:13 +0100 Subject: [PATCH 080/247] range2: enable Range::new(low, high); remove convenience function --- src/distributions/range2.rs | 47 ++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/src/distributions/range2.rs b/src/distributions/range2.rs index 9ba055c48de..2348ed3ef29 100644 --- a/src/distributions/range2.rs +++ b/src/distributions/range2.rs @@ -26,12 +26,6 @@ use core::num::Wrapping as w; use Rng; use distributions::{Distribution, Uniform01, Rand}; -/// Convenience function to construct a `Range` -pub fn range(low: X, high: X) -> Range { - assert!(low < high, "range called with `low >= high`"); - Range { inner: RangeImpl::new(low, high) } -} - /// Sample values uniformly between two bounds. /// /// This gives a uniform distribution (assuming the RNG used to sample @@ -50,10 +44,10 @@ pub fn range(low: X, high: X) -> Range { /// /// ```rust /// use rand::distributions::Distribution; -/// use rand::distributions::range2::range; +/// use rand::distributions::range2::Range; /// /// fn main() { -/// let between = range(10, 10000); +/// let between = Range::new(10, 10000); /// let mut rng = rand::thread_rng(); /// let mut sum = 0; /// for _ in 0..1000 { @@ -67,10 +61,12 @@ pub struct Range { inner: T, } -impl Range { +// Range must be parameterised so that `Self` is fully typed, but we don't +// actually use this type, so we just use any valid type here. (Minor lang bug?) +impl Range> { /// Create a new `Range` instance that samples uniformly from /// `[low, high)`. Panics if `low >= high`. - pub fn new(low: T::X, high: T::X) -> Range { + pub fn new(low: X, high: X) -> Range { assert!(low < high, "Range::new called with `low >= high`"); Range { inner: RangeImpl::new(low, high) } } @@ -217,23 +213,23 @@ range_float_impl! { f64 } mod tests { use {Rng, thread_rng}; use distributions::{Rand, Distribution}; - use distributions::range2::{Range, range, RangeImpl, RangeFloat}; + use distributions::range2::{Range, RangeImpl, RangeFloat, SampleRange}; #[test] fn test_fn_range() { let mut r = thread_rng(); for _ in 0..1000 { - let a = range(-3, 42).sample(&mut r); + let a = Range::new(-3, 42).sample(&mut r); assert!(a >= -3 && a < 42); - assert_eq!(range(0, 1).sample(&mut r), 0); - assert_eq!(range(-12, -11).sample(&mut r), -12); + assert_eq!(Range::new(0, 1).sample(&mut r), 0); + assert_eq!(Range::new(-12, -11).sample(&mut r), -12); } for _ in 0..1000 { - let a = range(10, 42).sample(&mut r); + let a = Range::new(10, 42).sample(&mut r); assert!(a >= 10 && a < 42); - assert_eq!(range(0, 1).sample(&mut r), 0); - assert_eq!(range(3_000_000, 3_000_001).sample(&mut r), 3_000_000); + assert_eq!(Range::new(0, 1).sample(&mut r), 0); + assert_eq!(Range::new(3_000_000, 3_000_001).sample(&mut r), 3_000_000); } } @@ -241,25 +237,25 @@ mod tests { #[should_panic] fn test_fn_range_panic_int() { let mut r = thread_rng(); - range(5, -2).sample(&mut r); + Range::new(5, -2).sample(&mut r); } #[test] #[should_panic] fn test_fn_range_panic_usize() { let mut r = thread_rng(); - range(5, 2).sample(&mut r); + Range::new(5, 2).sample(&mut r); } #[should_panic] #[test] fn test_range_bad_limits_equal() { - range(10, 10); + Range::new(10, 10); } #[should_panic] #[test] fn test_range_bad_limits_flipped() { - range(10, 5); + Range::new(10, 5); } #[test] @@ -272,7 +268,7 @@ mod tests { (10, 127), (::std::$ty::MIN, ::std::$ty::MAX)]; for &(low, high) in v.iter() { - let my_range = range(low, high); + let my_range = Range::new(low, high); for _ in 0..1000 { let v: $ty = Rand::rand(&mut rng, my_range); assert!(low <= v && v < high); @@ -298,7 +294,7 @@ mod tests { (1e-35, 1e-25), (-1e35, 1e35)]; for &(low, high) in v.iter() { - let my_range = range(low, high); + let my_range = Range::new(low, high); for _ in 0..1000 { let v: $ty = Rand::rand(&mut rng, my_range); assert!(low <= v && v < high); @@ -332,9 +328,12 @@ mod tests { MyF32 { x: self.inner.sample(rng) } } } + impl SampleRange for MyF32 { + type T = RangeMyF32; + } let (low, high) = (MyF32{ x: 17.0f32 }, MyF32{ x: 22.0f32 }); - let range = Range::::new(low, high); + let range = Range::new(low, high); let mut rng = ::test::rng(); for _ in 0..100 { let x = MyF32::rand(&mut rng, range); From db93780f7c5b4c694136470611825e36dfbcc9e7 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Fri, 1 Sep 2017 12:34:45 +0100 Subject: [PATCH 081/247] Fix benchmarks --- benches/distributions.rs | 24 ++++++++++++------------ benches/misc.rs | 4 ++-- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/benches/distributions.rs b/benches/distributions.rs index ea797c630e9..cff1e3271f7 100644 --- a/benches/distributions.rs +++ b/benches/distributions.rs @@ -19,7 +19,7 @@ use rand::distributions::gamma::Gamma; #[bench] fn distr_baseline(b: &mut Bencher) { - let mut rng = XorShiftRng::new(); + let mut rng = XorShiftRng::new().unwrap(); b.iter(|| { for _ in 0..::RAND_BENCH_N { @@ -32,7 +32,7 @@ fn distr_baseline(b: &mut Bencher) { #[bench] fn distr_range_int(b: &mut Bencher) { - let mut rng = XorShiftRng::new(); + let mut rng = XorShiftRng::new().unwrap(); let distr = Range::new(3i64, 134217671i64); b.iter(|| { @@ -45,7 +45,7 @@ fn distr_range_int(b: &mut Bencher) { #[bench] fn distr_range_float(b: &mut Bencher) { - let mut rng = XorShiftRng::new(); + let mut rng = XorShiftRng::new().unwrap(); let distr = Range::new(2.26f64, 2.319f64); b.iter(|| { @@ -59,8 +59,8 @@ fn distr_range_float(b: &mut Bencher) { #[bench] fn distr_range2_int(b: &mut Bencher) { - let mut rng = XorShiftRng::new(); - let distr = range2::range(3i64, 134217671i64); + let mut rng = XorShiftRng::new().unwrap(); + let distr = range2::Range::new(3i64, 134217671i64); b.iter(|| { for _ in 0..::RAND_BENCH_N { @@ -72,8 +72,8 @@ fn distr_range2_int(b: &mut Bencher) { #[bench] fn distr_range2_float(b: &mut Bencher) { - let mut rng = XorShiftRng::new(); - let distr = range2::range(2.26f64, 2.319f64); + let mut rng = XorShiftRng::new().unwrap(); + let distr = range2::Range::new(2.26f64, 2.319f64); b.iter(|| { for _ in 0..::RAND_BENCH_N { @@ -86,7 +86,7 @@ fn distr_range2_float(b: &mut Bencher) { #[bench] fn distr_exp(b: &mut Bencher) { - let mut rng = XorShiftRng::new(); + let mut rng = XorShiftRng::new().unwrap(); let distr = Exp::new(2.71828 * 3.14159); b.iter(|| { @@ -100,7 +100,7 @@ fn distr_exp(b: &mut Bencher) { #[bench] fn distr_normal(b: &mut Bencher) { - let mut rng = XorShiftRng::new(); + let mut rng = XorShiftRng::new().unwrap(); let distr = Normal::new(-2.71828, 3.14159); b.iter(|| { @@ -113,7 +113,7 @@ fn distr_normal(b: &mut Bencher) { #[bench] fn distr_log_normal(b: &mut Bencher) { - let mut rng = XorShiftRng::new(); + let mut rng = XorShiftRng::new().unwrap(); let distr = LogNormal::new(-2.71828, 3.14159); b.iter(|| { @@ -127,7 +127,7 @@ fn distr_log_normal(b: &mut Bencher) { #[bench] fn distr_gamma_large_shape(b: &mut Bencher) { - let mut rng = XorShiftRng::new(); + let mut rng = XorShiftRng::new().unwrap(); let distr = Gamma::new(10., 1.0); b.iter(|| { @@ -140,7 +140,7 @@ fn distr_gamma_large_shape(b: &mut Bencher) { #[bench] fn distr_gamma_small_shape(b: &mut Bencher) { - let mut rng = XorShiftRng::new(); + let mut rng = XorShiftRng::new().unwrap(); let distr = Gamma::new(0.1, 1.0); b.iter(|| { diff --git a/benches/misc.rs b/benches/misc.rs index 874b3758669..0de6b468872 100644 --- a/benches/misc.rs +++ b/benches/misc.rs @@ -58,7 +58,7 @@ fn misc_convert_f64(b: &mut Bencher) { #[bench] fn misc_shuffle_100(b: &mut Bencher) { - let mut rng = XorShiftRng::new(); + let mut rng = XorShiftRng::new().unwrap(); let x : &mut [usize] = &mut [1; 100]; b.iter(|| { x.shuffle(&mut rng); @@ -67,7 +67,7 @@ fn misc_shuffle_100(b: &mut Bencher) { #[bench] fn misc_sample_10_of_100(b: &mut Bencher) { - let mut rng = XorShiftRng::new(); + let mut rng = XorShiftRng::new().unwrap(); let x : &[usize] = &[1; 100]; b.iter(|| { sample(&mut rng, x, 10); From b7edfdb87fa4c3ba4e9f687e68df560bb115fc2a Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Sun, 3 Sep 2017 07:50:14 +0200 Subject: [PATCH 082/247] Higher precision for floats in range [0,1) Use a different technique to create floating point numbers uniformly distributed over [0,1). The previous method only used 23 bits of precision for a f32, this method uses all available 32 bits. This results in numbers with up to 9 bits of precision extra when getting closer to zero. `Closed01` and `Open01` used multiplication to adjust the range sampled from `Uniform01`. This does not play well with an `Uniform01` that gets higher precision as it gets closer to 0.0. `Closed01` now does it's own conversion to sample from the range (0,1]. `Open01` uses the rejection method. --- src/distributions/float_conversions.rs | 246 +++++++++++++++++++++++++ src/distributions/mod.rs | 1 + src/distributions/uniform.rs | 87 +++------ 3 files changed, 272 insertions(+), 62 deletions(-) create mode 100644 src/distributions/float_conversions.rs diff --git a/src/distributions/float_conversions.rs b/src/distributions/float_conversions.rs new file mode 100644 index 00000000000..2c98c07a753 --- /dev/null +++ b/src/distributions/float_conversions.rs @@ -0,0 +1,246 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Convert a random int to a float in the range [0,1] + +use core::mem; + +trait FloatBitOps { + type F; + + /// Helper function to combine the fraction, exponent and sign into a float. + /// `self` includes the bits from the fraction and sign. + fn binor_exp(self, exponent: i32) -> Self::F; +} + +impl FloatBitOps for u32 { + type F = f32; + #[inline(always)] + fn binor_exp(self, exponent: i32) -> f32 { + // The exponent is encoded using an offset-binary representation, + // with the zero offset being 127 + let exponent_bits = ((127 + exponent) as u32) << 23; + unsafe { mem::transmute(self | exponent_bits) } + } +} + +impl FloatBitOps for u64 { + type F = f64; + #[inline(always)] + fn binor_exp(self, exponent: i32) -> f64 { + // The exponent is encoded using an offset-binary representation, + // with the zero offset being 1023 + let exponent_bits = ((1023 + exponent) as u64) << 52; + unsafe { mem::transmute(self | exponent_bits) } + } +} + +pub trait FloatConversions { + type F; + + /// Convert a random number to a floating point number sampled from the + /// uniformly distributed half-open range [0,1). + /// Closer to zero the distribution gains more precision, up to + /// 2^-32 for f32 and 2^-64 for f64. + fn uniform01(self) -> Self::F; + + /// Convert a random number to a floating point number sampled from the + /// uniformly distributed closed range [0,1]. + /// Closer to zero the distribution gains more precision, up to + /// 2^-32 for f32 and 2^-64 for f64. + fn closed01(self) -> Self::F; + + /// Convert a random number to a floating point number sampled from the + /// uniformly distributed half-open range [-1,1). + /// Closer to zero the distribution gains more precision, up to + /// 2^-32 for f32 and 2^-64 for f64. + fn uniform11(self) -> Self::F; +} + +macro_rules! float_impls { + ($ty:ty, $uty:ty, $FLOAT_SIZE:expr, $FRACTION_BITS:expr, + $FRACTION_MASK:expr, $next_u:path) => { + + impl FloatConversions for $uty { + type F = $ty; + + /// Convert a random number to a floating point number sampled from + /// the uniformly distributed half-open range [0,1). + /// + /// We fill the fractional part of the f32 with 23 random bits. + /// The trick to generate a uniform distribution over [0,1) is to + /// fill the exponent with the -log2 of the remaining random bits. A + /// simpler alternative to -log2 is to count the number of trailing + /// zero's of the random bits, add 1, and negate the result. + /// + /// Each exponent is responsible for a piece of the distribution + /// between [0,1). The exponent -1 fills the part [0.5,1). -2 fills + /// [0.25,0.5). The lowest exponent we can get is -10. So a problem + /// with this method is that we can not fill the part between zero + /// and the part from -10. The solution is to treat numbers with an + /// exponent of -10 as if they have -9 as exponent, and substract + /// 2^-9. + #[inline(always)] + fn uniform01(self) -> $ty { + const MIN_EXPONENT: i32 = $FRACTION_BITS - $FLOAT_SIZE; + #[allow(non_snake_case)] + let ADJUST: $ty = (0 as $uty).binor_exp(MIN_EXPONENT); + // 2^MIN_EXPONENT + + // Use the last 23 bits to fill the fraction + let fraction = self & $FRACTION_MASK; + // Use the remaing (first) 9 bits for the exponent + let exp = $FRACTION_BITS - 1 + - ((self & !$FRACTION_MASK).trailing_zeros() as i32); + if exp < MIN_EXPONENT { + return fraction.binor_exp(MIN_EXPONENT) - ADJUST; + } + fraction.binor_exp(exp) + } + + /// Convert a random number to a floating point number sampled from + /// the uniformly distributed closed range [0,1]. + /// + /// The problem with a closed range over [0,1] is that the chance to + /// sample 1.0 exactly is 2^n+1. Which is difficult, as it is not a + /// power of two. Instead of a closed range, we actually sample from + /// the half-open range (0,1]. + /// + /// Floating-point numbers have more precision near zero. This means + /// for a f32 that only 1 in 2^32 samples will give exactly 0.0. But + /// 1 in 2^23 will give exactly 1.0 due to rounding. Because the + /// chance to sample 0.0 is so low, this half-open range is a very + /// good appoximation of a closed range. + /// + /// This method is similar to `Uniform01`, with one extra step: + /// If the fractional part ends up as zero, add 1 to the exponent in + /// 50% of the cases. This changes 0.5 to 1.0, 0.25 to 0.5, etc. + /// + /// The chance to select these numbers that staddle the boundery + /// between exponents is 33% to high in `Uniform01` (e.g. 33%*2^-23 + /// for 0.5f32). The reason is that with 0.5 as example the distance + /// to the next number is 2^-23, but to the previous number 2^-24. + /// With the adjustment they have exactly the right chance of + /// getting sampled. + #[inline(always)] + fn closed01(self) -> $ty { + const MIN_EXPONENT: i32 = $FRACTION_BITS - $FLOAT_SIZE; + #[allow(non_snake_case)] + let ADJUST: $ty = (0 as $uty).binor_exp(MIN_EXPONENT); + // 2^MIN_EXPONENT + + // Use the last 23 bits to fill the fraction + let fraction = self & $FRACTION_MASK; + // Use the remaing (first) 9 bits for the exponent + let mut exp = $FRACTION_BITS - 1 + - ((self & !$FRACTION_MASK).trailing_zeros() as i32); + if fraction == 0 { + // Shift the exponent in about 50% of the samples, based on + // one of the unused bits for the exponent. + // Shift uncondinionally for the lowest exponents, because + // there are no more random bits left. + if (exp > MIN_EXPONENT) && + (self & 1 << ($FLOAT_SIZE - 1) != 0) { + exp += 1; + } + } else if exp < MIN_EXPONENT { + return fraction.binor_exp(MIN_EXPONENT) - ADJUST; + } + fraction.binor_exp(exp) + } + + /// Convert a random number to a floating point number sampled from + /// the uniformly distributed half-open range [-1,1). + /// + /// This uses the same method as `uniform01`. One of the random bits + /// that it uses for the exponent, is now used as a sign bit. + /// + /// This samples numbers from the range (-1,-0] and [0,1). Ideally + /// we would not sample -0.0, but include -1.0. That is why for + /// negative numbers we use the extra step from `open01`. + #[inline(always)] + fn uniform11(self) -> $ty { + const MIN_EXPONENT: i32 = $FRACTION_BITS - $FLOAT_SIZE + 1; + const SIGN_BIT: $uty = 1 << ($FLOAT_SIZE - 1); + #[allow(non_snake_case)] + let ADJUST: $ty = (0 as $uty).binor_exp(MIN_EXPONENT); + // 2^MIN_EXPONENT + + // Use the last 23 bits to fill the fraction, and the first bit + // as sign + let fraction = self & ($FRACTION_MASK | SIGN_BIT); + // Use the remaing 8 bits for the exponent + let mut exp = $FRACTION_BITS - + (((self & !$FRACTION_MASK) << 1).trailing_zeros() as i32); + if fraction == SIGN_BIT { + // Shift the exponent in about 50% of the samples, based on + // one of the unused bits for the exponent. + // Shift uncondinionally for the lowest exponents, because + // there are no more random bits left. + if (exp > MIN_EXPONENT) && + (self & 1 << ($FLOAT_SIZE - 2) != 0) { + exp += 1; + } + } else if exp < MIN_EXPONENT { + let result: $ty = fraction.binor_exp(MIN_EXPONENT); + match fraction & SIGN_BIT == SIGN_BIT { + false => return result - ADJUST, + true => return result + ADJUST, + }; + } + fraction.binor_exp(exp) + } + } + } +} +float_impls! { f32, u32, 32, 23, 0x7FFFFF, Rng::next_u32 } +float_impls! { f64, u64, 64, 52, 0xFFFFFFFFFFFFF, Rng::next_u64 } + + +#[cfg(test)] +mod tests { + use super::FloatConversions; + + #[test] + fn uniform01_edge_cases() { + // Test that the distribution is a half-open range over [0,1). + // These constants happen to generate the lowest and highest floats in + // the range. + assert!(0u32.uniform01() == 0.0); + assert!(0xffff_ffffu32.uniform01() == 0.99999994); + + assert!(0u64.uniform01() == 0.0); + assert!(0xffff_ffff_ffff_ffffu64.uniform01() == 0.9999999999999999); + } + + #[test] + fn closed01_edge_cases() { + // Test that the distribution is a half-open range over (0,1]. + // These constants happen to generate the lowest and highest floats in + // the range. + assert!(1u32.closed01() == 2.3283064e-10); // 2^-32 + assert!(0xff80_0000u32.closed01() == 1.0); + + assert!(1u64.closed01() == 5.421010862427522e-20); // 2^-64 + assert!(0xfff0_0000_0000_0000u64.closed01() == 1.0); + } + + #[test] + fn uniform11_edge_cases() { + // Test that the distribution is a half-open range over [-1,1). + // These constants happen to generate the lowest and highest floats in + // the range. + assert!(0xff80_0000u32.uniform11() == -1.0); + assert!(0x7fff_ffffu32.uniform11() == 0.99999994); + + assert!(0xfff0_0000_0000_0000u64.uniform11() == -1.0); + assert!(0x7fff_ffff_ffff_ffffu64.uniform11() == 0.9999999999999999); + } +} diff --git a/src/distributions/mod.rs b/src/distributions/mod.rs index 4d01169a5a3..18417dcbe2c 100644 --- a/src/distributions/mod.rs +++ b/src/distributions/mod.rs @@ -37,6 +37,7 @@ mod default; mod uniform; #[cfg(feature="std")] mod ziggurat_tables; +mod float_conversions; pub mod range; pub mod range2; diff --git a/src/distributions/uniform.rs b/src/distributions/uniform.rs index 5694cd86e8b..8e7e2f38043 100644 --- a/src/distributions/uniform.rs +++ b/src/distributions/uniform.rs @@ -15,6 +15,7 @@ use core::mem; use Rng; use distributions::{Distribution, Rand}; +use distributions::float_conversions::FloatConversions; // ----- convenience functions ----- @@ -187,68 +188,39 @@ impl Distribution for Uniform { } } -impl Distribution for Uniform01 { - /// This uses a technique described by Saito and Matsumoto at - /// MCQMC'08. Given that the IEEE floating point numbers are - /// uniformly distributed over [1,2), we generate a number in - /// this range and then offset it onto the range [0,1). Our - /// choice of bits (masking v. shifting) is arbitrary and - /// should be immaterial for high quality generators. For low - /// quality generators (ex. LCG), prefer bitshifting due to - /// correlation between sequential low order bits. - /// - /// See: - /// A PRNG specialized in double precision floating point numbers using - /// an affine transition - /// http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/ARTICLES/dSFMT.pdf - /// http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/dSFMT-slide-e.pdf - fn sample(&self, rng: &mut R) -> f32 { - const UPPER_MASK: u32 = 0x3F800000; - const LOWER_MASK: u32 = 0x7FFFFF; - let tmp = UPPER_MASK | (rng.next_u32() & LOWER_MASK); - let result: f32 = unsafe { mem::transmute(tmp) }; - result - 1.0 - } -} - -impl Distribution for Uniform01 { - fn sample(&self, rng: &mut R) -> f64 { - const UPPER_MASK: u64 = 0x3FF0000000000000; - const LOWER_MASK: u64 = 0xFFFFFFFFFFFFF; - let tmp = UPPER_MASK | (rng.next_u64() & LOWER_MASK); - let result: f64 = unsafe { mem::transmute(tmp) }; - result - 1.0 - } -} - macro_rules! float_impls { - ($scale_name:ident, $ty:ty, $mantissa_bits:expr) => { - const $scale_name: $ty = (1u64 << $mantissa_bits) as $ty; - - impl Distribution<$ty> for Open01 { - #[inline] + ($ty:ty, $next_u:path) => { + impl Distribution<$ty> for Uniform01 { fn sample(&self, rng: &mut R) -> $ty { - // add a small amount (specifically 2 bits below - // the precision of f64/f32 at 1.0), so that small - // numbers are larger than 0, but large numbers - // aren't pushed to/above 1. - let x: $ty = Rand::rand(rng, Uniform01); - x + 0.25 / $scale_name + let x = $next_u(rng); + x.uniform01() } } + impl Distribution<$ty> for Closed01 { - #[inline] fn sample(&self, rng: &mut R) -> $ty { - // rescale so that 1.0 - epsilon becomes 1.0 - // precisely. - let x: $ty = Rand::rand(rng, Uniform01); - x * $scale_name / ($scale_name - 1.0) + let x = $next_u(rng); + x.closed01() + } + } + + impl Distribution<$ty> for Open01 { + // Sample from the open range (0,1). + // This uses the rejection method: use `Uniform01`, and if the + // result is 0.0, reject the result and try again. + fn sample(&self, rng: &mut R) -> $ty { + let mut x = 0; + while x == 0 { // 0 converts to 0.0 + x = $next_u(rng); + } + x.uniform01() } } } } -float_impls! { SCALE_F64, f64, 53 } -float_impls! { SCALE_F32, f32, 24 } +float_impls! { f32, Rng::next_u32 } +float_impls! { f64, Rng::next_u64 } + impl Distribution for AsciiWordChar { fn sample(&self, rng: &mut R) -> char { @@ -259,7 +231,7 @@ impl Distribution for AsciiWordChar { #[cfg(test)] mod tests { - use {ConstRng, thread_rng, iter}; + use {thread_rng, iter}; use distributions::{Rand, uniform, Uniform, Uniform01, Open01, Closed01}; use distributions::uniform::{codepoint, ascii_word_char}; @@ -308,15 +280,6 @@ mod tests { assert!(0.0 <= b && b < 1.0); } - #[test] - fn floating_point_edge_cases() { - // FIXME: this message and test predates this repo; message suggests - // test is supposed to be ==; using != is pretty useless - // the test for exact equality is correct here. - assert!(f64::rand(&mut ConstRng::new(0xffff_ffffu64), Uniform01) != 1.0); - assert!(f64::rand(&mut ConstRng::new(0xffff_ffff_ffff_ffffu64), Uniform01) != 1.0); - } - #[test] fn rand_open() { // this is unlikely to catch an incorrect implementation that From fadc260923c2d864b29c7a8507ac23fd2fa00ba8 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Sun, 3 Sep 2017 08:00:10 +0200 Subject: [PATCH 083/247] Make ranges preserve precision --- src/distributions/range2.rs | 110 +++++++++++++++++++++++++++++++----- 1 file changed, 95 insertions(+), 15 deletions(-) diff --git a/src/distributions/range2.rs b/src/distributions/range2.rs index 2348ed3ef29..d3f307b9b6d 100644 --- a/src/distributions/range2.rs +++ b/src/distributions/range2.rs @@ -24,7 +24,8 @@ use core::num::Wrapping as w; use Rng; -use distributions::{Distribution, Uniform01, Rand}; +use distributions::Distribution; +use distributions::float_conversions::FloatConversions; /// Sample values uniformly between two bounds. /// @@ -177,37 +178,116 @@ range_int_impl! { usize, usize } /// Implementation of `RangeImpl` for float types. #[derive(Clone, Copy, Debug)] -pub struct RangeFloat { - low: X, - range: X, +pub enum RangeFloat { + // A range that is completely positive + Positive { offset: X, scale: X }, + // A range that is completely negative + Negative { offset: X, scale: X }, + // A range that is goes from negative to positive, but has more positive + // than negative numbers + PosAndNeg { cutoff: X, scale: X }, + // A range that is goes from negative to positive, but has more negative + // than positive numbers + NegAndPos { cutoff: X, scale: X }, } macro_rules! range_float_impl { - ($ty:ty) => { + ($ty:ty, $next_u:path) => { impl SampleRange for $ty { type T = RangeFloat<$ty>; } - + impl RangeImpl for RangeFloat<$ty> { + // We can distinguish between two different kinds of ranges: + // + // Completely positive or negative. + // A characteristic of these ranges is that they get more bits of + // precision as they get closer to 0.0. For positive ranges it is as + // simple as applying a scale and offset to get the right range. + // For a negative range, we apply a negative scale to transform the + // range [0,1) to (-x,0]. Because this results in a range with it + // closed and open sides reversed, we do not sample from `Uniform01` + // but from `Closed01`, which actually returns samples from the + // range (0,1]. + // + // A range that is both negative and positive. + // This range has as characteristic that it does not have most of + // its bits of precision near its low or high end, but in the middle + // near 0.0. As the basis we use the [-1,1) range from `Uniform11`. + // How it works is easiest to explain with an example. + // Suppose we want to sample from the range [-20, 100). We are + // first going to scale the range from [-1,1) to (-60,60]. Note the + // range changes from closed-open to open-closed because the scale + // is negative. + // If the sample happens to fall in the part (-60,-20), move it to + // (-100,60). Then multiply by -1. + // Scaling keeps the bits of precision intact. Moving the assymetric + // part also does not waste precision, as the more you move away + // from zero the less precision can be stored. + type X = $ty; - + fn new(low: Self::X, high: Self::X) -> Self { - RangeFloat { - low: low, - range: high - low, + if low >= 0.0 { + RangeFloat::Positive { + offset: low, + scale: high - low, + } + } else if high <= 0.0 { + RangeFloat::Negative { + offset: high, + scale: low - high, + } + } else if -low <= high { + RangeFloat::PosAndNeg { + cutoff: low, + scale: (high + low) / -2.0 + low, + } + } else { + RangeFloat::NegAndPos { + cutoff: high, + scale: (high + low) / 2.0 - high, + } } } - + fn sample(&self, rng: &mut R) -> Self::X { - let x: $ty = Rand::rand(rng, Uniform01); - self.low + self.range * x + let rnd = $next_u(rng); + match *self { + RangeFloat::Positive { offset, scale } => { + let x: $ty = rnd.uniform01(); + offset + scale * x + } + RangeFloat::Negative { offset, scale } => { + let x: $ty = rnd.closed01(); + offset + scale * x + } + RangeFloat::PosAndNeg { cutoff, scale } => { + let x: $ty = rnd.uniform11(); + let x = x * scale; + if x < cutoff { + (cutoff - scale) - x + } else { + x + } + } + RangeFloat::NegAndPos { cutoff, scale } => { + let x: $ty = rnd.uniform11(); + let x = x * scale; + if x >= cutoff { + (cutoff + scale) - x + } else { + x + } + } + } } } } } -range_float_impl! { f32 } -range_float_impl! { f64 } +range_float_impl! { f32, Rng::next_u32 } +range_float_impl! { f64, Rng::next_u64 } #[cfg(test)] mod tests { From 3c800b15e7cf8174879c6df076efbaa1b3769a28 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Sun, 3 Sep 2017 08:11:43 +0200 Subject: [PATCH 084/247] Use optimized float conversions in ziggurat --- src/distributions/float_conversions.rs | 48 ++++++++++++++++++++++++++ src/distributions/mod.rs | 32 ++++++++--------- 2 files changed, 63 insertions(+), 17 deletions(-) diff --git a/src/distributions/float_conversions.rs b/src/distributions/float_conversions.rs index 2c98c07a753..335dcad0d85 100644 --- a/src/distributions/float_conversions.rs +++ b/src/distributions/float_conversions.rs @@ -62,6 +62,24 @@ pub trait FloatConversions { /// Closer to zero the distribution gains more precision, up to /// 2^-32 for f32 and 2^-64 for f64. fn uniform11(self) -> Self::F; + + /// Convert a random number to a floating point number sampled from the + /// uniformly distributed half-open range [0,1). + /// The distribution has a fixed precision of 2^-23 for f32 and + /// 2^-52 for f64. + /// The advantage of `uniform01_simple` is it leaves a couple of the random + /// bits available for other purposes (the 9 rightmost bits for f32, and + /// 12 for f64). + fn uniform01_simple(self) -> Self::F; + + /// Convert a random number to a floating point number sampled from the + /// uniformly distributed half-open range [-1,1). + /// The distribution has a fixed precision of 2^-22 for f32 and + /// 2^-51 for f64. + /// The advantage of `uniform11_simple` is it leaves a couple of the random + /// bits available for other purposes (the 9 rightmost bits for f32, and + /// 12 for f64). + fn uniform11_simple(self) -> Self::F; } macro_rules! float_impls { @@ -197,6 +215,36 @@ macro_rules! float_impls { } fraction.binor_exp(exp) } + + /// This uses a technique described by Saito and Matsumoto at + /// MCQMC'08. Given that the IEEE floating point numbers are + /// uniformly distributed over [1,2), we generate a number in this + /// range and then offset it onto the range [0,1). Our choice of + /// bits (masking v. shifting) is arbitrary and + /// should be immaterial for high quality generators. For low + /// quality generators (ex. LCG), prefer bitshifting due to + /// correlation between sequential low order bits. + /// + /// See: + /// A PRNG specialized in double precision floating point numbers + /// using an affine transition + /// http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/ARTICLES/dSFMT.pdf + /// http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/dSFMT-slide-e.pdf + #[inline(always)] + fn uniform01_simple(self) -> $ty { + const EXPONENT: i32 = 0; + let fraction = self >> ($FLOAT_SIZE - $FRACTION_BITS); + fraction.binor_exp(EXPONENT) - 1.0 + } + + /// Like `uniform01_simple`, but generate a number in the range + /// [2,4) and offset it onto the range [-1,1). + #[inline(always)] + fn uniform11_simple(self) -> $ty { + const EXPONENT: i32 = 1; + let fraction = self >> ($FLOAT_SIZE - $FRACTION_BITS); + fraction.binor_exp(EXPONENT) - 3.0 + } } } } diff --git a/src/distributions/mod.rs b/src/distributions/mod.rs index 18417dcbe2c..4ef7e725a7b 100644 --- a/src/distributions/mod.rs +++ b/src/distributions/mod.rs @@ -23,6 +23,7 @@ pub use self::default::Default; pub use self::uniform::{uniform, codepoint, ascii_word_char}; pub use self::uniform::{Uniform, Uniform01, Open01, Closed01, AsciiWordChar}; pub use self::range::{Range}; +use self::float_conversions::FloatConversions; #[cfg(feature="std")] pub use self::gamma::{Gamma, ChiSquared, FisherF, StudentT}; @@ -159,28 +160,25 @@ fn ziggurat( mut pdf: P, mut zero_case: Z) -> f64 where P: FnMut(f64) -> f64, Z: FnMut(&mut R, f64) -> f64 { - const SCALE: f64 = (1u64 << 53) as f64; loop { - // reimplement the f64 generation as an optimisation suggested - // by the Doornik paper: we have a lot of precision-space - // (i.e. there are 11 bits of the 64 of a u64 to use after - // creating a f64), so we might as well reuse some to save - // generating a whole extra random number. (Seems to be 15% - // faster.) - // - // This unfortunately misses out on the benefits of direct - // floating point generation if an RNG like dSMFT is - // used. (That is, such RNGs create floats directly, highly - // efficiently and overload next_f32/f64, so by not calling it - // this may be slower than it would be otherwise.) - // FIXME: investigate/optimise for the above. + // As an optimisation convert the random u64 to a f64 using only + // 53 bits, as many as will fit in the float's fraction. + // Of the remaining 11 least significant bits we use 8 to construct `i`. + // This saves us generating a whole extra random number, while the added + // precision of using 64 bits for f64 does not buy us much. let bits: u64 = uniform(rng); - let i = (bits & 0xff) as usize; - let f = (bits >> 11) as f64 / SCALE; // u is either U(-1, 1) or U(0, 1) depending on if this is a // symmetric distribution or not. - let u = if symmetric {2.0 * f - 1.0} else {f}; + // FIXME: the distribution is not open, but closed-open. + // Can that cause problems or a bias? + let u = if symmetric { + bits.uniform11_simple() + } else { + bits.uniform01_simple() + }; + let i = (bits & 0xff) as usize; + let x = u * x_tab[i]; let test_x = if symmetric { x.abs() } else {x}; From ca7b10f3223dc87e77a7af6506fa25a239c34339 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Sun, 3 Sep 2017 08:17:06 +0200 Subject: [PATCH 085/247] Move `FloatConversions` to a new module --- src/distributions/mod.rs | 7 +- src/distributions/range2.rs | 18 +- src/distributions/uniform.rs | 22 +- src/lib.rs | 1 + .../float_conversions.rs => utils.rs} | 237 ++++++++++++------ 5 files changed, 188 insertions(+), 97 deletions(-) rename src/{distributions/float_conversions.rs => utils.rs} (52%) diff --git a/src/distributions/mod.rs b/src/distributions/mod.rs index 4ef7e725a7b..ff35ae880a0 100644 --- a/src/distributions/mod.rs +++ b/src/distributions/mod.rs @@ -23,7 +23,7 @@ pub use self::default::Default; pub use self::uniform::{uniform, codepoint, ascii_word_char}; pub use self::uniform::{Uniform, Uniform01, Open01, Closed01, AsciiWordChar}; pub use self::range::{Range}; -use self::float_conversions::FloatConversions; +use utils::FloatConversions; #[cfg(feature="std")] pub use self::gamma::{Gamma, ChiSquared, FisherF, StudentT}; @@ -38,7 +38,6 @@ mod default; mod uniform; #[cfg(feature="std")] mod ziggurat_tables; -mod float_conversions; pub mod range; pub mod range2; @@ -173,9 +172,9 @@ fn ziggurat( // FIXME: the distribution is not open, but closed-open. // Can that cause problems or a bias? let u = if symmetric { - bits.uniform11_simple() + bits.closed_open11_fixed() } else { - bits.uniform01_simple() + bits.closed_open01_fixed() }; let i = (bits & 0xff) as usize; diff --git a/src/distributions/range2.rs b/src/distributions/range2.rs index d3f307b9b6d..1ff7fc51057 100644 --- a/src/distributions/range2.rs +++ b/src/distributions/range2.rs @@ -25,7 +25,7 @@ use core::num::Wrapping as w; use Rng; use distributions::Distribution; -use distributions::float_conversions::FloatConversions; +use utils::FloatConversions; /// Sample values uniformly between two bounds. /// @@ -206,14 +206,14 @@ macro_rules! range_float_impl { // simple as applying a scale and offset to get the right range. // For a negative range, we apply a negative scale to transform the // range [0,1) to (-x,0]. Because this results in a range with it - // closed and open sides reversed, we do not sample from `Uniform01` - // but from `Closed01`, which actually returns samples from the - // range (0,1]. + // closed and open sides reversed, we do not sample from + // `closed_open01` but from `open_closed01`. // // A range that is both negative and positive. // This range has as characteristic that it does not have most of // its bits of precision near its low or high end, but in the middle - // near 0.0. As the basis we use the [-1,1) range from `Uniform11`. + // near 0.0. As the basis we use the [-1,1) range from + // `closed_open11`. // How it works is easiest to explain with an example. // Suppose we want to sample from the range [-20, 100). We are // first going to scale the range from [-1,1) to (-60,60]. Note the @@ -255,15 +255,15 @@ macro_rules! range_float_impl { let rnd = $next_u(rng); match *self { RangeFloat::Positive { offset, scale } => { - let x: $ty = rnd.uniform01(); + let x: $ty = rnd.closed_open01(); offset + scale * x } RangeFloat::Negative { offset, scale } => { - let x: $ty = rnd.closed01(); + let x: $ty = rnd.open_closed01(); offset + scale * x } RangeFloat::PosAndNeg { cutoff, scale } => { - let x: $ty = rnd.uniform11(); + let x: $ty = rnd.closed_open11(); let x = x * scale; if x < cutoff { (cutoff - scale) - x @@ -272,7 +272,7 @@ macro_rules! range_float_impl { } } RangeFloat::NegAndPos { cutoff, scale } => { - let x: $ty = rnd.uniform11(); + let x: $ty = rnd.closed_open11(); let x = x * scale; if x >= cutoff { (cutoff + scale) - x diff --git a/src/distributions/uniform.rs b/src/distributions/uniform.rs index 8e7e2f38043..5dc9fc54cfb 100644 --- a/src/distributions/uniform.rs +++ b/src/distributions/uniform.rs @@ -15,7 +15,7 @@ use core::mem; use Rng; use distributions::{Distribution, Rand}; -use distributions::float_conversions::FloatConversions; +use utils::FloatConversions; // ----- convenience functions ----- @@ -193,27 +193,39 @@ macro_rules! float_impls { impl Distribution<$ty> for Uniform01 { fn sample(&self, rng: &mut R) -> $ty { let x = $next_u(rng); - x.uniform01() + x.closed_open01() } } impl Distribution<$ty> for Closed01 { fn sample(&self, rng: &mut R) -> $ty { + // The problem with a closed range over [0,1] is that it needs + // 2^n+1 samples to generate a uniform distribution. Which is + // difficult, as it means we either have to reject about half + // the generated random numbers, or just not include one number + // in the distribution. That is why, instead of a closed range, + // we actually sample from the half-open range (0,1]. + // + // Floating-point numbers have more precision near zero. This + // means for a f32 that only 1 in 2^32 samples will give exactly + // 0.0. But 1 in 2^23 will give exactly 1.0 due to rounding. + // Because the chance to sample 0.0 is so low, this half-open + // range is a very good appoximation of a closed range. let x = $next_u(rng); - x.closed01() + x.open_closed01() } } impl Distribution<$ty> for Open01 { // Sample from the open range (0,1). - // This uses the rejection method: use `Uniform01`, and if the + // This uses the rejection method: use `closed_open01`, and if the // result is 0.0, reject the result and try again. fn sample(&self, rng: &mut R) -> $ty { let mut x = 0; while x == 0 { // 0 converts to 0.0 x = $next_u(rng); } - x.uniform01() + x.closed_open01() } } } diff --git a/src/lib.rs b/src/lib.rs index 6883dad1cb6..d198120109b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -271,6 +271,7 @@ pub mod iter; pub mod prng; pub mod reseeding; pub mod sequences; +pub mod utils; #[cfg(feature="std")] mod os; diff --git a/src/distributions/float_conversions.rs b/src/utils.rs similarity index 52% rename from src/distributions/float_conversions.rs rename to src/utils.rs index 335dcad0d85..406d721b565 100644 --- a/src/distributions/float_conversions.rs +++ b/src/utils.rs @@ -8,7 +8,14 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! Convert a random int to a float in the range [0,1] +//! A collection of utility functions +//! +//! This module exposes some utility functions used internally by the `Rand` +//! crate. They are not the intended or most ergonomic way to use the library. +//! +//! The `FloatConversions` trait provides the building blocks to convert a +//! random integer to a IEEE floating point number uniformly distributed over a +//! some range. use core::mem; @@ -42,49 +49,53 @@ impl FloatBitOps for u64 { } } +/// Convert a random integer to a uniformly distributed floating point number. pub trait FloatConversions { type F; - /// Convert a random number to a floating point number sampled from the + /// Convert a random integer to a floating point number sampled from the /// uniformly distributed half-open range [0,1). - /// Closer to zero the distribution gains more precision, up to - /// 2^-32 for f32 and 2^-64 for f64. - fn uniform01(self) -> Self::F; + /// The precision gets higher closer to zero, up to 2^-32 for f32 + /// and 2^-64 for f64. + fn closed_open01(self) -> Self::F; - /// Convert a random number to a floating point number sampled from the - /// uniformly distributed closed range [0,1]. - /// Closer to zero the distribution gains more precision, up to - /// 2^-32 for f32 and 2^-64 for f64. - fn closed01(self) -> Self::F; + /// Convert a random integer to a floating point number sampled from the + /// uniformly distributed half-open range (0,1]. + /// The precision gets higher closer to zero, up to 2^-32 for f32 + /// and 2^-64 for f64. + fn open_closed01(self) -> Self::F; - /// Convert a random number to a floating point number sampled from the + /// Convert a random integer to a floating point number sampled from the /// uniformly distributed half-open range [-1,1). - /// Closer to zero the distribution gains more precision, up to - /// 2^-32 for f32 and 2^-64 for f64. - fn uniform11(self) -> Self::F; + /// The precision gets higher closer to zero, up to 2^-32 for f32 + /// and 2^-64 for f64. + fn closed_open11(self) -> Self::F; - /// Convert a random number to a floating point number sampled from the + /// Convert a random integer to a floating point number sampled from the /// uniformly distributed half-open range [0,1). - /// The distribution has a fixed precision of 2^-23 for f32 and - /// 2^-52 for f64. - /// The advantage of `uniform01_simple` is it leaves a couple of the random - /// bits available for other purposes (the 9 rightmost bits for f32, and - /// 12 for f64). - fn uniform01_simple(self) -> Self::F; - - /// Convert a random number to a floating point number sampled from the + /// The precision is fixed, with 2^-23 for f32 and 2^-52 for f64. + /// This leaves a couple of the random bits available for other purposes + /// (the 9 least significant bits for f32, and 12 for f64). + fn closed_open01_fixed(self) -> Self::F; + + /// Convert a random integer to a floating point number sampled from the + /// uniformly distributed half-open range (0,1]. + /// The precision is fixed, with 2^-23 for f32 and 2^-52 for f64. + /// This leaves a couple of the random bits available for other purposes + /// (the 9 least significant bits for f32, and 12 for f64). + fn open_closed01_fixed(self) -> Self::F; + + /// Convert a random integer to a floating point number sampled from the /// uniformly distributed half-open range [-1,1). - /// The distribution has a fixed precision of 2^-22 for f32 and - /// 2^-51 for f64. - /// The advantage of `uniform11_simple` is it leaves a couple of the random - /// bits available for other purposes (the 9 rightmost bits for f32, and - /// 12 for f64). - fn uniform11_simple(self) -> Self::F; + /// The precision is fixed, with 2^-22 for f32 and 2^-51 for f64. + /// This leaves a couple of the random bits available for other purposes + /// (the 9 least significant bits for f32, and 12 for f64). + fn closed_open11_fixed(self) -> Self::F; } macro_rules! float_impls { ($ty:ty, $uty:ty, $FLOAT_SIZE:expr, $FRACTION_BITS:expr, - $FRACTION_MASK:expr, $next_u:path) => { + $FRACTION_MASK:expr, $EXPONENT_BIAS:expr, $next_u:path) => { impl FloatConversions for $uty { type F = $ty; @@ -106,7 +117,7 @@ macro_rules! float_impls { /// exponent of -10 as if they have -9 as exponent, and substract /// 2^-9. #[inline(always)] - fn uniform01(self) -> $ty { + fn closed_open01(self) -> $ty { const MIN_EXPONENT: i32 = $FRACTION_BITS - $FLOAT_SIZE; #[allow(non_snake_case)] let ADJUST: $ty = (0 as $uty).binor_exp(MIN_EXPONENT); @@ -124,31 +135,20 @@ macro_rules! float_impls { } /// Convert a random number to a floating point number sampled from - /// the uniformly distributed closed range [0,1]. - /// - /// The problem with a closed range over [0,1] is that the chance to - /// sample 1.0 exactly is 2^n+1. Which is difficult, as it is not a - /// power of two. Instead of a closed range, we actually sample from - /// the half-open range (0,1]. + /// the uniformly distributed chalf-open range (0,1]. /// - /// Floating-point numbers have more precision near zero. This means - /// for a f32 that only 1 in 2^32 samples will give exactly 0.0. But - /// 1 in 2^23 will give exactly 1.0 due to rounding. Because the - /// chance to sample 0.0 is so low, this half-open range is a very - /// good appoximation of a closed range. - /// - /// This method is similar to `Uniform01`, with one extra step: + /// This method is similar to `closed_open01`, with one extra step: /// If the fractional part ends up as zero, add 1 to the exponent in /// 50% of the cases. This changes 0.5 to 1.0, 0.25 to 0.5, etc. /// /// The chance to select these numbers that staddle the boundery - /// between exponents is 33% to high in `Uniform01` (e.g. 33%*2^-23 - /// for 0.5f32). The reason is that with 0.5 as example the distance - /// to the next number is 2^-23, but to the previous number 2^-24. - /// With the adjustment they have exactly the right chance of - /// getting sampled. + /// between exponents is 33% to high in `closed_open01` + /// (e.g. 33%*2^-23 for 0.5f32). The reason is that with 0.5 as + /// example the distance to the next number is 2^-23, but to the + /// previous number 2^-24. With the adjustment they have exactly the + /// right chance of getting sampled. #[inline(always)] - fn closed01(self) -> $ty { + fn open_closed01(self) -> $ty { const MIN_EXPONENT: i32 = $FRACTION_BITS - $FLOAT_SIZE; #[allow(non_snake_case)] let ADJUST: $ty = (0 as $uty).binor_exp(MIN_EXPONENT); @@ -164,7 +164,7 @@ macro_rules! float_impls { // one of the unused bits for the exponent. // Shift uncondinionally for the lowest exponents, because // there are no more random bits left. - if (exp > MIN_EXPONENT) && + if (exp <= MIN_EXPONENT) || (self & 1 << ($FLOAT_SIZE - 1) != 0) { exp += 1; } @@ -177,14 +177,14 @@ macro_rules! float_impls { /// Convert a random number to a floating point number sampled from /// the uniformly distributed half-open range [-1,1). /// - /// This uses the same method as `uniform01`. One of the random bits - /// that it uses for the exponent, is now used as a sign bit. + /// This uses the same method as `closed_open01`. One of the random + /// bits that it uses for the exponent, is now used as a sign bit. /// /// This samples numbers from the range (-1,-0] and [0,1). Ideally /// we would not sample -0.0, but include -1.0. That is why for /// negative numbers we use the extra step from `open01`. #[inline(always)] - fn uniform11(self) -> $ty { + fn closed_open11(self) -> $ty { const MIN_EXPONENT: i32 = $FRACTION_BITS - $FLOAT_SIZE + 1; const SIGN_BIT: $uty = 1 << ($FLOAT_SIZE - 1); #[allow(non_snake_case)] @@ -202,7 +202,7 @@ macro_rules! float_impls { // one of the unused bits for the exponent. // Shift uncondinionally for the lowest exponents, because // there are no more random bits left. - if (exp > MIN_EXPONENT) && + if (exp <= MIN_EXPONENT) || (self & 1 << ($FLOAT_SIZE - 2) != 0) { exp += 1; } @@ -220,10 +220,10 @@ macro_rules! float_impls { /// MCQMC'08. Given that the IEEE floating point numbers are /// uniformly distributed over [1,2), we generate a number in this /// range and then offset it onto the range [0,1). Our choice of - /// bits (masking v. shifting) is arbitrary and - /// should be immaterial for high quality generators. For low - /// quality generators (ex. LCG), prefer bitshifting due to - /// correlation between sequential low order bits. + /// bits (masking v. shifting) is arbitrary and should be immaterial + /// for high quality generators. For low quality generators + /// (ex. LCG), prefer bitshifting due to correlation between + /// sequential low order bits. /// /// See: /// A PRNG specialized in double precision floating point numbers @@ -231,16 +231,32 @@ macro_rules! float_impls { /// http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/ARTICLES/dSFMT.pdf /// http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/dSFMT-slide-e.pdf #[inline(always)] - fn uniform01_simple(self) -> $ty { + fn closed_open01_fixed(self) -> $ty { const EXPONENT: i32 = 0; let fraction = self >> ($FLOAT_SIZE - $FRACTION_BITS); fraction.binor_exp(EXPONENT) - 1.0 } - /// Like `uniform01_simple`, but generate a number in the range + // Similar to `closed_open01_fixed`, with one modification. After + // converting to a float but before substracting -1.0, we want to + // shift every number one place to the next floating point number + // (e.g. +1 ulp). Thanks to how floating points a represented, this + // is as simple as adding 1 to the integer representation. + // Because we inline the `binor_exp` function, this looks more + // different than it really is. + #[inline(always)] + fn open_closed01_fixed(self) -> $ty { + let exponent_bits: $uty = $EXPONENT_BIAS << $FRACTION_BITS + 0; + let fraction = self >> ($FLOAT_SIZE - $FRACTION_BITS); + let bits = fraction | exponent_bits; + let float: $ty = unsafe { mem::transmute(bits + 1) }; + float - 1.0 + } + + /// Like `closed_open01_fixed`, but generate a number in the range /// [2,4) and offset it onto the range [-1,1). #[inline(always)] - fn uniform11_simple(self) -> $ty { + fn closed_open11_fixed(self) -> $ty { const EXPONENT: i32 = 1; let fraction = self >> ($FLOAT_SIZE - $FRACTION_BITS); fraction.binor_exp(EXPONENT) - 3.0 @@ -248,47 +264,110 @@ macro_rules! float_impls { } } } -float_impls! { f32, u32, 32, 23, 0x7FFFFF, Rng::next_u32 } -float_impls! { f64, u64, 64, 52, 0xFFFFFFFFFFFFF, Rng::next_u64 } +float_impls! { f32, u32, 32, 23, 0x7f_ffff, 127, Rng::next_u32 } +float_impls! { f64, u64, 64, 52, 0xf_ffff_ffff_ffff, 1023, Rng::next_u64 } #[cfg(test)] mod tests { use super::FloatConversions; + use distributions::uniform; #[test] - fn uniform01_edge_cases() { + fn closed_open01_edge_cases() { // Test that the distribution is a half-open range over [0,1). // These constants happen to generate the lowest and highest floats in // the range. - assert!(0u32.uniform01() == 0.0); - assert!(0xffff_ffffu32.uniform01() == 0.99999994); + assert!(0u32.closed_open01() == 0.0); + assert!(0xffff_ffffu32.closed_open01() == 0.99999994); - assert!(0u64.uniform01() == 0.0); - assert!(0xffff_ffff_ffff_ffffu64.uniform01() == 0.9999999999999999); + assert!(0u64.closed_open01() == 0.0); + assert!(0xffff_ffff_ffff_ffffu64.closed_open01() == 0.9999999999999999); } #[test] - fn closed01_edge_cases() { + fn open_closed01_edge_cases() { // Test that the distribution is a half-open range over (0,1]. // These constants happen to generate the lowest and highest floats in // the range. - assert!(1u32.closed01() == 2.3283064e-10); // 2^-32 - assert!(0xff80_0000u32.closed01() == 1.0); + assert!(1u32.open_closed01() == 2.3283064e-10); // 2^-32 + assert!(0xff80_0000u32.open_closed01() == 1.0); - assert!(1u64.closed01() == 5.421010862427522e-20); // 2^-64 - assert!(0xfff0_0000_0000_0000u64.closed01() == 1.0); + assert!(1u64.open_closed01() == 5.421010862427522e-20); // 2^-64 + assert!(0xfff0_0000_0000_0000u64.open_closed01() == 1.0); } #[test] - fn uniform11_edge_cases() { + fn closed_open11_edge_cases() { // Test that the distribution is a half-open range over [-1,1). // These constants happen to generate the lowest and highest floats in // the range. - assert!(0xff80_0000u32.uniform11() == -1.0); - assert!(0x7fff_ffffu32.uniform11() == 0.99999994); + assert!(0xff80_0000u32.closed_open11() == -1.0); + assert!(0x7fff_ffffu32.closed_open11() == 0.99999994); // 1 - 2^-24 + + assert!(0xfff0_0000_0000_0000u64.closed_open11() == -1.0); + assert!(0x7fff_ffff_ffff_ffffu64.closed_open11() == 0.9999999999999999); + } + + #[test] + fn closed_open01_fixed_edge_cases() { + // Test that the distribution is a half-open range over [0,1). + // These constants happen to generate the lowest and highest floats in + // the range. + let mut rng = ::test::rng(); + let mut bits: u32 = uniform(&mut rng); + bits = bits & 0x1ff; // 9 bits with no influence + + assert!((bits | 0).closed_open01_fixed() == 0.0); + assert!((bits | 0xfffffe00).closed_open01_fixed() + == 0.9999999); // 1 - 2^-23 + + let mut bits: u64 = uniform(&mut rng); + bits = bits & 0xfff; // 12 bits with no influence + + assert!((bits | 0).closed_open01_fixed() == 0.0); + assert!((bits | 0xffff_ffff_ffff_f000).closed_open01_fixed() == + 0.9999999999999998); // 1 - 2^-52 + } + + #[test] + fn open_closed01_fixed_edge_cases() { + // Test that the distribution is a half-open range over [0,1). + // These constants happen to generate the lowest and highest floats in + // the range. + let mut rng = ::test::rng(); + let mut bits: u32 = uniform(&mut rng); + bits = bits & 0x1ff; // 9 bits with no influence + + assert!((bits | 0).open_closed01_fixed() == 1.1920929e-7); // 2^-23 + assert!((bits | 0xfffffe00).open_closed01_fixed() == 1.0); + + let mut bits: u64 = uniform(&mut rng); + bits = bits & 0xfff; // 12 bits with no influence + + assert!((bits | 0).open_closed01_fixed() == + 2.220446049250313e-16); // 2^-52 + assert!((bits | 0xffff_ffff_ffff_f000).open_closed01_fixed() == 1.0); + } + + #[test] + fn closed_open11_fixed_edge_cases() { + // Test that the distribution is a half-open range over [0,1). + // These constants happen to generate the lowest and highest floats in + // the range. + let mut rng = ::test::rng(); + let mut bits: u32 = uniform(&mut rng); + bits = bits & 0x1ff; // 9 bits with no influence + + assert!((bits | 0).closed_open11_fixed() == -1.0); + assert!((bits | 0xfffffe00).closed_open11_fixed() + == 0.99999976); // 1 - 2^-22 + + let mut bits: u64 = uniform(&mut rng); + bits = bits & 0xfff; // 12 bits with no influence - assert!(0xfff0_0000_0000_0000u64.uniform11() == -1.0); - assert!(0x7fff_ffff_ffff_ffffu64.uniform11() == 0.9999999999999999); + assert!((bits | 0).closed_open11_fixed() == -1.0); + assert!((bits | 0xffff_ffff_ffff_f000).closed_open11_fixed() == + 0.9999999999999996); // 1 - 2^-51 } } From 1dbf23da15806ff5be08b09be89e66d304b22359 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Sun, 3 Sep 2017 08:19:53 +0200 Subject: [PATCH 086/247] Add a few extra benchmarks --- benches/distributions.rs | 4 ++-- benches/misc.rs | 50 +++++++++++++++++++++++++++++++++++++--- 2 files changed, 49 insertions(+), 5 deletions(-) diff --git a/benches/distributions.rs b/benches/distributions.rs index cff1e3271f7..35b6aa270c5 100644 --- a/benches/distributions.rs +++ b/benches/distributions.rs @@ -23,10 +23,10 @@ fn distr_baseline(b: &mut Bencher) { b.iter(|| { for _ in 0..::RAND_BENCH_N { - f64::rand(&mut rng, Default); + u64::rand(&mut rng, Default); } }); - b.bytes = size_of::() as u64 * ::RAND_BENCH_N; + b.bytes = size_of::() as u64 * ::RAND_BENCH_N; } diff --git a/benches/misc.rs b/benches/misc.rs index 0de6b468872..0eb39b2d3e7 100644 --- a/benches/misc.rs +++ b/benches/misc.rs @@ -10,7 +10,7 @@ use test::{black_box, Bencher}; use rand::StdRng; use rand::prng::XorShiftRng; use rand::sequences::{sample, Shuffle}; -use rand::distributions::{Rand, Uniform, Uniform01}; +use rand::distributions::{Rand, Uniform, Uniform01, Closed01, Open01}; #[bench] fn misc_baseline_32(b: &mut Bencher) { @@ -35,7 +35,7 @@ fn misc_baseline_64(b: &mut Bencher) { } #[bench] -fn misc_convert_f32(b: &mut Bencher) { +fn misc_uniform01_f32(b: &mut Bencher) { let mut rng = StdRng::new().unwrap(); b.iter(|| { for _ in 0..RAND_BENCH_N { @@ -46,7 +46,7 @@ fn misc_convert_f32(b: &mut Bencher) { } #[bench] -fn misc_convert_f64(b: &mut Bencher) { +fn misc_uniform01_f64(b: &mut Bencher) { let mut rng = StdRng::new().unwrap(); b.iter(|| { for _ in 0..RAND_BENCH_N { @@ -56,6 +56,50 @@ fn misc_convert_f64(b: &mut Bencher) { b.bytes = size_of::() as u64 * RAND_BENCH_N; } +#[bench] +fn misc_closed01_f32(b: &mut Bencher) { + let mut rng = StdRng::new().unwrap(); + b.iter(|| { + for _ in 0..RAND_BENCH_N { + black_box(f32::rand(&mut rng, Closed01)); + } + }); + b.bytes = size_of::() as u64 * RAND_BENCH_N; +} + +#[bench] +fn misc_closed01_f64(b: &mut Bencher) { + let mut rng = StdRng::new().unwrap(); + b.iter(|| { + for _ in 0..RAND_BENCH_N { + black_box(f64::rand(&mut rng, Closed01)); + } + }); + b.bytes = size_of::() as u64 * RAND_BENCH_N; +} + +#[bench] +fn misc_open01_f32(b: &mut Bencher) { + let mut rng = StdRng::new().unwrap(); + b.iter(|| { + for _ in 0..RAND_BENCH_N { + black_box(f32::rand(&mut rng, Open01)); + } + }); + b.bytes = size_of::() as u64 * RAND_BENCH_N; +} + +#[bench] +fn misc_open01_f64(b: &mut Bencher) { + let mut rng = StdRng::new().unwrap(); + b.iter(|| { + for _ in 0..RAND_BENCH_N { + black_box(f64::rand(&mut rng, Open01)); + } + }); + b.bytes = size_of::() as u64 * RAND_BENCH_N; +} + #[bench] fn misc_shuffle_100(b: &mut Bencher) { let mut rng = XorShiftRng::new().unwrap(); From ac33b288c0dd3585ff28e4c7c88c21cd04867c5b Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Sun, 3 Sep 2017 08:35:39 +0200 Subject: [PATCH 087/247] Add a changelog --- CHANGELOG.md | 233 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 233 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000000..b33ef597977 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,233 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) +and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). + +## [Unreleased] + + +## [0.3.16] - 2016-07-27 +### Added +- Implement Debug for mote non-public types +- implement `Rand` for (i|u)i128 +- Support for Fuchsia + +### Changed +- Add inline attribute to SampleRange::construct_range. + This improves the benchmark for sample in 11% and for shuffle in 16%. +- Use `RtlGenRandom` instead of `CryptGenRandom` + + +## [0.3.15] - 2016-11-26 +### Added +- Add `Rng` trait method `choose_mut` +- Redox support + +### Changed +- Use `arc4rand` for `OsRng` on FreeBSD. +- Use `arc4random(3)` for `OsRng` on OpenBSD. + +### Fixed +- Fix filling buffers 4 GiB or larger with `OsRng::fill_bytes` on Windows + + +## [0.3.14] - 2016-02-13 +### Fixed +- Inline definitions from winapi/advapi32, wich decreases build times + + +## [0.3.13] - 2016-01-09 +### Fixed +- Compatible with Rust 1.7.0-nightly (needed some extra type annotations) + + +## [0.3.12] - 2015-11-09 +### Changed +- Replaced the methods in `next_f32` and `next_f64` with the technique described + Saito & Matsumoto at MCQMC'08. The new method should exhibit a slightly more + uniform distribution. +- Depend on libc 0.2 + +### Fixed +- Fix iterator protocol issue in `rand::sample` + + +## [0.3.11] - 2015-08-31 +### Added +- Implement `Rand` for arrays with n <= 32 + + +## [0.3.10] - 2015-08-17 +### Added +- Support for NaCl platforms + +### Changed +- Allow `Rng` to be `?Sized`, impl for `&mut R` and `Box` where `R: ?Sized + Rng` + + +## [0.3.9] - 2015-06-18 +### Changed +- Use `winapi` for Windows API things + +### Fixed +- Fixed test on stable/nightly +- Fix `getrandom` syscall number for aarch64-unknown-linux-gnu + + +## [0.3.8] - 2015-04-23 +### Changed +- `log` is a dev dependency + +### Fixed +- Fix race condition of atomics in `is_getrandom_available` + + +## [0.3.7] - 2015-04-03 +### Fixed +- Derive Copy/Clone changes + + +## [0.3.6] - 2015-04-02 +### Changed +- Move to stable Rust! + + +## [0.3.5] - 2015-04-01 +### Fixed +- Compatible with Rust master + + +## [0.3.4] - 2015-03-31 +### Added +- Implement Clone for `Weighted` + +### Fixed +- Compatible with Rust master + + +## [0.3.3] - 2015-03-26 +### Fixed +- Fix compile on Windows + + +## [0.3.2] - 2015-03-26 + + +## [0.3.1] - 2015-03-26 +### Fixed +- Fix compile on Windows + + +## [0.3.0] - 2015-03-25 +### Changed +- Update to use log version 0.3.x + + +## [0.2.1] - 2015-03-22 +### Fixed +- Compatible with Rust master +- Fixed iOS compilation + + +## [0.2.0] - 2015-03-06 +### Fixed +- Compatible with Rust master (move from `old_io` to `std::io`) + + +## [0.1.4] - 2015-03-04 +### Fixed +- Compatible with Rust master (use wrapping ops) + + +## [0.1.3] - 2015-02-20 +### Fixed +- Compatible with Rust master + +### Removed +- Removed Copy inplementaions from RNGs + + +## [0.1.2] - 2015-02-03 +### Added +- Imported functionality from `std::rand`, including: + - `StdRng`, `SeedableRng`, `TreadRng`, `weak_rng()` + - `ReaderRng`: A wrapper around any Reader to treat it as an RNG. +- Imported documentation from `std::rand` +- Imported tests from `std::rand` + + +## [0.1.1] - 2015-02-03 +### Added +- Migrate to a cargo-compatible directory structure. + +### Fixed +- Do not use entropy during `gen_weighted_bool(1)` + + +## [Rust 0.12.0] - 2014-10-09 +### Added +- Impl Rand for tuples of arity 11 and 12 +- Include ChaCha pseudorandom generator +- Add `next_f64` and `next_f32` to Rng +- Implement Clone for PRNGs + +### Changed +- Rename `TaskRng` to `ThreadRng` and `task_rng` to `thread_rng` (since a + runtime is removed from Rust). + +### Fixed +- Improved performance of ISAAC and ISAAC64 by 30% and 12 % respectively, by + informing the optimiser that indexing is never out-of-bounds. + +### Removed +- Removed the Deprecated `choose_option` + + +## [Rust 0.11.0] - 2014-07-02 +### Added +- document when to use `OSRng` in cryptographic context, and explain why we use `/dev/urandom` instead of `/dev/random` +- `Rng::gen_iter()` which will return an infinite stream of random values +- `Rng::gen_ascii_chars()` which will return an infinite stream of random ascii characters + +### Changed +- Now only depends on libcore! 2adf5363f88ffe06f6d2ea5c338d1b186d47f4a1 +- Remove `Rng.choose()`, rename `Rng.choose_option()` to `.choose()` +- Rename OSRng to OsRng +- The WeightedChoice structure is no longer built with a `Vec>`, + but rather a `&mut [Weighted]`. This means that the WeightedChoice + structure now has a lifetime associated with it. +- The `sample` method on `Rng` has been moved to a top-level function in the + `rand` module due to its dependence on `Vec`. + +### Removed +- `Rng::gen_vec()` was removed. Previous behavior can be regained with + `rng.gen_iter().take(n).collect()` +- `Rng::gen_ascii_str()` was removed. Previous behavior can be regained with + `rng.gen_ascii_chars().take(n).collect()` +- {IsaacRng, Isaac64Rng, XorShiftRng}::new() have all been removed. These all + relied on being able to use an OSRng for seeding, but this is no longer + available in librand (where these types are defined). To retain the same + functionality, these types now implement the `Rand` trait so they can be + generated with a random seed from another random number generator. This allows + the stdlib to use an OSRng to create seeded instances of these RNGs. +- Rand implementations for `Box` and `@T` were removed. These seemed to be + pretty rare in the codebase, and it allows for librand to not depend on + liballoc. Additionally, other pointer types like Rc and Arc were not + supported. +- Remove a slew of old deprecated functions + + +## [Rust 0.10] - 2014-04-03 +### Changed +- replace `Rng.shuffle's` functionality with `.shuffle_mut` +- bubble up IO errors when creating an OSRng + +### Fixed +- Use `fill()` instead of `read()` +- Rewrite OsRng in Rust for windows + +## [0.10-pre] - 2014-03-02 +### Added +- Seperate `rand` out of the standard library + From a2b2196fc4d0b7f06191ed116a54f1873b8eddda Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sun, 3 Sep 2017 11:20:07 +0100 Subject: [PATCH 088/247] Fix no_std build --- src/lib.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d198120109b..cf1a13ed00e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -280,12 +280,6 @@ mod read; #[cfg(feature="std")] mod thread_local; -/// Error type for cryptographic generators. Only operating system and hardware -/// generators should be able to fail. In such cases there is little that can -/// be done besides try again later. -#[derive(Debug)] -pub struct CryptoError; - /// A random number generator. pub trait Rng { /// Return the next random u32. @@ -586,6 +580,13 @@ impl<'a> SeedableRng<&'a [usize]> for StdRng { } } +/// Error type for cryptographic generators. Only operating system and hardware +/// generators should be able to fail. In such cases there is little that can +/// be done besides try again later. +#[derive(Debug)] +pub struct CryptoError; + +#[cfg(feature="std")] impl From<::std::io::Error> for CryptoError { fn from(_: ::std::io::Error) -> CryptoError { CryptoError From eb162fea758415ae7ecd98cce62e8bb7b57a7a87 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sun, 3 Sep 2017 12:44:53 +0100 Subject: [PATCH 089/247] Remove SeedableRng impl for StdRng See https://github.com/rust-lang/rfcs/pull/2118#issuecomment-326401160 --- src/lib.rs | 43 ++++++------------------------------------- src/reseeding.rs | 29 ----------------------------- 2 files changed, 6 insertions(+), 66 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index cf1a13ed00e..152c5541ea3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -252,8 +252,6 @@ #[cfg(feature="std")] extern crate core; -use core::mem::transmute; - #[cfg(feature="std")] pub use read::ReadRng; #[cfg(feature="std")] @@ -533,6 +531,10 @@ impl Rng for ConstRng { /// The standard RNG. This is designed to be efficient on the current /// platform. +/// +/// The underlying algorithm is not fixed, thus values from this generator +/// cannot be guaranteed to be reproducible. For this reason, `StdRng` does +/// not support `SeedableRng`. #[derive(Copy, Clone, Debug)] pub struct StdRng { rng: IsaacWordRng, @@ -568,18 +570,6 @@ impl Rng for StdRng { } } -impl<'a> SeedableRng<&'a [usize]> for StdRng { - fn reseed(&mut self, seed: &'a [usize]) { - // the internal RNG can just be seeded from the above - // randomness. - self.rng.reseed(unsafe {transmute(seed)}) - } - - fn from_seed(seed: &'a [usize]) -> StdRng { - StdRng { rng: SeedableRng::from_seed(unsafe {transmute(seed)}) } - } -} - /// Error type for cryptographic generators. Only operating system and hardware /// generators should be able to fail. In such cases there is little that can /// be done besides try again later. @@ -596,8 +586,8 @@ impl From<::std::io::Error> for CryptoError { #[cfg(test)] mod test { - use {Rng, thread_rng, SeedableRng, StdRng, ConstRng, iter, Sample}; - use distributions::{uniform, ascii_word_char}; + use {Rng, thread_rng, ConstRng, Sample}; + use distributions::{uniform}; use distributions::{Uniform, Range, Exp}; use sequences::Shuffle; use std::iter::repeat; @@ -691,27 +681,6 @@ mod test { } } - #[test] - fn test_std_rng_seeded() { - let s = iter(&mut thread_rng()).map(|rng| uniform(rng)).take(256).collect::>(); - let mut ra: StdRng = SeedableRng::from_seed(&s[..]); - let mut rb: StdRng = SeedableRng::from_seed(&s[..]); - assert!(iter_eq(iter(&mut ra).map(|rng| ascii_word_char(rng)).take(100), - iter(&mut rb).map(|rng| ascii_word_char(rng)).take(100))); - } - - #[test] - fn test_std_rng_reseed() { - let s = iter(&mut thread_rng()).map(|rng| uniform(rng)).take(256).collect::>(); - let mut r: StdRng = SeedableRng::from_seed(&s[..]); - let string1 = iter(&mut r).map(|rng| ascii_word_char(rng)).take(100).collect::(); - - r.reseed(&s); - - let string2 = iter(&mut r).map(|rng| ascii_word_char(rng)).take(100).collect::(); - assert_eq!(string1, string2); - } - #[test] fn test_sample_from_rng() { // use a static Rng type: diff --git a/src/reseeding.rs b/src/reseeding.rs index 35498c4e3a4..51edcb8c8fa 100644 --- a/src/reseeding.rs +++ b/src/reseeding.rs @@ -100,35 +100,6 @@ impl, Rsdr: Reseeder + Debug + Default> } /// Something that can be used to reseed an RNG via `ReseedingRng`. -/// -/// # Example -/// -/// ```rust -/// use rand::{Rng, SeedableRng, StdRng, iter}; -/// use rand::distributions::ascii_word_char; -/// use rand::reseeding::{Reseeder, ReseedingRng}; -/// -/// #[derive(Debug)] -/// struct TickTockReseeder { tick: bool } -/// impl Reseeder for TickTockReseeder { -/// fn reseed(&mut self, rng: &mut StdRng) { -/// let val = if self.tick {0} else {1}; -/// rng.reseed(&[val]); -/// self.tick = !self.tick; -/// } -/// } -/// fn main() { -/// let rsdr = TickTockReseeder { tick: true }; -/// -/// let inner = StdRng::new().unwrap(); -/// let mut rng = ReseedingRng::new(inner, 10, rsdr); -/// -/// // this will repeat, because it gets reseeded very frequently. -/// let s: String = iter(&mut rng).map(|rng| ascii_word_char(rng)).take(100).collect(); -/// println!("{}", s); -/// } -/// -/// ``` pub trait Reseeder { /// Reseed the given RNG. fn reseed(&mut self, rng: &mut R); From d6f48319fd82840b455239e34afc2e4c0f8793fe Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sun, 3 Sep 2017 12:46:24 +0100 Subject: [PATCH 090/247] Rng: fix to LE, fix missing next_u128 in wrappers, improve fill_bytes default impl --- src/lib.rs | 107 +++++++++++++++++++++++++++++------------------ src/read.rs | 27 +++++++----- src/reseeding.rs | 7 ++++ 3 files changed, 91 insertions(+), 50 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 152c5541ea3..01f12ee7b2a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -279,46 +279,78 @@ mod read; mod thread_local; /// A random number generator. +/// +/// There are two classes of generators: *algorithmic* generators, also called +/// PRNGs (Pseudo-Random Number Generators) and *external* generators. +/// +/// Another classification for generators is those that are cryptographically +/// secure (CSPRNGs) and those that are not. CSPRNGs should satisfy two +/// additional properties: (1) given the first *k* bits of an algorithm's output +/// sequence, it should not be possible using polynomial-time algorithms to +/// predict the next bit with probability significantly greater than 50%, and +/// (2) if a CSPRNG's state is revealed, it should not be +/// computationally-feasible to reconstruct output prior to this. +/// +/// PRNGs are expected to be reproducible: that is, when a fixed algorithm is +/// seeded with a fixed value, then calling *any* sequence of the `Rng`'s +/// functions should produce a fixed sequence of values, and produce the *same* +/// sequence of values on every platform. This necessitates that a PRNG have +/// fixed endianness. +/// +/// All default implementations use little-endian code (e.g. to construct a +/// `u64` from two `u32` numbers, the first is the low part). To implement +/// `next_u32` in terms of `next_u64`, one should write `self.next_u64() as u32` +/// which takes the least-significant half (LE order). +/// +/// PRNGs are normally infallible, while external generators may fail. PRNGs +/// however have a finite period, and may emit an error rather than loop (this +/// is important for CSPRNGs which could conceivably cycle, but non-crypto +/// generators should simply cycle; in many cases the period is so long that +/// consuming all available values would be inconceivable). +/// +/// TODO: details on error handling are under discussion; for now implementations +/// may panic. pub trait Rng { /// Return the next random u32. - // FIXME #rust-lang/rfcs#628: Should be implemented in terms of next_u64 fn next_u32(&mut self) -> u32; /// Return the next random u64. /// - /// By default this is implemented in terms of `next_u32`. An + /// By default this is implemented in terms of `next_u32` in LE order. An /// implementation of this trait must provide at least one of /// these two methods. Similarly to `next_u32`, this rarely needs /// to be called directly, prefer `r.gen()` to `r.next_u64()`. fn next_u64(&mut self) -> u64 { - ((self.next_u32() as u64) << 32) | (self.next_u32() as u64) + // Use LE; we explicitly generate one value before the next. + let x = self.next_u32() as u64; + let y = self.next_u32() as u64; + (y << 32) | x } #[cfg(feature = "i128_support")] /// Return the next random u128. /// - /// By default this is implemented in terms of `next_u64`. + /// By default this is implemented in terms of `next_u64` in LE order. fn next_u128(&mut self) -> u128 { - ((self.next_u64() as u128) << 64) | (self.next_u64() as u128) + // Use LE; we explicitly generate one value before the next. + let x = self.next_u64() as u128; + let y = self.next_u64() as u128; + (y << 64) | x } - /// Fill `dest` with random data. - /// - /// This has a default implementation in terms of `next_u64` and - /// `next_u32`, but should be overridden by implementations that - /// offer a more efficient solution than just calling those - /// methods repeatedly. + /// Fill `dest` entirely with random data. /// - /// This method does *not* have a requirement to bear any fixed - /// relationship to the other methods, for example, it does *not* - /// have to result in the same output as progressively filling - /// `dest` with `self.gen::()`, and any such behaviour should - /// not be relied upon. + /// This has a default implementation in terms of `next_u64` in LE order, + /// but should be overridden by implementations that + /// offer a more efficient solution than just calling `next_u64` + /// repeatedly. /// - /// This method should guarantee that `dest` is entirely filled - /// with new data, and may panic if this is impossible - /// (e.g. reading past the end of a file that is being used as the - /// source of randomness). + /// This method does *not* have any requirement on how much of the + /// generated random number stream is consumed; e.g. the default + /// implementation uses `next_u64` thus consuming 8 bytes even when only + /// 1 is required. A different implementation might use `next_u32` and + /// only consume 4 bytes; *however* any change affecting *reproducibility* + /// of output must be considered a breaking change. /// /// # Example /// @@ -330,26 +362,21 @@ pub trait Rng { /// println!("{:?}", &v[..]); /// ``` fn fill_bytes(&mut self, dest: &mut [u8]) { - // this could, in theory, be done by transmuting dest to a - // [u64], but this is (1) likely to be undefined behaviour for - // LLVM, (2) has to be very careful about alignment concerns, - // (3) adds more `unsafe` that needs to be checked, (4) - // probably doesn't give much performance gain if - // optimisations are on. - let mut count = 0; - let mut num = 0; - for byte in dest.iter_mut() { - if count == 0 { - // we could micro-optimise here by generating a u32 if - // we only need a few more bytes to fill the vector - // (i.e. at most 4). - num = self.next_u64(); - count = 8; - } - - *byte = (num & 0xff) as u8; - num >>= 8; - count -= 1; + use core::cmp::min; + use core::intrinsics::copy_nonoverlapping; + use core::mem::size_of; + + let mut pos = 0; + let len = dest.len(); + while len > pos { + // Cast pointer, effectively to `&[u8; 8]`, and copy as many bytes + // as required. Byte-swap x on BE architectures. + let x = self.next_u64().to_le(); + let xp = &x as *const u64 as *const u8; + let p: *mut u8 = unsafe{ dest.as_mut_ptr().offset(pos as isize) }; + let n = min(len - pos, size_of::()); + unsafe{ copy_nonoverlapping(xp, p, n); } + pos += n; } } } diff --git a/src/read.rs b/src/read.rs index fbf35d38df3..42c6c0451fb 100644 --- a/src/read.rs +++ b/src/read.rs @@ -46,21 +46,28 @@ impl ReadRng { } } +macro_rules! impl_uint_from_fill { + ($ty:ty, $N:expr, $self:expr) => ({ + // Transmute and convert from LE (i.e. byte-swap on BE) + assert_eq!($N, ::core::mem::size_of::<$ty>()); + let mut buf = [0u8; $N]; + fill(&mut $self.reader, &mut buf).unwrap(); + unsafe{ *(buf.as_ptr() as *const $ty) }.to_le() + }); +} + impl Rng for ReadRng { fn next_u32(&mut self) -> u32 { - // This is designed for speed: reading a LE integer on a LE - // platform just involves blitting the bytes into the memory - // of the u32, similarly for BE on BE; avoiding byteswapping. - let mut buf = [0; 4]; - fill(&mut self.reader, &mut buf).unwrap(); - unsafe { *(buf.as_ptr() as *const u32) } + impl_uint_from_fill!(u32, 4, self) } fn next_u64(&mut self) -> u64 { - // see above for explanation. - let mut buf = [0; 8]; - fill(&mut self.reader, &mut buf).unwrap(); - unsafe { *(buf.as_ptr() as *const u64) } + impl_uint_from_fill!(u64, 8, self) + } + #[cfg(feature = "i128_support")] + fn next_u128(&mut self) -> u128 { + impl_uint_from_fill!(u128, 16, self) } + fn fill_bytes(&mut self, v: &mut [u8]) { if v.len() == 0 { return } fill(&mut self.reader, v).unwrap(); diff --git a/src/reseeding.rs b/src/reseeding.rs index 51edcb8c8fa..0d6aa52cd22 100644 --- a/src/reseeding.rs +++ b/src/reseeding.rs @@ -72,6 +72,13 @@ impl + Debug> Rng for ReseedingRng { self.rng.next_u64() } + #[cfg(feature = "i128_support")] + fn next_u128(&mut self) -> u128 { + self.reseed_if_necessary(); + self.bytes_generated += 16; + self.rng.next_u128() + } + fn fill_bytes(&mut self, dest: &mut [u8]) { self.reseed_if_necessary(); self.bytes_generated += dest.len() as u64; From 0ca5c4e52274d5665833ab9f43b391ebf522f2e8 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sun, 3 Sep 2017 12:51:09 +0100 Subject: [PATCH 091/247] no_std: fix "unused import" warnings --- src/distributions/mod.rs | 3 ++- src/prng/chacha.rs | 4 ++-- src/prng/isaac.rs | 4 ++-- src/prng/xorshift.rs | 4 ++-- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/distributions/mod.rs b/src/distributions/mod.rs index ff35ae880a0..9d63ba9d136 100644 --- a/src/distributions/mod.rs +++ b/src/distributions/mod.rs @@ -23,7 +23,6 @@ pub use self::default::Default; pub use self::uniform::{uniform, codepoint, ascii_word_char}; pub use self::uniform::{Uniform, Uniform01, Open01, Closed01, AsciiWordChar}; pub use self::range::{Range}; -use utils::FloatConversions; #[cfg(feature="std")] pub use self::gamma::{Gamma, ChiSquared, FisherF, StudentT}; @@ -159,6 +158,8 @@ fn ziggurat( mut pdf: P, mut zero_case: Z) -> f64 where P: FnMut(f64) -> f64, Z: FnMut(&mut R, f64) -> f64 { + use utils::FloatConversions; + loop { // As an optimisation convert the random u64 to a f64 using only // 53 bits, as many as will fit in the float's fraction. diff --git a/src/prng/chacha.rs b/src/prng/chacha.rs index 1ce1e7fe4d3..ce9ba2e97a6 100644 --- a/src/prng/chacha.rs +++ b/src/prng/chacha.rs @@ -11,9 +11,9 @@ //! The ChaCha random number generator. use core::num::Wrapping as w; -use {Rng, SeedableRng, CryptoError}; +use {Rng, SeedableRng}; #[cfg(feature="std")] -use OsRng; +use {OsRng, CryptoError}; #[allow(bad_style)] type w32 = w; diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index c52c83e428b..35e79088064 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -17,9 +17,9 @@ use core::iter::repeat; use core::num::Wrapping as w; use core::fmt; -use {Rng, SeedableRng, CryptoError}; +use {Rng, SeedableRng}; #[cfg(feature="std")] -use OsRng; +use {OsRng, CryptoError}; /// Select 32- or 64-bit variant dependent on pointer size. #[cfg(target_pointer_width = "32")] diff --git a/src/prng/xorshift.rs b/src/prng/xorshift.rs index 9534717e9a3..b8e42ebe059 100644 --- a/src/prng/xorshift.rs +++ b/src/prng/xorshift.rs @@ -11,9 +11,9 @@ //! Xorshift generators use core::num::Wrapping as w; -use {Rng, SeedableRng, CryptoError}; +use {Rng, SeedableRng}; #[cfg(feature="std")] -use OsRng; +use {OsRng, CryptoError}; /// An Xorshift[1] random number /// generator. From 04e326d9cd93ca6a34a5e74a30b71d3338865dc7 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 4 Sep 2017 15:27:24 +0100 Subject: [PATCH 092/247] Include gen_bytes_* generator benchmarks; use macros --- benches/generators.rs | 101 +++++++++++++++++------------------------- 1 file changed, 41 insertions(+), 60 deletions(-) diff --git a/benches/generators.rs b/benches/generators.rs index 1b4f2276d1d..24128a4dbd6 100644 --- a/benches/generators.rs +++ b/benches/generators.rs @@ -4,75 +4,56 @@ extern crate test; extern crate rand; const RAND_BENCH_N: u64 = 1000; +const BYTES_LEN: usize = 1024; use std::mem::size_of; use test::{black_box, Bencher}; -use rand::{StdRng, OsRng, Rand, Default}; +use rand::{Rng, StdRng, OsRng, Rand, Default}; use rand::prng::{XorShiftRng, IsaacRng, Isaac64Rng, ChaChaRng}; -#[bench] -fn gen_usize_xorshift(b: &mut Bencher) { - let mut rng = XorShiftRng::new().unwrap(); - b.iter(|| { - for _ in 0..RAND_BENCH_N { - black_box(usize::rand(&mut rng, Default)); +macro_rules! gen_bytes { + ($fnn:ident, $gen:ident) => { + #[bench] + fn $fnn(b: &mut Bencher) { + let mut rng = $gen::new().unwrap(); + let mut buf = [0u8; BYTES_LEN]; + b.iter(|| { + for _ in 0..RAND_BENCH_N { + rng.fill_bytes(&mut buf); + } + }); + b.bytes = BYTES_LEN as u64 * RAND_BENCH_N; } - }); - b.bytes = size_of::() as u64 * RAND_BENCH_N; + } } -#[bench] -fn gen_usize_isaac(b: &mut Bencher) { - let mut rng = IsaacRng::new().unwrap(); - b.iter(|| { - for _ in 0..RAND_BENCH_N { - black_box(usize::rand(&mut rng, Default)); +gen_bytes!(gen_bytes_xorshift, XorShiftRng); +gen_bytes!(gen_bytes_isaac, IsaacRng); +gen_bytes!(gen_bytes_isaac64, Isaac64Rng); +gen_bytes!(gen_bytes_chacha, ChaChaRng); +gen_bytes!(gen_bytes_std, StdRng); +gen_bytes!(gen_bytes_os, OsRng); + + +macro_rules! gen_usize { + ($fnn:ident, $gen:ident) => { + #[bench] + fn $fnn(b: &mut Bencher) { + let mut rng = $gen::new().unwrap(); + b.iter(|| { + for _ in 0..RAND_BENCH_N { + black_box(usize::rand(&mut rng, Default)); + } + }); + b.bytes = size_of::() as u64 * RAND_BENCH_N; } - }); - b.bytes = size_of::() as u64 * RAND_BENCH_N; + } } -#[bench] -fn gen_usize_isaac64(b: &mut Bencher) { - let mut rng = Isaac64Rng::new().unwrap(); - b.iter(|| { - for _ in 0..RAND_BENCH_N { - black_box(usize::rand(&mut rng, Default)); - } - }); - b.bytes = size_of::() as u64 * RAND_BENCH_N; -} - -#[bench] -fn gen_usize_chacha(b: &mut Bencher) { - let mut rng = ChaChaRng::new().unwrap(); - b.iter(|| { - for _ in 0..RAND_BENCH_N { - black_box(usize::rand(&mut rng, Default)); - } - }); - b.bytes = size_of::() as u64 * RAND_BENCH_N; -} - -#[bench] -fn gen_usize_std(b: &mut Bencher) { - let mut rng = StdRng::new().unwrap(); - b.iter(|| { - for _ in 0..RAND_BENCH_N { - black_box(usize::rand(&mut rng, Default)); - } - }); - b.bytes = size_of::() as u64 * RAND_BENCH_N; -} - -#[bench] -fn gen_usize_os(b: &mut Bencher) { - let mut rng = OsRng::new().unwrap(); - b.iter(|| { - for _ in 0..RAND_BENCH_N { - black_box(usize::rand(&mut rng, Default)); - } - }); - b.bytes = size_of::() as u64 * RAND_BENCH_N; -} +gen_usize!(gen_usize_xorshift, XorShiftRng); +gen_usize!(gen_usize_isaac, IsaacRng); +gen_usize!(gen_usize_isaac64, Isaac64Rng); +gen_usize!(gen_usize_chacha, ChaChaRng); +gen_usize!(gen_usize_std, StdRng); +gen_usize!(gen_usize_os, OsRng); From 0cd55395e14df906f6a9faffe49f216e61a69ae1 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 4 Sep 2017 15:42:11 +0100 Subject: [PATCH 093/247] Replace fill_bytes code with @newpavlov's code gen_bytes_* benchmarks show significant improvement, 8 - 68% over previous alg (or 38% to 182% over algorithm before that) --- src/lib.rs | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 01f12ee7b2a..840aa6ee1e7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -362,21 +362,23 @@ pub trait Rng { /// println!("{:?}", &v[..]); /// ``` fn fill_bytes(&mut self, dest: &mut [u8]) { - use core::cmp::min; - use core::intrinsics::copy_nonoverlapping; - use core::mem::size_of; + use core::intrinsics::transmute; - let mut pos = 0; - let len = dest.len(); - while len > pos { - // Cast pointer, effectively to `&[u8; 8]`, and copy as many bytes - // as required. Byte-swap x on BE architectures. - let x = self.next_u64().to_le(); - let xp = &x as *const u64 as *const u8; - let p: *mut u8 = unsafe{ dest.as_mut_ptr().offset(pos as isize) }; - let n = min(len - pos, size_of::()); - unsafe{ copy_nonoverlapping(xp, p, n); } - pos += n; + let mut left = dest; + while left.len() >= 8 { + let (l, r) = {left}.split_at_mut(8); + left = r; + let chunk: [u8; 8] = unsafe { + transmute(self.next_u64().to_le()) + }; + l.copy_from_slice(&chunk); + } + let n = left.len(); + if n > 0 { + let chunk: [u8; 8] = unsafe { + transmute(self.next_u64().to_le()) + }; + left.copy_from_slice(&chunk[..n]); } } } From e177594af6f46b9d42c2683b76c6bfde77e94685 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 4 Sep 2017 15:52:52 +0100 Subject: [PATCH 094/247] Use macro in distributions bench to reduce source size --- benches/distributions.rs | 113 ++++++++------------------------------- 1 file changed, 21 insertions(+), 92 deletions(-) diff --git a/benches/distributions.rs b/benches/distributions.rs index 35b6aa270c5..1f5b9111463 100644 --- a/benches/distributions.rs +++ b/benches/distributions.rs @@ -29,7 +29,6 @@ fn distr_baseline(b: &mut Bencher) { b.bytes = size_of::() as u64 * ::RAND_BENCH_N; } - #[bench] fn distr_range_int(b: &mut Bencher) { let mut rng = XorShiftRng::new().unwrap(); @@ -43,20 +42,6 @@ fn distr_range_int(b: &mut Bencher) { b.bytes = size_of::() as u64 * ::RAND_BENCH_N; } -#[bench] -fn distr_range_float(b: &mut Bencher) { - let mut rng = XorShiftRng::new().unwrap(); - let distr = Range::new(2.26f64, 2.319f64); - - b.iter(|| { - for _ in 0..::RAND_BENCH_N { - distr.sample(&mut rng); - } - }); - b.bytes = size_of::() as u64 * ::RAND_BENCH_N; -} - - #[bench] fn distr_range2_int(b: &mut Bencher) { let mut rng = XorShiftRng::new().unwrap(); @@ -70,83 +55,27 @@ fn distr_range2_int(b: &mut Bencher) { b.bytes = size_of::() as u64 * ::RAND_BENCH_N; } -#[bench] -fn distr_range2_float(b: &mut Bencher) { - let mut rng = XorShiftRng::new().unwrap(); - let distr = range2::Range::new(2.26f64, 2.319f64); - - b.iter(|| { - for _ in 0..::RAND_BENCH_N { - distr.sample(&mut rng); - } - }); - b.bytes = size_of::() as u64 * ::RAND_BENCH_N; -} - - -#[bench] -fn distr_exp(b: &mut Bencher) { - let mut rng = XorShiftRng::new().unwrap(); - let distr = Exp::new(2.71828 * 3.14159); - - b.iter(|| { - for _ in 0..::RAND_BENCH_N { - distr.sample(&mut rng); +macro_rules! distr_float { + ($fnn:ident, $distr:expr) => { + #[bench] + fn $fnn(b: &mut Bencher) { + let mut rng = XorShiftRng::new().unwrap(); + let distr = $distr; + + b.iter(|| { + for _ in 0..::RAND_BENCH_N { + distr.sample(&mut rng); + } + }); + b.bytes = size_of::() as u64 * ::RAND_BENCH_N; } - }); - b.bytes = size_of::() as u64 * ::RAND_BENCH_N; + } } - -#[bench] -fn distr_normal(b: &mut Bencher) { - let mut rng = XorShiftRng::new().unwrap(); - let distr = Normal::new(-2.71828, 3.14159); - - b.iter(|| { - for _ in 0..::RAND_BENCH_N { - distr.sample(&mut rng); - } - }); - b.bytes = size_of::() as u64 * ::RAND_BENCH_N; -} - -#[bench] -fn distr_log_normal(b: &mut Bencher) { - let mut rng = XorShiftRng::new().unwrap(); - let distr = LogNormal::new(-2.71828, 3.14159); - - b.iter(|| { - for _ in 0..::RAND_BENCH_N { - distr.sample(&mut rng); - } - }); - b.bytes = size_of::() as u64 * ::RAND_BENCH_N; -} - - -#[bench] -fn distr_gamma_large_shape(b: &mut Bencher) { - let mut rng = XorShiftRng::new().unwrap(); - let distr = Gamma::new(10., 1.0); - - b.iter(|| { - for _ in 0..::RAND_BENCH_N { - distr.sample(&mut rng); - } - }); - b.bytes = size_of::() as u64 * ::RAND_BENCH_N; -} - -#[bench] -fn distr_gamma_small_shape(b: &mut Bencher) { - let mut rng = XorShiftRng::new().unwrap(); - let distr = Gamma::new(0.1, 1.0); - - b.iter(|| { - for _ in 0..::RAND_BENCH_N { - distr.sample(&mut rng); - } - }); - b.bytes = size_of::() as u64 * ::RAND_BENCH_N; -} +distr_float!(distr_range_float, Range::new(2.26f64, 2.319f64)); +distr_float!(distr_range2_float, range2::Range::new(2.26f64, 2.319f64)); +distr_float!(distr_exp, Exp::new(2.71828 * 3.14159)); +distr_float!(distr_normal, Normal::new(-2.71828, 3.14159)); +distr_float!(distr_log_normal, LogNormal::new(-2.71828, 3.14159)); +distr_float!(distr_gamma_large_shape, Gamma::new(10., 1.0)); +distr_float!(distr_gamma_small_shape, Gamma::new(0.1, 1.0)); From 81821a622d2dd8456002040a5ef4ebf7028a58e8 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 4 Sep 2017 16:10:46 +0100 Subject: [PATCH 095/247] Move open/closed/uniform 01 benchs from misc to distributions; use black_box on all benches Use of black_box has a huge effect (half performance in some cases, removes odd effects) --- benches/distributions.rs | 20 ++++----- benches/generators.rs | 1 + benches/misc.rs | 91 +--------------------------------------- 3 files changed, 13 insertions(+), 99 deletions(-) diff --git a/benches/distributions.rs b/benches/distributions.rs index 1f5b9111463..5c564ff25fb 100644 --- a/benches/distributions.rs +++ b/benches/distributions.rs @@ -6,15 +6,11 @@ extern crate rand; const RAND_BENCH_N: u64 = 1000; use std::mem::size_of; -use test::Bencher; +use test::{black_box, Bencher}; use rand::{Default, Rand}; use rand::prng::XorShiftRng; -use rand::distributions::Distribution; -use rand::distributions::{Range, range2}; -use rand::distributions::exponential::Exp; -use rand::distributions::normal::{Normal, LogNormal}; -use rand::distributions::gamma::Gamma; +use rand::distributions::*; #[bench] @@ -23,7 +19,7 @@ fn distr_baseline(b: &mut Bencher) { b.iter(|| { for _ in 0..::RAND_BENCH_N { - u64::rand(&mut rng, Default); + black_box(u64::rand(&mut rng, Default)); } }); b.bytes = size_of::() as u64 * ::RAND_BENCH_N; @@ -36,7 +32,7 @@ fn distr_range_int(b: &mut Bencher) { b.iter(|| { for _ in 0..::RAND_BENCH_N { - distr.sample(&mut rng); + black_box(distr.sample(&mut rng)); } }); b.bytes = size_of::() as u64 * ::RAND_BENCH_N; @@ -49,7 +45,7 @@ fn distr_range2_int(b: &mut Bencher) { b.iter(|| { for _ in 0..::RAND_BENCH_N { - distr.sample(&mut rng); + black_box(distr.sample(&mut rng)); } }); b.bytes = size_of::() as u64 * ::RAND_BENCH_N; @@ -64,7 +60,8 @@ macro_rules! distr_float { b.iter(|| { for _ in 0..::RAND_BENCH_N { - distr.sample(&mut rng); + let x: f64 = distr.sample(&mut rng); + black_box(x); } }); b.bytes = size_of::() as u64 * ::RAND_BENCH_N; @@ -72,6 +69,9 @@ macro_rules! distr_float { } } +distr_float!(distr_uniform01_float, Uniform01); +distr_float!(distr_closed01_float, Closed01); +distr_float!(distr_open01_float, Open01); distr_float!(distr_range_float, Range::new(2.26f64, 2.319f64)); distr_float!(distr_range2_float, range2::Range::new(2.26f64, 2.319f64)); distr_float!(distr_exp, Exp::new(2.71828 * 3.14159)); diff --git a/benches/generators.rs b/benches/generators.rs index 24128a4dbd6..6dbf3a9954d 100644 --- a/benches/generators.rs +++ b/benches/generators.rs @@ -21,6 +21,7 @@ macro_rules! gen_bytes { b.iter(|| { for _ in 0..RAND_BENCH_N { rng.fill_bytes(&mut buf); + black_box(buf); } }); b.bytes = BYTES_LEN as u64 * RAND_BENCH_N; diff --git a/benches/misc.rs b/benches/misc.rs index 0eb39b2d3e7..9d6273cc08b 100644 --- a/benches/misc.rs +++ b/benches/misc.rs @@ -12,100 +12,13 @@ use rand::prng::XorShiftRng; use rand::sequences::{sample, Shuffle}; use rand::distributions::{Rand, Uniform, Uniform01, Closed01, Open01}; -#[bench] -fn misc_baseline_32(b: &mut Bencher) { - let mut rng = StdRng::new().unwrap(); - b.iter(|| { - for _ in 0..RAND_BENCH_N { - black_box(u32::rand(&mut rng, Uniform)); - } - }); - b.bytes = size_of::() as u64 * RAND_BENCH_N; -} - -#[bench] -fn misc_baseline_64(b: &mut Bencher) { - let mut rng = StdRng::new().unwrap(); - b.iter(|| { - for _ in 0..RAND_BENCH_N { - black_box(u64::rand(&mut rng, Uniform)); - } - }); - b.bytes = size_of::() as u64 * RAND_BENCH_N; -} - -#[bench] -fn misc_uniform01_f32(b: &mut Bencher) { - let mut rng = StdRng::new().unwrap(); - b.iter(|| { - for _ in 0..RAND_BENCH_N { - black_box(f32::rand(&mut rng, Uniform01)); - } - }); - b.bytes = size_of::() as u64 * RAND_BENCH_N; -} - -#[bench] -fn misc_uniform01_f64(b: &mut Bencher) { - let mut rng = StdRng::new().unwrap(); - b.iter(|| { - for _ in 0..RAND_BENCH_N { - black_box(f64::rand(&mut rng, Uniform01)); - } - }); - b.bytes = size_of::() as u64 * RAND_BENCH_N; -} - -#[bench] -fn misc_closed01_f32(b: &mut Bencher) { - let mut rng = StdRng::new().unwrap(); - b.iter(|| { - for _ in 0..RAND_BENCH_N { - black_box(f32::rand(&mut rng, Closed01)); - } - }); - b.bytes = size_of::() as u64 * RAND_BENCH_N; -} - -#[bench] -fn misc_closed01_f64(b: &mut Bencher) { - let mut rng = StdRng::new().unwrap(); - b.iter(|| { - for _ in 0..RAND_BENCH_N { - black_box(f64::rand(&mut rng, Closed01)); - } - }); - b.bytes = size_of::() as u64 * RAND_BENCH_N; -} - -#[bench] -fn misc_open01_f32(b: &mut Bencher) { - let mut rng = StdRng::new().unwrap(); - b.iter(|| { - for _ in 0..RAND_BENCH_N { - black_box(f32::rand(&mut rng, Open01)); - } - }); - b.bytes = size_of::() as u64 * RAND_BENCH_N; -} - -#[bench] -fn misc_open01_f64(b: &mut Bencher) { - let mut rng = StdRng::new().unwrap(); - b.iter(|| { - for _ in 0..RAND_BENCH_N { - black_box(f64::rand(&mut rng, Open01)); - } - }); - b.bytes = size_of::() as u64 * RAND_BENCH_N; -} - #[bench] fn misc_shuffle_100(b: &mut Bencher) { let mut rng = XorShiftRng::new().unwrap(); let x : &mut [usize] = &mut [1; 100]; b.iter(|| { x.shuffle(&mut rng); + black_box(&x); }) } @@ -114,6 +27,6 @@ fn misc_sample_10_of_100(b: &mut Bencher) { let mut rng = XorShiftRng::new().unwrap(); let x : &[usize] = &[1; 100]; b.iter(|| { - sample(&mut rng, x, 10); + black_box(sample(&mut rng, x, 10)); }) } From 2e26002de4a65477feb6dfe1fd52ba08a51ea9a1 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 4 Sep 2017 16:23:13 +0100 Subject: [PATCH 096/247] Simplify OsRng impls: only impl next_* once & use minimal ReadRng impl Benchmarks of OsRng show no significant change --- src/os.rs | 163 ++++++++++++++---------------------------------------- 1 file changed, 40 insertions(+), 123 deletions(-) diff --git a/src/os.rs b/src/os.rs index f00672f6592..de3b4a04e05 100644 --- a/src/os.rs +++ b/src/os.rs @@ -12,6 +12,7 @@ //! generators. use std::{mem, fmt}; +use std::io::Read; use {Rng, CryptoError}; @@ -43,9 +44,19 @@ impl OsRng { } impl Rng for OsRng { - fn next_u32(&mut self) -> u32 { self.0.next_u32() } - fn next_u64(&mut self) -> u64 { self.0.next_u64() } - fn fill_bytes(&mut self, v: &mut [u8]) { self.0.fill_bytes(v) } + fn next_u32(&mut self) -> u32 { + let mut buf: [u8; 4] = [0; 4]; + self.fill_bytes(&mut buf); + unsafe{ *(buf.as_ptr() as *const u32) } + } + fn next_u64(&mut self) -> u64 { + let mut buf: [u8; 8] = [0; 8]; + self.fill_bytes(&mut buf); + unsafe{ *(buf.as_ptr() as *const u64) } + } + fn fill_bytes(&mut self, v: &mut [u8]) { + self.0.fill_bytes(v) + } } impl fmt::Debug for OsRng { @@ -54,16 +65,19 @@ impl fmt::Debug for OsRng { } } -fn next_u32(fill_buf: &mut FnMut(&mut [u8])) -> u32 { - let mut buf: [u8; 4] = [0; 4]; - fill_buf(&mut buf); - unsafe { mem::transmute::<[u8; 4], u32>(buf) } -} +// Specialisation of `ReadRng` for our purposes +#[derive(Debug)] +struct ReadRng (R); -fn next_u64(fill_buf: &mut FnMut(&mut [u8])) -> u64 { - let mut buf: [u8; 8] = [0; 8]; - fill_buf(&mut buf); - unsafe { mem::transmute::<[u8; 8], u64>(buf) } +impl ReadRng { + fn fill_bytes(&mut self, mut buf: &mut [u8]) { + while buf.len() > 0 { + match self.0.read(buf).unwrap_or_else(|e| panic!("Read error: {}", e)) { + 0 => panic!("OsRng: no bytes available"), + n => buf = &mut mem::replace(&mut buf, &mut [])[n..], + } + } + } } #[cfg(all(unix, not(target_os = "ios"), @@ -75,13 +89,12 @@ fn next_u64(fill_buf: &mut FnMut(&mut [u8])) -> u64 { mod imp { extern crate libc; - use super::{next_u32, next_u64}; use self::OsRngInner::*; + use super::ReadRng; use std::io; use std::fs::File; - use {Rng, CryptoError}; - use read::ReadRng; + use CryptoError; #[cfg(all(target_os = "linux", any(target_arch = "x86_64", @@ -190,26 +203,12 @@ mod imp { } let reader = File::open("/dev/urandom")?; - let reader_rng = ReadRng::new(reader); + let reader_rng = ReadRng(reader); Ok(OsRng { inner: OsReadRng(reader_rng) }) } - } - - impl Rng for OsRng { - fn next_u32(&mut self) -> u32 { - match self.inner { - OsGetrandomRng => next_u32(&mut getrandom_fill_bytes), - OsReadRng(ref mut rng) => rng.next_u32(), - } - } - fn next_u64(&mut self) -> u64 { - match self.inner { - OsGetrandomRng => next_u64(&mut getrandom_fill_bytes), - OsReadRng(ref mut rng) => rng.next_u64(), - } - } - fn fill_bytes(&mut self, v: &mut [u8]) { + + pub fn fill_bytes(&mut self, v: &mut [u8]) { match self.inner { OsGetrandomRng => getrandom_fill_bytes(v), OsReadRng(ref mut rng) => rng.fill_bytes(v) @@ -222,10 +221,7 @@ mod imp { mod imp { extern crate libc; - use super::{next_u32, next_u64}; - use std::io; - use Rng; use self::libc::{c_int, size_t}; #[derive(Debug)] @@ -246,16 +242,7 @@ mod imp { pub fn new() -> Result { Ok(OsRng) } - } - - impl Rng for OsRng { - fn next_u32(&mut self) -> u32 { - next_u32(&mut |v| self.fill_bytes(v)) - } - fn next_u64(&mut self) -> u64 { - next_u64(&mut |v| self.fill_bytes(v)) - } - fn fill_bytes(&mut self, v: &mut [u8]) { + pub fn fill_bytes(&mut self, v: &mut [u8]) { let ret = unsafe { SecRandomCopyBytes(kSecRandomDefault, v.len() as size_t, v.as_mut_ptr()) }; @@ -271,9 +258,6 @@ mod imp { extern crate libc; use std::{io, ptr}; - use Rng; - - use super::{next_u32, next_u64}; #[derive(Debug)] pub struct OsRng; @@ -282,16 +266,7 @@ mod imp { pub fn new() -> Result { Ok(OsRng) } - } - - impl Rng for OsRng { - fn next_u32(&mut self) -> u32 { - next_u32(&mut |v| self.fill_bytes(v)) - } - fn next_u64(&mut self) -> u64 { - next_u64(&mut |v| self.fill_bytes(v)) - } - fn fill_bytes(&mut self, v: &mut [u8]) { + pub fn fill_bytes(&mut self, v: &mut [u8]) { let mib = [libc::CTL_KERN, libc::KERN_ARND]; // kern.arandom permits a maximum buffer size of 256 bytes for s in v.chunks_mut(256) { @@ -315,9 +290,6 @@ mod imp { extern crate libc; use std::io; - use Rng; - - use super::{next_u32, next_u64}; #[derive(Debug)] pub struct OsRng; @@ -326,16 +298,7 @@ mod imp { pub fn new() -> Result { Ok(OsRng) } - } - - impl Rng for OsRng { - fn next_u32(&mut self) -> u32 { - next_u32(&mut |v| self.fill_bytes(v)) - } - fn next_u64(&mut self) -> u64 { - next_u64(&mut |v| self.fill_bytes(v)) - } - fn fill_bytes(&mut self, v: &mut [u8]) { + pub fn fill_bytes(&mut self, v: &mut [u8]) { // getentropy(2) permits a maximum buffer size of 256 bytes for s in v.chunks_mut(256) { let ret = unsafe { @@ -354,8 +317,7 @@ mod imp { mod imp { use std::io; use std::fs::File; - use Rng; - use read::ReadRng; + use super::ReadRng; #[derive(Debug)] pub struct OsRng { @@ -365,20 +327,11 @@ mod imp { impl OsRng { pub fn new() -> Result { let reader = File::open("rand:")?; - let reader_rng = ReadRng::new(reader); + let reader_rng = ReadRng(reader); Ok(OsRng { inner: reader_rng }) } - } - - impl Rng for OsRng { - fn next_u32(&mut self) -> u32 { - self.inner.next_u32() - } - fn next_u64(&mut self) -> u64 { - self.inner.next_u64() - } - fn fill_bytes(&mut self, v: &mut [u8]) { + pub fn fill_bytes(&mut self, v: &mut [u8]) { self.inner.fill_bytes(v) } } @@ -389,9 +342,6 @@ mod imp { extern crate magenta; use std::io; - use Rng; - - use super::{next_u32, next_u64}; #[derive(Debug)] pub struct OsRng; @@ -400,16 +350,7 @@ mod imp { pub fn new() -> Result { Ok(OsRng) } - } - - impl Rng for OsRng { - fn next_u32(&mut self) -> u32 { - next_u32(&mut |v| self.fill_bytes(v)) - } - fn next_u64(&mut self) -> u64 { - next_u64(&mut |v| self.fill_bytes(v)) - } - fn fill_bytes(&mut self, v: &mut [u8]) { + pub fn fill_bytes(&mut self, v: &mut [u8]) { for s in v.chunks_mut(magenta::MX_CPRNG_DRAW_MAX_LEN) { let mut filled = 0; while filled < s.len() { @@ -426,9 +367,6 @@ mod imp { #[cfg(windows)] mod imp { use std::io; - use Rng; - - use super::{next_u32, next_u64}; type BOOLEAN = u8; type ULONG = u32; @@ -446,16 +384,7 @@ mod imp { pub fn new() -> Result { Ok(OsRng) } - } - - impl Rng for OsRng { - fn next_u32(&mut self) -> u32 { - next_u32(&mut |v| self.fill_bytes(v)) - } - fn next_u64(&mut self) -> u64 { - next_u64(&mut |v| self.fill_bytes(v)) - } - fn fill_bytes(&mut self, v: &mut [u8]) { + pub fn fill_bytes(&mut self, v: &mut [u8]) { // RtlGenRandom takes an ULONG (u32) for the length so we need to // split up the buffer. for slice in v.chunks_mut(::max_value() as usize) { @@ -477,9 +406,6 @@ mod imp { use std::io; use std::mem; - use Rng; - - use super::{next_u32, next_u64}; #[derive(Debug)] pub struct OsRng(extern fn(dest: *mut libc::c_void, @@ -521,16 +447,7 @@ mod imp { Err(CryptoError) } } - } - - impl Rng for OsRng { - fn next_u32(&mut self) -> u32 { - next_u32(&mut |v| self.fill_bytes(v)) - } - fn next_u64(&mut self) -> u64 { - next_u64(&mut |v| self.fill_bytes(v)) - } - fn fill_bytes(&mut self, v: &mut [u8]) { + pub fn fill_bytes(&mut self, v: &mut [u8]) { let mut read = 0; loop { let mut r: libc::size_t = 0; From 0fc565e4d045d4ccda7b7cc9da4ade8bdfe8923b Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 4 Sep 2017 17:33:58 +0100 Subject: [PATCH 097/247] misc benchmark: clean up unneeded imports --- benches/misc.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/benches/misc.rs b/benches/misc.rs index 9d6273cc08b..5c35c847d85 100644 --- a/benches/misc.rs +++ b/benches/misc.rs @@ -3,14 +3,9 @@ extern crate test; extern crate rand; -const RAND_BENCH_N: u64 = 1000; - -use std::mem::size_of; use test::{black_box, Bencher}; -use rand::StdRng; use rand::prng::XorShiftRng; use rand::sequences::{sample, Shuffle}; -use rand::distributions::{Rand, Uniform, Uniform01, Closed01, Open01}; #[bench] fn misc_shuffle_100(b: &mut Bencher) { From fa4eb5b88564f96239dc0f2ba8b2c150ccb443d2 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 4 Sep 2017 17:44:04 +0100 Subject: [PATCH 098/247] Add FromRng and NewRng This supports MyRng::new() using OsRng without requiring MyRng code to use OsRng. --- benches/distributions.rs | 2 +- benches/generators.rs | 2 +- benches/misc.rs | 1 + src/lib.rs | 30 ++++++++++++ src/prng/chacha.rs | 35 +++++--------- src/prng/isaac.rs | 102 +++++++++++++++------------------------ src/prng/xorshift.rs | 23 ++------- 7 files changed, 88 insertions(+), 107 deletions(-) diff --git a/benches/distributions.rs b/benches/distributions.rs index 5c564ff25fb..f2d4f8ac61b 100644 --- a/benches/distributions.rs +++ b/benches/distributions.rs @@ -8,7 +8,7 @@ const RAND_BENCH_N: u64 = 1000; use std::mem::size_of; use test::{black_box, Bencher}; -use rand::{Default, Rand}; +use rand::{Default, Rand, NewRng}; use rand::prng::XorShiftRng; use rand::distributions::*; diff --git a/benches/generators.rs b/benches/generators.rs index 6dbf3a9954d..a71fa88b56b 100644 --- a/benches/generators.rs +++ b/benches/generators.rs @@ -9,7 +9,7 @@ const BYTES_LEN: usize = 1024; use std::mem::size_of; use test::{black_box, Bencher}; -use rand::{Rng, StdRng, OsRng, Rand, Default}; +use rand::{Rng, NewRng, StdRng, OsRng, Rand, Default}; use rand::prng::{XorShiftRng, IsaacRng, Isaac64Rng, ChaChaRng}; macro_rules! gen_bytes { diff --git a/benches/misc.rs b/benches/misc.rs index 5c35c847d85..ce4e999d581 100644 --- a/benches/misc.rs +++ b/benches/misc.rs @@ -4,6 +4,7 @@ extern crate test; extern crate rand; use test::{black_box, Bencher}; +use rand::NewRng; use rand::prng::XorShiftRng; use rand::sequences::{sample, Shuffle}; diff --git a/src/lib.rs b/src/lib.rs index 840aa6ee1e7..1becb5d090d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -403,6 +403,36 @@ impl Rng for Box where R: Rng+?Sized { } } +/// Support mechanism for creating random number generators seeded by other +/// generators. All PRNGs should support this to enable `NewRng` support. +pub trait FromRng { + /// Creates a new instance, seeded from another `Rng`. + /// + /// Seeding from a cryptographic generator should be fine. On the other + /// hand, seeding a simple numerical generator from another of the same + /// type sometimes has serious side effects such as effectively cloning the + /// generator. + fn from_rng(rng: &mut R) -> Self; +} + +/// Support mechanism for creating random number generators securely seeded +/// using the OS generator. +/// +/// This is implemented automatically for any PRNG implementing `FromRng`. +#[cfg(feature="std")] +pub trait NewRng: Sized { + /// Creates a new instance, automatically seeded via `OsRng`. + fn new() -> Result; +} + +#[cfg(feature="std")] +impl NewRng for R { + fn new() -> Result { + let mut r = OsRng::new()?; + Ok(Self::from_rng(&mut r)) + } +} + /// A random number generator that can be explicitly seeded to produce /// the same stream of randomness multiple times. pub trait SeedableRng: Rng { diff --git a/src/prng/chacha.rs b/src/prng/chacha.rs index ce9ba2e97a6..df0bc8bcdcd 100644 --- a/src/prng/chacha.rs +++ b/src/prng/chacha.rs @@ -11,9 +11,7 @@ //! The ChaCha random number generator. use core::num::Wrapping as w; -use {Rng, SeedableRng}; -#[cfg(feature="std")] -use {OsRng, CryptoError}; +use {Rng, FromRng, SeedableRng}; #[allow(bad_style)] type w32 = w; @@ -83,13 +81,6 @@ fn core(output: &mut [w32; STATE_WORDS], input: &[w32; STATE_WORDS]) { } impl ChaChaRng { - /// Creates a new `ChaChaRng`, automatically seeded via `OsRng`. - #[cfg(feature="std")] - pub fn new() -> Result { - let mut r = OsRng::new()?; - Ok(ChaChaRng::from_rng(&mut r)) - } - /// Create an ChaCha random number generator using the default /// fixed key of 8 zero words. /// @@ -116,20 +107,6 @@ impl ChaChaRng { rng } - /// Create an ChaCha random number generator, seeding from another generator. - /// - /// Care should be taken when seeding one RNG from another. There is no - /// free entropy gained. In some cases where the parent and child RNGs use - /// the same algorithm, both generate the same output sequences (possibly - /// with a small lag). - pub fn from_rng(other: &mut R) -> ChaChaRng { - let mut key : [u32; KEY_WORDS] = [0; KEY_WORDS]; - for word in key.iter_mut() { - *word = other.next_u32(); - } - SeedableRng::from_seed(&key[..]) - } - /// Sets the internal 128-bit ChaCha counter to /// a user-provided value. This permits jumping /// arbitrarily ahead (or backwards) in the pseudorandom stream. @@ -222,6 +199,16 @@ impl Rng for ChaChaRng { } } +impl FromRng for ChaChaRng { + fn from_rng(other: &mut R) -> ChaChaRng { + let mut key : [u32; KEY_WORDS] = [0; KEY_WORDS]; + for word in key.iter_mut() { + *word = other.next_u32(); + } + SeedableRng::from_seed(&key[..]) + } +} + impl<'a> SeedableRng<&'a [u32]> for ChaChaRng { fn reseed(&mut self, seed: &'a [u32]) { // reset state diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index 35e79088064..5dfd8884115 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -17,9 +17,7 @@ use core::iter::repeat; use core::num::Wrapping as w; use core::fmt; -use {Rng, SeedableRng}; -#[cfg(feature="std")] -use {OsRng, CryptoError}; +use {Rng, FromRng, SeedableRng}; /// Select 32- or 64-bit variant dependent on pointer size. #[cfg(target_pointer_width = "32")] @@ -63,13 +61,6 @@ static EMPTY: IsaacRng = IsaacRng { }; impl IsaacRng { - /// Creates a new `IsaacRng`, automatically seeded via `OsRng`. - #[cfg(feature="std")] - pub fn new() -> Result { - let mut r = OsRng::new()?; - Ok(IsaacRng::from_rng(&mut r)) - } - /// Create an ISAAC random number generator using the default /// fixed seed. pub fn new_unseeded() -> IsaacRng { @@ -78,29 +69,6 @@ impl IsaacRng { rng } - /// Create an ISAAC random number generator, seeding from another generator. - /// - /// Care should be taken when seeding one RNG from another. There is no - /// free entropy gained. In some cases where the parent and child RNGs use - /// the same algorithm, both generate the same output sequences (possibly - /// with a small lag). - pub fn from_rng(other: &mut R) -> IsaacRng { - let mut ret = EMPTY; - unsafe { - let ptr = ret.rsl.as_mut_ptr() as *mut u8; - - let slice = slice::from_raw_parts_mut(ptr, RAND_SIZE_USIZE * 4); - other.fill_bytes(slice); - } - ret.cnt = 0; - ret.a = w(0); - ret.b = w(0); - ret.c = w(0); - - ret.init(true); - return ret; - } - /// Initialises `self`. If `use_rsl` is true, then use the current value /// of `rsl` as a seed, otherwise construct one algorithmically (not /// randomly). @@ -255,6 +223,25 @@ impl Rng for IsaacRng { } } +impl FromRng for IsaacRng { + fn from_rng(other: &mut R) -> IsaacRng { + let mut ret = EMPTY; + unsafe { + let ptr = ret.rsl.as_mut_ptr() as *mut u8; + + let slice = slice::from_raw_parts_mut(ptr, RAND_SIZE_USIZE * 4); + other.fill_bytes(slice); + } + ret.cnt = 0; + ret.a = w(0); + ret.b = w(0); + ret.c = w(0); + + ret.init(true); + return ret; + } +} + impl<'a> SeedableRng<&'a [u32]> for IsaacRng { fn reseed(&mut self, seed: &'a [u32]) { // make the seed into [seed[0], seed[1], ..., seed[seed.len() @@ -321,13 +308,6 @@ static EMPTY_64: Isaac64Rng = Isaac64Rng { }; impl Isaac64Rng { - /// Creates a new `Isaac64Rng`, automatically seeded via `OsRng`. - #[cfg(feature="std")] - pub fn new() -> Result { - let mut r = OsRng::new()?; - Ok(Isaac64Rng::from_rng(&mut r)) - } - /// Create a 64-bit ISAAC random number generator using the /// default fixed seed. pub fn new_unseeded() -> Isaac64Rng { @@ -336,29 +316,6 @@ impl Isaac64Rng { rng } - /// Create an ISAAC random number generator, seeding from another generator. - /// - /// Care should be taken when seeding one RNG from another. There is no - /// free entropy gained. In some cases where the parent and child RNGs use - /// the same algorithm, both generate the same output sequences (possibly - /// with a small lag). - pub fn from_rng(other: &mut R) -> Isaac64Rng { - let mut ret = EMPTY_64; - unsafe { - let ptr = ret.rsl.as_mut_ptr() as *mut u8; - - let slice = slice::from_raw_parts_mut(ptr, RAND_SIZE_64 * 8); - other.fill_bytes(slice); - } - ret.cnt = 0; - ret.a = w(0); - ret.b = w(0); - ret.c = w(0); - - ret.init(true); - return ret; - } - /// Initialises `self`. If `use_rsl` is true, then use the current value /// of `rsl` as a seed, otherwise construct one algorithmically (not /// randomly). @@ -514,6 +471,25 @@ impl Rng for Isaac64Rng { } } +impl FromRng for Isaac64Rng { + fn from_rng(other: &mut R) -> Isaac64Rng { + let mut ret = EMPTY_64; + unsafe { + let ptr = ret.rsl.as_mut_ptr() as *mut u8; + + let slice = slice::from_raw_parts_mut(ptr, RAND_SIZE_64 * 8); + other.fill_bytes(slice); + } + ret.cnt = 0; + ret.a = w(0); + ret.b = w(0); + ret.c = w(0); + + ret.init(true); + return ret; + } +} + impl<'a> SeedableRng<&'a [u64]> for Isaac64Rng { fn reseed(&mut self, seed: &'a [u64]) { // make the seed into [seed[0], seed[1], ..., seed[seed.len() diff --git a/src/prng/xorshift.rs b/src/prng/xorshift.rs index b8e42ebe059..6e41f54fd1e 100644 --- a/src/prng/xorshift.rs +++ b/src/prng/xorshift.rs @@ -11,9 +11,7 @@ //! Xorshift generators use core::num::Wrapping as w; -use {Rng, SeedableRng}; -#[cfg(feature="std")] -use {OsRng, CryptoError}; +use {Rng, FromRng, SeedableRng}; /// An Xorshift[1] random number /// generator. @@ -35,13 +33,6 @@ pub struct XorShiftRng { } impl XorShiftRng { - /// Creates a new `XorShiftRng`, automatically seeded via `OsRng`. - #[cfg(feature="std")] - pub fn new() -> Result { - let mut r = OsRng::new()?; - Ok(XorShiftRng::from_rng(&mut r)) - } - /// Creates a new XorShiftRng instance which is not seeded. /// /// The initial values of this RNG are constants, so all generators created @@ -56,14 +47,10 @@ impl XorShiftRng { w: w(0x113ba7bb), } } - - /// Create an ChaCha random number generator, seeding from another generator. - /// - /// Care should be taken when seeding one RNG from another. There is no - /// free entropy gained. In some cases where the parent and child RNGs use - /// the same algorithm, both generate the same output sequences (possibly - /// with a small lag). - pub fn from_rng(rng: &mut R) -> XorShiftRng { +} + +impl FromRng for XorShiftRng { + fn from_rng(rng: &mut R) -> XorShiftRng { let mut tuple: (u32, u32, u32, u32); loop { tuple = (rng.next_u32(), rng.next_u32(), rng.next_u32(), rng.next_u32()); From 31f964ea88482d40aaf6023b255357bac3631ce4 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Thu, 7 Sep 2017 10:58:05 +0200 Subject: [PATCH 099/247] Add benchmarks for ranges of i8, i16 and i32 --- benches/distributions.rs | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/benches/distributions.rs b/benches/distributions.rs index f2d4f8ac61b..3cfad2be471 100644 --- a/benches/distributions.rs +++ b/benches/distributions.rs @@ -38,19 +38,29 @@ fn distr_range_int(b: &mut Bencher) { b.bytes = size_of::() as u64 * ::RAND_BENCH_N; } -#[bench] -fn distr_range2_int(b: &mut Bencher) { - let mut rng = XorShiftRng::new().unwrap(); - let distr = range2::Range::new(3i64, 134217671i64); +macro_rules! distr_range2_int { + ($fnn:ident, $ty:ty, $low:expr, $high:expr) => { + #[bench] + fn $fnn(b: &mut Bencher) { + let mut rng = XorShiftRng::new().unwrap(); + let distr = range2::Range::new($low, $high); - b.iter(|| { - for _ in 0..::RAND_BENCH_N { - black_box(distr.sample(&mut rng)); + b.iter(|| { + for _ in 0..::RAND_BENCH_N { + let x: $ty = distr.sample(&mut rng); + black_box(x); + } + }); + b.bytes = size_of::<$ty>() as u64 * ::RAND_BENCH_N; } - }); - b.bytes = size_of::() as u64 * ::RAND_BENCH_N; + } } +distr_range2_int!(distr_range2_i8, i8, 20i8, 100); +distr_range2_int!(distr_range2_i16, i16, -500i16, 2000); +distr_range2_int!(distr_range2_i32, i32, -200_000_000i32, 800_000_000); +distr_range2_int!(distr_range2_i64, i64, 3i64, 134217671); + macro_rules! distr_float { ($fnn:ident, $distr:expr) => { #[bench] From 8906c292355ed845ce2f08b6657d7348b77aac83 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Fri, 8 Sep 2017 17:29:38 +0100 Subject: [PATCH 100/247] Optimise fill_bytes impls for ChaCha and Isaac RNGs --- src/prng/chacha.rs | 36 ++++++++++++++++++++++++++++++++++++ src/prng/isaac.rs | 22 ++++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/src/prng/chacha.rs b/src/prng/chacha.rs index df0bc8bcdcd..f40c4bf89a2 100644 --- a/src/prng/chacha.rs +++ b/src/prng/chacha.rs @@ -197,6 +197,42 @@ impl Rng for ChaChaRng { self.index += 1; value.0 } + + // Custom implementation allowing larger reads from buffer is about 8% + // faster than default implementation in my tests + fn fill_bytes(&mut self, dest: &mut [u8]) { + use core::cmp::min; + use core::intrinsics::{transmute, copy_nonoverlapping}; + + let mut left = dest; + while left.len() >= 4 { + if self.index == STATE_WORDS { + self.update(); + } + + let words = min(left.len() / 4, STATE_WORDS - self.index); + let (l, r) = {left}.split_at_mut(4 * words); + left = r; + + // convert to LE: + for ref mut x in self.buffer[self.index..self.index+words].iter_mut() { + **x = w((*x).0.to_le()); + } + + unsafe{ copy_nonoverlapping( + &self.buffer[self.index].0 as *const u32 as *const u8, + l.as_mut_ptr(), + words) }; + self.index += words; + } + let n = left.len(); + if n > 0 { + let chunk: [u8; 4] = unsafe { + transmute(self.next_u32().to_le()) + }; + left.copy_from_slice(&chunk[..n]); + } + } } impl FromRng for ChaChaRng { diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index 5dfd8884115..b001fc9163c 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -221,6 +221,28 @@ impl Rng for IsaacRng { // it optimises to a bitwise mask). self.rsl[(self.cnt % RAND_SIZE) as usize].0 } + + // Default impl adjusted for native byte size; approx 18% faster in tests + fn fill_bytes(&mut self, dest: &mut [u8]) { + use core::intrinsics::transmute; + + let mut left = dest; + while left.len() >= 4 { + let (l, r) = {left}.split_at_mut(4); + left = r; + let chunk: [u8; 4] = unsafe { + transmute(self.next_u32().to_le()) + }; + l.copy_from_slice(&chunk); + } + let n = left.len(); + if n > 0 { + let chunk: [u8; 4] = unsafe { + transmute(self.next_u32().to_le()) + }; + left.copy_from_slice(&chunk[..n]); + } + } } impl FromRng for IsaacRng { From 0a97209436092c30f55166fe047dd237ce5073d6 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sun, 10 Sep 2017 16:05:44 +0100 Subject: [PATCH 101/247] =?UTF-8?q?Rename=20CryptoError=20=E2=86=92=20Erro?= =?UTF-8?q?r;=20add=20Result=20using=20the=20prior=20error=20type?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib.rs | 16 +++++++++------- src/os.rs | 31 +++++++++++++++++++------------ src/read.rs | 6 +++--- 3 files changed, 31 insertions(+), 22 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 1becb5d090d..d85b9ceb15d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -422,12 +422,12 @@ pub trait FromRng { #[cfg(feature="std")] pub trait NewRng: Sized { /// Creates a new instance, automatically seeded via `OsRng`. - fn new() -> Result; + fn new() -> Result; } #[cfg(feature="std")] impl NewRng for R { - fn new() -> Result { + fn new() -> Result { let mut r = OsRng::new()?; Ok(Self::from_rng(&mut r)) } @@ -612,7 +612,7 @@ impl StdRng { /// /// Reading the randomness from the OS may fail, and any error is /// propagated via the `io::Result` return value. - pub fn new() -> Result { + pub fn new() -> Result { IsaacWordRng::new().map(|rng| StdRng { rng }) } } @@ -633,15 +633,17 @@ impl Rng for StdRng { /// generators should be able to fail. In such cases there is little that can /// be done besides try again later. #[derive(Debug)] -pub struct CryptoError; +pub struct Error; #[cfg(feature="std")] -impl From<::std::io::Error> for CryptoError { - fn from(_: ::std::io::Error) -> CryptoError { - CryptoError +impl From<::std::io::Error> for Error { + fn from(_: ::std::io::Error) -> Error { + Error } } +pub type Result = ::std::result::Result; + #[cfg(test)] mod test { diff --git a/src/os.rs b/src/os.rs index de3b4a04e05..71c294a55fc 100644 --- a/src/os.rs +++ b/src/os.rs @@ -14,7 +14,7 @@ use std::{mem, fmt}; use std::io::Read; -use {Rng, CryptoError}; +use {Rng, Result}; /// A random number generator that retrieves randomness straight from /// the operating system. Platform sources: @@ -38,7 +38,7 @@ pub struct OsRng(imp::OsRng); impl OsRng { /// Create a new `OsRng`. - pub fn new() -> Result { + pub fn new() -> Result { imp::OsRng::new().map(OsRng) } } @@ -94,7 +94,7 @@ mod imp { use std::io; use std::fs::File; - use CryptoError; + use Result; #[cfg(all(target_os = "linux", any(target_arch = "x86_64", @@ -197,7 +197,7 @@ mod imp { } impl OsRng { - pub fn new() -> Result { + pub fn new() -> Result { if is_getrandom_available() { return Ok(OsRng { inner: OsGetrandomRng }); } @@ -223,6 +223,7 @@ mod imp { use std::io; use self::libc::{c_int, size_t}; + use Result; #[derive(Debug)] pub struct OsRng; @@ -239,7 +240,7 @@ mod imp { } impl OsRng { - pub fn new() -> Result { + pub fn new() -> Result { Ok(OsRng) } pub fn fill_bytes(&mut self, v: &mut [u8]) { @@ -258,12 +259,13 @@ mod imp { extern crate libc; use std::{io, ptr}; + use Result; #[derive(Debug)] pub struct OsRng; impl OsRng { - pub fn new() -> Result { + pub fn new() -> Result { Ok(OsRng) } pub fn fill_bytes(&mut self, v: &mut [u8]) { @@ -290,12 +292,13 @@ mod imp { extern crate libc; use std::io; + use Result; #[derive(Debug)] pub struct OsRng; impl OsRng { - pub fn new() -> Result { + pub fn new() -> Result { Ok(OsRng) } pub fn fill_bytes(&mut self, v: &mut [u8]) { @@ -318,6 +321,7 @@ mod imp { use std::io; use std::fs::File; use super::ReadRng; + use Result; #[derive(Debug)] pub struct OsRng { @@ -325,7 +329,7 @@ mod imp { } impl OsRng { - pub fn new() -> Result { + pub fn new() -> Result { let reader = File::open("rand:")?; let reader_rng = ReadRng(reader); @@ -342,12 +346,13 @@ mod imp { extern crate magenta; use std::io; + use Result; #[derive(Debug)] pub struct OsRng; impl OsRng { - pub fn new() -> Result { + pub fn new() -> Result { Ok(OsRng) } pub fn fill_bytes(&mut self, v: &mut [u8]) { @@ -367,6 +372,7 @@ mod imp { #[cfg(windows)] mod imp { use std::io; + use Result; type BOOLEAN = u8; type ULONG = u32; @@ -381,7 +387,7 @@ mod imp { pub struct OsRng; impl OsRng { - pub fn new() -> Result { + pub fn new() -> Result { Ok(OsRng) } pub fn fill_bytes(&mut self, v: &mut [u8]) { @@ -406,6 +412,7 @@ mod imp { use std::io; use std::mem; + use Result; #[derive(Debug)] pub struct OsRng(extern fn(dest: *mut libc::c_void, @@ -428,7 +435,7 @@ mod imp { } impl OsRng { - pub fn new() -> Result { + pub fn new() -> Result { let mut iface = NaClIRTRandom { get_random_bytes: None, }; @@ -444,7 +451,7 @@ mod imp { } else { // let error = io::ErrorKind::NotFound; // let error = io::Error::new(error, "IRT random interface missing"); - Err(CryptoError) + Err(Result) } } pub fn fill_bytes(&mut self, v: &mut [u8]) { diff --git a/src/read.rs b/src/read.rs index 42c6c0451fb..53ab46ff2a8 100644 --- a/src/read.rs +++ b/src/read.rs @@ -14,7 +14,7 @@ use std::fmt::Debug; use std::io::Read; use std::mem; -use {Rng, CryptoError}; +use {Rng, Error, Result}; /// An RNG that reads random bytes straight from a `Read`. This will /// work best with an infinite reader, but this is not required. @@ -74,10 +74,10 @@ impl Rng for ReadRng { } } -fn fill(r: &mut Read, mut buf: &mut [u8]) -> Result<(), CryptoError> { +fn fill(r: &mut Read, mut buf: &mut [u8]) -> Result<()> { while buf.len() > 0 { match r.read(buf)? { - 0 => return Err(CryptoError), + 0 => return Err(Error), n => buf = &mut mem::replace(&mut buf, &mut [])[n..], } } From a07bcdfed0d706c0bc94e78b7cd101940a115273 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sun, 10 Sep 2017 16:06:43 +0100 Subject: [PATCH 102/247] Replace fill_bytes with try_fill returning Result<()> Does not appear to impact performance --- benches/generators.rs | 2 +- src/lib.rs | 17 +++++++++-------- src/os.rs | 41 ++++++++++++++++++++++------------------- src/prng/chacha.rs | 9 +++++---- src/prng/isaac.rs | 17 +++++++++-------- src/prng/xorshift.rs | 6 +++--- src/read.rs | 13 ++++++------- src/reseeding.rs | 14 +++++++------- src/thread_local.rs | 6 +++--- 9 files changed, 65 insertions(+), 60 deletions(-) diff --git a/benches/generators.rs b/benches/generators.rs index a71fa88b56b..46d200b31aa 100644 --- a/benches/generators.rs +++ b/benches/generators.rs @@ -20,7 +20,7 @@ macro_rules! gen_bytes { let mut buf = [0u8; BYTES_LEN]; b.iter(|| { for _ in 0..RAND_BENCH_N { - rng.fill_bytes(&mut buf); + rng.try_fill(&mut buf).unwrap(); black_box(buf); } }); diff --git a/src/lib.rs b/src/lib.rs index d85b9ceb15d..61ac60a85a3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -358,10 +358,10 @@ pub trait Rng { /// use rand::{thread_rng, Rng}; /// /// let mut v = [0u8; 13579]; - /// thread_rng().fill_bytes(&mut v); + /// thread_rng().try_fill(&mut v).unwrap(); /// println!("{:?}", &v[..]); /// ``` - fn fill_bytes(&mut self, dest: &mut [u8]) { + fn try_fill(&mut self, dest: &mut [u8]) -> Result<()> { use core::intrinsics::transmute; let mut left = dest; @@ -380,6 +380,7 @@ pub trait Rng { }; left.copy_from_slice(&chunk[..n]); } + Ok(()) } } @@ -398,21 +399,21 @@ impl Rng for Box where R: Rng+?Sized { (**self).next_u128() } - fn fill_bytes(&mut self, dest: &mut [u8]) { - (**self).fill_bytes(dest) + fn try_fill(&mut self, dest: &mut [u8]) -> Result<()> { + (**self).try_fill(dest) } } /// Support mechanism for creating random number generators seeded by other /// generators. All PRNGs should support this to enable `NewRng` support. -pub trait FromRng { +pub trait FromRng: Sized { /// Creates a new instance, seeded from another `Rng`. /// /// Seeding from a cryptographic generator should be fine. On the other /// hand, seeding a simple numerical generator from another of the same /// type sometimes has serious side effects such as effectively cloning the /// generator. - fn from_rng(rng: &mut R) -> Self; + fn from_rng(rng: &mut R) -> Result; } /// Support mechanism for creating random number generators securely seeded @@ -429,7 +430,7 @@ pub trait NewRng: Sized { impl NewRng for R { fn new() -> Result { let mut r = OsRng::new()?; - Ok(Self::from_rng(&mut r)) + Self::from_rng(&mut r) } } @@ -695,7 +696,7 @@ mod test { 80, 81, 82, 83, 84, 85, 86, 87]; for &n in lengths.iter() { let mut v = repeat(0u8).take(n).collect::>(); - r.fill_bytes(&mut v); + r.try_fill(&mut v).unwrap(); // use this to get nicer error messages. for (i, &byte) in v.iter().enumerate() { diff --git a/src/os.rs b/src/os.rs index 71c294a55fc..9e46560b9c9 100644 --- a/src/os.rs +++ b/src/os.rs @@ -15,6 +15,7 @@ use std::{mem, fmt}; use std::io::Read; use {Rng, Result}; +// TODO: replace many of the panics below with Result error handling /// A random number generator that retrieves randomness straight from /// the operating system. Platform sources: @@ -46,16 +47,16 @@ impl OsRng { impl Rng for OsRng { fn next_u32(&mut self) -> u32 { let mut buf: [u8; 4] = [0; 4]; - self.fill_bytes(&mut buf); + self.try_fill(&mut buf).unwrap_or_else(|e| panic!("try_fill failed: {:?}", e)); unsafe{ *(buf.as_ptr() as *const u32) } } fn next_u64(&mut self) -> u64 { let mut buf: [u8; 8] = [0; 8]; - self.fill_bytes(&mut buf); + self.try_fill(&mut buf).unwrap_or_else(|e| panic!("try_fill failed: {:?}", e)); unsafe{ *(buf.as_ptr() as *const u64) } } - fn fill_bytes(&mut self, v: &mut [u8]) { - self.0.fill_bytes(v) + fn try_fill(&mut self, v: &mut [u8]) -> Result<()> { + self.0.try_fill(v) } } @@ -70,13 +71,14 @@ impl fmt::Debug for OsRng { struct ReadRng (R); impl ReadRng { - fn fill_bytes(&mut self, mut buf: &mut [u8]) { + fn try_fill(&mut self, mut buf: &mut [u8]) -> Result<()> { while buf.len() > 0 { match self.0.read(buf).unwrap_or_else(|e| panic!("Read error: {}", e)) { 0 => panic!("OsRng: no bytes available"), n => buf = &mut mem::replace(&mut buf, &mut [])[n..], } } + Ok(()) } } @@ -131,7 +133,7 @@ mod imp { target_arch = "powerpc"))))] fn getrandom(_buf: &mut [u8]) -> libc::c_long { -1 } - fn getrandom_fill_bytes(v: &mut [u8]) { + fn getrandom_try_fill(v: &mut [u8]) -> Result<()> { let mut read = 0; let len = v.len(); while read < len { @@ -147,6 +149,7 @@ mod imp { read += result as usize; } } + Ok(()) } #[cfg(all(target_os = "linux", @@ -208,10 +211,10 @@ mod imp { Ok(OsRng { inner: OsReadRng(reader_rng) }) } - pub fn fill_bytes(&mut self, v: &mut [u8]) { + pub fn try_fill(&mut self, v: &mut [u8]) -> Result<()> { match self.inner { - OsGetrandomRng => getrandom_fill_bytes(v), - OsReadRng(ref mut rng) => rng.fill_bytes(v) + OsGetrandomRng => getrandom_try_fill(v), + OsReadRng(ref mut rng) => rng.try_fill(v) } } } @@ -243,7 +246,7 @@ mod imp { pub fn new() -> Result { Ok(OsRng) } - pub fn fill_bytes(&mut self, v: &mut [u8]) { + pub fn try_fill(&mut self, v: &mut [u8]) -> Result<()> { let ret = unsafe { SecRandomCopyBytes(kSecRandomDefault, v.len() as size_t, v.as_mut_ptr()) }; @@ -268,7 +271,7 @@ mod imp { pub fn new() -> Result { Ok(OsRng) } - pub fn fill_bytes(&mut self, v: &mut [u8]) { + pub fn try_fill(&mut self, v: &mut [u8]) -> Result<()> { let mib = [libc::CTL_KERN, libc::KERN_ARND]; // kern.arandom permits a maximum buffer size of 256 bytes for s in v.chunks_mut(256) { @@ -301,7 +304,7 @@ mod imp { pub fn new() -> Result { Ok(OsRng) } - pub fn fill_bytes(&mut self, v: &mut [u8]) { + pub fn try_fill(&mut self, v: &mut [u8]) -> Result<()> { // getentropy(2) permits a maximum buffer size of 256 bytes for s in v.chunks_mut(256) { let ret = unsafe { @@ -335,8 +338,8 @@ mod imp { Ok(OsRng { inner: reader_rng }) } - pub fn fill_bytes(&mut self, v: &mut [u8]) { - self.inner.fill_bytes(v) + pub fn try_fill(&mut self, v: &mut [u8]) -> Result<()> { + self.inner.try_fill(v) } } } @@ -355,7 +358,7 @@ mod imp { pub fn new() -> Result { Ok(OsRng) } - pub fn fill_bytes(&mut self, v: &mut [u8]) { + pub fn try_fill(&mut self, v: &mut [u8]) -> Result<()> { for s in v.chunks_mut(magenta::MX_CPRNG_DRAW_MAX_LEN) { let mut filled = 0; while filled < s.len() { @@ -390,7 +393,7 @@ mod imp { pub fn new() -> Result { Ok(OsRng) } - pub fn fill_bytes(&mut self, v: &mut [u8]) { + pub fn try_fill(&mut self, v: &mut [u8]) -> Result<()> { // RtlGenRandom takes an ULONG (u32) for the length so we need to // split up the buffer. for slice in v.chunks_mut(::max_value() as usize) { @@ -454,7 +457,7 @@ mod imp { Err(Result) } } - pub fn fill_bytes(&mut self, v: &mut [u8]) { + pub fn try_fill(&mut self, v: &mut [u8]) -> Result<()> { let mut read = 0; loop { let mut r: libc::size_t = 0; @@ -487,7 +490,7 @@ mod test { r.next_u64(); let mut v = [0u8; 1000]; - r.fill_bytes(&mut v); + r.try_fill(&mut v).unwrap(); } #[test] @@ -513,7 +516,7 @@ mod test { thread::yield_now(); r.next_u64(); thread::yield_now(); - r.fill_bytes(&mut v); + r.try_fill(&mut v).unwrap(); thread::yield_now(); } }); diff --git a/src/prng/chacha.rs b/src/prng/chacha.rs index f40c4bf89a2..9d2043eefbf 100644 --- a/src/prng/chacha.rs +++ b/src/prng/chacha.rs @@ -11,7 +11,7 @@ //! The ChaCha random number generator. use core::num::Wrapping as w; -use {Rng, FromRng, SeedableRng}; +use {Rng, FromRng, SeedableRng, Result}; #[allow(bad_style)] type w32 = w; @@ -200,7 +200,7 @@ impl Rng for ChaChaRng { // Custom implementation allowing larger reads from buffer is about 8% // faster than default implementation in my tests - fn fill_bytes(&mut self, dest: &mut [u8]) { + fn try_fill(&mut self, dest: &mut [u8]) -> Result<()> { use core::cmp::min; use core::intrinsics::{transmute, copy_nonoverlapping}; @@ -232,16 +232,17 @@ impl Rng for ChaChaRng { }; left.copy_from_slice(&chunk[..n]); } + Ok(()) } } impl FromRng for ChaChaRng { - fn from_rng(other: &mut R) -> ChaChaRng { + fn from_rng(other: &mut R) -> Result { let mut key : [u32; KEY_WORDS] = [0; KEY_WORDS]; for word in key.iter_mut() { *word = other.next_u32(); } - SeedableRng::from_seed(&key[..]) + Ok(SeedableRng::from_seed(&key[..])) } } diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index b001fc9163c..ee2a975810d 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -17,7 +17,7 @@ use core::iter::repeat; use core::num::Wrapping as w; use core::fmt; -use {Rng, FromRng, SeedableRng}; +use {Rng, FromRng, SeedableRng, Result}; /// Select 32- or 64-bit variant dependent on pointer size. #[cfg(target_pointer_width = "32")] @@ -223,7 +223,7 @@ impl Rng for IsaacRng { } // Default impl adjusted for native byte size; approx 18% faster in tests - fn fill_bytes(&mut self, dest: &mut [u8]) { + fn try_fill(&mut self, dest: &mut [u8]) -> Result<()> { use core::intrinsics::transmute; let mut left = dest; @@ -242,17 +242,18 @@ impl Rng for IsaacRng { }; left.copy_from_slice(&chunk[..n]); } + Ok(()) } } impl FromRng for IsaacRng { - fn from_rng(other: &mut R) -> IsaacRng { + fn from_rng(other: &mut R) -> Result { let mut ret = EMPTY; unsafe { let ptr = ret.rsl.as_mut_ptr() as *mut u8; let slice = slice::from_raw_parts_mut(ptr, RAND_SIZE_USIZE * 4); - other.fill_bytes(slice); + other.try_fill(slice)?; } ret.cnt = 0; ret.a = w(0); @@ -260,7 +261,7 @@ impl FromRng for IsaacRng { ret.c = w(0); ret.init(true); - return ret; + Ok(ret) } } @@ -494,13 +495,13 @@ impl Rng for Isaac64Rng { } impl FromRng for Isaac64Rng { - fn from_rng(other: &mut R) -> Isaac64Rng { + fn from_rng(other: &mut R) -> Result { let mut ret = EMPTY_64; unsafe { let ptr = ret.rsl.as_mut_ptr() as *mut u8; let slice = slice::from_raw_parts_mut(ptr, RAND_SIZE_64 * 8); - other.fill_bytes(slice); + other.try_fill(slice)?; } ret.cnt = 0; ret.a = w(0); @@ -508,7 +509,7 @@ impl FromRng for Isaac64Rng { ret.c = w(0); ret.init(true); - return ret; + Ok(ret) } } diff --git a/src/prng/xorshift.rs b/src/prng/xorshift.rs index 6e41f54fd1e..bff47aca8ba 100644 --- a/src/prng/xorshift.rs +++ b/src/prng/xorshift.rs @@ -11,7 +11,7 @@ //! Xorshift generators use core::num::Wrapping as w; -use {Rng, FromRng, SeedableRng}; +use {Rng, FromRng, SeedableRng, Result}; /// An Xorshift[1] random number /// generator. @@ -50,7 +50,7 @@ impl XorShiftRng { } impl FromRng for XorShiftRng { - fn from_rng(rng: &mut R) -> XorShiftRng { + fn from_rng(rng: &mut R) -> Result { let mut tuple: (u32, u32, u32, u32); loop { tuple = (rng.next_u32(), rng.next_u32(), rng.next_u32(), rng.next_u32()); @@ -59,7 +59,7 @@ impl FromRng for XorShiftRng { } } let (x, y, z, w_) = tuple; - XorShiftRng { x: w(x), y: w(y), z: w(z), w: w(w_) } + Ok(XorShiftRng { x: w(x), y: w(y), z: w(z), w: w(w_) }) } } diff --git a/src/read.rs b/src/read.rs index 53ab46ff2a8..e6697df72ba 100644 --- a/src/read.rs +++ b/src/read.rs @@ -68,9 +68,9 @@ impl Rng for ReadRng { impl_uint_from_fill!(u128, 16, self) } - fn fill_bytes(&mut self, v: &mut [u8]) { - if v.len() == 0 { return } - fill(&mut self.reader, v).unwrap(); + fn try_fill(&mut self, v: &mut [u8]) -> Result<()> { + if v.len() == 0 { return Ok(()); } + fill(&mut self.reader, v) } } @@ -111,21 +111,20 @@ mod test { assert_eq!(rng.next_u32(), 3_u32.to_be()); } #[test] - fn test_reader_rng_fill_bytes() { + fn test_reader_rng_try_fill() { let v = [1u8, 2, 3, 4, 5, 6, 7, 8]; let mut w = [0u8; 8]; let mut rng = ReadRng::new(&v[..]); - rng.fill_bytes(&mut w); + rng.try_fill(&mut w).unwrap(); assert!(v == w); } #[test] - #[should_panic] fn test_reader_rng_insufficient_bytes() { let mut rng = ReadRng::new(&[][..]); let mut v = [0u8; 3]; - rng.fill_bytes(&mut v); + assert!(rng.try_fill(&mut v).is_err()); } } diff --git a/src/reseeding.rs b/src/reseeding.rs index 0d6aa52cd22..00f2b0151ff 100644 --- a/src/reseeding.rs +++ b/src/reseeding.rs @@ -14,7 +14,7 @@ use core::default::Default; use core::fmt::Debug; -use {Rng, SeedableRng}; +use {Rng, SeedableRng, Result}; /// How many bytes of entropy the underling RNG is allowed to generate /// before it is reseeded @@ -79,10 +79,10 @@ impl + Debug> Rng for ReseedingRng { self.rng.next_u128() } - fn fill_bytes(&mut self, dest: &mut [u8]) { + fn try_fill(&mut self, dest: &mut [u8]) -> Result<()> { self.reseed_if_necessary(); self.bytes_generated += dest.len() as u64; - self.rng.fill_bytes(dest) + self.rng.try_fill(dest) } } @@ -193,15 +193,15 @@ mod test { const FILL_BYTES_V_LEN: usize = 13579; #[test] - fn test_rng_fill_bytes() { + fn test_rng_try_fill() { let mut v = repeat(0u8).take(FILL_BYTES_V_LEN).collect::>(); - ::test::rng().fill_bytes(&mut v); + ::test::rng().try_fill(&mut v).unwrap(); - // Sanity test: if we've gotten here, `fill_bytes` has not infinitely + // Sanity test: if we've gotten here, `try_fill` has not infinitely // recursed. assert_eq!(v.len(), FILL_BYTES_V_LEN); - // To test that `fill_bytes` actually did something, check that the + // To test that `try_fill` actually did something, check that the // average of `v` is not 0. let mut sum = 0.0; for &x in v.iter() { diff --git a/src/thread_local.rs b/src/thread_local.rs index 06eeb2ccef4..b309fd7c66e 100644 --- a/src/thread_local.rs +++ b/src/thread_local.rs @@ -14,7 +14,7 @@ use std::cell::RefCell; use std::rc::Rc; use std::sync::Mutex; -use {Rng, OsRng, Rand, Default}; +use {Rng, OsRng, Rand, Default, Result}; // use reseeding::{Reseeder, ReseedingRng}; // @@ -59,8 +59,8 @@ impl Rng for ThreadRng { } #[inline] - fn fill_bytes(&mut self, bytes: &mut [u8]) { - self.rng.borrow_mut().fill_bytes(bytes) + fn try_fill(&mut self, bytes: &mut [u8]) -> Result<()> { + self.rng.borrow_mut().try_fill(bytes) } } From 07b222bf0985b9b487526a3c424fbe46a972acba Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sun, 10 Sep 2017 16:44:33 +0100 Subject: [PATCH 103/247] Move Rng and other core bits to rand_core crate --- Cargo.toml | 4 + rand_core/Cargo.toml | 20 +++++ rand_core/src/lib.rs | 200 +++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 191 +---------------------------------------- 4 files changed, 228 insertions(+), 187 deletions(-) create mode 100644 rand_core/Cargo.toml create mode 100644 rand_core/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 991e781936e..52d1d3d0304 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,10 @@ i128_support = [] [dependencies] libc = "0.2" +rand_core = { path = 'rand_core' } [target.'cfg(target_os = "fuchsia")'.dependencies] magenta = "^0.1.1" + +[workspace] +members = ["rand_core"] diff --git a/rand_core/Cargo.toml b/rand_core/Cargo.toml new file mode 100644 index 00000000000..5968e530a55 --- /dev/null +++ b/rand_core/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "rand_core" +version = "0.0.1" +authors = ["The Rust Project Developers"] +license = "MIT/Apache-2.0" +readme = "README.md" +repository = "https://github.com/rust-lang-nursery/rand" +documentation = "https://docs.rs/rand_core" +homepage = "https://github.com/rust-lang-nursery/rand" +description = """ +Random number generator traits +""" +keywords = ["random", "rng"] +categories = ["algorithms"] + +[features] +default = ["std"] +nightly = ["i128_support"] +std = [] +i128_support = [] diff --git a/rand_core/src/lib.rs b/rand_core/src/lib.rs new file mode 100644 index 00000000000..218ae870fbe --- /dev/null +++ b/rand_core/src/lib.rs @@ -0,0 +1,200 @@ +// Copyright 2013-2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Random number generation traits +//! +//! This crate is mainly of interest to crates publishing implementations of +//! `Rng`. Other users are encouraged to use the +//! [rand crate](https://crates.io/crates/rand) instead. +//! +//! `Rng` is the core trait implemented by algorithmic pseudo-random number +//! generators and external random-number sources. +//! +//! `FromRng` and `SeedableRng` are extension traits. +//! +//! `Error` and `Result` are provided for error-handling. They are safe to use +//! in `no_std` environments. + +#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk.png", + html_favicon_url = "https://www.rust-lang.org/favicon.ico", + html_root_url = "https://docs.rs/rand/0.3")] + +#![deny(missing_debug_implementations)] + +#![cfg_attr(not(feature="std"), no_std)] +#![cfg_attr(feature = "i128_support", feature(i128_type, i128))] + +// We need to use several items from "core" for no_std support. +#[cfg(feature="std")] +extern crate core; + + +/// A random number generator. +/// +/// There are two classes of generators: *algorithmic* generators, also called +/// PRNGs (Pseudo-Random Number Generators) and *external* generators. +/// +/// Another classification for generators is those that are cryptographically +/// secure (CSPRNGs) and those that are not. CSPRNGs should satisfy two +/// additional properties: (1) given the first *k* bits of an algorithm's output +/// sequence, it should not be possible using polynomial-time algorithms to +/// predict the next bit with probability significantly greater than 50%, and +/// (2) if a CSPRNG's state is revealed, it should not be +/// computationally-feasible to reconstruct output prior to this. +/// +/// PRNGs are expected to be reproducible: that is, when a fixed algorithm is +/// seeded with a fixed value, then calling *any* sequence of the `Rng`'s +/// functions should produce a fixed sequence of values, and produce the *same* +/// sequence of values on every platform. This necessitates that a PRNG have +/// fixed endianness. +/// +/// All default implementations use little-endian code (e.g. to construct a +/// `u64` from two `u32` numbers, the first is the low part). To implement +/// `next_u32` in terms of `next_u64`, one should write `self.next_u64() as u32` +/// which takes the least-significant half (LE order). +/// +/// PRNGs are normally infallible, while external generators may fail. PRNGs +/// however have a finite period, and may emit an error rather than loop (this +/// is important for CSPRNGs which could conceivably cycle, but non-crypto +/// generators should simply cycle; in many cases the period is so long that +/// consuming all available values would be inconceivable). +/// +/// TODO: details on error handling are under discussion; for now implementations +/// may panic. +pub trait Rng { + /// Return the next random u32. + fn next_u32(&mut self) -> u32; + + /// Return the next random u64. + /// + /// By default this is implemented in terms of `next_u32` in LE order. An + /// implementation of this trait must provide at least one of + /// these two methods. Similarly to `next_u32`, this rarely needs + /// to be called directly, prefer `r.gen()` to `r.next_u64()`. + fn next_u64(&mut self) -> u64 { + // Use LE; we explicitly generate one value before the next. + let x = self.next_u32() as u64; + let y = self.next_u32() as u64; + (y << 32) | x + } + + #[cfg(feature = "i128_support")] + /// Return the next random u128. + /// + /// By default this is implemented in terms of `next_u64` in LE order. + fn next_u128(&mut self) -> u128 { + // Use LE; we explicitly generate one value before the next. + let x = self.next_u64() as u128; + let y = self.next_u64() as u128; + (y << 64) | x + } + + /// Fill `dest` entirely with random data. + /// + /// This has a default implementation in terms of `next_u64` in LE order, + /// but should be overridden by implementations that + /// offer a more efficient solution than just calling `next_u64` + /// repeatedly. + /// + /// This method does *not* have any requirement on how much of the + /// generated random number stream is consumed; e.g. the default + /// implementation uses `next_u64` thus consuming 8 bytes even when only + /// 1 is required. A different implementation might use `next_u32` and + /// only consume 4 bytes; *however* any change affecting *reproducibility* + /// of output must be considered a breaking change. + fn try_fill(&mut self, dest: &mut [u8]) -> Result<()> { + use core::intrinsics::transmute; + + let mut left = dest; + while left.len() >= 8 { + let (l, r) = {left}.split_at_mut(8); + left = r; + let chunk: [u8; 8] = unsafe { + transmute(self.next_u64().to_le()) + }; + l.copy_from_slice(&chunk); + } + let n = left.len(); + if n > 0 { + let chunk: [u8; 8] = unsafe { + transmute(self.next_u64().to_le()) + }; + left.copy_from_slice(&chunk[..n]); + } + Ok(()) + } +} + +#[cfg(feature="std")] +impl Rng for Box where R: Rng+?Sized { + fn next_u32(&mut self) -> u32 { + (**self).next_u32() + } + + fn next_u64(&mut self) -> u64 { + (**self).next_u64() + } + + #[cfg(feature = "i128_support")] + fn next_u128(&mut self) -> u128 { + (**self).next_u128() + } + + fn try_fill(&mut self, dest: &mut [u8]) -> Result<()> { + (**self).try_fill(dest) + } +} + + +/// Support mechanism for creating random number generators seeded by other +/// generators. All PRNGs should support this to enable `NewRng` support. +pub trait FromRng: Sized { + /// Creates a new instance, seeded from another `Rng`. + /// + /// Seeding from a cryptographic generator should be fine. On the other + /// hand, seeding a simple numerical generator from another of the same + /// type sometimes has serious side effects such as effectively cloning the + /// generator. + fn from_rng(rng: &mut R) -> Result; +} + + +/// A random number generator that can be explicitly seeded to produce +/// the same stream of randomness multiple times. +pub trait SeedableRng: Rng { + /// Reseed an RNG with the given seed. + /// + /// The type of `Seed` is specified by the implementation (implementation + /// for multiple seed types is possible). + fn reseed(&mut self, Seed); + + /// Create a new RNG with the given seed. + /// + /// The type of `Seed` is specified by the implementation (implementation + /// for multiple seed types is possible). + fn from_seed(seed: Seed) -> Self; +} + + +/// Error type for cryptographic generators. Only operating system and hardware +/// generators should be able to fail. In such cases there is little that can +/// be done besides try again later. +#[derive(Debug)] +pub struct Error; + +#[cfg(feature="std")] +impl From<::std::io::Error> for Error { + fn from(_: ::std::io::Error) -> Error { + Error + } +} + +/// Result type (convenience type-def) +pub type Result = ::std::result::Result; diff --git a/src/lib.rs b/src/lib.rs index 61ac60a85a3..b89aafcda80 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -252,6 +252,10 @@ #[cfg(feature="std")] extern crate core; +extern crate rand_core; + +pub use rand_core::{Rng, FromRng, SeedableRng, Error, Result}; + #[cfg(feature="std")] pub use read::ReadRng; #[cfg(feature="std")] @@ -278,144 +282,6 @@ mod read; #[cfg(feature="std")] mod thread_local; -/// A random number generator. -/// -/// There are two classes of generators: *algorithmic* generators, also called -/// PRNGs (Pseudo-Random Number Generators) and *external* generators. -/// -/// Another classification for generators is those that are cryptographically -/// secure (CSPRNGs) and those that are not. CSPRNGs should satisfy two -/// additional properties: (1) given the first *k* bits of an algorithm's output -/// sequence, it should not be possible using polynomial-time algorithms to -/// predict the next bit with probability significantly greater than 50%, and -/// (2) if a CSPRNG's state is revealed, it should not be -/// computationally-feasible to reconstruct output prior to this. -/// -/// PRNGs are expected to be reproducible: that is, when a fixed algorithm is -/// seeded with a fixed value, then calling *any* sequence of the `Rng`'s -/// functions should produce a fixed sequence of values, and produce the *same* -/// sequence of values on every platform. This necessitates that a PRNG have -/// fixed endianness. -/// -/// All default implementations use little-endian code (e.g. to construct a -/// `u64` from two `u32` numbers, the first is the low part). To implement -/// `next_u32` in terms of `next_u64`, one should write `self.next_u64() as u32` -/// which takes the least-significant half (LE order). -/// -/// PRNGs are normally infallible, while external generators may fail. PRNGs -/// however have a finite period, and may emit an error rather than loop (this -/// is important for CSPRNGs which could conceivably cycle, but non-crypto -/// generators should simply cycle; in many cases the period is so long that -/// consuming all available values would be inconceivable). -/// -/// TODO: details on error handling are under discussion; for now implementations -/// may panic. -pub trait Rng { - /// Return the next random u32. - fn next_u32(&mut self) -> u32; - - /// Return the next random u64. - /// - /// By default this is implemented in terms of `next_u32` in LE order. An - /// implementation of this trait must provide at least one of - /// these two methods. Similarly to `next_u32`, this rarely needs - /// to be called directly, prefer `r.gen()` to `r.next_u64()`. - fn next_u64(&mut self) -> u64 { - // Use LE; we explicitly generate one value before the next. - let x = self.next_u32() as u64; - let y = self.next_u32() as u64; - (y << 32) | x - } - - #[cfg(feature = "i128_support")] - /// Return the next random u128. - /// - /// By default this is implemented in terms of `next_u64` in LE order. - fn next_u128(&mut self) -> u128 { - // Use LE; we explicitly generate one value before the next. - let x = self.next_u64() as u128; - let y = self.next_u64() as u128; - (y << 64) | x - } - - /// Fill `dest` entirely with random data. - /// - /// This has a default implementation in terms of `next_u64` in LE order, - /// but should be overridden by implementations that - /// offer a more efficient solution than just calling `next_u64` - /// repeatedly. - /// - /// This method does *not* have any requirement on how much of the - /// generated random number stream is consumed; e.g. the default - /// implementation uses `next_u64` thus consuming 8 bytes even when only - /// 1 is required. A different implementation might use `next_u32` and - /// only consume 4 bytes; *however* any change affecting *reproducibility* - /// of output must be considered a breaking change. - /// - /// # Example - /// - /// ```rust - /// use rand::{thread_rng, Rng}; - /// - /// let mut v = [0u8; 13579]; - /// thread_rng().try_fill(&mut v).unwrap(); - /// println!("{:?}", &v[..]); - /// ``` - fn try_fill(&mut self, dest: &mut [u8]) -> Result<()> { - use core::intrinsics::transmute; - - let mut left = dest; - while left.len() >= 8 { - let (l, r) = {left}.split_at_mut(8); - left = r; - let chunk: [u8; 8] = unsafe { - transmute(self.next_u64().to_le()) - }; - l.copy_from_slice(&chunk); - } - let n = left.len(); - if n > 0 { - let chunk: [u8; 8] = unsafe { - transmute(self.next_u64().to_le()) - }; - left.copy_from_slice(&chunk[..n]); - } - Ok(()) - } -} - -#[cfg(feature="std")] -impl Rng for Box where R: Rng+?Sized { - fn next_u32(&mut self) -> u32 { - (**self).next_u32() - } - - fn next_u64(&mut self) -> u64 { - (**self).next_u64() - } - - #[cfg(feature = "i128_support")] - fn next_u128(&mut self) -> u128 { - (**self).next_u128() - } - - fn try_fill(&mut self, dest: &mut [u8]) -> Result<()> { - (**self).try_fill(dest) - } -} - -/// Support mechanism for creating random number generators seeded by other -/// generators. All PRNGs should support this to enable `NewRng` support. -pub trait FromRng: Sized { - /// Creates a new instance, seeded from another `Rng`. - /// - /// Seeding from a cryptographic generator should be fine. On the other - /// hand, seeding a simple numerical generator from another of the same - /// type sometimes has serious side effects such as effectively cloning the - /// generator. - fn from_rng(rng: &mut R) -> Result; -} - /// Support mechanism for creating random number generators securely seeded /// using the OS generator. /// @@ -434,40 +300,6 @@ impl NewRng for R { } } -/// A random number generator that can be explicitly seeded to produce -/// the same stream of randomness multiple times. -pub trait SeedableRng: Rng { - /// Reseed an RNG with the given seed. - /// - /// # Example - /// - /// ```rust - /// use rand::{Rng, SeedableRng, Rand, Default}; - /// use rand::prng::ChaChaRng; - /// - /// let seed: &[_] = &[1, 2, 3, 4]; - /// let mut rng: ChaChaRng = SeedableRng::from_seed(seed); - /// println!("{}", f64::rand(&mut rng, Default)); - /// rng.reseed(&[5, 6, 7, 8]); - /// println!("{}", f64::rand(&mut rng, Default)); - /// ``` - fn reseed(&mut self, Seed); - - /// Create a new RNG with the given seed. - /// - /// # Example - /// - /// ```rust - /// use rand::{Rng, SeedableRng, Rand, Default}; - /// use rand::prng::ChaChaRng; - /// - /// let seed: &[_] = &[1, 2, 3, 4]; - /// let mut rng: ChaChaRng = SeedableRng::from_seed(seed); - /// println!("{}", f64::rand(&mut rng, Default)); - /// ``` - fn from_seed(seed: Seed) -> Self; -} - use distributions::range::SampleRange; /// Extension trait on [`Rng`] with some convenience methods. @@ -630,21 +462,6 @@ impl Rng for StdRng { } } -/// Error type for cryptographic generators. Only operating system and hardware -/// generators should be able to fail. In such cases there is little that can -/// be done besides try again later. -#[derive(Debug)] -pub struct Error; - -#[cfg(feature="std")] -impl From<::std::io::Error> for Error { - fn from(_: ::std::io::Error) -> Error { - Error - } -} - -pub type Result = ::std::result::Result; - #[cfg(test)] mod test { From b8873f70a1eaf6d2b647e7cfe2d972d54053deb7 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 11 Sep 2017 12:03:51 +0100 Subject: [PATCH 104/247] =?UTF-8?q?Rename=20FromRng=20=E2=86=92=20SeedFrom?= =?UTF-8?q?Rng=20and=20NewRng=20=E2=86=92=20NewSeeded?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also some TODO notes --- benches/distributions.rs | 2 +- benches/generators.rs | 2 +- benches/misc.rs | 2 +- rand_core/src/lib.rs | 13 ++++++++++--- src/lib.rs | 11 ++++++----- src/prng/chacha.rs | 4 ++-- src/prng/isaac.rs | 6 +++--- src/prng/xorshift.rs | 4 ++-- 8 files changed, 26 insertions(+), 18 deletions(-) diff --git a/benches/distributions.rs b/benches/distributions.rs index f2d4f8ac61b..7f9a51424df 100644 --- a/benches/distributions.rs +++ b/benches/distributions.rs @@ -8,7 +8,7 @@ const RAND_BENCH_N: u64 = 1000; use std::mem::size_of; use test::{black_box, Bencher}; -use rand::{Default, Rand, NewRng}; +use rand::{Default, Rand, NewSeeded}; use rand::prng::XorShiftRng; use rand::distributions::*; diff --git a/benches/generators.rs b/benches/generators.rs index 46d200b31aa..af55a0f78f9 100644 --- a/benches/generators.rs +++ b/benches/generators.rs @@ -9,7 +9,7 @@ const BYTES_LEN: usize = 1024; use std::mem::size_of; use test::{black_box, Bencher}; -use rand::{Rng, NewRng, StdRng, OsRng, Rand, Default}; +use rand::{Rng, NewSeeded, StdRng, OsRng, Rand, Default}; use rand::prng::{XorShiftRng, IsaacRng, Isaac64Rng, ChaChaRng}; macro_rules! gen_bytes { diff --git a/benches/misc.rs b/benches/misc.rs index ce4e999d581..feb41bf9d5a 100644 --- a/benches/misc.rs +++ b/benches/misc.rs @@ -4,7 +4,7 @@ extern crate test; extern crate rand; use test::{black_box, Bencher}; -use rand::NewRng; +use rand::NewSeeded; use rand::prng::XorShiftRng; use rand::sequences::{sample, Shuffle}; diff --git a/rand_core/src/lib.rs b/rand_core/src/lib.rs index 218ae870fbe..9d9fc7c1aad 100644 --- a/rand_core/src/lib.rs +++ b/rand_core/src/lib.rs @@ -17,7 +17,7 @@ //! `Rng` is the core trait implemented by algorithmic pseudo-random number //! generators and external random-number sources. //! -//! `FromRng` and `SeedableRng` are extension traits. +//! `SeedFromRng` and `SeedableRng` are extension traits. //! //! `Error` and `Result` are provided for error-handling. They are safe to use //! in `no_std` environments. @@ -154,8 +154,15 @@ impl Rng for Box where R: Rng+?Sized { /// Support mechanism for creating random number generators seeded by other -/// generators. All PRNGs should support this to enable `NewRng` support. -pub trait FromRng: Sized { +/// generators. All PRNGs should support this to enable `NewSeeded` support. +/// +/// TODO: should this use `Distribution` instead? That would require moving +/// that trait and a distribution type to this crate. +/// TODO: should the source requirement be changed, e.g. to `CryptoRng`? +/// Note: this is distinct from `SeedableRng` because it is generic over the +/// RNG type (achieving the same with `SeedableRng` would require dynamic +/// dispatch: `SeedableRng<&mut Rng>`). +pub trait SeedFromRng: Sized { /// Creates a new instance, seeded from another `Rng`. /// /// Seeding from a cryptographic generator should be fine. On the other diff --git a/src/lib.rs b/src/lib.rs index b89aafcda80..e49e658f7bc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -254,7 +254,7 @@ extern crate core; extern crate rand_core; -pub use rand_core::{Rng, FromRng, SeedableRng, Error, Result}; +pub use rand_core::{Rng, SeedFromRng, SeedableRng, Error, Result}; #[cfg(feature="std")] pub use read::ReadRng; @@ -282,18 +282,19 @@ mod read; #[cfg(feature="std")] mod thread_local; -/// Support mechanism for creating random number generators securely seeded +/// Support mechanism for creating securely seeded objects /// using the OS generator. +/// Intended for use by RNGs, but not restricted to these. /// -/// This is implemented automatically for any PRNG implementing `FromRng`. +/// This is implemented automatically for any PRNG implementing `SeedFromRng`. #[cfg(feature="std")] -pub trait NewRng: Sized { +pub trait NewSeeded: Sized { /// Creates a new instance, automatically seeded via `OsRng`. fn new() -> Result; } #[cfg(feature="std")] -impl NewRng for R { +impl NewSeeded for R { fn new() -> Result { let mut r = OsRng::new()?; Self::from_rng(&mut r) diff --git a/src/prng/chacha.rs b/src/prng/chacha.rs index 9d2043eefbf..0b0a83b6c1f 100644 --- a/src/prng/chacha.rs +++ b/src/prng/chacha.rs @@ -11,7 +11,7 @@ //! The ChaCha random number generator. use core::num::Wrapping as w; -use {Rng, FromRng, SeedableRng, Result}; +use {Rng, SeedFromRng, SeedableRng, Result}; #[allow(bad_style)] type w32 = w; @@ -236,7 +236,7 @@ impl Rng for ChaChaRng { } } -impl FromRng for ChaChaRng { +impl SeedFromRng for ChaChaRng { fn from_rng(other: &mut R) -> Result { let mut key : [u32; KEY_WORDS] = [0; KEY_WORDS]; for word in key.iter_mut() { diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index ee2a975810d..f7d206c629f 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -17,7 +17,7 @@ use core::iter::repeat; use core::num::Wrapping as w; use core::fmt; -use {Rng, FromRng, SeedableRng, Result}; +use {Rng, SeedFromRng, SeedableRng, Result}; /// Select 32- or 64-bit variant dependent on pointer size. #[cfg(target_pointer_width = "32")] @@ -246,7 +246,7 @@ impl Rng for IsaacRng { } } -impl FromRng for IsaacRng { +impl SeedFromRng for IsaacRng { fn from_rng(other: &mut R) -> Result { let mut ret = EMPTY; unsafe { @@ -494,7 +494,7 @@ impl Rng for Isaac64Rng { } } -impl FromRng for Isaac64Rng { +impl SeedFromRng for Isaac64Rng { fn from_rng(other: &mut R) -> Result { let mut ret = EMPTY_64; unsafe { diff --git a/src/prng/xorshift.rs b/src/prng/xorshift.rs index bff47aca8ba..dbfa24a26a0 100644 --- a/src/prng/xorshift.rs +++ b/src/prng/xorshift.rs @@ -11,7 +11,7 @@ //! Xorshift generators use core::num::Wrapping as w; -use {Rng, FromRng, SeedableRng, Result}; +use {Rng, SeedFromRng, SeedableRng, Result}; /// An Xorshift[1] random number /// generator. @@ -49,7 +49,7 @@ impl XorShiftRng { } } -impl FromRng for XorShiftRng { +impl SeedFromRng for XorShiftRng { fn from_rng(rng: &mut R) -> Result { let mut tuple: (u32, u32, u32, u32); loop { From 10f794f4e31bdb252dde232722e184dfc23eb238 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 11 Sep 2017 12:04:26 +0100 Subject: [PATCH 105/247] Add rand_core/README.md --- rand_core/README.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 rand_core/README.md diff --git a/rand_core/README.md b/rand_core/README.md new file mode 100644 index 00000000000..1a354e62e9a --- /dev/null +++ b/rand_core/README.md @@ -0,0 +1,29 @@ +rand +==== + +Core functionality for the [rand] library. + +This crate contains the core trait, `Rng`, and some extension traits. This +should be sufficient for libraries publishing an RNG type, but most users should +prefer to use the [rand] crate. + +[Documentation](https://docs.rs/rand) + + +## Status + +This crate is experimental, provided as part of the [rand crate refactor]. Users +should be wary of depending on this crate at this time; breaking changes should +be expected and contents will conflict with those of the current published +version of rand (0.3.x). + +# License + +`rand` is primarily distributed under the terms of both the MIT +license and the Apache License (Version 2.0), with portions covered by various +BSD-like licenses. + +See LICENSE-APACHE, and LICENSE-MIT for details. + +[rand]: .. +[rand crate refactor]: https://github.com/rust-lang/rfcs/pull/2106 From ec29f80435eb2467dcc92785e456b04c27c6d522 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 11 Sep 2017 12:19:44 +0100 Subject: [PATCH 106/247] Fix i128_support for sub crate --- Cargo.toml | 2 +- rand_core/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 52d1d3d0304..7e455a4b407 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ categories = ["algorithms"] default = ["std"] nightly = ["i128_support"] std = [] -i128_support = [] +i128_support = ["rand_core/i128_support"] [dependencies] libc = "0.2" diff --git a/rand_core/src/lib.rs b/rand_core/src/lib.rs index 9d9fc7c1aad..fdb6a4a2ba9 100644 --- a/rand_core/src/lib.rs +++ b/rand_core/src/lib.rs @@ -29,7 +29,7 @@ #![deny(missing_debug_implementations)] #![cfg_attr(not(feature="std"), no_std)] -#![cfg_attr(feature = "i128_support", feature(i128_type, i128))] +#![cfg_attr(feature = "i128_support", feature(i128_type))] // We need to use several items from "core" for no_std support. #[cfg(feature="std")] From ad973f19c57dfe27276592cc7dc7d002f75a0adf Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 11 Sep 2017 12:40:53 +0100 Subject: [PATCH 107/247] Add notes on testing to README --- README.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/README.md b/README.md index cdbdb046931..18afef10cd0 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,24 @@ let mut rng = ChaChaRng::from_rng(&mut thread_rng()).unwrap(); println!("random between 0-9: {}", distributions::range(0, 10, &mut rng)); ``` +## Testing + +Unfortunately, `cargo test` does not test everything. The following tests are +recommended: + +``` +# Basic tests for rand and sub-crates +cargo test --all + +# Test no_std support (build only since nearly all tests require std) +cargo build --all --no-default-features + +# Test 128-bit support (requires nightly) +cargo test --all --features i128_support + +# Benchmarks (requires nightly) +cargo bench +``` # License From 95a16c30c62a18dc9a8eea5cf0466db4b3dc849b Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 11 Sep 2017 18:56:11 +0100 Subject: [PATCH 108/247] Replace ReseedWithDefault with ReseedWithNew using NewSeeded Rationale: ReseedWithDefault required the RNG used to support Default, which nothing other than StdRng supported. OTOH all RNGs support SeedFromRng which gives them NewSeeded support too, and mock RNGs can support NewSeeded directly. --- src/lib.rs | 4 +++- src/reseeding.rs | 53 ++++++++++++++++++++++++------------------------ 2 files changed, 29 insertions(+), 28 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index e49e658f7bc..b490c325d73 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -286,7 +286,9 @@ mod thread_local; /// using the OS generator. /// Intended for use by RNGs, but not restricted to these. /// -/// This is implemented automatically for any PRNG implementing `SeedFromRng`. +/// This is implemented automatically for any PRNG implementing `SeedFromRng`, +/// and for normal types shouldn't be implemented directly. For mock generators +/// it may be useful to implement this instead of `SeedFromRng`. #[cfg(feature="std")] pub trait NewSeeded: Sized { /// Creates a new instance, automatically seeded via `OsRng`. diff --git a/src/reseeding.rs b/src/reseeding.rs index 00f2b0151ff..29ce741e533 100644 --- a/src/reseeding.rs +++ b/src/reseeding.rs @@ -11,10 +11,9 @@ //! A wrapper around another RNG that reseeds it after it //! generates a certain number of random bytes. -use core::default::Default; use core::fmt::Debug; -use {Rng, SeedableRng, Result}; +use {Rng, SeedableRng, NewSeeded, Result}; /// How many bytes of entropy the underling RNG is allowed to generate /// before it is reseeded @@ -31,7 +30,7 @@ pub struct ReseedingRng { pub reseeder: Rsdr, } -impl + Debug> ReseedingRng { +impl> ReseedingRng { /// Create a new `ReseedingRng` with the given parameters. /// /// # Arguments @@ -59,7 +58,7 @@ impl + Debug> ReseedingRng { } -impl + Debug> Rng for ReseedingRng { +impl> Rng for ReseedingRng { fn next_u32(&mut self) -> u32 { self.reseed_if_necessary(); self.bytes_generated += 4; @@ -86,8 +85,9 @@ impl + Debug> Rng for ReseedingRng { } } -impl, Rsdr: Reseeder + Debug + Default> - SeedableRng<(Rsdr, S)> for ReseedingRng { +impl, Rsdr: Reseeder> SeedableRng<(Rsdr, S)> for + ReseedingRng +{ fn reseed(&mut self, (rsdr, seed): (Rsdr, S)) { self.rng.reseed(seed); self.reseeder = rsdr; @@ -107,32 +107,31 @@ impl, Rsdr: Reseeder + Debug + Default> } /// Something that can be used to reseed an RNG via `ReseedingRng`. -pub trait Reseeder { +pub trait Reseeder: Debug { /// Reseed the given RNG. fn reseed(&mut self, rng: &mut R); } -/// Reseed an RNG using a `Default` instance. This reseeds by -/// replacing the RNG with the result of a `Default::default` call. +/// Reseed an RNG using `NewSeeded` to replace the current instance. #[derive(Clone, Copy, Debug)] -pub struct ReseedWithDefault; +pub struct ReseedWithNew; -impl Reseeder for ReseedWithDefault { +impl Reseeder for ReseedWithNew { fn reseed(&mut self, rng: &mut R) { - *rng = Default::default(); + match R::new() { + Ok(result) => *rng = result, + // TODO: should we ignore and continue without reseeding? + Err(e) => panic!("Reseeding failed: {:?}", e), + } } } -impl Default for ReseedWithDefault { - fn default() -> ReseedWithDefault { ReseedWithDefault } -} #[cfg(test)] mod test { - use std::default::Default; use std::iter::repeat; - use {SeedableRng, Rng, iter}; + use {SeedableRng, Rng, iter, NewSeeded, Result}; use distributions::ascii_word_char; - use super::{ReseedingRng, ReseedWithDefault}; + use super::{ReseedingRng, ReseedWithNew}; #[derive(Debug)] struct Counter { @@ -146,9 +145,9 @@ mod test { self.i - 1 } } - impl Default for Counter { - fn default() -> Counter { - Counter { i: 0 } + impl NewSeeded for Counter { + fn new() -> Result { + Ok(Counter { i: 0 }) } } impl SeedableRng for Counter { @@ -159,11 +158,11 @@ mod test { Counter { i: seed } } } - type MyRng = ReseedingRng; + type MyRng = ReseedingRng; #[test] fn test_reseeding() { - let mut rs = ReseedingRng::new(Counter {i:0}, 400, ReseedWithDefault); + let mut rs = ReseedingRng::new(Counter {i:0}, 400, ReseedWithNew); let mut i = 0; for _ in 0..1000 { @@ -174,18 +173,18 @@ mod test { #[test] fn test_rng_seeded() { - let mut ra: MyRng = SeedableRng::from_seed((ReseedWithDefault, 2)); - let mut rb: MyRng = SeedableRng::from_seed((ReseedWithDefault, 2)); + let mut ra: MyRng = SeedableRng::from_seed((ReseedWithNew, 2)); + let mut rb: MyRng = SeedableRng::from_seed((ReseedWithNew, 2)); assert!(::test::iter_eq(iter(&mut ra).map(|rng| ascii_word_char(rng)).take(100), iter(&mut rb).map(|rng| ascii_word_char(rng)).take(100))); } #[test] fn test_rng_reseed() { - let mut r: MyRng = SeedableRng::from_seed((ReseedWithDefault, 3)); + let mut r: MyRng = SeedableRng::from_seed((ReseedWithNew, 3)); let string1: String = iter(&mut r).map(|rng| ascii_word_char(rng)).take(100).collect(); - r.reseed((ReseedWithDefault, 3)); + r.reseed((ReseedWithNew, 3)); let string2: String = iter(&mut r).map(|rng| ascii_word_char(rng)).take(100).collect(); assert_eq!(string1, string2); From ec7e6bf7dc47bc2d6a8c1a4a24c504c3650f3da9 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Mon, 11 Sep 2017 20:54:31 +0200 Subject: [PATCH 109/247] Split isaac into 3 modules For into `isaac` and `isaac64` for easier comparison. Split of `isaac_word` to generate correct the documentation (TODO). Nothing is changed here, just split in three files. --- src/prng/isaac.rs | 313 +------------------------------------ src/prng/isaac64.rs | 343 +++++++++++++++++++++++++++++++++++++++++ src/prng/isaac_word.rs | 17 ++ src/prng/mod.rs | 6 +- 4 files changed, 368 insertions(+), 311 deletions(-) create mode 100644 src/prng/isaac64.rs create mode 100644 src/prng/isaac_word.rs diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index 5dfd8884115..95aa8c75220 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -19,14 +19,6 @@ use core::fmt; use {Rng, FromRng, SeedableRng}; -/// Select 32- or 64-bit variant dependent on pointer size. -#[cfg(target_pointer_width = "32")] -pub use prng::IsaacRng as IsaacWordRng; -#[cfg(target_pointer_width = "64")] -pub use prng::Isaac64Rng as IsaacWordRng; - -#[allow(bad_style)] -type w64 = w; #[allow(bad_style)] type w32 = w; @@ -277,259 +269,11 @@ impl fmt::Debug for IsaacRng { } } -const RAND_SIZE_64_LEN: usize = 8; -const RAND_SIZE_64: usize = 1 << RAND_SIZE_64_LEN; - -/// A random number generator that uses ISAAC-64[1], the 64-bit -/// variant of the ISAAC algorithm. -/// -/// The ISAAC algorithm is generally accepted as suitable for -/// cryptographic purposes, but this implementation has not be -/// verified as such. Prefer a generator like `OsRng` that defers to -/// the operating system for cases that need high security. -/// -/// [1]: Bob Jenkins, [*ISAAC: A fast cryptographic random number -/// generator*](http://www.burtleburtle.net/bob/rand/isaacafa.html) -#[derive(Copy)] -pub struct Isaac64Rng { - cnt: usize, - rsl: [w64; RAND_SIZE_64], - mem: [w64; RAND_SIZE_64], - a: w64, - b: w64, - c: w64, -} - -static EMPTY_64: Isaac64Rng = Isaac64Rng { - cnt: 0, - rsl: [w(0); RAND_SIZE_64], - mem: [w(0); RAND_SIZE_64], - a: w(0), b: w(0), c: w(0), -}; - -impl Isaac64Rng { - /// Create a 64-bit ISAAC random number generator using the - /// default fixed seed. - pub fn new_unseeded() -> Isaac64Rng { - let mut rng = EMPTY_64; - rng.init(false); - rng - } - - /// Initialises `self`. If `use_rsl` is true, then use the current value - /// of `rsl` as a seed, otherwise construct one algorithmically (not - /// randomly). - fn init(&mut self, use_rsl: bool) { - macro_rules! init { - ($var:ident) => ( - let mut $var = w(0x9e3779b97f4a7c13); - ) - } - init!(a); init!(b); init!(c); init!(d); - init!(e); init!(f); init!(g); init!(h); - - macro_rules! mix { - () => {{ - a=a-e; f=f^(h>>9); h=h+a; - b=b-f; g=g^(a<<9); a=a+b; - c=c-g; h=h^(b>>23); b=b+c; - d=d-h; a=a^(c<<15); c=c+d; - e=e-a; b=b^(d>>14); d=d+e; - f=f-b; c=c^(e<<20); e=e+f; - g=g-c; d=d^(f>>17); f=f+g; - h=h-d; e=e^(g<<14); g=g+h; - }} - } - - for _ in 0..4 { - mix!(); - } - - if use_rsl { - macro_rules! memloop { - ($arr:expr) => {{ - for i in (0..RAND_SIZE_64 / 8).map(|i| i * 8) { - a=a+$arr[i ]; b=b+$arr[i+1]; - c=c+$arr[i+2]; d=d+$arr[i+3]; - e=e+$arr[i+4]; f=f+$arr[i+5]; - g=g+$arr[i+6]; h=h+$arr[i+7]; - mix!(); - self.mem[i ]=a; self.mem[i+1]=b; - self.mem[i+2]=c; self.mem[i+3]=d; - self.mem[i+4]=e; self.mem[i+5]=f; - self.mem[i+6]=g; self.mem[i+7]=h; - } - }} - } - - memloop!(self.rsl); - memloop!(self.mem); - } else { - for i in (0..RAND_SIZE_64 / 8).map(|i| i * 8) { - mix!(); - self.mem[i ]=a; self.mem[i+1]=b; - self.mem[i+2]=c; self.mem[i+3]=d; - self.mem[i+4]=e; self.mem[i+5]=f; - self.mem[i+6]=g; self.mem[i+7]=h; - } - } - - self.isaac64(); - } - - /// Refills the output buffer (`self.rsl`) - fn isaac64(&mut self) { - self.c = self.c + w(1); - // abbreviations - let mut a = self.a; - let mut b = self.b + self.c; - const MIDPOINT: usize = RAND_SIZE_64 / 2; - const MP_VEC: [(usize, usize); 2] = [(0,MIDPOINT), (MIDPOINT, 0)]; - macro_rules! ind { - ($x:expr) => { - *self.mem.get_unchecked((($x >> 3usize).0 as usize) & (RAND_SIZE_64 - 1)) - } - } - - for &(mr_offset, m2_offset) in MP_VEC.iter() { - for base in (0..MIDPOINT / 4).map(|i| i * 4) { - - macro_rules! rngstepp { - ($j:expr, $shift:expr) => {{ - let base = base + $j; - let mix = a ^ (a << $shift); - let mix = if $j == 0 {!mix} else {mix}; - - unsafe { - let x = *self.mem.get_unchecked(base + mr_offset); - a = mix + *self.mem.get_unchecked(base + m2_offset); - let y = ind!(x) + a + b; - *self.mem.get_unchecked_mut(base + mr_offset) = y; - - b = ind!(y >> RAND_SIZE_64_LEN) + x; - *self.rsl.get_unchecked_mut(base + mr_offset) = b; - } - }} - } - - macro_rules! rngstepn { - ($j:expr, $shift:expr) => {{ - let base = base + $j; - let mix = a ^ (a >> $shift); - let mix = if $j == 0 {!mix} else {mix}; - - unsafe { - let x = *self.mem.get_unchecked(base + mr_offset); - a = mix + *self.mem.get_unchecked(base + m2_offset); - let y = ind!(x) + a + b; - *self.mem.get_unchecked_mut(base + mr_offset) = y; - - b = ind!(y >> RAND_SIZE_64_LEN) + x; - *self.rsl.get_unchecked_mut(base + mr_offset) = b; - } - }} - } - - rngstepp!(0, 21); - rngstepn!(1, 5); - rngstepp!(2, 12); - rngstepn!(3, 33); - } - } - - self.a = a; - self.b = b; - self.cnt = RAND_SIZE_64; - } -} - -// Cannot be derived because [u32; 256] does not implement Clone -impl Clone for Isaac64Rng { - fn clone(&self) -> Isaac64Rng { - *self - } -} - -impl Rng for Isaac64Rng { - #[inline] - fn next_u32(&mut self) -> u32 { - self.next_u64() as u32 - } - - #[inline] - fn next_u64(&mut self) -> u64 { - if self.cnt == 0 { - // make some more numbers - self.isaac64(); - } - self.cnt -= 1; - - // See corresponding location in IsaacRng.next_u32 for - // explanation. - debug_assert!(self.cnt < RAND_SIZE_64); - self.rsl[(self.cnt % RAND_SIZE_64) as usize].0 - } -} - -impl FromRng for Isaac64Rng { - fn from_rng(other: &mut R) -> Isaac64Rng { - let mut ret = EMPTY_64; - unsafe { - let ptr = ret.rsl.as_mut_ptr() as *mut u8; - - let slice = slice::from_raw_parts_mut(ptr, RAND_SIZE_64 * 8); - other.fill_bytes(slice); - } - ret.cnt = 0; - ret.a = w(0); - ret.b = w(0); - ret.c = w(0); - - ret.init(true); - return ret; - } -} - -impl<'a> SeedableRng<&'a [u64]> for Isaac64Rng { - fn reseed(&mut self, seed: &'a [u64]) { - // make the seed into [seed[0], seed[1], ..., seed[seed.len() - // - 1], 0, 0, ...], to fill rng.rsl. - let seed_iter = seed.iter().map(|&x| x).chain(repeat(0u64)); - - for (rsl_elem, seed_elem) in self.rsl.iter_mut().zip(seed_iter) { - *rsl_elem = w(seed_elem); - } - self.cnt = 0; - self.a = w(0); - self.b = w(0); - self.c = w(0); - - self.init(true); - } - - /// Create an ISAAC random number generator with a seed. This can - /// be any length, although the maximum number of elements used is - /// 256 and any more will be silently ignored. A generator - /// constructed with a given seed will generate the same sequence - /// of values as all other generators constructed with that seed. - fn from_seed(seed: &'a [u64]) -> Isaac64Rng { - let mut rng = EMPTY_64; - rng.reseed(seed); - rng - } -} - -impl fmt::Debug for Isaac64Rng { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Isaac64Rng {{}}") - } -} - #[cfg(test)] mod test { use {Rng, SeedableRng, iter}; use distributions::ascii_word_char; - use super::{IsaacRng, Isaac64Rng}; + use super::IsaacRng; #[test] fn test_rng_32_rand_seeded() { @@ -539,14 +283,6 @@ mod test { assert!(::test::iter_eq(iter(&mut ra).map(|rng| ascii_word_char(rng)).take(100), iter(&mut rb).map(|rng| ascii_word_char(rng)).take(100))); } - #[test] - fn test_rng_64_rand_seeded() { - let s = iter(&mut ::test::rng()).map(|rng| rng.next_u64()).take(256).collect::>(); - let mut ra: Isaac64Rng = SeedableRng::from_seed(&s[..]); - let mut rb: Isaac64Rng = SeedableRng::from_seed(&s[..]); - assert!(::test::iter_eq(iter(&mut ra).map(|rng| ascii_word_char(rng)).take(100), - iter(&mut rb).map(|rng| ascii_word_char(rng)).take(100))); - } #[test] fn test_rng_32_seeded() { @@ -556,14 +292,6 @@ mod test { assert!(::test::iter_eq(iter(&mut ra).map(|rng| ascii_word_char(rng)).take(100), iter(&mut rb).map(|rng| ascii_word_char(rng)).take(100))); } - #[test] - fn test_rng_64_seeded() { - let seed: &[_] = &[1, 23, 456, 7890, 12345]; - let mut ra: Isaac64Rng = SeedableRng::from_seed(seed); - let mut rb: Isaac64Rng = SeedableRng::from_seed(seed); - assert!(::test::iter_eq(iter(&mut ra).map(|rng| ascii_word_char(rng)).take(100), - iter(&mut rb).map(|rng| ascii_word_char(rng)).take(100))); - } #[test] fn test_rng_32_reseed() { @@ -576,17 +304,6 @@ mod test { let string2: String = iter(&mut r).map(|rng| ascii_word_char(rng)).take(100).collect(); assert_eq!(string1, string2); } - #[test] - fn test_rng_64_reseed() { - let s = iter(&mut ::test::rng()).map(|rng| rng.next_u64()).take(256).collect::>(); - let mut r: Isaac64Rng = SeedableRng::from_seed(&s[..]); - let string1: String = iter(&mut r).map(|rng| ascii_word_char(rng)).take(100).collect(); - - r.reseed(&s[..]); - - let string2: String = iter(&mut r).map(|rng| ascii_word_char(rng)).take(100).collect(); - assert_eq!(string1, string2); - } #[test] fn test_rng_32_true_values() { @@ -608,38 +325,14 @@ mod test { vec!(3676831399, 3183332890, 2834741178, 3854698763, 2717568474, 1576568959, 3507990155, 179069555, 141456972, 2478885421)); } - #[test] - fn test_rng_64_true_values() { - let seed: &[_] = &[1, 23, 456, 7890, 12345]; - let mut ra: Isaac64Rng = SeedableRng::from_seed(seed); - // Regression test that isaac is actually using the above vector - let v = (0..10).map(|_| ra.next_u64()).collect::>(); - assert_eq!(v, - vec!(547121783600835980, 14377643087320773276, 17351601304698403469, - 1238879483818134882, 11952566807690396487, 13970131091560099343, - 4469761996653280935, 15552757044682284409, 6860251611068737823, - 13722198873481261842)); - - let seed: &[_] = &[12345, 67890, 54321, 9876]; - let mut rb: Isaac64Rng = SeedableRng::from_seed(seed); - // skip forward to the 10000th number - for _ in 0..10000 { rb.next_u64(); } - - let v = (0..10).map(|_| rb.next_u64()).collect::>(); - assert_eq!(v, - vec!(18143823860592706164, 8491801882678285927, 2699425367717515619, - 17196852593171130876, 2606123525235546165, 15790932315217671084, - 596345674630742204, 9947027391921273664, 11788097613744130851, - 10391409374914919106)); - } #[test] fn test_rng_clone() { let seed: &[_] = &[1, 23, 456, 7890, 12345]; - let mut rng: Isaac64Rng = SeedableRng::from_seed(seed); + let mut rng: IsaacRng = SeedableRng::from_seed(seed); let mut clone = rng.clone(); for _ in 0..16 { - assert_eq!(rng.next_u64(), clone.next_u64()); + assert_eq!(rng.next_u32(), clone.next_u32()); } } } diff --git a/src/prng/isaac64.rs b/src/prng/isaac64.rs new file mode 100644 index 00000000000..66bcd5e8a6f --- /dev/null +++ b/src/prng/isaac64.rs @@ -0,0 +1,343 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! The ISAAC random number generator. + +#![allow(non_camel_case_types)] + +use core::slice; +use core::iter::repeat; +use core::num::Wrapping as w; +use core::fmt; + +use {Rng, FromRng, SeedableRng}; + +#[allow(bad_style)] +type w64 = w; + +const RAND_SIZE_64_LEN: usize = 8; +const RAND_SIZE_64: usize = 1 << RAND_SIZE_64_LEN; + +/// A random number generator that uses ISAAC-64[1], the 64-bit +/// variant of the ISAAC algorithm. +/// +/// The ISAAC algorithm is generally accepted as suitable for +/// cryptographic purposes, but this implementation has not be +/// verified as such. Prefer a generator like `OsRng` that defers to +/// the operating system for cases that need high security. +/// +/// [1]: Bob Jenkins, [*ISAAC: A fast cryptographic random number +/// generator*](http://www.burtleburtle.net/bob/rand/isaacafa.html) +#[derive(Copy)] +pub struct Isaac64Rng { + cnt: usize, + rsl: [w64; RAND_SIZE_64], + mem: [w64; RAND_SIZE_64], + a: w64, + b: w64, + c: w64, +} + +static EMPTY_64: Isaac64Rng = Isaac64Rng { + cnt: 0, + rsl: [w(0); RAND_SIZE_64], + mem: [w(0); RAND_SIZE_64], + a: w(0), b: w(0), c: w(0), +}; + +impl Isaac64Rng { + /// Create a 64-bit ISAAC random number generator using the + /// default fixed seed. + pub fn new_unseeded() -> Isaac64Rng { + let mut rng = EMPTY_64; + rng.init(false); + rng + } + + /// Initialises `self`. If `use_rsl` is true, then use the current value + /// of `rsl` as a seed, otherwise construct one algorithmically (not + /// randomly). + fn init(&mut self, use_rsl: bool) { + macro_rules! init { + ($var:ident) => ( + let mut $var = w(0x9e3779b97f4a7c13); + ) + } + init!(a); init!(b); init!(c); init!(d); + init!(e); init!(f); init!(g); init!(h); + + macro_rules! mix { + () => {{ + a=a-e; f=f^(h>>9); h=h+a; + b=b-f; g=g^(a<<9); a=a+b; + c=c-g; h=h^(b>>23); b=b+c; + d=d-h; a=a^(c<<15); c=c+d; + e=e-a; b=b^(d>>14); d=d+e; + f=f-b; c=c^(e<<20); e=e+f; + g=g-c; d=d^(f>>17); f=f+g; + h=h-d; e=e^(g<<14); g=g+h; + }} + } + + for _ in 0..4 { + mix!(); + } + + if use_rsl { + macro_rules! memloop { + ($arr:expr) => {{ + for i in (0..RAND_SIZE_64 / 8).map(|i| i * 8) { + a=a+$arr[i ]; b=b+$arr[i+1]; + c=c+$arr[i+2]; d=d+$arr[i+3]; + e=e+$arr[i+4]; f=f+$arr[i+5]; + g=g+$arr[i+6]; h=h+$arr[i+7]; + mix!(); + self.mem[i ]=a; self.mem[i+1]=b; + self.mem[i+2]=c; self.mem[i+3]=d; + self.mem[i+4]=e; self.mem[i+5]=f; + self.mem[i+6]=g; self.mem[i+7]=h; + } + }} + } + + memloop!(self.rsl); + memloop!(self.mem); + } else { + for i in (0..RAND_SIZE_64 / 8).map(|i| i * 8) { + mix!(); + self.mem[i ]=a; self.mem[i+1]=b; + self.mem[i+2]=c; self.mem[i+3]=d; + self.mem[i+4]=e; self.mem[i+5]=f; + self.mem[i+6]=g; self.mem[i+7]=h; + } + } + + self.isaac64(); + } + + /// Refills the output buffer (`self.rsl`) + fn isaac64(&mut self) { + self.c = self.c + w(1); + // abbreviations + let mut a = self.a; + let mut b = self.b + self.c; + const MIDPOINT: usize = RAND_SIZE_64 / 2; + const MP_VEC: [(usize, usize); 2] = [(0,MIDPOINT), (MIDPOINT, 0)]; + macro_rules! ind { + ($x:expr) => { + *self.mem.get_unchecked((($x >> 3usize).0 as usize) & (RAND_SIZE_64 - 1)) + } + } + + for &(mr_offset, m2_offset) in MP_VEC.iter() { + for base in (0..MIDPOINT / 4).map(|i| i * 4) { + + macro_rules! rngstepp { + ($j:expr, $shift:expr) => {{ + let base = base + $j; + let mix = a ^ (a << $shift); + let mix = if $j == 0 {!mix} else {mix}; + + unsafe { + let x = *self.mem.get_unchecked(base + mr_offset); + a = mix + *self.mem.get_unchecked(base + m2_offset); + let y = ind!(x) + a + b; + *self.mem.get_unchecked_mut(base + mr_offset) = y; + + b = ind!(y >> RAND_SIZE_64_LEN) + x; + *self.rsl.get_unchecked_mut(base + mr_offset) = b; + } + }} + } + + macro_rules! rngstepn { + ($j:expr, $shift:expr) => {{ + let base = base + $j; + let mix = a ^ (a >> $shift); + let mix = if $j == 0 {!mix} else {mix}; + + unsafe { + let x = *self.mem.get_unchecked(base + mr_offset); + a = mix + *self.mem.get_unchecked(base + m2_offset); + let y = ind!(x) + a + b; + *self.mem.get_unchecked_mut(base + mr_offset) = y; + + b = ind!(y >> RAND_SIZE_64_LEN) + x; + *self.rsl.get_unchecked_mut(base + mr_offset) = b; + } + }} + } + + rngstepp!(0, 21); + rngstepn!(1, 5); + rngstepp!(2, 12); + rngstepn!(3, 33); + } + } + + self.a = a; + self.b = b; + self.cnt = RAND_SIZE_64; + } +} + +// Cannot be derived because [u32; 256] does not implement Clone +impl Clone for Isaac64Rng { + fn clone(&self) -> Isaac64Rng { + *self + } +} + +impl Rng for Isaac64Rng { + #[inline] + fn next_u32(&mut self) -> u32 { + self.next_u64() as u32 + } + + #[inline] + fn next_u64(&mut self) -> u64 { + if self.cnt == 0 { + // make some more numbers + self.isaac64(); + } + self.cnt -= 1; + + // See corresponding location in IsaacRng.next_u32 for + // explanation. + debug_assert!(self.cnt < RAND_SIZE_64); + self.rsl[(self.cnt % RAND_SIZE_64) as usize].0 + } +} + +impl FromRng for Isaac64Rng { + fn from_rng(other: &mut R) -> Isaac64Rng { + let mut ret = EMPTY_64; + unsafe { + let ptr = ret.rsl.as_mut_ptr() as *mut u8; + + let slice = slice::from_raw_parts_mut(ptr, RAND_SIZE_64 * 8); + other.fill_bytes(slice); + } + ret.cnt = 0; + ret.a = w(0); + ret.b = w(0); + ret.c = w(0); + + ret.init(true); + return ret; + } +} + +impl<'a> SeedableRng<&'a [u64]> for Isaac64Rng { + fn reseed(&mut self, seed: &'a [u64]) { + // make the seed into [seed[0], seed[1], ..., seed[seed.len() + // - 1], 0, 0, ...], to fill rng.rsl. + let seed_iter = seed.iter().map(|&x| x).chain(repeat(0u64)); + + for (rsl_elem, seed_elem) in self.rsl.iter_mut().zip(seed_iter) { + *rsl_elem = w(seed_elem); + } + self.cnt = 0; + self.a = w(0); + self.b = w(0); + self.c = w(0); + + self.init(true); + } + + /// Create an ISAAC random number generator with a seed. This can + /// be any length, although the maximum number of elements used is + /// 256 and any more will be silently ignored. A generator + /// constructed with a given seed will generate the same sequence + /// of values as all other generators constructed with that seed. + fn from_seed(seed: &'a [u64]) -> Isaac64Rng { + let mut rng = EMPTY_64; + rng.reseed(seed); + rng + } +} + +impl fmt::Debug for Isaac64Rng { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Isaac64Rng {{}}") + } +} + +#[cfg(test)] +mod test { + use {Rng, SeedableRng, iter}; + use distributions::ascii_word_char; + use super::Isaac64Rng; + + #[test] + fn test_rng_64_rand_seeded() { + let s = iter(&mut ::test::rng()).map(|rng| rng.next_u64()).take(256).collect::>(); + let mut ra: Isaac64Rng = SeedableRng::from_seed(&s[..]); + let mut rb: Isaac64Rng = SeedableRng::from_seed(&s[..]); + assert!(::test::iter_eq(iter(&mut ra).map(|rng| ascii_word_char(rng)).take(100), + iter(&mut rb).map(|rng| ascii_word_char(rng)).take(100))); + } + + #[test] + fn test_rng_64_seeded() { + let seed: &[_] = &[1, 23, 456, 7890, 12345]; + let mut ra: Isaac64Rng = SeedableRng::from_seed(seed); + let mut rb: Isaac64Rng = SeedableRng::from_seed(seed); + assert!(::test::iter_eq(iter(&mut ra).map(|rng| ascii_word_char(rng)).take(100), + iter(&mut rb).map(|rng| ascii_word_char(rng)).take(100))); + } + + #[test] + fn test_rng_64_reseed() { + let s = iter(&mut ::test::rng()).map(|rng| rng.next_u64()).take(256).collect::>(); + let mut r: Isaac64Rng = SeedableRng::from_seed(&s[..]); + let string1: String = iter(&mut r).map(|rng| ascii_word_char(rng)).take(100).collect(); + + r.reseed(&s[..]); + + let string2: String = iter(&mut r).map(|rng| ascii_word_char(rng)).take(100).collect(); + assert_eq!(string1, string2); + } + + #[test] + fn test_rng_64_true_values() { + let seed: &[_] = &[1, 23, 456, 7890, 12345]; + let mut ra: Isaac64Rng = SeedableRng::from_seed(seed); + // Regression test that isaac is actually using the above vector + let v = (0..10).map(|_| ra.next_u64()).collect::>(); + assert_eq!(v, + vec!(547121783600835980, 14377643087320773276, 17351601304698403469, + 1238879483818134882, 11952566807690396487, 13970131091560099343, + 4469761996653280935, 15552757044682284409, 6860251611068737823, + 13722198873481261842)); + + let seed: &[_] = &[12345, 67890, 54321, 9876]; + let mut rb: Isaac64Rng = SeedableRng::from_seed(seed); + // skip forward to the 10000th number + for _ in 0..10000 { rb.next_u64(); } + + let v = (0..10).map(|_| rb.next_u64()).collect::>(); + assert_eq!(v, + vec!(18143823860592706164, 8491801882678285927, 2699425367717515619, + 17196852593171130876, 2606123525235546165, 15790932315217671084, + 596345674630742204, 9947027391921273664, 11788097613744130851, + 10391409374914919106)); + } + + #[test] + fn test_rng_clone() { + let seed: &[_] = &[1, 23, 456, 7890, 12345]; + let mut rng: Isaac64Rng = SeedableRng::from_seed(seed); + let mut clone = rng.clone(); + for _ in 0..16 { + assert_eq!(rng.next_u64(), clone.next_u64()); + } + } +} diff --git a/src/prng/isaac_word.rs b/src/prng/isaac_word.rs new file mode 100644 index 00000000000..58c884d9f26 --- /dev/null +++ b/src/prng/isaac_word.rs @@ -0,0 +1,17 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! The ISAAC random number generator. + +/// Select 32- or 64-bit variant dependent on pointer size. +#[cfg(target_pointer_width = "32")] +pub use prng::isaac::IsaacRng as IsaacWordRng; +#[cfg(target_pointer_width = "64")] +pub use prng::isaac64::Isaac64Rng as IsaacWordRng; diff --git a/src/prng/mod.rs b/src/prng/mod.rs index c07f09b7994..b2a2f95763b 100644 --- a/src/prng/mod.rs +++ b/src/prng/mod.rs @@ -45,8 +45,12 @@ mod chacha; mod isaac; +mod isaac64; +mod isaac_word; mod xorshift; pub use self::chacha::ChaChaRng; -pub use self::isaac::{IsaacRng, Isaac64Rng, IsaacWordRng}; +pub use self::isaac::IsaacRng; +pub use self::isaac64::Isaac64Rng; +pub use self::isaac_word::IsaacWordRng; pub use self::xorshift::XorShiftRng; From 4747665d2281c6ef789875938ad18b000bd973e6 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Mon, 11 Sep 2017 21:01:04 +0200 Subject: [PATCH 110/247] Refactor isaac.rs This removes the unsafe blocks from the main isaac and isaac64 routines. Some macro's are replaced by functions. Over time the implementation of ISAAC and ISAAC-64 have diverged a bit, now they are as similar as possible (for easier comparison). The code is tested against the previous implementation, and the reference implementation. IsaacRng now does 34% better in the benchmark. --- src/prng/isaac.rs | 150 +++++++++++++++++------------------ src/prng/isaac64.rs | 187 +++++++++++++++++++++----------------------- 2 files changed, 161 insertions(+), 176 deletions(-) diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index 95aa8c75220..00343044eee 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -10,8 +10,6 @@ //! The ISAAC random number generator. -#![allow(non_camel_case_types)] - use core::slice; use core::iter::repeat; use core::num::Wrapping as w; @@ -19,12 +17,11 @@ use core::fmt; use {Rng, FromRng, SeedableRng}; -#[allow(bad_style)] +#[allow(non_camel_case_types)] type w32 = w; const RAND_SIZE_LEN: usize = 8; -const RAND_SIZE: u32 = 1 << RAND_SIZE_LEN; -const RAND_SIZE_USIZE: usize = 1 << RAND_SIZE_LEN; +const RAND_SIZE: usize = 1 << RAND_SIZE_LEN; /// A random number generator that uses the ISAAC algorithm[1]. /// @@ -37,18 +34,18 @@ const RAND_SIZE_USIZE: usize = 1 << RAND_SIZE_LEN; /// generator*](http://www.burtleburtle.net/bob/rand/isaacafa.html) #[derive(Copy)] pub struct IsaacRng { - cnt: u32, - rsl: [w32; RAND_SIZE_USIZE], - mem: [w32; RAND_SIZE_USIZE], + rsl: [w32; RAND_SIZE], + mem: [w32; RAND_SIZE], a: w32, b: w32, c: w32, + cnt: u32, } static EMPTY: IsaacRng = IsaacRng { cnt: 0, - rsl: [w(0); RAND_SIZE_USIZE], - mem: [w(0); RAND_SIZE_USIZE], + rsl: [w(0); RAND_SIZE], + mem: [w(0); RAND_SIZE], a: w(0), b: w(0), c: w(0), }; @@ -65,7 +62,7 @@ impl IsaacRng { /// of `rsl` as a seed, otherwise construct one algorithmically (not /// randomly). fn init(&mut self, use_rsl: bool) { - let mut a = w(0x9e3779b9); + let mut a = w(0x9e3779b9); // golden ratio let mut b = a; let mut c = a; let mut d = a; @@ -76,14 +73,14 @@ impl IsaacRng { macro_rules! mix { () => {{ - a=a^(b<<11); d=d+a; b=b+c; - b=b^(c>>2); e=e+b; c=c+d; - c=c^(d<<8); f=f+c; d=d+e; - d=d^(e>>16); g=g+d; e=e+f; - e=e^(f<<10); h=h+e; f=f+g; - f=f^(g>>4); a=a+f; g=g+h; - g=g^(h<<8); b=b+g; h=h+a; - h=h^(a>>9); c=c+h; a=a+b; + a ^= b << 11; d += a; b += c; + b ^= c >> 2; e += b; c += d; + c ^= d << 8; f += c; d += e; + d ^= e >> 16; g += d; e += f; + e ^= f << 10; h += e; f += g; + f ^= g >> 4; a += f; g += h; + g ^= h << 8; b += g; h += a; + h ^= a >> 9; c += h; a += b; }} } @@ -94,16 +91,16 @@ impl IsaacRng { if use_rsl { macro_rules! memloop { ($arr:expr) => {{ - for i in (0..RAND_SIZE_USIZE/8).map(|i| i * 8) { - a=a+$arr[i ]; b=b+$arr[i+1]; - c=c+$arr[i+2]; d=d+$arr[i+3]; - e=e+$arr[i+4]; f=f+$arr[i+5]; - g=g+$arr[i+6]; h=h+$arr[i+7]; + for i in (0..RAND_SIZE/8).map(|i| i * 8) { + a += $arr[i ]; b += $arr[i+1]; + c += $arr[i+2]; d += $arr[i+3]; + e += $arr[i+4]; f += $arr[i+5]; + g += $arr[i+6]; h += $arr[i+7]; mix!(); - self.mem[i ]=a; self.mem[i+1]=b; - self.mem[i+2]=c; self.mem[i+3]=d; - self.mem[i+4]=e; self.mem[i+5]=f; - self.mem[i+6]=g; self.mem[i+7]=h; + self.mem[i ] = a; self.mem[i+1] = b; + self.mem[i+2] = c; self.mem[i+3] = d; + self.mem[i+4] = e; self.mem[i+5] = f; + self.mem[i+6] = g; self.mem[i+7] = h; } }} } @@ -111,12 +108,12 @@ impl IsaacRng { memloop!(self.rsl); memloop!(self.mem); } else { - for i in (0..RAND_SIZE_USIZE/8).map(|i| i * 8) { + for i in (0..RAND_SIZE/8).map(|i| i * 8) { mix!(); - self.mem[i ]=a; self.mem[i+1]=b; - self.mem[i+2]=c; self.mem[i+3]=d; - self.mem[i+4]=e; self.mem[i+5]=f; - self.mem[i+6]=g; self.mem[i+7]=h; + self.mem[i ] = a; self.mem[i+1] = b; + self.mem[i+2] = c; self.mem[i+3] = d; + self.mem[i+4] = e; self.mem[i+5] = f; + self.mem[i+6] = g; self.mem[i+7] = h; } } @@ -124,63 +121,56 @@ impl IsaacRng { } /// Refills the output buffer (`self.rsl`) - #[inline] fn isaac(&mut self) { - self.c = self.c + w(1); + self.c += w(1); // abbreviations let mut a = self.a; let mut b = self.b + self.c; + const MIDPOINT: usize = RAND_SIZE / 2; - const MIDPOINT: usize = RAND_SIZE_USIZE / 2; - - macro_rules! ind { - ($x:expr) => ( self.mem[($x >> 2usize).0 as usize & (RAND_SIZE_USIZE - 1)] ) + #[inline(always)] + fn ind(mem:&[w32; RAND_SIZE], v: w32, amount: usize) -> w32 { + let index = (v >> amount).0 as usize % RAND_SIZE; + mem[index] } - let r = [(0, MIDPOINT), (MIDPOINT, 0)]; - for &(mr_offset, m2_offset) in r.iter() { - - macro_rules! rngstepp { - ($j:expr, $shift:expr) => {{ - let base = $j; - let mix = a << $shift; - - let x = self.mem[base + mr_offset]; - a = (a ^ mix) + self.mem[base + m2_offset]; - let y = ind!(x) + a + b; - self.mem[base + mr_offset] = y; - - b = ind!(y >> RAND_SIZE_LEN) + x; - self.rsl[base + mr_offset] = b; - }} - } - - macro_rules! rngstepn { - ($j:expr, $shift:expr) => {{ - let base = $j; - let mix = a >> $shift; - - let x = self.mem[base + mr_offset]; - a = (a ^ mix) + self.mem[base + m2_offset]; - let y = ind!(x) + a + b; - self.mem[base + mr_offset] = y; + #[inline(always)] + fn rngstep(ctx: &mut IsaacRng, + mix: w32, + a: &mut w32, + b: &mut w32, + base: usize, + m: usize, + m2: usize) { + let x = ctx.mem[base + m]; + *a = mix + ctx.mem[base + m2]; + let y = *a + *b + ind(&ctx.mem, x, 2); + ctx.mem[base + m] = y; + *b = x + ind(&ctx.mem, y, 2 + RAND_SIZE_LEN); + ctx.rsl[base + m] = *b; + } - b = ind!(y >> RAND_SIZE_LEN) + x; - self.rsl[base + mr_offset] = b; - }} - } + let mut m = 0; + let mut m2 = MIDPOINT; + for i in (0..MIDPOINT/4).map(|i| i * 4) { + rngstep(self, a ^ (a << 13), &mut a, &mut b, i + 0, m, m2); + rngstep(self, a ^ (a >> 6 ), &mut a, &mut b, i + 1, m, m2); + rngstep(self, a ^ (a << 2 ), &mut a, &mut b, i + 2, m, m2); + rngstep(self, a ^ (a >> 16), &mut a, &mut b, i + 3, m, m2); + } - for i in (0..MIDPOINT/4).map(|i| i * 4) { - rngstepp!(i + 0, 13); - rngstepn!(i + 1, 6); - rngstepp!(i + 2, 2); - rngstepn!(i + 3, 16); - } + m = MIDPOINT; + m2 = 0; + for i in (0..MIDPOINT/4).map(|i| i * 4) { + rngstep(self, a ^ (a << 13), &mut a, &mut b, i + 0, m, m2); + rngstep(self, a ^ (a >> 6 ), &mut a, &mut b, i + 1, m, m2); + rngstep(self, a ^ (a << 2 ), &mut a, &mut b, i + 2, m, m2); + rngstep(self, a ^ (a >> 16), &mut a, &mut b, i + 3, m, m2); } self.a = a; self.b = b; - self.cnt = RAND_SIZE; + self.cnt = RAND_SIZE as u32; } } @@ -206,12 +196,12 @@ impl Rng for IsaacRng { // misrefactors, so we check, sometimes. // // (Changes here should be reflected in Isaac64Rng.next_u64.) - debug_assert!(self.cnt < RAND_SIZE); + debug_assert!((self.cnt as usize) < RAND_SIZE); // (the % is cheaply telling the optimiser that we're always // in bounds, without unsafe. NB. this is a power of two, so // it optimises to a bitwise mask). - self.rsl[(self.cnt % RAND_SIZE) as usize].0 + self.rsl[self.cnt as usize % RAND_SIZE].0 } } @@ -221,7 +211,7 @@ impl FromRng for IsaacRng { unsafe { let ptr = ret.rsl.as_mut_ptr() as *mut u8; - let slice = slice::from_raw_parts_mut(ptr, RAND_SIZE_USIZE * 4); + let slice = slice::from_raw_parts_mut(ptr, RAND_SIZE * 4); other.fill_bytes(slice); } ret.cnt = 0; diff --git a/src/prng/isaac64.rs b/src/prng/isaac64.rs index 66bcd5e8a6f..b4ba3fe2b05 100644 --- a/src/prng/isaac64.rs +++ b/src/prng/isaac64.rs @@ -10,8 +10,6 @@ //! The ISAAC random number generator. -#![allow(non_camel_case_types)] - use core::slice; use core::iter::repeat; use core::num::Wrapping as w; @@ -19,11 +17,11 @@ use core::fmt; use {Rng, FromRng, SeedableRng}; -#[allow(bad_style)] +#[allow(non_camel_case_types)] type w64 = w; -const RAND_SIZE_64_LEN: usize = 8; -const RAND_SIZE_64: usize = 1 << RAND_SIZE_64_LEN; +const RAND_SIZE_LEN: usize = 8; +const RAND_SIZE: usize = 1 << RAND_SIZE_LEN; /// A random number generator that uses ISAAC-64[1], the 64-bit /// variant of the ISAAC algorithm. @@ -37,18 +35,18 @@ const RAND_SIZE_64: usize = 1 << RAND_SIZE_64_LEN; /// generator*](http://www.burtleburtle.net/bob/rand/isaacafa.html) #[derive(Copy)] pub struct Isaac64Rng { - cnt: usize, - rsl: [w64; RAND_SIZE_64], - mem: [w64; RAND_SIZE_64], + rsl: [w64; RAND_SIZE], + mem: [w64; RAND_SIZE], a: w64, b: w64, c: w64, + cnt: u32, } static EMPTY_64: Isaac64Rng = Isaac64Rng { cnt: 0, - rsl: [w(0); RAND_SIZE_64], - mem: [w(0); RAND_SIZE_64], + rsl: [w(0); RAND_SIZE], + mem: [w(0); RAND_SIZE], a: w(0), b: w(0), c: w(0), }; @@ -65,24 +63,25 @@ impl Isaac64Rng { /// of `rsl` as a seed, otherwise construct one algorithmically (not /// randomly). fn init(&mut self, use_rsl: bool) { - macro_rules! init { - ($var:ident) => ( - let mut $var = w(0x9e3779b97f4a7c13); - ) - } - init!(a); init!(b); init!(c); init!(d); - init!(e); init!(f); init!(g); init!(h); + let mut a = w(0x9e3779b97f4a7c13); // golden ratio + let mut b = a; + let mut c = a; + let mut d = a; + let mut e = a; + let mut f = a; + let mut g = a; + let mut h = a; macro_rules! mix { () => {{ - a=a-e; f=f^(h>>9); h=h+a; - b=b-f; g=g^(a<<9); a=a+b; - c=c-g; h=h^(b>>23); b=b+c; - d=d-h; a=a^(c<<15); c=c+d; - e=e-a; b=b^(d>>14); d=d+e; - f=f-b; c=c^(e<<20); e=e+f; - g=g-c; d=d^(f>>17); f=f+g; - h=h-d; e=e^(g<<14); g=g+h; + a -= e; f ^= h >> 9; h += a; + b -= f; g ^= a << 9; a += b; + c -= g; h ^= b >> 23; b += c; + d -= h; a ^= c << 15; c += d; + e -= a; b ^= d >> 14; d += e; + f -= b; c ^= e << 20; e += f; + g -= c; d ^= f >> 17; f += g; + h -= d; e ^= g << 14; g += h; }} } @@ -93,16 +92,16 @@ impl Isaac64Rng { if use_rsl { macro_rules! memloop { ($arr:expr) => {{ - for i in (0..RAND_SIZE_64 / 8).map(|i| i * 8) { - a=a+$arr[i ]; b=b+$arr[i+1]; - c=c+$arr[i+2]; d=d+$arr[i+3]; - e=e+$arr[i+4]; f=f+$arr[i+5]; - g=g+$arr[i+6]; h=h+$arr[i+7]; + for i in (0..RAND_SIZE/8).map(|i| i * 8) { + a += $arr[i ]; b += $arr[i+1]; + c += $arr[i+2]; d += $arr[i+3]; + e += $arr[i+4]; f += $arr[i+5]; + g += $arr[i+6]; h += $arr[i+7]; mix!(); - self.mem[i ]=a; self.mem[i+1]=b; - self.mem[i+2]=c; self.mem[i+3]=d; - self.mem[i+4]=e; self.mem[i+5]=f; - self.mem[i+6]=g; self.mem[i+7]=h; + self.mem[i ] = a; self.mem[i+1] = b; + self.mem[i+2] = c; self.mem[i+3] = d; + self.mem[i+4] = e; self.mem[i+5] = f; + self.mem[i+6] = g; self.mem[i+7] = h; } }} } @@ -110,12 +109,12 @@ impl Isaac64Rng { memloop!(self.rsl); memloop!(self.mem); } else { - for i in (0..RAND_SIZE_64 / 8).map(|i| i * 8) { + for i in (0..RAND_SIZE/8).map(|i| i * 8) { mix!(); - self.mem[i ]=a; self.mem[i+1]=b; - self.mem[i+2]=c; self.mem[i+3]=d; - self.mem[i+4]=e; self.mem[i+5]=f; - self.mem[i+6]=g; self.mem[i+7]=h; + self.mem[i ] = a; self.mem[i+1] = b; + self.mem[i+2] = c; self.mem[i+3] = d; + self.mem[i+4] = e; self.mem[i+5] = f; + self.mem[i+6] = g; self.mem[i+7] = h; } } @@ -124,67 +123,55 @@ impl Isaac64Rng { /// Refills the output buffer (`self.rsl`) fn isaac64(&mut self) { - self.c = self.c + w(1); + self.c += w(1); // abbreviations let mut a = self.a; let mut b = self.b + self.c; - const MIDPOINT: usize = RAND_SIZE_64 / 2; - const MP_VEC: [(usize, usize); 2] = [(0,MIDPOINT), (MIDPOINT, 0)]; - macro_rules! ind { - ($x:expr) => { - *self.mem.get_unchecked((($x >> 3usize).0 as usize) & (RAND_SIZE_64 - 1)) - } + const MIDPOINT: usize = RAND_SIZE / 2; + + #[inline(always)] + fn ind(mem:&[w64; RAND_SIZE], v: w64, amount: usize) -> w64 { + let index = (v >> amount).0 as usize % RAND_SIZE; + mem[index] } - for &(mr_offset, m2_offset) in MP_VEC.iter() { - for base in (0..MIDPOINT / 4).map(|i| i * 4) { - - macro_rules! rngstepp { - ($j:expr, $shift:expr) => {{ - let base = base + $j; - let mix = a ^ (a << $shift); - let mix = if $j == 0 {!mix} else {mix}; - - unsafe { - let x = *self.mem.get_unchecked(base + mr_offset); - a = mix + *self.mem.get_unchecked(base + m2_offset); - let y = ind!(x) + a + b; - *self.mem.get_unchecked_mut(base + mr_offset) = y; - - b = ind!(y >> RAND_SIZE_64_LEN) + x; - *self.rsl.get_unchecked_mut(base + mr_offset) = b; - } - }} - } - - macro_rules! rngstepn { - ($j:expr, $shift:expr) => {{ - let base = base + $j; - let mix = a ^ (a >> $shift); - let mix = if $j == 0 {!mix} else {mix}; - - unsafe { - let x = *self.mem.get_unchecked(base + mr_offset); - a = mix + *self.mem.get_unchecked(base + m2_offset); - let y = ind!(x) + a + b; - *self.mem.get_unchecked_mut(base + mr_offset) = y; - - b = ind!(y >> RAND_SIZE_64_LEN) + x; - *self.rsl.get_unchecked_mut(base + mr_offset) = b; - } - }} - } - - rngstepp!(0, 21); - rngstepn!(1, 5); - rngstepp!(2, 12); - rngstepn!(3, 33); - } + #[inline(always)] + fn rngstep(ctx: &mut Isaac64Rng, + mix: w64, + a: &mut w64, + b: &mut w64, + base: usize, + m: usize, + m2: usize) { + let x = ctx.mem[base + m]; + *a = mix + ctx.mem[base + m2]; + let y = *a + *b + ind(&ctx.mem, x, 3); + ctx.mem[base + m] = y; + *b = x + ind(&ctx.mem, y, 3 + RAND_SIZE_LEN); + ctx.rsl[base + m] = *b; + } + + let mut m = 0; + let mut m2 = MIDPOINT; + for i in (0..MIDPOINT/4).map(|i| i * 4) { + rngstep(self, !(a ^ (a << 21)), &mut a, &mut b, i + 0, m, m2); + rngstep(self, a ^ (a >> 5 ), &mut a, &mut b, i + 1, m, m2); + rngstep(self, a ^ (a << 12), &mut a, &mut b, i + 2, m, m2); + rngstep(self, a ^ (a >> 33), &mut a, &mut b, i + 3, m, m2); + } + + m = MIDPOINT; + m2 = 0; + for i in (0..MIDPOINT/4).map(|i| i * 4) { + rngstep(self, !(a ^ (a << 21)), &mut a, &mut b, i + 0, m, m2); + rngstep(self, a ^ (a >> 5 ), &mut a, &mut b, i + 1, m, m2); + rngstep(self, a ^ (a << 12), &mut a, &mut b, i + 2, m, m2); + rngstep(self, a ^ (a >> 33), &mut a, &mut b, i + 3, m, m2); } self.a = a; self.b = b; - self.cnt = RAND_SIZE_64; + self.cnt = RAND_SIZE as u32; } } @@ -209,10 +196,18 @@ impl Rng for Isaac64Rng { } self.cnt -= 1; - // See corresponding location in IsaacRng.next_u32 for - // explanation. - debug_assert!(self.cnt < RAND_SIZE_64); - self.rsl[(self.cnt % RAND_SIZE_64) as usize].0 + // self.cnt is at most RAND_SIZE, but that is before the + // subtraction above. We want to index without bounds + // checking, but this could lead to incorrect code if someone + // misrefactors, so we check, sometimes. + // + // (Changes here should be reflected in IsaacRng.next_u32.) + debug_assert!((self.cnt as usize) < RAND_SIZE); + + // (the % is cheaply telling the optimiser that we're always + // in bounds, without unsafe. NB. this is a power of two, so + // it optimises to a bitwise mask). + self.rsl[self.cnt as usize % RAND_SIZE].0 } } @@ -222,7 +217,7 @@ impl FromRng for Isaac64Rng { unsafe { let ptr = ret.rsl.as_mut_ptr() as *mut u8; - let slice = slice::from_raw_parts_mut(ptr, RAND_SIZE_64 * 8); + let slice = slice::from_raw_parts_mut(ptr, RAND_SIZE * 8); other.fill_bytes(slice); } ret.cnt = 0; From 90c094d52b3949b6f4cc259fedc965ea2ca6dd89 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 12 Sep 2017 11:41:12 +0100 Subject: [PATCH 111/247] Remove set_thread_rng/set_new_thread_rng; revert thread_rng to reseeding StdRng --- src/lib.rs | 27 ++------ src/reseeding.rs | 6 +- src/thread_local.rs | 155 +++++++------------------------------------- 3 files changed, 37 insertions(+), 151 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index b490c325d73..d898c892ada 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -263,8 +263,7 @@ pub use os::OsRng; pub use iter::iter; pub use distributions::{Distribution, Default, Rand}; #[cfg(feature="std")] -pub use thread_local::{ThreadRng, thread_rng, set_thread_rng, set_new_thread_rng, - random, random_with}; +pub use thread_local::{ThreadRng, thread_rng, random, random_with}; use prng::IsaacWordRng; @@ -435,24 +434,6 @@ pub struct StdRng { rng: IsaacWordRng, } -#[cfg(feature="std")] -impl StdRng { - /// Create a randomly seeded instance of `StdRng`. - /// - /// This is a very expensive operation as it has to read - /// randomness from the operating system and use this in an - /// expensive seeding operation. If one is only generating a small - /// number of random numbers, or doesn't need the utmost speed for - /// generating each number, `thread_rng` and/or `random` may be more - /// appropriate. - /// - /// Reading the randomness from the OS may fail, and any error is - /// propagated via the `io::Result` return value. - pub fn new() -> Result { - IsaacWordRng::new().map(|rng| StdRng { rng }) - } -} - impl Rng for StdRng { #[inline] fn next_u32(&mut self) -> u32 { @@ -465,6 +446,12 @@ impl Rng for StdRng { } } +impl SeedFromRng for StdRng { + fn from_rng(other: &mut R) -> Result { + IsaacWordRng::from_rng(other).map(|rng| StdRng{ rng }) + } +} + #[cfg(test)] mod test { diff --git a/src/reseeding.rs b/src/reseeding.rs index 29ce741e533..6e5c2ee343c 100644 --- a/src/reseeding.rs +++ b/src/reseeding.rs @@ -13,7 +13,9 @@ use core::fmt::Debug; -use {Rng, SeedableRng, NewSeeded, Result}; +use {Rng, SeedableRng, Result}; +#[cfg(feature="std")] +use NewSeeded; /// How many bytes of entropy the underling RNG is allowed to generate /// before it is reseeded @@ -113,9 +115,11 @@ pub trait Reseeder: Debug { } /// Reseed an RNG using `NewSeeded` to replace the current instance. +#[cfg(feature="std")] #[derive(Clone, Copy, Debug)] pub struct ReseedWithNew; +#[cfg(feature="std")] impl Reseeder for ReseedWithNew { fn reseed(&mut self, rng: &mut R) { match R::new() { diff --git a/src/thread_local.rs b/src/thread_local.rs index b309fd7c66e..d2a77890bf5 100644 --- a/src/thread_local.rs +++ b/src/thread_local.rs @@ -12,41 +12,19 @@ use std::cell::RefCell; use std::rc::Rc; -use std::sync::Mutex; -use {Rng, OsRng, Rand, Default, Result}; +use {Rng, StdRng, NewSeeded, Rand, Default, Result}; -// use reseeding::{Reseeder, ReseedingRng}; -// -// /// Controls how the thread-local RNG is reseeded. -// #[derive(Debug)] -// struct ThreadRngReseeder; -// -// impl Reseeder for ThreadRngReseeder { -// fn reseed(&mut self, rng: &mut StdRng) { -// *rng = match StdRng::new() { -// Ok(r) => r, -// Err(e) => panic!("could not reseed thread_rng: {}", e) -// } -// } -// } -// -// const THREAD_RNG_RESEED_THRESHOLD: u64 = 32_768; -// type ReseedingStdRng = ReseedingRng; -// thread_local!(static THREAD_RNG_KEY: Rc>>> = { -// let r = match StdRng::new() { -// Ok(r) => r, -// Err(e) => panic!("could not initialize thread_rng: {}", e) -// }; -// let rng = ReseedingRng::new(r, -// THREAD_RNG_RESEED_THRESHOLD, -// ThreadRngReseeder); +use reseeding::{ReseedingRng, ReseedWithNew}; + +const THREAD_RNG_RESEED_THRESHOLD: u64 = 32_768; +type ReseedingStdRng = ReseedingRng; /// The thread-local RNG. #[derive(Clone)] #[allow(missing_debug_implementations)] pub struct ThreadRng { - rng: Rc>>, + rng: Rc>, } impl Rng for ThreadRng { @@ -57,27 +35,26 @@ impl Rng for ThreadRng { fn next_u64(&mut self) -> u64 { self.rng.borrow_mut().next_u64() } - - #[inline] + + #[cfg(feature = "i128_support")] + fn next_u128(&mut self) -> u128 { + self.rng.borrow_mut().next_u128() + } + fn try_fill(&mut self, bytes: &mut [u8]) -> Result<()> { self.rng.borrow_mut().try_fill(bytes) } } thread_local!( - static THREAD_RNG_CREATOR: Mutex Box>>> = { - // TODO: should this panic? - Mutex::new(RefCell::new(Box::new(|| Box::new(OsRng::new().unwrap())))) - } -); - -thread_local!( - static THREAD_RNG_KEY: Rc>> = { - let rng = THREAD_RNG_CREATOR.with(|f| { - let creator = f.lock().unwrap(); - let rng = (creator.borrow())(); - rng - }); + static THREAD_RNG_KEY: Rc> = { + let r = match StdRng::new() { + Ok(r) => r, + Err(e) => panic!("could not initialize thread_rng: {:?}", e) + }; + let rng = ReseedingRng::new(r, + THREAD_RNG_RESEED_THRESHOLD, + ReseedWithNew); Rc::new(RefCell::new(rng)) } ); @@ -87,75 +64,15 @@ thread_local!( /// `random_with` to generate new values, and may be used directly with other /// distributions: `Range::new(0, 10).sample(&mut thread_rng())`. /// -/// By default, this uses `OsRng` which pulls random numbers directly from the -/// operating system. This is intended to be a good secure default; for more -/// see `set_thread_rng` and `set_new_thread_rng`. -/// -/// This is intended to provide securely generated random numbers, but this -/// behaviour can be changed by `set_thread_rng` and `set_new_thread_rng`. -/// -/// `thread_rng` is not especially fast, since it uses dynamic dispatch and -/// `OsRng`, which requires a system call on each usage. Users wanting a fast -/// generator for games or simulations may prefer not to use `thread_rng` at -/// all, especially if reproducibility of results is important. +/// Uses `StdRng` internally, set to reseed from `OsRng` periodically. +/// This should provide a reasonable compromise between speed and security; +/// while the generator is not approved for crytographic usage its output should +/// be hard to guess, and performance should be similar to non-cryptographic +/// generators. pub fn thread_rng() -> ThreadRng { ThreadRng { rng: THREAD_RNG_KEY.with(|t| t.clone()) } } -/// Set the thread-local RNG used by `thread_rng`. Only affects the current -/// thread. See also `set_new_thread_rng`. -/// -/// If this is never called in a thread and `set_new_thread_rng` is never called -/// globally, the first call to `thread_rng()` in the thread will create a new -/// `OsRng` and use that as a source of numbers. -/// This method allows a different generator to be set, and can be called at -/// any time to set a new generator. -/// -/// Calling this affects all users of `thread_rng`, `random` and `random_with` -/// in the current thread, including libraries. Users should ensure the -/// generator used is secure enough for all users of `thread_rng`, including -/// other libraries. -/// -/// This may have some use in testing, by spawning a new thread, overriding the -/// generator with known values (a constant or a PRNG with known seed), then -/// asserting the exact output of "randomly" generated values. The use of a new -/// thread avoids affecting other users of `thread_rng`. -/// -/// ```rust -/// use rand::{ConstRng, random, set_thread_rng}; -/// use std::thread; -/// -/// thread::spawn(move || { -/// set_thread_rng(Box::new(ConstRng::new(123u32))); -/// assert_eq!(random::(), 123u32); -/// }); -/// ``` -pub fn set_thread_rng(rng: Box) { - THREAD_RNG_KEY.with(|t| *t.borrow_mut() = rng); -} - -/// Control how thread-local generators are created for new threads. -/// -/// The first time `thread_rng()` is called in each thread, this closure is -/// used to create a new generator. If this closure has not been set, the -/// built-in closure returning a new boxed `OsRng` is used. -/// -/// Calling this affects all users of `thread_rng`, `random` and `random_with` -/// in all new threads (as well as existing threads which haven't called -/// `thread_rng` yet), including libraries. Users should ensure the generator -/// used is secure enough for all users of `thread_rng`, including other -/// libraries. -/// -/// This closure should not panic; doing so would kill whichever thread is -/// calling `thread_rng` at the time, poison its mutex, and cause all future -/// threads initialising a `thread_rng` (or calling this function) to panic. -pub fn set_new_thread_rng(creator: Box Box>) { - THREAD_RNG_CREATOR.with(|f| { - let p = f.lock().unwrap(); - *p.borrow_mut() = creator; - }); -} - /// Generates a random value using the thread-local random number generator. /// /// `random()` can generate various types of random things, and so may require @@ -236,25 +153,3 @@ pub fn random>() -> T { pub fn random_with>(distribution: D) -> T { T::rand(&mut thread_rng(), distribution) } - -#[cfg(test)] -mod test { - use std::thread; - use {set_thread_rng, random, ConstRng}; - - #[test] - fn test_set_thread_rng() { - let v = 12u64; - thread::spawn(move || { - random::(); - // affect this thread only: - set_thread_rng(Box::new(ConstRng::new(v))); - assert_eq!(random::(), v); - assert_eq!(random::(), v); - }); - - // The chance of 128 bits randomly equalling our value is miniscule: - let (x, y): (u64, u64) = (random(), random()); - assert!((x, y) != (v, v)); - } -} From 0ee54b1b96e744e33104e96a75a75c04c4091e6f Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 12 Sep 2017 11:43:38 +0100 Subject: [PATCH 112/247] Add MockAddRng to rand_core; remove all other mock RNGs --- rand_core/src/lib.rs | 2 ++ rand_core/src/mock.rs | 75 +++++++++++++++++++++++++++++++++++++++ src/lib.rs | 36 ++----------------- src/reseeding.rs | 46 ++++++++---------------- src/sequences/weighted.rs | 20 ++--------- 5 files changed, 97 insertions(+), 82 deletions(-) create mode 100644 rand_core/src/mock.rs diff --git a/rand_core/src/lib.rs b/rand_core/src/lib.rs index fdb6a4a2ba9..afa681641b3 100644 --- a/rand_core/src/lib.rs +++ b/rand_core/src/lib.rs @@ -35,6 +35,8 @@ #[cfg(feature="std")] extern crate core; +pub mod mock; + /// A random number generator. /// diff --git a/rand_core/src/mock.rs b/rand_core/src/mock.rs new file mode 100644 index 00000000000..a4fead00e4a --- /dev/null +++ b/rand_core/src/mock.rs @@ -0,0 +1,75 @@ +// Copyright 2013-2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Mock RNG implementations +//! +//! TODO: should this be behind a feature flag? Problem is that Cargo won't +//! simply use a dependency with a feature flag for tests and without for normal +//! build (i.e. `rand` couldn't use the mock feature only for tests). +//! Instead maybe this should be yet another crate? Or just leave it here? + +use core::num::Wrapping as w; +use {Rng, SeedableRng}; + +/// A simple implementation of `Rng`, purely for testing. +/// Returns an arithmetic sequence (i.e. adds a constant each step). +/// +/// ```rust +/// use rand_core::Rng; +/// use rand_core::mock::MockAddRng; +/// +/// let mut my_rng = MockAddRng::new(2u32, 1u32); +/// assert_eq!(my_rng.next_u32(), 2u32); +/// assert_eq!(my_rng.next_u64(), (4u64 << 32) + 3u64); +/// ``` +#[derive(Debug)] +pub struct MockAddRng { + v: w, + a: w, +} + +impl MockAddRng { + /// Create a `MockAddRng`, yielding an arithmetic sequence starting with + /// `v` and incremented by `a` each time. + pub fn new(v: T, a: T) -> Self { + MockAddRng { v: w(v), a: w(a) } + } +} + +impl Rng for MockAddRng { + fn next_u32(&mut self) -> u32 { + let result = self.v.0; + self.v += self.a; + result + } +} + +impl Rng for MockAddRng { + fn next_u32(&mut self) -> u32 { + self.next_u64() as u32 + } + fn next_u64(&mut self) -> u64 { + let result = self.v.0; + self.v += self.a; + result + } +} + +impl SeedableRng for MockAddRng where + MockAddRng: Rng, + T: From, // for 1.into() +{ + fn reseed(&mut self, seed: T) { + self.v = w(seed); + } + fn from_seed(seed: T) -> Self { + MockAddRng::new(seed, 1.into()) + } +} diff --git a/src/lib.rs b/src/lib.rs index d898c892ada..91c829ae0a1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -392,37 +392,6 @@ impl Sample for R { } } -/// A very simple implementation of `Rng`, purely for testing. Returns the same -/// value each time. -/// -/// ```rust -/// use rand::{ConstRng, Rng}; -/// -/// let mut my_rng = ConstRng::new(2u32); -/// assert_eq!(my_rng.next_u32(), 2u32); -/// assert_eq!(my_rng.next_u64(), (2u64 << 32) + 2u64); -/// ``` -#[derive(Debug)] -pub struct ConstRng { - v: T -} - -impl ConstRng { - /// Create a `ConstRng`, yielding the given value - pub fn new(v: T) -> Self { - ConstRng { v } - } -} - -impl Rng for ConstRng { - fn next_u32(&mut self) -> u32 { self.v } -} - -impl Rng for ConstRng { - fn next_u32(&mut self) -> u32 { self.v as u32 } - fn next_u64(&mut self) -> u64 { self.v } -} - /// The standard RNG. This is designed to be efficient on the current /// platform. /// @@ -455,7 +424,8 @@ impl SeedFromRng for StdRng { #[cfg(test)] mod test { - use {Rng, thread_rng, ConstRng, Sample}; + use {Rng, thread_rng, Sample}; + use rand_core::mock::MockAddRng; use distributions::{uniform}; use distributions::{Uniform, Range, Exp}; use sequences::Shuffle; @@ -496,7 +466,7 @@ mod test { #[test] fn test_fill_bytes_default() { - let mut r = ConstRng::new(0x11_22_33_44_55_66_77_88u64); + let mut r = MockAddRng::new(0x11_22_33_44_55_66_77_88u64, 0); // check every remainder mod 8, both in small and big vectors. let lengths = [0, 1, 2, 3, 4, 5, 6, 7, diff --git a/src/reseeding.rs b/src/reseeding.rs index 6e5c2ee343c..2d38088024b 100644 --- a/src/reseeding.rs +++ b/src/reseeding.rs @@ -133,40 +133,24 @@ impl Reseeder for ReseedWithNew { #[cfg(test)] mod test { use std::iter::repeat; - use {SeedableRng, Rng, iter, NewSeeded, Result}; + use rand_core::mock::MockAddRng; + use {SeedableRng, Rng, iter}; use distributions::ascii_word_char; - use super::{ReseedingRng, ReseedWithNew}; - + use super::{ReseedingRng, Reseeder}; + #[derive(Debug)] - struct Counter { - i: u32 - } - - impl Rng for Counter { - fn next_u32(&mut self) -> u32 { - self.i += 1; - // very random - self.i - 1 - } - } - impl NewSeeded for Counter { - fn new() -> Result { - Ok(Counter { i: 0 }) + struct ReseedMock; + impl Reseeder> for ReseedMock { + fn reseed(&mut self, rng: &mut MockAddRng) { + *rng = MockAddRng::new(0, 1); } } - impl SeedableRng for Counter { - fn reseed(&mut self, seed: u32) { - self.i = seed; - } - fn from_seed(seed: u32) -> Counter { - Counter { i: seed } - } - } - type MyRng = ReseedingRng; + + type MyRng = ReseedingRng, ReseedMock>; #[test] fn test_reseeding() { - let mut rs = ReseedingRng::new(Counter {i:0}, 400, ReseedWithNew); + let mut rs = ReseedingRng::new(MockAddRng::new(0, 1), 400, ReseedMock); let mut i = 0; for _ in 0..1000 { @@ -177,18 +161,18 @@ mod test { #[test] fn test_rng_seeded() { - let mut ra: MyRng = SeedableRng::from_seed((ReseedWithNew, 2)); - let mut rb: MyRng = SeedableRng::from_seed((ReseedWithNew, 2)); + let mut ra: MyRng = SeedableRng::from_seed((ReseedMock, 2)); + let mut rb: MyRng = SeedableRng::from_seed((ReseedMock, 2)); assert!(::test::iter_eq(iter(&mut ra).map(|rng| ascii_word_char(rng)).take(100), iter(&mut rb).map(|rng| ascii_word_char(rng)).take(100))); } #[test] fn test_rng_reseed() { - let mut r: MyRng = SeedableRng::from_seed((ReseedWithNew, 3)); + let mut r: MyRng = SeedableRng::from_seed((ReseedMock, 3)); let string1: String = iter(&mut r).map(|rng| ascii_word_char(rng)).take(100).collect(); - r.reseed((ReseedWithNew, 3)); + r.reseed((ReseedMock, 3)); let string2: String = iter(&mut r).map(|rng| ascii_word_char(rng)).take(100).collect(); assert_eq!(string1, string2); diff --git a/src/sequences/weighted.rs b/src/sequences/weighted.rs index 395bbb19bcc..827c188e754 100644 --- a/src/sequences/weighted.rs +++ b/src/sequences/weighted.rs @@ -133,26 +133,10 @@ impl Distribution for WeightedChoice { #[cfg(test)] mod tests { - use Rng; + use rand_core::mock::MockAddRng; use distributions::Distribution; use super::{WeightedChoice, Weighted}; - #[derive(PartialEq, Debug)] - struct ConstRand(usize); - - // 0, 1, 2, 3, ... - #[derive(Debug)] - struct CountingRng { i: u32 } - impl Rng for CountingRng { - fn next_u32(&mut self) -> u32 { - self.i += 1; - self.i - 1 - } - fn next_u64(&mut self) -> u64 { - self.next_u32() as u64 - } - } - #[test] fn test_weighted_choice() { // this makes assumptions about the internal implementation of @@ -166,7 +150,7 @@ mod tests { let wc = WeightedChoice::new(items); let expected = $expected; - let mut rng = CountingRng { i: 0 }; + let mut rng = MockAddRng::new(0u32, 1); for &val in expected.iter() { assert_eq!(wc.sample(&mut rng), val) From dd1241a256f2e22aa7d746f98fcedb2f91992aba Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 12 Sep 2017 11:56:46 +0100 Subject: [PATCH 113/247] Add rand_core::impls module; remove all default impls of Rng functions This is a controvesial change. The argument *for* is that it prevents RNG wrappers from accidentally implementing methods incorrectly via the default impls (since it is valid to throw away bits, e.g. "self.next_u64() as u32", this affects output, not just performance). The argument *against* is that it complicates Rng implementations. --- rand_core/src/impls.rs | 78 ++++++++++++++++++++++++++++++++++++++++++ rand_core/src/lib.rs | 53 ++++------------------------ rand_core/src/mock.rs | 21 +++++++++++- src/lib.rs | 27 ++++++++++----- src/os.rs | 6 ++++ src/prng/chacha.rs | 8 +++++ src/prng/isaac.rs | 37 +++++++++----------- src/prng/xorshift.rs | 12 +++++++ 8 files changed, 166 insertions(+), 76 deletions(-) create mode 100644 rand_core/src/impls.rs diff --git a/rand_core/src/impls.rs b/rand_core/src/impls.rs new file mode 100644 index 00000000000..fb45a3ecee2 --- /dev/null +++ b/rand_core/src/impls.rs @@ -0,0 +1,78 @@ +// Copyright 2013-2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Helper functions for implementing `Rng` functions. +//! +//! For cross-platform reproducibility, these functions all use Little Endian: +//! least-significant part first. For example, `next_u64_via_u32` takes `u32` +//! values `x, y`, then outputs `(y << 32) | x`. To implement `next_u32` +//! from `next_u64` in little-endian order, one should use `next_u64() as u32`. +//! +//! Byte-swapping (like the std `to_le` functions) is only needed to convert +//! to/from byte sequences, and since its purpose is reproducibility, +//! non-reproducible sources (e.g. `OsRng`) need not bother with it. + +use core::intrinsics::transmute; +use {Rng, Result}; + +/// Implement `next_u64` via `next_u32`, little-endian order. +pub fn next_u64_via_u32(rng: &mut R) -> u64 { + // Use LE; we explicitly generate one value before the next. + let x = rng.next_u32() as u64; + let y = rng.next_u32() as u64; + (y << 32) | x +} + +/// Implement `next_u128` via `next_u64`, little-endian order. +#[cfg(feature = "i128_support")] +pub fn next_u128_via_u64(rng: &mut R) -> u128 { + // Use LE; we explicitly generate one value before the next. + let x = rng.next_u64() as u128; + let y = rng.next_u64() as u128; + (y << 64) | x +} + +macro_rules! try_fill_via { + ($rng:ident, $next_u:ident, $BYTES:expr, $dest:ident) => {{ + let mut left = $dest; + while left.len() >= $BYTES { + let (l, r) = {left}.split_at_mut($BYTES); + left = r; + let chunk: [u8; $BYTES] = unsafe { + transmute($rng.$next_u().to_le()) + }; + l.copy_from_slice(&chunk); + } + let n = left.len(); + if n > 0 { + let chunk: [u8; $BYTES] = unsafe { + transmute($rng.$next_u().to_le()) + }; + left.copy_from_slice(&chunk[..n]); + } + Ok(()) + }} +} + +/// Implement `try_fill` via `next_u32`, little-endian order. +pub fn try_fill_via_u32(rng: &mut R, dest: &mut [u8]) -> Result<()> { + try_fill_via!(rng, next_u32, 4, dest) +} + +/// Implement `try_fill` via `next_u64`, little-endian order. +pub fn try_fill_via_u64(rng: &mut R, dest: &mut [u8]) -> Result<()> { + try_fill_via!(rng, next_u64, 8, dest) +} + +/// Implement `try_fill` via `next_u128`, little-endian order. +#[cfg(feature = "i128_support")] +pub fn try_fill_via_u128(rng: &mut R, dest: &mut [u8]) -> Result<()> { + try_fill_via!(rng, next_u128, 16, dest) +} diff --git a/rand_core/src/lib.rs b/rand_core/src/lib.rs index afa681641b3..7094b8ebe5e 100644 --- a/rand_core/src/lib.rs +++ b/rand_core/src/lib.rs @@ -35,6 +35,7 @@ #[cfg(feature="std")] extern crate core; +pub mod impls; pub mod mock; @@ -75,63 +76,21 @@ pub trait Rng { fn next_u32(&mut self) -> u32; /// Return the next random u64. - /// - /// By default this is implemented in terms of `next_u32` in LE order. An - /// implementation of this trait must provide at least one of - /// these two methods. Similarly to `next_u32`, this rarely needs - /// to be called directly, prefer `r.gen()` to `r.next_u64()`. - fn next_u64(&mut self) -> u64 { - // Use LE; we explicitly generate one value before the next. - let x = self.next_u32() as u64; - let y = self.next_u32() as u64; - (y << 32) | x - } + fn next_u64(&mut self) -> u64; - #[cfg(feature = "i128_support")] /// Return the next random u128. - /// - /// By default this is implemented in terms of `next_u64` in LE order. - fn next_u128(&mut self) -> u128 { - // Use LE; we explicitly generate one value before the next. - let x = self.next_u64() as u128; - let y = self.next_u64() as u128; - (y << 64) | x - } + #[cfg(feature = "i128_support")] + fn next_u128(&mut self) -> u128; /// Fill `dest` entirely with random data. /// - /// This has a default implementation in terms of `next_u64` in LE order, - /// but should be overridden by implementations that - /// offer a more efficient solution than just calling `next_u64` - /// repeatedly. - /// /// This method does *not* have any requirement on how much of the - /// generated random number stream is consumed; e.g. the default + /// generated random number stream is consumed; e.g. `try_fill_via_u64` /// implementation uses `next_u64` thus consuming 8 bytes even when only /// 1 is required. A different implementation might use `next_u32` and /// only consume 4 bytes; *however* any change affecting *reproducibility* /// of output must be considered a breaking change. - fn try_fill(&mut self, dest: &mut [u8]) -> Result<()> { - use core::intrinsics::transmute; - - let mut left = dest; - while left.len() >= 8 { - let (l, r) = {left}.split_at_mut(8); - left = r; - let chunk: [u8; 8] = unsafe { - transmute(self.next_u64().to_le()) - }; - l.copy_from_slice(&chunk); - } - let n = left.len(); - if n > 0 { - let chunk: [u8; 8] = unsafe { - transmute(self.next_u64().to_le()) - }; - left.copy_from_slice(&chunk[..n]); - } - Ok(()) - } + fn try_fill(&mut self, dest: &mut [u8]) -> Result<()>; } #[cfg(feature="std")] diff --git a/rand_core/src/mock.rs b/rand_core/src/mock.rs index a4fead00e4a..c32e9310d84 100644 --- a/rand_core/src/mock.rs +++ b/rand_core/src/mock.rs @@ -16,7 +16,7 @@ //! Instead maybe this should be yet another crate? Or just leave it here? use core::num::Wrapping as w; -use {Rng, SeedableRng}; +use {Rng, SeedableRng, impls, Result}; /// A simple implementation of `Rng`, purely for testing. /// Returns an arithmetic sequence (i.e. adds a constant each step). @@ -49,6 +49,17 @@ impl Rng for MockAddRng { self.v += self.a; result } + fn next_u64(&mut self) -> u64 { + impls::next_u64_via_u32(self) + } + #[cfg(feature = "i128_support")] + fn next_u128(&mut self) -> u128 { + impls::next_u128_via_u64(self) + } + + fn try_fill(&mut self, dest: &mut [u8]) -> Result<()> { + impls::try_fill_via_u32(self, dest) + } } impl Rng for MockAddRng { @@ -60,6 +71,14 @@ impl Rng for MockAddRng { self.v += self.a; result } + #[cfg(feature = "i128_support")] + fn next_u128(&mut self) -> u128 { + impls::next_u128_via_u64(self) + } + + fn try_fill(&mut self, dest: &mut [u8]) -> Result<()> { + impls::try_fill_via_u32(self, dest) + } } impl SeedableRng for MockAddRng where diff --git a/src/lib.rs b/src/lib.rs index 91c829ae0a1..53b2216a45e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -404,15 +404,19 @@ pub struct StdRng { } impl Rng for StdRng { - #[inline] fn next_u32(&mut self) -> u32 { self.rng.next_u32() } - - #[inline] fn next_u64(&mut self) -> u64 { self.rng.next_u64() } + #[cfg(feature = "i128_support")] + fn next_u128(&mut self) -> u128 { + self.rng.next_u128() + } + fn try_fill(&mut self, dest: &mut [u8]) -> Result<()> { + self.rng.try_fill(dest) + } } impl SeedFromRng for StdRng { @@ -424,7 +428,7 @@ impl SeedFromRng for StdRng { #[cfg(test)] mod test { - use {Rng, thread_rng, Sample}; + use {Rng, thread_rng, Sample, Result}; use rand_core::mock::MockAddRng; use distributions::{uniform}; use distributions::{Uniform, Range, Exp}; @@ -436,10 +440,17 @@ mod test { impl Rng for MyRng { fn next_u32(&mut self) -> u32 { - fn next(t: &mut T) -> u32 { - t.next_u32() - } - next(&mut self.inner) + self.inner.next_u32() + } + fn next_u64(&mut self) -> u64 { + self.inner.next_u64() + } + #[cfg(feature = "i128_support")] + fn next_u128(&mut self) -> u128 { + self.inner.next_u128() + } + fn try_fill(&mut self, dest: &mut [u8]) -> Result<()> { + self.inner.try_fill(dest) } } diff --git a/src/os.rs b/src/os.rs index 9e46560b9c9..eeb4c13d1eb 100644 --- a/src/os.rs +++ b/src/os.rs @@ -55,6 +55,12 @@ impl Rng for OsRng { self.try_fill(&mut buf).unwrap_or_else(|e| panic!("try_fill failed: {:?}", e)); unsafe{ *(buf.as_ptr() as *const u64) } } + #[cfg(feature = "i128_support")] + fn next_u128(&mut self) -> u128 { + let mut buf: [u8; 16] = [0; 16]; + self.try_fill(&mut buf).unwrap_or_else(|e| panic!("try_fill failed: {:?}", e)); + unsafe{ *(buf.as_ptr() as *const u128) } + } fn try_fill(&mut self, v: &mut [u8]) -> Result<()> { self.0.try_fill(v) } diff --git a/src/prng/chacha.rs b/src/prng/chacha.rs index 0b0a83b6c1f..2f0a5f78794 100644 --- a/src/prng/chacha.rs +++ b/src/prng/chacha.rs @@ -198,6 +198,14 @@ impl Rng for ChaChaRng { value.0 } + fn next_u64(&mut self) -> u64 { + ::rand_core::impls::next_u64_via_u32(self) + } + #[cfg(feature = "i128_support")] + fn next_u128(&mut self) -> u128 { + ::rand_core::impls::next_u128_via_u64(self) + } + // Custom implementation allowing larger reads from buffer is about 8% // faster than default implementation in my tests fn try_fill(&mut self, dest: &mut [u8]) -> Result<()> { diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index f7d206c629f..2e866028552 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -222,27 +222,16 @@ impl Rng for IsaacRng { self.rsl[(self.cnt % RAND_SIZE) as usize].0 } - // Default impl adjusted for native byte size; approx 18% faster in tests + fn next_u64(&mut self) -> u64 { + ::rand_core::impls::next_u64_via_u32(self) + } + #[cfg(feature = "i128_support")] + fn next_u128(&mut self) -> u128 { + ::rand_core::impls::next_u128_via_u64(self) + } + fn try_fill(&mut self, dest: &mut [u8]) -> Result<()> { - use core::intrinsics::transmute; - - let mut left = dest; - while left.len() >= 4 { - let (l, r) = {left}.split_at_mut(4); - left = r; - let chunk: [u8; 4] = unsafe { - transmute(self.next_u32().to_le()) - }; - l.copy_from_slice(&chunk); - } - let n = left.len(); - if n > 0 { - let chunk: [u8; 4] = unsafe { - transmute(self.next_u32().to_le()) - }; - left.copy_from_slice(&chunk[..n]); - } - Ok(()) + ::rand_core::impls::try_fill_via_u32(self, dest) } } @@ -492,6 +481,14 @@ impl Rng for Isaac64Rng { debug_assert!(self.cnt < RAND_SIZE_64); self.rsl[(self.cnt % RAND_SIZE_64) as usize].0 } + #[cfg(feature = "i128_support")] + fn next_u128(&mut self) -> u128 { + ::rand_core::impls::next_u128_via_u64(self) + } + + fn try_fill(&mut self, dest: &mut [u8]) -> Result<()> { + ::rand_core::impls::try_fill_via_u64(self, dest) + } } impl SeedFromRng for Isaac64Rng { diff --git a/src/prng/xorshift.rs b/src/prng/xorshift.rs index dbfa24a26a0..1e2d6cca252 100644 --- a/src/prng/xorshift.rs +++ b/src/prng/xorshift.rs @@ -75,6 +75,18 @@ impl Rng for XorShiftRng { self.w = w_ ^ (w_ >> 19) ^ (t ^ (t >> 8)); self.w.0 } + + fn next_u64(&mut self) -> u64 { + ::rand_core::impls::next_u64_via_u32(self) + } + #[cfg(feature = "i128_support")] + fn next_u128(&mut self) -> u128 { + ::rand_core::impls::next_u128_via_u64(self) + } + + fn try_fill(&mut self, dest: &mut [u8]) -> Result<()> { + ::rand_core::impls::try_fill_via_u32(self, dest) + } } impl SeedableRng<[u32; 4]> for XorShiftRng { From 14d05616b80302053ce186d4f46ed7d26541235e Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Mon, 11 Sep 2017 22:33:07 +0200 Subject: [PATCH 114/247] Add some extra documentation --- src/prng/isaac.rs | 83 ++++++++++++++++++++++++++++++--- src/prng/isaac64.rs | 70 ++++++++++++++++++++++++---- src/prng/isaac_word.rs | 103 +++++++++++++++++++++++++++++++++++++++-- 3 files changed, 237 insertions(+), 19 deletions(-) diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index 00343044eee..f5df6967854 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -23,15 +23,70 @@ type w32 = w; const RAND_SIZE_LEN: usize = 8; const RAND_SIZE: usize = 1 << RAND_SIZE_LEN; -/// A random number generator that uses the ISAAC algorithm[1]. +/// A random number generator that uses the ISAAC algorithm. /// -/// The ISAAC algorithm is generally accepted as suitable for -/// cryptographic purposes, but this implementation has not be -/// verified as such. Prefer a generator like `OsRng` that defers to -/// the operating system for cases that need high security. +/// ISAAC stands for "Indirection, Shift, Accumulate, Add, and Count" which are +/// the principal bitwise operations employed. It is the most advanced of a +/// series of array based random number generator designed by Robert Jenkins +/// in 1996[1][2]. /// -/// [1]: Bob Jenkins, [*ISAAC: A fast cryptographic random number -/// generator*](http://www.burtleburtle.net/bob/rand/isaacafa.html) +/// Although ISAAC is designed to be cryptographically secure, its design is not +/// founded in cryptographic theory. Therefore it is _not recommended for_ +/// cryptographic purposes. It is however one of the strongest non-cryptograpic +/// RNGs, and that while still being reasonably fast. +/// +/// Where fast random numbers are needed which should still be secure, but where +/// speed is more important than absolute (cryptographic) security (e.g. to +/// initialise hashes in the std library), a generator like ISAAC may be a good +/// choice. +/// +/// In 2006 an improvement to ISAAC was suggested by Jean-Philippe Aumasson, +/// named ISAAC+[3]. But because the specification is not complete, there is no +/// good implementation, and because the suggested bias may not exist, it is not +/// implemented here. +/// +/// ## Overview of the ISAAC algorithm: +/// (in pseudo-code) +/// +/// ```text +/// Input: a, b, c, s[256] // state +/// Output: r[256] // results +/// +/// mix(a,i) = a ^ a << 13 if i = 0 mod 4 +/// a ^ a >> 6 if i = 1 mod 4 +/// a ^ a << 2 if i = 2 mod 4 +/// a ^ a >> 16 if i = 3 mod 4 +/// +/// c = c + 1 +/// b = b + c +/// +/// for i in 0..256 { +/// x = s_[i] +/// a = f(a,i) + s[i+128 mod 256] +/// y = a + b + s[x>>2 mod 256] +/// s[i] = y +/// b = x + s[y>>10 mod 256] +/// r[i] = b +/// } +/// ``` +/// +/// Numbers are generated in blocks of 256. This means the function above only +/// runs once every 256 times you ask for a next random number. In all other +/// circumstances the last element of the results array is returned. +/// +/// ISAAC therefore needs a lot of memory, relative to other non-vrypto RNGs. +/// 2 * 256 * 4 = 2 kb to hold the state and results. +/// +/// ## References +/// [1]: Bob Jenkins, [*ISAAC: A fast cryptographic random number generator*] +/// (http://burtleburtle.net/bob/rand/isaacafa.html) +/// +/// [2]: Bob Jenkins, [*ISAAC and RC4*] +/// (http://burtleburtle.net/bob/rand/isaac.html) +/// +/// [3]: Jean-Philippe Aumasson, [*On the pseudo-random generator ISAAC*] +/// (http://eprint.iacr.org/2006/438) + #[derive(Copy)] pub struct IsaacRng { rsl: [w32; RAND_SIZE], @@ -121,6 +176,20 @@ impl IsaacRng { } /// Refills the output buffer (`self.rsl`) + /// See also the pseudocode desciption of the algorithm at the top of this + /// file. + /// + /// Optimisations used (similar to the reference implementation): + /// - The loop is unrolled 4 times, once for every constant of mix(). + /// - The contents of the main loop are moved to a function `rngstep`, to + /// reduce code duplication. + /// - We use local variables for a and b, which helps with optimisations. + /// - We split the main loop in two, one that operates over 0..128 and one + /// over 128..256. This way we can optimise out the addition and modulus + /// from `s[i+128 mod 256]`. + /// - We maintain one index `i` and add `m` or `m2` as base (m2 for the + /// `s[i+128 mod 256]`), relying on the optimizer to turn it into pointer + /// arithmetic. fn isaac(&mut self) { self.c += w(1); // abbreviations diff --git a/src/prng/isaac64.rs b/src/prng/isaac64.rs index b4ba3fe2b05..32c9198f83f 100644 --- a/src/prng/isaac64.rs +++ b/src/prng/isaac64.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! The ISAAC random number generator. +//! The ISAAC-64 random number generator. use core::slice; use core::iter::repeat; @@ -23,16 +23,54 @@ type w64 = w; const RAND_SIZE_LEN: usize = 8; const RAND_SIZE: usize = 1 << RAND_SIZE_LEN; -/// A random number generator that uses ISAAC-64[1], the 64-bit -/// variant of the ISAAC algorithm. +/// A random number generator that uses ISAAC-64, the 64-bit variant of the +/// ISAAC algorithm. /// -/// The ISAAC algorithm is generally accepted as suitable for -/// cryptographic purposes, but this implementation has not be -/// verified as such. Prefer a generator like `OsRng` that defers to -/// the operating system for cases that need high security. +/// ISAAC stands for "Indirection, Shift, Accumulate, Add, and Count" which are +/// the principal bitwise operations employed. It is the most advanced of a +/// series of array based random number generator designed by Robert Jenkins +/// in 1996[1]. /// -/// [1]: Bob Jenkins, [*ISAAC: A fast cryptographic random number -/// generator*](http://www.burtleburtle.net/bob/rand/isaacafa.html) +/// Although ISAAC is designed to be cryptographically secure, its design is not +/// founded in cryptographic theory. Therefore it is _not recommended for_ +/// cryptographic purposes. It is however one of the strongest non-cryptograpic +/// RNGs, and that while still being reasonably fast. +/// +/// ISAAC-64 is mostly similar to ISAAC. Because it operates on 64-bit integers +/// instead of 32-bit, it uses twice as much memory to hold its state and +/// results. Also it uses different constants for shifts and indirect indexing, +/// optimized to give good results for 64bit arithmetic. +/// +/// ## Overview of the ISAAC-64 algorithm: +/// (in pseudo-code) +/// +/// ```text +/// Input: a, b, c, s[256] // state +/// Output: r[256] // results +/// +/// mix(a,i) = !(a ^ a << 21) if i = 0 mod 4 +/// a ^ a >> 5 if i = 1 mod 4 +/// a ^ a << 12 if i = 2 mod 4 +/// a ^ a >> 33 if i = 3 mod 4 +/// +/// c = c + 1 +/// b = b + c +/// +/// for i in 0..256 { +/// x = s_[i] +/// a = mix(a,i) + s[i+128 mod 256] +/// y = a + b + s[x>>3 mod 256] +/// s[i] = y +/// b = x + s[y>>11 mod 256] +/// r[i] = b +/// } +/// ``` +/// +/// See for more information the description in rand::prng::IsaacRng. +/// +/// [1]: Bob Jenkins, [*ISAAC and RC4*] +/// (http://burtleburtle.net/bob/rand/isaac.html) + #[derive(Copy)] pub struct Isaac64Rng { rsl: [w64; RAND_SIZE], @@ -122,6 +160,20 @@ impl Isaac64Rng { } /// Refills the output buffer (`self.rsl`) + /// See also the pseudocode desciption of the algorithm at the top of this + /// file. + /// + /// Optimisations used (similar to the reference implementation): + /// - The loop is unrolled 4 times, once for every constant of mix(). + /// - The contents of the main loop are moved to a function `rngstep`, to + /// reduce code duplication. + /// - We use local variables for a and b, which helps with optimisations. + /// - We split the main loop in two, one that operates over 0..128 and one + /// over 128..256. This way we can optimise out the addition and modulus + /// from `s[i+128 mod 256]`. + /// - We maintain one index `i` and add `m` or `m2` as base (m2 for the + /// `s[i+128 mod 256]`), relying on the optimizer to turn it into pointer + /// arithmetic. fn isaac64(&mut self) { self.c += w(1); // abbreviations diff --git a/src/prng/isaac_word.rs b/src/prng/isaac_word.rs index 58c884d9f26..c1ca12f0f12 100644 --- a/src/prng/isaac_word.rs +++ b/src/prng/isaac_word.rs @@ -10,8 +10,105 @@ //! The ISAAC random number generator. -/// Select 32- or 64-bit variant dependent on pointer size. +use core::fmt; +use {Rng, FromRng, SeedableRng}; + +#[cfg(target_pointer_width = "32")] +#[derive(Copy)] +/// A random number generator that uses the ISAAC or ISAAC-64 algorithm, +/// depending on the pointer size of the target architecture. +/// +/// In general a random number generator that internally uses the word size of +/// the target architecture is faster than one that doesn't. Choosing +/// `IsaacWordRng` is therefore often a better choice than `IsaacRng` or +/// `Isaac64Rng`. The others can be a good choice if reproducability across +/// different architectures is desired. `IsaacRng` can also be a better choice +/// if memory usage is an issue, as it uses 2kb of state instead of the 4kb +/// `Isaac64Rng` uses. +/// +/// See for an explanation of the algorithm `IsaacRng` and `Isaac64Rng`. +pub struct IsaacWordRng(super::isaac::IsaacRng); + +#[cfg(target_pointer_width = "64")] +#[derive(Copy)] +/// A random number generator that uses the ISAAC or ISAAC-64 algorithm, +/// depending on the pointer size of the target architecture. +/// +/// In general a random number generator that internally uses the word size of +/// the target architecture is faster than one that doesn't. Choosing +/// `IsaacWordRng` is therefore often a better choice than `IsaacRng` or +/// `Isaac64Rng`. The others can be a good choice if reproducability across +/// different architectures is desired. `IsaacRng` can also be a better choice +/// if memory usage is an issue, as it uses 2kb of state instead of the 4kb +/// `Isaac64Rng` uses. +/// +/// See for an explanation of the algorithm `IsaacRng` and `Isaac64Rng`. +pub struct IsaacWordRng(super::isaac64::Isaac64Rng); + +impl Clone for IsaacWordRng { + fn clone(&self) -> IsaacWordRng { + *self + } +} + +impl Rng for IsaacWordRng { + #[inline] + fn next_u32(&mut self) -> u32 { + self.0.next_u32() + } + + #[inline] + fn next_u64(&mut self) -> u64 { + self.0.next_u64() + } +} + +impl FromRng for IsaacWordRng { + #[cfg(target_pointer_width = "32")] + fn from_rng(other: &mut R) -> IsaacWordRng { + IsaacWordRng(super::isaac::IsaacRng::from_rng(other)) + } + + #[cfg(target_pointer_width = "64")] + fn from_rng(other: &mut R) -> IsaacWordRng { + IsaacWordRng(super::isaac64::Isaac64Rng::from_rng(other)) + } +} + #[cfg(target_pointer_width = "32")] -pub use prng::isaac::IsaacRng as IsaacWordRng; +impl<'a> SeedableRng<&'a [u32]> for IsaacWordRng { + fn reseed(&mut self, seed: &'a [u32]) { + self.0.reseed(seed) + } + + /// Create an ISAAC random number generator with a seed. This can + /// be any length, although the maximum number of elements used is + /// 256 and any more will be silently ignored. A generator + /// constructed with a given seed will generate the same sequence + /// of values as all other generators constructed with that seed. + fn from_seed(seed: &'a [u32]) -> IsaacWordRng { + IsaacWordRng(super::isaac::IsaacRng::from_seed(seed)) + } +} + #[cfg(target_pointer_width = "64")] -pub use prng::isaac64::Isaac64Rng as IsaacWordRng; +impl<'a> SeedableRng<&'a [u64]> for IsaacWordRng { + fn reseed(&mut self, seed: &'a [u64]) { + self.0.reseed(seed) + } + + /// Create an ISAAC random number generator with a seed. This can + /// be any length, although the maximum number of elements used is + /// 256 and any more will be silently ignored. A generator + /// constructed with a given seed will generate the same sequence + /// of values as all other generators constructed with that seed. + fn from_seed(seed: &'a [u64]) -> IsaacWordRng { + IsaacWordRng(super::isaac64::Isaac64Rng::from_seed(seed)) + } +} + +impl fmt::Debug for IsaacWordRng { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "IsaacWordRng {{}}") + } +} From 2cb9acd139f7575f7061c3764266b63a58ffca94 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Wed, 13 Sep 2017 09:15:33 +0100 Subject: [PATCH 115/247] Fix OsRng for other platforms (hopefully) --- src/os.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/os.rs b/src/os.rs index eeb4c13d1eb..1b2a9b99a0d 100644 --- a/src/os.rs +++ b/src/os.rs @@ -259,6 +259,7 @@ mod imp { if ret == -1 { panic!("couldn't generate random bytes: {}", io::Error::last_os_error()); } + Ok(()) } } } @@ -292,6 +293,7 @@ mod imp { ret, s.len(), s_len); } } + Ok(()) } } } @@ -321,6 +323,7 @@ mod imp { panic!("getentropy failed: {}", err); } } + Ok(()) } } } @@ -374,6 +377,7 @@ mod imp { }; } } + Ok(()) } } } @@ -411,6 +415,7 @@ mod imp { io::Error::last_os_error()); } } + Ok(()) } } } @@ -476,6 +481,7 @@ mod imp { if read >= v.len() { break; } } + Ok(()) } } } From da1ff461fa44208dd204c7a8f82041ccd9d30f80 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Thu, 14 Sep 2017 12:52:28 +0100 Subject: [PATCH 116/247] rand_core: improve a few comments/doc --- rand_core/src/impls.rs | 8 ++++++++ rand_core/src/lib.rs | 29 ++++++++++++++++++++++++----- rand_core/src/mock.rs | 2 +- 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/rand_core/src/impls.rs b/rand_core/src/impls.rs index fb45a3ecee2..1c5a2be7e18 100644 --- a/rand_core/src/impls.rs +++ b/rand_core/src/impls.rs @@ -18,6 +18,10 @@ //! Byte-swapping (like the std `to_le` functions) is only needed to convert //! to/from byte sequences, and since its purpose is reproducibility, //! non-reproducible sources (e.g. `OsRng`) need not bother with it. +//! +//! Missing from here are implementations of `next_u*` in terms of `try_fill`. +//! Currently `OsRng` handles these implementations itself. +//! TODO: should we add more implementations? use core::intrinsics::transmute; use {Rng, Result}; @@ -31,6 +35,8 @@ pub fn next_u64_via_u32(rng: &mut R) -> u64 { } /// Implement `next_u128` via `next_u64`, little-endian order. +/// +/// This may be used even where `next_u64` is implemented via `next_u32`. #[cfg(feature = "i128_support")] pub fn next_u128_via_u64(rng: &mut R) -> u128 { // Use LE; we explicitly generate one value before the next. @@ -76,3 +82,5 @@ pub fn try_fill_via_u64(rng: &mut R, dest: &mut [u8]) -> Result<( pub fn try_fill_via_u128(rng: &mut R, dest: &mut [u8]) -> Result<()> { try_fill_via!(rng, next_u128, 16, dest) } + +// TODO: implement tests for the above diff --git a/rand_core/src/lib.rs b/rand_core/src/lib.rs index 7094b8ebe5e..d39a385e048 100644 --- a/rand_core/src/lib.rs +++ b/rand_core/src/lib.rs @@ -17,10 +17,18 @@ //! `Rng` is the core trait implemented by algorithmic pseudo-random number //! generators and external random-number sources. //! -//! `SeedFromRng` and `SeedableRng` are extension traits. +//! `SeedFromRng` and `SeedableRng` are extension traits for construction and +//! reseeding. //! //! `Error` and `Result` are provided for error-handling. They are safe to use //! in `no_std` environments. +//! +//! The `impls` sub-module includes a few small functions to assist +//! implementation of `Rng`. Since this module is only of interest to `Rng` +//! implementors, it is not re-exported from `rand`. +//! +//! The `mock` module includes a mock `Rng` implementation. Even though this is +//! only useful for testing, it is currently always built. #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk.png", html_favicon_url = "https://www.rust-lang.org/favicon.ico", @@ -115,7 +123,8 @@ impl Rng for Box where R: Rng+?Sized { /// Support mechanism for creating random number generators seeded by other -/// generators. All PRNGs should support this to enable `NewSeeded` support. +/// generators. All PRNGs should support this to enable `NewSeeded` support, +/// which should be the preferred way of creating randomly-seeded generators. /// /// TODO: should this use `Distribution` instead? That would require moving /// that trait and a distribution type to this crate. @@ -136,6 +145,13 @@ pub trait SeedFromRng: Sized { /// A random number generator that can be explicitly seeded to produce /// the same stream of randomness multiple times. +/// +/// Note: this should only be implemented by reproducible generators (i.e. +/// where the algorithm is fixed and results should be the same across +/// platforms). This should not be implemented by wrapper types which choose +/// the underlying implementation based on platform, or which may change the +/// algorithm used in the future. This is to ensure that manual seeding of PRNGs +/// actually does yield reproducible results. pub trait SeedableRng: Rng { /// Reseed an RNG with the given seed. /// @@ -151,9 +167,12 @@ pub trait SeedableRng: Rng { } -/// Error type for cryptographic generators. Only operating system and hardware -/// generators should be able to fail. In such cases there is little that can -/// be done besides try again later. +/// Error type for cryptographic generators. Technically external generators +/// such as the operating system or hardware generators could fail. A PRNG +/// (algorithm) could also fail if it detects cycles, though most PRNGs have +/// sufficiently long cycles that looping is not usually feasible. +/// +/// TODO: how should error details be reported? #[derive(Debug)] pub struct Error; diff --git a/rand_core/src/mock.rs b/rand_core/src/mock.rs index c32e9310d84..ecfea3184a5 100644 --- a/rand_core/src/mock.rs +++ b/rand_core/src/mock.rs @@ -27,7 +27,7 @@ use {Rng, SeedableRng, impls, Result}; /// /// let mut my_rng = MockAddRng::new(2u32, 1u32); /// assert_eq!(my_rng.next_u32(), 2u32); -/// assert_eq!(my_rng.next_u64(), (4u64 << 32) + 3u64); +/// assert_eq!(my_rng.next_u64(), 3u64 + (4u64 << 32)); /// ``` #[derive(Debug)] pub struct MockAddRng { From 67b22bd647d6d6a58c73c4f4d8376a4dd9d126ca Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Thu, 14 Sep 2017 12:58:14 +0100 Subject: [PATCH 117/247] rand_core: update Cargo.toml --- rand_core/Cargo.toml | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/rand_core/Cargo.toml b/rand_core/Cargo.toml index 5968e530a55..8d14f405f9b 100644 --- a/rand_core/Cargo.toml +++ b/rand_core/Cargo.toml @@ -2,16 +2,14 @@ name = "rand_core" version = "0.0.1" authors = ["The Rust Project Developers"] -license = "MIT/Apache-2.0" -readme = "README.md" -repository = "https://github.com/rust-lang-nursery/rand" +description = "Random number generator core: `Rng` and extensions" +homepage = "https://github.com/dhardy/rand/tree/master/rand_core" documentation = "https://docs.rs/rand_core" -homepage = "https://github.com/rust-lang-nursery/rand" -description = """ -Random number generator traits -""" +readme = "README.md" keywords = ["random", "rng"] categories = ["algorithms"] +license = "MIT/Apache-2.0" +repository = "https://github.com/dhardy/rand/" [features] default = ["std"] From ec70309be8b1f84af06552c7fd7c8c8456355975 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Thu, 14 Sep 2017 18:55:44 +0100 Subject: [PATCH 118/247] rand_core: add impl Rng for &mut R --- rand_core/src/lib.rs | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/rand_core/src/lib.rs b/rand_core/src/lib.rs index d39a385e048..86f8eab9586 100644 --- a/rand_core/src/lib.rs +++ b/rand_core/src/lib.rs @@ -101,8 +101,27 @@ pub trait Rng { fn try_fill(&mut self, dest: &mut [u8]) -> Result<()>; } +impl<'a, R: Rng+?Sized> Rng for &'a mut R { + fn next_u32(&mut self) -> u32 { + (**self).next_u32() + } + + fn next_u64(&mut self) -> u64 { + (**self).next_u64() + } + + #[cfg(feature = "i128_support")] + fn next_u128(&mut self) -> u128 { + (**self).next_u128() + } + + fn try_fill(&mut self, dest: &mut [u8]) -> Result<()> { + (**self).try_fill(dest) + } +} + #[cfg(feature="std")] -impl Rng for Box where R: Rng+?Sized { +impl Rng for Box { fn next_u32(&mut self) -> u32 { (**self).next_u32() } From d8b84745a0bea629cbeaa175b69f2d118005ea89 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Thu, 7 Sep 2017 11:12:45 +0200 Subject: [PATCH 119/247] Allow sampling from a closed integer range These changes make it possible to sample from closed ranges, not only from open. Included is a small optimisation for the modulus operator, and an optimisation for the types i8/u8 and i16/u16. --- src/distributions/range2.rs | 162 +++++++++++++++++++++++++----------- 1 file changed, 113 insertions(+), 49 deletions(-) diff --git a/src/distributions/range2.rs b/src/distributions/range2.rs index 1ff7fc51057..e44afdfe7b9 100644 --- a/src/distributions/range2.rs +++ b/src/distributions/range2.rs @@ -9,22 +9,21 @@ // except according to those terms. //! Alternative design for `Range`. -//! +//! //! TODO: decide whether to replace the old `range` module with this. //! Advantage: float ranges don't have to store a "zone" parameter. //! Advantage: custom implementations can store extra parameters. //! Possible advantage: easier implementations for custom types written in //! terms of implementations of other types. //! Disadvantage: complex? -//! +//! //! This is *almost* like having separate `RangeInt`, `RangeFloat`, //! etc. (or just `RangeI32`, etc.) types, each implementing `Distribution`, //! but it adds some magic to support generic `range` and `new_range` methods. -use core::num::Wrapping as w; - +use Rand; use Rng; -use distributions::Distribution; +use distributions::{Distribution, Uniform}; use utils::FloatConversions; /// Sample values uniformly between two bounds. @@ -40,7 +39,7 @@ use utils::FloatConversions; /// including `high`, but this may be very difficult. All the /// primitive integer types satisfy this property, and the float types /// normally satisfy it, but rounding may mean `high` can occur. -/// +/// /// # Example /// /// ```rust @@ -71,6 +70,13 @@ impl Range> { assert!(low < high, "Range::new called with `low >= high`"); Range { inner: RangeImpl::new(low, high) } } + + /// Create a new `Range` instance that samples uniformly from + /// `[low, high]` (inclusive). Panics if `low >= high`. + pub fn new_inclusive(low: X, high: X) -> Range { + assert!(low < high, "Range::new called with `low >= high`"); + Range { inner: RangeImpl::new_inclusive(low, high) } + } } impl Distribution for Range { @@ -88,13 +94,15 @@ pub trait SampleRange: PartialOrd+Sized { pub trait RangeImpl { /// The type sampled by this implementation. type X: PartialOrd; - + /// Construct self. - /// + /// /// This should not be called directly. `Range::new` asserts that /// `low < high` before calling this. fn new(low: Self::X, high: Self::X) -> Self; - + + fn new_inclusive(low: Self::X, high: Self::X) -> Self; + /// Sample a value. fn sample(&self, rng: &mut R) -> Self::X; } @@ -108,35 +116,73 @@ pub struct RangeInt { } macro_rules! range_int_impl { - ($ty:ty, $unsigned:ident) => { + ($ty:ty, $signed:ident, $unsigned:ident, + $i_large:ident, $u_large:ident) => { impl SampleRange for $ty { type T = RangeInt<$ty>; } - + impl RangeImpl for RangeInt<$ty> { - // we play free and fast with unsigned vs signed here + // We play free and fast with unsigned vs signed here // (when $ty is signed), but that's fine, since the // contract of this macro is for $ty and $unsigned to be - // "bit-equal", so casting between them is a no-op & a - // bijection. + // "bit-equal", so casting between them is a no-op. type X = $ty; - + fn new(low: Self::X, high: Self::X) -> Self { - let range = (w(high as $unsigned) - w(low as $unsigned)).0; - let unsigned_max: $unsigned = ::core::$unsigned::MAX; - - // We want to calculate type_range % range where type_range is - // pow(2, n_bits($ty)), but we can't represent type_range. - // (type_range - range) % range is equivalent, since we know - // type_range > range. Since range >= 1, - // type_range - range = (unsigned_max - range) + 1. - let ignore_zone = ((unsigned_max - range) + 1) % range; - // We want to sample from the zone - // [0, (type_range - ignore_zone)) - // however, ignore_zone may be zero. Instead use a closed range - // from zero to: - let zone = unsigned_max - ignore_zone; + RangeImpl::new_inclusive(low, high - 1) + } + + fn new_inclusive(low: Self::X, high: Self::X) -> Self { + // For a closed range the number of possible numbers we should + // generate is `range = (high - low + 1)`. It is not possible to + // end up with a uniform distribution if we map _all_ the random + // integers that can be generated to this range. We have to map + // integers from a `zone` that is a multiple of the range. The + // rest of the integers, that cause a bias, are rejected. The + // sampled number is `zone % range`. + // + // The problem with `range` is that to cover the full range of + // the type, it has to store `unsigned_max + 1`, which can't be + // represented. But if the range covers the full range of the + // type, no modulus is needed. A range of size 0 can't exist, so + // we use that to represent this special case. Wrapping + // arithmetic even makes representing `unsigned_max + 1` as 0 + // simple. + // + // We don't calculate zone directly, but first calculate the + // number of integers to reject. To handle `unsigned_max + 1` + // not fitting in the type, we use: + // ints_to_reject = (unsigned_max + 1) % range; + // ints_to_reject = (unsigned_max - range + 1) % range; + // + // The smallest integer prngs generate is u32. That is why for + // small integer sizes (i8/u8 and i16/u16) there is an + // optimisation: don't pick the largest zone that can fit in the + // small type, but pick the largest zone that can fit in an u32. + // This improves the chance to get a random integer that fits in + // the zone to 998 in 1000 in the worst case. + // + // There is a problem however: we can't store such a large range + // in `RangeInt`, that can only hold values of the size of $ty. + // `ints_to_reject` is always less than half the size of the + // small integer. For an u8 it only ever uses 7 bits. This means + // that all but the last 7 bits of `zone` are always 1's (or 15 + // in the case of u16). So nothing is lost by trucating `zone`. + + let unsigned_max: $u_large = ::core::$u_large::MAX; + + let range = (high as $u_large) + .wrapping_sub(low as $u_large) + .wrapping_add(1); + let ints_to_reject = + if range > 0 { + (unsigned_max - range + 1) % range + } else { + 0 + }; + let zone = unsigned_max - ints_to_reject; RangeInt { low: low, @@ -145,36 +191,45 @@ macro_rules! range_int_impl { zone: zone as $ty } } - + fn sample(&self, rng: &mut R) -> Self::X { - use $crate::distributions::uniform; - loop { - let v: $unsigned = uniform(rng); - // Reject samples not between 0 and zone: - if v <= self.zone as $unsigned { - // Adjustment sample for range and low value: - return (w(self.low) + w((v % self.range as $unsigned) as $ty)).0; + let range = self.range as $unsigned as $u_large; + if range > 0 { + // Some casting to recover the trucated bits of `zone`: + // First bit-cast to a signed int. Next sign-extend to the + // larger type. Then bit-cast to unsigned. + // For types that already have the right size, all the + // casting is a no-op. + let zone = self.zone as $signed as $i_large as $u_large; + loop { + let v: $u_large = Rand::rand(rng, Uniform); + if v <= zone { + return self.low.wrapping_add((v % range) as $ty); + } } + } else { + // Sample from the entire integer range. + Rand::rand(rng, Uniform) } } } } } -range_int_impl! { i8, u8 } -range_int_impl! { i16, u16 } -range_int_impl! { i32, u32 } -range_int_impl! { i64, u64 } +range_int_impl! { i8, i8, u8, i32, u32 } +range_int_impl! { i16, i16, u16, i32, u32 } +range_int_impl! { i32, i32, u32, i32, u32 } +range_int_impl! { i64, i64, u64, i64, u64 } #[cfg(feature = "i128_support")] -range_int_impl! { i128, u128 } -range_int_impl! { isize, usize } -range_int_impl! { u8, u8 } -range_int_impl! { u16, u16 } -range_int_impl! { u32, u32 } -range_int_impl! { u64, u64 } +range_int_impl! { i128, i128, u128, u128 } +range_int_impl! { isize, isize, usize, isize, usize } +range_int_impl! { u8, i8, u8, i32, u32 } +range_int_impl! { u16, i16, u16, i32, u32 } +range_int_impl! { u32, i32, u32, i32, u32 } +range_int_impl! { u64, i64, u64, i64, u64 } #[cfg(feature = "i128_support")] -range_int_impl! { u128, u128 } -range_int_impl! { usize, usize } +range_int_impl! { u128, u128, u128, i128, u128 } +range_int_impl! { usize, isize, usize, isize, usize } /// Implementation of `RangeImpl` for float types. #[derive(Clone, Copy, Debug)] @@ -251,6 +306,12 @@ macro_rules! range_float_impl { } } + fn new_inclusive(low: Self::X, high: Self::X) -> Self { + // Same as `new`, because the boundaries of a floats range are + // (at least currently) not exact due to rounding errors. + RangeImpl::new(low, high) + } + fn sample(&self, rng: &mut R) -> Self::X { let rnd = $next_u(rng); match *self { @@ -404,6 +465,9 @@ mod tests { inner: RangeFloat::::new(low.x, high.x), } } + fn new_inclusive(low: Self::X, high: Self::X) -> Self { + RangeImpl::new(low, high) + } fn sample(&self, rng: &mut R) -> Self::X { MyF32 { x: self.inner.sample(rng) } } From 7edd06baa42edf264a127d29ddfc2b5848bf670b Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Sat, 30 Sep 2017 18:55:22 +0200 Subject: [PATCH 120/247] Remove range.rs --- src/distributions/range.rs | 310 ------------------------------------- 1 file changed, 310 deletions(-) delete mode 100644 src/distributions/range.rs diff --git a/src/distributions/range.rs b/src/distributions/range.rs deleted file mode 100644 index b086df8ca43..00000000000 --- a/src/distributions/range.rs +++ /dev/null @@ -1,310 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Generating numbers between two others. - -// this is surprisingly complicated to be both generic & correct - -use core::num::Wrapping as w; - -use Rng; -use distributions::{Distribution, Uniform01, Rand}; - -/// Sample values uniformly between two bounds. -/// -/// This gives a uniform distribution (assuming the RNG used to sample -/// it is itself uniform & the `SampleRange` implementation for the -/// given type is correct), even for edge cases like `low = 0u8`, -/// `high = 170u8`, for which a naive modulo operation would return -/// numbers less than 85 with double the probability to those greater -/// than 85. -/// -/// Types should attempt to sample in `[low, high)`, i.e., not -/// including `high`, but this may be very difficult. All the -/// primitive integer types satisfy this property, and the float types -/// normally satisfy it, but rounding may mean `high` can occur. -/// -/// Internal details of this type are exposed so as to allow users to implement -/// `SampleRange` for their own types. Outside of `SampleRange` implementations, -/// accessing these members should not be necessary. -/// -/// # Example -/// -/// ```rust -/// use rand::distributions::{Distribution, Range}; -/// -/// fn main() { -/// let between = Range::new(10, 10000); -/// let mut rng = rand::thread_rng(); -/// let mut sum = 0; -/// for _ in 0..1000 { -/// sum += between.sample(&mut rng); -/// } -/// println!("{}", sum); -/// } -/// ``` -#[derive(Clone, Copy, Debug)] -pub struct Range { - /// The `low` of `Range::new(low, high)`. - pub low: X, - /// Usually it is more convenient to store `low` and `range = high - low` - /// internally. Custom implementations of `SampleRange` may however use - /// this differently. - pub range: X, - /// The integer implementation ensures uniform distribution by rejecting - /// parameters outside of a certain "acceptance zone", from `0` to `zone`. - /// Not all implementations use this parameter. - pub zone: X -} - -impl Range { - /// Create a new `Range` instance that samples uniformly from - /// `[low, high)`. Panics if `low >= high`. - pub fn new(low: X, high: X) -> Range { - assert!(low < high, "Range::new called with `low >= high`"); - SampleRange::construct_range(low, high) - } -} - -impl Distribution for Range { - fn sample(&self, rng: &mut R) -> T { - SampleRange::sample_range(self, rng) - } -} - -/// The helper trait for types that have a sensible way to sample -/// uniformly between two values. This should not be used directly, -/// and is only to facilitate `Range`. -pub trait SampleRange : PartialOrd+Sized { - /// Construct the `Range` object that `sample_range` - /// requires. This should not ever be called directly, only via - /// `Range::new`, which will check that `low < high`, so this - /// function doesn't have to repeat the check. - fn construct_range(low: Self, high: Self) -> Range; - - /// Sample a value from the given `Range` with the given `Rng` as - /// a source of randomness. - fn sample_range(r: &Range, rng: &mut R) -> Self; -} - -macro_rules! integer_impl { - ($ty:ty, $unsigned:ident) => { - impl SampleRange for $ty { - // we play free and fast with unsigned vs signed here - // (when $ty is signed), but that's fine, since the - // contract of this macro is for $ty and $unsigned to be - // "bit-equal", so casting between them is a no-op & a - // bijection. - - #[inline] - fn construct_range(low: $ty, high: $ty) -> Range<$ty> { - let range = (w(high as $unsigned) - w(low as $unsigned)).0; - let unsigned_max: $unsigned = ::core::$unsigned::MAX; - - // We want to calculate type_range % range where type_range is - // pow(2, n_bits($ty)), but we can't represent type_range. - // (type_range - range) % range is equivalent, since we know - // type_range > range. Since range >= 1, - // type_range - range = (unsigned_max - range) + 1. - let ignore_zone = ((unsigned_max - range) + 1) % range; - // We want to sample from the zone - // [0, (type_range - ignore_zone)) - // however, ignore_zone may be zero. Instead use a closed range - // from zero to: - let zone = unsigned_max - ignore_zone; - - Range { - low: low, - // These are really $unsigned values, but store as $ty: - range: range as $ty, - zone: zone as $ty - } - } - - #[inline] - fn sample_range(r: &Range<$ty>, rng: &mut R) -> $ty { - use $crate::distributions::uniform; - loop { - let v: $unsigned = uniform(rng); - // Reject samples not between 0 and zone: - if v <= r.zone as $unsigned { - // Adjustment sample for range and low value: - return (w(r.low) + w((v % r.range as $unsigned) as $ty)).0; - } - } - } - } - } -} - -integer_impl! { i8, u8 } -integer_impl! { i16, u16 } -integer_impl! { i32, u32 } -integer_impl! { i64, u64 } -#[cfg(feature = "i128_support")] -integer_impl! { i128, u128 } -integer_impl! { isize, usize } -integer_impl! { u8, u8 } -integer_impl! { u16, u16 } -integer_impl! { u32, u32 } -integer_impl! { u64, u64 } -#[cfg(feature = "i128_support")] -integer_impl! { u128, u128 } -integer_impl! { usize, usize } - -macro_rules! float_impl { - ($ty:ty) => { - impl SampleRange for $ty { - fn construct_range(low: $ty, high: $ty) -> Range<$ty> { - Range { - low: low, - range: high - low, - zone: 0.0 // unused - } - } - fn sample_range(r: &Range<$ty>, rng: &mut R) -> $ty { - let x: $ty = Rand::rand(rng, Uniform01); - r.low + r.range * x - } - } - } -} - -float_impl! { f32 } -float_impl! { f64 } - -#[cfg(test)] -mod tests { - use {Rng, thread_rng}; - use distributions::{Distribution, Rand, Uniform01}; - use distributions::range::{Range, SampleRange}; - - #[test] - fn test_fn_range() { - let mut r = thread_rng(); - for _ in 0..1000 { - let a = Range::new(-3, 42).sample(&mut r); - assert!(a >= -3 && a < 42); - assert_eq!(Range::new(0, 1).sample(&mut r), 0); - assert_eq!(Range::new(-12, -11).sample(&mut r), -12); - } - - for _ in 0..1000 { - let a = Range::new(10, 42).sample(&mut r); - assert!(a >= 10 && a < 42); - assert_eq!(Range::new(0, 1).sample(&mut r), 0); - assert_eq!(Range::new(3_000_000, 3_000_001).sample(&mut r), 3_000_000); - } - - } - - #[test] - #[should_panic] - fn test_fn_range_panic_int() { - let mut r = thread_rng(); - Range::new(5, -2).sample(&mut r); - } - - #[test] - #[should_panic] - fn test_fn_range_panic_usize() { - let mut r = thread_rng(); - Range::new(5, 2).sample(&mut r); - } - - #[should_panic] - #[test] - fn test_range_bad_limits_equal() { - Range::new(10, 10); - } - #[should_panic] - #[test] - fn test_range_bad_limits_flipped() { - Range::new(10, 5); - } - - #[test] - fn test_integers() { - let mut rng = ::test::rng(); - macro_rules! t { - ($($ty:ident),*) => {{ - $( - let v: &[($ty, $ty)] = &[(0, 10), - (10, 127), - (::std::$ty::MIN, ::std::$ty::MAX)]; - for &(low, high) in v.iter() { - let sampler: Range<$ty> = Range::new(low, high); - for _ in 0..1000 { - let v = sampler.sample(&mut rng); - assert!(low <= v && v < high); - } - } - )* - }} - } - t!(i8, i16, i32, i64, isize, - u8, u16, u32, u64, usize); - #[cfg(feature = "i128_support")] - t!(i128, u128) - } - - #[test] - fn test_floats() { - let mut rng = ::test::rng(); - macro_rules! t { - ($($ty:ty),*) => {{ - $( - let v: &[($ty, $ty)] = &[(0.0, 100.0), - (-1e35, -1e25), - (1e-35, 1e-25), - (-1e35, 1e35)]; - for &(low, high) in v.iter() { - let sampler: Range<$ty> = Range::new(low, high); - for _ in 0..1000 { - let v = sampler.sample(&mut rng); - assert!(low <= v && v < high); - } - } - )* - }} - } - - t!(f32, f64) - } - - #[test] - fn test_custom_range() { - #[derive(Clone, Copy, PartialEq, PartialOrd)] - struct MyF32 { - x: f32, - } - impl SampleRange for MyF32 { - fn construct_range(low: MyF32, high: MyF32) -> Range { - Range { - low: low, - range: MyF32 { x: high.x - low.x }, - zone: MyF32 { x: 0.0f32 } - } - } - fn sample_range(r: &Range, rng: &mut R) -> MyF32 { - let x = f32::rand(rng, Uniform01); - MyF32 { x: r.low.x + r.range.x * x } - } - } - - let (low, high) = (MyF32{ x: 17.0f32 }, MyF32{ x: 22.0f32 }); - let range = Range::new(low, high); - let mut rng = ::test::rng(); - for _ in 0..100 { - let x = MyF32::rand(&mut rng, range); - assert!(low <= x && x < high); - } - } -} From 96503f7f7b6bf9cfc6fe3fc58ad99b28787fc09a Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Sat, 30 Sep 2017 19:30:05 +0200 Subject: [PATCH 121/247] Replace range with range2 --- src/distributions/mod.rs | 3 +-- src/distributions/{range2.rs => range.rs} | 21 +++++---------------- src/lib.rs | 8 +++++--- src/sequences/weighted.rs | 5 +++-- 4 files changed, 14 insertions(+), 23 deletions(-) rename src/distributions/{range2.rs => range.rs} (95%) diff --git a/src/distributions/mod.rs b/src/distributions/mod.rs index 9d63ba9d136..2f5573a40d2 100644 --- a/src/distributions/mod.rs +++ b/src/distributions/mod.rs @@ -22,7 +22,7 @@ use Rng; pub use self::default::Default; pub use self::uniform::{uniform, codepoint, ascii_word_char}; pub use self::uniform::{Uniform, Uniform01, Open01, Closed01, AsciiWordChar}; -pub use self::range::{Range}; +pub use self::range::Range; #[cfg(feature="std")] pub use self::gamma::{Gamma, ChiSquared, FisherF, StudentT}; @@ -39,7 +39,6 @@ mod uniform; mod ziggurat_tables; pub mod range; -pub mod range2; #[cfg(feature="std")] pub mod gamma; diff --git a/src/distributions/range2.rs b/src/distributions/range.rs similarity index 95% rename from src/distributions/range2.rs rename to src/distributions/range.rs index e44afdfe7b9..ba8b3130107 100644 --- a/src/distributions/range2.rs +++ b/src/distributions/range.rs @@ -8,18 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! Alternative design for `Range`. -//! -//! TODO: decide whether to replace the old `range` module with this. -//! Advantage: float ranges don't have to store a "zone" parameter. -//! Advantage: custom implementations can store extra parameters. -//! Possible advantage: easier implementations for custom types written in -//! terms of implementations of other types. -//! Disadvantage: complex? -//! -//! This is *almost* like having separate `RangeInt`, `RangeFloat`, -//! etc. (or just `RangeI32`, etc.) types, each implementing `Distribution`, -//! but it adds some magic to support generic `range` and `new_range` methods. +//! Generating numbers between two others. use Rand; use Rng; @@ -44,7 +33,7 @@ use utils::FloatConversions; /// /// ```rust /// use rand::distributions::Distribution; -/// use rand::distributions::range2::Range; +/// use rand::distributions::range::Range; /// /// fn main() { /// let between = Range::new(10, 10000); @@ -354,7 +343,7 @@ range_float_impl! { f64, Rng::next_u64 } mod tests { use {Rng, thread_rng}; use distributions::{Rand, Distribution}; - use distributions::range2::{Range, RangeImpl, RangeFloat, SampleRange}; + use distributions::range::{Range, RangeImpl, RangeFloat, SampleRange}; #[test] fn test_fn_range() { @@ -373,7 +362,7 @@ mod tests { assert_eq!(Range::new(3_000_000, 3_000_001).sample(&mut r), 3_000_000); } } - + #[test] #[should_panic] fn test_fn_range_panic_int() { @@ -475,7 +464,7 @@ mod tests { impl SampleRange for MyF32 { type T = RangeMyF32; } - + let (low, high) = (MyF32{ x: 17.0f32 }, MyF32{ x: 22.0f32 }); let range = Range::new(low, high); let mut rng = ::test::rng(); diff --git a/src/lib.rs b/src/lib.rs index 1becb5d090d..d9aa0a64c98 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -155,6 +155,7 @@ //! ``` //! use rand::Rng; //! use rand::distributions::{Distribution, Range, uniform}; +//! use rand::distributions::range::RangeInt; //! use rand::sequences::Choose; //! //! struct SimulationResult { @@ -163,7 +164,7 @@ //! } //! //! // Run a single simulation of the Monty Hall problem. -//! fn simulate(random_door: &Range, rng: &mut R) +//! fn simulate(random_door: &Range>, rng: &mut R) //! -> SimulationResult { //! let car = random_door.sample(rng); //! @@ -204,7 +205,7 @@ //! let num_simulations = 10000; //! //! let mut rng = rand::thread_rng(); -//! let random_door = Range::new(0, 3); +//! let random_door = Range::new(0u32, 3); //! //! let (mut switch_wins, mut switch_losses) = (0, 0); //! let (mut keep_wins, mut keep_losses) = (0, 0); @@ -263,6 +264,7 @@ pub use thread_local::{ThreadRng, thread_rng, set_thread_rng, set_new_thread_rng random, random_with}; use prng::IsaacWordRng; +use distributions::range::Range; pub mod distributions; pub mod iter; @@ -532,7 +534,7 @@ pub trait Sample: Rng { /// ``` fn gen_range(&mut self, low: T, high: T) -> T { assert!(low < high, "Sample::gen_range called with low >= high"); - SampleRange::construct_range(low, high).sample(self) + Range::new(low, high).sample(self) } /// Construct an iterator on an `Rng`. diff --git a/src/sequences/weighted.rs b/src/sequences/weighted.rs index 395bbb19bcc..c283622dd24 100644 --- a/src/sequences/weighted.rs +++ b/src/sequences/weighted.rs @@ -14,7 +14,8 @@ //! adapted, or removed entirely. use Rng; -use distributions::{Range, Distribution}; +use distributions::Distribution; +use distributions::range::{Range, RangeInt}; /// A value with a particular weight for use with `WeightedChoice`. #[derive(Copy, Clone, Debug)] @@ -49,7 +50,7 @@ pub struct Weighted { #[derive(Clone, Debug)] pub struct WeightedChoice { items: Vec>, - weight_range: Range + weight_range: Range>, } impl WeightedChoice { From 86de12b6f6efb10d403e005489e23ac473b7c11a Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Sun, 24 Sep 2017 20:34:12 +0200 Subject: [PATCH 122/247] Remove `reseed` from `SeedableRng` --- rand_core/src/lib.rs | 6 ------ rand_core/src/mock.rs | 3 --- src/prng/chacha.rs | 31 ++++++++----------------------- src/prng/isaac.rs | 38 +++++++++----------------------------- src/prng/isaac64.rs | 38 +++++++++----------------------------- src/prng/xorshift.rs | 11 ----------- src/reseeding.rs | 17 ----------------- 7 files changed, 26 insertions(+), 118 deletions(-) diff --git a/rand_core/src/lib.rs b/rand_core/src/lib.rs index 86f8eab9586..651f9b51335 100644 --- a/rand_core/src/lib.rs +++ b/rand_core/src/lib.rs @@ -172,12 +172,6 @@ pub trait SeedFromRng: Sized { /// algorithm used in the future. This is to ensure that manual seeding of PRNGs /// actually does yield reproducible results. pub trait SeedableRng: Rng { - /// Reseed an RNG with the given seed. - /// - /// The type of `Seed` is specified by the implementation (implementation - /// for multiple seed types is possible). - fn reseed(&mut self, Seed); - /// Create a new RNG with the given seed. /// /// The type of `Seed` is specified by the implementation (implementation diff --git a/rand_core/src/mock.rs b/rand_core/src/mock.rs index ecfea3184a5..5abe444aa8c 100644 --- a/rand_core/src/mock.rs +++ b/rand_core/src/mock.rs @@ -85,9 +85,6 @@ impl SeedableRng for MockAddRng where MockAddRng: Rng, T: From, // for 1.into() { - fn reseed(&mut self, seed: T) { - self.v = w(seed); - } fn from_seed(seed: T) -> Self { MockAddRng::new(seed, 1.into()) } diff --git a/src/prng/chacha.rs b/src/prng/chacha.rs index 2f0a5f78794..bfd7133e565 100644 --- a/src/prng/chacha.rs +++ b/src/prng/chacha.rs @@ -255,23 +255,20 @@ impl SeedFromRng for ChaChaRng { } impl<'a> SeedableRng<&'a [u32]> for ChaChaRng { - fn reseed(&mut self, seed: &'a [u32]) { - // reset state - self.init(&[0u32; KEY_WORDS]); - // set key in place - let key = &mut self.state[4 .. 4+KEY_WORDS]; - for (k, s) in key.iter_mut().zip(seed.iter()) { - *k = w(*s); - } - } - /// Create a ChaCha generator from a seed, /// obtained from a variable-length u32 array. /// Only up to 8 words are used; if less than 8 /// words are used, the remaining are set to zero. fn from_seed(seed: &'a [u32]) -> ChaChaRng { let mut rng = EMPTY; - rng.reseed(seed); + rng.init(&[0u32; KEY_WORDS]); + // set key in place + { + let key = &mut rng.state[4 .. 4+KEY_WORDS]; + for (k, s) in key.iter_mut().zip(seed.iter()) { + *k = w(*s); + } + } rng } } @@ -301,18 +298,6 @@ mod test { iter(&mut rb).map(|rng| ascii_word_char(rng)).take(100))); } - #[test] - fn test_rng_reseed() { - let s = iter(&mut ::test::rng()).map(|rng| rng.next_u32()).take(8).collect::>(); - let mut r: ChaChaRng = SeedableRng::from_seed(&s[..]); - let string1: String = iter(&mut r).map(|rng| ascii_word_char(rng)).take(100).collect(); - - r.reseed(&s); - - let string2: String = iter(&mut r).map(|rng| ascii_word_char(rng)).take(100).collect(); - assert_eq!(string1, string2); - } - #[test] fn test_rng_true_values() { // Test vectors 1 and 2 from diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index 4cba80d4daa..8b762a03b31 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -306,22 +306,6 @@ impl SeedFromRng for IsaacRng { } impl<'a> SeedableRng<&'a [u32]> for IsaacRng { - fn reseed(&mut self, seed: &'a [u32]) { - // make the seed into [seed[0], seed[1], ..., seed[seed.len() - // - 1], 0, 0, ...], to fill rng.rsl. - let seed_iter = seed.iter().map(|&x| x).chain(repeat(0u32)); - - for (rsl_elem, seed_elem) in self.rsl.iter_mut().zip(seed_iter) { - *rsl_elem = w(seed_elem); - } - self.cnt = 0; - self.a = w(0); - self.b = w(0); - self.c = w(0); - - self.init(true); - } - /// Create an ISAAC random number generator with a seed. This can /// be any length, although the maximum number of elements used is /// 256 and any more will be silently ignored. A generator @@ -329,7 +313,15 @@ impl<'a> SeedableRng<&'a [u32]> for IsaacRng { /// of values as all other generators constructed with that seed. fn from_seed(seed: &'a [u32]) -> IsaacRng { let mut rng = EMPTY; - rng.reseed(seed); + + // make the seed into [seed[0], seed[1], ..., seed[seed.len() + // - 1], 0, 0, ...], to fill rng.rsl. + let seed_iter = seed.iter().map(|&x| x).chain(repeat(0u32)); + + for (rsl_elem, seed_elem) in rng.rsl.iter_mut().zip(seed_iter) { + *rsl_elem = w(seed_elem); + } + rng.init(true); rng } } @@ -364,18 +356,6 @@ mod test { iter(&mut rb).map(|rng| ascii_word_char(rng)).take(100))); } - #[test] - fn test_rng_32_reseed() { - let s = iter(&mut ::test::rng()).map(|rng| rng.next_u32()).take(256).collect::>(); - let mut r: IsaacRng = SeedableRng::from_seed(&s[..]); - let string1: String = iter(&mut r).map(|rng| ascii_word_char(rng)).take(100).collect(); - - r.reseed(&s[..]); - - let string2: String = iter(&mut r).map(|rng| ascii_word_char(rng)).take(100).collect(); - assert_eq!(string1, string2); - } - #[test] fn test_rng_32_true_values() { let seed: &[_] = &[1, 23, 456, 7890, 12345]; diff --git a/src/prng/isaac64.rs b/src/prng/isaac64.rs index d7f6168392f..3a538e1f01c 100644 --- a/src/prng/isaac64.rs +++ b/src/prng/isaac64.rs @@ -292,22 +292,6 @@ impl SeedFromRng for Isaac64Rng { } impl<'a> SeedableRng<&'a [u64]> for Isaac64Rng { - fn reseed(&mut self, seed: &'a [u64]) { - // make the seed into [seed[0], seed[1], ..., seed[seed.len() - // - 1], 0, 0, ...], to fill rng.rsl. - let seed_iter = seed.iter().map(|&x| x).chain(repeat(0u64)); - - for (rsl_elem, seed_elem) in self.rsl.iter_mut().zip(seed_iter) { - *rsl_elem = w(seed_elem); - } - self.cnt = 0; - self.a = w(0); - self.b = w(0); - self.c = w(0); - - self.init(true); - } - /// Create an ISAAC random number generator with a seed. This can /// be any length, although the maximum number of elements used is /// 256 and any more will be silently ignored. A generator @@ -315,7 +299,15 @@ impl<'a> SeedableRng<&'a [u64]> for Isaac64Rng { /// of values as all other generators constructed with that seed. fn from_seed(seed: &'a [u64]) -> Isaac64Rng { let mut rng = EMPTY_64; - rng.reseed(seed); + + // make the seed into [seed[0], seed[1], ..., seed[seed.len() + // - 1], 0, 0, ...], to fill rng.rsl. + let seed_iter = seed.iter().map(|&x| x).chain(repeat(0u64)); + + for (rsl_elem, seed_elem) in rng.rsl.iter_mut().zip(seed_iter) { + *rsl_elem = w(seed_elem); + } + rng.init(true); rng } } @@ -350,18 +342,6 @@ mod test { iter(&mut rb).map(|rng| ascii_word_char(rng)).take(100))); } - #[test] - fn test_rng_64_reseed() { - let s = iter(&mut ::test::rng()).map(|rng| rng.next_u64()).take(256).collect::>(); - let mut r: Isaac64Rng = SeedableRng::from_seed(&s[..]); - let string1: String = iter(&mut r).map(|rng| ascii_word_char(rng)).take(100).collect(); - - r.reseed(&s[..]); - - let string2: String = iter(&mut r).map(|rng| ascii_word_char(rng)).take(100).collect(); - assert_eq!(string1, string2); - } - #[test] fn test_rng_64_true_values() { let seed: &[_] = &[1, 23, 456, 7890, 12345]; diff --git a/src/prng/xorshift.rs b/src/prng/xorshift.rs index 1e2d6cca252..6181ad646fa 100644 --- a/src/prng/xorshift.rs +++ b/src/prng/xorshift.rs @@ -90,17 +90,6 @@ impl Rng for XorShiftRng { } impl SeedableRng<[u32; 4]> for XorShiftRng { - /// Reseed an XorShiftRng. This will panic if `seed` is entirely 0. - fn reseed(&mut self, seed: [u32; 4]) { - assert!(!seed.iter().all(|&x| x == 0), - "XorShiftRng.reseed called with an all zero seed."); - - self.x = w(seed[0]); - self.y = w(seed[1]); - self.z = w(seed[2]); - self.w = w(seed[3]); - } - /// Create a new XorShiftRng. This will panic if `seed` is entirely 0. fn from_seed(seed: [u32; 4]) -> XorShiftRng { assert!(!seed.iter().all(|&x| x == 0), diff --git a/src/reseeding.rs b/src/reseeding.rs index 2d38088024b..b01bab7067c 100644 --- a/src/reseeding.rs +++ b/src/reseeding.rs @@ -90,12 +90,6 @@ impl> Rng for ReseedingRng { impl, Rsdr: Reseeder> SeedableRng<(Rsdr, S)> for ReseedingRng { - fn reseed(&mut self, (rsdr, seed): (Rsdr, S)) { - self.rng.reseed(seed); - self.reseeder = rsdr; - self.bytes_generated = 0; - } - /// Create a new `ReseedingRng` from the given reseeder and /// seed. This uses a default value for `generation_threshold`. fn from_seed((rsdr, seed): (Rsdr, S)) -> ReseedingRng { @@ -167,17 +161,6 @@ mod test { iter(&mut rb).map(|rng| ascii_word_char(rng)).take(100))); } - #[test] - fn test_rng_reseed() { - let mut r: MyRng = SeedableRng::from_seed((ReseedMock, 3)); - let string1: String = iter(&mut r).map(|rng| ascii_word_char(rng)).take(100).collect(); - - r.reseed((ReseedMock, 3)); - - let string2: String = iter(&mut r).map(|rng| ascii_word_char(rng)).take(100).collect(); - assert_eq!(string1, string2); - } - const FILL_BYTES_V_LEN: usize = 13579; #[test] fn test_rng_try_fill() { From e3784abbe4668f4e65de3e72af6bf3e754c4d586 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Wed, 18 Oct 2017 17:10:50 +0100 Subject: [PATCH 123/247] Fix FlatMap::size_hint (thanks @bluss) --- src/iter.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/iter.rs b/src/iter.rs index 70bfd8e9b66..a819d33cdf7 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -145,8 +145,15 @@ impl<'a, R:?Sized+'a, U, F> Iterator for FlatMap<'a, R, U, F> } fn size_hint(&self) -> (usize, Option) { - // See impl for Map above - self.len.map_or((usize::MAX, None), |len| (len, Some(len))) + if self.len == Some(0) { + // No new iters, so we have frontiter or nothing + self.frontiter.as_ref().map_or((0, Some(0)), |it| it.size_hint()) + } else { + // Can't compute an actual bound without producing the sub-iters, + // which we don't want to do. But we may have a lower bound. + let lb = self.frontiter.as_ref().map_or(0, |it| it.size_hint().0); + (lb, None) + } } } From 496c0fc1f479a2510c2e325171851bb245290b02 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Thu, 19 Oct 2017 18:15:41 +0200 Subject: [PATCH 124/247] Fix i128_support in range.rs --- src/distributions/range.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/distributions/range.rs b/src/distributions/range.rs index ba8b3130107..e30d69fbace 100644 --- a/src/distributions/range.rs +++ b/src/distributions/range.rs @@ -210,7 +210,7 @@ range_int_impl! { i16, i16, u16, i32, u32 } range_int_impl! { i32, i32, u32, i32, u32 } range_int_impl! { i64, i64, u64, i64, u64 } #[cfg(feature = "i128_support")] -range_int_impl! { i128, i128, u128, u128 } +range_int_impl! { i128, i128, u128, u128, u128 } range_int_impl! { isize, isize, usize, isize, usize } range_int_impl! { u8, i8, u8, i32, u32 } range_int_impl! { u16, i16, u16, i32, u32 } From a6528a3a8e174b7b13952fd249b4415a7e9b5f1c Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Thu, 19 Oct 2017 21:09:49 +0200 Subject: [PATCH 125/247] Add initialization benchmarks --- benches/generators.rs | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/benches/generators.rs b/benches/generators.rs index af55a0f78f9..44adb0e1c2d 100644 --- a/benches/generators.rs +++ b/benches/generators.rs @@ -9,7 +9,7 @@ const BYTES_LEN: usize = 1024; use std::mem::size_of; use test::{black_box, Bencher}; -use rand::{Rng, NewSeeded, StdRng, OsRng, Rand, Default}; +use rand::{Rng, NewSeeded, SeedFromRng, StdRng, OsRng, Rand, Default}; use rand::prng::{XorShiftRng, IsaacRng, Isaac64Rng, ChaChaRng}; macro_rules! gen_bytes { @@ -58,3 +58,23 @@ gen_usize!(gen_usize_isaac64, Isaac64Rng); gen_usize!(gen_usize_chacha, ChaChaRng); gen_usize!(gen_usize_std, StdRng); gen_usize!(gen_usize_os, OsRng); + +macro_rules! init_gen { + ($fnn:ident, $gen:ident) => { + #[bench] + fn $fnn(b: &mut Bencher) { + let mut rng = XorShiftRng::new().unwrap(); + b.iter(|| { + for _ in 0..RAND_BENCH_N { + black_box($gen::from_rng(&mut rng).unwrap()); + } + }); + } + } +} + +init_gen!(init_xorshift, XorShiftRng); +init_gen!(init_isaac, IsaacRng); +init_gen!(init_isaac64, Isaac64Rng); +init_gen!(init_chacha, ChaChaRng); +init_gen!(init_std, StdRng); From 08ac7501ec73bff36d99624ffc672fa120450e86 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Thu, 19 Oct 2017 21:13:22 +0200 Subject: [PATCH 126/247] Clean up ISAAC tests --- src/prng/isaac.rs | 59 ++++++++++++++++++++++----------------- src/prng/isaac64.rs | 68 +++++++++++++++++++++++++-------------------- 2 files changed, 71 insertions(+), 56 deletions(-) diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index 8b762a03b31..79a4983f58c 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -335,55 +335,62 @@ impl fmt::Debug for IsaacRng { #[cfg(test)] mod test { use {Rng, SeedableRng, iter}; - use distributions::ascii_word_char; use super::IsaacRng; #[test] - fn test_rng_32_rand_seeded() { - let s = iter(&mut ::test::rng()).map(|rng| rng.next_u32()).take(256).collect::>(); - let mut ra: IsaacRng = SeedableRng::from_seed(&s[..]); - let mut rb: IsaacRng = SeedableRng::from_seed(&s[..]); - assert!(::test::iter_eq(iter(&mut ra).map(|rng| ascii_word_char(rng)).take(100), - iter(&mut rb).map(|rng| ascii_word_char(rng)).take(100))); + fn test_isaac_from_seed() { + let seed = iter(&mut ::test::rng()) + .map(|rng| rng.next_u32()) + .take(256) + .collect::>(); + let mut rng1 = IsaacRng::from_seed(&seed[..]); + let mut rng2 = IsaacRng::from_seed(&seed[..]); + for _ in 0..100 { + assert_eq!(rng1.next_u32(), rng2.next_u32()); + } } #[test] - fn test_rng_32_seeded() { + fn test_isaac_from_seed_fixed() { let seed: &[_] = &[1, 23, 456, 7890, 12345]; - let mut ra: IsaacRng = SeedableRng::from_seed(seed); - let mut rb: IsaacRng = SeedableRng::from_seed(seed); - assert!(::test::iter_eq(iter(&mut ra).map(|rng| ascii_word_char(rng)).take(100), - iter(&mut rb).map(|rng| ascii_word_char(rng)).take(100))); + let mut rng1 = IsaacRng::from_seed(&seed[..]); + let mut rng2 = IsaacRng::from_seed(&seed[..]); + for _ in 0..100 { + assert_eq!(rng1.next_u32(), rng2.next_u32()); + } } #[test] - fn test_rng_32_true_values() { + fn test_isaac_true_values() { let seed: &[_] = &[1, 23, 456, 7890, 12345]; - let mut ra: IsaacRng = SeedableRng::from_seed(seed); + let mut rng1 = IsaacRng::from_seed(seed); // Regression test that isaac is actually using the above vector - let v = (0..10).map(|_| ra.next_u32()).collect::>(); + let v = (0..10).map(|_| rng1.next_u32()).collect::>(); assert_eq!(v, - vec!(2558573138, 873787463, 263499565, 2103644246, 3595684709, - 4203127393, 264982119, 2765226902, 2737944514, 3900253796)); + vec!(2558573138, 873787463, 263499565, 2103644246, + 3595684709, 4203127393, 264982119, 2765226902, + 2737944514, 3900253796)); let seed: &[_] = &[12345, 67890, 54321, 9876]; - let mut rb: IsaacRng = SeedableRng::from_seed(seed); + let mut rng2 = IsaacRng::from_seed(seed); // skip forward to the 10000th number - for _ in 0..10000 { rb.next_u32(); } + for _ in 0..10000 { rng2.next_u32(); } - let v = (0..10).map(|_| rb.next_u32()).collect::>(); + let v = (0..10).map(|_| rng2.next_u32()).collect::>(); assert_eq!(v, - vec!(3676831399, 3183332890, 2834741178, 3854698763, 2717568474, - 1576568959, 3507990155, 179069555, 141456972, 2478885421)); + vec!(3676831399, 3183332890, 2834741178, 3854698763, + 2717568474, 1576568959, 3507990155, 179069555, + 141456972, 2478885421)); + } } #[test] - fn test_rng_clone() { + fn test_isaac_clone() { let seed: &[_] = &[1, 23, 456, 7890, 12345]; - let mut rng: IsaacRng = SeedableRng::from_seed(seed); - let mut clone = rng.clone(); + let mut rng1 = IsaacRng::from_seed(seed); + let mut rng2 = rng1.clone(); for _ in 0..16 { - assert_eq!(rng.next_u32(), clone.next_u32()); + assert_eq!(rng1.next_u32(), rng2.next_u32()); } } } diff --git a/src/prng/isaac64.rs b/src/prng/isaac64.rs index 3a538e1f01c..9fea0304f04 100644 --- a/src/prng/isaac64.rs +++ b/src/prng/isaac64.rs @@ -321,59 +321,67 @@ impl fmt::Debug for Isaac64Rng { #[cfg(test)] mod test { use {Rng, SeedableRng, iter}; - use distributions::ascii_word_char; use super::Isaac64Rng; #[test] - fn test_rng_64_rand_seeded() { - let s = iter(&mut ::test::rng()).map(|rng| rng.next_u64()).take(256).collect::>(); - let mut ra: Isaac64Rng = SeedableRng::from_seed(&s[..]); - let mut rb: Isaac64Rng = SeedableRng::from_seed(&s[..]); - assert!(::test::iter_eq(iter(&mut ra).map(|rng| ascii_word_char(rng)).take(100), - iter(&mut rb).map(|rng| ascii_word_char(rng)).take(100))); + fn test_isaac64_from_seed() { + let seed = iter(&mut ::test::rng()) + .map(|rng| rng.next_u64()) + .take(256) + .collect::>(); + let mut rng1 = Isaac64Rng::from_seed(&seed[..]); + let mut rng2 = Isaac64Rng::from_seed(&seed[..]); + for _ in 0..100 { + assert_eq!(rng1.next_u64(), rng2.next_u64()); + } } #[test] - fn test_rng_64_seeded() { + fn test_isaac64_from_seed_fixed() { let seed: &[_] = &[1, 23, 456, 7890, 12345]; - let mut ra: Isaac64Rng = SeedableRng::from_seed(seed); - let mut rb: Isaac64Rng = SeedableRng::from_seed(seed); - assert!(::test::iter_eq(iter(&mut ra).map(|rng| ascii_word_char(rng)).take(100), - iter(&mut rb).map(|rng| ascii_word_char(rng)).take(100))); + let mut rng1 = Isaac64Rng::from_seed(&seed[..]); + let mut rng2 = Isaac64Rng::from_seed(&seed[..]); + for _ in 0..100 { + assert_eq!(rng1.next_u64(), rng2.next_u64()); + } + } } #[test] - fn test_rng_64_true_values() { + fn test_isaac64_true_values() { let seed: &[_] = &[1, 23, 456, 7890, 12345]; - let mut ra: Isaac64Rng = SeedableRng::from_seed(seed); + let mut rng1 = Isaac64Rng::from_seed(seed); // Regression test that isaac is actually using the above vector - let v = (0..10).map(|_| ra.next_u64()).collect::>(); + let v = (0..10).map(|_| rng1.next_u64()).collect::>(); assert_eq!(v, - vec!(547121783600835980, 14377643087320773276, 17351601304698403469, - 1238879483818134882, 11952566807690396487, 13970131091560099343, - 4469761996653280935, 15552757044682284409, 6860251611068737823, - 13722198873481261842)); + vec!(547121783600835980, 14377643087320773276, + 17351601304698403469, 1238879483818134882, + 11952566807690396487, 13970131091560099343, + 4469761996653280935, 15552757044682284409, + 6860251611068737823, 13722198873481261842)); let seed: &[_] = &[12345, 67890, 54321, 9876]; - let mut rb: Isaac64Rng = SeedableRng::from_seed(seed); + let mut rng2 = Isaac64Rng::from_seed(seed); // skip forward to the 10000th number - for _ in 0..10000 { rb.next_u64(); } + for _ in 0..10000 { rng2.next_u64(); } - let v = (0..10).map(|_| rb.next_u64()).collect::>(); + let v = (0..10).map(|_| rng2.next_u64()).collect::>(); assert_eq!(v, - vec!(18143823860592706164, 8491801882678285927, 2699425367717515619, - 17196852593171130876, 2606123525235546165, 15790932315217671084, - 596345674630742204, 9947027391921273664, 11788097613744130851, - 10391409374914919106)); + vec!(18143823860592706164, 8491801882678285927, + 2699425367717515619, 17196852593171130876, + 2606123525235546165, 15790932315217671084, + 596345674630742204, 9947027391921273664, + 11788097613744130851, 10391409374914919106)); + } } #[test] - fn test_rng_clone() { + fn test_isaac64_clone() { let seed: &[_] = &[1, 23, 456, 7890, 12345]; - let mut rng: Isaac64Rng = SeedableRng::from_seed(seed); - let mut clone = rng.clone(); + let mut rng1 = Isaac64Rng::from_seed(seed); + let mut rng2 = rng1.clone(); for _ in 0..16 { - assert_eq!(rng.next_u64(), clone.next_u64()); + assert_eq!(rng1.next_u64(), rng2.next_u64()); } } } From 2e714f285d5d8b6b8d7b739a73f2472936f952be Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Thu, 19 Oct 2017 19:29:57 +0200 Subject: [PATCH 127/247] Remove `range2` from benchmarks --- benches/distributions.rs | 26 ++++++-------------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/benches/distributions.rs b/benches/distributions.rs index 687ad97172a..05265d688e9 100644 --- a/benches/distributions.rs +++ b/benches/distributions.rs @@ -25,25 +25,12 @@ fn distr_baseline(b: &mut Bencher) { b.bytes = size_of::() as u64 * ::RAND_BENCH_N; } -#[bench] -fn distr_range_int(b: &mut Bencher) { - let mut rng = XorShiftRng::new().unwrap(); - let distr = Range::new(3i64, 134217671i64); - - b.iter(|| { - for _ in 0..::RAND_BENCH_N { - black_box(distr.sample(&mut rng)); - } - }); - b.bytes = size_of::() as u64 * ::RAND_BENCH_N; -} - -macro_rules! distr_range2_int { +macro_rules! distr_range_int { ($fnn:ident, $ty:ty, $low:expr, $high:expr) => { #[bench] fn $fnn(b: &mut Bencher) { let mut rng = XorShiftRng::new().unwrap(); - let distr = range2::Range::new($low, $high); + let distr = Range::new($low, $high); b.iter(|| { for _ in 0..::RAND_BENCH_N { @@ -56,10 +43,10 @@ macro_rules! distr_range2_int { } } -distr_range2_int!(distr_range2_i8, i8, 20i8, 100); -distr_range2_int!(distr_range2_i16, i16, -500i16, 2000); -distr_range2_int!(distr_range2_i32, i32, -200_000_000i32, 800_000_000); -distr_range2_int!(distr_range2_i64, i64, 3i64, 134217671); +distr_range_int!(distr_range_i8, i8, 20i8, 100); +distr_range_int!(distr_range_i16, i16, -500i16, 2000); +distr_range_int!(distr_range_i32, i32, -200_000_000i32, 800_000_000); +distr_range_int!(distr_range_i64, i64, 3i64, 134217671); macro_rules! distr_float { ($fnn:ident, $distr:expr) => { @@ -83,7 +70,6 @@ distr_float!(distr_uniform01_float, Uniform01); distr_float!(distr_closed01_float, Closed01); distr_float!(distr_open01_float, Open01); distr_float!(distr_range_float, Range::new(2.26f64, 2.319f64)); -distr_float!(distr_range2_float, range2::Range::new(2.26f64, 2.319f64)); distr_float!(distr_exp, Exp::new(2.71828 * 3.14159)); distr_float!(distr_normal, Normal::new(-2.71828, 3.14159)); distr_float!(distr_log_normal, LogNormal::new(-2.71828, 3.14159)); From adcd8e56595a6e1c27c575620fdc162b42260037 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Thu, 19 Oct 2017 21:16:12 +0200 Subject: [PATCH 128/247] Add `new_from_u64` to `IsaacRng` and `Isaac64Rng` I have implemented it as a function instead of a trait, as that makes it easy to add it to every RNG ont at a time. I split the `init` function in two instead of the current version that uses a bool to select between two paths. This makes it more clear how the seed is used. The current `mix` macro has to be defined in the function, and would have to be duplicated. Therefore I converted it to a seperate function. I precalculated the values a...h, but am not sure this is a good idea. It makes the resulting code smaller, and gives a small performance win. Because it are 'magic' values anyway, I thought why not? --- src/lib.rs | 2 +- src/prng/isaac.rs | 273 ++++++++++++++++++++++++++--------------- src/prng/isaac64.rs | 249 ++++++++++++++++++++++--------------- src/prng/isaac_word.rs | 15 +-- 4 files changed, 323 insertions(+), 216 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 4d3e432cd75..cd5634f30c5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -400,7 +400,7 @@ impl Sample for R { /// The underlying algorithm is not fixed, thus values from this generator /// cannot be guaranteed to be reproducible. For this reason, `StdRng` does /// not support `SeedableRng`. -#[derive(Copy, Clone, Debug)] +#[derive(Clone, Debug)] pub struct StdRng { rng: IsaacWordRng, } diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index 79a4983f58c..310aac1d8c1 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -86,8 +86,6 @@ const RAND_SIZE: usize = 1 << RAND_SIZE_LEN; /// /// [3]: Jean-Philippe Aumasson, [*On the pseudo-random generator ISAAC*] /// (http://eprint.iacr.org/2006/438) - -#[derive(Copy)] pub struct IsaacRng { rsl: [w32; RAND_SIZE], mem: [w32; RAND_SIZE], @@ -97,82 +95,82 @@ pub struct IsaacRng { cnt: u32, } -static EMPTY: IsaacRng = IsaacRng { - cnt: 0, - rsl: [w(0); RAND_SIZE], - mem: [w(0); RAND_SIZE], - a: w(0), b: w(0), c: w(0), -}; - -impl IsaacRng { - /// Create an ISAAC random number generator using the default - /// fixed seed. - pub fn new_unseeded() -> IsaacRng { - let mut rng = EMPTY; - rng.init(false); - rng - } - - /// Initialises `self`. If `use_rsl` is true, then use the current value - /// of `rsl` as a seed, otherwise construct one algorithmically (not - /// randomly). - fn init(&mut self, use_rsl: bool) { - let mut a = w(0x9e3779b9); // golden ratio - let mut b = a; - let mut c = a; - let mut d = a; - let mut e = a; - let mut f = a; - let mut g = a; - let mut h = a; - - macro_rules! mix { - () => {{ - a ^= b << 11; d += a; b += c; - b ^= c >> 2; e += b; c += d; - c ^= d << 8; f += c; d += e; - d ^= e >> 16; g += d; e += f; - e ^= f << 10; h += e; f += g; - f ^= g >> 4; a += f; g += h; - g ^= h << 8; b += g; h += a; - h ^= a >> 9; c += h; a += b; - }} +// Cannot be derived because [u32; 256] does not implement Clone +impl Clone for IsaacRng { + fn clone(&self) -> IsaacRng { + IsaacRng { + rsl: self.rsl, + mem: self.mem, + a: self.a, + b: self.b, + c: self.c, + cnt: self.cnt, } + } +} - for _ in 0..4 { - mix!(); - } +impl fmt::Debug for IsaacRng { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "IsaacRng {{}}") + } +} - if use_rsl { - macro_rules! memloop { - ($arr:expr) => {{ - for i in (0..RAND_SIZE/8).map(|i| i * 8) { - a += $arr[i ]; b += $arr[i+1]; - c += $arr[i+2]; d += $arr[i+3]; - e += $arr[i+4]; f += $arr[i+5]; - g += $arr[i+6]; h += $arr[i+7]; - mix!(); - self.mem[i ] = a; self.mem[i+1] = b; - self.mem[i+2] = c; self.mem[i+3] = d; - self.mem[i+4] = e; self.mem[i+5] = f; - self.mem[i+6] = g; self.mem[i+7] = h; - } - }} - } +fn mix(a: &mut w32, b: &mut w32, c: &mut w32, d: &mut w32, + e: &mut w32, f: &mut w32, g: &mut w32, h: &mut w32) { + *a ^= *b << 11; *d += *a; *b += *c; + *b ^= *c >> 2; *e += *b; *c += *d; + *c ^= *d << 8; *f += *c; *d += *e; + *d ^= *e >> 16; *g += *d; *e += *f; + *e ^= *f << 10; *h += *e; *f += *g; + *f ^= *g >> 4; *a += *f; *g += *h; + *g ^= *h << 8; *b += *g; *h += *a; + *h ^= *a >> 9; *c += *h; *a += *b; +} - memloop!(self.rsl); - memloop!(self.mem); - } else { - for i in (0..RAND_SIZE/8).map(|i| i * 8) { - mix!(); - self.mem[i ] = a; self.mem[i+1] = b; - self.mem[i+2] = c; self.mem[i+3] = d; - self.mem[i+4] = e; self.mem[i+5] = f; - self.mem[i+6] = g; self.mem[i+7] = h; - } +impl IsaacRng { + /// Creates an ISAAC random number generator using an u64 as seed. + /// If `seed == 0` this will produce the same stream of random numbers as + /// the reference implementation when used unseeded. + pub fn new_from_u64(seed: u64) -> IsaacRng { + let mut a = w(0x1367df5a); + let mut b = w(0x95d90059); + let mut c = w(0xc3163e4b); + let mut d = w(0x0f421ad8); + let mut e = w(0xd92a4a78); + let mut f = w(0xa51a3c49); + let mut g = w(0xc4efea1b); + let mut h = w(0x30609119); + + let mut mem = [w(0); RAND_SIZE]; + + a += w(seed as u32); + b += w((seed >> 32) as u32); + + for i in (0..RAND_SIZE/8).map(|i| i * 8) { + mix(&mut a, &mut b, &mut c, &mut d, &mut e, &mut f, &mut g, &mut h); + mem[i ] = a; mem[i+1] = b; + mem[i+2] = c; mem[i+3] = d; + mem[i+4] = e; mem[i+5] = f; + mem[i+6] = g; mem[i+7] = h; } - - self.isaac(); + // A second pass does not improve the quality here, because all of + // the seed was already available in the first round. + // Not doing the second pass has the small advantage that if `seed == 0` + // this method produces exactly the same state as the reference + // implementation when used unseeded. + + let mut rng = IsaacRng { + rsl: [w(0); RAND_SIZE], + mem: mem, + a: w(0), + b: w(0), + c: w(0), + cnt: 0, + }; + + // Prepare the first set of results + rng.isaac(); + rng } /// Refills the output buffer (`self.rsl`) @@ -243,13 +241,6 @@ impl IsaacRng { } } -// Cannot be derived because [u32; 256] does not implement Clone -impl Clone for IsaacRng { - fn clone(&self) -> IsaacRng { - *self - } -} - impl Rng for IsaacRng { #[inline] fn next_u32(&mut self) -> u32 { @@ -272,36 +263,108 @@ impl Rng for IsaacRng { // it optimises to a bitwise mask). self.rsl[self.cnt as usize % RAND_SIZE].0 } - + fn next_u64(&mut self) -> u64 { ::rand_core::impls::next_u64_via_u32(self) } + #[cfg(feature = "i128_support")] fn next_u128(&mut self) -> u128 { ::rand_core::impls::next_u128_via_u64(self) } - + fn try_fill(&mut self, dest: &mut [u8]) -> Result<()> { ::rand_core::impls::try_fill_via_u32(self, dest) } } +/// Creates a new ISAAC-64 random number generator. +/// +/// The author Bob Jenkins describes how to best initialize ISAAC here: +/// https://rt.cpan.org/Public/Bug/Display.html?id=64324 +/// The answer is included here just in case: +/// +/// "No, you don't need a full 8192 bits of seed data. Normal key sizes will do +/// fine, and they should have their expected strength (eg a 40-bit key will +/// take as much time to brute force as 40-bit keys usually will). You could +/// fill the remainder with 0, but set the last array element to the length of +/// the key provided (to distinguish keys that differ only by different amounts +/// of 0 padding). You do still need to call randinit() to make sure the initial +/// state isn't uniform-looking." +/// "After publishing ISAAC, I wanted to limit the key to half the size of r[], +/// and repeat it twice. That would have made it hard to provide a key that sets +/// the whole internal state to anything convenient. But I'd already published +/// it." +/// +/// And his answer to the question "For my code, would repeating the key over +/// and over to fill 256 integers be a better solution than zero-filling, or +/// would they essentially be the same?": +/// "If the seed is under 32 bytes, they're essentially the same, otherwise +/// repeating the seed would be stronger. randinit() takes a chunk of 32 bytes, +/// mixes it, and combines that with the next 32 bytes, et cetera. Then loops +/// over all the elements the same way a second time." +#[inline] +fn init(key: [w32; RAND_SIZE]) -> IsaacRng { + // These numbers are the result of initializing a...h with the + // fractional part of the golden ratio in binary (0x9e3779b9) + // and applying mix() 4 times. + let mut a = w(0x1367df5a); + let mut b = w(0x95d90059); + let mut c = w(0xc3163e4b); + let mut d = w(0x0f421ad8); + let mut e = w(0xd92a4a78); + let mut f = w(0xa51a3c49); + let mut g = w(0xc4efea1b); + let mut h = w(0x30609119); + + let mut mem = [w(0); RAND_SIZE]; + + macro_rules! memloop { + ($arr:expr) => {{ + for i in (0..RAND_SIZE/8).map(|i| i * 8) { + a += $arr[i ]; b += $arr[i+1]; + c += $arr[i+2]; d += $arr[i+3]; + e += $arr[i+4]; f += $arr[i+5]; + g += $arr[i+6]; h += $arr[i+7]; + mix(&mut a, &mut b, &mut c, &mut d, + &mut e, &mut f, &mut g, &mut h); + mem[i ] = a; mem[i+1] = b; + mem[i+2] = c; mem[i+3] = d; + mem[i+4] = e; mem[i+5] = f; + mem[i+6] = g; mem[i+7] = h; + } + }} + } + + memloop!(key); + // Do a second pass to make all of the seed affect all of `mem` + memloop!(mem); + + let mut rng = IsaacRng { + rsl: [w(0); RAND_SIZE], + mem: mem, + a: w(0), + b: w(0), + c: w(0), + cnt: 0, + }; + + // Prepare the first set of results + rng.isaac(); + rng +} + impl SeedFromRng for IsaacRng { fn from_rng(other: &mut R) -> Result { - let mut ret = EMPTY; + let mut key = [w(0); RAND_SIZE]; unsafe { - let ptr = ret.rsl.as_mut_ptr() as *mut u8; + let ptr = key.as_mut_ptr() as *mut u8; let slice = slice::from_raw_parts_mut(ptr, RAND_SIZE * 4); other.try_fill(slice)?; } - ret.cnt = 0; - ret.a = w(0); - ret.b = w(0); - ret.c = w(0); - ret.init(true); - Ok(ret) + Ok(init(key)) } } @@ -312,23 +375,17 @@ impl<'a> SeedableRng<&'a [u32]> for IsaacRng { /// constructed with a given seed will generate the same sequence /// of values as all other generators constructed with that seed. fn from_seed(seed: &'a [u32]) -> IsaacRng { - let mut rng = EMPTY; + let mut key = [w(0); RAND_SIZE]; // make the seed into [seed[0], seed[1], ..., seed[seed.len() - // - 1], 0, 0, ...], to fill rng.rsl. + // - 1], 0, 0, ...], to fill `key`. let seed_iter = seed.iter().map(|&x| x).chain(repeat(0u32)); - for (rsl_elem, seed_elem) in rng.rsl.iter_mut().zip(seed_iter) { + for (rsl_elem, seed_elem) in key.iter_mut().zip(seed_iter) { *rsl_elem = w(seed_elem); } - rng.init(true); - rng - } -} -impl fmt::Debug for IsaacRng { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "IsaacRng {{}}") + init(key) } } @@ -382,6 +439,22 @@ mod test { 2717568474, 1576568959, 3507990155, 179069555, 141456972, 2478885421)); } + + #[test] + fn test_isaac_new_uninitialized() { + // Compare the results from initializing `IsaacRng` with + // `new_from_u64(0)`, to make sure it is the same as the reference + // implementation when used uninitialized. + // Note: We only test the first 16 integers, not the full 256 of the + // first block. + let mut rng = IsaacRng::new_from_u64(0); + let vec = (0..16).map(|_| rng.next_u32()).collect::>(); + let expected: [u32; 16] = [ + 0x71D71FD2, 0xB54ADAE7, 0xD4788559, 0xC36129FA, + 0x21DC1EA9, 0x3CB879CA, 0xD83B237F, 0xFA3CE5BD, + 0x8D048509, 0xD82E9489, 0xDB452848, 0xCA20E846, + 0x500F972E, 0x0EEFF940, 0x00D6B993, 0xBC12C17F]; + assert_eq!(vec, expected); } #[test] diff --git a/src/prng/isaac64.rs b/src/prng/isaac64.rs index 9fea0304f04..24c48256066 100644 --- a/src/prng/isaac64.rs +++ b/src/prng/isaac64.rs @@ -70,8 +70,6 @@ const RAND_SIZE: usize = 1 << RAND_SIZE_LEN; /// /// [1]: Bob Jenkins, [*ISAAC and RC4*] /// (http://burtleburtle.net/bob/rand/isaac.html) - -#[derive(Copy)] pub struct Isaac64Rng { rsl: [w64; RAND_SIZE], mem: [w64; RAND_SIZE], @@ -81,82 +79,81 @@ pub struct Isaac64Rng { cnt: u32, } -static EMPTY_64: Isaac64Rng = Isaac64Rng { - cnt: 0, - rsl: [w(0); RAND_SIZE], - mem: [w(0); RAND_SIZE], - a: w(0), b: w(0), c: w(0), -}; - -impl Isaac64Rng { - /// Create a 64-bit ISAAC random number generator using the - /// default fixed seed. - pub fn new_unseeded() -> Isaac64Rng { - let mut rng = EMPTY_64; - rng.init(false); - rng - } - - /// Initialises `self`. If `use_rsl` is true, then use the current value - /// of `rsl` as a seed, otherwise construct one algorithmically (not - /// randomly). - fn init(&mut self, use_rsl: bool) { - let mut a = w(0x9e3779b97f4a7c13); // golden ratio - let mut b = a; - let mut c = a; - let mut d = a; - let mut e = a; - let mut f = a; - let mut g = a; - let mut h = a; - - macro_rules! mix { - () => {{ - a -= e; f ^= h >> 9; h += a; - b -= f; g ^= a << 9; a += b; - c -= g; h ^= b >> 23; b += c; - d -= h; a ^= c << 15; c += d; - e -= a; b ^= d >> 14; d += e; - f -= b; c ^= e << 20; e += f; - g -= c; d ^= f >> 17; f += g; - h -= d; e ^= g << 14; g += h; - }} +// Cannot be derived because [u64; 256] does not implement Clone +impl Clone for Isaac64Rng { + fn clone(&self) -> Isaac64Rng { + Isaac64Rng { + rsl: self.rsl, + mem: self.mem, + a: self.a, + b: self.b, + c: self.c, + cnt: self.cnt, } + } +} - for _ in 0..4 { - mix!(); - } +impl fmt::Debug for Isaac64Rng { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Isaac64Rng {{}}") + } +} - if use_rsl { - macro_rules! memloop { - ($arr:expr) => {{ - for i in (0..RAND_SIZE/8).map(|i| i * 8) { - a += $arr[i ]; b += $arr[i+1]; - c += $arr[i+2]; d += $arr[i+3]; - e += $arr[i+4]; f += $arr[i+5]; - g += $arr[i+6]; h += $arr[i+7]; - mix!(); - self.mem[i ] = a; self.mem[i+1] = b; - self.mem[i+2] = c; self.mem[i+3] = d; - self.mem[i+4] = e; self.mem[i+5] = f; - self.mem[i+6] = g; self.mem[i+7] = h; - } - }} - } +fn mix(a: &mut w64, b: &mut w64, c: &mut w64, d: &mut w64, + e: &mut w64, f: &mut w64, g: &mut w64, h: &mut w64) { + *a -= *e; *f ^= *h >> 9; *h += *a; + *b -= *f; *g ^= *a << 9; *a += *b; + *c -= *g; *h ^= *b >> 23; *b += *c; + *d -= *h; *a ^= *c << 15; *c += *d; + *e -= *a; *b ^= *d >> 14; *d += *e; + *f -= *b; *c ^= *e << 20; *e += *f; + *g -= *c; *d ^= *f >> 17; *f += *g; + *h -= *d; *e ^= *g << 14; *g += *h; +} - memloop!(self.rsl); - memloop!(self.mem); - } else { - for i in (0..RAND_SIZE/8).map(|i| i * 8) { - mix!(); - self.mem[i ] = a; self.mem[i+1] = b; - self.mem[i+2] = c; self.mem[i+3] = d; - self.mem[i+4] = e; self.mem[i+5] = f; - self.mem[i+6] = g; self.mem[i+7] = h; - } +impl Isaac64Rng { + /// Creates an ISAAC-64 random number generator using an u64 as seed. + /// If `seed == 0` this will produce the same stream of random numbers as + /// the reference implementation when used unseeded. + pub fn new_from_u64(seed: u64) -> Isaac64Rng { + let mut a = w(0x647c4677a2884b7c); + let mut b = w(0xb9f8b322c73ac862); + let mut c = w(0x8c0ea5053d4712a0); + let mut d = w(0xb29b2e824a595524); + let mut e = w(0x82f053db8355e0ce); + let mut f = w(0x48fe4a0fa5a09315); + let mut g = w(0xae985bf2cbfc89ed); + let mut h = w(0x98f5704f6c44c0ab); + + let mut mem = [w(0); RAND_SIZE]; + + a += w(seed); + + for i in (0..RAND_SIZE/8).map(|i| i * 8) { + mix(&mut a, &mut b, &mut c, &mut d, &mut e, &mut f, &mut g, &mut h); + mem[i ] = a; mem[i+1] = b; + mem[i+2] = c; mem[i+3] = d; + mem[i+4] = e; mem[i+5] = f; + mem[i+6] = g; mem[i+7] = h; } - - self.isaac64(); + // A second pass does not improve the quality here, because all of + // the seed was already available in the first round. + // Not doing the second pass has the small advantage that if `seed == 0` + // this method produces exactly the same state as the reference + // implementation when used unseeded. + + let mut rng = Isaac64Rng { + rsl: [w(0); RAND_SIZE], + mem: mem, + a: w(0), + b: w(0), + c: w(0), + cnt: 0, + }; + + // Prepare the first set of results + rng.isaac64(); + rng } /// Refills the output buffer (`self.rsl`) @@ -227,13 +224,6 @@ impl Isaac64Rng { } } -// Cannot be derived because [u32; 256] does not implement Clone -impl Clone for Isaac64Rng { - fn clone(&self) -> Isaac64Rng { - *self - } -} - impl Rng for Isaac64Rng { #[inline] fn next_u32(&mut self) -> u32 { @@ -261,33 +251,79 @@ impl Rng for Isaac64Rng { // it optimises to a bitwise mask). self.rsl[self.cnt as usize % RAND_SIZE].0 } - + #[cfg(feature = "i128_support")] fn next_u128(&mut self) -> u128 { ::rand_core::impls::next_u128_via_u64(self) } - + fn try_fill(&mut self, dest: &mut [u8]) -> Result<()> { ::rand_core::impls::try_fill_via_u32(self, dest) } } +/// Creates a new ISAAC-64 random number generator. +fn init(key: [w64; RAND_SIZE]) -> Isaac64Rng { + // These numbers are the result of initializing a...h with the + // fractional part of the golden ratio in binary (0x9e3779b97f4a7c13) + // and applying mix() 4 times. + let mut a = w(0x647c4677a2884b7c); + let mut b = w(0xb9f8b322c73ac862); + let mut c = w(0x8c0ea5053d4712a0); + let mut d = w(0xb29b2e824a595524); + let mut e = w(0x82f053db8355e0ce); + let mut f = w(0x48fe4a0fa5a09315); + let mut g = w(0xae985bf2cbfc89ed); + let mut h = w(0x98f5704f6c44c0ab); + + let mut mem = [w(0); RAND_SIZE]; + + macro_rules! memloop { + ($arr:expr) => {{ + for i in (0..RAND_SIZE/8).map(|i| i * 8) { + a += $arr[i ]; b += $arr[i+1]; + c += $arr[i+2]; d += $arr[i+3]; + e += $arr[i+4]; f += $arr[i+5]; + g += $arr[i+6]; h += $arr[i+7]; + mix(&mut a, &mut b, &mut c, &mut d, + &mut e, &mut f, &mut g, &mut h); + mem[i ] = a; mem[i+1] = b; + mem[i+2] = c; mem[i+3] = d; + mem[i+4] = e; mem[i+5] = f; + mem[i+6] = g; mem[i+7] = h; + } + }} + } + + memloop!(key); + // Do a second pass to make all of the seed affect all of `mem` + memloop!(mem); + + let mut rng = Isaac64Rng { + rsl: [w(0); RAND_SIZE], + mem: mem, + a: w(0), + b: w(0), + c: w(0), + cnt: 0, + }; + + // Prepare the first set of results + rng.isaac64(); + rng +} + impl SeedFromRng for Isaac64Rng { fn from_rng(other: &mut R) -> Result { - let mut ret = EMPTY_64; + let mut key = [w(0); RAND_SIZE]; unsafe { - let ptr = ret.rsl.as_mut_ptr() as *mut u8; + let ptr = key.as_mut_ptr() as *mut u8; let slice = slice::from_raw_parts_mut(ptr, RAND_SIZE * 8); other.try_fill(slice)?; } - ret.cnt = 0; - ret.a = w(0); - ret.b = w(0); - ret.c = w(0); - ret.init(true); - Ok(ret) + Ok(init(key)) } } @@ -298,23 +334,17 @@ impl<'a> SeedableRng<&'a [u64]> for Isaac64Rng { /// constructed with a given seed will generate the same sequence /// of values as all other generators constructed with that seed. fn from_seed(seed: &'a [u64]) -> Isaac64Rng { - let mut rng = EMPTY_64; + let mut key = [w(0); RAND_SIZE]; // make the seed into [seed[0], seed[1], ..., seed[seed.len() - // - 1], 0, 0, ...], to fill rng.rsl. + // - 1], 0, 0, ...], to fill `key`. let seed_iter = seed.iter().map(|&x| x).chain(repeat(0u64)); - for (rsl_elem, seed_elem) in rng.rsl.iter_mut().zip(seed_iter) { + for (rsl_elem, seed_elem) in key.iter_mut().zip(seed_iter) { *rsl_elem = w(seed_elem); } - rng.init(true); - rng - } -} -impl fmt::Debug for Isaac64Rng { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Isaac64Rng {{}}") + init(key) } } @@ -345,7 +375,6 @@ mod test { assert_eq!(rng1.next_u64(), rng2.next_u64()); } } - } #[test] fn test_isaac64_true_values() { @@ -373,6 +402,24 @@ mod test { 596345674630742204, 9947027391921273664, 11788097613744130851, 10391409374914919106)); } + + #[test] + fn test_isaac_new_uninitialized() { + // Compare the results from initializing `IsaacRng` with + // `new_from_u64(0)`, to make sure it is the same as the reference + // implementation when used uninitialized. + // Note: We only test the first 16 integers, not the full 256 of the + // first block. + let mut rng = Isaac64Rng::new_from_u64(0); + let vec = (0..16).map(|_| rng.next_u64()).collect::>(); + let expected: [u64; 16] = [ + 0xF67DFBA498E4937C, 0x84A5066A9204F380, 0xFEE34BD5F5514DBB, + 0x4D1664739B8F80D6, 0x8607459AB52A14AA, 0x0E78BC5A98529E49, + 0xFE5332822AD13777, 0x556C27525E33D01A, 0x08643CA615F3149F, + 0xD0771FAF3CB04714, 0x30E86F68A37B008D, 0x3074EBC0488A3ADF, + 0x270645EA7A2790BC, 0x5601A0A8D3763C6A, 0x2F83071F53F325DD, + 0xB9090F3D42D2D2EA]; + assert_eq!(vec, expected); } #[test] diff --git a/src/prng/isaac_word.rs b/src/prng/isaac_word.rs index 9cbd5c36721..17fb00a2732 100644 --- a/src/prng/isaac_word.rs +++ b/src/prng/isaac_word.rs @@ -10,7 +10,6 @@ //! The ISAAC random number generator. -use core::fmt; use {Rng, SeedFromRng, Result}; #[cfg(target_pointer_width = "32")] @@ -30,15 +29,9 @@ type WordRngType = super::isaac64::Isaac64Rng; /// `Isaac64Rng` uses. /// /// See for an explanation of the algorithm `IsaacRng` and `Isaac64Rng`. -#[derive(Copy)] +#[derive(Clone, Debug)] pub struct IsaacWordRng(WordRngType); -impl Clone for IsaacWordRng { - fn clone(&self) -> IsaacWordRng { - *self - } -} - impl Rng for IsaacWordRng { fn next_u32(&mut self) -> u32 { self.0.next_u32() @@ -63,9 +56,3 @@ impl SeedFromRng for IsaacWordRng { WordRngType::from_rng(other).map(|rng| IsaacWordRng(rng)) } } - -impl fmt::Debug for IsaacWordRng { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "IsaacWordRng {{}}") - } -} From 849f01ab0cce881c8619cf9d8f1af4274adc66d4 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Thu, 19 Oct 2017 20:51:52 +0200 Subject: [PATCH 129/247] Add Error type and ErrorKind --- rand_core/src/impls.rs | 8 ++-- rand_core/src/lib.rs | 92 +++++++++++++++++++++++++++++++++--------- rand_core/src/mock.rs | 6 +-- src/lib.rs | 14 +++---- src/os.rs | 55 +++++++++++-------------- src/prng/chacha.rs | 6 +-- src/prng/isaac.rs | 6 +-- src/prng/isaac64.rs | 6 +-- src/prng/isaac_word.rs | 6 +-- src/prng/xorshift.rs | 6 +-- src/read.rs | 13 +++--- src/reseeding.rs | 4 +- src/thread_local.rs | 4 +- 13 files changed, 136 insertions(+), 90 deletions(-) diff --git a/rand_core/src/impls.rs b/rand_core/src/impls.rs index 1c5a2be7e18..f7f7074160d 100644 --- a/rand_core/src/impls.rs +++ b/rand_core/src/impls.rs @@ -24,7 +24,7 @@ //! TODO: should we add more implementations? use core::intrinsics::transmute; -use {Rng, Result}; +use {Rng, Error}; /// Implement `next_u64` via `next_u32`, little-endian order. pub fn next_u64_via_u32(rng: &mut R) -> u64 { @@ -68,18 +68,18 @@ macro_rules! try_fill_via { } /// Implement `try_fill` via `next_u32`, little-endian order. -pub fn try_fill_via_u32(rng: &mut R, dest: &mut [u8]) -> Result<()> { +pub fn try_fill_via_u32(rng: &mut R, dest: &mut [u8]) -> Result<(), Error> { try_fill_via!(rng, next_u32, 4, dest) } /// Implement `try_fill` via `next_u64`, little-endian order. -pub fn try_fill_via_u64(rng: &mut R, dest: &mut [u8]) -> Result<()> { +pub fn try_fill_via_u64(rng: &mut R, dest: &mut [u8]) -> Result<(), Error> { try_fill_via!(rng, next_u64, 8, dest) } /// Implement `try_fill` via `next_u128`, little-endian order. #[cfg(feature = "i128_support")] -pub fn try_fill_via_u128(rng: &mut R, dest: &mut [u8]) -> Result<()> { +pub fn try_fill_via_u128(rng: &mut R, dest: &mut [u8]) -> Result<(), Error> { try_fill_via!(rng, next_u128, 16, dest) } diff --git a/rand_core/src/lib.rs b/rand_core/src/lib.rs index 651f9b51335..2fbc2efc234 100644 --- a/rand_core/src/lib.rs +++ b/rand_core/src/lib.rs @@ -20,8 +20,8 @@ //! `SeedFromRng` and `SeedableRng` are extension traits for construction and //! reseeding. //! -//! `Error` and `Result` are provided for error-handling. They are safe to use -//! in `no_std` environments. +//! `Error` is provided for error-handling. It is safe to use in `no_std` +//! environments. //! //! The `impls` sub-module includes a few small functions to assist //! implementation of `Rng`. Since this module is only of interest to `Rng` @@ -43,6 +43,8 @@ #[cfg(feature="std")] extern crate core; +use core::fmt; + pub mod impls; pub mod mock; @@ -89,7 +91,7 @@ pub trait Rng { /// Return the next random u128. #[cfg(feature = "i128_support")] fn next_u128(&mut self) -> u128; - + /// Fill `dest` entirely with random data. /// /// This method does *not* have any requirement on how much of the @@ -98,7 +100,7 @@ pub trait Rng { /// 1 is required. A different implementation might use `next_u32` and /// only consume 4 bytes; *however* any change affecting *reproducibility* /// of output must be considered a breaking change. - fn try_fill(&mut self, dest: &mut [u8]) -> Result<()>; + fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error>; } impl<'a, R: Rng+?Sized> Rng for &'a mut R { @@ -115,7 +117,7 @@ impl<'a, R: Rng+?Sized> Rng for &'a mut R { (**self).next_u128() } - fn try_fill(&mut self, dest: &mut [u8]) -> Result<()> { + fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { (**self).try_fill(dest) } } @@ -135,7 +137,7 @@ impl Rng for Box { (**self).next_u128() } - fn try_fill(&mut self, dest: &mut [u8]) -> Result<()> { + fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { (**self).try_fill(dest) } } @@ -158,7 +160,7 @@ pub trait SeedFromRng: Sized { /// hand, seeding a simple numerical generator from another of the same /// type sometimes has serious side effects such as effectively cloning the /// generator. - fn from_rng(rng: &mut R) -> Result; + fn from_rng(rng: &mut R) -> Result; } @@ -180,21 +182,71 @@ pub trait SeedableRng: Rng { } -/// Error type for cryptographic generators. Technically external generators -/// such as the operating system or hardware generators could fail. A PRNG -/// (algorithm) could also fail if it detects cycles, though most PRNGs have -/// sufficiently long cycles that looping is not usually feasible. -/// -/// TODO: how should error details be reported? -#[derive(Debug)] -pub struct Error; +/// Error kind which can be matched over. +#[derive(PartialEq, Eq, Debug, Copy, Clone)] +pub enum ErrorKind { + /// Permanent failure: likely not recoverable without user action. + Unavailable, + /// Temporary failure: recommended to retry a few times, but may also be + /// irrecoverable. + Transient, + /// Not ready yet: recommended to try again a little later. + NotReady, + /// Uncategorised error + Other, + #[doc(hidden)] + __Nonexhaustive, +} #[cfg(feature="std")] -impl From<::std::io::Error> for Error { - fn from(_: ::std::io::Error) -> Error { - Error +#[derive(Debug)] +pub struct Error { + pub kind: ErrorKind, + pub cause: Option>, +} + +#[cfg(not(feature="std"))] +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Error { + pub kind: ErrorKind, + pub cause: Option<&'static str>, +} + +impl Error { + #[cfg(feature="std")] + pub fn new(kind: ErrorKind, cause: Option>) -> Error { + Error { + kind: kind, + cause: cause, + } } } -/// Result type (convenience type-def) -pub type Result = ::std::result::Result; +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self.kind { + ErrorKind::Unavailable => write!(f, "RNG not available."), + ErrorKind::Transient => write!(f, "RNG has failed, probably temporary."), + ErrorKind::NotReady => write!(f, "RNG not ready yet."), + ErrorKind::Other => write!(f, "An unspecified RNG error occurred."), + ErrorKind::__Nonexhaustive => unreachable!(), + } + } +} + +#[cfg(feature="std")] +impl ::std::error::Error for Error { + fn description(&self) -> &str { + match self.kind { + ErrorKind::Unavailable => "not available", + ErrorKind::Transient => "temporary failure", + ErrorKind::NotReady => "not ready yet", + ErrorKind::Other => "Uncategorised rng error", + ErrorKind::__Nonexhaustive => unreachable!(), + } + } + + fn cause(&self) -> Option<&::std::error::Error> { + self.cause.as_ref().map(|e| &**e) + } +} diff --git a/rand_core/src/mock.rs b/rand_core/src/mock.rs index 5abe444aa8c..21954805474 100644 --- a/rand_core/src/mock.rs +++ b/rand_core/src/mock.rs @@ -16,7 +16,7 @@ //! Instead maybe this should be yet another crate? Or just leave it here? use core::num::Wrapping as w; -use {Rng, SeedableRng, impls, Result}; +use {Error, Rng, SeedableRng, impls}; /// A simple implementation of `Rng`, purely for testing. /// Returns an arithmetic sequence (i.e. adds a constant each step). @@ -57,7 +57,7 @@ impl Rng for MockAddRng { impls::next_u128_via_u64(self) } - fn try_fill(&mut self, dest: &mut [u8]) -> Result<()> { + fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { impls::try_fill_via_u32(self, dest) } } @@ -76,7 +76,7 @@ impl Rng for MockAddRng { impls::next_u128_via_u64(self) } - fn try_fill(&mut self, dest: &mut [u8]) -> Result<()> { + fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { impls::try_fill_via_u32(self, dest) } } diff --git a/src/lib.rs b/src/lib.rs index 4d3e432cd75..fa4982f7cc2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -255,7 +255,7 @@ extern crate core; extern crate rand_core; -pub use rand_core::{Rng, SeedFromRng, SeedableRng, Error, Result}; +pub use rand_core::{Rng, SeedFromRng, SeedableRng, Error, ErrorKind}; #[cfg(feature="std")] pub use read::ReadRng; @@ -293,12 +293,12 @@ mod thread_local; #[cfg(feature="std")] pub trait NewSeeded: Sized { /// Creates a new instance, automatically seeded via `OsRng`. - fn new() -> Result; + fn new() -> Result; } #[cfg(feature="std")] impl NewSeeded for R { - fn new() -> Result { + fn new() -> Result { let mut r = OsRng::new()?; Self::from_rng(&mut r) } @@ -416,13 +416,13 @@ impl Rng for StdRng { fn next_u128(&mut self) -> u128 { self.rng.next_u128() } - fn try_fill(&mut self, dest: &mut [u8]) -> Result<()> { + fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { self.rng.try_fill(dest) } } impl SeedFromRng for StdRng { - fn from_rng(other: &mut R) -> Result { + fn from_rng(other: &mut R) -> Result { IsaacWordRng::from_rng(other).map(|rng| StdRng{ rng }) } } @@ -430,7 +430,7 @@ impl SeedFromRng for StdRng { #[cfg(test)] mod test { - use {Rng, thread_rng, Sample, Result}; + use {Rng, thread_rng, Sample, Error}; use rand_core::mock::MockAddRng; use distributions::{uniform}; use distributions::{Uniform, Range, Exp}; @@ -451,7 +451,7 @@ mod test { fn next_u128(&mut self) -> u128 { self.inner.next_u128() } - fn try_fill(&mut self, dest: &mut [u8]) -> Result<()> { + fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { self.inner.try_fill(dest) } } diff --git a/src/os.rs b/src/os.rs index 6ad297b85e7..c1315d62c6e 100644 --- a/src/os.rs +++ b/src/os.rs @@ -14,7 +14,7 @@ use std::{mem, fmt}; use std::io::Read; -use {Rng, Result}; +use {Rng, Error}; // TODO: replace many of the panics below with Result error handling /// A random number generator that retrieves randomness straight from @@ -39,7 +39,7 @@ pub struct OsRng(imp::OsRng); impl OsRng { /// Create a new `OsRng`. - pub fn new() -> Result { + pub fn new() -> Result { imp::OsRng::new().map(OsRng) } } @@ -61,7 +61,7 @@ impl Rng for OsRng { self.try_fill(&mut buf).unwrap_or_else(|e| panic!("try_fill failed: {:?}", e)); unsafe{ *(buf.as_ptr() as *const u128) } } - fn try_fill(&mut self, v: &mut [u8]) -> Result<()> { + fn try_fill(&mut self, v: &mut [u8]) -> Result<(), Error> { self.0.try_fill(v) } } @@ -77,7 +77,7 @@ impl fmt::Debug for OsRng { struct ReadRng (R); impl ReadRng { - fn try_fill(&mut self, mut buf: &mut [u8]) -> Result<()> { + fn try_fill(&mut self, mut buf: &mut [u8]) -> Result<(), Error> { while buf.len() > 0 { match self.0.read(buf).unwrap_or_else(|e| panic!("Read error: {}", e)) { 0 => panic!("OsRng: no bytes available"), @@ -99,10 +99,10 @@ mod imp { use self::OsRngInner::*; use super::ReadRng; + use Error; use std::io; use std::fs::File; - use Result; #[cfg(all(target_os = "linux", any(target_arch = "x86_64", @@ -139,7 +139,7 @@ mod imp { target_arch = "powerpc"))))] fn getrandom(_buf: &mut [u8]) -> libc::c_long { -1 } - fn getrandom_try_fill(v: &mut [u8]) -> Result<()> { + fn getrandom_try_fill(v: &mut [u8]) -> Result<(), Error> { let mut read = 0; let len = v.len(); while read < len { @@ -206,18 +206,18 @@ mod imp { } impl OsRng { - pub fn new() -> Result { + pub fn new() -> Result { if is_getrandom_available() { return Ok(OsRng { inner: OsGetrandomRng }); } - let reader = File::open("/dev/urandom")?; + let reader = File::open("/dev/urandom").unwrap(); let reader_rng = ReadRng(reader); Ok(OsRng { inner: OsReadRng(reader_rng) }) } - pub fn try_fill(&mut self, v: &mut [u8]) -> Result<()> { + pub fn try_fill(&mut self, v: &mut [u8]) -> Result<(), Error> { match self.inner { OsGetrandomRng => getrandom_try_fill(v), OsReadRng(ref mut rng) => rng.try_fill(v) @@ -232,7 +232,6 @@ mod imp { use std::io; use self::libc::{c_int, size_t}; - use Result; #[derive(Debug)] pub struct OsRng; @@ -249,10 +248,10 @@ mod imp { } impl OsRng { - pub fn new() -> Result { + pub fn new() -> Result { Ok(OsRng) } - pub fn try_fill(&mut self, v: &mut [u8]) -> Result<()> { + pub fn try_fill(&mut self, v: &mut [u8]) -> Result<(), Error> { let ret = unsafe { SecRandomCopyBytes(kSecRandomDefault, v.len() as size_t, v.as_mut_ptr()) }; @@ -269,16 +268,15 @@ mod imp { extern crate libc; use std::{io, ptr}; - use Result; #[derive(Debug)] pub struct OsRng; impl OsRng { - pub fn new() -> Result { + pub fn new() -> Result { Ok(OsRng) } - pub fn try_fill(&mut self, v: &mut [u8]) -> Result<()> { + pub fn try_fill(&mut self, v: &mut [u8]) -> Result<(), Error> { let mib = [libc::CTL_KERN, libc::KERN_ARND]; // kern.arandom permits a maximum buffer size of 256 bytes for s in v.chunks_mut(256) { @@ -303,16 +301,15 @@ mod imp { extern crate libc; use std::io; - use Result; #[derive(Debug)] pub struct OsRng; impl OsRng { - pub fn new() -> Result { + pub fn new() -> Result { Ok(OsRng) } - pub fn try_fill(&mut self, v: &mut [u8]) -> Result<()> { + pub fn try_fill(&mut self, v: &mut [u8]) -> Result<(), Error> { // getentropy(2) permits a maximum buffer size of 256 bytes for s in v.chunks_mut(256) { let ret = unsafe { @@ -333,7 +330,6 @@ mod imp { use std::io; use std::fs::File; use super::ReadRng; - use Result; #[derive(Debug)] pub struct OsRng { @@ -341,13 +337,13 @@ mod imp { } impl OsRng { - pub fn new() -> Result { - let reader = File::open("rand:")?; + pub fn new() -> Result { + let reader = File::open("rand:").unwrap(); let reader_rng = ReadRng(reader); Ok(OsRng { inner: reader_rng }) } - pub fn try_fill(&mut self, v: &mut [u8]) -> Result<()> { + pub fn try_fill(&mut self, v: &mut [u8]) -> Result<(), Error> { self.inner.try_fill(v) } } @@ -358,16 +354,15 @@ mod imp { extern crate fuchsia_zircon; use std::io; - use Result; #[derive(Debug)] pub struct OsRng; impl OsRng { - pub fn new() -> Result { + pub fn new() -> Result { Ok(OsRng) } - pub fn try_fill(&mut self, v: &mut [u8]) -> Result<()> { + pub fn try_fill(&mut self, v: &mut [u8]) -> Result<(), Error> { for s in v.chunks_mut(fuchsia_zircon::ZX_CPRNG_DRAW_MAX_LEN) { let mut filled = 0; while filled < s.len() { @@ -385,7 +380,6 @@ mod imp { #[cfg(windows)] mod imp { use std::io; - use Result; type BOOLEAN = u8; type ULONG = u32; @@ -400,10 +394,10 @@ mod imp { pub struct OsRng; impl OsRng { - pub fn new() -> Result { + pub fn new() -> Result { Ok(OsRng) } - pub fn try_fill(&mut self, v: &mut [u8]) -> Result<()> { + pub fn try_fill(&mut self, v: &mut [u8]) -> Result<(), Error> { // RtlGenRandom takes an ULONG (u32) for the length so we need to // split up the buffer. for slice in v.chunks_mut(::max_value() as usize) { @@ -426,7 +420,6 @@ mod imp { use std::io; use std::mem; - use Result; #[derive(Debug)] pub struct OsRng(extern fn(dest: *mut libc::c_void, @@ -449,7 +442,7 @@ mod imp { } impl OsRng { - pub fn new() -> Result { + pub fn new() -> Result { let mut iface = NaClIRTRandom { get_random_bytes: None, }; @@ -468,7 +461,7 @@ mod imp { Err(Result) } } - pub fn try_fill(&mut self, v: &mut [u8]) -> Result<()> { + pub fn try_fill(&mut self, v: &mut [u8]) -> Result<(), Error> { let mut read = 0; loop { let mut r: libc::size_t = 0; diff --git a/src/prng/chacha.rs b/src/prng/chacha.rs index bfd7133e565..46149f7d366 100644 --- a/src/prng/chacha.rs +++ b/src/prng/chacha.rs @@ -11,7 +11,7 @@ //! The ChaCha random number generator. use core::num::Wrapping as w; -use {Rng, SeedFromRng, SeedableRng, Result}; +use {Rng, SeedFromRng, SeedableRng, Error}; #[allow(bad_style)] type w32 = w; @@ -208,7 +208,7 @@ impl Rng for ChaChaRng { // Custom implementation allowing larger reads from buffer is about 8% // faster than default implementation in my tests - fn try_fill(&mut self, dest: &mut [u8]) -> Result<()> { + fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { use core::cmp::min; use core::intrinsics::{transmute, copy_nonoverlapping}; @@ -245,7 +245,7 @@ impl Rng for ChaChaRng { } impl SeedFromRng for ChaChaRng { - fn from_rng(other: &mut R) -> Result { + fn from_rng(other: &mut R) -> Result { let mut key : [u32; KEY_WORDS] = [0; KEY_WORDS]; for word in key.iter_mut() { *word = other.next_u32(); diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index 8b762a03b31..ce4aa45330e 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -15,7 +15,7 @@ use core::iter::repeat; use core::num::Wrapping as w; use core::fmt; -use {Rng, SeedFromRng, SeedableRng, Result}; +use {Rng, SeedFromRng, SeedableRng, Error}; #[allow(non_camel_case_types)] type w32 = w; @@ -281,13 +281,13 @@ impl Rng for IsaacRng { ::rand_core::impls::next_u128_via_u64(self) } - fn try_fill(&mut self, dest: &mut [u8]) -> Result<()> { + fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { ::rand_core::impls::try_fill_via_u32(self, dest) } } impl SeedFromRng for IsaacRng { - fn from_rng(other: &mut R) -> Result { + fn from_rng(other: &mut R) -> Result { let mut ret = EMPTY; unsafe { let ptr = ret.rsl.as_mut_ptr() as *mut u8; diff --git a/src/prng/isaac64.rs b/src/prng/isaac64.rs index 3a538e1f01c..8af4cfd2d33 100644 --- a/src/prng/isaac64.rs +++ b/src/prng/isaac64.rs @@ -15,7 +15,7 @@ use core::iter::repeat; use core::num::Wrapping as w; use core::fmt; -use {Rng, SeedFromRng, SeedableRng, Result}; +use {Rng, SeedFromRng, SeedableRng, Error}; #[allow(non_camel_case_types)] type w64 = w; @@ -267,13 +267,13 @@ impl Rng for Isaac64Rng { ::rand_core::impls::next_u128_via_u64(self) } - fn try_fill(&mut self, dest: &mut [u8]) -> Result<()> { + fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { ::rand_core::impls::try_fill_via_u32(self, dest) } } impl SeedFromRng for Isaac64Rng { - fn from_rng(other: &mut R) -> Result { + fn from_rng(other: &mut R) -> Result { let mut ret = EMPTY_64; unsafe { let ptr = ret.rsl.as_mut_ptr() as *mut u8; diff --git a/src/prng/isaac_word.rs b/src/prng/isaac_word.rs index 9cbd5c36721..18911523d07 100644 --- a/src/prng/isaac_word.rs +++ b/src/prng/isaac_word.rs @@ -11,7 +11,7 @@ //! The ISAAC random number generator. use core::fmt; -use {Rng, SeedFromRng, Result}; +use {Rng, SeedFromRng, Error}; #[cfg(target_pointer_width = "32")] type WordRngType = super::isaac::IsaacRng; @@ -53,13 +53,13 @@ impl Rng for IsaacWordRng { self.0.next_u128() } - fn try_fill(&mut self, dest: &mut [u8]) -> Result<()> { + fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { self.0.try_fill(dest) } } impl SeedFromRng for IsaacWordRng { - fn from_rng(other: &mut R) -> Result { + fn from_rng(other: &mut R) -> Result { WordRngType::from_rng(other).map(|rng| IsaacWordRng(rng)) } } diff --git a/src/prng/xorshift.rs b/src/prng/xorshift.rs index 6181ad646fa..d649fa33f27 100644 --- a/src/prng/xorshift.rs +++ b/src/prng/xorshift.rs @@ -11,7 +11,7 @@ //! Xorshift generators use core::num::Wrapping as w; -use {Rng, SeedFromRng, SeedableRng, Result}; +use {Rng, SeedFromRng, SeedableRng, Error}; /// An Xorshift[1] random number /// generator. @@ -50,7 +50,7 @@ impl XorShiftRng { } impl SeedFromRng for XorShiftRng { - fn from_rng(rng: &mut R) -> Result { + fn from_rng(rng: &mut R) -> Result { let mut tuple: (u32, u32, u32, u32); loop { tuple = (rng.next_u32(), rng.next_u32(), rng.next_u32(), rng.next_u32()); @@ -84,7 +84,7 @@ impl Rng for XorShiftRng { ::rand_core::impls::next_u128_via_u64(self) } - fn try_fill(&mut self, dest: &mut [u8]) -> Result<()> { + fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { ::rand_core::impls::try_fill_via_u32(self, dest) } } diff --git a/src/read.rs b/src/read.rs index e6697df72ba..f4f0f85626f 100644 --- a/src/read.rs +++ b/src/read.rs @@ -14,7 +14,7 @@ use std::fmt::Debug; use std::io::Read; use std::mem; -use {Rng, Error, Result}; +use {Rng, Error, ErrorKind}; /// An RNG that reads random bytes straight from a `Read`. This will /// work best with an infinite reader, but this is not required. @@ -68,17 +68,18 @@ impl Rng for ReadRng { impl_uint_from_fill!(u128, 16, self) } - fn try_fill(&mut self, v: &mut [u8]) -> Result<()> { + fn try_fill(&mut self, v: &mut [u8]) -> Result<(), Error> { if v.len() == 0 { return Ok(()); } fill(&mut self.reader, v) } } -fn fill(r: &mut Read, mut buf: &mut [u8]) -> Result<()> { +fn fill(r: &mut Read, mut buf: &mut [u8]) -> Result<(), Error> { while buf.len() > 0 { - match r.read(buf)? { - 0 => return Err(Error), - n => buf = &mut mem::replace(&mut buf, &mut [])[n..], + match r.read(buf) { + Ok(0) => return Err(Error::new(ErrorKind::Unavailable, None)), + Ok(n) => buf = &mut mem::replace(&mut buf, &mut [])[n..], + Err(_) => return Err(Error::new(ErrorKind::Other, None)), } } Ok(()) diff --git a/src/reseeding.rs b/src/reseeding.rs index b01bab7067c..5ba7f3008ca 100644 --- a/src/reseeding.rs +++ b/src/reseeding.rs @@ -13,7 +13,7 @@ use core::fmt::Debug; -use {Rng, SeedableRng, Result}; +use {Rng, SeedableRng, Error}; #[cfg(feature="std")] use NewSeeded; @@ -80,7 +80,7 @@ impl> Rng for ReseedingRng { self.rng.next_u128() } - fn try_fill(&mut self, dest: &mut [u8]) -> Result<()> { + fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { self.reseed_if_necessary(); self.bytes_generated += dest.len() as u64; self.rng.try_fill(dest) diff --git a/src/thread_local.rs b/src/thread_local.rs index d2a77890bf5..e9bdc7f76e7 100644 --- a/src/thread_local.rs +++ b/src/thread_local.rs @@ -13,7 +13,7 @@ use std::cell::RefCell; use std::rc::Rc; -use {Rng, StdRng, NewSeeded, Rand, Default, Result}; +use {Rng, StdRng, NewSeeded, Rand, Default, Error}; use reseeding::{ReseedingRng, ReseedWithNew}; @@ -41,7 +41,7 @@ impl Rng for ThreadRng { self.rng.borrow_mut().next_u128() } - fn try_fill(&mut self, bytes: &mut [u8]) -> Result<()> { + fn try_fill(&mut self, bytes: &mut [u8]) -> Result<(), Error> { self.rng.borrow_mut().try_fill(bytes) } } From dbbe143c94d2d4ba4826d9dd933fb2a608ce8e4a Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Thu, 19 Oct 2017 20:54:05 +0200 Subject: [PATCH 130/247] Add back `fill_bytes` --- rand_core/src/impls.rs | 23 +++++++++++------------ rand_core/src/lib.rs | 26 ++++++++++++++++++++++++-- rand_core/src/mock.rs | 10 +++++----- src/lib.rs | 8 +++++++- src/os.rs | 7 +++++++ src/prng/chacha.rs | 3 +-- src/prng/isaac.rs | 4 ++-- src/prng/isaac64.rs | 4 ++-- src/prng/isaac_word.rs | 4 ++-- src/prng/xorshift.rs | 4 ++-- src/read.rs | 12 +++++++++--- src/reseeding.rs | 14 ++++++++++---- src/thread_local.rs | 6 +++++- 13 files changed, 87 insertions(+), 38 deletions(-) diff --git a/rand_core/src/impls.rs b/rand_core/src/impls.rs index f7f7074160d..9acd9ac3b39 100644 --- a/rand_core/src/impls.rs +++ b/rand_core/src/impls.rs @@ -24,7 +24,7 @@ //! TODO: should we add more implementations? use core::intrinsics::transmute; -use {Rng, Error}; +use Rng; /// Implement `next_u64` via `next_u32`, little-endian order. pub fn next_u64_via_u32(rng: &mut R) -> u64 { @@ -45,7 +45,7 @@ pub fn next_u128_via_u64(rng: &mut R) -> u128 { (y << 64) | x } -macro_rules! try_fill_via { +macro_rules! fill_bytes_via { ($rng:ident, $next_u:ident, $BYTES:expr, $dest:ident) => {{ let mut left = $dest; while left.len() >= $BYTES { @@ -63,24 +63,23 @@ macro_rules! try_fill_via { }; left.copy_from_slice(&chunk[..n]); } - Ok(()) }} } -/// Implement `try_fill` via `next_u32`, little-endian order. -pub fn try_fill_via_u32(rng: &mut R, dest: &mut [u8]) -> Result<(), Error> { - try_fill_via!(rng, next_u32, 4, dest) +/// Implement `fill_bytes` via `next_u32`, little-endian order. +pub fn fill_bytes_via_u32(rng: &mut R, dest: &mut [u8]) { + fill_bytes_via!(rng, next_u32, 4, dest) } -/// Implement `try_fill` via `next_u64`, little-endian order. -pub fn try_fill_via_u64(rng: &mut R, dest: &mut [u8]) -> Result<(), Error> { - try_fill_via!(rng, next_u64, 8, dest) +/// Implement `fill_bytes` via `next_u64`, little-endian order. +pub fn fill_bytes_via_u64(rng: &mut R, dest: &mut [u8]) { + fill_bytes_via!(rng, next_u64, 8, dest) } -/// Implement `try_fill` via `next_u128`, little-endian order. +/// Implement `fill_bytes` via `next_u128`, little-endian order. #[cfg(feature = "i128_support")] -pub fn try_fill_via_u128(rng: &mut R, dest: &mut [u8]) -> Result<(), Error> { - try_fill_via!(rng, next_u128, 16, dest) +pub fn fill_bytes_via_u128(rng: &mut R, dest: &mut [u8]) { + fill_bytes_via!(rng, next_u128, 16, dest) } // TODO: implement tests for the above diff --git a/rand_core/src/lib.rs b/rand_core/src/lib.rs index 2fbc2efc234..4d03bf1e335 100644 --- a/rand_core/src/lib.rs +++ b/rand_core/src/lib.rs @@ -95,12 +95,27 @@ pub trait Rng { /// Fill `dest` entirely with random data. /// /// This method does *not* have any requirement on how much of the + /// generated random number stream is consumed; e.g. `fill_bytes_via_u64` + /// implementation uses `next_u64` thus consuming 8 bytes even when only + /// 1 is required. A different implementation might use `next_u32` and + /// only consume 4 bytes; *however* any change affecting *reproducibility* + /// of output must be considered a breaking change. + fn fill_bytes(&mut self, dest: &mut [u8]); + + /// Fill `dest` entirely with random data. + /// + /// If a RNG can encounter an error, this is the only method that reports + /// it. The other methods either handle the error, or panic. + /// + /// This method does *not* have any requirement on how much of the /// generated random number stream is consumed; e.g. `try_fill_via_u64` /// implementation uses `next_u64` thus consuming 8 bytes even when only /// 1 is required. A different implementation might use `next_u32` and /// only consume 4 bytes; *however* any change affecting *reproducibility* /// of output must be considered a breaking change. - fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error>; + fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { + Ok(self.fill_bytes(dest)) + } } impl<'a, R: Rng+?Sized> Rng for &'a mut R { @@ -117,6 +132,10 @@ impl<'a, R: Rng+?Sized> Rng for &'a mut R { (**self).next_u128() } + fn fill_bytes(&mut self, dest: &mut [u8]) { + (**self).fill_bytes(dest) + } + fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { (**self).try_fill(dest) } @@ -137,6 +156,10 @@ impl Rng for Box { (**self).next_u128() } + fn fill_bytes(&mut self, dest: &mut [u8]) { + (**self).fill_bytes(dest) + } + fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { (**self).try_fill(dest) } @@ -163,7 +186,6 @@ pub trait SeedFromRng: Sized { fn from_rng(rng: &mut R) -> Result; } - /// A random number generator that can be explicitly seeded to produce /// the same stream of randomness multiple times. /// diff --git a/rand_core/src/mock.rs b/rand_core/src/mock.rs index 21954805474..91fab73af4e 100644 --- a/rand_core/src/mock.rs +++ b/rand_core/src/mock.rs @@ -16,7 +16,7 @@ //! Instead maybe this should be yet another crate? Or just leave it here? use core::num::Wrapping as w; -use {Error, Rng, SeedableRng, impls}; +use {Rng, SeedableRng, impls}; /// A simple implementation of `Rng`, purely for testing. /// Returns an arithmetic sequence (i.e. adds a constant each step). @@ -57,8 +57,8 @@ impl Rng for MockAddRng { impls::next_u128_via_u64(self) } - fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { - impls::try_fill_via_u32(self, dest) + fn fill_bytes(&mut self, dest: &mut [u8]) { + impls::fill_bytes_via_u32(self, dest); } } @@ -76,8 +76,8 @@ impl Rng for MockAddRng { impls::next_u128_via_u64(self) } - fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { - impls::try_fill_via_u32(self, dest) + fn fill_bytes(&mut self, dest: &mut [u8]) { + impls::fill_bytes_via_u64(self, dest); } } diff --git a/src/lib.rs b/src/lib.rs index fa4982f7cc2..ef9937e0706 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -416,6 +416,9 @@ impl Rng for StdRng { fn next_u128(&mut self) -> u128 { self.rng.next_u128() } + fn fill_bytes(&mut self, dest: &mut [u8]) { + self.rng.fill_bytes(dest); + } fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { self.rng.try_fill(dest) } @@ -451,6 +454,9 @@ mod test { fn next_u128(&mut self) -> u128 { self.inner.next_u128() } + fn fill_bytes(&mut self, dest: &mut [u8]) { + self.inner.fill_bytes(dest) + } fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { self.inner.try_fill(dest) } @@ -486,7 +492,7 @@ mod test { 80, 81, 82, 83, 84, 85, 86, 87]; for &n in lengths.iter() { let mut v = repeat(0u8).take(n).collect::>(); - r.try_fill(&mut v).unwrap(); + r.fill_bytes(&mut v); // use this to get nicer error messages. for (i, &byte) in v.iter().enumerate() { diff --git a/src/os.rs b/src/os.rs index c1315d62c6e..6cc28615f1e 100644 --- a/src/os.rs +++ b/src/os.rs @@ -50,17 +50,24 @@ impl Rng for OsRng { self.try_fill(&mut buf).unwrap_or_else(|e| panic!("try_fill failed: {:?}", e)); unsafe{ *(buf.as_ptr() as *const u32) } } + fn next_u64(&mut self) -> u64 { let mut buf: [u8; 8] = [0; 8]; self.try_fill(&mut buf).unwrap_or_else(|e| panic!("try_fill failed: {:?}", e)); unsafe{ *(buf.as_ptr() as *const u64) } } + #[cfg(feature = "i128_support")] fn next_u128(&mut self) -> u128 { let mut buf: [u8; 16] = [0; 16]; self.try_fill(&mut buf).unwrap_or_else(|e| panic!("try_fill failed: {:?}", e)); unsafe{ *(buf.as_ptr() as *const u128) } } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + self.try_fill(dest).unwrap(); + } + fn try_fill(&mut self, v: &mut [u8]) -> Result<(), Error> { self.0.try_fill(v) } diff --git a/src/prng/chacha.rs b/src/prng/chacha.rs index 46149f7d366..d2d6415ec2d 100644 --- a/src/prng/chacha.rs +++ b/src/prng/chacha.rs @@ -208,7 +208,7 @@ impl Rng for ChaChaRng { // Custom implementation allowing larger reads from buffer is about 8% // faster than default implementation in my tests - fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { + fn fill_bytes(&mut self, dest: &mut [u8]) { use core::cmp::min; use core::intrinsics::{transmute, copy_nonoverlapping}; @@ -240,7 +240,6 @@ impl Rng for ChaChaRng { }; left.copy_from_slice(&chunk[..n]); } - Ok(()) } } diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index ce4aa45330e..7cea9ffe964 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -281,8 +281,8 @@ impl Rng for IsaacRng { ::rand_core::impls::next_u128_via_u64(self) } - fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { - ::rand_core::impls::try_fill_via_u32(self, dest) + fn fill_bytes(&mut self, dest: &mut [u8]) { + ::rand_core::impls::fill_bytes_via_u32(self, dest); } } diff --git a/src/prng/isaac64.rs b/src/prng/isaac64.rs index 8af4cfd2d33..4ac7954dd53 100644 --- a/src/prng/isaac64.rs +++ b/src/prng/isaac64.rs @@ -267,8 +267,8 @@ impl Rng for Isaac64Rng { ::rand_core::impls::next_u128_via_u64(self) } - fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { - ::rand_core::impls::try_fill_via_u32(self, dest) + fn fill_bytes(&mut self, dest: &mut [u8]) { + ::rand_core::impls::fill_bytes_via_u32(self, dest); } } diff --git a/src/prng/isaac_word.rs b/src/prng/isaac_word.rs index 18911523d07..af4b9233848 100644 --- a/src/prng/isaac_word.rs +++ b/src/prng/isaac_word.rs @@ -53,8 +53,8 @@ impl Rng for IsaacWordRng { self.0.next_u128() } - fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { - self.0.try_fill(dest) + fn fill_bytes(&mut self, dest: &mut [u8]) { + self.0.fill_bytes(dest) } } diff --git a/src/prng/xorshift.rs b/src/prng/xorshift.rs index d649fa33f27..9540f96c538 100644 --- a/src/prng/xorshift.rs +++ b/src/prng/xorshift.rs @@ -84,8 +84,8 @@ impl Rng for XorShiftRng { ::rand_core::impls::next_u128_via_u64(self) } - fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { - ::rand_core::impls::try_fill_via_u32(self, dest) + fn fill_bytes(&mut self, dest: &mut [u8]) { + ::rand_core::impls::fill_bytes_via_u32(self, dest); } } diff --git a/src/read.rs b/src/read.rs index f4f0f85626f..dd34f579faf 100644 --- a/src/read.rs +++ b/src/read.rs @@ -60,14 +60,20 @@ impl Rng for ReadRng { fn next_u32(&mut self) -> u32 { impl_uint_from_fill!(u32, 4, self) } + fn next_u64(&mut self) -> u64 { impl_uint_from_fill!(u64, 8, self) } + #[cfg(feature = "i128_support")] fn next_u128(&mut self) -> u128 { impl_uint_from_fill!(u128, 16, self) } - + + fn fill_bytes(&mut self, dest: &mut [u8]) { + self.try_fill(dest).unwrap(); + } + fn try_fill(&mut self, v: &mut [u8]) -> Result<(), Error> { if v.len() == 0 { return Ok(()); } fill(&mut self.reader, v) @@ -112,12 +118,12 @@ mod test { assert_eq!(rng.next_u32(), 3_u32.to_be()); } #[test] - fn test_reader_rng_try_fill() { + fn test_reader_rng_fill_bytes() { let v = [1u8, 2, 3, 4, 5, 6, 7, 8]; let mut w = [0u8; 8]; let mut rng = ReadRng::new(&v[..]); - rng.try_fill(&mut w).unwrap(); + rng.fill_bytes(&mut w); assert!(v == w); } diff --git a/src/reseeding.rs b/src/reseeding.rs index 5ba7f3008ca..30144644375 100644 --- a/src/reseeding.rs +++ b/src/reseeding.rs @@ -80,6 +80,12 @@ impl> Rng for ReseedingRng { self.rng.next_u128() } + fn fill_bytes(&mut self, dest: &mut [u8]) { + self.reseed_if_necessary(); + self.bytes_generated += dest.len() as u64; + self.rng.fill_bytes(dest); + } + fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { self.reseed_if_necessary(); self.bytes_generated += dest.len() as u64; @@ -163,15 +169,15 @@ mod test { const FILL_BYTES_V_LEN: usize = 13579; #[test] - fn test_rng_try_fill() { + fn test_rng_fill_bytes() { let mut v = repeat(0u8).take(FILL_BYTES_V_LEN).collect::>(); - ::test::rng().try_fill(&mut v).unwrap(); + ::test::rng().fill_bytes(&mut v); - // Sanity test: if we've gotten here, `try_fill` has not infinitely + // Sanity test: if we've gotten here, `fill_bytes` has not infinitely // recursed. assert_eq!(v.len(), FILL_BYTES_V_LEN); - // To test that `try_fill` actually did something, check that the + // To test that `fill_bytes` actually did something, check that the // average of `v` is not 0. let mut sum = 0.0; for &x in v.iter() { diff --git a/src/thread_local.rs b/src/thread_local.rs index e9bdc7f76e7..819fe1cb844 100644 --- a/src/thread_local.rs +++ b/src/thread_local.rs @@ -40,7 +40,11 @@ impl Rng for ThreadRng { fn next_u128(&mut self) -> u128 { self.rng.borrow_mut().next_u128() } - + + fn fill_bytes(&mut self, dest: &mut [u8]) { + self.rng.borrow_mut().fill_bytes(dest); + } + fn try_fill(&mut self, bytes: &mut [u8]) -> Result<(), Error> { self.rng.borrow_mut().try_fill(bytes) } From 019d9c1e796f37387796fb32e6673a3102d30373 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Thu, 19 Oct 2017 20:55:06 +0200 Subject: [PATCH 131/247] Improve error handeling of `ReadRng` Also moved the `impl_uint_from_fill` macro from `os.rs` to `randcore`. I had to modify its error handling anyway, and it is shared with `OsRng`. --- rand_core/src/impls.rs | 23 ++++++++++++++++++ src/os.rs | 14 ++++------- src/read.rs | 55 ++++++++++++++++++------------------------ 3 files changed, 52 insertions(+), 40 deletions(-) diff --git a/rand_core/src/impls.rs b/rand_core/src/impls.rs index 9acd9ac3b39..6923105d023 100644 --- a/rand_core/src/impls.rs +++ b/rand_core/src/impls.rs @@ -82,4 +82,27 @@ pub fn fill_bytes_via_u128(rng: &mut R, dest: &mut [u8]) { fill_bytes_via!(rng, next_u128, 16, dest) } +macro_rules! impl_uint_from_fill { + ($self:expr, $ty:ty, $N:expr) => ({ + // Transmute and convert from LE (i.e. byte-swap on BE) + debug_assert!($N == ::core::mem::size_of::<$ty>()); + let mut buf = [0u8; $N]; + $self.fill_bytes(&mut buf); + unsafe{ *(buf.as_ptr() as *const $ty) }.to_le() + }); +} + +pub fn next_u32_via_fill(rng: &mut R) -> u32 { + impl_uint_from_fill!(rng, u32, 4) +} + +pub fn next_u64_via_fill(rng: &mut R) -> u64 { + impl_uint_from_fill!(rng, u64, 8) +} + +#[cfg(feature = "i128_support")] +pub fn next_u128_via_fill(rng: &mut R) -> u128 { + impl_uint_from_fill!(rng, u128, 16) +} + // TODO: implement tests for the above diff --git a/src/os.rs b/src/os.rs index 6cc28615f1e..e15f218210c 100644 --- a/src/os.rs +++ b/src/os.rs @@ -46,22 +46,18 @@ impl OsRng { impl Rng for OsRng { fn next_u32(&mut self) -> u32 { - let mut buf: [u8; 4] = [0; 4]; - self.try_fill(&mut buf).unwrap_or_else(|e| panic!("try_fill failed: {:?}", e)); - unsafe{ *(buf.as_ptr() as *const u32) } + // note: `next_u32_via_fill` does a byte-swap on big-endian + // architectures, which is not really needed here + ::rand_core::impls::next_u32_via_fill(self) } fn next_u64(&mut self) -> u64 { - let mut buf: [u8; 8] = [0; 8]; - self.try_fill(&mut buf).unwrap_or_else(|e| panic!("try_fill failed: {:?}", e)); - unsafe{ *(buf.as_ptr() as *const u64) } + ::rand_core::impls::next_u64_via_fill(self) } #[cfg(feature = "i128_support")] fn next_u128(&mut self) -> u128 { - let mut buf: [u8; 16] = [0; 16]; - self.try_fill(&mut buf).unwrap_or_else(|e| panic!("try_fill failed: {:?}", e)); - unsafe{ *(buf.as_ptr() as *const u128) } + ::rand_core::impls::next_u128_via_fill(self) } fn fill_bytes(&mut self, dest: &mut [u8]) { diff --git a/src/read.rs b/src/read.rs index dd34f579faf..6eaaaedadc8 100644 --- a/src/read.rs +++ b/src/read.rs @@ -11,8 +11,8 @@ //! A wrapper around any Read to treat it as an RNG. use std::fmt::Debug; +use std::io; use std::io::Read; -use std::mem; use {Rng, Error, ErrorKind}; @@ -21,7 +21,9 @@ use {Rng, Error, ErrorKind}; /// /// # Panics /// -/// It will panic if it there is insufficient data to fulfill a request. +/// Only the `try_fill` method will report errors. All other methods will panic +/// if the underlying reader encounters an error. They will also panic if there +/// is insufficient data to fulfill a request. /// /// # Example /// @@ -46,55 +48,43 @@ impl ReadRng { } } -macro_rules! impl_uint_from_fill { - ($ty:ty, $N:expr, $self:expr) => ({ - // Transmute and convert from LE (i.e. byte-swap on BE) - assert_eq!($N, ::core::mem::size_of::<$ty>()); - let mut buf = [0u8; $N]; - fill(&mut $self.reader, &mut buf).unwrap(); - unsafe{ *(buf.as_ptr() as *const $ty) }.to_le() - }); -} - impl Rng for ReadRng { fn next_u32(&mut self) -> u32 { - impl_uint_from_fill!(u32, 4, self) + ::rand_core::impls::next_u32_via_fill(self) } fn next_u64(&mut self) -> u64 { - impl_uint_from_fill!(u64, 8, self) + ::rand_core::impls::next_u64_via_fill(self) } #[cfg(feature = "i128_support")] fn next_u128(&mut self) -> u128 { - impl_uint_from_fill!(u128, 16, self) + ::rand_core::impls::next_u128_via_fill(self) } fn fill_bytes(&mut self, dest: &mut [u8]) { self.try_fill(dest).unwrap(); } - fn try_fill(&mut self, v: &mut [u8]) -> Result<(), Error> { - if v.len() == 0 { return Ok(()); } - fill(&mut self.reader, v) + fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { + if dest.len() == 0 { return Ok(()); } + // Use `std::io::read_exact`, which retries on `ErrorKind::Interrupted`. + self.reader.read_exact(dest).map_err(map_err) } } -fn fill(r: &mut Read, mut buf: &mut [u8]) -> Result<(), Error> { - while buf.len() > 0 { - match r.read(buf) { - Ok(0) => return Err(Error::new(ErrorKind::Unavailable, None)), - Ok(n) => buf = &mut mem::replace(&mut buf, &mut [])[n..], - Err(_) => return Err(Error::new(ErrorKind::Other, None)), - } - } - Ok(()) +fn map_err(err: io::Error) -> Error { + let kind = match err.kind() { + io::ErrorKind::UnexpectedEof => ErrorKind::Unavailable, + _ => ErrorKind::Other, + }; + Error::new(kind, Some(Box::new(err))) } #[cfg(test)] mod test { use super::ReadRng; - use Rng; + use {Rng, ErrorKind}; #[test] fn test_reader_rng_u64() { @@ -130,8 +120,11 @@ mod test { #[test] fn test_reader_rng_insufficient_bytes() { - let mut rng = ReadRng::new(&[][..]); - let mut v = [0u8; 3]; - assert!(rng.try_fill(&mut v).is_err()); + let v = [1u8, 2, 3, 4, 5, 6, 7, 8]; + let mut w = [0u8; 9]; + + let mut rng = ReadRng::new(&v[..]); + + assert!(rng.try_fill(&mut w).err().unwrap().kind == ErrorKind::Unavailable); } } From 44ef65fd486b0e583623023bcdbff683d8096148 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Fri, 20 Oct 2017 20:39:44 +0200 Subject: [PATCH 132/247] Remove default implementation from `try_fill` --- rand_core/src/lib.rs | 4 +--- rand_core/src/mock.rs | 10 +++++++++- src/prng/chacha.rs | 4 ++++ src/prng/isaac.rs | 4 ++++ src/prng/isaac64.rs | 4 ++++ src/prng/isaac_word.rs | 4 ++++ src/prng/xorshift.rs | 4 ++++ 7 files changed, 30 insertions(+), 4 deletions(-) diff --git a/rand_core/src/lib.rs b/rand_core/src/lib.rs index 4d03bf1e335..64de49be221 100644 --- a/rand_core/src/lib.rs +++ b/rand_core/src/lib.rs @@ -113,9 +113,7 @@ pub trait Rng { /// 1 is required. A different implementation might use `next_u32` and /// only consume 4 bytes; *however* any change affecting *reproducibility* /// of output must be considered a breaking change. - fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { - Ok(self.fill_bytes(dest)) - } + fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error>; } impl<'a, R: Rng+?Sized> Rng for &'a mut R { diff --git a/rand_core/src/mock.rs b/rand_core/src/mock.rs index 91fab73af4e..1e4f17dcf5f 100644 --- a/rand_core/src/mock.rs +++ b/rand_core/src/mock.rs @@ -16,7 +16,7 @@ //! Instead maybe this should be yet another crate? Or just leave it here? use core::num::Wrapping as w; -use {Rng, SeedableRng, impls}; +use {Rng, SeedableRng, Error, impls}; /// A simple implementation of `Rng`, purely for testing. /// Returns an arithmetic sequence (i.e. adds a constant each step). @@ -60,6 +60,10 @@ impl Rng for MockAddRng { fn fill_bytes(&mut self, dest: &mut [u8]) { impls::fill_bytes_via_u32(self, dest); } + + fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { + Ok(self.fill_bytes(dest)) + } } impl Rng for MockAddRng { @@ -79,6 +83,10 @@ impl Rng for MockAddRng { fn fill_bytes(&mut self, dest: &mut [u8]) { impls::fill_bytes_via_u64(self, dest); } + + fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { + Ok(self.fill_bytes(dest)) + } } impl SeedableRng for MockAddRng where diff --git a/src/prng/chacha.rs b/src/prng/chacha.rs index d2d6415ec2d..eb0192b4ea1 100644 --- a/src/prng/chacha.rs +++ b/src/prng/chacha.rs @@ -241,6 +241,10 @@ impl Rng for ChaChaRng { left.copy_from_slice(&chunk[..n]); } } + + fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { + Ok(self.fill_bytes(dest)) + } } impl SeedFromRng for ChaChaRng { diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index 7cea9ffe964..b5385db425c 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -284,6 +284,10 @@ impl Rng for IsaacRng { fn fill_bytes(&mut self, dest: &mut [u8]) { ::rand_core::impls::fill_bytes_via_u32(self, dest); } + + fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { + Ok(self.fill_bytes(dest)) + } } impl SeedFromRng for IsaacRng { diff --git a/src/prng/isaac64.rs b/src/prng/isaac64.rs index 4ac7954dd53..ca840af3471 100644 --- a/src/prng/isaac64.rs +++ b/src/prng/isaac64.rs @@ -270,6 +270,10 @@ impl Rng for Isaac64Rng { fn fill_bytes(&mut self, dest: &mut [u8]) { ::rand_core::impls::fill_bytes_via_u32(self, dest); } + + fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { + Ok(self.fill_bytes(dest)) + } } impl SeedFromRng for Isaac64Rng { diff --git a/src/prng/isaac_word.rs b/src/prng/isaac_word.rs index af4b9233848..63201b64e76 100644 --- a/src/prng/isaac_word.rs +++ b/src/prng/isaac_word.rs @@ -56,6 +56,10 @@ impl Rng for IsaacWordRng { fn fill_bytes(&mut self, dest: &mut [u8]) { self.0.fill_bytes(dest) } + + fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { + self.0.try_fill(dest) + } } impl SeedFromRng for IsaacWordRng { diff --git a/src/prng/xorshift.rs b/src/prng/xorshift.rs index 9540f96c538..031fd4b843c 100644 --- a/src/prng/xorshift.rs +++ b/src/prng/xorshift.rs @@ -87,6 +87,10 @@ impl Rng for XorShiftRng { fn fill_bytes(&mut self, dest: &mut [u8]) { ::rand_core::impls::fill_bytes_via_u32(self, dest); } + + fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { + Ok(self.fill_bytes(dest)) + } } impl SeedableRng<[u32; 4]> for XorShiftRng { From 130b64c00fe94ed1989b8b29dcf8e0b0cb29a2bf Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Fri, 20 Oct 2017 18:20:47 +0200 Subject: [PATCH 133/247] Simplify isaac init code --- src/prng/isaac.rs | 108 +++++++++++++++----------------------------- src/prng/isaac64.rs | 104 ++++++++++++++---------------------------- 2 files changed, 72 insertions(+), 140 deletions(-) diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index 310aac1d8c1..dd419afe49d 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -96,6 +96,7 @@ pub struct IsaacRng { } // Cannot be derived because [u32; 256] does not implement Clone +// FIXME: remove once RFC 2000 gets implemented impl Clone for IsaacRng { fn clone(&self) -> IsaacRng { IsaacRng { @@ -115,62 +116,21 @@ impl fmt::Debug for IsaacRng { } } -fn mix(a: &mut w32, b: &mut w32, c: &mut w32, d: &mut w32, - e: &mut w32, f: &mut w32, g: &mut w32, h: &mut w32) { - *a ^= *b << 11; *d += *a; *b += *c; - *b ^= *c >> 2; *e += *b; *c += *d; - *c ^= *d << 8; *f += *c; *d += *e; - *d ^= *e >> 16; *g += *d; *e += *f; - *e ^= *f << 10; *h += *e; *f += *g; - *f ^= *g >> 4; *a += *f; *g += *h; - *g ^= *h << 8; *b += *g; *h += *a; - *h ^= *a >> 9; *c += *h; *a += *b; -} - impl IsaacRng { /// Creates an ISAAC random number generator using an u64 as seed. /// If `seed == 0` this will produce the same stream of random numbers as /// the reference implementation when used unseeded. pub fn new_from_u64(seed: u64) -> IsaacRng { - let mut a = w(0x1367df5a); - let mut b = w(0x95d90059); - let mut c = w(0xc3163e4b); - let mut d = w(0x0f421ad8); - let mut e = w(0xd92a4a78); - let mut f = w(0xa51a3c49); - let mut g = w(0xc4efea1b); - let mut h = w(0x30609119); - - let mut mem = [w(0); RAND_SIZE]; - - a += w(seed as u32); - b += w((seed >> 32) as u32); - - for i in (0..RAND_SIZE/8).map(|i| i * 8) { - mix(&mut a, &mut b, &mut c, &mut d, &mut e, &mut f, &mut g, &mut h); - mem[i ] = a; mem[i+1] = b; - mem[i+2] = c; mem[i+3] = d; - mem[i+4] = e; mem[i+5] = f; - mem[i+6] = g; mem[i+7] = h; - } + let mut key = [w(0); RAND_SIZE]; + key[0] = w(seed as u32); + key[1] = w((seed >> 32) as u32); + // Initialize with only one pass. // A second pass does not improve the quality here, because all of // the seed was already available in the first round. // Not doing the second pass has the small advantage that if `seed == 0` // this method produces exactly the same state as the reference // implementation when used unseeded. - - let mut rng = IsaacRng { - rsl: [w(0); RAND_SIZE], - mem: mem, - a: w(0), - b: w(0), - c: w(0), - cnt: 0, - }; - - // Prepare the first set of results - rng.isaac(); - rng + init(key, 1) } /// Refills the output buffer (`self.rsl`) @@ -278,7 +238,7 @@ impl Rng for IsaacRng { } } -/// Creates a new ISAAC-64 random number generator. +/// Creates a new ISAAC random number generator. /// /// The author Bob Jenkins describes how to best initialize ISAAC here: /// https://rt.cpan.org/Public/Bug/Display.html?id=64324 @@ -304,7 +264,7 @@ impl Rng for IsaacRng { /// mixes it, and combines that with the next 32 bytes, et cetera. Then loops /// over all the elements the same way a second time." #[inline] -fn init(key: [w32; RAND_SIZE]) -> IsaacRng { +fn init(mut mem: [w32; RAND_SIZE], rounds: u32) -> IsaacRng { // These numbers are the result of initializing a...h with the // fractional part of the golden ratio in binary (0x9e3779b9) // and applying mix() 4 times. @@ -317,29 +277,23 @@ fn init(key: [w32; RAND_SIZE]) -> IsaacRng { let mut g = w(0xc4efea1b); let mut h = w(0x30609119); - let mut mem = [w(0); RAND_SIZE]; - - macro_rules! memloop { - ($arr:expr) => {{ - for i in (0..RAND_SIZE/8).map(|i| i * 8) { - a += $arr[i ]; b += $arr[i+1]; - c += $arr[i+2]; d += $arr[i+3]; - e += $arr[i+4]; f += $arr[i+5]; - g += $arr[i+6]; h += $arr[i+7]; - mix(&mut a, &mut b, &mut c, &mut d, - &mut e, &mut f, &mut g, &mut h); - mem[i ] = a; mem[i+1] = b; - mem[i+2] = c; mem[i+3] = d; - mem[i+4] = e; mem[i+5] = f; - mem[i+6] = g; mem[i+7] = h; - } - }} + // Normally this should do two passes, to make all of the seed effect all + // of `mem` + for _ in 0..rounds { + for i in (0..RAND_SIZE/8).map(|i| i * 8) { + a += mem[i ]; b += mem[i+1]; + c += mem[i+2]; d += mem[i+3]; + e += mem[i+4]; f += mem[i+5]; + g += mem[i+6]; h += mem[i+7]; + mix(&mut a, &mut b, &mut c, &mut d, + &mut e, &mut f, &mut g, &mut h); + mem[i ] = a; mem[i+1] = b; + mem[i+2] = c; mem[i+3] = d; + mem[i+4] = e; mem[i+5] = f; + mem[i+6] = g; mem[i+7] = h; + } } - memloop!(key); - // Do a second pass to make all of the seed affect all of `mem` - memloop!(mem); - let mut rng = IsaacRng { rsl: [w(0); RAND_SIZE], mem: mem, @@ -354,6 +308,18 @@ fn init(key: [w32; RAND_SIZE]) -> IsaacRng { rng } +fn mix(a: &mut w32, b: &mut w32, c: &mut w32, d: &mut w32, + e: &mut w32, f: &mut w32, g: &mut w32, h: &mut w32) { + *a ^= *b << 11; *d += *a; *b += *c; + *b ^= *c >> 2; *e += *b; *c += *d; + *c ^= *d << 8; *f += *c; *d += *e; + *d ^= *e >> 16; *g += *d; *e += *f; + *e ^= *f << 10; *h += *e; *f += *g; + *f ^= *g >> 4; *a += *f; *g += *h; + *g ^= *h << 8; *b += *g; *h += *a; + *h ^= *a >> 9; *c += *h; *a += *b; +} + impl SeedFromRng for IsaacRng { fn from_rng(other: &mut R) -> Result { let mut key = [w(0); RAND_SIZE]; @@ -364,7 +330,7 @@ impl SeedFromRng for IsaacRng { other.try_fill(slice)?; } - Ok(init(key)) + Ok(init(key, 2)) } } @@ -385,7 +351,7 @@ impl<'a> SeedableRng<&'a [u32]> for IsaacRng { *rsl_elem = w(seed_elem); } - init(key) + init(key, 2) } } diff --git a/src/prng/isaac64.rs b/src/prng/isaac64.rs index 24c48256066..2017c79f673 100644 --- a/src/prng/isaac64.rs +++ b/src/prng/isaac64.rs @@ -80,6 +80,7 @@ pub struct Isaac64Rng { } // Cannot be derived because [u64; 256] does not implement Clone +// FIXME: remove once RFC 2000 gets implemented impl Clone for Isaac64Rng { fn clone(&self) -> Isaac64Rng { Isaac64Rng { @@ -99,61 +100,20 @@ impl fmt::Debug for Isaac64Rng { } } -fn mix(a: &mut w64, b: &mut w64, c: &mut w64, d: &mut w64, - e: &mut w64, f: &mut w64, g: &mut w64, h: &mut w64) { - *a -= *e; *f ^= *h >> 9; *h += *a; - *b -= *f; *g ^= *a << 9; *a += *b; - *c -= *g; *h ^= *b >> 23; *b += *c; - *d -= *h; *a ^= *c << 15; *c += *d; - *e -= *a; *b ^= *d >> 14; *d += *e; - *f -= *b; *c ^= *e << 20; *e += *f; - *g -= *c; *d ^= *f >> 17; *f += *g; - *h -= *d; *e ^= *g << 14; *g += *h; -} - impl Isaac64Rng { /// Creates an ISAAC-64 random number generator using an u64 as seed. /// If `seed == 0` this will produce the same stream of random numbers as /// the reference implementation when used unseeded. pub fn new_from_u64(seed: u64) -> Isaac64Rng { - let mut a = w(0x647c4677a2884b7c); - let mut b = w(0xb9f8b322c73ac862); - let mut c = w(0x8c0ea5053d4712a0); - let mut d = w(0xb29b2e824a595524); - let mut e = w(0x82f053db8355e0ce); - let mut f = w(0x48fe4a0fa5a09315); - let mut g = w(0xae985bf2cbfc89ed); - let mut h = w(0x98f5704f6c44c0ab); - - let mut mem = [w(0); RAND_SIZE]; - - a += w(seed); - - for i in (0..RAND_SIZE/8).map(|i| i * 8) { - mix(&mut a, &mut b, &mut c, &mut d, &mut e, &mut f, &mut g, &mut h); - mem[i ] = a; mem[i+1] = b; - mem[i+2] = c; mem[i+3] = d; - mem[i+4] = e; mem[i+5] = f; - mem[i+6] = g; mem[i+7] = h; - } + let mut key = [w(0); RAND_SIZE]; + key[0] = w(seed); + // Initialize with only one pass. // A second pass does not improve the quality here, because all of // the seed was already available in the first round. // Not doing the second pass has the small advantage that if `seed == 0` // this method produces exactly the same state as the reference // implementation when used unseeded. - - let mut rng = Isaac64Rng { - rsl: [w(0); RAND_SIZE], - mem: mem, - a: w(0), - b: w(0), - c: w(0), - cnt: 0, - }; - - // Prepare the first set of results - rng.isaac64(); - rng + init(key, 1) } /// Refills the output buffer (`self.rsl`) @@ -263,7 +223,7 @@ impl Rng for Isaac64Rng { } /// Creates a new ISAAC-64 random number generator. -fn init(key: [w64; RAND_SIZE]) -> Isaac64Rng { +fn init(mut mem: [w64; RAND_SIZE], rounds: u32) -> Isaac64Rng { // These numbers are the result of initializing a...h with the // fractional part of the golden ratio in binary (0x9e3779b97f4a7c13) // and applying mix() 4 times. @@ -276,29 +236,23 @@ fn init(key: [w64; RAND_SIZE]) -> Isaac64Rng { let mut g = w(0xae985bf2cbfc89ed); let mut h = w(0x98f5704f6c44c0ab); - let mut mem = [w(0); RAND_SIZE]; - - macro_rules! memloop { - ($arr:expr) => {{ - for i in (0..RAND_SIZE/8).map(|i| i * 8) { - a += $arr[i ]; b += $arr[i+1]; - c += $arr[i+2]; d += $arr[i+3]; - e += $arr[i+4]; f += $arr[i+5]; - g += $arr[i+6]; h += $arr[i+7]; - mix(&mut a, &mut b, &mut c, &mut d, - &mut e, &mut f, &mut g, &mut h); - mem[i ] = a; mem[i+1] = b; - mem[i+2] = c; mem[i+3] = d; - mem[i+4] = e; mem[i+5] = f; - mem[i+6] = g; mem[i+7] = h; - } - }} + // Normally this should do two passes, to make all of the seed effect all + // of `mem` + for _ in 0..rounds { + for i in (0..RAND_SIZE/8).map(|i| i * 8) { + a += mem[i ]; b += mem[i+1]; + c += mem[i+2]; d += mem[i+3]; + e += mem[i+4]; f += mem[i+5]; + g += mem[i+6]; h += mem[i+7]; + mix(&mut a, &mut b, &mut c, &mut d, + &mut e, &mut f, &mut g, &mut h); + mem[i ] = a; mem[i+1] = b; + mem[i+2] = c; mem[i+3] = d; + mem[i+4] = e; mem[i+5] = f; + mem[i+6] = g; mem[i+7] = h; + } } - memloop!(key); - // Do a second pass to make all of the seed affect all of `mem` - memloop!(mem); - let mut rng = Isaac64Rng { rsl: [w(0); RAND_SIZE], mem: mem, @@ -313,6 +267,18 @@ fn init(key: [w64; RAND_SIZE]) -> Isaac64Rng { rng } +fn mix(a: &mut w64, b: &mut w64, c: &mut w64, d: &mut w64, + e: &mut w64, f: &mut w64, g: &mut w64, h: &mut w64) { + *a -= *e; *f ^= *h >> 9; *h += *a; + *b -= *f; *g ^= *a << 9; *a += *b; + *c -= *g; *h ^= *b >> 23; *b += *c; + *d -= *h; *a ^= *c << 15; *c += *d; + *e -= *a; *b ^= *d >> 14; *d += *e; + *f -= *b; *c ^= *e << 20; *e += *f; + *g -= *c; *d ^= *f >> 17; *f += *g; + *h -= *d; *e ^= *g << 14; *g += *h; +} + impl SeedFromRng for Isaac64Rng { fn from_rng(other: &mut R) -> Result { let mut key = [w(0); RAND_SIZE]; @@ -323,7 +289,7 @@ impl SeedFromRng for Isaac64Rng { other.try_fill(slice)?; } - Ok(init(key)) + Ok(init(key, 2)) } } @@ -344,7 +310,7 @@ impl<'a> SeedableRng<&'a [u64]> for Isaac64Rng { *rsl_elem = w(seed_elem); } - init(key) + init(key, 2) } } From ebec6c7de694440dc288172d6614aa9f62878505 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sat, 21 Oct 2017 16:07:38 +0100 Subject: [PATCH 134/247] rand-core: add CryptoRng trait --- rand_core/src/lib.rs | 100 ++++++++++++++++++++++++++++--------------- src/lib.rs | 2 +- src/prng/chacha.rs | 4 +- 3 files changed, 69 insertions(+), 37 deletions(-) diff --git a/rand_core/src/lib.rs b/rand_core/src/lib.rs index 64de49be221..d51246bd375 100644 --- a/rand_core/src/lib.rs +++ b/rand_core/src/lib.rs @@ -49,38 +49,55 @@ pub mod impls; pub mod mock; -/// A random number generator. +/// A random number generator (not necessarily suitable for cryptography). /// -/// There are two classes of generators: *algorithmic* generators, also called -/// PRNGs (Pseudo-Random Number Generators) and *external* generators. +/// "Random" number generators can be categorised multiple ways: /// -/// Another classification for generators is those that are cryptographically -/// secure (CSPRNGs) and those that are not. CSPRNGs should satisfy two -/// additional properties: (1) given the first *k* bits of an algorithm's output -/// sequence, it should not be possible using polynomial-time algorithms to -/// predict the next bit with probability significantly greater than 50%, and -/// (2) if a CSPRNG's state is revealed, it should not be -/// computationally-feasible to reconstruct output prior to this. -/// -/// PRNGs are expected to be reproducible: that is, when a fixed algorithm is -/// seeded with a fixed value, then calling *any* sequence of the `Rng`'s -/// functions should produce a fixed sequence of values, and produce the *same* -/// sequence of values on every platform. This necessitates that a PRNG have -/// fixed endianness. -/// -/// All default implementations use little-endian code (e.g. to construct a -/// `u64` from two `u32` numbers, the first is the low part). To implement -/// `next_u32` in terms of `next_u64`, one should write `self.next_u64() as u32` -/// which takes the least-significant half (LE order). +/// * *True* and *apparent* random number generators: *true* generators must +/// depend on some phenomenon which is actually random, such as atomic decay +/// or photon polarisation (note: bias may still be present, and it is +/// possible that these phenomena are in fact dependent on other unseen +/// parameters), while *apparent* random numbers are merely unpredictable. +/// * *Algorithmic* and *external* generators: *algorithmic generators* can +/// never produce *true random numbers* but can still yield hard-to-predict +/// output. External generators may or may not use *true random sources*. +/// +/// *Algorithmic generators* are also known as *psuedo-random number +/// generators* or *PRNGs*. +/// +/// *Algorithmic* generators are necessarily deterministic: if two +/// generators using the same algorithm are initialised with the same seed, +/// they will necessarily produce the same output. PRNGs should normally +/// implement the `SeedableRng` trait, allowing this. To be reproducible +/// across platforms, conversion of output from one type to another should +/// pay special attention to endianness (we generally choose LE, e.g. +/// `fn next_u32(&mut self) -> u32 { self.next_u64() as u32 }`). +/// * *Cryptographically secure*, *trivially predictable*, and various states +/// in between: if, after observing some output from an algorithmic +/// generator future output of the generator can be predicted, then it is +/// insecure. +/// +/// Note that all algorithmic generators eventually cycle, +/// returning to previous internal state and repeating all output, but in +/// good generators the period is so long that it is never reached (e.g. our +/// implementation of ChaCha would produce 2^134 bytes of output before +/// cycling, although cycles are not always so long). Predictability may not +/// be a problem for games, simulations and some randomised algorithms, +/// but unpredictability is essential in cryptography. /// -/// PRNGs are normally infallible, while external generators may fail. PRNGs -/// however have a finite period, and may emit an error rather than loop (this -/// is important for CSPRNGs which could conceivably cycle, but non-crypto -/// generators should simply cycle; in many cases the period is so long that -/// consuming all available values would be inconceivable). +/// The `Rng` trait can be used for all the above types of generators. If using +/// random numbers for cryptography prefer to use numbers pulled from the OS +/// directly (`OsRng`) or at least use a generator implementing `CryptoRng`. +/// For applications where performance is important and unpredictability is +/// less critical but still somewhat important (e.g. to prevent a DoS attack), +/// one may prefer to use a `CryptoRng` generator or may consider other "hard +/// to predict" but not cryptographically proven generators. /// -/// TODO: details on error handling are under discussion; for now implementations -/// may panic. +/// PRNGs are usually infallible, while external generators may fail. Since +/// errors are rare and may be hard for the user to handle, most of the output +/// functions only allow errors to be reported as panics; byte output can +/// however be retrieved from `try_fill` which allows for the usual error +/// handling. pub trait Rng { /// Return the next random u32. fn next_u32(&mut self) -> u32; @@ -116,6 +133,26 @@ pub trait Rng { fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error>; } +/// A marker trait for an `Rng` which may be considered for use in +/// cryptography. +/// +/// *Cryptographically secure generators*, also known as *CSPRNGs*, should +/// satisfy an additional properties over other generators: given the first +/// *k* bits of an algorithm's output +/// sequence, it should not be possible using polynomial-time algorithms to +/// predict the next bit with probability significantly greater than 50%. +/// +/// Some generators may satisfy an additional property, however this is not +/// required: if the CSPRNG's state is revealed, it should not be +/// computationally-feasible to reconstruct output prior to this. Some other +/// generators allow backwards-computation and are consided *reversible*. +/// +/// Note that this trait is provided for guidance only and cannot guarantee +/// suitability for cryptographic applications. In general it should only be +/// implemented for well-reviewed code implementing well-regarded algorithms. +pub trait CryptoRng: Rng {} + + impl<'a, R: Rng+?Sized> Rng for &'a mut R { fn next_u32(&mut self) -> u32 { (**self).next_u32() @@ -167,13 +204,6 @@ impl Rng for Box { /// Support mechanism for creating random number generators seeded by other /// generators. All PRNGs should support this to enable `NewSeeded` support, /// which should be the preferred way of creating randomly-seeded generators. -/// -/// TODO: should this use `Distribution` instead? That would require moving -/// that trait and a distribution type to this crate. -/// TODO: should the source requirement be changed, e.g. to `CryptoRng`? -/// Note: this is distinct from `SeedableRng` because it is generic over the -/// RNG type (achieving the same with `SeedableRng` would require dynamic -/// dispatch: `SeedableRng<&mut Rng>`). pub trait SeedFromRng: Sized { /// Creates a new instance, seeded from another `Rng`. /// diff --git a/src/lib.rs b/src/lib.rs index e5d823c2442..8e02c04d69e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -255,7 +255,7 @@ extern crate core; extern crate rand_core; -pub use rand_core::{Rng, SeedFromRng, SeedableRng, Error, ErrorKind}; +pub use rand_core::{Rng, CryptoRng, SeedFromRng, SeedableRng, Error, ErrorKind}; #[cfg(feature="std")] pub use read::ReadRng; diff --git a/src/prng/chacha.rs b/src/prng/chacha.rs index eb0192b4ea1..f6559afe6e6 100644 --- a/src/prng/chacha.rs +++ b/src/prng/chacha.rs @@ -11,7 +11,7 @@ //! The ChaCha random number generator. use core::num::Wrapping as w; -use {Rng, SeedFromRng, SeedableRng, Error}; +use {Rng, CryptoRng, SeedFromRng, SeedableRng, Error}; #[allow(bad_style)] type w32 = w; @@ -247,6 +247,8 @@ impl Rng for ChaChaRng { } } +impl CryptoRng for ChaChaRng {} + impl SeedFromRng for ChaChaRng { fn from_rng(other: &mut R) -> Result { let mut key : [u32; KEY_WORDS] = [0; KEY_WORDS]; From 6712a3afabdcfd1e795f2b52f7bdda7000c54e57 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Sat, 21 Oct 2017 17:02:09 +0200 Subject: [PATCH 135/247] Remove `Copy` trait from ChaCha --- src/prng/chacha.rs | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/prng/chacha.rs b/src/prng/chacha.rs index f6559afe6e6..9c5bf5fc846 100644 --- a/src/prng/chacha.rs +++ b/src/prng/chacha.rs @@ -29,20 +29,13 @@ const CHACHA_ROUNDS: u32 = 20; // Cryptographically secure from 8 upwards as of /// /// [1]: D. J. Bernstein, [*ChaCha, a variant of /// Salsa20*](http://cr.yp.to/chacha.html) -#[derive(Copy, Clone, Debug)] +#[derive(Clone, Debug)] pub struct ChaChaRng { buffer: [w32; STATE_WORDS], // Internal buffer of output state: [w32; STATE_WORDS], // Initial state index: usize, // Index into state } -static EMPTY: ChaChaRng = ChaChaRng { - buffer: [w(0); STATE_WORDS], - state: [w(0); STATE_WORDS], - index: STATE_WORDS -}; - - macro_rules! quarter_round{ ($a: expr, $b: expr, $c: expr, $d: expr) => {{ $a = $a + $b; $d = $d ^ $a; $d = w($d.0.rotate_left(16)); @@ -102,7 +95,11 @@ impl ChaChaRng { /// - 2917185654 /// - 2419978656 pub fn new_unseeded() -> ChaChaRng { - let mut rng = EMPTY; + let mut rng = ChaChaRng { + buffer: [w(0); STATE_WORDS], + state: [w(0); STATE_WORDS], + index: STATE_WORDS + }; rng.init(&[0; KEY_WORDS]); rng } @@ -265,7 +262,11 @@ impl<'a> SeedableRng<&'a [u32]> for ChaChaRng { /// Only up to 8 words are used; if less than 8 /// words are used, the remaining are set to zero. fn from_seed(seed: &'a [u32]) -> ChaChaRng { - let mut rng = EMPTY; + let mut rng = ChaChaRng { + buffer: [w(0); STATE_WORDS], + state: [w(0); STATE_WORDS], + index: STATE_WORDS + }; rng.init(&[0u32; KEY_WORDS]); // set key in place { From e513aaa8e2d93357aab0fac5982af24eb62dbb72 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Sat, 21 Oct 2017 18:56:15 +0200 Subject: [PATCH 136/247] Custom Debug implementation for ChaCha and Xorshift So the internal state is never exposed (may be security-sensitive) --- src/prng/chacha.rs | 10 +++++++++- src/prng/isaac.rs | 1 + src/prng/isaac64.rs | 1 + src/prng/xorshift.rs | 11 +++++++++-- 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/prng/chacha.rs b/src/prng/chacha.rs index 9c5bf5fc846..d066e652c29 100644 --- a/src/prng/chacha.rs +++ b/src/prng/chacha.rs @@ -11,6 +11,7 @@ //! The ChaCha random number generator. use core::num::Wrapping as w; +use core::fmt; use {Rng, CryptoRng, SeedFromRng, SeedableRng, Error}; #[allow(bad_style)] @@ -29,13 +30,20 @@ const CHACHA_ROUNDS: u32 = 20; // Cryptographically secure from 8 upwards as of /// /// [1]: D. J. Bernstein, [*ChaCha, a variant of /// Salsa20*](http://cr.yp.to/chacha.html) -#[derive(Clone, Debug)] +#[derive(Clone)] pub struct ChaChaRng { buffer: [w32; STATE_WORDS], // Internal buffer of output state: [w32; STATE_WORDS], // Initial state index: usize, // Index into state } +// Custom Debug implementation that does not expose the internal state +impl fmt::Debug for ChaChaRng { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "ChaChaRng {{}}") + } +} + macro_rules! quarter_round{ ($a: expr, $b: expr, $c: expr, $d: expr) => {{ $a = $a + $b; $d = $d ^ $a; $d = w($d.0.rotate_left(16)); diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index fa2aa0de99e..9e5ab2d91e9 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -110,6 +110,7 @@ impl Clone for IsaacRng { } } +// Custom Debug implementation that does not expose the internal state impl fmt::Debug for IsaacRng { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "IsaacRng {{}}") diff --git a/src/prng/isaac64.rs b/src/prng/isaac64.rs index 403572d9157..26daa705ea6 100644 --- a/src/prng/isaac64.rs +++ b/src/prng/isaac64.rs @@ -94,6 +94,7 @@ impl Clone for Isaac64Rng { } } +// Custom Debug implementation that does not expose the internal state impl fmt::Debug for Isaac64Rng { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Isaac64Rng {{}}") diff --git a/src/prng/xorshift.rs b/src/prng/xorshift.rs index 031fd4b843c..a1455cea270 100644 --- a/src/prng/xorshift.rs +++ b/src/prng/xorshift.rs @@ -11,6 +11,7 @@ //! Xorshift generators use core::num::Wrapping as w; +use core::fmt; use {Rng, SeedFromRng, SeedableRng, Error}; /// An Xorshift[1] random number @@ -23,8 +24,7 @@ use {Rng, SeedFromRng, SeedableRng, Error}; /// [1]: Marsaglia, George (July 2003). ["Xorshift /// RNGs"](http://www.jstatsoft.org/v08/i14/paper). *Journal of /// Statistical Software*. Vol. 8 (Issue 14). -#[allow(missing_copy_implementations)] -#[derive(Clone, Debug)] +#[derive(Clone)] pub struct XorShiftRng { x: w, y: w, @@ -32,6 +32,13 @@ pub struct XorShiftRng { w: w, } +// Custom Debug implementation that does not expose the internal state +impl fmt::Debug for XorShiftRng { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "XorShiftRng {{}}") + } +} + impl XorShiftRng { /// Creates a new XorShiftRng instance which is not seeded. /// From fdf401723396013e08f202a9f2678d9ea88a9e2b Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Sat, 21 Oct 2017 17:20:07 +0200 Subject: [PATCH 137/247] Implement Debug for TreadRng --- src/thread_local.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/thread_local.rs b/src/thread_local.rs index 819fe1cb844..0d7348ec244 100644 --- a/src/thread_local.rs +++ b/src/thread_local.rs @@ -21,8 +21,7 @@ const THREAD_RNG_RESEED_THRESHOLD: u64 = 32_768; type ReseedingStdRng = ReseedingRng; /// The thread-local RNG. -#[derive(Clone)] -#[allow(missing_debug_implementations)] +#[derive(Clone, Debug)] pub struct ThreadRng { rng: Rc>, } From 37f745068f277e9db35c3a463f2b877aca396723 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Sat, 21 Oct 2017 17:22:04 +0200 Subject: [PATCH 138/247] Remove Debug bound from ReadRng --- src/read.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/read.rs b/src/read.rs index 6eaaaedadc8..30da8d88331 100644 --- a/src/read.rs +++ b/src/read.rs @@ -10,7 +10,6 @@ //! A wrapper around any Read to treat it as an RNG. -use std::fmt::Debug; use std::io; use std::io::Read; @@ -35,11 +34,12 @@ use {Rng, Error, ErrorKind}; /// println!("{:x}", distributions::uniform::(&mut rng)); /// ``` #[derive(Debug)] -pub struct ReadRng { +// Do not derive Clone, because it could share the underlying reader +pub struct ReadRng { reader: R } -impl ReadRng { +impl ReadRng { /// Create a new `ReadRng` from a `Read`. pub fn new(r: R) -> ReadRng { ReadRng { @@ -48,7 +48,7 @@ impl ReadRng { } } -impl Rng for ReadRng { +impl Rng for ReadRng { fn next_u32(&mut self) -> u32 { ::rand_core::impls::next_u32_via_fill(self) } From b5556c6a845141ed8b590eb2355a112f98e3181a Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Sat, 21 Oct 2017 17:50:27 +0200 Subject: [PATCH 139/247] Make `OsRng` Debug use `imp::OsRng` Debug --- src/os.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/os.rs b/src/os.rs index e15f218210c..1b8f791fcbd 100644 --- a/src/os.rs +++ b/src/os.rs @@ -18,7 +18,9 @@ use {Rng, Error}; // TODO: replace many of the panics below with Result error handling /// A random number generator that retrieves randomness straight from -/// the operating system. Platform sources: +/// the operating system. +/// +/// Platform sources: /// /// - Unix-like systems (Linux, Android, Mac OSX): read directly from /// `/dev/urandom`, or from `getrandom(2)` system call if available. @@ -37,6 +39,12 @@ use {Rng, Error}; /// in-depth discussion. pub struct OsRng(imp::OsRng); +impl fmt::Debug for OsRng { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.0.fmt(f) + } +} + impl OsRng { /// Create a new `OsRng`. pub fn new() -> Result { @@ -69,12 +77,6 @@ impl Rng for OsRng { } } -impl fmt::Debug for OsRng { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "OsRng {{}}") - } -} - // Specialisation of `ReadRng` for our purposes #[derive(Debug)] struct ReadRng (R); From 47bdc03121df0d426c77c93196459dc26ce803d0 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Sun, 22 Oct 2017 14:57:05 +0200 Subject: [PATCH 140/247] Fix unaligned cast in `impl_uint_from_fill` --- rand_core/src/impls.rs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/rand_core/src/impls.rs b/rand_core/src/impls.rs index 6923105d023..cffca3e882f 100644 --- a/rand_core/src/impls.rs +++ b/rand_core/src/impls.rs @@ -18,12 +18,9 @@ //! Byte-swapping (like the std `to_le` functions) is only needed to convert //! to/from byte sequences, and since its purpose is reproducibility, //! non-reproducible sources (e.g. `OsRng`) need not bother with it. -//! -//! Missing from here are implementations of `next_u*` in terms of `try_fill`. -//! Currently `OsRng` handles these implementations itself. -//! TODO: should we add more implementations? use core::intrinsics::transmute; +use core::slice; use Rng; /// Implement `next_u64` via `next_u32`, little-endian order. @@ -84,22 +81,29 @@ pub fn fill_bytes_via_u128(rng: &mut R, dest: &mut [u8]) { macro_rules! impl_uint_from_fill { ($self:expr, $ty:ty, $N:expr) => ({ - // Transmute and convert from LE (i.e. byte-swap on BE) debug_assert!($N == ::core::mem::size_of::<$ty>()); - let mut buf = [0u8; $N]; - $self.fill_bytes(&mut buf); - unsafe{ *(buf.as_ptr() as *const $ty) }.to_le() + + let mut int: $ty = 0; + unsafe { + let ptr = &mut int as *mut $ty as *mut u8; + let slice = slice::from_raw_parts_mut(ptr, $N); + $self.fill_bytes(slice); + } + int.to_le() }); } +/// Implement `next_u32` via `fill_bytes`, little-endian order. pub fn next_u32_via_fill(rng: &mut R) -> u32 { impl_uint_from_fill!(rng, u32, 4) } +/// Implement `next_u64` via `fill_bytes`, little-endian order. pub fn next_u64_via_fill(rng: &mut R) -> u64 { impl_uint_from_fill!(rng, u64, 8) } +/// Implement `next_u128` via `fill_bytes`, little-endian order. #[cfg(feature = "i128_support")] pub fn next_u128_via_fill(rng: &mut R) -> u128 { impl_uint_from_fill!(rng, u128, 16) From b9d9987aa2ed578d0fea77f29745ba84f88dbb53 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Sun, 22 Oct 2017 20:22:39 +0200 Subject: [PATCH 141/247] Drop support for PNaCL --- src/os.rs | 66 ------------------------------------------------------- 1 file changed, 66 deletions(-) diff --git a/src/os.rs b/src/os.rs index 1b8f791fcbd..0e8975cb97c 100644 --- a/src/os.rs +++ b/src/os.rs @@ -29,7 +29,6 @@ use {Rng, Error}; /// - Windows: calls `RtlGenRandom`, exported from `advapi32.dll` as /// `SystemFunction036`. /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed. -/// - PNaCl: calls into the `nacl-irt-random-0.1` IRT interface. /// /// This usually does not block. On some systems (e.g. FreeBSD, OpenBSD, /// Max OS X, and modern Linux) this may block very early in the init @@ -419,71 +418,6 @@ mod imp { } } -#[cfg(target_os = "nacl")] -mod imp { - extern crate libc; - - use std::io; - use std::mem; - - #[derive(Debug)] - pub struct OsRng(extern fn(dest: *mut libc::c_void, - bytes: libc::size_t, - read: *mut libc::size_t) -> libc::c_int); - - extern { - fn nacl_interface_query(name: *const libc::c_char, - table: *mut libc::c_void, - table_size: libc::size_t) -> libc::size_t; - } - - const INTERFACE: &'static [u8] = b"nacl-irt-random-0.1\0"; - - #[repr(C)] - struct NaClIRTRandom { - get_random_bytes: Option libc::c_int>, - } - - impl OsRng { - pub fn new() -> Result { - let mut iface = NaClIRTRandom { - get_random_bytes: None, - }; - let result = unsafe { - nacl_interface_query(INTERFACE.as_ptr() as *const _, - mem::transmute(&mut iface), - mem::size_of::() as libc::size_t) - }; - if result != 0 { - assert!(iface.get_random_bytes.is_some()); - let result = OsRng(iface.get_random_bytes.take().unwrap()); - Ok(result) - } else { - // let error = io::ErrorKind::NotFound; - // let error = io::Error::new(error, "IRT random interface missing"); - Err(Result) - } - } - pub fn try_fill(&mut self, v: &mut [u8]) -> Result<(), Error> { - let mut read = 0; - loop { - let mut r: libc::size_t = 0; - let len = v.len(); - let error = (self.0)(v[read..].as_mut_ptr() as *mut _, - (len - read) as libc::size_t, - &mut r as *mut _); - assert!(error == 0, "`get_random_bytes` failed!"); - read += r as usize; - - if read >= v.len() { break; } - } - Ok(()) - } - } -} - #[cfg(test)] mod test { From 32e325c2660829bb980e1ad2500a58f6745e91e9 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 23 Oct 2017 09:30:26 +0100 Subject: [PATCH 142/247] from_rng: remove explicit ref; rely on impl rule --- rand_core/src/lib.rs | 2 +- src/lib.rs | 2 +- src/prng/chacha.rs | 2 +- src/prng/isaac.rs | 2 +- src/prng/isaac64.rs | 2 +- src/prng/isaac_word.rs | 2 +- src/prng/xorshift.rs | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/rand_core/src/lib.rs b/rand_core/src/lib.rs index d51246bd375..c64db368172 100644 --- a/rand_core/src/lib.rs +++ b/rand_core/src/lib.rs @@ -211,7 +211,7 @@ pub trait SeedFromRng: Sized { /// hand, seeding a simple numerical generator from another of the same /// type sometimes has serious side effects such as effectively cloning the /// generator. - fn from_rng(rng: &mut R) -> Result; + fn from_rng(rng: R) -> Result; } /// A random number generator that can be explicitly seeded to produce diff --git a/src/lib.rs b/src/lib.rs index 8e02c04d69e..acd79405e84 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -425,7 +425,7 @@ impl Rng for StdRng { } impl SeedFromRng for StdRng { - fn from_rng(other: &mut R) -> Result { + fn from_rng(other: R) -> Result { IsaacWordRng::from_rng(other).map(|rng| StdRng{ rng }) } } diff --git a/src/prng/chacha.rs b/src/prng/chacha.rs index f6559afe6e6..f564c99ba96 100644 --- a/src/prng/chacha.rs +++ b/src/prng/chacha.rs @@ -250,7 +250,7 @@ impl Rng for ChaChaRng { impl CryptoRng for ChaChaRng {} impl SeedFromRng for ChaChaRng { - fn from_rng(other: &mut R) -> Result { + fn from_rng(mut other: R) -> Result { let mut key : [u32; KEY_WORDS] = [0; KEY_WORDS]; for word in key.iter_mut() { *word = other.next_u32(); diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index fa2aa0de99e..28eb558a0c7 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -325,7 +325,7 @@ fn mix(a: &mut w32, b: &mut w32, c: &mut w32, d: &mut w32, } impl SeedFromRng for IsaacRng { - fn from_rng(other: &mut R) -> Result { + fn from_rng(mut other: R) -> Result { let mut key = [w(0); RAND_SIZE]; unsafe { let ptr = key.as_mut_ptr() as *mut u8; diff --git a/src/prng/isaac64.rs b/src/prng/isaac64.rs index 403572d9157..0ef77201dfc 100644 --- a/src/prng/isaac64.rs +++ b/src/prng/isaac64.rs @@ -284,7 +284,7 @@ fn mix(a: &mut w64, b: &mut w64, c: &mut w64, d: &mut w64, } impl SeedFromRng for Isaac64Rng { - fn from_rng(other: &mut R) -> Result { + fn from_rng(mut other: R) -> Result { let mut key = [w(0); RAND_SIZE]; unsafe { let ptr = key.as_mut_ptr() as *mut u8; diff --git a/src/prng/isaac_word.rs b/src/prng/isaac_word.rs index 69746d21e71..330656b3b8b 100644 --- a/src/prng/isaac_word.rs +++ b/src/prng/isaac_word.rs @@ -56,7 +56,7 @@ impl Rng for IsaacWordRng { } impl SeedFromRng for IsaacWordRng { - fn from_rng(other: &mut R) -> Result { + fn from_rng(other: R) -> Result { WordRngType::from_rng(other).map(|rng| IsaacWordRng(rng)) } } diff --git a/src/prng/xorshift.rs b/src/prng/xorshift.rs index 031fd4b843c..e297ef3f607 100644 --- a/src/prng/xorshift.rs +++ b/src/prng/xorshift.rs @@ -50,7 +50,7 @@ impl XorShiftRng { } impl SeedFromRng for XorShiftRng { - fn from_rng(rng: &mut R) -> Result { + fn from_rng(mut rng: R) -> Result { let mut tuple: (u32, u32, u32, u32); loop { tuple = (rng.next_u32(), rng.next_u32(), rng.next_u32(), rng.next_u32()); From c4eb96cac6a6507d20c85140caed21167595b295 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 23 Oct 2017 09:35:13 +0100 Subject: [PATCH 143/247] Move mock module from rand_core to rand --- rand_core/src/lib.rs | 1 - src/lib.rs | 3 ++- {rand_core/src => src}/mock.rs | 7 ++++--- src/reseeding.rs | 2 +- src/sequences/weighted.rs | 2 +- 5 files changed, 8 insertions(+), 7 deletions(-) rename {rand_core/src => src}/mock.rs (96%) diff --git a/rand_core/src/lib.rs b/rand_core/src/lib.rs index c64db368172..b782daa5698 100644 --- a/rand_core/src/lib.rs +++ b/rand_core/src/lib.rs @@ -46,7 +46,6 @@ extern crate core; use core::fmt; pub mod impls; -pub mod mock; /// A random number generator (not necessarily suitable for cryptography). diff --git a/src/lib.rs b/src/lib.rs index acd79405e84..8c79dc9a7ee 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -271,6 +271,7 @@ use distributions::range::Range; pub mod distributions; pub mod iter; +pub mod mock; pub mod prng; pub mod reseeding; pub mod sequences; @@ -434,7 +435,7 @@ impl SeedFromRng for StdRng { #[cfg(test)] mod test { use {Rng, thread_rng, Sample, Error}; - use rand_core::mock::MockAddRng; + use mock::MockAddRng; use distributions::{uniform}; use distributions::{Uniform, Range, Exp}; use sequences::Shuffle; diff --git a/rand_core/src/mock.rs b/src/mock.rs similarity index 96% rename from rand_core/src/mock.rs rename to src/mock.rs index 1e4f17dcf5f..a3dc7229ac8 100644 --- a/rand_core/src/mock.rs +++ b/src/mock.rs @@ -16,14 +16,15 @@ //! Instead maybe this should be yet another crate? Or just leave it here? use core::num::Wrapping as w; -use {Rng, SeedableRng, Error, impls}; +use {Rng, SeedableRng, Error}; +use rand_core::impls; /// A simple implementation of `Rng`, purely for testing. /// Returns an arithmetic sequence (i.e. adds a constant each step). /// /// ```rust -/// use rand_core::Rng; -/// use rand_core::mock::MockAddRng; +/// use rand::Rng; +/// use rand::mock::MockAddRng; /// /// let mut my_rng = MockAddRng::new(2u32, 1u32); /// assert_eq!(my_rng.next_u32(), 2u32); diff --git a/src/reseeding.rs b/src/reseeding.rs index 30144644375..28f6fd37d5a 100644 --- a/src/reseeding.rs +++ b/src/reseeding.rs @@ -133,7 +133,7 @@ impl Reseeder for ReseedWithNew { #[cfg(test)] mod test { use std::iter::repeat; - use rand_core::mock::MockAddRng; + use mock::MockAddRng; use {SeedableRng, Rng, iter}; use distributions::ascii_word_char; use super::{ReseedingRng, Reseeder}; diff --git a/src/sequences/weighted.rs b/src/sequences/weighted.rs index a39e1f6e821..a368990effc 100644 --- a/src/sequences/weighted.rs +++ b/src/sequences/weighted.rs @@ -134,7 +134,7 @@ impl Distribution for WeightedChoice { #[cfg(test)] mod tests { - use rand_core::mock::MockAddRng; + use mock::MockAddRng; use distributions::Distribution; use super::{WeightedChoice, Weighted}; From 0b1ac0fe92b04958fee248b3b59a8c2a8a9eba8f Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 30 Oct 2017 11:17:43 +0000 Subject: [PATCH 144/247] Use fill_bytes(..) instead of try_fill(..).unwrap() --- benches/generators.rs | 2 +- rand_core/src/impls.rs | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/benches/generators.rs b/benches/generators.rs index 44adb0e1c2d..dd223a2fbc9 100644 --- a/benches/generators.rs +++ b/benches/generators.rs @@ -20,7 +20,7 @@ macro_rules! gen_bytes { let mut buf = [0u8; BYTES_LEN]; b.iter(|| { for _ in 0..RAND_BENCH_N { - rng.try_fill(&mut buf).unwrap(); + rng.fill_bytes(&mut buf); black_box(buf); } }); diff --git a/rand_core/src/impls.rs b/rand_core/src/impls.rs index 6923105d023..d0f221eb35a 100644 --- a/rand_core/src/impls.rs +++ b/rand_core/src/impls.rs @@ -18,10 +18,6 @@ //! Byte-swapping (like the std `to_le` functions) is only needed to convert //! to/from byte sequences, and since its purpose is reproducibility, //! non-reproducible sources (e.g. `OsRng`) need not bother with it. -//! -//! Missing from here are implementations of `next_u*` in terms of `try_fill`. -//! Currently `OsRng` handles these implementations itself. -//! TODO: should we add more implementations? use core::intrinsics::transmute; use Rng; From 07e1d58c16b063add71a608604dca8c9ec8d1d14 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Mon, 30 Oct 2017 19:58:57 +0100 Subject: [PATCH 145/247] Implement `JitterRng`, based on `jitterentropy-library`. This is a pretty direct translation from C to Rust. Some notes per function: ### `random_loop_cnt` (`jent_loop_shuffle`) Because the `min` argument was always `0`, I removed that argument. The C code did not seem to fold the time optimally. When `bits` is set to `7`, as `mem_access` does, it will fold `64 / 7 = 9` times, leaving 1 bit unused. It is minor, but we use `folds = (64 + n_bits - 1) / n_bits;`, so all is used. We do not add 1 to the resulting loop count, this should be done in the calling code. `memaccess` already adds 128 anyway. For `lfsr_time` we use the loop count on a `throw_away` value, and then run the real calculation once. ### `lfsr_time` (`jent_lfsr_time`) We do not allow overriding `loop_cnt`, and also do not return the actual `loop_cnt` used. This only had some use in testing the C code, but was 'not needed during runtime'. Only the last round of the outer loop (in C) effect `self.data`. In Rust the inner loop is part of the `lfsr` helper function. All but the last round operate on a `throw_away` function, that does not get reset between the loop rounds. ### `memaccess` (`jent_memaccess`) We do not allow overriding `loop_cnt`, and also do not return the actual `loop_cnt` used. This only had some use in testing the C code, but was 'not needed during runtime'. We do not do NULL pointer checks, and running `JitterRng` without the Memory Access noise source is (currently) not supported. We (currently) do not support changing `memblocksize` and `memblocks` except by changing the constants `MEMORY_BLOCKS` and `MEMORY_BLOCKSIZE`. So instead of recalculating `wrap`, we can just re-use MEMORY_SIZE. Instead of a `memlocation` pointer we use indexing. The `index` is calculated before accessing `self.mem[index] instead of after. This convinces the compiler indexing is safe, and eliminates bounds checks. ### `stuck` (`jent_stuck`) For all delta's we use an `i64`, instead of an `u64` for the first delta in the C implementation. This handles a clock that may not be entirely monotonic (for example due to NTP) slightly better. Also, we return a `bool` instead of an `u64`. ### `measure_jitter` (`jent_measure_jitter`) It seemed clearer here to not return an `u64` or `bool`, but `Option<()>`. `Some` and `None` indicate clearly whether we have been able to add some entropy. For `current_delta` we use an `i64` instead of an `u64`. It is cast back to an `u64` for `lfsr_time`, which only cares about bits. ### `stir_pool` (`jent_stir_pool`) The C code does something difficult with initializing an `u64` with two `u32`'s in an `union`. The numbers it uses for initialization are from SHA-1, and the order does not really matter. The Rust code just sets the `u64`'s directly, and uses the constants in the order they appear in FIPS 180-4 section 5.3.1. The function tries to be constant time to prevent leaking timing information about the generated random number. Using a `trow_away` value like it does is optimised out, and I don't trust branches to be constant time. I used a bit mask trick instead, and verified the assembly does not include anything conditional. Not sure it matters anything, we just went through a lot of effort to have as much timing variance as possible to generate the random number. ### `gen_entropy` (`jent_gen_entropy`) We do not support oversampling, so no need to repeat the loop more times. `self.memaccess()` in `measure_jitter` is easily optimised out, because LLVM recognises we never read the results. Therefore we do a single read from `self.mem`, hidden to the optimizer with `black_box()`. We return `self.data` instead of requiring the calling code to read from it. ### (not included) `jent_read_entropy` Here we have the convienent `fill_bytes_via_u64` for. The C code calls `jent_gen_entropy` one last time after filling the buffer, to make sure that something reading the processes memory can not read the last generated random number. 'This call reduces the speed of the RNG by up to half`. It seems to me setting it to 0 is just as good. I did not bother with this. As an alternative a user caring very much about this can just call `next_u64` after receiving a result. ### `entropy_init` (`jent_entropy_init`) Wrap `lfsr_time` in the loop in a `black_box` to make sure it is not optimised out. For delta we use an `i64` instead of an `u64`. Instead of `lowdelta` we just use `delta`. It seems a hack to compile on some 32-bit architectures. --- benches/generators.rs | 25 +- src/jitter_rng.rs | 596 ++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 2 + 3 files changed, 617 insertions(+), 6 deletions(-) create mode 100644 src/jitter_rng.rs diff --git a/benches/generators.rs b/benches/generators.rs index 44adb0e1c2d..a48752cca31 100644 --- a/benches/generators.rs +++ b/benches/generators.rs @@ -9,7 +9,7 @@ const BYTES_LEN: usize = 1024; use std::mem::size_of; use test::{black_box, Bencher}; -use rand::{Rng, NewSeeded, SeedFromRng, StdRng, OsRng, Rand, Default}; +use rand::{Rng, NewSeeded, SeedFromRng, StdRng, OsRng, JitterRng, Rand, Default}; use rand::prng::{XorShiftRng, IsaacRng, Isaac64Rng, ChaChaRng}; macro_rules! gen_bytes { @@ -59,15 +59,22 @@ gen_usize!(gen_usize_chacha, ChaChaRng); gen_usize!(gen_usize_std, StdRng); gen_usize!(gen_usize_os, OsRng); +#[bench] +fn gen_usize_jitter(b: &mut Bencher) { + let mut rng = JitterRng::new().unwrap(); + b.iter(|| { + black_box(usize::rand(&mut rng, Default)); + }); + b.bytes = size_of::() as u64 * RAND_BENCH_N; +} + macro_rules! init_gen { ($fnn:ident, $gen:ident) => { #[bench] fn $fnn(b: &mut Bencher) { - let mut rng = XorShiftRng::new().unwrap(); + let mut rng = OsRng::new().unwrap(); b.iter(|| { - for _ in 0..RAND_BENCH_N { - black_box($gen::from_rng(&mut rng).unwrap()); - } + black_box($gen::from_rng(&mut rng).unwrap()); }); } } @@ -77,4 +84,10 @@ init_gen!(init_xorshift, XorShiftRng); init_gen!(init_isaac, IsaacRng); init_gen!(init_isaac64, Isaac64Rng); init_gen!(init_chacha, ChaChaRng); -init_gen!(init_std, StdRng); + +#[bench] +fn init_jitter(b: &mut Bencher) { + b.iter(|| { + black_box(JitterRng::new().unwrap()); + }); +} diff --git a/src/jitter_rng.rs b/src/jitter_rng.rs new file mode 100644 index 00000000000..25fbc26126a --- /dev/null +++ b/src/jitter_rng.rs @@ -0,0 +1,596 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +// +// Based on jitterentropy-library +// +// FIXME: does the 3-clause BSD license below allow us to dual-license with the +// MIT license? +// +// Copyright Stephan Mueller , 2014 - 2017 +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, and the entire permission notice in its entirety, +// including the disclaimer of warranties. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. The name of the author may not be used to endorse or promote +// products derived from this software without specific prior +// written permission. +// +// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF +// WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +// OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH +// DAMAGE. + +//! Non-physical true random number generator based on timing jitter. + +use {CryptoRng, Rng, Error}; +use rand_core; +use rand_core::impls; + +use core; +use core::fmt; + +const MEMORY_BLOCKS: usize = 64; +const MEMORY_BLOCKSIZE: usize = 32; +const MEMORY_SIZE: usize = MEMORY_BLOCKS * MEMORY_BLOCKSIZE; + +/// A true random number generator based on jitter in the CPU execution time, +/// and jitter in memory access time. +/// +/// This is a true random number generator, as opposed to pseudo-random +/// generators. Random numbers generated by `JitterRng` can be seen as fresh +/// entropy. A consequence is that is orders of magnitude slower than `OsRng` +/// and PRNGs (about 10^3 .. 10^6 slower). +/// +/// There are very few situations where using this RNG is appropriate. Only very +/// few applications require true entropy. A normal PRNG can be statistically +/// indistinguishable, and a cryptographic PRNG should also be as impossible to +/// predict. +/// +/// Use of `JitterRng` is recommended for initializing cryptographic PRNGs when +/// `OsRng` is not available. +/// +/// This implementation is based on +/// [Jitterentropy](http://www.chronox.de/jent.html) version 2.1.0. +// +// Note: the C implementation relies on being compiled without optimizations. +// This implementation goes through lengths to make the compiler not optimise +// out what is technically dead code, but that does influence timing jitter. +pub struct JitterRng { + data: u64, // Actual random number + // Timer and previous time stamp, used by `measure_jitter` + timer: fn() -> u64, + prev_time: u64, + // Deltas used for the stuck test + last_delta: i64, + last_delta2: i64, + // Memory for the Memory Access noise source + mem_prev_index: usize, + mem: [u8; MEMORY_SIZE], + // Make `next_u32` not waste 32 bits + data_remaining: Option, +} + +// Custom Debug implementation that does not expose the internal state +impl fmt::Debug for JitterRng { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "JitterRng {{}}") + } +} + +/// An error that can occur when intializing `JitterRng`. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Error { + kind: ErrorKind, +} + +/// Error kind which can be matched over. +#[derive(PartialEq, Eq, Debug, Copy, Clone)] +pub enum ErrorKind { + /// No timer available. + NoTimer, + /// Timer too coarse to use as an entropy source. + CoarseTimer, + /// Timer is not monotonically increasing. + NotMonotonic, + /// Variations of deltas of time too small. + TinyVariantions, + /// Too many stuck results (indicating no added entropy). + ToManyStuck, + #[doc(hidden)] + __Nonexhaustive, +} + +impl ErrorKind { + fn description(&self) -> &'static str { + match *self { + ErrorKind::NoTimer => "no timer available", + ErrorKind::CoarseTimer => "coarse timer", + ErrorKind::NotMonotonic => "timer not monotonic", + ErrorKind::TinyVariantions => "time delta variations too small", + ErrorKind::ToManyStuck => "to many stuck results", + ErrorKind::__Nonexhaustive => unreachable!(), + } + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self.kind { + ErrorKind::NoTimer => + write!(f, "No timer available."), + ErrorKind::CoarseTimer => + write!(f, "Timer too coarse to use as an entropy source."), + ErrorKind::NotMonotonic => + write!(f, "Timer is not monotonically increasing."), + ErrorKind::TinyVariantions => + write!(f, "Variations of deltas of time too small."), + ErrorKind::ToManyStuck => + write!(f, "To many stuck results (indicating no added entropy)."), + ErrorKind::__Nonexhaustive => unreachable!(), + } + } +} + +#[cfg(feature="std")] +impl ::std::error::Error for Error { + fn description(&self) -> &str { + self.kind.description() + } +} + +#[cfg(feature="std")] +fn new_error(kind: ErrorKind) -> rand_core::Error { + rand_core::Error { + kind: rand_core::ErrorKind::Unavailable, + cause: Some(Box::new(Error { kind: kind })), + } +} + +#[cfg(not(feature="std"))] +fn new_error(kind: ErrorKind) -> rand_core::Error { + rand_core::Error { + kind: rand_core::ErrorKind::Unavailable, + cause: kind.description(), + } +} + +impl JitterRng { + /// Create a new `JitterRng`. + /// Makes use of `std::time` for a timer. + /// + /// During initialization CPU execution timing jitter is measured a few + /// hundred times. If this does not pass basic quality tests, an error is + /// returned. + #[cfg(feature="std")] + pub fn new() -> Result { + JitterRng::new_with_timer(get_nstime) + } + + /// Create a new `JitterRng`. + /// A custom timer can be supplied, making it possible to use `JitterRng` in + /// `no_std` environments. + /// + /// The timer must have nanosecond precision. + /// + /// During initialization CPU execution timing jitter is measured a few + /// hundred times. If this does not pass basic quality tests, an error is + /// returned. + pub fn new_with_timer(timer: fn() -> u64) + -> Result { + let mut ec = JitterRng { + data: 0, + timer: timer, + prev_time: 0, + last_delta: 0, + last_delta2: 0, + mem_prev_index: 0, + mem: [0; MEMORY_SIZE], + data_remaining: None, + }; + + ec.entropy_init()?; + Ok(ec) + } + + // Calculate a random loop count used for the next round of an entropy + // collection, based on bits from a fresh value from the timer. + // + // The timer is folded to produce a number that contains at most `n_bits` + // bits. + // + // Note: A constant should be added to the resulting random loop count to + // prevent loops that run 0 times. + #[inline(never)] + fn random_loop_cnt(&mut self, n_bits: u32) -> u32 { + let mut rounds = 0; + + let mut time = (self.timer)(); + // Mix with the current state of the random number balance the random + // loop counter a bit more. + time ^= self.data; + + // We fold the time value as much as possible to ensure that as many + // bits of the time stamp are included as possible. + let folds = (64 + n_bits - 1) / n_bits; + let mask = (1 << n_bits) - 1; + for _ in 0..folds { + rounds ^= time & mask; + time = time >> n_bits; + } + + rounds as u32 + } + + // CPU jitter noise source + // Noise source based on the CPU execution time jitter + // + // This function injects the individual bits of the time value into the + // entropy pool using an LFSR. + // + // The code is deliberately inefficient with respect to the bit shifting. + // This function not only acts as folding operation, but this function's + // execution is used to measure the CPU execution time jitter. Any change to + // the loop in this function implies that careful retesting must be done. + #[inline(never)] + fn lfsr_time(&mut self, time: u64) { + fn lfsr(mut data: u64, time: u64) -> u64{ + for i in 1..65 { + let mut tmp = time << (64 - i); + tmp = tmp >> (64 - 1); + + // Fibonacci LSFR with polynomial of + // x^64 + x^61 + x^56 + x^31 + x^28 + x^23 + 1 which is + // primitive according to + // http://poincare.matf.bg.ac.rs/~ezivkovm/publications/primpol1.pdf + // (the shift values are the polynomial values minus one + // due to counting bits from 0 to 63). As the current + // position is always the LSB, the polynomial only needs + // to shift data in from the left without wrap. + data ^= tmp; + data ^= (data >> 63) & 1; + data ^= (data >> 60) & 1; + data ^= (data >> 55) & 1; + data ^= (data >> 30) & 1; + data ^= (data >> 27) & 1; + data ^= (data >> 22) & 1; + data = data.rotate_left(1); + } + data + } + + // Note: in the reference implementation only the last round effects + // `self.data`, all the other results are ignored. To make sure the + // other rounds are not optimised out, we first run all but the last + // round on a throw-away value instead of the real `self.data`. + let mut throw_away: u64 = 0; + for _ in 0..(self.random_loop_cnt(4)) { + throw_away = lfsr(throw_away, time); + } + black_box(throw_away); + + self.data = lfsr(self.data, time); + } + + // Memory Access noise source + // This is a noise source based on variations in memory access times + // + // This function performs memory accesses which will add to the timing + // variations due to an unknown amount of CPU wait states that need to be + // added when accessing memory. The memory size should be larger than the L1 + // caches as outlined in the documentation and the associated testing. + // + // The L1 cache has a very high bandwidth, albeit its access rate is usually + // slower than accessing CPU registers. Therefore, L1 accesses only add + // minimal variations as the CPU has hardly to wait. Starting with L2, + // significant variations are added because L2 typically does not belong to + // the CPU any more and therefore a wider range of CPU wait states is + // necessary for accesses. L3 and real memory accesses have even a wider + // range of wait states. However, to reliably access either L3 or memory, + // the `self.mem` memory must be quite large which is usually not desirable. + #[inline(never)] + fn memaccess(&mut self) { + let mut index = self.mem_prev_index; + for _ in 0..(self.random_loop_cnt(7) + 128) { + // Addition of memblocksize - 1 to index with wrap around logic to + // ensure that every memory location is hit evenly. + // The modulus also allows the compiler to remove the indexing + // bounds check. + index = (index + MEMORY_BLOCKSIZE - 1) % MEMORY_SIZE; + + // memory access: just add 1 to one byte + // memory access implies read from and write to memory location + let tmp = self.mem[index]; + self.mem[index] = tmp.wrapping_add(1); + } + self.mem_prev_index = index; + } + + + // Stuck test by checking the: + // - 1st derivation of the jitter measurement (time delta) + // - 2nd derivation of the jitter measurement (delta of time deltas) + // - 3rd derivation of the jitter measurement (delta of delta of time + // deltas) + // + // All values must always be non-zero. + // This test is a heuristic to see whether the last measurement holds + // entropy. + fn stuck(&mut self, current_delta: i64) -> bool { + let delta2 = self.last_delta - current_delta; + let delta3 = delta2 - self.last_delta2; + + self.last_delta = current_delta; + self.last_delta2 = delta2; + + current_delta == 0 || delta2 == 0 || delta3 == 0 + } + + // This is the heart of the entropy generation: calculate time deltas and + // use the CPU jitter in the time deltas. The jitter is injected into the + // entropy pool. + // + // Ensure that `self.prev_time` is primed before using the output of this + // function. This can be done by calling this function and not using its + // result. + fn measure_jitter(&mut self) -> Option<()> { + // Invoke one noise source before time measurement to add variations + self.memaccess(); + + // Get time stamp and calculate time delta to previous + // invocation to measure the timing variations + let time = (self.timer)(); + let current_delta = time.wrapping_sub(self.prev_time) as i64; + self.prev_time = time; + + // Call the next noise source which also injects the data + self.lfsr_time(current_delta as u64); + + // Check whether we have a stuck measurement (i.e. does the last + // measurement holds entropy?). + if self.stuck(current_delta) { return None }; + + // Rotate the data buffer by a prime number (any odd number would + // do) to ensure that every bit position of the input time stamp + // has an even chance of being merged with a bit position in the + // entropy pool. We do not use one here as the adjacent bits in + // successive time deltas may have some form of dependency. The + // chosen value of 7 implies that the low 7 bits of the next + // time delta value is concatenated with the current time delta. + self.data = self.data.rotate_left(7); + + Some(()) + } + + // Shuffle the pool a bit by mixing some value with a bijective function + // (XOR) into the pool. + // + // The function generates a mixer value that depends on the bits set and + // the location of the set bits in the random number generated by the + // entropy source. Therefore, based on the generated random number, this + // mixer value can have 2^64 different values. That mixer value is + // initialized with the first two SHA-1 constants. After obtaining the + // mixer value, it is XORed into the random number. + // + // The mixer value is not assumed to contain any entropy. But due to the + // XOR operation, it can also not destroy any entropy present in the + // entropy pool. + #[inline(never)] + fn stir_pool(&mut self) { + // This constant is derived from the first two 32 bit initialization + // vectors of SHA-1 as defined in FIPS 180-4 section 5.3.1 + // The order does not really matter as we do not rely on the specific + // numbers. We just pick the SHA-1 constants as they have a good mix of + // bit set and unset. + const CONSTANT: u64 = 0x67452301efcdab89; + + // The start value of the mixer variable is derived from the third + // and fourth 32 bit initialization vector of SHA-1 as defined in + // FIPS 180-4 section 5.3.1 + let mut mixer = 0x98badcfe10325476; + + // This is a constant time function to prevent leaking timing + // information about the random number. + // The normal code is: + // ``` + // for i in 0..64 { + // if ((self.data >> i) & 1) == 1 { mixer ^= CONSTANT; } + // } + // ``` + // This is a bit fragile, as LLVM really wants to use branches here, and + // we rely on it to not recognise the opportunity. + for i in 0..64 { + let apply = (self.data >> i) & 1; + let mask = !((apply as i64) - 1) as u64; + mixer ^= CONSTANT & mask; + mixer = mixer.rotate_left(1); + } + + self.data ^= mixer; + } + + fn gen_entropy(&mut self) -> u64 { + // Prime `self.prev_time`, and run the noice sources to make sure the + // first loop round collects the expected entropy. + let _ = self.measure_jitter(); + + for _ in 0..64 { + // If a stuck measurement is received, repeat measurement + // Note: we do not guard against an infinite loop, that would mean + // the timer suddenly became broken. + while self.measure_jitter().is_none() {} + } + + self.stir_pool(); + + // Do a single read from `self.mem` to make sure the Memory Access noise + // source is not optimised out. + black_box(self.mem[0]); + + self.data + } + + fn entropy_init(&mut self) -> Result<(), rand_core::Error> { + // We could add a check for system capabilities such as `clock_getres` + // or check for `CONFIG_X86_TSC`, but it does not make much sense as the + // following sanity checks verify that we have a high-resolution timer. + + let mut delta_sum = 0; + let mut old_delta = 0; + + let mut time_backwards = 0; + let mut count_mod = 0; + let mut count_stuck = 0; + + // TESTLOOPCOUNT needs some loops to identify edge systems. + // 100 is definitely too little. + const TESTLOOPCOUNT: u64 = 300; + const CLEARCACHE: u64 = 100; + + for i in 0..(CLEARCACHE + TESTLOOPCOUNT) { + // Invoke core entropy collection logic + let time = (self.timer)(); + self.lfsr_time(time); + let time2 = (self.timer)(); + + // Test whether timer works + if time == 0 || time2 == 0 { + return Err(new_error(ErrorKind::NoTimer)); + } + let delta = time2.wrapping_sub(time) as i64; + + // Test whether timer is fine grained enough to provide delta even + // when called shortly after each other -- this implies that we also + // have a high resolution timer + if delta == 0 { + return Err(new_error(ErrorKind::CoarseTimer)); + } + + // Up to here we did not modify any variable that will be + // evaluated later, but we already performed some work. Thus we + // already have had an impact on the caches, branch prediction, + // etc. with the goal to clear it to get the worst case + // measurements. + if i < CLEARCACHE { continue; } + + if self.stuck(delta) { count_stuck += 1; } + + // Test whether we have an increasing timer. + if !(time2 > time) { time_backwards += 1; } + + // Count the number of times the counter increases in steps of 100ns + // or greater. + if (delta % 100) == 0 { count_mod += 1; } + + // Ensure that we have a varying delta timer which is necessary for + // the calculation of entropy -- perform this check only after the + // first loop is executed as we need to prime the old_delta value + delta_sum += (delta - old_delta).abs() as u64; + old_delta = delta; + } + + // We allow the time to run backwards for up to three times. + // This can happen if the clock is being adjusted by NTP operations. + // If such an operation just happens to interfere with our test, it + // should not fail. The value of 3 should cover the NTP case being + // performed during our test run. + if time_backwards > 3 { + return Err(new_error(ErrorKind::NotMonotonic)); + } + + // Variations of deltas of time must on average be larger than 1 to + // ensure the entropy estimation implied with 1 is preserved + if delta_sum < (TESTLOOPCOUNT) { + return Err(new_error(ErrorKind::TinyVariantions)); + } + + // Ensure that we have variations in the time stamp below 100 for at + // least 10% of all checks -- on some platforms, the counter increments + // in multiples of 100, but not always + if count_mod > (TESTLOOPCOUNT/10 * 9) { + return Err(new_error(ErrorKind::CoarseTimer)); + } + + // If we have more than 90% stuck results, then this Jitter RNG is + // likely to not work well. + if count_stuck > (TESTLOOPCOUNT/10 * 9) { + return Err(new_error(ErrorKind::ToManyStuck)); + } + + Ok(()) + } +} + +#[cfg(feature="std")] +fn get_nstime() -> u64 { + use std::time::{SystemTime, UNIX_EPOCH}; + + let dur = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); + // The correct way to calculate the current time is + // `dur.as_secs() * 1_000_000_000 + dur.subsec_nanos() as u64` + // But this is faster, and the difference in terms of entropy is negligible + // (log2(10^9) == 29.9). + dur.as_secs() << 30 | dur.subsec_nanos() as u64 +} + +// A function that is opaque to the optimizer to assist in avoiding dead-code +// elimination. Taken from `bencher`. +fn black_box(dummy: T) -> T { + unsafe { + let ret = core::ptr::read_volatile(&dummy); + core::mem::forget(dummy); + ret + } +} + +impl Rng for JitterRng { + fn next_u32(&mut self) -> u32 { + // We want to use both parts of the generated entropy + if let Some(high) = self.data_remaining.take() { + high + } else { + let data = self.next_u64(); + self.data_remaining = Some((data >> 32) as u32); + data as u32 + } + } + + fn next_u64(&mut self) -> u64 { + self.gen_entropy() + } + + #[cfg(feature = "i128_support")] + fn next_u128(&mut self) -> u128 { + impls::next_u128_via_u64(self) + } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + impls::fill_bytes_via_u64(self, dest) + } + + fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> { + Ok(self.fill_bytes(dest)) + } +} + +impl CryptoRng for JitterRng {} diff --git a/src/lib.rs b/src/lib.rs index 8c79dc9a7ee..68ce87e7c92 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -261,6 +261,7 @@ pub use rand_core::{Rng, CryptoRng, SeedFromRng, SeedableRng, Error, ErrorKind}; pub use read::ReadRng; #[cfg(feature="std")] pub use os::OsRng; +pub use jitter_rng::JitterRng; pub use iter::iter; pub use distributions::{Distribution, Default, Rand}; #[cfg(feature="std")] @@ -271,6 +272,7 @@ use distributions::range::Range; pub mod distributions; pub mod iter; +pub mod jitter_rng; pub mod mock; pub mod prng; pub mod reseeding; From 6e40910714b60ce6ce4f7064cf9af8873734c41c Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Thu, 2 Nov 2017 21:49:25 +0100 Subject: [PATCH 146/247] Relicensing permission > One question about the license. All libraries in the Rust ecosystem try > to be dual-licensed with the permissive Apache 2.0 license and the MIT > license. I am not sure it is ok for me to do so, because your code used > the 3-clause BSD. The difference seems to be in the third clause: "The > name of the author may not be used to endorse or promote products > derived from this software without specific prior written permission." > Can you maybe give permission to license this Rust translation under the > MIT license? Granted. I am fine with the mentioned license as long as there is a reference to my code. --- src/jitter_rng.rs | 35 ++++------------------------------- 1 file changed, 4 insertions(+), 31 deletions(-) diff --git a/src/jitter_rng.rs b/src/jitter_rng.rs index 25fbc26126a..213bec654fd 100644 --- a/src/jitter_rng.rs +++ b/src/jitter_rng.rs @@ -8,38 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. // -// Based on jitterentropy-library +// Based on jitterentropy-library, http://www.chronox.de/jent.html. +// Copyright Stephan Mueller , 2014 - 2017. // -// FIXME: does the 3-clause BSD license below allow us to dual-license with the -// MIT license? -// -// Copyright Stephan Mueller , 2014 - 2017 -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// 1. Redistributions of source code must retain the above copyright -// notice, and the entire permission notice in its entirety, -// including the disclaimer of warranties. -// 2. Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// 3. The name of the author may not be used to endorse or promote -// products derived from this software without specific prior -// written permission. -// -// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED -// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF -// WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT -// OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR -// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH -// DAMAGE. +// With permission from Stephan Mueller to relicense the Rust translation under +// the MIT license. //! Non-physical true random number generator based on timing jitter. From 8ff9394146b406184b6e0b035af670e3b5df71eb Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Wed, 8 Nov 2017 12:54:32 +0000 Subject: [PATCH 147/247] Revise error type (copy from error_details branch) --- rand_core/src/lib.rs | 105 ++++++++++++++++++++++++++++++------------- src/read.rs | 2 +- 2 files changed, 76 insertions(+), 31 deletions(-) diff --git a/rand_core/src/lib.rs b/rand_core/src/lib.rs index b782daa5698..c1cdfc74c78 100644 --- a/rand_core/src/lib.rs +++ b/rand_core/src/lib.rs @@ -43,6 +43,9 @@ #[cfg(feature="std")] extern crate core; +#[cfg(feature="std")] +use std::error::Error as stdError; + use core::fmt; pub mod impls; @@ -247,55 +250,97 @@ pub enum ErrorKind { __Nonexhaustive, } -#[cfg(feature="std")] -#[derive(Debug)] -pub struct Error { - pub kind: ErrorKind, - pub cause: Option>, +impl ErrorKind { + /// True if this kind of error may resolve itself on retry. + /// + /// See also `should_wait()`. + pub fn should_retry(self) -> bool { + match self { + ErrorKind::Transient | ErrorKind::NotReady => true, + _ => false, + } + } + /// True if we should retry but wait before retrying + /// + /// This implies `should_retry()` is true. + pub fn should_wait(self) -> bool { + match self { + ErrorKind::NotReady => true, + _ => false, + } + } + /// A description of this error kind + pub fn description(self) -> &'static str { + match self { + ErrorKind::Unavailable => "permanent failure or unavailable", + ErrorKind::Transient => "transient failure", + ErrorKind::NotReady => "not ready yet", + ErrorKind::Other => "uncategorised", + ErrorKind::__Nonexhaustive => unreachable!(), + } + } } -#[cfg(not(feature="std"))] -#[derive(Debug, Clone, PartialEq, Eq)] +/// Error type of random number generators +/// +/// This embeds an `ErrorKind` which can be matched over, a *message* to tell +/// users what happened, and optionally a *cause* (which allows chaining back +/// to the original error). +/// +/// The cause is omitted in `no_std` mode (see `Error::new` for details). +#[derive(Debug)] pub struct Error { + /// Error kind. This enum is included to aid handling of errors. pub kind: ErrorKind, - pub cause: Option<&'static str>, + msg: &'static str, + #[cfg(feature="std")] + cause: Option>, } impl Error { + /// Create a new instance, with specified kind, message, and optionally a + /// chained cause. + /// + /// Note: `stdError` is an alias for `std::error::Error`. + /// + /// If not targetting `std` (i.e. `no_std`), this function is replaced by + /// another with the same prototype, except that there are no bounds on the + /// type `E` (because both `Box` and `stdError` are unavailable), and the + /// `cause` is ignored. #[cfg(feature="std")] - pub fn new(kind: ErrorKind, cause: Option>) -> Error { - Error { - kind: kind, - cause: cause, - } + pub fn new(kind: ErrorKind, msg: &'static str, cause: Option) -> Self + where E: Into> + { + Self { kind, msg, cause: cause.map(|inner| inner.into()) } + } + /// Create a new instance, with specified kind, message, and optionally a + /// chained cause. + /// + /// In `no_std` mode the *cause* is ignored. + #[cfg(not(feature="std"))] + pub fn new(kind: ErrorKind, msg: &'static str, _cause: Option) -> Self { + Self { kind, msg } + } + + /// Get the error message + pub fn msg(&self) -> &'static str { + self.msg } } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self.kind { - ErrorKind::Unavailable => write!(f, "RNG not available."), - ErrorKind::Transient => write!(f, "RNG has failed, probably temporary."), - ErrorKind::NotReady => write!(f, "RNG not ready yet."), - ErrorKind::Other => write!(f, "An unspecified RNG error occurred."), - ErrorKind::__Nonexhaustive => unreachable!(), - } + write!(f, "RNG error [{}]: {}", self.kind.description(), self.msg()) } } #[cfg(feature="std")] -impl ::std::error::Error for Error { +impl stdError for Error { fn description(&self) -> &str { - match self.kind { - ErrorKind::Unavailable => "not available", - ErrorKind::Transient => "temporary failure", - ErrorKind::NotReady => "not ready yet", - ErrorKind::Other => "Uncategorised rng error", - ErrorKind::__Nonexhaustive => unreachable!(), - } + self.msg } - fn cause(&self) -> Option<&::std::error::Error> { - self.cause.as_ref().map(|e| &**e) + fn cause(&self) -> Option<&stdError> { + self.cause.as_ref().map(|e| e.as_ref() as &stdError) } } diff --git a/src/read.rs b/src/read.rs index 30da8d88331..c63d69a4c7f 100644 --- a/src/read.rs +++ b/src/read.rs @@ -78,7 +78,7 @@ fn map_err(err: io::Error) -> Error { io::ErrorKind::UnexpectedEof => ErrorKind::Unavailable, _ => ErrorKind::Other, }; - Error::new(kind, Some(Box::new(err))) + Error::new(kind, "ReadRng: read error", Some(err)) } #[cfg(test)] From f4a43f79217af90e04c20b1f0f0ef8617e1ec929 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Wed, 8 Nov 2017 12:58:56 +0000 Subject: [PATCH 148/247] Revise handling in os and read modules --- src/os.rs | 73 ++++++++++++++++++++++++++++++++++++----------------- src/read.rs | 12 ++------- 2 files changed, 52 insertions(+), 33 deletions(-) diff --git a/src/os.rs b/src/os.rs index 0e8975cb97c..2649764de71 100644 --- a/src/os.rs +++ b/src/os.rs @@ -11,10 +11,10 @@ //! Interfaces to the operating system provided random number //! generators. -use std::{mem, fmt}; +use std::fmt; use std::io::Read; -use {Rng, Error}; +use {Rng, Error, ErrorKind}; // TODO: replace many of the panics below with Result error handling /// A random number generator that retrieves randomness straight from @@ -81,14 +81,11 @@ impl Rng for OsRng { struct ReadRng (R); impl ReadRng { - fn try_fill(&mut self, mut buf: &mut [u8]) -> Result<(), Error> { - while buf.len() > 0 { - match self.0.read(buf).unwrap_or_else(|e| panic!("Read error: {}", e)) { - 0 => panic!("OsRng: no bytes available"), - n => buf = &mut mem::replace(&mut buf, &mut [])[n..], - } - } - Ok(()) + fn try_fill(&mut self, mut dest: &mut [u8]) -> Result<(), Error> { + if dest.len() == 0 { return Ok(()); } + // Use `std::io::read_exact`, which retries on `ErrorKind::Interrupted`. + self.0.read_exact(dest).map_err(|err| + Error::new(ErrorKind::Unavailable, "error reading random device", Some(err))) } } @@ -103,7 +100,7 @@ mod imp { use self::OsRngInner::*; use super::ReadRng; - use Error; + use {Error, ErrorKind}; use std::io; use std::fs::File; @@ -153,7 +150,7 @@ mod imp { if err.kind() == io::ErrorKind::Interrupted { continue } else { - panic!("unexpected getrandom error: {}", err); + return Err(Error::new(ErrorKind::Other, "unexpected getrandom error", Some(err))); } } else { read += result as usize; @@ -215,7 +212,8 @@ mod imp { return Ok(OsRng { inner: OsGetrandomRng }); } - let reader = File::open("/dev/urandom").unwrap(); + let reader = File::open("/dev/urandom").map_err(|err| + Error::new(ErrorKind::Unavailable, "error opening random device", Some(err)))?; let reader_rng = ReadRng(reader); Ok(OsRng { inner: OsReadRng(reader_rng) }) @@ -234,6 +232,8 @@ mod imp { mod imp { extern crate libc; + use {Error, ErrorKind}; + use std::io; use self::libc::{c_int, size_t}; @@ -260,9 +260,13 @@ mod imp { SecRandomCopyBytes(kSecRandomDefault, v.len() as size_t, v.as_mut_ptr()) }; if ret == -1 { - panic!("couldn't generate random bytes: {}", io::Error::last_os_error()); + Err(Error::new( + ErrorKind::Unavailable, + "couldn't generate random bytes", + Some(io::Error::last_os_error()))) + } else { + Ok(()) } - Ok(()) } } } @@ -271,6 +275,8 @@ mod imp { mod imp { extern crate libc; + use {Error, ErrorKind}; + use std::{io, ptr}; #[derive(Debug)] @@ -291,8 +297,11 @@ mod imp { ptr::null(), 0) }; if ret == -1 || s_len != s.len() { - panic!("kern.arandom sysctl failed! (returned {}, s.len() {}, oldlenp {})", - ret, s.len(), s_len); + // Old format string: "kern.arandom sysctl failed! (returned {}, s.len() {}, oldlenp {})" + return Err(Error::new( + ErrorKind::Unavailable, + "kern.arandom sysctl failed", + None)); } } Ok(()) @@ -304,6 +313,8 @@ mod imp { mod imp { extern crate libc; + use {Error, ErrorKind}; + use std::io; #[derive(Debug)] @@ -320,8 +331,10 @@ mod imp { libc::getentropy(s.as_mut_ptr() as *mut libc::c_void, s.len()) }; if ret == -1 { - let err = io::Error::last_os_error(); - panic!("getentropy failed: {}", err); + return Err(Error::new( + ErrorKind::Unavailable, + "getentropy failed", + Some(io::Error::last_os_error()))); } } Ok(()) @@ -331,6 +344,8 @@ mod imp { #[cfg(target_os = "redox")] mod imp { + use {Error, ErrorKind}; + use std::io; use std::fs::File; use super::ReadRng; @@ -342,7 +357,8 @@ mod imp { impl OsRng { pub fn new() -> Result { - let reader = File::open("rand:").unwrap(); + let reader = File::open("rand:").map_err(|err| + Error::new(ErrorKind::Unavailable, "error opening random device", Some(err)))?; let reader_rng = ReadRng(reader); Ok(OsRng { inner: reader_rng }) @@ -357,6 +373,8 @@ mod imp { mod imp { extern crate fuchsia_zircon; + use {Error, ErrorKind}; + use std::io; #[derive(Debug)] @@ -372,7 +390,12 @@ mod imp { while filled < s.len() { match fuchsia_zircon::cprng_draw(&mut s[filled..]) { Ok(actual) => filled += actual, - Err(e) => panic!("cprng_draw failed: {:?}", e), + Err(e) => { + return Err(Error::new( + ErrorKind::Unavailable, + "cprng_draw failed", + Some(e))); + } }; } } @@ -383,6 +406,8 @@ mod imp { #[cfg(windows)] mod imp { + use {Error, ErrorKind}; + use std::io; type BOOLEAN = u8; @@ -409,8 +434,10 @@ mod imp { SystemFunction036(slice.as_mut_ptr(), slice.len() as ULONG) }; if ret == 0 { - panic!("couldn't generate random bytes: {}", - io::Error::last_os_error()); + return Err(Error::new( + ErrorKind::Unavailable, + "couldn't generate random bytes", + Some(io::Error::last_os_error()))); } } Ok(()) diff --git a/src/read.rs b/src/read.rs index c63d69a4c7f..64fb1485620 100644 --- a/src/read.rs +++ b/src/read.rs @@ -10,7 +10,6 @@ //! A wrapper around any Read to treat it as an RNG. -use std::io; use std::io::Read; use {Rng, Error, ErrorKind}; @@ -69,18 +68,11 @@ impl Rng for ReadRng { fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { if dest.len() == 0 { return Ok(()); } // Use `std::io::read_exact`, which retries on `ErrorKind::Interrupted`. - self.reader.read_exact(dest).map_err(map_err) + self.reader.read_exact(dest).map_err(|err| + Error::new(ErrorKind::Unavailable, "ReadRng: read error", Some(err))) } } -fn map_err(err: io::Error) -> Error { - let kind = match err.kind() { - io::ErrorKind::UnexpectedEof => ErrorKind::Unavailable, - _ => ErrorKind::Other, - }; - Error::new(kind, "ReadRng: read error", Some(err)) -} - #[cfg(test)] mod test { use super::ReadRng; From 442caeee3280f083154272c9f0732df20cf4abf8 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Wed, 8 Nov 2017 12:48:25 +0000 Subject: [PATCH 149/247] Reseeding: handle/forward failures from source RNG --- src/reseeding.rs | 77 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 63 insertions(+), 14 deletions(-) diff --git a/src/reseeding.rs b/src/reseeding.rs index 28f6fd37d5a..376fd761cc6 100644 --- a/src/reseeding.rs +++ b/src/reseeding.rs @@ -11,9 +11,10 @@ //! A wrapper around another RNG that reseeds it after it //! generates a certain number of random bytes. +use core::cmp::max; use core::fmt::Debug; -use {Rng, SeedableRng, Error}; +use {Rng, SeedableRng, Error, ErrorKind}; #[cfg(feature="std")] use NewSeeded; @@ -23,6 +24,9 @@ const DEFAULT_GENERATION_THRESHOLD: u64 = 32 * 1024; /// A wrapper around any RNG which reseeds the underlying RNG after it /// has generated a certain number of random bytes. +/// +/// Note that reseeding is considered advisory only. If reseeding fails, the +/// generator may delay reseeding or not reseed at all. #[derive(Debug)] pub struct ReseedingRng { rng: R, @@ -51,12 +55,51 @@ impl> ReseedingRng { /// Reseed the internal RNG if the number of bytes that have been /// generated exceed the threshold. + /// + /// On error, this may delay reseeding or not reseed at all. pub fn reseed_if_necessary(&mut self) { if self.bytes_generated >= self.generation_threshold { - self.reseeder.reseed(&mut self.rng); + while if let Err(e) = self.reseeder.reseed(&mut self.rng) { + // TODO: log? + if e.kind.should_wait() { + // Delay reseeding + let delay = max(self.generation_threshold >> 8, self.bytes_generated); + self.bytes_generated -= delay; + false + } else if e.kind.should_retry() { + true // immediate retry + } else { + false // give up trying to reseed + } + } else { false } { + // empty loop body + } self.bytes_generated = 0; } } + /// Reseed the internal RNG if the number of bytes that have been + /// generated exceed the threshold. + /// + /// If reseeding fails, return an error with the original cause. Note that + /// if the cause has a permanent failure, we report a transient error and + /// skip reseeding. + pub fn try_reseed_if_necessary(&mut self) -> Result<(), Error> { + if self.bytes_generated >= self.generation_threshold { + if let Err(err) = self.reseeder.reseed(&mut self.rng) { + let newkind = match err.kind { + a @ ErrorKind::NotReady => a, + b @ ErrorKind::Transient => b, + _ => { + self.bytes_generated = 0; // skip reseeding + ErrorKind::Transient + } + }; + return Err(Error::new(newkind, "reseeding failed", Some(err))); + } + self.bytes_generated = 0; + } + Ok(()) + } } @@ -87,7 +130,7 @@ impl> Rng for ReseedingRng { } fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { - self.reseed_if_necessary(); + self.try_reseed_if_necessary()?; self.bytes_generated += dest.len() as u64; self.rng.try_fill(dest) } @@ -111,7 +154,10 @@ impl, Rsdr: Reseeder> SeedableRng<(Rsdr, S)> for /// Something that can be used to reseed an RNG via `ReseedingRng`. pub trait Reseeder: Debug { /// Reseed the given RNG. - fn reseed(&mut self, rng: &mut R); + /// + /// On error, this should just forward the source error; errors are handled + /// by the caller. + fn reseed(&mut self, rng: &mut R) -> Result<(), Error>; } /// Reseed an RNG using `NewSeeded` to replace the current instance. @@ -121,11 +167,13 @@ pub struct ReseedWithNew; #[cfg(feature="std")] impl Reseeder for ReseedWithNew { - fn reseed(&mut self, rng: &mut R) { + fn reseed(&mut self, rng: &mut R) -> Result<(), Error> { match R::new() { - Ok(result) => *rng = result, - // TODO: should we ignore and continue without reseeding? - Err(e) => panic!("Reseeding failed: {:?}", e), + Ok(result) => { + *rng = result; + Ok(()) + } + Err(e) => Err(e) } } } @@ -134,15 +182,15 @@ impl Reseeder for ReseedWithNew { mod test { use std::iter::repeat; use mock::MockAddRng; - use {SeedableRng, Rng, iter}; - use distributions::ascii_word_char; + use {SeedableRng, Rng, iter, Error}; use super::{ReseedingRng, Reseeder}; #[derive(Debug)] struct ReseedMock; impl Reseeder> for ReseedMock { - fn reseed(&mut self, rng: &mut MockAddRng) { + fn reseed(&mut self, rng: &mut MockAddRng) -> Result<(), Error> { *rng = MockAddRng::new(0, 1); + Ok(()) } } @@ -161,10 +209,11 @@ mod test { #[test] fn test_rng_seeded() { + // Default seed threshold is way beyond what we use here let mut ra: MyRng = SeedableRng::from_seed((ReseedMock, 2)); - let mut rb: MyRng = SeedableRng::from_seed((ReseedMock, 2)); - assert!(::test::iter_eq(iter(&mut ra).map(|rng| ascii_word_char(rng)).take(100), - iter(&mut rb).map(|rng| ascii_word_char(rng)).take(100))); + let mut rb: MockAddRng = SeedableRng::from_seed(2); + assert!(::test::iter_eq(iter(&mut ra).map(|rng| rng.next_u32()).take(100), + iter(&mut rb).map(|rng| rng.next_u32()).take(100))); } const FILL_BYTES_V_LEN: usize = 13579; From 49e9bb0cd22835dc77daf911331480e1541a180f Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Sat, 4 Nov 2017 20:20:39 +0100 Subject: [PATCH 150/247] Provide statistics function Add the function `timer_stats` (`jent_lfsr_var_stat` in C). Like in other the other functions we use a `int64` to represent a time delta instead of a `u64`. Instead of the `min` variable to indicate how many times the noice source loops should run at least, we use a `var_rounds` boolean. Setting `min` seems like an historic leftover, and does not fit `memaccess`. A simple bool covers everything needed for testing. This effects `timer_stats`, `lfsr_time` and `memaccess`. It is useful to be able to run the statistics function, even when initializing `JitterRng` might fail because of the `test_timer` function. Therefore `new_with_timer` does not automatically test the timer jitter, and expects the user code to do so. Now that `new_with_timer` calls `gen_entropy`, it was possible to move `black_box(ec.mem[0])` here instead of `measure_jitter`, so it is executed even less. # Conflicts: # src/jitter_rng.rs --- src/jitter_rng.rs | 207 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 164 insertions(+), 43 deletions(-) diff --git a/src/jitter_rng.rs b/src/jitter_rng.rs index 213bec654fd..e64cb0fa190 100644 --- a/src/jitter_rng.rs +++ b/src/jitter_rng.rs @@ -71,12 +71,18 @@ impl fmt::Debug for JitterRng { } } -/// An error that can occur when intializing `JitterRng`. +/// An error that can occur when `test_timer` fails. #[derive(Debug, Clone, PartialEq, Eq)] -pub struct Error { +pub struct TimerError { kind: ErrorKind, } +impl TimerError { + pub fn kind(&self) -> ErrorKind { + self.kind + } +} + /// Error kind which can be matched over. #[derive(PartialEq, Eq, Debug, Copy, Clone)] pub enum ErrorKind { @@ -107,7 +113,7 @@ impl ErrorKind { } } -impl fmt::Display for Error { +impl fmt::Display for TimerError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self.kind { ErrorKind::NoTimer => @@ -126,25 +132,27 @@ impl fmt::Display for Error { } #[cfg(feature="std")] -impl ::std::error::Error for Error { +impl ::std::error::Error for TimerError { fn description(&self) -> &str { self.kind.description() } } -#[cfg(feature="std")] -fn new_error(kind: ErrorKind) -> rand_core::Error { - rand_core::Error { - kind: rand_core::ErrorKind::Unavailable, - cause: Some(Box::new(Error { kind: kind })), +impl From for Error { + #[cfg(feature="std")] + fn from(err: TimerError) -> Error { + Error { + kind: rand_core::ErrorKind::Unavailable, + cause: Some(err.into()), + } } -} -#[cfg(not(feature="std"))] -fn new_error(kind: ErrorKind) -> rand_core::Error { - rand_core::Error { - kind: rand_core::ErrorKind::Unavailable, - cause: kind.description(), + #[cfg(not(feature="std"))] + fn from(err: TimerError) -> Error { + Error { + kind: rand_core::ErrorKind::Unavailable, + cause: errkind.description(), + } } } @@ -156,8 +164,10 @@ impl JitterRng { /// hundred times. If this does not pass basic quality tests, an error is /// returned. #[cfg(feature="std")] - pub fn new() -> Result { - JitterRng::new_with_timer(get_nstime) + pub fn new() -> Result { + let mut ec = JitterRng::new_with_timer(get_nstime); + ec.test_timer()?; + Ok(ec) } /// Create a new `JitterRng`. @@ -166,11 +176,10 @@ impl JitterRng { /// /// The timer must have nanosecond precision. /// - /// During initialization CPU execution timing jitter is measured a few - /// hundred times. If this does not pass basic quality tests, an error is - /// returned. - pub fn new_with_timer(timer: fn() -> u64) - -> Result { + /// This method is more low-level than `new()`. It is the responsibility of + /// the caller to run `test_timer` before using any numbers generated with + /// `JitterRng`. + pub fn new_with_timer(timer: fn() -> u64) -> JitterRng { let mut ec = JitterRng { data: 0, timer: timer, @@ -182,8 +191,18 @@ impl JitterRng { data_remaining: None, }; - ec.entropy_init()?; - Ok(ec) + // Fill `data`, `prev_time`, `last_delta` and `last_delta2` with + // non-zero values. + ec.prev_time = timer(); + ec.gen_entropy(); + + // Do a single read from `self.mem` to make sure the Memory Access noise + // source is not optimised out. + // Note: this read is important, it effects optimisations for the entire + // module! + black_box(ec.mem[0]); + + ec } // Calculate a random loop count used for the next round of an entropy @@ -226,7 +245,7 @@ impl JitterRng { // execution is used to measure the CPU execution time jitter. Any change to // the loop in this function implies that careful retesting must be done. #[inline(never)] - fn lfsr_time(&mut self, time: u64) { + fn lfsr_time(&mut self, time: u64, var_rounds: bool) { fn lfsr(mut data: u64, time: u64) -> u64{ for i in 1..65 { let mut tmp = time << (64 - i); @@ -256,8 +275,11 @@ impl JitterRng { // `self.data`, all the other results are ignored. To make sure the // other rounds are not optimised out, we first run all but the last // round on a throw-away value instead of the real `self.data`. + let mut lfsr_loop_cnt = 0; + if var_rounds { lfsr_loop_cnt = self.random_loop_cnt(4) }; + let mut throw_away: u64 = 0; - for _ in 0..(self.random_loop_cnt(4)) { + for _ in 0..lfsr_loop_cnt { throw_away = lfsr(throw_away, time); } black_box(throw_away); @@ -282,9 +304,12 @@ impl JitterRng { // range of wait states. However, to reliably access either L3 or memory, // the `self.mem` memory must be quite large which is usually not desirable. #[inline(never)] - fn memaccess(&mut self) { + fn memaccess(&mut self, var_rounds: bool) { + let mut acc_loop_cnt = 128; + if var_rounds { acc_loop_cnt += self.random_loop_cnt(4) }; + let mut index = self.mem_prev_index; - for _ in 0..(self.random_loop_cnt(7) + 128) { + for _ in 0..acc_loop_cnt { // Addition of memblocksize - 1 to index with wrap around logic to // ensure that every memory location is hit evenly. // The modulus also allows the compiler to remove the indexing @@ -328,7 +353,7 @@ impl JitterRng { // result. fn measure_jitter(&mut self) -> Option<()> { // Invoke one noise source before time measurement to add variations - self.memaccess(); + self.memaccess(true); // Get time stamp and calculate time delta to previous // invocation to measure the timing variations @@ -337,7 +362,7 @@ impl JitterRng { self.prev_time = time; // Call the next noise source which also injects the data - self.lfsr_time(current_delta as u64); + self.lfsr_time(current_delta as u64, true); // Check whether we have a stuck measurement (i.e. does the last // measurement holds entropy?). @@ -415,15 +440,12 @@ impl JitterRng { } self.stir_pool(); - - // Do a single read from `self.mem` to make sure the Memory Access noise - // source is not optimised out. - black_box(self.mem[0]); - self.data } - fn entropy_init(&mut self) -> Result<(), rand_core::Error> { + /// Basic quality tests on the timer, by measuring CPU timing jitter a few + /// hundred times. + pub fn test_timer(&mut self) -> Result<(), TimerError> { // We could add a check for system capabilities such as `clock_getres` // or check for `CONFIG_X86_TSC`, but it does not make much sense as the // following sanity checks verify that we have a high-resolution timer. @@ -443,12 +465,12 @@ impl JitterRng { for i in 0..(CLEARCACHE + TESTLOOPCOUNT) { // Invoke core entropy collection logic let time = (self.timer)(); - self.lfsr_time(time); + self.lfsr_time(time, true); let time2 = (self.timer)(); // Test whether timer works if time == 0 || time2 == 0 { - return Err(new_error(ErrorKind::NoTimer)); + return Err(TimerError { kind: ErrorKind::NoTimer }); } let delta = time2.wrapping_sub(time) as i64; @@ -456,7 +478,7 @@ impl JitterRng { // when called shortly after each other -- this implies that we also // have a high resolution timer if delta == 0 { - return Err(new_error(ErrorKind::CoarseTimer)); + return Err(TimerError { kind: ErrorKind::CoarseTimer }); } // Up to here we did not modify any variable that will be @@ -488,30 +510,129 @@ impl JitterRng { // should not fail. The value of 3 should cover the NTP case being // performed during our test run. if time_backwards > 3 { - return Err(new_error(ErrorKind::NotMonotonic)); + return Err(TimerError { kind: ErrorKind::NotMonotonic }); } // Variations of deltas of time must on average be larger than 1 to // ensure the entropy estimation implied with 1 is preserved if delta_sum < (TESTLOOPCOUNT) { - return Err(new_error(ErrorKind::TinyVariantions)); + return Err(TimerError { kind: ErrorKind::TinyVariantions }); } // Ensure that we have variations in the time stamp below 100 for at // least 10% of all checks -- on some platforms, the counter increments // in multiples of 100, but not always if count_mod > (TESTLOOPCOUNT/10 * 9) { - return Err(new_error(ErrorKind::CoarseTimer)); + return Err(TimerError { kind: ErrorKind::CoarseTimer }); } // If we have more than 90% stuck results, then this Jitter RNG is // likely to not work well. if count_stuck > (TESTLOOPCOUNT/10 * 9) { - return Err(new_error(ErrorKind::ToManyStuck)); + return Err(TimerError { kind: ErrorKind::ToManyStuck }); } Ok(()) } + + /// Statistical test: return the timer delta of one normal run of the + /// `JitterEntropy` entropy collector. + /// + /// Setting `var_rounds` to `true` will execute the memory access and the + /// CPU jitter noice sources a variable amount of times (just like a real + /// `JitterEntropy` round). + /// + /// Setting `var_rounds` to `false` will execute the noice sources the + /// minimal number of times. This can be used to measure the minimum amount + /// of entropy one round of entropy collector can collect in the worst case. + /// + /// # Example + /// + /// Use `timer_stats` to run the [NIST SP 800-90B Entropy Estimation Suite] + /// (https://github.com/usnistgov/SP800-90B_EntropyAssessment). + /// + /// This is the recommended way to test the quality of `JitterRng`. It + /// should be run before using the RNG on untested hardware, after changes + /// that could effect how the code is optimised, and after major compiler + /// compiler changes, like a new LLVM version. + /// + /// First generate two files `jitter_rng_var.bin` and `jitter_rng_var.min`. + /// + /// Execute `python noniid_main.py -v jitter_rng_var.bin 8`, and validate it + /// with `restart.py -v jitter_rng_var.bin 8 `. + /// This number is the expected amount of entropy that is at least available + /// for each round of the entropy collector. This number should be greater + /// than the amount estimated with `64 / test_timer()`. + /// + /// Execute `python noniid_main.py -v -u 4 jitter_rng_var.bin 4`, and + /// validate it with `restart.py -v -u 4 jitter_rng_var.bin 4 `. + /// This number is the expected amount of entropy that is available in the + /// last 4 bits of the timer delta after running noice sources. Note that + /// a value of 3.70 is the minimum estimated entropy for true randomness. + /// + /// Execute `python noniid_main.py -v -u 4 jitter_rng_var.bin 4`, and + /// validate it with `restart.py -v -u 4 jitter_rng_var.bin 4 `. + /// This number is the expected amount of entropy that is available to the + /// entropy collecter if both noice sources only run their minimal number of + /// times. This measures the absolute worst-case, and gives a lower bound + /// for the available entropy. + /// + /// ```rust + /// use rand::JitterRng; + /// + /// # use std::error::Error; + /// # use std::fs::File; + /// # use std::io::Write; + /// # + /// # fn try_main() -> Result<(), Box> { + /// fn get_nstime() -> u64 { + /// use std::time::{SystemTime, UNIX_EPOCH}; + /// + /// let dur = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); + /// // The correct way to calculate the current time is + /// // `dur.as_secs() * 1_000_000_000 + dur.subsec_nanos() as u64` + /// // But this is faster, and the difference in terms of entropy is + /// // negligible (log2(10^9) == 29.9). + /// dur.as_secs() << 30 | dur.subsec_nanos() as u64 + /// } + /// + /// // Do not initialize with `JitterRng::new`, but with `new_with_timer`. + /// // 'new' always runst `test_timer`, and can therefore fail to + /// // initialize. We want to be able to get the statistics even when the + /// // timer test fails. + /// let mut rng = JitterRng::new_with_timer(get_nstime); + /// + /// // 1_000_000 results are required for the NIST SP 800-90B Entropy + /// // Estimation Suite + /// // FIXME: this number is smaller here, otherwise the Doc-test is to slow + /// const ROUNDS: usize = 10_000; + /// let mut deltas_variable: Vec = Vec::with_capacity(ROUNDS); + /// let mut deltas_minimal: Vec = Vec::with_capacity(ROUNDS); + /// + /// for _ in 0..ROUNDS { + /// deltas_variable.push(rng.timer_stats(true) as u8); + /// deltas_minimal.push(rng.timer_stats(false) as u8); + /// } + /// + /// // Write out after the statistics collection loop, to not disturb the + /// // test results. + /// File::create("jitter_rng_var.bin")?.write(&deltas_variable)?; + /// File::create("jitter_rng_min.bin")?.write(&deltas_minimal)?; + /// # + /// # Ok(()) + /// # } + /// # + /// # fn main() { + /// # try_main().unwrap(); + /// # } + /// ``` + pub fn timer_stats(&mut self, var_rounds: bool) -> i64 { + let time = get_nstime(); + self.memaccess(var_rounds); + self.lfsr_time(time, var_rounds); + let time2 = get_nstime(); + time2.wrapping_sub(time) as i64 + } } #[cfg(feature="std")] From ed358de37964a011080440b5c9b2ac93e813298c Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Wed, 8 Nov 2017 16:31:13 +0100 Subject: [PATCH 151/247] Make the number of entropy colelction rounds variable --- src/jitter_rng.rs | 53 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 45 insertions(+), 8 deletions(-) diff --git a/src/jitter_rng.rs b/src/jitter_rng.rs index e64cb0fa190..c1fe5ab7580 100644 --- a/src/jitter_rng.rs +++ b/src/jitter_rng.rs @@ -51,6 +51,8 @@ const MEMORY_SIZE: usize = MEMORY_BLOCKS * MEMORY_BLOCKSIZE; // out what is technically dead code, but that does influence timing jitter. pub struct JitterRng { data: u64, // Actual random number + // Number of rounds to run the entropy collector per 64 bits + rounds: u16, // Timer and previous time stamp, used by `measure_jitter` timer: fn() -> u64, prev_time: u64, @@ -166,7 +168,7 @@ impl JitterRng { #[cfg(feature="std")] pub fn new() -> Result { let mut ec = JitterRng::new_with_timer(get_nstime); - ec.test_timer()?; + ec.rounds = ec.test_timer()?; Ok(ec) } @@ -182,6 +184,7 @@ impl JitterRng { pub fn new_with_timer(timer: fn() -> u64) -> JitterRng { let mut ec = JitterRng { data: 0, + rounds: 64, timer: timer, prev_time: 0, last_delta: 0, @@ -432,7 +435,7 @@ impl JitterRng { // first loop round collects the expected entropy. let _ = self.measure_jitter(); - for _ in 0..64 { + for _ in 0..self.rounds { // If a stuck measurement is received, repeat measurement // Note: we do not guard against an infinite loop, that would mean // the timer suddenly became broken. @@ -445,7 +448,11 @@ impl JitterRng { /// Basic quality tests on the timer, by measuring CPU timing jitter a few /// hundred times. - pub fn test_timer(&mut self) -> Result<(), TimerError> { + /// + /// If succesful, this will return the estimated number of rounds necessary + /// to collect 64 bits of entropy. Otherwise a `TimerError` with the cause + /// of the failure will be returned. + pub fn test_timer(&mut self) -> Result { // We could add a check for system capabilities such as `clock_getres` // or check for `CONFIG_X86_TSC`, but it does not make much sense as the // following sanity checks verify that we have a high-resolution timer. @@ -463,8 +470,9 @@ impl JitterRng { const CLEARCACHE: u64 = 100; for i in 0..(CLEARCACHE + TESTLOOPCOUNT) { - // Invoke core entropy collection logic + // Measure time delta of core entropy collection logic let time = (self.timer)(); + self.memaccess(true); self.lfsr_time(time, true); let time2 = (self.timer)(); @@ -513,9 +521,14 @@ impl JitterRng { return Err(TimerError { kind: ErrorKind::NotMonotonic }); } - // Variations of deltas of time must on average be larger than 1 to - // ensure the entropy estimation implied with 1 is preserved - if delta_sum < (TESTLOOPCOUNT) { + // Test that the available amount of entropy per round does not get to + // low. We expect 1 bit of entropy per round as a reasonable minimum + // (although less is possible, it means the collector loop has to run + // much more often). + // `assert!(delta_average >= log2(1))` + // `assert!(delta_sum / TESTLOOPCOUNT >= 1)` + // `assert!(delta_sum >= TESTLOOPCOUNT)` + if delta_sum < TESTLOOPCOUNT { return Err(TimerError { kind: ErrorKind::TinyVariantions }); } @@ -532,7 +545,31 @@ impl JitterRng { return Err(TimerError { kind: ErrorKind::ToManyStuck }); } - Ok(()) + // Estimate the number of `measure_jitter` rounds necessary for 64 bits + // of entropy. + // + // We don't try very hard to come up with a good estimate of the + // available bits of entropy per round here for two reasons: + // 1. Simple estimates of the available bits (like Shannon entropy) are + // to optimistic. + // 2) Unless we want to waste a lot of time during intialization, there + // is only a small amount of samples available. + // + // Therefore we use a very simple and conservative estimate: + // `let bits_of_entropy = log2(delta_average / TESTLOOPCOUNT) / 2`. + // + // The number of rounds `measure_jitter` should run to collect 64 bits + // of entropy is `64 / bits_of_entropy`. + // + // To have smaller rounding errors, intermediate values are multiplied + // by `FACTOR`. To compensate for `log2` and division rounding down, + // add 1. + let delta_average = delta_sum / TESTLOOPCOUNT; + + const FACTOR: u32 = 5; + fn log2(x: u64) -> u32 { 64 - x.leading_zeros() as u32 } + + Ok((64 * 2 * FACTOR / log2(delta_average.pow(FACTOR)) + 1) as u16) } /// Statistical test: return the timer delta of one normal run of the From c57aa1a6da68be8f1956008b917bf42df06864ed Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Wed, 8 Nov 2017 16:38:29 +0100 Subject: [PATCH 152/247] Address review comments --- src/jitter_rng.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/jitter_rng.rs b/src/jitter_rng.rs index c1fe5ab7580..bdf2474c21d 100644 --- a/src/jitter_rng.rs +++ b/src/jitter_rng.rs @@ -109,7 +109,7 @@ impl ErrorKind { ErrorKind::CoarseTimer => "coarse timer", ErrorKind::NotMonotonic => "timer not monotonic", ErrorKind::TinyVariantions => "time delta variations too small", - ErrorKind::ToManyStuck => "to many stuck results", + ErrorKind::ToManyStuck => "too many stuck results", ErrorKind::__Nonexhaustive => unreachable!(), } } @@ -127,7 +127,7 @@ impl fmt::Display for TimerError { ErrorKind::TinyVariantions => write!(f, "Variations of deltas of time too small."), ErrorKind::ToManyStuck => - write!(f, "To many stuck results (indicating no added entropy)."), + write!(f, "Too many stuck results (indicating no added entropy)."), ErrorKind::__Nonexhaustive => unreachable!(), } } @@ -361,6 +361,9 @@ impl JitterRng { // Get time stamp and calculate time delta to previous // invocation to measure the timing variations let time = (self.timer)(); + // Note: wrapping_sub combined with a cast to `i64` generates a correct + // delta, even in the unlikely case this is a timer than is not strictly + // monotonic. let current_delta = time.wrapping_sub(self.prev_time) as i64; self.prev_time = time; @@ -422,7 +425,7 @@ impl JitterRng { // we rely on it to not recognise the opportunity. for i in 0..64 { let apply = (self.data >> i) & 1; - let mask = !((apply as i64) - 1) as u64; + let mask = !apply.wrapping_sub(1); mixer ^= CONSTANT & mask; mixer = mixer.rotate_left(1); } From 908fda4206e9fc9f7cbd320acfdab274a0f3fb3d Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Thu, 9 Nov 2017 12:01:07 +0000 Subject: [PATCH 153/247] Fix: Error::new(_, _, None) was not usable Type deduction failed due to lack of bounds on E. --- rand_core/src/lib.rs | 19 ++++++++++++++----- src/os.rs | 45 +++++++++++++++++++++++++------------------- src/read.rs | 5 +++-- 3 files changed, 43 insertions(+), 26 deletions(-) diff --git a/rand_core/src/lib.rs b/rand_core/src/lib.rs index c1cdfc74c78..d5a9ab0b321 100644 --- a/rand_core/src/lib.rs +++ b/rand_core/src/lib.rs @@ -298,7 +298,16 @@ pub struct Error { } impl Error { - /// Create a new instance, with specified kind, message, and optionally a + /// Create a new instance, with specified kind and a message. + pub fn new(kind: ErrorKind, msg: &'static str) -> Self { + #[cfg(feature="std")] { + Self { kind, msg, cause: None } + } + #[cfg(not(feature="std"))] { + Self { kind, msg } + } + } + /// Create a new instance, with specified kind, message, and a /// chained cause. /// /// Note: `stdError` is an alias for `std::error::Error`. @@ -308,17 +317,17 @@ impl Error { /// type `E` (because both `Box` and `stdError` are unavailable), and the /// `cause` is ignored. #[cfg(feature="std")] - pub fn new(kind: ErrorKind, msg: &'static str, cause: Option) -> Self + pub fn new_with_cause(kind: ErrorKind, msg: &'static str, cause: E) -> Self where E: Into> { - Self { kind, msg, cause: cause.map(|inner| inner.into()) } + Self { kind, msg, cause: Some(cause.into()) } } - /// Create a new instance, with specified kind, message, and optionally a + /// Create a new instance, with specified kind, message, and a /// chained cause. /// /// In `no_std` mode the *cause* is ignored. #[cfg(not(feature="std"))] - pub fn new(kind: ErrorKind, msg: &'static str, _cause: Option) -> Self { + pub fn new_with_cause(kind: ErrorKind, msg: &'static str, _cause: E) -> Self { Self { kind, msg } } diff --git a/src/os.rs b/src/os.rs index 2649764de71..e65d770af1e 100644 --- a/src/os.rs +++ b/src/os.rs @@ -84,8 +84,9 @@ impl ReadRng { fn try_fill(&mut self, mut dest: &mut [u8]) -> Result<(), Error> { if dest.len() == 0 { return Ok(()); } // Use `std::io::read_exact`, which retries on `ErrorKind::Interrupted`. - self.0.read_exact(dest).map_err(|err| - Error::new(ErrorKind::Unavailable, "error reading random device", Some(err))) + self.0.read_exact(dest).map_err(|err| { + Error::new_with_cause(ErrorKind::Unavailable, "error reading random device", err) + }) } } @@ -148,9 +149,13 @@ mod imp { if result == -1 { let err = io::Error::last_os_error(); if err.kind() == io::ErrorKind::Interrupted { - continue + continue; } else { - return Err(Error::new(ErrorKind::Other, "unexpected getrandom error", Some(err))); + return Err(Error::new_with_cause( + ErrorKind::Other, + "unexpected getrandom error", + err, + )); } } else { read += result as usize; @@ -212,8 +217,9 @@ mod imp { return Ok(OsRng { inner: OsGetrandomRng }); } - let reader = File::open("/dev/urandom").map_err(|err| - Error::new(ErrorKind::Unavailable, "error opening random device", Some(err)))?; + let reader = File::open("/dev/urandom").map_err(|err| { + Error::new_with_cause(ErrorKind::Unavailable, "error opening random device", err) + })?; let reader_rng = ReadRng(reader); Ok(OsRng { inner: OsReadRng(reader_rng) }) @@ -260,10 +266,10 @@ mod imp { SecRandomCopyBytes(kSecRandomDefault, v.len() as size_t, v.as_mut_ptr()) }; if ret == -1 { - Err(Error::new( + Err(Error::new_with_cause( ErrorKind::Unavailable, "couldn't generate random bytes", - Some(io::Error::last_os_error()))) + io::Error::last_os_error())) } else { Ok(()) } @@ -297,11 +303,11 @@ mod imp { ptr::null(), 0) }; if ret == -1 || s_len != s.len() { - // Old format string: "kern.arandom sysctl failed! (returned {}, s.len() {}, oldlenp {})" + // Old format string: + // "kern.arandom sysctl failed! (returned {}, s.len() {}, oldlenp {})" return Err(Error::new( ErrorKind::Unavailable, - "kern.arandom sysctl failed", - None)); + "kern.arandom sysctl failed")); } } Ok(()) @@ -331,10 +337,10 @@ mod imp { libc::getentropy(s.as_mut_ptr() as *mut libc::c_void, s.len()) }; if ret == -1 { - return Err(Error::new( + return Err(Error::new_with_cause( ErrorKind::Unavailable, "getentropy failed", - Some(io::Error::last_os_error()))); + io::Error::last_os_error())); } } Ok(()) @@ -357,8 +363,9 @@ mod imp { impl OsRng { pub fn new() -> Result { - let reader = File::open("rand:").map_err(|err| - Error::new(ErrorKind::Unavailable, "error opening random device", Some(err)))?; + let reader = File::open("rand:").map_err(|err| { + Error::new_with_cause(ErrorKind::Unavailable, "error opening random device", err) + })?; let reader_rng = ReadRng(reader); Ok(OsRng { inner: reader_rng }) @@ -391,10 +398,10 @@ mod imp { match fuchsia_zircon::cprng_draw(&mut s[filled..]) { Ok(actual) => filled += actual, Err(e) => { - return Err(Error::new( + return Err(Error::new_with_cause( ErrorKind::Unavailable, "cprng_draw failed", - Some(e))); + e)); } }; } @@ -434,10 +441,10 @@ mod imp { SystemFunction036(slice.as_mut_ptr(), slice.len() as ULONG) }; if ret == 0 { - return Err(Error::new( + return Err(Error::new_with_cause( ErrorKind::Unavailable, "couldn't generate random bytes", - Some(io::Error::last_os_error()))); + io::Error::last_os_error())); } } Ok(()) diff --git a/src/read.rs b/src/read.rs index 64fb1485620..acc905b7010 100644 --- a/src/read.rs +++ b/src/read.rs @@ -68,8 +68,9 @@ impl Rng for ReadRng { fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { if dest.len() == 0 { return Ok(()); } // Use `std::io::read_exact`, which retries on `ErrorKind::Interrupted`. - self.reader.read_exact(dest).map_err(|err| - Error::new(ErrorKind::Unavailable, "ReadRng: read error", Some(err))) + self.reader.read_exact(dest).map_err(|err| { + Error::new_with_cause(ErrorKind::Unavailable, "ReadRng: read error", err) + }) } } From 6302f53b3565b92fd10b58e657c828f0d961dabe Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Thu, 9 Nov 2017 12:47:05 +0000 Subject: [PATCH 154/247] Fix Debug/Clone support of mock and reseeding RNGs as per #13 --- src/mock.rs | 8 ++++---- src/reseeding.rs | 20 +++++++++++++------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/mock.rs b/src/mock.rs index a3dc7229ac8..e070818a2ef 100644 --- a/src/mock.rs +++ b/src/mock.rs @@ -30,13 +30,13 @@ use rand_core::impls; /// assert_eq!(my_rng.next_u32(), 2u32); /// assert_eq!(my_rng.next_u64(), 3u64 + (4u64 << 32)); /// ``` -#[derive(Debug)] -pub struct MockAddRng { +#[derive(Debug, Clone)] +pub struct MockAddRng { v: w, a: w, } -impl MockAddRng { +impl MockAddRng { /// Create a `MockAddRng`, yielding an arithmetic sequence starting with /// `v` and incremented by `a` each time. pub fn new(v: T, a: T) -> Self { @@ -90,7 +90,7 @@ impl Rng for MockAddRng { } } -impl SeedableRng for MockAddRng where +impl SeedableRng for MockAddRng where MockAddRng: Rng, T: From, // for 1.into() { diff --git a/src/reseeding.rs b/src/reseeding.rs index 28f6fd37d5a..8d217462f15 100644 --- a/src/reseeding.rs +++ b/src/reseeding.rs @@ -11,8 +11,6 @@ //! A wrapper around another RNG that reseeds it after it //! generates a certain number of random bytes. -use core::fmt::Debug; - use {Rng, SeedableRng, Error}; #[cfg(feature="std")] use NewSeeded; @@ -23,8 +21,12 @@ const DEFAULT_GENERATION_THRESHOLD: u64 = 32 * 1024; /// A wrapper around any RNG which reseeds the underlying RNG after it /// has generated a certain number of random bytes. -#[derive(Debug)] -pub struct ReseedingRng { +/// +/// This derives `Clone` if both the inner RNG `R` and the reseeder `Rsdr` do. +/// Note that reseeders using external entropy should deliberately not +/// implement `Clone`. +#[derive(Debug, Clone)] +pub struct ReseedingRng> { rng: R, generation_threshold: u64, bytes_generated: u64, @@ -109,14 +111,18 @@ impl, Rsdr: Reseeder> SeedableRng<(Rsdr, S)> for } /// Something that can be used to reseed an RNG via `ReseedingRng`. -pub trait Reseeder: Debug { +/// +/// Note that implementations should support `Clone` only if reseeding is +/// deterministic (no external entropy source). This is so that a `ReseedingRng` +/// only supports `Clone` if fully deterministic. +pub trait Reseeder { /// Reseed the given RNG. fn reseed(&mut self, rng: &mut R); } /// Reseed an RNG using `NewSeeded` to replace the current instance. #[cfg(feature="std")] -#[derive(Clone, Copy, Debug)] +#[derive(Debug)] pub struct ReseedWithNew; #[cfg(feature="std")] @@ -138,7 +144,7 @@ mod test { use distributions::ascii_word_char; use super::{ReseedingRng, Reseeder}; - #[derive(Debug)] + #[derive(Debug, Clone)] struct ReseedMock; impl Reseeder> for ReseedMock { fn reseed(&mut self, rng: &mut MockAddRng) { From 89f043076364e01fb551350f382b1d82bff37fce Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Thu, 9 Nov 2017 14:27:57 +0000 Subject: [PATCH 155/247] Reseeding: cleaner code --- src/reseeding.rs | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/reseeding.rs b/src/reseeding.rs index b000bc0217a..d5f5010e472 100644 --- a/src/reseeding.rs +++ b/src/reseeding.rs @@ -59,20 +59,22 @@ impl> ReseedingRng { /// On error, this may delay reseeding or not reseed at all. pub fn reseed_if_necessary(&mut self) { if self.bytes_generated >= self.generation_threshold { - while if let Err(e) = self.reseeder.reseed(&mut self.rng) { + loop { + if let Err(e) = self.reseeder.reseed(&mut self.rng) { // TODO: log? if e.kind.should_wait() { // Delay reseeding let delay = max(self.generation_threshold >> 8, self.bytes_generated); self.bytes_generated -= delay; - false + break; } else if e.kind.should_retry() { - true // immediate retry + continue; // immediate retry } else { - false // give up trying to reseed + break; // give up trying to reseed } - } else { false } { - // empty loop body + } else { + break; // no reseeding + } } self.bytes_generated = 0; } @@ -168,13 +170,7 @@ pub struct ReseedWithNew; #[cfg(feature="std")] impl Reseeder for ReseedWithNew { fn reseed(&mut self, rng: &mut R) -> Result<(), Error> { - match R::new() { - Ok(result) => { - *rng = result; - Ok(()) - } - Err(e) => Err(e) - } + R::new().map(|result| *rng = result) } } From fdb09d4359fd524874e8fb9b3e9f2c97eb6749c6 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Fri, 10 Nov 2017 10:45:27 +0000 Subject: [PATCH 156/247] Update travis build scripts --- .travis.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index fb6a2df233d..e790f0913d9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,12 +12,14 @@ matrix: - rust: beta - rust: nightly script: - - cargo test - - cargo test --features nightly - - cargo build --no-default-features + - cargo test --all + - cargo test --all --features nightly + - cargo test --all --benches + - cargo build --all --no-default-features - cargo doc --no-deps --features nightly script: - - cargo test + - cargo test --all + - cargo build --all --no-default-features after_success: - travis-cargo --only nightly doc-upload env: From 3bf4680838f839c0e5016c339b35ca00a070f4ed Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Thu, 9 Nov 2017 15:44:20 +0000 Subject: [PATCH 157/247] Implicit error handling (#25) Add rand_core::impls::fill_via_try_fill with handling for errors. --- rand_core/src/impls.rs | 36 ++++++++++++++++++++++++++++++++++++ rand_core/src/lib.rs | 12 ++++++++---- src/os.rs | 4 ++-- src/read.rs | 2 +- 4 files changed, 47 insertions(+), 7 deletions(-) diff --git a/rand_core/src/impls.rs b/rand_core/src/impls.rs index cffca3e882f..f8215213c87 100644 --- a/rand_core/src/impls.rs +++ b/rand_core/src/impls.rs @@ -21,6 +21,7 @@ use core::intrinsics::transmute; use core::slice; +use core::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering}; use Rng; /// Implement `next_u64` via `next_u32`, little-endian order. @@ -109,4 +110,39 @@ pub fn next_u128_via_fill(rng: &mut R) -> u128 { impl_uint_from_fill!(rng, u128, 16) } +const LIMIT_ERR_MAX: usize = 20; // arbitrary +static LIMIT_ERR_COUNT: AtomicUsize = ATOMIC_USIZE_INIT; + +/// Implement `fill_bytes` via `try_fill` with implicit error handling. +pub fn fill_via_try_fill(rng: &mut R, dest: &mut [u8]) { + loop { + if let Err(e) = rng.try_fill(dest) { + if e.kind.should_retry() { + if e.kind.limit_retries() { + // We use a global counter since we don't have any local memory. + let count = LIMIT_ERR_COUNT.fetch_add(1, Ordering::Relaxed); + if count > LIMIT_ERR_MAX { + // TODO: log details & cause? + panic!("Too many RNG errors; last error: {}", e.msg()); + } + } + + if e.kind.should_wait() { + #[cfg(feature="std")]{ + let dur = ::std::time::Duration::from_millis(10); + ::std::thread::sleep(dur); + } + } + + continue; + } + + // TODO: log details & cause? + panic!("Fatal RNG error: {}", e.msg()); + } + + break; + } +} + // TODO: implement tests for the above diff --git a/rand_core/src/lib.rs b/rand_core/src/lib.rs index d5a9ab0b321..a10d735c669 100644 --- a/rand_core/src/lib.rs +++ b/rand_core/src/lib.rs @@ -260,15 +260,19 @@ impl ErrorKind { _ => false, } } + /// True if we should retry but wait before retrying /// /// This implies `should_retry()` is true. pub fn should_wait(self) -> bool { - match self { - ErrorKind::NotReady => true, - _ => false, - } + self == ErrorKind::NotReady } + + /// True if we should limit the number of retries before giving up. + pub fn limit_retries(self) -> bool { + self == ErrorKind::Transient + } + /// A description of this error kind pub fn description(self) -> &'static str { match self { diff --git a/src/os.rs b/src/os.rs index e65d770af1e..34958cf8597 100644 --- a/src/os.rs +++ b/src/os.rs @@ -68,7 +68,7 @@ impl Rng for OsRng { } fn fill_bytes(&mut self, dest: &mut [u8]) { - self.try_fill(dest).unwrap(); + ::rand_core::impls::fill_via_try_fill(self, dest) } fn try_fill(&mut self, v: &mut [u8]) -> Result<(), Error> { @@ -152,7 +152,7 @@ mod imp { continue; } else { return Err(Error::new_with_cause( - ErrorKind::Other, + ErrorKind::Unavailable, "unexpected getrandom error", err, )); diff --git a/src/read.rs b/src/read.rs index acc905b7010..0169f53f80b 100644 --- a/src/read.rs +++ b/src/read.rs @@ -62,7 +62,7 @@ impl Rng for ReadRng { } fn fill_bytes(&mut self, dest: &mut [u8]) { - self.try_fill(dest).unwrap(); + ::rand_core::impls::fill_via_try_fill(self, dest) } fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { From 585b7380e49a41167cfcf1e3a9852dd017b3961d Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Thu, 9 Nov 2017 16:22:39 +0000 Subject: [PATCH 158/247] OsRng: return NotReady if not initialised yet (linux getrandom) --- src/os.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/os.rs b/src/os.rs index 34958cf8597..bcaef27e6d4 100644 --- a/src/os.rs +++ b/src/os.rs @@ -81,7 +81,7 @@ impl Rng for OsRng { struct ReadRng (R); impl ReadRng { - fn try_fill(&mut self, mut dest: &mut [u8]) -> Result<(), Error> { + fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { if dest.len() == 0 { return Ok(()); } // Use `std::io::read_exact`, which retries on `ErrorKind::Interrupted`. self.0.read_exact(dest).map_err(|err| { @@ -127,9 +127,11 @@ mod imp { const NR_GETRANDOM: libc::c_long = 278; #[cfg(target_arch = "powerpc")] const NR_GETRANDOM: libc::c_long = 384; + + const GRND_NONBLOCK: libc::c_uint = 0x0001; unsafe { - syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), 0) + syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), GRND_NONBLOCK) } } @@ -148,8 +150,14 @@ mod imp { let result = getrandom(&mut v[read..]); if result == -1 { let err = io::Error::last_os_error(); - if err.kind() == io::ErrorKind::Interrupted { + let kind = err.kind(); + if kind == io::ErrorKind::Interrupted { continue; + } else if kind == io::ErrorKind::WouldBlock { + // Potentially this would waste bytes, but since we use + // /dev/urandom blocking only happens if not initialised. + // Also, wasting the bytes in v doesn't matter very much. + return Err(Error::new(ErrorKind::NotReady, "getrandom not ready")); } else { return Err(Error::new_with_cause( ErrorKind::Unavailable, From 508f22cda4fbfacc4c253a13bb587476eecc159b Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Fri, 10 Nov 2017 11:19:54 +0000 Subject: [PATCH 159/247] Implicit handling: use local counter --- rand_core/src/impls.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/rand_core/src/impls.rs b/rand_core/src/impls.rs index f8215213c87..e10087a999b 100644 --- a/rand_core/src/impls.rs +++ b/rand_core/src/impls.rs @@ -21,7 +21,6 @@ use core::intrinsics::transmute; use core::slice; -use core::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering}; use Rng; /// Implement `next_u64` via `next_u32`, little-endian order. @@ -110,21 +109,18 @@ pub fn next_u128_via_fill(rng: &mut R) -> u128 { impl_uint_from_fill!(rng, u128, 16) } -const LIMIT_ERR_MAX: usize = 20; // arbitrary -static LIMIT_ERR_COUNT: AtomicUsize = ATOMIC_USIZE_INIT; - /// Implement `fill_bytes` via `try_fill` with implicit error handling. pub fn fill_via_try_fill(rng: &mut R, dest: &mut [u8]) { + let mut err_count = 0; loop { if let Err(e) = rng.try_fill(dest) { if e.kind.should_retry() { if e.kind.limit_retries() { - // We use a global counter since we don't have any local memory. - let count = LIMIT_ERR_COUNT.fetch_add(1, Ordering::Relaxed); - if count > LIMIT_ERR_MAX { + if err_count > 8 { // arbitrary limit // TODO: log details & cause? panic!("Too many RNG errors; last error: {}", e.msg()); } + err_count += 1; } if e.kind.should_wait() { From 4b8371f21aace3114cfd333c33acc38eb69aad91 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Fri, 10 Nov 2017 16:28:02 +0000 Subject: [PATCH 160/247] Reseeding: limit retries for transient errors --- src/reseeding.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/reseeding.rs b/src/reseeding.rs index d5f5010e472..770af28cbf6 100644 --- a/src/reseeding.rs +++ b/src/reseeding.rs @@ -59,6 +59,7 @@ impl> ReseedingRng { /// On error, this may delay reseeding or not reseed at all. pub fn reseed_if_necessary(&mut self) { if self.bytes_generated >= self.generation_threshold { + let mut err_count = 0; loop { if let Err(e) = self.reseeder.reseed(&mut self.rng) { // TODO: log? @@ -68,6 +69,11 @@ impl> ReseedingRng { self.bytes_generated -= delay; break; } else if e.kind.should_retry() { + if err_count > 4 { // arbitrary limit + // TODO: log details & cause? + break; // give up trying to reseed + } + err_count += 1; continue; // immediate retry } else { break; // give up trying to reseed From d38757052fc42f138508cbe1e8f7f2422bd7e4b1 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Fri, 10 Nov 2017 16:42:59 +0000 Subject: [PATCH 161/247] Split gen_usize_* benchmarks into gen_u32_* and gen_u64_* Copy non-controversial part of dhardy#36 Credit: Paul Dicker --- benches/generators.rs | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/benches/generators.rs b/benches/generators.rs index dd223a2fbc9..fcf31879194 100644 --- a/benches/generators.rs +++ b/benches/generators.rs @@ -9,7 +9,7 @@ const BYTES_LEN: usize = 1024; use std::mem::size_of; use test::{black_box, Bencher}; -use rand::{Rng, NewSeeded, SeedFromRng, StdRng, OsRng, Rand, Default}; +use rand::{Rng, NewSeeded, Sample, SeedFromRng, StdRng, OsRng}; use rand::prng::{XorShiftRng, IsaacRng, Isaac64Rng, ChaChaRng}; macro_rules! gen_bytes { @@ -37,27 +37,34 @@ gen_bytes!(gen_bytes_std, StdRng); gen_bytes!(gen_bytes_os, OsRng); -macro_rules! gen_usize { - ($fnn:ident, $gen:ident) => { +macro_rules! gen_uint { + ($fnn:ident, $ty:ty, $gen:ident) => { #[bench] fn $fnn(b: &mut Bencher) { let mut rng = $gen::new().unwrap(); b.iter(|| { for _ in 0..RAND_BENCH_N { - black_box(usize::rand(&mut rng, Default)); + black_box(rng.gen::<$ty>()); } }); - b.bytes = size_of::() as u64 * RAND_BENCH_N; + b.bytes = size_of::<$ty>() as u64 * RAND_BENCH_N; } } } -gen_usize!(gen_usize_xorshift, XorShiftRng); -gen_usize!(gen_usize_isaac, IsaacRng); -gen_usize!(gen_usize_isaac64, Isaac64Rng); -gen_usize!(gen_usize_chacha, ChaChaRng); -gen_usize!(gen_usize_std, StdRng); -gen_usize!(gen_usize_os, OsRng); +gen_uint!(gen_u32_xorshift, u32, XorShiftRng); +gen_uint!(gen_u32_isaac, u32, IsaacRng); +gen_uint!(gen_u32_isaac64, u32, Isaac64Rng); +gen_uint!(gen_u32_chacha, u32, ChaChaRng); +gen_uint!(gen_u32_std, u32, StdRng); +gen_uint!(gen_u32_os, u32, OsRng); + +gen_uint!(gen_u64_xorshift, u64, XorShiftRng); +gen_uint!(gen_u64_isaac, u64, IsaacRng); +gen_uint!(gen_u64_isaac64, u64, Isaac64Rng); +gen_uint!(gen_u64_chacha, u64, ChaChaRng); +gen_uint!(gen_u64_std, u64, StdRng); +gen_uint!(gen_u64_os, u64, OsRng); macro_rules! init_gen { ($fnn:ident, $gen:ident) => { From 6e9c1ab2bae5021f469eeba8fa828a4abb96d027 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Fri, 10 Nov 2017 17:21:32 +0000 Subject: [PATCH 162/247] Rejig ChaCha and Isaac construction tests --- src/prng/chacha.rs | 25 +++++++++++-------------- src/prng/isaac.rs | 26 +++++++++++--------------- src/prng/isaac64.rs | 27 ++++++++++++--------------- 3 files changed, 34 insertions(+), 44 deletions(-) diff --git a/src/prng/chacha.rs b/src/prng/chacha.rs index 66b6424dce8..db1d127bb9c 100644 --- a/src/prng/chacha.rs +++ b/src/prng/chacha.rs @@ -290,26 +290,23 @@ impl<'a> SeedableRng<&'a [u32]> for ChaChaRng { #[cfg(test)] mod test { - use {Rng, SeedableRng, iter}; - use distributions::ascii_word_char; + use {Rng, SeedableRng, SeedFromRng, iter}; use super::ChaChaRng; #[test] fn test_rng_rand_seeded() { + // Test that various construction techniques produce a working RNG. + let s = iter(&mut ::test::rng()).map(|rng| rng.next_u32()).take(8).collect::>(); - let mut ra: ChaChaRng = SeedableRng::from_seed(&s[..]); - let mut rb: ChaChaRng = SeedableRng::from_seed(&s[..]); - assert!(::test::iter_eq(iter(&mut ra).map(|rng| ascii_word_char(rng)).take(100), - iter(&mut rb).map(|rng| ascii_word_char(rng)).take(100))); - } - - #[test] - fn test_rng_seeded() { + let mut ra = ChaChaRng::from_seed(&s[..]); + ra.next_u32(); + + let mut rb = ChaChaRng::from_rng(&mut ::test::rng()).unwrap(); + rb.next_u32(); + let seed : &[_] = &[0,1,2,3,4,5,6,7]; - let mut ra: ChaChaRng = SeedableRng::from_seed(seed); - let mut rb: ChaChaRng = SeedableRng::from_seed(seed); - assert!(::test::iter_eq(iter(&mut ra).map(|rng| ascii_word_char(rng)).take(100), - iter(&mut rb).map(|rng| ascii_word_char(rng)).take(100))); + let mut rc = ChaChaRng::from_seed(seed); + rc.next_u32(); } #[test] diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index c5bc12c5bc5..996bc684f5b 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -362,30 +362,26 @@ impl<'a> SeedableRng<&'a [u32]> for IsaacRng { #[cfg(test)] mod test { - use {Rng, SeedableRng, iter}; + use {Rng, SeedableRng, SeedFromRng, iter}; use super::IsaacRng; #[test] - fn test_isaac_from_seed() { + fn test_isaac_construction() { + // Test that various construction techniques produce a working RNG. + let seed = iter(&mut ::test::rng()) .map(|rng| rng.next_u32()) .take(256) .collect::>(); let mut rng1 = IsaacRng::from_seed(&seed[..]); - let mut rng2 = IsaacRng::from_seed(&seed[..]); - for _ in 0..100 { - assert_eq!(rng1.next_u32(), rng2.next_u32()); - } - } - - #[test] - fn test_isaac_from_seed_fixed() { + rng1.next_u32(); + + let mut rng2 = IsaacRng::from_rng(&mut ::test::rng()).unwrap(); + rng2.next_u32(); + let seed: &[_] = &[1, 23, 456, 7890, 12345]; - let mut rng1 = IsaacRng::from_seed(&seed[..]); - let mut rng2 = IsaacRng::from_seed(&seed[..]); - for _ in 0..100 { - assert_eq!(rng1.next_u32(), rng2.next_u32()); - } + let mut rng3 = IsaacRng::from_seed(&seed[..]); + rng3.next_u32(); } #[test] diff --git a/src/prng/isaac64.rs b/src/prng/isaac64.rs index 7ae27117720..91c0186f3c7 100644 --- a/src/prng/isaac64.rs +++ b/src/prng/isaac64.rs @@ -321,30 +321,27 @@ impl<'a> SeedableRng<&'a [u64]> for Isaac64Rng { #[cfg(test)] mod test { - use {Rng, SeedableRng, iter}; + use {Rng, SeedableRng, SeedFromRng, iter}; use super::Isaac64Rng; #[test] - fn test_isaac64_from_seed() { + fn test_isaac64_construction() { + // Test that various construction techniques produce a working RNG. + let seed = iter(&mut ::test::rng()) .map(|rng| rng.next_u64()) .take(256) .collect::>(); let mut rng1 = Isaac64Rng::from_seed(&seed[..]); - let mut rng2 = Isaac64Rng::from_seed(&seed[..]); - for _ in 0..100 { - assert_eq!(rng1.next_u64(), rng2.next_u64()); - } - } - - #[test] - fn test_isaac64_from_seed_fixed() { + rng1.next_u64(); + + let mut rng2 = Isaac64Rng::from_rng(&mut ::test::rng()).unwrap(); + rng2.next_u64(); + let seed: &[_] = &[1, 23, 456, 7890, 12345]; - let mut rng1 = Isaac64Rng::from_seed(&seed[..]); - let mut rng2 = Isaac64Rng::from_seed(&seed[..]); - for _ in 0..100 { - assert_eq!(rng1.next_u64(), rng2.next_u64()); - } + let mut rng3 = Isaac64Rng::from_seed(&seed[..]); + rng3.next_u64(); + } #[test] From ae365ef352eeb0a4a249b598dde7723ddf37a32d Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Fri, 10 Nov 2017 17:21:53 +0000 Subject: [PATCH 163/247] Add true_bytes tests for ChaCha and Isaac; fix 2 bugs in fill_bytes impls --- src/prng/chacha.rs | 16 +++++++++++++++- src/prng/isaac.rs | 14 ++++++++++++++ src/prng/isaac64.rs | 18 ++++++++++++++++-- 3 files changed, 45 insertions(+), 3 deletions(-) diff --git a/src/prng/chacha.rs b/src/prng/chacha.rs index db1d127bb9c..a9ae8996b09 100644 --- a/src/prng/chacha.rs +++ b/src/prng/chacha.rs @@ -235,7 +235,7 @@ impl Rng for ChaChaRng { unsafe{ copy_nonoverlapping( &self.buffer[self.index].0 as *const u32 as *const u8, l.as_mut_ptr(), - words) }; + 4 * words) }; self.index += words; } let n = left.len(); @@ -351,6 +351,20 @@ mod test { 0x2c5bad8f, 0x898881dc, 0x5f1c86d9, 0xc1f8e7f4)); } + #[test] + fn test_rng_true_bytes() { + let seed : &[_] = &[0u32; 8]; + let mut ra: ChaChaRng = SeedableRng::from_seed(seed); + let mut buf = [0u8; 32]; + ra.fill_bytes(&mut buf); + // Same as first values in test_isaac_true_values as bytes in LE order + assert_eq!(buf, + [118, 184, 224, 173, 160, 241, 61, 144, + 64, 93, 106, 229, 83, 134, 189, 40, + 189, 210, 25, 184, 160, 141, 237, 26, + 168, 54, 239, 204, 139, 119, 13, 199]); + } + #[test] fn test_rng_clone() { let seed : &[_] = &[0u32; 8]; diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index 996bc684f5b..82944144cb8 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -407,6 +407,20 @@ mod test { 141456972, 2478885421)); } + #[test] + fn test_isaac_true_bytes() { + let seed: &[_] = &[1, 23, 456, 7890, 12345]; + let mut rng1 = IsaacRng::from_seed(seed); + let mut buf = [0u8; 32]; + rng1.fill_bytes(&mut buf); + // Same as first values in test_isaac_true_values as bytes in LE order + assert_eq!(buf, + [82, 186, 128, 152, 71, 240, 20, 52, + 45, 175, 180, 15, 86, 16, 99, 125, + 101, 203, 81, 214, 97, 162, 134, 250, + 103, 78, 203, 15, 150, 3, 210, 164]); + } + #[test] fn test_isaac_new_uninitialized() { // Compare the results from initializing `IsaacRng` with diff --git a/src/prng/isaac64.rs b/src/prng/isaac64.rs index 91c0186f3c7..1d84d555f72 100644 --- a/src/prng/isaac64.rs +++ b/src/prng/isaac64.rs @@ -219,7 +219,7 @@ impl Rng for Isaac64Rng { } fn fill_bytes(&mut self, dest: &mut [u8]) { - ::rand_core::impls::fill_bytes_via_u32(self, dest); + ::rand_core::impls::fill_bytes_via_u64(self, dest); } fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { @@ -370,7 +370,21 @@ mod test { 596345674630742204, 9947027391921273664, 11788097613744130851, 10391409374914919106)); } - + + #[test] + fn test_isaac64_true_bytes() { + let seed: &[_] = &[1, 23, 456, 7890, 12345]; + let mut rng1 = Isaac64Rng::from_seed(seed); + let mut buf = [0u8; 32]; + rng1.fill_bytes(&mut buf); + // Same as first values in test_isaac64_true_values as bytes in LE order + assert_eq!(buf, + [140, 237, 103, 8, 93, 196, 151, 7, + 156, 242, 26, 63, 54, 166, 135, 199, + 141, 186, 192, 50, 116, 69, 205, 240, + 98, 205, 127, 160, 83, 98, 49, 17]); + } + #[test] fn test_isaac_new_uninitialized() { // Compare the results from initializing `IsaacRng` with From fd2660b5427265320e145d11d5e26dc4d7844203 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Fri, 10 Nov 2017 17:34:55 +0000 Subject: [PATCH 164/247] Isaac64: add test for true 32-bit values Includes both the values output now and the values which should be output by #36. --- src/prng/isaac64.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/prng/isaac64.rs b/src/prng/isaac64.rs index 1d84d555f72..2c84927c2d8 100644 --- a/src/prng/isaac64.rs +++ b/src/prng/isaac64.rs @@ -371,6 +371,28 @@ mod test { 11788097613744130851, 10391409374914919106)); } + #[test] + fn test_isaac64_true_values_32() { + let seed: &[_] = &[1, 23, 456, 7890, 12345]; + let mut rng1 = Isaac64Rng::from_seed(seed); + let v = (0..10).map(|_| rng1.next_u32()).collect::>(); + // Subset of above values, as an LE u32 sequence + // TODO: switch to this sequence? +// assert_eq!(v, +// [141028748, 127386717, +// 1058730652, 3347555894, +// 851491469, 4039984500, +// 2692730210, 288449107, +// 646103879, 2782923823]); + // Subset of above values, using only low-half of each u64 + assert_eq!(v, + [141028748, 1058730652, + 851491469, 2692730210, + 646103879, 4195642895, + 2836348583, 1312677241, + 999139615, 253604626]); + } + #[test] fn test_isaac64_true_bytes() { let seed: &[_] = &[1, 23, 456, 7890, 12345]; From d67864abf66b8cbe62d368af12575b9a1f55a077 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Fri, 10 Nov 2017 19:32:56 +0100 Subject: [PATCH 165/247] Add `rand_core::impl::fill_via_u*_chunks` --- rand_core/src/impls.rs | 66 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/rand_core/src/impls.rs b/rand_core/src/impls.rs index cffca3e882f..7ad7fd7d0a3 100644 --- a/rand_core/src/impls.rs +++ b/rand_core/src/impls.rs @@ -21,6 +21,7 @@ use core::intrinsics::transmute; use core::slice; +use core::cmp::min; use Rng; /// Implement `next_u64` via `next_u32`, little-endian order. @@ -93,6 +94,71 @@ macro_rules! impl_uint_from_fill { }); } +macro_rules! fill_via_chunks { + ($src:expr, $dest:expr, $N:expr) => ({ + let chunk_size_u8 = min($src.len() * $N, $dest.len()); + let chunk_size = (chunk_size_u8 + $N - 1) / $N; + + // Convert to little-endian: + for ref mut x in $src[0..chunk_size].iter_mut() { + **x = (*x).to_le(); + } + + let bytes = unsafe { slice::from_raw_parts($src.as_ptr() as *const u8, + $src.len() * $N) }; + + let dest_chunk = &mut $dest[0..chunk_size_u8]; + dest_chunk.copy_from_slice(&bytes[0..chunk_size_u8]); + + (chunk_size, chunk_size_u8) + }); +} + +/// Implement `fill_bytes` by reading chunks from the output buffer of a block +/// based RNG. +/// +/// The return values are `(consumed_u32, filled_u8)`. +/// +/// Note that on big-endian systems values in the output buffer `src` are +/// mutated: they get converted to little-endian before copying. +/// +/// # Example +/// (from `IsaacRng`) +/// +/// ```rust,ignore +/// fn fill_bytes(&mut self, dest: &mut [u8]) { +/// let mut read_len = 0; +/// while read_len < dest.len() { +/// if self.index >= self.rsl.len() { +/// self.isaac(); +/// } +/// +/// let (consumed_u32, filled_u8) = +/// impls::fill_via_u32_chunks(&mut self.rsl[self.index..], +/// &mut dest[read_len..]); +/// +/// self.index += consumed_u32; +/// read_len += filled_u8; +/// } +/// } +/// ``` +pub fn fill_via_u32_chunks(src: &mut [u32], dest: &mut [u8]) -> (usize, usize) { + fill_via_chunks!(src, dest, 4) +} + +/// Implement `fill_bytes` by reading chunks from the output buffer of a block +/// based RNG. +/// +/// Note that on big-endian systems values in the output buffer `src` are +/// mutated: they get converted to little-endian before copying. +/// +/// The return values are `(consumed_u64, filled_u8)`. +/// +/// See `fill_via_u32_chunks` for an example. +pub fn fill_via_u64_chunks(src: &mut [u64], dest: &mut [u8]) -> (usize, usize) { + fill_via_chunks!(src, dest, 8) +} + /// Implement `next_u32` via `fill_bytes`, little-endian order. pub fn next_u32_via_fill(rng: &mut R) -> u32 { impl_uint_from_fill!(rng, u32, 4) From d7b014c143e2a38787a4c4e73eaf7498607854d9 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Sat, 11 Nov 2017 07:43:26 +0100 Subject: [PATCH 166/247] Convert ChaCha to use `fill_via_u32_chunks` This also replaces `core::num::Wrapping` with a few `wrapping_add`'s. There were about 30 conversions to and from `Wrapping`, while there are only 9 wrapping operations. Because `fill_via_u32_chunks` expects a `[u32]`, converting away was just easier. --- src/prng/chacha.rs | 136 ++++++++++++++++++++------------------------- 1 file changed, 59 insertions(+), 77 deletions(-) diff --git a/src/prng/chacha.rs b/src/prng/chacha.rs index a9ae8996b09..49e8653b27b 100644 --- a/src/prng/chacha.rs +++ b/src/prng/chacha.rs @@ -10,13 +10,10 @@ //! The ChaCha random number generator. -use core::num::Wrapping as w; use core::fmt; +use rand_core::impls; use {Rng, CryptoRng, SeedFromRng, SeedableRng, Error}; -#[allow(bad_style)] -type w32 = w; - const KEY_WORDS : usize = 8; // 8 words for the 256-bit key const STATE_WORDS : usize = 16; const CHACHA_ROUNDS: u32 = 20; // Cryptographically secure from 8 upwards as of this writing @@ -32,9 +29,9 @@ const CHACHA_ROUNDS: u32 = 20; // Cryptographically secure from 8 upwards as of /// Salsa20*](http://cr.yp.to/chacha.html) #[derive(Clone)] pub struct ChaChaRng { - buffer: [w32; STATE_WORDS], // Internal buffer of output - state: [w32; STATE_WORDS], // Initial state - index: usize, // Index into state + buffer: [u32; STATE_WORDS], // Internal buffer of output + state: [u32; STATE_WORDS], // Initial state + index: usize, // Index into state } // Custom Debug implementation that does not expose the internal state @@ -46,10 +43,10 @@ impl fmt::Debug for ChaChaRng { macro_rules! quarter_round{ ($a: expr, $b: expr, $c: expr, $d: expr) => {{ - $a = $a + $b; $d = $d ^ $a; $d = w($d.0.rotate_left(16)); - $c = $c + $d; $b = $b ^ $c; $b = w($b.0.rotate_left(12)); - $a = $a + $b; $d = $d ^ $a; $d = w($d.0.rotate_left( 8)); - $c = $c + $d; $b = $b ^ $c; $b = w($b.0.rotate_left( 7)); + $a = $a.wrapping_add($b); $d ^= $a; $d = $d.rotate_left(16); + $c = $c.wrapping_add($d); $b ^= $c; $b = $b.rotate_left(12); + $a = $a.wrapping_add($b); $d ^= $a; $d = $d.rotate_left( 8); + $c = $c.wrapping_add($d); $b ^= $c; $b = $b.rotate_left( 7); }} } @@ -69,15 +66,15 @@ macro_rules! double_round{ } #[inline] -fn core(output: &mut [w32; STATE_WORDS], input: &[w32; STATE_WORDS]) { - *output = *input; +fn core(new: &mut [u32; STATE_WORDS], input: &[u32; STATE_WORDS]) { + *new = *input; for _ in 0..CHACHA_ROUNDS / 2 { - double_round!(output); + double_round!(new); } for i in 0..STATE_WORDS { - output[i] = output[i] + input[i]; + new[i] = new[i].wrapping_add(input[i]); } } @@ -104,8 +101,8 @@ impl ChaChaRng { /// - 2419978656 pub fn new_unseeded() -> ChaChaRng { let mut rng = ChaChaRng { - buffer: [w(0); STATE_WORDS], - state: [w(0); STATE_WORDS], + buffer: [0; STATE_WORDS], + state: [0; STATE_WORDS], index: STATE_WORDS }; rng.init(&[0; KEY_WORDS]); @@ -133,10 +130,10 @@ impl ChaChaRng { /// println!("{:?}", ra.next_u32()); /// ``` pub fn set_counter(&mut self, counter_low: u64, counter_high: u64) { - self.state[12] = w((counter_low >> 0) as u32); - self.state[13] = w((counter_low >> 32) as u32); - self.state[14] = w((counter_high >> 0) as u32); - self.state[15] = w((counter_high >> 32) as u32); + self.state[12] = (counter_low >> 0) as u32; + self.state[13] = (counter_low >> 32) as u32; + self.state[14] = (counter_high >> 0) as u32; + self.state[15] = (counter_high >> 32) as u32; self.index = STATE_WORDS; // force recomputation } @@ -159,19 +156,19 @@ impl ChaChaRng { /// [1]: Daniel J. Bernstein. [*Extending the Salsa20 /// nonce.*](http://cr.yp.to/papers.html#xsalsa) fn init(&mut self, key: &[u32; KEY_WORDS]) { - self.state[0] = w(0x61707865); - self.state[1] = w(0x3320646E); - self.state[2] = w(0x79622D32); - self.state[3] = w(0x6B206574); + self.state[0] = 0x61707865; + self.state[1] = 0x3320646E; + self.state[2] = 0x79622D32; + self.state[3] = 0x6B206574; for i in 0..KEY_WORDS { - self.state[4+i] = w(key[i]); + self.state[4+i] = key[i]; } - self.state[12] = w(0); - self.state[13] = w(0); - self.state[14] = w(0); - self.state[15] = w(0); + self.state[12] = 0; + self.state[13] = 0; + self.state[14] = 0; + self.state[15] = 0; self.index = STATE_WORDS; } @@ -181,69 +178,54 @@ impl ChaChaRng { core(&mut self.buffer, &self.state); self.index = 0; // update 128-bit counter - self.state[12] = self.state[12] + w(1); - if self.state[12] != w(0) { return }; - self.state[13] = self.state[13] + w(1); - if self.state[13] != w(0) { return }; - self.state[14] = self.state[14] + w(1); - if self.state[14] != w(0) { return }; - self.state[15] = self.state[15] + w(1); + self.state[12] = self.state[12].wrapping_add(1); + if self.state[12] != 0 { return }; + self.state[13] = self.state[13].wrapping_add(1); + if self.state[13] != 0 { return }; + self.state[14] = self.state[14].wrapping_add(1); + if self.state[14] != 0 { return }; + self.state[15] = self.state[15].wrapping_add(1); } } impl Rng for ChaChaRng { #[inline] fn next_u32(&mut self) -> u32 { - if self.index == STATE_WORDS { + // Using a local variable for `index`, and checking the size avoids a + // bounds check later on. + let mut index = self.index as usize; + if index >= STATE_WORDS { self.update(); + index = 0; } - let value = self.buffer[self.index % STATE_WORDS]; + let value = self.buffer[index]; self.index += 1; - value.0 + value } - + fn next_u64(&mut self) -> u64 { - ::rand_core::impls::next_u64_via_u32(self) + impls::next_u64_via_u32(self) } + #[cfg(feature = "i128_support")] fn next_u128(&mut self) -> u128 { - ::rand_core::impls::next_u128_via_u64(self) + impls::next_u128_via_u64(self) } - - // Custom implementation allowing larger reads from buffer is about 8% - // faster than default implementation in my tests + fn fill_bytes(&mut self, dest: &mut [u8]) { - use core::cmp::min; - use core::intrinsics::{transmute, copy_nonoverlapping}; - - let mut left = dest; - while left.len() >= 4 { - if self.index == STATE_WORDS { + let mut read_len = 0; + while read_len < dest.len() { + if self.index >= self.buffer.len() { self.update(); } - - let words = min(left.len() / 4, STATE_WORDS - self.index); - let (l, r) = {left}.split_at_mut(4 * words); - left = r; - - // convert to LE: - for ref mut x in self.buffer[self.index..self.index+words].iter_mut() { - **x = w((*x).0.to_le()); - } - - unsafe{ copy_nonoverlapping( - &self.buffer[self.index].0 as *const u32 as *const u8, - l.as_mut_ptr(), - 4 * words) }; - self.index += words; - } - let n = left.len(); - if n > 0 { - let chunk: [u8; 4] = unsafe { - transmute(self.next_u32().to_le()) - }; - left.copy_from_slice(&chunk[..n]); + + let (consumed_u32, filled_u8) = + impls::fill_via_u32_chunks(&mut self.buffer[self.index..], + &mut dest[read_len..]); + + self.index += consumed_u32; + read_len += filled_u8; } } @@ -271,8 +253,8 @@ impl<'a> SeedableRng<&'a [u32]> for ChaChaRng { /// words are used, the remaining are set to zero. fn from_seed(seed: &'a [u32]) -> ChaChaRng { let mut rng = ChaChaRng { - buffer: [w(0); STATE_WORDS], - state: [w(0); STATE_WORDS], + buffer: [0; STATE_WORDS], + state: [0; STATE_WORDS], index: STATE_WORDS }; rng.init(&[0u32; KEY_WORDS]); @@ -280,7 +262,7 @@ impl<'a> SeedableRng<&'a [u32]> for ChaChaRng { { let key = &mut rng.state[4 .. 4+KEY_WORDS]; for (k, s) in key.iter_mut().zip(seed.iter()) { - *k = w(*s); + *k = *s; } } rng From f92a3476c730db469b357b231da186db71e9411c Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Sat, 11 Nov 2017 07:46:17 +0100 Subject: [PATCH 167/247] Fill `isaac` backwards, and use `fill_via_u32_chunks` Also uses a different solution to index without bounds checking, to recover a very little bit of lost performance. --- src/prng/isaac.rs | 67 +++++++++++++++++++++++++++-------------------- 1 file changed, 39 insertions(+), 28 deletions(-) diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index 82944144cb8..47dc8247f7a 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -15,6 +15,8 @@ use core::iter::repeat; use core::num::Wrapping as w; use core::fmt; +use rand_core::impls; + use {Rng, SeedFromRng, SeedableRng, Error}; #[allow(non_camel_case_types)] @@ -87,12 +89,12 @@ const RAND_SIZE: usize = 1 << RAND_SIZE_LEN; /// [3]: Jean-Philippe Aumasson, [*On the pseudo-random generator ISAAC*] /// (http://eprint.iacr.org/2006/438) pub struct IsaacRng { - rsl: [w32; RAND_SIZE], + rsl: [u32; RAND_SIZE], mem: [w32; RAND_SIZE], a: w32, b: w32, c: w32, - cnt: u32, + index: u32, } // Cannot be derived because [u32; 256] does not implement Clone @@ -105,7 +107,7 @@ impl Clone for IsaacRng { a: self.a, b: self.b, c: self.c, - cnt: self.cnt, + index: self.index, } } } @@ -149,6 +151,9 @@ impl IsaacRng { /// - We maintain one index `i` and add `m` or `m2` as base (m2 for the /// `s[i+128 mod 256]`), relying on the optimizer to turn it into pointer /// arithmetic. + /// - We fill `rsl` backwards. The reference implementation reads values + /// from `rsl` in reverse. We read them in the normal direction, to make + /// `fill_bytes` a memcopy. To maintain compatibility we fill in reverse. fn isaac(&mut self) { self.c += w(1); // abbreviations @@ -156,13 +161,13 @@ impl IsaacRng { let mut b = self.b + self.c; const MIDPOINT: usize = RAND_SIZE / 2; - #[inline(always)] + #[inline] fn ind(mem:&[w32; RAND_SIZE], v: w32, amount: usize) -> w32 { let index = (v >> amount).0 as usize % RAND_SIZE; mem[index] } - #[inline(always)] + #[inline] fn rngstep(ctx: &mut IsaacRng, mix: w32, a: &mut w32, @@ -175,7 +180,7 @@ impl IsaacRng { let y = *a + *b + ind(&ctx.mem, x, 2); ctx.mem[base + m] = y; *b = x + ind(&ctx.mem, y, 2 + RAND_SIZE_LEN); - ctx.rsl[base + m] = *b; + ctx.rsl[RAND_SIZE - 1 - base - m] = (*b).0; } let mut m = 0; @@ -198,44 +203,50 @@ impl IsaacRng { self.a = a; self.b = b; - self.cnt = RAND_SIZE as u32; + self.index = 0; } } impl Rng for IsaacRng { #[inline] fn next_u32(&mut self) -> u32 { - if self.cnt == 0 { - // make some more numbers + // Using a local variable for `index`, and checking the size avoids a + // bounds check later on. + let mut index = self.index as usize; + if index >= RAND_SIZE { self.isaac(); + index = 0; } - self.cnt -= 1; - - // self.cnt is at most RAND_SIZE, but that is before the - // subtraction above. We want to index without bounds - // checking, but this could lead to incorrect code if someone - // misrefactors, so we check, sometimes. - // - // (Changes here should be reflected in Isaac64Rng.next_u64.) - debug_assert!((self.cnt as usize) < RAND_SIZE); - - // (the % is cheaply telling the optimiser that we're always - // in bounds, without unsafe. NB. this is a power of two, so - // it optimises to a bitwise mask). - self.rsl[self.cnt as usize % RAND_SIZE].0 + + let value = self.rsl[index]; + self.index += 1; + value } + #[inline] fn next_u64(&mut self) -> u64 { - ::rand_core::impls::next_u64_via_u32(self) + impls::next_u64_via_u32(self) } #[cfg(feature = "i128_support")] fn next_u128(&mut self) -> u128 { - ::rand_core::impls::next_u128_via_u64(self) + impls::next_u128_via_u64(self) } fn fill_bytes(&mut self, dest: &mut [u8]) { - ::rand_core::impls::fill_bytes_via_u32(self, dest); + let mut read_len = 0; + while read_len < dest.len() { + if self.index as usize >= RAND_SIZE { + self.isaac(); + } + + let (consumed_u32, filled_u8) = + impls::fill_via_u32_chunks(&mut self.rsl[(self.index as usize)..], + &mut dest[read_len..]); + + self.index += consumed_u32 as u32; + read_len += filled_u8; + } } fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { @@ -300,12 +311,12 @@ fn init(mut mem: [w32; RAND_SIZE], rounds: u32) -> IsaacRng { } let mut rng = IsaacRng { - rsl: [w(0); RAND_SIZE], + rsl: [0; RAND_SIZE], mem: mem, a: w(0), b: w(0), c: w(0), - cnt: 0, + index: 0, }; // Prepare the first set of results From 707c3e109209962426896ceabb0dec7be9c19171 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Sat, 11 Nov 2017 07:52:59 +0100 Subject: [PATCH 168/247] Fill `isaac64` backwards, and use `fill_via_u32_chunks` --- src/prng/isaac64.rs | 62 +++++++++++++++++++++++++-------------------- 1 file changed, 35 insertions(+), 27 deletions(-) diff --git a/src/prng/isaac64.rs b/src/prng/isaac64.rs index 2c84927c2d8..18d76e4a671 100644 --- a/src/prng/isaac64.rs +++ b/src/prng/isaac64.rs @@ -15,6 +15,8 @@ use core::iter::repeat; use core::num::Wrapping as w; use core::fmt; +use rand_core::impls; + use {Rng, SeedFromRng, SeedableRng, Error}; #[allow(non_camel_case_types)] @@ -71,12 +73,12 @@ const RAND_SIZE: usize = 1 << RAND_SIZE_LEN; /// [1]: Bob Jenkins, [*ISAAC and RC4*] /// (http://burtleburtle.net/bob/rand/isaac.html) pub struct Isaac64Rng { - rsl: [w64; RAND_SIZE], + rsl: [u64; RAND_SIZE], mem: [w64; RAND_SIZE], a: w64, b: w64, c: w64, - cnt: u32, + index: u32, } // Cannot be derived because [u64; 256] does not implement Clone @@ -89,7 +91,7 @@ impl Clone for Isaac64Rng { a: self.a, b: self.b, c: self.c, - cnt: self.cnt, + index: self.index, } } } @@ -132,6 +134,9 @@ impl Isaac64Rng { /// - We maintain one index `i` and add `m` or `m2` as base (m2 for the /// `s[i+128 mod 256]`), relying on the optimizer to turn it into pointer /// arithmetic. + /// - We fill `rsl` backwards. The reference implementation reads values + /// from `rsl` in reverse. We read them in the normal direction, to make + /// `fill_bytes` a memcopy. To maintain compatibility we fill in reverse. fn isaac64(&mut self) { self.c += w(1); // abbreviations @@ -139,13 +144,13 @@ impl Isaac64Rng { let mut b = self.b + self.c; const MIDPOINT: usize = RAND_SIZE / 2; - #[inline(always)] + #[inline] fn ind(mem:&[w64; RAND_SIZE], v: w64, amount: usize) -> w64 { let index = (v >> amount).0 as usize % RAND_SIZE; mem[index] } - #[inline(always)] + #[inline] fn rngstep(ctx: &mut Isaac64Rng, mix: w64, a: &mut w64, @@ -158,7 +163,7 @@ impl Isaac64Rng { let y = *a + *b + ind(&ctx.mem, x, 3); ctx.mem[base + m] = y; *b = x + ind(&ctx.mem, y, 3 + RAND_SIZE_LEN); - ctx.rsl[base + m] = *b; + ctx.rsl[RAND_SIZE - 1 - base - m] = (*b).0; } let mut m = 0; @@ -181,7 +186,7 @@ impl Isaac64Rng { self.a = a; self.b = b; - self.cnt = RAND_SIZE as u32; + self.index = 0; } } @@ -193,33 +198,36 @@ impl Rng for Isaac64Rng { #[inline] fn next_u64(&mut self) -> u64 { - if self.cnt == 0 { - // make some more numbers + let mut index = self.index as usize; + if index >= RAND_SIZE { self.isaac64(); + index = 0; } - self.cnt -= 1; - - // self.cnt is at most RAND_SIZE, but that is before the - // subtraction above. We want to index without bounds - // checking, but this could lead to incorrect code if someone - // misrefactors, so we check, sometimes. - // - // (Changes here should be reflected in IsaacRng.next_u32.) - debug_assert!((self.cnt as usize) < RAND_SIZE); - - // (the % is cheaply telling the optimiser that we're always - // in bounds, without unsafe. NB. this is a power of two, so - // it optimises to a bitwise mask). - self.rsl[self.cnt as usize % RAND_SIZE].0 + + let value = self.rsl[index]; + self.index += 1; + value } #[cfg(feature = "i128_support")] fn next_u128(&mut self) -> u128 { - ::rand_core::impls::next_u128_via_u64(self) + impls::next_u128_via_u64(self) } fn fill_bytes(&mut self, dest: &mut [u8]) { - ::rand_core::impls::fill_bytes_via_u64(self, dest); + let mut read_len = 0; + while read_len < dest.len() { + if self.index as usize >= RAND_SIZE { + self.isaac64(); + } + + let (consumed_u64, filled_u8) = + impls::fill_via_u64_chunks(&mut self.rsl[(self.index as usize)..], + &mut dest[read_len..]); + + self.index += consumed_u64 as u32; + read_len += filled_u8; + } } fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { @@ -259,12 +267,12 @@ fn init(mut mem: [w64; RAND_SIZE], rounds: u32) -> Isaac64Rng { } let mut rng = Isaac64Rng { - rsl: [w(0); RAND_SIZE], + rsl: [0; RAND_SIZE], mem: mem, a: w(0), b: w(0), c: w(0), - cnt: 0, + index: 0, }; // Prepare the first set of results From 415ef6f440cce67c1a75257617e9d31021f1d65b Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Sat, 11 Nov 2017 08:04:20 +0100 Subject: [PATCH 169/247] Improve performance of `isaac64::next_u32`. This does some crazy things with indexing, but is 45% faster. We are no longer throwing away half of the results. --- src/prng/isaac64.rs | 61 ++++++++++++++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 20 deletions(-) diff --git a/src/prng/isaac64.rs b/src/prng/isaac64.rs index 18d76e4a671..56b2a16255b 100644 --- a/src/prng/isaac64.rs +++ b/src/prng/isaac64.rs @@ -137,6 +137,13 @@ impl Isaac64Rng { /// - We fill `rsl` backwards. The reference implementation reads values /// from `rsl` in reverse. We read them in the normal direction, to make /// `fill_bytes` a memcopy. To maintain compatibility we fill in reverse. + /// - We store `index` as if `rsl` contains `u32`'s instead of `u64`'s, plus + /// one. This way we can make more efficient use of the generated results + /// in `next_u32`. + /// For `next_u32` the correct index is `index - 1`. + /// For `next_u64` the correct index is `index >> 1`, which also takes + /// care of any alignment issues that could arise if `next_u64` was called + /// after `next_u32`. fn isaac64(&mut self) { self.c += w(1); // abbreviations @@ -186,26 +193,48 @@ impl Isaac64Rng { self.a = a; self.b = b; - self.index = 0; + self.index = 1; } } impl Rng for Isaac64Rng { #[inline] fn next_u32(&mut self) -> u32 { - self.next_u64() as u32 + // Using a local variable for `index`, and checking the size avoids a + // bounds check later on. + let mut index = self.index as usize - 1; + if index >= RAND_SIZE * 2 { + self.isaac64(); + index = 0; + } + + let value; + if cfg!(target_endian = "little") { + // Index as if this is a u32 slice. + let rsl = unsafe { &*(&mut self.rsl as *mut [u64; RAND_SIZE] + as *mut [u32; RAND_SIZE * 2]) }; + value = rsl[index]; + } else { + // Index into the u64 slice, rotate and truncate the result. + // Works always, also on big-endian systems, but is slower. + let tmp = self.rsl[index >> 1]; + value = tmp as u32; + self.rsl[index >> 1] = tmp.rotate_right(32); + } + self.index += 1; + value } #[inline] fn next_u64(&mut self) -> u64 { - let mut index = self.index as usize; + let mut index = self.index as usize >> 1; if index >= RAND_SIZE { self.isaac64(); index = 0; } let value = self.rsl[index]; - self.index += 1; + self.index += 2; value } @@ -217,15 +246,15 @@ impl Rng for Isaac64Rng { fn fill_bytes(&mut self, dest: &mut [u8]) { let mut read_len = 0; while read_len < dest.len() { - if self.index as usize >= RAND_SIZE { + if (self.index as usize >> 1) >= RAND_SIZE { self.isaac64(); } let (consumed_u64, filled_u8) = - impls::fill_via_u64_chunks(&mut self.rsl[(self.index as usize)..], + impls::fill_via_u64_chunks(&mut self.rsl[(self.index as usize >> 1)..], &mut dest[read_len..]); - self.index += consumed_u64 as u32; + self.index += consumed_u64 as u32 * 2; read_len += filled_u8; } } @@ -385,20 +414,12 @@ mod test { let mut rng1 = Isaac64Rng::from_seed(seed); let v = (0..10).map(|_| rng1.next_u32()).collect::>(); // Subset of above values, as an LE u32 sequence - // TODO: switch to this sequence? -// assert_eq!(v, -// [141028748, 127386717, -// 1058730652, 3347555894, -// 851491469, 4039984500, -// 2692730210, 288449107, -// 646103879, 2782923823]); - // Subset of above values, using only low-half of each u64 assert_eq!(v, - [141028748, 1058730652, - 851491469, 2692730210, - 646103879, 4195642895, - 2836348583, 1312677241, - 999139615, 253604626]); + [141028748, 127386717, + 1058730652, 3347555894, + 851491469, 4039984500, + 2692730210, 288449107, + 646103879, 2782923823]); } #[test] From c218f7a4480f90f1dbc3f26c554ce2161a61b3e3 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Fri, 10 Nov 2017 21:01:15 +0100 Subject: [PATCH 170/247] Add CI based on `trust` --- .travis.yml | 126 ++++++++++++++++++++++++++++++++++------- ci/install.sh | 47 +++++++++++++++ ci/script.sh | 28 +++++++++ rand_core/src/impls.rs | 2 +- src/os.rs | 2 +- 5 files changed, 182 insertions(+), 23 deletions(-) create mode 100644 ci/install.sh create mode 100644 ci/script.sh diff --git a/.travis.yml b/.travis.yml index e790f0913d9..6b1b1252def 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,30 +1,114 @@ +# Based on the "trust" template v0.1.1 +# https://github.com/japaric/trust/tree/v0.1.1 + +dist: trusty language: rust -sudo: false -before_script: - - pip install 'travis-cargo<0.2' --user && export PATH=$HOME/.local/bin:$PATH +services: docker +sudo: required + +# TODO Rust builds on stable by default, this can be +# overridden on a case by case basis down below. + +env: + global: + - CRATE_NAME=rand matrix: + # TODO These are all the build jobs. Adjust as necessary. Comment out what you + # don't need include: - - rust: 1.18.0 - - rust: stable - - rust: stable + # Android + # - env: TARGET=aarch64-linux-android DISABLE_TESTS=1 + # - env: TARGET=arm-linux-androideabi DISABLE_TESTS=1 + - env: TARGET=armv7-linux-androideabi DISABLE_TESTS=1 + # - env: TARGET=i686-linux-android DISABLE_TESTS=1 + # - env: TARGET=x86_64-linux-android DISABLE_TESTS=1 + + # iOS + # - env: TARGET=aarch64-apple-ios DISABLE_TESTS=1 + # os: osx + - env: TARGET=armv7-apple-ios DISABLE_TESTS=1 os: osx - - rust: beta - - rust: nightly - script: - - cargo test --all - - cargo test --all --features nightly - - cargo test --all --benches - - cargo build --all --no-default-features - - cargo doc --no-deps --features nightly + # - env: TARGET=armv7s-apple-ios DISABLE_TESTS=1 + # os: osx + # - env: TARGET=i386-apple-ios DISABLE_TESTS=1 + # os: osx + # - env: TARGET=x86_64-apple-ios DISABLE_TESTS=1 + # os: osx + + # Linux + # - env: TARGET=aarch64-unknown-linux-gnu + # - env: TARGET=arm-unknown-linux-gnueabi + # - env: TARGET=armv7-unknown-linux-gnueabihf + - env: TARGET=i686-unknown-linux-gnu + # - env: TARGET=i686-unknown-linux-musl + - env: TARGET=mips-unknown-linux-gnu + - env: TARGET=mips64-unknown-linux-gnuabi64 + # - env: TARGET=mips64el-unknown-linux-gnuabi64 + # - env: TARGET=mipsel-unknown-linux-gnu + # - env: TARGET=powerpc-unknown-linux-gnu + # - env: TARGET=powerpc64-unknown-linux-gnu + # - env: TARGET=powerpc64le-unknown-linux-gnu + # - env: TARGET=s390x-unknown-linux-gnu DISABLE_TESTS=1 + - env: TARGET=x86_64-unknown-linux-gnu + # - env: TARGET=x86_64-unknown-linux-musl + + # OSX + - env: TARGET=i686-apple-darwin + os: osx + - env: TARGET=x86_64-apple-darwin + os: osx + + # *BSD + - env: TARGET=i686-unknown-freebsd DISABLE_TESTS=1 + - env: TARGET=x86_64-unknown-freebsd DISABLE_TESTS=1 + - env: TARGET=x86_64-unknown-netbsd DISABLE_TESTS=1 + + # Windows + # - env: TARGET=x86_64-pc-windows-gnu + + # Bare metal + # These targets don't support std and as such are likely not suitable for + # most crates. + # - env: TARGET=thumbv6m-none-eabi + # - env: TARGET=thumbv7em-none-eabi + # - env: TARGET=thumbv7em-none-eabihf + # - env: TARGET=thumbv7m-none-eabi + + # Testing other channels + - env: TARGET=x86_64-unknown-linux-gnu NIGHTLY=1 + rust: nightly + - env: TARGET=x86_64-apple-darwin NIGHTLY=1 + os: osx + rust: nightly + +before_install: + - set -e + - rustup self update + +install: + - sh ci/install.sh + - source ~/.cargo/env || true + script: - - cargo test --all - - cargo build --all --no-default-features -after_success: - - travis-cargo --only nightly doc-upload -env: - global: - secure: "BdDntVHSompN+Qxz5Rz45VI4ZqhD72r6aPl166FADlnkIwS6N6FLWdqs51O7G5CpoMXEDvyYrjmRMZe/GYLIG9cmqmn/wUrWPO+PauGiIuG/D2dmfuUNvSTRcIe7UQLXrfP3yyfZPgqsH6pSnNEVopquQKy3KjzqepgriOJtbyY=" + - bash ci/script.sh + +after_script: set +e + +before_deploy: + - sh ci/before_deploy.sh + +cache: cargo +before_cache: + # Travis can't cache files that are not readable by "others" + - chmod -R a+r $HOME/.cargo + +# after_success: +# - travis-cargo --only nightly doc-upload + +# env: +# global: +# secure: "BdDntVHSompN+Qxz5Rz45VI4ZqhD72r6aPl166FADlnkIwS6N6FLWdqs51O7G5CpoMXEDvyYrjmRMZe/GYLIG9cmqmn/wUrWPO+PauGiIuG/D2dmfuUNvSTRcIe7UQLXrfP3yyfZPgqsH6pSnNEVopquQKy3KjzqepgriOJtbyY=" notifications: email: diff --git a/ci/install.sh b/ci/install.sh new file mode 100644 index 00000000000..80e18e47208 --- /dev/null +++ b/ci/install.sh @@ -0,0 +1,47 @@ +set -ex + +main() { + local target= + if [ $TRAVIS_OS_NAME = linux ]; then + target=x86_64-unknown-linux-musl + sort=sort + else + target=x86_64-apple-darwin + sort=gsort # for `sort --sort-version`, from brew's coreutils. + fi + + # Builds for iOS are done on OSX, but require the specific target to be + # installed. + case $TARGET in + aarch64-apple-ios) + rustup target install aarch64-apple-ios + ;; + armv7-apple-ios) + rustup target install armv7-apple-ios + ;; + armv7s-apple-ios) + rustup target install armv7s-apple-ios + ;; + i386-apple-ios) + rustup target install i386-apple-ios + ;; + x86_64-apple-ios) + rustup target install x86_64-apple-ios + ;; + esac + + # This fetches latest stable release + local tag=$(git ls-remote --tags --refs --exit-code https://github.com/japaric/cross \ + | cut -d/ -f3 \ + | grep -E '^v[0.1.0-9.]+$' \ + | $sort --version-sort \ + | tail -n1) + curl -LSfs https://japaric.github.io/trust/install.sh | \ + sh -s -- \ + --force \ + --git japaric/cross \ + --tag $tag \ + --target $target +} + +main diff --git a/ci/script.sh b/ci/script.sh new file mode 100644 index 00000000000..6a80436aeaf --- /dev/null +++ b/ci/script.sh @@ -0,0 +1,28 @@ +# This script takes care of testing your crate + +set -ex + +# TODO This is the "test phase", tweak it as you see fit +main() { + cross build --target $TARGET + cross build --all --no-default-features --target $TARGET --release + if [ ! -z $NIGHTLY ]; then + cross doc --no-deps --features nightly + fi + + if [ ! -z $DISABLE_TESTS ]; then + return + fi + + cross test --all --target $TARGET + + if [ ! -z $NIGHTLY ]; then + cross test --all --features nightly --target $TARGET + cross test --all --benches --target $TARGET + fi +} + +# we don't run the "test phase" when doing deploys +if [ -z $TRAVIS_TAG ]; then + main +fi diff --git a/rand_core/src/impls.rs b/rand_core/src/impls.rs index cffca3e882f..44319b95341 100644 --- a/rand_core/src/impls.rs +++ b/rand_core/src/impls.rs @@ -89,7 +89,7 @@ macro_rules! impl_uint_from_fill { let slice = slice::from_raw_parts_mut(ptr, $N); $self.fill_bytes(slice); } - int.to_le() + int }); } diff --git a/src/os.rs b/src/os.rs index e65d770af1e..cd0614927e8 100644 --- a/src/os.rs +++ b/src/os.rs @@ -81,7 +81,7 @@ impl Rng for OsRng { struct ReadRng (R); impl ReadRng { - fn try_fill(&mut self, mut dest: &mut [u8]) -> Result<(), Error> { + fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { if dest.len() == 0 { return Ok(()); } // Use `std::io::read_exact`, which retries on `ErrorKind::Interrupted`. self.0.read_exact(dest).map_err(|err| { From 0bdb1c3926514ea83cc14c920d4ada864ae4e900 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Sat, 11 Nov 2017 13:37:17 +0100 Subject: [PATCH 171/247] Remove complex indexing, use a bool. --- src/prng/isaac64.rs | 47 ++++++++++++++++++++------------------------- 1 file changed, 21 insertions(+), 26 deletions(-) diff --git a/src/prng/isaac64.rs b/src/prng/isaac64.rs index 56b2a16255b..6731f559ba5 100644 --- a/src/prng/isaac64.rs +++ b/src/prng/isaac64.rs @@ -79,6 +79,7 @@ pub struct Isaac64Rng { b: w64, c: w64, index: u32, + half_used: bool, // true if only half of the previous result is used } // Cannot be derived because [u64; 256] does not implement Clone @@ -92,6 +93,7 @@ impl Clone for Isaac64Rng { b: self.b, c: self.c, index: self.index, + half_used: self.half_used, } } } @@ -137,13 +139,6 @@ impl Isaac64Rng { /// - We fill `rsl` backwards. The reference implementation reads values /// from `rsl` in reverse. We read them in the normal direction, to make /// `fill_bytes` a memcopy. To maintain compatibility we fill in reverse. - /// - We store `index` as if `rsl` contains `u32`'s instead of `u64`'s, plus - /// one. This way we can make more efficient use of the generated results - /// in `next_u32`. - /// For `next_u32` the correct index is `index - 1`. - /// For `next_u64` the correct index is `index >> 1`, which also takes - /// care of any alignment issues that could arise if `next_u64` was called - /// after `next_u32`. fn isaac64(&mut self) { self.c += w(1); // abbreviations @@ -193,7 +188,8 @@ impl Isaac64Rng { self.a = a; self.b = b; - self.index = 1; + self.index = 0; + self.half_used = false; } } @@ -202,39 +198,37 @@ impl Rng for Isaac64Rng { fn next_u32(&mut self) -> u32 { // Using a local variable for `index`, and checking the size avoids a // bounds check later on. - let mut index = self.index as usize - 1; + let mut index = self.index as usize * 2 - self.half_used as usize; if index >= RAND_SIZE * 2 { self.isaac64(); index = 0; } - let value; + self.half_used = !self.half_used; + self.index += self.half_used as u32; + + // Index as if this is a u32 slice. + let rsl = unsafe { &*(&mut self.rsl as *mut [u64; RAND_SIZE] + as *mut [u32; RAND_SIZE * 2]) }; + if cfg!(target_endian = "little") { - // Index as if this is a u32 slice. - let rsl = unsafe { &*(&mut self.rsl as *mut [u64; RAND_SIZE] - as *mut [u32; RAND_SIZE * 2]) }; - value = rsl[index]; + rsl[index] } else { - // Index into the u64 slice, rotate and truncate the result. - // Works always, also on big-endian systems, but is slower. - let tmp = self.rsl[index >> 1]; - value = tmp as u32; - self.rsl[index >> 1] = tmp.rotate_right(32); + rsl[index ^ 1] } - self.index += 1; - value } #[inline] fn next_u64(&mut self) -> u64 { - let mut index = self.index as usize >> 1; + let mut index = self.index as usize; if index >= RAND_SIZE { self.isaac64(); index = 0; } let value = self.rsl[index]; - self.index += 2; + self.index += 1; + self.half_used = false; value } @@ -246,15 +240,15 @@ impl Rng for Isaac64Rng { fn fill_bytes(&mut self, dest: &mut [u8]) { let mut read_len = 0; while read_len < dest.len() { - if (self.index as usize >> 1) >= RAND_SIZE { + if self.index as usize >= RAND_SIZE { self.isaac64(); } let (consumed_u64, filled_u8) = - impls::fill_via_u64_chunks(&mut self.rsl[(self.index as usize >> 1)..], + impls::fill_via_u64_chunks(&mut self.rsl[self.index as usize..], &mut dest[read_len..]); - self.index += consumed_u64 as u32 * 2; + self.index += consumed_u64 as u32; read_len += filled_u8; } } @@ -302,6 +296,7 @@ fn init(mut mem: [w64; RAND_SIZE], rounds: u32) -> Isaac64Rng { b: w(0), c: w(0), index: 0, + half_used: false, }; // Prepare the first set of results From 5f4bedf78cde32468e5f17ea128d08a921eea93a Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Sat, 11 Nov 2017 14:03:42 +0100 Subject: [PATCH 172/247] Add test for alternating between `next_u64` and `next_u32` --- src/prng/isaac64.rs | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/src/prng/isaac64.rs b/src/prng/isaac64.rs index 6731f559ba5..3c34b7fde5e 100644 --- a/src/prng/isaac64.rs +++ b/src/prng/isaac64.rs @@ -402,21 +402,39 @@ mod test { 596345674630742204, 9947027391921273664, 11788097613744130851, 10391409374914919106)); } - + #[test] fn test_isaac64_true_values_32() { let seed: &[_] = &[1, 23, 456, 7890, 12345]; let mut rng1 = Isaac64Rng::from_seed(seed); - let v = (0..10).map(|_| rng1.next_u32()).collect::>(); + let v = (0..12).map(|_| rng1.next_u32()).collect::>(); // Subset of above values, as an LE u32 sequence assert_eq!(v, [141028748, 127386717, 1058730652, 3347555894, 851491469, 4039984500, 2692730210, 288449107, - 646103879, 2782923823]); + 646103879, 2782923823, + 4195642895, 3252674613]); } - + + #[test] + fn test_isaac64_true_values_mixed() { + let seed: &[_] = &[1, 23, 456, 7890, 12345]; + let mut rng = Isaac64Rng::from_seed(seed); + // Test alternating between `next_u64` and `next_u32` works as expected. + // Values are the same as `test_isaac64_true_values` and + // `test_isaac64_true_values_32`. + assert_eq!(rng.next_u64(), 547121783600835980); + assert_eq!(rng.next_u32(), 1058730652); + assert_eq!(rng.next_u32(), 3347555894); + assert_eq!(rng.next_u64(), 17351601304698403469); + assert_eq!(rng.next_u32(), 2692730210); + // Skip one u32 + assert_eq!(rng.next_u64(), 11952566807690396487); + assert_eq!(rng.next_u32(), 4195642895); + } + #[test] fn test_isaac64_true_bytes() { let seed: &[_] = &[1, 23, 456, 7890, 12345]; @@ -430,7 +448,7 @@ mod test { 141, 186, 192, 50, 116, 69, 205, 240, 98, 205, 127, 160, 83, 98, 49, 17]); } - + #[test] fn test_isaac_new_uninitialized() { // Compare the results from initializing `IsaacRng` with From 69d940f36049f8ae17074108235be0de7f0bd80a Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Sat, 11 Nov 2017 14:15:13 +0100 Subject: [PATCH 173/247] Improve documentation --- rand_core/src/impls.rs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/rand_core/src/impls.rs b/rand_core/src/impls.rs index 7ad7fd7d0a3..0d2c98a2d19 100644 --- a/rand_core/src/impls.rs +++ b/rand_core/src/impls.rs @@ -119,8 +119,14 @@ macro_rules! fill_via_chunks { /// /// The return values are `(consumed_u32, filled_u8)`. /// +/// `filled_u8` is the number of filled bytes in `dest`, which may be less than +/// the length of `dest`. +/// `consumed_u32` is the number of words consumed from `src`, which is the same +/// as `filled_u8 / 4` rounded up. +/// /// Note that on big-endian systems values in the output buffer `src` are -/// mutated: they get converted to little-endian before copying. +/// mutated. `src[0..consumed_u32]` get converted to little-endian before +/// copying. /// /// # Example /// (from `IsaacRng`) @@ -149,10 +155,15 @@ pub fn fill_via_u32_chunks(src: &mut [u32], dest: &mut [u8]) -> (usize, usize) { /// Implement `fill_bytes` by reading chunks from the output buffer of a block /// based RNG. /// -/// Note that on big-endian systems values in the output buffer `src` are -/// mutated: they get converted to little-endian before copying. -/// /// The return values are `(consumed_u64, filled_u8)`. +/// `filled_u8` is the number of filled bytes in `dest`, which may be less than +/// the length of `dest`. +/// `consumed_u64` is the number of words consumed from `src`, which is the same +/// as `filled_u8 / 8` rounded up. +/// +/// Note that on big-endian systems values in the output buffer `src` are +/// mutated. `src[0..consumed_u64]` get converted to little-endian before +/// copying. /// /// See `fill_via_u32_chunks` for an example. pub fn fill_via_u64_chunks(src: &mut [u64], dest: &mut [u8]) -> (usize, usize) { From 500b574ed1f7e905c25b2afe00115e768ce2c647 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sat, 11 Nov 2017 15:11:27 +0000 Subject: [PATCH 174/247] Implicit handling: max wait 1 minute if not ready; extra doc --- rand_core/src/impls.rs | 18 +++++++++++------- rand_core/src/lib.rs | 17 +++++++++-------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/rand_core/src/impls.rs b/rand_core/src/impls.rs index e10087a999b..67edabcfa57 100644 --- a/rand_core/src/impls.rs +++ b/rand_core/src/impls.rs @@ -111,23 +111,27 @@ pub fn next_u128_via_fill(rng: &mut R) -> u128 { /// Implement `fill_bytes` via `try_fill` with implicit error handling. pub fn fill_via_try_fill(rng: &mut R, dest: &mut [u8]) { + const WAIT_DUR_MS: u32 = 100; + const MAX_WAIT: u32 = (1 * 60 * 1000) / WAIT_DUR_MS; + const TRANSIENT_STEP: u32 = MAX_WAIT / 8; let mut err_count = 0; + loop { if let Err(e) = rng.try_fill(dest) { if e.kind.should_retry() { - if e.kind.limit_retries() { - if err_count > 8 { // arbitrary limit - // TODO: log details & cause? - panic!("Too many RNG errors; last error: {}", e.msg()); - } - err_count += 1; + if err_count > MAX_WAIT { + // TODO: log details & cause? + panic!("Too many RNG errors or timeout; last error: {}", e.msg()); } if e.kind.should_wait() { #[cfg(feature="std")]{ - let dur = ::std::time::Duration::from_millis(10); + let dur = ::std::time::Duration::from_millis(WAIT_DUR_MS as u64); ::std::thread::sleep(dur); } + err_count += 1; + } else { + err_count += TRANSIENT_STEP; } continue; diff --git a/rand_core/src/lib.rs b/rand_core/src/lib.rs index a10d735c669..3912957771b 100644 --- a/rand_core/src/lib.rs +++ b/rand_core/src/lib.rs @@ -97,9 +97,15 @@ pub mod impls; /// /// PRNGs are usually infallible, while external generators may fail. Since /// errors are rare and may be hard for the user to handle, most of the output -/// functions only allow errors to be reported as panics; byte output can -/// however be retrieved from `try_fill` which allows for the usual error -/// handling. +/// functions do not return a `Result`; byte output can however be retrieved +/// with `try_fill` which allows for the usual error handling. If the random +/// source implements other output functions in terms of `try_fill` (see +/// `impls::fill_via_try_fill`), then some errors will be handled implicitly, +/// so long as not too many retries are needed (specifically: `NotReady` is +/// handled by waiting up to 1 minute, and `Transient` is handled by retrying +/// a few times). In some applications it may make sense to ensure your entropy +/// source (e.g. `OsRng`) is ready by calling `try_fill` explicitly before +/// using any of the other output functions. pub trait Rng { /// Return the next random u32. fn next_u32(&mut self) -> u32; @@ -268,11 +274,6 @@ impl ErrorKind { self == ErrorKind::NotReady } - /// True if we should limit the number of retries before giving up. - pub fn limit_retries(self) -> bool { - self == ErrorKind::Transient - } - /// A description of this error kind pub fn description(self) -> &'static str { match self { From 7f3c9b0cbce498d1cf3048126acd23b135627037 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Sat, 11 Nov 2017 16:25:27 +0100 Subject: [PATCH 175/247] Fix `no_std` errors --- Cargo.toml | 5 ++++- src/jitter_rng.rs | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 5f294b9e38a..0b4c65f614c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,10 @@ i128_support = ["rand_core/i128_support"] [dependencies] libc = "0.2" -rand_core = { path = 'rand_core' } + +[dependencies.rand_core] +path = 'rand_core' +default-features = false [target.'cfg(target_os = "fuchsia")'.dependencies] fuchsia-zircon = "^0.2.1" diff --git a/src/jitter_rng.rs b/src/jitter_rng.rs index 2531cd2ec62..d22700b8e78 100644 --- a/src/jitter_rng.rs +++ b/src/jitter_rng.rs @@ -655,6 +655,7 @@ impl JitterRng { /// # try_main().unwrap(); /// # } /// ``` + #[cfg(feature="std")] pub fn timer_stats(&mut self, var_rounds: bool) -> i64 { let time = get_nstime(); self.memaccess(var_rounds); From d92451afa9c0aa4a5ec2f5cbce90719cd556fa6d Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sat, 11 Nov 2017 15:36:46 +0000 Subject: [PATCH 176/247] JitterRng: reduce error-handling code --- src/jitter_rng.rs | 62 +++++++++++++++-------------------------------- 1 file changed, 19 insertions(+), 43 deletions(-) diff --git a/src/jitter_rng.rs b/src/jitter_rng.rs index d22700b8e78..270a5aa9208 100644 --- a/src/jitter_rng.rs +++ b/src/jitter_rng.rs @@ -16,7 +16,7 @@ //! Non-physical true random number generator based on timing jitter. -use {CryptoRng, Rng, Error}; +use {CryptoRng, Rng, Error, ErrorKind}; use rand_core; use rand_core::impls; @@ -75,19 +75,7 @@ impl fmt::Debug for JitterRng { /// An error that can occur when `test_timer` fails. #[derive(Debug, Clone, PartialEq, Eq)] -pub struct TimerError { - kind: ErrorKind, -} - -impl TimerError { - pub fn kind(&self) -> ErrorKind { - self.kind - } -} - -/// Error kind which can be matched over. -#[derive(PartialEq, Eq, Debug, Copy, Clone)] -pub enum ErrorKind { +pub enum TimerError { /// No timer available. NoTimer, /// Timer too coarse to use as an entropy source. @@ -97,52 +85,40 @@ pub enum ErrorKind { /// Variations of deltas of time too small. TinyVariantions, /// Too many stuck results (indicating no added entropy). - ToManyStuck, + TooManyStuck, #[doc(hidden)] __Nonexhaustive, } -impl ErrorKind { +impl TimerError { fn description(&self) -> &'static str { match *self { - ErrorKind::NoTimer => "no timer available", - ErrorKind::CoarseTimer => "coarse timer", - ErrorKind::NotMonotonic => "timer not monotonic", - ErrorKind::TinyVariantions => "time delta variations too small", - ErrorKind::ToManyStuck => "too many stuck results", - ErrorKind::__Nonexhaustive => unreachable!(), + TimerError::NoTimer => "no timer available", + TimerError::CoarseTimer => "coarse timer", + TimerError::NotMonotonic => "timer not monotonic", + TimerError::TinyVariantions => "time delta variations too small", + TimerError::TooManyStuck => "too many stuck results", + TimerError::__Nonexhaustive => unreachable!(), } } } impl fmt::Display for TimerError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self.kind { - ErrorKind::NoTimer => - write!(f, "No timer available."), - ErrorKind::CoarseTimer => - write!(f, "Timer too coarse to use as an entropy source."), - ErrorKind::NotMonotonic => - write!(f, "Timer is not monotonically increasing."), - ErrorKind::TinyVariantions => - write!(f, "Variations of deltas of time too small."), - ErrorKind::ToManyStuck => - write!(f, "Too many stuck results (indicating no added entropy)."), - ErrorKind::__Nonexhaustive => unreachable!(), - } + write!(f, "{}", self.description()) } } #[cfg(feature="std")] impl ::std::error::Error for TimerError { fn description(&self) -> &str { - self.kind.description() + self.description() } } impl From for Error { fn from(err: TimerError) -> Error { - Error::new_with_cause(rand_core::ErrorKind::Unavailable, + Error::new_with_cause(ErrorKind::Unavailable, "timer jitter failed basic quality tests", err) } } @@ -470,7 +446,7 @@ impl JitterRng { // Test whether timer works if time == 0 || time2 == 0 { - return Err(TimerError { kind: ErrorKind::NoTimer }); + return Err(TimerError::NoTimer); } let delta = time2.wrapping_sub(time) as i64; @@ -478,7 +454,7 @@ impl JitterRng { // when called shortly after each other -- this implies that we also // have a high resolution timer if delta == 0 { - return Err(TimerError { kind: ErrorKind::CoarseTimer }); + return Err(TimerError::CoarseTimer); } // Up to here we did not modify any variable that will be @@ -510,7 +486,7 @@ impl JitterRng { // should not fail. The value of 3 should cover the NTP case being // performed during our test run. if time_backwards > 3 { - return Err(TimerError { kind: ErrorKind::NotMonotonic }); + return Err(TimerError::NotMonotonic); } // Test that the available amount of entropy per round does not get to @@ -521,20 +497,20 @@ impl JitterRng { // `assert!(delta_sum / TESTLOOPCOUNT >= 1)` // `assert!(delta_sum >= TESTLOOPCOUNT)` if delta_sum < TESTLOOPCOUNT { - return Err(TimerError { kind: ErrorKind::TinyVariantions }); + return Err(TimerError::TinyVariantions); } // Ensure that we have variations in the time stamp below 100 for at // least 10% of all checks -- on some platforms, the counter increments // in multiples of 100, but not always if count_mod > (TESTLOOPCOUNT/10 * 9) { - return Err(TimerError { kind: ErrorKind::CoarseTimer }); + return Err(TimerError::CoarseTimer); } // If we have more than 90% stuck results, then this Jitter RNG is // likely to not work well. if count_stuck > (TESTLOOPCOUNT/10 * 9) { - return Err(TimerError { kind: ErrorKind::ToManyStuck }); + return Err(TimerError::TooManyStuck); } // Estimate the number of `measure_jitter` rounds necessary for 64 bits From 14136e785d8339ba493d43ad9375eac0ae4183b5 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Sat, 11 Nov 2017 16:25:27 +0100 Subject: [PATCH 177/247] Fix `no_std` errors --- Cargo.toml | 7 +++++-- benches/generators.rs | 2 +- src/jitter_rng.rs | 1 + 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5f294b9e38a..dc5da6bc5a7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,12 +16,15 @@ categories = ["algorithms"] [features] default = ["std"] nightly = ["i128_support"] -std = [] +std = ["rand_core/std"] i128_support = ["rand_core/i128_support"] [dependencies] libc = "0.2" -rand_core = { path = 'rand_core' } + +[dependencies.rand_core] +path = 'rand_core' +default-features = false [target.'cfg(target_os = "fuchsia")'.dependencies] fuchsia-zircon = "^0.2.1" diff --git a/benches/generators.rs b/benches/generators.rs index 568b2f5900a..67309f9b5b7 100644 --- a/benches/generators.rs +++ b/benches/generators.rs @@ -79,7 +79,7 @@ macro_rules! init_gen { ($fnn:ident, $gen:ident) => { #[bench] fn $fnn(b: &mut Bencher) { - let mut rng = OsRng::new().unwrap(); + let mut rng = XorShiftRng::new().unwrap(); b.iter(|| { black_box($gen::from_rng(&mut rng).unwrap()); }); diff --git a/src/jitter_rng.rs b/src/jitter_rng.rs index 2531cd2ec62..d22700b8e78 100644 --- a/src/jitter_rng.rs +++ b/src/jitter_rng.rs @@ -655,6 +655,7 @@ impl JitterRng { /// # try_main().unwrap(); /// # } /// ``` + #[cfg(feature="std")] pub fn timer_stats(&mut self, var_rounds: bool) -> i64 { let time = get_nstime(); self.memaccess(var_rounds); From 8971695c72079e505b6a3de715f9dd71ae2a54a4 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sat, 11 Nov 2017 15:49:50 +0000 Subject: [PATCH 178/247] Travis: test on a bare-metal target (for no_std testing) --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 6b1b1252def..ef12f1c6f15 100644 --- a/.travis.yml +++ b/.travis.yml @@ -70,7 +70,7 @@ matrix: # Bare metal # These targets don't support std and as such are likely not suitable for # most crates. - # - env: TARGET=thumbv6m-none-eabi + - env: TARGET=thumbv6m-none-eabi # - env: TARGET=thumbv7em-none-eabi # - env: TARGET=thumbv7em-none-eabihf # - env: TARGET=thumbv7m-none-eabi From 6701189835e05405a44b283f2bd2d201f695d88f Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sat, 11 Nov 2017 15:48:02 +0000 Subject: [PATCH 179/247] Crate dependencies: set default-features = false This is required for proper no-std support --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5f294b9e38a..9a0c1bb08be 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,8 +20,8 @@ std = [] i128_support = ["rand_core/i128_support"] [dependencies] -libc = "0.2" -rand_core = { path = 'rand_core' } +libc = { version = "0.2", default-features = false } +rand_core = { path = 'rand_core', default-features = false } [target.'cfg(target_os = "fuchsia")'.dependencies] fuchsia-zircon = "^0.2.1" From 2486226cb322dc391278ff3ca7deadee92d06cc6 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Sat, 11 Nov 2017 20:55:03 +0100 Subject: [PATCH 180/247] Include ziggurat_tables.py Taken from rust/src/etc/ziggurat_tables.py --- src/distributions/ziggurat_tables.py | 127 +++++++++++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100755 src/distributions/ziggurat_tables.py diff --git a/src/distributions/ziggurat_tables.py b/src/distributions/ziggurat_tables.py new file mode 100755 index 00000000000..762f9565b78 --- /dev/null +++ b/src/distributions/ziggurat_tables.py @@ -0,0 +1,127 @@ +#!/usr/bin/env python +# +# Copyright 2013 The Rust Project Developers. See the COPYRIGHT +# file at the top-level directory of this distribution and at +# http://rust-lang.org/COPYRIGHT. +# +# Licensed under the Apache License, Version 2.0 or the MIT license +# , at your +# option. This file may not be copied, modified, or distributed +# except according to those terms. + +# This creates the tables used for distributions implemented using the +# ziggurat algorithm in `rand::distributions;`. They are +# (basically) the tables as used in the ZIGNOR variant (Doornik 2005). +# They are changed rarely, so the generated file should be checked in +# to git. +# +# It creates 3 tables: X as in the paper, F which is f(x_i), and +# F_DIFF which is f(x_i) - f(x_{i-1}). The latter two are just cached +# values which is not done in that paper (but is done in other +# variants). Note that the adZigR table is unnecessary because of +# algebra. +# +# It is designed to be compatible with Python 2 and 3. + +from math import exp, sqrt, log, floor +import random + +# The order should match the return value of `tables` +TABLE_NAMES = ['X', 'F'] + +# The actual length of the table is 1 more, to stop +# index-out-of-bounds errors. This should match the bitwise operation +# to find `i` in `zigurrat` in `libstd/rand/mod.rs`. Also the *_R and +# *_V constants below depend on this value. +TABLE_LEN = 256 + +# equivalent to `zigNorInit` in Doornik2005, but generalised to any +# distribution. r = dR, v = dV, f = probability density function, +# f_inv = inverse of f +def tables(r, v, f, f_inv): + # compute the x_i + xvec = [0]*(TABLE_LEN+1) + + xvec[0] = v / f(r) + xvec[1] = r + + for i in range(2, TABLE_LEN): + last = xvec[i-1] + xvec[i] = f_inv(v / last + f(last)) + + # cache the f's + fvec = [0]*(TABLE_LEN+1) + for i in range(TABLE_LEN+1): + fvec[i] = f(xvec[i]) + + return xvec, fvec + +# Distributions +# N(0, 1) +def norm_f(x): + return exp(-x*x/2.0) +def norm_f_inv(y): + return sqrt(-2.0*log(y)) + +NORM_R = 3.6541528853610088 +NORM_V = 0.00492867323399 + +NORM = tables(NORM_R, NORM_V, + norm_f, norm_f_inv) + +# Exp(1) +def exp_f(x): + return exp(-x) +def exp_f_inv(y): + return -log(y) + +EXP_R = 7.69711747013104972 +EXP_V = 0.0039496598225815571993 + +EXP = tables(EXP_R, EXP_V, + exp_f, exp_f_inv) + + +# Output the tables/constants/types + +def render_static(name, type, value): + # no space or + return 'pub static %s: %s =%s;\n' % (name, type, value) + +# static `name`: [`type`, .. `len(values)`] = +# [values[0], ..., values[3], +# values[4], ..., values[7], +# ... ]; +def render_table(name, values): + rows = [] + # 4 values on each row + for i in range(0, len(values), 4): + row = values[i:i+4] + rows.append(', '.join('%.18f' % f for f in row)) + + rendered = '\n [%s]' % ',\n '.join(rows) + return render_static(name, '[f64, .. %d]' % len(values), rendered) + + +with open('ziggurat_tables.rs', 'w') as f: + f.write('''// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Tables for distributions which are sampled using the ziggurat +// algorithm. Autogenerated by `ziggurat_tables.py`. + +pub type ZigTable = &\'static [f64, .. %d]; +''' % (TABLE_LEN + 1)) + for name, tables, r in [('NORM', NORM, NORM_R), + ('EXP', EXP, EXP_R)]: + f.write(render_static('ZIG_%s_R' % name, 'f64', ' %.18f' % r)) + for (tabname, table) in zip(TABLE_NAMES, tables): + f.write(render_table('ZIG_%s_%s' % (name, tabname), table)) From 35f0cd7ea3345e16bcf920963ccaa92e286bdc4d Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 13 Nov 2017 12:31:43 +0000 Subject: [PATCH 181/247] Cargo: make std feature optional and transitive --- Cargo.toml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 9a0c1bb08be..3929602aaae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,10 +16,14 @@ categories = ["algorithms"] [features] default = ["std"] nightly = ["i128_support"] -std = [] + i128_support = ["rand_core/i128_support"] +# Deps should use 'std' if we do: +std = ["rand_core/std", "libc/use_std"] + [dependencies] +# All deps must not use 'std' by default libc = { version = "0.2", default-features = false } rand_core = { path = 'rand_core', default-features = false } From 32ea1d2fc7f70d5d92c77f68e6bb5b9bd101020d Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 13 Nov 2017 12:39:24 +0000 Subject: [PATCH 182/247] Travis: adjust build targets --- .travis.yml | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/.travis.yml b/.travis.yml index ef12f1c6f15..5db7a4aa4e2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,16 +6,15 @@ language: rust services: docker sudo: required -# TODO Rust builds on stable by default, this can be -# overridden on a case by case basis down below. - env: global: - CRATE_NAME=rand matrix: - # TODO These are all the build jobs. Adjust as necessary. Comment out what you - # don't need + # All available targets (at time script was copied) listed + # Test on: common platforms, plus: + # + big endian (mips) 32- and 64-bit + # + no_std (thumbv7) include: # Android # - env: TARGET=aarch64-linux-android DISABLE_TESTS=1 @@ -54,15 +53,15 @@ matrix: # - env: TARGET=x86_64-unknown-linux-musl # OSX - - env: TARGET=i686-apple-darwin - os: osx + # - env: TARGET=i686-apple-darwin + # os: osx - env: TARGET=x86_64-apple-darwin os: osx # *BSD - env: TARGET=i686-unknown-freebsd DISABLE_TESTS=1 - env: TARGET=x86_64-unknown-freebsd DISABLE_TESTS=1 - - env: TARGET=x86_64-unknown-netbsd DISABLE_TESTS=1 + # - env: TARGET=x86_64-unknown-netbsd DISABLE_TESTS=1 # Windows # - env: TARGET=x86_64-pc-windows-gnu @@ -70,8 +69,8 @@ matrix: # Bare metal # These targets don't support std and as such are likely not suitable for # most crates. - - env: TARGET=thumbv6m-none-eabi - # - env: TARGET=thumbv7em-none-eabi + # - env: TARGET=thumbv6m-none-eabi + - env: TARGET=thumbv7em-none-eabi # - env: TARGET=thumbv7em-none-eabihf # - env: TARGET=thumbv7m-none-eabi From 026b50ed5c5baab0edd4d78631119bfd2395b77e Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 13 Nov 2017 12:39:53 +0000 Subject: [PATCH 183/247] OsRng: squelch warnings about ununsed code on some targets --- src/os.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/os.rs b/src/os.rs index cd0614927e8..e2a60a4be0a 100644 --- a/src/os.rs +++ b/src/os.rs @@ -36,6 +36,7 @@ use {Rng, Error, ErrorKind}; /// /// [1] See for a more /// in-depth discussion. +#[allow(unused)] // not used by all targets pub struct OsRng(imp::OsRng); impl fmt::Debug for OsRng { @@ -81,6 +82,7 @@ impl Rng for OsRng { struct ReadRng (R); impl ReadRng { + #[allow(unused)] // not used by all targets fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { if dest.len() == 0 { return Ok(()); } // Use `std::io::read_exact`, which retries on `ErrorKind::Interrupted`. From c74cb3fa5e268aea94176d3572377b0a2ad2c951 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 13 Nov 2017 12:50:32 +0000 Subject: [PATCH 184/247] Travis: fix build script for no_std --- .travis.yml | 8 ++++---- ci/script.sh | 6 +++++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5db7a4aa4e2..288f4924cbc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -69,10 +69,10 @@ matrix: # Bare metal # These targets don't support std and as such are likely not suitable for # most crates. - # - env: TARGET=thumbv6m-none-eabi - - env: TARGET=thumbv7em-none-eabi - # - env: TARGET=thumbv7em-none-eabihf - # - env: TARGET=thumbv7m-none-eabi + # - env: TARGET=thumbv6m-none-eabi DISABLE_STD=1 + - env: TARGET=thumbv7em-none-eabi DISABLE_STD=1 + # - env: TARGET=thumbv7em-none-eabihf DISABLE_STD=1 + # - env: TARGET=thumbv7m-none-eabi DISABLE_STD=1 # Testing other channels - env: TARGET=x86_64-unknown-linux-gnu NIGHTLY=1 diff --git a/ci/script.sh b/ci/script.sh index 6a80436aeaf..c41788e168e 100644 --- a/ci/script.sh +++ b/ci/script.sh @@ -4,8 +4,12 @@ set -ex # TODO This is the "test phase", tweak it as you see fit main() { - cross build --target $TARGET cross build --all --no-default-features --target $TARGET --release + if [ ! -z $DISABLE_STD ]; then + return + fi + + cross build --target $TARGET if [ ! -z $NIGHTLY ]; then cross doc --no-deps --features nightly fi From 065baa77d7cb8d345653c3ae6c8332b2c82d0493 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 13 Nov 2017 16:45:52 +0000 Subject: [PATCH 185/247] Move ziggurat_tables.py out of source tree --- {src/distributions => utils}/ziggurat_tables.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {src/distributions => utils}/ziggurat_tables.py (100%) diff --git a/src/distributions/ziggurat_tables.py b/utils/ziggurat_tables.py similarity index 100% rename from src/distributions/ziggurat_tables.py rename to utils/ziggurat_tables.py From 82e18c6e75029ad76414205266d13b940502ab1c Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 13 Nov 2017 16:56:26 +0000 Subject: [PATCH 186/247] Enable all thumb* targets We probably don't want all enabled; this is just to convince GH the branch isn't fully merged --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 288f4924cbc..a3efeffc26a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -69,10 +69,10 @@ matrix: # Bare metal # These targets don't support std and as such are likely not suitable for # most crates. - # - env: TARGET=thumbv6m-none-eabi DISABLE_STD=1 + - env: TARGET=thumbv6m-none-eabi DISABLE_STD=1 - env: TARGET=thumbv7em-none-eabi DISABLE_STD=1 - # - env: TARGET=thumbv7em-none-eabihf DISABLE_STD=1 - # - env: TARGET=thumbv7m-none-eabi DISABLE_STD=1 + - env: TARGET=thumbv7em-none-eabihf DISABLE_STD=1 + - env: TARGET=thumbv7m-none-eabi DISABLE_STD=1 # Testing other channels - env: TARGET=x86_64-unknown-linux-gnu NIGHTLY=1 From 137fe01b11853ab424df2270261b920451c81b90 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 13 Nov 2017 17:07:24 +0000 Subject: [PATCH 187/247] Use nightly for bare-metal targets https://github.com/japaric/cross/issues/152 --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index a3efeffc26a..8804c706e59 100644 --- a/.travis.yml +++ b/.travis.yml @@ -70,9 +70,13 @@ matrix: # These targets don't support std and as such are likely not suitable for # most crates. - env: TARGET=thumbv6m-none-eabi DISABLE_STD=1 + rust: nightly - env: TARGET=thumbv7em-none-eabi DISABLE_STD=1 + rust: nightly - env: TARGET=thumbv7em-none-eabihf DISABLE_STD=1 + rust: nightly - env: TARGET=thumbv7m-none-eabi DISABLE_STD=1 + rust: nightly # Testing other channels - env: TARGET=x86_64-unknown-linux-gnu NIGHTLY=1 From afce69d23283fcb50d1ba120a9ecb90823067759 Mon Sep 17 00:00:00 2001 From: Vinzent Steinberg Date: Tue, 14 Nov 2017 18:12:49 +0100 Subject: [PATCH 188/247] Fix link to documentaion It linked to `rand` instead of `rand_core`. --- rand_core/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rand_core/README.md b/rand_core/README.md index 1a354e62e9a..b62329696db 100644 --- a/rand_core/README.md +++ b/rand_core/README.md @@ -7,7 +7,7 @@ This crate contains the core trait, `Rng`, and some extension traits. This should be sufficient for libraries publishing an RNG type, but most users should prefer to use the [rand] crate. -[Documentation](https://docs.rs/rand) +[Documentation](https://docs.rs/rand_core) ## Status From 4fff5afa109668c33108f16bcd7f72cb976b7715 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 14 Nov 2017 18:42:12 +0000 Subject: [PATCH 189/247] Make libc dep optional (use with std only) --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3929602aaae..1cef95f5e61 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,11 +20,11 @@ nightly = ["i128_support"] i128_support = ["rand_core/i128_support"] # Deps should use 'std' if we do: -std = ["rand_core/std", "libc/use_std"] +std = ["rand_core/std", "libc"] [dependencies] +libc = { version = "0.2", optional = true } # All deps must not use 'std' by default -libc = { version = "0.2", default-features = false } rand_core = { path = 'rand_core', default-features = false } [target.'cfg(target_os = "fuchsia")'.dependencies] From 1e84e50290e96ee210795a580c23ed9efddcded1 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Wed, 15 Nov 2017 12:44:39 +0000 Subject: [PATCH 190/247] Add cat_rng tool --- examples/cat_rng.rs | 67 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 examples/cat_rng.rs diff --git a/examples/cat_rng.rs b/examples/cat_rng.rs new file mode 100644 index 00000000000..97dd41c93b9 --- /dev/null +++ b/examples/cat_rng.rs @@ -0,0 +1,67 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! A small utility to concatenate the output of an RNG to stdout. + +extern crate rand; + +use rand::{Rng, NewSeeded, OsRng}; +use rand::prng::{XorShiftRng, IsaacRng, Isaac64Rng, ChaChaRng}; +use std::collections::HashMap; +use std::env; +use std::io::{self, Write, Error}; +use std::iter::Iterator; + +fn print_usage(cmd: &String, names: Vec) { + println!("Usage: {} RNG +where RNG is one of: {:?} + +This is a small tool to endlessly contatenate output from an RNG. It can for +example be used with PractRand: ./cat_rng chacha | ./RNG_test stdin", + cmd, names); +} + +type BR = Box; + +fn main() { + let mut ctors: HashMap<&'static str, + Box Result>> = HashMap::new(); + ctors.insert("os", Box::new(|| OsRng::new().map(|rng| Box::new(rng) as BR))); + + ctors.insert("xorshift", Box::new(|| XorShiftRng::new().map(|rng| Box::new(rng) as BR))); + ctors.insert("isaac", Box::new(|| IsaacRng::new().map(|rng| Box::new(rng) as BR))); + ctors.insert("isaac64", Box::new(|| Isaac64Rng::new().map(|rng| Box::new(rng) as BR))); + ctors.insert("chacha", Box::new(|| ChaChaRng::new().map(|rng| Box::new(rng) as BR))); + + let args: Vec = env::args().collect(); + if args.len() != 2 { + print_usage(&args[0], ctors.keys().map(|s| String::from(*s)).collect()); + } else { + if let Some(ctor) = ctors.get(&*args[1]) { + let rng = ctor().unwrap(); + cat_rng(rng).unwrap(); + } else { + println!("Error: unknown RNG: {}", args[1]); + println!(); + print_usage(&args[0], ctors.keys().map(|s| String::from(*s)).collect()); + } + } +} + +fn cat_rng(mut rng: Box) -> Result<(), Error> { + let mut buf = [0u8; 32]; + let stdout = io::stdout(); + let mut lock = stdout.lock(); + + loop { + rng.fill_bytes(&mut buf); + lock.write(&buf)?; + } +} From 38e5d8d89578d9fb23ed0be43fdd81a834d12841 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 14 Nov 2017 12:14:55 +0000 Subject: [PATCH 191/247] JitterRng: reduce FACTOR to 3 to avoid overflow Rationale: have observed delta_average high enough to overflow with FACTOR=4 (i.e. over 65536); overflow with FACTOR=3 would require 40-times higher which seems unlikely. FACTOR=3 appears to provide enough precision. --- src/jitter_rng.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/jitter_rng.rs b/src/jitter_rng.rs index 270a5aa9208..776fd17be30 100644 --- a/src/jitter_rng.rs +++ b/src/jitter_rng.rs @@ -503,13 +503,13 @@ impl JitterRng { // Ensure that we have variations in the time stamp below 100 for at // least 10% of all checks -- on some platforms, the counter increments // in multiples of 100, but not always - if count_mod > (TESTLOOPCOUNT/10 * 9) { + if count_mod > (TESTLOOPCOUNT * 9 / 10) { return Err(TimerError::CoarseTimer); } // If we have more than 90% stuck results, then this Jitter RNG is // likely to not work well. - if count_stuck > (TESTLOOPCOUNT/10 * 9) { + if count_stuck > (TESTLOOPCOUNT * 9 / 10) { return Err(TimerError::TooManyStuck); } @@ -519,12 +519,12 @@ impl JitterRng { // We don't try very hard to come up with a good estimate of the // available bits of entropy per round here for two reasons: // 1. Simple estimates of the available bits (like Shannon entropy) are - // to optimistic. + // too optimistic. // 2) Unless we want to waste a lot of time during intialization, there - // is only a small amount of samples available. + // only a small number of samples are available. // // Therefore we use a very simple and conservative estimate: - // `let bits_of_entropy = log2(delta_average / TESTLOOPCOUNT) / 2`. + // `let bits_of_entropy = log2(delta_average) / 2`. // // The number of rounds `measure_jitter` should run to collect 64 bits // of entropy is `64 / bits_of_entropy`. @@ -533,11 +533,13 @@ impl JitterRng { // by `FACTOR`. To compensate for `log2` and division rounding down, // add 1. let delta_average = delta_sum / TESTLOOPCOUNT; + // println!("delta_average: {}", delta_average); - const FACTOR: u32 = 5; + const FACTOR: u32 = 3; fn log2(x: u64) -> u32 { 64 - x.leading_zeros() as u32 } - - Ok((64 * 2 * FACTOR / log2(delta_average.pow(FACTOR)) + 1) as u16) + + // pow(δ, FACTOR) must be representable; if you have overflow reduce FACTOR + Ok((64 * 2 * FACTOR / (log2(delta_average.pow(FACTOR)) + 1)) as u16) } /// Statistical test: return the timer delta of one normal run of the From aafcba86b93756af96b7a608cc3bbf12cd0c969a Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Wed, 15 Nov 2017 12:30:05 +0000 Subject: [PATCH 192/247] JitterRng: allow users to set number of rounds; cache timer test result --- src/jitter_rng.rs | 38 +++++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/src/jitter_rng.rs b/src/jitter_rng.rs index 776fd17be30..344b0efdebf 100644 --- a/src/jitter_rng.rs +++ b/src/jitter_rng.rs @@ -22,6 +22,7 @@ use rand_core::impls; use core; use core::fmt; +use core::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering}; const MEMORY_BLOCKS: usize = 64; const MEMORY_BLOCKSIZE: usize = 32; @@ -52,7 +53,7 @@ const MEMORY_SIZE: usize = MEMORY_BLOCKS * MEMORY_BLOCKSIZE; pub struct JitterRng { data: u64, // Actual random number // Number of rounds to run the entropy collector per 64 bits - rounds: u16, + rounds: u32, // Timer and previous time stamp, used by `measure_jitter` timer: fn() -> u64, prev_time: u64, @@ -123,17 +124,27 @@ impl From for Error { } } +// Initialise to zero; must be positive +static JITTER_ROUNDS: AtomicUsize = ATOMIC_USIZE_INIT; + impl JitterRng { /// Create a new `JitterRng`. /// Makes use of `std::time` for a timer. /// /// During initialization CPU execution timing jitter is measured a few /// hundred times. If this does not pass basic quality tests, an error is - /// returned. + /// returned. The test result is cached to make subsequent calls faster. #[cfg(feature="std")] pub fn new() -> Result { let mut ec = JitterRng::new_with_timer(get_nstime); - ec.rounds = ec.test_timer()?; + let mut rounds = JITTER_ROUNDS.load(Ordering::Relaxed) as u32; + if rounds == 0 { + // No result yet: run test. + // This allows the timer test to run multiple times; we don't care. + rounds = ec.test_timer()?; + JITTER_ROUNDS.store(rounds as usize, Ordering::Relaxed); + } + ec.set_rounds(rounds); Ok(ec) } @@ -145,7 +156,7 @@ impl JitterRng { /// /// This method is more low-level than `new()`. It is the responsibility of /// the caller to run `test_timer` before using any numbers generated with - /// `JitterRng`. + /// `JitterRng`, and optionally call `set_rounds()`. pub fn new_with_timer(timer: fn() -> u64) -> JitterRng { let mut ec = JitterRng { data: 0, @@ -172,6 +183,19 @@ impl JitterRng { ec } + + /// Configures how many rounds are used to generate each 64-bit value. + /// This must be greater than zero, and has a big impact on performance + /// and output quality. + /// + /// `new_with_timer` conservatively uses 64 rounds, but often less rounds + /// can be used. The `test_timer()` function returns the minimum number of + /// rounds required for full strength (platform dependent), so one may use + /// `rng.set_rounds(rng.test_timer()?);` or cache the value. + pub fn set_rounds(&mut self, rounds: u32) { + assert!(rounds > 0); + self.rounds = rounds; + } // Calculate a random loop count used for the next round of an entropy // collection, based on bits from a fresh value from the timer. @@ -420,7 +444,7 @@ impl JitterRng { /// If succesful, this will return the estimated number of rounds necessary /// to collect 64 bits of entropy. Otherwise a `TimerError` with the cause /// of the failure will be returned. - pub fn test_timer(&mut self) -> Result { + pub fn test_timer(&mut self) -> Result { // We could add a check for system capabilities such as `clock_getres` // or check for `CONFIG_X86_TSC`, but it does not make much sense as the // following sanity checks verify that we have a high-resolution timer. @@ -536,10 +560,10 @@ impl JitterRng { // println!("delta_average: {}", delta_average); const FACTOR: u32 = 3; - fn log2(x: u64) -> u32 { 64 - x.leading_zeros() as u32 } + fn log2(x: u64) -> u32 { 64 - x.leading_zeros() } // pow(δ, FACTOR) must be representable; if you have overflow reduce FACTOR - Ok((64 * 2 * FACTOR / (log2(delta_average.pow(FACTOR)) + 1)) as u16) + Ok(64 * 2 * FACTOR / (log2(delta_average.pow(FACTOR)) + 1)) } /// Statistical test: return the timer delta of one normal run of the From 9d8021bde89f91ba10c218f1ee63bad11c3ba3bc Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Fri, 17 Nov 2017 12:44:18 +0000 Subject: [PATCH 193/247] JitterRng: fix benchmark --- benches/generators.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benches/generators.rs b/benches/generators.rs index 67309f9b5b7..5959ad839b9 100644 --- a/benches/generators.rs +++ b/benches/generators.rs @@ -72,7 +72,7 @@ fn gen_u64_jitter(b: &mut Bencher) { b.iter(|| { black_box(rng.gen::()); }); - b.bytes = size_of::() as u64 * RAND_BENCH_N; + b.bytes = size_of::() as u64; } macro_rules! init_gen { From b4746c886f61a45da6607f2ac8c8d7f8c3b6d64a Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Fri, 17 Nov 2017 13:14:52 +0000 Subject: [PATCH 194/247] NewSeeded: use JitterRng fallback Also restrict to prevent user implementation --- src/lib.rs | 51 +++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 41 insertions(+), 10 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 68ce87e7c92..b1353ff6b09 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -286,24 +286,55 @@ mod read; #[cfg(feature="std")] mod thread_local; -/// Support mechanism for creating securely seeded objects -/// using the OS generator. -/// Intended for use by RNGs, but not restricted to these. +/// Seeding mechanism for PRNGs, providing a `new` function. +/// This is the recommended way to create (pseudo) random number generators, +/// unless a deterministic seed is desired (in which case the `SeedableRng` +/// trait should be used directly). /// -/// This is implemented automatically for any PRNG implementing `SeedFromRng`, -/// and for normal types shouldn't be implemented directly. For mock generators -/// it may be useful to implement this instead of `SeedFromRng`. +/// Note: this trait is automatically implemented for any PRNG implementing +/// `SeedFromRng` and is not intended to be implemented by users. +/// +/// ## Example +/// +/// ``` +/// use rand::{StdRng, Sample, NewSeeded}; +/// +/// let mut rng = StdRng::new().unwrap(); +/// println!("Random die roll: {}", rng.gen_range(1, 7)); +/// ``` #[cfg(feature="std")] -pub trait NewSeeded: Sized { - /// Creates a new instance, automatically seeded via `OsRng`. +pub trait NewSeeded: SeedFromRng { + /// Creates a new instance, automatically seeded with fresh entropy. + /// + /// Normally this will use `OsRng`, but if that fails `JitterRng` will be + /// used instead. Both should be suitable for cryptography. It is possible + /// that both entropy sources will fail though unlikely. fn new() -> Result; } #[cfg(feature="std")] impl NewSeeded for R { fn new() -> Result { - let mut r = OsRng::new()?; - Self::from_rng(&mut r) + // Note: error handling would be easier with try/catch blocks + fn new_os() -> Result { + let mut r = OsRng::new()?; + T::from_rng(&mut r) + } + fn new_jitter() -> Result { + let mut r = JitterRng::new()?; + T::from_rng(&mut r) + } + + new_os().or_else(|e1| { + new_jitter().map_err(|_e2| { + // TODO: log + // TODO: can we somehow return both error sources? + Error::new_with_cause( + ErrorKind::Unavailable, + "seeding a new RNG failed: both OS and Jitter entropy sources failed", + e1) + }) + }) } } From bd4c7938f4a4e7389c9a530799d66715a4a53845 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Fri, 17 Nov 2017 17:49:56 +0000 Subject: [PATCH 195/247] CI: move ci to utils/ci --- .travis.yml | 8 ++++---- {ci => utils/ci}/install.sh | 0 {ci => utils/ci}/script.sh | 0 3 files changed, 4 insertions(+), 4 deletions(-) rename {ci => utils/ci}/install.sh (100%) rename {ci => utils/ci}/script.sh (100%) diff --git a/.travis.yml b/.travis.yml index 3ad158c1552..6721fa72335 100644 --- a/.travis.yml +++ b/.travis.yml @@ -86,16 +86,16 @@ before_install: - rustup self update install: - - sh ci/install.sh + - sh utils/ci/install.sh - source ~/.cargo/env || true script: - - bash ci/script.sh + - bash utils/ci/script.sh after_script: set +e -before_deploy: - - sh ci/before_deploy.sh +#before_deploy: +# - sh utils/ci/before_deploy.sh cache: cargo before_cache: diff --git a/ci/install.sh b/utils/ci/install.sh similarity index 100% rename from ci/install.sh rename to utils/ci/install.sh diff --git a/ci/script.sh b/utils/ci/script.sh similarity index 100% rename from ci/script.sh rename to utils/ci/script.sh From cf211f9f8acba824eb0760d609d9084870d2c438 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Fri, 17 Nov 2017 17:53:28 +0000 Subject: [PATCH 196/247] CI: show status of my branch in readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 51441ac9421..8f34a76ef1f 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,8 @@ rand A Rust library for random number generators and other randomness functionality. -[![Build Status](https://travis-ci.org/rust-lang-nursery/rand.svg?branch=master)](https://travis-ci.org/rust-lang-nursery/rand) -[![Build status](https://ci.appveyor.com/api/projects/status/rm5c9o33k3jhchbw?svg=true)](https://ci.appveyor.com/project/alexcrichton/rand) +[![Build Status](https://travis-ci.org/dhardy/rand.svg?branch=master)](https://travis-ci.org/dhardy/rand) + [Documentation](https://docs.rs/rand) From 28213284421c855ca7c45c99389cf35728a6f66a Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Fri, 17 Nov 2017 18:05:04 +0000 Subject: [PATCH 197/247] Cargo: don't use libc at all if no_std --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3929602aaae..8290d9e5c22 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,11 +20,11 @@ nightly = ["i128_support"] i128_support = ["rand_core/i128_support"] # Deps should use 'std' if we do: -std = ["rand_core/std", "libc/use_std"] +std = ["rand_core/std", "libc"] [dependencies] # All deps must not use 'std' by default -libc = { version = "0.2", default-features = false } +libc = { version = "0.2", optional = true } rand_core = { path = 'rand_core', default-features = false } [target.'cfg(target_os = "fuchsia")'.dependencies] From 53ab77e320184b0950dfbdd44439fd44bdb57700 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Fri, 17 Nov 2017 18:05:32 +0000 Subject: [PATCH 198/247] JitterRng: remove unused stuff for no_std --- src/jitter_rng.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/jitter_rng.rs b/src/jitter_rng.rs index 344b0efdebf..64ab67732c5 100644 --- a/src/jitter_rng.rs +++ b/src/jitter_rng.rs @@ -22,7 +22,9 @@ use rand_core::impls; use core; use core::fmt; -use core::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering}; + +#[cfg(feature="std")] +use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering}; const MEMORY_BLOCKS: usize = 64; const MEMORY_BLOCKSIZE: usize = 32; @@ -125,6 +127,7 @@ impl From for Error { } // Initialise to zero; must be positive +#[cfg(feature="std")] static JITTER_ROUNDS: AtomicUsize = ATOMIC_USIZE_INIT; impl JitterRng { From 8be4f63d211a76781f85067511589f80691babed Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Fri, 17 Nov 2017 18:12:02 +0000 Subject: [PATCH 199/247] Cargo: do not use std by default in rand_core This is to work around what I'm pretty sure is a Cargo bug, and is causing build failures with no_std builds now. --- rand_core/Cargo.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rand_core/Cargo.toml b/rand_core/Cargo.toml index 8d14f405f9b..c87a39e51dc 100644 --- a/rand_core/Cargo.toml +++ b/rand_core/Cargo.toml @@ -12,7 +12,8 @@ license = "MIT/Apache-2.0" repository = "https://github.com/dhardy/rand/" [features] -default = ["std"] +# Bug: https://github.com/rust-lang/cargo/issues/4361 +# default = ["std"] nightly = ["i128_support"] std = [] i128_support = [] From eca5e3614a23edf32dfa9cd9291eed17708e5c57 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Fri, 17 Nov 2017 19:12:19 +0000 Subject: [PATCH 200/247] JitterRng::timer_stats: do not run example This writes to disk, which fails on CI. --- src/jitter_rng.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/jitter_rng.rs b/src/jitter_rng.rs index 64ab67732c5..78d8e250c37 100644 --- a/src/jitter_rng.rs +++ b/src/jitter_rng.rs @@ -611,7 +611,7 @@ impl JitterRng { /// times. This measures the absolute worst-case, and gives a lower bound /// for the available entropy. /// - /// ```rust + /// ```no_run /// use rand::JitterRng; /// /// # use std::error::Error; @@ -638,7 +638,7 @@ impl JitterRng { /// /// // 1_000_000 results are required for the NIST SP 800-90B Entropy /// // Estimation Suite - /// // FIXME: this number is smaller here, otherwise the Doc-test is to slow + /// // FIXME: this number is smaller here, otherwise the Doc-test is too slow /// const ROUNDS: usize = 10_000; /// let mut deltas_variable: Vec = Vec::with_capacity(ROUNDS); /// let mut deltas_minimal: Vec = Vec::with_capacity(ROUNDS); From e47268213efe8e49f2fb0a3dafded2f125af39bf Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sun, 19 Nov 2017 10:17:02 +0000 Subject: [PATCH 201/247] Workaround #55: don't run nightly tests on darwin --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9ec7cbc232b..3aa652c5400 100644 --- a/.travis.yml +++ b/.travis.yml @@ -78,10 +78,10 @@ matrix: rust: nightly # Testing other channels + # Don't run nightly tests on darwin: JitterRng bench fails due to low-res timer - env: TARGET=x86_64-unknown-linux-gnu NIGHTLY=1 rust: nightly - - env: TARGET=x86_64-apple-darwin NIGHTLY=1 - os: osx + - env: TARGET=i686-unknown-freebsd NIGHTLY=1 rust: nightly before_install: From 442e7a76a033f407f134951381e5354e2fda357f Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 20 Nov 2017 08:22:22 +0000 Subject: [PATCH 202/247] Disable FreeBSD nightly too 'error: An unknown error occurred' --- .travis.yml | 2 -- src/os.rs | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3aa652c5400..9704ea6cfd3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -81,8 +81,6 @@ matrix: # Don't run nightly tests on darwin: JitterRng bench fails due to low-res timer - env: TARGET=x86_64-unknown-linux-gnu NIGHTLY=1 rust: nightly - - env: TARGET=i686-unknown-freebsd NIGHTLY=1 - rust: nightly before_install: - set -e diff --git a/src/os.rs b/src/os.rs index 8af06859062..acd3d0ae036 100644 --- a/src/os.rs +++ b/src/os.rs @@ -293,7 +293,7 @@ mod imp { use {Error, ErrorKind}; - use std::{io, ptr}; + use std::ptr; #[derive(Debug)] pub struct OsRng; From 0ccaa6bea90d72946be8d88fbed584bd7bd64563 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Mon, 20 Nov 2017 21:00:20 +0100 Subject: [PATCH 203/247] Fix `JitterRng` on Mac OS --- .travis.yml | 4 +++- src/jitter_rng.rs | 16 ++++++++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9704ea6cfd3..9ec7cbc232b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -78,9 +78,11 @@ matrix: rust: nightly # Testing other channels - # Don't run nightly tests on darwin: JitterRng bench fails due to low-res timer - env: TARGET=x86_64-unknown-linux-gnu NIGHTLY=1 rust: nightly + - env: TARGET=x86_64-apple-darwin NIGHTLY=1 + os: osx + rust: nightly before_install: - set -e diff --git a/src/jitter_rng.rs b/src/jitter_rng.rs index 78d8e250c37..d36cf79aafe 100644 --- a/src/jitter_rng.rs +++ b/src/jitter_rng.rs @@ -611,7 +611,7 @@ impl JitterRng { /// times. This measures the absolute worst-case, and gives a lower bound /// for the available entropy. /// - /// ```no_run + /// ```rust,no_run /// use rand::JitterRng; /// /// # use std::error::Error; @@ -670,7 +670,7 @@ impl JitterRng { } } -#[cfg(feature="std")] +#[cfg(all(feature="std", not(any(target_os = "macos", target_os = "ios"))))] fn get_nstime() -> u64 { use std::time::{SystemTime, UNIX_EPOCH}; @@ -682,6 +682,18 @@ fn get_nstime() -> u64 { dur.as_secs() << 30 | dur.subsec_nanos() as u64 } +#[cfg(all(feature="std", any(target_os = "macos", target_os = "ios")))] +fn get_nstime() -> u64 { + extern crate libc; + // On Mac OS and iOS std::time::SystemTime only has 1000ns resolution. + // We use `mach_absolute_time` instead. This provides a CPU dependent unit, + // to get real nanoseconds the result should by multiplied by numer/denom + // from `mach_timebase_info`. + // But we are not interested in the exact nanoseconds, just entropy. So we + // use the raw result. + unsafe { libc::mach_absolute_time() } +} + // A function that is opaque to the optimizer to assist in avoiding dead-code // elimination. Taken from `bencher`. fn black_box(dummy: T) -> T { From 17e08deb568de83c89a987299eb0d89becf6c91c Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sun, 19 Nov 2017 10:17:02 +0000 Subject: [PATCH 204/247] Workaround #55: don't run nightly tests on darwin --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9ec7cbc232b..3aa652c5400 100644 --- a/.travis.yml +++ b/.travis.yml @@ -78,10 +78,10 @@ matrix: rust: nightly # Testing other channels + # Don't run nightly tests on darwin: JitterRng bench fails due to low-res timer - env: TARGET=x86_64-unknown-linux-gnu NIGHTLY=1 rust: nightly - - env: TARGET=x86_64-apple-darwin NIGHTLY=1 - os: osx + - env: TARGET=i686-unknown-freebsd NIGHTLY=1 rust: nightly before_install: From 05d04496235234f6e511e44b797ed52b1641e557 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 20 Nov 2017 08:22:22 +0000 Subject: [PATCH 205/247] Disable FreeBSD nightly too 'error: An unknown error occurred' --- .travis.yml | 2 -- src/os.rs | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3aa652c5400..9704ea6cfd3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -81,8 +81,6 @@ matrix: # Don't run nightly tests on darwin: JitterRng bench fails due to low-res timer - env: TARGET=x86_64-unknown-linux-gnu NIGHTLY=1 rust: nightly - - env: TARGET=i686-unknown-freebsd NIGHTLY=1 - rust: nightly before_install: - set -e diff --git a/src/os.rs b/src/os.rs index 8af06859062..acd3d0ae036 100644 --- a/src/os.rs +++ b/src/os.rs @@ -293,7 +293,7 @@ mod imp { use {Error, ErrorKind}; - use std::{io, ptr}; + use std::ptr; #[derive(Debug)] pub struct OsRng; From db99fda6fc480b6468e29df941a371008c311a70 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Mon, 20 Nov 2017 21:00:20 +0100 Subject: [PATCH 206/247] Fix `JitterRng` on Mac OS --- .travis.yml | 4 +++- src/jitter_rng.rs | 16 ++++++++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9704ea6cfd3..9ec7cbc232b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -78,9 +78,11 @@ matrix: rust: nightly # Testing other channels - # Don't run nightly tests on darwin: JitterRng bench fails due to low-res timer - env: TARGET=x86_64-unknown-linux-gnu NIGHTLY=1 rust: nightly + - env: TARGET=x86_64-apple-darwin NIGHTLY=1 + os: osx + rust: nightly before_install: - set -e diff --git a/src/jitter_rng.rs b/src/jitter_rng.rs index 78d8e250c37..d36cf79aafe 100644 --- a/src/jitter_rng.rs +++ b/src/jitter_rng.rs @@ -611,7 +611,7 @@ impl JitterRng { /// times. This measures the absolute worst-case, and gives a lower bound /// for the available entropy. /// - /// ```no_run + /// ```rust,no_run /// use rand::JitterRng; /// /// # use std::error::Error; @@ -670,7 +670,7 @@ impl JitterRng { } } -#[cfg(feature="std")] +#[cfg(all(feature="std", not(any(target_os = "macos", target_os = "ios"))))] fn get_nstime() -> u64 { use std::time::{SystemTime, UNIX_EPOCH}; @@ -682,6 +682,18 @@ fn get_nstime() -> u64 { dur.as_secs() << 30 | dur.subsec_nanos() as u64 } +#[cfg(all(feature="std", any(target_os = "macos", target_os = "ios")))] +fn get_nstime() -> u64 { + extern crate libc; + // On Mac OS and iOS std::time::SystemTime only has 1000ns resolution. + // We use `mach_absolute_time` instead. This provides a CPU dependent unit, + // to get real nanoseconds the result should by multiplied by numer/denom + // from `mach_timebase_info`. + // But we are not interested in the exact nanoseconds, just entropy. So we + // use the raw result. + unsafe { libc::mach_absolute_time() } +} + // A function that is opaque to the optimizer to assist in avoiding dead-code // elimination. Taken from `bencher`. fn black_box(dummy: T) -> T { From cc437de3d083ce80c40454a64458d2300462efb4 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Fri, 24 Nov 2017 15:43:53 +0000 Subject: [PATCH 207/247] MockAddRng: remove templating This becomes difficult to deal with in WIP change --- src/mock.rs | 50 ++++++++++----------------------------- src/reseeding.rs | 8 +++---- src/sequences/weighted.rs | 2 +- 3 files changed, 17 insertions(+), 43 deletions(-) diff --git a/src/mock.rs b/src/mock.rs index e070818a2ef..650cc445d30 100644 --- a/src/mock.rs +++ b/src/mock.rs @@ -26,48 +26,25 @@ use rand_core::impls; /// use rand::Rng; /// use rand::mock::MockAddRng; /// -/// let mut my_rng = MockAddRng::new(2u32, 1u32); -/// assert_eq!(my_rng.next_u32(), 2u32); -/// assert_eq!(my_rng.next_u64(), 3u64 + (4u64 << 32)); +/// let mut my_rng = MockAddRng::new(2, 1); +/// assert_eq!(my_rng.next_u32(), 2); +/// assert_eq!(my_rng.next_u64(), 3); /// ``` #[derive(Debug, Clone)] -pub struct MockAddRng { - v: w, - a: w, +pub struct MockAddRng { + v: w, + a: w, } -impl MockAddRng { +impl MockAddRng { /// Create a `MockAddRng`, yielding an arithmetic sequence starting with /// `v` and incremented by `a` each time. - pub fn new(v: T, a: T) -> Self { + pub fn new(v: u64, a: u64) -> Self { MockAddRng { v: w(v), a: w(a) } } } -impl Rng for MockAddRng { - fn next_u32(&mut self) -> u32 { - let result = self.v.0; - self.v += self.a; - result - } - fn next_u64(&mut self) -> u64 { - impls::next_u64_via_u32(self) - } - #[cfg(feature = "i128_support")] - fn next_u128(&mut self) -> u128 { - impls::next_u128_via_u64(self) - } - - fn fill_bytes(&mut self, dest: &mut [u8]) { - impls::fill_bytes_via_u32(self, dest); - } - - fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { - Ok(self.fill_bytes(dest)) - } -} - -impl Rng for MockAddRng { +impl Rng for MockAddRng { fn next_u32(&mut self) -> u32 { self.next_u64() as u32 } @@ -90,11 +67,8 @@ impl Rng for MockAddRng { } } -impl SeedableRng for MockAddRng where - MockAddRng: Rng, - T: From, // for 1.into() -{ - fn from_seed(seed: T) -> Self { - MockAddRng::new(seed, 1.into()) +impl SeedableRng for MockAddRng { + fn from_seed(seed: u64) -> Self { + MockAddRng::new(seed, 1) } } diff --git a/src/reseeding.rs b/src/reseeding.rs index 0091dae3945..18f7b42768d 100644 --- a/src/reseeding.rs +++ b/src/reseeding.rs @@ -195,14 +195,14 @@ mod test { #[derive(Debug, Clone)] struct ReseedMock; - impl Reseeder> for ReseedMock { - fn reseed(&mut self, rng: &mut MockAddRng) -> Result<(), Error> { + impl Reseeder for ReseedMock { + fn reseed(&mut self, rng: &mut MockAddRng) -> Result<(), Error> { *rng = MockAddRng::new(0, 1); Ok(()) } } - type MyRng = ReseedingRng, ReseedMock>; + type MyRng = ReseedingRng; #[test] fn test_reseeding() { @@ -219,7 +219,7 @@ mod test { fn test_rng_seeded() { // Default seed threshold is way beyond what we use here let mut ra: MyRng = SeedableRng::from_seed((ReseedMock, 2)); - let mut rb: MockAddRng = SeedableRng::from_seed(2); + let mut rb: MockAddRng = SeedableRng::from_seed(2); assert!(::test::iter_eq(iter(&mut ra).map(|rng| rng.next_u32()).take(100), iter(&mut rb).map(|rng| rng.next_u32()).take(100))); } diff --git a/src/sequences/weighted.rs b/src/sequences/weighted.rs index a368990effc..6589fa0a24b 100644 --- a/src/sequences/weighted.rs +++ b/src/sequences/weighted.rs @@ -151,7 +151,7 @@ mod tests { let wc = WeightedChoice::new(items); let expected = $expected; - let mut rng = MockAddRng::new(0u32, 1); + let mut rng = MockAddRng::new(0, 1); for &val in expected.iter() { assert_eq!(wc.sample(&mut rng), val) From e621639834e23bc1b4c8454d58cd90fb2f8da27e Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Fri, 24 Nov 2017 16:33:58 +0000 Subject: [PATCH 208/247] SeaHash, seeding via hash, SeadableRng::Seed Implement seeding via hash function Embed a compatible but customised SeaHash implementation Adjust SeadableRng to have associated Seed type --- rand_core/src/lib.rs | 46 ++++++++++++++++++--- src/mock.rs | 3 +- src/prng/chacha.rs | 59 ++++++++++++--------------- src/prng/isaac.rs | 43 +++++++------------- src/prng/isaac64.rs | 96 +++++++++++++++++++------------------------- src/prng/xorshift.rs | 13 +++--- src/reseeding.rs | 10 ++--- 7 files changed, 134 insertions(+), 136 deletions(-) diff --git a/rand_core/src/lib.rs b/rand_core/src/lib.rs index 3912957771b..7e0c7c1d9de 100644 --- a/rand_core/src/lib.rs +++ b/rand_core/src/lib.rs @@ -48,6 +48,9 @@ use std::error::Error as stdError; use core::fmt; +use hash::{AsBytesFixed, Finalize, SeaHash, SEA_SEED}; + +pub mod hash; pub mod impls; @@ -212,6 +215,12 @@ impl Rng for Box { /// Support mechanism for creating random number generators seeded by other /// generators. All PRNGs should support this to enable `NewSeeded` support, /// which should be the preferred way of creating randomly-seeded generators. +/// +/// There are two subtle differences between `SeedFromRng::from_rng` and +/// `SeedableRng::from_seed` (beyond the obvious): first, that `from_rng` has +/// no reproducibility requirement, and second, that `from_rng` may directly +/// fill internal states larger than `SeedableRng::Seed`, where `from_seed` may +/// need some extra step to expand the input. pub trait SeedFromRng: Sized { /// Creates a new instance, seeded from another `Rng`. /// @@ -223,7 +232,7 @@ pub trait SeedFromRng: Sized { } /// A random number generator that can be explicitly seeded to produce -/// the same stream of randomness multiple times. +/// the same stream of randomness multiple times (i.e. is reproducible). /// /// Note: this should only be implemented by reproducible generators (i.e. /// where the algorithm is fixed and results should be the same across @@ -231,12 +240,37 @@ pub trait SeedFromRng: Sized { /// the underlying implementation based on platform, or which may change the /// algorithm used in the future. This is to ensure that manual seeding of PRNGs /// actually does yield reproducible results. -pub trait SeedableRng: Rng { - /// Create a new RNG with the given seed. +pub trait SeedableRng: Sized { + /// Seed type. TODO: allow override? + type Seed: Finalize; + + /// Create a new PRNG using the given seed. + /// + /// Each PRNG should implement this. /// - /// The type of `Seed` is specified by the implementation (implementation - /// for multiple seed types is possible). - fn from_seed(seed: Seed) -> Self; + /// Reproducibility is required; that is, a fixed PRNG seeded using this + /// function with a fixed seed should produce the same sequence of output + /// today, and in the future. PRNGs not able to satisfy this should make + /// clear notes in their documentation or not implement `SeedableRng` at + /// all. It is however not required that this function yield the same state + /// as a reference implementation of the PRNG given equivalent seed; if + /// necessary another constructor should be used. + /// + /// It may be expected that bits in the seed are well distributed, i.e. that + /// values like 0, 1 and (size - 1) are unlikely. Users with poorly + /// distributed input should use `from_hashable`. + fn from_seed(seed: Self::Seed) -> Self; + + /// Create a new PRNG using any hashable input as the seed. + /// + /// Again, reproducibility is required; that is, use of this function with + /// fixed input should produce the same result on all platforms and across + /// all supporting versions. PRNGs do not need to implement this function + /// themselves. + fn from_hashable(x: &T) -> Self { + let seed = Finalize::from(SeaHash::hash(x, SEA_SEED)); + Self::from_seed(seed) + } } diff --git a/src/mock.rs b/src/mock.rs index 650cc445d30..6e514781a4b 100644 --- a/src/mock.rs +++ b/src/mock.rs @@ -67,7 +67,8 @@ impl Rng for MockAddRng { } } -impl SeedableRng for MockAddRng { +impl SeedableRng for MockAddRng { + type Seed = u64; fn from_seed(seed: u64) -> Self { MockAddRng::new(seed, 1) } diff --git a/src/prng/chacha.rs b/src/prng/chacha.rs index 49e8653b27b..d982d9ea034 100644 --- a/src/prng/chacha.rs +++ b/src/prng/chacha.rs @@ -18,6 +18,12 @@ const KEY_WORDS : usize = 8; // 8 words for the 256-bit key const STATE_WORDS : usize = 16; const CHACHA_ROUNDS: u32 = 20; // Cryptographically secure from 8 upwards as of this writing +const CHACHA_EMPTY: ChaChaRng = ChaChaRng { + buffer: [0; STATE_WORDS], + state: [0; STATE_WORDS], + index: STATE_WORDS + }; + /// A random number generator that uses the ChaCha20 algorithm [1]. /// /// The ChaCha algorithm is widely accepted as suitable for @@ -100,11 +106,7 @@ impl ChaChaRng { /// - 2917185654 /// - 2419978656 pub fn new_unseeded() -> ChaChaRng { - let mut rng = ChaChaRng { - buffer: [0; STATE_WORDS], - state: [0; STATE_WORDS], - index: STATE_WORDS - }; + let mut rng = CHACHA_EMPTY; rng.init(&[0; KEY_WORDS]); rng } @@ -238,33 +240,23 @@ impl CryptoRng for ChaChaRng {} impl SeedFromRng for ChaChaRng { fn from_rng(mut other: R) -> Result { - let mut key : [u32; KEY_WORDS] = [0; KEY_WORDS]; + let mut key = [0; KEY_WORDS]; for word in key.iter_mut() { *word = other.next_u32(); } - Ok(SeedableRng::from_seed(&key[..])) + let mut rng = CHACHA_EMPTY; + rng.init(&key); + Ok(rng) } } -impl<'a> SeedableRng<&'a [u32]> for ChaChaRng { - /// Create a ChaCha generator from a seed, - /// obtained from a variable-length u32 array. - /// Only up to 8 words are used; if less than 8 - /// words are used, the remaining are set to zero. - fn from_seed(seed: &'a [u32]) -> ChaChaRng { - let mut rng = ChaChaRng { - buffer: [0; STATE_WORDS], - state: [0; STATE_WORDS], - index: STATE_WORDS - }; - rng.init(&[0u32; KEY_WORDS]); - // set key in place - { - let key = &mut rng.state[4 .. 4+KEY_WORDS]; - for (k, s) in key.iter_mut().zip(seed.iter()) { - *k = *s; - } - } +impl SeedableRng for ChaChaRng { + type Seed = [u64; 4]; + fn from_seed(seed: Self::Seed) -> Self { + let mut rng = CHACHA_EMPTY; + let p = &seed as *const [u64; 4] as *const [u32; 8]; + let key = unsafe{ &*p }; + rng.init(key); rng } } @@ -272,21 +264,20 @@ impl<'a> SeedableRng<&'a [u32]> for ChaChaRng { #[cfg(test)] mod test { - use {Rng, SeedableRng, SeedFromRng, iter}; + use {Rng, SeedableRng, SeedFromRng}; use super::ChaChaRng; #[test] fn test_rng_rand_seeded() { // Test that various construction techniques produce a working RNG. - let s = iter(&mut ::test::rng()).map(|rng| rng.next_u32()).take(8).collect::>(); - let mut ra = ChaChaRng::from_seed(&s[..]); + let mut ra = ChaChaRng::from_hashable("hey ho lets go"); ra.next_u32(); let mut rb = ChaChaRng::from_rng(&mut ::test::rng()).unwrap(); rb.next_u32(); - let seed : &[_] = &[0,1,2,3,4,5,6,7]; + let seed = [0,1,2,3]; let mut rc = ChaChaRng::from_seed(seed); rc.next_u32(); } @@ -295,7 +286,7 @@ mod test { fn test_rng_true_values() { // Test vectors 1 and 2 from // http://tools.ietf.org/html/draft-nir-cfrg-chacha20-poly1305-04 - let seed : &[_] = &[0u32; 8]; + let seed = [0u64; 4]; let mut ra: ChaChaRng = SeedableRng::from_seed(seed); let v = (0..16).map(|_| ra.next_u32()).collect::>(); @@ -313,7 +304,7 @@ mod test { 0x281fed31, 0x45fb0a51, 0x1f0ae1ac, 0x6f4d794b)); - let seed : &[_] = &[0,1,2,3,4,5,6,7]; + let seed = [0 + (1 << 32), 2 + (3 << 32), 4 + (5 << 32), 6 + (7 << 32)]; let mut ra: ChaChaRng = SeedableRng::from_seed(seed); // Store the 17*i-th 32-bit word, @@ -335,7 +326,7 @@ mod test { #[test] fn test_rng_true_bytes() { - let seed : &[_] = &[0u32; 8]; + let seed = [0u64; 4]; let mut ra: ChaChaRng = SeedableRng::from_seed(seed); let mut buf = [0u8; 32]; ra.fill_bytes(&mut buf); @@ -349,7 +340,7 @@ mod test { #[test] fn test_rng_clone() { - let seed : &[_] = &[0u32; 8]; + let seed = [0u64; 4]; let mut rng: ChaChaRng = SeedableRng::from_seed(seed); let mut clone = rng.clone(); for _ in 0..16 { diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index 47dc8247f7a..f1f225e6226 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -11,7 +11,6 @@ //! The ISAAC random number generator. use core::slice; -use core::iter::repeat; use core::num::Wrapping as w; use core::fmt; @@ -350,54 +349,42 @@ impl SeedFromRng for IsaacRng { } } -impl<'a> SeedableRng<&'a [u32]> for IsaacRng { - /// Create an ISAAC random number generator with a seed. This can - /// be any length, although the maximum number of elements used is - /// 256 and any more will be silently ignored. A generator - /// constructed with a given seed will generate the same sequence - /// of values as all other generators constructed with that seed. - fn from_seed(seed: &'a [u32]) -> IsaacRng { +impl SeedableRng for IsaacRng { + type Seed = [u64; 4]; + fn from_seed(seed: Self::Seed) -> Self { let mut key = [w(0); RAND_SIZE]; - - // make the seed into [seed[0], seed[1], ..., seed[seed.len() - // - 1], 0, 0, ...], to fill `key`. - let seed_iter = seed.iter().map(|&x| x).chain(repeat(0u32)); - - for (rsl_elem, seed_elem) in key.iter_mut().zip(seed_iter) { - *rsl_elem = w(seed_elem); + for i in 0..4 { + // fix to LE + key[2 * i + 0] = w(seed[i] as u32); + key[2 * i + 1] = w((seed[i] >> 32) as u32); } - init(key, 2) } } #[cfg(test)] mod test { - use {Rng, SeedableRng, SeedFromRng, iter}; + use {Rng, SeedableRng, SeedFromRng}; use super::IsaacRng; #[test] fn test_isaac_construction() { // Test that various construction techniques produce a working RNG. - let seed = iter(&mut ::test::rng()) - .map(|rng| rng.next_u32()) - .take(256) - .collect::>(); - let mut rng1 = IsaacRng::from_seed(&seed[..]); + let mut rng1 = IsaacRng::from_hashable("some weak seed"); rng1.next_u32(); let mut rng2 = IsaacRng::from_rng(&mut ::test::rng()).unwrap(); rng2.next_u32(); - let seed: &[_] = &[1, 23, 456, 7890, 12345]; - let mut rng3 = IsaacRng::from_seed(&seed[..]); + let seed = [1, 23, 456, 7890]; + let mut rng3 = IsaacRng::from_seed(seed); rng3.next_u32(); } #[test] fn test_isaac_true_values() { - let seed: &[_] = &[1, 23, 456, 7890, 12345]; + let seed = [1 + (23 << 32), 456 + (7890 << 32), 12345, 0]; let mut rng1 = IsaacRng::from_seed(seed); // Regression test that isaac is actually using the above vector let v = (0..10).map(|_| rng1.next_u32()).collect::>(); @@ -406,7 +393,7 @@ mod test { 3595684709, 4203127393, 264982119, 2765226902, 2737944514, 3900253796)); - let seed: &[_] = &[12345, 67890, 54321, 9876]; + let seed = [12345 + (67890 << 32), 54321 + (9876 << 32), 0, 0]; let mut rng2 = IsaacRng::from_seed(seed); // skip forward to the 10000th number for _ in 0..10000 { rng2.next_u32(); } @@ -420,7 +407,7 @@ mod test { #[test] fn test_isaac_true_bytes() { - let seed: &[_] = &[1, 23, 456, 7890, 12345]; + let seed = [1 + (23 << 32), 456 + (7890 << 32), 12345, 0]; let mut rng1 = IsaacRng::from_seed(seed); let mut buf = [0u8; 32]; rng1.fill_bytes(&mut buf); @@ -451,7 +438,7 @@ mod test { #[test] fn test_isaac_clone() { - let seed: &[_] = &[1, 23, 456, 7890, 12345]; + let seed = [1, 23, 456, 7890]; let mut rng1 = IsaacRng::from_seed(seed); let mut rng2 = rng1.clone(); for _ in 0..16 { diff --git a/src/prng/isaac64.rs b/src/prng/isaac64.rs index 3c34b7fde5e..e1ed7f2a782 100644 --- a/src/prng/isaac64.rs +++ b/src/prng/isaac64.rs @@ -11,7 +11,6 @@ //! The ISAAC-64 random number generator. use core::slice; -use core::iter::repeat; use core::num::Wrapping as w; use core::fmt; @@ -330,66 +329,53 @@ impl SeedFromRng for Isaac64Rng { } } -impl<'a> SeedableRng<&'a [u64]> for Isaac64Rng { - /// Create an ISAAC random number generator with a seed. This can - /// be any length, although the maximum number of elements used is - /// 256 and any more will be silently ignored. A generator - /// constructed with a given seed will generate the same sequence - /// of values as all other generators constructed with that seed. - fn from_seed(seed: &'a [u64]) -> Isaac64Rng { +impl SeedableRng for Isaac64Rng { + type Seed = [u64; 4]; + fn from_seed(seed: Self::Seed) -> Self { let mut key = [w(0); RAND_SIZE]; - - // make the seed into [seed[0], seed[1], ..., seed[seed.len() - // - 1], 0, 0, ...], to fill `key`. - let seed_iter = seed.iter().map(|&x| x).chain(repeat(0u64)); - - for (rsl_elem, seed_elem) in key.iter_mut().zip(seed_iter) { - *rsl_elem = w(seed_elem); - } - + key[0] = w(seed[0]); + key[1] = w(seed[1]); + key[2] = w(seed[2]); + key[3] = w(seed[3]); init(key, 2) } } #[cfg(test)] mod test { - use {Rng, SeedableRng, SeedFromRng, iter}; + use {Rng, SeedableRng, SeedFromRng}; use super::Isaac64Rng; #[test] fn test_isaac64_construction() { // Test that various construction techniques produce a working RNG. - let seed = iter(&mut ::test::rng()) - .map(|rng| rng.next_u64()) - .take(256) - .collect::>(); - let mut rng1 = Isaac64Rng::from_seed(&seed[..]); + let mut rng1 = Isaac64Rng::from_hashable("some weak seed"); rng1.next_u64(); let mut rng2 = Isaac64Rng::from_rng(&mut ::test::rng()).unwrap(); rng2.next_u64(); - let seed: &[_] = &[1, 23, 456, 7890, 12345]; - let mut rng3 = Isaac64Rng::from_seed(&seed[..]); + let seed = [1, 23, 456, 7890]; + let mut rng3 = Isaac64Rng::from_seed(seed); rng3.next_u64(); } - + #[test] fn test_isaac64_true_values() { - let seed: &[_] = &[1, 23, 456, 7890, 12345]; + let seed = [1, 23, 456, 7890]; let mut rng1 = Isaac64Rng::from_seed(seed); // Regression test that isaac is actually using the above vector let v = (0..10).map(|_| rng1.next_u64()).collect::>(); assert_eq!(v, - vec!(547121783600835980, 14377643087320773276, - 17351601304698403469, 1238879483818134882, - 11952566807690396487, 13970131091560099343, - 4469761996653280935, 15552757044682284409, - 6860251611068737823, 13722198873481261842)); + vec!(15071495833797886820, 7720185633435529318, + 10836773366498097981, 5414053799617603544, + 12890513357046278984, 17001051845652595546, + 9240803642279356310, 12558996012687158051, + 14673053937227185542, 1677046725350116783)); - let seed: &[_] = &[12345, 67890, 54321, 9876]; + let seed = [12345, 67890, 54321, 9876]; let mut rng2 = Isaac64Rng::from_seed(seed); // skip forward to the 10000th number for _ in 0..10000 { rng2.next_u64(); } @@ -405,50 +391,50 @@ mod test { #[test] fn test_isaac64_true_values_32() { - let seed: &[_] = &[1, 23, 456, 7890, 12345]; + let seed = [1, 23, 456, 7890]; let mut rng1 = Isaac64Rng::from_seed(seed); let v = (0..12).map(|_| rng1.next_u32()).collect::>(); // Subset of above values, as an LE u32 sequence assert_eq!(v, - [141028748, 127386717, - 1058730652, 3347555894, - 851491469, 4039984500, - 2692730210, 288449107, - 646103879, 2782923823, - 4195642895, 3252674613]); + [3477963620, 3509106075, + 687845478, 1797495790, + 227048253, 2523132918, + 4044335064, 1260557630, + 4079741768, 3001306521, + 69157722, 3958365844]); } #[test] fn test_isaac64_true_values_mixed() { - let seed: &[_] = &[1, 23, 456, 7890, 12345]; + let seed = [1, 23, 456, 7890]; let mut rng = Isaac64Rng::from_seed(seed); // Test alternating between `next_u64` and `next_u32` works as expected. // Values are the same as `test_isaac64_true_values` and // `test_isaac64_true_values_32`. - assert_eq!(rng.next_u64(), 547121783600835980); - assert_eq!(rng.next_u32(), 1058730652); - assert_eq!(rng.next_u32(), 3347555894); - assert_eq!(rng.next_u64(), 17351601304698403469); - assert_eq!(rng.next_u32(), 2692730210); + assert_eq!(rng.next_u64(), 15071495833797886820); + assert_eq!(rng.next_u32(), 687845478); + assert_eq!(rng.next_u32(), 1797495790); + assert_eq!(rng.next_u64(), 10836773366498097981); + assert_eq!(rng.next_u32(), 4044335064); // Skip one u32 - assert_eq!(rng.next_u64(), 11952566807690396487); - assert_eq!(rng.next_u32(), 4195642895); + assert_eq!(rng.next_u64(), 12890513357046278984); + assert_eq!(rng.next_u32(), 69157722); } #[test] fn test_isaac64_true_bytes() { - let seed: &[_] = &[1, 23, 456, 7890, 12345]; + let seed = [1, 23, 456, 7890]; let mut rng1 = Isaac64Rng::from_seed(seed); let mut buf = [0u8; 32]; rng1.fill_bytes(&mut buf); // Same as first values in test_isaac64_true_values as bytes in LE order assert_eq!(buf, - [140, 237, 103, 8, 93, 196, 151, 7, - 156, 242, 26, 63, 54, 166, 135, 199, - 141, 186, 192, 50, 116, 69, 205, 240, - 98, 205, 127, 160, 83, 98, 49, 17]); + [100, 131, 77, 207, 155, 181, 40, 209, + 102, 176, 255, 40, 238, 155, 35, 107, + 61, 123, 136, 13, 246, 243, 99, 150, + 216, 167, 15, 241, 62, 149, 34, 75]); } - + #[test] fn test_isaac_new_uninitialized() { // Compare the results from initializing `IsaacRng` with @@ -470,7 +456,7 @@ mod test { #[test] fn test_isaac64_clone() { - let seed: &[_] = &[1, 23, 456, 7890, 12345]; + let seed = [1, 23, 456, 7890]; let mut rng1 = Isaac64Rng::from_seed(seed); let mut rng2 = rng1.clone(); for _ in 0..16 { diff --git a/src/prng/xorshift.rs b/src/prng/xorshift.rs index 34d5b33870e..78afde11f78 100644 --- a/src/prng/xorshift.rs +++ b/src/prng/xorshift.rs @@ -100,17 +100,18 @@ impl Rng for XorShiftRng { } } -impl SeedableRng<[u32; 4]> for XorShiftRng { +impl SeedableRng for XorShiftRng { + type Seed = [u64; 2]; /// Create a new XorShiftRng. This will panic if `seed` is entirely 0. - fn from_seed(seed: [u32; 4]) -> XorShiftRng { + fn from_seed(seed: Self::Seed) -> Self { assert!(!seed.iter().all(|&x| x == 0), "XorShiftRng::from_seed called with an all zero seed."); XorShiftRng { - x: w(seed[0]), - y: w(seed[1]), - z: w(seed[2]), - w: w(seed[3]), + x: w(seed[0] as u32), + y: w((seed[0] >> 32) as u32), + z: w(seed[1] as u32), + w: w((seed[1] >> 32) as u32), } } } diff --git a/src/reseeding.rs b/src/reseeding.rs index 18f7b42768d..6b8f71ebf38 100644 --- a/src/reseeding.rs +++ b/src/reseeding.rs @@ -146,12 +146,10 @@ impl> Rng for ReseedingRng { } } -impl, Rsdr: Reseeder> SeedableRng<(Rsdr, S)> for - ReseedingRng -{ +impl> ReseedingRng { /// Create a new `ReseedingRng` from the given reseeder and /// seed. This uses a default value for `generation_threshold`. - fn from_seed((rsdr, seed): (Rsdr, S)) -> ReseedingRng { + pub fn from_reseeder(rsdr: Rsdr, seed: ::Seed) -> ReseedingRng { ReseedingRng { rng: SeedableRng::from_seed(seed), generation_threshold: DEFAULT_GENERATION_THRESHOLD, @@ -218,8 +216,8 @@ mod test { #[test] fn test_rng_seeded() { // Default seed threshold is way beyond what we use here - let mut ra: MyRng = SeedableRng::from_seed((ReseedMock, 2)); - let mut rb: MockAddRng = SeedableRng::from_seed(2); + let mut ra: MyRng = ReseedingRng::from_reseeder(ReseedMock, 2); + let mut rb = MockAddRng::from_seed(2); assert!(::test::iter_eq(iter(&mut ra).map(|rng| rng.next_u32()).take(100), iter(&mut rb).map(|rng| rng.next_u32()).take(100))); } From e87b0a7523385a38dc65216214863935aa741f02 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Sat, 25 Nov 2017 16:23:33 +0100 Subject: [PATCH 209/247] Fix JitterRng on Windows --- src/jitter_rng.rs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/jitter_rng.rs b/src/jitter_rng.rs index d36cf79aafe..21609dfa456 100644 --- a/src/jitter_rng.rs +++ b/src/jitter_rng.rs @@ -670,7 +670,8 @@ impl JitterRng { } } -#[cfg(all(feature="std", not(any(target_os = "macos", target_os = "ios"))))] +#[cfg(all(feature="std", not(any(target_os = "macos", target_os = "ios", + target_os = "windows"))))] fn get_nstime() -> u64 { use std::time::{SystemTime, UNIX_EPOCH}; @@ -694,6 +695,21 @@ fn get_nstime() -> u64 { unsafe { libc::mach_absolute_time() } } +#[cfg(all(feature="std", any(target_os = "windows")))] +fn get_nstime() -> u64 { + #[allow(non_camel_case_types)] + type LARGE_INTEGER = i64; + #[allow(non_camel_case_types)] + type BOOL = i32; + extern "system" { + fn QueryPerformanceCounter(lpPerformanceCount: *mut LARGE_INTEGER) -> BOOL; + } + + let mut t = 0; + unsafe { QueryPerformanceCounter(&mut t); } + t as u64 +} + // A function that is opaque to the optimizer to assist in avoiding dead-code // elimination. Taken from `bencher`. fn black_box(dummy: T) -> T { From 697d88a26918c5e6f2f8ea2544ac8fac767d2007 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Fri, 1 Dec 2017 17:01:48 +0000 Subject: [PATCH 210/247] Remove from_hashable code --- rand_core/src/lib.rs | 20 ++++---------------- src/prng/chacha.rs | 6 +++--- src/prng/isaac.rs | 4 ++-- src/prng/isaac64.rs | 4 ++-- 4 files changed, 11 insertions(+), 23 deletions(-) diff --git a/rand_core/src/lib.rs b/rand_core/src/lib.rs index 7e0c7c1d9de..baf458fb76f 100644 --- a/rand_core/src/lib.rs +++ b/rand_core/src/lib.rs @@ -48,9 +48,6 @@ use std::error::Error as stdError; use core::fmt; -use hash::{AsBytesFixed, Finalize, SeaHash, SEA_SEED}; - -pub mod hash; pub mod impls; @@ -241,8 +238,10 @@ pub trait SeedFromRng: Sized { /// algorithm used in the future. This is to ensure that manual seeding of PRNGs /// actually does yield reproducible results. pub trait SeedableRng: Sized { - /// Seed type. TODO: allow override? - type Seed: Finalize; + /// Seed type. + /// + /// TODO: restrict to `[u8; N]` where N in 8, 16, 32 + type Seed; /// Create a new PRNG using the given seed. /// @@ -260,17 +259,6 @@ pub trait SeedableRng: Sized { /// values like 0, 1 and (size - 1) are unlikely. Users with poorly /// distributed input should use `from_hashable`. fn from_seed(seed: Self::Seed) -> Self; - - /// Create a new PRNG using any hashable input as the seed. - /// - /// Again, reproducibility is required; that is, use of this function with - /// fixed input should produce the same result on all platforms and across - /// all supporting versions. PRNGs do not need to implement this function - /// themselves. - fn from_hashable(x: &T) -> Self { - let seed = Finalize::from(SeaHash::hash(x, SEA_SEED)); - Self::from_seed(seed) - } } diff --git a/src/prng/chacha.rs b/src/prng/chacha.rs index d982d9ea034..46a069b6bbd 100644 --- a/src/prng/chacha.rs +++ b/src/prng/chacha.rs @@ -270,10 +270,10 @@ mod test { #[test] fn test_rng_rand_seeded() { // Test that various construction techniques produce a working RNG. - - let mut ra = ChaChaRng::from_hashable("hey ho lets go"); + /* TODO: from_hashable + let mut ra = ChaChaRng::from_hashable("some weak seed"); ra.next_u32(); - + */ let mut rb = ChaChaRng::from_rng(&mut ::test::rng()).unwrap(); rb.next_u32(); diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index f1f225e6226..9451b552f46 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -370,10 +370,10 @@ mod test { #[test] fn test_isaac_construction() { // Test that various construction techniques produce a working RNG. - + /* TODO: from_hashable let mut rng1 = IsaacRng::from_hashable("some weak seed"); rng1.next_u32(); - + */ let mut rng2 = IsaacRng::from_rng(&mut ::test::rng()).unwrap(); rng2.next_u32(); diff --git a/src/prng/isaac64.rs b/src/prng/isaac64.rs index e1ed7f2a782..4189092c51b 100644 --- a/src/prng/isaac64.rs +++ b/src/prng/isaac64.rs @@ -349,10 +349,10 @@ mod test { #[test] fn test_isaac64_construction() { // Test that various construction techniques produce a working RNG. - + /* TODO: from_hashable let mut rng1 = Isaac64Rng::from_hashable("some weak seed"); rng1.next_u64(); - + */ let mut rng2 = Isaac64Rng::from_rng(&mut ::test::rng()).unwrap(); rng2.next_u64(); From eae85e26fd8fa9e3e07a0e9324b37647abf45ace Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 4 Dec 2017 18:34:14 +0000 Subject: [PATCH 211/247] =?UTF-8?q?Rename=20new=5Fwith=5Fcause=20=E2=86=92?= =?UTF-8?q?=20with=5Fcause?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://rust-lang-nursery.github.io/api-guidelines/naming.html --- rand_core/src/lib.rs | 4 ++-- src/jitter.rs | 2 +- src/lib.rs | 2 +- src/os.rs | 16 ++++++++-------- src/read.rs | 2 +- src/reseeding.rs | 2 +- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/rand_core/src/lib.rs b/rand_core/src/lib.rs index baf458fb76f..37ff872a930 100644 --- a/rand_core/src/lib.rs +++ b/rand_core/src/lib.rs @@ -344,7 +344,7 @@ impl Error { /// type `E` (because both `Box` and `stdError` are unavailable), and the /// `cause` is ignored. #[cfg(feature="std")] - pub fn new_with_cause(kind: ErrorKind, msg: &'static str, cause: E) -> Self + pub fn with_cause(kind: ErrorKind, msg: &'static str, cause: E) -> Self where E: Into> { Self { kind, msg, cause: Some(cause.into()) } @@ -354,7 +354,7 @@ impl Error { /// /// In `no_std` mode the *cause* is ignored. #[cfg(not(feature="std"))] - pub fn new_with_cause(kind: ErrorKind, msg: &'static str, _cause: E) -> Self { + pub fn with_cause(kind: ErrorKind, msg: &'static str, _cause: E) -> Self { Self { kind, msg } } diff --git a/src/jitter.rs b/src/jitter.rs index f19c596cdde..79b4b2d7cdc 100644 --- a/src/jitter.rs +++ b/src/jitter.rs @@ -118,7 +118,7 @@ impl ::std::error::Error for TimerError { impl From for Error { fn from(err: TimerError) -> Error { - Error::new_with_cause(ErrorKind::Unavailable, + Error::with_cause(ErrorKind::Unavailable, "timer jitter failed basic quality tests", err) } } diff --git a/src/lib.rs b/src/lib.rs index 1d7ed3371fe..c8f1ced2c5a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -329,7 +329,7 @@ impl NewSeeded for R { new_jitter().map_err(|_e2| { // TODO: log // TODO: can we somehow return both error sources? - Error::new_with_cause( + Error::with_cause( ErrorKind::Unavailable, "seeding a new RNG failed: both OS and Jitter entropy sources failed", e1) diff --git a/src/os.rs b/src/os.rs index acd3d0ae036..fe3cfecf4ae 100644 --- a/src/os.rs +++ b/src/os.rs @@ -87,7 +87,7 @@ impl ReadRng { if dest.len() == 0 { return Ok(()); } // Use `std::io::read_exact`, which retries on `ErrorKind::Interrupted`. self.0.read_exact(dest).map_err(|err| { - Error::new_with_cause(ErrorKind::Unavailable, "error reading random device", err) + Error::with_cause(ErrorKind::Unavailable, "error reading random device", err) }) } } @@ -161,7 +161,7 @@ mod imp { // Also, wasting the bytes in v doesn't matter very much. return Err(Error::new(ErrorKind::NotReady, "getrandom not ready")); } else { - return Err(Error::new_with_cause( + return Err(Error::with_cause( ErrorKind::Unavailable, "unexpected getrandom error", err, @@ -228,7 +228,7 @@ mod imp { } let reader = File::open("/dev/urandom").map_err(|err| { - Error::new_with_cause(ErrorKind::Unavailable, "error opening random device", err) + Error::with_cause(ErrorKind::Unavailable, "error opening random device", err) })?; let reader_rng = ReadRng(reader); @@ -276,7 +276,7 @@ mod imp { SecRandomCopyBytes(kSecRandomDefault, v.len() as size_t, v.as_mut_ptr()) }; if ret == -1 { - Err(Error::new_with_cause( + Err(Error::with_cause( ErrorKind::Unavailable, "couldn't generate random bytes", io::Error::last_os_error())) @@ -347,7 +347,7 @@ mod imp { libc::getentropy(s.as_mut_ptr() as *mut libc::c_void, s.len()) }; if ret == -1 { - return Err(Error::new_with_cause( + return Err(Error::with_cause( ErrorKind::Unavailable, "getentropy failed", io::Error::last_os_error())); @@ -374,7 +374,7 @@ mod imp { impl OsRng { pub fn new() -> Result { let reader = File::open("rand:").map_err(|err| { - Error::new_with_cause(ErrorKind::Unavailable, "error opening random device", err) + Error::with_cause(ErrorKind::Unavailable, "error opening random device", err) })?; let reader_rng = ReadRng(reader); @@ -408,7 +408,7 @@ mod imp { match fuchsia_zircon::cprng_draw(&mut s[filled..]) { Ok(actual) => filled += actual, Err(e) => { - return Err(Error::new_with_cause( + return Err(Error::with_cause( ErrorKind::Unavailable, "cprng_draw failed", e)); @@ -451,7 +451,7 @@ mod imp { SystemFunction036(slice.as_mut_ptr(), slice.len() as ULONG) }; if ret == 0 { - return Err(Error::new_with_cause( + return Err(Error::with_cause( ErrorKind::Unavailable, "couldn't generate random bytes", io::Error::last_os_error())); diff --git a/src/read.rs b/src/read.rs index 0169f53f80b..772d83e7223 100644 --- a/src/read.rs +++ b/src/read.rs @@ -69,7 +69,7 @@ impl Rng for ReadRng { if dest.len() == 0 { return Ok(()); } // Use `std::io::read_exact`, which retries on `ErrorKind::Interrupted`. self.reader.read_exact(dest).map_err(|err| { - Error::new_with_cause(ErrorKind::Unavailable, "ReadRng: read error", err) + Error::with_cause(ErrorKind::Unavailable, "ReadRng: read error", err) }) } } diff --git a/src/reseeding.rs b/src/reseeding.rs index 6b8f71ebf38..6e38c7fee1c 100644 --- a/src/reseeding.rs +++ b/src/reseeding.rs @@ -104,7 +104,7 @@ impl> ReseedingRng { ErrorKind::Transient } }; - return Err(Error::new_with_cause(newkind, "reseeding failed", err)); + return Err(Error::with_cause(newkind, "reseeding failed", err)); } self.bytes_generated = 0; } From c826587e80a9c8cb0377851ba646094b6c393ded Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 5 Dec 2017 11:53:13 +0000 Subject: [PATCH 212/247] Benchmarks: add f32 open/closed/half-open distr tests --- benches/distributions.rs | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/benches/distributions.rs b/benches/distributions.rs index 05265d688e9..8424bd6eb3f 100644 --- a/benches/distributions.rs +++ b/benches/distributions.rs @@ -49,7 +49,7 @@ distr_range_int!(distr_range_i32, i32, -200_000_000i32, 800_000_000); distr_range_int!(distr_range_i64, i64, 3i64, 134217671); macro_rules! distr_float { - ($fnn:ident, $distr:expr) => { + ($fnn:ident, $ty:ty, $distr:expr) => { #[bench] fn $fnn(b: &mut Bencher) { let mut rng = XorShiftRng::new().unwrap(); @@ -57,21 +57,25 @@ macro_rules! distr_float { b.iter(|| { for _ in 0..::RAND_BENCH_N { - let x: f64 = distr.sample(&mut rng); + let x: $ty = distr.sample(&mut rng); black_box(x); } }); - b.bytes = size_of::() as u64 * ::RAND_BENCH_N; + b.bytes = size_of::<$ty>() as u64 * ::RAND_BENCH_N; } } } -distr_float!(distr_uniform01_float, Uniform01); -distr_float!(distr_closed01_float, Closed01); -distr_float!(distr_open01_float, Open01); -distr_float!(distr_range_float, Range::new(2.26f64, 2.319f64)); -distr_float!(distr_exp, Exp::new(2.71828 * 3.14159)); -distr_float!(distr_normal, Normal::new(-2.71828, 3.14159)); -distr_float!(distr_log_normal, LogNormal::new(-2.71828, 3.14159)); -distr_float!(distr_gamma_large_shape, Gamma::new(10., 1.0)); -distr_float!(distr_gamma_small_shape, Gamma::new(0.1, 1.0)); +distr_float!(distr_uniform01_float32, f32, Uniform01); +distr_float!(distr_closed01_float32, f32, Closed01); +distr_float!(distr_open01_float32, f32, Open01); + +distr_float!(distr_uniform01_float, f64, Uniform01); +distr_float!(distr_closed01_float, f64, Closed01); +distr_float!(distr_open01_float, f64, Open01); +distr_float!(distr_range_float, f64, Range::new(2.26f64, 2.319f64)); +distr_float!(distr_exp, f64, Exp::new(2.71828 * 3.14159)); +distr_float!(distr_normal, f64, Normal::new(-2.71828, 3.14159)); +distr_float!(distr_log_normal, f64, LogNormal::new(-2.71828, 3.14159)); +distr_float!(distr_gamma_large_shape, f64, Gamma::new(10., 1.0)); +distr_float!(distr_gamma_small_shape, f64, Gamma::new(0.1, 1.0)); From 51a27e1052fba7c2960855a3f59b278ecc60da4e Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Fri, 8 Dec 2017 15:21:37 +0000 Subject: [PATCH 213/247] Add alloc feature; enables seq code Yet another way to complicate the library; not too bad however. README updated with regards to features (new section) --- Cargo.toml | 2 ++ README.md | 11 +++++++++++ src/lib.rs | 5 ++++- src/seq.rs | 11 +++++++++-- 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c338cd7f298..41145fbb851 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,8 @@ nightly = ["i128_support"] i128_support = ["rand_core/i128_support"] +alloc = [] + # Deps should use 'std' if we do: std = ["rand_core/std", "libc"] diff --git a/README.md b/README.md index 8f34a76ef1f..7ed4c432211 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,17 @@ let mut rng = ChaChaRng::from_rng(&mut thread_rng()).unwrap(); println!("random between 0-9: {}", distributions::range(0, 10, &mut rng)); ``` +## Features + +By default, `rand` is built with all stable features available. The following +optional features are available: + +- `i128_support` enables support for generating `u128` and `i128` values +- `nightly` enables all unstable features (`i128_support`) +- `std` enabled by default; by setting "default-features = false" `no_std` + mode is activated; unfortunately this removes several important features +- `alloc` without `std` re-enables some functionality + ## Testing Unfortunately, `cargo test` does not test everything. The following tests are diff --git a/src/lib.rs b/src/lib.rs index 5cbeb72e233..20ae189afad 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -247,11 +247,14 @@ #![deny(missing_debug_implementations)] #![cfg_attr(not(feature="std"), no_std)] +#![cfg_attr(all(feature="alloc"), feature(alloc))] #![cfg_attr(feature = "i128_support", feature(i128_type, i128))] // We need to use several items from "core" for no_std support. #[cfg(feature="std")] extern crate core; +#[cfg(all(feature="alloc"))] +extern crate alloc; extern crate rand_core; @@ -277,7 +280,7 @@ pub mod mock; pub mod prng; pub mod reseeding; // TODO: move sequences code to seq maybe? -#[cfg(feature="std")] +#[cfg(any(feature="alloc", feature="std"))] pub mod seq; pub mod sequences; pub mod utils; diff --git a/src/seq.rs b/src/seq.rs index 63bf640f4dd..df7816c4292 100644 --- a/src/seq.rs +++ b/src/seq.rs @@ -11,7 +11,13 @@ //! Functions for randomly accessing and sampling sequences. use super::{Rng, Sample}; -use std::collections::hash_map::HashMap; + +// This crate is only enabled when either std or alloc is available. +// BTreeMap is not as fast in tests, but better than nothing. +#[cfg(feature="std")] use std::collections::HashMap; +#[cfg(not(feature="std"))] use alloc::btree_map::BTreeMap; + +#[cfg(not(feature="std"))] use alloc::Vec; /// Randomly sample `amount` elements from a finite iterator. /// @@ -189,7 +195,8 @@ fn sample_indices_cache( where R: Rng, { debug_assert!(amount <= length); - let mut cache = HashMap::with_capacity(amount); + #[cfg(feature="std")] let mut cache = HashMap::with_capacity(amount); + #[cfg(not(feature="std"))] let mut cache = BTreeMap::new(); let mut out = Vec::with_capacity(amount); for i in 0..amount { let j: usize = rng.gen_range(i, length); From 43267f7d931fb438cfa627c923d3bb612981d5b4 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Thu, 7 Dec 2017 20:35:58 +0100 Subject: [PATCH 214/247] Use a sign test for bools --- src/distributions/uniform.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/distributions/uniform.rs b/src/distributions/uniform.rs index 5dc9fc54cfb..fce3b6c9746 100644 --- a/src/distributions/uniform.rs +++ b/src/distributions/uniform.rs @@ -184,7 +184,11 @@ impl Distribution for Uniform { impl Distribution for Uniform { #[inline] fn sample(&self, rng: &mut R) -> bool { - rng.next_u32() & 1 == 1 + // We can compare against an arbitrary bit of an u32 to get a bool. + // Because the least significant bits of a lower quality RNG can have + // simple patterns, we compare against the most significant bit. This is + // easiest done using a sign test. + (rng.next_u32() as i32) < 0 } } From c8c720ebdc7f16f498738b02947bc0dea64e97aa Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Thu, 7 Dec 2017 20:40:00 +0100 Subject: [PATCH 215/247] Ignore the 3 least significant bits for the ziggurat layer --- src/distributions/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/distributions/mod.rs b/src/distributions/mod.rs index 2f5573a40d2..4c93fe5cc04 100644 --- a/src/distributions/mod.rs +++ b/src/distributions/mod.rs @@ -165,6 +165,8 @@ fn ziggurat( // Of the remaining 11 least significant bits we use 8 to construct `i`. // This saves us generating a whole extra random number, while the added // precision of using 64 bits for f64 does not buy us much. + // Because for some RNG's the least significant bits can be of lower + // statistical quality, we use bits 3..10 for i. let bits: u64 = uniform(rng); // u is either U(-1, 1) or U(0, 1) depending on if this is a @@ -176,7 +178,7 @@ fn ziggurat( } else { bits.closed_open01_fixed() }; - let i = (bits & 0xff) as usize; + let i = ((bits >> 3) & 0xff) as usize; let x = u * x_tab[i]; From 3f22ddd7eabc6d5bc500a20d5bfa648d05007305 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Sun, 10 Dec 2017 09:02:59 +0100 Subject: [PATCH 216/247] Use widening multiply instead of modulus in RangeInt --- benches/distributions.rs | 5 +- src/distributions/range.rs | 146 +++++++++++++++++++++++++++++++++---- src/sequences/weighted.rs | 19 +++-- 3 files changed, 147 insertions(+), 23 deletions(-) diff --git a/benches/distributions.rs b/benches/distributions.rs index 8424bd6eb3f..d7415e9fb74 100644 --- a/benches/distributions.rs +++ b/benches/distributions.rs @@ -1,4 +1,5 @@ #![feature(test)] +#![cfg_attr(feature = "i128_support", feature(i128_type, i128))] extern crate test; extern crate rand; @@ -46,7 +47,9 @@ macro_rules! distr_range_int { distr_range_int!(distr_range_i8, i8, 20i8, 100); distr_range_int!(distr_range_i16, i16, -500i16, 2000); distr_range_int!(distr_range_i32, i32, -200_000_000i32, 800_000_000); -distr_range_int!(distr_range_i64, i64, 3i64, 134217671); +distr_range_int!(distr_range_i64, i64, 3i64, 12345678901234); +#[cfg(feature = "i128_support")] +distr_range_int!(distr_range_i128, i128, -12345678901234i128, 12345678901234567890); macro_rules! distr_float { ($fnn:ident, $ty:ty, $distr:expr) => { diff --git a/src/distributions/range.rs b/src/distributions/range.rs index e30d69fbace..06d8ddaf464 100644 --- a/src/distributions/range.rs +++ b/src/distributions/range.rs @@ -106,7 +106,7 @@ pub struct RangeInt { macro_rules! range_int_impl { ($ty:ty, $signed:ident, $unsigned:ident, - $i_large:ident, $u_large:ident) => { + $i_large:ident, $u_large:ident, $use_mult:expr) => { impl SampleRange for $ty { type T = RangeInt<$ty>; } @@ -159,6 +159,16 @@ macro_rules! range_int_impl { // small integer. For an u8 it only ever uses 7 bits. This means // that all but the last 7 bits of `zone` are always 1's (or 15 // in the case of u16). So nothing is lost by trucating `zone`. + // + // An alternative to using a modulus is widening multiply: + // After a widening multiply by `range`, the result is in the + // high word. Then comparing the low word against `zone` makes + // sure our distribution is uniform. + // + // FIXME: This method is not used for i128/u128. It will + // probably help a lot for performance, because a 128-bit + // modulus is terribly slow. It means hand-coding a big-integer + // widening multiply. let unsigned_max: $u_large = ::core::$u_large::MAX; @@ -192,8 +202,15 @@ macro_rules! range_int_impl { let zone = self.zone as $signed as $i_large as $u_large; loop { let v: $u_large = Rand::rand(rng, Uniform); - if v <= zone { - return self.low.wrapping_add((v % range) as $ty); + if $use_mult { + let (high, low) = v.wmul(range); + if low <= zone { + return self.low.wrapping_add(high as $ty); + } + } else { + if v <= zone { + return self.low.wrapping_add((v % range) as $ty); + } } } } else { @@ -205,20 +222,119 @@ macro_rules! range_int_impl { } } -range_int_impl! { i8, i8, u8, i32, u32 } -range_int_impl! { i16, i16, u16, i32, u32 } -range_int_impl! { i32, i32, u32, i32, u32 } -range_int_impl! { i64, i64, u64, i64, u64 } +range_int_impl! { i8, i8, u8, i32, u32, true } +range_int_impl! { i16, i16, u16, i32, u32, true } +range_int_impl! { i32, i32, u32, i32, u32, true } +range_int_impl! { i64, i64, u64, i64, u64, true } #[cfg(feature = "i128_support")] -range_int_impl! { i128, i128, u128, u128, u128 } -range_int_impl! { isize, isize, usize, isize, usize } -range_int_impl! { u8, i8, u8, i32, u32 } -range_int_impl! { u16, i16, u16, i32, u32 } -range_int_impl! { u32, i32, u32, i32, u32 } -range_int_impl! { u64, i64, u64, i64, u64 } +range_int_impl! { i128, i128, u128, u128, u128, false } +range_int_impl! { isize, isize, usize, isize, usize, true } +range_int_impl! { u8, i8, u8, i32, u32, true } +range_int_impl! { u16, i16, u16, i32, u32, true } +range_int_impl! { u32, i32, u32, i32, u32, true } +range_int_impl! { u64, i64, u64, i64, u64, true } +range_int_impl! { usize, isize, usize, isize, usize, true } #[cfg(feature = "i128_support")] -range_int_impl! { u128, u128, u128, i128, u128 } -range_int_impl! { usize, isize, usize, isize, usize } +range_int_impl! { u128, u128, u128, i128, u128, false } + + +trait WideningMultiply { + type Output; + + fn wmul(self, x: RHS) -> Self::Output; +} + +macro_rules! wmul_impl { + ($ty:ty, $wide:ident, $shift:expr) => { + impl WideningMultiply for $ty { + type Output = ($ty, $ty); + + #[inline(always)] + fn wmul(self, x: $ty) -> Self::Output { + let tmp = (self as $wide) * (x as $wide); + ((tmp >> $shift) as $ty, tmp as $ty) + } + } + } +} + +wmul_impl! { u8, u16, 8 } +wmul_impl! { u16, u32, 16 } +wmul_impl! { u32, u64, 32 } + +#[cfg(feature = "i128_support")] +wmul_impl! { u64, u128, 64 } + +#[cfg(not(feature = "i128_support"))] +impl WideningMultiply for u64 { + type Output = (u64, u64); + + // This code is a translation of the __mulddi3 function in LLVM's + // compiler-rt. It is an optimised variant of the common method + // `(a + b) * (c + d) = ac + ad + bc + bd`. + // + // For some reason LLVM can optimise the C version very well, but keeps + // shuffeling registers in this Rust translation. + #[inline(always)] + fn wmul(self, b: u64) -> Self::Output { + const LOWER_MASK: u64 = !0u64 >> 32; + let mut low = (self & LOWER_MASK).wrapping_mul(b & LOWER_MASK); + let mut t = low >> 32; + low &= LOWER_MASK; + t += (self >> 32).wrapping_mul(b & LOWER_MASK); + low += (t & LOWER_MASK) << 32; + let mut high = (t >> 32) as i64; + t = low >> 32; + low &= LOWER_MASK; + t += (b >> 32).wrapping_mul(self & LOWER_MASK); + low += (t & LOWER_MASK) << 32; + high += (t >> 32) as i64; + high += (self >> 32).wrapping_mul(b >> 32) as i64; + + (high as u64, low) + } +} + + +macro_rules! wmul_impl_usize { + ($ty:ty) => { + impl WideningMultiply for usize { + type Output = (usize, usize); + + #[inline(always)] + fn wmul(self, x: usize) -> Self::Output { + let (high, low) = (self as $ty).wmul(x as $ty); + (high as usize, low as usize) + } + } + } +} + +#[cfg(target_pointer_width = "32")] +wmul_impl_usize! { u32 } +#[cfg(target_pointer_width = "64")] +wmul_impl_usize! { u64 } + +#[cfg(feature = "i128_support")] +macro_rules! wmul_impl_128 { + ($ty:ty) => { + impl WideningMultiply for $ty { + type Output = ($ty, $ty); + + #[inline(always)] + fn wmul(self, _: $ty) -> Self::Output { + unimplemented!(); + } + } + } +} + +#[cfg(feature = "i128_support")] +wmul_impl_128! { i128 } +#[cfg(feature = "i128_support")] +wmul_impl_128! { u128 } + + /// Implementation of `RangeImpl` for float types. #[derive(Clone, Copy, Debug)] diff --git a/src/sequences/weighted.rs b/src/sequences/weighted.rs index 6589fa0a24b..37d88c0e610 100644 --- a/src/sequences/weighted.rs +++ b/src/sequences/weighted.rs @@ -141,17 +141,22 @@ mod tests { #[test] fn test_weighted_choice() { // this makes assumptions about the internal implementation of - // WeightedChoice, specifically: it doesn't reorder the items, - // it doesn't do weird things to the RNG (so 0 maps to 0, 1 to - // 1, internally; modulo a modulo operation). + // WeightedChoice. It may fail when the implementation in + // `distributions::range::RangeInt changes. macro_rules! t { ($items:expr, $expected:expr) => {{ let items = $items; + let mut total_weight = 0; + for item in &items { total_weight += item.weight; } + let wc = WeightedChoice::new(items); let expected = $expected; - let mut rng = MockAddRng::new(0, 1); + // Use extremely large steps between the random numbers, because + // we test with small ranges and RangeInt is designed to prefer + // the most significant bits. + let mut rng = MockAddRng::new(0, !0 / (total_weight as u64)); for &val in expected.iter() { assert_eq!(wc.sample(&mut rng), val) @@ -166,12 +171,12 @@ mod tests { Weighted { weight: 2, item: 21}, Weighted { weight: 0, item: 22}, Weighted { weight: 1, item: 23}), - [21,21, 23]); + [21, 21, 23]); // different weights t!(vec!(Weighted { weight: 4, item: 30}, Weighted { weight: 3, item: 31}), - [30,30,30,30, 31,31,31]); + [30, 31, 30, 31, 30, 31, 30]); // check that we're binary searching // correctly with some vectors of odd @@ -189,7 +194,7 @@ mod tests { Weighted { weight: 1, item: 54}, Weighted { weight: 1, item: 55}, Weighted { weight: 1, item: 56}), - [50, 51, 52, 53, 54, 55, 56]); + [50, 54, 51, 55, 52, 56, 53]); } #[test] From 68edc9fc4e67b9ebe6b6d36035b0059bee27cd78 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Mon, 11 Dec 2017 12:34:44 +0100 Subject: [PATCH 217/247] Add a few extra distr benchmarks --- benches/distributions.rs | 85 +++++++++++++++++----------------------- 1 file changed, 35 insertions(+), 50 deletions(-) diff --git a/benches/distributions.rs b/benches/distributions.rs index d7415e9fb74..7fded29c9ea 100644 --- a/benches/distributions.rs +++ b/benches/distributions.rs @@ -9,29 +9,16 @@ const RAND_BENCH_N: u64 = 1000; use std::mem::size_of; use test::{black_box, Bencher}; -use rand::{Default, Rand, NewSeeded}; +use rand::NewSeeded; use rand::prng::XorShiftRng; use rand::distributions::*; - -#[bench] -fn distr_baseline(b: &mut Bencher) { - let mut rng = XorShiftRng::new().unwrap(); - - b.iter(|| { - for _ in 0..::RAND_BENCH_N { - black_box(u64::rand(&mut rng, Default)); - } - }); - b.bytes = size_of::() as u64 * ::RAND_BENCH_N; -} - -macro_rules! distr_range_int { - ($fnn:ident, $ty:ty, $low:expr, $high:expr) => { +macro_rules! distr { + ($fnn:ident, $ty:ty, $distr:expr) => { #[bench] fn $fnn(b: &mut Bencher) { let mut rng = XorShiftRng::new().unwrap(); - let distr = Range::new($low, $high); + let distr = $distr; b.iter(|| { for _ in 0..::RAND_BENCH_N { @@ -44,41 +31,39 @@ macro_rules! distr_range_int { } } -distr_range_int!(distr_range_i8, i8, 20i8, 100); -distr_range_int!(distr_range_i16, i16, -500i16, 2000); -distr_range_int!(distr_range_i32, i32, -200_000_000i32, 800_000_000); -distr_range_int!(distr_range_i64, i64, 3i64, 12345678901234); +// range +distr!(distr_range_i8, i8, Range::new(20i8, 100)); +distr!(distr_range_i16, i16, Range::new(-500i16, 2000)); +distr!(distr_range_i32, i32, Range::new(-200_000_000i32, 800_000_000)); +distr!(distr_range_i64, i64, Range::new(3i64, 12345678901234)); #[cfg(feature = "i128_support")] -distr_range_int!(distr_range_i128, i128, -12345678901234i128, 12345678901234567890); +distr!(distr_range_i128, i128, Range::new(-12345678901234i128, 12345678901234567890)); -macro_rules! distr_float { - ($fnn:ident, $ty:ty, $distr:expr) => { - #[bench] - fn $fnn(b: &mut Bencher) { - let mut rng = XorShiftRng::new().unwrap(); - let distr = $distr; +distr!(distr_range_float32, f32, Range::new(2.26f32, 2.319)); +distr!(distr_range_float, f64, Range::new(2.26f64, 2.319)); - b.iter(|| { - for _ in 0..::RAND_BENCH_N { - let x: $ty = distr.sample(&mut rng); - black_box(x); - } - }); - b.bytes = size_of::<$ty>() as u64 * ::RAND_BENCH_N; - } - } -} +// uniform +distr!(distr_uniform_i8, i8, Uniform); +distr!(distr_uniform_i16, i16, Uniform); +distr!(distr_uniform_i32, i32, Uniform); +distr!(distr_uniform_i64, i64, Uniform); +#[cfg(feature = "i128_support")] +distr!(distr_uniform_i128, i128, Uniform); + +distr!(distr_uniform_bool, bool, Uniform); +distr!(distr_uniform_ascii_char, char, AsciiWordChar); + +distr!(distr_uniform01_float32, f32, Uniform01); +distr!(distr_closed01_float32, f32, Closed01); +distr!(distr_open01_float32, f32, Open01); -distr_float!(distr_uniform01_float32, f32, Uniform01); -distr_float!(distr_closed01_float32, f32, Closed01); -distr_float!(distr_open01_float32, f32, Open01); +distr!(distr_uniform01_float, f64, Uniform01); +distr!(distr_closed01_float, f64, Closed01); +distr!(distr_open01_float, f64, Open01); -distr_float!(distr_uniform01_float, f64, Uniform01); -distr_float!(distr_closed01_float, f64, Closed01); -distr_float!(distr_open01_float, f64, Open01); -distr_float!(distr_range_float, f64, Range::new(2.26f64, 2.319f64)); -distr_float!(distr_exp, f64, Exp::new(2.71828 * 3.14159)); -distr_float!(distr_normal, f64, Normal::new(-2.71828, 3.14159)); -distr_float!(distr_log_normal, f64, LogNormal::new(-2.71828, 3.14159)); -distr_float!(distr_gamma_large_shape, f64, Gamma::new(10., 1.0)); -distr_float!(distr_gamma_small_shape, f64, Gamma::new(0.1, 1.0)); +// distributions +distr!(distr_exp, f64, Exp::new(2.71828 * 3.14159)); +distr!(distr_normal, f64, Normal::new(-2.71828, 3.14159)); +distr!(distr_log_normal, f64, LogNormal::new(-2.71828, 3.14159)); +distr!(distr_gamma_large_shape, f64, Gamma::new(10., 1.0)); +distr!(distr_gamma_small_shape, f64, Gamma::new(0.1, 1.0)); From 173c2b4c64ad08584043a179f1ac0697946c4cbe Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Mon, 11 Dec 2017 13:40:56 +0100 Subject: [PATCH 218/247] Benchmark constructing and sampling from a range --- benches/distributions.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/benches/distributions.rs b/benches/distributions.rs index 7fded29c9ea..ec2906a7353 100644 --- a/benches/distributions.rs +++ b/benches/distributions.rs @@ -67,3 +67,29 @@ distr!(distr_normal, f64, Normal::new(-2.71828, 3.14159)); distr!(distr_log_normal, f64, LogNormal::new(-2.71828, 3.14159)); distr!(distr_gamma_large_shape, f64, Gamma::new(10., 1.0)); distr!(distr_gamma_small_shape, f64, Gamma::new(0.1, 1.0)); + + +// construct and sample from a range +macro_rules! gen_range_int { + ($fnn:ident, $ty:ty, $low:expr, $high:expr) => { + #[bench] + fn $fnn(b: &mut Bencher) { + let mut rng = XorShiftRng::new().unwrap(); + + b.iter(|| { + for _ in 0..::RAND_BENCH_N { + let x: $ty = Range::new($low, $high).sample(&mut rng); + black_box(x); + } + }); + b.bytes = size_of::<$ty>() as u64 * ::RAND_BENCH_N; + } + } +} + +gen_range_int!(gen_range_i8, i8, 20i8, 100); +gen_range_int!(gen_range_i16, i16, -500i16, 2000); +gen_range_int!(gen_range_i32, i32, -200_000_000i32, 800_000_000); +gen_range_int!(gen_range_i64, i64, 3i64, 12345678901234); +#[cfg(feature = "i128_support")] +gen_range_int!(gen_range_i128, i128, -12345678901234i128, 12345678901234567890); From 888c5d2336d178bad0af6960a234ecd3af76dd3b Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 27 Nov 2017 14:39:37 +0000 Subject: [PATCH 219/247] Cherry-pick 243e1ea: Add rand_core::le module, switch SeedableRng::Seed types to u8 arrays --- rand_core/src/lib.rs | 71 ++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 17 +++++++++++ src/mock.rs | 7 +++-- src/prng/chacha.rs | 24 +++++++++------ src/prng/isaac.rs | 22 +++++++------- src/prng/isaac64.rs | 27 ++++++++--------- src/prng/xorshift.rs | 11 +++---- src/reseeding.rs | 5 ++-- src/seq.rs | 3 +- 9 files changed, 141 insertions(+), 46 deletions(-) diff --git a/rand_core/src/lib.rs b/rand_core/src/lib.rs index 37ff872a930..4ada973f00b 100644 --- a/rand_core/src/lib.rs +++ b/rand_core/src/lib.rs @@ -380,3 +380,74 @@ impl stdError for Error { self.cause.as_ref().map(|e| e.as_ref() as &stdError) } } + +/// Little-Endian utilities +/// +/// Little-Endian order has been chosen for internal usage; this makes some +/// useful functions available. +pub mod le { + use core::slice; + + /// Helper function to turn a slice into an array reference + + /// Read a `u32` from a byte sequence, in litte-endian order + /// + /// Consider usage with the `arrayref` crate. + pub fn read_u32(bytes: &[u8; 4]) -> u32 { + unsafe{ *(bytes as *const [u8; 4] as *const u32) }.to_le() + } + + /// Read a `u64` from a byte sequence, in litte-endian order + /// + /// Consider usage with the `arrayref` crate. + pub fn read_u64(bytes: &[u8; 8]) -> u64 { + unsafe{ *(bytes as *const [u8; 8] as *const u64) }.to_le() + } + + /// Convert a byte slice to a `u32` slice and mutate endianness in-place + pub fn convert_slice_32(bytes: &mut [u8]) -> &mut [u32] { + assert_eq!(bytes.len() % 4, 0); + let l = bytes.len() / 4; + let p = bytes.as_ptr() as *mut u8 as *mut u32; + let s = unsafe{ slice::from_raw_parts_mut(p, l) }; + for i in s.iter_mut() { + *i = (*i).to_le(); + } + s + } + + /// Convert a byte slice to a `u64` slice and mutate endianness in-place + pub fn convert_slice_64(bytes: &mut [u8]) -> &mut [u64] { + assert_eq!(bytes.len() % 8, 0); + let l = bytes.len() / 8; + let p = bytes.as_ptr() as *mut u8 as *mut u64; + let s = unsafe{ slice::from_raw_parts_mut(p, l) }; + for i in s.iter_mut() { + *i = (*i).to_le(); + } + s + } + + #[cfg(test)] + mod tests { + use super::*; + #[test] + + fn test_read() { + assert_eq!(read_u32(&[1, 2, 3, 4]), 0x04030201); + assert_eq!(read_u64(&[1, 2, 3, 4, 5, 6, 7, 8]), 0x0807060504030201); + + let mut bytes = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; + { + let slice = convert_slice_32(&mut bytes[..]); + assert_eq!(slice[0], 0x04030201); + assert_eq!(slice[3], 0x100F0E0D); + } + { + let slice = convert_slice_64(&mut bytes[..]); + assert_eq!(slice[0], 0x0807060504030201); + assert_eq!(slice[1], 0x100F0E0D0C0B0A09); + } + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 20ae189afad..6d3645e7035 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -273,6 +273,23 @@ pub use thread_local::{ThreadRng, thread_rng, random, random_with}; use prng::IsaacWordRng; use distributions::range::Range; +/// Copied from `arrayref` crate +macro_rules! array_ref { + ($arr:expr, $offset:expr, $len:expr) => {{ + { + #[inline] + unsafe fn as_array(slice: &[T]) -> &[T; $len] { + &*(slice.as_ptr() as *const [_; $len]) + } + let offset = $offset; + let slice = & $arr[offset..offset + $len]; + unsafe { + as_array(slice) + } + } + }} +} + pub mod distributions; pub mod iter; pub mod jitter; diff --git a/src/mock.rs b/src/mock.rs index 6e514781a4b..447d6cc8090 100644 --- a/src/mock.rs +++ b/src/mock.rs @@ -17,7 +17,7 @@ use core::num::Wrapping as w; use {Rng, SeedableRng, Error}; -use rand_core::impls; +use rand_core::{impls, le}; /// A simple implementation of `Rng`, purely for testing. /// Returns an arithmetic sequence (i.e. adds a constant each step). @@ -68,8 +68,9 @@ impl Rng for MockAddRng { } impl SeedableRng for MockAddRng { - type Seed = u64; - fn from_seed(seed: u64) -> Self { + type Seed = [u8; 8]; + fn from_seed(seed: Self::Seed) -> Self { + let seed = le::read_u64(array_ref!(seed, 0, 8)); MockAddRng::new(seed, 1) } } diff --git a/src/prng/chacha.rs b/src/prng/chacha.rs index 46a069b6bbd..d651d510027 100644 --- a/src/prng/chacha.rs +++ b/src/prng/chacha.rs @@ -251,11 +251,14 @@ impl SeedFromRng for ChaChaRng { } impl SeedableRng for ChaChaRng { - type Seed = [u64; 4]; - fn from_seed(seed: Self::Seed) -> Self { + type Seed = [u8; 32]; + fn from_seed(mut seed: Self::Seed) -> Self { let mut rng = CHACHA_EMPTY; - let p = &seed as *const [u64; 4] as *const [u32; 8]; - let key = unsafe{ &*p }; + let p = &mut seed as *mut [u8; 32] as *mut [u32; 8]; + let key = unsafe{ &mut *p }; + for k in key.iter_mut() { + *k = k.to_le(); + } rng.init(key); rng } @@ -277,7 +280,10 @@ mod test { let mut rb = ChaChaRng::from_rng(&mut ::test::rng()).unwrap(); rb.next_u32(); - let seed = [0,1,2,3]; + let seed = [0,0,0,0,0,0,0,0, + 1,0,0,0,0,0,0,0, + 2,0,0,0,0,0,0,0, + 3,0,0,0,0,0,0,0]; let mut rc = ChaChaRng::from_seed(seed); rc.next_u32(); } @@ -286,7 +292,7 @@ mod test { fn test_rng_true_values() { // Test vectors 1 and 2 from // http://tools.ietf.org/html/draft-nir-cfrg-chacha20-poly1305-04 - let seed = [0u64; 4]; + let seed = [0u8; 32]; let mut ra: ChaChaRng = SeedableRng::from_seed(seed); let v = (0..16).map(|_| ra.next_u32()).collect::>(); @@ -304,7 +310,7 @@ mod test { 0x281fed31, 0x45fb0a51, 0x1f0ae1ac, 0x6f4d794b)); - let seed = [0 + (1 << 32), 2 + (3 << 32), 4 + (5 << 32), 6 + (7 << 32)]; + let seed = [0,0,0,0, 1,0,0,0, 2,0,0,0, 3,0,0,0, 4,0,0,0, 5,0,0,0, 6,0,0,0, 7,0,0,0]; let mut ra: ChaChaRng = SeedableRng::from_seed(seed); // Store the 17*i-th 32-bit word, @@ -326,7 +332,7 @@ mod test { #[test] fn test_rng_true_bytes() { - let seed = [0u64; 4]; + let seed = [0u8; 32]; let mut ra: ChaChaRng = SeedableRng::from_seed(seed); let mut buf = [0u8; 32]; ra.fill_bytes(&mut buf); @@ -340,7 +346,7 @@ mod test { #[test] fn test_rng_clone() { - let seed = [0u64; 4]; + let seed = [0u8; 32]; let mut rng: ChaChaRng = SeedableRng::from_seed(seed); let mut clone = rng.clone(); for _ in 0..16 { diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index 9451b552f46..bd82e306a74 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -14,7 +14,7 @@ use core::slice; use core::num::Wrapping as w; use core::fmt; -use rand_core::impls; +use rand_core::{impls, le}; use {Rng, SeedFromRng, SeedableRng, Error}; @@ -350,13 +350,11 @@ impl SeedFromRng for IsaacRng { } impl SeedableRng for IsaacRng { - type Seed = [u64; 4]; - fn from_seed(seed: Self::Seed) -> Self { + type Seed = [u8; 32]; + fn from_seed(mut seed: Self::Seed) -> Self { let mut key = [w(0); RAND_SIZE]; - for i in 0..4 { - // fix to LE - key[2 * i + 0] = w(seed[i] as u32); - key[2 * i + 1] = w((seed[i] >> 32) as u32); + for (x, y) in key.iter_mut().zip(le::convert_slice_32(&mut seed[..]).iter()) { + *x = w(*y); } init(key, 2) } @@ -377,14 +375,14 @@ mod test { let mut rng2 = IsaacRng::from_rng(&mut ::test::rng()).unwrap(); rng2.next_u32(); - let seed = [1, 23, 456, 7890]; + let seed = [1,0,0,0, 23,0,0,0, 200,1,0,0, 210,30,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]; let mut rng3 = IsaacRng::from_seed(seed); rng3.next_u32(); } #[test] fn test_isaac_true_values() { - let seed = [1 + (23 << 32), 456 + (7890 << 32), 12345, 0]; + let seed = [1,0,0,0, 23,0,0,0, 200,1,0,0, 210,30,0,0, 57,48,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]; let mut rng1 = IsaacRng::from_seed(seed); // Regression test that isaac is actually using the above vector let v = (0..10).map(|_| rng1.next_u32()).collect::>(); @@ -393,7 +391,7 @@ mod test { 3595684709, 4203127393, 264982119, 2765226902, 2737944514, 3900253796)); - let seed = [12345 + (67890 << 32), 54321 + (9876 << 32), 0, 0]; + let seed = [57,48,0,0, 50,9,1,0, 49,212,0,0, 148,38,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]; let mut rng2 = IsaacRng::from_seed(seed); // skip forward to the 10000th number for _ in 0..10000 { rng2.next_u32(); } @@ -407,7 +405,7 @@ mod test { #[test] fn test_isaac_true_bytes() { - let seed = [1 + (23 << 32), 456 + (7890 << 32), 12345, 0]; + let seed = [1,0,0,0, 23,0,0,0, 200,1,0,0, 210,30,0,0, 57,48,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]; let mut rng1 = IsaacRng::from_seed(seed); let mut buf = [0u8; 32]; rng1.fill_bytes(&mut buf); @@ -438,7 +436,7 @@ mod test { #[test] fn test_isaac_clone() { - let seed = [1, 23, 456, 7890]; + let seed = [1,0,0,0, 23,0,0,0, 200,1,0,0, 210,30,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]; let mut rng1 = IsaacRng::from_seed(seed); let mut rng2 = rng1.clone(); for _ in 0..16 { diff --git a/src/prng/isaac64.rs b/src/prng/isaac64.rs index 4189092c51b..2fc6e5c3513 100644 --- a/src/prng/isaac64.rs +++ b/src/prng/isaac64.rs @@ -14,7 +14,7 @@ use core::slice; use core::num::Wrapping as w; use core::fmt; -use rand_core::impls; +use rand_core::{impls, le}; use {Rng, SeedFromRng, SeedableRng, Error}; @@ -330,13 +330,12 @@ impl SeedFromRng for Isaac64Rng { } impl SeedableRng for Isaac64Rng { - type Seed = [u64; 4]; - fn from_seed(seed: Self::Seed) -> Self { + type Seed = [u8; 32]; + fn from_seed(mut seed: Self::Seed) -> Self { let mut key = [w(0); RAND_SIZE]; - key[0] = w(seed[0]); - key[1] = w(seed[1]); - key[2] = w(seed[2]); - key[3] = w(seed[3]); + for (x, y) in key.iter_mut().zip(le::convert_slice_64(&mut seed[..]).iter()) { + *x = w(*y); + } init(key, 2) } } @@ -356,7 +355,7 @@ mod test { let mut rng2 = Isaac64Rng::from_rng(&mut ::test::rng()).unwrap(); rng2.next_u64(); - let seed = [1, 23, 456, 7890]; + let seed = [1,0,0,0, 0,0,0,0, 23,0,0,0, 0,0,0,0, 200,1,0,0, 0,0,0,0, 210,30,0,0, 0,0,0,0]; let mut rng3 = Isaac64Rng::from_seed(seed); rng3.next_u64(); @@ -364,7 +363,7 @@ mod test { #[test] fn test_isaac64_true_values() { - let seed = [1, 23, 456, 7890]; + let seed = [1,0,0,0, 0,0,0,0, 23,0,0,0, 0,0,0,0, 200,1,0,0, 0,0,0,0, 210,30,0,0, 0,0,0,0]; let mut rng1 = Isaac64Rng::from_seed(seed); // Regression test that isaac is actually using the above vector let v = (0..10).map(|_| rng1.next_u64()).collect::>(); @@ -375,7 +374,7 @@ mod test { 9240803642279356310, 12558996012687158051, 14673053937227185542, 1677046725350116783)); - let seed = [12345, 67890, 54321, 9876]; + let seed = [57,48,0,0, 0,0,0,0, 50,9,1,0, 0,0,0,0, 49,212,0,0, 0,0,0,0, 148,38,0,0, 0,0,0,0]; let mut rng2 = Isaac64Rng::from_seed(seed); // skip forward to the 10000th number for _ in 0..10000 { rng2.next_u64(); } @@ -391,7 +390,7 @@ mod test { #[test] fn test_isaac64_true_values_32() { - let seed = [1, 23, 456, 7890]; + let seed = [1,0,0,0, 0,0,0,0, 23,0,0,0, 0,0,0,0, 200,1,0,0, 0,0,0,0, 210,30,0,0, 0,0,0,0]; let mut rng1 = Isaac64Rng::from_seed(seed); let v = (0..12).map(|_| rng1.next_u32()).collect::>(); // Subset of above values, as an LE u32 sequence @@ -406,7 +405,7 @@ mod test { #[test] fn test_isaac64_true_values_mixed() { - let seed = [1, 23, 456, 7890]; + let seed = [1,0,0,0, 0,0,0,0, 23,0,0,0, 0,0,0,0, 200,1,0,0, 0,0,0,0, 210,30,0,0, 0,0,0,0]; let mut rng = Isaac64Rng::from_seed(seed); // Test alternating between `next_u64` and `next_u32` works as expected. // Values are the same as `test_isaac64_true_values` and @@ -423,7 +422,7 @@ mod test { #[test] fn test_isaac64_true_bytes() { - let seed = [1, 23, 456, 7890]; + let seed = [1,0,0,0, 0,0,0,0, 23,0,0,0, 0,0,0,0, 200,1,0,0, 0,0,0,0, 210,30,0,0, 0,0,0,0]; let mut rng1 = Isaac64Rng::from_seed(seed); let mut buf = [0u8; 32]; rng1.fill_bytes(&mut buf); @@ -456,7 +455,7 @@ mod test { #[test] fn test_isaac64_clone() { - let seed = [1, 23, 456, 7890]; + let seed = [1,0,0,0, 0,0,0,0, 23,0,0,0, 0,0,0,0, 200,1,0,0, 0,0,0,0, 210,30,0,0, 0,0,0,0]; let mut rng1 = Isaac64Rng::from_seed(seed); let mut rng2 = rng1.clone(); for _ in 0..16 { diff --git a/src/prng/xorshift.rs b/src/prng/xorshift.rs index 78afde11f78..4fb829bba56 100644 --- a/src/prng/xorshift.rs +++ b/src/prng/xorshift.rs @@ -13,6 +13,7 @@ use core::num::Wrapping as w; use core::fmt; use {Rng, SeedFromRng, SeedableRng, Error}; +use rand_core::le; /// An Xorshift[1] random number /// generator. @@ -101,17 +102,17 @@ impl Rng for XorShiftRng { } impl SeedableRng for XorShiftRng { - type Seed = [u64; 2]; + type Seed = [u8; 16]; /// Create a new XorShiftRng. This will panic if `seed` is entirely 0. fn from_seed(seed: Self::Seed) -> Self { assert!(!seed.iter().all(|&x| x == 0), "XorShiftRng::from_seed called with an all zero seed."); XorShiftRng { - x: w(seed[0] as u32), - y: w((seed[0] >> 32) as u32), - z: w(seed[1] as u32), - w: w((seed[1] >> 32) as u32), + x: w(le::read_u32(array_ref!(seed, 0, 4))), + y: w(le::read_u32(array_ref!(seed, 4, 4))), + z: w(le::read_u32(array_ref!(seed, 8, 4))), + w: w(le::read_u32(array_ref!(seed, 12, 4))), } } } diff --git a/src/reseeding.rs b/src/reseeding.rs index 6e38c7fee1c..5d95910825f 100644 --- a/src/reseeding.rs +++ b/src/reseeding.rs @@ -216,8 +216,9 @@ mod test { #[test] fn test_rng_seeded() { // Default seed threshold is way beyond what we use here - let mut ra: MyRng = ReseedingRng::from_reseeder(ReseedMock, 2); - let mut rb = MockAddRng::from_seed(2); + let seed = [0, 1, 2, 3, 4, 5, 6, 7]; + let mut ra: MyRng = ReseedingRng::from_reseeder(ReseedMock, seed); + let mut rb = MockAddRng::from_seed(seed); assert!(::test::iter_eq(iter(&mut ra).map(|rng| rng.next_u32()).take(100), iter(&mut rb).map(|rng| rng.next_u32()).take(100))); } diff --git a/src/seq.rs b/src/seq.rs index df7816c4292..cd320fc1894 100644 --- a/src/seq.rs +++ b/src/seq.rs @@ -301,7 +301,8 @@ mod test { for length in 1usize..max_range { let amount = r.gen_range(0, length); - let seed = [r.gen(), r.gen()]; + let mut seed = [0u8; 16]; + r.fill_bytes(&mut seed); println!("Selecting indices: len={}, amount={}, seed={:?}", length, amount, seed); From ada83bdf0eea855ce74791be0c0985c1681176c9 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 11 Dec 2017 18:47:50 +0000 Subject: [PATCH 220/247] Fix rand_core::le test on convert_slice_64 --- rand_core/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rand_core/src/lib.rs b/rand_core/src/lib.rs index 4ada973f00b..6fcd517dcb6 100644 --- a/rand_core/src/lib.rs +++ b/rand_core/src/lib.rs @@ -437,13 +437,14 @@ pub mod le { assert_eq!(read_u32(&[1, 2, 3, 4]), 0x04030201); assert_eq!(read_u64(&[1, 2, 3, 4, 5, 6, 7, 8]), 0x0807060504030201); - let mut bytes = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; { + let mut bytes = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; let slice = convert_slice_32(&mut bytes[..]); assert_eq!(slice[0], 0x04030201); assert_eq!(slice[3], 0x100F0E0D); } { + let mut bytes = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; let slice = convert_slice_64(&mut bytes[..]); assert_eq!(slice[0], 0x0807060504030201); assert_eq!(slice[1], 0x100F0E0D0C0B0A09); From 5a7dbb0d0521986373c2018c5f81867d7a0f03b0 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Mon, 11 Dec 2017 19:59:25 +0100 Subject: [PATCH 221/247] Add sample_single to Range --- benches/distributions.rs | 4 ++- src/distributions/range.rs | 57 ++++++++++++++++++++++++++++++++++++-- src/lib.rs | 3 +- 3 files changed, 58 insertions(+), 6 deletions(-) diff --git a/benches/distributions.rs b/benches/distributions.rs index ec2906a7353..48fe2c8aa64 100644 --- a/benches/distributions.rs +++ b/benches/distributions.rs @@ -75,11 +75,13 @@ macro_rules! gen_range_int { #[bench] fn $fnn(b: &mut Bencher) { let mut rng = XorShiftRng::new().unwrap(); + let high = $high; b.iter(|| { for _ in 0..::RAND_BENCH_N { - let x: $ty = Range::new($low, $high).sample(&mut rng); + let x: $ty = Range::sample_single($low, high, &mut rng); black_box(x); + black_box(high); } }); b.bytes = size_of::<$ty>() as u64 * ::RAND_BENCH_N; diff --git a/src/distributions/range.rs b/src/distributions/range.rs index 06d8ddaf464..8e9aafce4a2 100644 --- a/src/distributions/range.rs +++ b/src/distributions/range.rs @@ -66,6 +66,13 @@ impl Range> { assert!(low < high, "Range::new called with `low >= high`"); Range { inner: RangeImpl::new_inclusive(low, high) } } + + /// Sample a single value uniformly from `[low, high)`. + /// Panics if `low >= high`. + pub fn sample_single(low: X, high: X, rng: &mut R) -> X { + assert!(low < high, "Range::sample_single called with low >= high"); + X::T::sample_single(low, high, rng) + } } impl Distribution for Range { @@ -94,6 +101,10 @@ pub trait RangeImpl { /// Sample a value. fn sample(&self, rng: &mut R) -> Self::X; + + /// Sample a single value. + fn sample_single(low: Self::X, high: Self::X, rng: &mut R) + -> Self::X; } /// Implementation of `RangeImpl` for integer types. @@ -203,9 +214,9 @@ macro_rules! range_int_impl { loop { let v: $u_large = Rand::rand(rng, Uniform); if $use_mult { - let (high, low) = v.wmul(range); - if low <= zone { - return self.low.wrapping_add(high as $ty); + let (hi, lo) = v.wmul(range); + if lo <= zone { + return self.low.wrapping_add(hi as $ty); } } else { if v <= zone { @@ -218,6 +229,37 @@ macro_rules! range_int_impl { Rand::rand(rng, Uniform) } } + + fn sample_single(low: Self::X, high: Self::X, rng: &mut R) -> Self::X { + let range = (high as $u_large) + .wrapping_sub(low as $u_large); + let zone = + if ::core::$unsigned::MAX <= ::core::u16::MAX as $unsigned { + // Using a modulus is faster than the approximation for + // i8 and i16. I suppose we trade the cost of one + // modulus for near-perfect branch prediction. + let unsigned_max: $u_large = ::core::$u_large::MAX; + let ints_to_reject = (unsigned_max - range + 1) % range; + unsigned_max - ints_to_reject + } else { + // conservative but fast approximation + range << range.leading_zeros() + }; + + loop { + let v: $u_large = Rand::rand(rng, Uniform); + if $use_mult { + let (hi, lo) = v.wmul(range); + if lo <= zone { + return low.wrapping_add(hi as $ty); + } + } else { + if v <= zone { + return low.wrapping_add((v % range) as $ty); + } + } + } + } } } } @@ -448,6 +490,11 @@ macro_rules! range_float_impl { } } } + + fn sample_single(low: Self::X, high: Self::X, rng: &mut R) -> Self::X { + let range: Self = RangeImpl::new(low, high); + range.sample(rng) + } } } } @@ -576,6 +623,10 @@ mod tests { fn sample(&self, rng: &mut R) -> Self::X { MyF32 { x: self.inner.sample(rng) } } + fn sample_single(low: Self::X, high: Self::X, rng: &mut R) -> Self::X { + let range: Self = RangeImpl::new(low, high); + range.sample(rng) + } } impl SampleRange for MyF32 { type T = RangeMyF32; diff --git a/src/lib.rs b/src/lib.rs index 6d3645e7035..4f336131e9b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -425,8 +425,7 @@ pub trait Sample: Rng { /// } /// ``` fn gen_range(&mut self, low: T, high: T) -> T { - assert!(low < high, "Sample::gen_range called with low >= high"); - Range::new(low, high).sample(self) + Range::sample_single(low, high, self) } /// Construct an iterator on an `Rng`. From 4d832a7e418b69a7f8179c609a99d963608cd477 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Tue, 12 Dec 2017 07:15:27 +0100 Subject: [PATCH 222/247] Use mask to reduce range in ascii_word_char --- src/distributions/uniform.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/distributions/uniform.rs b/src/distributions/uniform.rs index fce3b6c9746..13aaa4620c7 100644 --- a/src/distributions/uniform.rs +++ b/src/distributions/uniform.rs @@ -54,12 +54,17 @@ pub fn codepoint(rng: &mut R) -> char { /// TODO: should this be removed in favour of `AsciiWordChar`? #[inline] pub fn ascii_word_char(rng: &mut R) -> char { - use sequences::Choose; + const RANGE: u32 = 26 + 26 + 10; const GEN_ASCII_STR_CHARSET: &'static [u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\ abcdefghijklmnopqrstuvwxyz\ 0123456789"; - *GEN_ASCII_STR_CHARSET.choose(rng).unwrap() as char + loop { + let var = rng.next_u32() & 0x3F; + if var < RANGE { + return GEN_ASCII_STR_CHARSET[var as usize] as char + } + } } From 4e79c0677b359a1ca47ebfb0db60fa15bfe7013e Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 12 Dec 2017 16:18:58 +0000 Subject: [PATCH 223/247] Improve documentation of range module --- src/distributions/mod.rs | 2 - src/distributions/range.rs | 86 +++++++++++++++++++++++++++++--------- 2 files changed, 67 insertions(+), 21 deletions(-) diff --git a/src/distributions/mod.rs b/src/distributions/mod.rs index 4c93fe5cc04..e0523cb93f7 100644 --- a/src/distributions/mod.rs +++ b/src/distributions/mod.rs @@ -14,8 +14,6 @@ //! generated values; for example `Range` needs to know its upper and lower //! bounds. Distributions use the `Distribution` trait to yield values: call //! `distr.sample(&mut rng)` to get a random variable. -//! -//! TODO: is it worth exposing both submodules and re-exporting their members? use Rng; diff --git a/src/distributions/range.rs b/src/distributions/range.rs index 06d8ddaf464..11abc4c6262 100644 --- a/src/distributions/range.rs +++ b/src/distributions/range.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! Generating numbers between two others. +//! A distribution generating numbers within a given range. use Rand; use Rng; @@ -18,22 +18,21 @@ use utils::FloatConversions; /// Sample values uniformly between two bounds. /// /// This gives a uniform distribution (assuming the RNG used to sample -/// it is itself uniform & the `RangeImpl` implementation is correct), +/// it is itself uniform and the `RangeImpl` implementation is correct), /// even for edge cases like `low = 0u8`, /// `high = 170u8`, for which a naive modulo operation would return /// numbers less than 85 with double the probability to those greater /// than 85. /// -/// Types should attempt to sample in `[low, high)`, i.e., not -/// including `high`, but this may be very difficult. All the -/// primitive integer types satisfy this property, and the float types -/// normally satisfy it, but rounding may mean `high` can occur. +/// Types should attempt to sample in `[low, high)` for +/// `Range::new(low, high)`, i.e., excluding `high`, but this may be very +/// difficult. All the primitive integer types satisfy this property, and the +/// float types normally satisfy it, but rounding may mean `high` can occur. /// /// # Example /// /// ```rust -/// use rand::distributions::Distribution; -/// use rand::distributions::range::Range; +/// use rand::distributions::{Distribution, Range}; /// /// fn main() { /// let between = Range::new(10, 10000); @@ -50,18 +49,19 @@ pub struct Range { inner: T, } -// Range must be parameterised so that `Self` is fully typed, but we don't -// actually use this type, so we just use any valid type here. (Minor lang bug?) +/// Ignore the `RangeInt` parameterisation; these non-member functions are +/// available to all range types. (Rust requires a type, and won't accept +/// `T: RangeImpl`. Consider this a minor language issue.) impl Range> { - /// Create a new `Range` instance that samples uniformly from - /// `[low, high)`. Panics if `low >= high`. + /// Create a new `Range` instance which samples uniformly from the half + /// open range `[low, high)` (excluding `high`). Panics if `low >= high`. pub fn new(low: X, high: X) -> Range { assert!(low < high, "Range::new called with `low >= high`"); Range { inner: RangeImpl::new(low, high) } } - /// Create a new `Range` instance that samples uniformly from - /// `[low, high]` (inclusive). Panics if `low >= high`. + /// Create a new `Range` instance which samples uniformly from the closed + /// range `[low, high]` (inclusive). Panics if `low >= high`. pub fn new_inclusive(low: X, high: X) -> Range { assert!(low < high, "Range::new called with `low >= high`"); Range { inner: RangeImpl::new_inclusive(low, high) } @@ -74,22 +74,66 @@ impl Distribution for Range { } } -/// Helper trait for creating implementations of `RangeImpl`. +/// Helper trait for creating objects using the correct implementation of +/// `RangeImpl` for the sampling type; this enables `Range::new(a, b)` to work. pub trait SampleRange: PartialOrd+Sized { type T: RangeImpl; } /// Helper trait handling actual range sampling. +/// +/// If you want to implement `Range` sampling for your own type, then +/// implement both this trait and `SampleRange`: +/// +/// ```rust +/// use rand::{Rng, thread_rng, Distribution}; +/// use rand::distributions::range::{Range, SampleRange, RangeImpl, RangeFloat}; +/// +/// #[derive(Clone, Copy, PartialEq, PartialOrd)] +/// struct MyF32(f32); +/// +/// #[derive(Clone, Copy, Debug)] +/// struct RangeMyF32 { +/// inner: RangeFloat, +/// } +/// impl RangeImpl for RangeMyF32 { +/// type X = MyF32; +/// fn new(low: Self::X, high: Self::X) -> Self { +/// RangeMyF32 { +/// inner: RangeFloat::::new(low.0, high.0), +/// } +/// } +/// fn new_inclusive(low: Self::X, high: Self::X) -> Self { +/// RangeImpl::new(low, high) +/// } +/// fn sample(&self, rng: &mut R) -> Self::X { +/// MyF32(self.inner.sample(rng)) +/// } +/// } +/// +/// impl SampleRange for MyF32 { +/// type T = RangeMyF32; +/// } +/// +/// let (low, high) = (MyF32(17.0f32), MyF32(22.0f32)); +/// let range = Range::new(low, high); +/// let x = range.sample(&mut thread_rng()); +/// ``` pub trait RangeImpl { - /// The type sampled by this implementation. + /// The type sampled by this implementation (output type). type X: PartialOrd; - /// Construct self. + /// Construct self, with inclusive lower bound and exclusive upper bound + /// `[low, high)`. /// - /// This should not be called directly. `Range::new` asserts that - /// `low < high` before calling this. + /// Usually users should not call this directly but instead use + /// `Range::new`, which asserts that `low < high` before calling this. fn new(low: Self::X, high: Self::X) -> Self; + /// Construct self, with inclusive bounds `[low, high]`. + /// + /// Usually users should not call this directly but instead use + /// `Range::new`, which asserts that `low < high` before calling this. fn new_inclusive(low: Self::X, high: Self::X) -> Self; /// Sample a value. @@ -340,14 +384,18 @@ wmul_impl_128! { u128 } #[derive(Clone, Copy, Debug)] pub enum RangeFloat { // A range that is completely positive + #[doc(hidden)] Positive { offset: X, scale: X }, // A range that is completely negative + #[doc(hidden)] Negative { offset: X, scale: X }, // A range that is goes from negative to positive, but has more positive // than negative numbers + #[doc(hidden)] PosAndNeg { cutoff: X, scale: X }, // A range that is goes from negative to positive, but has more negative // than positive numbers + #[doc(hidden)] NegAndPos { cutoff: X, scale: X }, } From 259281e06e47a93905340d7c080908add424d00f Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 12 Dec 2017 17:48:17 +0000 Subject: [PATCH 224/247] Select "SimpleRand" over generic "Rand" for backwards compatibility --- src/distributions/default.rs | 14 ++++----- src/distributions/exponential.rs | 4 +-- src/distributions/gamma.rs | 6 ++-- src/distributions/mod.rs | 51 +++++++------------------------- src/distributions/normal.rs | 6 ++-- src/distributions/range.rs | 5 ++-- src/distributions/uniform.rs | 14 ++++----- src/thread_local.rs | 17 +++++------ 8 files changed, 42 insertions(+), 75 deletions(-) diff --git a/src/distributions/default.rs b/src/distributions/default.rs index 054598e774f..5dd5612f57e 100644 --- a/src/distributions/default.rs +++ b/src/distributions/default.rs @@ -11,7 +11,7 @@ //! Generic value creation use Rng; -use distributions::{Distribution, Rand}; +use distributions::Distribution; use distributions::uniform::{Uniform, Uniform01, codepoint}; /// A generic random value distribution. Generates values using what appears to @@ -32,27 +32,27 @@ pub struct Default; // ----- implementations ----- -impl> Distribution for Default { +impl Distribution for Default where Uniform: Distribution{ fn sample(&self, rng: &mut R) -> T { - T::rand(rng, Uniform) + Uniform.sample(rng) } } // FIXME: https://github.com/rust-lang/rust/issues/23341 -// impl> Distribution for Default { +// impl Distribution for Default where Uniform01: Distribution{ // fn sample(&self, rng: &mut R) -> T { -// T::rand(rng, Uniform01) +// Uniform01.sample(rng) // } // } // workaround above issue: impl Distribution for Default { fn sample(&self, rng: &mut R) -> f32 { - f32::rand(rng, Uniform01) + Uniform01.sample(rng) } } impl Distribution for Default { fn sample(&self, rng: &mut R) -> f64 { - f64::rand(rng, Uniform01) + Uniform01.sample(rng) } } diff --git a/src/distributions/exponential.rs b/src/distributions/exponential.rs index be8616391e2..48a988dee3f 100644 --- a/src/distributions/exponential.rs +++ b/src/distributions/exponential.rs @@ -11,7 +11,7 @@ //! The exponential distribution. use {Rng}; -use distributions::{ziggurat, ziggurat_tables, Distribution, Uniform01, Rand}; +use distributions::{ziggurat, ziggurat_tables, Distribution, Uniform01}; /// Generates Exp(1) random numbers. /// @@ -43,7 +43,7 @@ pub fn exp1(rng: &mut R) -> f64 { } #[inline] fn zero_case(rng: &mut R, _u: f64) -> f64 { - let x = f64::rand(rng, Uniform01); + let x: f64 = Uniform01.sample(rng); ziggurat_tables::ZIG_EXP_R - x.ln() } diff --git a/src/distributions/gamma.rs b/src/distributions/gamma.rs index 93f1a4835c3..5ddbc1cc6c5 100644 --- a/src/distributions/gamma.rs +++ b/src/distributions/gamma.rs @@ -17,7 +17,7 @@ use self::ChiSquaredRepr::*; use {Rng}; use distributions::normal::standard_normal; -use distributions::{Distribution, Exp, Rand, Open01}; +use distributions::{Distribution, Exp, Open01}; /// The Gamma distribution `Gamma(shape, scale)` distribution. /// @@ -145,7 +145,7 @@ impl Distribution for Gamma { } impl Distribution for GammaSmallShape { fn sample(&self, rng: &mut R) -> f64 { - let u = f64::rand(rng, Open01); + let u: f64 = Open01.sample(rng); self.large_shape.sample(rng) * u.powf(self.inv_shape) } @@ -160,7 +160,7 @@ impl Distribution for GammaLargeShape { } let v = v_cbrt * v_cbrt * v_cbrt; - let u = f64::rand(rng, Open01); + let u: f64 = Open01.sample(rng); let x_sqr = x * x; if u < 1.0 - 0.0331 * x_sqr * x_sqr || diff --git a/src/distributions/mod.rs b/src/distributions/mod.rs index e0523cb93f7..add3785e180 100644 --- a/src/distributions/mod.rs +++ b/src/distributions/mod.rs @@ -77,55 +77,26 @@ impl<'a, T, D: Distribution> Distribution for &'a D { } } -/// Generic trait for sampling random values from some distribution +/// Trait for sampling values from the `Default` distribution. /// -/// TODO: quite possibly remove both this and `SimpleRand` since `Sample` is -/// more convenient and distributions like `Default` handle all the real work. +/// This is mostly a shim around `Default` for backwards compatibility; the +/// implementation is simply `Default.sample(rng)`. /// /// # Example /// ```rust -/// use rand::distributions::{Rand, Default, Range}; +/// use rand::{Rand, thread_rng}; /// -/// let mut rng = rand::thread_rng(); -/// println!("Random byte: {}", u8::rand(&mut rng, Default)); -/// println!("Random range: {}", i32::rand(&mut rng, Range::new(-99, 100))); -/// ``` -pub trait Rand { - /// Generate a random value of the given type, according to the specified - /// distribution. - /// - /// The distributions `Default` (or `Uniform` and `Uniform01`) and `Range` - /// should cover most simpler usages; `Normal`, `LogNormal`, `Exp`, `Gamma` - /// and a few others are also available. - fn rand(rng: &mut R, distr: D) -> Self; -} - -impl> Rand for T { - fn rand(rng: &mut R, distr: D) -> Self { - distr.sample(rng) - } -} - -/// Simpler version of `Rand`, without support for alternative distributions. -/// -/// TODO: decide which version of `Rand` to keep. If this one, rename struct to -/// `Rand` and function to `rand`. -/// -/// # Example -/// ```rust -/// use rand::distributions::SimpleRand; -/// -/// let mut rng = rand::thread_rng(); -/// println!("Random byte: {}", u8::simple_rand(&mut rng)); +/// let mut rng = thread_rng(); +/// println!("Random byte: {}", u8::rand(&mut rng)); /// ``` -pub trait SimpleRand { +pub trait Rand : Sized { /// Generate a random value of the given type, using the `Default` /// distribution. - fn simple_rand(rng: &mut R) -> Self; + fn rand(rng: &mut R) -> Self; } -impl SimpleRand for T where Default: Distribution { - fn simple_rand(rng: &mut R) -> Self { +impl Rand for T where Default: Distribution { + fn rand(rng: &mut R) -> Self { Default.sample(rng) } } @@ -190,7 +161,7 @@ fn ziggurat( return zero_case(rng, u); } // algebraically equivalent to f1 + DRanU()*(f0 - f1) < 1 - if f_tab[i + 1] + (f_tab[i] - f_tab[i + 1]) * f64::rand(rng, Uniform01) < pdf(x) { + if f_tab[i + 1] + (f_tab[i] - f_tab[i + 1]) * rng.sample::(Uniform01) < pdf(x) { return x; } } diff --git a/src/distributions/normal.rs b/src/distributions/normal.rs index 6cda325df46..92664b94a18 100644 --- a/src/distributions/normal.rs +++ b/src/distributions/normal.rs @@ -11,7 +11,7 @@ //! The normal and derived distributions. use {Rng}; -use distributions::{ziggurat, ziggurat_tables, Distribution, Rand, Open01}; +use distributions::{ziggurat, ziggurat_tables, Distribution, Open01}; /// Generates N(0, 1) random numbers /// (a.k.a. a standard normal, or Gaussian). @@ -50,8 +50,8 @@ pub fn standard_normal(rng: &mut R) -> f64 { let mut y = 0.0f64; while -2.0 * y < x * x { - let x_ = f64::rand(rng, Open01); - let y_ = f64::rand(rng, Open01); + let x_: f64 = Open01.sample(rng); + let y_: f64 = Open01.sample(rng); x = x_.ln() / ziggurat_tables::ZIG_NORM_R; y = y_.ln(); diff --git a/src/distributions/range.rs b/src/distributions/range.rs index 11abc4c6262..5f51d1ec32b 100644 --- a/src/distributions/range.rs +++ b/src/distributions/range.rs @@ -10,7 +10,6 @@ //! A distribution generating numbers within a given range. -use Rand; use Rng; use distributions::{Distribution, Uniform}; use utils::FloatConversions; @@ -245,7 +244,7 @@ macro_rules! range_int_impl { // casting is a no-op. let zone = self.zone as $signed as $i_large as $u_large; loop { - let v: $u_large = Rand::rand(rng, Uniform); + let v: $u_large = Uniform.sample(rng); if $use_mult { let (high, low) = v.wmul(range); if low <= zone { @@ -259,7 +258,7 @@ macro_rules! range_int_impl { } } else { // Sample from the entire integer range. - Rand::rand(rng, Uniform) + Uniform.sample(rng) } } } diff --git a/src/distributions/uniform.rs b/src/distributions/uniform.rs index fce3b6c9746..b4e4fa7c8da 100644 --- a/src/distributions/uniform.rs +++ b/src/distributions/uniform.rs @@ -14,7 +14,7 @@ use core::char; use core::mem; use Rng; -use distributions::{Distribution, Rand}; +use distributions::Distribution; use utils::FloatConversions; // ----- convenience functions ----- @@ -23,8 +23,8 @@ use utils::FloatConversions; /// /// This method has precisely two template parameters. To fix the output type, /// use the syntax `uniform::(rng)`. -pub fn uniform, R: Rng+?Sized>(rng: &mut R) -> T { - T::rand(rng, Uniform) +pub fn uniform(rng: &mut R) -> T where Uniform: Distribution { + Uniform.sample(rng) } /// Sample a `char`, uniformly distributed over all Unicode scalar values, @@ -91,9 +91,9 @@ pub struct AsciiWordChar; impl Distribution for Uniform { fn sample(&self, rng: &mut R) -> isize { if mem::size_of::() == 4 { - i32::rand(rng, Uniform) as isize + Distribution::::sample(&Uniform, rng) as isize } else { - i64::rand(rng, Uniform) as isize + Distribution::::sample(&Uniform, rng) as isize } } } @@ -138,9 +138,9 @@ impl Distribution for Uniform { #[inline] fn sample(&self, rng: &mut R) -> usize { if mem::size_of::() == 4 { - u32::rand(rng, Uniform) as usize + Distribution::::sample(&Uniform, rng) as usize } else { - u64::rand(rng, Uniform) as usize + Distribution::::sample(&Uniform, rng) as usize } } } diff --git a/src/thread_local.rs b/src/thread_local.rs index 0d7348ec244..fc99a65b67c 100644 --- a/src/thread_local.rs +++ b/src/thread_local.rs @@ -13,7 +13,7 @@ use std::cell::RefCell; use std::rc::Rc; -use {Rng, StdRng, NewSeeded, Rand, Default, Error}; +use {Rng, StdRng, NewSeeded, Distribution, Default, Sample, Error}; use reseeding::{ReseedingRng, ReseedWithNew}; @@ -100,7 +100,7 @@ pub fn thread_rng() -> ThreadRng { /// Caching the thread local random number generator: /// /// ``` -/// use rand::{Rand, Default}; +/// use rand::{Rand, Sample}; /// /// let mut v = vec![1, 2, 3]; /// @@ -113,15 +113,12 @@ pub fn thread_rng() -> ThreadRng { /// let mut rng = rand::thread_rng(); /// /// for x in v.iter_mut() { -/// *x = Rand::rand(&mut rng, Default); +/// *x = rng.gen() /// } /// ``` -/// -/// Note that the above example uses `SampleDefault` which is a zero-sized -/// marker type. #[inline] -pub fn random>() -> T { - T::rand(&mut thread_rng(), Default) +pub fn random() -> T where Default: Distribution { + thread_rng().sample(Default) } /// Generates a random value using the thread-local random number generator. @@ -153,6 +150,6 @@ pub fn random>() -> T { /// let v = f64::rand(&mut rng, range); /// ``` #[inline] -pub fn random_with>(distribution: D) -> T { - T::rand(&mut thread_rng(), distribution) +pub fn random_with(distribution: D) -> T where D: Distribution { + thread_rng().sample(distribution) } From 1913e08afecb29adba6481832ff5c480fbb6f06a Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Wed, 13 Dec 2017 08:01:55 +0100 Subject: [PATCH 225/247] Add a default implementation for sample_single --- src/distributions/range.rs | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/distributions/range.rs b/src/distributions/range.rs index 8e9aafce4a2..4a15800af1a 100644 --- a/src/distributions/range.rs +++ b/src/distributions/range.rs @@ -87,7 +87,7 @@ pub trait SampleRange: PartialOrd+Sized { } /// Helper trait handling actual range sampling. -pub trait RangeImpl { +pub trait RangeImpl: Sized { /// The type sampled by this implementation. type X: PartialOrd; @@ -104,7 +104,11 @@ pub trait RangeImpl { /// Sample a single value. fn sample_single(low: Self::X, high: Self::X, rng: &mut R) - -> Self::X; + -> Self::X + { + let range: Self = RangeImpl::new(low, high); + range.sample(rng) + } } /// Implementation of `RangeImpl` for integer types. @@ -490,11 +494,6 @@ macro_rules! range_float_impl { } } } - - fn sample_single(low: Self::X, high: Self::X, rng: &mut R) -> Self::X { - let range: Self = RangeImpl::new(low, high); - range.sample(rng) - } } } } @@ -623,10 +622,6 @@ mod tests { fn sample(&self, rng: &mut R) -> Self::X { MyF32 { x: self.inner.sample(rng) } } - fn sample_single(low: Self::X, high: Self::X, rng: &mut R) -> Self::X { - let range: Self = RangeImpl::new(low, high); - range.sample(rng) - } } impl SampleRange for MyF32 { type T = RangeMyF32; From af4f3f9d65505ccce9f7a2c36efdbfbf9f32bd85 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Thu, 14 Dec 2017 11:47:30 +0000 Subject: [PATCH 226/247] Fix tests: use Sample not Rand --- src/distributions/default.rs | 21 ++++++++--------- src/distributions/range.rs | 10 ++++---- src/distributions/uniform.rs | 45 +++++++++++++++++------------------- src/lib.rs | 15 ++++++------ src/thread_local.rs | 8 +++---- src/utils.rs | 2 +- 6 files changed, 48 insertions(+), 53 deletions(-) diff --git a/src/distributions/default.rs b/src/distributions/default.rs index 5dd5612f57e..206e7caba41 100644 --- a/src/distributions/default.rs +++ b/src/distributions/default.rs @@ -65,23 +65,20 @@ impl Distribution for Default { #[cfg(test)] mod tests { - use {Rng, thread_rng}; - use distributions::{Rand, Default}; + use {Rng, Sample, thread_rng}; + use distributions::{Default}; #[test] fn test_types() { let rng: &mut Rng = &mut thread_rng(); - fn do_test>(rng: &mut Rng) -> T { - T::rand(rng, Default) - } - do_test::(rng); - do_test::(rng); - do_test::(rng); - do_test::(rng); + rng.sample::(Default); + rng.sample::(Default); + rng.sample::(Default); + rng.sample::(Default); #[cfg(feature = "i128_support")] - do_test::(rng); - do_test::(rng); - do_test::(rng); + rng.sample::(Default); + rng.sample::(Default); + rng.sample::(Default); } } diff --git a/src/distributions/range.rs b/src/distributions/range.rs index 5f51d1ec32b..77c66bf6ddf 100644 --- a/src/distributions/range.rs +++ b/src/distributions/range.rs @@ -504,8 +504,8 @@ range_float_impl! { f64, Rng::next_u64 } #[cfg(test)] mod tests { - use {Rng, thread_rng}; - use distributions::{Rand, Distribution}; + use {Rng, Sample, thread_rng}; + use distributions::{Distribution}; use distributions::range::{Range, RangeImpl, RangeFloat, SampleRange}; #[test] @@ -563,7 +563,7 @@ mod tests { for &(low, high) in v.iter() { let my_range = Range::new(low, high); for _ in 0..1000 { - let v: $ty = Rand::rand(&mut rng, my_range); + let v: $ty = rng.sample(my_range); assert!(low <= v && v < high); } } @@ -589,7 +589,7 @@ mod tests { for &(low, high) in v.iter() { let my_range = Range::new(low, high); for _ in 0..1000 { - let v: $ty = Rand::rand(&mut rng, my_range); + let v: $ty = rng.sample(my_range); assert!(low <= v && v < high); } } @@ -632,7 +632,7 @@ mod tests { let range = Range::new(low, high); let mut rng = ::test::rng(); for _ in 0..100 { - let x = MyF32::rand(&mut rng, range); + let x: MyF32 = rng.sample(range); assert!(low <= x && x < high); } } diff --git a/src/distributions/uniform.rs b/src/distributions/uniform.rs index b4e4fa7c8da..935e770681b 100644 --- a/src/distributions/uniform.rs +++ b/src/distributions/uniform.rs @@ -247,32 +247,29 @@ impl Distribution for AsciiWordChar { #[cfg(test)] mod tests { - use {thread_rng, iter}; - use distributions::{Rand, uniform, Uniform, Uniform01, Open01, Closed01}; + use {Sample, thread_rng, iter}; + use distributions::{Uniform, Uniform01, Open01, Closed01}; use distributions::uniform::{codepoint, ascii_word_char}; #[test] fn test_integers() { let mut rng = ::test::rng(); - let _: i32 = Rand::rand(&mut rng, Uniform); - let _ = i32::rand(&mut rng, Uniform); - - let _: isize = uniform(&mut rng); - let _: i8 = uniform(&mut rng); - let _: i16 = uniform(&mut rng); - let _: i32 = uniform(&mut rng); - let _: i64 = uniform(&mut rng); + rng.sample::(Uniform); + rng.sample::(Uniform); + rng.sample::(Uniform); + rng.sample::(Uniform); + rng.sample::(Uniform); #[cfg(feature = "i128_support")] - let _: i128 = uniform(&mut rng); + rng.sample::(Uniform); - let _: usize = uniform(&mut rng); - let _: u8 = uniform(&mut rng); - let _: u16 = uniform(&mut rng); - let _: u32 = uniform(&mut rng); - let _: u64 = uniform(&mut rng); + rng.sample::(Uniform); + rng.sample::(Uniform); + rng.sample::(Uniform); + rng.sample::(Uniform); + rng.sample::(Uniform); #[cfg(feature = "i128_support")] - let _: u128 = uniform(&mut rng); + rng.sample::(Uniform); } #[test] @@ -289,9 +286,9 @@ mod tests { #[test] fn test_f64() { - let mut r = thread_rng(); - let a: f64 = Rand::rand(&mut r, Uniform01); - let b = f64::rand(&mut r, Uniform01); + let mut rng = thread_rng(); + let a: f64 = rng.sample(Uniform01); + let b = rng.sample::(Uniform01); assert!(0.0 <= a && a < 1.0); assert!(0.0 <= b && b < 1.0); } @@ -303,10 +300,10 @@ mod tests { let mut rng = thread_rng(); for _ in 0..1_000 { // strict inequalities - let f = f64::rand(&mut rng, Open01); + let f: f64 = rng.sample(Open01); assert!(0.0 < f && f < 1.0); - let f = f32::rand(&mut rng, Open01); + let f: f32 = rng.sample(Open01); assert!(0.0 < f && f < 1.0); } } @@ -316,10 +313,10 @@ mod tests { let mut rng = thread_rng(); for _ in 0..1_000 { // strict inequalities - let f = f64::rand(&mut rng, Closed01); + let f: f64 = rng.sample(Closed01); assert!(0.0 <= f && f <= 1.0); - let f = f32::rand(&mut rng, Closed01); + let f: f32 = rng.sample(Closed01); assert!(0.0 <= f && f <= 1.0); } } diff --git a/src/lib.rs b/src/lib.rs index 6d3645e7035..175cc5a4abd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -86,10 +86,10 @@ //! ``` //! //! ```rust -//! use rand::thread_rng; -//! use rand::distributions::{Rand, Uniform, Uniform01}; +//! use rand::{Sample, thread_rng}; +//! use rand::distributions::{Uniform, Uniform01}; //! let mut rng = thread_rng(); -//! let tuple = (f64::rand(&mut rng, Uniform01), u8::rand(&mut rng, Uniform)); +//! let tuple: (f64, u8) = (rng.sample(Uniform01), rng.sample(Uniform)); //! println!("{:?}", tuple) //! ``` //! @@ -111,18 +111,19 @@ //! and multiply this fraction by 4. //! //! ``` -//! use rand::distributions::{Rand, Range}; +//! use rand::{thread_rng, Sample}; +//! use rand::distributions::{Range}; //! //! fn main() { //! let between = Range::new(-1f64, 1.); -//! let mut rng = rand::thread_rng(); +//! let mut rng = thread_rng(); //! //! let total = 1_000_000; //! let mut in_circle = 0; //! //! for _ in 0..total { -//! let a = f64::rand(&mut rng, between); -//! let b = f64::rand(&mut rng, between); +//! let a = rng.sample(between); +//! let b = rng.sample(between); //! if a*a + b*b <= 1. { //! in_circle += 1; //! } diff --git a/src/thread_local.rs b/src/thread_local.rs index fc99a65b67c..fe5b9e46191 100644 --- a/src/thread_local.rs +++ b/src/thread_local.rs @@ -100,7 +100,7 @@ pub fn thread_rng() -> ThreadRng { /// Caching the thread local random number generator: /// /// ``` -/// use rand::{Rand, Sample}; +/// use rand::{Sample}; /// /// let mut v = vec![1, 2, 3]; /// @@ -127,8 +127,8 @@ pub fn random() -> T where Default: Distribution { /// distribution used. For example: /// /// ``` -/// use rand::random_with; -/// use rand::distributions::{Rand, Default, Uniform01, Closed01, Range}; +/// use rand::{Sample, random_with}; +/// use rand::distributions::{Default, Uniform01, Closed01, Range}; /// /// // identical to calling `random()`: /// let x: f64 = random_with(Default); @@ -147,7 +147,7 @@ pub fn random() -> T where Default: Distribution { /// let mut rng = rand::thread_rng(); /// let range = Range::new(0.0, 2.0); /// // Do this bit many times: -/// let v = f64::rand(&mut rng, range); +/// let v = rng.sample(range); /// ``` #[inline] pub fn random_with(distribution: D) -> T where D: Distribution { diff --git a/src/utils.rs b/src/utils.rs index 406d721b565..3f9736390fd 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -10,7 +10,7 @@ //! A collection of utility functions //! -//! This module exposes some utility functions used internally by the `Rand` +//! This module exposes some utility functions used internally by the `rand` //! crate. They are not the intended or most ergonomic way to use the library. //! //! The `FloatConversions` trait provides the building blocks to convert a From 9d236da3a4a5290e694a4e2e607f046126a63a07 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Thu, 14 Dec 2017 12:01:38 +0000 Subject: [PATCH 227/247] Replace uniform, codepoint and ascii_word_char functions with distributions --- src/distributions/default.rs | 9 ++-- src/distributions/mod.rs | 5 +- src/distributions/uniform.rs | 89 +++++++++++++++--------------------- src/iter.rs | 18 ++++---- src/lib.rs | 15 +++--- src/read.rs | 4 +- src/utils.rs | 15 +++--- 7 files changed, 67 insertions(+), 88 deletions(-) diff --git a/src/distributions/default.rs b/src/distributions/default.rs index 206e7caba41..fab58fae085 100644 --- a/src/distributions/default.rs +++ b/src/distributions/default.rs @@ -12,7 +12,7 @@ use Rng; use distributions::Distribution; -use distributions::uniform::{Uniform, Uniform01, codepoint}; +use distributions::uniform::{Uniform, Uniform01, Codepoint}; /// A generic random value distribution. Generates values using what appears to /// be "the best" distribution for each type, but ultimately the choice is arbitrary. @@ -21,10 +21,7 @@ use distributions::uniform::{Uniform, Uniform01, codepoint}; /// /// * [`Uniform`] for integer types /// * [`Uniform01`] for floating point types -/// -/// Makes use of the following methods: -/// -/// * [`codepoint`] for `char` +/// * [`Codepoint`] for `char` /// /// TODO: link #[derive(Debug)] @@ -58,7 +55,7 @@ impl Distribution for Default { impl Distribution for Default { fn sample(&self, rng: &mut R) -> char { - codepoint(rng) + Codepoint.sample(rng) } } diff --git a/src/distributions/mod.rs b/src/distributions/mod.rs index add3785e180..6db1a5af15c 100644 --- a/src/distributions/mod.rs +++ b/src/distributions/mod.rs @@ -18,8 +18,7 @@ use Rng; pub use self::default::Default; -pub use self::uniform::{uniform, codepoint, ascii_word_char}; -pub use self::uniform::{Uniform, Uniform01, Open01, Closed01, AsciiWordChar}; +pub use self::uniform::{Uniform, Uniform01, Open01, Closed01, Codepoint, AsciiWordChar}; pub use self::range::Range; #[cfg(feature="std")] @@ -136,7 +135,7 @@ fn ziggurat( // precision of using 64 bits for f64 does not buy us much. // Because for some RNG's the least significant bits can be of lower // statistical quality, we use bits 3..10 for i. - let bits: u64 = uniform(rng); + let bits: u64 = rng.sample(Uniform); // u is either U(-1, 1) or U(0, 1) depending on if this is a // symmetric distribution or not. diff --git a/src/distributions/uniform.rs b/src/distributions/uniform.rs index 935e770681b..bd70e58ab5c 100644 --- a/src/distributions/uniform.rs +++ b/src/distributions/uniform.rs @@ -17,52 +17,6 @@ use Rng; use distributions::Distribution; use utils::FloatConversions; -// ----- convenience functions ----- - -/// Sample values uniformly over the whole range supported by the type -/// -/// This method has precisely two template parameters. To fix the output type, -/// use the syntax `uniform::(rng)`. -pub fn uniform(rng: &mut R) -> T where Uniform: Distribution { - Uniform.sample(rng) -} - -/// Sample a `char`, uniformly distributed over all Unicode scalar values, -/// i.e. all code points in the range `0...0x10_FFFF`, except for the range -/// `0xD800...0xDFFF` (the surrogate code points). This includes -/// unassigned/reserved code points. -/// -/// TODO: should this be removed in favour of a distribution? -#[inline] -pub fn codepoint(rng: &mut R) -> char { - // a char is 21 bits - const CHAR_MASK: u32 = 0x001f_ffff; - loop { - // Rejection sampling. About 0.2% of numbers with at most - // 21-bits are invalid codepoints (surrogates), so this - // will succeed first go almost every time. - match char::from_u32(rng.next_u32() & CHAR_MASK) { - Some(c) => return c, - None => {} - } - } -} - -/// Sample a `char`, uniformly distributed over ASCII letters and numbers: -/// a-z, A-Z and 0-9. -/// -/// TODO: should this be removed in favour of `AsciiWordChar`? -#[inline] -pub fn ascii_word_char(rng: &mut R) -> char { - use sequences::Choose; - const GEN_ASCII_STR_CHARSET: &'static [u8] = - b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\ - abcdefghijklmnopqrstuvwxyz\ - 0123456789"; - *GEN_ASCII_STR_CHARSET.choose(rng).unwrap() as char -} - - // ----- Sampling distributions ----- /// Sample values uniformly over the whole range supported by the type @@ -81,7 +35,15 @@ pub struct Open01; #[derive(Debug)] pub struct Closed01; -/// Sample values uniformly from the ASCII ranges z-a, A-Z, and 0-9. +/// Sample a `char`, uniformly distributed over all Unicode scalar values, +/// i.e. all code points in the range `0...0x10_FFFF`, except for the range +/// `0xD800...0xDFFF` (the surrogate code points). This includes +/// unassigned/reserved code points. +#[derive(Debug)] +pub struct Codepoint; + +/// Sample a `char`, uniformly distributed over ASCII letters and numbers: +/// a-z, A-Z and 0-9. #[derive(Debug)] pub struct AsciiWordChar; @@ -238,9 +200,30 @@ float_impls! { f32, Rng::next_u32 } float_impls! { f64, Rng::next_u64 } +impl Distribution for Codepoint { + fn sample(&self, rng: &mut R) -> char { + // a char is 21 bits + const CHAR_MASK: u32 = 0x001f_ffff; + loop { + // Rejection sampling. About 0.2% of numbers with at most + // 21-bits are invalid codepoints (surrogates), so this + // will succeed first go almost every time. + match char::from_u32(rng.next_u32() & CHAR_MASK) { + Some(c) => return c, + None => {} + } + } + } +} + impl Distribution for AsciiWordChar { fn sample(&self, rng: &mut R) -> char { - ascii_word_char(rng) + use sequences::Choose; + const GEN_ASCII_STR_CHARSET: &'static [u8] = + b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\ + abcdefghijklmnopqrstuvwxyz\ + 0123456789"; + *GEN_ASCII_STR_CHARSET.choose(rng).unwrap() as char } } @@ -248,8 +231,8 @@ impl Distribution for AsciiWordChar { #[cfg(test)] mod tests { use {Sample, thread_rng, iter}; - use distributions::{Uniform, Uniform01, Open01, Closed01}; - use distributions::uniform::{codepoint, ascii_word_char}; + use distributions::{Uniform, Uniform01, Open01, Closed01, + Codepoint, AsciiWordChar}; #[test] fn test_integers() { @@ -276,11 +259,11 @@ mod tests { fn test_chars() { let mut rng = ::test::rng(); - let _ = codepoint(&mut rng); - let c = ascii_word_char(&mut rng); + let _ = rng.sample(Codepoint); + let c = rng.sample(AsciiWordChar); assert!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')); - let word: String = iter(&mut rng).take(5).map(|rng| ascii_word_char(rng)).collect(); + let word: String = iter(&mut rng).take(5).map(|rng| rng.sample(AsciiWordChar)).collect(); assert_eq!(word.len(), 5); } diff --git a/src/iter.rs b/src/iter.rs index a819d33cdf7..1d44d2c9d0f 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -36,14 +36,14 @@ pub struct Iter<'a, R: Rng+?Sized+'a> { /// # Example /// /// ``` -/// use rand::{thread_rng, Rng, iter}; -/// use rand::distributions::{uniform, ascii_word_char}; +/// use rand::{thread_rng, Rng, Sample, iter}; +/// use rand::distributions::{Uniform, AsciiWordChar}; /// /// let mut rng = thread_rng(); -/// let x: Vec = iter(&mut rng).take(10).map(|rng| uniform(rng)).collect(); +/// let x: Vec = iter(&mut rng).take(10).map(|rng| rng.sample(Uniform)).collect(); /// println!("{:?}", x); /// -/// let w: String = iter(&mut rng).take(6).map(|rng| ascii_word_char(rng)).collect(); +/// let w: String = iter(&mut rng).take(6).map(|rng| rng.sample(AsciiWordChar)).collect(); /// println!("{}", w); /// ``` pub fn iter<'a, R: Rng+?Sized+'a>(rng: &'a mut R) -> Iter<'a, R> { @@ -159,8 +159,8 @@ impl<'a, R:?Sized+'a, U, F> Iterator for FlatMap<'a, R, U, F> #[cfg(test)] mod tests { - use {Rng, thread_rng, iter}; - use distributions::{uniform, ascii_word_char}; + use {Rng, Sample, thread_rng, iter}; + use distributions::{Uniform, AsciiWordChar}; #[test] fn test_iter() { @@ -168,10 +168,10 @@ mod tests { let x: Vec<()> = iter(&mut rng).take(10).map(|_| ()).collect(); assert_eq!(x.len(), 10); - let y: Vec = iter(&mut rng).take(10).map(|rng| uniform(rng)).collect(); + let y: Vec = iter(&mut rng).take(10).map(|rng| rng.sample(Uniform)).collect(); assert_eq!(y.len(), 10); let z: Vec = iter(&mut rng).take(10).flat_map(|rng| - vec![uniform(rng), uniform(rng)].into_iter()).collect(); + vec![rng.sample(Uniform), rng.sample(Uniform)].into_iter()).collect(); assert_eq!(z.len(), 20); let w: Vec = iter(&mut rng).take(10).flat_map(|_| vec![].into_iter()).collect(); assert_eq!(w.len(), 0); @@ -181,7 +181,7 @@ mod tests { fn test_dyn_dispatch() { let r: &mut Rng = &mut thread_rng(); - let x: String = iter(r).take(10).map(|rng| ascii_word_char(rng)).collect(); + let x: String = iter(r).take(10).map(|rng| rng.sample(AsciiWordChar)).collect(); assert_eq!(x.len(), 10); } } diff --git a/src/lib.rs b/src/lib.rs index 175cc5a4abd..eb083e93bd8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -154,8 +154,8 @@ //! [Monty Hall Problem]: http://en.wikipedia.org/wiki/Monty_Hall_problem //! //! ``` -//! use rand::Rng; -//! use rand::distributions::{Distribution, Range, uniform}; +//! use rand::{Rng, Sample}; +//! use rand::distributions::{Distribution, Range}; //! use rand::distributions::range::RangeInt; //! use rand::sequences::Choose; //! @@ -176,7 +176,7 @@ //! let open = game_host_open(car, choice, rng); //! //! // Shall we switch? -//! let switch = uniform(rng); +//! let switch = rng.gen(); //! if switch { //! choice = switch_door(choice, open); //! } @@ -492,8 +492,7 @@ impl SeedFromRng for StdRng { #[cfg(test)] mod test { use {Rng, thread_rng, Sample, Error}; - use mock::MockAddRng; - use distributions::{uniform}; + use mock::MockAddRng; use distributions::{Uniform, Range, Exp}; use sequences::Shuffle; use std::iter::repeat; @@ -564,7 +563,7 @@ mod test { #[test] fn test_thread_rng() { let mut r = thread_rng(); - uniform::(&mut r); + r.sample::(Uniform); let mut v = [1, 1, 1]; v.shuffle(&mut r); let b: &[_] = &[1, 1, 1]; @@ -578,7 +577,7 @@ mod test { { let r = &mut rng as &mut Rng; r.next_u32(); - uniform::(r); + r.sample::(Uniform); let mut v = [1, 1, 1]; v[..].shuffle(r); let b: &[_] = &[1, 1, 1]; @@ -588,7 +587,7 @@ mod test { { let mut r = Box::new(rng) as Box; r.next_u32(); - uniform::(&mut r); + r.sample::(Uniform); let mut v = [1, 1, 1]; v[..].shuffle(&mut *r); let b: &[_] = &[1, 1, 1]; diff --git a/src/read.rs b/src/read.rs index 772d83e7223..c4a41bbbd12 100644 --- a/src/read.rs +++ b/src/read.rs @@ -26,11 +26,11 @@ use {Rng, Error, ErrorKind}; /// # Example /// /// ```rust -/// use rand::{ReadRng, distributions}; +/// use rand::{Rng, ReadRng, distributions}; /// /// let data = vec![1, 2, 3, 4, 5, 6, 7, 8]; /// let mut rng = ReadRng::new(&data[..]); -/// println!("{:x}", distributions::uniform::(&mut rng)); +/// println!("{:x}", rng.next_u32()); /// ``` #[derive(Debug)] // Do not derive Clone, because it could share the underlying reader diff --git a/src/utils.rs b/src/utils.rs index 3f9736390fd..e6ed5ecbf17 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -271,7 +271,8 @@ float_impls! { f64, u64, 64, 52, 0xf_ffff_ffff_ffff, 1023, Rng::next_u64 } #[cfg(test)] mod tests { use super::FloatConversions; - use distributions::uniform; + use Sample; + use distributions::Uniform; #[test] fn closed_open01_edge_cases() { @@ -315,14 +316,14 @@ mod tests { // These constants happen to generate the lowest and highest floats in // the range. let mut rng = ::test::rng(); - let mut bits: u32 = uniform(&mut rng); + let mut bits: u32 = rng.sample(Uniform); bits = bits & 0x1ff; // 9 bits with no influence assert!((bits | 0).closed_open01_fixed() == 0.0); assert!((bits | 0xfffffe00).closed_open01_fixed() == 0.9999999); // 1 - 2^-23 - let mut bits: u64 = uniform(&mut rng); + let mut bits: u64 = rng.sample(Uniform); bits = bits & 0xfff; // 12 bits with no influence assert!((bits | 0).closed_open01_fixed() == 0.0); @@ -336,13 +337,13 @@ mod tests { // These constants happen to generate the lowest and highest floats in // the range. let mut rng = ::test::rng(); - let mut bits: u32 = uniform(&mut rng); + let mut bits: u32 = rng.sample(Uniform); bits = bits & 0x1ff; // 9 bits with no influence assert!((bits | 0).open_closed01_fixed() == 1.1920929e-7); // 2^-23 assert!((bits | 0xfffffe00).open_closed01_fixed() == 1.0); - let mut bits: u64 = uniform(&mut rng); + let mut bits: u64 = rng.sample(Uniform); bits = bits & 0xfff; // 12 bits with no influence assert!((bits | 0).open_closed01_fixed() == @@ -356,14 +357,14 @@ mod tests { // These constants happen to generate the lowest and highest floats in // the range. let mut rng = ::test::rng(); - let mut bits: u32 = uniform(&mut rng); + let mut bits: u32 = rng.sample(Uniform); bits = bits & 0x1ff; // 9 bits with no influence assert!((bits | 0).closed_open11_fixed() == -1.0); assert!((bits | 0xfffffe00).closed_open11_fixed() == 0.99999976); // 1 - 2^-22 - let mut bits: u64 = uniform(&mut rng); + let mut bits: u64 = rng.sample(Uniform); bits = bits & 0xfff; // 12 bits with no influence assert!((bits | 0).closed_open11_fixed() == -1.0); From fe40af0090342ae27d1984c967da21bfd05a8e41 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Thu, 14 Dec 2017 13:26:20 +0000 Subject: [PATCH 228/247] Don't assume usize is 4 or 8 bytes --- src/distributions/uniform.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/distributions/uniform.rs b/src/distributions/uniform.rs index b9b101d033d..afda3ddbc14 100644 --- a/src/distributions/uniform.rs +++ b/src/distributions/uniform.rs @@ -52,10 +52,12 @@ pub struct AsciiWordChar; impl Distribution for Uniform { fn sample(&self, rng: &mut R) -> isize { - if mem::size_of::() == 4 { - Distribution::::sample(&Uniform, rng) as isize + if mem::size_of::() <= 4 { + rng.next_u32() as isize + } else if mem::size_of::() == 8 { + rng.next_u64() as isize } else { - Distribution::::sample(&Uniform, rng) as isize + unreachable!() } } } @@ -99,10 +101,12 @@ impl Distribution for Uniform { impl Distribution for Uniform { #[inline] fn sample(&self, rng: &mut R) -> usize { - if mem::size_of::() == 4 { - Distribution::::sample(&Uniform, rng) as usize + if mem::size_of::() <= 4 { + rng.next_u32() as usize + } else if mem::size_of::() == 8 { + rng.next_u64() as usize } else { - Distribution::::sample(&Uniform, rng) as usize + unreachable!() } } } From e18d4896582e1607f1e94642e21f411656412633 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Thu, 14 Dec 2017 18:29:16 +0100 Subject: [PATCH 229/247] Add HC-128 RNG --- benches/generators.rs | 6 +- src/prng/hc128.rs | 504 ++++++++++++++++++++++++++++++++++++++++++ src/prng/mod.rs | 2 + 3 files changed, 511 insertions(+), 1 deletion(-) create mode 100644 src/prng/hc128.rs diff --git a/benches/generators.rs b/benches/generators.rs index 5959ad839b9..d02d2d43bbd 100644 --- a/benches/generators.rs +++ b/benches/generators.rs @@ -10,7 +10,7 @@ use std::mem::size_of; use test::{black_box, Bencher}; use rand::{Rng, NewSeeded, Sample, SeedFromRng, StdRng, OsRng, JitterRng}; -use rand::prng::{XorShiftRng, IsaacRng, Isaac64Rng, ChaChaRng}; +use rand::prng::*; macro_rules! gen_bytes { ($fnn:ident, $gen:ident) => { @@ -30,6 +30,7 @@ macro_rules! gen_bytes { } gen_bytes!(gen_bytes_xorshift, XorShiftRng); +gen_bytes!(gen_bytes_hc128, Hc128Rng); gen_bytes!(gen_bytes_isaac, IsaacRng); gen_bytes!(gen_bytes_isaac64, Isaac64Rng); gen_bytes!(gen_bytes_chacha, ChaChaRng); @@ -53,6 +54,7 @@ macro_rules! gen_uint { } gen_uint!(gen_u32_xorshift, u32, XorShiftRng); +gen_uint!(gen_u32_hc128, u32, Hc128Rng); gen_uint!(gen_u32_isaac, u32, IsaacRng); gen_uint!(gen_u32_isaac64, u32, Isaac64Rng); gen_uint!(gen_u32_chacha, u32, ChaChaRng); @@ -60,6 +62,7 @@ gen_uint!(gen_u32_std, u32, StdRng); gen_uint!(gen_u32_os, u32, OsRng); gen_uint!(gen_u64_xorshift, u64, XorShiftRng); +gen_uint!(gen_u64_hc128, u64, Hc128Rng); gen_uint!(gen_u64_isaac, u64, IsaacRng); gen_uint!(gen_u64_isaac64, u64, Isaac64Rng); gen_uint!(gen_u64_chacha, u64, ChaChaRng); @@ -88,6 +91,7 @@ macro_rules! init_gen { } init_gen!(init_xorshift, XorShiftRng); +init_gen!(init_hc128, Hc128Rng); init_gen!(init_isaac, IsaacRng); init_gen!(init_isaac64, Isaac64Rng); init_gen!(init_chacha, ChaChaRng); diff --git a/src/prng/hc128.rs b/src/prng/hc128.rs new file mode 100644 index 00000000000..d9683227d7b --- /dev/null +++ b/src/prng/hc128.rs @@ -0,0 +1,504 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! The HC-128 random number generator. + +use core::fmt; +use core::slice; + +use rand_core::{impls, le}; + +use {Rng, SeedFromRng, SeedableRng, Error}; + +/// A cryptographically secure random number generator that uses the HC-128 +/// algorithm. +/// +/// HC-128 is a stream cipher designed by Hongjun Wu[1], that we use as an RNG. +/// It is selected as one of the "stream ciphers suitable for widespread +/// adoption" by eSTREAM[2]. +/// +/// HC-128 is an array based RNG. In this it is similar to RC-4 and ISAAC before +/// it, but those have never been proven cryptographically secure (or have even +/// been broken). +/// +/// Because HC-128 works with simple indexing into a large array and with a few +/// operations that parallelize well, it has very good performance. The size of +/// the array it needs, 4kb, can however be a disadvantage. +/// +/// This implementation is not based on the version of HC-128 submitted to the +/// eSTREAM contest, but on a later version by the author with a few small +/// improvements from December 15, 2009[3]. +/// +/// HC-128 has no known weaknesses that are easier to exploit than doing a +/// brute-force search of 2128. A very comprehensive analysis of the +/// current state of known attacks / weaknesses of HC-128 is given in [4]. +/// +/// ## References +/// [1]: Hongjun Wu (2008). ["The Stream Cipher HC-128"] +/// (http://www.ecrypt.eu.org/stream/p3ciphers/hc/hc128_p3.pdf). +/// *The eSTREAM Finalists*, LNCS 4986, pp. 39--47, Springer-Verlag. +/// +/// [2]: [eSTREAM: the ECRYPT Stream Cipher Project] +/// (http://www.ecrypt.eu.org/stream/) +/// +/// [3]: Hongjun Wu, [Stream Ciphers HC-128 and HC-256] +/// (http://www3.ntu.edu.sg/home/wuhj/research/hc/index.html) +/// +/// [4]: Shashwat Raizada (January 2015), +/// ["Some Results On Analysis And Implementation Of HC-128 Stream Cipher"] +/// (http://library.isical.ac.in:8080/jspui/bitstream/123456789/6636/1/TH431.pdf). +pub struct Hc128Rng { + state: Hc128, + results: [u32; 16], + index: usize, +} + +impl Clone for Hc128Rng { + fn clone(&self) -> Hc128Rng { + Hc128Rng { + state: self.state, + results: self.results, + index: self.index + } + } +} + +#[derive(Copy, Clone)] +struct Hc128 { + t: [u32; 1024], + counter1024: usize, +} + +// Custom Debug implementation that does not expose the internal state +impl fmt::Debug for Hc128Rng { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Hc128Rng {{}}") + } +} + +impl Hc128Rng { + pub fn init(seed: &[u32]) -> Hc128Rng { + #[inline] + fn f1(x: u32) -> u32 { + x.rotate_right(7) ^ x.rotate_right(18) ^ (x >> 3) + } + + #[inline] + fn f2(x: u32) -> u32 { + x.rotate_right(17) ^ x.rotate_right(19) ^ (x >> 10) + } + + let mut t = [0u32; 1024]; + + // Expand the key and iv into P and Q + let (key, iv) = seed.split_at(4); + t[..4].copy_from_slice(key); + t[4..8].copy_from_slice(key); + t[8..12].copy_from_slice(iv); + t[12..16].copy_from_slice(iv); + + // Generate the 256 intermediate values W[16] ... W[256+16-1], and + // copy the last 16 generated values to the start op P. + for i in 16..256+16 { + t[i] = f2(t[i-2]).wrapping_add(t[i-7]).wrapping_add(f1(t[i-15])) + .wrapping_add(t[i-16]).wrapping_add(i as u32); + } + { + let (p1, p2) = t.split_at_mut(256); + p1[0..16].copy_from_slice(&p2[0..16]); + } + + // Generate both the P and Q tables + for i in 16..1024 { + t[i] = f2(t[i-2]).wrapping_add(t[i-7]).wrapping_add(f1(t[i-15])) + .wrapping_add(t[i-16]).wrapping_add(256 + i as u32); + } + + let mut state = Hc128Rng { + state: Hc128 { t: t, counter1024: 0 }, + results: [0; 16], + index: 0, + }; + + // run the cipher 1024 steps + for _ in 0..64 { state.state.sixteen_steps() }; + state.state.counter1024 = 0; + + // Prepare the first set of results + state.state.update(&mut state.results); + state + } +} + +impl Hc128 { + // One step of HC-128, update P and generate 32 bits keystream + #[inline(always)] + fn step_p(&mut self, i: usize, i511: usize, i3: usize, i10: usize, i12: usize) + -> u32 + { + let (p, q) = self.t.split_at_mut(512); + // FIXME: it would be great if we the bounds checks here could be + // optimized out, and we would not need unsafe. + // This improves performance by about 7%. + unsafe { + let temp0 = p.get_unchecked(i511).rotate_right(23); + let temp1 = p.get_unchecked(i3).rotate_right(10); + let temp2 = p.get_unchecked(i10).rotate_right(8); + *p.get_unchecked_mut(i) = p.get_unchecked(i) + .wrapping_add(temp2) + .wrapping_add(temp0 ^ temp1); + let temp3 = { + // The h1 function in HC-128 + let a = *p.get_unchecked(i12) as u8; + let c = (p.get_unchecked(i12) >> 16) as u8; + q[a as usize].wrapping_add(q[256 + c as usize]) + }; + temp3 ^ p.get_unchecked(i) + } + } + + // One step of HC-128, update Q and generate 32 bits keystream + // Similar to `step_p`, but `p` and `q` are swapped, and the rotates are to + // the left instead of to the right. + #[inline(always)] + fn step_q(&mut self, i: usize, i511: usize, i3: usize, i10: usize, i12: usize) + -> u32 + { + let (p, q) = self.t.split_at_mut(512); + unsafe { + let temp0 = q.get_unchecked(i511).rotate_left(23); + let temp1 = q.get_unchecked(i3).rotate_left(10); + let temp2 = q.get_unchecked(i10).rotate_left(8); + *q.get_unchecked_mut(i) = q.get_unchecked(i) + .wrapping_add(temp2) + .wrapping_add(temp0 ^ temp1); + let temp3 = { + // The h2 function in HC-128 + let a = *q.get_unchecked(i12) as u8; + let c = (q.get_unchecked(i12) >> 16) as u8; + p[a as usize].wrapping_add(p[256 + c as usize]) + }; + temp3 ^ q.get_unchecked(i) + } + } + + fn update(&mut self, results: &mut [u32]) { + assert!(self.counter1024 % 16 == 0); + + let cc = self.counter1024 % 512; + let dd = (cc + 16) % 512; + let ee = cc.wrapping_sub(16) % 512; + + if self.counter1024 & 512 == 0 { + // P block + results[0] = self.step_p(cc+0, cc+1, ee+13, ee+6, ee+4); + results[1] = self.step_p(cc+1, cc+2, ee+14, ee+7, ee+5); + results[2] = self.step_p(cc+2, cc+3, ee+15, ee+8, ee+6); + results[3] = self.step_p(cc+3, cc+4, cc+0, ee+9, ee+7); + results[4] = self.step_p(cc+4, cc+5, cc+1, ee+10, ee+8); + results[5] = self.step_p(cc+5, cc+6, cc+2, ee+11, ee+9); + results[6] = self.step_p(cc+6, cc+7, cc+3, ee+12, ee+10); + results[7] = self.step_p(cc+7, cc+8, cc+4, ee+13, ee+11); + results[8] = self.step_p(cc+8, cc+9, cc+5, ee+14, ee+12); + results[9] = self.step_p(cc+9, cc+10, cc+6, ee+15, ee+13); + results[10] = self.step_p(cc+10, cc+11, cc+7, cc+0, ee+14); + results[11] = self.step_p(cc+11, cc+12, cc+8, cc+1, ee+15); + results[12] = self.step_p(cc+12, cc+13, cc+9, cc+2, cc+0); + results[13] = self.step_p(cc+13, cc+14, cc+10, cc+3, cc+1); + results[14] = self.step_p(cc+14, cc+15, cc+11, cc+4, cc+2); + results[15] = self.step_p(cc+15, dd+0, cc+12, cc+5, cc+3); + } else { + // Q block + results[0] = self.step_q(cc+0, cc+1, ee+13, ee+6, ee+4); + results[1] = self.step_q(cc+1, cc+2, ee+14, ee+7, ee+5); + results[2] = self.step_q(cc+2, cc+3, ee+15, ee+8, ee+6); + results[3] = self.step_q(cc+3, cc+4, cc+0, ee+9, ee+7); + results[4] = self.step_q(cc+4, cc+5, cc+1, ee+10, ee+8); + results[5] = self.step_q(cc+5, cc+6, cc+2, ee+11, ee+9); + results[6] = self.step_q(cc+6, cc+7, cc+3, ee+12, ee+10); + results[7] = self.step_q(cc+7, cc+8, cc+4, ee+13, ee+11); + results[8] = self.step_q(cc+8, cc+9, cc+5, ee+14, ee+12); + results[9] = self.step_q(cc+9, cc+10, cc+6, ee+15, ee+13); + results[10] = self.step_q(cc+10, cc+11, cc+7, cc+0, ee+14); + results[11] = self.step_q(cc+11, cc+12, cc+8, cc+1, ee+15); + results[12] = self.step_q(cc+12, cc+13, cc+9, cc+2, cc+0); + results[13] = self.step_q(cc+13, cc+14, cc+10, cc+3, cc+1); + results[14] = self.step_q(cc+14, cc+15, cc+11, cc+4, cc+2); + results[15] = self.step_q(cc+15, dd+0, cc+12, cc+5, cc+3); + } + self.counter1024 = self.counter1024.wrapping_add(16); + } + + fn sixteen_steps(&mut self) { + assert!(self.counter1024 % 16 == 0); + + let cc = self.counter1024 % 512; + let dd = (cc + 16) % 512; + let ee = cc.wrapping_sub(16) % 512; + + if self.counter1024 < 512 { + // P block + self.t[cc+0] = self.step_p(cc+0, cc+1, ee+13, ee+6, ee+4); + self.t[cc+1] = self.step_p(cc+1, cc+2, ee+14, ee+7, ee+5); + self.t[cc+2] = self.step_p(cc+2, cc+3, ee+15, ee+8, ee+6); + self.t[cc+3] = self.step_p(cc+3, cc+4, cc+0, ee+9, ee+7); + self.t[cc+4] = self.step_p(cc+4, cc+5, cc+1, ee+10, ee+8); + self.t[cc+5] = self.step_p(cc+5, cc+6, cc+2, ee+11, ee+9); + self.t[cc+6] = self.step_p(cc+6, cc+7, cc+3, ee+12, ee+10); + self.t[cc+7] = self.step_p(cc+7, cc+8, cc+4, ee+13, ee+11); + self.t[cc+8] = self.step_p(cc+8, cc+9, cc+5, ee+14, ee+12); + self.t[cc+9] = self.step_p(cc+9, cc+10, cc+6, ee+15, ee+13); + self.t[cc+10] = self.step_p(cc+10, cc+11, cc+7, cc+0, ee+14); + self.t[cc+11] = self.step_p(cc+11, cc+12, cc+8, cc+1, ee+15); + self.t[cc+12] = self.step_p(cc+12, cc+13, cc+9, cc+2, cc+0); + self.t[cc+13] = self.step_p(cc+13, cc+14, cc+10, cc+3, cc+1); + self.t[cc+14] = self.step_p(cc+14, cc+15, cc+11, cc+4, cc+2); + self.t[cc+15] = self.step_p(cc+15, dd+0, cc+12, cc+5, cc+3); + } else { + // Q block + self.t[cc+512+0] = self.step_q(cc+0, cc+1, ee+13, ee+6, ee+4); + self.t[cc+512+1] = self.step_q(cc+1, cc+2, ee+14, ee+7, ee+5); + self.t[cc+512+2] = self.step_q(cc+2, cc+3, ee+15, ee+8, ee+6); + self.t[cc+512+3] = self.step_q(cc+3, cc+4, cc+0, ee+9, ee+7); + self.t[cc+512+4] = self.step_q(cc+4, cc+5, cc+1, ee+10, ee+8); + self.t[cc+512+5] = self.step_q(cc+5, cc+6, cc+2, ee+11, ee+9); + self.t[cc+512+6] = self.step_q(cc+6, cc+7, cc+3, ee+12, ee+10); + self.t[cc+512+7] = self.step_q(cc+7, cc+8, cc+4, ee+13, ee+11); + self.t[cc+512+8] = self.step_q(cc+8, cc+9, cc+5, ee+14, ee+12); + self.t[cc+512+9] = self.step_q(cc+9, cc+10, cc+6, ee+15, ee+13); + self.t[cc+512+10] = self.step_q(cc+10, cc+11, cc+7, cc+0, ee+14); + self.t[cc+512+11] = self.step_q(cc+11, cc+12, cc+8, cc+1, ee+15); + self.t[cc+512+12] = self.step_q(cc+12, cc+13, cc+9, cc+2, cc+0); + self.t[cc+512+13] = self.step_q(cc+13, cc+14, cc+10, cc+3, cc+1); + self.t[cc+512+14] = self.step_q(cc+14, cc+15, cc+11, cc+4, cc+2); + self.t[cc+512+15] = self.step_q(cc+15, dd+0, cc+12, cc+5, cc+3); + } + self.counter1024 += 16; + } +} + +impl Rng for Hc128Rng { + #[inline] + fn next_u32(&mut self) -> u32 { + if self.index >= 16 { + self.state.update(&mut self.results); + self.index = 0; + } + + let value = self.results[self.index]; + self.index += 1; + value + } + + #[inline] + fn next_u64(&mut self) -> u64 { + let index = self.index; + if index < 15 { + self.index += 2; + // Read an u64 from the current index + if cfg!(any(target_arch = "x86", target_arch = "x86_64")) { + unsafe { *(&self.results[index] as *const u32 as *const u64) } + } else { + let x = self.results[index] as u64; + let y = self.results[index + 1] as u64; + (y << 32) | x + } + } else if index >= 16 { + self.state.update(&mut self.results); + self.index = 2; + let x = self.results[0] as u64; + let y = self.results[1] as u64; + (y << 32) | x + } else { + let x = self.results[15] as u64; + self.state.update(&mut self.results); + self.index = 1; + let y = self.results[0] as u64; + (y << 32) | x + } + } + + #[cfg(feature = "i128_support")] + fn next_u128(&mut self) -> u128 { + impls::next_u128_via_u64(self) + } + + // As an optimization we try to write directly into the output buffer. + // This is only enabled for platforms where unaligned writes are known to + // be safe and fast. + // This improves performance by about 12%. + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + fn fill_bytes(&mut self, dest: &mut [u8]) { + let mut filled = 0; + + // Continue filling from the current set of results + if self.index < 16 { + let (consumed_u32, filled_u8) = + impls::fill_via_u32_chunks(&mut self.results[self.index..], + dest); + + self.index += consumed_u32; + filled += filled_u8; + } + + let len_remainder = (dest.len() - filled) % 16; + let len_direct = dest.len() - len_remainder; + + while filled < len_direct { + let dest_u32: &mut [u32] = unsafe { + slice::from_raw_parts_mut( + dest[filled..].as_mut_ptr() as *mut u8 as *mut u32, + 16) + }; + self.state.update(dest_u32); + filled += 16 * 4; + } + self.index = 16; + + if len_remainder > 0 { + self.state.update(&mut self.results); + + let (consumed_u32, _) = + impls::fill_via_u32_chunks(&mut self.results, + &mut dest[filled..]); + + self.index = consumed_u32; + } + } + + #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] + fn fill_bytes(&mut self, dest: &mut [u8]) { + let mut read_len = 0; + while read_len < dest.len() { + if self.index >= 16 { + self.state.update(&mut self.results); + self.index = 0 + } + + let (consumed_u32, filled_u8) = + impls::fill_via_u32_chunks(&mut self.results[self.index..], + dest); + + self.index += consumed_u32; + read_len += filled_u8; + } + } + + fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { + Ok(self.fill_bytes(dest)) + } +} + +impl SeedFromRng for Hc128Rng { + fn from_rng(mut other: R) -> Result { + let mut seed = [0u32; 8]; + unsafe { + let ptr = seed.as_mut_ptr() as *mut u8; + let slice = slice::from_raw_parts_mut(ptr, 8 * 4); + other.try_fill(slice)?; + } + Ok(Hc128Rng::init(&seed)) + } +} + +impl SeedableRng for Hc128Rng { + type Seed = [u8; 32]; /* 128 bit key followed by 128 bit iv */ + fn from_seed(mut seed: Self::Seed) -> Self { + Hc128Rng::init(&le::convert_slice_32(&mut seed)) + } +} + +#[cfg(test)] +mod test { + use {Rng, SeedableRng}; + use super::Hc128Rng; + + #[test] + // Test vector 1 from the paper "The Stream Cipher HC-128" + fn test_hc128_true_values_a() { + let seed = [0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // key + 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]; // iv + let mut rng = Hc128Rng::from_seed(seed); + + let v = (0..16).map(|_| rng.next_u32()).collect::>(); + assert_eq!(v, + vec!(0x73150082, 0x3bfd03a0, 0xfb2fd77f, 0xaa63af0e, + 0xde122fc6, 0xa7dc29b6, 0x62a68527, 0x8b75ec68, + 0x9036db1e, 0x81896005, 0x00ade078, 0x491fbf9a, + 0x1cdc3013, 0x6c3d6e24, 0x90f664b2, 0x9cd57102)); + } + + #[test] + // Test vector 2 from the paper "The Stream Cipher HC-128" + fn test_hc128_true_values_b() { + let seed = [0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // key + 1,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]; // iv + let mut rng = Hc128Rng::from_seed(seed); + + let v = (0..16).map(|_| rng.next_u32()).collect::>(); + assert_eq!(v, + vec!(0xc01893d5, 0xb7dbe958, 0x8f65ec98, 0x64176604, + 0x36fc6724, 0xc82c6eec, 0x1b1c38a7, 0xc9b42a95, + 0x323ef123, 0x0a6a908b, 0xce757b68, 0x9f14f7bb, + 0xe4cde011, 0xaeb5173f, 0x89608c94, 0xb5cf46ca)); + } + + #[test] + // Test vector 3 from the paper "The Stream Cipher HC-128" + fn test_hc128_true_values_c() { + let seed = [0x55,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // key + 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]; // iv + let mut rng = Hc128Rng::from_seed(seed); + + let v = (0..16).map(|_| rng.next_u32()).collect::>(); + assert_eq!(v, + vec!(0x518251a4, 0x04b4930a, 0xb02af931, 0x0639f032, + 0xbcb4a47a, 0x5722480b, 0x2bf99f72, 0xcdc0e566, + 0x310f0c56, 0xd3cc83e8, 0x663db8ef, 0x62dfe07f, + 0x593e1790, 0xc5ceaa9c, 0xab03806f, 0xc9a6e5a0)); + } + + #[test] + fn test_hc128_true_values_u64() { + let seed = [0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // key + 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]; // iv + let mut rng = Hc128Rng::from_seed(seed); + + let v = (0..8).map(|_| rng.next_u64()).collect::>(); + assert_eq!(v, + vec!(0x3bfd03a073150082, 0xaa63af0efb2fd77f, + 0xa7dc29b6de122fc6, 0x8b75ec6862a68527, + 0x818960059036db1e, 0x491fbf9a00ade078, + 0x6c3d6e241cdc3013, 0x9cd5710290f664b2)); + + // The RNG operates in a P block of 512 results and next a Q block. + // After skipping 2*800 u32 results we end up somewhere in the Q block + // of the second round + for _ in 0..800 { rng.next_u64(); } + + let v = (0..8).map(|_| rng.next_u64()).collect::>(); + assert_eq!(v, + vec!(0xd8c4d6ca84d0fc10, 0xf16a5d91dc66e8e7, + 0xd800de5bc37a8653, 0x7bae1f88c0dfbb4c, + 0x3bfe1f374e6d4d14, 0x424b55676be3fa06, + 0xe3a1e8758cbff579, 0x417f7198c5652bcd)); + } + + #[test] + fn test_hc128_clone() { + let seed = [0x55,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // key + 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]; // iv + let mut rng1 = Hc128Rng::from_seed(seed); + let mut rng2 = rng1.clone(); + for _ in 0..16 { + assert_eq!(rng1.next_u32(), rng2.next_u32()); + } + } +} diff --git a/src/prng/mod.rs b/src/prng/mod.rs index b2a2f95763b..25ffdc81c57 100644 --- a/src/prng/mod.rs +++ b/src/prng/mod.rs @@ -44,12 +44,14 @@ //! statistical properties, performance. mod chacha; +mod hc128; mod isaac; mod isaac64; mod isaac_word; mod xorshift; pub use self::chacha::ChaChaRng; +pub use self::hc128::Hc128Rng; pub use self::isaac::IsaacRng; pub use self::isaac64::Isaac64Rng; pub use self::isaac_word::IsaacWordRng; From 0ac7b86f4da01ef67d4e623d77ac51e532cbcb54 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Thu, 14 Dec 2017 21:40:01 +0100 Subject: [PATCH 230/247] impl CryptoRng for Hc128Rng That is kind of the goal here :-) --- src/prng/hc128.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/prng/hc128.rs b/src/prng/hc128.rs index d9683227d7b..09e0472331f 100644 --- a/src/prng/hc128.rs +++ b/src/prng/hc128.rs @@ -15,7 +15,7 @@ use core::slice; use rand_core::{impls, le}; -use {Rng, SeedFromRng, SeedableRng, Error}; +use {Rng, CryptoRng, SeedFromRng, SeedableRng, Error}; /// A cryptographically secure random number generator that uses the HC-128 /// algorithm. @@ -415,6 +415,8 @@ impl SeedableRng for Hc128Rng { } } +impl CryptoRng for Hc128Rng {} + #[cfg(test)] mod test { use {Rng, SeedableRng}; From b9f7123d1c2e91b136e17077a6d686e50a3bf686 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Thu, 14 Dec 2017 21:40:35 +0100 Subject: [PATCH 231/247] Add test for `fill_bytes` --- src/prng/hc128.rs | 41 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/src/prng/hc128.rs b/src/prng/hc128.rs index 09e0472331f..f50e27fc3d7 100644 --- a/src/prng/hc128.rs +++ b/src/prng/hc128.rs @@ -379,12 +379,12 @@ impl Rng for Hc128Rng { while read_len < dest.len() { if self.index >= 16 { self.state.update(&mut self.results); - self.index = 0 + self.index = 0; } let (consumed_u32, filled_u8) = impls::fill_via_u32_chunks(&mut self.results[self.index..], - dest); + &mut dest[read_len..]); self.index += consumed_u32; read_len += filled_u8; @@ -493,6 +493,43 @@ mod test { 0xe3a1e8758cbff579, 0x417f7198c5652bcd)); } + #[test] + fn test_hc128_true_values_bytes() { + let seed = [0x55,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // key + 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]; // iv + let mut rng = Hc128Rng::from_seed(seed); + let expected = + vec!(0x31, 0xf9, 0x2a, 0xb0, 0x32, 0xf0, 0x39, 0x06, + 0x7a, 0xa4, 0xb4, 0xbc, 0x0b, 0x48, 0x22, 0x57, + 0x72, 0x9f, 0xf9, 0x2b, 0x66, 0xe5, 0xc0, 0xcd, + 0x56, 0x0c, 0x0f, 0x31, 0xe8, 0x83, 0xcc, 0xd3, + 0xef, 0xb8, 0x3d, 0x66, 0x7f, 0xe0, 0xdf, 0x62, + 0x90, 0x17, 0x3e, 0x59, 0x9c, 0xaa, 0xce, 0xc5, + 0x6f, 0x80, 0x03, 0xab, 0xa0, 0xe5, 0xa6, 0xc9, + 0x60, 0x95, 0x84, 0x7a, 0xa5, 0x68, 0x5a, 0x84, + 0xea, 0xd5, 0xf3, 0xea, 0x73, 0xa9, 0xad, 0x01, + 0x79, 0x7d, 0xbe, 0x9f, 0xea, 0xe3, 0xf9, 0x74, + 0x0e, 0xda, 0x2f, 0xa0, 0xe4, 0x7b, 0x4b, 0x1b, + 0xdd, 0x17, 0x69, 0x4a, 0xfe, 0x9f, 0x56, 0x95, + 0xad, 0x83, 0x6b, 0x9d, 0x60, 0xa1, 0x99, 0x96, + 0x90, 0x00, 0x66, 0x7f, 0xfa, 0x7e, 0x65, 0xe9, + 0xac, 0x8b, 0x92, 0x34, 0x77, 0xb4, 0x23, 0xd0, + 0xb9, 0xab, 0xb1, 0x47, 0x7d, 0x4a, 0x13, 0x0a); + + // Pick a somewhat large buffer so we can test filling with the + // remainder from `state.results`, directly filling the buffer, and + // filling the remainder of the buffer. + let mut buffer = vec!(0u8; 16*4*2); + // Consume a value the we have a remainder. + let _ = rng.next_u64(); + rng.fill_bytes(&mut buffer); + + for i in buffer.iter() { + print!("0x{:02x}, ", i); + } + assert_eq!(buffer, expected); + } + #[test] fn test_hc128_clone() { let seed = [0x55,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // key From 5741e16fc3d6ba28c8892afc769179699de710a9 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Sat, 16 Dec 2017 07:22:20 +0100 Subject: [PATCH 232/247] Rename AsciiWordChar to AlphanumericChar --- benches/distributions.rs | 2 +- src/distributions/mod.rs | 2 +- src/distributions/uniform.rs | 14 +++++++------- src/iter.rs | 8 ++++---- src/lib.rs | 4 ++-- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/benches/distributions.rs b/benches/distributions.rs index 48fe2c8aa64..a1294c508de 100644 --- a/benches/distributions.rs +++ b/benches/distributions.rs @@ -51,7 +51,7 @@ distr!(distr_uniform_i64, i64, Uniform); distr!(distr_uniform_i128, i128, Uniform); distr!(distr_uniform_bool, bool, Uniform); -distr!(distr_uniform_ascii_char, char, AsciiWordChar); +distr!(distr_uniform_alphanumeric, char, Alphanumeric); distr!(distr_uniform01_float32, f32, Uniform01); distr!(distr_closed01_float32, f32, Closed01); diff --git a/src/distributions/mod.rs b/src/distributions/mod.rs index 6db1a5af15c..076ab52c6cb 100644 --- a/src/distributions/mod.rs +++ b/src/distributions/mod.rs @@ -18,7 +18,7 @@ use Rng; pub use self::default::Default; -pub use self::uniform::{Uniform, Uniform01, Open01, Closed01, Codepoint, AsciiWordChar}; +pub use self::uniform::{Uniform, Uniform01, Open01, Closed01, Codepoint, Alphanumeric}; pub use self::range::Range; #[cfg(feature="std")] diff --git a/src/distributions/uniform.rs b/src/distributions/uniform.rs index afda3ddbc14..2f1fd22ef56 100644 --- a/src/distributions/uniform.rs +++ b/src/distributions/uniform.rs @@ -14,7 +14,7 @@ use core::char; use core::mem; use Rng; -use distributions::Distribution; +use distributions::{Distribution, Range}; use utils::FloatConversions; // ----- Sampling distributions ----- @@ -45,7 +45,7 @@ pub struct Codepoint; /// Sample a `char`, uniformly distributed over ASCII letters and numbers: /// a-z, A-Z and 0-9. #[derive(Debug)] -pub struct AsciiWordChar; +pub struct Alphanumeric; // ----- actual implementations ----- @@ -220,7 +220,7 @@ impl Distribution for Codepoint { } } -impl Distribution for AsciiWordChar { +impl Distribution for Alphanumeric { fn sample(&self, rng: &mut R) -> char { const RANGE: u32 = 26 + 26 + 10; const GEN_ASCII_STR_CHARSET: &'static [u8] = @@ -228,7 +228,7 @@ impl Distribution for AsciiWordChar { abcdefghijklmnopqrstuvwxyz\ 0123456789"; loop { - let var = rng.next_u32() & 0x3F; + let var = rng.next_u32() >> 26; if var < RANGE { return GEN_ASCII_STR_CHARSET[var as usize] as char } @@ -241,7 +241,7 @@ impl Distribution for AsciiWordChar { mod tests { use {Sample, thread_rng, iter}; use distributions::{Uniform, Uniform01, Open01, Closed01, - Codepoint, AsciiWordChar}; + Codepoint, Alphanumeric}; #[test] fn test_integers() { @@ -269,10 +269,10 @@ mod tests { let mut rng = ::test::rng(); let _ = rng.sample(Codepoint); - let c = rng.sample(AsciiWordChar); + let c = rng.sample(Alphanumeric); assert!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')); - let word: String = iter(&mut rng).take(5).map(|rng| rng.sample(AsciiWordChar)).collect(); + let word: String = iter(&mut rng).take(5).map(|rng| rng.sample(Alphanumeric)).collect(); assert_eq!(word.len(), 5); } diff --git a/src/iter.rs b/src/iter.rs index 1d44d2c9d0f..176a110aa45 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -37,13 +37,13 @@ pub struct Iter<'a, R: Rng+?Sized+'a> { /// /// ``` /// use rand::{thread_rng, Rng, Sample, iter}; -/// use rand::distributions::{Uniform, AsciiWordChar}; +/// use rand::distributions::{Uniform, Alphanumeric}; /// /// let mut rng = thread_rng(); /// let x: Vec = iter(&mut rng).take(10).map(|rng| rng.sample(Uniform)).collect(); /// println!("{:?}", x); /// -/// let w: String = iter(&mut rng).take(6).map(|rng| rng.sample(AsciiWordChar)).collect(); +/// let w: String = iter(&mut rng).take(6).map(|rng| rng.sample(Alphanumeric)).collect(); /// println!("{}", w); /// ``` pub fn iter<'a, R: Rng+?Sized+'a>(rng: &'a mut R) -> Iter<'a, R> { @@ -160,7 +160,7 @@ impl<'a, R:?Sized+'a, U, F> Iterator for FlatMap<'a, R, U, F> #[cfg(test)] mod tests { use {Rng, Sample, thread_rng, iter}; - use distributions::{Uniform, AsciiWordChar}; + use distributions::{Uniform, Alphanumeric}; #[test] fn test_iter() { @@ -181,7 +181,7 @@ mod tests { fn test_dyn_dispatch() { let r: &mut Rng = &mut thread_rng(); - let x: String = iter(r).take(10).map(|rng| rng.sample(AsciiWordChar)).collect(); + let x: String = iter(r).take(10).map(|rng| rng.sample(Alphanumeric)).collect(); assert_eq!(x.len(), 10); } } diff --git a/src/lib.rs b/src/lib.rs index fcc0e18078d..f05d8062740 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -435,10 +435,10 @@ pub trait Sample: Rng { /// /// ```rust /// use rand::{thread_rng, Sample}; - /// use rand::distributions::AsciiWordChar; + /// use rand::distributions::Alphanumeric; /// /// let mut rng = thread_rng(); - /// let x: String = rng.iter().map(|rng| rng.sample(AsciiWordChar)).take(6).collect(); + /// let x: String = rng.iter().map(|rng| rng.sample(Alphanumeric)).take(6).collect(); /// ``` fn iter<'a>(&'a mut self) -> iter::Iter<'a, Self> { iter(self) From a0deb78d6753d43f1a4ff01078e776554b345927 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Sat, 16 Dec 2017 07:37:41 +0100 Subject: [PATCH 233/247] Optimize Codepoint distribution --- benches/distributions.rs | 1 + src/distributions/range.rs | 4 ++++ src/distributions/uniform.rs | 10 ++++------ 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/benches/distributions.rs b/benches/distributions.rs index a1294c508de..bc0435968ca 100644 --- a/benches/distributions.rs +++ b/benches/distributions.rs @@ -52,6 +52,7 @@ distr!(distr_uniform_i128, i128, Uniform); distr!(distr_uniform_bool, bool, Uniform); distr!(distr_uniform_alphanumeric, char, Alphanumeric); +distr!(distr_uniform_codepoint, char, Codepoint); distr!(distr_uniform01_float32, f32, Uniform01); distr!(distr_closed01_float32, f32, Closed01); diff --git a/src/distributions/range.rs b/src/distributions/range.rs index 9120028de8d..fa43fd17ee5 100644 --- a/src/distributions/range.rs +++ b/src/distributions/range.rs @@ -179,10 +179,14 @@ macro_rules! range_int_impl { type X = $ty; + #[inline] // if the range is constant, this helps LLVM to do the + // calculations at compile-time. fn new(low: Self::X, high: Self::X) -> Self { RangeImpl::new_inclusive(low, high - 1) } + #[inline] // if the range is constant, this helps LLVM to do the + // calculations at compile-time. fn new_inclusive(low: Self::X, high: Self::X) -> Self { // For a closed range the number of possible numbers we should // generate is `range = (high - low + 1)`. It is not possible to diff --git a/src/distributions/uniform.rs b/src/distributions/uniform.rs index 2f1fd22ef56..4b3f6cfc2c6 100644 --- a/src/distributions/uniform.rs +++ b/src/distributions/uniform.rs @@ -206,14 +206,12 @@ float_impls! { f64, Rng::next_u64 } impl Distribution for Codepoint { fn sample(&self, rng: &mut R) -> char { - // a char is 21 bits - const CHAR_MASK: u32 = 0x001f_ffff; + let range = Range::new(0u32, 0x11_0000); loop { - // Rejection sampling. About 0.2% of numbers with at most - // 21-bits are invalid codepoints (surrogates), so this - // will succeed first go almost every time. - match char::from_u32(rng.next_u32() & CHAR_MASK) { + match char::from_u32(range.sample(rng)) { Some(c) => return c, + // About 0.2% of numbers in the range 0..0x110000 are invalid + // codepoints (surrogates). None => {} } } From 4e088f1fd486f82a3fe1f16bbb9ce08cf947677a Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 19 Dec 2017 17:10:07 +0000 Subject: [PATCH 234/247] Fix comment --- rand_core/src/lib.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/rand_core/src/lib.rs b/rand_core/src/lib.rs index 6fcd517dcb6..e60fea97f1b 100644 --- a/rand_core/src/lib.rs +++ b/rand_core/src/lib.rs @@ -26,9 +26,6 @@ //! The `impls` sub-module includes a few small functions to assist //! implementation of `Rng`. Since this module is only of interest to `Rng` //! implementors, it is not re-exported from `rand`. -//! -//! The `mock` module includes a mock `Rng` implementation. Even though this is -//! only useful for testing, it is currently always built. #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk.png", html_favicon_url = "https://www.rust-lang.org/favicon.ico", From 729644cce8d2b8a367cace62ad3366b48a946ec7 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Wed, 20 Dec 2017 15:14:00 +0000 Subject: [PATCH 235/247] Remove log dependency; make i128_support transitive --- Cargo.toml | 5 +---- src/lib.rs | 1 - 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index bcbf24f5352..1eb23d16837 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ nightly = ["i128_support"] # enables all features requiring nightly rust std = ["rand_core/std", "libc"] # default feature; without this rand uses libcore alloc = [] # enables Vec and Box support without std -i128_support = [] # enables i128 and u128 support +i128_support = ["rand_core/i128_support"] # enables i128 and u128 support [dependencies] # All deps must not use 'std' by default @@ -32,9 +32,6 @@ libc = { version = "0.2", optional = true } [target.'cfg(windows)'.dependencies] winapi = { version = "0.3", features = ["minwindef", "ntsecapi", "profileapi", "winnt"] } -[dev-dependencies] -log = "0.3.0" - [workspace] members = ["rand_core"] diff --git a/src/lib.rs b/src/lib.rs index 5d4c46fc2a1..9ce3f0a67e1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -253,7 +253,6 @@ #[cfg(feature="std")] extern crate std as core; #[cfg(all(feature = "alloc", not(feature="std")))] extern crate alloc; -#[cfg(test)] #[macro_use] extern crate log; extern crate rand_core; From 4f1deb7114a34dc618c5ba97793edd7e751db99f Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Thu, 21 Dec 2017 11:22:17 +0000 Subject: [PATCH 236/247] Restrict Seed type --- rand_core/src/lib.rs | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/rand_core/src/lib.rs b/rand_core/src/lib.rs index e60fea97f1b..d660fe1c846 100644 --- a/rand_core/src/lib.rs +++ b/rand_core/src/lib.rs @@ -225,6 +225,21 @@ pub trait SeedFromRng: Sized { fn from_rng(rng: R) -> Result; } +mod private { + pub trait Sealed {} + impl Sealed for S where S: super::SeedRestriction {} +} + +/// The seed type is restricted to these types. This trait is sealed to prevent +/// user-extension. +/// +/// Use of byte-arrays avoids endianness issues. We may extend this to allow +/// byte arrays of other lengths in the future. +pub trait SeedRestriction: private::Sealed {} +impl SeedRestriction for [u8; 8] {} +impl SeedRestriction for [u8; 16] {} +impl SeedRestriction for [u8; 32] {} + /// A random number generator that can be explicitly seeded to produce /// the same stream of randomness multiple times (i.e. is reproducible). /// @@ -236,9 +251,7 @@ pub trait SeedFromRng: Sized { /// actually does yield reproducible results. pub trait SeedableRng: Sized { /// Seed type. - /// - /// TODO: restrict to `[u8; N]` where N in 8, 16, 32 - type Seed; + type Seed: SeedRestriction; /// Create a new PRNG using the given seed. /// From 92831f37c9d793a58d32b8572150aa9f93fa2d4a Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Thu, 21 Dec 2017 21:59:36 +0100 Subject: [PATCH 237/247] Replace `convert_slice_{32,64}` with `read_u{32,64}_into` And a little cleanup around the init functions --- rand_core/src/lib.rs | 80 +++++++++++++++++--------------------------- src/prng/chacha.rs | 73 +++++++++++++++------------------------- src/prng/hc128.rs | 28 +++++++--------- src/prng/isaac.rs | 31 +++++++---------- src/prng/isaac64.rs | 31 +++++++---------- 5 files changed, 97 insertions(+), 146 deletions(-) diff --git a/rand_core/src/lib.rs b/rand_core/src/lib.rs index d660fe1c846..54be12ac14a 100644 --- a/rand_core/src/lib.rs +++ b/rand_core/src/lib.rs @@ -396,69 +396,51 @@ impl stdError for Error { /// Little-Endian order has been chosen for internal usage; this makes some /// useful functions available. pub mod le { - use core::slice; - + use core::ptr; + /// Helper function to turn a slice into an array reference - + /// Read a `u32` from a byte sequence, in litte-endian order /// /// Consider usage with the `arrayref` crate. pub fn read_u32(bytes: &[u8; 4]) -> u32 { unsafe{ *(bytes as *const [u8; 4] as *const u32) }.to_le() } - + /// Read a `u64` from a byte sequence, in litte-endian order /// /// Consider usage with the `arrayref` crate. pub fn read_u64(bytes: &[u8; 8]) -> u64 { unsafe{ *(bytes as *const [u8; 8] as *const u64) }.to_le() } - - /// Convert a byte slice to a `u32` slice and mutate endianness in-place - pub fn convert_slice_32(bytes: &mut [u8]) -> &mut [u32] { - assert_eq!(bytes.len() % 4, 0); - let l = bytes.len() / 4; - let p = bytes.as_ptr() as *mut u8 as *mut u32; - let s = unsafe{ slice::from_raw_parts_mut(p, l) }; - for i in s.iter_mut() { - *i = (*i).to_le(); - } - s - } - - /// Convert a byte slice to a `u64` slice and mutate endianness in-place - pub fn convert_slice_64(bytes: &mut [u8]) -> &mut [u64] { - assert_eq!(bytes.len() % 8, 0); - let l = bytes.len() / 8; - let p = bytes.as_ptr() as *mut u8 as *mut u64; - let s = unsafe{ slice::from_raw_parts_mut(p, l) }; - for i in s.iter_mut() { - *i = (*i).to_le(); - } - s - } - - #[cfg(test)] - mod tests { - use super::*; - #[test] - - fn test_read() { - assert_eq!(read_u32(&[1, 2, 3, 4]), 0x04030201); - assert_eq!(read_u64(&[1, 2, 3, 4, 5, 6, 7, 8]), 0x0807060504030201); - - { - let mut bytes = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; - let slice = convert_slice_32(&mut bytes[..]); - assert_eq!(slice[0], 0x04030201); - assert_eq!(slice[3], 0x100F0E0D); + + macro_rules! read_slice { + ($src:expr, $dst:expr, $size:expr, $which:ident) => {{ + assert_eq!($src.len(), $size * $dst.len()); + + unsafe { + ptr::copy_nonoverlapping( + $src.as_ptr(), + $dst.as_mut_ptr() as *mut u8, + $src.len()); } - { - let mut bytes = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; - let slice = convert_slice_64(&mut bytes[..]); - assert_eq!(slice[0], 0x0807060504030201); - assert_eq!(slice[1], 0x100F0E0D0C0B0A09); + for v in $dst.iter_mut() { + *v = v.$which(); } - } + }}; + } + + /// Reads unsigned 32 bit integers from `src` into `dst`. + /// Borrowed from the `byteorder` crate. + #[inline] + pub fn read_u32_into(src: &[u8], dst: &mut [u32]) { + read_slice!(src, dst, 4, to_le); + } + + /// Reads unsigned 64 bit integers from `src` into `dst`. + /// Borrowed from the `byteorder` crate. + #[inline] + pub fn read_u64_into(src: &[u8], dst: &mut [u64]) { + read_slice!(src, dst, 8, to_le); } } diff --git a/src/prng/chacha.rs b/src/prng/chacha.rs index d651d510027..54710fbe1b1 100644 --- a/src/prng/chacha.rs +++ b/src/prng/chacha.rs @@ -10,19 +10,14 @@ //! The ChaCha random number generator. -use core::fmt; -use rand_core::impls; +use core::{fmt, slice}; +use rand_core::{impls, le}; use {Rng, CryptoRng, SeedFromRng, SeedableRng, Error}; -const KEY_WORDS : usize = 8; // 8 words for the 256-bit key -const STATE_WORDS : usize = 16; -const CHACHA_ROUNDS: u32 = 20; // Cryptographically secure from 8 upwards as of this writing - -const CHACHA_EMPTY: ChaChaRng = ChaChaRng { - buffer: [0; STATE_WORDS], - state: [0; STATE_WORDS], - index: STATE_WORDS - }; +const SEED_WORDS: usize = 8; // 8 words for the 256-bit key +const STATE_WORDS: usize = 16; +const CHACHA_ROUNDS: u32 = 20; // Cryptographically secure from 8 upwards as of + // this writing /// A random number generator that uses the ChaCha20 algorithm [1]. /// @@ -106,9 +101,7 @@ impl ChaChaRng { /// - 2917185654 /// - 2419978656 pub fn new_unseeded() -> ChaChaRng { - let mut rng = CHACHA_EMPTY; - rng.init(&[0; KEY_WORDS]); - rng + ChaChaRng::init([0; SEED_WORDS]) } /// Sets the internal 128-bit ChaCha counter to @@ -157,22 +150,15 @@ impl ChaChaRng { /// ``` /// [1]: Daniel J. Bernstein. [*Extending the Salsa20 /// nonce.*](http://cr.yp.to/papers.html#xsalsa) - fn init(&mut self, key: &[u32; KEY_WORDS]) { - self.state[0] = 0x61707865; - self.state[1] = 0x3320646E; - self.state[2] = 0x79622D32; - self.state[3] = 0x6B206574; - - for i in 0..KEY_WORDS { - self.state[4+i] = key[i]; - } - - self.state[12] = 0; - self.state[13] = 0; - self.state[14] = 0; - self.state[15] = 0; - - self.index = STATE_WORDS; + fn init(seed: [u32; SEED_WORDS]) -> Self { + ChaChaRng { + buffer: [0; STATE_WORDS], + state: [0x61707865, 0x3320646E, 0x79622D32, 0x6B206574, // constants + seed[0], seed[1], seed[2], seed[3], // seed + seed[4], seed[5], seed[6], seed[7], // seed + 0, 0, 0, 0], // counter + index: STATE_WORDS, // generate on first use + } } /// Refill the internal output buffer (`self.buffer`) @@ -240,27 +226,22 @@ impl CryptoRng for ChaChaRng {} impl SeedFromRng for ChaChaRng { fn from_rng(mut other: R) -> Result { - let mut key = [0; KEY_WORDS]; - for word in key.iter_mut() { - *word = other.next_u32(); + let mut seed = [0u32; SEED_WORDS]; + unsafe { + let ptr = seed.as_mut_ptr() as *mut u8; + let slice = slice::from_raw_parts_mut(ptr, SEED_WORDS * 4); + other.try_fill(slice)?; } - let mut rng = CHACHA_EMPTY; - rng.init(&key); - Ok(rng) + Ok(ChaChaRng::init(seed)) } } impl SeedableRng for ChaChaRng { - type Seed = [u8; 32]; - fn from_seed(mut seed: Self::Seed) -> Self { - let mut rng = CHACHA_EMPTY; - let p = &mut seed as *mut [u8; 32] as *mut [u32; 8]; - let key = unsafe{ &mut *p }; - for k in key.iter_mut() { - *k = k.to_le(); - } - rng.init(key); - rng + type Seed = [u8; SEED_WORDS*4]; + fn from_seed(seed: Self::Seed) -> Self { + let mut seed_u32 = [0u32; SEED_WORDS]; + le::read_u32_into(&seed, &mut seed_u32); + ChaChaRng::init(seed_u32) } } diff --git a/src/prng/hc128.rs b/src/prng/hc128.rs index f50e27fc3d7..38e1655c3f4 100644 --- a/src/prng/hc128.rs +++ b/src/prng/hc128.rs @@ -10,13 +10,12 @@ //! The HC-128 random number generator. -use core::fmt; -use core::slice; - +use core::{fmt, slice}; use rand_core::{impls, le}; - use {Rng, CryptoRng, SeedFromRng, SeedableRng, Error}; +const SEED_WORDS: usize = 8; // 128 bit key followed by 128 bit iv + /// A cryptographically secure random number generator that uses the HC-128 /// algorithm. /// @@ -84,7 +83,7 @@ impl fmt::Debug for Hc128Rng { } impl Hc128Rng { - pub fn init(seed: &[u32]) -> Hc128Rng { + fn init(seed: [u32; SEED_WORDS]) -> Self { #[inline] fn f1(x: u32) -> u32 { x.rotate_right(7) ^ x.rotate_right(18) ^ (x >> 3) @@ -124,15 +123,12 @@ impl Hc128Rng { let mut state = Hc128Rng { state: Hc128 { t: t, counter1024: 0 }, results: [0; 16], - index: 0, + index: 16, // generate on first use }; // run the cipher 1024 steps for _ in 0..64 { state.state.sixteen_steps() }; state.state.counter1024 = 0; - - // Prepare the first set of results - state.state.update(&mut state.results); state } } @@ -398,20 +394,22 @@ impl Rng for Hc128Rng { impl SeedFromRng for Hc128Rng { fn from_rng(mut other: R) -> Result { - let mut seed = [0u32; 8]; + let mut seed = [0u32; SEED_WORDS]; unsafe { let ptr = seed.as_mut_ptr() as *mut u8; - let slice = slice::from_raw_parts_mut(ptr, 8 * 4); + let slice = slice::from_raw_parts_mut(ptr, SEED_WORDS * 4); other.try_fill(slice)?; } - Ok(Hc128Rng::init(&seed)) + Ok(Hc128Rng::init(seed)) } } impl SeedableRng for Hc128Rng { - type Seed = [u8; 32]; /* 128 bit key followed by 128 bit iv */ - fn from_seed(mut seed: Self::Seed) -> Self { - Hc128Rng::init(&le::convert_slice_32(&mut seed)) + type Seed = [u8; SEED_WORDS*4]; + fn from_seed(seed: Self::Seed) -> Self { + let mut seed_u32 = [0u32; SEED_WORDS]; + le::read_u32_into(&seed, &mut seed_u32); + Hc128Rng::init(seed_u32) } } diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index bd82e306a74..06d16dd77be 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -10,12 +10,9 @@ //! The ISAAC random number generator. -use core::slice; +use core::{fmt, slice}; use core::num::Wrapping as w; -use core::fmt; - use rand_core::{impls, le}; - use {Rng, SeedFromRng, SeedableRng, Error}; #[allow(non_camel_case_types)] @@ -309,18 +306,14 @@ fn init(mut mem: [w32; RAND_SIZE], rounds: u32) -> IsaacRng { } } - let mut rng = IsaacRng { + IsaacRng { rsl: [0; RAND_SIZE], mem: mem, a: w(0), b: w(0), c: w(0), - index: 0, - }; - - // Prepare the first set of results - rng.isaac(); - rng + index: RAND_SIZE as u32, // generate on first use + } } fn mix(a: &mut w32, b: &mut w32, c: &mut w32, d: &mut w32, @@ -337,26 +330,28 @@ fn mix(a: &mut w32, b: &mut w32, c: &mut w32, d: &mut w32, impl SeedFromRng for IsaacRng { fn from_rng(mut other: R) -> Result { - let mut key = [w(0); RAND_SIZE]; + let mut seed = [w(0); RAND_SIZE]; unsafe { - let ptr = key.as_mut_ptr() as *mut u8; + let ptr = seed.as_mut_ptr() as *mut u8; let slice = slice::from_raw_parts_mut(ptr, RAND_SIZE * 4); other.try_fill(slice)?; } - Ok(init(key, 2)) + Ok(init(seed, 2)) } } impl SeedableRng for IsaacRng { type Seed = [u8; 32]; - fn from_seed(mut seed: Self::Seed) -> Self { - let mut key = [w(0); RAND_SIZE]; - for (x, y) in key.iter_mut().zip(le::convert_slice_32(&mut seed[..]).iter()) { + fn from_seed(seed: Self::Seed) -> Self { + let mut seed_u32 = [0u32; 8]; + le::read_u32_into(&seed, &mut seed_u32); + let mut seed_extended = [w(0); RAND_SIZE]; + for (x, y) in seed_extended.iter_mut().zip(seed_u32.iter()) { *x = w(*y); } - init(key, 2) + init(seed_extended, 2) } } diff --git a/src/prng/isaac64.rs b/src/prng/isaac64.rs index 2fc6e5c3513..93f9b7059b8 100644 --- a/src/prng/isaac64.rs +++ b/src/prng/isaac64.rs @@ -10,12 +10,9 @@ //! The ISAAC-64 random number generator. -use core::slice; +use core::{fmt, slice}; use core::num::Wrapping as w; -use core::fmt; - use rand_core::{impls, le}; - use {Rng, SeedFromRng, SeedableRng, Error}; #[allow(non_camel_case_types)] @@ -288,19 +285,15 @@ fn init(mut mem: [w64; RAND_SIZE], rounds: u32) -> Isaac64Rng { } } - let mut rng = Isaac64Rng { + Isaac64Rng { rsl: [0; RAND_SIZE], mem: mem, a: w(0), b: w(0), c: w(0), - index: 0, + index: RAND_SIZE as u32, // generate on first use half_used: false, - }; - - // Prepare the first set of results - rng.isaac64(); - rng + } } fn mix(a: &mut w64, b: &mut w64, c: &mut w64, d: &mut w64, @@ -317,26 +310,28 @@ fn mix(a: &mut w64, b: &mut w64, c: &mut w64, d: &mut w64, impl SeedFromRng for Isaac64Rng { fn from_rng(mut other: R) -> Result { - let mut key = [w(0); RAND_SIZE]; + let mut seed = [w(0); RAND_SIZE]; unsafe { - let ptr = key.as_mut_ptr() as *mut u8; + let ptr = seed.as_mut_ptr() as *mut u8; let slice = slice::from_raw_parts_mut(ptr, RAND_SIZE * 8); other.try_fill(slice)?; } - Ok(init(key, 2)) + Ok(init(seed, 2)) } } impl SeedableRng for Isaac64Rng { type Seed = [u8; 32]; - fn from_seed(mut seed: Self::Seed) -> Self { - let mut key = [w(0); RAND_SIZE]; - for (x, y) in key.iter_mut().zip(le::convert_slice_64(&mut seed[..]).iter()) { + fn from_seed(seed: Self::Seed) -> Self { + let mut seed_u64 = [0u64; 4]; + le::read_u64_into(&seed, &mut seed_u64); + let mut seed_extended = [w(0); RAND_SIZE]; + for (x, y) in seed_extended.iter_mut().zip(seed_u64.iter()) { *x = w(*y); } - init(key, 2) + init(seed_extended, 2) } } From cc6da7abe736bdf5ff6344b4c6293d5e65212356 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Sat, 23 Dec 2017 09:38:13 +0100 Subject: [PATCH 238/247] Fold from_rng into SeedableRng --- benches/generators.rs | 2 +- rand_core/src/lib.rs | 75 ++++++++++++++++++++---------------------- src/lib.rs | 40 +++++++++++----------- src/prng/chacha.rs | 18 ++-------- src/prng/hc128.rs | 14 +------- src/prng/isaac.rs | 46 ++++++++++++++------------ src/prng/isaac64.rs | 49 ++++++++++++++------------- src/prng/isaac_word.rs | 18 ++++++---- src/prng/xorshift.rs | 56 ++++++++++++++++++------------- 9 files changed, 158 insertions(+), 160 deletions(-) diff --git a/benches/generators.rs b/benches/generators.rs index d02d2d43bbd..ec603d198cf 100644 --- a/benches/generators.rs +++ b/benches/generators.rs @@ -9,7 +9,7 @@ const BYTES_LEN: usize = 1024; use std::mem::size_of; use test::{black_box, Bencher}; -use rand::{Rng, NewSeeded, Sample, SeedFromRng, StdRng, OsRng, JitterRng}; +use rand::{Rng, NewSeeded, Sample, SeedableRng, StdRng, OsRng, JitterRng}; use rand::prng::*; macro_rules! gen_bytes { diff --git a/rand_core/src/lib.rs b/rand_core/src/lib.rs index 54be12ac14a..c587c97e6f6 100644 --- a/rand_core/src/lib.rs +++ b/rand_core/src/lib.rs @@ -43,7 +43,7 @@ extern crate core; #[cfg(feature="std")] use std::error::Error as stdError; -use core::fmt; +use core::{fmt, slice, mem}; pub mod impls; @@ -206,25 +206,6 @@ impl Rng for Box { } -/// Support mechanism for creating random number generators seeded by other -/// generators. All PRNGs should support this to enable `NewSeeded` support, -/// which should be the preferred way of creating randomly-seeded generators. -/// -/// There are two subtle differences between `SeedFromRng::from_rng` and -/// `SeedableRng::from_seed` (beyond the obvious): first, that `from_rng` has -/// no reproducibility requirement, and second, that `from_rng` may directly -/// fill internal states larger than `SeedableRng::Seed`, where `from_seed` may -/// need some extra step to expand the input. -pub trait SeedFromRng: Sized { - /// Creates a new instance, seeded from another `Rng`. - /// - /// Seeding from a cryptographic generator should be fine. On the other - /// hand, seeding a simple numerical generator from another of the same - /// type sometimes has serious side effects such as effectively cloning the - /// generator. - fn from_rng(rng: R) -> Result; -} - mod private { pub trait Sealed {} impl Sealed for S where S: super::SeedRestriction {} @@ -232,43 +213,59 @@ mod private { /// The seed type is restricted to these types. This trait is sealed to prevent /// user-extension. -/// +/// /// Use of byte-arrays avoids endianness issues. We may extend this to allow /// byte arrays of other lengths in the future. -pub trait SeedRestriction: private::Sealed {} +pub trait SeedRestriction: private::Sealed + + ::core::default::Default + + ::core::convert::AsMut<[u8]> {} impl SeedRestriction for [u8; 8] {} impl SeedRestriction for [u8; 16] {} impl SeedRestriction for [u8; 32] {} -/// A random number generator that can be explicitly seeded to produce -/// the same stream of randomness multiple times (i.e. is reproducible). -/// -/// Note: this should only be implemented by reproducible generators (i.e. -/// where the algorithm is fixed and results should be the same across -/// platforms). This should not be implemented by wrapper types which choose -/// the underlying implementation based on platform, or which may change the -/// algorithm used in the future. This is to ensure that manual seeding of PRNGs -/// actually does yield reproducible results. +/// A random number generator that can be explicitly seeded. +/// +/// There are two subtle differences between `from_rng` and`from_seed` (beyond +/// the obvious): first, that `from_rng` has no reproducibility requirement, and +/// second, that `from_rng` may directly fill internal states larger than +/// `SeedableRng::Seed`, where `from_seed` may need some extra step to expand +/// the input. pub trait SeedableRng: Sized { /// Seed type. type Seed: SeedRestriction; - + /// Create a new PRNG using the given seed. - /// + /// /// Each PRNG should implement this. - /// + /// /// Reproducibility is required; that is, a fixed PRNG seeded using this /// function with a fixed seed should produce the same sequence of output /// today, and in the future. PRNGs not able to satisfy this should make - /// clear notes in their documentation or not implement `SeedableRng` at - /// all. It is however not required that this function yield the same state - /// as a reference implementation of the PRNG given equivalent seed; if - /// necessary another constructor should be used. - /// + /// clear notes in their documentation. It is however not required that this + /// function yield the same state as a reference implementation of the PRNG + /// given equivalent seed; if necessary another constructor should be used. + /// /// It may be expected that bits in the seed are well distributed, i.e. that /// values like 0, 1 and (size - 1) are unlikely. Users with poorly /// distributed input should use `from_hashable`. fn from_seed(seed: Self::Seed) -> Self; + + /// Create a new PRNG seeded from another `Rng`. + /// + /// Seeding from a cryptographic generator should be fine. On the other + /// hand, seeding a simple numerical generator from another of the same + /// type sometimes has serious side effects such as effectively cloning the + /// generator. + fn from_rng(mut rng: R) -> Result { + let mut seed = Self::Seed::default(); + let size = mem::size_of::() as usize; + unsafe { + let ptr = seed.as_mut().as_mut_ptr() as *mut u8; + let slice = slice::from_raw_parts_mut(ptr, size); + rng.try_fill(slice)?; + } + Ok(Self::from_seed(seed)) + } } diff --git a/src/lib.rs b/src/lib.rs index 9ce3f0a67e1..4f83e2b7413 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -257,7 +257,7 @@ extern crate rand_core; // core traits and types -pub use rand_core::{Rng, CryptoRng, SeedFromRng, SeedableRng, Error, ErrorKind}; +pub use rand_core::{Rng, CryptoRng, SeedableRng, Error, ErrorKind}; // external rngs pub use jitter::JitterRng; @@ -315,11 +315,11 @@ mod thread_local; /// Seeding mechanism for PRNGs, providing a `new` function. /// This is the recommended way to create (pseudo) random number generators, -/// unless a deterministic seed is desired (in which case the `SeedableRng` -/// trait should be used directly). +/// unless a deterministic seed is desired (in which case +/// `SeedableRng::from_seed` should be used). /// /// Note: this trait is automatically implemented for any PRNG implementing -/// `SeedFromRng` and is not intended to be implemented by users. +/// `SeedableRng` and is not intended to be implemented by users. /// /// ## Example /// @@ -330,7 +330,7 @@ mod thread_local; /// println!("Random die roll: {}", rng.gen_range(1, 7)); /// ``` #[cfg(feature="std")] -pub trait NewSeeded: SeedFromRng { +pub trait NewSeeded: SeedableRng { /// Creates a new instance, automatically seeded with fresh entropy. /// /// Normally this will use `OsRng`, but if that fails `JitterRng` will be @@ -340,14 +340,14 @@ pub trait NewSeeded: SeedFromRng { } #[cfg(feature="std")] -impl NewSeeded for R { +impl NewSeeded for R { fn new() -> Result { // Note: error handling would be easier with try/catch blocks - fn new_os() -> Result { + fn new_os() -> Result { let mut r = OsRng::new()?; T::from_rng(&mut r) } - fn new_jitter() -> Result { + fn new_jitter() -> Result { let mut r = JitterRng::new()?; T::from_rng(&mut r) } @@ -461,32 +461,34 @@ impl Sample for R { /// cannot be guaranteed to be reproducible. For this reason, `StdRng` does /// not support `SeedableRng`. #[derive(Clone, Debug)] -pub struct StdRng { - rng: IsaacWordRng, -} +pub struct StdRng(IsaacWordRng); impl Rng for StdRng { fn next_u32(&mut self) -> u32 { - self.rng.next_u32() + self.0.next_u32() } fn next_u64(&mut self) -> u64 { - self.rng.next_u64() + self.0.next_u64() } #[cfg(feature = "i128_support")] fn next_u128(&mut self) -> u128 { - self.rng.next_u128() + self.0.next_u128() } fn fill_bytes(&mut self, dest: &mut [u8]) { - self.rng.fill_bytes(dest); + self.0.fill_bytes(dest); } fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { - self.rng.try_fill(dest) + self.0.try_fill(dest) } } -impl SeedFromRng for StdRng { - fn from_rng(other: R) -> Result { - IsaacWordRng::from_rng(other).map(|rng| StdRng{ rng }) +impl SeedableRng for StdRng { + type Seed = ::Seed; + fn from_seed(seed: Self::Seed) -> Self { + StdRng(IsaacWordRng::from_seed(seed)) + } + fn from_rng(rng: R) -> Result { + IsaacWordRng::from_rng(rng).map(|rng| StdRng(rng)) } } diff --git a/src/prng/chacha.rs b/src/prng/chacha.rs index 54710fbe1b1..b371480505a 100644 --- a/src/prng/chacha.rs +++ b/src/prng/chacha.rs @@ -10,9 +10,9 @@ //! The ChaCha random number generator. -use core::{fmt, slice}; +use core::fmt; use rand_core::{impls, le}; -use {Rng, CryptoRng, SeedFromRng, SeedableRng, Error}; +use {Rng, CryptoRng, SeedableRng, Error}; const SEED_WORDS: usize = 8; // 8 words for the 256-bit key const STATE_WORDS: usize = 16; @@ -224,18 +224,6 @@ impl Rng for ChaChaRng { impl CryptoRng for ChaChaRng {} -impl SeedFromRng for ChaChaRng { - fn from_rng(mut other: R) -> Result { - let mut seed = [0u32; SEED_WORDS]; - unsafe { - let ptr = seed.as_mut_ptr() as *mut u8; - let slice = slice::from_raw_parts_mut(ptr, SEED_WORDS * 4); - other.try_fill(slice)?; - } - Ok(ChaChaRng::init(seed)) - } -} - impl SeedableRng for ChaChaRng { type Seed = [u8; SEED_WORDS*4]; fn from_seed(seed: Self::Seed) -> Self { @@ -248,7 +236,7 @@ impl SeedableRng for ChaChaRng { #[cfg(test)] mod test { - use {Rng, SeedableRng, SeedFromRng}; + use {Rng, SeedableRng}; use super::ChaChaRng; #[test] diff --git a/src/prng/hc128.rs b/src/prng/hc128.rs index 38e1655c3f4..41a6362a3e8 100644 --- a/src/prng/hc128.rs +++ b/src/prng/hc128.rs @@ -12,7 +12,7 @@ use core::{fmt, slice}; use rand_core::{impls, le}; -use {Rng, CryptoRng, SeedFromRng, SeedableRng, Error}; +use {Rng, CryptoRng, SeedableRng, Error}; const SEED_WORDS: usize = 8; // 128 bit key followed by 128 bit iv @@ -392,18 +392,6 @@ impl Rng for Hc128Rng { } } -impl SeedFromRng for Hc128Rng { - fn from_rng(mut other: R) -> Result { - let mut seed = [0u32; SEED_WORDS]; - unsafe { - let ptr = seed.as_mut_ptr() as *mut u8; - let slice = slice::from_raw_parts_mut(ptr, SEED_WORDS * 4); - other.try_fill(slice)?; - } - Ok(Hc128Rng::init(seed)) - } -} - impl SeedableRng for Hc128Rng { type Seed = [u8; SEED_WORDS*4]; fn from_seed(seed: Self::Seed) -> Self { diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index 06d16dd77be..6950d08c17e 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -13,7 +13,7 @@ use core::{fmt, slice}; use core::num::Wrapping as w; use rand_core::{impls, le}; -use {Rng, SeedFromRng, SeedableRng, Error}; +use {Rng, SeedableRng, Error}; #[allow(non_camel_case_types)] type w32 = w; @@ -328,22 +328,9 @@ fn mix(a: &mut w32, b: &mut w32, c: &mut w32, d: &mut w32, *h ^= *a >> 9; *c += *h; *a += *b; } -impl SeedFromRng for IsaacRng { - fn from_rng(mut other: R) -> Result { - let mut seed = [w(0); RAND_SIZE]; - unsafe { - let ptr = seed.as_mut_ptr() as *mut u8; - - let slice = slice::from_raw_parts_mut(ptr, RAND_SIZE * 4); - other.try_fill(slice)?; - } - - Ok(init(seed, 2)) - } -} - impl SeedableRng for IsaacRng { type Seed = [u8; 32]; + fn from_seed(seed: Self::Seed) -> Self { let mut seed_u32 = [0u32; 8]; le::read_u32_into(&seed, &mut seed_u32); @@ -353,11 +340,27 @@ impl SeedableRng for IsaacRng { } init(seed_extended, 2) } + + fn from_rng(mut other: R) -> Result { + // Custom `from_rng` implementations that fills the entire state + let mut seed = [w(0u32); RAND_SIZE]; + unsafe { + let ptr = seed.as_mut_ptr() as *mut u8; + + let slice = slice::from_raw_parts_mut(ptr, RAND_SIZE * 4); + other.try_fill(slice)?; + } + for i in seed.iter_mut() { + *i = w(i.0.to_le()); + } + + Ok(init(seed, 2)) + } } #[cfg(test)] mod test { - use {Rng, SeedableRng, SeedFromRng}; + use {Rng, SeedableRng}; use super::IsaacRng; #[test] @@ -367,12 +370,13 @@ mod test { let mut rng1 = IsaacRng::from_hashable("some weak seed"); rng1.next_u32(); */ - let mut rng2 = IsaacRng::from_rng(&mut ::test::rng()).unwrap(); - rng2.next_u32(); - + let seed = [1,0,0,0, 23,0,0,0, 200,1,0,0, 210,30,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]; - let mut rng3 = IsaacRng::from_seed(seed); - rng3.next_u32(); + let mut rng2 = IsaacRng::from_seed(seed); + assert_eq!(rng2.next_u32(), 2869442790); + + let mut rng3 = IsaacRng::from_rng(&mut rng2).unwrap(); + assert_eq!(rng3.next_u32(), 3094074039); } #[test] diff --git a/src/prng/isaac64.rs b/src/prng/isaac64.rs index 93f9b7059b8..85da60e6b4a 100644 --- a/src/prng/isaac64.rs +++ b/src/prng/isaac64.rs @@ -13,7 +13,7 @@ use core::{fmt, slice}; use core::num::Wrapping as w; use rand_core::{impls, le}; -use {Rng, SeedFromRng, SeedableRng, Error}; +use {Rng, SeedableRng, Error}; #[allow(non_camel_case_types)] type w64 = w; @@ -308,22 +308,9 @@ fn mix(a: &mut w64, b: &mut w64, c: &mut w64, d: &mut w64, *h -= *d; *e ^= *g << 14; *g += *h; } -impl SeedFromRng for Isaac64Rng { - fn from_rng(mut other: R) -> Result { - let mut seed = [w(0); RAND_SIZE]; - unsafe { - let ptr = seed.as_mut_ptr() as *mut u8; - - let slice = slice::from_raw_parts_mut(ptr, RAND_SIZE * 8); - other.try_fill(slice)?; - } - - Ok(init(seed, 2)) - } -} - impl SeedableRng for Isaac64Rng { type Seed = [u8; 32]; + fn from_seed(seed: Self::Seed) -> Self { let mut seed_u64 = [0u64; 4]; le::read_u64_into(&seed, &mut seed_u64); @@ -333,11 +320,27 @@ impl SeedableRng for Isaac64Rng { } init(seed_extended, 2) } + + fn from_rng(mut other: R) -> Result { + // Custom `from_rng` implementations that fills the entire state + let mut seed = [w(0u64); RAND_SIZE]; + unsafe { + let ptr = seed.as_mut_ptr() as *mut u8; + + let slice = slice::from_raw_parts_mut(ptr, RAND_SIZE * 8); + other.try_fill(slice)?; + } + for i in seed.iter_mut() { + *i = w(i.0.to_le()); + } + + Ok(init(seed, 2)) + } } #[cfg(test)] mod test { - use {Rng, SeedableRng, SeedFromRng}; + use {Rng, SeedableRng}; use super::Isaac64Rng; #[test] @@ -347,13 +350,13 @@ mod test { let mut rng1 = Isaac64Rng::from_hashable("some weak seed"); rng1.next_u64(); */ - let mut rng2 = Isaac64Rng::from_rng(&mut ::test::rng()).unwrap(); - rng2.next_u64(); - - let seed = [1,0,0,0, 0,0,0,0, 23,0,0,0, 0,0,0,0, 200,1,0,0, 0,0,0,0, 210,30,0,0, 0,0,0,0]; - let mut rng3 = Isaac64Rng::from_seed(seed); - rng3.next_u64(); - + + let seed = [1,0,0,0, 23,0,0,0, 200,1,0,0, 210,30,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]; + let mut rng2 = Isaac64Rng::from_seed(seed); + assert_eq!(rng2.next_u64(), 14964555543728284049); + + let mut rng3 = Isaac64Rng::from_rng(&mut rng2).unwrap(); + assert_eq!(rng3.next_u64(), 919595328260451758); } #[test] diff --git a/src/prng/isaac_word.rs b/src/prng/isaac_word.rs index 330656b3b8b..1770244f048 100644 --- a/src/prng/isaac_word.rs +++ b/src/prng/isaac_word.rs @@ -10,7 +10,7 @@ //! The ISAAC random number generator. -use {Rng, SeedFromRng, Error}; +use {Rng, SeedableRng, Error}; #[cfg(target_pointer_width = "32")] type WordRngType = super::isaac::IsaacRng; @@ -40,12 +40,12 @@ impl Rng for IsaacWordRng { fn next_u64(&mut self) -> u64 { self.0.next_u64() } - + #[cfg(feature = "i128_support")] fn next_u128(&mut self) -> u128 { self.0.next_u128() } - + fn fill_bytes(&mut self, dest: &mut [u8]) { self.0.fill_bytes(dest) } @@ -55,8 +55,14 @@ impl Rng for IsaacWordRng { } } -impl SeedFromRng for IsaacWordRng { - fn from_rng(other: R) -> Result { - WordRngType::from_rng(other).map(|rng| IsaacWordRng(rng)) +impl SeedableRng for IsaacWordRng { + type Seed = ::Seed; + + fn from_seed(seed: Self::Seed) -> Self { + IsaacWordRng(WordRngType::from_seed(seed)) + } + + fn from_rng(rng: R) -> Result { + WordRngType::from_rng(rng).map(|rng| IsaacWordRng(rng)) } } diff --git a/src/prng/xorshift.rs b/src/prng/xorshift.rs index 4fb829bba56..d2b237663b5 100644 --- a/src/prng/xorshift.rs +++ b/src/prng/xorshift.rs @@ -11,8 +11,8 @@ //! Xorshift generators use core::num::Wrapping as w; -use core::fmt; -use {Rng, SeedFromRng, SeedableRng, Error}; +use core::{fmt, slice}; +use {Rng, SeedableRng, Error}; use rand_core::le; /// An Xorshift[1] random number @@ -57,20 +57,6 @@ impl XorShiftRng { } } -impl SeedFromRng for XorShiftRng { - fn from_rng(mut rng: R) -> Result { - let mut tuple: (u32, u32, u32, u32); - loop { - tuple = (rng.next_u32(), rng.next_u32(), rng.next_u32(), rng.next_u32()); - if tuple != (0, 0, 0, 0) { - break; - } - } - let (x, y, z, w_) = tuple; - Ok(XorShiftRng { x: w(x), y: w(y), z: w(z), w: w(w_) }) - } -} - impl Rng for XorShiftRng { #[inline] fn next_u32(&mut self) -> u32 { @@ -103,16 +89,40 @@ impl Rng for XorShiftRng { impl SeedableRng for XorShiftRng { type Seed = [u8; 16]; - /// Create a new XorShiftRng. This will panic if `seed` is entirely 0. + fn from_seed(seed: Self::Seed) -> Self { - assert!(!seed.iter().all(|&x| x == 0), - "XorShiftRng::from_seed called with an all zero seed."); + let mut seed_u32 = [0u32; 4]; + le::read_u32_into(&seed, &mut seed_u32); + + if seed_u32.iter().all(|&x| x == 0) { + seed_u32 = [0xBAD_5EED, 0xBAD_5EED, 0xBAD_5EED, 0xBAD_5EED]; + } XorShiftRng { - x: w(le::read_u32(array_ref!(seed, 0, 4))), - y: w(le::read_u32(array_ref!(seed, 4, 4))), - z: w(le::read_u32(array_ref!(seed, 8, 4))), - w: w(le::read_u32(array_ref!(seed, 12, 4))), + x: w(seed_u32[0]), + y: w(seed_u32[1]), + z: w(seed_u32[2]), + w: w(seed_u32[3]), } } + + fn from_rng(mut rng: R) -> Result { + let mut seed_u32 = [0u32; 4]; + loop { + unsafe { + let ptr = seed_u32.as_mut_ptr() as *mut u8; + + let slice = slice::from_raw_parts_mut(ptr, 4 * 4); + rng.try_fill(slice)?; + } + if !seed_u32.iter().all(|&x| x == 0) { break; } + } + + Ok(XorShiftRng { + x: w(seed_u32[0]), + y: w(seed_u32[1]), + z: w(seed_u32[2]), + w: w(seed_u32[3]), + }) + } } From e9359ac5f19a0678568f7e8096cad72e69cb1fa4 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Wed, 27 Dec 2017 17:42:38 +0000 Subject: [PATCH 239/247] Fix sealed implementation of SeedRestriction to prevent extension --- rand_core/src/lib.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/rand_core/src/lib.rs b/rand_core/src/lib.rs index c587c97e6f6..e95ee95a91e 100644 --- a/rand_core/src/lib.rs +++ b/rand_core/src/lib.rs @@ -44,6 +44,8 @@ extern crate core; use std::error::Error as stdError; use core::{fmt, slice, mem}; +use core::default::Default; +use core::convert::AsMut; pub mod impls; @@ -208,7 +210,9 @@ impl Rng for Box { mod private { pub trait Sealed {} - impl Sealed for S where S: super::SeedRestriction {} + impl Sealed for [u8; 8] {} + impl Sealed for [u8; 16] {} + impl Sealed for [u8; 32] {} } /// The seed type is restricted to these types. This trait is sealed to prevent @@ -216,12 +220,8 @@ mod private { /// /// Use of byte-arrays avoids endianness issues. We may extend this to allow /// byte arrays of other lengths in the future. -pub trait SeedRestriction: private::Sealed + - ::core::default::Default + - ::core::convert::AsMut<[u8]> {} -impl SeedRestriction for [u8; 8] {} -impl SeedRestriction for [u8; 16] {} -impl SeedRestriction for [u8; 32] {} +pub trait SeedRestriction: private::Sealed + Default + AsMut<[u8]> {} +impl SeedRestriction for S where S: private::Sealed + Default + AsMut<[u8]> {} /// A random number generator that can be explicitly seeded. /// From 945e8ff0b34545133bbe38228b6baa4cd045fa8e Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Thu, 28 Dec 2017 14:53:24 +0100 Subject: [PATCH 240/247] Make u128 range use widening multiply --- src/distributions/range.rs | 132 +++++++++++++++---------------------- 1 file changed, 54 insertions(+), 78 deletions(-) diff --git a/src/distributions/range.rs b/src/distributions/range.rs index 9120028de8d..ca4f8cb7da2 100644 --- a/src/distributions/range.rs +++ b/src/distributions/range.rs @@ -166,7 +166,7 @@ pub struct RangeInt { macro_rules! range_int_impl { ($ty:ty, $signed:ident, $unsigned:ident, - $i_large:ident, $u_large:ident, $use_mult:expr) => { + $i_large:ident, $u_large:ident) => { impl SampleRange for $ty { type T = RangeInt<$ty>; } @@ -262,15 +262,9 @@ macro_rules! range_int_impl { let zone = self.zone as $signed as $i_large as $u_large; loop { let v: $u_large = Uniform.sample(rng); - if $use_mult { - let (hi, lo) = v.wmul(range); - if lo <= zone { - return self.low.wrapping_add(hi as $ty); - } - } else { - if v <= zone { - return self.low.wrapping_add((v % range) as $ty); - } + let (hi, lo) = v.wmul(range); + if lo <= zone { + return self.low.wrapping_add(hi as $ty); } } } else { @@ -297,15 +291,9 @@ macro_rules! range_int_impl { loop { let v: $u_large = Uniform.sample(rng); - if $use_mult { - let (hi, lo) = v.wmul(range); - if lo <= zone { - return low.wrapping_add(hi as $ty); - } - } else { - if v <= zone { - return low.wrapping_add((v % range) as $ty); - } + let (hi, lo) = v.wmul(range); + if lo <= zone { + return low.wrapping_add(hi as $ty); } } } @@ -313,20 +301,20 @@ macro_rules! range_int_impl { } } -range_int_impl! { i8, i8, u8, i32, u32, true } -range_int_impl! { i16, i16, u16, i32, u32, true } -range_int_impl! { i32, i32, u32, i32, u32, true } -range_int_impl! { i64, i64, u64, i64, u64, true } +range_int_impl! { i8, i8, u8, i32, u32 } +range_int_impl! { i16, i16, u16, i32, u32 } +range_int_impl! { i32, i32, u32, i32, u32 } +range_int_impl! { i64, i64, u64, i64, u64 } #[cfg(feature = "i128_support")] -range_int_impl! { i128, i128, u128, u128, u128, false } -range_int_impl! { isize, isize, usize, isize, usize, true } -range_int_impl! { u8, i8, u8, i32, u32, true } -range_int_impl! { u16, i16, u16, i32, u32, true } -range_int_impl! { u32, i32, u32, i32, u32, true } -range_int_impl! { u64, i64, u64, i64, u64, true } -range_int_impl! { usize, isize, usize, isize, usize, true } +range_int_impl! { i128, i128, u128, u128, u128 } +range_int_impl! { isize, isize, usize, isize, usize } +range_int_impl! { u8, i8, u8, i32, u32 } +range_int_impl! { u16, i16, u16, i32, u32 } +range_int_impl! { u32, i32, u32, i32, u32 } +range_int_impl! { u64, i64, u64, i64, u64 } +range_int_impl! { usize, isize, usize, isize, usize } #[cfg(feature = "i128_support")] -range_int_impl! { u128, u128, u128, i128, u128, false } +range_int_impl! { u128, u128, u128, i128, u128 } trait WideningMultiply { @@ -352,40 +340,47 @@ macro_rules! wmul_impl { wmul_impl! { u8, u16, 8 } wmul_impl! { u16, u32, 16 } wmul_impl! { u32, u64, 32 } - #[cfg(feature = "i128_support")] wmul_impl! { u64, u128, 64 } -#[cfg(not(feature = "i128_support"))] -impl WideningMultiply for u64 { - type Output = (u64, u64); - - // This code is a translation of the __mulddi3 function in LLVM's - // compiler-rt. It is an optimised variant of the common method - // `(a + b) * (c + d) = ac + ad + bc + bd`. - // - // For some reason LLVM can optimise the C version very well, but keeps - // shuffeling registers in this Rust translation. - #[inline(always)] - fn wmul(self, b: u64) -> Self::Output { - const LOWER_MASK: u64 = !0u64 >> 32; - let mut low = (self & LOWER_MASK).wrapping_mul(b & LOWER_MASK); - let mut t = low >> 32; - low &= LOWER_MASK; - t += (self >> 32).wrapping_mul(b & LOWER_MASK); - low += (t & LOWER_MASK) << 32; - let mut high = (t >> 32) as i64; - t = low >> 32; - low &= LOWER_MASK; - t += (b >> 32).wrapping_mul(self & LOWER_MASK); - low += (t & LOWER_MASK) << 32; - high += (t >> 32) as i64; - high += (self >> 32).wrapping_mul(b >> 32) as i64; - - (high as u64, low) +// This code is a translation of the __mulddi3 function in LLVM's +// compiler-rt. It is an optimised variant of the common method +// `(a + b) * (c + d) = ac + ad + bc + bd`. +// +// For some reason LLVM can optimise the C version very well, but +// keeps shuffeling registers in this Rust translation. +macro_rules! wmul_impl_large { + ($ty:ty, $half:expr) => { + impl WideningMultiply for $ty { + type Output = ($ty, $ty); + + #[inline(always)] + fn wmul(self, b: $ty) -> Self::Output { + const LOWER_MASK: $ty = !0 >> $half; + let mut low = (self & LOWER_MASK).wrapping_mul(b & LOWER_MASK); + let mut t = low >> $half; + low &= LOWER_MASK; + t += (self >> $half).wrapping_mul(b & LOWER_MASK); + low += (t & LOWER_MASK) << $half; + let mut high = t >> $half; + t = low >> $half; + low &= LOWER_MASK; + t += (b >> $half).wrapping_mul(self & LOWER_MASK); + low += (t & LOWER_MASK) << $half; + high += t >> $half; + high += (self >> $half).wrapping_mul(b >> $half); + + (high, low) + } + } } } +#[cfg(not(feature = "i128_support"))] +wmul_impl_large! { u64, 32 } +#[cfg(feature = "i128_support")] +wmul_impl_large! { u128, 64 } + macro_rules! wmul_impl_usize { ($ty:ty) => { @@ -406,25 +401,6 @@ wmul_impl_usize! { u32 } #[cfg(target_pointer_width = "64")] wmul_impl_usize! { u64 } -#[cfg(feature = "i128_support")] -macro_rules! wmul_impl_128 { - ($ty:ty) => { - impl WideningMultiply for $ty { - type Output = ($ty, $ty); - - #[inline(always)] - fn wmul(self, _: $ty) -> Self::Output { - unimplemented!(); - } - } - } -} - -#[cfg(feature = "i128_support")] -wmul_impl_128! { i128 } -#[cfg(feature = "i128_support")] -wmul_impl_128! { u128 } - /// Implementation of `RangeImpl` for float types. From 4ea098b9ad7f33ba1e15a0e98054a7dbb9273de0 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Fri, 29 Dec 2017 16:20:41 +0100 Subject: [PATCH 241/247] Restrict the seed type to a few more array sizes --- rand_core/src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/rand_core/src/lib.rs b/rand_core/src/lib.rs index e95ee95a91e..fa111bb019e 100644 --- a/rand_core/src/lib.rs +++ b/rand_core/src/lib.rs @@ -210,8 +210,11 @@ impl Rng for Box { mod private { pub trait Sealed {} + impl Sealed for [u8; 4] {} impl Sealed for [u8; 8] {} + impl Sealed for [u8; 12] {} impl Sealed for [u8; 16] {} + impl Sealed for [u8; 24] {} impl Sealed for [u8; 32] {} } From 402e673eb3c41f69d017e30bdc1dad65f9edbbfd Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Sat, 16 Dec 2017 19:50:43 +0100 Subject: [PATCH 242/247] Add benchmarks for `ReseedingRng` --- benches/generators.rs | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/benches/generators.rs b/benches/generators.rs index ec603d198cf..1c0262e7d56 100644 --- a/benches/generators.rs +++ b/benches/generators.rs @@ -11,6 +11,7 @@ use test::{black_box, Bencher}; use rand::{Rng, NewSeeded, Sample, SeedableRng, StdRng, OsRng, JitterRng}; use rand::prng::*; +use rand::reseeding::{ReseedingRng, ReseedWithNew}; macro_rules! gen_bytes { ($fnn:ident, $gen:ident) => { @@ -102,3 +103,40 @@ fn init_jitter(b: &mut Bencher) { black_box(JitterRng::new().unwrap()); }); } + + + +#[bench] +fn reseeding_xorshift_bytes(b: &mut Bencher) { + let mut rng = ReseedingRng::new(XorShiftRng::new().unwrap(), + 128*1024*1024*1024, + ReseedWithNew); + let mut buf = [0u8; BYTES_LEN]; + b.iter(|| { + for _ in 0..RAND_BENCH_N { + rng.fill_bytes(&mut buf); + black_box(buf); + } + }); + b.bytes = BYTES_LEN as u64 * RAND_BENCH_N; +} + +macro_rules! reseeding_uint { + ($fnn:ident, $ty:ty) => { + #[bench] + fn $fnn(b: &mut Bencher) { + let mut rng = ReseedingRng::new(XorShiftRng::new().unwrap(), + 128*1024*1024*1024, + ReseedWithNew); + b.iter(|| { + for _ in 0..RAND_BENCH_N { + black_box(rng.gen::<$ty>()); + } + }); + b.bytes = size_of::<$ty>() as u64 * RAND_BENCH_N; + } + } +} + +reseeding_uint!(reseeding_xorshift_u32, u32); +reseeding_uint!(reseeding_xorshift_u64, u64); From 4d1032893acaeb9483ff5b89d30f42e537d61b49 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Sat, 16 Dec 2017 19:50:48 +0100 Subject: [PATCH 243/247] Improve performance of `ReseedingRng` * Move the check if it is time to reseed out of the `try_reseed_if_necessary` and make sure that function does not get inlined. * Invert the counter direction. This way we can compare against 0 instead of `self.threshold` * Doing the reseed check after generating a value turns out to be a bit faster.` --- src/reseeding.rs | 131 +++++++++++++++++++++++++---------------------- 1 file changed, 71 insertions(+), 60 deletions(-) diff --git a/src/reseeding.rs b/src/reseeding.rs index 5d95910825f..3187eefd3f1 100644 --- a/src/reseeding.rs +++ b/src/reseeding.rs @@ -11,14 +11,13 @@ //! A wrapper around another RNG that reseeds it after it //! generates a certain number of random bytes. -use core::cmp::max; use {Rng, SeedableRng, Error, ErrorKind}; #[cfg(feature="std")] use NewSeeded; /// How many bytes of entropy the underling RNG is allowed to generate /// before it is reseeded -const DEFAULT_GENERATION_THRESHOLD: u64 = 32 * 1024; +const DEFAULT_RESEEDING_THRESHOLD: i64 = 32 * 1024; /// A wrapper around any RNG which reseeds the underlying RNG after it /// has generated a certain number of random bytes. @@ -32,8 +31,8 @@ const DEFAULT_GENERATION_THRESHOLD: u64 = 32 * 1024; #[derive(Debug, Clone)] pub struct ReseedingRng> { rng: R, - generation_threshold: u64, - bytes_generated: u64, + threshold: i64, + bytes_until_reseed: i64, /// Controls the behaviour when reseeding the RNG. pub reseeder: Rsdr, } @@ -44,13 +43,14 @@ impl> ReseedingRng { /// # Arguments /// /// * `rng`: the random number generator to use. - /// * `generation_threshold`: the number of bytes of entropy at which to reseed the RNG. + /// * `threshold`: the amount of generated bytes after which to reseed the RNG. /// * `reseeder`: the reseeding object to use. - pub fn new(rng: R, generation_threshold: u64, reseeder: Rsdr) -> ReseedingRng { + pub fn new(rng: R, threshold: u64, reseeder: Rsdr) -> ReseedingRng { + assert!(threshold <= ::core::i64::MAX as u64); ReseedingRng { rng: rng, - generation_threshold: generation_threshold, - bytes_generated: 0, + threshold: threshold as i64, + bytes_until_reseed: threshold as i64, reseeder: reseeder } } @@ -59,33 +59,31 @@ impl> ReseedingRng { /// generated exceed the threshold. /// /// On error, this may delay reseeding or not reseed at all. - pub fn reseed_if_necessary(&mut self) { - if self.bytes_generated >= self.generation_threshold { - let mut err_count = 0; - loop { - if let Err(e) = self.reseeder.reseed(&mut self.rng) { - // TODO: log? - if e.kind.should_wait() { - // Delay reseeding - let delay = max(self.generation_threshold >> 8, self.bytes_generated); - self.bytes_generated -= delay; - break; - } else if e.kind.should_retry() { - if err_count > 4 { // arbitrary limit - // TODO: log details & cause? - break; // give up trying to reseed - } - err_count += 1; - continue; // immediate retry - } else { + #[inline(never)] + pub fn reseed(&mut self) { + let mut err_count = 0; + loop { + if let Err(e) = self.reseeder.reseed(&mut self.rng) { + // TODO: log? + if e.kind.should_wait() { + // Delay reseeding + self.bytes_until_reseed = self.threshold >> 8; + break; + } else if e.kind.should_retry() { + if err_count > 4 { // arbitrary limit + // TODO: log details & cause? break; // give up trying to reseed } + err_count += 1; + continue; // immediate retry } else { - break; // no reseeding + break; // give up trying to reseed } + } else { + break; // no reseeding } - self.bytes_generated = 0; } + self.bytes_until_reseed = self.threshold; } /// Reseed the internal RNG if the number of bytes that have been /// generated exceed the threshold. @@ -93,21 +91,20 @@ impl> ReseedingRng { /// If reseeding fails, return an error with the original cause. Note that /// if the cause has a permanent failure, we report a transient error and /// skip reseeding. - pub fn try_reseed_if_necessary(&mut self) -> Result<(), Error> { - if self.bytes_generated >= self.generation_threshold { - if let Err(err) = self.reseeder.reseed(&mut self.rng) { - let newkind = match err.kind { - a @ ErrorKind::NotReady => a, - b @ ErrorKind::Transient => b, - _ => { - self.bytes_generated = 0; // skip reseeding - ErrorKind::Transient - } - }; - return Err(Error::with_cause(newkind, "reseeding failed", err)); - } - self.bytes_generated = 0; + #[inline(never)] + pub fn try_reseed(&mut self) -> Result<(), Error> { + if let Err(err) = self.reseeder.reseed(&mut self.rng) { + let newkind = match err.kind { + a @ ErrorKind::NotReady => a, + b @ ErrorKind::Transient => b, + _ => { + self.bytes_until_reseed = self.threshold; // skip reseeding + ErrorKind::Transient + } + }; + return Err(Error::with_cause(newkind, "reseeding failed", err)); } + self.bytes_until_reseed = self.threshold; Ok(()) } } @@ -115,45 +112,59 @@ impl> ReseedingRng { impl> Rng for ReseedingRng { fn next_u32(&mut self) -> u32 { - self.reseed_if_necessary(); - self.bytes_generated += 4; - self.rng.next_u32() + let value = self.rng.next_u32(); + self.bytes_until_reseed -= 4; + if self.bytes_until_reseed <= 0 { + self.reseed(); + } + value } fn next_u64(&mut self) -> u64 { - self.reseed_if_necessary(); - self.bytes_generated += 8; - self.rng.next_u64() + let value = self.rng.next_u64(); + self.bytes_until_reseed -= 8; + if self.bytes_until_reseed <= 0 { + self.reseed(); + } + value } #[cfg(feature = "i128_support")] fn next_u128(&mut self) -> u128 { - self.reseed_if_necessary(); - self.bytes_generated += 16; - self.rng.next_u128() + let value = self.rng.next_u128(); + self.bytes_until_reseed -= 16; + if self.bytes_until_reseed <= 0 { + self.reseed(); + } + value } fn fill_bytes(&mut self, dest: &mut [u8]) { - self.reseed_if_necessary(); - self.bytes_generated += dest.len() as u64; self.rng.fill_bytes(dest); + self.bytes_until_reseed -= dest.len() as i64; + if self.bytes_until_reseed <= 0 { + self.reseed(); + } } fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { - self.try_reseed_if_necessary()?; - self.bytes_generated += dest.len() as u64; - self.rng.try_fill(dest) + self.rng.try_fill(dest)?; + self.bytes_until_reseed -= dest.len() as i64; + if self.bytes_until_reseed <= 0 { + self.try_reseed()?; + } + Ok(()) } } impl> ReseedingRng { /// Create a new `ReseedingRng` from the given reseeder and - /// seed. This uses a default value for `generation_threshold`. + /// seed. This uses a default value for `threshold`. pub fn from_reseeder(rsdr: Rsdr, seed: ::Seed) -> ReseedingRng { ReseedingRng { rng: SeedableRng::from_seed(seed), - generation_threshold: DEFAULT_GENERATION_THRESHOLD, - bytes_generated: 0, + threshold: DEFAULT_RESEEDING_THRESHOLD, + bytes_until_reseed: DEFAULT_RESEEDING_THRESHOLD, reseeder: rsdr } } From 07717bcafde6ae31db1b052e48a5da8c048e9378 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Sun, 31 Dec 2017 13:29:46 +0100 Subject: [PATCH 244/247] Simplify reseeding erro logic --- src/reseeding.rs | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/src/reseeding.rs b/src/reseeding.rs index 3187eefd3f1..fd48051fe20 100644 --- a/src/reseeding.rs +++ b/src/reseeding.rs @@ -55,12 +55,11 @@ impl> ReseedingRng { } } - /// Reseed the internal RNG if the number of bytes that have been - /// generated exceed the threshold. - /// + /// Reseed the internal RNG. /// On error, this may delay reseeding or not reseed at all. #[inline(never)] pub fn reseed(&mut self) { + self.bytes_until_reseed = self.threshold; let mut err_count = 0; loop { if let Err(e) = self.reseeder.reseed(&mut self.rng) { @@ -68,23 +67,18 @@ impl> ReseedingRng { if e.kind.should_wait() { // Delay reseeding self.bytes_until_reseed = self.threshold >> 8; - break; } else if e.kind.should_retry() { - if err_count > 4 { // arbitrary limit - // TODO: log details & cause? - break; // give up trying to reseed - } err_count += 1; - continue; // immediate retry - } else { - break; // give up trying to reseed + if err_count <= 5 { // arbitrary limit + continue; // retry immediately + } } - } else { - break; // no reseeding + // give up trying to reseed } + break; // successfully reseeded, delayed, or given up. } - self.bytes_until_reseed = self.threshold; } + /// Reseed the internal RNG if the number of bytes that have been /// generated exceed the threshold. /// From 8990da2662cb1b3021206ce3355d3904cbc33e61 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 27 Nov 2017 14:39:37 +0000 Subject: [PATCH 245/247] Add rand_core::le::test_read unit test Also minor doc fix --- rand_core/src/lib.rs | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/rand_core/src/lib.rs b/rand_core/src/lib.rs index fa111bb019e..ce8c15d6bd0 100644 --- a/rand_core/src/lib.rs +++ b/rand_core/src/lib.rs @@ -17,8 +17,8 @@ //! `Rng` is the core trait implemented by algorithmic pseudo-random number //! generators and external random-number sources. //! -//! `SeedFromRng` and `SeedableRng` are extension traits for construction and -//! reseeding. +//! `SeedableRng` is an extension trait for construction from fixed seeds and +//! other random number generators. //! //! `Error` is provided for error-handling. It is safe to use in `no_std` //! environments. @@ -443,4 +443,22 @@ pub mod le { pub fn read_u64_into(src: &[u8], dst: &mut [u64]) { read_slice!(src, dst, 8, to_le); } + + #[test] + fn test_read() { + assert_eq!(read_u32(&[1, 2, 3, 4]), 0x04030201); + assert_eq!(read_u64(&[1, 2, 3, 4, 5, 6, 7, 8]), 0x0807060504030201); + + let bytes = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; + + let mut buf = [0u32; 4]; + read_u32_into(&bytes, &mut buf); + assert_eq!(buf[0], 0x04030201); + assert_eq!(buf[3], 0x100F0E0D); + + let mut buf = [0u64; 2]; + read_u64_into(&bytes, &mut buf); + assert_eq!(buf[0], 0x0807060504030201); + assert_eq!(buf[1], 0x100F0E0D0C0B0A09); + } } From 14f02a226390a02fa2b42146c3c0c03243da80fa Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Sun, 31 Dec 2017 16:34:34 +0100 Subject: [PATCH 246/247] Benchmark reseeding HC-128 instead of Xorshift --- benches/generators.rs | 10 +++++----- src/reseeding.rs | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/benches/generators.rs b/benches/generators.rs index 1c0262e7d56..b01a1725ceb 100644 --- a/benches/generators.rs +++ b/benches/generators.rs @@ -107,8 +107,8 @@ fn init_jitter(b: &mut Bencher) { #[bench] -fn reseeding_xorshift_bytes(b: &mut Bencher) { - let mut rng = ReseedingRng::new(XorShiftRng::new().unwrap(), +fn reseeding_hc128_bytes(b: &mut Bencher) { + let mut rng = ReseedingRng::new(Hc128Rng::new().unwrap(), 128*1024*1024*1024, ReseedWithNew); let mut buf = [0u8; BYTES_LEN]; @@ -125,7 +125,7 @@ macro_rules! reseeding_uint { ($fnn:ident, $ty:ty) => { #[bench] fn $fnn(b: &mut Bencher) { - let mut rng = ReseedingRng::new(XorShiftRng::new().unwrap(), + let mut rng = ReseedingRng::new(Hc128Rng::new().unwrap(), 128*1024*1024*1024, ReseedWithNew); b.iter(|| { @@ -138,5 +138,5 @@ macro_rules! reseeding_uint { } } -reseeding_uint!(reseeding_xorshift_u32, u32); -reseeding_uint!(reseeding_xorshift_u64, u64); +reseeding_uint!(reseeding_hc128_u32, u32); +reseeding_uint!(reseeding_hc128_u64, u64); diff --git a/src/reseeding.rs b/src/reseeding.rs index fd48051fe20..8af604f697e 100644 --- a/src/reseeding.rs +++ b/src/reseeding.rs @@ -43,7 +43,7 @@ impl> ReseedingRng { /// # Arguments /// /// * `rng`: the random number generator to use. - /// * `threshold`: the amount of generated bytes after which to reseed the RNG. + /// * `threshold`: the number of generated bytes after which to reseed the RNG. /// * `reseeder`: the reseeding object to use. pub fn new(rng: R, threshold: u64, reseeder: Rsdr) -> ReseedingRng { assert!(threshold <= ::core::i64::MAX as u64); From a4ff5d7f2fb5ff35702c83e1e8e9c0f06218a051 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 8 Jan 2018 12:24:31 +0000 Subject: [PATCH 247/247] Merge in upstream --- CHANGELOG.md | 6 + Cargo.toml | 4 +- README.md | 10 +- src/jitter.rs | 1516 +++++++++++++++++++++---------------------- src/lib.rs | 2 +- src/os.rs | 2 +- src/prng/isaac.rs | 16 +- src/prng/isaac64.rs | 6 + 8 files changed, 793 insertions(+), 769 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f39d29c2d4b..4898fc50933 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [0.4.2] - 2018-01-05 +### Changed +- Use winapi on Windows +- Update for Fuchsia OS +- Remove dev-dependency on `log` + ## [0.4.1] - 2017-12-17 ### Added - `no_std` support diff --git a/Cargo.toml b/Cargo.toml index 1eb23d16837..c5e7b5e9062 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rand" -version = "0.4.1" +version = "0.4.2" authors = ["The Rust Project Developers"] license = "MIT/Apache-2.0" readme = "README.md" @@ -36,4 +36,4 @@ winapi = { version = "0.3", features = ["minwindef", "ntsecapi", "profileapi", " members = ["rand_core"] [target.'cfg(target_os = "fuchsia")'.dependencies] -fuchsia-zircon = "0.3" +fuchsia-zircon = "0.3.2" diff --git a/README.md b/README.md index 3176e37581b..28a6a84c20a 100644 --- a/README.md +++ b/README.md @@ -29,10 +29,12 @@ The `rand` crate has been at version `0.3` since March 2015. If you wish to avoid all breaking changes you may wish to stick with this version. Version `0.4`was released in December 2017. It contains almost no breaking -changes since the `0.3` series, but nevertheless (will) contain a significant -amount of new code, including a new "external" entropy source (`JitterRng`), -`no_std` support, and significant performance improvements for the ISAAC random -number generators. +changes since the `0.3` series, but nevertheless contains some significant +new code, including a new "external" entropy source (`JitterRng`) and `no_std` +support. + +Version `0.5` is in development and contains significant performance +improvements for the ISAAC random number generators. ## Examples diff --git a/src/jitter.rs b/src/jitter.rs index f2f208e52a0..51ef61e1b6e 100644 --- a/src/jitter.rs +++ b/src/jitter.rs @@ -1,758 +1,758 @@ -// Copyright 2017 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. -// -// Based on jitterentropy-library, http://www.chronox.de/jent.html. -// Copyright Stephan Mueller , 2014 - 2017. -// -// With permission from Stephan Mueller to relicense the Rust translation under -// the MIT license. - -//! Non-physical true random number generator based on timing jitter. - -use {CryptoRng, Rng, Error, ErrorKind}; -use rand_core::impls; - -use core::{fmt, mem, ptr}; -#[cfg(feature="std")] -use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering}; - -const MEMORY_BLOCKS: usize = 64; -const MEMORY_BLOCKSIZE: usize = 32; -const MEMORY_SIZE: usize = MEMORY_BLOCKS * MEMORY_BLOCKSIZE; - -/// A true random number generator based on jitter in the CPU execution time, -/// and jitter in memory access time. -/// -/// This is a true random number generator, as opposed to pseudo-random -/// generators. Random numbers generated by `JitterRng` can be seen as fresh -/// entropy. A consequence is that is orders of magnitude slower than `OsRng` -/// and PRNGs (about 10^3 .. 10^6 slower). -/// -/// There are very few situations where using this RNG is appropriate. Only very -/// few applications require true entropy. A normal PRNG can be statistically -/// indistinguishable, and a cryptographic PRNG should also be as impossible to -/// predict. -/// -/// Use of `JitterRng` is recommended for initializing cryptographic PRNGs when -/// `OsRng` is not available. -/// -/// This implementation is based on -/// [Jitterentropy](http://www.chronox.de/jent.html) version 2.1.0. -// -// Note: the C implementation relies on being compiled without optimizations. -// This implementation goes through lengths to make the compiler not optimise -// out what is technically dead code, but that does influence timing jitter. -pub struct JitterRng { - data: u64, // Actual random number - // Number of rounds to run the entropy collector per 64 bits - rounds: u32, - // Timer and previous time stamp, used by `measure_jitter` - timer: fn() -> u64, - prev_time: u64, - // Deltas used for the stuck test - last_delta: i64, - last_delta2: i64, - // Memory for the Memory Access noise source - mem_prev_index: usize, - mem: [u8; MEMORY_SIZE], - // Make `next_u32` not waste 32 bits - data_remaining: Option, -} - -// Custom Debug implementation that does not expose the internal state -impl fmt::Debug for JitterRng { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "JitterRng {{}}") - } -} - -/// An error that can occur when `test_timer` fails. -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum TimerError { - /// No timer available. - NoTimer, - /// Timer too coarse to use as an entropy source. - CoarseTimer, - /// Timer is not monotonically increasing. - NotMonotonic, - /// Variations of deltas of time too small. - TinyVariantions, - /// Too many stuck results (indicating no added entropy). - TooManyStuck, - #[doc(hidden)] - __Nonexhaustive, -} - -impl TimerError { - fn description(&self) -> &'static str { - match *self { - TimerError::NoTimer => "no timer available", - TimerError::CoarseTimer => "coarse timer", - TimerError::NotMonotonic => "timer not monotonic", - TimerError::TinyVariantions => "time delta variations too small", - TimerError::TooManyStuck => "too many stuck results", - TimerError::__Nonexhaustive => unreachable!(), - } - } -} - -impl fmt::Display for TimerError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.description()) - } -} - -#[cfg(feature="std")] -impl ::std::error::Error for TimerError { - fn description(&self) -> &str { - self.description() - } -} - -impl From for Error { - fn from(err: TimerError) -> Error { - Error::with_cause(ErrorKind::Unavailable, - "timer jitter failed basic quality tests", err) - } -} - -// Initialise to zero; must be positive -#[cfg(feature="std")] -static JITTER_ROUNDS: AtomicUsize = ATOMIC_USIZE_INIT; - -impl JitterRng { - /// Create a new `JitterRng`. - /// Makes use of `std::time` for a timer. - /// - /// During initialization CPU execution timing jitter is measured a few - /// hundred times. If this does not pass basic quality tests, an error is - /// returned. The test result is cached to make subsequent calls faster. - #[cfg(feature="std")] - pub fn new() -> Result { - let mut ec = JitterRng::new_with_timer(platform::get_nstime); - let mut rounds = JITTER_ROUNDS.load(Ordering::Relaxed) as u32; - if rounds == 0 { - // No result yet: run test. - // This allows the timer test to run multiple times; we don't care. - rounds = ec.test_timer()?; - JITTER_ROUNDS.store(rounds as usize, Ordering::Relaxed); - } - ec.set_rounds(rounds); - Ok(ec) - } - - /// Create a new `JitterRng`. - /// A custom timer can be supplied, making it possible to use `JitterRng` in - /// `no_std` environments. - /// - /// The timer must have nanosecond precision. - /// - /// This method is more low-level than `new()`. It is the responsibility of - /// the caller to run `test_timer` before using any numbers generated with - /// `JitterRng`, and optionally call `set_rounds()`. - pub fn new_with_timer(timer: fn() -> u64) -> JitterRng { - let mut ec = JitterRng { - data: 0, - rounds: 64, - timer: timer, - prev_time: 0, - last_delta: 0, - last_delta2: 0, - mem_prev_index: 0, - mem: [0; MEMORY_SIZE], - data_remaining: None, - }; - - // Fill `data`, `prev_time`, `last_delta` and `last_delta2` with - // non-zero values. - ec.prev_time = timer(); - ec.gen_entropy(); - - // Do a single read from `self.mem` to make sure the Memory Access noise - // source is not optimised out. - // Note: this read is important, it effects optimisations for the entire - // module! - black_box(ec.mem[0]); - - ec - } - - /// Configures how many rounds are used to generate each 64-bit value. - /// This must be greater than zero, and has a big impact on performance - /// and output quality. - /// - /// `new_with_timer` conservatively uses 64 rounds, but often less rounds - /// can be used. The `test_timer()` function returns the minimum number of - /// rounds required for full strength (platform dependent), so one may use - /// `rng.set_rounds(rng.test_timer()?);` or cache the value. - pub fn set_rounds(&mut self, rounds: u32) { - assert!(rounds > 0); - self.rounds = rounds; - } - - // Calculate a random loop count used for the next round of an entropy - // collection, based on bits from a fresh value from the timer. - // - // The timer is folded to produce a number that contains at most `n_bits` - // bits. - // - // Note: A constant should be added to the resulting random loop count to - // prevent loops that run 0 times. - #[inline(never)] - fn random_loop_cnt(&mut self, n_bits: u32) -> u32 { - let mut rounds = 0; - - let mut time = (self.timer)(); - // Mix with the current state of the random number balance the random - // loop counter a bit more. - time ^= self.data; - - // We fold the time value as much as possible to ensure that as many - // bits of the time stamp are included as possible. - let folds = (64 + n_bits - 1) / n_bits; - let mask = (1 << n_bits) - 1; - for _ in 0..folds { - rounds ^= time & mask; - time = time >> n_bits; - } - - rounds as u32 - } - - // CPU jitter noise source - // Noise source based on the CPU execution time jitter - // - // This function injects the individual bits of the time value into the - // entropy pool using an LFSR. - // - // The code is deliberately inefficient with respect to the bit shifting. - // This function not only acts as folding operation, but this function's - // execution is used to measure the CPU execution time jitter. Any change to - // the loop in this function implies that careful retesting must be done. - #[inline(never)] - fn lfsr_time(&mut self, time: u64, var_rounds: bool) { - fn lfsr(mut data: u64, time: u64) -> u64{ - for i in 1..65 { - let mut tmp = time << (64 - i); - tmp = tmp >> (64 - 1); - - // Fibonacci LSFR with polynomial of - // x^64 + x^61 + x^56 + x^31 + x^28 + x^23 + 1 which is - // primitive according to - // http://poincare.matf.bg.ac.rs/~ezivkovm/publications/primpol1.pdf - // (the shift values are the polynomial values minus one - // due to counting bits from 0 to 63). As the current - // position is always the LSB, the polynomial only needs - // to shift data in from the left without wrap. - data ^= tmp; - data ^= (data >> 63) & 1; - data ^= (data >> 60) & 1; - data ^= (data >> 55) & 1; - data ^= (data >> 30) & 1; - data ^= (data >> 27) & 1; - data ^= (data >> 22) & 1; - data = data.rotate_left(1); - } - data - } - - // Note: in the reference implementation only the last round effects - // `self.data`, all the other results are ignored. To make sure the - // other rounds are not optimised out, we first run all but the last - // round on a throw-away value instead of the real `self.data`. - let mut lfsr_loop_cnt = 0; - if var_rounds { lfsr_loop_cnt = self.random_loop_cnt(4) }; - - let mut throw_away: u64 = 0; - for _ in 0..lfsr_loop_cnt { - throw_away = lfsr(throw_away, time); - } - black_box(throw_away); - - self.data = lfsr(self.data, time); - } - - // Memory Access noise source - // This is a noise source based on variations in memory access times - // - // This function performs memory accesses which will add to the timing - // variations due to an unknown amount of CPU wait states that need to be - // added when accessing memory. The memory size should be larger than the L1 - // caches as outlined in the documentation and the associated testing. - // - // The L1 cache has a very high bandwidth, albeit its access rate is usually - // slower than accessing CPU registers. Therefore, L1 accesses only add - // minimal variations as the CPU has hardly to wait. Starting with L2, - // significant variations are added because L2 typically does not belong to - // the CPU any more and therefore a wider range of CPU wait states is - // necessary for accesses. L3 and real memory accesses have even a wider - // range of wait states. However, to reliably access either L3 or memory, - // the `self.mem` memory must be quite large which is usually not desirable. - #[inline(never)] - fn memaccess(&mut self, var_rounds: bool) { - let mut acc_loop_cnt = 128; - if var_rounds { acc_loop_cnt += self.random_loop_cnt(4) }; - - let mut index = self.mem_prev_index; - for _ in 0..acc_loop_cnt { - // Addition of memblocksize - 1 to index with wrap around logic to - // ensure that every memory location is hit evenly. - // The modulus also allows the compiler to remove the indexing - // bounds check. - index = (index + MEMORY_BLOCKSIZE - 1) % MEMORY_SIZE; - - // memory access: just add 1 to one byte - // memory access implies read from and write to memory location - let tmp = self.mem[index]; - self.mem[index] = tmp.wrapping_add(1); - } - self.mem_prev_index = index; - } - - - // Stuck test by checking the: - // - 1st derivation of the jitter measurement (time delta) - // - 2nd derivation of the jitter measurement (delta of time deltas) - // - 3rd derivation of the jitter measurement (delta of delta of time - // deltas) - // - // All values must always be non-zero. - // This test is a heuristic to see whether the last measurement holds - // entropy. - fn stuck(&mut self, current_delta: i64) -> bool { - let delta2 = self.last_delta - current_delta; - let delta3 = delta2 - self.last_delta2; - - self.last_delta = current_delta; - self.last_delta2 = delta2; - - current_delta == 0 || delta2 == 0 || delta3 == 0 - } - - // This is the heart of the entropy generation: calculate time deltas and - // use the CPU jitter in the time deltas. The jitter is injected into the - // entropy pool. - // - // Ensure that `self.prev_time` is primed before using the output of this - // function. This can be done by calling this function and not using its - // result. - fn measure_jitter(&mut self) -> Option<()> { - // Invoke one noise source before time measurement to add variations - self.memaccess(true); - - // Get time stamp and calculate time delta to previous - // invocation to measure the timing variations - let time = (self.timer)(); - // Note: wrapping_sub combined with a cast to `i64` generates a correct - // delta, even in the unlikely case this is a timer that is not strictly - // monotonic. - let current_delta = time.wrapping_sub(self.prev_time) as i64; - self.prev_time = time; - - // Call the next noise source which also injects the data - self.lfsr_time(current_delta as u64, true); - - // Check whether we have a stuck measurement (i.e. does the last - // measurement holds entropy?). - if self.stuck(current_delta) { return None }; - - // Rotate the data buffer by a prime number (any odd number would - // do) to ensure that every bit position of the input time stamp - // has an even chance of being merged with a bit position in the - // entropy pool. We do not use one here as the adjacent bits in - // successive time deltas may have some form of dependency. The - // chosen value of 7 implies that the low 7 bits of the next - // time delta value is concatenated with the current time delta. - self.data = self.data.rotate_left(7); - - Some(()) - } - - // Shuffle the pool a bit by mixing some value with a bijective function - // (XOR) into the pool. - // - // The function generates a mixer value that depends on the bits set and - // the location of the set bits in the random number generated by the - // entropy source. Therefore, based on the generated random number, this - // mixer value can have 2^64 different values. That mixer value is - // initialized with the first two SHA-1 constants. After obtaining the - // mixer value, it is XORed into the random number. - // - // The mixer value is not assumed to contain any entropy. But due to the - // XOR operation, it can also not destroy any entropy present in the - // entropy pool. - #[inline(never)] - fn stir_pool(&mut self) { - // This constant is derived from the first two 32 bit initialization - // vectors of SHA-1 as defined in FIPS 180-4 section 5.3.1 - // The order does not really matter as we do not rely on the specific - // numbers. We just pick the SHA-1 constants as they have a good mix of - // bit set and unset. - const CONSTANT: u64 = 0x67452301efcdab89; - - // The start value of the mixer variable is derived from the third - // and fourth 32 bit initialization vector of SHA-1 as defined in - // FIPS 180-4 section 5.3.1 - let mut mixer = 0x98badcfe10325476; - - // This is a constant time function to prevent leaking timing - // information about the random number. - // The normal code is: - // ``` - // for i in 0..64 { - // if ((self.data >> i) & 1) == 1 { mixer ^= CONSTANT; } - // } - // ``` - // This is a bit fragile, as LLVM really wants to use branches here, and - // we rely on it to not recognise the opportunity. - for i in 0..64 { - let apply = (self.data >> i) & 1; - let mask = !apply.wrapping_sub(1); - mixer ^= CONSTANT & mask; - mixer = mixer.rotate_left(1); - } - - self.data ^= mixer; - } - - fn gen_entropy(&mut self) -> u64 { - // Prime `self.prev_time`, and run the noice sources to make sure the - // first loop round collects the expected entropy. - let _ = self.measure_jitter(); - - for _ in 0..self.rounds { - // If a stuck measurement is received, repeat measurement - // Note: we do not guard against an infinite loop, that would mean - // the timer suddenly became broken. - while self.measure_jitter().is_none() {} - } - - self.stir_pool(); - self.data - } - - /// Basic quality tests on the timer, by measuring CPU timing jitter a few - /// hundred times. - /// - /// If succesful, this will return the estimated number of rounds necessary - /// to collect 64 bits of entropy. Otherwise a `TimerError` with the cause - /// of the failure will be returned. - pub fn test_timer(&mut self) -> Result { - // We could add a check for system capabilities such as `clock_getres` - // or check for `CONFIG_X86_TSC`, but it does not make much sense as the - // following sanity checks verify that we have a high-resolution timer. - - #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] - return Err(TimerError::NoTimer); - - let mut delta_sum = 0; - let mut old_delta = 0; - - let mut time_backwards = 0; - let mut count_mod = 0; - let mut count_stuck = 0; - - // TESTLOOPCOUNT needs some loops to identify edge systems. - // 100 is definitely too little. - const TESTLOOPCOUNT: u64 = 300; - const CLEARCACHE: u64 = 100; - - for i in 0..(CLEARCACHE + TESTLOOPCOUNT) { - // Measure time delta of core entropy collection logic - let time = (self.timer)(); - self.memaccess(true); - self.lfsr_time(time, true); - let time2 = (self.timer)(); - - // Test whether timer works - if time == 0 || time2 == 0 { - return Err(TimerError::NoTimer); - } - let delta = time2.wrapping_sub(time) as i64; - - // Test whether timer is fine grained enough to provide delta even - // when called shortly after each other -- this implies that we also - // have a high resolution timer - if delta == 0 { - return Err(TimerError::CoarseTimer); - } - - // Up to here we did not modify any variable that will be - // evaluated later, but we already performed some work. Thus we - // already have had an impact on the caches, branch prediction, - // etc. with the goal to clear it to get the worst case - // measurements. - if i < CLEARCACHE { continue; } - - if self.stuck(delta) { count_stuck += 1; } - - // Test whether we have an increasing timer. - if !(time2 > time) { time_backwards += 1; } - - // Count the number of times the counter increases in steps of 100ns - // or greater. - if (delta % 100) == 0 { count_mod += 1; } - - // Ensure that we have a varying delta timer which is necessary for - // the calculation of entropy -- perform this check only after the - // first loop is executed as we need to prime the old_delta value - delta_sum += (delta - old_delta).abs() as u64; - old_delta = delta; - } - - // We allow the time to run backwards for up to three times. - // This can happen if the clock is being adjusted by NTP operations. - // If such an operation just happens to interfere with our test, it - // should not fail. The value of 3 should cover the NTP case being - // performed during our test run. - if time_backwards > 3 { - return Err(TimerError::NotMonotonic); - } - - // Test that the available amount of entropy per round does not get to - // low. We expect 1 bit of entropy per round as a reasonable minimum - // (although less is possible, it means the collector loop has to run - // much more often). - // `assert!(delta_average >= log2(1))` - // `assert!(delta_sum / TESTLOOPCOUNT >= 1)` - // `assert!(delta_sum >= TESTLOOPCOUNT)` - if delta_sum < TESTLOOPCOUNT { - return Err(TimerError::TinyVariantions); - } - - // Ensure that we have variations in the time stamp below 100 for at - // least 10% of all checks -- on some platforms, the counter increments - // in multiples of 100, but not always - if count_mod > (TESTLOOPCOUNT * 9 / 10) { - return Err(TimerError::CoarseTimer); - } - - // If we have more than 90% stuck results, then this Jitter RNG is - // likely to not work well. - if count_stuck > (TESTLOOPCOUNT * 9 / 10) { - return Err(TimerError::TooManyStuck); - } - - // Estimate the number of `measure_jitter` rounds necessary for 64 bits - // of entropy. - // - // We don't try very hard to come up with a good estimate of the - // available bits of entropy per round here for two reasons: - // 1. Simple estimates of the available bits (like Shannon entropy) are - // too optimistic. - // 2) Unless we want to waste a lot of time during intialization, there - // only a small number of samples are available. - // - // Therefore we use a very simple and conservative estimate: - // `let bits_of_entropy = log2(delta_average) / 2`. - // - // The number of rounds `measure_jitter` should run to collect 64 bits - // of entropy is `64 / bits_of_entropy`. - // - // To have smaller rounding errors, intermediate values are multiplied - // by `FACTOR`. To compensate for `log2` and division rounding down, - // add 1. - let delta_average = delta_sum / TESTLOOPCOUNT; - // println!("delta_average: {}", delta_average); - - const FACTOR: u32 = 3; - fn log2(x: u64) -> u32 { 64 - x.leading_zeros() } - - // pow(δ, FACTOR) must be representable; if you have overflow reduce FACTOR - Ok(64 * 2 * FACTOR / (log2(delta_average.pow(FACTOR)) + 1)) - } - - /// Statistical test: return the timer delta of one normal run of the - /// `JitterEntropy` entropy collector. - /// - /// Setting `var_rounds` to `true` will execute the memory access and the - /// CPU jitter noice sources a variable amount of times (just like a real - /// `JitterEntropy` round). - /// - /// Setting `var_rounds` to `false` will execute the noice sources the - /// minimal number of times. This can be used to measure the minimum amount - /// of entropy one round of entropy collector can collect in the worst case. - /// - /// # Example - /// - /// Use `timer_stats` to run the [NIST SP 800-90B Entropy Estimation Suite] - /// (https://github.com/usnistgov/SP800-90B_EntropyAssessment). - /// - /// This is the recommended way to test the quality of `JitterRng`. It - /// should be run before using the RNG on untested hardware, after changes - /// that could effect how the code is optimised, and after major compiler - /// compiler changes, like a new LLVM version. - /// - /// First generate two files `jitter_rng_var.bin` and `jitter_rng_var.min`. - /// - /// Execute `python noniid_main.py -v jitter_rng_var.bin 8`, and validate it - /// with `restart.py -v jitter_rng_var.bin 8 `. - /// This number is the expected amount of entropy that is at least available - /// for each round of the entropy collector. This number should be greater - /// than the amount estimated with `64 / test_timer()`. - /// - /// Execute `python noniid_main.py -v -u 4 jitter_rng_var.bin 4`, and - /// validate it with `restart.py -v -u 4 jitter_rng_var.bin 4 `. - /// This number is the expected amount of entropy that is available in the - /// last 4 bits of the timer delta after running noice sources. Note that - /// a value of 3.70 is the minimum estimated entropy for true randomness. - /// - /// Execute `python noniid_main.py -v -u 4 jitter_rng_var.bin 4`, and - /// validate it with `restart.py -v -u 4 jitter_rng_var.bin 4 `. - /// This number is the expected amount of entropy that is available to the - /// entropy collecter if both noice sources only run their minimal number of - /// times. This measures the absolute worst-case, and gives a lower bound - /// for the available entropy. - /// - /// ```rust,no_run - /// use rand::JitterRng; - /// - /// # use std::error::Error; - /// # use std::fs::File; - /// # use std::io::Write; - /// # - /// # fn try_main() -> Result<(), Box> { - /// fn get_nstime() -> u64 { - /// use std::time::{SystemTime, UNIX_EPOCH}; - /// - /// let dur = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); - /// // The correct way to calculate the current time is - /// // `dur.as_secs() * 1_000_000_000 + dur.subsec_nanos() as u64` - /// // But this is faster, and the difference in terms of entropy is - /// // negligible (log2(10^9) == 29.9). - /// dur.as_secs() << 30 | dur.subsec_nanos() as u64 - /// } - /// - /// // Do not initialize with `JitterRng::new`, but with `new_with_timer`. - /// // 'new' always runst `test_timer`, and can therefore fail to - /// // initialize. We want to be able to get the statistics even when the - /// // timer test fails. - /// let mut rng = JitterRng::new_with_timer(get_nstime); - /// - /// // 1_000_000 results are required for the NIST SP 800-90B Entropy - /// // Estimation Suite - /// // FIXME: this number is smaller here, otherwise the Doc-test is too slow - /// const ROUNDS: usize = 10_000; - /// let mut deltas_variable: Vec = Vec::with_capacity(ROUNDS); - /// let mut deltas_minimal: Vec = Vec::with_capacity(ROUNDS); - /// - /// for _ in 0..ROUNDS { - /// deltas_variable.push(rng.timer_stats(true) as u8); - /// deltas_minimal.push(rng.timer_stats(false) as u8); - /// } - /// - /// // Write out after the statistics collection loop, to not disturb the - /// // test results. - /// File::create("jitter_rng_var.bin")?.write(&deltas_variable)?; - /// File::create("jitter_rng_min.bin")?.write(&deltas_minimal)?; - /// # - /// # Ok(()) - /// # } - /// # - /// # fn main() { - /// # try_main().unwrap(); - /// # } - /// ``` - #[cfg(feature="std")] - pub fn timer_stats(&mut self, var_rounds: bool) -> i64 { - let time = platform::get_nstime(); - self.memaccess(var_rounds); - self.lfsr_time(time, var_rounds); - let time2 = platform::get_nstime(); - time2.wrapping_sub(time) as i64 - } -} - -#[cfg(feature="std")] -mod platform { - #[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "windows", all(target_arch = "wasm32", not(target_os = "emscripten")))))] - pub fn get_nstime() -> u64 { - use std::time::{SystemTime, UNIX_EPOCH}; - - let dur = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); - // The correct way to calculate the current time is - // `dur.as_secs() * 1_000_000_000 + dur.subsec_nanos() as u64` - // But this is faster, and the difference in terms of entropy is negligible - // (log2(10^9) == 29.9). - dur.as_secs() << 30 | dur.subsec_nanos() as u64 - } - - #[cfg(any(target_os = "macos", target_os = "ios"))] - pub fn get_nstime() -> u64 { - extern crate libc; - // On Mac OS and iOS std::time::SystemTime only has 1000ns resolution. - // We use `mach_absolute_time` instead. This provides a CPU dependent unit, - // to get real nanoseconds the result should by multiplied by numer/denom - // from `mach_timebase_info`. - // But we are not interested in the exact nanoseconds, just entropy. So we - // use the raw result. - unsafe { libc::mach_absolute_time() } - } - - #[cfg(target_os = "windows")] - pub fn get_nstime() -> u64 { - extern crate winapi; - unsafe { - let mut t = super::mem::zeroed(); - winapi::um::profileapi::QueryPerformanceCounter(&mut t); - *t.QuadPart() as u64 - } - } - - #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] - pub fn get_nstime() -> u64 { - unreachable!() - } -} - -// A function that is opaque to the optimizer to assist in avoiding dead-code -// elimination. Taken from `bencher`. -fn black_box(dummy: T) -> T { - unsafe { - let ret = ptr::read_volatile(&dummy); - mem::forget(dummy); - ret - } -} - -impl Rng for JitterRng { - fn next_u32(&mut self) -> u32 { - // We want to use both parts of the generated entropy - if let Some(high) = self.data_remaining.take() { - high - } else { - let data = self.next_u64(); - self.data_remaining = Some((data >> 32) as u32); - data as u32 - } - } - - fn next_u64(&mut self) -> u64 { - self.gen_entropy() - } - - #[cfg(feature = "i128_support")] - fn next_u128(&mut self) -> u128 { - impls::next_u128_via_u64(self) - } - - fn fill_bytes(&mut self, dest: &mut [u8]) { - impls::fill_bytes_via_u64(self, dest) - } - - fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { - Ok(self.fill_bytes(dest)) - } -} - -impl CryptoRng for JitterRng {} - -// There are no tests included because (1) this is an "external" RNG, so output -// is not reproducible and (2) `test_timer` *will* fail on some platforms. +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +// +// Based on jitterentropy-library, http://www.chronox.de/jent.html. +// Copyright Stephan Mueller , 2014 - 2017. +// +// With permission from Stephan Mueller to relicense the Rust translation under +// the MIT license. + +//! Non-physical true random number generator based on timing jitter. + +use {CryptoRng, Rng, Error, ErrorKind}; +use rand_core::impls; + +use core::{fmt, mem, ptr}; +#[cfg(feature="std")] +use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering}; + +const MEMORY_BLOCKS: usize = 64; +const MEMORY_BLOCKSIZE: usize = 32; +const MEMORY_SIZE: usize = MEMORY_BLOCKS * MEMORY_BLOCKSIZE; + +/// A true random number generator based on jitter in the CPU execution time, +/// and jitter in memory access time. +/// +/// This is a true random number generator, as opposed to pseudo-random +/// generators. Random numbers generated by `JitterRng` can be seen as fresh +/// entropy. A consequence is that is orders of magnitude slower than `OsRng` +/// and PRNGs (about 10^3 .. 10^6 slower). +/// +/// There are very few situations where using this RNG is appropriate. Only very +/// few applications require true entropy. A normal PRNG can be statistically +/// indistinguishable, and a cryptographic PRNG should also be as impossible to +/// predict. +/// +/// Use of `JitterRng` is recommended for initializing cryptographic PRNGs when +/// `OsRng` is not available. +/// +/// This implementation is based on +/// [Jitterentropy](http://www.chronox.de/jent.html) version 2.1.0. +// +// Note: the C implementation relies on being compiled without optimizations. +// This implementation goes through lengths to make the compiler not optimise +// out what is technically dead code, but that does influence timing jitter. +pub struct JitterRng { + data: u64, // Actual random number + // Number of rounds to run the entropy collector per 64 bits + rounds: u32, + // Timer and previous time stamp, used by `measure_jitter` + timer: fn() -> u64, + prev_time: u64, + // Deltas used for the stuck test + last_delta: i64, + last_delta2: i64, + // Memory for the Memory Access noise source + mem_prev_index: usize, + mem: [u8; MEMORY_SIZE], + // Make `next_u32` not waste 32 bits + data_remaining: Option, +} + +// Custom Debug implementation that does not expose the internal state +impl fmt::Debug for JitterRng { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "JitterRng {{}}") + } +} + +/// An error that can occur when `test_timer` fails. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum TimerError { + /// No timer available. + NoTimer, + /// Timer too coarse to use as an entropy source. + CoarseTimer, + /// Timer is not monotonically increasing. + NotMonotonic, + /// Variations of deltas of time too small. + TinyVariantions, + /// Too many stuck results (indicating no added entropy). + TooManyStuck, + #[doc(hidden)] + __Nonexhaustive, +} + +impl TimerError { + fn description(&self) -> &'static str { + match *self { + TimerError::NoTimer => "no timer available", + TimerError::CoarseTimer => "coarse timer", + TimerError::NotMonotonic => "timer not monotonic", + TimerError::TinyVariantions => "time delta variations too small", + TimerError::TooManyStuck => "too many stuck results", + TimerError::__Nonexhaustive => unreachable!(), + } + } +} + +impl fmt::Display for TimerError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.description()) + } +} + +#[cfg(feature="std")] +impl ::std::error::Error for TimerError { + fn description(&self) -> &str { + self.description() + } +} + +impl From for Error { + fn from(err: TimerError) -> Error { + Error::with_cause(ErrorKind::Unavailable, + "timer jitter failed basic quality tests", err) + } +} + +// Initialise to zero; must be positive +#[cfg(feature="std")] +static JITTER_ROUNDS: AtomicUsize = ATOMIC_USIZE_INIT; + +impl JitterRng { + /// Create a new `JitterRng`. + /// Makes use of `std::time` for a timer. + /// + /// During initialization CPU execution timing jitter is measured a few + /// hundred times. If this does not pass basic quality tests, an error is + /// returned. The test result is cached to make subsequent calls faster. + #[cfg(feature="std")] + pub fn new() -> Result { + let mut ec = JitterRng::new_with_timer(platform::get_nstime); + let mut rounds = JITTER_ROUNDS.load(Ordering::Relaxed) as u32; + if rounds == 0 { + // No result yet: run test. + // This allows the timer test to run multiple times; we don't care. + rounds = ec.test_timer()?; + JITTER_ROUNDS.store(rounds as usize, Ordering::Relaxed); + } + ec.set_rounds(rounds); + Ok(ec) + } + + /// Create a new `JitterRng`. + /// A custom timer can be supplied, making it possible to use `JitterRng` in + /// `no_std` environments. + /// + /// The timer must have nanosecond precision. + /// + /// This method is more low-level than `new()`. It is the responsibility of + /// the caller to run `test_timer` before using any numbers generated with + /// `JitterRng`, and optionally call `set_rounds()`. + pub fn new_with_timer(timer: fn() -> u64) -> JitterRng { + let mut ec = JitterRng { + data: 0, + rounds: 64, + timer: timer, + prev_time: 0, + last_delta: 0, + last_delta2: 0, + mem_prev_index: 0, + mem: [0; MEMORY_SIZE], + data_remaining: None, + }; + + // Fill `data`, `prev_time`, `last_delta` and `last_delta2` with + // non-zero values. + ec.prev_time = timer(); + ec.gen_entropy(); + + // Do a single read from `self.mem` to make sure the Memory Access noise + // source is not optimised out. + // Note: this read is important, it effects optimisations for the entire + // module! + black_box(ec.mem[0]); + + ec + } + + /// Configures how many rounds are used to generate each 64-bit value. + /// This must be greater than zero, and has a big impact on performance + /// and output quality. + /// + /// `new_with_timer` conservatively uses 64 rounds, but often less rounds + /// can be used. The `test_timer()` function returns the minimum number of + /// rounds required for full strength (platform dependent), so one may use + /// `rng.set_rounds(rng.test_timer()?);` or cache the value. + pub fn set_rounds(&mut self, rounds: u32) { + assert!(rounds > 0); + self.rounds = rounds; + } + + // Calculate a random loop count used for the next round of an entropy + // collection, based on bits from a fresh value from the timer. + // + // The timer is folded to produce a number that contains at most `n_bits` + // bits. + // + // Note: A constant should be added to the resulting random loop count to + // prevent loops that run 0 times. + #[inline(never)] + fn random_loop_cnt(&mut self, n_bits: u32) -> u32 { + let mut rounds = 0; + + let mut time = (self.timer)(); + // Mix with the current state of the random number balance the random + // loop counter a bit more. + time ^= self.data; + + // We fold the time value as much as possible to ensure that as many + // bits of the time stamp are included as possible. + let folds = (64 + n_bits - 1) / n_bits; + let mask = (1 << n_bits) - 1; + for _ in 0..folds { + rounds ^= time & mask; + time = time >> n_bits; + } + + rounds as u32 + } + + // CPU jitter noise source + // Noise source based on the CPU execution time jitter + // + // This function injects the individual bits of the time value into the + // entropy pool using an LFSR. + // + // The code is deliberately inefficient with respect to the bit shifting. + // This function not only acts as folding operation, but this function's + // execution is used to measure the CPU execution time jitter. Any change to + // the loop in this function implies that careful retesting must be done. + #[inline(never)] + fn lfsr_time(&mut self, time: u64, var_rounds: bool) { + fn lfsr(mut data: u64, time: u64) -> u64{ + for i in 1..65 { + let mut tmp = time << (64 - i); + tmp = tmp >> (64 - 1); + + // Fibonacci LSFR with polynomial of + // x^64 + x^61 + x^56 + x^31 + x^28 + x^23 + 1 which is + // primitive according to + // http://poincare.matf.bg.ac.rs/~ezivkovm/publications/primpol1.pdf + // (the shift values are the polynomial values minus one + // due to counting bits from 0 to 63). As the current + // position is always the LSB, the polynomial only needs + // to shift data in from the left without wrap. + data ^= tmp; + data ^= (data >> 63) & 1; + data ^= (data >> 60) & 1; + data ^= (data >> 55) & 1; + data ^= (data >> 30) & 1; + data ^= (data >> 27) & 1; + data ^= (data >> 22) & 1; + data = data.rotate_left(1); + } + data + } + + // Note: in the reference implementation only the last round effects + // `self.data`, all the other results are ignored. To make sure the + // other rounds are not optimised out, we first run all but the last + // round on a throw-away value instead of the real `self.data`. + let mut lfsr_loop_cnt = 0; + if var_rounds { lfsr_loop_cnt = self.random_loop_cnt(4) }; + + let mut throw_away: u64 = 0; + for _ in 0..lfsr_loop_cnt { + throw_away = lfsr(throw_away, time); + } + black_box(throw_away); + + self.data = lfsr(self.data, time); + } + + // Memory Access noise source + // This is a noise source based on variations in memory access times + // + // This function performs memory accesses which will add to the timing + // variations due to an unknown amount of CPU wait states that need to be + // added when accessing memory. The memory size should be larger than the L1 + // caches as outlined in the documentation and the associated testing. + // + // The L1 cache has a very high bandwidth, albeit its access rate is usually + // slower than accessing CPU registers. Therefore, L1 accesses only add + // minimal variations as the CPU has hardly to wait. Starting with L2, + // significant variations are added because L2 typically does not belong to + // the CPU any more and therefore a wider range of CPU wait states is + // necessary for accesses. L3 and real memory accesses have even a wider + // range of wait states. However, to reliably access either L3 or memory, + // the `self.mem` memory must be quite large which is usually not desirable. + #[inline(never)] + fn memaccess(&mut self, var_rounds: bool) { + let mut acc_loop_cnt = 128; + if var_rounds { acc_loop_cnt += self.random_loop_cnt(4) }; + + let mut index = self.mem_prev_index; + for _ in 0..acc_loop_cnt { + // Addition of memblocksize - 1 to index with wrap around logic to + // ensure that every memory location is hit evenly. + // The modulus also allows the compiler to remove the indexing + // bounds check. + index = (index + MEMORY_BLOCKSIZE - 1) % MEMORY_SIZE; + + // memory access: just add 1 to one byte + // memory access implies read from and write to memory location + let tmp = self.mem[index]; + self.mem[index] = tmp.wrapping_add(1); + } + self.mem_prev_index = index; + } + + + // Stuck test by checking the: + // - 1st derivation of the jitter measurement (time delta) + // - 2nd derivation of the jitter measurement (delta of time deltas) + // - 3rd derivation of the jitter measurement (delta of delta of time + // deltas) + // + // All values must always be non-zero. + // This test is a heuristic to see whether the last measurement holds + // entropy. + fn stuck(&mut self, current_delta: i64) -> bool { + let delta2 = self.last_delta - current_delta; + let delta3 = delta2 - self.last_delta2; + + self.last_delta = current_delta; + self.last_delta2 = delta2; + + current_delta == 0 || delta2 == 0 || delta3 == 0 + } + + // This is the heart of the entropy generation: calculate time deltas and + // use the CPU jitter in the time deltas. The jitter is injected into the + // entropy pool. + // + // Ensure that `self.prev_time` is primed before using the output of this + // function. This can be done by calling this function and not using its + // result. + fn measure_jitter(&mut self) -> Option<()> { + // Invoke one noise source before time measurement to add variations + self.memaccess(true); + + // Get time stamp and calculate time delta to previous + // invocation to measure the timing variations + let time = (self.timer)(); + // Note: wrapping_sub combined with a cast to `i64` generates a correct + // delta, even in the unlikely case this is a timer that is not strictly + // monotonic. + let current_delta = time.wrapping_sub(self.prev_time) as i64; + self.prev_time = time; + + // Call the next noise source which also injects the data + self.lfsr_time(current_delta as u64, true); + + // Check whether we have a stuck measurement (i.e. does the last + // measurement holds entropy?). + if self.stuck(current_delta) { return None }; + + // Rotate the data buffer by a prime number (any odd number would + // do) to ensure that every bit position of the input time stamp + // has an even chance of being merged with a bit position in the + // entropy pool. We do not use one here as the adjacent bits in + // successive time deltas may have some form of dependency. The + // chosen value of 7 implies that the low 7 bits of the next + // time delta value is concatenated with the current time delta. + self.data = self.data.rotate_left(7); + + Some(()) + } + + // Shuffle the pool a bit by mixing some value with a bijective function + // (XOR) into the pool. + // + // The function generates a mixer value that depends on the bits set and + // the location of the set bits in the random number generated by the + // entropy source. Therefore, based on the generated random number, this + // mixer value can have 2^64 different values. That mixer value is + // initialized with the first two SHA-1 constants. After obtaining the + // mixer value, it is XORed into the random number. + // + // The mixer value is not assumed to contain any entropy. But due to the + // XOR operation, it can also not destroy any entropy present in the + // entropy pool. + #[inline(never)] + fn stir_pool(&mut self) { + // This constant is derived from the first two 32 bit initialization + // vectors of SHA-1 as defined in FIPS 180-4 section 5.3.1 + // The order does not really matter as we do not rely on the specific + // numbers. We just pick the SHA-1 constants as they have a good mix of + // bit set and unset. + const CONSTANT: u64 = 0x67452301efcdab89; + + // The start value of the mixer variable is derived from the third + // and fourth 32 bit initialization vector of SHA-1 as defined in + // FIPS 180-4 section 5.3.1 + let mut mixer = 0x98badcfe10325476; + + // This is a constant time function to prevent leaking timing + // information about the random number. + // The normal code is: + // ``` + // for i in 0..64 { + // if ((self.data >> i) & 1) == 1 { mixer ^= CONSTANT; } + // } + // ``` + // This is a bit fragile, as LLVM really wants to use branches here, and + // we rely on it to not recognise the opportunity. + for i in 0..64 { + let apply = (self.data >> i) & 1; + let mask = !apply.wrapping_sub(1); + mixer ^= CONSTANT & mask; + mixer = mixer.rotate_left(1); + } + + self.data ^= mixer; + } + + fn gen_entropy(&mut self) -> u64 { + // Prime `self.prev_time`, and run the noice sources to make sure the + // first loop round collects the expected entropy. + let _ = self.measure_jitter(); + + for _ in 0..self.rounds { + // If a stuck measurement is received, repeat measurement + // Note: we do not guard against an infinite loop, that would mean + // the timer suddenly became broken. + while self.measure_jitter().is_none() {} + } + + self.stir_pool(); + self.data + } + + /// Basic quality tests on the timer, by measuring CPU timing jitter a few + /// hundred times. + /// + /// If succesful, this will return the estimated number of rounds necessary + /// to collect 64 bits of entropy. Otherwise a `TimerError` with the cause + /// of the failure will be returned. + pub fn test_timer(&mut self) -> Result { + // We could add a check for system capabilities such as `clock_getres` + // or check for `CONFIG_X86_TSC`, but it does not make much sense as the + // following sanity checks verify that we have a high-resolution timer. + + #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] + return Err(TimerError::NoTimer); + + let mut delta_sum = 0; + let mut old_delta = 0; + + let mut time_backwards = 0; + let mut count_mod = 0; + let mut count_stuck = 0; + + // TESTLOOPCOUNT needs some loops to identify edge systems. + // 100 is definitely too little. + const TESTLOOPCOUNT: u64 = 300; + const CLEARCACHE: u64 = 100; + + for i in 0..(CLEARCACHE + TESTLOOPCOUNT) { + // Measure time delta of core entropy collection logic + let time = (self.timer)(); + self.memaccess(true); + self.lfsr_time(time, true); + let time2 = (self.timer)(); + + // Test whether timer works + if time == 0 || time2 == 0 { + return Err(TimerError::NoTimer); + } + let delta = time2.wrapping_sub(time) as i64; + + // Test whether timer is fine grained enough to provide delta even + // when called shortly after each other -- this implies that we also + // have a high resolution timer + if delta == 0 { + return Err(TimerError::CoarseTimer); + } + + // Up to here we did not modify any variable that will be + // evaluated later, but we already performed some work. Thus we + // already have had an impact on the caches, branch prediction, + // etc. with the goal to clear it to get the worst case + // measurements. + if i < CLEARCACHE { continue; } + + if self.stuck(delta) { count_stuck += 1; } + + // Test whether we have an increasing timer. + if !(time2 > time) { time_backwards += 1; } + + // Count the number of times the counter increases in steps of 100ns + // or greater. + if (delta % 100) == 0 { count_mod += 1; } + + // Ensure that we have a varying delta timer which is necessary for + // the calculation of entropy -- perform this check only after the + // first loop is executed as we need to prime the old_delta value + delta_sum += (delta - old_delta).abs() as u64; + old_delta = delta; + } + + // We allow the time to run backwards for up to three times. + // This can happen if the clock is being adjusted by NTP operations. + // If such an operation just happens to interfere with our test, it + // should not fail. The value of 3 should cover the NTP case being + // performed during our test run. + if time_backwards > 3 { + return Err(TimerError::NotMonotonic); + } + + // Test that the available amount of entropy per round does not get to + // low. We expect 1 bit of entropy per round as a reasonable minimum + // (although less is possible, it means the collector loop has to run + // much more often). + // `assert!(delta_average >= log2(1))` + // `assert!(delta_sum / TESTLOOPCOUNT >= 1)` + // `assert!(delta_sum >= TESTLOOPCOUNT)` + if delta_sum < TESTLOOPCOUNT { + return Err(TimerError::TinyVariantions); + } + + // Ensure that we have variations in the time stamp below 100 for at + // least 10% of all checks -- on some platforms, the counter increments + // in multiples of 100, but not always + if count_mod > (TESTLOOPCOUNT * 9 / 10) { + return Err(TimerError::CoarseTimer); + } + + // If we have more than 90% stuck results, then this Jitter RNG is + // likely to not work well. + if count_stuck > (TESTLOOPCOUNT * 9 / 10) { + return Err(TimerError::TooManyStuck); + } + + // Estimate the number of `measure_jitter` rounds necessary for 64 bits + // of entropy. + // + // We don't try very hard to come up with a good estimate of the + // available bits of entropy per round here for two reasons: + // 1. Simple estimates of the available bits (like Shannon entropy) are + // too optimistic. + // 2) Unless we want to waste a lot of time during intialization, there + // only a small number of samples are available. + // + // Therefore we use a very simple and conservative estimate: + // `let bits_of_entropy = log2(delta_average) / 2`. + // + // The number of rounds `measure_jitter` should run to collect 64 bits + // of entropy is `64 / bits_of_entropy`. + // + // To have smaller rounding errors, intermediate values are multiplied + // by `FACTOR`. To compensate for `log2` and division rounding down, + // add 1. + let delta_average = delta_sum / TESTLOOPCOUNT; + // println!("delta_average: {}", delta_average); + + const FACTOR: u32 = 3; + fn log2(x: u64) -> u32 { 64 - x.leading_zeros() } + + // pow(δ, FACTOR) must be representable; if you have overflow reduce FACTOR + Ok(64 * 2 * FACTOR / (log2(delta_average.pow(FACTOR)) + 1)) + } + + /// Statistical test: return the timer delta of one normal run of the + /// `JitterEntropy` entropy collector. + /// + /// Setting `var_rounds` to `true` will execute the memory access and the + /// CPU jitter noice sources a variable amount of times (just like a real + /// `JitterEntropy` round). + /// + /// Setting `var_rounds` to `false` will execute the noice sources the + /// minimal number of times. This can be used to measure the minimum amount + /// of entropy one round of entropy collector can collect in the worst case. + /// + /// # Example + /// + /// Use `timer_stats` to run the [NIST SP 800-90B Entropy Estimation Suite] + /// (https://github.com/usnistgov/SP800-90B_EntropyAssessment). + /// + /// This is the recommended way to test the quality of `JitterRng`. It + /// should be run before using the RNG on untested hardware, after changes + /// that could effect how the code is optimised, and after major compiler + /// compiler changes, like a new LLVM version. + /// + /// First generate two files `jitter_rng_var.bin` and `jitter_rng_var.min`. + /// + /// Execute `python noniid_main.py -v jitter_rng_var.bin 8`, and validate it + /// with `restart.py -v jitter_rng_var.bin 8 `. + /// This number is the expected amount of entropy that is at least available + /// for each round of the entropy collector. This number should be greater + /// than the amount estimated with `64 / test_timer()`. + /// + /// Execute `python noniid_main.py -v -u 4 jitter_rng_var.bin 4`, and + /// validate it with `restart.py -v -u 4 jitter_rng_var.bin 4 `. + /// This number is the expected amount of entropy that is available in the + /// last 4 bits of the timer delta after running noice sources. Note that + /// a value of 3.70 is the minimum estimated entropy for true randomness. + /// + /// Execute `python noniid_main.py -v -u 4 jitter_rng_var.bin 4`, and + /// validate it with `restart.py -v -u 4 jitter_rng_var.bin 4 `. + /// This number is the expected amount of entropy that is available to the + /// entropy collecter if both noice sources only run their minimal number of + /// times. This measures the absolute worst-case, and gives a lower bound + /// for the available entropy. + /// + /// ```rust,no_run + /// use rand::JitterRng; + /// + /// # use std::error::Error; + /// # use std::fs::File; + /// # use std::io::Write; + /// # + /// # fn try_main() -> Result<(), Box> { + /// fn get_nstime() -> u64 { + /// use std::time::{SystemTime, UNIX_EPOCH}; + /// + /// let dur = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); + /// // The correct way to calculate the current time is + /// // `dur.as_secs() * 1_000_000_000 + dur.subsec_nanos() as u64` + /// // But this is faster, and the difference in terms of entropy is + /// // negligible (log2(10^9) == 29.9). + /// dur.as_secs() << 30 | dur.subsec_nanos() as u64 + /// } + /// + /// // Do not initialize with `JitterRng::new`, but with `new_with_timer`. + /// // 'new' always runst `test_timer`, and can therefore fail to + /// // initialize. We want to be able to get the statistics even when the + /// // timer test fails. + /// let mut rng = JitterRng::new_with_timer(get_nstime); + /// + /// // 1_000_000 results are required for the NIST SP 800-90B Entropy + /// // Estimation Suite + /// // FIXME: this number is smaller here, otherwise the Doc-test is too slow + /// const ROUNDS: usize = 10_000; + /// let mut deltas_variable: Vec = Vec::with_capacity(ROUNDS); + /// let mut deltas_minimal: Vec = Vec::with_capacity(ROUNDS); + /// + /// for _ in 0..ROUNDS { + /// deltas_variable.push(rng.timer_stats(true) as u8); + /// deltas_minimal.push(rng.timer_stats(false) as u8); + /// } + /// + /// // Write out after the statistics collection loop, to not disturb the + /// // test results. + /// File::create("jitter_rng_var.bin")?.write(&deltas_variable)?; + /// File::create("jitter_rng_min.bin")?.write(&deltas_minimal)?; + /// # + /// # Ok(()) + /// # } + /// # + /// # fn main() { + /// # try_main().unwrap(); + /// # } + /// ``` + #[cfg(feature="std")] + pub fn timer_stats(&mut self, var_rounds: bool) -> i64 { + let time = platform::get_nstime(); + self.memaccess(var_rounds); + self.lfsr_time(time, var_rounds); + let time2 = platform::get_nstime(); + time2.wrapping_sub(time) as i64 + } +} + +#[cfg(feature="std")] +mod platform { + #[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "windows", all(target_arch = "wasm32", not(target_os = "emscripten")))))] + pub fn get_nstime() -> u64 { + use std::time::{SystemTime, UNIX_EPOCH}; + + let dur = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); + // The correct way to calculate the current time is + // `dur.as_secs() * 1_000_000_000 + dur.subsec_nanos() as u64` + // But this is faster, and the difference in terms of entropy is negligible + // (log2(10^9) == 29.9). + dur.as_secs() << 30 | dur.subsec_nanos() as u64 + } + + #[cfg(any(target_os = "macos", target_os = "ios"))] + pub fn get_nstime() -> u64 { + extern crate libc; + // On Mac OS and iOS std::time::SystemTime only has 1000ns resolution. + // We use `mach_absolute_time` instead. This provides a CPU dependent unit, + // to get real nanoseconds the result should by multiplied by numer/denom + // from `mach_timebase_info`. + // But we are not interested in the exact nanoseconds, just entropy. So we + // use the raw result. + unsafe { libc::mach_absolute_time() } + } + + #[cfg(target_os = "windows")] + pub fn get_nstime() -> u64 { + extern crate winapi; + unsafe { + let mut t = super::mem::zeroed(); + winapi::um::profileapi::QueryPerformanceCounter(&mut t); + *t.QuadPart() as u64 + } + } + + #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] + pub fn get_nstime() -> u64 { + unreachable!() + } +} + +// A function that is opaque to the optimizer to assist in avoiding dead-code +// elimination. Taken from `bencher`. +fn black_box(dummy: T) -> T { + unsafe { + let ret = ptr::read_volatile(&dummy); + mem::forget(dummy); + ret + } +} + +impl Rng for JitterRng { + fn next_u32(&mut self) -> u32 { + // We want to use both parts of the generated entropy + if let Some(high) = self.data_remaining.take() { + high + } else { + let data = self.next_u64(); + self.data_remaining = Some((data >> 32) as u32); + data as u32 + } + } + + fn next_u64(&mut self) -> u64 { + self.gen_entropy() + } + + #[cfg(feature = "i128_support")] + fn next_u128(&mut self) -> u128 { + impls::next_u128_via_u64(self) + } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + impls::fill_bytes_via_u64(self, dest) + } + + fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { + Ok(self.fill_bytes(dest)) + } +} + +impl CryptoRng for JitterRng {} + +// There are no tests included because (1) this is an "external" RNG, so output +// is not reproducible and (2) `test_timer` *will* fail on some platforms. diff --git a/src/lib.rs b/src/lib.rs index 4f83e2b7413..037b6a69b33 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,7 +23,7 @@ //! //! ```toml //! [dependencies] -//! rand = "0.3" +//! rand = "0.4" //! ``` //! //! and this to your crate root: diff --git a/src/os.rs b/src/os.rs index 2ceccd13dc3..15a7e736677 100644 --- a/src/os.rs +++ b/src/os.rs @@ -402,7 +402,7 @@ mod imp { Ok(OsRng) } pub fn try_fill(&mut self, v: &mut [u8]) -> Result<(), Error> { - for s in v.chunks_mut(fuchsia_zircon::ZX_CPRNG_DRAW_MAX_LEN) { + for s in v.chunks_mut(fuchsia_zircon::sys::ZX_CPRNG_DRAW_MAX_LEN) { let mut filled = 0; while filled < s.len() { match fuchsia_zircon::cprng_draw(&mut s[filled..]) { diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index 6950d08c17e..f6985a95ef4 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -116,6 +116,12 @@ impl fmt::Debug for IsaacRng { } impl IsaacRng { + /// Create an ISAAC random number generator using the default + /// fixed seed. + pub fn new_unseeded() -> IsaacRng { + Self::new_from_u64(0) + } + /// Creates an ISAAC random number generator using an u64 as seed. /// If `seed == 0` this will produce the same stream of random numbers as /// the reference implementation when used unseeded. @@ -306,14 +312,18 @@ fn init(mut mem: [w32; RAND_SIZE], rounds: u32) -> IsaacRng { } } - IsaacRng { + let mut rng = IsaacRng { rsl: [0; RAND_SIZE], mem: mem, a: w(0), b: w(0), c: w(0), - index: RAND_SIZE as u32, // generate on first use - } + index: 0, + }; + + // Prepare the first set of results + rng.isaac(); + rng } fn mix(a: &mut w32, b: &mut w32, c: &mut w32, d: &mut w32, diff --git a/src/prng/isaac64.rs b/src/prng/isaac64.rs index 85da60e6b4a..528883d6a65 100644 --- a/src/prng/isaac64.rs +++ b/src/prng/isaac64.rs @@ -102,6 +102,12 @@ impl fmt::Debug for Isaac64Rng { } impl Isaac64Rng { + /// Create a 64-bit ISAAC random number generator using the + /// default fixed seed. + pub fn new_unseeded() -> Isaac64Rng { + Self::new_from_u64(0) + } + /// Creates an ISAAC-64 random number generator using an u64 as seed. /// If `seed == 0` this will produce the same stream of random numbers as /// the reference implementation when used unseeded.