1use super::float_parts::FloatParts;
6use crate::gen;
7use crate::BoxGen;
8use crate::MapWithGen;
9use num_traits::Float;
10use rand::distributions::uniform::SampleUniform;
11use rand::Rng;
12use rand::SeedableRng;
13use std::fmt::Debug;
14use std::ops::Bound;
15use std::ops::RangeBounds;
16
17pub fn any<F>() -> BoxGen<F>
20where
21 F: Float + FloatParts + SampleUniform + 'static,
22{
23 let nans = gen::fixed::constant(F::nan());
24 gen::mix_with_ratio(&[(98, number()), (2, nans)])
25}
26
27pub fn number<F>() -> BoxGen<F>
30where
31 F: Float + FloatParts + SampleUniform + 'static,
32{
33 let infs = gen::pick_evenly(&[F::neg_infinity(), F::infinity()]);
34 gen::mix_with_ratio(&[(98, finite()), (2, infs)])
35}
36
37pub fn positive<F>() -> BoxGen<F>
39where
40 F: Float + FloatParts + SampleUniform + 'static,
41{
42 let infs = gen::fixed::constant(F::infinity());
43 let finites = ranged(F::zero()..=F::max_value());
44 gen::mix_with_ratio(&[(98, finites), (2, infs)])
45}
46
47pub fn negative<F>() -> BoxGen<F>
49where
50 F: Float + FloatParts + SampleUniform + 'static,
51{
52 let infs = gen::fixed::constant(F::neg_infinity());
53 let finites = ranged(F::min_value()..=F::neg_zero());
54 gen::mix_with_ratio(&[(98, finites), (2, infs)])
55}
56
57pub fn finite<F>() -> BoxGen<F>
60where
61 F: Float + FloatParts + SampleUniform + 'static,
62{
63 ranged(F::min_value()..=F::max_value())
64}
65
66pub fn zero_to_one<F>() -> BoxGen<F>
69where
70 F: Float + FloatParts + SampleUniform + 'static,
71{
72 ranged(F::zero()..F::one())
73}
74
75pub fn ranged<F, B>(bound: B) -> BoxGen<F>
77where
78 F: Float + FloatParts + SampleUniform + 'static,
79 B: RangeBounds<F>,
80{
81 let start: F = from_twos_complement_bits(start(&bound));
82 let end: F = from_twos_complement_bits(end(&bound));
83
84 check_bounds_are_finite(start, end);
85
86 let (start, end) = if start > end {
87 (end, start)
88 } else {
89 (start, end)
90 };
91
92 let all_special_values = [
93 F::zero(),
94 F::neg_zero(),
95 F::one(),
96 F::one().neg(),
97 F::min_value(),
98 F::max_value(),
99 F::min_positive_value(),
100 F::min_positive_value().neg(),
101 start,
102 end,
103 ];
104
105 let relevant_special_values = all_special_values
106 .iter()
107 .filter(|v| {
108 let value_has_sign_within_range = v.is_sign_negative()
111 && start.is_sign_negative()
112 || v.is_sign_positive() && end.is_sign_positive();
113
114 let is_in_range = start <= **v && **v <= end;
115
116 is_in_range && value_has_sign_within_range
117 })
118 .cloned()
119 .collect::<Vec<_>>();
120
121 let special_values = gen::pick_evenly(&relevant_special_values);
122
123 gen::mix_with_ratio(&[
124 (90, completely_random_range(bound)),
125 (10, special_values),
126 ])
127}
128
129pub fn completely_random_range<F, B>(bound: B) -> BoxGen<F>
132where
133 F: Float + FloatParts + SampleUniform + 'static,
134 B: RangeBounds<F>,
135{
136 let min = start(&bound);
137 let max = end(&bound);
138
139 check_bounds_are_finite::<F>(
140 from_twos_complement_bits(min),
141 from_twos_complement_bits(max),
142 );
143
144 let (min, max) = if min > max { (max, min) } else { (min, max) };
145
146 gen::from_fn(move |seed| {
149 let mut x = rand_chacha::ChaCha8Rng::seed_from_u64(seed);
151
152 std::iter::from_fn(move || Some(x.gen_range(min..=max)))
153 })
154 .map(
156 |i| from_twos_complement_bits(i),
157 |f| to_twos_complement_bits(f),
158 )
159 .with_shrinker(crate::shrink::float())
160}
161
162fn check_bounds_are_finite<F>(start: F, end: F)
163where
164 F: Float + Debug + 'static,
165{
166 assert!(
167 start.is_finite(),
168 "Given range can not have non-finite value {start:?} as range start"
169 );
170 assert!(
171 end.is_finite(),
172 "Given range can not have non-finite value {end:?} as range end"
173 );
174}
175
176fn to_twos_complement_bits<F>(f: F) -> i64
178where
179 F: Float + FloatParts,
180{
181 let sign_is_negative = f.is_sign_negative();
182 let exponent = f.exponent() as i64;
183 let fraction = f.fraction() as i64;
184
185 let unsigned_bits = (exponent << F::exponent_bit_position()) | fraction;
186
187 if sign_is_negative {
188 -unsigned_bits - 1
191 } else {
192 unsigned_bits
193 }
194}
195
196fn from_twos_complement_bits<F>(i: i64) -> F
197where
198 F: Float + FloatParts,
199{
200 let sign_is_negative = i < 0;
201 let unsigned_bits = if sign_is_negative { -i - 1 } else { i };
202
203 let fraction_mask = (1 << F::exponent_bit_position()) - 1;
204 let fraction = (unsigned_bits & fraction_mask) as u64;
205 let exponent = (unsigned_bits >> F::exponent_bit_position()) as u16;
206
207 F::from_bits(F::compose(sign_is_negative, exponent, fraction))
208}
209
210fn start<F, B>(bounds: &B) -> i64
211where
212 F: Float + FloatParts + Copy,
213 B: RangeBounds<F>,
214{
215 match bounds.start_bound() {
216 Bound::Included(x) => to_twos_complement_bits(*x),
217 Bound::Excluded(x) => to_twos_complement_bits(*x) + 1,
218 Bound::Unbounded => to_twos_complement_bits(F::min_value()),
219 }
220}
221
222fn end<F, B>(bounds: &B) -> i64
223where
224 F: Float + FloatParts + Copy,
225 B: RangeBounds<F>,
226{
227 match bounds.end_bound() {
228 Bound::Included(x) => to_twos_complement_bits(*x),
229 Bound::Excluded(x) => to_twos_complement_bits(*x) - 1,
230 Bound::Unbounded => to_twos_complement_bits(F::max_value()),
231 }
232}
233
234#[cfg(test)]
235mod test {
236 use super::from_twos_complement_bits;
240 use super::to_twos_complement_bits;
241 use crate::gen;
242 use crate::monkey_test;
243 use crate::testing::assert_generator_can_shrink;
244 use crate::BoxGen;
245 use std::ops::RangeBounds;
246
247 #[test]
248 fn convert_to_and_from_finite_f64() {
249 let exponent_max: i64 = 0x7fe; let fraction_max: i64 = 0xF_FFFF_FFFF_FFFF; let max = (exponent_max << 52) | fraction_max;
254 let min = -max;
255
256 monkey_test()
257 .with_generator(gen::i64::ranged(min..=max))
258 .assert_true(|i| {
259 let f: f64 = from_twos_complement_bits(i);
260 let j = to_twos_complement_bits(f);
261
262 i == j
263 });
264 }
265
266 #[test]
267 fn convert_to_and_from_finite_f32() {
268 let exponent_max: i64 = 0xfe; let fraction_max: i64 = 0x7f_ffff; let max = (exponent_max << 23) | fraction_max;
273 let min = -max;
274
275 monkey_test()
276 .with_generator(gen::i64::ranged(min..=max))
277 .assert_true(|i| {
278 let f: f32 = from_twos_complement_bits(i);
279 let j = to_twos_complement_bits(f);
280
281 i == j
282 });
283 }
284
285 #[test]
286 fn sign_is_kept_on_zero_in_conversion() {
287 let original = -0.0;
288 let bits = to_twos_complement_bits(original);
289
290 let copy: f64 = from_twos_complement_bits(bits);
291 assert!(bits < 0, "negative sign is kept in bits representation");
292 assert!(
293 copy.is_sign_negative(),
294 "negative sign is kept in roundtrip float"
295 );
296 assert!(
297 copy == 0.0,
298 "value 0 is kept in roundtrip float, got {copy}"
299 );
300 }
301
302 #[test]
303 fn verify_generator_any() {
304 let gen = gen::f32::any();
305
306 assert_has_values(
307 gen,
308 &[
309 f32::NAN,
310 f32::MAX,
311 f32::MIN,
312 f32::INFINITY,
313 f32::NEG_INFINITY,
314 0.0,
315 -0.0,
316 1.0,
317 -1.0,
318 ],
319 );
320 }
321
322 #[test]
323 fn verify_generator_number() {
324 let gen = gen::f32::number();
325 assert_all_values_are_in_range(
326 gen.clone(),
327 f32::NEG_INFINITY..=f32::INFINITY,
328 );
329 assert_has_values(gen, &[0.0]);
330 }
331
332 #[test]
333 fn verify_generator_positive() {
334 let gen = gen::f32::positive();
335 assert_all_values_are_in_range(gen.clone(), 0.0..=f32::INFINITY);
336 assert_does_not_have_value(gen.clone(), -0.0);
337 assert_has_values(gen, &[0.0, 1.0, f32::MAX, f32::INFINITY]);
338 }
339
340 #[test]
341 fn verify_generator_negative() {
342 let gen = gen::f32::negative();
343 assert_all_values_are_in_range(gen.clone(), f32::NEG_INFINITY..=-0.0);
344 assert_does_not_have_value(gen.clone(), 0.0);
345 assert_has_values(gen, &[-0.0, -1.0, f32::MIN, f32::NEG_INFINITY]);
346 }
347
348 #[test]
349 fn verify_generator_finite() {
350 let gen = gen::f32::finite();
351 assert_all_values_are_in_range(gen.clone(), f32::MIN..=f32::MAX);
352 assert_has_values(gen, &[-0.0, -1.0, 0.0, 1.0, f32::MIN, f32::MAX]);
353 }
354
355 #[test]
356 fn verify_generator_ranged() {
357 let gen = gen::f32::ranged(-555.0..=-72.0);
359 assert_all_values_are_in_range(gen.clone(), -555.0..=-72.0);
360 assert_has_values(gen, &[-555.0, -72.0]);
361
362 let gen = gen::f32::ranged(72.0..=555.0);
364 assert_all_values_are_in_range(gen.clone(), 72.0..=555.0);
365 assert_has_values(gen, &[72.0, 555.0]);
366
367 let gen = gen::f32::ranged(555.0..=72.0);
369 assert_all_values_are_in_range(gen.clone(), 72.0..=555.0);
370 assert_has_values(gen, &[72.0, 555.0]);
371
372 let gen = gen::f32::ranged(-555.0..=72.0);
374 assert_all_values_are_in_range(gen.clone(), -555.0..=72.0);
375 assert_has_values(gen, &[-555.0, 72.0, -0.0, 0.0, -1.0, 1.0]);
376 }
377
378 #[test]
379 fn verify_generator_zero_to_one() {
380 let gen = gen::f32::zero_to_one();
381 assert_all_values_are_in_range(gen.clone(), 0.0..1.0);
382 assert_has_values(gen.clone(), &[0.0]);
383 assert_does_not_have_value(gen, -0.0);
384 }
385
386 #[test]
387 #[should_panic]
388 fn let_ranged_panic_on_nan() {
389 gen::f32::ranged(f32::NAN..10.0);
390 }
391
392 #[test]
393 #[should_panic]
394 fn let_ranged_panic_on_neg_inf() {
395 gen::f32::ranged(f32::NEG_INFINITY..10.0);
396 }
397
398 #[test]
399 #[should_panic]
400 fn let_ranged_panic_on_pos_inf() {
401 gen::f32::ranged(10.0..=f32::INFINITY);
402 }
403
404 #[test]
405 #[should_panic]
406 fn let_completely_random_panic_on_nan() {
407 gen::f32::completely_random(f32::NAN..10.0);
408 }
409
410 #[test]
411 #[should_panic]
412 fn let_completely_random_panic_on_neg_inf() {
413 gen::f32::completely_random(f32::NEG_INFINITY..10.0);
414 }
415
416 #[test]
417 #[should_panic]
418 fn let_completely_random_panic_on_pos_inf() {
419 gen::f32::completely_random(10.0..=f32::INFINITY);
420 }
421
422 #[test]
423 fn should_have_shrinker() {
424 assert_generator_can_shrink(gen::f64::any(), std::f64::consts::PI)
425 }
426
427 fn assert_all_values_are_in_range<B>(gen: BoxGen<f32>, range: B)
428 where
429 B: RangeBounds<f32> + std::fmt::Debug,
430 {
431 gen.examples(crate::seed_to_use()).take(1000).for_each(|v| {
432 assert!(range.contains(&v), "Range {range:?} should contain {v}")
433 });
434 }
435
436 fn assert_has_values(gen: BoxGen<f32>, values: &[f32]) {
437 values.iter().for_each(|v| {
438 let examples_count = 1000;
439 let has_value =
440 gen.examples(crate::seed_to_use()).take(examples_count).any(|e| {
441 float_equals(e, *v)
442 });
443
444 assert!(
445 has_value,
446 "Generator did not have {v} in the first {examples_count} examples."
447 );
448 });
449 }
450
451 fn assert_does_not_have_value(gen: BoxGen<f32>, value: f32) {
452 let examples_count = 500;
453 gen.examples(crate::seed_to_use())
454 .take(examples_count)
455 .for_each(|e| {
456 let r = float_equals(e, value);
457
458 println!("cmp: {e} == {value}: {r}");
459
460 assert!(
461 !r,
462 "Search for {value} in generator found {e} in \
463 the first {examples_count} examples."
464 );
465 });
466 }
467
468 fn float_equals(a: f32, b: f32) -> bool {
469 let both_are_nan = a.is_nan() && b.is_nan();
471 let signums_are_the_same = a.signum() == b.signum();
474 both_are_nan || (signums_are_the_same && a == b)
476 }
477}