| //! Random value generation. |
| |
| use crate::range::{RangeFull, RangeInclusive}; |
| |
| /// A source of randomness. |
| #[unstable(feature = "random", issue = "130703")] |
| pub trait Rng { |
| /// Fills `bytes` with random bytes. |
| /// |
| /// Note that calling `fill_bytes` multiple times is not equivalent to calling `fill_bytes` once |
| /// with a larger buffer. An `Rng` is allowed to return different bytes for those two cases. For |
| /// instance, this allows an `Rng` to generate a word at a time and throw part of it away if not |
| /// needed. |
| fn fill_bytes(&mut self, bytes: &mut [u8]); |
| } |
| |
| /// Implements `Rng` for mutable references to random number generators by |
| /// forwarding all methods to the referenced generator. |
| #[unstable(feature = "random", issue = "130703")] |
| impl<'a, R: Rng + ?Sized> Rng for &'a mut R { |
| fn fill_bytes(&mut self, bytes: &mut [u8]) { |
| R::fill_bytes(self, bytes); |
| } |
| } |
| |
| /// A trait representing a distribution of random values for a type. |
| #[unstable(feature = "random", issue = "130703")] |
| pub trait Distribution<T> { |
| /// Samples a random value from the distribution, using the specified random source. |
| fn sample(&self, source: &mut (impl Rng + ?Sized)) -> T; |
| } |
| |
| impl<T, DT: Distribution<T>> Distribution<T> for &DT { |
| fn sample(&self, source: &mut (impl Rng + ?Sized)) -> T { |
| (*self).sample(source) |
| } |
| } |
| |
| impl Distribution<bool> for RangeFull { |
| fn sample(&self, source: &mut (impl Rng + ?Sized)) -> bool { |
| let byte: u8 = RangeFull.sample(source); |
| byte & 1 == 1 |
| } |
| } |
| |
| macro_rules! impl_full { |
| ($t:ty) => { |
| impl Distribution<$t> for RangeFull { |
| fn sample(&self, source: &mut (impl Rng + ?Sized)) -> $t { |
| let mut bytes = (0 as $t).to_ne_bytes(); |
| source.fill_bytes(&mut bytes); |
| // Always use little-endian for reproducibility. Since the vast majority of code is |
| // mainly or exclusively tested on LE targets, giving different PRNG results for the |
| // same seed on BE targets is a serious portability hazard. |
| <$t>::from_le_bytes(bytes) |
| } |
| } |
| }; |
| } |
| |
| impl_full!(u8); |
| impl_full!(i8); |
| impl_full!(u16); |
| impl_full!(i16); |
| impl_full!(u32); |
| impl_full!(i32); |
| impl_full!(u64); |
| impl_full!(i64); |
| impl_full!(u128); |
| impl_full!(i128); |
| impl_full!(usize); |
| impl_full!(isize); |
| |
| #[cold] |
| fn empty_range() -> ! { |
| panic!("cannot sample from an empty distribution") |
| } |
| |
| macro_rules! lemire_sample { |
| ($name:ident($ty:ty)) => { |
| // Unbiased uniform sampling of a number within the range [0, bound). |
| // |
| // By performing some clever modular arithmetic, this algorithm manages |
| // to both reduce divisions and minimize the chance of sample rejections. |
| // |
| // Algorithm from: |
| // spellchecker:off |
| // Daniel Lemire. 2019. Fast Random Integer Generation in an Interval. |
| // ACM Trans. Model. Comput. Simul. 29, 1, Article 3 (January 2019), 12 pages. |
| // https://doi.org/10.1145/3230636 |
| // spellchecker:on |
| fn $name(bound: $ty, source: &mut (impl Rng + ?Sized)) -> $ty { |
| debug_assert_ne!(bound, 0); |
| |
| let sample: $ty = (..).sample(source); |
| |
| let (mut l, mut res) = sample.carrying_mul(bound, 0); |
| if l < bound { |
| let t = bound.wrapping_neg() % bound; |
| while l < t { |
| let sample: $ty = (..).sample(source); |
| (l, res) = sample.carrying_mul(bound, 0); |
| } |
| } |
| |
| debug_assert!(res < bound); |
| res |
| } |
| }; |
| } |
| |
| lemire_sample!(bounded32(u32)); |
| lemire_sample!(bounded64(u64)); |
| lemire_sample!(bounded128(u128)); |
| |
| macro_rules! impl_range { |
| ($unsigned:ty, $signed:ty as $base:ty => $bounded:ident) => { |
| impl Distribution<$unsigned> for RangeInclusive<$unsigned> { |
| /// Chooses a random number within the range. |
| /// |
| /// Every possible result value is equally likely. In other words, |
| /// this operation uses unbiased uniform sampling. |
| /// |
| /// # Panics |
| /// |
| /// Panics if the range is empty. |
| /// |
| /// # Side-channels |
| /// |
| /// This implementation does not claim to be resistant against side- |
| /// channel attacks. In particular, the execution time of this operation |
| /// may leak information about the returned value, and not just the |
| /// values of the range bounds. While this implementation tries to |
| /// avoid operations with particularly data-dependent timing (such |
| /// as divisions), Rust as a language has no facilities for ensuring |
| /// data-independent timing, voiding all promises about side-channel- |
| /// freedom. |
| /// |
| /// # Examples |
| /// |
| /// A D20 dice roll: |
| /// ``` |
| /// #![feature(random)] |
| /// |
| /// use std::random::{Distribution, SystemRng}; |
| /// use std::range::RangeInclusive; |
| /// |
| /// let roll = RangeInclusive::from(1..=20).sample(&mut SystemRng); |
| /// assert!(1 <= roll && roll <= 20); |
| /// if roll == 20 { |
| /// println!("Wow! You achieve writing a sound linked list."); |
| /// } else { |
| /// println!("Miri attacks!"); |
| /// } |
| /// ``` |
| #[inline] |
| fn sample(&self, source: &mut (impl Rng + ?Sized)) -> $unsigned { |
| if self.start > self.last { |
| empty_range(); |
| } |
| |
| if self.start == self.last { |
| return self.start; |
| } |
| |
| let Some(bound) = (self.last - self.start).checked_add(1) else { |
| // Overflow can only occur for Self::MIN..=Self::MAX, meaning |
| // the range is effectively unbounded. |
| return RangeFull.sample(source); |
| }; |
| |
| let offset = if bound.is_power_of_two() { |
| let sample: $unsigned = RangeFull.sample(source); |
| sample & (bound - 1) |
| } else { |
| $bounded(bound as $base, source) as $unsigned |
| }; |
| |
| self.start + offset |
| } |
| } |
| |
| impl Distribution<$signed> for RangeInclusive<$signed> { |
| /// Chooses a random number within the range. |
| /// |
| /// Every possible result value is equally likely. In other words, |
| /// this operation uses unbiased uniform sampling. |
| /// |
| /// # Panics |
| /// |
| /// Panics if the range is empty. |
| /// |
| /// # Side-channels |
| /// |
| /// This implementation does not claim to be resistant against side- |
| /// channel attacks. In particular, the execution time of this operation |
| /// may leak information about the returned value, and not just the |
| /// values of the range bounds. While this implementation tries to |
| /// avoid operations with particularly data-dependent timing (such |
| /// as divisions), Rust as a language has no facilities for ensuring |
| /// data-independent timing, voiding all promises about side-channel- |
| /// freedom. |
| /// |
| /// # Examples |
| /// |
| /// A D20 dice roll: |
| /// ``` |
| /// #![feature(random)] |
| /// |
| /// use std::random::{Distribution, SystemRng}; |
| /// use std::range::RangeInclusive; |
| /// |
| /// let roll = RangeInclusive::from(1..=20).sample(&mut SystemRng); |
| /// assert!(1 <= roll && roll <= 20); |
| /// if roll == 20 { |
| /// println!("Wow! You achieve writing a sound linked list."); |
| /// } else { |
| /// println!("Miri attacks!"); |
| /// } |
| /// ``` |
| #[inline] |
| fn sample(&self, source: &mut (impl Rng + ?Sized)) -> $signed { |
| if self.start > self.last { |
| empty_range(); |
| } |
| |
| if self.start == self.last { |
| return self.start; |
| } |
| |
| let Some(bound) = self.last.wrapping_sub(self.start).cast_unsigned().checked_add(1) |
| else { |
| // Overflow can only occur for Self::MIN..=Self::MAX, meaning |
| // the range is effectively unbounded. |
| return RangeFull.sample(source); |
| }; |
| |
| let offset = if bound.is_power_of_two() { |
| let sample: $unsigned = RangeFull.sample(source); |
| sample & (bound - 1) |
| } else { |
| $bounded(bound as $base, source) as $unsigned |
| }; |
| |
| self.start.wrapping_add_unsigned(offset) |
| } |
| } |
| }; |
| } |
| |
| // Use 32-bit integers for small integers since it reduces the likelihood of |
| // sample rejections. |
| impl_range!(u8, i8 as u32 => bounded32); |
| impl_range!(u16, i16 as u32 => bounded32); |
| |
| impl_range!(u32, i32 as u32 => bounded32); |
| impl_range!(u64, i64 as u64 => bounded64); |
| impl_range!(u128, i128 as u128 => bounded128); |
| #[cfg(any(target_pointer_width = "16", target_pointer_width = "32",))] |
| impl_range!(usize, isize as u32 => bounded32); |
| #[cfg(target_pointer_width = "64")] |
| impl_range!(usize, isize as u64 => bounded64); |