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 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
//! A global, thread-local [Wdg] instance.
use fastrand as fr;
use crate::Wdg;
use std::cell::Cell;
// clippy is not aware that deriving Default is only possible when no std
// because Rng does not implement in no std either
#[allow(clippy::derivable_impls)]
impl Default for Wdg {
fn default() -> Self {
Self(fr::Rng::default())
}
}
impl Wdg {
/// Create a new Wdg by forking the global Wdg.
///
/// If you want to control the initial seed, use [with_seed] instead.
pub fn new() -> Self {
try_with_wdg(Wdg::fork).unwrap_or_else(|_| Wdg::with_seed(0x0d_6a_b0_f1_c7_ff_b9_1b))
}
}
thread_local! {
/// Likely to be truly random, using system provided entropy. It may be
/// based on a default seed if the system entropy isn't available.
static GLOBAL_WDG: Cell<Wdg> = Cell::new(Wdg(fr::Rng::new()));
}
/// Run an operation with the current thread-local generator.
fn with_wdg<R>(f: impl FnOnce(&mut Wdg) -> R) -> R {
GLOBAL_WDG.with(|wdg| {
let current = wdg.replace(Wdg::with_seed(0));
let mut restore = RestoreOnDrop { wdg, current };
f(&mut restore.current)
})
}
/// Try to run an operation with the current thread-local generator.
fn try_with_wdg<R>(f: impl FnOnce(&mut Wdg) -> R) -> Result<R, std::thread::AccessError> {
GLOBAL_WDG.try_with(|wdg| {
let current = wdg.replace(Wdg::with_seed(0));
let mut restore = RestoreOnDrop { wdg, current };
f(&mut restore.current)
})
}
/// Make sure the original WDG is restored even on panic.
struct RestoreOnDrop<'a> {
wdg: &'a Cell<Wdg>,
current: Wdg,
}
impl Drop for RestoreOnDrop<'_> {
fn drop(&mut self) {
self.wdg.set(Wdg(self.current.0.clone()));
}
}
/// Initialize the thread-local generator with the given seed.
pub fn seed(seed: u64) {
with_wdg(|wdg| wdg.seed(seed));
}
/// Gives back the _current_ seed that is being held by the thread-local generator.
pub fn get_seed() -> u64 {
with_wdg(|wdg| wdg.get_seed())
}
// Generates a random f32 `NAN` value.
///
/// There are multiple bit patterns that are equivalent to a `NAN`.
/// This generator covers all possible `NAN` values as specified in
/// IEEE-754, even ones that Rust would normally not generate.
pub fn nan_f32() -> f32 {
with_wdg(|wdg| wdg.nan_f32())
}
/// Generates a random f64 `NAN` value.
///
/// There are multiple bit patterns that are equivalent to a `NAN`.
/// This generator covers all possible `NAN` values as specified in
/// IEEE-754, even ones that Rust would normally not generate.
pub fn nan_f64() -> f64 {
with_wdg(|wdg| wdg.nan_f64())
}
/// Generates a random f32 denormal value.
///
/// This generator covers all possible denormal values as specified in
/// IEEE-754.
pub fn subnormal_f32() -> f32 {
with_wdg(|wdg| wdg.subnormal_f32())
}
/// Generates a random f64 denormal value.
///
/// This generator covers all possible denormal values as specified in
/// IEEE-754.
pub fn subnormal_f64() -> f64 {
with_wdg(|wdg| wdg.subnormal_f64())
}
/// Generate a random f32 normal value
pub fn normal_f32() -> f32 {
with_wdg(|wdg| wdg.normal_f32())
}
/// Generate a random f64 normal value
pub fn normal_f64() -> f64 {
with_wdg(|wdg| wdg.normal_f64())
}
/// Generate a random f32 "special" value
///
/// A special value is what I call specific float values that are unique and
/// are pretty much impossible to generate by chance, and have some unusual
/// properties.
pub fn special_f32() -> f32 {
with_wdg(|wdg| wdg.special_f32())
}
/// Generate a random f64 "special" value
///
/// A special value is what I call specific float values that are unique and
/// are pretty much impossible to generate by chance, and have some unusual
/// properties.
pub fn special_f64() -> f64 {
with_wdg(|wdg| wdg.special_f64())
}
/// Generate a random f32, such that special or problematic values are much
/// more common than normal.
///
/// The distribution is not statistically useful, but it ensures that all edge-case
/// values get a fair chance of being generated. This is better than using a regular
/// random number generator, because in the vast majority of cases, a random number
/// generator will generate perfectly regular and well-behaved values, and certain
/// values, like `INFINITY` and `NAN` may be impossible to generate.
///
/// The distribution is as follows:
/// - 25% normal values
/// - 25% subnormal values
/// - 25% `NAN` values, including all possible payloads, quiet and signaling `NAN`.
/// - 25% "special" values, i.e. unique values with special properties such as `INFINITY` and `-0.0`
pub fn f32() -> f32 {
with_wdg(|wdg| wdg.f32())
}
/// Generate a random f64, such that special or problematic values are much
/// more common than normal.
///
/// The distribution is not statistically useful, but it ensures that all edge-case
/// values get a fair chance of being generated. This is better than using a regular
/// random number generator, because in the vast majority of cases, a random number
/// generator will generate perfectly regular and well-behaved values, and certain
/// values, like `INFINITY` and `NAN` may be impossible to generate.
///
/// The distribution is as follows:
/// - 25% normal values
/// - 25% subnormal values
/// - 25% `NAN` values, including all possible payloads, quiet and signaling `NAN`.
/// - 25% "special" values, i.e. unique values with special properties such as `INFINITY` and `-0.0`
pub fn f64() -> f64 {
with_wdg(|wdg| wdg.f64())
}