pink_trombone/
rng.rs

1#[cfg(test)]
2pub mod xorshift {
3    use crate::NoiseSource;
4
5    // from https://gitlab.com/pomma89/troschuetz-random/-/blob/main/src/Troschuetz.Random/Generators/XorShift128Generator.cs
6    struct BaseGenerator {
7        bit_buffer: u32,
8        bit_count: i32,
9        seed: u32,
10    }
11
12    impl BaseGenerator {
13        pub fn reset(&mut self, seed: u32) {
14            self.bit_buffer = 0;
15            self.bit_count = 0;
16            self.seed = seed;
17        }
18    }
19
20    pub struct XorShift128 {
21        x: u64,
22        y: u64,
23        bytes_available: bool,
24        base: BaseGenerator,
25    }
26
27    const SEED_X: u64 = 521288629 << 32;
28    const SEED_Y: u64 = 362436069;
29
30    impl XorShift128 {
31        pub fn new(seed: u32) -> XorShift128 {
32            let mut gen = XorShift128 {
33                base: BaseGenerator {
34                    bit_buffer: 0,
35                    bit_count: 0,
36                    seed: seed,
37                },
38                x: 0,
39                y: 0,
40                bytes_available: false,
41            };
42
43            // base code
44            gen.reset(seed);
45
46            // this code
47            gen
48        }
49
50        pub fn next_f64(&mut self) -> f64 {
51            let mut tx = self.x;
52            let ty = self.y;
53            self.x = ty;
54            tx ^= tx << 23;
55            tx ^= tx >> 17;
56            tx ^= ty ^ (ty >> 26);
57            self.y = tx;
58            self.bytes_available = false;
59
60            let result = to_f64(tx.overflowing_add(ty).0);
61            assert!(result >= 0.0 && result <= 1.0);
62            result
63        }
64
65        pub fn next_u64(&mut self) -> u64 {
66            let mut tx = self.x;
67            let ty = self.y;
68            self.x = ty;
69            tx ^= tx << 23;
70            tx ^= tx >> 17;
71            tx ^= ty ^ (ty >> 26);
72            self.y = tx;
73            self.bytes_available = false;
74            tx.overflowing_add(ty).0
75        }
76
77        pub fn reset(&mut self, seed: u32) {
78            // base
79            self.base.reset(seed);
80
81            // this
82            self.x = SEED_X + seed as u64;
83            self.y = SEED_Y.overflowing_mul((seed as u64) << 32).0;
84            self.bytes_available = false;
85
86            self.next_u64();
87        }
88    }
89
90    impl NoiseSource<f64> for XorShift128 {
91        fn noise(&mut self) -> f64 {
92            self.next_f64()
93        }
94    }
95
96    fn to_f64(mut value: u64) -> f64 {
97        value = (value >> 12) | 0x3FF0000000000000;
98        let res = unsafe { std::mem::transmute::<u64, f64>(value) };
99        res - 1.0
100    }
101
102    mod tests {
103        // Note this useful idiom: importing names from outer (for mod tests) scope.
104        use super::*;
105
106        #[test]
107        fn reproducible() {
108            let mut generator = XorShift128::new(9452);
109            let vals: Vec<f64> = (0..461456).map(|_| generator.next_f64()).collect();
110            assert_eq!(format!("{:.10}", vals.last().unwrap()), "0.5612585810");
111        }
112    }
113}