// Copyright 2018 Developers of the Rand project. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use core::convert::Infallible; use rand_core::{Rng, SeedableRng, TryRng, utils}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; /// A xoshiro256** random number generator. /// /// The xoshiro256** algorithm is not suitable for cryptographic purposes, but /// is very fast and has excellent statistical properties. /// /// The algorithm used here is translated from [the `xoshiro256starstar.c` /// reference source code](http://xoshiro.di.unimi.it/xoshiro256starstar.c) by /// David Blackman and Sebastiano Vigna. #[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Xoshiro256StarStar { s: [u64; 4], } impl Xoshiro256StarStar { /// Jump forward, equivalently to 2^128 calls to `next_u64()`. /// /// This can be used to generate 2^128 non-overlapping subsequences for /// parallel computations. /// /// ``` /// use rand_xoshiro::rand_core::SeedableRng; /// use rand_xoshiro::Xoshiro256StarStar; /// /// let rng1 = Xoshiro256StarStar::seed_from_u64(0); /// let mut rng2 = rng1.clone(); /// rng2.jump(); /// let mut rng3 = rng2.clone(); /// rng3.jump(); /// ``` pub fn jump(&mut self) { impl_jump!( u64, self, [ 0x180ec6d33cfd0aba, 0xd5a61266f0c9392c, 0xa9582618e03fc9aa, 0x39abdc4529b1661c ] ); } /// Jump forward, equivalently to 2^192 calls to `next_u64()`. /// /// This can be used to generate 2^64 starting points, from each of which /// `jump()` will generate 2^64 non-overlapping subsequences for parallel /// distributed computations. pub fn long_jump(&mut self) { impl_jump!( u64, self, [ 0x76e15d3efefdcbbf, 0xc5004e441c522fb3, 0x77710069854ee241, 0x39109bb02acbe635 ] ); } /// Jump forward by c · 2^e calls to `next_u64()`. /// /// For example, `jump_ce(1, 128)` is equivalent to [`jump`](Self::jump) /// and `jump_ce(1, 192)` is equivalent to [`long_jump`](Self::long_jump). /// Expressing the distance as c · 2^e makes it possible to request both /// ordinary counts (`jump_ce(k, 0)`) and very large power-of-two jumps /// without multiple-precision integers. For the jump to be meaningful, /// c · 2^e should be smaller than the period 2^256 − 1. /// /// See [`jump_n`](Self::jump_n) to jump by an arbitrary distance. pub fn jump_ce(&mut self, c: u64, e: u32) { impl_jump_ce!( u64, self, [ 0x9d116f2bb0f0f001, 0x0280002bcefd1a5e, 0x04b4edcf26259f85, 0x0003c03c3f3ecb19 ], c, e, array4 ); } /// Jump forward by an arbitrary number of calls to `next_u64()`. /// /// This is equivalent to *n* calls to `next_u64()`, where *n* = `jump[0]` + /// `jump[1]` · 2^64 + … is the little-endian integer held in `jump`. /// Unlike [`jump_ce`](Self::jump_ce), it can express any jump distance. /// For the jump to be meaningful, *n* should be smaller than the period /// 2^256 − 1. pub fn jump_n(&mut self, jump: &[u64; 4]) { impl_jump_n!( u64, self, [ 0x9d116f2bb0f0f001, 0x0280002bcefd1a5e, 0x04b4edcf26259f85, 0x0003c03c3f3ecb19 ], jump, array4 ); } } impl_state_array_of_four!(Xoshiro256StarStar, u64); impl SeedableRng for Xoshiro256StarStar { type Seed = [u8; 32]; /// Create a new `Xoshiro256StarStar`. If `seed` is entirely 0, it will be /// mapped to a different seed. #[inline] fn from_seed(seed: [u8; 32]) -> Xoshiro256StarStar { Xoshiro256StarStar { s: utils::read_words(crate::common::zero_seed_fallback(&seed)), } } /// Seed a `Xoshiro256StarStar` from a `u64` using `SplitMix64`. fn seed_from_u64(seed: u64) -> Xoshiro256StarStar { from_splitmix!(seed) } } impl TryRng for Xoshiro256StarStar { type Error = Infallible; #[inline] fn try_next_u32(&mut self) -> Result { // The lowest bits have some linear dependencies, so we use the // upper bits instead. Ok((self.next_u64() >> 32) as u32) } #[inline] fn try_next_u64(&mut self) -> Result { let result_starstar = starstar_u64!(self.s[1]); impl_xoshiro_u64!(self); Ok(result_starstar) } #[inline] fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Self::Error> { utils::fill_bytes_via_next_word(dest, || self.try_next_u64()) } } #[cfg(test)] mod tests { use super::*; fn fresh() -> Xoshiro256StarStar { Xoshiro256StarStar::seed_from_u64(0x0123456789abcdef) } #[test] #[cfg_attr(miri, ignore)] // Miri is too slow fn jump_ce_small_distances_match_stepping() { for &d in &[0, 1, 2, 3, 7, 64, 1000, 1_000_000] { let mut a = fresh(); for _ in 0..d { a.next_u64(); } let mut b = fresh(); b.jump_ce(d, 0); assert_eq!(a, b, "jump_ce({d}, 0)"); } let mut a = fresh(); for _ in 0..3 * 256 { a.next_u64(); } let mut b = fresh(); b.jump_ce(3, 8); assert_eq!(a, b, "jump_ce(3, 8)"); } #[test] #[cfg_attr(miri, ignore)] // Miri is too slow fn jump_ce_matches_predefined_jumps() { let mut a = fresh(); a.jump(); let mut b = fresh(); b.jump_ce(1, 128); assert_eq!(a, b, "jump_ce(1,128) == jump()"); let mut a = fresh(); a.long_jump(); let mut b = fresh(); b.jump_ce(1, 192); assert_eq!(a, b, "jump_ce(1,192) == long_jump()"); } #[test] #[cfg_attr(miri, ignore)] // Miri is too slow fn jump_n_matches_jump_ce() { // jump_n(&[d, 0, …]) == jump_ce(d, 0) for a single-word distance. for &d in &[0, 1, 2, 3, 7, 64, 1000, 1_000_000] { let mut a = fresh(); a.jump_ce(d, 0); let mut b = fresh(); b.jump_n(&[d, 0, 0, 0]); assert_eq!(a, b, "jump_n(&[{d}, …])"); } // A distance that needs a high limb: 2^128 == jump(). let mut a = fresh(); a.jump(); let mut b = fresh(); b.jump_n(&[0, 0, 1, 0]); assert_eq!(a, b, "jump_n(2^128) == jump()"); // A distance jump_ce cannot express (odd part exceeds 64 bits): // 3 + 5 · 2^64, checked via x^(a + b) = x^a · x^b. let mut a = fresh(); a.jump_ce(3, 0); a.jump_ce(5, 64); let mut b = fresh(); b.jump_n(&[3, 5, 0, 0]); assert_eq!(a, b, "jump_n(3 + 5 · 2^64)"); } #[test] fn reference() { let mut rng = Xoshiro256StarStar::from_seed([ 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, 0, 0, 0, ]); // These values were produced with the reference implementation: // http://xoshiro.di.unimi.it/xoshiro128starstar.c let expected = [ 11520, 0, 1509978240, 1215971899390074240, 1216172134540287360, 607988272756665600, 16172922978634559625, 8476171486693032832, 10595114339597558777, 2904607092377533576, ]; for &e in &expected { assert_eq!(rng.next_u64(), e); } } #[test] fn zero_seed_maps_to_seed_from_u64_zero() { let from_zero = Xoshiro256StarStar::from_seed([0u8; 32]); let from_sm0 = Xoshiro256StarStar::seed_from_u64(0); assert_eq!(from_zero, from_sm0); } #[test] fn state_roundtrip() { let rng = Xoshiro256StarStar::seed_from_u64(42); let clone = Xoshiro256StarStar::from_seed(rng.state()); assert_eq!(clone, rng); } }