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}