rand_xoshiro/
xoshiro512plus.rs

1// Copyright 2018 Developers of the Rand project.
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9use rand_core::impls::fill_bytes_via_next;
10use rand_core::le::read_u64_into;
11use rand_core::{SeedableRng, RngCore, Error};
12
13use Seed512;
14
15/// A xoshiro512+ random number generator.
16///
17/// The xoshiro512+ algorithm is not suitable for cryptographic purposes, but
18/// is very fast and has good statistical properties, besides a low linear
19/// complexity in the lowest bits.
20///
21/// The algorithm used here is translated from [the `xoshiro512plus.c`
22/// reference source code](http://xoshiro.di.unimi.it/xoshiro512plus.c) by
23/// David Blackman and Sebastiano Vigna.
24#[derive(Debug, Clone)]
25pub struct Xoshiro512Plus {
26    s: [u64; 8],
27}
28
29impl Xoshiro512Plus {
30    /// Jump forward, equivalently to 2^256 calls to `next_u64()`.
31    ///
32    /// This can be used to generate 2^256 non-overlapping subsequences for
33    /// parallel computations.
34    ///
35    /// ```
36    /// # extern crate rand;
37    /// # extern crate rand_xoshiro;
38    /// # fn main() {
39    /// use rand::SeedableRng;
40    /// use rand_xoshiro::Xoshiro512Plus;
41    ///
42    /// let rng1 = Xoshiro512Plus::seed_from_u64(0);
43    /// let mut rng2 = rng1.clone();
44    /// rng2.jump();
45    /// let mut rng3 = rng2.clone();
46    /// rng3.jump();
47    /// # }
48    /// ```
49    pub fn jump(&mut self) {
50        impl_jump!(u64, self, [
51            0x33ed89b6e7a353f9, 0x760083d7955323be, 0x2837f2fbb5f22fae,
52            0x4b8c5674d309511c, 0xb11ac47a7ba28c25, 0xf1be7667092bcc1c,
53            0x53851efdb6df0aaf, 0x1ebbc8b23eaf25db
54        ]);
55    }
56}
57
58impl SeedableRng for Xoshiro512Plus {
59    type Seed = Seed512;
60
61    /// Create a new `Xoshiro512Plus`.  If `seed` is entirely 0, it will be
62    /// mapped to a different seed.
63    #[inline]
64    fn from_seed(seed: Seed512) -> Xoshiro512Plus {
65        deal_with_zero_seed!(seed, Self);
66        let mut state = [0; 8];
67        read_u64_into(&seed.0, &mut state);
68        Xoshiro512Plus { s: state }
69    }
70
71    /// Seed a `Xoshiro512Plus` from a `u64` using `SplitMix64`.
72    fn seed_from_u64(seed: u64) -> Xoshiro512Plus {
73        from_splitmix!(seed)
74    }
75}
76
77impl RngCore for Xoshiro512Plus {
78    #[inline]
79    fn next_u32(&mut self) -> u32 {
80        self.next_u64() as u32
81    }
82
83    #[inline]
84    fn next_u64(&mut self) -> u64 {
85        let result_plus = self.s[0].wrapping_add(self.s[2]);
86        impl_xoshiro_large!(self);
87        result_plus
88    }
89
90    #[inline]
91    fn fill_bytes(&mut self, dest: &mut [u8]) {
92        fill_bytes_via_next(self, dest);
93    }
94
95    #[inline]
96    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
97        self.fill_bytes(dest);
98        Ok(())
99    }
100}
101
102#[cfg(test)]
103mod tests {
104    use super::*;
105
106    #[test]
107    fn reference() {
108        let mut rng = Xoshiro512Plus::from_seed(Seed512(
109            [1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0,
110             3, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0,
111             5, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0,
112             7, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0]));
113        // These values were produced with the reference implementation:
114        // http://xoshiro.di.unimi.it/xoshiro512plus.c
115        let expected = [
116            4, 8, 4113, 25169936, 52776585412635, 57174648719367,
117            9223482039571869716, 9331471677901559830, 9340533895746033672,
118            14078399799840753678,
119        ];
120        for &e in &expected {
121            assert_eq!(rng.next_u64(), e);
122        }
123    }
124}