Skip to main content

harness/
rand.rs

1//! Randomness utilities.
2
3use std::marker::PhantomData;
4
5use rand::distr::Distribution;
6use rand::{Rng, RngExt};
7use rand_distr::LogNormal;
8
9// ===========================================================================
10// Probe — a boundary-biased magnitude sampler. ~1/8 of draws are a boundary
11// value, the rest a typical log-normal magnitude.
12// ===========================================================================
13
14/// `u64` boundary values: 0, 1, and each fixed-width max ±1.
15const BOUNDARIES_U64: &[u64] = &[
16    0,
17    1,
18    i8::MAX as u64 - 1,
19    i8::MAX as u64,
20    i8::MAX as u64 + 1,
21    u8::MAX as u64 - 1,
22    u8::MAX as u64,
23    u8::MAX as u64 + 1,
24    i16::MAX as u64 - 1,
25    i16::MAX as u64,
26    i16::MAX as u64 + 1,
27    u16::MAX as u64 - 1,
28    u16::MAX as u64,
29    u16::MAX as u64 + 1,
30    i32::MAX as u64 - 1,
31    i32::MAX as u64,
32    i32::MAX as u64 + 1,
33    u32::MAX as u64 - 1,
34    u32::MAX as u64,
35    u32::MAX as u64 + 1,
36    i64::MAX as u64 - 1,
37    i64::MAX as u64,
38    i64::MAX as u64 + 1,
39    u64::MAX - 1,
40    u64::MAX,
41];
42
43/// `i64` boundary values: 0, ±1, and each signed-width min/max.
44const BOUNDARIES_I64: &[i64] = &[
45    i64::MIN,
46    i64::MIN + 1,
47    i32::MIN as i64,
48    i16::MIN as i64,
49    i8::MIN as i64,
50    -1,
51    0,
52    1,
53    i8::MAX as i64,
54    i16::MAX as i64,
55    i32::MAX as i64,
56    i64::MAX - 1,
57    i64::MAX,
58];
59
60/// `f64` boundary values (no NaN/inf — those break frame parsing and belong to a
61/// dedicated malformed-input driver).
62const BOUNDARIES_F64: &[f64] = &[
63    0.0,
64    1.0,
65    -1.0,
66    f64::MIN_POSITIVE,
67    -f64::MIN_POSITIVE,
68    f64::MAX,
69    f64::MIN,
70];
71
72/// A boundary-biased distribution: ~1/8 of draws are a boundary value, the rest a
73/// "typical" log-normal magnitude. Generic over the numeric output type so a draw
74/// site reads `let v: i64 = Probe.sample(rng)` and gets type-appropriate
75/// boundaries. `i64`/`f64` draws carry a random sign.
76#[derive(Debug, Clone, Copy)]
77pub struct Probe;
78
79impl Distribution<u64> for Probe {
80    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> u64 {
81        if rng.random_ratio(1, 8) {
82            BOUNDARIES_U64[rng.random_range(0..BOUNDARIES_U64.len())]
83        } else {
84            typical(rng)
85        }
86    }
87}
88
89impl Distribution<i64> for Probe {
90    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> i64 {
91        if rng.random_ratio(1, 8) {
92            BOUNDARIES_I64[rng.random_range(0..BOUNDARIES_I64.len())]
93        } else {
94            let magnitude = num_traits::cast::<u64, i64>(typical(rng)).unwrap_or(i64::MAX);
95            if rng.random_ratio(1, 2) {
96                -magnitude
97            } else {
98                magnitude
99            }
100        }
101    }
102}
103
104impl Distribution<f64> for Probe {
105    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> f64 {
106        if rng.random_ratio(1, 8) {
107            BOUNDARIES_F64[rng.random_range(0..BOUNDARIES_F64.len())]
108        } else {
109            let magnitude = num_traits::cast::<u64, f64>(typical(rng)).unwrap_or(f64::MAX);
110            if rng.random_ratio(1, 2) {
111                -magnitude
112            } else {
113                magnitude
114            }
115        }
116    }
117}
118
119/// Approximate probability of a typical draw landing in each range:
120///
121/// | Value range            | Probability |
122/// |------------------------|-------------|
123/// | `<= 16`                | ~15%        |
124/// | `16 ..= 256`           | ~21%        |
125/// | `256 ..= 1_024`        | ~14%        |
126/// | `1_024 ..= 4_096`      | ~14%        |
127/// | `4_096 ..= 65_536`     | ~22%        |
128/// | `65_536 ..= 1_048_576` | ~11%        |
129/// | `> 1_048_576`          | ~4%         |
130fn typical<R: Rng + ?Sized>(rng: &mut R) -> u64 {
131    let dist = LogNormal::new(1024.0_f64.ln(), 4.0).expect("median > 0 and sigma >= 0");
132    num_traits::cast::<f64, u64>(dist.sample(rng).round()).unwrap_or(u64::MAX)
133}
134
135// ===========================================================================
136// Boundary<T> — a finite type-boundary sampler: each fixed-width max ±1 and the
137// half-range midpoint ±1, the same idea as Probe's arrays but for one type.
138// ===========================================================================
139
140/// A boundary-value sampler for `T`: each fixed-width max ±1 and the half-range
141/// midpoint ±1. `Boundary::<T>::new().sample(rng)` returns one.
142#[derive(Clone, Copy, Debug, Default)]
143pub struct Boundary<T>(PhantomData<T>);
144
145impl<T> Boundary<T> {
146    /// A boundary sampler for `T`.
147    #[must_use]
148    pub const fn new() -> Self {
149        Boundary(PhantomData)
150    }
151}
152
153const BOUNDARY_U8: &[u8] = &[0, 1, 2, 126, 127, 128, 129, 254, 255];
154
155impl Distribution<u8> for Boundary<u8> {
156    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> u8 {
157        BOUNDARY_U8[rng.random_range(0..BOUNDARY_U8.len())]
158    }
159}
160
161const BOUNDARY_U64: &[u64] = &[
162    0,
163    1,
164    2,
165    u8::MAX as u64 - 1,
166    u8::MAX as u64,
167    u8::MAX as u64 + 1,
168    u16::MAX as u64 - 1,
169    u16::MAX as u64,
170    u16::MAX as u64 + 1,
171    u32::MAX as u64 - 1,
172    u32::MAX as u64,
173    u32::MAX as u64 + 1,
174    u64::MAX / 2 - 1,
175    u64::MAX / 2,
176    u64::MAX / 2 + 1,
177    u64::MAX - 1,
178    u64::MAX,
179];
180
181impl Distribution<u64> for Boundary<u64> {
182    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> u64 {
183        BOUNDARY_U64[rng.random_range(0..BOUNDARY_U64.len())]
184    }
185}
186
187const BOUNDARY_I64: &[i64] = &[
188    i64::MIN,
189    i64::MIN + 1,
190    i64::MIN / 2 - 1,
191    i64::MIN / 2,
192    i64::MIN / 2 + 1,
193    i32::MIN as i64,
194    i16::MIN as i64,
195    i8::MIN as i64,
196    -1,
197    0,
198    1,
199    i8::MAX as i64,
200    i16::MAX as i64,
201    i32::MAX as i64,
202    i64::MAX / 2 - 1,
203    i64::MAX / 2,
204    i64::MAX / 2 + 1,
205    i64::MAX - 1,
206    i64::MAX,
207];
208
209impl Distribution<i64> for Boundary<i64> {
210    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> i64 {
211        BOUNDARY_I64[rng.random_range(0..BOUNDARY_I64.len())]
212    }
213}