Skip to main content

rand_xoshiro/
xoshiro256plus.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 core::convert::Infallible;
10use rand_core::{Rng, SeedableRng, TryRng, utils};
11#[cfg(feature = "serde")]
12use serde::{Deserialize, Serialize};
13
14/// A xoshiro256+ random number generator.
15///
16/// The xoshiro256+ algorithm is not suitable for cryptographic purposes, but
17/// is very fast and has good statistical properties, besides a low linear
18/// complexity in the lowest bits.
19///
20/// The algorithm used here is translated from [the `xoshiro256plus.c`
21/// reference source code](http://xoshiro.di.unimi.it/xoshiro256plus.c) by
22/// David Blackman and Sebastiano Vigna.
23#[derive(Debug, Clone, PartialEq, Eq)]
24#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
25pub struct Xoshiro256Plus {
26    s: [u64; 4],
27}
28
29impl Xoshiro256Plus {
30    /// Jump forward, equivalently to 2^128 calls to `next_u64()`.
31    ///
32    /// This can be used to generate 2^128 non-overlapping subsequences for
33    /// parallel computations.
34    ///
35    /// ```
36    /// use rand_xoshiro::rand_core::SeedableRng;
37    /// use rand_xoshiro::Xoshiro256Plus;
38    ///
39    /// let rng1 = Xoshiro256Plus::seed_from_u64(0);
40    /// let mut rng2 = rng1.clone();
41    /// rng2.jump();
42    /// let mut rng3 = rng2.clone();
43    /// rng3.jump();
44    /// ```
45    pub fn jump(&mut self) {
46        impl_jump!(
47            u64,
48            self,
49            [
50                0x180ec6d33cfd0aba,
51                0xd5a61266f0c9392c,
52                0xa9582618e03fc9aa,
53                0x39abdc4529b1661c
54            ]
55        );
56    }
57
58    /// Jump forward, equivalently to 2^192 calls to `next_u64()`.
59    ///
60    /// This can be used to generate 2^64 starting points, from each of which
61    /// `jump()` will generate 2^64 non-overlapping subsequences for parallel
62    /// distributed computations.
63    pub fn long_jump(&mut self) {
64        impl_jump!(
65            u64,
66            self,
67            [
68                0x76e15d3efefdcbbf,
69                0xc5004e441c522fb3,
70                0x77710069854ee241,
71                0x39109bb02acbe635
72            ]
73        );
74    }
75}
76
77impl_state_array_of_four!(Xoshiro256Plus, u64);
78
79impl SeedableRng for Xoshiro256Plus {
80    type Seed = [u8; 32];
81
82    /// Create a new `Xoshiro256Plus`.  If `seed` is entirely 0, it will be
83    /// mapped to a different seed.
84    #[inline]
85    fn from_seed(seed: [u8; 32]) -> Xoshiro256Plus {
86        Xoshiro256Plus {
87            s: utils::read_words(crate::common::zero_seed_fallback(&seed)),
88        }
89    }
90
91    /// Seed a `Xoshiro256Plus` from a `u64` using `SplitMix64`.
92    fn seed_from_u64(seed: u64) -> Xoshiro256Plus {
93        from_splitmix!(seed)
94    }
95}
96
97impl TryRng for Xoshiro256Plus {
98    type Error = Infallible;
99
100    #[inline]
101    fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
102        // The lowest bits have some linear dependencies, so we use the
103        // upper bits instead.
104        Ok((self.next_u64() >> 32) as u32)
105    }
106
107    #[inline]
108    fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
109        let result_plus = self.s[0].wrapping_add(self.s[3]);
110        impl_xoshiro_u64!(self);
111        Ok(result_plus)
112    }
113
114    #[inline]
115    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Self::Error> {
116        utils::fill_bytes_via_next_word(dest, || self.try_next_u64())
117    }
118}
119
120#[cfg(test)]
121mod tests {
122    use super::*;
123
124    #[test]
125    fn reference() {
126        let mut rng = Xoshiro256Plus::from_seed([
127            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,
128            0, 0, 0,
129        ]);
130        // These values were produced with the reference implementation:
131        // http://xoshiro.di.unimi.it/xoshiro256plus.c
132        let expected = [
133            5,
134            211106232532999,
135            211106635186183,
136            9223759065350669058,
137            9250833439874351877,
138            13862484359527728515,
139            2346507365006083650,
140            1168864526675804870,
141            34095955243042024,
142            3466914240207415127,
143        ];
144        for &e in &expected {
145            assert_eq!(rng.next_u64(), e);
146        }
147    }
148
149    #[test]
150    fn zero_seed_maps_to_seed_from_u64_zero() {
151        let from_zero = Xoshiro256Plus::from_seed([0u8; 32]);
152        let from_sm0 = Xoshiro256Plus::seed_from_u64(0);
153        assert_eq!(from_zero, from_sm0);
154    }
155
156    #[test]
157    fn state_roundtrip() {
158        let rng = Xoshiro256Plus::seed_from_u64(42);
159        let clone = Xoshiro256Plus::from_seed(rng.state());
160        assert_eq!(clone, rng);
161    }
162}