1mod float_samplers;
16
17use crate::test_runner::TestRunner;
18use rand::distr::uniform::{SampleUniform, Uniform};
19use rand::distr::{Distribution, StandardUniform};
20
21pub(crate) fn sample_uniform<X: SampleUniform>(
24 run: &mut TestRunner,
25 start: X,
26 end: X,
27) -> X {
28 Uniform::new(start, end)
29 .expect("not uniform")
30 .sample(run.rng())
31}
32
33pub fn sample_uniform_incl<X: SampleUniform>(
36 run: &mut TestRunner,
37 start: X,
38 end: X,
39) -> X {
40 Uniform::new_inclusive(start, end)
41 .expect("not uniform")
42 .sample(run.rng())
43}
44
45macro_rules! sample_uniform {
46 ($name: ident, $incl:ident, $from:ty, $to:ty) => {
47 fn $name<X>(run: &mut TestRunner, start: $to, end: $to) -> $to {
48 Uniform::<$from>::new(start as $from, end as $from)
49 .expect("not uniform")
50 .sample(run.rng()) as $to
51 }
52
53 fn $incl<X>(run: &mut TestRunner, start: $to, end: $to) -> $to {
54 Uniform::<$from>::new_inclusive(start as $from, end as $from)
55 .expect("not uniform")
56 .sample(run.rng()) as $to
57 }
58 };
59}
60
61#[cfg(target_pointer_width = "64")]
62sample_uniform!(usize_sample_uniform, usize_sample_uniform_incl, u64, usize);
63#[cfg(target_pointer_width = "32")]
64sample_uniform!(usize_sample_uniform, usize_sample_uniform_incl, u32, usize);
65#[cfg(target_pointer_width = "16")]
66sample_uniform!(usize_sample_uniform, usize_sample_uniform_incl, u16, usize);
67
68#[cfg(target_pointer_width = "64")]
69sample_uniform!(isize_sample_uniform, isize_sample_uniform_incl, i64, isize);
70#[cfg(target_pointer_width = "32")]
71sample_uniform!(isize_sample_uniform, isize_sample_uniform_incl, i32, isize);
72#[cfg(target_pointer_width = "16")]
73sample_uniform!(isize_sample_uniform, isize_sample_uniform_incl, i16, isize);
74
75macro_rules! supported_int_any {
76 ($runner:ident, $typ:ty) => {
77 $runner.rng().random()
78 };
79}
80
81#[cfg(target_pointer_width = "64")]
82macro_rules! unsupported_int_any {
83 ($runner:ident, $typ:ty) => {
84 $runner.rng().next_u64() as $typ
85 };
86}
87
88#[cfg(not(target_pointer_width = "64"))]
89macro_rules! unsupported_int_any {
90 ($runner:ident, $typ:ty) => {
91 $runner.rng().next_u32() as $typ
92 };
93}
94
95macro_rules! int_any {
96 ($typ: ident, $int_any: ident) => {
97 #[derive(Clone, Copy, Debug)]
99 #[must_use = "strategies do nothing unless used"]
100 pub struct Any(());
101 pub const ANY: Any = Any(());
104
105 impl Strategy for Any {
106 type Tree = BinarySearch;
107 type Value = $typ;
108
109 fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
110 Ok(BinarySearch::new($int_any!(runner, $typ)))
111 }
112 }
113 };
114}
115
116macro_rules! numeric_api {
117 ($typ:ident, $epsilon:expr) => {
118 numeric_api!($typ, $typ, $epsilon);
119 };
120 ($typ:ident, $sample_typ:ty, $epsilon:expr) => {
121 numeric_api!(
122 $typ,
123 $sample_typ,
124 $epsilon,
125 sample_uniform,
126 sample_uniform_incl
127 );
128 };
129 ($typ:ident, $epsilon:expr, $uniform:ident, $incl:ident) => {
130 numeric_api!($typ, $typ, $epsilon, $uniform, $incl);
131 };
132 ($typ:ident, $sample_typ:ty, $epsilon:expr, $uniform:ident, $incl:ident) => {
133 impl Strategy for ::core::ops::Range<$typ> {
134 type Tree = BinarySearch;
135 type Value = $typ;
136
137 fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
138 if self.is_empty() {
139 panic!(
140 "Invalid use of empty range {}..{}.",
141 self.start, self.end
142 );
143 }
144
145 Ok(BinarySearch::new_clamped(
146 self.start,
147 $crate::num::$uniform::<$sample_typ>(
148 runner,
149 self.start.into(),
150 self.end.into(),
151 )
152 .into(),
153 self.end - $epsilon,
154 ))
155 }
156 }
157
158 impl Strategy for ::core::ops::RangeInclusive<$typ> {
159 type Tree = BinarySearch;
160 type Value = $typ;
161
162 fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
163 if self.is_empty() {
164 panic!(
165 "Invalid use of empty range {}..={}.",
166 self.start(),
167 self.end()
168 );
169 }
170
171 Ok(BinarySearch::new_clamped(
172 *self.start(),
173 $crate::num::$incl::<$sample_typ>(
174 runner,
175 (*self.start()).into(),
176 (*self.end()).into(),
177 )
178 .into(),
179 *self.end(),
180 ))
181 }
182 }
183
184 impl Strategy for ::core::ops::RangeFrom<$typ> {
185 type Tree = BinarySearch;
186 type Value = $typ;
187
188 fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
189 Ok(BinarySearch::new_clamped(
190 self.start,
191 $crate::num::$incl::<$sample_typ>(
192 runner,
193 self.start.into(),
194 <$typ>::MAX.into(),
195 )
196 .into(),
197 <$typ>::MAX,
198 ))
199 }
200 }
201
202 impl Strategy for ::core::ops::RangeTo<$typ> {
203 type Tree = BinarySearch;
204 type Value = $typ;
205
206 fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
207 Ok(BinarySearch::new_clamped(
208 <$typ>::MIN,
209 $crate::num::$uniform::<$sample_typ>(
210 runner,
211 <$typ>::MIN.into(),
212 self.end.into(),
213 )
214 .into(),
215 self.end,
216 ))
217 }
218 }
219
220 impl Strategy for ::core::ops::RangeToInclusive<$typ> {
221 type Tree = BinarySearch;
222 type Value = $typ;
223
224 fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
225 Ok(BinarySearch::new_clamped(
226 <$typ>::MIN,
227 $crate::num::$incl::<$sample_typ>(
228 runner,
229 <$typ>::MIN.into(),
230 self.end.into(),
231 )
232 .into(),
233 self.end,
234 ))
235 }
236 }
237 };
238}
239
240macro_rules! signed_integer_bin_search {
241 ($typ:ident) => {
242 signed_integer_bin_search!(
243 $typ,
244 supported_int_any,
245 sample_uniform,
246 sample_uniform_incl
247 );
248 };
249 ($typ:ident, $int_any: ident, $uniform: ident, $incl: ident) => {
250 #[allow(missing_docs)]
251 pub mod $typ {
252 #[allow(unused_imports)]
253 use rand::{Rng, RngExt};
254
255 use crate::strategy::*;
256 use crate::test_runner::TestRunner;
257
258 int_any!($typ, $int_any);
259
260 #[derive(Clone, Copy, Debug)]
263 pub struct BinarySearch {
264 lo: $typ,
265 curr: $typ,
266 hi: $typ,
267 }
268 impl BinarySearch {
269 pub fn new(start: $typ) -> Self {
271 BinarySearch {
272 lo: 0,
273 curr: start,
274 hi: start,
275 }
276 }
277
278 fn new_clamped(lo: $typ, start: $typ, hi: $typ) -> Self {
282 use core::cmp::{max, min};
283
284 BinarySearch {
285 lo: if start < 0 {
286 min(0, hi - 1)
287 } else {
288 max(0, lo)
289 },
290 hi: start,
291 curr: start,
292 }
293 }
294
295 fn reposition(&mut self) -> bool {
296 let interval = self.hi - self.lo;
299 let new_mid = self.lo + interval / 2;
300
301 if new_mid == self.curr {
302 false
303 } else {
304 self.curr = new_mid;
305 true
306 }
307 }
308
309 fn magnitude_greater(lhs: $typ, rhs: $typ) -> bool {
310 if 0 == lhs {
311 false
312 } else if lhs < 0 {
313 lhs < rhs
314 } else {
315 lhs > rhs
316 }
317 }
318 }
319 impl ValueTree for BinarySearch {
320 type Value = $typ;
321
322 fn current(&self) -> $typ {
323 self.curr
324 }
325
326 fn simplify(&mut self) -> bool {
327 if !BinarySearch::magnitude_greater(self.hi, self.lo) {
328 return false;
329 }
330
331 self.hi = self.curr;
332 self.reposition()
333 }
334
335 fn complicate(&mut self) -> bool {
336 if !BinarySearch::magnitude_greater(self.hi, self.lo) {
337 return false;
338 }
339
340 self.lo = self.curr + if self.hi < 0 { -1 } else { 1 };
341
342 self.reposition()
343 }
344 }
345
346 numeric_api!($typ, 1, $uniform, $incl);
347 }
348 };
349}
350
351macro_rules! unsigned_integer_bin_search {
352 ($typ:ident) => {
353 unsigned_integer_bin_search!(
354 $typ,
355 supported_int_any,
356 sample_uniform,
357 sample_uniform_incl
358 );
359 };
360 ($typ:ident, $int_any: ident, $uniform: ident, $incl: ident) => {
361 #[allow(missing_docs)]
362 pub mod $typ {
363 #[allow(unused_imports)]
364 use rand::{Rng, RngExt};
365
366 use crate::strategy::*;
367 use crate::test_runner::TestRunner;
368
369 int_any!($typ, $int_any);
370
371 #[derive(Clone, Copy, Debug)]
374 pub struct BinarySearch {
375 lo: $typ,
376 curr: $typ,
377 hi: $typ,
378 }
379 impl BinarySearch {
380 pub fn new(start: $typ) -> Self {
382 BinarySearch {
383 lo: 0,
384 curr: start,
385 hi: start,
386 }
387 }
388
389 fn new_clamped(lo: $typ, start: $typ, _hi: $typ) -> Self {
392 BinarySearch {
393 lo: lo,
394 curr: start,
395 hi: start,
396 }
397 }
398
399 pub fn new_above(lo: $typ, start: $typ) -> Self {
402 BinarySearch::new_clamped(lo, start, start)
403 }
404
405 fn reposition(&mut self) -> bool {
406 let interval = self.hi - self.lo;
407 let new_mid = self.lo + interval / 2;
408
409 if new_mid == self.curr {
410 false
411 } else {
412 self.curr = new_mid;
413 true
414 }
415 }
416 }
417 impl ValueTree for BinarySearch {
418 type Value = $typ;
419
420 fn current(&self) -> $typ {
421 self.curr
422 }
423
424 fn simplify(&mut self) -> bool {
425 if self.hi <= self.lo {
426 return false;
427 }
428
429 self.hi = self.curr;
430 self.reposition()
431 }
432
433 fn complicate(&mut self) -> bool {
434 if self.hi <= self.lo {
435 return false;
436 }
437
438 self.lo = self.curr + 1;
439 self.reposition()
440 }
441 }
442
443 numeric_api!($typ, 1, $uniform, $incl);
444 }
445 };
446}
447
448signed_integer_bin_search!(i8);
449signed_integer_bin_search!(i16);
450signed_integer_bin_search!(i32);
451signed_integer_bin_search!(i64);
452signed_integer_bin_search!(i128);
453signed_integer_bin_search!(
454 isize,
455 unsupported_int_any,
456 isize_sample_uniform,
457 isize_sample_uniform_incl
458);
459unsigned_integer_bin_search!(u8);
460unsigned_integer_bin_search!(u16);
461unsigned_integer_bin_search!(u32);
462unsigned_integer_bin_search!(u64);
463unsigned_integer_bin_search!(u128);
464unsigned_integer_bin_search!(
465 usize,
466 unsupported_int_any,
467 usize_sample_uniform,
468 usize_sample_uniform_incl
469);
470
471bitflags! {
472 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
473 pub(crate) struct FloatTypes: u32 {
474 const POSITIVE = 0b0000_0001;
475 const NEGATIVE = 0b0000_0010;
476 const NORMAL = 0b0000_0100;
477 const SUBNORMAL = 0b0000_1000;
478 const ZERO = 0b0001_0000;
479 const INFINITE = 0b0010_0000;
480 const QUIET_NAN = 0b0100_0000;
481 const SIGNALING_NAN = 0b1000_0000;
482 const ANY =
483 Self::POSITIVE.bits() |
484 Self::NEGATIVE.bits() |
485 Self::NORMAL.bits() |
486 Self::SUBNORMAL.bits() |
487 Self::ZERO.bits() |
488 Self::INFINITE.bits() |
489 Self::QUIET_NAN.bits();
490 }
491}
492
493impl FloatTypes {
494 fn normalise(mut self) -> Self {
495 if !self.intersects(FloatTypes::POSITIVE | FloatTypes::NEGATIVE) {
496 self |= FloatTypes::POSITIVE;
497 }
498
499 if !self.intersects(
500 FloatTypes::NORMAL
501 | FloatTypes::SUBNORMAL
502 | FloatTypes::ZERO
503 | FloatTypes::INFINITE
504 | FloatTypes::QUIET_NAN
505 | FloatTypes::SIGNALING_NAN,
506 ) {
507 self |= FloatTypes::NORMAL;
508 }
509 self
510 }
511}
512
513trait FloatLayout
514where
515 StandardUniform: Distribution<Self::Bits>,
516{
517 type Bits: Copy;
518
519 const SIGN_MASK: Self::Bits;
520 const EXP_MASK: Self::Bits;
521 const EXP_ZERO: Self::Bits;
522 const MANTISSA_MASK: Self::Bits;
523}
524
525#[cfg(feature = "f16")]
526impl FloatLayout for f16 {
527 type Bits = u16;
528
529 const SIGN_MASK: u16 = 0x8000;
530 const EXP_MASK: u16 = 0x7c00;
531 const EXP_ZERO: u16 = f16::to_bits(1.0);
532 const MANTISSA_MASK: u16 =
533 !(<Self as FloatLayout>::SIGN_MASK | Self::EXP_MASK);
534}
535
536impl FloatLayout for f32 {
537 type Bits = u32;
538
539 const SIGN_MASK: u32 = 0x8000_0000;
540 const EXP_MASK: u32 = 0x7F80_0000;
541 const EXP_ZERO: u32 = 0x3F80_0000;
542 const MANTISSA_MASK: u32 = 0x007F_FFFF;
543}
544
545impl FloatLayout for f64 {
546 type Bits = u64;
547
548 const SIGN_MASK: u64 = 0x8000_0000_0000_0000;
549 const EXP_MASK: u64 = 0x7FF0_0000_0000_0000;
550 const EXP_ZERO: u64 = 0x3FF0_0000_0000_0000;
551 const MANTISSA_MASK: u64 = 0x000F_FFFF_FFFF_FFFF;
552}
553
554macro_rules! float_any {
555 ($typ:ident) => {
556 #[derive(Clone, Copy, Debug)]
585 #[must_use = "strategies do nothing unless used"]
586 pub struct Any(FloatTypes);
587
588 #[cfg(test)]
589 impl Any {
590 pub(crate) fn from_bits(bits: u32) -> Self {
591 Any(FloatTypes::from_bits_truncate(bits))
592 }
593
594 pub(crate) fn normal_bits(&self) -> FloatTypes {
595 self.0.normalise()
596 }
597 }
598
599 impl ops::BitOr for Any {
600 type Output = Self;
601
602 fn bitor(self, rhs: Self) -> Self {
603 Any(self.0 | rhs.0)
604 }
605 }
606
607 impl ops::BitOrAssign for Any {
608 fn bitor_assign(&mut self, rhs: Self) {
609 self.0 |= rhs.0
610 }
611 }
612
613 pub const POSITIVE: Any = Any(FloatTypes::POSITIVE);
621 pub const NEGATIVE: Any = Any(FloatTypes::NEGATIVE);
629 pub const NORMAL: Any = Any(FloatTypes::NORMAL);
644 pub const SUBNORMAL: Any = Any(FloatTypes::SUBNORMAL);
656 pub const ZERO: Any = Any(FloatTypes::ZERO);
664 pub const INFINITE: Any = Any(FloatTypes::INFINITE);
669 pub const QUIET_NAN: Any = Any(FloatTypes::QUIET_NAN);
690 pub const SIGNALING_NAN: Any = Any(FloatTypes::SIGNALING_NAN);
710
711 pub const ANY: Any = Any(FloatTypes::ANY);
726
727 impl Strategy for Any {
728 type Tree = BinarySearch;
729 type Value = $typ;
730
731 fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
732 let flags = self.0.normalise();
733 let sign_mask = if flags.contains(FloatTypes::NEGATIVE) {
734 <$typ as FloatLayout>::SIGN_MASK
735 } else {
736 0
737 };
738 let sign_or = if flags.contains(FloatTypes::POSITIVE) {
739 0
740 } else {
741 <$typ as FloatLayout>::SIGN_MASK
742 };
743
744 macro_rules! weight {
745 ($case:ident, $weight:expr) => {
746 if flags.contains(FloatTypes::$case) {
747 $weight
748 } else {
749 0
750 }
751 }
752 }
753
754 let quiet_or = $typ::NAN.to_bits() &
759 ($typ::EXP_MASK | ($typ::EXP_MASK >> 1));
760 let signaling_or = (quiet_or ^ ($typ::EXP_MASK >> 1)) |
761 $typ::EXP_MASK;
762
763 let (class_mask, class_or, allow_edge_exp, allow_zero_mant) =
764 prop_oneof![
765 weight!(NORMAL, 20) => Just(
766 ($typ::EXP_MASK | <$typ as FloatLayout>::MANTISSA_MASK, 0,
767 false, true)),
768 weight!(SUBNORMAL, 3) => Just(
769 (<$typ as FloatLayout>::MANTISSA_MASK, 0, true, false)),
770 weight!(ZERO, 4) => Just(
771 (0, 0, true, true)),
772 weight!(INFINITE, 2) => Just(
773 (0, $typ::EXP_MASK, true, true)),
774 weight!(QUIET_NAN, 1) => Just(
775 (<$typ as FloatLayout>::MANTISSA_MASK >> 1, quiet_or,
776 true, false)),
777 weight!(SIGNALING_NAN, 1) => Just(
778 (<$typ as FloatLayout>::MANTISSA_MASK >> 1, signaling_or,
779 true, false)),
780 ].new_tree(runner)?.current();
781
782 let mut generated_value: <$typ as FloatLayout>::Bits =
783 runner.rng().random();
784 generated_value &= sign_mask | class_mask;
785 generated_value |= sign_or | class_or;
786 let exp = generated_value & $typ::EXP_MASK;
787 if !allow_edge_exp && (0 == exp || $typ::EXP_MASK == exp) {
788 generated_value &= !$typ::EXP_MASK;
789 generated_value |= $typ::EXP_ZERO;
790 }
791 if !allow_zero_mant &&
792 0 == generated_value & <$typ as FloatLayout>::MANTISSA_MASK
793 {
794 generated_value |= 1;
795 }
796
797 Ok(BinarySearch::new_with_types(
798 $typ::from_bits(generated_value), flags))
799 }
800 }
801 }
802}
803
804macro_rules! float_bin_search {
805 ($typ:ident, $sample_typ:ident) => {
806 #[allow(missing_docs)]
807 pub mod $typ {
808 use super::float_samplers::$sample_typ;
809
810 use core::ops;
811 #[cfg(not(feature = "std"))]
812 use num_traits::float::FloatCore;
813
814 use rand::RngExt;
815
816 use super::{FloatLayout, FloatTypes};
817 use crate::strategy::*;
818 use crate::test_runner::TestRunner;
819
820 float_any!($typ);
821
822 #[derive(Clone, Copy, Debug)]
827 pub struct BinarySearch {
828 lo: $typ,
829 curr: $typ,
830 hi: $typ,
831 allowed: FloatTypes,
832 }
833
834 impl BinarySearch {
835 pub fn new(start: $typ) -> Self {
837 BinarySearch {
838 lo: 0.0,
839 curr: start,
840 hi: start,
841 allowed: FloatTypes::all(),
842 }
843 }
844
845 fn new_with_types(start: $typ, allowed: FloatTypes) -> Self {
846 BinarySearch {
847 lo: 0.0,
848 curr: start,
849 hi: start,
850 allowed,
851 }
852 }
853
854 fn new_clamped(lo: $typ, start: $typ, hi: $typ) -> Self {
858 BinarySearch {
859 lo: if start.is_sign_negative() {
860 hi.min(0.0)
861 } else {
862 lo.max(0.0)
863 },
864 hi: start,
865 curr: start,
866 allowed: FloatTypes::all(),
867 }
868 }
869
870 fn current_allowed(&self) -> bool {
871 use core::num::FpCategory::*;
872
873 let class_allowed = match self.curr.classify() {
875 Nan =>
876 {
881 self.allowed.contains(FloatTypes::QUIET_NAN)
882 || self
883 .allowed
884 .contains(FloatTypes::SIGNALING_NAN)
885 }
886 Infinite => self.allowed.contains(FloatTypes::INFINITE),
887 Zero => self.allowed.contains(FloatTypes::ZERO),
888 Subnormal => {
889 self.allowed.contains(FloatTypes::SUBNORMAL)
890 }
891 Normal => self.allowed.contains(FloatTypes::NORMAL),
892 };
893 let signum = self.curr.signum();
894 let sign_allowed = if signum > 0.0 {
895 self.allowed.contains(FloatTypes::POSITIVE)
896 } else if signum < 0.0 {
897 self.allowed.contains(FloatTypes::NEGATIVE)
898 } else {
899 true
900 };
901
902 class_allowed && sign_allowed
903 }
904
905 fn ensure_acceptable(&mut self) {
906 while !self.current_allowed() {
907 if !self.complicate_once() {
908 panic!(
909 "Unable to complicate floating-point back \
910 to acceptable value"
911 );
912 }
913 }
914 }
915
916 fn reposition(&mut self) -> bool {
917 let interval = self.hi - self.lo;
918 let interval =
919 if interval.is_finite() { interval } else { 0.0 };
920 let new_mid = self.lo + interval / 2.0;
921
922 let new_mid = if new_mid == self.curr || 0.0 == interval {
923 new_mid
924 } else {
925 self.lo
926 };
927
928 if new_mid == self.curr {
929 false
930 } else {
931 self.curr = new_mid;
932 true
933 }
934 }
935
936 fn done(lo: $typ, hi: $typ) -> bool {
937 (lo.abs() > hi.abs() && !hi.is_nan()) || lo.is_nan()
938 }
939
940 fn complicate_once(&mut self) -> bool {
941 if BinarySearch::done(self.lo, self.hi) {
942 return false;
943 }
944
945 self.lo = if self.curr == self.lo {
946 self.hi
947 } else {
948 self.curr
949 };
950
951 self.reposition()
952 }
953 }
954 impl ValueTree for BinarySearch {
955 type Value = $typ;
956
957 fn current(&self) -> $typ {
958 self.curr
959 }
960
961 fn simplify(&mut self) -> bool {
962 if BinarySearch::done(self.lo, self.hi) {
963 return false;
964 }
965
966 self.hi = self.curr;
967 if self.reposition() {
968 self.ensure_acceptable();
969 true
970 } else {
971 false
972 }
973 }
974
975 fn complicate(&mut self) -> bool {
976 if self.complicate_once() {
977 self.ensure_acceptable();
978 true
979 } else {
980 false
981 }
982 }
983 }
984
985 numeric_api!($typ, $sample_typ, 0.0);
986 }
987 };
988}
989
990#[cfg(feature = "f16")]
991float_bin_search!(f16, F16U);
992float_bin_search!(f32, F32U);
993float_bin_search!(f64, F64U);
994
995#[cfg(test)]
996mod test {
997 use crate::strategy::*;
998 use crate::test_runner::*;
999
1000 use super::*;
1001
1002 #[test]
1003 fn u8_inclusive_end_included() {
1004 let mut runner = TestRunner::deterministic();
1005 let mut ok = 0;
1006 for _ in 0..20 {
1007 let tree = (0..=1).new_tree(&mut runner).unwrap();
1008 let test = runner.run_one(tree, |v| {
1009 prop_assert_eq!(v, 1);
1010 Ok(())
1011 });
1012 if test.is_ok() {
1013 ok += 1;
1014 }
1015 }
1016 assert!(ok > 1, "inclusive end not included.");
1017 }
1018
1019 #[test]
1020 fn u8_inclusive_to_end_included() {
1021 let mut runner = TestRunner::deterministic();
1022 let mut ok = 0;
1023 for _ in 0..20 {
1024 let tree = (..=1u8).new_tree(&mut runner).unwrap();
1025 let test = runner.run_one(tree, |v| {
1026 prop_assert_eq!(v, 1);
1027 Ok(())
1028 });
1029 if test.is_ok() {
1030 ok += 1;
1031 }
1032 }
1033 assert!(ok > 1, "inclusive end not included.");
1034 }
1035
1036 #[test]
1037 fn i8_binary_search_always_converges() {
1038 fn assert_converges<P: Fn(i32) -> bool>(start: i8, pass: P) {
1039 let mut state = i8::BinarySearch::new(start);
1040 loop {
1041 if !pass(state.current() as i32) {
1042 if !state.simplify() {
1043 break;
1044 }
1045 } else {
1046 if !state.complicate() {
1047 break;
1048 }
1049 }
1050 }
1051
1052 assert!(!pass(state.current() as i32));
1053 assert!(
1054 pass(state.current() as i32 - 1)
1055 || pass(state.current() as i32 + 1)
1056 );
1057 }
1058
1059 for start in -128..0 {
1060 for target in start + 1..1 {
1061 assert_converges(start as i8, |v| v > target);
1062 }
1063 }
1064
1065 for start in 0..128 {
1066 for target in 0..start {
1067 assert_converges(start as i8, |v| v < target);
1068 }
1069 }
1070 }
1071
1072 #[test]
1073 fn u8_binary_search_always_converges() {
1074 fn assert_converges<P: Fn(u32) -> bool>(start: u8, pass: P) {
1075 let mut state = u8::BinarySearch::new(start);
1076 loop {
1077 if !pass(state.current() as u32) {
1078 if !state.simplify() {
1079 break;
1080 }
1081 } else {
1082 if !state.complicate() {
1083 break;
1084 }
1085 }
1086 }
1087
1088 assert!(!pass(state.current() as u32));
1089 assert!(pass(state.current() as u32 - 1));
1090 }
1091
1092 for start in 0..255 {
1093 for target in 0..start {
1094 assert_converges(start as u8, |v| v <= target);
1095 }
1096 }
1097 }
1098
1099 #[test]
1100 fn signed_integer_range_including_zero_converges_to_zero() {
1101 let mut runner = TestRunner::default();
1102 for _ in 0..100 {
1103 let mut state = (-42i32..64i32).new_tree(&mut runner).unwrap();
1104 let init_value = state.current();
1105 assert!(init_value >= -42 && init_value < 64);
1106
1107 while state.simplify() {
1108 let v = state.current();
1109 assert!(v >= -42 && v < 64);
1110 }
1111
1112 assert_eq!(0, state.current());
1113 }
1114 }
1115
1116 #[test]
1117 fn negative_integer_range_stays_in_bounds() {
1118 let mut runner = TestRunner::default();
1119 for _ in 0..100 {
1120 let mut state = (..-42i32).new_tree(&mut runner).unwrap();
1121 let init_value = state.current();
1122 assert!(init_value < -42);
1123
1124 while state.simplify() {
1125 assert!(
1126 state.current() < -42,
1127 "Violated bounds: {}",
1128 state.current()
1129 );
1130 }
1131
1132 assert_eq!(-43, state.current());
1133 }
1134 }
1135
1136 #[test]
1137 fn positive_signed_integer_range_stays_in_bounds() {
1138 let mut runner = TestRunner::default();
1139 for _ in 0..100 {
1140 let mut state = (42i32..).new_tree(&mut runner).unwrap();
1141 let init_value = state.current();
1142 assert!(init_value >= 42);
1143
1144 while state.simplify() {
1145 assert!(
1146 state.current() >= 42,
1147 "Violated bounds: {}",
1148 state.current()
1149 );
1150 }
1151
1152 assert_eq!(42, state.current());
1153 }
1154 }
1155
1156 #[test]
1157 fn unsigned_integer_range_stays_in_bounds() {
1158 let mut runner = TestRunner::default();
1159 for _ in 0..100 {
1160 let mut state = (42u32..56u32).new_tree(&mut runner).unwrap();
1161 let init_value = state.current();
1162 assert!(init_value >= 42 && init_value < 56);
1163
1164 while state.simplify() {
1165 assert!(
1166 state.current() >= 42,
1167 "Violated bounds: {}",
1168 state.current()
1169 );
1170 }
1171
1172 assert_eq!(42, state.current());
1173 }
1174 }
1175
1176 mod contract_sanity {
1177 macro_rules! contract_sanity {
1178 ($t:tt) => {
1179 mod $t {
1180 use crate::strategy::check_strategy_sanity;
1181
1182 const FORTY_TWO: $t = 42 as $t;
1183 const FIFTY_SIX: $t = 56 as $t;
1184
1185 #[test]
1186 fn range() {
1187 check_strategy_sanity(FORTY_TWO..FIFTY_SIX, None);
1188 }
1189
1190 #[test]
1191 fn range_inclusive() {
1192 check_strategy_sanity(FORTY_TWO..=FIFTY_SIX, None);
1193 }
1194
1195 #[test]
1196 fn range_to() {
1197 check_strategy_sanity(..FIFTY_SIX, None);
1198 }
1199
1200 #[test]
1201 fn range_to_inclusive() {
1202 check_strategy_sanity(..=FIFTY_SIX, None);
1203 }
1204
1205 #[test]
1206 fn range_from() {
1207 check_strategy_sanity(FORTY_TWO.., None);
1208 }
1209 }
1210 };
1211 }
1212 contract_sanity!(u8);
1213 contract_sanity!(i8);
1214 contract_sanity!(u16);
1215 contract_sanity!(i16);
1216 contract_sanity!(u32);
1217 contract_sanity!(i32);
1218 contract_sanity!(u64);
1219 contract_sanity!(i64);
1220 contract_sanity!(usize);
1221 contract_sanity!(isize);
1222 #[cfg(feature = "f16")]
1223 contract_sanity!(f16);
1224 contract_sanity!(f32);
1225 contract_sanity!(f64);
1226 }
1227
1228 #[test]
1229 fn unsigned_integer_binsearch_simplify_complicate_contract_upheld() {
1230 check_strategy_sanity(0u32..1000u32, None);
1231 check_strategy_sanity(0u32..1u32, None);
1232 }
1233
1234 #[test]
1235 fn signed_integer_binsearch_simplify_complicate_contract_upheld() {
1236 check_strategy_sanity(0i32..1000i32, None);
1237 check_strategy_sanity(0i32..1i32, None);
1238 }
1239
1240 #[test]
1241 fn positive_float_simplifies_to_zero() {
1242 let mut runner = TestRunner::default();
1243 let mut value = (0.0f64..2.0).new_tree(&mut runner).unwrap();
1244
1245 while value.simplify() {}
1246
1247 assert_eq!(0.0, value.current());
1248 }
1249
1250 #[test]
1251 fn positive_float_simplifies_to_base() {
1252 let mut runner = TestRunner::default();
1253 let mut value = (1.0f64..2.0).new_tree(&mut runner).unwrap();
1254
1255 while value.simplify() {}
1256
1257 assert_eq!(1.0, value.current());
1258 }
1259
1260 #[test]
1261 fn negative_float_simplifies_to_zero() {
1262 let mut runner = TestRunner::default();
1263 let mut value = (-2.0f64..0.0).new_tree(&mut runner).unwrap();
1264
1265 while value.simplify() {}
1266
1267 assert_eq!(0.0, value.current());
1268 }
1269
1270 #[test]
1271 fn positive_float_complicates_to_original() {
1272 let mut runner = TestRunner::default();
1273 let mut value = (1.0f64..2.0).new_tree(&mut runner).unwrap();
1274 let orig = value.current();
1275
1276 assert!(value.simplify());
1277 while value.complicate() {}
1278
1279 assert_eq!(orig, value.current());
1280 }
1281
1282 #[test]
1283 fn positive_infinity_simplifies_directly_to_zero() {
1284 let mut value = f64::BinarySearch::new(::std::f64::INFINITY);
1285
1286 assert!(value.simplify());
1287 assert_eq!(0.0, value.current());
1288 assert!(value.complicate());
1289 assert_eq!(::std::f64::INFINITY, value.current());
1290 assert!(!value.clone().complicate());
1291 assert!(!value.clone().simplify());
1292 }
1293
1294 #[test]
1295 fn negative_infinity_simplifies_directly_to_zero() {
1296 let mut value = f64::BinarySearch::new(::std::f64::NEG_INFINITY);
1297
1298 assert!(value.simplify());
1299 assert_eq!(0.0, value.current());
1300 assert!(value.complicate());
1301 assert_eq!(::std::f64::NEG_INFINITY, value.current());
1302 assert!(!value.clone().complicate());
1303 assert!(!value.clone().simplify());
1304 }
1305
1306 #[test]
1307 fn nan_simplifies_directly_to_zero() {
1308 let mut value = f64::BinarySearch::new(::std::f64::NAN);
1309
1310 assert!(value.simplify());
1311 assert_eq!(0.0, value.current());
1312 assert!(value.complicate());
1313 assert!(value.current().is_nan());
1314 assert!(!value.clone().complicate());
1315 assert!(!value.clone().simplify());
1316 }
1317
1318 #[test]
1319 fn float_simplifies_to_smallest_normal() {
1320 let mut runner = TestRunner::default();
1321 let mut value = (f64::MIN_POSITIVE..2.0).new_tree(&mut runner).unwrap();
1322
1323 while value.simplify() {}
1324
1325 assert_eq!(f64::MIN_POSITIVE, value.current());
1326 }
1327
1328 macro_rules! float_generation_test_body {
1329 ($strategy:ident, $typ:ident) => {
1330 use std::num::FpCategory;
1331
1332 let strategy = $strategy;
1333 let bits = strategy.normal_bits();
1334
1335 let mut seen_positive = 0;
1336 let mut seen_negative = 0;
1337 let mut seen_normal = 0;
1338 let mut seen_subnormal = 0;
1339 let mut seen_zero = 0;
1340 let mut seen_infinite = 0;
1341 let mut seen_quiet_nan = 0;
1342 let mut seen_signaling_nan = 0;
1343 let mut runner = TestRunner::deterministic();
1344
1345 let fidelity_1 = f32::from_bits(0x7F80_0001).to_bits();
1348 let fidelity_2 = f32::from_bits(0xFF80_0001).to_bits();
1349 let nan_fidelity = fidelity_1 != fidelity_2;
1350
1351 for _ in 0..1024 {
1352 let mut tree = strategy.new_tree(&mut runner).unwrap();
1353 let mut increment = 1;
1354
1355 loop {
1356 let value = tree.current();
1357
1358 let sign = value.signum(); if sign < 0.0 {
1360 prop_assert!(bits.contains(FloatTypes::NEGATIVE));
1361 seen_negative += increment;
1362 } else if sign > 0.0 {
1363 prop_assert!(bits.contains(FloatTypes::POSITIVE));
1365 seen_positive += increment;
1366 }
1367
1368 match value.classify() {
1369 FpCategory::Nan if nan_fidelity => {
1370 let raw = value.to_bits();
1371 let is_negative = raw << 1 >> 1 != raw;
1372 if is_negative {
1373 prop_assert!(
1374 bits.contains(FloatTypes::NEGATIVE)
1375 );
1376 seen_negative += increment;
1377 } else {
1378 prop_assert!(
1379 bits.contains(FloatTypes::POSITIVE)
1380 );
1381 seen_positive += increment;
1382 }
1383
1384 let is_quiet = raw & ($typ::EXP_MASK >> 1)
1385 == $typ::NAN.to_bits() & ($typ::EXP_MASK >> 1);
1386 if is_quiet {
1387 prop_assert!(
1392 bits.contains(FloatTypes::QUIET_NAN)
1393 || bits.contains(
1394 FloatTypes::SIGNALING_NAN
1395 )
1396 );
1397 seen_quiet_nan += increment;
1398 seen_signaling_nan += increment;
1399 } else {
1400 prop_assert!(
1401 bits.contains(FloatTypes::SIGNALING_NAN)
1402 );
1403 seen_signaling_nan += increment;
1404 }
1405 }
1406
1407 FpCategory::Nan => {
1408 seen_positive += increment;
1414 seen_negative += increment;
1415 seen_quiet_nan += increment;
1416 seen_signaling_nan += increment;
1417 prop_assert!(
1418 bits.contains(FloatTypes::QUIET_NAN)
1419 || bits.contains(FloatTypes::SIGNALING_NAN)
1420 );
1421 }
1422 FpCategory::Infinite => {
1423 prop_assert!(bits.contains(FloatTypes::INFINITE));
1424 seen_infinite += increment;
1425 }
1426 FpCategory::Zero => {
1427 prop_assert!(bits.contains(FloatTypes::ZERO));
1428 seen_zero += increment;
1429 }
1430 FpCategory::Subnormal => {
1431 prop_assert!(bits.contains(FloatTypes::SUBNORMAL));
1432 seen_subnormal += increment;
1433 }
1434 FpCategory::Normal => {
1435 prop_assert!(bits.contains(FloatTypes::NORMAL));
1436 seen_normal += increment;
1437 }
1438 }
1439
1440 increment = 0;
1442 if !tree.simplify() {
1443 break;
1444 }
1445 }
1446 }
1447
1448 if bits.contains(FloatTypes::POSITIVE) {
1449 prop_assert!(seen_positive > 200);
1450 }
1451 if bits.contains(FloatTypes::NEGATIVE) {
1452 prop_assert!(seen_negative > 200);
1453 }
1454 if bits.contains(FloatTypes::NORMAL) {
1455 prop_assert!(seen_normal > 100);
1456 }
1457 if bits.contains(FloatTypes::SUBNORMAL) {
1458 prop_assert!(seen_subnormal > 5);
1459 }
1460 if bits.contains(FloatTypes::ZERO) {
1461 prop_assert!(seen_zero > 5);
1462 }
1463 if bits.contains(FloatTypes::INFINITE) {
1464 prop_assert!(seen_infinite > 0);
1465 }
1466 if bits.contains(FloatTypes::QUIET_NAN) {
1467 prop_assert!(seen_quiet_nan > 0);
1468 }
1469 if bits.contains(FloatTypes::SIGNALING_NAN) {
1470 prop_assert!(seen_signaling_nan > 0);
1471 }
1472 };
1473 }
1474
1475 proptest! {
1476 #![proptest_config(crate::test_runner::Config::with_cases(1024))]
1477
1478 #[cfg(feature = "f16")]
1479 #[test]
1480 fn f16_any_generates_desired_values(
1481 strategy in crate::bits::u32::ANY.prop_map(f16::Any::from_bits)
1482 ) {
1483 float_generation_test_body!(strategy, f16);
1484 }
1485
1486 #[cfg(feature = "f16")]
1487 #[test]
1488 fn f16_any_sanity(
1489 strategy in crate::bits::u32::ANY.prop_map(f16::Any::from_bits)
1490 ) {
1491 check_strategy_sanity(strategy, Some(CheckStrategySanityOptions {
1492 strict_complicate_after_simplify: false,
1493 .. CheckStrategySanityOptions::default()
1494 }));
1495 }
1496
1497 #[test]
1498 fn f32_any_generates_desired_values(
1499 strategy in crate::bits::u32::ANY.prop_map(f32::Any::from_bits)
1500 ) {
1501 float_generation_test_body!(strategy, f32);
1502 }
1503
1504 #[test]
1505 fn f32_any_sanity(
1506 strategy in crate::bits::u32::ANY.prop_map(f32::Any::from_bits)
1507 ) {
1508 check_strategy_sanity(strategy, Some(CheckStrategySanityOptions {
1509 strict_complicate_after_simplify: false,
1510 .. CheckStrategySanityOptions::default()
1511 }));
1512 }
1513
1514 #[test]
1515 fn f64_any_generates_desired_values(
1516 strategy in crate::bits::u32::ANY.prop_map(f64::Any::from_bits)
1517 ) {
1518 float_generation_test_body!(strategy, f64);
1519 }
1520
1521 #[test]
1522 fn f64_any_sanity(
1523 strategy in crate::bits::u32::ANY.prop_map(f64::Any::from_bits)
1524 ) {
1525 check_strategy_sanity(strategy, Some(CheckStrategySanityOptions {
1526 strict_complicate_after_simplify: false,
1527 .. CheckStrategySanityOptions::default()
1528 }));
1529 }
1530 }
1531
1532 mod panic_on_empty {
1533 macro_rules! panic_on_empty {
1534 ($t:tt) => {
1535 mod $t {
1536 use crate::strategy::Strategy;
1537 use crate::test_runner::TestRunner;
1538 use std::panic;
1539 use std::string::String;
1540
1541 const ZERO: $t = 0 as $t;
1542 const ONE: $t = 1 as $t;
1543
1544 #[test]
1545 fn range() {
1546 assert_eq!(
1547 panic::catch_unwind(|| {
1548 let mut runner = TestRunner::deterministic();
1549 let _ = (ZERO..ZERO).new_tree(&mut runner);
1550 })
1551 .err()
1552 .and_then(|a| a
1553 .downcast_ref::<String>()
1554 .map(|s| {
1555 s == "Invalid use of empty range 0..0."
1556 })),
1557 Some(true)
1558 );
1559 }
1560
1561 #[test]
1562 fn range_inclusive() {
1563 assert_eq!(
1564 panic::catch_unwind(|| {
1565 let mut runner = TestRunner::deterministic();
1566 let _ = (ONE..=ZERO).new_tree(&mut runner);
1567 })
1568 .err()
1569 .and_then(|a| a
1570 .downcast_ref::<String>()
1571 .map(|s| {
1572 s == "Invalid use of empty range 1..=0."
1573 })),
1574 Some(true)
1575 );
1576 }
1577 }
1578 };
1579 }
1580 panic_on_empty!(u8);
1581 panic_on_empty!(i8);
1582 panic_on_empty!(u16);
1583 panic_on_empty!(i16);
1584 panic_on_empty!(u32);
1585 panic_on_empty!(i32);
1586 panic_on_empty!(u64);
1587 panic_on_empty!(i64);
1588 panic_on_empty!(usize);
1589 panic_on_empty!(isize);
1590 #[cfg(feature = "f16")]
1591 panic_on_empty!(f16);
1592 panic_on_empty!(f32);
1593 panic_on_empty!(f64);
1594 }
1595}