diff --git a/CHANGELOG.md b/CHANGELOG.md index 9667aebfa13..1a098453c0e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ You may also find the [Upgrade Guide](https://rust-random.github.io/book/update. - Enable feature `small_rng` by default (#1455) - Allow `UniformFloat::new` samples and `UniformFloat::sample_single` to yield `high` (#1462) - Fix portability of `rand::distributions::Slice` (#1469) +- Rename `rand::distributions` to `rand::distr` (#1470) ## [0.9.0-alpha.1] - 2024-03-18 - Add the `Slice::num_choices` method to the Slice distribution (#1402) diff --git a/README.md b/README.md index 9fa7a2f8528..18f22a89eb8 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ A Rust library for random number generation, featuring: ([see the book](https://rust-random.github.io/book/crates.html)) - Fast implementations of the best-in-class [cryptographic](https://rust-random.github.io/book/guide-rngs.html#cryptographically-secure-pseudo-random-number-generators-csprngs) and [non-cryptographic](https://rust-random.github.io/book/guide-rngs.html#basic-pseudo-random-number-generators-prngs) generators -- A flexible [`distributions`](https://docs.rs/rand/*/rand/distributions/index.html) module +- A flexible [`distributions`](https://docs.rs/rand/*/rand/distr/index.html) module - Samplers for a large number of random number distributions via our own [`rand_distr`](https://docs.rs/rand_distr) and via the [`statrs`](https://docs.rs/statrs/0.13.0/statrs/) diff --git a/benches/Cargo.toml b/benches/Cargo.toml index b3068c2f758..083512d0bc6 100644 --- a/benches/Cargo.toml +++ b/benches/Cargo.toml @@ -15,8 +15,8 @@ criterion = "0.5" criterion-cycles-per-byte = "0.6" [[bench]] -name = "distributions" -path = "src/distributions.rs" +name = "distr" +path = "src/distr.rs" harness = false [[bench]] diff --git a/benches/benches/base_distributions.rs b/benches/benches/base_distributions.rs index c60ce47aeab..17202a30350 100644 --- a/benches/benches/base_distributions.rs +++ b/benches/benches/base_distributions.rs @@ -16,8 +16,8 @@ extern crate test; const RAND_BENCH_N: u64 = 1000; -use rand::distributions::{Alphanumeric, Open01, OpenClosed01, Standard, Uniform}; -use rand::distributions::uniform::{UniformInt, UniformSampler}; +use rand::distr::{Alphanumeric, Open01, OpenClosed01, Standard, Uniform}; +use rand::distr::uniform::{UniformInt, UniformSampler}; use core::mem::size_of; use core::num::{NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8}; use core::time::Duration; @@ -253,7 +253,7 @@ gen_range_float!(gen_range_f32, f32, -20000.0f32, 100000.0); gen_range_float!(gen_range_f64, f64, 123.456f64, 7890.12); -// In src/distributions/uniform.rs, we say: +// In src/distr/uniform.rs, we say: // Implementation of [`uniform_single`] is optional, and is only useful when // the implementation can be faster than `Self::new(low, high).sample(rng)`. diff --git a/benches/benches/misc.rs b/benches/benches/misc.rs index 50dfc0ea8a7..8a3b4767ef2 100644 --- a/benches/benches/misc.rs +++ b/benches/benches/misc.rs @@ -14,7 +14,7 @@ const RAND_BENCH_N: u64 = 1000; use test::Bencher; -use rand::distributions::{Bernoulli, Distribution, Standard}; +use rand::distr::{Bernoulli, Distribution, Standard}; use rand::prelude::*; use rand_pcg::{Pcg32, Pcg64Mcg}; diff --git a/benches/benches/weighted.rs b/benches/benches/weighted.rs index 68722908a9e..da437ab5b0b 100644 --- a/benches/benches/weighted.rs +++ b/benches/benches/weighted.rs @@ -10,7 +10,7 @@ extern crate test; -use rand::distributions::WeightedIndex; +use rand::distr::WeightedIndex; use rand::Rng; use test::Bencher; diff --git a/benches/src/distributions.rs b/benches/src/distr.rs similarity index 100% rename from benches/src/distributions.rs rename to benches/src/distr.rs diff --git a/benches/src/uniform.rs b/benches/src/uniform.rs index 948d1315889..78eb066eac7 100644 --- a/benches/src/uniform.rs +++ b/benches/src/uniform.rs @@ -10,7 +10,7 @@ use core::time::Duration; use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; -use rand::distributions::uniform::{SampleRange, Uniform}; +use rand::distr::uniform::{SampleRange, Uniform}; use rand::prelude::*; use rand_chacha::ChaCha8Rng; use rand_pcg::{Pcg32, Pcg64}; diff --git a/benches/src/uniform_float.rs b/benches/src/uniform_float.rs index 91c0eff4b7b..f33a2b729d3 100644 --- a/benches/src/uniform_float.rs +++ b/benches/src/uniform_float.rs @@ -14,7 +14,7 @@ use core::time::Duration; use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; -use rand::distributions::uniform::{SampleUniform, Uniform, UniformSampler}; +use rand::distr::uniform::{SampleUniform, Uniform, UniformSampler}; use rand::prelude::*; use rand_chacha::ChaCha8Rng; use rand_pcg::{Pcg32, Pcg64}; diff --git a/examples/monte-carlo.rs b/examples/monte-carlo.rs index 21ab109ce05..0f50a4aeb0d 100644 --- a/examples/monte-carlo.rs +++ b/examples/monte-carlo.rs @@ -23,7 +23,7 @@ //! We can use the above fact to estimate the value of π: pick many points in //! the square at random, calculate the fraction that fall within the circle, //! and multiply this fraction by 4. -use rand::distributions::{Distribution, Uniform}; +use rand::distr::{Distribution, Uniform}; fn main() { let range = Uniform::new(-1.0f64, 1.0).unwrap(); diff --git a/examples/monty-hall.rs b/examples/monty-hall.rs index aaff41cad33..a6fcd04e802 100644 --- a/examples/monty-hall.rs +++ b/examples/monty-hall.rs @@ -26,7 +26,7 @@ //! //! [Monty Hall Problem]: https://en.wikipedia.org/wiki/Monty_Hall_problem -use rand::distributions::{Distribution, Uniform}; +use rand::distr::{Distribution, Uniform}; use rand::Rng; struct SimulationResult { diff --git a/examples/rayon-monte-carlo.rs b/examples/rayon-monte-carlo.rs index 839a1593a1b..31d8e681067 100644 --- a/examples/rayon-monte-carlo.rs +++ b/examples/rayon-monte-carlo.rs @@ -38,7 +38,7 @@ //! over BATCH_SIZE trials. Manually batching also turns out to be faster //! for the nondeterministic version of this program as well. -use rand::distributions::{Distribution, Uniform}; +use rand::distr::{Distribution, Uniform}; use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng}; use rayon::prelude::*; diff --git a/rand_distr/README.md b/rand_distr/README.md index 16a44bc85c9..0c8b20b95ef 100644 --- a/rand_distr/README.md +++ b/rand_distr/README.md @@ -8,7 +8,7 @@ Implements a full suite of random number distribution sampling routines. -This crate is a superset of the [rand::distributions] module, including support +This crate is a superset of the [rand::distr] module, including support for sampling from Beta, Binomial, Cauchy, ChiSquared, Dirichlet, Exponential, FisherF, Gamma, Geometric, Hypergeometric, InverseGaussian, LogNormal, Normal, Pareto, PERT, Poisson, StudentT, Triangular and Weibull distributions. Sampling @@ -46,7 +46,7 @@ can be enabled. (Note that any other crate depending on `num-traits` with the [statrs]: https://github.com/boxtown/statrs -[rand::distributions]: https://rust-random.github.io/rand/rand/distributions/index.html +[rand::distr]: https://rust-random.github.io/rand/rand/distr/index.html ## License diff --git a/rand_distr/src/hypergeometric.rs b/rand_distr/src/hypergeometric.rs index 4e4f4306353..683db15314d 100644 --- a/rand_distr/src/hypergeometric.rs +++ b/rand_distr/src/hypergeometric.rs @@ -4,7 +4,7 @@ use crate::Distribution; use core::fmt; #[allow(unused_imports)] use num_traits::Float; -use rand::distributions::uniform::Uniform; +use rand::distr::uniform::Uniform; use rand::Rng; #[derive(Clone, Copy, Debug, PartialEq)] diff --git a/rand_distr/src/lib.rs b/rand_distr/src/lib.rs index f6f3ad54cfa..90a534ff8cb 100644 --- a/rand_distr/src/lib.rs +++ b/rand_distr/src/lib.rs @@ -27,8 +27,8 @@ //! //! ## Re-exports //! -//! This crate is a super-set of the [`rand::distributions`] module. See the -//! [`rand::distributions`] module documentation for an overview of the core +//! This crate is a super-set of the [`rand::distr`] module. See the +//! [`rand::distr`] module documentation for an overview of the core //! [`Distribution`] trait and implementations. //! //! The following are re-exported: @@ -93,7 +93,7 @@ extern crate std; #[allow(unused)] use rand::Rng; -pub use rand::distributions::{ +pub use rand::distr::{ uniform, Alphanumeric, Bernoulli, BernoulliError, DistIter, Distribution, Open01, OpenClosed01, Standard, Uniform, }; @@ -129,7 +129,7 @@ pub use self::weibull::{Error as WeibullError, Weibull}; pub use self::zeta::{Error as ZetaError, Zeta}; pub use self::zipf::{Error as ZipfError, Zipf}; #[cfg(feature = "alloc")] -pub use rand::distributions::{WeightError, WeightedIndex}; +pub use rand::distr::{WeightError, WeightedIndex}; pub use student_t::StudentT; #[cfg(feature = "alloc")] pub use weighted_alias::WeightedAliasIndex; diff --git a/rand_distr/src/utils.rs b/rand_distr/src/utils.rs index fb49ab85762..5879a152670 100644 --- a/rand_distr/src/utils.rs +++ b/rand_distr/src/utils.rs @@ -10,7 +10,7 @@ use crate::ziggurat_tables; use num_traits::Float; -use rand::distributions::hidden_export::IntoFloat; +use rand::distr::hidden_export::IntoFloat; use rand::Rng; /// Calculates ln(gamma(x)) (natural logarithm of the gamma diff --git a/rand_distr/src/weighted_tree.rs b/rand_distr/src/weighted_tree.rs index f3463bcd960..28edab700d4 100644 --- a/rand_distr/src/weighted_tree.rs +++ b/rand_distr/src/weighted_tree.rs @@ -14,8 +14,8 @@ use core::ops::SubAssign; use super::WeightError; use crate::Distribution; use alloc::vec::Vec; -use rand::distributions::uniform::{SampleBorrow, SampleUniform}; -use rand::distributions::Weight; +use rand::distr::uniform::{SampleBorrow, SampleUniform}; +use rand::distr::Weight; use rand::Rng; #[cfg(feature = "serde1")] use serde::{Deserialize, Serialize}; @@ -30,7 +30,7 @@ use serde::{Deserialize, Serialize}; /// /// # Key differences /// -/// The main distinction between [`WeightedTreeIndex`] and [`rand::distributions::WeightedIndex`] +/// The main distinction between [`WeightedTreeIndex`] and [`rand::distr::WeightedIndex`] /// lies in the internal representation of weights. In [`WeightedTreeIndex`], /// weights are structured as a tree, which is optimized for frequent updates of the weights. /// diff --git a/rand_distr/src/zeta.rs b/rand_distr/src/zeta.rs index da146883f0a..922458436f5 100644 --- a/rand_distr/src/zeta.rs +++ b/rand_distr/src/zeta.rs @@ -11,7 +11,7 @@ use crate::{Distribution, Standard}; use core::fmt; use num_traits::Float; -use rand::{distributions::OpenClosed01, Rng}; +use rand::{distr::OpenClosed01, Rng}; /// The [Zeta distribution](https://en.wikipedia.org/wiki/Zeta_distribution) `Zeta(s)`. /// diff --git a/src/distributions/bernoulli.rs b/src/distr/bernoulli.rs similarity index 98% rename from src/distributions/bernoulli.rs rename to src/distr/bernoulli.rs index e49b415fea9..5a56d079a83 100644 --- a/src/distributions/bernoulli.rs +++ b/src/distr/bernoulli.rs @@ -8,7 +8,7 @@ //! The Bernoulli distribution `Bernoulli(p)`. -use crate::distributions::Distribution; +use crate::distr::Distribution; use crate::Rng; use core::fmt; @@ -31,7 +31,7 @@ use serde::{Deserialize, Serialize}; /// # Example /// /// ```rust -/// use rand::distributions::{Bernoulli, Distribution}; +/// use rand::distr::{Bernoulli, Distribution}; /// /// let d = Bernoulli::new(0.3).unwrap(); /// let v = d.sample(&mut rand::thread_rng()); @@ -153,7 +153,7 @@ impl Distribution for Bernoulli { #[cfg(test)] mod test { use super::Bernoulli; - use crate::distributions::Distribution; + use crate::distr::Distribution; use crate::Rng; #[test] diff --git a/src/distributions/distribution.rs b/src/distr/distribution.rs similarity index 96% rename from src/distributions/distribution.rs rename to src/distr/distribution.rs index d545eeea457..8a59e11e13c 100644 --- a/src/distributions/distribution.rs +++ b/src/distr/distribution.rs @@ -49,7 +49,7 @@ pub trait Distribution { /// /// ``` /// use rand::thread_rng; - /// use rand::distributions::{Distribution, Alphanumeric, Uniform, Standard}; + /// use rand::distr::{Distribution, Alphanumeric, Uniform, Standard}; /// /// let mut rng = thread_rng(); /// @@ -89,7 +89,7 @@ pub trait Distribution { /// /// ``` /// use rand::thread_rng; - /// use rand::distributions::{Distribution, Uniform}; + /// use rand::distr::{Distribution, Uniform}; /// /// let mut rng = thread_rng(); /// @@ -201,12 +201,12 @@ pub trait DistString { #[cfg(test)] mod tests { - use crate::distributions::{Distribution, Uniform}; + use crate::distr::{Distribution, Uniform}; use crate::Rng; #[test] fn test_distributions_iter() { - use crate::distributions::Open01; + use crate::distr::Open01; let mut rng = crate::test::rng(210); let distr = Open01; let mut iter = Distribution::::sample_iter(distr, &mut rng); @@ -248,7 +248,7 @@ mod tests { #[test] #[cfg(feature = "alloc")] fn test_dist_string() { - use crate::distributions::{Alphanumeric, DistString, Standard}; + use crate::distr::{Alphanumeric, DistString, Standard}; use core::str; let mut rng = crate::test::rng(213); diff --git a/src/distributions/float.rs b/src/distr/float.rs similarity index 96% rename from src/distributions/float.rs rename to src/distr/float.rs index 427385e50a2..67e6d4b250d 100644 --- a/src/distributions/float.rs +++ b/src/distr/float.rs @@ -8,8 +8,8 @@ //! Basic floating-point number distributions -use crate::distributions::utils::{FloatAsSIMD, FloatSIMDUtils, IntAsSIMD}; -use crate::distributions::{Distribution, Standard}; +use crate::distr::utils::{FloatAsSIMD, FloatSIMDUtils, IntAsSIMD}; +use crate::distr::{Distribution, Standard}; use crate::Rng; use core::mem; #[cfg(feature = "simd_support")] @@ -33,15 +33,15 @@ use serde::{Deserialize, Serialize}; /// # Example /// ``` /// use rand::{thread_rng, Rng}; -/// use rand::distributions::OpenClosed01; +/// use rand::distr::OpenClosed01; /// /// let val: f32 = thread_rng().sample(OpenClosed01); /// println!("f32 from (0, 1): {}", val); /// ``` /// -/// [`Standard`]: crate::distributions::Standard -/// [`Open01`]: crate::distributions::Open01 -/// [`Uniform`]: crate::distributions::uniform::Uniform +/// [`Standard`]: crate::distr::Standard +/// [`Open01`]: crate::distr::Open01 +/// [`Uniform`]: crate::distr::uniform::Uniform #[derive(Clone, Copy, Debug)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pub struct OpenClosed01; @@ -60,15 +60,15 @@ pub struct OpenClosed01; /// # Example /// ``` /// use rand::{thread_rng, Rng}; -/// use rand::distributions::Open01; +/// use rand::distr::Open01; /// /// let val: f32 = thread_rng().sample(Open01); /// println!("f32 from (0, 1): {}", val); /// ``` /// -/// [`Standard`]: crate::distributions::Standard -/// [`OpenClosed01`]: crate::distributions::OpenClosed01 -/// [`Uniform`]: crate::distributions::uniform::Uniform +/// [`Standard`]: crate::distr::Standard +/// [`OpenClosed01`]: crate::distr::OpenClosed01 +/// [`Uniform`]: crate::distr::uniform::Uniform #[derive(Clone, Copy, Debug)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pub struct Open01; diff --git a/src/distributions/integer.rs b/src/distr/integer.rs similarity index 99% rename from src/distributions/integer.rs rename to src/distr/integer.rs index 66258dcbd5c..49546a39417 100644 --- a/src/distributions/integer.rs +++ b/src/distr/integer.rs @@ -8,7 +8,7 @@ //! The implementations of the `Standard` distribution for integer types. -use crate::distributions::{Distribution, Standard}; +use crate::distr::{Distribution, Standard}; use crate::Rng; #[cfg(all(target_arch = "x86", feature = "simd_support"))] use core::arch::x86::__m512i; diff --git a/src/distributions/mod.rs b/src/distr/mod.rs similarity index 99% rename from src/distributions/mod.rs rename to src/distr/mod.rs index b31ebe14f18..8b5c0054271 100644 --- a/src/distributions/mod.rs +++ b/src/distr/mod.rs @@ -172,7 +172,7 @@ use crate::Rng; /// ``` /// # #![allow(dead_code)] /// use rand::Rng; -/// use rand::distributions::{Distribution, Standard}; +/// use rand::distr::{Distribution, Standard}; /// /// struct MyF32 { /// x: f32, @@ -188,7 +188,7 @@ use crate::Rng; /// ## Example usage /// ``` /// use rand::prelude::*; -/// use rand::distributions::Standard; +/// use rand::distr::Standard; /// /// let val: f32 = StdRng::from_os_rng().sample(Standard); /// println!("f32 from [0, 1): {}", val); diff --git a/src/distributions/other.rs b/src/distr/other.rs similarity index 98% rename from src/distributions/other.rs rename to src/distr/other.rs index 5b05854ac24..3ce24e00a01 100644 --- a/src/distributions/other.rs +++ b/src/distr/other.rs @@ -14,8 +14,8 @@ use core::char; use core::num::Wrapping; #[cfg(feature = "alloc")] -use crate::distributions::DistString; -use crate::distributions::{Distribution, Standard, Uniform}; +use crate::distr::DistString; +use crate::distr::{Distribution, Standard, Uniform}; use crate::Rng; use core::mem::{self, MaybeUninit}; @@ -35,7 +35,7 @@ use serde::{Deserialize, Serialize}; /// /// ``` /// use rand::{Rng, thread_rng}; -/// use rand::distributions::Alphanumeric; +/// use rand::distr::Alphanumeric; /// /// let mut rng = thread_rng(); /// let chars: String = (0..7).map(|_| rng.sample(Alphanumeric) as char).collect(); @@ -45,7 +45,7 @@ use serde::{Deserialize, Serialize}; /// The [`DistString`] trait provides an easier method of generating /// a random `String`, and offers more efficient allocation: /// ``` -/// use rand::distributions::{Alphanumeric, DistString}; +/// use rand::distr::{Alphanumeric, DistString}; /// let string = Alphanumeric.sample_string(&mut rand::thread_rng(), 16); /// println!("Random string: {}", string); /// ``` diff --git a/src/distributions/slice.rs b/src/distr/slice.rs similarity index 97% rename from src/distributions/slice.rs rename to src/distr/slice.rs index 8b8f9662595..4677b4e89e4 100644 --- a/src/distributions/slice.rs +++ b/src/distr/slice.rs @@ -8,7 +8,7 @@ use core::num::NonZeroUsize; -use crate::distributions::{Distribution, Uniform}; +use crate::distr::{Distribution, Uniform}; use crate::Rng; #[cfg(feature = "alloc")] use alloc::string::String; @@ -63,7 +63,7 @@ impl UniformUsize { /// /// ``` /// use rand::Rng; -/// use rand::distributions::Slice; +/// use rand::distr::Slice; /// /// let vowels = ['a', 'e', 'i', 'o', 'u']; /// let vowels_dist = Slice::new(&vowels).unwrap(); @@ -146,10 +146,7 @@ pub struct EmptySlice; impl core::fmt::Display for EmptySlice { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!( - f, - "Tried to create a `distributions::Slice` with an empty slice" - ) + write!(f, "Tried to create a `distr::Slice` with an empty slice") } } diff --git a/src/distr/uniform.rs b/src/distr/uniform.rs new file mode 100644 index 00000000000..fa245c3aaf8 --- /dev/null +++ b/src/distr/uniform.rs @@ -0,0 +1,581 @@ +// 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).unwrap()` 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::distr::Uniform; +//! +//! let mut rng = thread_rng(); +//! let side = Uniform::new(-10.0, 10.0).unwrap(); +//! +//! // 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 assertion to check the range is valid (i.e. +//! `low < high`). The example below merely wraps another back-end. +//! +//! The `new`, `new_inclusive`, `sample_single` and `sample_single_inclusive` +//! functions use arguments of +//! type `SampleBorrow` 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::distr::uniform::{Uniform, SampleUniform, +//! UniformSampler, UniformFloat, SampleBorrow, Error}; +//! +//! struct MyF32(f32); +//! +//! #[derive(Clone, Copy, Debug)] +//! struct UniformMyF32(UniformFloat); +//! +//! impl UniformSampler for UniformMyF32 { +//! type X = MyF32; +//! +//! fn new(low: B1, high: B2) -> Result +//! where B1: SampleBorrow + Sized, +//! B2: SampleBorrow + Sized +//! { +//! UniformFloat::::new(low.borrow().0, high.borrow().0).map(UniformMyF32) +//! } +//! fn new_inclusive(low: B1, high: B2) -> Result +//! where B1: SampleBorrow + Sized, +//! B2: SampleBorrow + Sized +//! { +//! UniformFloat::::new_inclusive(low.borrow().0, high.borrow().0).map(UniformMyF32) +//! } +//! 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).unwrap(); +//! let x = uniform.sample(&mut thread_rng()); +//! ``` +//! +//! [`SampleUniform`]: crate::distr::uniform::SampleUniform +//! [`UniformSampler`]: crate::distr::uniform::UniformSampler +//! [`UniformInt`]: crate::distr::uniform::UniformInt +//! [`UniformFloat`]: crate::distr::uniform::UniformFloat +//! [`UniformDuration`]: crate::distr::uniform::UniformDuration +//! [`SampleBorrow::borrow`]: crate::distr::uniform::SampleBorrow::borrow + +#[path = "uniform_float.rs"] +mod float; +#[doc(inline)] +pub use float::UniformFloat; + +#[path = "uniform_int.rs"] +mod int; +#[doc(inline)] +pub use int::UniformInt; + +#[path = "uniform_other.rs"] +mod other; +#[doc(inline)] +pub use other::{UniformChar, UniformDuration}; + +use core::fmt; +use core::ops::{Range, RangeInclusive}; + +use crate::distr::Distribution; +use crate::{Rng, RngCore}; + +/// Error type returned from [`Uniform::new`] and `new_inclusive`. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Error { + /// `low > high`, or equal in case of exclusive range. + EmptyRange, + /// Input or range `high - low` is non-finite. Not relevant to integer types. + NonFinite, +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(match self { + Error::EmptyRange => "low > high (or equal if exclusive) in uniform distribution", + Error::NonFinite => "Non-finite range in uniform distribution", + }) + } +} + +#[cfg(feature = "std")] +impl std::error::Error for Error {} + +#[cfg(feature = "serde1")] +use serde::{Deserialize, Serialize}; + +/// 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::distr::{Distribution, Uniform}; +/// +/// let between = Uniform::try_from(10..10000).unwrap(); +/// 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, Eq)] +#[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`). + /// + /// For discrete types (e.g. integers), samples will always be strictly less + /// than `high`. For (approximations of) continuous types (e.g. `f32`, `f64`), + /// samples may equal `high` due to loss of precision but may not be + /// greater than `high`. + /// + /// Fails if `low >= high`, or if `low`, `high` or the range `high - low` is + /// non-finite. In release mode, only the range is checked. + pub fn new(low: B1, high: B2) -> Result, Error> + where + B1: SampleBorrow + Sized, + B2: SampleBorrow + Sized, + { + X::Sampler::new(low, high).map(Uniform) + } + + /// Create a new `Uniform` instance, which samples uniformly from the closed + /// range `[low, high]` (inclusive). + /// + /// Fails if `low > high`, or if `low`, `high` or the range `high - low` is + /// non-finite. In release mode, only the range is checked. + pub fn new_inclusive(low: B1, high: B2) -> Result, Error> + where + B1: SampleBorrow + Sized, + B2: SampleBorrow + Sized, + { + X::Sampler::new_inclusive(low, high).map(Uniform) + } +} + +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::distr::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::distr::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)`. + /// + /// For discrete types (e.g. integers), samples will always be strictly less + /// than `high`. For (approximations of) continuous types (e.g. `f32`, `f64`), + /// samples may equal `high` due to loss of precision but may not be + /// greater than `high`. + /// + /// Usually users should not call this directly but prefer to use + /// [`Uniform::new`]. + fn new(low: B1, high: B2) -> Result + where + B1: SampleBorrow + Sized, + B2: SampleBorrow + Sized; + + /// Construct self, with inclusive bounds `[low, high]`. + /// + /// Usually users should not call this directly but prefer to use + /// [`Uniform::new_inclusive`]. + fn new_inclusive(low: B1, high: B2) -> Result + 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)`. + /// + /// For discrete types (e.g. integers), samples will always be strictly less + /// than `high`. For (approximations of) continuous types (e.g. `f32`, `f64`), + /// samples may equal `high` due to loss of precision but may not be + /// greater than `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, distr::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).unwrap() + /// } + /// ``` + fn sample_single( + low: B1, + high: B2, + rng: &mut R, + ) -> Result + where + B1: SampleBorrow + Sized, + B2: SampleBorrow + Sized, + { + let uniform: Self = UniformSampler::new(low, high)?; + Ok(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, + ) -> Result + where + B1: SampleBorrow + Sized, + B2: SampleBorrow + Sized, + { + let uniform: Self = UniformSampler::new_inclusive(low, high)?; + Ok(uniform.sample(rng)) + } +} + +impl TryFrom> for Uniform { + type Error = Error; + + fn try_from(r: Range) -> Result, Error> { + Uniform::new(r.start, r.end) + } +} + +impl TryFrom> for Uniform { + type Error = Error; + + fn try_from(r: ::core::ops::RangeInclusive) -> Result, Error> { + 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) -> Result; + + /// Check whether the range is empty. + fn is_empty(&self) -> bool; +} + +impl SampleRange for Range { + #[inline] + fn sample_single(self, rng: &mut R) -> Result { + 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) -> Result { + T::Sampler::sample_single_inclusive(self.start(), self.end(), rng) + } + + #[inline] + fn is_empty(&self) -> bool { + !(self.start() <= self.end()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use core::time::Duration; + + #[test] + #[cfg(feature = "serde1")] + fn test_uniform_serialization() { + let unit_box: Uniform = Uniform::new(-1, 1).unwrap(); + let de_unit_box: Uniform = + bincode::deserialize(&bincode::serialize(&unit_box).unwrap()).unwrap(); + assert_eq!(unit_box.0, de_unit_box.0); + + let unit_box: Uniform = Uniform::new(-1., 1.).unwrap(); + let de_unit_box: Uniform = + bincode::deserialize(&bincode::serialize(&unit_box).unwrap()).unwrap(); + assert_eq!(unit_box.0, de_unit_box.0); + } + + #[test] + fn test_custom_uniform() { + use crate::distr::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) -> Result + where + B1: SampleBorrow + Sized, + B2: SampleBorrow + Sized, + { + UniformFloat::::new(low.borrow().x, high.borrow().x).map(UniformMyF32) + } + + fn new_inclusive(low: B1, high: B2) -> Result + 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).unwrap(); + let mut rng = crate::test::rng(804); + for _ in 0..100 { + let x: MyF32 = rng.sample(uniform); + assert!(low <= x && x < high); + } + } + + #[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).unwrap(); + } + assert_eq!(&buf, expected_single); + + let distr = Uniform::new(lb, ub).unwrap(); + 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).unwrap(), + Uniform::new(1.0, 2.0).unwrap() + ); + + // To cover UniformInt + assert_eq!( + Uniform::new(1_u32, 2_u32).unwrap(), + Uniform::new(1_u32, 2_u32).unwrap() + ); + } +} diff --git a/src/distr/uniform_float.rs b/src/distr/uniform_float.rs new file mode 100644 index 00000000000..b44e192c65d --- /dev/null +++ b/src/distr/uniform_float.rs @@ -0,0 +1,450 @@ +// 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. + +//! `UniformFloat` implementation + +use super::{Error, SampleBorrow, SampleUniform, UniformSampler}; +use crate::distr::float::IntoFloat; +use crate::distr::utils::{BoolAsSIMD, FloatAsSIMD, FloatSIMDUtils, IntAsSIMD}; +use crate::Rng; + +#[cfg(feature = "simd_support")] +use core::simd::prelude::*; +// #[cfg(feature = "simd_support")] +// use core::simd::{LaneCount, SupportedLaneCount}; + +#[cfg(feature = "serde1")] +use serde::{Deserialize, Serialize}; + +/// 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::distr::Standard +/// [`Uniform`]: super::Uniform +#[derive(Clone, Copy, Debug, PartialEq)] +#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] +pub struct UniformFloat { + low: X, + scale: X, +} + +macro_rules! uniform_float_impl { + ($($meta:meta)?, $ty:ty, $uty:ident, $f_scalar:ident, $u_scalar:ident, $bits_to_discard:expr) => { + $(#[cfg($meta)])? + impl UniformFloat<$ty> { + /// Construct, reducing `scale` as required to ensure that rounding + /// can never yield values greater than `high`. + /// + /// Note: though it may be tempting to use a variant of this method + /// to ensure that samples from `[low, high)` are always strictly + /// less than `high`, this approach may be very slow where + /// `scale.abs()` is much smaller than `high.abs()` + /// (example: `low=0.99999999997819644, high=1.`). + fn new_bounded(low: $ty, high: $ty, mut scale: $ty) -> Self { + let max_rand = <$ty>::splat(1.0 as $f_scalar - $f_scalar::EPSILON); + + loop { + let mask = (scale * max_rand + low).gt_mask(high); + if !mask.any() { + break; + } + scale = scale.decrease_masked(mask); + } + + debug_assert!(<$ty>::splat(0.0).all_le(scale)); + + UniformFloat { low, scale } + } + } + + $(#[cfg($meta)])? + impl SampleUniform for $ty { + type Sampler = UniformFloat<$ty>; + } + + $(#[cfg($meta)])? + impl UniformSampler for UniformFloat<$ty> { + type X = $ty; + + fn new(low_b: B1, high_b: B2) -> Result + where + B1: SampleBorrow + Sized, + B2: SampleBorrow + Sized, + { + let low = *low_b.borrow(); + let high = *high_b.borrow(); + #[cfg(debug_assertions)] + if !(low.all_finite()) || !(high.all_finite()) { + return Err(Error::NonFinite); + } + if !(low.all_lt(high)) { + return Err(Error::EmptyRange); + } + + let scale = high - low; + if !(scale.all_finite()) { + return Err(Error::NonFinite); + } + + Ok(Self::new_bounded(low, high, scale)) + } + + fn new_inclusive(low_b: B1, high_b: B2) -> Result + where + B1: SampleBorrow + Sized, + B2: SampleBorrow + Sized, + { + let low = *low_b.borrow(); + let high = *high_b.borrow(); + #[cfg(debug_assertions)] + if !(low.all_finite()) || !(high.all_finite()) { + return Err(Error::NonFinite); + } + if !low.all_le(high) { + return Err(Error::EmptyRange); + } + + let max_rand = <$ty>::splat(1.0 as $f_scalar - $f_scalar::EPSILON); + let scale = (high - low) / max_rand; + if !scale.all_finite() { + return Err(Error::NonFinite); + } + + Ok(Self::new_bounded(low, high, scale)) + } + + fn sample(&self, rng: &mut R) -> Self::X { + // Generate a value in the range [1, 2) + let value1_2 = (rng.random::<$uty>() >> $uty::splat($bits_to_discard)).into_float_with_exponent(0); + + // Get a value in the range [0, 1) to avoid overflow when multiplying by scale + let value0_1 = value1_2 - <$ty>::splat(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) -> Result + where + B1: SampleBorrow + Sized, + B2: SampleBorrow + Sized, + { + Self::sample_single_inclusive(low_b, high_b, rng) + } + + #[inline] + fn sample_single_inclusive(low_b: B1, high_b: B2, rng: &mut R) -> Result + where + B1: SampleBorrow + Sized, + B2: SampleBorrow + Sized, + { + let low = *low_b.borrow(); + let high = *high_b.borrow(); + #[cfg(debug_assertions)] + if !low.all_finite() || !high.all_finite() { + return Err(Error::NonFinite); + } + if !low.all_le(high) { + return Err(Error::EmptyRange); + } + let scale = high - low; + if !scale.all_finite() { + return Err(Error::NonFinite); + } + + // Generate a value in the range [1, 2) + let value1_2 = + (rng.random::<$uty>() >> $uty::splat($bits_to_discard)).into_float_with_exponent(0); + + // Get a value in the range [0, 1) to avoid overflow when multiplying by scale + let value0_1 = value1_2 - <$ty>::splat(1.0); + + // Doing multiply before addition allows some architectures + // to use a single instruction. + Ok(value0_1 * scale + low) + } + } + }; +} + +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! { feature = "simd_support", f32x2, u32x2, f32, u32, 32 - 23 } +#[cfg(feature = "simd_support")] +uniform_float_impl! { feature = "simd_support", f32x4, u32x4, f32, u32, 32 - 23 } +#[cfg(feature = "simd_support")] +uniform_float_impl! { feature = "simd_support", f32x8, u32x8, f32, u32, 32 - 23 } +#[cfg(feature = "simd_support")] +uniform_float_impl! { feature = "simd_support", f32x16, u32x16, f32, u32, 32 - 23 } + +#[cfg(feature = "simd_support")] +uniform_float_impl! { feature = "simd_support", f64x2, u64x2, f64, u64, 64 - 52 } +#[cfg(feature = "simd_support")] +uniform_float_impl! { feature = "simd_support", f64x4, u64x4, f64, u64, 64 - 52 } +#[cfg(feature = "simd_support")] +uniform_float_impl! { feature = "simd_support", f64x8, u64x8, f64, u64, 64 - 52 } + +#[cfg(test)] +mod tests { + use super::*; + use crate::distr::{utils::FloatSIMDScalarUtils, Uniform}; + use crate::rngs::mock::StepRng; + + #[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 * $f_scalar::MAX, $f_scalar::MAX), + (-$f_scalar::MAX * 0.2, $f_scalar::MAX * 0.7), + ]; + for &(low_scalar, high_scalar) in v.iter() { + for lane in 0..<$ty>::LEN { + 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).unwrap(); + let my_incl_uniform = Uniform::new_inclusive(low, high).unwrap(); + 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) + .unwrap() + .extract(lane); + assert!(low_scalar <= v && v <= high_scalar); + let v = <$ty as SampleUniform>::Sampler::sample_single_inclusive( + low, high, &mut rng, + ) + .unwrap() + .extract(lane); + assert!(low_scalar <= v && v <= high_scalar); + } + + assert_eq!( + rng.sample(Uniform::new_inclusive(low, low).unwrap()) + .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 + ) + .unwrap() + .extract(lane), + low_scalar + ); + assert_eq!( + <$ty as SampleUniform>::Sampler::sample_single_inclusive( + low, + high, + &mut zero_rng + ) + .unwrap() + .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); + // sample_single cannot cope with max_rng: + // assert!(<$ty as SampleUniform>::Sampler + // ::sample_single(low, high, &mut max_rng).unwrap() + // .extract(lane) <= high_scalar); + assert!( + <$ty as SampleUniform>::Sampler::sample_single_inclusive( + low, + high, + &mut max_rng + ) + .unwrap() + .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 + ) + .unwrap() + .extract(lane) + <= high_scalar + ); + } + } + } + + assert_eq!( + rng.sample(Uniform::new_inclusive($f_scalar::MAX, $f_scalar::MAX).unwrap()), + $f_scalar::MAX + ); + assert_eq!( + rng.sample(Uniform::new_inclusive(-$f_scalar::MAX, -$f_scalar::MAX).unwrap()), + -$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] + fn test_float_overflow() { + assert_eq!(Uniform::try_from(f64::MIN..f64::MAX), Err(Error::NonFinite)); + } + + #[test] + #[should_panic] + fn test_float_overflow_single() { + let mut rng = crate::test::rng(252); + rng.gen_range(f64::MIN..f64::MAX); + } + + #[test] + #[cfg(all(feature = "std", panic = "unwind"))] + fn test_float_assertions() { + use super::SampleUniform; + fn range(low: T, high: T) -> Result { + 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)] = &[ + ($f_scalar::NAN, 0.0), + (1.0, $f_scalar::NAN), + ($f_scalar::NAN, $f_scalar::NAN), + (1.0, 0.5), + ($f_scalar::MAX, -$f_scalar::MAX), + ($f_scalar::INFINITY, $f_scalar::INFINITY), + ($f_scalar::NEG_INFINITY, $f_scalar::NEG_INFINITY), + ($f_scalar::NEG_INFINITY, 5.0), + (5.0, $f_scalar::INFINITY), + ($f_scalar::NAN, $f_scalar::INFINITY), + ($f_scalar::NEG_INFINITY, $f_scalar::NAN), + ($f_scalar::NEG_INFINITY, $f_scalar::INFINITY), + ]; + for &(low_scalar, high_scalar) in v.iter() { + for lane in 0..<$ty>::LEN { + 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!(range(low, high).is_err()); + assert!(Uniform::new(low, high).is_err()); + assert!(Uniform::new_inclusive(low, high).is_err()); + assert!(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] + fn test_uniform_from_std_range() { + let r = Uniform::try_from(2.0f64..7.0).unwrap(); + assert_eq!(r.0.low, 2.0); + assert_eq!(r.0.scale, 5.0); + } + + #[test] + fn test_uniform_from_std_range_bad_limits() { + #![allow(clippy::reversed_empty_ranges)] + assert!(Uniform::try_from(100.0..10.0).is_err()); + assert!(Uniform::try_from(100.0..100.0).is_err()); + } + + #[test] + fn test_uniform_from_std_range_inclusive() { + let r = Uniform::try_from(2.0f64..=7.0).unwrap(); + assert_eq!(r.0.low, 2.0); + assert!(r.0.scale > 5.0); + assert!(r.0.scale < 5.0 + 1e-14); + } + + #[test] + fn test_uniform_from_std_range_inclusive_bad_limits() { + #![allow(clippy::reversed_empty_ranges)] + assert!(Uniform::try_from(100.0..=10.0).is_err()); + assert!(Uniform::try_from(100.0..=99.0).is_err()); + } +} diff --git a/src/distr/uniform_int.rs b/src/distr/uniform_int.rs new file mode 100644 index 00000000000..5a6254058e0 --- /dev/null +++ b/src/distr/uniform_int.rs @@ -0,0 +1,521 @@ +// 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. + +//! `UniformInt` implementation + +use super::{Error, SampleBorrow, SampleUniform, UniformSampler}; +use crate::distr::utils::WideningMultiply; +#[cfg(feature = "simd_support")] +use crate::distr::{Distribution, Standard}; +use crate::Rng; + +#[cfg(feature = "simd_support")] +use core::simd::prelude::*; +#[cfg(feature = "simd_support")] +use core::simd::{LaneCount, SupportedLaneCount}; + +#[cfg(feature = "serde1")] +use serde::{Deserialize, Serialize}; + +/// 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 <= $uty::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. +/// +/// [`Uniform`]: super::Uniform +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] +pub struct UniformInt { + pub(super) low: X, + pub(super) range: X, + thresh: X, // effectively 2.pow(max(64, uty_bits)) % range +} + +macro_rules! uniform_int_impl { + ($ty:ty, $uty:ty, $sample_ty: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 $uty 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) -> Result + where + B1: SampleBorrow + Sized, + B2: SampleBorrow + Sized, + { + let low = *low_b.borrow(); + let high = *high_b.borrow(); + if !(low < high) { + return Err(Error::EmptyRange); + } + 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) -> Result + where + B1: SampleBorrow + Sized, + B2: SampleBorrow + Sized, + { + let low = *low_b.borrow(); + let high = *high_b.borrow(); + if !(low <= high) { + return Err(Error::EmptyRange); + } + + let range = high.wrapping_sub(low).wrapping_add(1) as $uty; + let thresh = if range > 0 { + let range = $sample_ty::from(range); + (range.wrapping_neg() % range) + } else { + 0 + }; + + Ok(UniformInt { + low, + range: range as $ty, // type: $uty + thresh: thresh as $uty as $ty, // type: $sample_ty + }) + } + + /// Sample from distribution, Lemire's method, unbiased + #[inline] + fn sample(&self, rng: &mut R) -> Self::X { + let range = self.range as $uty as $sample_ty; + if range == 0 { + return rng.random(); + } + + let thresh = self.thresh as $uty as $sample_ty; + let hi = loop { + let (hi, lo) = rng.random::<$sample_ty>().wmul(range); + if lo >= thresh { + break hi; + } + }; + self.low.wrapping_add(hi as $ty) + } + + #[inline] + fn sample_single( + low_b: B1, + high_b: B2, + rng: &mut R, + ) -> Result + where + B1: SampleBorrow + Sized, + B2: SampleBorrow + Sized, + { + let low = *low_b.borrow(); + let high = *high_b.borrow(); + if !(low < high) { + return Err(Error::EmptyRange); + } + Self::sample_single_inclusive(low, high - 1, rng) + } + + /// Sample single value, Canon's method, biased + /// + /// In the worst case, bias affects 1 in `2^n` samples where n is + /// 56 (`i8`), 48 (`i16`), 96 (`i32`), 64 (`i64`), 128 (`i128`). + #[cfg(not(feature = "unbiased"))] + #[inline] + fn sample_single_inclusive( + low_b: B1, + high_b: B2, + rng: &mut R, + ) -> Result + where + B1: SampleBorrow + Sized, + B2: SampleBorrow + Sized, + { + let low = *low_b.borrow(); + let high = *high_b.borrow(); + if !(low <= high) { + return Err(Error::EmptyRange); + } + let range = high.wrapping_sub(low).wrapping_add(1) as $uty as $sample_ty; + if range == 0 { + // Range is MAX+1 (unrepresentable), so we need a special case + return Ok(rng.random()); + } + + // generate a sample using a sensible integer type + let (mut result, lo_order) = rng.random::<$sample_ty>().wmul(range); + + // if the sample is biased... + if lo_order > range.wrapping_neg() { + // ...generate a new sample to reduce bias... + let (new_hi_order, _) = (rng.random::<$sample_ty>()).wmul(range as $sample_ty); + // ... incrementing result on overflow + let is_overflow = lo_order.checked_add(new_hi_order as $sample_ty).is_none(); + result += is_overflow as $sample_ty; + } + + Ok(low.wrapping_add(result as $ty)) + } + + /// Sample single value, Canon's method, unbiased + #[cfg(feature = "unbiased")] + #[inline] + fn sample_single_inclusive( + low_b: B1, + high_b: B2, + rng: &mut R, + ) -> Result + where + B1: SampleBorrow<$ty> + Sized, + B2: SampleBorrow<$ty> + Sized, + { + let low = *low_b.borrow(); + let high = *high_b.borrow(); + if !(low <= high) { + return Err(Error::EmptyRange); + } + let range = high.wrapping_sub(low).wrapping_add(1) as $uty as $sample_ty; + if range == 0 { + // Range is MAX+1 (unrepresentable), so we need a special case + return Ok(rng.random()); + } + + let (mut result, mut lo) = rng.random::<$sample_ty>().wmul(range); + + // In contrast to the biased sampler, we use a loop: + while lo > range.wrapping_neg() { + let (new_hi, new_lo) = (rng.random::<$sample_ty>()).wmul(range); + match lo.checked_add(new_hi) { + Some(x) if x < $sample_ty::MAX => { + // Anything less than MAX: last term is 0 + break; + } + None => { + // Overflow: last term is 1 + result += 1; + break; + } + _ => { + // Unlikely case: must check next sample + lo = new_lo; + continue; + } + } + } + + Ok(low.wrapping_add(result 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) => { + // 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. + + #[cfg(feature = "simd_support")] + impl SampleUniform for Simd<$ty, LANES> + where + LaneCount: SupportedLaneCount, + Simd<$unsigned, LANES>: + WideningMultiply, Simd<$unsigned, LANES>)>, + Standard: Distribution>, + { + type Sampler = UniformInt>; + } + + #[cfg(feature = "simd_support")] + impl UniformSampler for UniformInt> + where + LaneCount: SupportedLaneCount, + Simd<$unsigned, LANES>: + WideningMultiply, Simd<$unsigned, LANES>)>, + Standard: Distribution>, + { + type X = Simd<$ty, LANES>; + + #[inline] // if the range is constant, this helps LLVM to do the + // calculations at compile-time. + fn new(low_b: B1, high_b: B2) -> Result + where B1: SampleBorrow + Sized, + B2: SampleBorrow + Sized + { + let low = *low_b.borrow(); + let high = *high_b.borrow(); + if !(low.simd_lt(high).all()) { + return Err(Error::EmptyRange); + } + UniformSampler::new_inclusive(low, high - Simd::splat(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) -> Result + where B1: SampleBorrow + Sized, + B2: SampleBorrow + Sized + { + let low = *low_b.borrow(); + let high = *high_b.borrow(); + if !(low.simd_le(high).all()) { + return Err(Error::EmptyRange); + } + + // NOTE: all `Simd` operations are inherently wrapping, + // see https://doc.rust-lang.org/std/simd/struct.Simd.html + let range: Simd<$unsigned, LANES> = ((high - low) + Simd::splat(1)).cast(); + + // We must avoid divide-by-zero by using 0 % 1 == 0. + let not_full_range = range.simd_gt(Simd::splat(0)); + let modulo = not_full_range.select(range, Simd::splat(1)); + let ints_to_reject = range.wrapping_neg() % modulo; + + Ok(UniformInt { + low, + // These are really $unsigned values, but store as $ty: + range: range.cast(), + thresh: ints_to_reject.cast(), + }) + } + + fn sample(&self, rng: &mut R) -> Self::X { + let range: Simd<$unsigned, LANES> = self.range.cast(); + let thresh: Simd<$unsigned, LANES> = self.thresh.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: Simd<$unsigned, LANES> = rng.random(); + loop { + let (hi, lo) = v.wmul(range); + let mask = lo.simd_ge(thresh); + if mask.all() { + let hi: Simd<$ty, LANES> = 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: Simd<$ty, LANES> = v.cast(); + return range.simd_gt(Simd::splat(0)).select(result, v); + } + // Replace only the failing lanes + v = mask.select(v, rng.random()); + } + } + } + }; + + // bulk implementation + ($(($unsigned:ident, $signed:ident)),+) => { + $( + uniform_simd_int_impl!($unsigned, $unsigned); + uniform_simd_int_impl!($signed, $unsigned); + )+ + }; +} + +#[cfg(feature = "simd_support")] +uniform_simd_int_impl! { (u8, i8), (u16, i16), (u32, i32), (u64, i64) } + +#[cfg(test)] +mod tests { + use super::*; + use crate::distr::Uniform; + + #[test] + fn test_uniform_bad_limits_equal_int() { + assert_eq!(Uniform::new(10, 10), Err(Error::EmptyRange)); + } + + #[test] + fn test_uniform_good_limits_equal_int() { + let mut rng = crate::test::rng(804); + let dist = Uniform::new_inclusive(10, 10).unwrap(); + for _ in 0..20 { + assert_eq!(rng.sample(dist), 10); + } + } + + #[test] + fn test_uniform_bad_limits_flipped_int() { + assert_eq!(Uniform::new(10, 5), Err(Error::EmptyRange)); + } + + #[test] + #[cfg_attr(miri, ignore)] // Miri is too slow + fn test_integers() { + 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).unwrap(); + 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).unwrap(); + 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).unwrap(); + 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).unwrap(); + 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).unwrap(); + assert!($le(low, v) && $lt(v, high)); + } + + for _ in 0..1000 { + let v = <$ty as SampleUniform>::Sampler::sample_single_inclusive(low, high, &mut rng).unwrap(); + 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.simd_le(y).all(), + |x: $ty, y| x.simd_lt(y).all() + );)* + }}; + } + t!(i8, i16, i32, i64, isize, u8, u16, u32, u64, usize, i128, u128); + + #[cfg(feature = "simd_support")] + { + t!(u8x4, u8x8, u8x16, u8x32, u8x64 => u8); + t!(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] + fn test_uniform_from_std_range() { + let r = Uniform::try_from(2u32..7).unwrap(); + assert_eq!(r.0.low, 2); + assert_eq!(r.0.range, 5); + } + + #[test] + fn test_uniform_from_std_range_bad_limits() { + #![allow(clippy::reversed_empty_ranges)] + assert!(Uniform::try_from(100..10).is_err()); + assert!(Uniform::try_from(100..100).is_err()); + } + + #[test] + fn test_uniform_from_std_range_inclusive() { + let r = Uniform::try_from(2u32..=6).unwrap(); + assert_eq!(r.0.low, 2); + assert_eq!(r.0.range, 5); + } + + #[test] + fn test_uniform_from_std_range_inclusive_bad_limits() { + #![allow(clippy::reversed_empty_ranges)] + assert!(Uniform::try_from(100..=10).is_err()); + assert!(Uniform::try_from(100..=99).is_err()); + } +} diff --git a/src/distr/uniform_other.rs b/src/distr/uniform_other.rs new file mode 100644 index 00000000000..af10eed50d1 --- /dev/null +++ b/src/distr/uniform_other.rs @@ -0,0 +1,322 @@ +// 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. + +//! `UniformChar`, `UniformDuration` implementations + +use super::{Error, SampleBorrow, SampleUniform, Uniform, UniformInt, UniformSampler}; +use crate::distr::Distribution; +use crate::Rng; +use core::time::Duration; + +#[cfg(feature = "serde1")] +use serde::{Deserialize, Serialize}; + +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, PartialEq, Eq)] +#[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) -> Result + 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); + sampler.map(|sampler| 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) -> Result + 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); + sampler.map(|sampler| 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) } + } +} + +/// Note: the `String` is potentially left with excess capacity if the range +/// includes non ascii chars; optionally the user may call +/// `string.shrink_to_fit()` afterwards. +#[cfg(feature = "alloc")] +impl crate::distr::DistString for Uniform { + fn append_string( + &self, + rng: &mut R, + string: &mut alloc::string::String, + len: usize, + ) { + // Getting the hi value to assume the required length to reserve in string. + let mut hi = self.0.sampler.low + self.0.sampler.range - 1; + if hi >= CHAR_SURROGATE_START { + hi += CHAR_SURROGATE_LEN; + } + // Get the utf8 length of hi to minimize extra space. + let max_char_len = char::from_u32(hi).map(char::len_utf8).unwrap_or(4); + string.reserve(max_char_len * len); + string.extend(self.sample_iter(rng).take(len)) + } +} + +/// 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, PartialEq, Eq)] +#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] +pub struct UniformDuration { + mode: UniformDurationMode, + offset: u32, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[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) -> Result + where + B1: SampleBorrow + Sized, + B2: SampleBorrow + Sized, + { + let low = *low_b.borrow(); + let high = *high_b.borrow(); + if !(low < high) { + return Err(Error::EmptyRange); + } + UniformDuration::new_inclusive(low, high - Duration::new(0, 1)) + } + + #[inline] + fn new_inclusive(low_b: B1, high_b: B2) -> Result + where + B1: SampleBorrow + Sized, + B2: SampleBorrow + Sized, + { + let low = *low_b.borrow(); + let high = *high_b.borrow(); + if !(low <= high) { + return Err(Error::EmptyRange); + } + + 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)?, + } + } + }; + Ok(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).unwrap(); + 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::*; + + #[test] + #[cfg(feature = "serde1")] + fn test_serialization_uniform_duration() { + let distr = UniformDuration::new(Duration::from_secs(10), Duration::from_secs(60)).unwrap(); + let de_distr: UniformDuration = + bincode::deserialize(&bincode::serialize(&distr).unwrap()).unwrap(); + assert_eq!(distr, de_distr); + } + + #[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!(c.is_ascii_uppercase()); + max = max.max(c); + } + assert_eq!(max, 'Z'); + let d = Uniform::new( + core::char::from_u32(0xD7F0).unwrap(), + core::char::from_u32(0xE010).unwrap(), + ) + .unwrap(); + for _ in 0..100 { + let c = d.sample(&mut rng); + assert!((c as u32) < 0xD800 || (c as u32) > 0xDFFF); + } + #[cfg(feature = "alloc")] + { + use crate::distr::DistString; + let string1 = d.sample_string(&mut rng, 100); + assert_eq!(string1.capacity(), 300); + let string2 = Uniform::new( + core::char::from_u32(0x0000).unwrap(), + core::char::from_u32(0x0080).unwrap(), + ) + .unwrap() + .sample_string(&mut rng, 100); + assert_eq!(string2.capacity(), 100); + let string3 = Uniform::new_inclusive( + core::char::from_u32(0x0000).unwrap(), + core::char::from_u32(0x0080).unwrap(), + ) + .unwrap() + .sample_string(&mut rng, 100); + assert_eq!(string3.capacity(), 200); + } + } + + #[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, 999_999_999)), + ]; + for &(low, high) in v.iter() { + let my_uniform = Uniform::new(low, high).unwrap(); + for _ in 0..1000 { + let v = rng.sample(my_uniform); + assert!(low <= v && v < high); + } + } + } +} diff --git a/src/distributions/utils.rs b/src/distr/utils.rs similarity index 100% rename from src/distributions/utils.rs rename to src/distr/utils.rs diff --git a/src/distributions/weighted_index.rs b/src/distr/weighted_index.rs similarity index 98% rename from src/distributions/weighted_index.rs rename to src/distr/weighted_index.rs index 88fad5a88ad..c0ceefee5d1 100644 --- a/src/distributions/weighted_index.rs +++ b/src/distr/weighted_index.rs @@ -8,8 +8,8 @@ //! Weighted index sampling -use crate::distributions::uniform::{SampleBorrow, SampleUniform, UniformSampler}; -use crate::distributions::Distribution; +use crate::distr::uniform::{SampleBorrow, SampleUniform, UniformSampler}; +use crate::distr::Distribution; use crate::Rng; use core::fmt; @@ -59,7 +59,7 @@ use serde::{Deserialize, Serialize}; /// /// ``` /// use rand::prelude::*; -/// use rand::distributions::WeightedIndex; +/// use rand::distr::WeightedIndex; /// /// let choices = ['a', 'b', 'c']; /// let weights = [2, 1, 1]; @@ -78,7 +78,7 @@ use serde::{Deserialize, Serialize}; /// } /// ``` /// -/// [`Uniform`]: crate::distributions::Uniform +/// [`Uniform`]: crate::distr::Uniform /// [`RngCore`]: crate::RngCore /// [`rand_distr::weighted_alias`]: https://docs.rs/rand_distr/*/rand_distr/weighted_alias/index.html /// [`rand_distr::weighted_tree`]: https://docs.rs/rand_distr/*/rand_distr/weighted_tree/index.html @@ -101,7 +101,7 @@ impl WeightedIndex { /// - [`WeightError::InsufficientNonZero`] when the sum of all weights is zero. /// - [`WeightError::Overflow`] when the sum of all weights overflows. /// - /// [`Uniform`]: crate::distributions::uniform::Uniform + /// [`Uniform`]: crate::distr::uniform::Uniform pub fn new(weights: I) -> Result, WeightError> where I: IntoIterator, @@ -306,7 +306,7 @@ impl WeightedIndex { /// # Example /// /// ``` - /// use rand::distributions::WeightedIndex; + /// use rand::distr::WeightedIndex; /// /// let weights = [0, 1, 2]; /// let dist = WeightedIndex::new(&weights).unwrap(); @@ -341,7 +341,7 @@ impl WeightedIndex { /// # Example /// /// ``` - /// use rand::distributions::WeightedIndex; + /// use rand::distr::WeightedIndex; /// /// let weights = [1, 2, 3]; /// let mut dist = WeightedIndex::new(&weights).unwrap(); diff --git a/src/distributions/uniform.rs b/src/distributions/uniform.rs deleted file mode 100644 index 306b0cced65..00000000000 --- a/src/distributions/uniform.rs +++ /dev/null @@ -1,1774 +0,0 @@ -// 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).unwrap()` 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).unwrap(); -//! -//! // 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 assertion to check the range is valid (i.e. -//! `low < high`). The example below merely wraps another back-end. -//! -//! The `new`, `new_inclusive`, `sample_single` and `sample_single_inclusive` -//! functions use arguments of -//! type `SampleBorrow` 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, Error}; -//! -//! struct MyF32(f32); -//! -//! #[derive(Clone, Copy, Debug)] -//! struct UniformMyF32(UniformFloat); -//! -//! impl UniformSampler for UniformMyF32 { -//! type X = MyF32; -//! -//! fn new(low: B1, high: B2) -> Result -//! where B1: SampleBorrow + Sized, -//! B2: SampleBorrow + Sized -//! { -//! UniformFloat::::new(low.borrow().0, high.borrow().0).map(UniformMyF32) -//! } -//! fn new_inclusive(low: B1, high: B2) -> Result -//! where B1: SampleBorrow + Sized, -//! B2: SampleBorrow + Sized -//! { -//! UniformFloat::::new_inclusive(low.borrow().0, high.borrow().0).map(UniformMyF32) -//! } -//! 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).unwrap(); -//! 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::fmt; -use core::ops::{Range, RangeInclusive}; -use core::time::Duration; - -use crate::distributions::float::IntoFloat; -use crate::distributions::utils::{ - BoolAsSIMD, FloatAsSIMD, FloatSIMDUtils, IntAsSIMD, WideningMultiply, -}; -use crate::distributions::Distribution; -#[cfg(feature = "simd_support")] -use crate::distributions::Standard; -use crate::{Rng, RngCore}; - -#[cfg(feature = "simd_support")] -use core::simd::prelude::*; -#[cfg(feature = "simd_support")] -use core::simd::{LaneCount, SupportedLaneCount}; - -/// Error type returned from [`Uniform::new`] and `new_inclusive`. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum Error { - /// `low > high`, or equal in case of exclusive range. - EmptyRange, - /// Input or range `high - low` is non-finite. Not relevant to integer types. - NonFinite, -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(match self { - Error::EmptyRange => "low > high (or equal if exclusive) in uniform distribution", - Error::NonFinite => "Non-finite range in uniform distribution", - }) - } -} - -#[cfg(feature = "std")] -impl std::error::Error for Error {} - -#[cfg(feature = "serde1")] -use serde::{Deserialize, Serialize}; - -/// 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::try_from(10..10000).unwrap(); -/// 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, Eq)] -#[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`). - /// - /// For discrete types (e.g. integers), samples will always be strictly less - /// than `high`. For (approximations of) continuous types (e.g. `f32`, `f64`), - /// samples may equal `high` due to loss of precision but may not be - /// greater than `high`. - /// - /// Fails if `low >= high`, or if `low`, `high` or the range `high - low` is - /// non-finite. In release mode, only the range is checked. - pub fn new(low: B1, high: B2) -> Result, Error> - where - B1: SampleBorrow + Sized, - B2: SampleBorrow + Sized, - { - X::Sampler::new(low, high).map(Uniform) - } - - /// Create a new `Uniform` instance, which samples uniformly from the closed - /// range `[low, high]` (inclusive). - /// - /// Fails if `low > high`, or if `low`, `high` or the range `high - low` is - /// non-finite. In release mode, only the range is checked. - pub fn new_inclusive(low: B1, high: B2) -> Result, Error> - where - B1: SampleBorrow + Sized, - B2: SampleBorrow + Sized, - { - X::Sampler::new_inclusive(low, high).map(Uniform) - } -} - -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)`. - /// - /// For discrete types (e.g. integers), samples will always be strictly less - /// than `high`. For (approximations of) continuous types (e.g. `f32`, `f64`), - /// samples may equal `high` due to loss of precision but may not be - /// greater than `high`. - /// - /// Usually users should not call this directly but prefer to use - /// [`Uniform::new`]. - fn new(low: B1, high: B2) -> Result - where - B1: SampleBorrow + Sized, - B2: SampleBorrow + Sized; - - /// Construct self, with inclusive bounds `[low, high]`. - /// - /// Usually users should not call this directly but prefer to use - /// [`Uniform::new_inclusive`]. - fn new_inclusive(low: B1, high: B2) -> Result - 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)`. - /// - /// For discrete types (e.g. integers), samples will always be strictly less - /// than `high`. For (approximations of) continuous types (e.g. `f32`, `f64`), - /// samples may equal `high` due to loss of precision but may not be - /// greater than `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).unwrap() - /// } - /// ``` - fn sample_single( - low: B1, - high: B2, - rng: &mut R, - ) -> Result - where - B1: SampleBorrow + Sized, - B2: SampleBorrow + Sized, - { - let uniform: Self = UniformSampler::new(low, high)?; - Ok(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, - ) -> Result - where - B1: SampleBorrow + Sized, - B2: SampleBorrow + Sized, - { - let uniform: Self = UniformSampler::new_inclusive(low, high)?; - Ok(uniform.sample(rng)) - } -} - -impl TryFrom> for Uniform { - type Error = Error; - - fn try_from(r: Range) -> Result, Error> { - Uniform::new(r.start, r.end) - } -} - -impl TryFrom> for Uniform { - type Error = Error; - - fn try_from(r: ::core::ops::RangeInclusive) -> Result, Error> { - 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) -> Result; - - /// Check whether the range is empty. - fn is_empty(&self) -> bool; -} - -impl SampleRange for Range { - #[inline] - fn sample_single(self, rng: &mut R) -> Result { - 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) -> Result { - 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 <= $uty::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, Eq)] -#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] -pub struct UniformInt { - low: X, - range: X, - thresh: X, // effectively 2.pow(max(64, uty_bits)) % range -} - -macro_rules! uniform_int_impl { - ($ty:ty, $uty:ty, $sample_ty: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 $uty 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) -> Result - where - B1: SampleBorrow + Sized, - B2: SampleBorrow + Sized, - { - let low = *low_b.borrow(); - let high = *high_b.borrow(); - if !(low < high) { - return Err(Error::EmptyRange); - } - 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) -> Result - where - B1: SampleBorrow + Sized, - B2: SampleBorrow + Sized, - { - let low = *low_b.borrow(); - let high = *high_b.borrow(); - if !(low <= high) { - return Err(Error::EmptyRange); - } - - let range = high.wrapping_sub(low).wrapping_add(1) as $uty; - let thresh = if range > 0 { - let range = $sample_ty::from(range); - (range.wrapping_neg() % range) - } else { - 0 - }; - - Ok(UniformInt { - low, - range: range as $ty, // type: $uty - thresh: thresh as $uty as $ty, // type: $sample_ty - }) - } - - /// Sample from distribution, Lemire's method, unbiased - #[inline] - fn sample(&self, rng: &mut R) -> Self::X { - let range = self.range as $uty as $sample_ty; - if range == 0 { - return rng.random(); - } - - let thresh = self.thresh as $uty as $sample_ty; - let hi = loop { - let (hi, lo) = rng.random::<$sample_ty>().wmul(range); - if lo >= thresh { - break hi; - } - }; - self.low.wrapping_add(hi as $ty) - } - - #[inline] - fn sample_single( - low_b: B1, - high_b: B2, - rng: &mut R, - ) -> Result - where - B1: SampleBorrow + Sized, - B2: SampleBorrow + Sized, - { - let low = *low_b.borrow(); - let high = *high_b.borrow(); - if !(low < high) { - return Err(Error::EmptyRange); - } - Self::sample_single_inclusive(low, high - 1, rng) - } - - /// Sample single value, Canon's method, biased - /// - /// In the worst case, bias affects 1 in `2^n` samples where n is - /// 56 (`i8`), 48 (`i16`), 96 (`i32`), 64 (`i64`), 128 (`i128`). - #[cfg(not(feature = "unbiased"))] - #[inline] - fn sample_single_inclusive( - low_b: B1, - high_b: B2, - rng: &mut R, - ) -> Result - where - B1: SampleBorrow + Sized, - B2: SampleBorrow + Sized, - { - let low = *low_b.borrow(); - let high = *high_b.borrow(); - if !(low <= high) { - return Err(Error::EmptyRange); - } - let range = high.wrapping_sub(low).wrapping_add(1) as $uty as $sample_ty; - if range == 0 { - // Range is MAX+1 (unrepresentable), so we need a special case - return Ok(rng.random()); - } - - // generate a sample using a sensible integer type - let (mut result, lo_order) = rng.random::<$sample_ty>().wmul(range); - - // if the sample is biased... - if lo_order > range.wrapping_neg() { - // ...generate a new sample to reduce bias... - let (new_hi_order, _) = (rng.random::<$sample_ty>()).wmul(range as $sample_ty); - // ... incrementing result on overflow - let is_overflow = lo_order.checked_add(new_hi_order as $sample_ty).is_none(); - result += is_overflow as $sample_ty; - } - - Ok(low.wrapping_add(result as $ty)) - } - - /// Sample single value, Canon's method, unbiased - #[cfg(feature = "unbiased")] - #[inline] - fn sample_single_inclusive( - low_b: B1, - high_b: B2, - rng: &mut R, - ) -> Result - where - B1: SampleBorrow<$ty> + Sized, - B2: SampleBorrow<$ty> + Sized, - { - let low = *low_b.borrow(); - let high = *high_b.borrow(); - if !(low <= high) { - return Err(Error::EmptyRange); - } - let range = high.wrapping_sub(low).wrapping_add(1) as $uty as $sample_ty; - if range == 0 { - // Range is MAX+1 (unrepresentable), so we need a special case - return Ok(rng.random()); - } - - let (mut result, mut lo) = rng.random::<$sample_ty>().wmul(range); - - // In contrast to the biased sampler, we use a loop: - while lo > range.wrapping_neg() { - let (new_hi, new_lo) = (rng.random::<$sample_ty>()).wmul(range); - match lo.checked_add(new_hi) { - Some(x) if x < $sample_ty::MAX => { - // Anything less than MAX: last term is 0 - break; - } - None => { - // Overflow: last term is 1 - result += 1; - break; - } - _ => { - // Unlikely case: must check next sample - lo = new_lo; - continue; - } - } - } - - Ok(low.wrapping_add(result 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) => { - // 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. - - #[cfg(feature = "simd_support")] - impl SampleUniform for Simd<$ty, LANES> - where - LaneCount: SupportedLaneCount, - Simd<$unsigned, LANES>: - WideningMultiply, Simd<$unsigned, LANES>)>, - Standard: Distribution>, - { - type Sampler = UniformInt>; - } - - #[cfg(feature = "simd_support")] - impl UniformSampler for UniformInt> - where - LaneCount: SupportedLaneCount, - Simd<$unsigned, LANES>: - WideningMultiply, Simd<$unsigned, LANES>)>, - Standard: Distribution>, - { - type X = Simd<$ty, LANES>; - - #[inline] // if the range is constant, this helps LLVM to do the - // calculations at compile-time. - fn new(low_b: B1, high_b: B2) -> Result - where B1: SampleBorrow + Sized, - B2: SampleBorrow + Sized - { - let low = *low_b.borrow(); - let high = *high_b.borrow(); - if !(low.simd_lt(high).all()) { - return Err(Error::EmptyRange); - } - UniformSampler::new_inclusive(low, high - Simd::splat(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) -> Result - where B1: SampleBorrow + Sized, - B2: SampleBorrow + Sized - { - let low = *low_b.borrow(); - let high = *high_b.borrow(); - if !(low.simd_le(high).all()) { - return Err(Error::EmptyRange); - } - - // NOTE: all `Simd` operations are inherently wrapping, - // see https://doc.rust-lang.org/std/simd/struct.Simd.html - let range: Simd<$unsigned, LANES> = ((high - low) + Simd::splat(1)).cast(); - - // We must avoid divide-by-zero by using 0 % 1 == 0. - let not_full_range = range.simd_gt(Simd::splat(0)); - let modulo = not_full_range.select(range, Simd::splat(1)); - let ints_to_reject = range.wrapping_neg() % modulo; - - Ok(UniformInt { - low, - // These are really $unsigned values, but store as $ty: - range: range.cast(), - thresh: ints_to_reject.cast(), - }) - } - - fn sample(&self, rng: &mut R) -> Self::X { - let range: Simd<$unsigned, LANES> = self.range.cast(); - let thresh: Simd<$unsigned, LANES> = self.thresh.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: Simd<$unsigned, LANES> = rng.random(); - loop { - let (hi, lo) = v.wmul(range); - let mask = lo.simd_ge(thresh); - if mask.all() { - let hi: Simd<$ty, LANES> = 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: Simd<$ty, LANES> = v.cast(); - return range.simd_gt(Simd::splat(0)).select(result, v); - } - // Replace only the failing lanes - v = mask.select(v, rng.random()); - } - } - } - }; - - // bulk implementation - ($(($unsigned:ident, $signed:ident)),+) => { - $( - uniform_simd_int_impl!($unsigned, $unsigned); - uniform_simd_int_impl!($signed, $unsigned); - )+ - }; -} - -#[cfg(feature = "simd_support")] -uniform_simd_int_impl! { (u8, i8), (u16, i16), (u32, i32), (u64, i64) } - -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, PartialEq, Eq)] -#[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) -> Result - 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); - sampler.map(|sampler| 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) -> Result - 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); - sampler.map(|sampler| 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) } - } -} - -/// Note: the `String` is potentially left with excess capacity if the range -/// includes non ascii chars; optionally the user may call -/// `string.shrink_to_fit()` afterwards. -#[cfg(feature = "alloc")] -impl super::DistString for Uniform { - fn append_string( - &self, - rng: &mut R, - string: &mut alloc::string::String, - len: usize, - ) { - // Getting the hi value to assume the required length to reserve in string. - let mut hi = self.0.sampler.low + self.0.sampler.range - 1; - if hi >= CHAR_SURROGATE_START { - hi += CHAR_SURROGATE_LEN; - } - // Get the utf8 length of hi to minimize extra space. - let max_char_len = char::from_u32(hi).map(char::len_utf8).unwrap_or(4); - string.reserve(max_char_len * len); - string.extend(self.sample_iter(rng).take(len)) - } -} - -/// 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 { - ($($meta:meta)?, $ty:ty, $uty:ident, $f_scalar:ident, $u_scalar:ident, $bits_to_discard:expr) => { - $(#[cfg($meta)])? - impl UniformFloat<$ty> { - /// Construct, reducing `scale` as required to ensure that rounding - /// can never yield values greater than `high`. - /// - /// Note: though it may be tempting to use a variant of this method - /// to ensure that samples from `[low, high)` are always strictly - /// less than `high`, this approach may be very slow where - /// `scale.abs()` is much smaller than `high.abs()` - /// (example: `low=0.99999999997819644, high=1.`). - fn new_bounded(low: $ty, high: $ty, mut scale: $ty) -> Self { - let max_rand = <$ty>::splat(1.0 as $f_scalar - $f_scalar::EPSILON); - - loop { - let mask = (scale * max_rand + low).gt_mask(high); - if !mask.any() { - break; - } - scale = scale.decrease_masked(mask); - } - - debug_assert!(<$ty>::splat(0.0).all_le(scale)); - - UniformFloat { low, scale } - } - } - - $(#[cfg($meta)])? - impl SampleUniform for $ty { - type Sampler = UniformFloat<$ty>; - } - - $(#[cfg($meta)])? - impl UniformSampler for UniformFloat<$ty> { - type X = $ty; - - fn new(low_b: B1, high_b: B2) -> Result - where - B1: SampleBorrow + Sized, - B2: SampleBorrow + Sized, - { - let low = *low_b.borrow(); - let high = *high_b.borrow(); - #[cfg(debug_assertions)] - if !(low.all_finite()) || !(high.all_finite()) { - return Err(Error::NonFinite); - } - if !(low.all_lt(high)) { - return Err(Error::EmptyRange); - } - - let scale = high - low; - if !(scale.all_finite()) { - return Err(Error::NonFinite); - } - - Ok(Self::new_bounded(low, high, scale)) - } - - fn new_inclusive(low_b: B1, high_b: B2) -> Result - where - B1: SampleBorrow + Sized, - B2: SampleBorrow + Sized, - { - let low = *low_b.borrow(); - let high = *high_b.borrow(); - #[cfg(debug_assertions)] - if !(low.all_finite()) || !(high.all_finite()) { - return Err(Error::NonFinite); - } - if !low.all_le(high) { - return Err(Error::EmptyRange); - } - - let max_rand = <$ty>::splat(1.0 as $f_scalar - $f_scalar::EPSILON); - let scale = (high - low) / max_rand; - if !scale.all_finite() { - return Err(Error::NonFinite); - } - - Ok(Self::new_bounded(low, high, scale)) - } - - fn sample(&self, rng: &mut R) -> Self::X { - // Generate a value in the range [1, 2) - let value1_2 = (rng.random::<$uty>() >> $uty::splat($bits_to_discard)).into_float_with_exponent(0); - - // Get a value in the range [0, 1) to avoid overflow when multiplying by scale - let value0_1 = value1_2 - <$ty>::splat(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) -> Result - where - B1: SampleBorrow + Sized, - B2: SampleBorrow + Sized, - { - Self::sample_single_inclusive(low_b, high_b, rng) - } - - #[inline] - fn sample_single_inclusive(low_b: B1, high_b: B2, rng: &mut R) -> Result - where - B1: SampleBorrow + Sized, - B2: SampleBorrow + Sized, - { - let low = *low_b.borrow(); - let high = *high_b.borrow(); - #[cfg(debug_assertions)] - if !low.all_finite() || !high.all_finite() { - return Err(Error::NonFinite); - } - if !low.all_le(high) { - return Err(Error::EmptyRange); - } - let scale = high - low; - if !scale.all_finite() { - return Err(Error::NonFinite); - } - - // Generate a value in the range [1, 2) - let value1_2 = - (rng.random::<$uty>() >> $uty::splat($bits_to_discard)).into_float_with_exponent(0); - - // Get a value in the range [0, 1) to avoid overflow when multiplying by scale - let value0_1 = value1_2 - <$ty>::splat(1.0); - - // Doing multiply before addition allows some architectures - // to use a single instruction. - Ok(value0_1 * scale + low) - } - } - }; -} - -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! { feature = "simd_support", f32x2, u32x2, f32, u32, 32 - 23 } -#[cfg(feature = "simd_support")] -uniform_float_impl! { feature = "simd_support", f32x4, u32x4, f32, u32, 32 - 23 } -#[cfg(feature = "simd_support")] -uniform_float_impl! { feature = "simd_support", f32x8, u32x8, f32, u32, 32 - 23 } -#[cfg(feature = "simd_support")] -uniform_float_impl! { feature = "simd_support", f32x16, u32x16, f32, u32, 32 - 23 } - -#[cfg(feature = "simd_support")] -uniform_float_impl! { feature = "simd_support", f64x2, u64x2, f64, u64, 64 - 52 } -#[cfg(feature = "simd_support")] -uniform_float_impl! { feature = "simd_support", f64x4, u64x4, f64, u64, 64 - 52 } -#[cfg(feature = "simd_support")] -uniform_float_impl! { feature = "simd_support", 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, PartialEq, Eq)] -#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] -pub struct UniformDuration { - mode: UniformDurationMode, - offset: u32, -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -#[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) -> Result - where - B1: SampleBorrow + Sized, - B2: SampleBorrow + Sized, - { - let low = *low_b.borrow(); - let high = *high_b.borrow(); - if !(low < high) { - return Err(Error::EmptyRange); - } - UniformDuration::new_inclusive(low, high - Duration::new(0, 1)) - } - - #[inline] - fn new_inclusive(low_b: B1, high_b: B2) -> Result - where - B1: SampleBorrow + Sized, - B2: SampleBorrow + Sized, - { - let low = *low_b.borrow(); - let high = *high_b.borrow(); - if !(low <= high) { - return Err(Error::EmptyRange); - } - - 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)?, - } - } - }; - Ok(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).unwrap(); - 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::distributions::utils::FloatSIMDScalarUtils; - 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)).unwrap(); - let de_distr: UniformDuration = - bincode::deserialize(&bincode::serialize(&distr).unwrap()).unwrap(); - assert_eq!(distr, de_distr); - } - - #[test] - #[cfg(feature = "serde1")] - fn test_uniform_serialization() { - let unit_box: Uniform = Uniform::new(-1, 1).unwrap(); - let de_unit_box: Uniform = - bincode::deserialize(&bincode::serialize(&unit_box).unwrap()).unwrap(); - assert_eq!(unit_box.0, de_unit_box.0); - - let unit_box: Uniform = Uniform::new(-1., 1.).unwrap(); - let de_unit_box: Uniform = - bincode::deserialize(&bincode::serialize(&unit_box).unwrap()).unwrap(); - assert_eq!(unit_box.0, de_unit_box.0); - } - - #[test] - fn test_uniform_bad_limits_equal_int() { - assert_eq!(Uniform::new(10, 10), Err(Error::EmptyRange)); - } - - #[test] - fn test_uniform_good_limits_equal_int() { - let mut rng = crate::test::rng(804); - let dist = Uniform::new_inclusive(10, 10).unwrap(); - for _ in 0..20 { - assert_eq!(rng.sample(dist), 10); - } - } - - #[test] - fn test_uniform_bad_limits_flipped_int() { - assert_eq!(Uniform::new(10, 5), Err(Error::EmptyRange)); - } - - #[test] - #[cfg_attr(miri, ignore)] // Miri is too slow - fn test_integers() { - 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).unwrap(); - 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).unwrap(); - 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).unwrap(); - 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).unwrap(); - 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).unwrap(); - assert!($le(low, v) && $lt(v, high)); - } - - for _ in 0..1000 { - let v = <$ty as SampleUniform>::Sampler::sample_single_inclusive(low, high, &mut rng).unwrap(); - 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.simd_le(y).all(), - |x: $ty, y| x.simd_lt(y).all() - );)* - }}; - } - t!(i8, i16, i32, i64, isize, u8, u16, u32, u64, usize, i128, u128); - - #[cfg(feature = "simd_support")] - { - t!(u8x4, u8x8, u8x16, u8x32, u8x64 => u8); - t!(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!(c.is_ascii_uppercase()); - max = max.max(c); - } - assert_eq!(max, 'Z'); - let d = Uniform::new( - core::char::from_u32(0xD7F0).unwrap(), - core::char::from_u32(0xE010).unwrap(), - ) - .unwrap(); - for _ in 0..100 { - let c = d.sample(&mut rng); - assert!((c as u32) < 0xD800 || (c as u32) > 0xDFFF); - } - #[cfg(feature = "alloc")] - { - use crate::distributions::DistString; - let string1 = d.sample_string(&mut rng, 100); - assert_eq!(string1.capacity(), 300); - let string2 = Uniform::new( - core::char::from_u32(0x0000).unwrap(), - core::char::from_u32(0x0080).unwrap(), - ) - .unwrap() - .sample_string(&mut rng, 100); - assert_eq!(string2.capacity(), 100); - let string3 = Uniform::new_inclusive( - core::char::from_u32(0x0000).unwrap(), - core::char::from_u32(0x0080).unwrap(), - ) - .unwrap() - .sample_string(&mut rng, 100); - assert_eq!(string3.capacity(), 200); - } - } - - #[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 * $f_scalar::MAX, $f_scalar::MAX), - (-$f_scalar::MAX * 0.2, $f_scalar::MAX * 0.7), - ]; - for &(low_scalar, high_scalar) in v.iter() { - for lane in 0..<$ty>::LEN { - 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).unwrap(); - let my_incl_uniform = Uniform::new_inclusive(low, high).unwrap(); - 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) - .unwrap() - .extract(lane); - assert!(low_scalar <= v && v <= high_scalar); - let v = <$ty as SampleUniform>::Sampler::sample_single_inclusive( - low, high, &mut rng, - ) - .unwrap() - .extract(lane); - assert!(low_scalar <= v && v <= high_scalar); - } - - assert_eq!( - rng.sample(Uniform::new_inclusive(low, low).unwrap()) - .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 - ) - .unwrap() - .extract(lane), - low_scalar - ); - assert_eq!( - <$ty as SampleUniform>::Sampler::sample_single_inclusive( - low, - high, - &mut zero_rng - ) - .unwrap() - .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); - // sample_single cannot cope with max_rng: - // assert!(<$ty as SampleUniform>::Sampler - // ::sample_single(low, high, &mut max_rng).unwrap() - // .extract(lane) <= high_scalar); - assert!( - <$ty as SampleUniform>::Sampler::sample_single_inclusive( - low, - high, - &mut max_rng - ) - .unwrap() - .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 - ) - .unwrap() - .extract(lane) - <= high_scalar - ); - } - } - } - - assert_eq!( - rng.sample(Uniform::new_inclusive($f_scalar::MAX, $f_scalar::MAX).unwrap()), - $f_scalar::MAX - ); - assert_eq!( - rng.sample(Uniform::new_inclusive(-$f_scalar::MAX, -$f_scalar::MAX).unwrap()), - -$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] - fn test_float_overflow() { - assert_eq!(Uniform::try_from(f64::MIN..f64::MAX), Err(Error::NonFinite)); - } - - #[test] - #[should_panic] - fn test_float_overflow_single() { - let mut rng = crate::test::rng(252); - rng.gen_range(f64::MIN..f64::MAX); - } - - #[test] - #[cfg(all(feature = "std", panic = "unwind"))] - fn test_float_assertions() { - use super::SampleUniform; - fn range(low: T, high: T) -> Result { - 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)] = &[ - ($f_scalar::NAN, 0.0), - (1.0, $f_scalar::NAN), - ($f_scalar::NAN, $f_scalar::NAN), - (1.0, 0.5), - ($f_scalar::MAX, -$f_scalar::MAX), - ($f_scalar::INFINITY, $f_scalar::INFINITY), - ($f_scalar::NEG_INFINITY, $f_scalar::NEG_INFINITY), - ($f_scalar::NEG_INFINITY, 5.0), - (5.0, $f_scalar::INFINITY), - ($f_scalar::NAN, $f_scalar::INFINITY), - ($f_scalar::NEG_INFINITY, $f_scalar::NAN), - ($f_scalar::NEG_INFINITY, $f_scalar::INFINITY), - ]; - for &(low_scalar, high_scalar) in v.iter() { - for lane in 0..<$ty>::LEN { - 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!(range(low, high).is_err()); - assert!(Uniform::new(low, high).is_err()); - assert!(Uniform::new_inclusive(low, high).is_err()); - assert!(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, 999_999_999)), - ]; - for &(low, high) in v.iter() { - let my_uniform = Uniform::new(low, high).unwrap(); - 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) -> Result - where - B1: SampleBorrow + Sized, - B2: SampleBorrow + Sized, - { - UniformFloat::::new(low.borrow().x, high.borrow().x).map(UniformMyF32) - } - - fn new_inclusive(low: B1, high: B2) -> Result - 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).unwrap(); - 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::try_from(2u32..7).unwrap(); - assert_eq!(r.0.low, 2); - assert_eq!(r.0.range, 5); - let r = Uniform::try_from(2.0f64..7.0).unwrap(); - assert_eq!(r.0.low, 2.0); - assert_eq!(r.0.scale, 5.0); - } - - #[test] - fn test_uniform_from_std_range_bad_limits() { - #![allow(clippy::reversed_empty_ranges)] - assert!(Uniform::try_from(100..10).is_err()); - assert!(Uniform::try_from(100..100).is_err()); - assert!(Uniform::try_from(100.0..10.0).is_err()); - assert!(Uniform::try_from(100.0..100.0).is_err()); - } - - #[test] - fn test_uniform_from_std_range_inclusive() { - let r = Uniform::try_from(2u32..=6).unwrap(); - assert_eq!(r.0.low, 2); - assert_eq!(r.0.range, 5); - let r = Uniform::try_from(2.0f64..=7.0).unwrap(); - assert_eq!(r.0.low, 2.0); - assert!(r.0.scale > 5.0); - assert!(r.0.scale < 5.0 + 1e-14); - } - - #[test] - fn test_uniform_from_std_range_inclusive_bad_limits() { - #![allow(clippy::reversed_empty_ranges)] - assert!(Uniform::try_from(100..=10).is_err()); - assert!(Uniform::try_from(100..=99).is_err()); - assert!(Uniform::try_from(100.0..=10.0).is_err()); - assert!(Uniform::try_from(100.0..=99.0).is_err()); - } - - #[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).unwrap(); - } - assert_eq!(&buf, expected_single); - - let distr = Uniform::new(lb, ub).unwrap(); - 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).unwrap(), - Uniform::new(1.0, 2.0).unwrap() - ); - - // To cover UniformInt - assert_eq!( - Uniform::new(1_u32, 2_u32).unwrap(), - Uniform::new(1_u32, 2_u32).unwrap() - ); - } -} diff --git a/src/lib.rs b/src/lib.rs index 8b43f0320f9..ce0206db09b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,7 +17,7 @@ //! To get you started quickly, the easiest and highest-level way to get //! a random value is to use [`random()`]; alternatively you can use //! [`thread_rng()`]. The [`Rng`] trait provides a useful API on all RNGs, while -//! the [`distributions`] and [`seq`] modules provide further +//! the [`distr`] and [`seq`] modules provide further //! functionality on top of RNGs. //! //! ``` @@ -97,7 +97,7 @@ macro_rules! error { ($($x:tt)*) => ( pub use rand_core::{CryptoRng, RngCore, SeedableRng, TryCryptoRng, TryRngCore}; // Public modules -pub mod distributions; +pub mod distr; pub mod prelude; mod rng; pub mod rngs; @@ -109,7 +109,7 @@ pub use crate::rngs::thread::thread_rng; pub use rng::{Fill, Rng}; #[cfg(all(feature = "std", feature = "std_rng", feature = "getrandom"))] -use crate::distributions::{Distribution, Standard}; +use crate::distr::{Distribution, Standard}; /// Generates a random value using the thread-local random number generator. /// @@ -153,7 +153,7 @@ use crate::distributions::{Distribution, Standard}; /// } /// ``` /// -/// [`Standard`]: distributions::Standard +/// [`Standard`]: distr::Standard /// [`ThreadRng`]: rngs::ThreadRng #[cfg(all(feature = "std", feature = "std_rng", feature = "getrandom"))] #[inline] diff --git a/src/prelude.rs b/src/prelude.rs index 2605bca91f4..37b703742df 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -19,7 +19,7 @@ //! ``` #[doc(no_inline)] -pub use crate::distributions::Distribution; +pub use crate::distr::Distribution; #[cfg(feature = "small_rng")] #[doc(no_inline)] pub use crate::rngs::SmallRng; diff --git a/src/rng.rs b/src/rng.rs index 06fc2bb741e..9c015eddd41 100644 --- a/src/rng.rs +++ b/src/rng.rs @@ -9,8 +9,8 @@ //! [`Rng`] trait -use crate::distributions::uniform::{SampleRange, SampleUniform}; -use crate::distributions::{self, Distribution, Standard}; +use crate::distr::uniform::{SampleRange, SampleUniform}; +use crate::distr::{self, Distribution, Standard}; use core::num::Wrapping; use core::{mem, slice}; use rand_core::RngCore; @@ -86,7 +86,7 @@ pub trait Rng: RngCore { /// rng.fill(&mut arr2); // array fill /// ``` /// - /// [`Standard`]: distributions::Standard + /// [`Standard`]: distr::Standard #[inline] fn random(&mut self) -> T where @@ -125,7 +125,7 @@ pub trait Rng: RngCore { /// println!("{}", n); /// ``` /// - /// [`Uniform`]: distributions::uniform::Uniform + /// [`Uniform`]: distr::uniform::Uniform #[track_caller] fn gen_range(&mut self, range: R) -> T where @@ -139,7 +139,7 @@ pub trait Rng: RngCore { /// Generate values via an iterator /// /// This is a just a wrapper over [`Rng::sample_iter`] using - /// [`distributions::Standard`]. + /// [`distr::Standard`]. /// /// Note: this method consumes its argument. Use /// `(&mut rng).gen_iter()` to avoid consuming the RNG. @@ -154,7 +154,7 @@ pub trait Rng: RngCore { /// assert_eq!(&v, &[1, 2, 3, 4, 5]); /// ``` #[inline] - fn gen_iter(self) -> distributions::DistIter + fn gen_iter(self) -> distr::DistIter where Self: Sized, Standard: Distribution, @@ -168,7 +168,7 @@ pub trait Rng: RngCore { /// /// ``` /// use rand::{thread_rng, Rng}; - /// use rand::distributions::Uniform; + /// use rand::distr::Uniform; /// /// let mut rng = thread_rng(); /// let x = rng.sample(Uniform::new(10u32, 15).unwrap()); @@ -189,7 +189,7 @@ pub trait Rng: RngCore { /// /// ``` /// use rand::{thread_rng, Rng}; - /// use rand::distributions::{Alphanumeric, Uniform, Standard}; + /// use rand::distr::{Alphanumeric, Uniform, Standard}; /// /// let mut rng = thread_rng(); /// @@ -213,7 +213,7 @@ pub trait Rng: RngCore { /// println!("Not a 6; rolling again!"); /// } /// ``` - fn sample_iter(self, distr: D) -> distributions::DistIter + fn sample_iter(self, distr: D) -> distr::DistIter where D: Distribution, Self: Sized, @@ -259,11 +259,11 @@ pub trait Rng: RngCore { /// /// If `p < 0` or `p > 1`. /// - /// [`Bernoulli`]: distributions::Bernoulli + /// [`Bernoulli`]: distr::Bernoulli #[inline] #[track_caller] fn gen_bool(&mut self, p: f64) -> bool { - match distributions::Bernoulli::new(p) { + match distr::Bernoulli::new(p) { Ok(d) => self.sample(d), Err(_) => panic!("p={:?} is outside range [0.0, 1.0]", p), } @@ -291,11 +291,11 @@ pub trait Rng: RngCore { /// println!("{}", rng.gen_ratio(2, 3)); /// ``` /// - /// [`Bernoulli`]: distributions::Bernoulli + /// [`Bernoulli`]: distr::Bernoulli #[inline] #[track_caller] fn gen_ratio(&mut self, numerator: u32, denominator: u32) -> bool { - match distributions::Bernoulli::from_ratio(numerator, denominator) { + match distr::Bernoulli::from_ratio(numerator, denominator) { Ok(d) => self.sample(d), Err(_) => panic!( "p={}/{} is outside range [0.0, 1.0]", @@ -554,7 +554,7 @@ mod test { #[test] fn test_rng_trait_object() { - use crate::distributions::{Distribution, Standard}; + use crate::distr::{Distribution, Standard}; let mut rng = rng(109); let mut r = &mut rng as &mut dyn RngCore; r.next_u32(); @@ -566,7 +566,7 @@ mod test { #[test] #[cfg(feature = "alloc")] fn test_rng_boxed_trait() { - use crate::distributions::{Distribution, Standard}; + use crate::distr::{Distribution, Standard}; let rng = rng(110); let mut r = Box::new(rng) as Box; r.next_u32(); diff --git a/src/rngs/mock.rs b/src/rngs/mock.rs index a01a6bd7b4e..6218626db9b 100644 --- a/src/rngs/mock.rs +++ b/src/rngs/mock.rs @@ -22,7 +22,7 @@ use serde::{Deserialize, Serialize}; /// Other integer types (64-bit and smaller) are produced via cast from `u64`. /// /// Other types are produced via their implementation of [`Rng`](crate::Rng) or -/// [`Distribution`](crate::distributions::Distribution). +/// [`Distribution`](crate::distr::Distribution). /// Output values may not be intuitive and may change in future releases but /// are considered /// [portable](https://rust-random.github.io/book/portability.html). @@ -95,7 +95,7 @@ mod tests { #[test] #[cfg(feature = "alloc")] fn test_bool() { - use crate::{distributions::Standard, Rng}; + use crate::{distr::Standard, Rng}; // If this result ever changes, update doc on StepRng! let rng = StepRng::new(0, 1 << 31); diff --git a/src/seq/index.rs b/src/seq/index.rs index e37198bd67a..d7d1636570c 100644 --- a/src/seq/index.rs +++ b/src/seq/index.rs @@ -7,19 +7,16 @@ // except according to those terms. //! Low-level API for sampling indices -use super::gen_index; -#[cfg(feature = "alloc")] use alloc::vec::{self, Vec}; use core::slice; use core::{hash::Hash, ops::AddAssign}; // BTreeMap is not as fast in tests, but better than nothing. #[cfg(feature = "std")] use super::WeightError; -use crate::distributions::uniform::SampleUniform; -#[cfg(feature = "alloc")] -use crate::distributions::{Distribution, Uniform}; +use crate::distr::uniform::SampleUniform; +use crate::distr::{Distribution, Uniform}; use crate::Rng; -#[cfg(all(feature = "alloc", not(feature = "std")))] +#[cfg(not(feature = "std"))] use alloc::collections::BTreeSet; #[cfg(feature = "serde1")] use serde::{Deserialize, Serialize}; @@ -30,7 +27,6 @@ use std::collections::HashSet; /// /// Multiple internal representations are possible. #[derive(Clone, Debug)] -#[cfg(feature = "alloc")] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pub enum IndexVec { #[doc(hidden)] @@ -39,7 +35,6 @@ pub enum IndexVec { USize(Vec), } -#[cfg(feature = "alloc")] impl IndexVec { /// Returns the number of indices #[inline] @@ -90,7 +85,6 @@ impl IndexVec { } } -#[cfg(feature = "alloc")] impl IntoIterator for IndexVec { type IntoIter = IndexVecIntoIter; type Item = usize; @@ -105,7 +99,6 @@ impl IntoIterator for IndexVec { } } -#[cfg(feature = "alloc")] impl PartialEq for IndexVec { fn eq(&self, other: &IndexVec) -> bool { use self::IndexVec::*; @@ -122,7 +115,6 @@ impl PartialEq for IndexVec { } } -#[cfg(feature = "alloc")] impl From> for IndexVec { #[inline] fn from(v: Vec) -> Self { @@ -130,7 +122,6 @@ impl From> for IndexVec { } } -#[cfg(feature = "alloc")] impl From> for IndexVec { #[inline] fn from(v: Vec) -> Self { @@ -171,7 +162,6 @@ impl<'a> Iterator for IndexVecIter<'a> { impl<'a> ExactSizeIterator for IndexVecIter<'a> {} /// Return type of `IndexVec::into_iter`. -#[cfg(feature = "alloc")] #[derive(Clone, Debug)] pub enum IndexVecIntoIter { #[doc(hidden)] @@ -180,7 +170,6 @@ pub enum IndexVecIntoIter { USize(vec::IntoIter), } -#[cfg(feature = "alloc")] impl Iterator for IndexVecIntoIter { type Item = usize; @@ -203,7 +192,6 @@ impl Iterator for IndexVecIntoIter { } } -#[cfg(feature = "alloc")] impl ExactSizeIterator for IndexVecIntoIter {} /// Randomly sample exactly `amount` distinct indices from `0..length`, and @@ -228,7 +216,6 @@ impl ExactSizeIterator for IndexVecIntoIter {} /// to adapt the internal `sample_floyd` implementation. /// /// Panics if `amount > length`. -#[cfg(feature = "alloc")] #[track_caller] pub fn sample(rng: &mut R, length: usize, amount: usize) -> IndexVec where @@ -271,33 +258,6 @@ where } } -/// Randomly sample exactly `N` distinct indices from `0..len`, and -/// return them in random order (fully shuffled). -/// -/// This is implemented via Floyd's algorithm. Time complexity is `O(N^2)` -/// and memory complexity is `O(N)`. -/// -/// Returns `None` if (and only if) `N > len`. -pub fn sample_array(rng: &mut R, len: usize) -> Option<[usize; N]> -where - R: Rng + ?Sized, -{ - if N > len { - return None; - } - - // Floyd's algorithm - let mut indices = [0; N]; - for (i, j) in (len - N..len).enumerate() { - let t = gen_index(rng, j + 1); - if let Some(pos) = indices[0..i].iter().position(|&x| x == t) { - indices[pos] = j; - } - indices[i] = t; - } - Some(indices) -} - /// Randomly sample exactly `amount` distinct indices from `0..length`, and /// return them in an arbitrary order (there is no guarantee of shuffling or /// ordering). The weights are to be provided by the input function `weights`, @@ -432,7 +392,6 @@ where /// The output values are fully shuffled. (Overhead is under 50%.) /// /// This implementation uses `O(amount)` memory and `O(amount^2)` time. -#[cfg(feature = "alloc")] fn sample_floyd(rng: &mut R, length: u32, amount: u32) -> IndexVec where R: Rng + ?Sized, @@ -464,7 +423,6 @@ where /// performance in all cases). /// /// Set-up is `O(length)` time and memory and shuffling is `O(amount)` time. -#[cfg(feature = "alloc")] fn sample_inplace(rng: &mut R, length: u32, amount: u32) -> IndexVec where R: Rng + ?Sized, @@ -483,6 +441,7 @@ where trait UInt: Copy + PartialOrd + Ord + PartialEq + Eq + SampleUniform + Hash + AddAssign { fn zero() -> Self; + #[cfg_attr(feature = "alloc", allow(dead_code))] fn one() -> Self; fn as_usize(self) -> usize; } @@ -530,7 +489,6 @@ impl UInt for usize { /// /// This function is generic over X primarily so that results are value-stable /// over 32-bit and 64-bit platforms. -#[cfg(feature = "alloc")] fn sample_rejection(rng: &mut R, length: X, amount: X) -> IndexVec where R: Rng + ?Sized, @@ -555,7 +513,6 @@ where IndexVec::from(indices) } -#[cfg(feature = "alloc")] #[cfg(test)] mod test { use super::*; diff --git a/src/seq/mod.rs b/src/seq/mod.rs index b7cd1729d21..0015517907a 100644 --- a/src/seq/mod.rs +++ b/src/seq/mod.rs @@ -19,7 +19,7 @@ //! //! Also see: //! -//! * [`crate::distributions::WeightedIndex`] distribution which provides +//! * [`crate::distr::WeightedIndex`] distribution which provides //! weighted index sampling. //! //! In order to make results reproducible across 32-64 bit architectures, all @@ -31,11 +31,13 @@ mod increasing_uniform; mod iterator; mod slice; -pub mod index; +#[cfg(feature = "alloc")] +#[path = "index.rs"] +mod index_; #[cfg(feature = "alloc")] #[doc(no_inline)] -pub use crate::distributions::WeightError; +pub use crate::distr::WeightError; pub use iterator::IteratorRandom; #[cfg(feature = "alloc")] pub use slice::SliceChooseIter; @@ -54,3 +56,40 @@ fn gen_index(rng: &mut R, ubound: usize) -> usize { rng.gen_range(0..ubound) } } + +/// Low-level API for sampling indices +pub mod index { + use super::gen_index; + use crate::Rng; + + #[cfg(feature = "alloc")] + #[doc(inline)] + pub use super::index_::*; + + /// Randomly sample exactly `N` distinct indices from `0..len`, and + /// return them in random order (fully shuffled). + /// + /// This is implemented via Floyd's algorithm. Time complexity is `O(N^2)` + /// and memory complexity is `O(N)`. + /// + /// Returns `None` if (and only if) `N > len`. + pub fn sample_array(rng: &mut R, len: usize) -> Option<[usize; N]> + where + R: Rng + ?Sized, + { + if N > len { + return None; + } + + // Floyd's algorithm + let mut indices = [0; N]; + for (i, j) in (len - N..len).enumerate() { + let t = gen_index(rng, j + 1); + if let Some(pos) = indices[0..i].iter().position(|&x| x == t) { + indices[pos] = j; + } + indices[i] = t; + } + Some(indices) + } +} diff --git a/src/seq/slice.rs b/src/seq/slice.rs index c82998fd358..7c86f00a736 100644 --- a/src/seq/slice.rs +++ b/src/seq/slice.rs @@ -11,9 +11,9 @@ use super::increasing_uniform::IncreasingUniform; use super::{gen_index, index}; #[cfg(feature = "alloc")] -use crate::distributions::uniform::{SampleBorrow, SampleUniform}; +use crate::distr::uniform::{SampleBorrow, SampleUniform}; #[cfg(feature = "alloc")] -use crate::distributions::{Weight, WeightError}; +use crate::distr::{Weight, WeightError}; use crate::Rng; use core::ops::{Index, IndexMut}; @@ -137,7 +137,7 @@ pub trait IndexedRandom: Index { /// /// For slices of length `n`, complexity is `O(n)`. /// For more information about the underlying algorithm, - /// see [`distributions::WeightedIndex`]. + /// see [`distr::WeightedIndex`]. /// /// See also [`choose_weighted_mut`]. /// @@ -154,7 +154,7 @@ pub trait IndexedRandom: Index { /// ``` /// [`choose`]: IndexedRandom::choose /// [`choose_weighted_mut`]: IndexedMutRandom::choose_weighted_mut - /// [`distributions::WeightedIndex`]: crate::distributions::WeightedIndex + /// [`distr::WeightedIndex`]: crate::distr::WeightedIndex #[cfg(feature = "alloc")] fn choose_weighted( &self, @@ -167,7 +167,7 @@ pub trait IndexedRandom: Index { B: SampleBorrow, X: SampleUniform + Weight + PartialOrd, { - use crate::distributions::{Distribution, WeightedIndex}; + use crate::distr::{Distribution, WeightedIndex}; let distr = WeightedIndex::new((0..self.len()).map(|idx| weight(&self[idx])))?; Ok(&self[distr.sample(rng)]) } @@ -268,13 +268,13 @@ pub trait IndexedMutRandom: IndexedRandom + IndexMut { /// /// For slices of length `n`, complexity is `O(n)`. /// For more information about the underlying algorithm, - /// see [`distributions::WeightedIndex`]. + /// see [`distr::WeightedIndex`]. /// /// See also [`choose_weighted`]. /// /// [`choose_mut`]: IndexedMutRandom::choose_mut /// [`choose_weighted`]: IndexedRandom::choose_weighted - /// [`distributions::WeightedIndex`]: crate::distributions::WeightedIndex + /// [`distr::WeightedIndex`]: crate::distr::WeightedIndex #[cfg(feature = "alloc")] fn choose_weighted_mut( &mut self, @@ -287,7 +287,7 @@ pub trait IndexedMutRandom: IndexedRandom + IndexMut { B: SampleBorrow, X: SampleUniform + Weight + PartialOrd, { - use crate::distributions::{Distribution, WeightedIndex}; + use crate::distr::{Distribution, WeightedIndex}; let distr = WeightedIndex::new((0..self.len()).map(|idx| weight(&self[idx])))?; let index = distr.sample(rng); Ok(&mut self[index]) diff --git a/utils/ziggurat_tables.py b/utils/ziggurat_tables.py index 88cfdab6ba2..87a766ccc36 100755 --- a/utils/ziggurat_tables.py +++ b/utils/ziggurat_tables.py @@ -10,7 +10,7 @@ # except according to those terms. # This creates the tables used for distributions implemented using the -# ziggurat algorithm in `rand::distributions;`. They are +# ziggurat algorithm in `rand::distr;`. 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.