Skip to content

Commit 1947c89

Browse files
committed
Move Distribution trait and associates to sub-module
1 parent 98a0339 commit 1947c89

File tree

2 files changed

+260
-250
lines changed

2 files changed

+260
-250
lines changed

src/distributions/distribution.rs

+236
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
// Copyright 2018 Developers of the Rand project.
2+
// Copyright 2013-2017 The Rust Project Developers.
3+
//
4+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5+
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6+
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
7+
// option. This file may not be copied, modified, or distributed
8+
// except according to those terms.
9+
10+
//! Distribution trait and associates
11+
12+
use crate::Rng;
13+
use core::iter;
14+
15+
/// Types (distributions) that can be used to create a random instance of `T`.
16+
///
17+
/// It is possible to sample from a distribution through both the
18+
/// `Distribution` and [`Rng`] traits, via `distr.sample(&mut rng)` and
19+
/// `rng.sample(distr)`. They also both offer the [`sample_iter`] method, which
20+
/// produces an iterator that samples from the distribution.
21+
///
22+
/// All implementations are expected to be immutable; this has the significant
23+
/// advantage of not needing to consider thread safety, and for most
24+
/// distributions efficient state-less sampling algorithms are available.
25+
///
26+
/// Implementations are typically expected to be portable with reproducible
27+
/// results when used with a PRNG with fixed seed; see the
28+
/// [portability chapter](https://rust-random.github.io/book/portability.html)
29+
/// of The Rust Rand Book. In some cases this does not apply, e.g. the `usize`
30+
/// type requires different sampling on 32-bit and 64-bit machines.
31+
///
32+
/// [`sample_iter`]: Distribution::sample_iter
33+
pub trait Distribution<T> {
34+
/// Generate a random value of `T`, using `rng` as the source of randomness.
35+
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> T;
36+
37+
/// Create an iterator that generates random values of `T`, using `rng` as
38+
/// the source of randomness.
39+
///
40+
/// Note that this function takes `self` by value. This works since
41+
/// `Distribution<T>` is impl'd for `&D` where `D: Distribution<T>`,
42+
/// however borrowing is not automatic hence `distr.sample_iter(...)` may
43+
/// need to be replaced with `(&distr).sample_iter(...)` to borrow or
44+
/// `(&*distr).sample_iter(...)` to reborrow an existing reference.
45+
///
46+
/// # Example
47+
///
48+
/// ```
49+
/// use rand::thread_rng;
50+
/// use rand::distributions::{Distribution, Alphanumeric, Uniform, Standard};
51+
///
52+
/// let mut rng = thread_rng();
53+
///
54+
/// // Vec of 16 x f32:
55+
/// let v: Vec<f32> = Standard.sample_iter(&mut rng).take(16).collect();
56+
///
57+
/// // String:
58+
/// let s: String = Alphanumeric
59+
/// .sample_iter(&mut rng)
60+
/// .take(7)
61+
/// .map(char::from)
62+
/// .collect();
63+
///
64+
/// // Dice-rolling:
65+
/// let die_range = Uniform::new_inclusive(1, 6);
66+
/// let mut roll_die = die_range.sample_iter(&mut rng);
67+
/// while roll_die.next().unwrap() != 6 {
68+
/// println!("Not a 6; rolling again!");
69+
/// }
70+
/// ```
71+
fn sample_iter<R>(self, rng: R) -> DistIter<Self, R, T>
72+
where
73+
R: Rng,
74+
Self: Sized,
75+
{
76+
DistIter {
77+
distr: self,
78+
rng,
79+
phantom: ::core::marker::PhantomData,
80+
}
81+
}
82+
83+
/// Create a distribution of values of 'S' by mapping the output of `Self`
84+
/// through the closure `F`
85+
///
86+
/// # Example
87+
///
88+
/// ```
89+
/// use rand::thread_rng;
90+
/// use rand::distributions::{Distribution, Uniform};
91+
///
92+
/// let mut rng = thread_rng();
93+
///
94+
/// let die = Uniform::new_inclusive(1, 6);
95+
/// let even_number = die.map(|num| num % 2 == 0);
96+
/// while !even_number.sample(&mut rng) {
97+
/// println!("Still odd; rolling again!");
98+
/// }
99+
/// ```
100+
fn map<F, S>(self, func: F) -> DistMap<Self, F, T, S>
101+
where
102+
F: Fn(T) -> S,
103+
Self: Sized,
104+
{
105+
DistMap {
106+
distr: self,
107+
func,
108+
phantom: ::core::marker::PhantomData,
109+
}
110+
}
111+
}
112+
113+
impl<'a, T, D: Distribution<T>> Distribution<T> for &'a D {
114+
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> T {
115+
(*self).sample(rng)
116+
}
117+
}
118+
119+
/// An iterator that generates random values of `T` with distribution `D`,
120+
/// using `R` as the source of randomness.
121+
///
122+
/// This `struct` is created by the [`sample_iter`] method on [`Distribution`].
123+
/// See its documentation for more.
124+
///
125+
/// [`sample_iter`]: Distribution::sample_iter
126+
#[derive(Debug)]
127+
pub struct DistIter<D, R, T> {
128+
distr: D,
129+
rng: R,
130+
phantom: ::core::marker::PhantomData<T>,
131+
}
132+
133+
impl<D, R, T> Iterator for DistIter<D, R, T>
134+
where
135+
D: Distribution<T>,
136+
R: Rng,
137+
{
138+
type Item = T;
139+
140+
#[inline(always)]
141+
fn next(&mut self) -> Option<T> {
142+
// Here, self.rng may be a reference, but we must take &mut anyway.
143+
// Even if sample could take an R: Rng by value, we would need to do this
144+
// since Rng is not copyable and we cannot enforce that this is "reborrowable".
145+
Some(self.distr.sample(&mut self.rng))
146+
}
147+
148+
fn size_hint(&self) -> (usize, Option<usize>) {
149+
(usize::max_value(), None)
150+
}
151+
}
152+
153+
impl<D, R, T> iter::FusedIterator for DistIter<D, R, T>
154+
where
155+
D: Distribution<T>,
156+
R: Rng,
157+
{
158+
}
159+
160+
#[cfg(features = "nightly")]
161+
impl<D, R, T> iter::TrustedLen for DistIter<D, R, T>
162+
where
163+
D: Distribution<T>,
164+
R: Rng,
165+
{
166+
}
167+
168+
/// A distribution of values of type `S` derived from the distribution `D`
169+
/// by mapping its output of type `T` through the closure `F`.
170+
///
171+
/// This `struct` is created by the [`Distribution::map`] method.
172+
/// See its documentation for more.
173+
#[derive(Debug)]
174+
pub struct DistMap<D, F, T, S> {
175+
distr: D,
176+
func: F,
177+
phantom: ::core::marker::PhantomData<fn(T) -> S>,
178+
}
179+
180+
impl<D, F, T, S> Distribution<S> for DistMap<D, F, T, S>
181+
where
182+
D: Distribution<T>,
183+
F: Fn(T) -> S,
184+
{
185+
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> S {
186+
(self.func)(self.distr.sample(rng))
187+
}
188+
}
189+
190+
#[cfg(test)]
191+
mod tests {
192+
use crate::distributions::{Distribution, Uniform};
193+
use crate::Rng;
194+
195+
#[test]
196+
fn test_distributions_iter() {
197+
use crate::distributions::Open01;
198+
let mut rng = crate::test::rng(210);
199+
let distr = Open01;
200+
let mut iter = Distribution::<f32>::sample_iter(distr, &mut rng);
201+
let mut sum: f32 = 0.;
202+
for _ in 0..100 {
203+
sum += iter.next().unwrap();
204+
}
205+
assert!(0. < sum && sum < 100.);
206+
}
207+
208+
#[test]
209+
fn test_distributions_map() {
210+
let dist = Uniform::new_inclusive(0, 5).map(|val| val + 15);
211+
212+
let mut rng = crate::test::rng(212);
213+
let val = dist.sample(&mut rng);
214+
assert!(val >= 15 && val <= 20);
215+
}
216+
217+
#[test]
218+
fn test_make_an_iter() {
219+
fn ten_dice_rolls_other_than_five<R: Rng>(
220+
rng: &mut R,
221+
) -> impl Iterator<Item = i32> + '_ {
222+
Uniform::new_inclusive(1, 6)
223+
.sample_iter(rng)
224+
.filter(|x| *x != 5)
225+
.take(10)
226+
}
227+
228+
let mut rng = crate::test::rng(211);
229+
let mut count = 0;
230+
for val in ten_dice_rolls_other_than_five(&mut rng) {
231+
assert!((1..=6).contains(&val) && val != 5);
232+
count += 1;
233+
}
234+
assert_eq!(count, 10);
235+
}
236+
}

0 commit comments

Comments
 (0)