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