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