Skip to main content

malachite_base/random/
mod.rs

1// Copyright © 2026 Mikhail Hogrefe
2//
3// This file is part of Malachite.
4//
5// Malachite is free software: you can redistribute it and/or modify it under the terms of the GNU
6// Lesser General Public License (LGPL) as published by the Free Software Foundation; either version
7// 3 of the License, or (at your option) any later version. See <https://www.gnu.org/licenses/>.
8
9use rand::{RngCore, SeedableRng};
10use rand_chacha::ChaCha20Rng;
11use sha3::{Digest, Sha3_256};
12
13/// A random seed used for reproducible testing.
14pub const EXAMPLE_SEED: Seed = Seed::from_bytes([
15    0xbf, 0x18, 0x11, 0xce, 0x15, 0xee, 0xfd, 0x20, 0x2f, 0xdf, 0x67, 0x6a, 0x6b, 0xba, 0xaf, 0x04,
16    0xff, 0x71, 0xe0, 0xf8, 0x0b, 0x2a, 0xcf, 0x27, 0x85, 0xb3, 0x32, 0xc6, 0x20, 0x80, 0x5e, 0x36,
17]);
18
19/// A type representing a random seed.
20#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
21pub struct Seed {
22    pub bytes: [u8; 32],
23}
24
25impl Seed {
26    /// Creates a `Seed` from a slice of 32 bytes.
27    ///
28    /// # Worst-case complexity
29    /// Constant time and additional memory.
30    ///
31    /// # Examples
32    /// ```
33    /// use malachite_base::random::Seed;
34    ///
35    /// Seed::from_bytes([10; 32]);
36    /// ```
37    #[inline]
38    pub const fn from_bytes(bytes: [u8; 32]) -> Self {
39        Self { bytes }
40    }
41
42    /// Creates a PRNG from a slice of 32 bytes.
43    ///
44    /// # Worst-case complexity
45    /// Constant time and additional memory.
46    ///
47    /// # Examples
48    /// ```
49    /// use malachite_base::random::EXAMPLE_SEED;
50    ///
51    /// EXAMPLE_SEED.get_rng();
52    /// ```
53    #[inline]
54    pub fn get_rng(self) -> ChaCha20Rng {
55        ChaCha20Rng::from_seed(self.bytes)
56    }
57
58    /// Uniformly generates a random `Seed`.
59    ///
60    /// # Worst-case complexity
61    /// Constant time and additional memory.
62    ///
63    /// # Examples
64    /// ```
65    /// use malachite_base::random::{Seed, EXAMPLE_SEED};
66    ///
67    /// assert_eq!(
68    ///     EXAMPLE_SEED.next(),
69    ///     Seed::from_bytes([
70    ///         0x71, 0xef, 0x45, 0x6c, 0xe4, 0xd2, 0xa8, 0xa1, 0x57, 0x20, 0x6e, 0x53, 0xbc, 0x22,
71    ///         0x59, 0xee, 0x5d, 0xc8, 0x95, 0x73, 0xbd, 0x95, 0xd9, 0xc9, 0x75, 0x92, 0x1f, 0x48,
72    ///         0x97, 0xa9, 0xae, 0x21
73    ///     ])
74    /// );
75    /// ```
76    #[inline]
77    pub fn next(self) -> Self {
78        let mut bytes = [0; 32];
79        self.get_rng().fill_bytes(&mut bytes);
80        Self::from_bytes(bytes)
81    }
82
83    /// Generates a new `Seed` from this seed. Passing different `key`s will, with very high
84    /// probability, generate different seeds.
85    ///
86    /// # Worst-case complexity
87    /// $T(n) = O(n)$
88    ///
89    /// $M(n) = O(1)$
90    ///
91    /// where $T$ is time, $M$ is additional memory, and $n$ is `key.len()`.
92    ///
93    /// # Examples
94    /// ```
95    /// use malachite_base::random::{Seed, EXAMPLE_SEED};
96    ///
97    /// assert_eq!(
98    ///     EXAMPLE_SEED.fork("first"),
99    ///     Seed::from_bytes([
100    ///         0x20, 0x18, 0x1, 0x3d, 0x96, 0x4d, 0x3e, 0x98, 0x10, 0x9d, 0x35, 0x75, 0x22, 0x89,
101    ///         0xf7, 0xe9, 0xbe, 0x2f, 0x9c, 0x15, 0x95, 0x42, 0x1a, 0x79, 0x52, 0xf, 0x56, 0x9a,
102    ///         0x7b, 0x8c, 0xd9, 0x34
103    ///     ])
104    /// );
105    /// assert_eq!(
106    ///     EXAMPLE_SEED.fork("second"),
107    ///     Seed::from_bytes([
108    ///         0xe0, 0x36, 0x88, 0x58, 0x6d, 0x67, 0x33, 0xea, 0xf2, 0x1c, 0x88, 0xf9, 0xe3, 0xbd,
109    ///         0x52, 0xc0, 0xe5, 0xad, 0x61, 0x81, 0x21, 0xd8, 0x2f, 0x8e, 0xcd, 0xf, 0x89, 0x9d,
110    ///         0x32, 0xc5, 0x35, 0x83
111    ///     ])
112    /// );
113    /// ```
114    pub fn fork(&self, key: &str) -> Self {
115        let mut seed = self.next();
116        let forked_seed = &mut seed.bytes;
117        let hash = Sha3_256::digest(key.as_bytes());
118        for i in 0..32 {
119            forked_seed[i] ^= hash[i];
120        }
121        seed.next()
122    }
123}