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
9#[cfg(feature="serde1")] use serde::{Serialize, Deserialize};
10use rand_core::impls::fill_bytes_via_next;
11use rand_core::le::read_u64_into;
12use rand_core::{SeedableRng, RngCore, Error};
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="serde1", 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!(u64, self, [
48 0x33ed89b6e7a353f9, 0x760083d7955323be, 0x2837f2fbb5f22fae,
49 0x4b8c5674d309511c, 0xb11ac47a7ba28c25, 0xf1be7667092bcc1c,
50 0x53851efdb6df0aaf, 0x1ebbc8b23eaf25db
51 ]);
52 }
53
54 /// Jump forward, equivalently to 2^384 calls to `next_u64()`.
55 ///
56 /// This can be used to generate 2^128 starting points, from each of which
57 /// `jump()` will generate 2^128 non-overlapping subsequences for parallel
58 /// distributed computations.
59 pub fn long_jump(&mut self) {
60 impl_jump!(u64, self, [
61 0x11467fef8f921d28, 0xa2a819f2e79c8ea8, 0xa8299fc284b3959a,
62 0xb4d347340ca63ee1, 0x1cb0940bedbff6ce, 0xd956c5c4fa1f8e17,
63 0x915e38fd4eda93bc, 0x5b3ccdfa5d7daca5
64 ]);
65 }
66}
67
68
69impl SeedableRng for Xoshiro512PlusPlus {
70 type Seed = Seed512;
71
72 /// Create a new `Xoshiro512PlusPlus`. If `seed` is entirely 0, it will be
73 /// mapped to a different seed.
74 #[inline]
75 fn from_seed(seed: Seed512) -> Xoshiro512PlusPlus {
76 deal_with_zero_seed!(seed, Self);
77 let mut state: [u64; 8] = [0; 8];
78 read_u64_into(&seed.0, &mut state);
79 Xoshiro512PlusPlus { s: state }
80 }
81
82 /// Seed a `Xoshiro512PlusPlus` from a `u64` using `SplitMix64`.
83 fn seed_from_u64(seed: u64) -> Xoshiro512PlusPlus {
84 from_splitmix!(seed)
85 }
86}
87
88impl RngCore for Xoshiro512PlusPlus {
89 #[inline]
90 fn next_u32(&mut self) -> u32 {
91 // The lowest bits have some linear dependencies, so we use the
92 // upper bits instead.
93 (self.next_u64() >> 32) as u32
94 }
95
96 #[inline]
97 fn next_u64(&mut self) -> u64 {
98 let result_plusplus = plusplus_u64!(self.s[2], self.s[0], 17);
99 impl_xoshiro_large!(self);
100 result_plusplus
101 }
102
103 #[inline]
104 fn fill_bytes(&mut self, dest: &mut [u8]) {
105 fill_bytes_via_next(self, dest);
106 }
107
108 #[inline]
109 fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
110 self.fill_bytes(dest);
111 Ok(())
112 }
113}
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118
119 #[test]
120 fn reference() {
121 let mut rng = Xoshiro512PlusPlus::from_seed(Seed512(
122 [1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0,
123 3, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0,
124 5, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0,
125 7, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0]));
126 // These values were produced with the reference implementation:
127 // http://xoshiro.di.unimi.it/xoshiro512plusplus.c
128 let expected = [
129 524291, 1048578, 539099140, 3299073855497, 6917532603230064654,
130 7494048333530275843, 14418333309547923463, 10960079161595355914,
131 18279570946505382726, 10209173166699159237,
132 ];
133 for &e in &expected {
134 assert_eq!(rng.next_u64(), e);
135 }
136 }
137}
138