Skip to main content

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