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}