1#![no_std]
2pub mod new_rng;
3pub mod old_rng;
4pub use old_rng as rng;
5
6#[derive(Debug)]
7pub enum SeedInitializer {
8 SeedPair(u64, u64),
9 Seed(u64),
10 Seed0(u64),
11 Seed1(u64),
12}
13
14impl From<i64> for SeedInitializer {
15 fn from(seed: i64) -> Self {
16 Self::Seed(seed as u64)
17 }
18}
19
20impl From<(u64, u64)> for SeedInitializer {
21 fn from((seed0, seed1): (u64, u64)) -> Self {
22 Self::SeedPair(seed0, seed1)
23 }
24}
25
26pub const MH3_FACTOR_1: u64 = 0xff51afd7ed558ccd;
27pub const MH3_FACTOR_2: u64 = 0xc4ceb9fe1a85ec53;
28
29pub const INV_MH3_FACTOR_1: u64 = 0x9cb4b2f8129337db;
30pub const INV_MH3_FACTOR_2: u64 = 0x4f74430c22a54005;
31
32pub trait RandomXS128:
33 From<SeedInitializer>
34{
35 fn new(seed: u64) -> Self;
36 fn next_u64(&mut self) -> u64;
37 fn advance(&mut self, n: u32) {
38 (0..n).for_each(|_| { let _ = self.next_u64(); })
39 }
40
41 fn next_capped_u64(&mut self, modulus: u64) -> u64 {
42 loop {
43 let (residue, overflowed) =
44 self.overflowing_next_capped_u64(modulus);
45 #[cfg(feature = "reroll")]
46 if overflowed {
47 continue;
48 }
49 return residue;
50 }
51 }
52
53 fn unchecked_next_capped_u64(&mut self, modulus: u64) -> u64 {
54 self.overflowing_next_capped_u64(modulus).0
55 }
56
57 fn overflowing_next_capped_u64(&mut self, modulus: u64) -> (u64, bool);
58}
59
60#[cfg(test)]
61mod unit_tests {
62 enum RngValue {
63 U64(u64),
64 CappedU64 { modulus: u64, residue: u64 },
65 Advance(u32),
66 }
67
68 struct RngValues {
69 seed: u64,
70 values: [Option<RngValue>; 16],
71 }
72
73 use crate::{new_rng, old_rng, RandomXS128};
74
75 const VALUE_LISTS: [RngValues; 7] = [
76 RngValues {
77 seed: 12345i64 as u64,
78 values: [ Some(RngValue::U64(1382432690769144372i64 as u64)), Some(RngValue::Advance(10)), Some(RngValue::CappedU64 { modulus: 134, residue: 83, }), Some(RngValue::U64(-5355119237153046436i64 as u64)), None, None, None, None, None, None, None, None, None, None, None, None, ],
79 },
80 RngValues {
81 seed: 42i64 as u64,
82 values: [ Some(RngValue::CappedU64 { modulus: 1, residue: 0, }), Some(RngValue::CappedU64 { modulus: 2, residue: 1, }), Some(RngValue::CappedU64 { modulus: 3, residue: 0, }), Some(RngValue::CappedU64 { modulus: 4, residue: 0, }), Some(RngValue::CappedU64 { modulus: 5, residue: 1, }), Some(RngValue::CappedU64 { modulus: 6, residue: 5, }), Some(RngValue::CappedU64 { modulus: 7, residue: 5, }), Some(RngValue::CappedU64 { modulus: 8, residue: 3, }), Some(RngValue::CappedU64 { modulus: 9, residue: 7, }), Some(RngValue::CappedU64 { modulus: 10, residue: 7, }), Some(RngValue::CappedU64 { modulus: 11, residue: 9, }), Some(RngValue::CappedU64 { modulus: 12, residue: 9, }), Some(RngValue::CappedU64 { modulus: 13, residue: 4, }), Some(RngValue::CappedU64 { modulus: 14, residue: 13, }), Some(RngValue::CappedU64 { modulus: 15, residue: 3, }), Some(RngValue::CappedU64 { modulus: 16, residue: 6, }), ],
83 },
84 RngValues {
85 seed: -10i64 as u64,
86 values: [ Some(RngValue::CappedU64 { modulus: 5, residue: 3, }), Some(RngValue::CappedU64 { modulus: 2, residue: 0, }), Some(RngValue::CappedU64 { modulus: 9, residue: 7, }), Some(RngValue::Advance(1)), Some(RngValue::U64(-1057127458437580682i64 as u64)), Some(RngValue::CappedU64 { modulus: 7, residue: 6, }), Some(RngValue::CappedU64 { modulus: 13, residue: 3, }), Some(RngValue::CappedU64 { modulus: 8, residue: 0, }), Some(RngValue::CappedU64 { modulus: 6, residue: 0, }), Some(RngValue::CappedU64 { modulus: 4, residue: 0, }), Some(RngValue::CappedU64 { modulus: 3, residue: 1, }), Some(RngValue::CappedU64 { modulus: 11, residue: 3, }), Some(RngValue::CappedU64 { modulus: 16, residue: 7, }), Some(RngValue::CappedU64 { modulus: 12, residue: 7, }), Some(RngValue::CappedU64 { modulus: 15, residue: 8, }), None, ],
87 },
88 RngValues {
89 seed: 1000i64 as u64,
90 values: [ Some(RngValue::Advance(2)), Some(RngValue::Advance(3)), Some(RngValue::Advance(4)), Some(RngValue::CappedU64 { modulus: 1, residue: 0, }), Some(RngValue::CappedU64 { modulus: 999, residue: 913, }), Some(RngValue::CappedU64 { modulus: 555, residue: 61, }), Some(RngValue::CappedU64 { modulus: 100, residue: 79, }), Some(RngValue::Advance(5)), Some(RngValue::CappedU64 { modulus: 777, residue: 616, }), Some(RngValue::Advance(6)), Some(RngValue::CappedU64 { modulus: 123, residue: 23, }), Some(RngValue::Advance(7)), Some(RngValue::CappedU64 { modulus: 9999, residue: 7303, }), Some(RngValue::CappedU64 { modulus: 888, residue: 133, }), Some(RngValue::CappedU64 { modulus: 444, residue: 167, }), None, ],
91 },
92 RngValues {
93 seed: 1234567890i64 as u64,
94 values: [ Some(RngValue::CappedU64 { modulus: 5, residue: 3, }), Some(RngValue::CappedU64 { modulus: 10, residue: 5, }), Some(RngValue::CappedU64 { modulus: 15, residue: 4, }), Some(RngValue::CappedU64 { modulus: 20, residue: 13, }), Some(RngValue::CappedU64 { modulus: 25, residue: 1, }), Some(RngValue::CappedU64 { modulus: 30, residue: 10, }), Some(RngValue::CappedU64 { modulus: 35, residue: 5, }), Some(RngValue::CappedU64 { modulus: 40, residue: 1, }), Some(RngValue::CappedU64 { modulus: 45, residue: 41, }), Some(RngValue::CappedU64 { modulus: 50, residue: 14, }), Some(RngValue::CappedU64 { modulus: 55, residue: 35, }), Some(RngValue::CappedU64 { modulus: 60, residue: 40, }), Some(RngValue::CappedU64 { modulus: 65, residue: 11, }), Some(RngValue::CappedU64 { modulus: 70, residue: 22, }), Some(RngValue::CappedU64 { modulus: 75, residue: 40, }), Some(RngValue::CappedU64 { modulus: 80, residue: 5, }), ],
95 },
96 RngValues {
97 seed: -9876543210i64 as u64,
98 values: [ Some(RngValue::Advance(3)), Some(RngValue::CappedU64 { modulus: 42, residue: 25, }), Some(RngValue::Advance(6)), Some(RngValue::Advance(9)), Some(RngValue::CappedU64 { modulus: 99999999999999, residue: 21627940712847, }), Some(RngValue::CappedU64 { modulus: 77777777777777, residue: 75498257424579, }), Some(RngValue::CappedU64 { modulus: 88888888888888, residue: 14384965901149, }), Some(RngValue::CappedU64 { modulus: 1234567890, residue: 975571841, }), Some(RngValue::Advance(5)), Some(RngValue::CappedU64 { modulus: 55555555555555, residue: 29505003891522, }), Some(RngValue::CappedU64 { modulus: 10000000000000, residue: 551591108261, }), Some(RngValue::CappedU64 { modulus: 123, residue: 15, }), Some(RngValue::Advance(4)), Some(RngValue::CappedU64 { modulus: 99999999999, residue: 52894208431, }), Some(RngValue::Advance(1)), None, ],
99 },
100 RngValues {
101 seed: 9223372036854775807i64 as u64,
102 values: [ Some(RngValue::CappedU64 { modulus: 100, residue: 58, }), Some(RngValue::CappedU64 { modulus: 200, residue: 87, }), Some(RngValue::CappedU64 { modulus: 300, residue: 297, }), Some(RngValue::CappedU64 { modulus: 400, residue: 281, }), Some(RngValue::CappedU64 { modulus: 500, residue: 321, }), Some(RngValue::CappedU64 { modulus: 600, residue: 304, }), Some(RngValue::CappedU64 { modulus: 700, residue: 649, }), Some(RngValue::CappedU64 { modulus: 800, residue: 574, }), Some(RngValue::CappedU64 { modulus: 900, residue: 775, }), Some(RngValue::CappedU64 { modulus: 1000, residue: 373, }), Some(RngValue::CappedU64 { modulus: 1100, residue: 465, }), Some(RngValue::CappedU64 { modulus: 1200, residue: 980, }), Some(RngValue::CappedU64 { modulus: 1300, residue: 234, }), Some(RngValue::CappedU64 { modulus: 1400, residue: 917, }), Some(RngValue::CappedU64 { modulus: 1500, residue: 1185, }), None, ],
103 },
104 ];
105
106 #[test]
107 fn old_rng_vs_java_rng() {
108 for RngValues { seed, values } in VALUE_LISTS {
109 let mut rng = old_rng::Random::new(seed);
110 for value in values {
111 match value {
112 Some(RngValue::U64(n)) =>
113 assert_eq!(rng.next_u64(), n),
114 Some(RngValue::CappedU64 { modulus, residue }) =>
115 assert_eq!(rng.next_capped_u64(modulus), residue),
116 Some(RngValue::Advance(k)) =>
117 rng.advance(k),
118 None => {}
119 }
120 }
121 }
122 }
123
124 #[test]
125 fn new_rng_vs_java_rng() {
126 for RngValues { seed, values } in VALUE_LISTS {
127 let mut rng = new_rng::Random::new(seed);
128 for value in values {
129 match value {
130 Some(RngValue::U64(n)) => {
131 assert_eq!(rng.next_u64(), n)
132 }
133 Some(RngValue::CappedU64 { modulus, residue }) => {
134 assert_eq!(rng.next_capped_u64(modulus), residue)
135 }
136 Some(RngValue::Advance(k)) => (0..k).for_each(|_| {
137 let _ = rng.next_u64();
138 }),
139 None => {}
140 }
141 }
142 }
143 }
144}
145
146#[cfg(kani)]
147mod verification {
148
149 use crate::old_rng::Random as OldRandom;
150 use crate::new_rng::Random as NewRandom;
151 use super::*;
152
153 #[kani::proof]
154 fn next_u64() {
155 let seed0 = kani::any();
156 let seed1 = kani::any();
157 let mut old_rng: OldRandom = (seed0, seed1).into();
158 let mut new_rng: NewRandom = (seed0, seed1).into();
159
160 assert!(
161 old_rng.next_u64() ==
162 new_rng.next_u64()
163 );
164 }
165
166 #[kani::proof]
167 fn overflowing_next_capped_u64() {
168 let seed0 = kani::any();
169 let seed1 = kani::any();
170 let modulus: u64 = kani::any();
171
172
173 kani::assume(
174 modulus.is_power_of_two() ||
175 [3, 5, 6, 7].contains(&modulus)
176 );
177
178 let mut old_rng: OldRandom = (seed0, seed1).into();
179 let mut new_rng: NewRandom = (seed0, seed1).into();
180
181 assert!(
182 old_rng.overflowing_next_capped_u64(modulus) ==
183 new_rng.overflowing_next_capped_u64(modulus)
184 );
185 }
186
187 #[kani::proof]
188 fn unchecked_next_capped_u64() {
189 let seed0 = kani::any();
190 let seed1 = kani::any();
191 let modulus: u64 = kani::any();
192
193
194 kani::assume(
195 modulus.is_power_of_two()
196 );
198
199 let mut old_rng: OldRandom = (seed0, seed1).into();
200 let mut new_rng: NewRandom = (seed0, seed1).into();
201
202 assert!(
203 old_rng.unchecked_next_capped_u64(modulus) ==
204 new_rng.unchecked_next_capped_u64(modulus)
205 );
206 }
207
208 #[kani::proof]
209 fn wrapping_xor_shr33() {
210 let seed0 = kani::any();
211
212 let old_shr33: u64 = seed0 ^ seed0 >> 33;
213 let new_shr33: u64 = new_rng::Random
214 ::wrapping_xor_shr33(seed0);
215 assert!(
216 old_shr33 ==
217 new_shr33
218 );
219 }
220
221 const FACTORS: [u64; 4] = [
222 MH3_FACTOR_1,
223 MH3_FACTOR_2,
224 INV_MH3_FACTOR_1,
225 INV_MH3_FACTOR_2
226 ];
227
228 const SHIFTS: [u32; 8] = [0, 8, 16, 24, 32, 40, 48, 56];
229
230 fn wrapping_mul
231 <const FACTOR: u64, const SHIFT: u32>
232 (seed0: u64) {
233 let old_mul: u64 = seed0.wrapping_mul(FACTOR);
234 let new_mul: u64 = new_rng::Random
235 ::wrapping_const_mul::<FACTOR>(seed0);
236 assert!(
237 old_mul.wrapping_shr(SHIFT) as u8 ==
238 new_mul.wrapping_shr(SHIFT) as u8
239 );
240 }
241
242 #[kani::proof]
243 fn wrapping_mul_0_0() {
244 let seed0 = kani::any();
245 wrapping_mul::<{FACTORS[0]}, {SHIFTS[0]}>(seed0);
246 }
247
248 #[kani::proof]
249 fn wrapping_mul_0_1() {
250 let seed0 = kani::any();
251 wrapping_mul::<{FACTORS[0]}, {SHIFTS[1]}>(seed0);
252 }
253
254 }