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