blob: d609d25cff51f6d4a714dac2679aa882f9461b92 [file]
//! 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);