rand_krull/
krull64.rs

1#[cfg(feature = "serde")]
2use serde::{Deserialize, Serialize};
3use wrapping_arithmetic::wrappit;
4
5// Krull64 features
6// -"trivially strong" design by Sami Perttu
7// -64-bit output, 192-bit state and footprint
8// -full 192-bit state space with no bad states and no bad seeds
9// -2**64 pairwise independent streams of length 2**128
10// -streams are equidistributed with each 64-bit number appearing 2**64 times
11// -random access inside streams
12// -generation takes approximately 3.0 ns (where PCG-128 is 2.4 ns and Krull65 is 4.6 ns)
13
14/// Krull64 non-cryptographic RNG. 64-bit output, 192-bit state.
15#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
16#[derive(Clone, Eq, PartialEq, Debug)]
17pub struct Krull64 {
18    /// LCG state low bits.
19    lcg0: u64,
20    /// LCG state high bits.
21    lcg1: u64,
22    /// Stream number.
23    stream: u64,
24}
25
26// Stream position is measured in relation to an origin LCG state at position 0.
27// We define the origin as equal to the stream number XOR some arbitrary constant
28// in order to desynchronize the streams. Here we invert all the bits,
29// which potentially enhances compression of RNGs at position 0 when serialized.
30#[inline]
31fn origin_0(stream: u64) -> u64 {
32    !stream
33}
34
35#[inline]
36fn origin_128(stream: u64) -> u128 {
37    origin_0(stream) as u128
38}
39
40impl Krull64 {
41    #[inline]
42    fn lcg_128(&self) -> u128 {
43        self.lcg0 as u128 | ((self.lcg1 as u128) << 64)
44    }
45
46    #[inline]
47    fn multiplier(&self) -> u64 {
48        super::LCG_M65_1 as u64
49    }
50
51    #[inline]
52    fn multiplier_128(&self) -> u128 {
53        super::LCG_M65_1
54    }
55
56    #[inline]
57    fn increment_128(&self) -> u128 {
58        // LCG increment is odd in full period sequences.
59        // Unlike with LCG multipliers, any odd increment works fine.
60        // Flip of increment bit B causes changes with a period of 2**(128 - B):
61        // LCG sequences that differ only in high bits of the increment are correlated.
62        // So it's important to rely on the low increment bits only.
63        // The increment is a mirror image of the state in this sense,
64        // as in state it is the low bits that repeat.
65        ((self.stream as u128) << 1) | 1
66    }
67
68    /// Origin is LCG state at position 0 in current stream.
69    #[inline]
70    fn origin_0(&self) -> u64 {
71        origin_0(self.stream)
72    }
73
74    /// Origin is LCG state at position 0 in current stream.
75    #[inline]
76    fn origin_128(&self) -> u128 {
77        origin_128(self.stream)
78    }
79
80    /// Generates the next 64-bit random number.
81    #[wrappit]
82    #[inline]
83    pub fn step(&mut self) -> u64 {
84        // We can get a widening 64-to-128-bit multiply by casting the arguments from 64 bits.
85        // We also add the increment in 128-bit to get the carry for free.
86        let lcg = (self.lcg0 as u128) * self.multiplier() as u128 + self.increment_128();
87        self.lcg1 = ((lcg >> 64) as u64) + self.lcg1 * self.multiplier() + self.lcg0;
88        self.lcg0 = lcg as u64;
89        self.get()
90    }
91
92    /// Generates the next 128-bit random number.
93    #[inline]
94    pub fn step_128(&mut self) -> u128 {
95        self.step() as u128 | ((self.step() as u128) << 64)
96    }
97
98    /// Returns the current 64-bit output.
99    #[wrappit]
100    #[inline]
101    pub fn get(&self) -> u64 {
102        // Take high 64 bits from the LCG, they are the most random.
103        // The 1-to-1 mapping guarantees equidistribution
104        // as the rest of the pipeline is bijective.
105        let x = self.lcg1;
106
107        // We want the output stage to pass tests also as an indexed RNG.
108        // It was tested with PractRand to 1 TB in this use.
109        // The output hash is a combination of stages from SplitMix64
110        // combined with a final stage from a hash by degski.
111        let x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9;
112        let x = (x ^ (x >> 27)) * 0x94d049bb133111eb;
113        let x = (x ^ (x >> 31)) * 0xd6e8feb86659fd93;
114        x ^ (x >> 32)
115    }
116
117    /// 128-bit version of step() for benchmarking.
118    #[wrappit]
119    #[inline]
120    pub fn step_slow(&mut self) -> u64 {
121        let lcg = self.lcg_128() * self.multiplier_128() + self.increment_128();
122        self.lcg0 = lcg as u64;
123        self.lcg1 = (lcg >> 64) as u64;
124        self.get()
125    }
126
127    /// Creates a new Krull64 RNG.
128    /// Stream and position are set to 0.
129    #[allow(clippy::new_without_default)]
130    pub fn new() -> Self {
131        Krull64 {
132            lcg0: origin_0(0),
133            lcg1: 0,
134            stream: 0,
135        }
136    }
137
138    /// Creates a new Krull64 RNG from a 32-bit seed.
139    /// Stream is set to the given seed and position is set to 0.
140    /// All seeds work equally well.
141    pub fn from_32(seed: u32) -> Self {
142        Krull64::from_64(seed as u64)
143    }
144
145    /// Creates a new Krull64 RNG from a 64-bit seed.
146    /// Stream is set to the given seed and position is set to 0.
147    /// All seeds work equally well.
148    pub fn from_64(seed: u64) -> Self {
149        Krull64 {
150            lcg0: origin_0(seed),
151            lcg1: 0,
152            stream: seed,
153        }
154    }
155
156    /// Creates a new Krull64 RNG from a 128-bit seed.
157    /// Each seed accesses a unique sequence of length 2**64.
158    /// All seeds work equally well.
159    /// Sets stream to a XOR of the high and low bits of seed
160    /// to decorrelate nearby seeds in both arguments.
161    /// Sets high bits of position from low bits of seed.
162    pub fn from_128(seed: u128) -> Self {
163        let mut krull = Krull64::from_64(((seed >> 64) ^ seed) as u64);
164        krull.set_position((seed as u128) << 64);
165        krull
166    }
167
168    /// Jumps forward (if steps > 0) or backward (if steps < 0) or does nothing (if steps = 0).
169    /// The stream wraps around, so signed steps can be interpreted as unsigned.
170    pub fn jump(&mut self, steps: i128) {
171        let lcg = crate::lcg::get_state(
172            self.multiplier_128(),
173            self.increment_128(),
174            self.lcg_128(),
175            steps as u128,
176        );
177        self.lcg0 = lcg as u64;
178        self.lcg1 = (lcg >> 64) as u64;
179    }
180
181    /// Returns current position in stream. The full state of the generator is (stream, position).
182    pub fn position(&self) -> u128 {
183        crate::lcg::get_iterations(
184            self.multiplier_128(),
185            self.increment_128(),
186            self.origin_128(),
187            self.lcg_128(),
188        )
189    }
190
191    /// Sets position in stream.
192    pub fn set_position(&mut self, position: u128) {
193        let lcg = crate::lcg::get_state(
194            self.multiplier_128(),
195            self.increment_128(),
196            self.origin_128(),
197            position,
198        );
199        self.lcg0 = lcg as u64;
200        self.lcg1 = (lcg >> 64) as u64;
201    }
202
203    /// Resets stream position to 0. Equivalent to set_position(0).
204    #[inline]
205    pub fn reset(&mut self) {
206        self.lcg0 = self.origin_0();
207        self.lcg1 = 0;
208    }
209
210    /// Returns current stream. The full state of the generator is (stream, position).
211    #[inline]
212    pub fn stream(&self) -> u64 {
213        self.stream
214    }
215
216    /// Sets stream and initializes position to 0.
217    pub fn set_stream(&mut self, stream: u64) {
218        self.stream = stream;
219        self.reset();
220    }
221}
222
223use super::{Error, RngCore, SeedableRng};
224
225impl RngCore for Krull64 {
226    fn next_u32(&mut self) -> u32 {
227        self.step() as u32
228    }
229
230    fn next_u64(&mut self) -> u64 {
231        self.step()
232    }
233
234    fn fill_bytes(&mut self, dest: &mut [u8]) {
235        let bytes = dest.len();
236        let mut i = 0;
237        while i < bytes {
238            let x = self.step();
239            let j = bytes.min(i + 8);
240            // Always use Little-Endian.
241            dest[i..j].copy_from_slice(&x.to_le_bytes()[0..(j - i)]);
242            i = j;
243        }
244    }
245
246    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
247        self.fill_bytes(dest);
248        Ok(())
249    }
250}
251
252impl SeedableRng for Krull64 {
253    type Seed = [u8; 16];
254
255    /// Creates a new Krull64 RNG from a seed.
256    /// All seeds work equally well.
257    fn from_seed(seed: Self::Seed) -> Self {
258        // Always use Little-Endian.
259        Krull64::from_128(u128::from_le_bytes(seed))
260    }
261}
262
263#[cfg(test)]
264mod tests {
265    use super::super::*;
266    use super::*;
267
268    #[test]
269    pub fn run_tests() {
270        let krull64_expected: [u64; 16] = [
271            0x57c1b6c1df5ed4d2,
272            0x1efdba83398cf412,
273            0xa02d8dfda06ac9ce,
274            0xf6e3f32be5e81841,
275            0xc2a690083e597e0d,
276            0x3b1b2ed3fa6c15aa,
277            0x241c691340a479b2,
278            0x88c24c8d79bb67c1,
279            0x09f213c4fc2b61dc,
280            0xa4b6ad95c713c951,
281            0xa43904ae3341edf7,
282            0xee2dca4d5fd5f8fa,
283            0x27bdddbeaa4aadb0,
284            0x98c78e68dbf634b2,
285            0xf0edc57017a0d5a5,
286            0x8647ea5de51eca23,
287        ];
288        let mut krull64 = Krull64::from_64(0);
289        for x in krull64_expected {
290            assert_eq!(x, krull64.next_u64());
291        }
292
293        let mut r: u128 = 0;
294        let mut rnd = || -> u128 {
295            r = r.wrapping_mul(LCG_M128_1).wrapping_add(0xffff);
296            r
297        };
298
299        for _ in 0..1 << 12 {
300            let seed = rnd() as u64;
301            let mut krull1 = Krull64::new();
302            assert_eq!(0, krull1.stream());
303            assert_eq!(0, krull1.position());
304            krull1.set_stream(seed);
305            assert_eq!(seed, krull1.stream());
306            assert_eq!(0, krull1.position());
307            let mut krull2 = Krull64::from_64(seed);
308            assert_eq!(seed, krull2.stream());
309            assert_eq!(0, krull2.position());
310
311            let pos2 = rnd();
312            let pos1 = pos2 & rnd();
313            krull1.set_position(pos1);
314            krull2.set_position(pos2);
315            assert_eq!(pos1, krull1.position());
316            assert_eq!(pos2, krull2.position());
317            krull1.jump((pos2 - pos1) as i128);
318            assert_eq!(pos2, krull1.position());
319            assert_eq!(krull1.next_u64(), krull2.next_u64());
320            krull1.jump(-1);
321            assert_eq!(pos2, krull1.position());
322            krull2.jump(-1);
323            assert_eq!(pos2, krull2.position());
324            krull1.jump(-((pos2 - pos1) as i128));
325            assert_eq!(pos1, krull1.position());
326
327            let n = 1 + (rnd() & 0x3ff);
328            for _ in 0..n {
329                krull1.next_u64();
330            }
331            assert_eq!(pos1 + n, krull1.position());
332
333            assert_eq!(seed, krull1.stream());
334
335            let bytes = 1 + (rnd() & 0x7f);
336            let mut buffer1 = [0u8; 0x80];
337            let mut buffer2 = [0u8; 0x80];
338            krull1.reset();
339            assert_eq!(0, krull1.position());
340            krull1.fill_bytes(&mut buffer1[0..bytes as usize]);
341            krull2.reset();
342            for i in 0..0x10 {
343                let x = krull2.next_u64();
344                buffer2[(i << 3)..((i + 1) << 3)].copy_from_slice(&x.to_le_bytes());
345            }
346            assert!(buffer1[0..bytes as usize]
347                .iter()
348                .zip(buffer2[0..bytes as usize].iter())
349                .all(|(x, y)| x == y));
350        }
351    }
352}