Skip to main content

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}