weird_data/
global_functions.rs

1//! A global, thread-local [Wdg] instance.
2
3use fastrand as fr;
4use paste::paste;
5
6use crate::Wdg;
7
8use std::cell::Cell;
9
10// clippy is not aware that deriving Default is only possible when no std
11// because Rng does not implement in no std either
12#[allow(clippy::derivable_impls)]
13impl Default for Wdg {
14    fn default() -> Self {
15        Self(fr::Rng::default())
16    }
17}
18
19impl Wdg {
20    /// Create a new Wdg by forking the global Wdg.
21    ///
22    /// If you want to control the initial seed, use [with_seed] instead.
23    pub fn new() -> Self {
24        try_with_wdg(Wdg::fork).unwrap_or_else(|_| Wdg::with_seed(0x0d_6a_b0_f1_c7_ff_b9_1b))
25    }
26}
27
28thread_local! {
29    /// Likely to be truly random, using system provided entropy. It may be
30    /// based on a default seed if the system entropy isn't available.
31    static GLOBAL_WDG: Cell<Wdg> = Cell::new(Wdg(fr::Rng::new()));
32}
33
34/// Run an operation with the current thread-local generator.
35fn with_wdg<R>(f: impl FnOnce(&mut Wdg) -> R) -> R {
36    GLOBAL_WDG.with(|wdg| {
37        let current = wdg.replace(Wdg::with_seed(0));
38        let mut restore = RestoreOnDrop { wdg, current };
39        f(&mut restore.current)
40    })
41}
42
43/// Try to run an operation with the current thread-local generator.
44fn try_with_wdg<R>(f: impl FnOnce(&mut Wdg) -> R) -> Result<R, std::thread::AccessError> {
45    GLOBAL_WDG.try_with(|wdg| {
46        let current = wdg.replace(Wdg::with_seed(0));
47        let mut restore = RestoreOnDrop { wdg, current };
48        f(&mut restore.current)
49    })
50}
51
52/// Make sure the original WDG is restored even on panic.
53struct RestoreOnDrop<'a> {
54    wdg: &'a Cell<Wdg>,
55    current: Wdg,
56}
57
58impl Drop for RestoreOnDrop<'_> {
59    fn drop(&mut self) {
60        self.wdg.set(Wdg(self.current.0.clone()));
61    }
62}
63
64/// Initialize the thread-local generator with the given seed.
65pub fn seed(seed: u64) {
66    with_wdg(|wdg| wdg.seed(seed));
67}
68
69/// Gives back the _current_ seed that is being held by the thread-local generator.
70pub fn get_seed() -> u64 {
71    with_wdg(|wdg| wdg.get_seed())
72}
73
74// Generates a random f32 `NAN` value.
75///
76/// There are multiple bit patterns that are equivalent to a `NAN`.
77/// This generator covers all possible `NAN` values as specified in
78/// IEEE-754, even ones that Rust would normally not generate.
79pub fn nan_f32() -> f32 {
80    with_wdg(|wdg| wdg.nan_f32())
81}
82
83/// Generates a random f64 `NAN` value.
84///
85/// There are multiple bit patterns that are equivalent to a `NAN`.
86/// This generator covers all possible `NAN` values as specified in
87/// IEEE-754, even ones that Rust would normally not generate.
88pub fn nan_f64() -> f64 {
89    with_wdg(|wdg| wdg.nan_f64())
90}
91
92/// Generates a random f32 denormal value.
93///
94/// This generator covers all possible denormal values as specified in
95/// IEEE-754.
96pub fn subnormal_f32() -> f32 {
97    with_wdg(|wdg| wdg.subnormal_f32())
98}
99
100/// Generates a random f64 denormal value.
101///
102/// This generator covers all possible denormal values as specified in
103/// IEEE-754.
104pub fn subnormal_f64() -> f64 {
105    with_wdg(|wdg| wdg.subnormal_f64())
106}
107
108/// Generate a random f32 normal value
109pub fn normal_f32() -> f32 {
110    with_wdg(|wdg| wdg.normal_f32())
111}
112
113/// Generate a random f64 normal value
114pub fn normal_f64() -> f64 {
115    with_wdg(|wdg| wdg.normal_f64())
116}
117
118/// Generate a random f32 "special" value
119///
120/// A special value is what I call specific float values that are unique and
121/// are pretty much impossible to generate by chance, and have some unusual
122/// properties.
123pub fn special_f32() -> f32 {
124    with_wdg(|wdg| wdg.special_f32())
125}
126
127/// Generate a random f64 "special" value
128///
129/// A special value is what I call specific float values that are unique and
130/// are pretty much impossible to generate by chance, and have some unusual
131/// properties.
132pub fn special_f64() -> f64 {
133    with_wdg(|wdg| wdg.special_f64())
134}
135
136/// Generate a random f32, such that special or problematic values are much
137/// more common than normal.
138///
139/// The distribution is not statistically useful, but it ensures that all edge-case
140/// values get a fair chance of being generated. This is better than using a regular
141/// random number generator, because in the vast majority of cases, a random number
142/// generator will generate perfectly regular and well-behaved values, and certain
143/// values, like `INFINITY` and `NAN` may be impossible to generate.
144///
145/// The distribution is as follows:
146/// - 25% normal values
147/// - 25% subnormal values
148/// - 25% `NAN` values, including all possible payloads, quiet and signaling `NAN`.
149/// - 25% "special" values, i.e. unique values with special properties such as `INFINITY` and `-0.0`
150pub fn f32() -> f32 {
151    with_wdg(|wdg| wdg.f32())
152}
153
154/// Generate a random f64, such that special or problematic values are much
155/// more common than normal.
156///
157/// The distribution is not statistically useful, but it ensures that all edge-case
158/// values get a fair chance of being generated. This is better than using a regular
159/// random number generator, because in the vast majority of cases, a random number
160/// generator will generate perfectly regular and well-behaved values, and certain
161/// values, like `INFINITY` and `NAN` may be impossible to generate.
162///
163/// The distribution is as follows:
164/// - 25% normal values
165/// - 25% subnormal values
166/// - 25% `NAN` values, including all possible payloads, quiet and signaling `NAN`.
167/// - 25% "special" values, i.e. unique values with special properties such as `INFINITY` and `-0.0`
168pub fn f64() -> f64 {
169    with_wdg(|wdg| wdg.f64())
170}
171
172macro_rules! int_uint {
173    ($($t:ty),+ $(,)?) => {
174        $(
175            int_uint_inner!($t);
176        )+
177    };
178}
179
180macro_rules! int_uint_inner {
181    ($t:ty) => {
182        paste! {
183            /// Generate a random
184            #[doc = stringify!($t)]
185            /// "special" value
186            ///
187            /// A special value is what I call specific values that are unique and
188            /// are pretty much impossible to generate by chance, and have some unusual
189            /// properties. For instance `MAX` and 0.
190            pub fn [<special_ $t>]() -> $t {
191                with_wdg(|wdg| wdg.[<special_ $t>]())
192            }
193
194            /// Generate a random
195            #[doc = stringify!($t)]
196            /// , such that special or problematic values are much
197            /// more common than normal.
198            pub fn $t() -> $t {
199                with_wdg(|wdg| wdg.$t())
200            }
201        }
202    };
203}
204
205int_uint!(u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize);