From 9f119ff2739fface42c5f87f5e21545834d6a545 Mon Sep 17 00:00:00 2001 From: Benjamin Saunders Date: Sun, 9 Jun 2019 19:08:01 -0700 Subject: [PATCH 1/3] Add UniformSampler::sample_single_inclusive --- src/distributions/uniform.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/distributions/uniform.rs b/src/distributions/uniform.rs index f07fba95374..48d6f3f64f8 100644 --- a/src/distributions/uniform.rs +++ b/src/distributions/uniform.rs @@ -259,6 +259,23 @@ pub trait UniformSampler: Sized { let uniform: Self = UniformSampler::new(low, high); uniform.sample(rng) } + + /// Sample a single value uniformly from a range with inclusive lower bound + /// and inclusive upper bound `[low, high]`. + /// + /// By default this is implemented using + /// `UniformSampler::new_inclusive(low, high).sample(rng)`. However, for + /// some types more optimal implementations for single usage may be provided + /// via this method. + /// Results may not be identical. + fn sample_single_inclusive(low: B1, high: B2, rng: &mut R) + -> Self::X + where B1: SampleBorrow + Sized, + B2: SampleBorrow + Sized + { + let uniform: Self = UniformSampler::new_inclusive(low, high); + uniform.sample(rng) + } } impl From<::core::ops::Range> for Uniform { From b25b6572ccd69fe4a0575f8397a535d941af231e Mon Sep 17 00:00:00 2001 From: Benjamin Saunders Date: Sun, 9 Jun 2019 19:04:39 -0700 Subject: [PATCH 2/3] Implement Distribution for Range(Inclusive) --- src/distributions/uniform.rs | 31 +++++++++++++++++++++++++++++-- src/lib.rs | 5 +++-- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/distributions/uniform.rs b/src/distributions/uniform.rs index 48d6f3f64f8..400c8442c9f 100644 --- a/src/distributions/uniform.rs +++ b/src/distributions/uniform.rs @@ -111,6 +111,7 @@ use std::time::Duration; #[cfg(not(feature = "std"))] use core::time::Duration; +use core::ops::{Range, RangeInclusive}; use crate::Rng; use crate::distributions::Distribution; @@ -147,6 +148,9 @@ use packed_simd::*; /// `Uniform::new(low, high)`, i.e., excluding `high`. In particular care must /// be taken to ensure that rounding never results values `< low` or `>= high`. /// +/// Range expressions like `0..10` can be used as a `Uniform` distribution, +/// but are less efficient if multiple samples are taken. +/// /// # Example /// /// ``` @@ -278,18 +282,30 @@ pub trait UniformSampler: Sized { } } -impl From<::core::ops::Range> for Uniform { +impl From> for Uniform { fn from(r: ::core::ops::Range) -> Uniform { Uniform::new(r.start, r.end) } } -impl From<::core::ops::RangeInclusive> for Uniform { +impl From> for Uniform { fn from(r: ::core::ops::RangeInclusive) -> Uniform { Uniform::new_inclusive(r.start(), r.end()) } } +impl Distribution for Range { + fn sample(&self, rng: &mut R) -> T { + T::Sampler::sample_single(&self.start, &self.end, rng) + } +} + +impl Distribution for RangeInclusive { + fn sample(&self, rng: &mut R) -> T { + T::Sampler::sample_single_inclusive(self.start(), self.end(), rng) + } +} + /// Helper trait similar to [`Borrow`] but implemented /// only for SampleUniform and references to SampleUniform in /// order to resolve ambiguity issues. @@ -1274,6 +1290,17 @@ mod tests { assert_eq!(r.inner.scale, 5.0); } + #[test] + fn test_std_range_distribution() { + let mut rng = crate::test::rng(474); + for _ in 0..100 { + let x = rng.sample(0..10); + assert!(x >= 0 && x < 10); + let x = rng.sample(0..=10); + assert!(x >= 0 && x <= 10); + } + } + #[test] fn test_uniform_from_std_range_inclusive() { let r = Uniform::from(2u32..=6); diff --git a/src/lib.rs b/src/lib.rs index 31f0169e1ae..b4de455ac4e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -227,9 +227,10 @@ pub trait Rng: RngCore { /// use rand::distributions::Uniform; /// /// let mut rng = thread_rng(); - /// let x = rng.sample(Uniform::new(10u32, 15)); + /// let x = rng.sample(10u32..15); /// // Type annotation requires two types, the type and distribution; the - /// // distribution can be inferred. + /// // distribution can be inferred. `Uniform` is more efficient than the + /// // above range syntax if multiple samples are taken. /// let y = rng.sample::(Uniform::new(10, 15)); /// ``` fn sample>(&mut self, distr: D) -> T { From 64af289356237ece811d3f437a615b5a1df88e35 Mon Sep 17 00:00:00 2001 From: Benjamin Saunders Date: Mon, 1 Jul 2019 11:00:19 -0700 Subject: [PATCH 3/3] Implement Distribution for RangeTo(Inclusive) for unsigned ints --- src/distributions/uniform.rs | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/src/distributions/uniform.rs b/src/distributions/uniform.rs index 400c8442c9f..20526c5ca64 100644 --- a/src/distributions/uniform.rs +++ b/src/distributions/uniform.rs @@ -111,7 +111,7 @@ use std::time::Duration; #[cfg(not(feature = "std"))] use core::time::Duration; -use core::ops::{Range, RangeInclusive}; +use core::ops::{Range, RangeInclusive, RangeTo, RangeToInclusive}; use crate::Rng; use crate::distributions::Distribution; @@ -306,6 +306,28 @@ impl Distribution for RangeInclusive { } } +macro_rules! impl_distrib_range_to { + ($($ty:ty),*) => { + $( + impl Distribution<$ty> for RangeTo<$ty> { + fn sample(&self, rng: &mut R) -> $ty { + <$ty as SampleUniform>::Sampler::sample_single(0, self.end, rng) + } + } + impl Distribution<$ty> for RangeToInclusive<$ty> { + fn sample(&self, rng: &mut R) -> $ty { + <$ty as SampleUniform>::Sampler::sample_single_inclusive(0, self.end, rng) + } + } + )* + } +} + +impl_distrib_range_to!(usize, u8, u16, u32, u64); +#[cfg(not(target_os = "emscripten"))] +impl_distrib_range_to!(u128); + + /// Helper trait similar to [`Borrow`] but implemented /// only for SampleUniform and references to SampleUniform in /// order to resolve ambiguity issues. @@ -1294,10 +1316,14 @@ mod tests { fn test_std_range_distribution() { let mut rng = crate::test::rng(474); for _ in 0..100 { - let x = rng.sample(0..10); - assert!(x >= 0 && x < 10); - let x = rng.sample(0..=10); - assert!(x >= 0 && x <= 10); + let x = rng.sample(-5..5); + assert!(x >= -5 && x < 5); + let x = rng.sample(-5..=5); + assert!(x >= -5 && x <= 5); + let x = rng.sample(..10u8); + assert!(x < 10); + let x = rng.sample(..=10u8); + assert!(x <= 10); } }