1use std::marker::PhantomData;
4
5use rand::distr::Distribution;
6use rand::{Rng, RngExt};
7
8const BOUNDARIES_I64: &[i64] = &[
14 i64::MIN,
15 i64::MIN + 1,
16 i32::MIN as i64,
17 i16::MIN as i64,
18 i8::MIN as i64,
19 -1,
20 0,
21 1,
22 i8::MAX as i64,
23 i16::MAX as i64,
24 i32::MAX as i64,
25 i64::MAX - 1,
26 i64::MAX,
27];
28
29const BOUNDARIES_F64: &[f64] = &[
32 0.0,
33 1.0,
34 -1.0,
35 f64::MIN_POSITIVE,
36 -f64::MIN_POSITIVE,
37 f64::MAX,
38 f64::MIN,
39];
40
41#[derive(Debug, Clone, Copy)]
45pub struct Probe {
46 min: u64,
47 max: u64,
48}
49
50impl Probe {
51 #[must_use]
57 pub const fn new(min: u64, max: u64) -> Self {
58 assert!(min <= max, "Probe min must not exceed max");
59 Self { min, max }
60 }
61}
62
63impl Distribution<u64> for Probe {
64 fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> u64 {
65 if rng.random_ratio(1, 4) {
66 if rng.random_ratio(1, 2) {
67 self.min
68 } else {
69 self.max
70 }
71 } else {
72 let lo = num_traits::cast::<u64, f64>(self.min.max(1)).unwrap_or(1.0).ln();
74 let hi = num_traits::cast::<u64, f64>(self.max.max(1)).unwrap_or(1.0).ln();
75 let exponent = lo + rng.random::<f64>() * (hi - lo);
76 num_traits::cast::<f64, u64>(exponent.exp().round())
77 .unwrap_or(self.max)
78 .clamp(self.min, self.max)
79 }
80 }
81}
82
83#[derive(Debug, Clone, Copy)]
94pub struct Wide;
95
96impl Distribution<f64> for Wide {
97 fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> f64 {
98 if rng.random_ratio(1, 8) {
99 return BOUNDARIES_F64[rng.random_range(0..BOUNDARIES_F64.len())];
100 }
101 let mag = 10f64.powf(rng.random_range(-30.0..30.0));
102 if rng.random_range(0..2u8) == 0 {
103 -mag
104 } else {
105 mag
106 }
107 }
108}
109
110impl Distribution<i64> for Wide {
111 fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> i64 {
112 if rng.random_ratio(1, 8) {
113 return BOUNDARIES_I64[rng.random_range(0..BOUNDARIES_I64.len())];
114 }
115 let mag = num_traits::cast::<f64, i64>(10f64.powf(rng.random_range(0.0..18.0))).unwrap_or(i64::MAX);
116 if rng.random_range(0..2u8) == 0 {
117 -mag
118 } else {
119 mag
120 }
121 }
122}
123
124#[derive(Clone, Copy, Debug, Default)]
132pub struct Boundary<T>(PhantomData<T>);
133
134impl<T> Boundary<T> {
135 #[must_use]
137 pub const fn new() -> Self {
138 Boundary(PhantomData)
139 }
140}
141
142const BOUNDARY_U8: &[u8] = &[0, 1, 2, 126, 127, 128, 129, 254, 255];
143
144impl Distribution<u8> for Boundary<u8> {
145 fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> u8 {
146 BOUNDARY_U8[rng.random_range(0..BOUNDARY_U8.len())]
147 }
148}
149
150const BOUNDARY_U64: &[u64] = &[
151 0,
152 1,
153 2,
154 u8::MAX as u64 - 1,
155 u8::MAX as u64,
156 u8::MAX as u64 + 1,
157 u16::MAX as u64 - 1,
158 u16::MAX as u64,
159 u16::MAX as u64 + 1,
160 u32::MAX as u64 - 1,
161 u32::MAX as u64,
162 u32::MAX as u64 + 1,
163 u64::MAX / 2 - 1,
164 u64::MAX / 2,
165 u64::MAX / 2 + 1,
166 u64::MAX - 1,
167 u64::MAX,
168];
169
170impl Distribution<u64> for Boundary<u64> {
171 fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> u64 {
172 BOUNDARY_U64[rng.random_range(0..BOUNDARY_U64.len())]
173 }
174}
175
176const BOUNDARY_I64: &[i64] = &[
177 i64::MIN,
178 i64::MIN + 1,
179 i64::MIN / 2 - 1,
180 i64::MIN / 2,
181 i64::MIN / 2 + 1,
182 i32::MIN as i64,
183 i16::MIN as i64,
184 i8::MIN as i64,
185 -1,
186 0,
187 1,
188 i8::MAX as i64,
189 i16::MAX as i64,
190 i32::MAX as i64,
191 i64::MAX / 2 - 1,
192 i64::MAX / 2,
193 i64::MAX / 2 + 1,
194 i64::MAX - 1,
195 i64::MAX,
196];
197
198impl Distribution<i64> for Boundary<i64> {
199 fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> i64 {
200 BOUNDARY_I64[rng.random_range(0..BOUNDARY_I64.len())]
201 }
202}