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