miden_crypto/rand/test_utils.rs
1//! Test and benchmark utilities for generating random data.
2//!
3//! This module provides helper functions for tests and benchmarks that need
4//! random data generation. These functions replace the functionality previously
5//! provided by winter-rand-utils.
6//!
7//! # no_std Compatibility
8//!
9//! This module provides both `std`-dependent and `no_std`-compatible functions:
10//!
11//! - **`std` required**: [`rand_value`], [`rand_array`], [`rand_vector`] use the thread-local RNG
12//! and require the `std` feature.
13//! - **`no_std` compatible**: [`seeded_rng`], [`prng_array`], [`prng_vector`] use deterministic
14//! seeded PRNGs and work in `no_std` environments.
15//!
16//! For tests that should run in `no_std` mode, prefer using [`seeded_rng`] to obtain
17//! a deterministic RNG instead of `rand::rng()`.
18
19use alloc::{vec, vec::Vec};
20
21use rand::{Rng, RngExt, SeedableRng};
22use rand_chacha::ChaCha20Rng;
23
24use crate::rand::Randomizable;
25
26/// Creates a deterministic seeded RNG suitable for tests.
27///
28/// This function returns a ChaCha20 PRNG seeded with the provided seed, providing
29/// deterministic random number generation that works in `no_std` environments.
30///
31/// # Examples
32/// ```
33/// # use miden_crypto::rand::test_utils::seeded_rng;
34/// let mut rng = seeded_rng([0u8; 32]);
35/// // Use rng with any function that accepts impl Rng
36/// ```
37pub fn seeded_rng(seed: [u8; 32]) -> ChaCha20Rng {
38 ChaCha20Rng::from_seed(seed)
39}
40
41/// Generates a random value of type T from an RNG.
42fn rng_value<T: Randomizable>(rng: &mut impl Rng) -> T {
43 let mut bytes = vec![0u8; T::VALUE_SIZE];
44 loop {
45 rng.fill(&mut bytes[..]);
46 if let Some(value) = T::from_random_bytes(&bytes) {
47 return value;
48 }
49 }
50}
51
52/// Generates a random value of type T using the thread-local random number generator.
53///
54/// # Examples
55/// ```
56/// # use miden_crypto::rand::test_utils::rand_value;
57/// let x: u64 = rand_value();
58/// let y: u128 = rand_value();
59/// ```
60#[cfg(feature = "std")]
61pub fn rand_value<T: Randomizable>() -> T {
62 rng_value(&mut rand::rng())
63}
64
65/// Generates a deterministic value of type `T` in `no_std` builds.
66///
67/// This keeps tests and feature-matrix checks buildable without relying on
68/// thread-local RNG support.
69#[cfg(not(feature = "std"))]
70pub fn rand_value<T: Randomizable>() -> T {
71 prng_value([0u8; 32])
72}
73
74/// Generates a random array of type T with N elements.
75///
76/// # Examples
77/// ```
78/// # use miden_crypto::rand::test_utils::rand_array;
79/// let arr: [u64; 4] = rand_array();
80/// ```
81#[cfg(feature = "std")]
82pub fn rand_array<T: Randomizable, const N: usize>() -> [T; N] {
83 let mut rng = rand::rng();
84 core::array::from_fn(|_| rng_value(&mut rng))
85}
86
87/// Generates a random vector of type T with the specified length.
88///
89/// # Examples
90/// ```
91/// # use miden_crypto::rand::test_utils::rand_vector;
92/// let vec: Vec<u64> = rand_vector(100);
93/// ```
94#[cfg(feature = "std")]
95pub fn rand_vector<T: Randomizable>(length: usize) -> Vec<T> {
96 let mut rng = rand::rng();
97 (0..length).map(|_| rng_value(&mut rng)).collect()
98}
99
100/// Generates a deterministic value using a PRNG seeded with the provided seed.
101///
102/// This function uses ChaCha20 PRNG for deterministic random generation, which is
103/// useful for reproducible tests and benchmarks.
104///
105/// # Examples
106/// ```
107/// # use miden_crypto::rand::test_utils::prng_value;
108/// let seed = [0u8; 32];
109/// let val: u64 = prng_value(seed);
110/// ```
111pub fn prng_value<T: Randomizable>(seed: [u8; 32]) -> T {
112 rng_value(&mut seeded_rng(seed))
113}
114
115/// Generates a deterministic array using a PRNG seeded with the provided seed.
116///
117/// # Examples
118/// ```
119/// # use miden_crypto::rand::test_utils::prng_array;
120/// let seed = [0u8; 32];
121/// let arr: [u64; 4] = prng_array(seed);
122/// ```
123pub fn prng_array<T: Randomizable, const N: usize>(seed: [u8; 32]) -> [T; N] {
124 let mut rng = seeded_rng(seed);
125 core::array::from_fn(|_| rng_value(&mut rng))
126}
127
128/// Generates a deterministic vector using a PRNG seeded with the provided seed.
129///
130/// # Examples
131/// ```
132/// # use miden_crypto::rand::test_utils::prng_vector;
133/// let seed = [0u8; 32];
134/// let vec: Vec<u64> = prng_vector(seed, 100);
135/// ```
136pub fn prng_vector<T: Randomizable>(seed: [u8; 32], length: usize) -> Vec<T> {
137 let mut rng = seeded_rng(seed);
138 (0..length).map(|_| rng_value(&mut rng)).collect()
139}
140
141// CONTINUOUS RNG
142// ================================================================================================
143
144/// A continuous random number generator that works in `no-std` contexts.
145#[derive(Debug)]
146pub struct ContinuousRng {
147 rng: ChaCha20Rng,
148}
149impl ContinuousRng {
150 /// Creates a new instance of the random number generator from the seed.
151 pub fn new(seed: [u8; 32]) -> ContinuousRng {
152 ContinuousRng { rng: ChaCha20Rng::from_seed(seed) }
153 }
154
155 /// Generates a random value of the [`Randomizable`] type `T`.
156 pub fn value<T: Randomizable>(&mut self) -> T {
157 rng_value(&mut self.rng)
158 }
159}