// Copyright 2018-2020 Developers of the Rand project. // Copyright 2017 The Rust Project Developers. // // 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 distribution uniformly sampling numbers within a given range. //! //! [`Uniform`] is the standard distribution to sample uniformly from a range; //! e.g. `Uniform::new_inclusive(1, 6)` can sample integers from 1 to 6, like a //! standard die. [`Rng::gen_range`] supports any type supported by //! [`Uniform`]. //! //! This distribution is provided with support for several primitive types //! (all integer and floating-point types) as well as [`std::time::Duration`], //! and supports extension to user-defined types via a type-specific *back-end* //! implementation. //! //! The types [`UniformInt`], [`UniformFloat`] and [`UniformDuration`] are the //! back-ends supporting sampling from primitive integer and floating-point //! ranges as well as from [`std::time::Duration`]; these types do not normally //! need to be used directly (unless implementing a derived back-end). //! //! # Example usage //! //! ``` //! use rand::{Rng, thread_rng}; //! use rand::distributions::Uniform; //! //! let mut rng = thread_rng(); //! let side = Uniform::new(-10.0, 10.0); //! //! // sample between 1 and 10 points //! for _ in 0..rng.gen_range(1..=10) { //! // sample a point from the square with sides -10 - 10 in two dimensions //! let (x, y) = (rng.sample(side), rng.sample(side)); //! println!("Point: {}, {}", x, y); //! } //! ``` //! //! # Extending `Uniform` to support a custom type //! //! To extend [`Uniform`] to support your own types, write a back-end which //! implements the [`UniformSampler`] trait, then implement the [`SampleUniform`] //! helper trait to "register" your back-end. See the `MyF32` example below. //! //! At a minimum, the back-end needs to store any parameters needed for sampling //! (e.g. the target range) and implement `new`, `new_inclusive` and `sample`. //! Those methods should include an assert to check the range is valid (i.e. //! `low < high`). The example below merely wraps another back-end. //! //! The `new`, `new_inclusive` and `sample_single` functions use arguments of //! type SampleBorrow in order to support passing in values by reference or //! by value. In the implementation of these functions, you can choose to //! simply use the reference returned by [`SampleBorrow::borrow`], or you can choose //! to copy or clone the value, whatever is appropriate for your type. //! //! ``` //! use rand::prelude::*; //! use rand::distributions::uniform::{Uniform, SampleUniform, //! UniformSampler, UniformFloat, SampleBorrow}; //! //! struct MyF32(f32); //! //! #[derive(Clone, Copy, Debug)] //! struct UniformMyF32(UniformFloat); //! //! impl UniformSampler for UniformMyF32 { //! type X = MyF32; //! fn new(low: B1, high: B2) -> Self //! where B1: SampleBorrow + Sized, //! B2: SampleBorrow + Sized //! { //! UniformMyF32(UniformFloat::::new(low.borrow().0, high.borrow().0)) //! } //! fn new_inclusive(low: B1, high: B2) -> Self //! where B1: SampleBorrow + Sized, //! B2: SampleBorrow + Sized //! { //! UniformMyF32(UniformFloat::::new_inclusive( //! low.borrow().0, //! high.borrow().0, //! )) //! } //! fn sample(&self, rng: &mut R) -> Self::X { //! MyF32(self.0.sample(rng)) //! } //! } //! //! impl SampleUniform for MyF32 { //! type Sampler = UniformMyF32; //! } //! //! let (low, high) = (MyF32(17.0f32), MyF32(22.0f32)); //! let uniform = Uniform::new(low, high); //! let x = uniform.sample(&mut thread_rng()); //! ``` //! //! [`SampleUniform`]: crate::distributions::uniform::SampleUniform //! [`UniformSampler`]: crate::distributions::uniform::UniformSampler //! [`UniformInt`]: crate::distributions::uniform::UniformInt //! [`UniformFloat`]: crate::distributions::uniform::UniformFloat //! [`UniformDuration`]: crate::distributions::uniform::UniformDuration //! [`SampleBorrow::borrow`]: crate::distributions::uniform::SampleBorrow::borrow use core::time::Duration; use core::ops::{Range, RangeInclusive}; use crate::distributions::float::IntoFloat; use crate::distributions::utils::{BoolAsSIMD, FloatAsSIMD, FloatSIMDUtils, WideningMultiply}; use crate::distributions::Distribution; use crate::{Rng, RngCore}; #[cfg(not(feature = "std"))] #[allow(unused_imports)] // rustc doesn't detect that this is actually used use crate::distributions::utils::Float; #[cfg(feature = "simd_support")] use packed_simd::*; #[cfg(feature = "serde1")] use serde::{Serialize, Deserialize}; /// Sample values uniformly between two bounds. /// /// [`Uniform::new`] and [`Uniform::new_inclusive`] construct a uniform /// distribution sampling from the given range; these functions may do extra /// work up front to make sampling of multiple values faster. If only one sample /// from the range is required, [`Rng::gen_range`] can be more efficient. /// /// When sampling from a constant range, many calculations can happen at /// compile-time and all methods should be fast; for floating-point ranges and /// the full range of integer types this should have comparable performance to /// the `Standard` distribution. /// /// Steps are taken to avoid bias which might be present in naive /// implementations; for example `rng.gen::() % 170` samples from the range /// `[0, 169]` but is twice as likely to select numbers less than 85 than other /// values. Further, the implementations here give more weight to the high-bits /// generated by the RNG than the low bits, since with some RNGs the low-bits /// are of lower quality than the high bits. /// /// Implementations must sample in `[low, high)` range for /// `Uniform::new(low, high)`, i.e., excluding `high`. In particular, care must /// be taken to ensure that rounding never results values `< low` or `>= high`. /// /// # Example /// /// ``` /// use rand::distributions::{Distribution, Uniform}; /// /// let between = Uniform::from(10..10000); /// let mut rng = rand::thread_rng(); /// let mut sum = 0; /// for _ in 0..1000 { /// sum += between.sample(&mut rng); /// } /// println!("{}", sum); /// ``` /// /// For a single sample, [`Rng::gen_range`] may be preferred: /// /// ``` /// use rand::Rng; /// /// let mut rng = rand::thread_rng(); /// println!("{}", rng.gen_range(0..10)); /// ``` /// /// [`new`]: Uniform::new /// [`new_inclusive`]: Uniform::new_inclusive /// [`Rng::gen_range`]: Rng::gen_range #[derive(Clone, Copy, Debug, PartialEq)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde1", serde(bound(serialize = "X::Sampler: Serialize")))] #[cfg_attr(feature = "serde1", serde(bound(deserialize = "X::Sampler: Deserialize<'de>")))] pub struct Uniform(X::Sampler); impl Uniform { /// Create a new `Uniform` instance which samples uniformly from the half /// open range `[low, high)` (excluding `high`). Panics if `low >= high`. pub fn new(low: B1, high: B2) -> Uniform where B1: SampleBorrow + Sized, B2: SampleBorrow + Sized, { Uniform(X::Sampler::new(low, high)) } /// Create a new `Uniform` instance which samples uniformly from the closed /// range `[low, high]` (inclusive). Panics if `low > high`. pub fn new_inclusive(low: B1, high: B2) -> Uniform where B1: SampleBorrow + Sized, B2: SampleBorrow + Sized, { Uniform(X::Sampler::new_inclusive(low, high)) } } impl Distribution for Uniform { fn sample(&self, rng: &mut R) -> X { self.0.sample(rng) } } /// Helper trait for creating objects using the correct implementation of /// [`UniformSampler`] for the sampling type. /// /// See the [module documentation] on how to implement [`Uniform`] range /// sampling for a custom type. /// /// [module documentation]: crate::distributions::uniform pub trait SampleUniform: Sized { /// The `UniformSampler` implementation supporting type `X`. type Sampler: UniformSampler; } /// Helper trait handling actual uniform sampling. /// /// See the [module documentation] on how to implement [`Uniform`] range /// sampling for a custom type. /// /// Implementation of [`sample_single`] is optional, and is only useful when /// the implementation can be faster than `Self::new(low, high).sample(rng)`. /// /// [module documentation]: crate::distributions::uniform /// [`sample_single`]: UniformSampler::sample_single pub trait UniformSampler: Sized { /// The type sampled by this implementation. type X; /// Construct self, with inclusive lower bound and exclusive upper bound /// `[low, high)`. /// /// Usually users should not call this directly but instead use /// `Uniform::new`, which asserts that `low < high` before calling this. fn new(low: B1, high: B2) -> Self where B1: SampleBorrow + Sized, B2: SampleBorrow + Sized; /// Construct self, with inclusive bounds `[low, high]`. /// /// Usually users should not call this directly but instead use /// `Uniform::new_inclusive`, which asserts that `low <= high` before /// calling this. fn new_inclusive(low: B1, high: B2) -> Self where B1: SampleBorrow + Sized, B2: SampleBorrow + Sized; /// Sample a value. fn sample(&self, rng: &mut R) -> Self::X; /// Sample a single value uniformly from a range with inclusive lower bound /// and exclusive upper bound `[low, high)`. /// /// By default this is implemented using /// `UniformSampler::new(low, high).sample(rng)`. However, for some types /// more optimal implementations for single usage may be provided via this /// method (which is the case for integers and floats). /// Results may not be identical. /// /// Note that to use this method in a generic context, the type needs to be /// retrieved via `SampleUniform::Sampler` as follows: /// ``` /// use rand::{thread_rng, distributions::uniform::{SampleUniform, UniformSampler}}; /// # #[allow(unused)] /// fn sample_from_range(lb: T, ub: T) -> T { /// let mut rng = thread_rng(); /// ::Sampler::sample_single(lb, ub, &mut rng) /// } /// ``` fn sample_single(low: B1, high: B2, rng: &mut R) -> Self::X where B1: SampleBorrow + Sized, B2: SampleBorrow + 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> for Uniform { fn from(r: ::core::ops::Range) -> Uniform { Uniform::new(r.start, r.end) } } impl From> for Uniform { fn from(r: ::core::ops::RangeInclusive) -> Uniform { Uniform::new_inclusive(r.start(), r.end()) } } /// Helper trait similar to [`Borrow`] but implemented /// only for SampleUniform and references to SampleUniform in /// order to resolve ambiguity issues. /// /// [`Borrow`]: std::borrow::Borrow pub trait SampleBorrow { /// Immutably borrows from an owned value. See [`Borrow::borrow`] /// /// [`Borrow::borrow`]: std::borrow::Borrow::borrow fn borrow(&self) -> &Borrowed; } impl SampleBorrow for Borrowed where Borrowed: SampleUniform { #[inline(always)] fn borrow(&self) -> &Borrowed { self } } impl<'a, Borrowed> SampleBorrow for &'a Borrowed where Borrowed: SampleUniform { #[inline(always)] fn borrow(&self) -> &Borrowed { *self } } /// Range that supports generating a single sample efficiently. /// /// Any type implementing this trait can be used to specify the sampled range /// for `Rng::gen_range`. pub trait SampleRange { /// Generate a sample from the given range. fn sample_single(self, rng: &mut R) -> T; /// Check whether the range is empty. fn is_empty(&self) -> bool; } impl SampleRange for Range { #[inline] fn sample_single(self, rng: &mut R) -> T { T::Sampler::sample_single(self.start, self.end, rng) } #[inline] fn is_empty(&self) -> bool { !(self.start < self.end) } } impl SampleRange for RangeInclusive { #[inline] fn sample_single(self, rng: &mut R) -> T { T::Sampler::sample_single_inclusive(self.start(), self.end(), rng) } #[inline] fn is_empty(&self) -> bool { !(self.start() <= self.end()) } } //////////////////////////////////////////////////////////////////////////////// // What follows are all back-ends. /// The back-end implementing [`UniformSampler`] for integer types. /// /// Unless you are implementing [`UniformSampler`] for your own type, this type /// should not be used directly, use [`Uniform`] instead. /// /// # Implementation notes /// /// For simplicity, we use the same generic struct `UniformInt` for all /// integer types `X`. This gives us only one field type, `X`; to store unsigned /// values of this size, we take use the fact that these conversions are no-ops. /// /// For a closed range, the number of possible numbers we should generate is /// `range = (high - low + 1)`. To avoid bias, we must ensure that the size of /// our sample space, `zone`, is a multiple of `range`; other values must be /// rejected (by replacing with a new random sample). /// /// As a special case, we use `range = 0` to represent the full range of the /// result type (i.e. for `new_inclusive($ty::MIN, $ty::MAX)`). /// /// The optimum `zone` is the largest product of `range` which fits in our /// (unsigned) target type. We calculate this by calculating how many numbers we /// must reject: `reject = (MAX + 1) % range = (MAX - range + 1) % range`. Any (large) /// product of `range` will suffice, thus in `sample_single` we multiply by a /// power of 2 via bit-shifting (faster but may cause more rejections). /// /// The smallest integer PRNGs generate is `u32`. For 8- and 16-bit outputs we /// use `u32` for our `zone` and samples (because it's not slower and because /// it reduces the chance of having to reject a sample). In this case we cannot /// store `zone` in the target type since it is too large, however we know /// `ints_to_reject < range <= $unsigned::MAX`. /// /// 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. #[derive(Clone, Copy, Debug, PartialEq)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pub struct UniformInt { low: X, range: X, z: X, // either ints_to_reject or zone depending on implementation } macro_rules! uniform_int_impl { ($ty:ty, $unsigned:ident, $u_large:ident) => { impl SampleUniform for $ty { type Sampler = UniformInt<$ty>; } impl UniformSampler for UniformInt<$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. type X = $ty; #[inline] // if the range is constant, this helps LLVM to do the // calculations at compile-time. fn new(low_b: B1, high_b: B2) -> Self where B1: SampleBorrow + Sized, B2: SampleBorrow + Sized, { let low = *low_b.borrow(); let high = *high_b.borrow(); assert!(low < high, "Uniform::new called with `low >= high`"); UniformSampler::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_b: B1, high_b: B2) -> Self where B1: SampleBorrow + Sized, B2: SampleBorrow + Sized, { let low = *low_b.borrow(); let high = *high_b.borrow(); assert!( low <= high, "Uniform::new_inclusive called with `low > high`" ); let unsigned_max = ::core::$u_large::MAX; let range = high.wrapping_sub(low).wrapping_add(1) as $unsigned; let ints_to_reject = if range > 0 { let range = $u_large::from(range); (unsigned_max - range + 1) % range } else { 0 }; UniformInt { low, // These are really $unsigned values, but store as $ty: range: range as $ty, z: ints_to_reject as $unsigned as $ty, } } #[inline] fn sample(&self, rng: &mut R) -> Self::X { let range = self.range as $unsigned as $u_large; if range > 0 { let unsigned_max = ::core::$u_large::MAX; let zone = unsigned_max - (self.z as $unsigned as $u_large); loop { let v: $u_large = rng.gen(); let (hi, lo) = v.wmul(range); if lo <= zone { return self.low.wrapping_add(hi as $ty); } } } else { // Sample from the entire integer range. rng.gen() } } #[inline] fn sample_single(low_b: B1, high_b: B2, rng: &mut R) -> Self::X where B1: SampleBorrow + Sized, B2: SampleBorrow + Sized, { let low = *low_b.borrow(); let high = *high_b.borrow(); assert!(low < high, "UniformSampler::sample_single: low >= high"); Self::sample_single_inclusive(low, high - 1, rng) } #[inline] fn sample_single_inclusive(low_b: B1, high_b: B2, rng: &mut R) -> Self::X where B1: SampleBorrow + Sized, B2: SampleBorrow + Sized, { let low = *low_b.borrow(); let high = *high_b.borrow(); assert!(low <= high, "UniformSampler::sample_single_inclusive: low > high"); let range = high.wrapping_sub(low).wrapping_add(1) as $unsigned as $u_large; // If the above resulted in wrap-around to 0, the range is $ty::MIN..=$ty::MAX, // and any integer will do. if range == 0 { return rng.gen(); } 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. `- 1` is necessary to allow the // same comparison without bias. (range << range.leading_zeros()).wrapping_sub(1) }; loop { let v: $u_large = rng.gen(); let (hi, lo) = v.wmul(range); if lo <= zone { return low.wrapping_add(hi as $ty); } } } } }; } uniform_int_impl! { i8, u8, u32 } uniform_int_impl! { i16, u16, u32 } uniform_int_impl! { i32, u32, u32 } uniform_int_impl! { i64, u64, u64 } uniform_int_impl! { i128, u128, u128 } uniform_int_impl! { isize, usize, usize } uniform_int_impl! { u8, u8, u32 } uniform_int_impl! { u16, u16, u32 } uniform_int_impl! { u32, u32, u32 } uniform_int_impl! { u64, u64, u64 } uniform_int_impl! { usize, usize, usize } uniform_int_impl! { u128, u128, u128 } #[cfg(feature = "simd_support")] macro_rules! uniform_simd_int_impl { ($ty:ident, $unsigned:ident, $u_scalar:ident) => { // The "pick the largest zone that can fit in an `u32`" optimization // is less useful here. Multiple lanes complicate things, we don't // know the PRNG's minimal output size, and casting to a larger vector // is generally a bad idea for SIMD performance. The user can still // implement it manually. // TODO: look into `Uniform::::new(0u32, 100)` functionality // perhaps `impl SampleUniform for $u_scalar`? impl SampleUniform for $ty { type Sampler = UniformInt<$ty>; } impl UniformSampler for UniformInt<$ty> { type X = $ty; #[inline] // if the range is constant, this helps LLVM to do the // calculations at compile-time. fn new(low_b: B1, high_b: B2) -> Self where B1: SampleBorrow + Sized, B2: SampleBorrow + Sized { let low = *low_b.borrow(); let high = *high_b.borrow(); assert!(low.lt(high).all(), "Uniform::new called with `low >= high`"); UniformSampler::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_b: B1, high_b: B2) -> Self where B1: SampleBorrow + Sized, B2: SampleBorrow + Sized { let low = *low_b.borrow(); let high = *high_b.borrow(); assert!(low.le(high).all(), "Uniform::new_inclusive called with `low > high`"); let unsigned_max = ::core::$u_scalar::MAX; // NOTE: these may need to be replaced with explicitly // wrapping operations if `packed_simd` changes let range: $unsigned = ((high - low) + 1).cast(); // `% 0` will panic at runtime. let not_full_range = range.gt($unsigned::splat(0)); // replacing 0 with `unsigned_max` allows a faster `select` // with bitwise OR let modulo = not_full_range.select(range, $unsigned::splat(unsigned_max)); // wrapping addition let ints_to_reject = (unsigned_max - range + 1) % modulo; // When `range` is 0, `lo` of `v.wmul(range)` will always be // zero which means only one sample is needed. let zone = unsigned_max - ints_to_reject; UniformInt { low, // These are really $unsigned values, but store as $ty: range: range.cast(), z: zone.cast(), } } fn sample(&self, rng: &mut R) -> Self::X { let range: $unsigned = self.range.cast(); let zone: $unsigned = self.z.cast(); // This might seem very slow, generating a whole new // SIMD vector for every sample rejection. For most uses // though, the chance of rejection is small and provides good // general performance. With multiple lanes, that chance is // multiplied. To mitigate this, we replace only the lanes of // the vector which fail, iteratively reducing the chance of // rejection. The replacement method does however add a little // overhead. Benchmarking or calculating probabilities might // reveal contexts where this replacement method is slower. let mut v: $unsigned = rng.gen(); loop { let (hi, lo) = v.wmul(range); let mask = lo.le(zone); if mask.all() { let hi: $ty = hi.cast(); // wrapping addition let result = self.low + hi; // `select` here compiles to a blend operation // When `range.eq(0).none()` the compare and blend // operations are avoided. let v: $ty = v.cast(); return range.gt($unsigned::splat(0)).select(result, v); } // Replace only the failing lanes v = mask.select(v, rng.gen()); } } } }; // bulk implementation ($(($unsigned:ident, $signed:ident),)+ $u_scalar:ident) => { $( uniform_simd_int_impl!($unsigned, $unsigned, $u_scalar); uniform_simd_int_impl!($signed, $unsigned, $u_scalar); )+ }; } #[cfg(feature = "simd_support")] uniform_simd_int_impl! { (u64x2, i64x2), (u64x4, i64x4), (u64x8, i64x8), u64 } #[cfg(feature = "simd_support")] uniform_simd_int_impl! { (u32x2, i32x2), (u32x4, i32x4), (u32x8, i32x8), (u32x16, i32x16), u32 } #[cfg(feature = "simd_support")] uniform_simd_int_impl! { (u16x2, i16x2), (u16x4, i16x4), (u16x8, i16x8), (u16x16, i16x16), (u16x32, i16x32), u16 } #[cfg(feature = "simd_support")] uniform_simd_int_impl! { (u8x2, i8x2), (u8x4, i8x4), (u8x8, i8x8), (u8x16, i8x16), (u8x32, i8x32), (u8x64, i8x64), u8 } impl SampleUniform for char { type Sampler = UniformChar; } /// The back-end implementing [`UniformSampler`] for `char`. /// /// Unless you are implementing [`UniformSampler`] for your own type, this type /// should not be used directly, use [`Uniform`] instead. /// /// This differs from integer range sampling since the range `0xD800..=0xDFFF` /// are used for surrogate pairs in UCS and UTF-16, and consequently are not /// valid Unicode code points. We must therefore avoid sampling values in this /// range. #[derive(Clone, Copy, Debug)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pub struct UniformChar { sampler: UniformInt, } /// UTF-16 surrogate range start const CHAR_SURROGATE_START: u32 = 0xD800; /// UTF-16 surrogate range size const CHAR_SURROGATE_LEN: u32 = 0xE000 - CHAR_SURROGATE_START; /// Convert `char` to compressed `u32` fn char_to_comp_u32(c: char) -> u32 { match c as u32 { c if c >= CHAR_SURROGATE_START => c - CHAR_SURROGATE_LEN, c => c, } } impl UniformSampler for UniformChar { type X = char; #[inline] // if the range is constant, this helps LLVM to do the // calculations at compile-time. fn new(low_b: B1, high_b: B2) -> Self where B1: SampleBorrow + Sized, B2: SampleBorrow + Sized, { let low = char_to_comp_u32(*low_b.borrow()); let high = char_to_comp_u32(*high_b.borrow()); let sampler = UniformInt::::new(low, high); UniformChar { sampler } } #[inline] // if the range is constant, this helps LLVM to do the // calculations at compile-time. fn new_inclusive(low_b: B1, high_b: B2) -> Self where B1: SampleBorrow + Sized, B2: SampleBorrow + Sized, { let low = char_to_comp_u32(*low_b.borrow()); let high = char_to_comp_u32(*high_b.borrow()); let sampler = UniformInt::::new_inclusive(low, high); UniformChar { sampler } } fn sample(&self, rng: &mut R) -> Self::X { let mut x = self.sampler.sample(rng); if x >= CHAR_SURROGATE_START { x += CHAR_SURROGATE_LEN; } // SAFETY: x must not be in surrogate range or greater than char::MAX. // This relies on range constructors which accept char arguments. // Validity of input char values is assumed. unsafe { core::char::from_u32_unchecked(x) } } } /// The back-end implementing [`UniformSampler`] for floating-point types. /// /// Unless you are implementing [`UniformSampler`] for your own type, this type /// should not be used directly, use [`Uniform`] instead. /// /// # Implementation notes /// /// Instead of generating a float in the `[0, 1)` range using [`Standard`], the /// `UniformFloat` implementation converts the output of an PRNG itself. This /// way one or two steps can be optimized out. /// /// The floats are first converted to a value in the `[1, 2)` interval using a /// transmute-based method, and then mapped to the expected range with a /// multiply and addition. Values produced this way have what equals 23 bits of /// random digits for an `f32`, and 52 for an `f64`. /// /// [`new`]: UniformSampler::new /// [`new_inclusive`]: UniformSampler::new_inclusive /// [`Standard`]: crate::distributions::Standard #[derive(Clone, Copy, Debug, PartialEq)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pub struct UniformFloat { low: X, scale: X, } macro_rules! uniform_float_impl { ($ty:ty, $uty:ident, $f_scalar:ident, $u_scalar:ident, $bits_to_discard:expr) => { impl SampleUniform for $ty { type Sampler = UniformFloat<$ty>; } impl UniformSampler for UniformFloat<$ty> { type X = $ty; fn new(low_b: B1, high_b: B2) -> Self where B1: SampleBorrow + Sized, B2: SampleBorrow + Sized, { let low = *low_b.borrow(); let high = *high_b.borrow(); debug_assert!( low.all_finite(), "Uniform::new called with `low` non-finite." ); debug_assert!( high.all_finite(), "Uniform::new called with `high` non-finite." ); assert!(low.all_lt(high), "Uniform::new called with `low >= high`"); let max_rand = <$ty>::splat( (::core::$u_scalar::MAX >> $bits_to_discard).into_float_with_exponent(0) - 1.0, ); let mut scale = high - low; assert!(scale.all_finite(), "Uniform::new: range overflow"); loop { let mask = (scale * max_rand + low).ge_mask(high); if mask.none() { break; } scale = scale.decrease_masked(mask); } debug_assert!(<$ty>::splat(0.0).all_le(scale)); UniformFloat { low, scale } } fn new_inclusive(low_b: B1, high_b: B2) -> Self where B1: SampleBorrow + Sized, B2: SampleBorrow + Sized, { let low = *low_b.borrow(); let high = *high_b.borrow(); debug_assert!( low.all_finite(), "Uniform::new_inclusive called with `low` non-finite." ); debug_assert!( high.all_finite(), "Uniform::new_inclusive called with `high` non-finite." ); assert!( low.all_le(high), "Uniform::new_inclusive called with `low > high`" ); let max_rand = <$ty>::splat( (::core::$u_scalar::MAX >> $bits_to_discard).into_float_with_exponent(0) - 1.0, ); let mut scale = (high - low) / max_rand; assert!(scale.all_finite(), "Uniform::new_inclusive: range overflow"); loop { let mask = (scale * max_rand + low).gt_mask(high); if mask.none() { break; } scale = scale.decrease_masked(mask); } debug_assert!(<$ty>::splat(0.0).all_le(scale)); UniformFloat { low, scale } } fn sample(&self, rng: &mut R) -> Self::X { // Generate a value in the range [1, 2) let value1_2 = (rng.gen::<$uty>() >> $bits_to_discard).into_float_with_exponent(0); // Get a value in the range [0, 1) in order to avoid // overflowing into infinity when multiplying with scale let value0_1 = value1_2 - 1.0; // We don't use `f64::mul_add`, because it is not available with // `no_std`. Furthermore, it is slower for some targets (but // faster for others). However, the order of multiplication and // addition is important, because on some platforms (e.g. ARM) // it will be optimized to a single (non-FMA) instruction. value0_1 * self.scale + self.low } #[inline] fn sample_single(low_b: B1, high_b: B2, rng: &mut R) -> Self::X where B1: SampleBorrow + Sized, B2: SampleBorrow + Sized, { let low = *low_b.borrow(); let high = *high_b.borrow(); debug_assert!( low.all_finite(), "UniformSampler::sample_single called with `low` non-finite." ); debug_assert!( high.all_finite(), "UniformSampler::sample_single called with `high` non-finite." ); assert!( low.all_lt(high), "UniformSampler::sample_single: low >= high" ); let mut scale = high - low; assert!(scale.all_finite(), "UniformSampler::sample_single: range overflow"); loop { // Generate a value in the range [1, 2) let value1_2 = (rng.gen::<$uty>() >> $bits_to_discard).into_float_with_exponent(0); // Get a value in the range [0, 1) in order to avoid // overflowing into infinity when multiplying with scale let value0_1 = value1_2 - 1.0; // Doing multiply before addition allows some architectures // to use a single instruction. let res = value0_1 * scale + low; debug_assert!(low.all_le(res) || !scale.all_finite()); if res.all_lt(high) { return res; } // This handles a number of edge cases. // * `low` or `high` is NaN. In this case `scale` and // `res` are going to end up as NaN. // * `low` is negative infinity and `high` is finite. // `scale` is going to be infinite and `res` will be // NaN. // * `high` is positive infinity and `low` is finite. // `scale` is going to be infinite and `res` will // be infinite or NaN (if value0_1 is 0). // * `low` is negative infinity and `high` is positive // infinity. `scale` will be infinite and `res` will // be NaN. // * `low` and `high` are finite, but `high - low` // overflows to infinite. `scale` will be infinite // and `res` will be infinite or NaN (if value0_1 is 0). // So if `high` or `low` are non-finite, we are guaranteed // to fail the `res < high` check above and end up here. // // While we technically should check for non-finite `low` // and `high` before entering the loop, by doing the checks // here instead, we allow the common case to avoid these // checks. But we are still guaranteed that if `low` or // `high` are non-finite we'll end up here and can do the // appropriate checks. // // Likewise `high - low` overflowing to infinity is also // rare, so handle it here after the common case. let mask = !scale.finite_mask(); if mask.any() { assert!( low.all_finite() && high.all_finite(), "Uniform::sample_single: low and high must be finite" ); scale = scale.decrease_masked(mask); } } } } }; } uniform_float_impl! { f32, u32, f32, u32, 32 - 23 } uniform_float_impl! { f64, u64, f64, u64, 64 - 52 } #[cfg(feature = "simd_support")] uniform_float_impl! { f32x2, u32x2, f32, u32, 32 - 23 } #[cfg(feature = "simd_support")] uniform_float_impl! { f32x4, u32x4, f32, u32, 32 - 23 } #[cfg(feature = "simd_support")] uniform_float_impl! { f32x8, u32x8, f32, u32, 32 - 23 } #[cfg(feature = "simd_support")] uniform_float_impl! { f32x16, u32x16, f32, u32, 32 - 23 } #[cfg(feature = "simd_support")] uniform_float_impl! { f64x2, u64x2, f64, u64, 64 - 52 } #[cfg(feature = "simd_support")] uniform_float_impl! { f64x4, u64x4, f64, u64, 64 - 52 } #[cfg(feature = "simd_support")] uniform_float_impl! { f64x8, u64x8, f64, u64, 64 - 52 } /// The back-end implementing [`UniformSampler`] for `Duration`. /// /// Unless you are implementing [`UniformSampler`] for your own types, this type /// should not be used directly, use [`Uniform`] instead. #[derive(Clone, Copy, Debug)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pub struct UniformDuration { mode: UniformDurationMode, offset: u32, } #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] enum UniformDurationMode { Small { secs: u64, nanos: Uniform, }, Medium { nanos: Uniform, }, Large { max_secs: u64, max_nanos: u32, secs: Uniform, }, } impl SampleUniform for Duration { type Sampler = UniformDuration; } impl UniformSampler for UniformDuration { type X = Duration; #[inline] fn new(low_b: B1, high_b: B2) -> Self where B1: SampleBorrow + Sized, B2: SampleBorrow + Sized, { let low = *low_b.borrow(); let high = *high_b.borrow(); assert!(low < high, "Uniform::new called with `low >= high`"); UniformDuration::new_inclusive(low, high - Duration::new(0, 1)) } #[inline] fn new_inclusive(low_b: B1, high_b: B2) -> Self where B1: SampleBorrow + Sized, B2: SampleBorrow + Sized, { let low = *low_b.borrow(); let high = *high_b.borrow(); assert!( low <= high, "Uniform::new_inclusive called with `low > high`" ); let low_s = low.as_secs(); let low_n = low.subsec_nanos(); let mut high_s = high.as_secs(); let mut high_n = high.subsec_nanos(); if high_n < low_n { high_s -= 1; high_n += 1_000_000_000; } let mode = if low_s == high_s { UniformDurationMode::Small { secs: low_s, nanos: Uniform::new_inclusive(low_n, high_n), } } else { let max = high_s .checked_mul(1_000_000_000) .and_then(|n| n.checked_add(u64::from(high_n))); if let Some(higher_bound) = max { let lower_bound = low_s * 1_000_000_000 + u64::from(low_n); UniformDurationMode::Medium { nanos: Uniform::new_inclusive(lower_bound, higher_bound), } } else { // An offset is applied to simplify generation of nanoseconds let max_nanos = high_n - low_n; UniformDurationMode::Large { max_secs: high_s, max_nanos, secs: Uniform::new_inclusive(low_s, high_s), } } }; UniformDuration { mode, offset: low_n, } } #[inline] fn sample(&self, rng: &mut R) -> Duration { match self.mode { UniformDurationMode::Small { secs, nanos } => { let n = nanos.sample(rng); Duration::new(secs, n) } UniformDurationMode::Medium { nanos } => { let nanos = nanos.sample(rng); Duration::new(nanos / 1_000_000_000, (nanos % 1_000_000_000) as u32) } UniformDurationMode::Large { max_secs, max_nanos, secs, } => { // constant folding means this is at least as fast as `Rng::sample(Range)` let nano_range = Uniform::new(0, 1_000_000_000); loop { let s = secs.sample(rng); let n = nano_range.sample(rng); if !(s == max_secs && n > max_nanos) { let sum = n + self.offset; break Duration::new(s, sum); } } } } } } #[cfg(test)] mod tests { use super::*; use crate::rngs::mock::StepRng; #[test] #[cfg(feature = "serde1")] fn test_serialization_uniform_duration() { let distr = UniformDuration::new(Duration::from_secs(10), Duration::from_secs(60)); let de_distr: UniformDuration = bincode::deserialize(&bincode::serialize(&distr).unwrap()).unwrap(); assert_eq!( distr.offset, de_distr.offset ); match (distr.mode, de_distr.mode) { (UniformDurationMode::Small {secs: a_secs, nanos: a_nanos}, UniformDurationMode::Small {secs, nanos}) => { assert_eq!(a_secs, secs); assert_eq!(a_nanos.0.low, nanos.0.low); assert_eq!(a_nanos.0.range, nanos.0.range); assert_eq!(a_nanos.0.z, nanos.0.z); } (UniformDurationMode::Medium {nanos: a_nanos} , UniformDurationMode::Medium {nanos}) => { assert_eq!(a_nanos.0.low, nanos.0.low); assert_eq!(a_nanos.0.range, nanos.0.range); assert_eq!(a_nanos.0.z, nanos.0.z); } (UniformDurationMode::Large {max_secs:a_max_secs, max_nanos:a_max_nanos, secs:a_secs}, UniformDurationMode::Large {max_secs, max_nanos, secs} ) => { assert_eq!(a_max_secs, max_secs); assert_eq!(a_max_nanos, max_nanos); assert_eq!(a_secs.0.low, secs.0.low); assert_eq!(a_secs.0.range, secs.0.range); assert_eq!(a_secs.0.z, secs.0.z); } _ => panic!("`UniformDurationMode` was not serialized/deserialized correctly") } } #[test] #[cfg(feature = "serde1")] fn test_uniform_serialization() { let unit_box: Uniform = Uniform::new(-1, 1); let de_unit_box: Uniform = bincode::deserialize(&bincode::serialize(&unit_box).unwrap()).unwrap(); assert_eq!(unit_box.0.low, de_unit_box.0.low); assert_eq!(unit_box.0.range, de_unit_box.0.range); assert_eq!(unit_box.0.z, de_unit_box.0.z); let unit_box: Uniform = Uniform::new(-1., 1.); let de_unit_box: Uniform = bincode::deserialize(&bincode::serialize(&unit_box).unwrap()).unwrap(); assert_eq!(unit_box.0.low, de_unit_box.0.low); assert_eq!(unit_box.0.scale, de_unit_box.0.scale); } #[should_panic] #[test] fn test_uniform_bad_limits_equal_int() { Uniform::new(10, 10); } #[test] fn test_uniform_good_limits_equal_int() { let mut rng = crate::test::rng(804); let dist = Uniform::new_inclusive(10, 10); for _ in 0..20 { assert_eq!(rng.sample(dist), 10); } } #[should_panic] #[test] fn test_uniform_bad_limits_flipped_int() { Uniform::new(10, 5); } #[test] #[cfg_attr(miri, ignore)] // Miri is too slow fn test_integers() { use core::{i128, u128}; use core::{i16, i32, i64, i8, isize}; use core::{u16, u32, u64, u8, usize}; let mut rng = crate::test::rng(251); macro_rules! t { ($ty:ident, $v:expr, $le:expr, $lt:expr) => {{ for &(low, high) in $v.iter() { let my_uniform = Uniform::new(low, high); for _ in 0..1000 { let v: $ty = rng.sample(my_uniform); assert!($le(low, v) && $lt(v, high)); } let my_uniform = Uniform::new_inclusive(low, high); for _ in 0..1000 { let v: $ty = rng.sample(my_uniform); assert!($le(low, v) && $le(v, high)); } let my_uniform = Uniform::new(&low, high); for _ in 0..1000 { let v: $ty = rng.sample(my_uniform); assert!($le(low, v) && $lt(v, high)); } let my_uniform = Uniform::new_inclusive(&low, &high); for _ in 0..1000 { let v: $ty = rng.sample(my_uniform); assert!($le(low, v) && $le(v, high)); } for _ in 0..1000 { let v = <$ty as SampleUniform>::Sampler::sample_single(low, high, &mut rng); assert!($le(low, v) && $lt(v, high)); } for _ in 0..1000 { let v = <$ty as SampleUniform>::Sampler::sample_single_inclusive(low, high, &mut rng); assert!($le(low, v) && $le(v, high)); } } }}; // scalar bulk ($($ty:ident),*) => {{ $(t!( $ty, [(0, 10), (10, 127), ($ty::MIN, $ty::MAX)], |x, y| x <= y, |x, y| x < y );)* }}; // simd bulk ($($ty:ident),* => $scalar:ident) => {{ $(t!( $ty, [ ($ty::splat(0), $ty::splat(10)), ($ty::splat(10), $ty::splat(127)), ($ty::splat($scalar::MIN), $ty::splat($scalar::MAX)), ], |x: $ty, y| x.le(y).all(), |x: $ty, y| x.lt(y).all() );)* }}; } t!(i8, i16, i32, i64, isize, u8, u16, u32, u64, usize, i128, u128); #[cfg(feature = "simd_support")] { t!(u8x2, u8x4, u8x8, u8x16, u8x32, u8x64 => u8); t!(i8x2, i8x4, i8x8, i8x16, i8x32, i8x64 => i8); t!(u16x2, u16x4, u16x8, u16x16, u16x32 => u16); t!(i16x2, i16x4, i16x8, i16x16, i16x32 => i16); t!(u32x2, u32x4, u32x8, u32x16 => u32); t!(i32x2, i32x4, i32x8, i32x16 => i32); t!(u64x2, u64x4, u64x8 => u64); t!(i64x2, i64x4, i64x8 => i64); } } #[test] #[cfg_attr(miri, ignore)] // Miri is too slow fn test_char() { let mut rng = crate::test::rng(891); let mut max = core::char::from_u32(0).unwrap(); for _ in 0..100 { let c = rng.gen_range('A'..='Z'); assert!(('A'..='Z').contains(&c)); max = max.max(c); } assert_eq!(max, 'Z'); let d = Uniform::new( core::char::from_u32(0xD7F0).unwrap(), core::char::from_u32(0xE010).unwrap(), ); for _ in 0..100 { let c = d.sample(&mut rng); assert!((c as u32) < 0xD800 || (c as u32) > 0xDFFF); } } #[test] #[cfg_attr(miri, ignore)] // Miri is too slow fn test_floats() { let mut rng = crate::test::rng(252); let mut zero_rng = StepRng::new(0, 0); let mut max_rng = StepRng::new(0xffff_ffff_ffff_ffff, 0); macro_rules! t { ($ty:ty, $f_scalar:ident, $bits_shifted:expr) => {{ let v: &[($f_scalar, $f_scalar)] = &[ (0.0, 100.0), (-1e35, -1e25), (1e-35, 1e-25), (-1e35, 1e35), (<$f_scalar>::from_bits(0), <$f_scalar>::from_bits(3)), (-<$f_scalar>::from_bits(10), -<$f_scalar>::from_bits(1)), (-<$f_scalar>::from_bits(5), 0.0), (-<$f_scalar>::from_bits(7), -0.0), (0.1 * ::core::$f_scalar::MAX, ::core::$f_scalar::MAX), (-::core::$f_scalar::MAX * 0.2, ::core::$f_scalar::MAX * 0.7), ]; for &(low_scalar, high_scalar) in v.iter() { for lane in 0..<$ty>::lanes() { let low = <$ty>::splat(0.0 as $f_scalar).replace(lane, low_scalar); let high = <$ty>::splat(1.0 as $f_scalar).replace(lane, high_scalar); let my_uniform = Uniform::new(low, high); let my_incl_uniform = Uniform::new_inclusive(low, high); for _ in 0..100 { let v = rng.sample(my_uniform).extract(lane); assert!(low_scalar <= v && v < high_scalar); let v = rng.sample(my_incl_uniform).extract(lane); assert!(low_scalar <= v && v <= high_scalar); let v = <$ty as SampleUniform>::Sampler ::sample_single(low, high, &mut rng).extract(lane); assert!(low_scalar <= v && v < high_scalar); } assert_eq!( rng.sample(Uniform::new_inclusive(low, low)).extract(lane), low_scalar ); assert_eq!(zero_rng.sample(my_uniform).extract(lane), low_scalar); assert_eq!(zero_rng.sample(my_incl_uniform).extract(lane), low_scalar); assert_eq!(<$ty as SampleUniform>::Sampler ::sample_single(low, high, &mut zero_rng) .extract(lane), low_scalar); assert!(max_rng.sample(my_uniform).extract(lane) < high_scalar); assert!(max_rng.sample(my_incl_uniform).extract(lane) <= high_scalar); // Don't run this test for really tiny differences between high and low // since for those rounding might result in selecting high for a very // long time. if (high_scalar - low_scalar) > 0.0001 { let mut lowering_max_rng = StepRng::new( 0xffff_ffff_ffff_ffff, (-1i64 << $bits_shifted) as u64, ); assert!( <$ty as SampleUniform>::Sampler ::sample_single(low, high, &mut lowering_max_rng) .extract(lane) < high_scalar ); } } } assert_eq!( rng.sample(Uniform::new_inclusive( ::core::$f_scalar::MAX, ::core::$f_scalar::MAX )), ::core::$f_scalar::MAX ); assert_eq!( rng.sample(Uniform::new_inclusive( -::core::$f_scalar::MAX, -::core::$f_scalar::MAX )), -::core::$f_scalar::MAX ); }}; } t!(f32, f32, 32 - 23); t!(f64, f64, 64 - 52); #[cfg(feature = "simd_support")] { t!(f32x2, f32, 32 - 23); t!(f32x4, f32, 32 - 23); t!(f32x8, f32, 32 - 23); t!(f32x16, f32, 32 - 23); t!(f64x2, f64, 64 - 52); t!(f64x4, f64, 64 - 52); t!(f64x8, f64, 64 - 52); } } #[test] #[should_panic] fn test_float_overflow() { let _ = Uniform::from(::core::f64::MIN..::core::f64::MAX); } #[test] #[should_panic] fn test_float_overflow_single() { let mut rng = crate::test::rng(252); rng.gen_range(::core::f64::MIN..::core::f64::MAX); } #[test] #[cfg(all( feature = "std", not(target_arch = "wasm32"), not(target_arch = "asmjs") ))] fn test_float_assertions() { use super::SampleUniform; use std::panic::catch_unwind; fn range(low: T, high: T) { let mut rng = crate::test::rng(253); T::Sampler::sample_single(low, high, &mut rng); } macro_rules! t { ($ty:ident, $f_scalar:ident) => {{ let v: &[($f_scalar, $f_scalar)] = &[ (::std::$f_scalar::NAN, 0.0), (1.0, ::std::$f_scalar::NAN), (::std::$f_scalar::NAN, ::std::$f_scalar::NAN), (1.0, 0.5), (::std::$f_scalar::MAX, -::std::$f_scalar::MAX), (::std::$f_scalar::INFINITY, ::std::$f_scalar::INFINITY), ( ::std::$f_scalar::NEG_INFINITY, ::std::$f_scalar::NEG_INFINITY, ), (::std::$f_scalar::NEG_INFINITY, 5.0), (5.0, ::std::$f_scalar::INFINITY), (::std::$f_scalar::NAN, ::std::$f_scalar::INFINITY), (::std::$f_scalar::NEG_INFINITY, ::std::$f_scalar::NAN), (::std::$f_scalar::NEG_INFINITY, ::std::$f_scalar::INFINITY), ]; for &(low_scalar, high_scalar) in v.iter() { for lane in 0..<$ty>::lanes() { let low = <$ty>::splat(0.0 as $f_scalar).replace(lane, low_scalar); let high = <$ty>::splat(1.0 as $f_scalar).replace(lane, high_scalar); assert!(catch_unwind(|| range(low, high)).is_err()); assert!(catch_unwind(|| Uniform::new(low, high)).is_err()); assert!(catch_unwind(|| Uniform::new_inclusive(low, high)).is_err()); assert!(catch_unwind(|| range(low, low)).is_err()); assert!(catch_unwind(|| Uniform::new(low, low)).is_err()); } } }}; } t!(f32, f32); t!(f64, f64); #[cfg(feature = "simd_support")] { t!(f32x2, f32); t!(f32x4, f32); t!(f32x8, f32); t!(f32x16, f32); t!(f64x2, f64); t!(f64x4, f64); t!(f64x8, f64); } } #[test] #[cfg_attr(miri, ignore)] // Miri is too slow fn test_durations() { let mut rng = crate::test::rng(253); let v = &[ (Duration::new(10, 50000), Duration::new(100, 1234)), (Duration::new(0, 100), Duration::new(1, 50)), ( Duration::new(0, 0), Duration::new(u64::max_value(), 999_999_999), ), ]; for &(low, high) in v.iter() { let my_uniform = Uniform::new(low, high); for _ in 0..1000 { let v = rng.sample(my_uniform); assert!(low <= v && v < high); } } } #[test] fn test_custom_uniform() { use crate::distributions::uniform::{ SampleBorrow, SampleUniform, UniformFloat, UniformSampler, }; #[derive(Clone, Copy, PartialEq, PartialOrd)] struct MyF32 { x: f32, } #[derive(Clone, Copy, Debug)] struct UniformMyF32(UniformFloat); impl UniformSampler for UniformMyF32 { type X = MyF32; fn new(low: B1, high: B2) -> Self where B1: SampleBorrow + Sized, B2: SampleBorrow + Sized, { UniformMyF32(UniformFloat::::new(low.borrow().x, high.borrow().x)) } fn new_inclusive(low: B1, high: B2) -> Self where B1: SampleBorrow + Sized, B2: SampleBorrow + Sized, { UniformSampler::new(low, high) } fn sample(&self, rng: &mut R) -> Self::X { MyF32 { x: self.0.sample(rng), } } } impl SampleUniform for MyF32 { type Sampler = UniformMyF32; } let (low, high) = (MyF32 { x: 17.0f32 }, MyF32 { x: 22.0f32 }); let uniform = Uniform::new(low, high); let mut rng = crate::test::rng(804); for _ in 0..100 { let x: MyF32 = rng.sample(uniform); assert!(low <= x && x < high); } } #[test] fn test_uniform_from_std_range() { let r = Uniform::from(2u32..7); assert_eq!(r.0.low, 2); assert_eq!(r.0.range, 5); let r = Uniform::from(2.0f64..7.0); assert_eq!(r.0.low, 2.0); assert_eq!(r.0.scale, 5.0); } #[test] fn test_uniform_from_std_range_inclusive() { let r = Uniform::from(2u32..=6); assert_eq!(r.0.low, 2); assert_eq!(r.0.range, 5); let r = Uniform::from(2.0f64..=7.0); assert_eq!(r.0.low, 2.0); assert!(r.0.scale > 5.0); assert!(r.0.scale < 5.0 + 1e-14); } #[test] fn value_stability() { fn test_samples( lb: T, ub: T, expected_single: &[T], expected_multiple: &[T], ) where Uniform: Distribution { let mut rng = crate::test::rng(897); let mut buf = [lb; 3]; for x in &mut buf { *x = T::Sampler::sample_single(lb, ub, &mut rng); } assert_eq!(&buf, expected_single); let distr = Uniform::new(lb, ub); for x in &mut buf { *x = rng.sample(&distr); } assert_eq!(&buf, expected_multiple); } // We test on a sub-set of types; possibly we should do more. // TODO: SIMD types test_samples(11u8, 219, &[17, 66, 214], &[181, 93, 165]); test_samples(11u32, 219, &[17, 66, 214], &[181, 93, 165]); test_samples(0f32, 1e-2f32, &[0.0003070104, 0.0026630748, 0.00979833], &[ 0.008194133, 0.00398172, 0.007428536, ]); test_samples( -1e10f64, 1e10f64, &[-4673848682.871551, 6388267422.932352, 4857075081.198343], &[1173375212.1808167, 1917642852.109581, 2365076174.3153973], ); test_samples( Duration::new(2, 0), Duration::new(4, 0), &[ Duration::new(2, 532615131), Duration::new(3, 638826742), Duration::new(3, 485707508), ], &[ Duration::new(3, 117337521), Duration::new(3, 191764285), Duration::new(3, 236507617), ], ); } #[test] fn uniform_distributions_can_be_compared() { assert_eq!(Uniform::new(1.0, 2.0), Uniform::new(1.0, 2.0)); // To cover UniformInt assert_eq!(Uniform::new(1 as u32, 2 as u32), Uniform::new(1 as u32, 2 as u32)); } }