ferray_random/bitgen/
xoshiro256.rs1use super::BitGenerator;
8
9pub struct Xoshiro256StarStar {
24 s: [u64; 4],
25}
26
27impl Xoshiro256StarStar {
28 fn from_state(s: [u64; 4]) -> Self {
30 debug_assert!(
31 s != [0, 0, 0, 0],
32 "Xoshiro256** state must not be all zeros"
33 );
34 Self { s }
35 }
36}
37
38fn splitmix64(state: &mut u64) -> u64 {
41 *state = state.wrapping_add(0x9e3779b97f4a7c15);
42 let mut z = *state;
43 z = (z ^ (z >> 30)).wrapping_mul(0xbf58476d1ce4e5b9);
44 z = (z ^ (z >> 27)).wrapping_mul(0x94d049bb133111eb);
45 z ^ (z >> 31)
46}
47
48impl BitGenerator for Xoshiro256StarStar {
49 fn next_u64(&mut self) -> u64 {
50 let result = (self.s[1].wrapping_mul(5)).rotate_left(7).wrapping_mul(9);
51 let t = self.s[1] << 17;
52
53 self.s[2] ^= self.s[0];
54 self.s[3] ^= self.s[1];
55 self.s[1] ^= self.s[2];
56 self.s[0] ^= self.s[3];
57
58 self.s[2] ^= t;
59 self.s[3] = self.s[3].rotate_left(45);
60
61 result
62 }
63
64 fn seed_from_u64(seed: u64) -> Self {
65 let mut sm = seed;
66 let s0 = splitmix64(&mut sm);
67 let s1 = splitmix64(&mut sm);
68 let s2 = splitmix64(&mut sm);
69 let s3 = splitmix64(&mut sm);
70 if s0 | s1 | s2 | s3 == 0 {
72 Self::from_state([1, 0, 0, 0])
73 } else {
74 Self::from_state([s0, s1, s2, s3])
75 }
76 }
77
78 fn jump(&mut self) -> Option<()> {
79 const JUMP: [u64; 4] = [
81 0x180ec6d33cfd0aba,
82 0xd5a61266f0c9392c,
83 0xa9582618e03fc9aa,
84 0x39abdc4529b1661c,
85 ];
86
87 let mut s0: u64 = 0;
88 let mut s1: u64 = 0;
89 let mut s2: u64 = 0;
90 let mut s3: u64 = 0;
91
92 for &jmp in &JUMP {
93 for b in 0..64 {
94 if (jmp >> b) & 1 != 0 {
95 s0 ^= self.s[0];
96 s1 ^= self.s[1];
97 s2 ^= self.s[2];
98 s3 ^= self.s[3];
99 }
100 self.next_u64();
101 }
102 }
103
104 self.s[0] = s0;
105 self.s[1] = s1;
106 self.s[2] = s2;
107 self.s[3] = s3;
108
109 Some(())
110 }
111
112 fn stream(_seed: u64, _stream_id: u64) -> Option<Self> {
113 None
115 }
116}
117
118impl Clone for Xoshiro256StarStar {
119 fn clone(&self) -> Self {
120 Self { s: self.s }
121 }
122}
123
124#[cfg(test)]
125mod tests {
126 use super::*;
127
128 #[test]
129 fn deterministic_output() {
130 let mut rng1 = Xoshiro256StarStar::seed_from_u64(42);
131 let mut rng2 = Xoshiro256StarStar::seed_from_u64(42);
132 for _ in 0..1000 {
133 assert_eq!(rng1.next_u64(), rng2.next_u64());
134 }
135 }
136
137 #[test]
138 fn different_seeds_differ() {
139 let mut rng1 = Xoshiro256StarStar::seed_from_u64(42);
140 let mut rng2 = Xoshiro256StarStar::seed_from_u64(43);
141 let mut same = true;
142 for _ in 0..100 {
143 if rng1.next_u64() != rng2.next_u64() {
144 same = false;
145 break;
146 }
147 }
148 assert!(!same);
149 }
150
151 #[test]
152 fn jump_advances_state() {
153 let mut rng = Xoshiro256StarStar::seed_from_u64(1234);
154 let before = rng.next_u64();
155 let mut rng2 = Xoshiro256StarStar::seed_from_u64(1234);
156 let _ = rng2.next_u64();
157 rng2.jump();
158 let after = rng2.next_u64();
159 assert_ne!(before, after);
161 }
162
163 #[test]
164 fn stream_not_supported() {
165 assert!(Xoshiro256StarStar::stream(42, 0).is_none());
166 }
167
168 #[test]
169 fn next_f64_range() {
170 let mut rng = Xoshiro256StarStar::seed_from_u64(999);
171 for _ in 0..10_000 {
172 let v = rng.next_f64();
173 assert!((0.0..1.0).contains(&v));
174 }
175 }
176}