rand_xoshiro/
xoshiro512plusplus.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::{RngCore, SeedableRng};
12#[cfg(feature = "serde")]
13use serde::{Deserialize, Serialize};
14
15use crate::Seed512;
16
17/// A xoshiro512++ random number generator.
18///
19/// The xoshiro512++ algorithm is not suitable for cryptographic purposes, but
20/// is very fast and has excellent statistical properties.
21///
22/// The algorithm used here is translated from [the `xoshiro512plusplus.c`
23/// reference source code](http://xoshiro.di.unimi.it/xoshiro512plusplus.c) by
24/// David Blackman and Sebastiano Vigna.
25#[derive(Debug, Clone, PartialEq, Eq)]
26#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
27pub struct Xoshiro512PlusPlus {
28    s: [u64; 8],
29}
30
31impl Xoshiro512PlusPlus {
32    /// Jump forward, equivalently to 2^256 calls to `next_u64()`.
33    ///
34    /// This can be used to generate 2^256 non-overlapping subsequences for
35    /// parallel computations.
36    ///
37    /// ```
38    /// use rand_xoshiro::rand_core::SeedableRng;
39    /// use rand_xoshiro::Xoshiro512PlusPlus;
40    ///
41    /// let rng1 = Xoshiro512PlusPlus::seed_from_u64(0);
42    /// let mut rng2 = rng1.clone();
43    /// rng2.jump();
44    /// let mut rng3 = rng2.clone();
45    /// rng3.jump();
46    /// ```
47    pub fn jump(&mut self) {
48        impl_jump!(
49            u64,
50            self,
51            [
52                0x33ed89b6e7a353f9,
53                0x760083d7955323be,
54                0x2837f2fbb5f22fae,
55                0x4b8c5674d309511c,
56                0xb11ac47a7ba28c25,
57                0xf1be7667092bcc1c,
58                0x53851efdb6df0aaf,
59                0x1ebbc8b23eaf25db
60            ]
61        );
62    }
63
64    /// Jump forward, equivalently to 2^384 calls to `next_u64()`.
65    ///
66    /// This can be used to generate 2^128 starting points, from each of which
67    /// `jump()` will generate 2^128 non-overlapping subsequences for parallel
68    /// distributed computations.
69    pub fn long_jump(&mut self) {
70        impl_jump!(
71            u64,
72            self,
73            [
74                0x11467fef8f921d28,
75                0xa2a819f2e79c8ea8,
76                0xa8299fc284b3959a,
77                0xb4d347340ca63ee1,
78                0x1cb0940bedbff6ce,
79                0xd956c5c4fa1f8e17,
80                0x915e38fd4eda93bc,
81                0x5b3ccdfa5d7daca5
82            ]
83        );
84    }
85}
86
87impl SeedableRng for Xoshiro512PlusPlus {
88    type Seed = Seed512;
89
90    /// Create a new `Xoshiro512PlusPlus`.  If `seed` is entirely 0, it will be
91    /// mapped to a different seed.
92    #[inline]
93    fn from_seed(seed: Seed512) -> Xoshiro512PlusPlus {
94        deal_with_zero_seed!(seed, Self);
95        let mut state = [0; 8];
96        read_u64_into(&seed.0, &mut state);
97        Xoshiro512PlusPlus { s: state }
98    }
99
100    /// Seed a `Xoshiro512PlusPlus` from a `u64` using `SplitMix64`.
101    fn seed_from_u64(seed: u64) -> Xoshiro512PlusPlus {
102        from_splitmix!(seed)
103    }
104}
105
106impl RngCore for Xoshiro512PlusPlus {
107    #[inline]
108    fn next_u32(&mut self) -> u32 {
109        // The lowest bits have some linear dependencies, so we use the
110        // upper bits instead.
111        (self.next_u64() >> 32) as u32
112    }
113
114    #[inline]
115    fn next_u64(&mut self) -> u64 {
116        let result_plusplus = plusplus_u64!(self.s[2], self.s[0], 17);
117        impl_xoshiro_large!(self);
118        result_plusplus
119    }
120
121    #[inline]
122    fn fill_bytes(&mut self, dest: &mut [u8]) {
123        fill_bytes_via_next(self, dest);
124    }
125}
126
127#[cfg(test)]
128mod tests {
129    use super::*;
130
131    #[test]
132    fn reference() {
133        let mut rng = Xoshiro512PlusPlus::from_seed(Seed512([
134            1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0,
135            0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 8, 0,
136            0, 0, 0, 0, 0, 0,
137        ]));
138        // These values were produced with the reference implementation:
139        // http://xoshiro.di.unimi.it/xoshiro512plusplus.c
140        let expected = [
141            524291,
142            1048578,
143            539099140,
144            3299073855497,
145            6917532603230064654,
146            7494048333530275843,
147            14418333309547923463,
148            10960079161595355914,
149            18279570946505382726,
150            10209173166699159237,
151        ];
152        for &e in &expected {
153            assert_eq!(rng.next_u64(), e);
154        }
155    }
156}