1// Copyright 2021 Developers of the Rand project.
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
89//! Distributions over slices
1011use core::num::NonZeroUsize;
1213use crate::Rng;
14use crate::distr::Distribution;
15use crate::distr::uniform::{UniformSampler, UniformUsize};
16#[cfg(feature = "alloc")]
17use alloc::string::String;
1819/// A distribution to uniformly sample elements of a slice
20///
21/// Like [`IndexedRandom::choose`], this uniformly samples elements of a slice
22/// without modification of the slice (so called "sampling with replacement").
23/// This distribution object may be a little faster for repeated sampling (but
24/// slower for small numbers of samples).
25///
26/// ## Examples
27///
28/// Since this is a distribution, [`Rng::sample_iter`] and
29/// [`Distribution::sample_iter`] may be used, for example:
30/// ```
31/// use rand::distr::{Distribution, slice::Choose};
32///
33/// let vowels = ['a', 'e', 'i', 'o', 'u'];
34/// let vowels_dist = Choose::new(&vowels).unwrap();
35///
36/// // build a string of 10 vowels
37/// let vowel_string: String = vowels_dist
38/// .sample_iter(&mut rand::rng())
39/// .take(10)
40/// .collect();
41///
42/// println!("{}", vowel_string);
43/// assert_eq!(vowel_string.len(), 10);
44/// assert!(vowel_string.chars().all(|c| vowels.contains(&c)));
45/// ```
46///
47/// For a single sample, [`IndexedRandom::choose`] may be preferred:
48/// ```
49/// use rand::seq::IndexedRandom;
50///
51/// let vowels = ['a', 'e', 'i', 'o', 'u'];
52/// let mut rng = rand::rng();
53///
54/// println!("{}", vowels.choose(&mut rng).unwrap());
55/// ```
56///
57/// [`IndexedRandom::choose`]: crate::seq::IndexedRandom::choose
58/// [`Rng::sample_iter`]: crate::RngExt::sample_iter
59#[derive(#[automatically_derived]
impl<'a, T: ::core::fmt::Debug> ::core::fmt::Debug for Choose<'a, T> {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field3_finish(f, "Choose",
"slice", &self.slice, "range", &self.range, "num_choices",
&&self.num_choices)
}
}Debug, #[automatically_derived]
impl<'a, T: ::core::clone::Clone> ::core::clone::Clone for Choose<'a, T> {
#[inline]
fn clone(&self) -> Choose<'a, T> {
Choose {
slice: ::core::clone::Clone::clone(&self.slice),
range: ::core::clone::Clone::clone(&self.range),
num_choices: ::core::clone::Clone::clone(&self.num_choices),
}
}
}Clone, #[automatically_derived]
impl<'a, T: ::core::marker::Copy> ::core::marker::Copy for Choose<'a, T> { }Copy)]
60pub struct Choose<'a, T> {
61 slice: &'a [T],
62 range: UniformUsize,
63 num_choices: NonZeroUsize,
64}
6566impl<'a, T> Choose<'a, T> {
67/// Create a new `Choose` instance which samples uniformly from the slice.
68 ///
69 /// Returns error [`Empty`] if the slice is empty.
70pub fn new(slice: &'a [T]) -> Result<Self, Empty> {
71let num_choices = NonZeroUsize::new(slice.len()).ok_or(Empty)?;
7273Ok(Self {
74slice,
75 range: UniformUsize::new(0, num_choices.get()).unwrap(),
76num_choices,
77 })
78 }
7980/// Returns the count of choices in this distribution
81pub fn num_choices(&self) -> NonZeroUsize {
82self.num_choices
83 }
84}
8586impl<'a, T> Distribution<&'a T> for Choose<'a, T> {
87fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> &'a T {
88let idx = self.range.sample(rng);
8990if true {
if !(idx < self.slice.len()) {
{
::core::panicking::panic_fmt(format_args!("Uniform::new(0, {0}) somehow returned {1}",
self.slice.len(), idx));
}
};
};debug_assert!(
91 idx < self.slice.len(),
92"Uniform::new(0, {}) somehow returned {}",
93self.slice.len(),
94 idx
95 );
9697// Safety: at construction time, it was ensured that the slice was
98 // non-empty, and that the `Uniform` range produces values in range
99 // for the slice
100unsafe { self.slice.get_unchecked(idx) }
101 }
102}
103104/// Error: empty slice
105///
106/// This error is returned when [`Choose::new`] is given an empty slice.
107#[derive(#[automatically_derived]
impl ::core::fmt::Debug for Empty {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::write_str(f, "Empty")
}
}Debug, #[automatically_derived]
impl ::core::clone::Clone for Empty {
#[inline]
fn clone(&self) -> Empty { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for Empty { }Copy)]
108pub struct Empty;
109110impl core::fmt::Displayfor Empty {
111fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
112f.write_fmt(format_args!("Tried to create a `rand::distr::slice::Choose` with an empty slice"))write!(
113f,
114"Tried to create a `rand::distr::slice::Choose` with an empty slice"
115)116 }
117}
118119impl core::error::Errorfor Empty {}
120121#[cfg(feature = "alloc")]
122impl super::SampleString for Choose<'_, char> {
123fn append_string<R: Rng + ?Sized>(&self, rng: &mut R, string: &mut String, len: usize) {
124// Get the max char length to minimize extra space.
125 // Limit this check to avoid searching for long slice.
126let max_char_len = if self.slice.len() < 200 {
127self.slice
128 .iter()
129 .try_fold(1, |max_len, char| {
130// When the current max_len is 4, the result max_char_len will be 4.
131Some(max_len.max(char.len_utf8())).filter(|len| *len < 4)
132 })
133 .unwrap_or(4)
134 } else {
1354
136};
137138// Split the extension of string to reuse the unused capacities.
139 // Skip the split for small length or only ascii slice.
140let mut extend_len = if max_char_len == 1 || len < 100 {
141 len
142 } else {
143 len / 4
144};
145let mut remain_len = len;
146while extend_len > 0 {
147 string.reserve(max_char_len * extend_len);
148 string.extend(self.sample_iter(&mut *rng).take(extend_len));
149 remain_len -= extend_len;
150 extend_len = extend_len.min(remain_len);
151 }
152 }
153}
154155#[cfg(test)]
156mod test {
157use super::*;
158use core::iter;
159160#[test]
161fn value_stability() {
162let rng = crate::test::rng(651);
163let slice = Choose::new(b"escaped emus explore extensively").unwrap();
164let expected = b"eaxee";
165assert!(iter::zip(slice.sample_iter(rng), expected).all(|(a, b)| a == b));
166 }
167}