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