1 use std::cell::Cell;
2
3 /// Fast random number generate
4 ///
5 /// Implement xorshift64+: 2 32-bit xorshift sequences added together.
6 /// Shift triplet [17,7,16] was calculated as indicated in Marsaglia's
7 /// Xorshift paper: https://www.jstatsoft.org/article/view/v008i14/xorshift.pdf
8 /// This generator passes the SmallCrush suite, part of TestU01 framework:
9 /// http://simul.iro.umontreal.ca/testu01/tu01.html
10 #[derive(Debug)]
11 pub(crate) struct FastRand {
12 one: Cell<u32>,
13 two: Cell<u32>,
14 }
15
16 impl FastRand {
17 /// Initialize a new, thread-local, fast random number generator.
new(seed: u64) -> FastRand18 pub(crate) fn new(seed: u64) -> FastRand {
19 let one = (seed >> 32) as u32;
20 let mut two = seed as u32;
21
22 if two == 0 {
23 // This value cannot be zero
24 two = 1;
25 }
26
27 FastRand {
28 one: Cell::new(one),
29 two: Cell::new(two),
30 }
31 }
32
fastrand_n(&self, n: u32) -> u3233 pub(crate) fn fastrand_n(&self, n: u32) -> u32 {
34 // This is similar to fastrand() % n, but faster.
35 // See https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/
36 let mul = (self.fastrand() as u64).wrapping_mul(n as u64);
37 (mul >> 32) as u32
38 }
39
fastrand(&self) -> u3240 fn fastrand(&self) -> u32 {
41 let mut s1 = self.one.get();
42 let s0 = self.two.get();
43
44 s1 ^= s1 << 17;
45 s1 = s1 ^ s0 ^ s1 >> 7 ^ s0 >> 16;
46
47 self.one.set(s0);
48 self.two.set(s1);
49
50 s0.wrapping_add(s1)
51 }
52 }
53
54 // Used by the select macro and `StreamMap`
55 #[cfg(any(feature = "macros"))]
56 #[doc(hidden)]
57 #[cfg_attr(not(feature = "macros"), allow(unreachable_pub))]
thread_rng_n(n: u32) -> u3258 pub fn thread_rng_n(n: u32) -> u32 {
59 thread_local! {
60 static THREAD_RNG: FastRand = FastRand::new(crate::loom::rand::seed());
61 }
62
63 THREAD_RNG.with(|rng| rng.fastrand_n(n))
64 }
65