tiny_rng/
lib.rs

1
2/*! ## Tiny RNG, a minimal random number generator
3Warning: Not cryptographically secure.
4
5Proven mathematical methods are applied to obtain unbiased samples.
6Specifically, rejection sampling is applied to obtain samples from
7the uniform distribution on an integer range and Fisher–Yates shuffle is
8applied to obtain a random permutation from the uniform distribution
9on the set of all permutations.
10
11```
12use tiny_rng::{Rng, Rand};
13
14let mut rng = Rng::from_seed(0);
15
16// Throw a dice:
17println!("{}", rng.rand_range_u32(1, 7));
18
19// Choose a random color:
20let colors = ["red", "green", "blue"];
21println!("{}", rng.choice(&colors));
22
23// Shuffle an array:
24let mut a = [1, 2, 3, 4];
25rng.shuffle(&mut a);
26println!("{:?}", a);
27```
28*/
29
30#![cfg_attr(not(test), no_std)]
31
32#[cfg(feature = "std")]
33extern crate std;
34
35fn wrapping_next_power_of_two_u32(x: u32) -> u32 {
36    const H: u32 = 1 << 31;
37    if x <= H {x.next_power_of_two()} else {0}
38}
39
40fn wrapping_next_power_of_two_u64(x: u64) -> u64 {
41    const H: u64 = 1 << 63;
42    if x <= H {x.next_power_of_two()} else {0}
43}
44
45/** Provided utilities.
46
47This interface permits to hide the used engine:
48```
49# use tiny_rng::{Rng, Rand};
50fn rng_from_seed(seed: u64) -> impl Rand {
51    Rng::from_seed(seed)
52}
53```
54*/
55pub trait Rand: Sized {
56    /// To obtain reproducible results.
57    /// If feature `std` is enabled, `from_time()` may be used instead,
58    /// to use system time as microseconds as a seed. Apply
59    /// `from_seed(random_seed())` to establish a faster local RNG
60    /// from a global seed RNG.
61    fn from_seed(seed: u64) -> Self;
62
63    #[cfg(feature = "std")]
64    /// Use system time as micro seconds as the seed.
65    /// Needs feature `std` to be enabled.
66    fn from_time() -> Self {
67        use std::time::SystemTime;
68        let time_seed = SystemTime::now()
69            .duration_since(SystemTime::UNIX_EPOCH)
70            .unwrap_or_else(|e| e.duration()).as_micros() as u64;
71        Self::from_seed(time_seed)
72    }
73
74    /// A sample from the uniform distribution on `0..=u32::MAX`.
75    fn rand_u32(&mut self) -> u32;
76
77    /// A sample from the uniform distribution on `0..=u8::MAX`.
78    #[inline]
79    fn rand_u8(&mut self) -> u8 {
80        self.rand_u32() as u8
81    }
82
83    /// A sample from the uniform distribution on `0..=u16::MAX`.
84    #[inline]
85    fn rand_u16(&mut self) -> u16 {
86        self.rand_u32() as u16
87    }
88
89    /// A sample from the uniform distribution on `0..=u64::MAX`.
90    #[inline]
91    fn rand_u64(&mut self) -> u64 {
92        (self.rand_u32() as u64) << 32 | (self.rand_u32() as u64)
93    }
94
95    /// A sample from the uniform distribution on `0..=usize::MAX`.
96    #[cfg(target_pointer_width = "32")]
97    #[inline]
98    fn rand_usize(&mut self) -> usize {
99        self.rand_u32() as usize
100    }
101    
102    /// A sample from the uniform distribution on `0..=usize::MAX`.
103    #[cfg(target_pointer_width = "64")]
104    #[inline]
105    fn rand_usize(&mut self) -> usize {
106        self.rand_u64() as usize
107    }
108
109    /// A sample from the uniform distribution on `0..m`.
110    // Applies the idea of rejection sampling.
111    fn rand_bounded_u32(&mut self, m: u32) -> u32 {
112        let mask = wrapping_next_power_of_two_u32(m).wrapping_sub(1);
113        loop {
114            let x = mask & self.rand_u32();
115            if x < m {return x;}
116        }
117    }
118
119    /// A sample from the uniform distribution on `0..m`.
120    fn rand_bounded_u64(&mut self, m: u64) -> u64 {
121        let mask = wrapping_next_power_of_two_u64(m).wrapping_sub(1);
122        loop {
123            let x = mask & self.rand_u64();
124            if x < m {return x;}
125        }
126    }
127
128    /// A sample from the uniform distribution on `0..m`.
129    #[cfg(target_pointer_width = "32")]
130    fn rand_bounded_usize(&mut self, m: usize) -> usize {
131        self.rand_bounded_u32(m as u32) as usize
132    }
133
134    /// A sample from the uniform distribution on `0..m`.
135    #[cfg(target_pointer_width = "64")]
136    fn rand_bounded_usize(&mut self, m: usize) -> usize {
137        self.rand_bounded_u64(m as u64) as usize
138    }
139
140    /// A sample from the uniform distribution on `a..b`.
141    fn rand_range_u32(&mut self, a: u32, b: u32) -> u32 {
142       a + self.rand_bounded_u32(b - a)
143    }
144    
145    /// A sample from the uniform distribution on `a..b`.
146    fn rand_range_u64(&mut self, a: u64, b: u64) -> u64 {
147       a + self.rand_bounded_u64(b - a)
148    }
149
150    /// A sample from the uniform distribution on `a..b`.
151    fn rand_range_i32(&mut self, a: i32, b: i32) -> i32 {
152        a + self.rand_bounded_u32((b - a) as u32) as i32
153    }
154
155    /// A sample from the uniform distribution on `a..b`.
156    fn rand_range_i64(&mut self, a: i64, b: i64) -> i64 {
157        a + self.rand_bounded_u64((b - a) as u64) as i64
158    }
159
160    /// A sample from the uniform distribution on the interval [0, 1).<br>
161    /// ⚠️ WARNING: Achieving uniform distribution of floats is complex and
162    /// requires analysis yet to be accomplished.
163    fn rand_f32(&mut self) -> f32 {
164        self.rand_u32() as f32 * 2.3283064E-10
165    }
166
167    /// A sample from the uniform distribution on the interval [0, 1).<br>
168    /// ⚠️ WARNING: Achieving uniform distribution of floats is complex and
169    /// requires analysis yet to be accomplished.
170    fn rand_f64(&mut self) -> f64 {
171        self.rand_u32() as f64 * 2.3283064365386963E-10
172    }
173
174    #[cfg(feature = "std")]
175    /// A sample from the normal distribution with mean `mu` and
176    /// standard deviation `sigma`.<br>
177    /// ⚠️ WARNING: depends on rand_f64, which requires further analysis.
178    // Marsaglia polar method.
179    fn rand_normal_f64(&mut self, mu: f64, sigma: f64) -> f64 {
180        loop {
181            let u = 2.0*self.rand_f64() - 1.0;
182            let v = 2.0*self.rand_f64() - 1.0;
183            let s = u*u + v*v;
184            if s < 1.0 && s > 0.0 {
185                return mu + sigma*u*f64::sqrt(-2.0*f64::ln(s)/s);
186            }
187        }
188    }
189    
190    /// A sample from the uniform distribution on the non-empty slice.
191    fn choice<'a, T>(&mut self, a: &'a [T]) -> &'a T {
192        &a[self.rand_bounded_usize(a.len())]
193    }
194
195    /// Shuffle an array randomly. The method is called Fisher–Yates shuffle and has linear time complexity.
196    fn shuffle<T>(&mut self, a: &mut [T]) {
197        if a.is_empty() {return;}
198        let mut i = a.len() - 1;
199        while i > 0 {
200            let j = self.rand_bounded_usize(i + 1);
201            a.swap(i, j);
202            i -= 1;
203        }
204    }
205
206    /// Fill a buffer with random bytes.
207    fn fill(&mut self, a: &mut[u8]) {
208        let mut x = self.rand_u32();
209        let mut count = 3;
210        for p in a {
211            *p = x as u8;
212            if count == 0 {
213                x = self.rand_u32();
214                count = 3;
215            } else {
216                x  >>= 8;
217                count -= 1;
218            }
219        }
220    }
221}
222
223/** A helper function to turn random number generation into an iterator.
224
225Example:
226```
227use tiny_rng::{Rng, Rand, rand_iter};
228
229let mut rng = Rng::from_seed(0);
230for x in rand_iter(&mut rng, Rand::rand_u32).take(10) {
231    println!("0x{:08x}", x);
232}
233```
234*/
235pub fn rand_iter<T: 'static, Generator: Rand>(
236    rng: &mut Generator,
237    rand: fn(&mut Generator) -> T
238) -> impl '_ + Iterator<Item = T>
239{
240    core::iter::from_fn(move || Some(rand(rng)))
241}
242
243/*
244Xorshift128+ is a modern algorithm for the fast generation of random
245numbers of relatively high quality, which passes the most important
246statistical tests. The generator is said to have a period length of
2472^128-1 and to pass "BigCrush" from the test suite "TestU01". It should
248be noted that this is by no means a cryptographic generator. The
249following implementation is as described in Vigna's article [1], with
250the assignment a=23, b=17, c=26 described there as favorable, and is
251also listed that way in the Firefox source code [2] and in
252Wikipedia [3][4]. The internal state may be arbitrary,
253but not (0, 0).
254
255Since the low-order 32 bits of a random value of Xorshift128+ are not
256supposed to pass some statistical tests, it is better to extract
257the high-order 32 bits for rand_u32.
258
259[1] Sebastiano Vigna: "Further scramblings of Marsaglia's
260xorshift generators".
261Journal of Computational and Applied Mathematics (Mai 2017).
262arXiv:1404.0390. doi:10.1016/j.cam.2016.11.006.
263https://arxiv.org/abs/1404.0390
264
265[2] mfbt/XorShift128PlusRNG.h, retrieved 8 Feb. 2020.
266https://github.com/mozilla/gecko-dev/blob/master/mfbt/XorShift128PlusRNG.h
267-- Gecko is the current engine of Firefox.
268
269[3] Xorshift. English Wikipedia, retrieved 8 Feb. 2020.
270https://en.wikipedia.org/wiki/Xorshift
271
272[4] Xorshift. German Wikipedia, retrievend 8 Feb. 2020.
273https://de.wikipedia.org/wiki/Xorshift.
274*/
275
276/// Recommended engine. Currently Xorshift128+.
277pub struct Rng {
278    state: (u64, u64)
279}
280
281impl Rand for Rng {
282    fn from_seed(seed: u64) -> Self {
283        Self {state: (
284            seed ^ 0xf4dbdf2183dcefb7, // [crc32(b"0"), crc32(b"1")]
285            seed ^ 0x1ad5be0d6dd28e9b  // [crc32(b"2"), crc32(b"3")]
286        )}
287    }
288
289    fn rand_u64(&mut self) -> u64 {
290        let (mut x, y) = self.state;
291        self.state.0 = y;
292        x ^= x << 23;
293        self.state.1 = x ^ y ^ (x >> 17) ^ (y >> 26);
294        self.state.1.wrapping_add(y)
295    }
296
297    #[inline]
298    fn rand_u32(&mut self) -> u32 {
299        (self.rand_u64() >> 32) as u32
300    }
301}
302
303impl Rng {
304    /** A helper function to turn random number generation into an iterator.
305
306    Example:
307    ```
308    use tiny_rng::{Rng, Rand};
309
310    let mut rng = Rng::from_seed(0);
311    for x in rng.iter(Rand::rand_u32).take(10) {
312        println!("0x{:08x}", x);
313    }
314    ```
315    */
316    pub fn iter<T: 'static>(&mut self, rand: fn(&mut Self) -> T)
317    -> impl '_ + Iterator<Item = T>
318    {
319        rand_iter(self, rand)
320    }
321}
322
323#[cfg(test)]
324mod tests {
325    use crate::{Rng, Rand};
326
327    fn rng_test<Generator: Rand>() {
328        let mut rng = Generator::from_seed(0);
329        println!("{:?}", rng.rand_range_u32(1, 6));
330    }
331
332    #[test]
333    fn test0() {
334        rng_test::<Rng>();
335    }
336}