|
| 1 | +use std::fmt; |
| 2 | +use std::ops::RangeInclusive; |
| 3 | + |
| 4 | +use libm::support::MinInt; |
| 5 | + |
| 6 | +use crate::domain::HasDomain; |
| 7 | +use crate::gen::KnownSize; |
| 8 | +use crate::op::OpITy; |
| 9 | +use crate::run_cfg::{int_range, iteration_count}; |
| 10 | +use crate::{CheckCtx, GeneratorKind, MathOp, logspace}; |
| 11 | + |
| 12 | +/// Generate a sequence of inputs that either cover the domain in completeness (for smaller float |
| 13 | +/// types and single argument functions) or provide evenly spaced inputs across the domain with |
| 14 | +/// approximately `u32::MAX` total iterations. |
| 15 | +pub trait ExtensiveInput<Op> { |
| 16 | + fn get_cases(ctx: &CheckCtx) -> impl ExactSizeIterator<Item = Self> + Send; |
| 17 | +} |
| 18 | + |
| 19 | +/// Construct an iterator from `logspace` and also calculate the total number of steps expected |
| 20 | +/// for that iterator. |
| 21 | +fn logspace_steps<Op>( |
| 22 | + start: Op::FTy, |
| 23 | + end: Op::FTy, |
| 24 | + ctx: &CheckCtx, |
| 25 | + argnum: usize, |
| 26 | +) -> (impl Iterator<Item = Op::FTy> + Clone, u64) |
| 27 | +where |
| 28 | + Op: MathOp, |
| 29 | + OpITy<Op>: TryFrom<u64, Error: fmt::Debug>, |
| 30 | + RangeInclusive<OpITy<Op>>: Iterator, |
| 31 | +{ |
| 32 | + let max_steps = iteration_count(ctx, GeneratorKind::Extensive, argnum); |
| 33 | + let max_steps = OpITy::<Op>::try_from(max_steps).unwrap_or(OpITy::<Op>::MAX); |
| 34 | + let iter = logspace(start, end, max_steps); |
| 35 | + |
| 36 | + // `logspace` can't implement `ExactSizeIterator` because of the range, but its size hint |
| 37 | + // should be accurate (assuming <= usize::MAX iterations). |
| 38 | + let size_hint = iter.size_hint(); |
| 39 | + assert_eq!(size_hint.0, size_hint.1.unwrap()); |
| 40 | + |
| 41 | + (iter, size_hint.0.try_into().unwrap()) |
| 42 | +} |
| 43 | + |
| 44 | +macro_rules! impl_extensive_input { |
| 45 | + ($fty:ty) => { |
| 46 | + impl<Op> ExtensiveInput<Op> for ($fty,) |
| 47 | + where |
| 48 | + Op: MathOp<RustArgs = Self, FTy = $fty>, |
| 49 | + Op: HasDomain<Op::FTy>, |
| 50 | + { |
| 51 | + fn get_cases(ctx: &CheckCtx) -> impl ExactSizeIterator<Item = Self> { |
| 52 | + let start = Op::DOMAIN.range_start(); |
| 53 | + let end = Op::DOMAIN.range_end(); |
| 54 | + let (iter0, steps0) = logspace_steps::<Op>(start, end, ctx, 0); |
| 55 | + let iter0 = iter0.map(|v| (v,)); |
| 56 | + KnownSize::new(iter0, steps0) |
| 57 | + } |
| 58 | + } |
| 59 | + |
| 60 | + impl<Op> ExtensiveInput<Op> for ($fty, $fty) |
| 61 | + where |
| 62 | + Op: MathOp<RustArgs = Self, FTy = $fty>, |
| 63 | + { |
| 64 | + fn get_cases(ctx: &CheckCtx) -> impl ExactSizeIterator<Item = Self> { |
| 65 | + let start = <$fty>::NEG_INFINITY; |
| 66 | + let end = <$fty>::INFINITY; |
| 67 | + let (iter0, steps0) = logspace_steps::<Op>(start, end, ctx, 0); |
| 68 | + let (iter1, steps1) = logspace_steps::<Op>(start, end, ctx, 1); |
| 69 | + let iter = |
| 70 | + iter0.flat_map(move |first| iter1.clone().map(move |second| (first, second))); |
| 71 | + let count = steps0.checked_mul(steps1).unwrap(); |
| 72 | + KnownSize::new(iter, count) |
| 73 | + } |
| 74 | + } |
| 75 | + |
| 76 | + impl<Op> ExtensiveInput<Op> for ($fty, $fty, $fty) |
| 77 | + where |
| 78 | + Op: MathOp<RustArgs = Self, FTy = $fty>, |
| 79 | + { |
| 80 | + fn get_cases(ctx: &CheckCtx) -> impl ExactSizeIterator<Item = Self> { |
| 81 | + let start = <$fty>::NEG_INFINITY; |
| 82 | + let end = <$fty>::INFINITY; |
| 83 | + |
| 84 | + let (iter0, steps0) = logspace_steps::<Op>(start, end, ctx, 0); |
| 85 | + let (iter1, steps1) = logspace_steps::<Op>(start, end, ctx, 1); |
| 86 | + let (iter2, steps2) = logspace_steps::<Op>(start, end, ctx, 2); |
| 87 | + |
| 88 | + let iter = iter0 |
| 89 | + .flat_map(move |first| iter1.clone().map(move |second| (first, second))) |
| 90 | + .flat_map(move |(first, second)| { |
| 91 | + iter2.clone().map(move |third| (first, second, third)) |
| 92 | + }); |
| 93 | + let count = steps0.checked_mul(steps1).unwrap().checked_mul(steps2).unwrap(); |
| 94 | + |
| 95 | + KnownSize::new(iter, count) |
| 96 | + } |
| 97 | + } |
| 98 | + |
| 99 | + impl<Op> ExtensiveInput<Op> for (i32, $fty) |
| 100 | + where |
| 101 | + Op: MathOp<RustArgs = Self, FTy = $fty>, |
| 102 | + { |
| 103 | + fn get_cases(ctx: &CheckCtx) -> impl ExactSizeIterator<Item = Self> { |
| 104 | + let start = <$fty>::NEG_INFINITY; |
| 105 | + let end = <$fty>::INFINITY; |
| 106 | + |
| 107 | + let iter0 = int_range(ctx, GeneratorKind::Extensive, 0); |
| 108 | + let steps0 = iteration_count(ctx, GeneratorKind::Extensive, 0); |
| 109 | + let (iter1, steps1) = logspace_steps::<Op>(start, end, ctx, 1); |
| 110 | + |
| 111 | + let iter = |
| 112 | + iter0.flat_map(move |first| iter1.clone().map(move |second| (first, second))); |
| 113 | + let count = steps0.checked_mul(steps1).unwrap(); |
| 114 | + |
| 115 | + KnownSize::new(iter, count) |
| 116 | + } |
| 117 | + } |
| 118 | + |
| 119 | + impl<Op> ExtensiveInput<Op> for ($fty, i32) |
| 120 | + where |
| 121 | + Op: MathOp<RustArgs = Self, FTy = $fty>, |
| 122 | + { |
| 123 | + fn get_cases(ctx: &CheckCtx) -> impl ExactSizeIterator<Item = Self> { |
| 124 | + let start = <$fty>::NEG_INFINITY; |
| 125 | + let end = <$fty>::INFINITY; |
| 126 | + |
| 127 | + let (iter0, steps0) = logspace_steps::<Op>(start, end, ctx, 0); |
| 128 | + let iter1 = int_range(ctx, GeneratorKind::Extensive, 0); |
| 129 | + let steps1 = iteration_count(ctx, GeneratorKind::Extensive, 0); |
| 130 | + |
| 131 | + let iter = |
| 132 | + iter0.flat_map(move |first| iter1.clone().map(move |second| (first, second))); |
| 133 | + let count = steps0.checked_mul(steps1).unwrap(); |
| 134 | + |
| 135 | + KnownSize::new(iter, count) |
| 136 | + } |
| 137 | + } |
| 138 | + }; |
| 139 | +} |
| 140 | + |
| 141 | +impl_extensive_input!(f32); |
| 142 | +impl_extensive_input!(f64); |
| 143 | + |
| 144 | +/// Create a test case iterator for extensive inputs. |
| 145 | +pub fn get_test_cases<Op>( |
| 146 | + ctx: &CheckCtx, |
| 147 | +) -> impl ExactSizeIterator<Item = Op::RustArgs> + Send + use<'_, Op> |
| 148 | +where |
| 149 | + Op: MathOp, |
| 150 | + Op::RustArgs: ExtensiveInput<Op>, |
| 151 | +{ |
| 152 | + Op::RustArgs::get_cases(ctx) |
| 153 | +} |
0 commit comments