Skip to main content

rand_xoshiro/
xoshiro256starstar.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 `xoshiro256starstar.c`
20/// reference source code](http://xoshiro.di.unimi.it/xoshiro256starstar.c) by
21/// David Blackman and Sebastiano Vigna.
22#[derive(Debug, Clone, PartialEq, Eq)]
23#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
24pub struct Xoshiro256StarStar {
25    s: [u64; 4],
26}
27
28impl Xoshiro256StarStar {
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::Xoshiro256StarStar;
37    ///
38    /// let rng1 = Xoshiro256StarStar::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!(Xoshiro256StarStar, u64);
77
78impl SeedableRng for Xoshiro256StarStar {
79    type Seed = [u8; 32];
80
81    /// Create a new `Xoshiro256StarStar`.  If `seed` is entirely 0, it will be
82    /// mapped to a different seed.
83    #[inline]
84    fn from_seed(seed: [u8; 32]) -> Xoshiro256StarStar {
85        Xoshiro256StarStar {
86            s: utils::read_words(crate::common::zero_seed_fallback(&seed)),
87        }
88    }
89
90    /// Seed a `Xoshiro256StarStar` from a `u64` using `SplitMix64`.
91    fn seed_from_u64(seed: u64) -> Xoshiro256StarStar {
92        from_splitmix!(seed)
93    }
94}
95
96impl TryRng for Xoshiro256StarStar {
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_starstar = starstar_u64!(self.s[1]);
109        impl_xoshiro_u64!(self);
110        Ok(result_starstar)
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 = Xoshiro256StarStar::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/xoshiro128starstar.c
131        let expected = [
132            11520,
133            0,
134            1509978240,
135            1215971899390074240,
136            1216172134540287360,
137            607988272756665600,
138            16172922978634559625,
139            8476171486693032832,
140            10595114339597558777,
141            2904607092377533576,
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 = Xoshiro256StarStar::from_seed([0u8; 32]);
151        let from_sm0 = Xoshiro256StarStar::seed_from_u64(0);
152        assert_eq!(from_zero, from_sm0);
153    }
154
155    #[test]
156    fn state_roundtrip() {
157        let rng = Xoshiro256StarStar::seed_from_u64(42);
158        let clone = Xoshiro256StarStar::from_seed(rng.state());
159        assert_eq!(clone, rng);
160    }
161}