rand::distributions -> distr; split uniform module (#1470)

This commit is contained in:
Diggory Hardy 2024-07-23 14:14:11 +01:00 committed by GitHub
parent 605476c4e4
commit 2d5948d264
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
40 changed files with 2012 additions and 1918 deletions

View File

@ -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)

View File

@ -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/)

View File

@ -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]]

View File

@ -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)`.

View File

@ -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};

View File

@ -10,7 +10,7 @@
extern crate test;
use rand::distributions::WeightedIndex;
use rand::distr::WeightedIndex;
use rand::Rng;
use test::Bencher;

View File

@ -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};

View File

@ -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};

View File

@ -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();

View File

@ -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 {

View File

@ -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::*;

View File

@ -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

View File

@ -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)]

View File

@ -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;

View File

@ -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

View File

@ -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<W>`] and [`rand::distributions::WeightedIndex<W>`]
/// The main distinction between [`WeightedTreeIndex<W>`] and [`rand::distr::WeightedIndex<W>`]
/// lies in the internal representation of weights. In [`WeightedTreeIndex<W>`],
/// weights are structured as a tree, which is optimized for frequent updates of the weights.
///

View File

@ -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)`.
///

View File

@ -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<bool> for Bernoulli {
#[cfg(test)]
mod test {
use super::Bernoulli;
use crate::distributions::Distribution;
use crate::distr::Distribution;
use crate::Rng;
#[test]

View File

@ -49,7 +49,7 @@ pub trait Distribution<T> {
///
/// ```
/// 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<T> {
///
/// ```
/// 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::<f32>::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);

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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);
/// ```

View File

@ -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")
}
}

581
src/distr/uniform.rs Normal file
View File

@ -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 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, 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<X>` 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<f32>);
//!
//! impl UniformSampler for UniformMyF32 {
//! type X = MyF32;
//!
//! fn new<B1, B2>(low: B1, high: B2) -> Result<Self, Error>
//! where B1: SampleBorrow<Self::X> + Sized,
//! B2: SampleBorrow<Self::X> + Sized
//! {
//! UniformFloat::<f32>::new(low.borrow().0, high.borrow().0).map(UniformMyF32)
//! }
//! fn new_inclusive<B1, B2>(low: B1, high: B2) -> Result<Self, Error>
//! where B1: SampleBorrow<Self::X> + Sized,
//! B2: SampleBorrow<Self::X> + Sized
//! {
//! UniformFloat::<f32>::new_inclusive(low.borrow().0, high.borrow().0).map(UniformMyF32)
//! }
//! fn sample<R: Rng + ?Sized>(&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::<u8>() % 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: SampleUniform>(X::Sampler);
impl<X: SampleUniform> Uniform<X> {
/// 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<B1, B2>(low: B1, high: B2) -> Result<Uniform<X>, Error>
where
B1: SampleBorrow<X> + Sized,
B2: SampleBorrow<X> + 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<B1, B2>(low: B1, high: B2) -> Result<Uniform<X>, Error>
where
B1: SampleBorrow<X> + Sized,
B2: SampleBorrow<X> + Sized,
{
X::Sampler::new_inclusive(low, high).map(Uniform)
}
}
impl<X: SampleUniform> Distribution<X> for Uniform<X> {
fn sample<R: Rng + ?Sized>(&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<X = Self>;
}
/// 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<B1, B2>(low: B1, high: B2) -> Result<Self, Error>
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + 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<B1, B2>(low: B1, high: B2) -> Result<Self, Error>
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized;
/// Sample a value.
fn sample<R: Rng + ?Sized>(&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<T: SampleUniform>(lb: T, ub: T) -> T {
/// let mut rng = thread_rng();
/// <T as SampleUniform>::Sampler::sample_single(lb, ub, &mut rng).unwrap()
/// }
/// ```
fn sample_single<R: Rng + ?Sized, B1, B2>(
low: B1,
high: B2,
rng: &mut R,
) -> Result<Self::X, Error>
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + 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<R: Rng + ?Sized, B1, B2>(
low: B1,
high: B2,
rng: &mut R,
) -> Result<Self::X, Error>
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
let uniform: Self = UniformSampler::new_inclusive(low, high)?;
Ok(uniform.sample(rng))
}
}
impl<X: SampleUniform> TryFrom<Range<X>> for Uniform<X> {
type Error = Error;
fn try_from(r: Range<X>) -> Result<Uniform<X>, Error> {
Uniform::new(r.start, r.end)
}
}
impl<X: SampleUniform> TryFrom<RangeInclusive<X>> for Uniform<X> {
type Error = Error;
fn try_from(r: ::core::ops::RangeInclusive<X>) -> Result<Uniform<X>, 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<Borrowed> {
/// Immutably borrows from an owned value. See [`Borrow::borrow`]
///
/// [`Borrow::borrow`]: std::borrow::Borrow::borrow
fn borrow(&self) -> &Borrowed;
}
impl<Borrowed> SampleBorrow<Borrowed> for Borrowed
where
Borrowed: SampleUniform,
{
#[inline(always)]
fn borrow(&self) -> &Borrowed {
self
}
}
impl<'a, Borrowed> SampleBorrow<Borrowed> 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<T> {
/// Generate a sample from the given range.
fn sample_single<R: RngCore + ?Sized>(self, rng: &mut R) -> Result<T, Error>;
/// Check whether the range is empty.
fn is_empty(&self) -> bool;
}
impl<T: SampleUniform + PartialOrd> SampleRange<T> for Range<T> {
#[inline]
fn sample_single<R: RngCore + ?Sized>(self, rng: &mut R) -> Result<T, Error> {
T::Sampler::sample_single(self.start, self.end, rng)
}
#[inline]
fn is_empty(&self) -> bool {
!(self.start < self.end)
}
}
impl<T: SampleUniform + PartialOrd> SampleRange<T> for RangeInclusive<T> {
#[inline]
fn sample_single<R: RngCore + ?Sized>(self, rng: &mut R) -> Result<T, Error> {
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<i32> = Uniform::new(-1, 1).unwrap();
let de_unit_box: Uniform<i32> =
bincode::deserialize(&bincode::serialize(&unit_box).unwrap()).unwrap();
assert_eq!(unit_box.0, de_unit_box.0);
let unit_box: Uniform<f32> = Uniform::new(-1., 1.).unwrap();
let de_unit_box: Uniform<f32> =
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<f32>);
impl UniformSampler for UniformMyF32 {
type X = MyF32;
fn new<B1, B2>(low: B1, high: B2) -> Result<Self, Error>
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
UniformFloat::<f32>::new(low.borrow().x, high.borrow().x).map(UniformMyF32)
}
fn new_inclusive<B1, B2>(low: B1, high: B2) -> Result<Self, Error>
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
UniformSampler::new(low, high)
}
fn sample<R: Rng + ?Sized>(&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<T: SampleUniform + Copy + fmt::Debug + PartialEq>(
lb: T,
ub: T,
expected_single: &[T],
expected_multiple: &[T],
) where
Uniform<T>: Distribution<T>,
{
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()
);
}
}

450
src/distr/uniform_float.rs Normal file
View File

@ -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 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, 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<X> {
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<B1, B2>(low_b: B1, high_b: B2) -> Result<Self, Error>
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + 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<B1, B2>(low_b: B1, high_b: B2) -> Result<Self, Error>
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + 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<R: Rng + ?Sized>(&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<R: Rng + ?Sized, B1, B2>(low_b: B1, high_b: B2, rng: &mut R) -> Result<Self::X, Error>
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
Self::sample_single_inclusive(low_b, high_b, rng)
}
#[inline]
fn sample_single_inclusive<R: Rng + ?Sized, B1, B2>(low_b: B1, high_b: B2, rng: &mut R) -> Result<Self::X, Error>
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + 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<T: SampleUniform>(low: T, high: T) -> Result<T, Error> {
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());
}
}

521
src/distr/uniform_int.rs Normal file
View File

@ -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 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, 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<X>` 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<X> {
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<B1, B2>(low_b: B1, high_b: B2) -> Result<Self, Error>
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + 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<B1, B2>(low_b: B1, high_b: B2) -> Result<Self, Error>
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + 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<R: Rng + ?Sized>(&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<R: Rng + ?Sized, B1, B2>(
low_b: B1,
high_b: B2,
rng: &mut R,
) -> Result<Self::X, Error>
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + 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<R: Rng + ?Sized, B1, B2>(
low_b: B1,
high_b: B2,
rng: &mut R,
) -> Result<Self::X, Error>
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + 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<R: Rng + ?Sized, B1, B2>(
low_b: B1,
high_b: B2,
rng: &mut R,
) -> Result<Self::X, Error>
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<const LANES: usize> SampleUniform for Simd<$ty, LANES>
where
LaneCount<LANES>: SupportedLaneCount,
Simd<$unsigned, LANES>:
WideningMultiply<Output = (Simd<$unsigned, LANES>, Simd<$unsigned, LANES>)>,
Standard: Distribution<Simd<$unsigned, LANES>>,
{
type Sampler = UniformInt<Simd<$ty, LANES>>;
}
#[cfg(feature = "simd_support")]
impl<const LANES: usize> UniformSampler for UniformInt<Simd<$ty, LANES>>
where
LaneCount<LANES>: SupportedLaneCount,
Simd<$unsigned, LANES>:
WideningMultiply<Output = (Simd<$unsigned, LANES>, Simd<$unsigned, LANES>)>,
Standard: Distribution<Simd<$unsigned, LANES>>,
{
type X = Simd<$ty, LANES>;
#[inline] // if the range is constant, this helps LLVM to do the
// calculations at compile-time.
fn new<B1, B2>(low_b: B1, high_b: B2) -> Result<Self, Error>
where B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + 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<B1, B2>(low_b: B1, high_b: B2) -> Result<Self, Error>
where B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + 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<R: Rng + ?Sized>(&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());
}
}

322
src/distr/uniform_other.rs Normal file
View File

@ -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 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, 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<u32>,
}
/// 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<B1, B2>(low_b: B1, high_b: B2) -> Result<Self, Error>
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
let low = char_to_comp_u32(*low_b.borrow());
let high = char_to_comp_u32(*high_b.borrow());
let sampler = UniformInt::<u32>::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<B1, B2>(low_b: B1, high_b: B2) -> Result<Self, Error>
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
let low = char_to_comp_u32(*low_b.borrow());
let high = char_to_comp_u32(*high_b.borrow());
let sampler = UniformInt::<u32>::new_inclusive(low, high);
sampler.map(|sampler| UniformChar { sampler })
}
fn sample<R: Rng + ?Sized>(&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<char> {
fn append_string<R: Rng + ?Sized>(
&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<u32>,
},
Medium {
nanos: Uniform<u64>,
},
Large {
max_secs: u64,
max_nanos: u32,
secs: Uniform<u64>,
},
}
impl SampleUniform for Duration {
type Sampler = UniformDuration;
}
impl UniformSampler for UniformDuration {
type X = Duration;
#[inline]
fn new<B1, B2>(low_b: B1, high_b: B2) -> Result<Self, Error>
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + 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<B1, B2>(low_b: B1, high_b: B2) -> Result<Self, Error>
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + 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<R: Rng + ?Sized>(&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);
}
}
}
}

View File

@ -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<X>`]: crate::distributions::Uniform
/// [`Uniform<X>`]: 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<X: SampleUniform + PartialOrd> WeightedIndex<X> {
/// - [`WeightError::InsufficientNonZero`] when the sum of all weights is zero.
/// - [`WeightError::Overflow`] when the sum of all weights overflows.
///
/// [`Uniform<X>`]: crate::distributions::uniform::Uniform
/// [`Uniform<X>`]: crate::distr::uniform::Uniform
pub fn new<I>(weights: I) -> Result<WeightedIndex<X>, WeightError>
where
I: IntoIterator,
@ -306,7 +306,7 @@ impl<X: SampleUniform + PartialOrd + Clone> WeightedIndex<X> {
/// # 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<X: SampleUniform + PartialOrd + Clone> WeightedIndex<X> {
/// # Example
///
/// ```
/// use rand::distributions::WeightedIndex;
/// use rand::distr::WeightedIndex;
///
/// let weights = [1, 2, 3];
/// let mut dist = WeightedIndex::new(&weights).unwrap();

File diff suppressed because it is too large Load Diff

View File

@ -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]

View File

@ -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;

View File

@ -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<T>(&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<T, R>(&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<T>(self) -> distributions::DistIter<Standard, Self, T>
fn gen_iter<T>(self) -> distr::DistIter<Standard, Self, T>
where
Self: Sized,
Standard: Distribution<T>,
@ -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<T, D>(self, distr: D) -> distributions::DistIter<D, Self, T>
fn sample_iter<T, D>(self, distr: D) -> distr::DistIter<D, Self, T>
where
D: Distribution<T>,
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<dyn RngCore>;
r.next_u32();

View File

@ -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);

View File

@ -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<usize>),
}
#[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<Vec<u32>> for IndexVec {
#[inline]
fn from(v: Vec<u32>) -> Self {
@ -130,7 +122,6 @@ impl From<Vec<u32>> for IndexVec {
}
}
#[cfg(feature = "alloc")]
impl From<Vec<usize>> for IndexVec {
#[inline]
fn from(v: Vec<usize>) -> 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<usize>),
}
#[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<R>(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<R, const N: usize>(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<R>(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<R>(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<X: UInt, R>(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::*;

View File

@ -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<R: Rng + ?Sized>(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<R, const N: usize>(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)
}
}

View File

@ -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<usize> {
///
/// 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<usize> {
/// ```
/// [`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<R, F, B, X>(
&self,
@ -167,7 +167,7 @@ pub trait IndexedRandom: Index<usize> {
B: SampleBorrow<X>,
X: SampleUniform + Weight + PartialOrd<X>,
{
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<usize> {
///
/// 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<R, F, B, X>(
&mut self,
@ -287,7 +287,7 @@ pub trait IndexedMutRandom: IndexedRandom + IndexMut<usize> {
B: SampleBorrow<X>,
X: SampleUniform + Weight + PartialOrd<X>,
{
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])

View File

@ -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.