rand_isaac/
isaac64.rs

1// Copyright 2018 Developers of the Rand project.
2// Copyright 2013-2018 The Rust Project Developers.
3//
4// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
7// option. This file may not be copied, modified, or distributed
8// except according to those terms.
9
10//! The ISAAC-64 random number generator.
11
12use core::{fmt, slice};
13use core::num::Wrapping as w;
14#[cfg(feature="serde1")] use serde::{Serialize, Deserialize};
15use rand_core::{RngCore, SeedableRng, Error, le};
16use rand_core::block::{BlockRngCore, BlockRng64};
17use crate::isaac_array::IsaacArray;
18
19#[allow(non_camel_case_types)]
20type w64 = w<u64>;
21
22const RAND_SIZE_LEN: usize = 8;
23const RAND_SIZE: usize = 1 << RAND_SIZE_LEN;
24
25/// A random number generator that uses ISAAC-64, the 64-bit variant of the
26/// ISAAC algorithm.
27///
28/// ISAAC stands for "Indirection, Shift, Accumulate, Add, and Count" which are
29/// the principal bitwise operations employed. It is the most advanced of a
30/// series of array based random number generator designed by Robert Jenkins
31/// in 1996[^1].
32///
33/// ISAAC-64 is mostly similar to ISAAC. Because it operates on 64-bit integers
34/// instead of 32-bit, it uses twice as much memory to hold its state and
35/// results. Also it uses different constants for shifts and indirect indexing,
36/// optimized to give good results for 64bit arithmetic.
37///
38/// ISAAC-64 is notably fast and produces excellent quality random numbers for
39/// non-cryptographic applications.
40///
41/// In spite of being designed with cryptographic security in mind, ISAAC hasn't
42/// been stringently cryptanalyzed and thus cryptographers do not not
43/// consensually trust it to be secure. When looking for a secure RNG, prefer
44/// `Hc128Rng` from the [`rand_hc`] crate instead, which, like ISAAC, is an
45/// array-based RNG and one of the stream-ciphers selected the by eSTREAM
46///
47/// ## Overview of the ISAAC-64 algorithm:
48/// (in pseudo-code)
49///
50/// ```text
51/// Input: a, b, c, s[256] // state
52/// Output: r[256] // results
53///
54/// mix(a,i) = !(a ^ a << 21)  if i = 0 mod 4
55///              a ^ a >>  5   if i = 1 mod 4
56///              a ^ a << 12   if i = 2 mod 4
57///              a ^ a >> 33   if i = 3 mod 4
58///
59/// c = c + 1
60/// b = b + c
61///
62/// for i in 0..256 {
63///     x = s_[i]
64///     a = mix(a,i) + s[i+128 mod 256]
65///     y = a + b + s[x>>3 mod 256]
66///     s[i] = y
67///     b = x + s[y>>11 mod 256]
68///     r[i] = b
69/// }
70/// ```
71///
72/// This implementation uses [`BlockRng64`] to implement the [`RngCore`] methods.
73///
74/// See for more information the documentation of [`IsaacRng`].
75///
76/// [^1]: Bob Jenkins, [*ISAAC and RC4*](
77///       http://burtleburtle.net/bob/rand/isaac.html)
78///
79/// [`IsaacRng`]: crate::isaac::IsaacRng
80/// [`rand_hc`]: https://docs.rs/rand_hc
81/// [`BlockRng64`]: rand_core::block::BlockRng64
82#[derive(Debug, Clone)]
83#[cfg_attr(feature="serde1", derive(Serialize, Deserialize))]
84pub struct Isaac64Rng(BlockRng64<Isaac64Core>);
85
86impl RngCore for Isaac64Rng {
87    #[inline]
88    fn next_u32(&mut self) -> u32 {
89        self.0.next_u32()
90    }
91
92    #[inline]
93    fn next_u64(&mut self) -> u64 {
94        self.0.next_u64()
95    }
96
97    #[inline]
98    fn fill_bytes(&mut self, dest: &mut [u8]) {
99        self.0.fill_bytes(dest)
100    }
101
102    #[inline]
103    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
104        self.0.try_fill_bytes(dest)
105    }
106}
107
108impl SeedableRng for Isaac64Rng {
109    type Seed = <Isaac64Core as SeedableRng>::Seed;
110
111    #[inline]
112    fn from_seed(seed: Self::Seed) -> Self {
113        Isaac64Rng(BlockRng64::<Isaac64Core>::from_seed(seed))
114    }
115
116    /// Create an ISAAC random number generator using an `u64` as seed.
117    /// If `seed == 0` this will produce the same stream of random numbers as
118    /// the reference implementation when used unseeded.
119    #[inline]
120    fn seed_from_u64(seed: u64) -> Self {
121        Isaac64Rng(BlockRng64::<Isaac64Core>::seed_from_u64(seed))
122    }
123
124    #[inline]
125    fn from_rng<S: RngCore>(rng: S) -> Result<Self, Error> {
126        BlockRng64::<Isaac64Core>::from_rng(rng).map(|rng| Isaac64Rng(rng))
127    }
128}
129
130/// The core of `Isaac64Rng`, used with `BlockRng`.
131#[derive(Clone)]
132#[cfg_attr(feature="serde1", derive(Serialize, Deserialize))]
133pub struct Isaac64Core {
134    #[cfg_attr(feature="serde1",serde(with="super::isaac_array::isaac_array_serde"))]
135    mem: [w64; RAND_SIZE],
136    a: w64,
137    b: w64,
138    c: w64,
139}
140
141// Custom Debug implementation that does not expose the internal state
142impl fmt::Debug for Isaac64Core {
143    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
144        write!(f, "Isaac64Core {{}}")
145    }
146}
147
148// Custom PartialEq implementation as it can't currently be derived from an array of size RAND_SIZE
149impl ::core::cmp::PartialEq for Isaac64Core {
150    fn eq(&self, other: &Isaac64Core) -> bool {
151        &self.mem[..] == &other.mem[..]
152            && self.a == other.a
153            && self.b == other.b
154            && self.c == other.c
155    }
156}
157
158// Custom Eq implementation as it can't currently be derived from an array of size RAND_SIZE
159impl ::core::cmp::Eq for Isaac64Core {
160}
161
162impl BlockRngCore for Isaac64Core {
163    type Item = u64;
164    type Results = IsaacArray<Self::Item>;
165
166    /// Refills the output buffer, `results`. See also the pseudocode desciption
167    /// of the algorithm in the `Isaac64Rng` documentation.
168    ///
169    /// Optimisations used (similar to the reference implementation):
170    ///
171    /// - The loop is unrolled 4 times, once for every constant of mix().
172    /// - The contents of the main loop are moved to a function `rngstep`, to
173    ///   reduce code duplication.
174    /// - We use local variables for a and b, which helps with optimisations.
175    /// - We split the main loop in two, one that operates over 0..128 and one
176    ///   over 128..256. This way we can optimise out the addition and modulus
177    ///   from `s[i+128 mod 256]`.
178    /// - We maintain one index `i` and add `m` or `m2` as base (m2 for the
179    ///   `s[i+128 mod 256]`), relying on the optimizer to turn it into pointer
180    ///   arithmetic.
181    /// - We fill `results` backwards. The reference implementation reads values
182    ///   from `results` in reverse. We read them in the normal direction, to
183    ///   make `fill_bytes` a memcopy. To maintain compatibility we fill in
184    ///   reverse.
185    fn generate(&mut self, results: &mut IsaacArray<Self::Item>) {
186        self.c += w(1);
187        // abbreviations
188        let mut a = self.a;
189        let mut b = self.b + self.c;
190        const MIDPOINT: usize = RAND_SIZE / 2;
191
192        #[inline]
193        fn ind(mem:&[w64; RAND_SIZE], v: w64, amount: usize) -> w64 {
194            let index = (v >> amount).0 as usize % RAND_SIZE;
195            mem[index]
196        }
197
198        #[inline]
199        fn rngstep(mem: &mut [w64; RAND_SIZE],
200                   results: &mut [u64; RAND_SIZE],
201                   mix: w64,
202                   a: &mut w64,
203                   b: &mut w64,
204                   base: usize,
205                   m: usize,
206                   m2: usize) {
207            let x = mem[base + m];
208            *a = mix + mem[base + m2];
209            let y = *a + *b + ind(&mem, x, 3);
210            mem[base + m] = y;
211            *b = x + ind(&mem, y, 3 + RAND_SIZE_LEN);
212            results[RAND_SIZE - 1 - base - m] = (*b).0;
213        }
214
215        let mut m = 0;
216        let mut m2 = MIDPOINT;
217        for i in (0..MIDPOINT/4).map(|i| i * 4) {
218            rngstep(&mut self.mem, results, !(a ^ (a << 21)), &mut a, &mut b, i + 0, m, m2);
219            rngstep(&mut self.mem, results,   a ^ (a >> 5 ),  &mut a, &mut b, i + 1, m, m2);
220            rngstep(&mut self.mem, results,   a ^ (a << 12),  &mut a, &mut b, i + 2, m, m2);
221            rngstep(&mut self.mem, results,   a ^ (a >> 33),  &mut a, &mut b, i + 3, m, m2);
222        }
223
224        m = MIDPOINT;
225        m2 = 0;
226        for i in (0..MIDPOINT/4).map(|i| i * 4) {
227            rngstep(&mut self.mem, results, !(a ^ (a << 21)), &mut a, &mut b, i + 0, m, m2);
228            rngstep(&mut self.mem, results,   a ^ (a >> 5 ),  &mut a, &mut b, i + 1, m, m2);
229            rngstep(&mut self.mem, results,   a ^ (a << 12),  &mut a, &mut b, i + 2, m, m2);
230            rngstep(&mut self.mem, results,   a ^ (a >> 33),  &mut a, &mut b, i + 3, m, m2);
231        }
232
233        self.a = a;
234        self.b = b;
235    }
236}
237
238impl Isaac64Core {
239    /// Create a new ISAAC-64 random number generator.
240    fn init(mut mem: [w64; RAND_SIZE], rounds: u32) -> Self {
241        fn mix(a: &mut w64, b: &mut w64, c: &mut w64, d: &mut w64,
242               e: &mut w64, f: &mut w64, g: &mut w64, h: &mut w64) {
243            *a -= *e; *f ^= *h >> 9;  *h += *a;
244            *b -= *f; *g ^= *a << 9;  *a += *b;
245            *c -= *g; *h ^= *b >> 23; *b += *c;
246            *d -= *h; *a ^= *c << 15; *c += *d;
247            *e -= *a; *b ^= *d >> 14; *d += *e;
248            *f -= *b; *c ^= *e << 20; *e += *f;
249            *g -= *c; *d ^= *f >> 17; *f += *g;
250            *h -= *d; *e ^= *g << 14; *g += *h;
251        }
252
253        // These numbers are the result of initializing a...h with the
254        // fractional part of the golden ratio in binary (0x9e3779b97f4a7c13)
255        // and applying mix() 4 times.
256        let mut a = w(0x647c4677a2884b7c);
257        let mut b = w(0xb9f8b322c73ac862);
258        let mut c = w(0x8c0ea5053d4712a0);
259        let mut d = w(0xb29b2e824a595524);
260        let mut e = w(0x82f053db8355e0ce);
261        let mut f = w(0x48fe4a0fa5a09315);
262        let mut g = w(0xae985bf2cbfc89ed);
263        let mut h = w(0x98f5704f6c44c0ab);
264
265        // Normally this should do two passes, to make all of the seed effect
266        // all of `mem`
267        for _ in 0..rounds {
268            for i in (0..RAND_SIZE/8).map(|i| i * 8) {
269                a += mem[i  ]; b += mem[i+1];
270                c += mem[i+2]; d += mem[i+3];
271                e += mem[i+4]; f += mem[i+5];
272                g += mem[i+6]; h += mem[i+7];
273                mix(&mut a, &mut b, &mut c, &mut d,
274                    &mut e, &mut f, &mut g, &mut h);
275                mem[i  ] = a; mem[i+1] = b;
276                mem[i+2] = c; mem[i+3] = d;
277                mem[i+4] = e; mem[i+5] = f;
278                mem[i+6] = g; mem[i+7] = h;
279            }
280        }
281
282        Self { mem, a: w(0), b: w(0), c: w(0) }
283    }
284}
285
286impl SeedableRng for Isaac64Core {
287    type Seed = [u8; 32];
288
289    fn from_seed(seed: Self::Seed) -> Self {
290        let mut seed_u64 = [0u64; 4];
291        le::read_u64_into(&seed, &mut seed_u64);
292        // Convert the seed to `Wrapping<u64>` and zero-extend to `RAND_SIZE`.
293        let mut seed_extended = [w(0); RAND_SIZE];
294        for (x, y) in seed_extended.iter_mut().zip(seed_u64.iter()) {
295            *x = w(*y);
296        }
297        Self::init(seed_extended, 2)
298    }
299
300    fn seed_from_u64(seed: u64) -> Self {
301        let mut key = [w(0); RAND_SIZE];
302        key[0] = w(seed);
303        // Initialize with only one pass.
304        // A second pass does not improve the quality here, because all of the
305        // seed was already available in the first round.
306        // Not doing the second pass has the small advantage that if
307        // `seed == 0` this method produces exactly the same state as the
308        // reference implementation when used unseeded.
309        Self::init(key, 1)
310    }
311
312    fn from_rng<R: RngCore>(mut rng: R) -> Result<Self, Error> {
313        // Custom `from_rng` implementation that fills a seed with the same size
314        // as the entire state.
315        let mut seed = [w(0u64); RAND_SIZE];
316        unsafe {
317            let ptr = seed.as_mut_ptr() as *mut u8;
318            let slice = slice::from_raw_parts_mut(ptr, RAND_SIZE * 8);
319            rng.try_fill_bytes(slice)?;
320        }
321        for i in seed.iter_mut() {
322            *i = w(i.0.to_le());
323        }
324
325        Ok(Self::init(seed, 2))
326    }
327}
328
329#[cfg(test)]
330mod test {
331    use rand_core::{RngCore, SeedableRng};
332    use super::Isaac64Rng;
333
334    #[test]
335    fn test_isaac64_construction() {
336        // Test that various construction techniques produce a working RNG.
337        let seed = [1,0,0,0, 23,0,0,0, 200,1,0,0, 210,30,0,0,
338                    0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0];
339        let mut rng1 = Isaac64Rng::from_seed(seed);
340        assert_eq!(rng1.next_u64(), 14964555543728284049);
341
342        let mut rng2 = Isaac64Rng::from_rng(rng1).unwrap();
343        assert_eq!(rng2.next_u64(), 919595328260451758);
344    }
345
346    #[test]
347    fn test_isaac64_true_values_64() {
348        let seed = [1,0,0,0, 0,0,0,0, 23,0,0,0, 0,0,0,0,
349                    200,1,0,0, 0,0,0,0, 210,30,0,0, 0,0,0,0];
350        let mut rng1 = Isaac64Rng::from_seed(seed);
351        let mut results = [0u64; 10];
352        for i in results.iter_mut() { *i = rng1.next_u64(); }
353        let expected = [
354                   15071495833797886820, 7720185633435529318,
355                   10836773366498097981, 5414053799617603544,
356                   12890513357046278984, 17001051845652595546,
357                   9240803642279356310, 12558996012687158051,
358                   14673053937227185542, 1677046725350116783];
359        assert_eq!(results, expected);
360
361        let seed = [57,48,0,0, 0,0,0,0, 50,9,1,0, 0,0,0,0,
362                    49,212,0,0, 0,0,0,0, 148,38,0,0, 0,0,0,0];
363        let mut rng2 = Isaac64Rng::from_seed(seed);
364        // skip forward to the 10000th number
365        for _ in 0..10000 { rng2.next_u64(); }
366
367        for i in results.iter_mut() { *i = rng2.next_u64(); }
368        let expected = [
369            18143823860592706164, 8491801882678285927, 2699425367717515619,
370            17196852593171130876, 2606123525235546165, 15790932315217671084,
371            596345674630742204, 9947027391921273664, 11788097613744130851,
372            10391409374914919106];
373        assert_eq!(results, expected);
374    }
375
376    #[test]
377    fn test_isaac64_true_values_32() {
378        let seed = [1,0,0,0, 0,0,0,0, 23,0,0,0, 0,0,0,0,
379                    200,1,0,0, 0,0,0,0, 210,30,0,0, 0,0,0,0];
380        let mut rng = Isaac64Rng::from_seed(seed);
381        let mut results = [0u32; 12];
382        for i in results.iter_mut() { *i = rng.next_u32(); }
383        // Subset of above values, as an LE u32 sequence
384        let expected = [
385                    3477963620, 3509106075,
386                    687845478, 1797495790,
387                    227048253, 2523132918,
388                    4044335064, 1260557630,
389                    4079741768, 3001306521,
390                    69157722, 3958365844];
391        assert_eq!(results, expected);
392    }
393
394    #[test]
395    fn test_isaac64_true_values_mixed() {
396        let seed = [1,0,0,0, 0,0,0,0, 23,0,0,0, 0,0,0,0,
397                    200,1,0,0, 0,0,0,0, 210,30,0,0, 0,0,0,0];
398        let mut rng = Isaac64Rng::from_seed(seed);
399        // Test alternating between `next_u64` and `next_u32` works as expected.
400        // Values are the same as `test_isaac64_true_values` and
401        // `test_isaac64_true_values_32`.
402        assert_eq!(rng.next_u64(), 15071495833797886820);
403        assert_eq!(rng.next_u32(), 687845478);
404        assert_eq!(rng.next_u32(), 1797495790);
405        assert_eq!(rng.next_u64(), 10836773366498097981);
406        assert_eq!(rng.next_u32(), 4044335064);
407        // Skip one u32
408        assert_eq!(rng.next_u64(), 12890513357046278984);
409        assert_eq!(rng.next_u32(), 69157722);
410    }
411
412    #[test]
413    fn test_isaac64_true_bytes() {
414        let seed = [1,0,0,0, 0,0,0,0, 23,0,0,0, 0,0,0,0,
415                    200,1,0,0, 0,0,0,0, 210,30,0,0, 0,0,0,0];
416        let mut rng = Isaac64Rng::from_seed(seed);
417        let mut results = [0u8; 32];
418        rng.fill_bytes(&mut results);
419        // Same as first values in test_isaac64_true_values as bytes in LE order
420        let expected = [100, 131, 77, 207, 155, 181, 40, 209,
421                        102, 176, 255, 40, 238, 155, 35, 107,
422                        61, 123, 136, 13, 246, 243, 99, 150,
423                        216, 167, 15, 241, 62, 149, 34, 75];
424        assert_eq!(results, expected);
425    }
426
427    #[test]
428    fn test_isaac64_new_uninitialized() {
429        // Compare the results from initializing `IsaacRng` with
430        // `seed_from_u64(0)`, to make sure it is the same as the reference
431        // implementation when used uninitialized.
432        // Note: We only test the first 16 integers, not the full 256 of the
433        // first block.
434        let mut rng = Isaac64Rng::seed_from_u64(0);
435        let mut results = [0u64; 16];
436        for i in results.iter_mut() { *i = rng.next_u64(); }
437        let expected: [u64; 16] = [
438            0xF67DFBA498E4937C, 0x84A5066A9204F380, 0xFEE34BD5F5514DBB,
439            0x4D1664739B8F80D6, 0x8607459AB52A14AA, 0x0E78BC5A98529E49,
440            0xFE5332822AD13777, 0x556C27525E33D01A, 0x08643CA615F3149F,
441            0xD0771FAF3CB04714, 0x30E86F68A37B008D, 0x3074EBC0488A3ADF,
442            0x270645EA7A2790BC, 0x5601A0A8D3763C6A, 0x2F83071F53F325DD,
443            0xB9090F3D42D2D2EA];
444        assert_eq!(results, expected);
445    }
446
447    #[test]
448    fn test_isaac64_clone() {
449        let seed = [1,0,0,0, 0,0,0,0, 23,0,0,0, 0,0,0,0,
450                    200,1,0,0, 0,0,0,0, 210,30,0,0, 0,0,0,0];
451        let mut rng1 = Isaac64Rng::from_seed(seed);
452        let mut rng2 = rng1.clone();
453        for _ in 0..16 {
454            assert_eq!(rng1.next_u64(), rng2.next_u64());
455        }
456    }
457
458    #[test]
459    #[cfg(feature="serde1")]
460    fn test_isaac64_serde() {
461        use bincode;
462        use std::io::{BufWriter, BufReader};
463
464        let seed = [1,0,0,0, 23,0,0,0, 200,1,0,0, 210,30,0,0,
465                     57,48,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0];
466        let mut rng = Isaac64Rng::from_seed(seed);
467
468        let buf: Vec<u8> = Vec::new();
469        let mut buf = BufWriter::new(buf);
470        bincode::serialize_into(&mut buf, &rng).expect("Could not serialize");
471
472        let buf = buf.into_inner().unwrap();
473        let mut read = BufReader::new(&buf[..]);
474        let mut deserialized: Isaac64Rng = bincode::deserialize_from(&mut read).expect("Could not deserialize");
475
476        for _ in 0..300 { // more than the 256 buffered results
477            assert_eq!(rng.next_u64(), deserialized.next_u64());
478        }
479    }
480}