vrd/random.rs
1// Copyright © 2023-2026 vrd. All rights reserved.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3
4//! The [`Random`] facade and the enum-dispatched [`RngBackend`] backends.
5
6use crate::mersenne_twister::MersenneTwisterConfig;
7use crate::xoshiro::Xoshiro256PlusPlus;
8use core::convert::Infallible;
9use rand::rand_core::{SeedableRng, TryRng};
10
11#[cfg(feature = "alloc")]
12use alloc::boxed::Box;
13#[cfg(feature = "alloc")]
14use alloc::string::String;
15#[cfg(feature = "alloc")]
16use alloc::vec::Vec;
17
18#[cfg(feature = "serde")]
19use serde::{Deserialize, Serialize};
20#[cfg(feature = "serde")]
21use serde_big_array::BigArray;
22
23// ---------------------------------------------------------------------------
24// FloatExt - abstraction over std vs libm for no_std math.
25// ---------------------------------------------------------------------------
26
27/// Floating-point math operations bridged across `std` / `libm`.
28///
29/// This trait provides a unified interface for mathematical functions that
30/// are usually available in the standard library but missing in `core`.
31///
32/// # Examples
33///
34/// ```
35/// use vrd::FloatExt;
36///
37/// let n = 2.0f64;
38/// let ln_n = n.ln();
39/// let sqrt_n = n.sqrt();
40/// ```
41pub trait FloatExt {
42 /// Natural logarithm.
43 ///
44 /// # Examples
45 ///
46 /// ```
47 /// use vrd::FloatExt;
48 /// let n: f64 = std::f64::consts::E;
49 /// assert!((FloatExt::ln(n) - 1.0).abs() < 1e-12);
50 /// ```
51 fn ln(self) -> Self;
52
53 /// Square root.
54 ///
55 /// # Examples
56 ///
57 /// ```
58 /// use vrd::FloatExt;
59 /// assert!((FloatExt::sqrt(4.0_f64) - 2.0).abs() < 1e-12);
60 /// ```
61 fn sqrt(self) -> Self;
62
63 /// Cosine.
64 ///
65 /// # Examples
66 ///
67 /// ```
68 /// use vrd::FloatExt;
69 /// assert!(FloatExt::cos(0.0_f64) - 1.0 < 1e-12);
70 /// ```
71 fn cos(self) -> Self;
72
73 /// Exponential.
74 ///
75 /// # Examples
76 ///
77 /// ```
78 /// use vrd::FloatExt;
79 /// assert!((FloatExt::exp(0.0_f64) - 1.0).abs() < 1e-12);
80 /// ```
81 fn exp(self) -> Self;
82}
83
84#[cfg(feature = "std")]
85impl FloatExt for f64 {
86 #[inline]
87 fn ln(self) -> Self {
88 f64::ln(self)
89 }
90 #[inline]
91 fn sqrt(self) -> Self {
92 f64::sqrt(self)
93 }
94 #[inline]
95 fn cos(self) -> Self {
96 f64::cos(self)
97 }
98 #[inline]
99 fn exp(self) -> Self {
100 f64::exp(self)
101 }
102}
103
104// The no-std libm impl lives in `float_libm.rs`. Validated by the
105// no_std embedded CI job (`cargo check --no-default-features`);
106// excluded from coverage measurement via `.tarpaulin.toml` because
107// `cargo test` always runs with `std`, leaving the libm bodies
108// unobservable on a single-feature-set coverage run.
109#[cfg(not(feature = "std"))]
110#[path = "float_libm.rs"]
111mod float_libm;
112
113// ---------------------------------------------------------------------------
114// MersenneTwister generator - relegated, available only with `alloc`.
115// ---------------------------------------------------------------------------
116
117/// Canonical MT19937 generator (`N = 624`, `M = 397`).
118///
119/// 2496-byte state - kept behind `alloc` because [`RngBackend`] always
120/// boxes it.
121///
122/// # Examples
123///
124/// ```
125/// use vrd::random::MersenneTwister;
126///
127/// let mut mt = MersenneTwister::new();
128/// mt.seed(42);
129/// let n = mt.rand();
130/// ```
131#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
132#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
133pub struct MersenneTwister {
134 /// Internal MT19937 state vector - 624 32-bit words.
135 ///
136 /// # Examples
137 ///
138 /// ```
139 /// use vrd::random::MersenneTwister;
140 ///
141 /// let mt = MersenneTwister::new();
142 /// assert_eq!(mt.mt.len(), 624);
143 /// ```
144 #[cfg_attr(feature = "serde", serde(with = "BigArray"))]
145 pub mt: [u32; 624],
146
147 /// Current index into [`Self::mt`]. Reaches `624` and triggers
148 /// the next twist; an unseeded generator starts at `625`.
149 ///
150 /// # Examples
151 ///
152 /// ```
153 /// use vrd::random::MersenneTwister;
154 ///
155 /// let mut mt = MersenneTwister::new();
156 /// assert_eq!(mt.mti, 625); // unseeded sentinel
157 /// mt.seed(1);
158 /// assert_eq!(mt.mti, 624);
159 /// ```
160 pub mti: usize,
161}
162
163impl Default for MersenneTwister {
164 /// Returns a new instance using [`Self::new`].
165 fn default() -> Self {
166 Self::new()
167 }
168}
169
170impl MersenneTwister {
171 /// Creates a new generator with an empty state. Call [`Self::seed`]
172 /// before use.
173 ///
174 /// # Examples
175 ///
176 /// ```
177 /// use vrd::random::MersenneTwister;
178 ///
179 /// let mt = MersenneTwister::new();
180 /// assert_eq!(mt.mti(), 625);
181 /// ```
182 pub fn new() -> Self {
183 Self {
184 mt: [0; 624],
185 mti: 625,
186 }
187 }
188
189 /// Seeds the generator from a 32-byte buffer; only the low 4 bytes
190 /// are needed for MT19937 itself, but accepting 32 bytes keeps the
191 /// API consistent with [`Xoshiro256PlusPlus::from_seed`]. The full
192 /// 32 bytes are mixed via XOR-fold so callers don't silently lose
193 /// entropy.
194 ///
195 /// # Examples
196 ///
197 /// ```
198 /// use vrd::random::MersenneTwister;
199 ///
200 /// let seed = [1u8; 32];
201 /// let mt = MersenneTwister::from_seed(seed);
202 /// ```
203 pub fn from_seed(seed: [u8; 32]) -> Self {
204 let mut s = 0u32;
205 for chunk in seed.chunks_exact(4) {
206 s ^= u32::from_le_bytes([
207 chunk[0], chunk[1], chunk[2], chunk[3],
208 ]);
209 }
210 let mut mt = Self::new();
211 mt.seed(s);
212 mt
213 }
214
215 /// Seeds the MT state from a 32-bit value using the canonical
216 /// Knuth multiplier constant.
217 ///
218 /// # Examples
219 ///
220 /// ```
221 /// use vrd::random::MersenneTwister;
222 ///
223 /// let mut mt = MersenneTwister::new();
224 /// mt.seed(12345);
225 /// ```
226 pub fn seed(&mut self, seed: u32) {
227 const N: usize = 624;
228 self.mt[0] = seed;
229 for i in 1..N {
230 self.mt[i] = 1_812_433_253u32
231 .wrapping_mul(self.mt[i - 1] ^ (self.mt[i - 1] >> 30))
232 .wrapping_add(i as u32);
233 }
234 self.mti = N;
235 }
236
237 /// Performs the MT twist on the state vector.
238 ///
239 /// # Examples
240 ///
241 /// ```
242 /// use vrd::random::MersenneTwister;
243 ///
244 /// let mut mt = MersenneTwister::new();
245 /// mt.seed(42);
246 /// mt.twist();
247 /// ```
248 pub fn twist(&mut self) {
249 const N: usize = 624;
250 const M: usize = 397;
251 let config = MersenneTwisterConfig::<N, M>::default();
252 for i in 0..N {
253 let x = (self.mt[i] & config.params.upper_mask)
254 + (self.mt[(i + 1) % N] & config.params.lower_mask);
255 let x_a = x >> 1;
256 self.mt[i] = if x % 2 != 0 {
257 self.mt[(i + M) % N] ^ x_a ^ config.params.matrix_a
258 } else {
259 self.mt[(i + M) % N] ^ x_a
260 };
261 }
262 self.mti = 0;
263 }
264
265 /// Generates the next `u32`.
266 ///
267 /// # Examples
268 ///
269 /// ```
270 /// use vrd::random::MersenneTwister;
271 ///
272 /// let mut mt = MersenneTwister::new();
273 /// mt.seed(42);
274 /// let n = mt.rand();
275 /// ```
276 #[inline]
277 pub fn rand(&mut self) -> u32 {
278 const N: usize = 624;
279 if self.mti >= N {
280 self.twist();
281 }
282 let mut y = self.mt[self.mti];
283 self.mti += 1;
284 y ^= y >> 11;
285 y ^= (y << 7) & 0x9d2c_5680;
286 y ^= (y << 15) & 0xefc6_0000;
287 y ^ (y >> 18)
288 }
289
290 /// Generates the next `u64` by combining two `u32` outputs.
291 ///
292 /// # Examples
293 ///
294 /// ```
295 /// use vrd::random::MersenneTwister;
296 ///
297 /// let mut mt = MersenneTwister::new();
298 /// mt.seed(42);
299 /// let n = mt.next_u64();
300 /// ```
301 #[inline]
302 pub fn next_u64(&mut self) -> u64 {
303 let hi = self.rand() as u64;
304 let lo = self.rand() as u64;
305 (hi << 32) | lo
306 }
307
308 /// Returns the current index into the state vector.
309 ///
310 /// # Examples
311 ///
312 /// ```
313 /// use vrd::random::MersenneTwister;
314 ///
315 /// let mt = MersenneTwister::new();
316 /// assert_eq!(mt.mti(), 625);
317 /// ```
318 pub fn mti(&self) -> usize {
319 self.mti
320 }
321
322 /// Forces the index to `value`; mostly useful for round-trip tests.
323 ///
324 /// # Examples
325 ///
326 /// ```
327 /// use vrd::random::MersenneTwister;
328 ///
329 /// let mut mt = MersenneTwister::new();
330 /// mt.set_mti(100);
331 /// assert_eq!(mt.mti(), 100);
332 /// ```
333 pub fn set_mti(&mut self, value: usize) {
334 self.mti = value;
335 }
336}
337
338impl TryRng for MersenneTwister {
339 type Error = Infallible;
340
341 fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
342 Ok(self.rand())
343 }
344
345 fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
346 Ok(self.next_u64())
347 }
348
349 fn try_fill_bytes(
350 &mut self,
351 dest: &mut [u8],
352 ) -> Result<(), Self::Error> {
353 let mut i = 0;
354 while i + 4 <= dest.len() {
355 let bytes = self.rand().to_le_bytes();
356 dest[i..i + 4].copy_from_slice(&bytes);
357 i += 4;
358 }
359 if i < dest.len() {
360 let bytes = self.rand().to_le_bytes();
361 let remaining = dest.len() - i;
362 dest[i..].copy_from_slice(&bytes[..remaining]);
363 }
364 Ok(())
365 }
366}
367
368impl SeedableRng for MersenneTwister {
369 type Seed = [u8; 32];
370
371 fn from_seed(seed: Self::Seed) -> Self {
372 MersenneTwister::from_seed(seed)
373 }
374}
375
376// ---------------------------------------------------------------------------
377// RngBackend - Xoshiro inline (always), MT boxed (alloc-gated).
378// ---------------------------------------------------------------------------
379
380/// Available backends for [`Random`].
381///
382/// Xoshiro is inline (no allocation) so it works on pure `no_std`. MT is
383/// only available with the `alloc` feature because its 2496-byte state is
384/// stored on the heap to keep the enum size small.
385///
386/// # Examples
387///
388/// ```
389/// use vrd::RngBackend;
390/// use vrd::xoshiro::Xoshiro256PlusPlus;
391///
392/// let backend = RngBackend::Xoshiro256PlusPlus(Xoshiro256PlusPlus::from_u64_seed(42));
393/// ```
394#[allow(variant_size_differences)]
395#[derive(Clone, Debug, PartialEq)]
396#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
397#[non_exhaustive]
398pub enum RngBackend {
399 /// Xoshiro256++ - fast, small-state, statistically strong.
400 /// Default backend produced by [`Random::new`].
401 ///
402 /// # Examples
403 ///
404 /// ```
405 /// use vrd::{Random, RngBackend};
406 ///
407 /// let rng = Random::from_u64_seed(1);
408 /// assert!(matches!(rng.backend(), RngBackend::Xoshiro256PlusPlus(_)));
409 /// ```
410 Xoshiro256PlusPlus(Xoshiro256PlusPlus),
411
412 /// Mersenne Twister (MT19937) - for callers needing legacy
413 /// reproducibility. Requires the `alloc` feature; produced by
414 /// [`Random::new_mersenne_twister_with_seed`].
415 ///
416 /// # Examples
417 ///
418 /// ```
419 /// use vrd::{Random, RngBackend};
420 ///
421 /// # #[cfg(feature = "alloc")]
422 /// # {
423 /// let rng = Random::new_mersenne_twister_with_seed(1);
424 /// assert!(matches!(rng.backend(), RngBackend::MersenneTwister(_)));
425 /// # }
426 /// ```
427 #[cfg(feature = "alloc")]
428 MersenneTwister(Box<MersenneTwister>),
429
430 /// PCG-XSH-RR-64/32 - 16-byte state, 32-bit output. Available
431 /// under the `pcg` feature.
432 #[cfg(feature = "pcg")]
433 Pcg32(crate::pcg::Pcg32),
434
435 /// PCG-XSL-RR-128/64 - 32-byte state, 64-bit output. Available
436 /// under the `pcg` feature.
437 #[cfg(feature = "pcg")]
438 Pcg64(crate::pcg::Pcg64),
439
440 /// ChaCha20 CSPRNG - crypto-quality output. Produced by
441 /// [`Random::new_secure`] / [`Random::from_secure_seed`].
442 /// Available under the `crypto` feature.
443 #[cfg(feature = "crypto")]
444 ChaCha20(Box<crate::chacha::ChaChaRng>),
445}
446
447// ---------------------------------------------------------------------------
448// Random - the user-facing facade.
449// ---------------------------------------------------------------------------
450
451/// Random number generator dispatched over [`RngBackend`].
452///
453/// The default backend is Xoshiro256++. Construct with [`Random::new`] for
454/// entropy-seeded operation under `std`, or [`Random::from_seed`] for a
455/// deterministic, allocation-free generator on any target.
456///
457/// # Examples
458///
459/// ```
460/// use vrd::Random;
461///
462/// # #[cfg(feature = "std")]
463/// # {
464/// let mut rng = Random::new();
465/// let n = rng.rand();
466/// # }
467/// ```
468#[derive(Clone, Debug, PartialEq)]
469#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
470#[non_exhaustive]
471pub struct Random {
472 /// The active generator backend. Tagged enum dispatch via
473 /// `match` in every method; the inliner elides the match
474 /// in release builds - verified in `cargo bench`.
475 backend: RngBackend,
476}
477
478/// Iterator returned by [`Random::iter_bytes`].
479///
480/// Buffers a `u64` per 8 bytes, so cost-per-byte matches
481/// [`Random::try_fill_bytes`].
482///
483/// # Examples
484///
485/// ```
486/// use vrd::Random;
487///
488/// let mut rng = Random::from_u64_seed(1);
489/// let mut iter = rng.iter_bytes();
490/// let _: u8 = iter.next().unwrap();
491/// ```
492#[derive(Debug)]
493pub struct ByteIter<'a> {
494 /// Source RNG; one `u64` is drained per 8 bytes emitted.
495 rng: &'a mut Random,
496 /// Buffer holding the most-recent `u64` as little-endian bytes.
497 buf: [u8; 8],
498 /// Offset into `buf` of the next byte to emit. When `idx == 8`
499 /// the buffer is exhausted and the next call refills it.
500 idx: u8,
501}
502
503impl Iterator for ByteIter<'_> {
504 type Item = u8;
505
506 fn next(&mut self) -> Option<u8> {
507 if self.idx >= 8 {
508 self.buf = self.rng.u64().to_le_bytes();
509 self.idx = 0;
510 }
511 let b = self.buf[self.idx as usize];
512 self.idx += 1;
513 Some(b)
514 }
515}
516
517impl Random {
518 // ----------------------------- constructors -----------------------------
519
520 /// Creates a new entropy-seeded Xoshiro256++ generator. Requires
521 /// `std` for the OS entropy source.
522 ///
523 /// # Examples
524 ///
525 /// ```
526 /// use vrd::Random;
527 ///
528 /// # #[cfg(feature = "std")]
529 /// # {
530 /// let mut rng = Random::new();
531 /// # }
532 /// ```
533 #[cfg(feature = "std")]
534 pub fn new() -> Self {
535 let mut seed = [0u8; 32];
536 let mut sm: u64 = rand::random();
537 for chunk in seed.chunks_exact_mut(8) {
538 sm = sm.wrapping_mul(0x9E37_79B9_7F4A_7C15).wrapping_add(1);
539 let v: u64 = rand::random::<u64>() ^ sm;
540 chunk.copy_from_slice(&v.to_le_bytes());
541 }
542 Self::from_seed(seed)
543 }
544
545 /// Creates a Xoshiro256++-backed [`Random`] from a 32-byte seed.
546 /// Allocation-free; available on any target.
547 ///
548 /// # Examples
549 ///
550 /// ```
551 /// use vrd::Random;
552 ///
553 /// let seed = [0x42; 32];
554 /// let mut rng = Random::from_seed(seed);
555 /// ```
556 pub fn from_seed(seed: [u8; 32]) -> Self {
557 Self {
558 backend: RngBackend::Xoshiro256PlusPlus(
559 Xoshiro256PlusPlus::from_seed(seed),
560 ),
561 }
562 }
563
564 /// Convenience constructor for a Xoshiro256++-backed instance from a
565 /// `u64` seed. Allocation-free.
566 ///
567 /// # Examples
568 ///
569 /// ```
570 /// use vrd::Random;
571 ///
572 /// let mut rng = Random::from_u64_seed(123456789);
573 /// ```
574 pub fn from_u64_seed(seed: u64) -> Self {
575 Self {
576 backend: RngBackend::Xoshiro256PlusPlus(
577 Xoshiro256PlusPlus::from_u64_seed(seed),
578 ),
579 }
580 }
581
582 /// Creates a Mersenne-Twister-backed [`Random`] seeded with a `u32`.
583 /// Requires `alloc`.
584 ///
585 /// # Examples
586 ///
587 /// ```
588 /// use vrd::Random;
589 ///
590 /// # #[cfg(feature = "alloc")]
591 /// # {
592 /// let mut rng = Random::new_mersenne_twister_with_seed(42);
593 /// # }
594 /// ```
595 #[cfg(feature = "alloc")]
596 pub fn new_mersenne_twister_with_seed(seed: u32) -> Self {
597 let mut mt = MersenneTwister::new();
598 mt.seed(seed);
599 Self {
600 backend: RngBackend::MersenneTwister(Box::new(mt)),
601 }
602 }
603
604 /// Creates a PCG32-backed [`Random`] seeded from the given `u64`.
605 /// Requires the `pcg` feature. 16-byte state - the smallest of
606 /// the family.
607 ///
608 /// # Examples
609 ///
610 /// ```
611 /// use vrd::Random;
612 ///
613 /// # #[cfg(feature = "pcg")]
614 /// # {
615 /// let mut rng = Random::new_pcg32_with_seed(42);
616 /// let _ = rng.rand();
617 /// # }
618 /// ```
619 #[cfg(feature = "pcg")]
620 pub fn new_pcg32_with_seed(seed: u64) -> Self {
621 Self {
622 backend: RngBackend::Pcg32(
623 crate::pcg::Pcg32::from_u64_seed(seed),
624 ),
625 }
626 }
627
628 /// Creates an entropy-seeded PCG32-backed [`Random`]. Requires
629 /// the `pcg` feature and `std` for the OS entropy source.
630 ///
631 /// # Examples
632 ///
633 /// ```
634 /// use vrd::Random;
635 ///
636 /// # #[cfg(all(feature = "pcg", feature = "std"))]
637 /// # {
638 /// let mut rng = Random::new_pcg32();
639 /// let _ = rng.rand();
640 /// # }
641 /// ```
642 #[cfg(all(feature = "pcg", feature = "std"))]
643 pub fn new_pcg32() -> Self {
644 Self::new_pcg32_with_seed(rand::random())
645 }
646
647 /// Creates a PCG64-backed [`Random`] seeded from the given
648 /// `u128`. Requires the `pcg` feature. 32-byte state with
649 /// native 64-bit output.
650 ///
651 /// # Examples
652 ///
653 /// ```
654 /// use vrd::Random;
655 ///
656 /// # #[cfg(feature = "pcg")]
657 /// # {
658 /// let mut rng = Random::new_pcg64_with_seed(0xCAFE_F00D);
659 /// let _ = rng.u64();
660 /// # }
661 /// ```
662 #[cfg(feature = "pcg")]
663 pub fn new_pcg64_with_seed(seed: u128) -> Self {
664 Self {
665 backend: RngBackend::Pcg64(
666 crate::pcg::Pcg64::from_u128_seed(seed),
667 ),
668 }
669 }
670
671 /// Creates an entropy-seeded PCG64-backed [`Random`]. Requires
672 /// the `pcg` feature and `std` for the OS entropy source.
673 ///
674 /// # Examples
675 ///
676 /// ```
677 /// use vrd::Random;
678 ///
679 /// # #[cfg(all(feature = "pcg", feature = "std"))]
680 /// # {
681 /// let mut rng = Random::new_pcg64();
682 /// let _ = rng.u64();
683 /// # }
684 /// ```
685 #[cfg(all(feature = "pcg", feature = "std"))]
686 pub fn new_pcg64() -> Self {
687 let lo: u64 = rand::random();
688 let hi: u64 = rand::random();
689 Self::new_pcg64_with_seed(
690 (u128::from(hi) << 64) | u128::from(lo),
691 )
692 }
693
694 /// Creates a ChaCha20-CSPRNG-backed [`Random`] from a
695 /// deterministic 32-byte seed. Requires the `crypto` feature.
696 /// Output is bit-for-bit equivalent to
697 /// `rand_chacha::ChaCha20Rng::from_seed(seed)`.
698 ///
699 /// # Examples
700 ///
701 /// ```
702 /// use vrd::Random;
703 ///
704 /// # #[cfg(feature = "crypto")]
705 /// # {
706 /// let mut rng = Random::from_secure_seed([42u8; 32]);
707 /// let _ = rng.u64();
708 /// # }
709 /// ```
710 #[cfg(feature = "crypto")]
711 pub fn from_secure_seed(seed: [u8; 32]) -> Self {
712 Self {
713 backend: RngBackend::ChaCha20(Box::new(
714 crate::chacha::ChaChaRng::from_seed(seed),
715 )),
716 }
717 }
718
719 /// Creates a ChaCha20-CSPRNG-backed [`Random`] seeded from the
720 /// OS entropy source. Requires the `crypto` feature and `std`.
721 ///
722 /// # Examples
723 ///
724 /// ```
725 /// use vrd::Random;
726 ///
727 /// # #[cfg(all(feature = "crypto", feature = "std"))]
728 /// # {
729 /// let mut rng = Random::new_secure();
730 /// // crypto-grade UUID v4 / token / etc.
731 /// # let _ = rng.u64();
732 /// # }
733 /// ```
734 #[cfg(all(feature = "crypto", feature = "std"))]
735 pub fn new_secure() -> Self {
736 Self {
737 backend: RngBackend::ChaCha20(Box::new(
738 crate::chacha::ChaChaRng::from_os_rng(),
739 )),
740 }
741 }
742
743 /// Creates an entropy-seeded Mersenne-Twister-backed [`Random`].
744 /// Requires `alloc` + `std`.
745 ///
746 /// # Examples
747 ///
748 /// ```
749 /// use vrd::Random;
750 ///
751 /// # #[cfg(all(feature = "alloc", feature = "std"))]
752 /// # {
753 /// let mut rng = Random::new_mersenne_twister();
754 /// # }
755 /// ```
756 #[cfg(all(feature = "alloc", feature = "std"))]
757 pub fn new_mersenne_twister() -> Self {
758 Self::new_mersenne_twister_with_seed(rand::random())
759 }
760
761 // ------------------------------ raw output ------------------------------
762
763 /// Generates a pseudo-random number by combining multiple random number generations.
764 ///
765 /// # Examples
766 ///
767 /// ```
768 /// use vrd::Random;
769 ///
770 /// # #[cfg(feature = "std")]
771 /// # {
772 /// let mut rng = Random::new();
773 /// let n = rng.pseudo();
774 /// # }
775 /// ```
776 pub fn pseudo(&mut self) -> u32 {
777 let mut res = self.rand();
778 for _ in 0..31 {
779 res ^= self.rand();
780 }
781 res
782 }
783
784 /// Generates the next `u32`.
785 ///
786 /// # Examples
787 ///
788 /// ```
789 /// use vrd::Random;
790 ///
791 /// # #[cfg(feature = "std")]
792 /// # {
793 /// let mut rng = Random::new();
794 /// let n = rng.rand();
795 /// # }
796 /// ```
797 #[inline]
798 pub fn rand(&mut self) -> u32 {
799 match &mut self.backend {
800 RngBackend::Xoshiro256PlusPlus(xs) => xs.next_u32(),
801 #[cfg(feature = "alloc")]
802 RngBackend::MersenneTwister(mt) => mt.rand(),
803 #[cfg(feature = "pcg")]
804 RngBackend::Pcg32(p) => p.next_u32(),
805 #[cfg(feature = "pcg")]
806 RngBackend::Pcg64(p) => p.next_u32(),
807 #[cfg(feature = "crypto")]
808 RngBackend::ChaCha20(c) => c.next_u32(),
809 }
810 }
811
812 /// Generates the next `u64`.
813 ///
814 /// # Examples
815 ///
816 /// ```
817 /// use vrd::Random;
818 ///
819 /// # #[cfg(feature = "std")]
820 /// # {
821 /// let mut rng = Random::new();
822 /// let n = rng.u64();
823 /// # }
824 /// ```
825 #[inline]
826 pub fn u64(&mut self) -> u64 {
827 match &mut self.backend {
828 RngBackend::Xoshiro256PlusPlus(xs) => xs.next_u64(),
829 #[cfg(feature = "alloc")]
830 RngBackend::MersenneTwister(mt) => mt.next_u64(),
831 #[cfg(feature = "pcg")]
832 RngBackend::Pcg32(p) => p.next_u64(),
833 #[cfg(feature = "pcg")]
834 RngBackend::Pcg64(p) => p.next_u64(),
835 #[cfg(feature = "crypto")]
836 RngBackend::ChaCha20(c) => c.next_u64(),
837 }
838 }
839
840 /// Generates the next `i64`.
841 ///
842 /// # Examples
843 ///
844 /// ```
845 /// use vrd::Random;
846 ///
847 /// # #[cfg(feature = "std")]
848 /// # {
849 /// let mut rng = Random::new();
850 /// let n = rng.i64();
851 /// # }
852 /// ```
853 #[inline]
854 pub fn i64(&mut self) -> i64 {
855 self.u64() as i64
856 }
857
858 /// Re-seeds the active backend from a `u32`.
859 ///
860 /// # Examples
861 ///
862 /// ```
863 /// use vrd::Random;
864 ///
865 /// # #[cfg(feature = "std")]
866 /// # {
867 /// let mut rng = Random::new();
868 /// rng.seed(999);
869 /// # }
870 /// ```
871 pub fn seed(&mut self, seed: u32) {
872 match &mut self.backend {
873 RngBackend::Xoshiro256PlusPlus(xs) => {
874 *xs = Xoshiro256PlusPlus::from_u64_seed(seed as u64);
875 }
876 #[cfg(feature = "alloc")]
877 RngBackend::MersenneTwister(mt) => mt.seed(seed),
878 #[cfg(feature = "pcg")]
879 RngBackend::Pcg32(p) => {
880 *p = crate::pcg::Pcg32::from_u64_seed(seed as u64);
881 }
882 #[cfg(feature = "pcg")]
883 RngBackend::Pcg64(p) => {
884 *p = crate::pcg::Pcg64::from_u128_seed(seed as u128);
885 }
886 #[cfg(feature = "crypto")]
887 RngBackend::ChaCha20(c) => {
888 let mut s = [0u8; 32];
889 s[0..4].copy_from_slice(&seed.to_le_bytes());
890 **c = crate::chacha::ChaChaRng::from_seed(s);
891 }
892 }
893 }
894
895 /// Returns a reference to the active backend.
896 ///
897 /// # Examples
898 ///
899 /// ```
900 /// use vrd::{Random, RngBackend};
901 ///
902 /// let rng = Random::from_u64_seed(42);
903 /// match rng.backend() {
904 /// RngBackend::Xoshiro256PlusPlus(_) => println!("Using Xoshiro"),
905 /// _ => unreachable!(),
906 /// }
907 /// ```
908 pub fn backend(&self) -> &RngBackend {
909 &self.backend
910 }
911
912 /// Splits this RNG into a second instance whose stream starts
913 /// 2¹²⁸ calls ahead of `self`. Both halves remain valid and
914 /// produce non-overlapping subsequences - safe to hand to two
915 /// parallel workers without contention.
916 ///
917 /// Available only on the Xoshiro256++ backend (which has the
918 /// `jump` operation). Returns `None` on the Mersenne Twister
919 /// backend, which has no analogous fixed-distance jump.
920 ///
921 /// Cost: one `jump()` on `self`, roughly 256 scalar
922 /// `next_u64` cycles.
923 ///
924 /// # Examples
925 ///
926 /// ```
927 /// use vrd::Random;
928 ///
929 /// let mut parent = Random::from_u64_seed(42);
930 /// let mut child = parent.split().expect("Xoshiro backend");
931 /// // Two independent streams from a single seed.
932 /// assert_ne!(parent.u64(), child.u64());
933 /// ```
934 pub fn split(&mut self) -> Option<Random> {
935 match &mut self.backend {
936 RngBackend::Xoshiro256PlusPlus(xs) => {
937 let child = *xs;
938 xs.jump();
939 Some(Random {
940 backend: RngBackend::Xoshiro256PlusPlus(child),
941 })
942 }
943 #[cfg(feature = "alloc")]
944 RngBackend::MersenneTwister(_) => None,
945 #[cfg(feature = "pcg")]
946 RngBackend::Pcg32(_) | RngBackend::Pcg64(_) => None,
947 #[cfg(feature = "crypto")]
948 RngBackend::ChaCha20(_) => None,
949 }
950 }
951
952 // -------------------------- bounded sampling ---------------------------
953
954 /// Generates an unbiased `u32` in `[0, range)`.
955 ///
956 /// # Examples
957 ///
958 /// ```
959 /// use vrd::Random;
960 ///
961 /// # #[cfg(feature = "std")]
962 /// # {
963 /// let mut rng = Random::new();
964 /// let n = rng.bounded(10);
965 /// assert!(n < 10);
966 /// # }
967 /// ```
968 #[inline]
969 pub fn bounded(&mut self, range: u32) -> u32 {
970 assert!(range > 0, "range must be greater than zero");
971 let x = u64::from(self.rand()).wrapping_mul(u64::from(range));
972 let l = x as u32;
973 if l < range {
974 // Rejection branch hits <1% for ranges < 2^30; pulled
975 // out so the hot path stays small in i-cache.
976 return self.bounded_reject(range, x);
977 }
978 (x >> 32) as u32
979 }
980
981 /// Rejection-loop tail of [`Self::bounded`]. Marked `#[cold]` and
982 /// never inlined so the common-path bytes in `bounded` stay tight.
983 #[cold]
984 #[inline(never)]
985 fn bounded_reject(&mut self, range: u32, mut x: u64) -> u32 {
986 let t = range.wrapping_neg() % range;
987 let mut l = x as u32;
988 while l < t {
989 x = u64::from(self.rand()).wrapping_mul(u64::from(range));
990 l = x as u32;
991 }
992 (x >> 32) as u32
993 }
994
995 /// Generates an unbiased `u32` in `[min, max)`.
996 ///
997 /// # Examples
998 ///
999 /// ```
1000 /// use vrd::Random;
1001 ///
1002 /// # #[cfg(feature = "std")]
1003 /// # {
1004 /// let mut rng = Random::new();
1005 /// let n = rng.random_range(10, 20);
1006 /// assert!(n >= 10 && n < 20);
1007 /// # }
1008 /// ```
1009 #[inline]
1010 pub fn random_range(&mut self, min: u32, max: u32) -> u32 {
1011 assert!(max > min, "max must be greater than min");
1012 min + self.bounded(max - min)
1013 }
1014
1015 /// Generates an unbiased `i32` in `[min, max]` (inclusive).
1016 ///
1017 /// # Examples
1018 ///
1019 /// ```
1020 /// use vrd::Random;
1021 ///
1022 /// # #[cfg(feature = "std")]
1023 /// # {
1024 /// let mut rng = Random::new();
1025 /// let n = rng.int(-10, 10);
1026 /// assert!(n >= -10 && n <= 10);
1027 /// # }
1028 /// ```
1029 pub fn int(&mut self, min: i32, max: i32) -> i32 {
1030 assert!(min <= max, "min must be <= max for int");
1031 if min == max {
1032 return min;
1033 }
1034 let range = (max as i64) - (min as i64);
1035 if range == u32::MAX as i64 {
1036 return min.wrapping_add(self.rand() as i32);
1037 }
1038 let range_u32 = (range + 1) as u32;
1039 min.wrapping_add(self.bounded(range_u32) as i32)
1040 }
1041
1042 /// Inclusive alias for [`Self::int`].
1043 ///
1044 /// # Examples
1045 ///
1046 /// ```
1047 /// use vrd::Random;
1048 ///
1049 /// let mut rng = Random::from_u64_seed(1);
1050 /// let n = rng.range(1, 10);
1051 /// assert!((1..=10).contains(&n));
1052 /// ```
1053 pub fn range(&mut self, min: i32, max: i32) -> i32 {
1054 self.int(min, max)
1055 }
1056
1057 /// Generates an unbiased `u32` in `[min, max]` (inclusive).
1058 ///
1059 /// # Examples
1060 ///
1061 /// ```
1062 /// use vrd::Random;
1063 ///
1064 /// let mut rng = Random::from_u64_seed(1);
1065 /// let n = rng.uint(1, 100);
1066 /// assert!((1..=100).contains(&n));
1067 /// ```
1068 ///
1069 /// # Panics
1070 ///
1071 /// Panics if `min > max`.
1072 pub fn uint(&mut self, min: u32, max: u32) -> u32 {
1073 assert!(min <= max, "min must be <= max for uint");
1074 if min == max {
1075 return min;
1076 }
1077 let range = (max as u64) - (min as u64);
1078 if range == u32::MAX as u64 {
1079 return min.wrapping_add(self.rand());
1080 }
1081 let range_u32 = (range + 1) as u32;
1082 min + self.bounded(range_u32)
1083 }
1084
1085 // --------------------------- bools, chars ------------------------------
1086
1087 /// Generates a random `bool` whose probability of `true` is
1088 /// `probability`.
1089 ///
1090 /// # Examples
1091 ///
1092 /// ```
1093 /// use vrd::Random;
1094 ///
1095 /// let mut rng = Random::from_u64_seed(1);
1096 /// let _coin: bool = rng.bool(0.5);
1097 /// assert!(!rng.bool(0.0));
1098 /// assert!( rng.bool(1.0));
1099 /// ```
1100 ///
1101 /// # Panics
1102 ///
1103 /// Panics if `probability` is outside `[0.0, 1.0]`.
1104 pub fn bool(&mut self, probability: f64) -> bool {
1105 assert!(
1106 (0.0..=1.0).contains(&probability),
1107 "probability must be in [0.0, 1.0]"
1108 );
1109 self.f64() < probability
1110 }
1111
1112 /// Generates a lowercase ASCII character in `'a'..='z'`.
1113 ///
1114 /// # Examples
1115 ///
1116 /// ```
1117 /// use vrd::Random;
1118 ///
1119 /// let mut rng = Random::from_u64_seed(1);
1120 /// let c = rng.char();
1121 /// assert!(c.is_ascii_lowercase());
1122 /// ```
1123 pub fn char(&mut self) -> char {
1124 let v = self.bounded(26) as u8;
1125 (b'a' + v) as char
1126 }
1127
1128 /// Picks a random reference into `values`. Returns `None` if the
1129 /// slice is empty.
1130 ///
1131 /// # Examples
1132 ///
1133 /// ```
1134 /// use vrd::Random;
1135 ///
1136 /// let mut rng = Random::from_u64_seed(1);
1137 /// let pool = [10, 20, 30, 40, 50];
1138 /// let pick = rng.choose(&pool).unwrap();
1139 /// assert!(pool.contains(pick));
1140 ///
1141 /// let empty: [i32; 0] = [];
1142 /// assert!(rng.choose(&empty).is_none());
1143 /// ```
1144 pub fn choose<'a, T>(&mut self, values: &'a [T]) -> Option<&'a T> {
1145 if values.is_empty() {
1146 return None;
1147 }
1148 let idx = self.bounded(values.len() as u32) as usize;
1149 Some(&values[idx])
1150 }
1151
1152 // ------------------------------ floats ---------------------------------
1153
1154 /// Generates an `f32` in `[0.0, 1.0)` with full 24-bit mantissa
1155 /// precision.
1156 ///
1157 /// # Examples
1158 ///
1159 /// ```
1160 /// use vrd::Random;
1161 ///
1162 /// let mut rng = Random::from_u64_seed(1);
1163 /// let x = rng.float();
1164 /// assert!((0.0..1.0).contains(&x));
1165 /// ```
1166 #[inline]
1167 pub fn float(&mut self) -> f32 {
1168 const SCALE: f32 = 1.0 / ((1u32 << 24) as f32);
1169 ((self.rand() >> 8) as f32) * SCALE
1170 }
1171
1172 /// Generates an `f64` in `[0.0, 1.0)` with full 53-bit mantissa
1173 /// precision.
1174 ///
1175 /// # Examples
1176 ///
1177 /// ```
1178 /// use vrd::Random;
1179 ///
1180 /// let mut rng = Random::from_u64_seed(1);
1181 /// let x = rng.double();
1182 /// assert!((0.0..1.0).contains(&x));
1183 /// ```
1184 #[inline]
1185 pub fn double(&mut self) -> f64 {
1186 const SCALE: f64 = 1.0 / ((1u64 << 53) as f64);
1187 ((self.u64() >> 11) as f64) * SCALE
1188 }
1189
1190 /// Alias for [`Self::double`].
1191 ///
1192 /// # Examples
1193 ///
1194 /// ```
1195 /// use vrd::Random;
1196 ///
1197 /// let mut rng = Random::from_u64_seed(1);
1198 /// let x = rng.f64();
1199 /// assert!((0.0..1.0).contains(&x));
1200 /// ```
1201 #[inline]
1202 pub fn f64(&mut self) -> f64 {
1203 self.double()
1204 }
1205
1206 // -------------------------- byte / Vec output --------------------------
1207
1208 /// Returns `N` random bytes on the stack. Allocation-free; works
1209 /// in pure `no_std` (no `alloc` feature required).
1210 ///
1211 /// # Examples
1212 ///
1213 /// ```
1214 /// use vrd::Random;
1215 ///
1216 /// let mut rng = Random::from_u64_seed(1);
1217 /// let buf: [u8; 32] = rng.fill_array();
1218 /// assert!(buf.iter().any(|&b| b != 0));
1219 /// ```
1220 #[inline]
1221 pub fn fill_array<const N: usize>(&mut self) -> [u8; N] {
1222 let mut buf = [0u8; N];
1223 let _ = TryRng::try_fill_bytes(self, &mut buf);
1224 buf
1225 }
1226
1227 /// Returns a fresh `Vec<u8>` of `len` random bytes. Requires the
1228 /// `alloc` feature.
1229 ///
1230 /// # Examples
1231 ///
1232 /// ```
1233 /// use vrd::Random;
1234 ///
1235 /// # #[cfg(feature = "alloc")]
1236 /// # {
1237 /// let mut rng = Random::from_u64_seed(1);
1238 /// let buf = rng.bytes(16);
1239 /// assert_eq!(buf.len(), 16);
1240 /// # }
1241 /// ```
1242 #[cfg(feature = "alloc")]
1243 pub fn bytes(&mut self, len: usize) -> Vec<u8> {
1244 let mut buf = alloc::vec![0u8; len];
1245 let _ = self.try_fill_bytes(&mut buf);
1246 buf
1247 }
1248
1249 /// Returns a fresh `String` of `length` lowercase ASCII chars.
1250 /// Requires the `alloc` feature.
1251 ///
1252 /// # Examples
1253 ///
1254 /// ```
1255 /// use vrd::Random;
1256 ///
1257 /// # #[cfg(feature = "alloc")]
1258 /// # {
1259 /// let mut rng = Random::from_u64_seed(1);
1260 /// let s = rng.string(8);
1261 /// assert_eq!(s.len(), 8);
1262 /// assert!(s.chars().all(|c| c.is_ascii_lowercase()));
1263 /// # }
1264 /// ```
1265 #[cfg(feature = "alloc")]
1266 pub fn string(&mut self, length: usize) -> String {
1267 (0..length).map(|_| self.char()).collect()
1268 }
1269
1270 // -------------------------- iterator adapters ---------------------------
1271
1272 /// Returns an unbounded iterator yielding random `u32` values.
1273 ///
1274 /// The iterator borrows `self` mutably; collect-into-Vec or
1275 /// `.take(n)` to bound it.
1276 ///
1277 /// # Examples
1278 ///
1279 /// ```
1280 /// use vrd::Random;
1281 ///
1282 /// let mut rng = Random::from_u64_seed(1);
1283 /// # #[cfg(feature = "alloc")]
1284 /// # {
1285 /// let xs: Vec<u32> = rng.iter_u32().take(5).collect();
1286 /// assert_eq!(xs.len(), 5);
1287 /// # }
1288 /// ```
1289 pub fn iter_u32(&mut self) -> impl Iterator<Item = u32> + '_ {
1290 core::iter::from_fn(move || Some(self.rand()))
1291 }
1292
1293 /// Returns an unbounded iterator yielding random `u64` values.
1294 ///
1295 /// # Examples
1296 ///
1297 /// ```
1298 /// use vrd::Random;
1299 ///
1300 /// let mut rng = Random::from_u64_seed(1);
1301 /// # #[cfg(feature = "alloc")]
1302 /// # {
1303 /// let xs: Vec<u64> = rng.iter_u64().take(5).collect();
1304 /// assert_eq!(xs.len(), 5);
1305 /// # }
1306 /// ```
1307 pub fn iter_u64(&mut self) -> impl Iterator<Item = u64> + '_ {
1308 core::iter::from_fn(move || Some(self.u64()))
1309 }
1310
1311 /// Returns an unbounded iterator yielding random bytes.
1312 ///
1313 /// Internally buffers a `u64` per 8 bytes - the same throughput as
1314 /// [`Self::try_fill_bytes`], but ergonomic for `take`/`collect` use.
1315 ///
1316 /// # Examples
1317 ///
1318 /// ```
1319 /// use vrd::Random;
1320 ///
1321 /// let mut rng = Random::from_u64_seed(1);
1322 /// # #[cfg(feature = "alloc")]
1323 /// # {
1324 /// let bytes: Vec<u8> = rng.iter_bytes().take(16).collect();
1325 /// assert_eq!(bytes.len(), 16);
1326 /// # }
1327 /// ```
1328 pub fn iter_bytes(&mut self) -> ByteIter<'_> {
1329 ByteIter {
1330 rng: self,
1331 buf: [0u8; 8],
1332 idx: 8,
1333 }
1334 }
1335
1336 // ----------------------------- UUIDs / tokens ---------------------------
1337
1338 /// Generates a random 16-byte buffer formatted as an RFC 4122
1339 /// **version 4** UUID. Allocation-free.
1340 ///
1341 /// Variant bits and version bits are set per spec; the remaining
1342 /// 122 bits come from the active backend.
1343 ///
1344 /// # Examples
1345 ///
1346 /// ```
1347 /// use vrd::Random;
1348 ///
1349 /// let mut rng = Random::from_u64_seed(1);
1350 /// let bytes = rng.uuid_v4_bytes();
1351 /// // Version 4: high nibble of byte 6 is 0x4.
1352 /// assert_eq!(bytes[6] >> 4, 0x4);
1353 /// // Variant 10x: high two bits of byte 8 are 0b10.
1354 /// assert_eq!(bytes[8] >> 6, 0b10);
1355 /// ```
1356 pub fn uuid_v4_bytes(&mut self) -> [u8; 16] {
1357 let mut bytes = [0u8; 16];
1358 // try_fill_bytes is infallible for both backends.
1359 let _ = TryRng::try_fill_bytes(self, &mut bytes);
1360 // RFC 4122 §4.4: version 4 in the high nibble of byte 6.
1361 bytes[6] = (bytes[6] & 0x0f) | 0x40;
1362 // RFC 4122 §4.1.1: variant 10x in the top two bits of byte 8.
1363 bytes[8] = (bytes[8] & 0x3f) | 0x80;
1364 bytes
1365 }
1366
1367 /// Generates a random RFC 4122 v4 UUID as a hyphenated lowercase
1368 /// `String`. Requires the `alloc` feature.
1369 ///
1370 /// # Examples
1371 ///
1372 /// ```
1373 /// use vrd::Random;
1374 ///
1375 /// # #[cfg(feature = "alloc")]
1376 /// # {
1377 /// let mut rng = Random::from_u64_seed(1);
1378 /// let s = rng.uuid_v4();
1379 /// assert_eq!(s.len(), 36);
1380 /// // Hyphens at the canonical 8-4-4-4-12 positions.
1381 /// assert_eq!(s.as_bytes()[8], b'-');
1382 /// assert_eq!(s.as_bytes()[13], b'-');
1383 /// assert_eq!(s.as_bytes()[18], b'-');
1384 /// assert_eq!(s.as_bytes()[23], b'-');
1385 /// # }
1386 /// ```
1387 #[cfg(feature = "alloc")]
1388 pub fn uuid_v4(&mut self) -> String {
1389 let b = self.uuid_v4_bytes();
1390 let mut s = String::with_capacity(36);
1391 const HEX: &[u8; 16] = b"0123456789abcdef";
1392 let push_hex = |byte: u8, s: &mut String| {
1393 s.push(HEX[(byte >> 4) as usize] as char);
1394 s.push(HEX[(byte & 0x0f) as usize] as char);
1395 };
1396 for (i, &byte) in b.iter().enumerate() {
1397 if matches!(i, 4 | 6 | 8 | 10) {
1398 s.push('-');
1399 }
1400 push_hex(byte, &mut s);
1401 }
1402 s
1403 }
1404
1405 /// Generates a lowercase hex token of `byte_len` bytes (so the
1406 /// returned string has length `byte_len * 2`). Requires `alloc`.
1407 ///
1408 /// # Examples
1409 ///
1410 /// ```
1411 /// use vrd::Random;
1412 ///
1413 /// # #[cfg(feature = "alloc")]
1414 /// # {
1415 /// let mut rng = Random::from_u64_seed(1);
1416 /// let token = rng.hex_token(16);
1417 /// assert_eq!(token.len(), 32);
1418 /// assert!(token.chars().all(|c| c.is_ascii_hexdigit()));
1419 /// # }
1420 /// ```
1421 #[cfg(feature = "alloc")]
1422 pub fn hex_token(&mut self, byte_len: usize) -> String {
1423 const HEX: &[u8; 16] = b"0123456789abcdef";
1424 let mut s = String::with_capacity(byte_len * 2);
1425 for _ in 0..byte_len {
1426 let byte = self.rand() as u8;
1427 s.push(HEX[(byte >> 4) as usize] as char);
1428 s.push(HEX[(byte & 0x0f) as usize] as char);
1429 }
1430 s
1431 }
1432
1433 /// Generates an unpadded URL-safe **base64** token of `byte_len`
1434 /// random bytes. The returned string has length
1435 /// `((byte_len + 2) / 3) * 4`, minus padding. Alphabet per
1436 /// RFC 4648 §5: `A-Z a-z 0-9 - _`. Requires `alloc`.
1437 ///
1438 /// # Examples
1439 ///
1440 /// ```
1441 /// use vrd::Random;
1442 ///
1443 /// # #[cfg(feature = "alloc")]
1444 /// # {
1445 /// let mut rng = Random::from_u64_seed(1);
1446 /// let token = rng.base64_token(15);
1447 /// assert_eq!(token.len(), 20); // 15 bytes -> 20 base64 chars (no padding)
1448 /// assert!(token.chars().all(|c| matches!(c,
1449 /// 'A'..='Z' | 'a'..='z' | '0'..='9' | '-' | '_'
1450 /// )));
1451 /// # }
1452 /// ```
1453 #[cfg(feature = "alloc")]
1454 pub fn base64_token(&mut self, byte_len: usize) -> String {
1455 const ALPHABET: &[u8; 64] =
1456 b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
1457 let mut bytes = alloc::vec![0u8; byte_len];
1458 let _ = TryRng::try_fill_bytes(self, &mut bytes);
1459 let n = bytes.len();
1460 let chunks = n / 3;
1461 let rem = n % 3;
1462 let mut out = String::with_capacity(((n + 2) / 3) * 4);
1463 for i in 0..chunks {
1464 let a = bytes[i * 3];
1465 let b = bytes[i * 3 + 1];
1466 let c = bytes[i * 3 + 2];
1467 out.push(ALPHABET[(a >> 2) as usize] as char);
1468 out.push(
1469 ALPHABET[(((a & 0x03) << 4) | (b >> 4)) as usize]
1470 as char,
1471 );
1472 out.push(
1473 ALPHABET[(((b & 0x0f) << 2) | (c >> 6)) as usize]
1474 as char,
1475 );
1476 out.push(ALPHABET[(c & 0x3f) as usize] as char);
1477 }
1478 if rem == 1 {
1479 let a = bytes[chunks * 3];
1480 out.push(ALPHABET[(a >> 2) as usize] as char);
1481 out.push(ALPHABET[((a & 0x03) << 4) as usize] as char);
1482 } else if rem == 2 {
1483 let a = bytes[chunks * 3];
1484 let b = bytes[chunks * 3 + 1];
1485 out.push(ALPHABET[(a >> 2) as usize] as char);
1486 out.push(
1487 ALPHABET[(((a & 0x03) << 4) | (b >> 4)) as usize]
1488 as char,
1489 );
1490 out.push(ALPHABET[((b & 0x0f) << 2) as usize] as char);
1491 }
1492 out
1493 }
1494
1495 // ------------------------ uniform float in (low, high) -------------------
1496
1497 /// Generates an `f64` uniformly distributed in `[low, high)`.
1498 ///
1499 /// # Examples
1500 ///
1501 /// ```
1502 /// use vrd::Random;
1503 ///
1504 /// let mut rng = Random::from_u64_seed(1);
1505 /// let x = rng.uniform(-3.0, 3.0);
1506 /// assert!((-3.0..3.0).contains(&x));
1507 /// ```
1508 ///
1509 /// # Panics
1510 ///
1511 /// Panics if `low >= high` or if either bound is non-finite.
1512 pub fn uniform(&mut self, low: f64, high: f64) -> f64 {
1513 assert!(
1514 low.is_finite() && high.is_finite(),
1515 "bounds must be finite"
1516 );
1517 assert!(low < high, "low must be < high");
1518 low + (high - low) * self.f64()
1519 }
1520
1521 // ------------------------------ shuffling -------------------------------
1522
1523 /// Fisher-Yates shuffle in place.
1524 ///
1525 /// # Examples
1526 ///
1527 /// ```
1528 /// use vrd::Random;
1529 ///
1530 /// let mut rng = Random::from_u64_seed(1);
1531 /// let mut deck = [1, 2, 3, 4, 5];
1532 /// rng.shuffle(&mut deck);
1533 /// // The shuffled array is a permutation of the original.
1534 /// let mut sorted = deck;
1535 /// sorted.sort_unstable();
1536 /// assert_eq!(sorted, [1, 2, 3, 4, 5]);
1537 /// ```
1538 pub fn shuffle<T>(&mut self, slice: &mut [T]) {
1539 if slice.len() < 2 {
1540 return;
1541 }
1542 for i in (1..slice.len()).rev() {
1543 let j = self.bounded((i + 1) as u32) as usize;
1544 slice.swap(i, j);
1545 }
1546 }
1547
1548 /// Sample `amount` references without replacement via partial
1549 /// Fisher-Yates with `swap_remove` - O(amount) draws, each O(1).
1550 /// Requires the `alloc` feature.
1551 ///
1552 /// # Examples
1553 ///
1554 /// ```
1555 /// use vrd::Random;
1556 ///
1557 /// # #[cfg(feature = "alloc")]
1558 /// # {
1559 /// let mut rng = Random::from_u64_seed(1);
1560 /// let pool: Vec<u32> = (1..=20).collect();
1561 /// let picks = rng.sample(&pool, 5);
1562 /// assert_eq!(picks.len(), 5);
1563 /// // No duplicates.
1564 /// let mut as_vals: Vec<u32> = picks.iter().map(|r| **r).collect();
1565 /// as_vals.sort_unstable();
1566 /// let mut deduped = as_vals.clone();
1567 /// deduped.dedup();
1568 /// assert_eq!(as_vals, deduped);
1569 /// # }
1570 /// ```
1571 #[cfg(feature = "alloc")]
1572 pub fn sample<'a, T>(
1573 &mut self,
1574 slice: &'a [T],
1575 amount: usize,
1576 ) -> Vec<&'a T> {
1577 let mut result = Vec::with_capacity(amount);
1578 let mut indices: Vec<usize> = (0..slice.len()).collect();
1579 for _ in 0..amount {
1580 let pick = self.bounded(indices.len() as u32) as usize;
1581 let chosen = indices.swap_remove(pick);
1582 result.push(&slice[chosen]);
1583 }
1584 result
1585 }
1586
1587 /// Sample `amount` references with replacement. Requires the
1588 /// `alloc` feature.
1589 ///
1590 /// # Examples
1591 ///
1592 /// ```
1593 /// use vrd::Random;
1594 ///
1595 /// # #[cfg(feature = "alloc")]
1596 /// # {
1597 /// let mut rng = Random::from_u64_seed(1);
1598 /// let pool = ["alpha", "beta", "gamma"];
1599 /// let picks = rng.sample_with_replacement(&pool, 5);
1600 /// assert_eq!(picks.len(), 5);
1601 /// // Every pick is one of the pool entries (duplicates allowed).
1602 /// for p in picks {
1603 /// assert!(pool.contains(p));
1604 /// }
1605 /// # }
1606 /// ```
1607 #[cfg(feature = "alloc")]
1608 pub fn sample_with_replacement<'a, T>(
1609 &mut self,
1610 slice: &'a [T],
1611 amount: usize,
1612 ) -> Vec<&'a T> {
1613 let mut result = Vec::with_capacity(amount);
1614 for _ in 0..amount {
1615 let idx = self.bounded(slice.len() as u32) as usize;
1616 result.push(&slice[idx]);
1617 }
1618 result
1619 }
1620
1621 /// Returns a contiguous random subslice of `length` from `slice`.
1622 ///
1623 /// # Examples
1624 ///
1625 /// ```
1626 /// use vrd::Random;
1627 ///
1628 /// let mut rng = Random::from_u64_seed(1);
1629 /// let pool = [1, 2, 3, 4, 5, 6, 7, 8];
1630 /// let window = rng.rand_slice(&pool, 3).unwrap();
1631 /// assert_eq!(window.len(), 3);
1632 /// ```
1633 ///
1634 /// # Errors
1635 ///
1636 /// Returns `Err(&'static str)` when:
1637 /// - the input slice is empty,
1638 /// - `length` is `0`, or
1639 /// - `length` exceeds `slice.len()`.
1640 pub fn rand_slice<'a, T>(
1641 &mut self,
1642 slice: &'a [T],
1643 length: usize,
1644 ) -> Result<&'a [T], &'static str> {
1645 if slice.is_empty() {
1646 return Err("input slice is empty");
1647 }
1648 if length == 0 {
1649 return Err("requested length must be greater than zero");
1650 }
1651 if length > slice.len() {
1652 return Err("requested length exceeds slice length");
1653 }
1654 let start =
1655 self.bounded((slice.len() - length + 1) as u32) as usize;
1656 Ok(&slice[start..start + length])
1657 }
1658
1659 // ---------------------- statistical distributions -----------------------
1660
1661 /// Standard normal sample, parameterized by `(mu, sigma)`.
1662 ///
1663 /// Uses the **256-strip Ziggurat method** (Marsaglia & Tsang, 2000)
1664 /// with tables generated at build time. The fast path costs one
1665 /// `u32` draw, one table lookup, and one `f64` multiply; the
1666 /// overhang branch (~1% of calls) adds one `exp` and one `f64`
1667 /// draw; the tail branch (~0.03% of calls) falls back to
1668 /// exponential rejection.
1669 ///
1670 /// # Examples
1671 ///
1672 /// ```
1673 /// use vrd::Random;
1674 ///
1675 /// let mut rng = Random::from_u64_seed(1);
1676 /// let z = rng.normal(0.0, 1.0);
1677 /// assert!(z.is_finite());
1678 /// ```
1679 pub fn normal(&mut self, mu: f64, sigma: f64) -> f64 {
1680 mu + sigma * crate::ziggurat::sample_normal(self)
1681 }
1682
1683 /// Exponential sample with the given `rate` (λ). Mean of the
1684 /// distribution is `1.0 / rate`.
1685 ///
1686 /// # Examples
1687 ///
1688 /// ```
1689 /// use vrd::Random;
1690 ///
1691 /// let mut rng = Random::from_u64_seed(1);
1692 /// let x = rng.exponential(1.5);
1693 /// assert!(x >= 0.0);
1694 /// ```
1695 ///
1696 /// # Panics
1697 ///
1698 /// Panics if `rate <= 0.0`.
1699 pub fn exponential(&mut self, rate: f64) -> f64 {
1700 assert!(rate > 0.0, "rate must be positive");
1701 let u = 1.0 - self.f64();
1702 let u = if u == 0.0 { f64::MIN_POSITIVE } else { u };
1703 -FloatExt::ln(u) / rate
1704 }
1705
1706 /// Poisson sample with the given `mean` (λ). Uses Knuth's
1707 /// multiplicative algorithm; cost is O(λ).
1708 ///
1709 /// # Examples
1710 ///
1711 /// ```
1712 /// use vrd::Random;
1713 ///
1714 /// let mut rng = Random::from_u64_seed(1);
1715 /// let k = rng.poisson(3.0);
1716 /// // k is a non-negative count; with mean 3.0, values cluster
1717 /// // near 3 but the tail is unbounded.
1718 /// let _: u64 = k;
1719 /// ```
1720 pub fn poisson(&mut self, mean: f64) -> u64 {
1721 let l = FloatExt::exp(-mean);
1722 let mut k = 0u64;
1723 let mut p = 1.0;
1724 loop {
1725 k += 1;
1726 p *= self.f64();
1727 if p < l {
1728 break;
1729 }
1730 }
1731 k - 1
1732 }
1733
1734 // ------------------- MT-specific helpers (no-op on Xoshiro) ------------
1735
1736 /// Returns the current Mersenne-Twister state index. Returns `0`
1737 /// when the active backend is Xoshiro256++.
1738 ///
1739 /// # Examples
1740 ///
1741 /// ```
1742 /// use vrd::Random;
1743 ///
1744 /// let rng = Random::from_u64_seed(1);
1745 /// assert_eq!(rng.mti(), 0); // Xoshiro backend
1746 /// ```
1747 pub fn mti(&self) -> usize {
1748 match &self.backend {
1749 #[cfg(feature = "alloc")]
1750 RngBackend::MersenneTwister(mt) => mt.mti(),
1751 _ => 0,
1752 }
1753 }
1754
1755 /// Sets the Mersenne-Twister state index. No-op on the Xoshiro
1756 /// backend.
1757 ///
1758 /// # Examples
1759 ///
1760 /// ```
1761 /// use vrd::Random;
1762 ///
1763 /// let mut rng = Random::from_u64_seed(1);
1764 /// rng.set_mti(0); // no-op on Xoshiro; mti() still returns 0
1765 /// assert_eq!(rng.mti(), 0);
1766 /// ```
1767 pub fn set_mti(&mut self, value: usize) {
1768 match &mut self.backend {
1769 #[cfg(feature = "alloc")]
1770 RngBackend::MersenneTwister(mt) => mt.set_mti(value),
1771 _ => {
1772 let _ = value;
1773 }
1774 }
1775 }
1776
1777 /// Forces a Mersenne-Twister state-vector twist. No-op on the
1778 /// Xoshiro backend.
1779 ///
1780 /// # Examples
1781 ///
1782 /// ```
1783 /// use vrd::Random;
1784 ///
1785 /// let mut rng = Random::from_u64_seed(1);
1786 /// rng.twist(); // no-op on Xoshiro
1787 /// ```
1788 pub fn twist(&mut self) {
1789 match &mut self.backend {
1790 #[cfg(feature = "alloc")]
1791 RngBackend::MersenneTwister(mt) => mt.twist(),
1792 _ => {}
1793 }
1794 }
1795}
1796
1797#[cfg(feature = "std")]
1798impl Default for Random {
1799 fn default() -> Self {
1800 Self::new()
1801 }
1802}
1803
1804impl core::fmt::Display for Random {
1805 fn fmt(
1806 &self,
1807 f: &mut core::fmt::Formatter<'_>,
1808 ) -> core::fmt::Result {
1809 match &self.backend {
1810 RngBackend::Xoshiro256PlusPlus(_) => {
1811 write!(f, "Random {{ backend: Xoshiro256PlusPlus }}")
1812 }
1813 #[cfg(feature = "alloc")]
1814 RngBackend::MersenneTwister(mt) => write!(
1815 f,
1816 "Random {{ backend: MersenneTwister, mti: {} }}",
1817 mt.mti
1818 ),
1819 #[cfg(feature = "pcg")]
1820 RngBackend::Pcg32(_) => {
1821 write!(f, "Random {{ backend: Pcg32 }}")
1822 }
1823 #[cfg(feature = "pcg")]
1824 RngBackend::Pcg64(_) => {
1825 write!(f, "Random {{ backend: Pcg64 }}")
1826 }
1827 #[cfg(feature = "crypto")]
1828 RngBackend::ChaCha20(_) => {
1829 write!(f, "Random {{ backend: ChaCha20 }}")
1830 }
1831 }
1832 }
1833}
1834
1835impl TryRng for Random {
1836 type Error = Infallible;
1837
1838 fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
1839 Ok(self.rand())
1840 }
1841
1842 fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
1843 Ok(self.u64())
1844 }
1845
1846 fn try_fill_bytes(
1847 &mut self,
1848 dest: &mut [u8],
1849 ) -> Result<(), Self::Error> {
1850 match &mut self.backend {
1851 RngBackend::Xoshiro256PlusPlus(xs) => {
1852 xs.try_fill_bytes(dest)
1853 }
1854 #[cfg(feature = "alloc")]
1855 RngBackend::MersenneTwister(mt) => mt.try_fill_bytes(dest),
1856 #[cfg(feature = "pcg")]
1857 RngBackend::Pcg32(p) => p.try_fill_bytes(dest),
1858 #[cfg(feature = "pcg")]
1859 RngBackend::Pcg64(p) => p.try_fill_bytes(dest),
1860 #[cfg(feature = "crypto")]
1861 RngBackend::ChaCha20(c) => c.try_fill_bytes(dest),
1862 }
1863 }
1864}
1865
1866impl SeedableRng for Random {
1867 type Seed = [u8; 32];
1868
1869 fn from_seed(seed: Self::Seed) -> Self {
1870 Random::from_seed(seed)
1871 }
1872}
1873
1874impl crate::ziggurat::NormalSource for Random {
1875 #[inline]
1876 fn next_u32(&mut self) -> u32 {
1877 self.rand()
1878 }
1879
1880 #[inline]
1881 fn next_f64(&mut self) -> f64 {
1882 self.double()
1883 }
1884}
1885
1886#[cfg(test)]
1887mod tests {
1888 use super::*;
1889 use crate::rand_weighted_choice;
1890
1891 #[cfg(feature = "alloc")]
1892 use alloc::format;
1893 #[cfg(all(not(feature = "alloc"), feature = "std"))]
1894 use std::format;
1895
1896 #[cfg(feature = "alloc")]
1897 #[allow(unused_imports)]
1898 use alloc::vec;
1899 #[cfg(all(not(feature = "alloc"), feature = "std"))]
1900 #[allow(unused_imports)]
1901 use std::vec;
1902
1903 #[test]
1904 fn test_floatext() {
1905 let n = 2.0f64;
1906 let _ = n.ln();
1907 let _ = n.sqrt();
1908 let _ = n.cos();
1909 let _ = n.exp();
1910
1911 let _ = FloatExt::ln(n);
1912 let _ = FloatExt::sqrt(n);
1913 let _ = FloatExt::cos(0.0_f64);
1914 let _ = FloatExt::exp(n);
1915 }
1916
1917 #[test]
1918 #[cfg(feature = "alloc")]
1919 fn test_mt_tryrng_direct() {
1920 let mut mt = MersenneTwister::new();
1921 mt.seed(42);
1922 assert!(mt.try_next_u32().is_ok());
1923 assert!(mt.try_next_u64().is_ok());
1924 }
1925
1926 #[test]
1927 fn test_random_seedable_rng_trait() {
1928 let mut rng = <Random as SeedableRng>::from_seed([7u8; 32]);
1929 let _ = rng.rand();
1930 }
1931
1932 #[test]
1933 fn test_iter_u32_u64_and_bytes() {
1934 let mut rng = Random::from_u64_seed(1);
1935 let v32: u32 = rng.iter_u32().next().unwrap();
1936 let _ = v32;
1937 let v64: u64 = rng.iter_u64().next().unwrap();
1938 let _ = v64;
1939 let b: u8 = rng.iter_bytes().next().unwrap();
1940 let _ = b;
1941 }
1942
1943 #[test]
1944 fn test_rand_slice_error_empty() {
1945 let mut rng = Random::from_u64_seed(1);
1946 let pool: [u8; 0] = [];
1947 assert!(rng.rand_slice(&pool, 1).is_err());
1948 }
1949
1950 #[test]
1951 fn test_rand_slice_error_zero_length() {
1952 let mut rng = Random::from_u64_seed(1);
1953 let pool = [1u8, 2, 3];
1954 assert!(rng.rand_slice(&pool, 0).is_err());
1955 }
1956
1957 #[test]
1958 fn test_rand_slice_error_length_exceeds_slice() {
1959 let mut rng = Random::from_u64_seed(1);
1960 let pool = [1u8, 2, 3];
1961 assert!(rng.rand_slice(&pool, 10).is_err());
1962 }
1963
1964 #[test]
1965 #[cfg(feature = "alloc")]
1966 fn test_mt_direct() {
1967 let mut mt = MersenneTwister::new();
1968 mt.seed(42);
1969 assert_eq!(mt.mti(), 624);
1970 let _ = mt.rand();
1971 let _ = mt.next_u64();
1972 mt.twist();
1973 assert_eq!(mt.mti(), 0);
1974 mt.set_mti(100);
1975 assert_eq!(mt.mti(), 100);
1976 let mut buf = [0u8; 10];
1977 assert!(mt.try_fill_bytes(&mut buf).is_ok());
1978 }
1979
1980 #[test]
1981 #[cfg(feature = "alloc")]
1982 fn test_mt_seedable() {
1983 let seed = [1u8; 32];
1984 let mut mt = <MersenneTwister as SeedableRng>::from_seed(seed);
1985 assert_ne!(mt.rand(), 0);
1986 }
1987
1988 #[test]
1989 fn test_random_constructors() {
1990 let _ = Random::from_u64_seed(42);
1991 let _ = Random::from_seed([1u8; 32]);
1992 #[cfg(feature = "alloc")]
1993 let _ = Random::new_mersenne_twister_with_seed(42);
1994 }
1995
1996 #[test]
1997 #[cfg(feature = "std")]
1998 fn test_random_std_constructors() {
1999 let _ = Random::new();
2000 #[cfg(feature = "alloc")]
2001 let _ = Random::new_mersenne_twister();
2002 }
2003
2004 #[test]
2005 fn test_random_methods() {
2006 let mut rng = Random::from_u64_seed(42);
2007 let _ = rng.rand();
2008 let _ = rng.u64();
2009 let _ = rng.i64();
2010 let _ = rng.float();
2011 let _ = rng.double();
2012 let _ = rng.f64();
2013 let _ = rng.bool(0.5);
2014 let _ = rng.char();
2015 let _ = rng.int(1, 10);
2016 let _ = rng.uint(1, 10);
2017 let _ = rng.range(1, 10);
2018 let _ = rng.random_range(1, 10);
2019 let _ = rng.pseudo();
2020 let _ = rng.choose(&[1, 2, 3]);
2021 let _ = rng.rand_slice(&[1, 2, 3], 2);
2022 let _ = rng.normal(0.0, 1.0);
2023 let _ = rng.exponential(1.0);
2024 let _ = rng.poisson(3.0);
2025 rng.seed(123);
2026 let _ = rng.backend();
2027 let _ = rng.mti();
2028 rng.set_mti(10);
2029 rng.twist();
2030 #[cfg(any(feature = "alloc", feature = "std"))]
2031 let _ = format!("{}", rng);
2032 }
2033
2034 #[test]
2035 #[cfg(feature = "alloc")]
2036 fn test_random_alloc_methods() {
2037 let mut rng = Random::new_mersenne_twister_with_seed(42);
2038 let _ = rng.bytes(10);
2039 let _ = rng.string(10);
2040 let mut nums = [1, 2, 3];
2041 rng.shuffle(&mut nums);
2042 let _ = rng.sample(&nums, 2);
2043 let _ = rng.sample_with_replacement(&nums, 2);
2044 }
2045
2046 #[test]
2047 fn test_bounded_loop() {
2048 let mut rng = Random::from_u64_seed(1);
2049 for _ in 0..2000 {
2050 let _ = rng.bounded(0x8000_0001);
2051 }
2052 }
2053
2054 #[test]
2055 fn test_uint_int_full_range() {
2056 let mut rng = Random::from_u64_seed(42);
2057 let _ = rng.uint(0, u32::MAX);
2058 let _ = rng.int(i32::MIN, i32::MAX);
2059 assert_eq!(rng.int(5, 5), 5);
2060 assert_eq!(rng.uint(5, 5), 5);
2061 }
2062
2063 #[test]
2064 #[should_panic]
2065 fn test_bool_panic() {
2066 let mut rng = Random::from_u64_seed(42);
2067 let _ = rng.bool(1.1);
2068 }
2069
2070 #[test]
2071 fn test_try_rng() {
2072 let mut rng = Random::from_u64_seed(42);
2073 assert!(rng.try_next_u32().is_ok());
2074 assert!(rng.try_next_u64().is_ok());
2075 let mut buf = [0u8; 10];
2076 assert!(rng.try_fill_bytes(&mut buf).is_ok());
2077 }
2078
2079 #[test]
2080 #[should_panic(
2081 expected = "choices and weights must have the same length"
2082 )]
2083 fn test_weighted_choice_diff_length() {
2084 let mut rng = Random::from_u64_seed(42);
2085 let choices = [1, 2];
2086 let weights = [1];
2087 let _ = rand_weighted_choice!(rng, &choices, &weights);
2088 }
2089
2090 #[test]
2091 #[should_panic(expected = "total weight must be positive")]
2092 fn test_weighted_choice_zero_weight() {
2093 let mut rng = Random::from_u64_seed(42);
2094 let choices = [1, 2];
2095 let weights = [0, 0];
2096 let _ = rand_weighted_choice!(rng, &choices, &weights);
2097 }
2098
2099 #[test]
2100 fn test_weighted_choice_selection() {
2101 let mut rng = Random::from_u64_seed(42);
2102 let choices = [1, 2];
2103 let weights = [100, 0];
2104 let pick = rand_weighted_choice!(rng, &choices, &weights);
2105 assert_eq!(pick, &1);
2106 }
2107
2108 #[test]
2109 #[cfg(feature = "std")]
2110 fn test_random_new_std() {
2111 let mut rng = Random::new();
2112 assert_ne!(rng.rand(), rng.rand());
2113 }
2114
2115 #[test]
2116 fn test_random_normal_zero_edge() {
2117 let mut rng = Random::from_u64_seed(0);
2118 let _ = rng.normal(0.0, 1.0);
2119 }
2120
2121 #[test]
2122 #[cfg(feature = "alloc")]
2123 fn test_mersenne_twister_default_trait() {
2124 let mt = <MersenneTwister as Default>::default();
2125 assert_eq!(mt.mti(), 625);
2126 }
2127
2128 #[test]
2129 #[cfg(feature = "alloc")]
2130 fn test_mt_from_seed_loop() {
2131 let seed = [1u8; 32];
2132 let mt = MersenneTwister::from_seed(seed);
2133 assert_eq!(mt.mti(), 624);
2134 }
2135
2136 #[test]
2137 #[cfg(feature = "alloc")]
2138 fn test_mt_try_fill_bytes_exhaust_alignment() {
2139 let mut mt = MersenneTwister::new();
2140 let mut buf = [0u8; 15];
2141 assert!(mt.try_fill_bytes(&mut buf).is_ok());
2142 }
2143
2144 #[test]
2145 #[cfg(feature = "alloc")]
2146 fn test_random_display_mt_direct() {
2147 let rng = Random::new_mersenne_twister_with_seed(42);
2148 let s = format!("{}", rng);
2149 assert!(s.contains("MersenneTwister"));
2150 }
2151}