Skip to main content

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