1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
// Copyright © 2020 Alexandra Frydl
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

//! Utilities for generating randomness.

use crate::prelude::*;
use parking_lot::Mutex;
use rand::distributions::uniform::{SampleRange, SampleUniform};
use rand::distributions::{self, Distribution};
use rand::seq::SliceRandom;
use rand::{Rng as _, SeedableRng as _};
use rand_xoshiro::Xoshiro256StarStar;
use std::cell::RefCell;

/// Returns `true` with a given probability.
///
/// The `probability` is the chance from `0.0` (never) to `1.0` (always) that
/// this function returns `true`.
pub fn chance(probability: f64) -> bool {
  THREAD_RNG.with(|rng| rng.borrow_mut().gen_chance(probability))
}

/// Fills a slice with random bytes.
pub fn fill_bytes(bytes: &mut [u8]) {
  THREAD_RNG.with(|rng| rng.borrow_mut().fill_bytes(bytes))
}

/// Generates a random value.
pub fn random<T: Random>() -> T {
  THREAD_RNG.with(|rng| T::random_with(&mut rng.borrow_mut()))
}

/// Generates a random number within a range.
pub fn range<T: SampleUniform>(range: impl SampleRange<T>) -> T {
  THREAD_RNG.with(|rng| rng.borrow_mut().gen_range(range))
}

/// Returns `true` with a probability expressed by the ratio between two given
/// numbers.
pub fn ratio<T: Number + SampleUniform>(numerator: T, denominator: T) -> bool {
  THREAD_RNG.with(|rng| rng.borrow_mut().gen_ratio(numerator, denominator))
}

/// Randomly shuffles a slice in place.
pub fn shuffle<T>(slice: &mut [T]) {
  THREAD_RNG.with(|rng| rng.borrow_mut().shuffle(slice))
}

/// A trait for types that can be created randomly.
pub trait Random: Sized {
  /// Returns a random value using the given `Rng`.
  fn random_with(rng: &mut Rng) -> Self;

  /// Returns a random value.
  fn random() -> Self {
    random()
  }
}

// Implement `Random` for all types that can be used with `rng.gen()`.

impl<T> Random for T
where
  distributions::Standard: Distribution<T>,
{
  fn random_with(rng: &mut Rng) -> Self {
    rng.inner.gen()
  }
}

/// A random number generator.
#[derive(Clone)]
pub struct Rng {
  inner: Xoshiro256StarStar,
}

/// The global RNG to use to create thread-local RNGs.
static GLOBAL_RNG: Lazy<Mutex<Rng>> =
  Lazy::new(|| Mutex::new(Rng { inner: Xoshiro256StarStar::from_entropy() }));

thread_local! {
  /// The thread-local RNG.
  static THREAD_RNG: RefCell<Rng> = {
    let mut global_rng = GLOBAL_RNG.lock();
    let thread_rng = global_rng.clone();

    global_rng.inner.long_jump();

    RefCell::new(thread_rng)
  };
}

impl Rng {
  /// Creates a new `Rng` with a random seed.
  pub fn new() -> Rng {
    THREAD_RNG.with(|rng| {
      let mut thread_rng = rng.borrow_mut();
      let local_rng = thread_rng.clone();

      thread_rng.inner.jump();

      local_rng
    })
  }

  /// Fills a slice with random bytes.
  pub fn fill_bytes(&mut self, bytes: &mut [u8]) {
    self.inner.fill(bytes);
  }

  /// Generates a random value.
  pub fn gen<T: Random>(&mut self) -> T {
    T::random_with(self)
  }

  /// Returns `true` with a given probability.
  ///
  /// The probability is the chance from `0.0` (never) to `1.0` (always) that
  /// this function returns `true`.
  pub fn gen_chance(&mut self, probability: f64) -> bool {
    probability > self.gen()
  }

  /// Generates a random number within a given range.
  pub fn gen_range<T: SampleUniform>(&mut self, range: impl SampleRange<T>) -> T {
    self.inner.gen_range(range)
  }

  /// Returns `true` with a probability expressed by the ratio between two given
  /// numbers.
  pub fn gen_ratio<T: Number + SampleUniform>(&mut self, numerator: T, denominator: T) -> bool {
    debug_assert!(denominator > T::zero(), "The denominator of a ratio must be greater than zero.");

    numerator > self.gen_range(T::zero()..denominator)
  }

  /// Randomly shuffles a slice in place.
  pub fn shuffle<T>(&mut self, slice: &mut [T]) {
    slice.shuffle(&mut self.inner);
  }
}

impl Default for Rng {
  fn default() -> Self {
    Self::new()
  }
}