Coverage Report

Created: 2025-10-28 06:54

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/rand-0.9.2/src/rngs/xoshiro256plusplus.rs
Line
Count
Source
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
use rand_core::impls::fill_bytes_via_next;
10
use rand_core::le::read_u64_into;
11
use rand_core::{RngCore, SeedableRng};
12
#[cfg(feature = "serde")]
13
use 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 excellent statistical properties.
19
///
20
/// The algorithm used here is translated from [the `xoshiro256plusplus.c`
21
/// reference source code](http://xoshiro.di.unimi.it/xoshiro256plusplus.c) by
22
/// David Blackman and Sebastiano Vigna.
23
#[derive(Debug, Clone, PartialEq, Eq)]
24
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
25
pub struct Xoshiro256PlusPlus {
26
    s: [u64; 4],
27
}
28
29
impl SeedableRng for Xoshiro256PlusPlus {
30
    type Seed = [u8; 32];
31
32
    /// Create a new `Xoshiro256PlusPlus`.  If `seed` is entirely 0, it will be
33
    /// mapped to a different seed.
34
    #[inline]
35
0
    fn from_seed(seed: [u8; 32]) -> Xoshiro256PlusPlus {
36
0
        let mut state = [0; 4];
37
0
        read_u64_into(&seed, &mut state);
38
        // Check for zero on aligned integers for better code generation.
39
        // Furtermore, seed_from_u64(0) will expand to a constant when optimized.
40
0
        if state.iter().all(|&x| x == 0) {
41
0
            return Self::seed_from_u64(0);
42
0
        }
43
0
        Xoshiro256PlusPlus { s: state }
44
0
    }
45
46
    /// Create a new `Xoshiro256PlusPlus` from a `u64` seed.
47
    ///
48
    /// This uses the SplitMix64 generator internally.
49
    #[inline]
50
0
    fn seed_from_u64(mut state: u64) -> Self {
51
        const PHI: u64 = 0x9e3779b97f4a7c15;
52
0
        let mut s = [0; 4];
53
0
        for i in s.iter_mut() {
54
0
            state = state.wrapping_add(PHI);
55
0
            let mut z = state;
56
0
            z = (z ^ (z >> 30)).wrapping_mul(0xbf58476d1ce4e5b9);
57
0
            z = (z ^ (z >> 27)).wrapping_mul(0x94d049bb133111eb);
58
0
            z = z ^ (z >> 31);
59
0
            *i = z;
60
0
        }
61
        // By using a non-zero PHI we are guaranteed to generate a non-zero state
62
        // Thus preventing a recursion between from_seed and seed_from_u64.
63
0
        debug_assert_ne!(s, [0; 4]);
64
0
        Xoshiro256PlusPlus { s }
65
0
    }
66
}
67
68
impl RngCore for Xoshiro256PlusPlus {
69
    #[inline]
70
0
    fn next_u32(&mut self) -> u32 {
71
        // The lowest bits have some linear dependencies, so we use the
72
        // upper bits instead.
73
0
        let val = self.next_u64();
74
0
        (val >> 32) as u32
75
0
    }
76
77
    #[inline]
78
0
    fn next_u64(&mut self) -> u64 {
79
0
        let res = self.s[0]
80
0
            .wrapping_add(self.s[3])
81
0
            .rotate_left(23)
82
0
            .wrapping_add(self.s[0]);
83
84
0
        let t = self.s[1] << 17;
85
86
0
        self.s[2] ^= self.s[0];
87
0
        self.s[3] ^= self.s[1];
88
0
        self.s[1] ^= self.s[2];
89
0
        self.s[0] ^= self.s[3];
90
91
0
        self.s[2] ^= t;
92
93
0
        self.s[3] = self.s[3].rotate_left(45);
94
95
0
        res
96
0
    }
97
98
    #[inline]
99
0
    fn fill_bytes(&mut self, dst: &mut [u8]) {
100
0
        fill_bytes_via_next(self, dst)
101
0
    }
102
}
103
104
#[cfg(test)]
105
mod tests {
106
    use super::Xoshiro256PlusPlus;
107
    use rand_core::{RngCore, SeedableRng};
108
109
    #[test]
110
    fn reference() {
111
        let mut rng = Xoshiro256PlusPlus::from_seed([
112
            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,
113
            0, 0, 0,
114
        ]);
115
        // These values were produced with the reference implementation:
116
        // http://xoshiro.di.unimi.it/xoshiro256plusplus.c
117
        let expected = [
118
            41943041,
119
            58720359,
120
            3588806011781223,
121
            3591011842654386,
122
            9228616714210784205,
123
            9973669472204895162,
124
            14011001112246962877,
125
            12406186145184390807,
126
            15849039046786891736,
127
            10450023813501588000,
128
        ];
129
        for &e in &expected {
130
            assert_eq!(rng.next_u64(), e);
131
        }
132
    }
133
134
    #[test]
135
    fn stable_seed_from_u64_and_from_seed() {
136
        // We don't guarantee value-stability for SmallRng but this
137
        // could influence keeping stability whenever possible (e.g. after optimizations).
138
        let mut rng = Xoshiro256PlusPlus::seed_from_u64(0);
139
        // from_seed([0; 32]) should produce the same state as seed_from_u64(0).
140
        let mut rng_from_seed_0 = Xoshiro256PlusPlus::from_seed([0; 32]);
141
        let expected = [
142
            5987356902031041503,
143
            7051070477665621255,
144
            6633766593972829180,
145
            211316841551650330,
146
            9136120204379184874,
147
            379361710973160858,
148
            15813423377499357806,
149
            15596884590815070553,
150
            5439680534584881407,
151
            1369371744833522710,
152
        ];
153
        for &e in &expected {
154
            assert_eq!(rng.next_u64(), e);
155
            assert_eq!(rng_from_seed_0.next_u64(), e);
156
        }
157
    }
158
}