1#[derive(Clone, Debug)]
11pub struct SplitMix64 {
12 state: u64,
13}
14
15impl SplitMix64 {
16 pub fn new(seed: u64) -> SplitMix64 {
17 SplitMix64 { state: seed }
18 }
19
20 pub fn derive(seed: u64, label: &str) -> SplitMix64 {
24 SplitMix64::new(seed ^ fnv1a(label))
25 }
26
27 pub fn next_u64(&mut self) -> u64 {
28 self.state = self.state.wrapping_add(0x9E37_79B9_7F4A_7C15);
29 let mut z = self.state;
30 z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
31 z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
32 z ^ (z >> 31)
33 }
34
35 pub fn below(&mut self, n: u64) -> u64 {
39 assert!(n > 0, "below(0)");
40 let zone = u64::MAX - (u64::MAX % n) - 1;
41 loop {
42 let v = self.next_u64();
43 if v <= zone {
44 return v % n;
45 }
46 }
47 }
48
49 pub fn chance(&mut self, num: u64, den: u64) -> bool {
51 self.below(den) < num
52 }
53
54 pub fn range(&mut self, lo: u64, hi: u64) -> u64 {
56 assert!(hi >= lo);
57 lo + self.below(hi - lo + 1)
58 }
59
60 pub fn shuffle<T>(&mut self, v: &mut [T]) {
63 if v.len() < 2 {
64 return;
65 }
66 for i in (1..v.len()).rev() {
67 let j = self.below(i as u64 + 1) as usize;
68 v.swap(i, j);
69 }
70 }
71}
72
73pub fn fnv1a(s: &str) -> u64 {
74 let mut h: u64 = 0xcbf2_9ce4_8422_2325;
75 for b in s.as_bytes() {
76 h ^= *b as u64;
77 h = h.wrapping_mul(0x1000_0000_01b3);
78 }
79 h
80}
81
82#[cfg(test)]
83mod tests {
84 use super::*;
85
86 #[test]
89 fn the_stream_is_pinned() {
90 let mut r = SplitMix64::new(0);
91 assert_eq!(r.next_u64(), 16294208416658607535);
92 assert_eq!(r.next_u64(), 7960286522194355700);
93 assert_eq!(r.next_u64(), 487617019471545679);
94 }
95
96 #[test]
97 fn below_is_uniform_enough_to_measure_a_rate() {
98 let mut r = SplitMix64::new(7);
99 let mut hits = 0;
100 for _ in 0..100_000 {
101 if r.chance(1, 100) {
102 hits += 1;
103 }
104 }
105 assert!((900..=1100).contains(&hits), "{hits}");
107 }
108
109 #[test]
110 fn shuffle_is_a_permutation() {
111 let mut r = SplitMix64::new(3);
112 let mut v: Vec<u32> = (0..64).collect();
113 r.shuffle(&mut v);
114 let mut back = v.clone();
115 back.sort_unstable();
116 assert_eq!(back, (0..64).collect::<Vec<_>>());
117 assert_ne!(v, back, "a shuffle that is the identity is not a shuffle");
118 }
119
120 #[test]
121 fn derive_decorrelates() {
122 let mut a = SplitMix64::derive(99, "out_of_stock");
123 let a = a.next_u64();
124 let mut b = SplitMix64::derive(99, "price_reset");
125 let b = b.next_u64();
126 assert_ne!(a, b);
127 }
128}