1use crate::test_runner::TestRunner;
16use core::ops::Range;
17use rand::distributions::uniform::{SampleUniform, Uniform};
18use rand::distributions::{Distribution, Standard};
19
20pub(crate) fn sample_uniform<X: SampleUniform>(
21 run: &mut TestRunner,
22 range: Range<X>,
23) -> X {
24 Uniform::new(range.start, range.end).sample(run.rng())
25}
26
27pub(crate) fn sample_uniform_incl<X: SampleUniform>(
28 run: &mut TestRunner,
29 start: X,
30 end: X,
31) -> X {
32 Uniform::new_inclusive(start, end).sample(run.rng())
33}
34
35macro_rules! int_any {
36 ($typ: ident) => {
37 #[derive(Clone, Copy, Debug)]
39 #[must_use = "strategies do nothing unless used"]
40 pub struct Any(());
41 pub const ANY: Any = Any(());
44
45 impl Strategy for Any {
46 type Tree = BinarySearch;
47 type Value = $typ;
48
49 fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
50 Ok(BinarySearch::new(runner.rng().gen()))
51 }
52 }
53 };
54}
55
56macro_rules! numeric_api {
57 ($typ:ident, $epsilon:expr) => {
58 impl Strategy for ::core::ops::Range<$typ> {
59 type Tree = BinarySearch;
60 type Value = $typ;
61
62 fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
63 Ok(BinarySearch::new_clamped(
64 self.start,
65 $crate::num::sample_uniform(runner, self.clone()),
66 self.end - $epsilon,
67 ))
68 }
69 }
70
71 impl Strategy for ::core::ops::RangeInclusive<$typ> {
72 type Tree = BinarySearch;
73 type Value = $typ;
74
75 fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
76 Ok(BinarySearch::new_clamped(
77 *self.start(),
78 $crate::num::sample_uniform_incl(
79 runner,
80 *self.start(),
81 *self.end(),
82 ),
83 *self.end(),
84 ))
85 }
86 }
87
88 impl Strategy for ::core::ops::RangeFrom<$typ> {
89 type Tree = BinarySearch;
90 type Value = $typ;
91
92 fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
93 Ok(BinarySearch::new_clamped(
94 self.start,
95 $crate::num::sample_uniform_incl(
96 runner,
97 self.start,
98 ::core::$typ::MAX,
99 ),
100 ::core::$typ::MAX,
101 ))
102 }
103 }
104
105 impl Strategy for ::core::ops::RangeTo<$typ> {
106 type Tree = BinarySearch;
107 type Value = $typ;
108
109 fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
110 Ok(BinarySearch::new_clamped(
111 ::core::$typ::MIN,
112 $crate::num::sample_uniform(
113 runner,
114 ::core::$typ::MIN..self.end,
115 ),
116 self.end,
117 ))
118 }
119 }
120
121 impl Strategy for ::core::ops::RangeToInclusive<$typ> {
122 type Tree = BinarySearch;
123 type Value = $typ;
124
125 fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
126 Ok(BinarySearch::new_clamped(
127 ::core::$typ::MIN,
128 $crate::num::sample_uniform_incl(
129 runner,
130 ::core::$typ::MIN,
131 self.end,
132 ),
133 self.end,
134 ))
135 }
136 }
137 };
138}
139
140macro_rules! signed_integer_bin_search {
141 ($typ:ident) => {
142 #[allow(missing_docs)]
143 pub mod $typ {
144 use rand::Rng;
145
146 use crate::strategy::*;
147 use crate::test_runner::TestRunner;
148
149 int_any!($typ);
150
151 #[derive(Clone, Copy, Debug)]
154 pub struct BinarySearch {
155 lo: $typ,
156 curr: $typ,
157 hi: $typ,
158 }
159 impl BinarySearch {
160 pub fn new(start: $typ) -> Self {
162 BinarySearch {
163 lo: 0,
164 curr: start,
165 hi: start,
166 }
167 }
168
169 fn new_clamped(lo: $typ, start: $typ, hi: $typ) -> Self {
173 use core::cmp::{max, min};
174
175 BinarySearch {
176 lo: if start < 0 {
177 min(0, hi - 1)
178 } else {
179 max(0, lo)
180 },
181 hi: start,
182 curr: start,
183 }
184 }
185
186 fn reposition(&mut self) -> bool {
187 let interval = self.hi - self.lo;
190 let new_mid = self.lo + interval / 2;
191
192 if new_mid == self.curr {
193 false
194 } else {
195 self.curr = new_mid;
196 true
197 }
198 }
199
200 fn magnitude_greater(lhs: $typ, rhs: $typ) -> bool {
201 if 0 == lhs {
202 false
203 } else if lhs < 0 {
204 lhs < rhs
205 } else {
206 lhs > rhs
207 }
208 }
209 }
210 impl ValueTree for BinarySearch {
211 type Value = $typ;
212
213 fn current(&self) -> $typ {
214 self.curr
215 }
216
217 fn simplify(&mut self) -> bool {
218 if !BinarySearch::magnitude_greater(self.hi, self.lo) {
219 return false;
220 }
221
222 self.hi = self.curr;
223 self.reposition()
224 }
225
226 fn complicate(&mut self) -> bool {
227 if !BinarySearch::magnitude_greater(self.hi, self.lo) {
228 return false;
229 }
230
231 self.lo = self.curr + if self.hi < 0 { -1 } else { 1 };
232
233 self.reposition()
234 }
235 }
236
237 numeric_api!($typ, 1);
238 }
239 };
240}
241
242macro_rules! unsigned_integer_bin_search {
243 ($typ:ident) => {
244 #[allow(missing_docs)]
245 pub mod $typ {
246 use rand::Rng;
247
248 use crate::strategy::*;
249 use crate::test_runner::TestRunner;
250
251 int_any!($typ);
252
253 #[derive(Clone, Copy, Debug)]
256 pub struct BinarySearch {
257 lo: $typ,
258 curr: $typ,
259 hi: $typ,
260 }
261 impl BinarySearch {
262 pub fn new(start: $typ) -> Self {
264 BinarySearch {
265 lo: 0,
266 curr: start,
267 hi: start,
268 }
269 }
270
271 fn new_clamped(lo: $typ, start: $typ, _hi: $typ) -> Self {
274 BinarySearch {
275 lo: lo,
276 curr: start,
277 hi: start,
278 }
279 }
280
281 pub fn new_above(lo: $typ, start: $typ) -> Self {
284 BinarySearch::new_clamped(lo, start, start)
285 }
286
287 fn reposition(&mut self) -> bool {
288 let interval = self.hi - self.lo;
289 let new_mid = self.lo + interval / 2;
290
291 if new_mid == self.curr {
292 false
293 } else {
294 self.curr = new_mid;
295 true
296 }
297 }
298 }
299 impl ValueTree for BinarySearch {
300 type Value = $typ;
301
302 fn current(&self) -> $typ {
303 self.curr
304 }
305
306 fn simplify(&mut self) -> bool {
307 if self.hi <= self.lo {
308 return false;
309 }
310
311 self.hi = self.curr;
312 self.reposition()
313 }
314
315 fn complicate(&mut self) -> bool {
316 if self.hi <= self.lo {
317 return false;
318 }
319
320 self.lo = self.curr + 1;
321 self.reposition()
322 }
323 }
324
325 numeric_api!($typ, 1);
326 }
327 };
328}
329
330signed_integer_bin_search!(i8);
331signed_integer_bin_search!(i16);
332signed_integer_bin_search!(i32);
333signed_integer_bin_search!(i64);
334#[cfg(not(target_arch = "wasm32"))]
335signed_integer_bin_search!(i128);
336signed_integer_bin_search!(isize);
337unsigned_integer_bin_search!(u8);
338unsigned_integer_bin_search!(u16);
339unsigned_integer_bin_search!(u32);
340unsigned_integer_bin_search!(u64);
341#[cfg(not(target_arch = "wasm32"))]
342unsigned_integer_bin_search!(u128);
343unsigned_integer_bin_search!(usize);
344
345bitflags! {
346 pub(crate) struct FloatTypes: u32 {
347 const POSITIVE = 0b0000_0001;
348 const NEGATIVE = 0b0000_0010;
349 const NORMAL = 0b0000_0100;
350 const SUBNORMAL = 0b0000_1000;
351 const ZERO = 0b0001_0000;
352 const INFINITE = 0b0010_0000;
353 const QUIET_NAN = 0b0100_0000;
354 const SIGNALING_NAN = 0b1000_0000;
355 const ANY =
356 Self::POSITIVE.bits |
357 Self::NEGATIVE.bits |
358 Self::NORMAL.bits |
359 Self::SUBNORMAL.bits |
360 Self::ZERO.bits |
361 Self::INFINITE.bits |
362 Self::QUIET_NAN.bits;
363 }
364}
365
366impl FloatTypes {
367 fn normalise(mut self) -> Self {
368 if !self.intersects(FloatTypes::POSITIVE | FloatTypes::NEGATIVE) {
369 self |= FloatTypes::POSITIVE;
370 }
371
372 if !self.intersects(
373 FloatTypes::NORMAL
374 | FloatTypes::SUBNORMAL
375 | FloatTypes::ZERO
376 | FloatTypes::INFINITE
377 | FloatTypes::QUIET_NAN
378 | FloatTypes::SIGNALING_NAN,
379 ) {
380 self |= FloatTypes::NORMAL;
381 }
382 self
383 }
384}
385
386trait FloatLayout
387where
388 Standard: Distribution<Self::Bits>,
389{
390 type Bits: Copy;
391
392 const SIGN_MASK: Self::Bits;
393 const EXP_MASK: Self::Bits;
394 const EXP_ZERO: Self::Bits;
395 const MANTISSA_MASK: Self::Bits;
396}
397
398impl FloatLayout for f32 {
399 type Bits = u32;
400
401 const SIGN_MASK: u32 = 0x8000_0000;
402 const EXP_MASK: u32 = 0x7F80_0000;
403 const EXP_ZERO: u32 = 0x3F80_0000;
404 const MANTISSA_MASK: u32 = 0x007F_FFFF;
405}
406
407impl FloatLayout for f64 {
408 type Bits = u64;
409
410 const SIGN_MASK: u64 = 0x8000_0000_0000_0000;
411 const EXP_MASK: u64 = 0x7FF0_0000_0000_0000;
412 const EXP_ZERO: u64 = 0x3FF0_0000_0000_0000;
413 const MANTISSA_MASK: u64 = 0x000F_FFFF_FFFF_FFFF;
414}
415
416macro_rules! float_any {
417 ($typ:ident) => {
418 #[derive(Clone, Copy, Debug)]
447 #[must_use = "strategies do nothing unless used"]
448 pub struct Any(FloatTypes);
449
450 #[cfg(test)]
451 impl Any {
452 pub(crate) fn from_bits(bits: u32) -> Self {
453 Any(FloatTypes::from_bits_truncate(bits))
454 }
455
456 pub(crate) fn normal_bits(&self) -> FloatTypes {
457 self.0.normalise()
458 }
459 }
460
461 impl ops::BitOr for Any {
462 type Output = Self;
463
464 fn bitor(self, rhs: Self) -> Self {
465 Any(self.0 | rhs.0)
466 }
467 }
468
469 impl ops::BitOrAssign for Any {
470 fn bitor_assign(&mut self, rhs: Self) {
471 self.0 |= rhs.0
472 }
473 }
474
475 pub const POSITIVE: Any = Any(FloatTypes::POSITIVE);
483 pub const NEGATIVE: Any = Any(FloatTypes::NEGATIVE);
491 pub const NORMAL: Any = Any(FloatTypes::NORMAL);
506 pub const SUBNORMAL: Any = Any(FloatTypes::SUBNORMAL);
518 pub const ZERO: Any = Any(FloatTypes::ZERO);
526 pub const INFINITE: Any = Any(FloatTypes::INFINITE);
531 pub const QUIET_NAN: Any = Any(FloatTypes::QUIET_NAN);
552 pub const SIGNALING_NAN: Any = Any(FloatTypes::SIGNALING_NAN);
572
573 pub const ANY: Any = Any(FloatTypes::ANY);
588
589 impl Strategy for Any {
590 type Tree = BinarySearch;
591 type Value = $typ;
592
593 fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
594 let flags = self.0.normalise();
595 let sign_mask = if flags.contains(FloatTypes::NEGATIVE) {
596 $typ::SIGN_MASK
597 } else {
598 0
599 };
600 let sign_or = if flags.contains(FloatTypes::POSITIVE) {
601 0
602 } else {
603 $typ::SIGN_MASK
604 };
605
606 macro_rules! weight {
607 ($case:ident, $weight:expr) => {
608 if flags.contains(FloatTypes::$case) {
609 $weight
610 } else {
611 0
612 }
613 }
614 }
615
616 let quiet_or = ::core::$typ::NAN.to_bits() &
621 ($typ::EXP_MASK | ($typ::EXP_MASK >> 1));
622 let signaling_or = (quiet_or ^ ($typ::EXP_MASK >> 1)) |
623 $typ::EXP_MASK;
624
625 let (class_mask, class_or, allow_edge_exp, allow_zero_mant) =
626 prop_oneof![
627 weight!(NORMAL, 20) => Just(
628 ($typ::EXP_MASK | $typ::MANTISSA_MASK, 0,
629 false, true)),
630 weight!(SUBNORMAL, 3) => Just(
631 ($typ::MANTISSA_MASK, 0, true, false)),
632 weight!(ZERO, 4) => Just(
633 (0, 0, true, true)),
634 weight!(INFINITE, 2) => Just(
635 (0, $typ::EXP_MASK, true, true)),
636 weight!(QUIET_NAN, 1) => Just(
637 ($typ::MANTISSA_MASK >> 1, quiet_or,
638 true, false)),
639 weight!(SIGNALING_NAN, 1) => Just(
640 ($typ::MANTISSA_MASK >> 1, signaling_or,
641 true, false)),
642 ].new_tree(runner)?.current();
643
644 let mut generated_value: <$typ as FloatLayout>::Bits =
645 runner.rng().gen();
646 generated_value &= sign_mask | class_mask;
647 generated_value |= sign_or | class_or;
648 let exp = generated_value & $typ::EXP_MASK;
649 if !allow_edge_exp && (0 == exp || $typ::EXP_MASK == exp) {
650 generated_value &= !$typ::EXP_MASK;
651 generated_value |= $typ::EXP_ZERO;
652 }
653 if !allow_zero_mant &&
654 0 == generated_value & $typ::MANTISSA_MASK
655 {
656 generated_value |= 1;
657 }
658
659 Ok(BinarySearch::new_with_types(
660 $typ::from_bits(generated_value), flags))
661 }
662 }
663 }
664}
665
666macro_rules! float_bin_search {
667 ($typ:ident) => {
668 #[allow(missing_docs)]
669 pub mod $typ {
670 use core::ops;
671 #[cfg(not(feature = "std"))]
672 use num_traits::float::FloatCore;
673
674 use rand::Rng;
675
676 use super::{FloatLayout, FloatTypes};
677 use crate::strategy::*;
678 use crate::test_runner::TestRunner;
679
680 float_any!($typ);
681
682 #[derive(Clone, Copy, Debug)]
687 pub struct BinarySearch {
688 lo: $typ,
689 curr: $typ,
690 hi: $typ,
691 allowed: FloatTypes,
692 }
693
694 impl BinarySearch {
695 pub fn new(start: $typ) -> Self {
697 BinarySearch {
698 lo: 0.0,
699 curr: start,
700 hi: start,
701 allowed: FloatTypes::all(),
702 }
703 }
704
705 fn new_with_types(start: $typ, allowed: FloatTypes) -> Self {
706 BinarySearch {
707 lo: 0.0,
708 curr: start,
709 hi: start,
710 allowed,
711 }
712 }
713
714 fn new_clamped(lo: $typ, start: $typ, hi: $typ) -> Self {
718 BinarySearch {
719 lo: if start.is_sign_negative() {
720 hi.min(0.0)
721 } else {
722 lo.max(0.0)
723 },
724 hi: start,
725 curr: start,
726 allowed: FloatTypes::all(),
727 }
728 }
729
730 fn current_allowed(&self) -> bool {
731 use core::num::FpCategory::*;
732
733 let class_allowed = match self.curr.classify() {
735 Nan =>
736 {
741 self.allowed.contains(FloatTypes::QUIET_NAN)
742 || self
743 .allowed
744 .contains(FloatTypes::SIGNALING_NAN)
745 }
746 Infinite => self.allowed.contains(FloatTypes::INFINITE),
747 Zero => self.allowed.contains(FloatTypes::ZERO),
748 Subnormal => {
749 self.allowed.contains(FloatTypes::SUBNORMAL)
750 }
751 Normal => self.allowed.contains(FloatTypes::NORMAL),
752 };
753 let signum = self.curr.signum();
754 let sign_allowed = if signum > 0.0 {
755 self.allowed.contains(FloatTypes::POSITIVE)
756 } else if signum < 0.0 {
757 self.allowed.contains(FloatTypes::NEGATIVE)
758 } else {
759 true
760 };
761
762 class_allowed && sign_allowed
763 }
764
765 fn ensure_acceptable(&mut self) {
766 while !self.current_allowed() {
767 if !self.complicate_once() {
768 panic!(
769 "Unable to complicate floating-point back \
770 to acceptable value"
771 );
772 }
773 }
774 }
775
776 fn reposition(&mut self) -> bool {
777 let interval = self.hi - self.lo;
778 let interval =
779 if interval.is_finite() { interval } else { 0.0 };
780 let new_mid = self.lo + interval / 2.0;
781
782 let new_mid = if new_mid == self.curr || 0.0 == interval {
783 new_mid
784 } else {
785 self.lo
786 };
787
788 if new_mid == self.curr {
789 false
790 } else {
791 self.curr = new_mid;
792 true
793 }
794 }
795
796 fn done(lo: $typ, hi: $typ) -> bool {
797 (lo.abs() > hi.abs() && !hi.is_nan()) || lo.is_nan()
798 }
799
800 fn complicate_once(&mut self) -> bool {
801 if BinarySearch::done(self.lo, self.hi) {
802 return false;
803 }
804
805 self.lo = if self.curr == self.lo {
806 self.hi
807 } else {
808 self.curr
809 };
810
811 self.reposition()
812 }
813 }
814 impl ValueTree for BinarySearch {
815 type Value = $typ;
816
817 fn current(&self) -> $typ {
818 self.curr
819 }
820
821 fn simplify(&mut self) -> bool {
822 if BinarySearch::done(self.lo, self.hi) {
823 return false;
824 }
825
826 self.hi = self.curr;
827 if self.reposition() {
828 self.ensure_acceptable();
829 true
830 } else {
831 false
832 }
833 }
834
835 fn complicate(&mut self) -> bool {
836 if self.complicate_once() {
837 self.ensure_acceptable();
838 true
839 } else {
840 false
841 }
842 }
843 }
844
845 numeric_api!($typ, 0.0);
846 }
847 };
848}
849
850float_bin_search!(f32);
851float_bin_search!(f64);
852
853#[cfg(test)]
854mod test {
855 use crate::strategy::*;
856 use crate::test_runner::*;
857
858 use super::*;
859
860 #[test]
861 fn u8_inclusive_end_included() {
862 let mut runner = TestRunner::deterministic();
863 let mut ok = 0;
864 for _ in 0..20 {
865 let tree = (0..=1).new_tree(&mut runner).unwrap();
866 let test = runner.run_one(tree, |v| {
867 prop_assert_eq!(v, 1);
868 Ok(())
869 });
870 if test.is_ok() {
871 ok += 1;
872 }
873 }
874 assert!(ok > 1, "inclusive end not included.");
875 }
876
877 #[test]
878 fn u8_inclusive_to_end_included() {
879 let mut runner = TestRunner::deterministic();
880 let mut ok = 0;
881 for _ in 0..20 {
882 let tree = (..=1u8).new_tree(&mut runner).unwrap();
883 let test = runner.run_one(tree, |v| {
884 prop_assert_eq!(v, 1);
885 Ok(())
886 });
887 if test.is_ok() {
888 ok += 1;
889 }
890 }
891 assert!(ok > 1, "inclusive end not included.");
892 }
893
894 #[test]
895 fn i8_binary_search_always_converges() {
896 fn assert_converges<P: Fn(i32) -> bool>(start: i8, pass: P) {
897 let mut state = i8::BinarySearch::new(start);
898 loop {
899 if !pass(state.current() as i32) {
900 if !state.simplify() {
901 break;
902 }
903 } else {
904 if !state.complicate() {
905 break;
906 }
907 }
908 }
909
910 assert!(!pass(state.current() as i32));
911 assert!(
912 pass(state.current() as i32 - 1)
913 || pass(state.current() as i32 + 1)
914 );
915 }
916
917 for start in -128..0 {
918 for target in start + 1..1 {
919 assert_converges(start as i8, |v| v > target);
920 }
921 }
922
923 for start in 0..128 {
924 for target in 0..start {
925 assert_converges(start as i8, |v| v < target);
926 }
927 }
928 }
929
930 #[test]
931 fn u8_binary_search_always_converges() {
932 fn assert_converges<P: Fn(u32) -> bool>(start: u8, pass: P) {
933 let mut state = u8::BinarySearch::new(start);
934 loop {
935 if !pass(state.current() as u32) {
936 if !state.simplify() {
937 break;
938 }
939 } else {
940 if !state.complicate() {
941 break;
942 }
943 }
944 }
945
946 assert!(!pass(state.current() as u32));
947 assert!(pass(state.current() as u32 - 1));
948 }
949
950 for start in 0..255 {
951 for target in 0..start {
952 assert_converges(start as u8, |v| v <= target);
953 }
954 }
955 }
956
957 #[test]
958 fn signed_integer_range_including_zero_converges_to_zero() {
959 let mut runner = TestRunner::default();
960 for _ in 0..100 {
961 let mut state = (-42i32..64i32).new_tree(&mut runner).unwrap();
962 let init_value = state.current();
963 assert!(init_value >= -42 && init_value < 64);
964
965 while state.simplify() {
966 let v = state.current();
967 assert!(v >= -42 && v < 64);
968 }
969
970 assert_eq!(0, state.current());
971 }
972 }
973
974 #[test]
975 fn negative_integer_range_stays_in_bounds() {
976 let mut runner = TestRunner::default();
977 for _ in 0..100 {
978 let mut state = (..-42i32).new_tree(&mut runner).unwrap();
979 let init_value = state.current();
980 assert!(init_value < -42);
981
982 while state.simplify() {
983 assert!(
984 state.current() < -42,
985 "Violated bounds: {}",
986 state.current()
987 );
988 }
989
990 assert_eq!(-43, state.current());
991 }
992 }
993
994 #[test]
995 fn positive_signed_integer_range_stays_in_bounds() {
996 let mut runner = TestRunner::default();
997 for _ in 0..100 {
998 let mut state = (42i32..).new_tree(&mut runner).unwrap();
999 let init_value = state.current();
1000 assert!(init_value >= 42);
1001
1002 while state.simplify() {
1003 assert!(
1004 state.current() >= 42,
1005 "Violated bounds: {}",
1006 state.current()
1007 );
1008 }
1009
1010 assert_eq!(42, state.current());
1011 }
1012 }
1013
1014 #[test]
1015 fn unsigned_integer_range_stays_in_bounds() {
1016 let mut runner = TestRunner::default();
1017 for _ in 0..100 {
1018 let mut state = (42u32..56u32).new_tree(&mut runner).unwrap();
1019 let init_value = state.current();
1020 assert!(init_value >= 42 && init_value < 56);
1021
1022 while state.simplify() {
1023 assert!(
1024 state.current() >= 42,
1025 "Violated bounds: {}",
1026 state.current()
1027 );
1028 }
1029
1030 assert_eq!(42, state.current());
1031 }
1032 }
1033
1034 mod contract_sanity {
1035 macro_rules! contract_sanity {
1036 ($t:tt) => {
1037 mod $t {
1038 use crate::strategy::check_strategy_sanity;
1039
1040 const FOURTY_TWO: $t = 42 as $t;
1041 const FIFTY_SIX: $t = 56 as $t;
1042
1043 #[test]
1044 fn range() {
1045 check_strategy_sanity(FOURTY_TWO..FIFTY_SIX, None);
1046 }
1047
1048 #[test]
1049 fn range_inclusive() {
1050 check_strategy_sanity(FOURTY_TWO..=FIFTY_SIX, None);
1051 }
1052
1053 #[test]
1054 fn range_to() {
1055 check_strategy_sanity(..FIFTY_SIX, None);
1056 }
1057
1058 #[test]
1059 fn range_to_inclusive() {
1060 check_strategy_sanity(..=FIFTY_SIX, None);
1061 }
1062
1063 #[test]
1064 fn range_from() {
1065 check_strategy_sanity(FOURTY_TWO.., None);
1066 }
1067 }
1068 };
1069 }
1070 contract_sanity!(u8);
1071 contract_sanity!(i8);
1072 contract_sanity!(u16);
1073 contract_sanity!(i16);
1074 contract_sanity!(u32);
1075 contract_sanity!(i32);
1076 contract_sanity!(u64);
1077 contract_sanity!(i64);
1078 contract_sanity!(usize);
1079 contract_sanity!(isize);
1080 contract_sanity!(f32);
1081 contract_sanity!(f64);
1082 }
1083
1084 #[test]
1085 fn unsigned_integer_binsearch_simplify_complicate_contract_upheld() {
1086 check_strategy_sanity(0u32..1000u32, None);
1087 check_strategy_sanity(0u32..1u32, None);
1088 }
1089
1090 #[test]
1091 fn signed_integer_binsearch_simplify_complicate_contract_upheld() {
1092 check_strategy_sanity(0i32..1000i32, None);
1093 check_strategy_sanity(0i32..1i32, None);
1094 }
1095
1096 #[test]
1097 fn positive_float_simplifies_to_zero() {
1098 let mut runner = TestRunner::default();
1099 let mut value = (0.0f64..2.0).new_tree(&mut runner).unwrap();
1100
1101 while value.simplify() {}
1102
1103 assert_eq!(0.0, value.current());
1104 }
1105
1106 #[test]
1107 fn positive_float_simplifies_to_base() {
1108 let mut runner = TestRunner::default();
1109 let mut value = (1.0f64..2.0).new_tree(&mut runner).unwrap();
1110
1111 while value.simplify() {}
1112
1113 assert_eq!(1.0, value.current());
1114 }
1115
1116 #[test]
1117 fn negative_float_simplifies_to_zero() {
1118 let mut runner = TestRunner::default();
1119 let mut value = (-2.0f64..0.0).new_tree(&mut runner).unwrap();
1120
1121 while value.simplify() {}
1122
1123 assert_eq!(0.0, value.current());
1124 }
1125
1126 #[test]
1127 fn positive_float_complicates_to_original() {
1128 let mut runner = TestRunner::default();
1129 let mut value = (1.0f64..2.0).new_tree(&mut runner).unwrap();
1130 let orig = value.current();
1131
1132 assert!(value.simplify());
1133 while value.complicate() {}
1134
1135 assert_eq!(orig, value.current());
1136 }
1137
1138 #[test]
1139 fn positive_infinity_simplifies_directly_to_zero() {
1140 let mut value = f64::BinarySearch::new(::std::f64::INFINITY);
1141
1142 assert!(value.simplify());
1143 assert_eq!(0.0, value.current());
1144 assert!(value.complicate());
1145 assert_eq!(::std::f64::INFINITY, value.current());
1146 assert!(!value.clone().complicate());
1147 assert!(!value.clone().simplify());
1148 }
1149
1150 #[test]
1151 fn negative_infinity_simplifies_directly_to_zero() {
1152 let mut value = f64::BinarySearch::new(::std::f64::NEG_INFINITY);
1153
1154 assert!(value.simplify());
1155 assert_eq!(0.0, value.current());
1156 assert!(value.complicate());
1157 assert_eq!(::std::f64::NEG_INFINITY, value.current());
1158 assert!(!value.clone().complicate());
1159 assert!(!value.clone().simplify());
1160 }
1161
1162 #[test]
1163 fn nan_simplifies_directly_to_zero() {
1164 let mut value = f64::BinarySearch::new(::std::f64::NAN);
1165
1166 assert!(value.simplify());
1167 assert_eq!(0.0, value.current());
1168 assert!(value.complicate());
1169 assert!(value.current().is_nan());
1170 assert!(!value.clone().complicate());
1171 assert!(!value.clone().simplify());
1172 }
1173
1174 #[test]
1175 fn float_simplifies_to_smallest_normal() {
1176 let mut runner = TestRunner::default();
1177 let mut value = (::std::f64::MIN_POSITIVE..2.0)
1178 .new_tree(&mut runner)
1179 .unwrap();
1180
1181 while value.simplify() {}
1182
1183 assert_eq!(::std::f64::MIN_POSITIVE, value.current());
1184 }
1185
1186 macro_rules! float_generation_test_body {
1187 ($strategy:ident, $typ:ident) => {
1188 use std::num::FpCategory;
1189
1190 let strategy = $strategy;
1191 let bits = strategy.normal_bits();
1192
1193 let mut seen_positive = 0;
1194 let mut seen_negative = 0;
1195 let mut seen_normal = 0;
1196 let mut seen_subnormal = 0;
1197 let mut seen_zero = 0;
1198 let mut seen_infinite = 0;
1199 let mut seen_quiet_nan = 0;
1200 let mut seen_signaling_nan = 0;
1201 let mut runner = TestRunner::deterministic();
1202
1203 let fidelity_1 = f32::from_bits(0x7F80_0001).to_bits();
1206 let fidelity_2 = f32::from_bits(0xFF80_0001).to_bits();
1207 let nan_fidelity = fidelity_1 != fidelity_2;
1208
1209 for _ in 0..1024 {
1210 let mut tree = strategy.new_tree(&mut runner).unwrap();
1211 let mut increment = 1;
1212
1213 loop {
1214 let value = tree.current();
1215
1216 let sign = value.signum(); if sign < 0.0 {
1218 prop_assert!(bits.contains(FloatTypes::NEGATIVE));
1219 seen_negative += increment;
1220 } else if sign > 0.0 {
1221 prop_assert!(bits.contains(FloatTypes::POSITIVE));
1223 seen_positive += increment;
1224 }
1225
1226 match value.classify() {
1227 FpCategory::Nan if nan_fidelity => {
1228 let raw = value.to_bits();
1229 let is_negative = raw << 1 >> 1 != raw;
1230 if is_negative {
1231 prop_assert!(
1232 bits.contains(FloatTypes::NEGATIVE)
1233 );
1234 seen_negative += increment;
1235 } else {
1236 prop_assert!(
1237 bits.contains(FloatTypes::POSITIVE)
1238 );
1239 seen_positive += increment;
1240 }
1241
1242 let is_quiet = raw & ($typ::EXP_MASK >> 1)
1243 == ::std::$typ::NAN.to_bits()
1244 & ($typ::EXP_MASK >> 1);
1245 if is_quiet {
1246 prop_assert!(
1251 bits.contains(FloatTypes::QUIET_NAN)
1252 || bits.contains(
1253 FloatTypes::SIGNALING_NAN
1254 )
1255 );
1256 seen_quiet_nan += increment;
1257 seen_signaling_nan += increment;
1258 } else {
1259 prop_assert!(
1260 bits.contains(FloatTypes::SIGNALING_NAN)
1261 );
1262 seen_signaling_nan += increment;
1263 }
1264 }
1265
1266 FpCategory::Nan => {
1267 seen_positive += increment;
1273 seen_negative += increment;
1274 seen_quiet_nan += increment;
1275 seen_signaling_nan += increment;
1276 prop_assert!(
1277 bits.contains(FloatTypes::QUIET_NAN)
1278 || bits.contains(FloatTypes::SIGNALING_NAN)
1279 );
1280 }
1281 FpCategory::Infinite => {
1282 prop_assert!(bits.contains(FloatTypes::INFINITE));
1283 seen_infinite += increment;
1284 }
1285 FpCategory::Zero => {
1286 prop_assert!(bits.contains(FloatTypes::ZERO));
1287 seen_zero += increment;
1288 }
1289 FpCategory::Subnormal => {
1290 prop_assert!(bits.contains(FloatTypes::SUBNORMAL));
1291 seen_subnormal += increment;
1292 }
1293 FpCategory::Normal => {
1294 prop_assert!(bits.contains(FloatTypes::NORMAL));
1295 seen_normal += increment;
1296 }
1297 }
1298
1299 increment = 0;
1301 if !tree.simplify() {
1302 break;
1303 }
1304 }
1305 }
1306
1307 if bits.contains(FloatTypes::POSITIVE) {
1308 prop_assert!(seen_positive > 200);
1309 }
1310 if bits.contains(FloatTypes::NEGATIVE) {
1311 prop_assert!(seen_negative > 200);
1312 }
1313 if bits.contains(FloatTypes::NORMAL) {
1314 prop_assert!(seen_normal > 100);
1315 }
1316 if bits.contains(FloatTypes::SUBNORMAL) {
1317 prop_assert!(seen_subnormal > 5);
1318 }
1319 if bits.contains(FloatTypes::ZERO) {
1320 prop_assert!(seen_zero > 5);
1321 }
1322 if bits.contains(FloatTypes::INFINITE) {
1323 prop_assert!(seen_infinite > 0);
1324 }
1325 if bits.contains(FloatTypes::QUIET_NAN) {
1326 prop_assert!(seen_quiet_nan > 0);
1327 }
1328 if bits.contains(FloatTypes::SIGNALING_NAN) {
1329 prop_assert!(seen_signaling_nan > 0);
1330 }
1331 };
1332 }
1333
1334 proptest! {
1335 #![proptest_config(crate::test_runner::Config::with_cases(1024))]
1336
1337 #[test]
1338 fn f32_any_generates_desired_values(
1339 strategy in crate::bits::u32::ANY.prop_map(f32::Any::from_bits)
1340 ) {
1341 float_generation_test_body!(strategy, f32);
1342 }
1343
1344 #[test]
1345 fn f32_any_sanity(
1346 strategy in crate::bits::u32::ANY.prop_map(f32::Any::from_bits)
1347 ) {
1348 check_strategy_sanity(strategy, Some(CheckStrategySanityOptions {
1349 strict_complicate_after_simplify: false,
1350 .. CheckStrategySanityOptions::default()
1351 }));
1352 }
1353
1354 #[test]
1355 fn f64_any_generates_desired_values(
1356 strategy in crate::bits::u32::ANY.prop_map(f64::Any::from_bits)
1357 ) {
1358 float_generation_test_body!(strategy, f64);
1359 }
1360
1361 #[test]
1362 fn f64_any_sanity(
1363 strategy in crate::bits::u32::ANY.prop_map(f64::Any::from_bits)
1364 ) {
1365 check_strategy_sanity(strategy, Some(CheckStrategySanityOptions {
1366 strict_complicate_after_simplify: false,
1367 .. CheckStrategySanityOptions::default()
1368 }));
1369 }
1370 }
1371}