Skip to main content

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