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 rand_core::impls::fill_bytes_via_next;
10use rand_core::le::read_u64_into;
11use rand_core::{RngCore, SeedableRng};
12#[cfg(feature = "serde")]
13use serde::{Deserialize, Serialize};
14
15/// A xoshiro256+ random number generator.
16///
17/// The xoshiro256+ algorithm is not suitable for cryptographic purposes, but
18/// is very fast and has good statistical properties, besides a low linear
19/// complexity in the lowest bits.
20///
21/// The algorithm used here is translated from [the `xoshiro256plus.c`
22/// reference source code](http://xoshiro.di.unimi.it/xoshiro256plus.c) by
23/// David Blackman and Sebastiano Vigna.
24#[derive(Debug, Clone, PartialEq, Eq)]
25#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
26pub struct Xoshiro256Plus {
27 s: [u64; 4],
28}
29
30impl Xoshiro256Plus {
31 /// Jump forward, equivalently to 2^128 calls to `next_u64()`.
32 ///
33 /// This can be used to generate 2^128 non-overlapping subsequences for
34 /// parallel computations.
35 ///
36 /// ```
37 /// use rand_xoshiro::rand_core::SeedableRng;
38 /// use rand_xoshiro::Xoshiro256Plus;
39 ///
40 /// let rng1 = Xoshiro256Plus::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 0x180ec6d33cfd0aba,
52 0xd5a61266f0c9392c,
53 0xa9582618e03fc9aa,
54 0x39abdc4529b1661c
55 ]
56 );
57 }
58
59 /// Jump forward, equivalently to 2^192 calls to `next_u64()`.
60 ///
61 /// This can be used to generate 2^64 starting points, from each of which
62 /// `jump()` will generate 2^64 non-overlapping subsequences for parallel
63 /// distributed computations.
64 pub fn long_jump(&mut self) {
65 impl_jump!(
66 u64,
67 self,
68 [
69 0x76e15d3efefdcbbf,
70 0xc5004e441c522fb3,
71 0x77710069854ee241,
72 0x39109bb02acbe635
73 ]
74 );
75 }
76}
77
78impl SeedableRng for Xoshiro256Plus {
79 type Seed = [u8; 32];
80
81 /// Create a new `Xoshiro256Plus`. If `seed` is entirely 0, it will be
82 /// mapped to a different seed.
83 #[inline]
84 fn from_seed(seed: [u8; 32]) -> Xoshiro256Plus {
85 deal_with_zero_seed!(seed, Self);
86 let mut state = [0; 4];
87 read_u64_into(&seed, &mut state);
88 Xoshiro256Plus { s: state }
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 RngCore for Xoshiro256Plus {
98 #[inline]
99 fn next_u32(&mut self) -> u32 {
100 // The lowest bits have some linear dependencies, so we use the
101 // upper bits instead.
102 (self.next_u64() >> 32) as u32
103 }
104
105 #[inline]
106 fn next_u64(&mut self) -> u64 {
107 let result_plus = self.s[0].wrapping_add(self.s[3]);
108 impl_xoshiro_u64!(self);
109 result_plus
110 }
111
112 #[inline]
113 fn fill_bytes(&mut self, dest: &mut [u8]) {
114 fill_bytes_via_next(self, dest);
115 }
116}
117
118#[cfg(test)]
119mod tests {
120 use super::*;
121
122 #[test]
123 fn reference() {
124 let mut rng = Xoshiro256Plus::from_seed([
125 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,
126 0, 0, 0,
127 ]);
128 // These values were produced with the reference implementation:
129 // http://xoshiro.di.unimi.it/xoshiro256plus.c
130 let expected = [
131 5,
132 211106232532999,
133 211106635186183,
134 9223759065350669058,
135 9250833439874351877,
136 13862484359527728515,
137 2346507365006083650,
138 1168864526675804870,
139 34095955243042024,
140 3466914240207415127,
141 ];
142 for &e in &expected {
143 assert_eq!(rng.next_u64(), e);
144 }
145 }
146}