1
1
use std::fmt;
2
2
use std::ops::RangeInclusive;
3
3
4
- use libm::support::MinInt;
4
+ use libm::support::{Float, MinInt} ;
5
5
6
6
use crate::domain::HasDomain;
7
- use crate::gen::KnownSize;
8
7
use crate::op::OpITy;
9
8
use crate::run_cfg::{int_range, iteration_count};
10
- use crate::{CheckCtx, GeneratorKind, MathOp, logspace};
9
+ use crate::{CheckCtx, GeneratorKind, MathOp, linear_ints, logspace};
11
10
12
11
/// Generate a sequence of inputs that either cover the domain in completeness (for smaller float
13
12
/// types and single argument functions) or provide evenly spaced inputs across the domain with
14
13
/// approximately `u32::MAX` total iterations.
15
14
pub trait ExtensiveInput<Op> {
16
- fn get_cases(ctx: &CheckCtx) -> impl ExactSizeIterator <Item = Self> + Send;
15
+ fn get_cases(ctx: &CheckCtx) -> ( impl Iterator <Item = Self> + Send, u64) ;
17
16
}
18
17
19
18
/// Construct an iterator from `logspace` and also calculate the total number of steps expected
20
19
/// for that iterator.
21
20
fn logspace_steps<Op>(
22
21
start: Op::FTy,
23
22
end: Op::FTy,
24
- ctx: &CheckCtx,
25
- argnum: usize,
23
+ max_steps: u64,
26
24
) -> (impl Iterator<Item = Op::FTy> + Clone, u64)
27
25
where
28
26
Op: MathOp,
29
27
OpITy<Op>: TryFrom<u64, Error: fmt::Debug>,
28
+ u64: TryFrom<OpITy<Op>, Error: fmt::Debug>,
30
29
RangeInclusive<OpITy<Op>>: Iterator,
31
30
{
32
- let max_steps = iteration_count(ctx, GeneratorKind::Extensive, argnum);
33
31
let max_steps = OpITy::<Op>::try_from(max_steps).unwrap_or(OpITy::<Op>::MAX);
34
- let iter = logspace(start, end, max_steps);
32
+ let (iter, steps) = logspace(start, end, max_steps);
33
+
34
+ // `steps` will be <= the original `max_steps`, which is a `u64`.
35
+ (iter, steps.try_into().unwrap())
36
+ }
37
+
38
+ /// Represents the iterator in either `Left` or `Right`.
39
+ enum EitherIter<A, B> {
40
+ A(A),
41
+ B(B),
42
+ }
35
43
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());
44
+ impl<T, A: Iterator<Item = T>, B: Iterator<Item = T>> Iterator for EitherIter<A, B> {
45
+ type Item = T;
40
46
41
- (iter, size_hint.0.try_into().unwrap())
47
+ fn next(&mut self) -> Option<Self::Item> {
48
+ match self {
49
+ Self::A(iter) => iter.next(),
50
+ Self::B(iter) => iter.next(),
51
+ }
52
+ }
53
+
54
+ fn size_hint(&self) -> (usize, Option<usize>) {
55
+ match self {
56
+ Self::A(iter) => iter.size_hint(),
57
+ Self::B(iter) => iter.size_hint(),
58
+ }
59
+ }
60
+ }
61
+
62
+ /// Gets the total number of possible values, returning `None` if that number doesn't fit in a
63
+ /// `u64`.
64
+ fn value_count<F: Float>() -> Option<u64>
65
+ where
66
+ u64: TryFrom<F::Int>,
67
+ {
68
+ u64::try_from(F::Int::MAX).ok().and_then(|max| max.checked_add(1))
69
+ }
70
+
71
+ /// Returns an iterator of every possible value of type `F`.
72
+ fn all_values<F: Float>() -> impl Iterator<Item = F>
73
+ where
74
+ RangeInclusive<F::Int>: Iterator<Item = F::Int>,
75
+ {
76
+ (F::Int::MIN..=F::Int::MAX).map(|bits| F::from_bits(bits))
42
77
}
43
78
44
79
macro_rules! impl_extensive_input {
@@ -48,91 +83,161 @@ macro_rules! impl_extensive_input {
48
83
Op: MathOp<RustArgs = Self, FTy = $fty>,
49
84
Op: HasDomain<Op::FTy>,
50
85
{
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)
86
+ fn get_cases(ctx: &CheckCtx) -> (impl Iterator<Item = Self>, u64) {
87
+ let max_steps0 = iteration_count(ctx, GeneratorKind::Extensive, 0);
88
+ // `f16` and `f32` can have exhaustive tests.
89
+ match value_count::<Op::FTy>() {
90
+ Some(steps0) if steps0 <= max_steps0 => {
91
+ let iter0 = all_values();
92
+ let iter0 = iter0.map(|v| (v,));
93
+ (EitherIter::A(iter0), steps0)
94
+ }
95
+ _ => {
96
+ let start = Op::DOMAIN.range_start();
97
+ let end = Op::DOMAIN.range_end();
98
+ let (iter0, steps0) = logspace_steps::<Op>(start, end, max_steps0);
99
+ let iter0 = iter0.map(|v| (v,));
100
+ (EitherIter::B(iter0), steps0)
101
+ }
102
+ }
57
103
}
58
104
}
59
105
60
106
impl<Op> ExtensiveInput<Op> for ($fty, $fty)
61
107
where
62
108
Op: MathOp<RustArgs = Self, FTy = $fty>,
63
109
{
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)
110
+ fn get_cases(ctx: &CheckCtx) -> (impl Iterator<Item = Self>, u64) {
111
+ let max_steps0 = iteration_count(ctx, GeneratorKind::Extensive, 0);
112
+ let max_steps1 = iteration_count(ctx, GeneratorKind::Extensive, 1);
113
+ // `f16` can have exhaustive tests.
114
+ match value_count::<Op::FTy>() {
115
+ Some(count) if count <= max_steps0 && count <= max_steps1 => {
116
+ let iter = all_values()
117
+ .flat_map(|first| all_values().map(move |second| (first, second)));
118
+ (EitherIter::A(iter), count.checked_mul(count).unwrap())
119
+ }
120
+ _ => {
121
+ let start = <$fty>::NEG_INFINITY;
122
+ let end = <$fty>::INFINITY;
123
+ let (iter0, steps0) = logspace_steps::<Op>(start, end, max_steps0);
124
+ let (iter1, steps1) = logspace_steps::<Op>(start, end, max_steps1);
125
+ let iter = iter0.flat_map(move |first| {
126
+ iter1.clone().map(move |second| (first, second))
127
+ });
128
+ let count = steps0.checked_mul(steps1).unwrap();
129
+ (EitherIter::B(iter), count)
130
+ }
131
+ }
73
132
}
74
133
}
75
134
76
135
impl<Op> ExtensiveInput<Op> for ($fty, $fty, $fty)
77
136
where
78
137
Op: MathOp<RustArgs = Self, FTy = $fty>,
79
138
{
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)
139
+ fn get_cases(ctx: &CheckCtx) -> (impl Iterator<Item = Self>, u64) {
140
+ let max_steps0 = iteration_count(ctx, GeneratorKind::Extensive, 0);
141
+ let max_steps1 = iteration_count(ctx, GeneratorKind::Extensive, 1);
142
+ let max_steps2 = iteration_count(ctx, GeneratorKind::Extensive, 2);
143
+ // `f16` can be exhaustive tested if `LIBM_EXTENSIVE_TESTS` is incresed.
144
+ match value_count::<Op::FTy>() {
145
+ Some(count)
146
+ if count <= max_steps0 && count <= max_steps1 && count <= max_steps2 =>
147
+ {
148
+ let iter = all_values().flat_map(|first| {
149
+ all_values().flat_map(move |second| {
150
+ all_values().map(move |third| (first, second, third))
151
+ })
152
+ });
153
+ (EitherIter::A(iter), count.checked_pow(3).unwrap())
154
+ }
155
+ _ => {
156
+ let start = <$fty>::NEG_INFINITY;
157
+ let end = <$fty>::INFINITY;
158
+
159
+ let (iter0, steps0) = logspace_steps::<Op>(start, end, max_steps0);
160
+ let (iter1, steps1) = logspace_steps::<Op>(start, end, max_steps1);
161
+ let (iter2, steps2) = logspace_steps::<Op>(start, end, max_steps2);
162
+
163
+ let iter = iter0
164
+ .flat_map(move |first| iter1.clone().map(move |second| (first, second)))
165
+ .flat_map(move |(first, second)| {
166
+ iter2.clone().map(move |third| (first, second, third))
167
+ });
168
+ let count =
169
+ steps0.checked_mul(steps1).unwrap().checked_mul(steps2).unwrap();
170
+
171
+ (EitherIter::B(iter), count)
172
+ }
173
+ }
96
174
}
97
175
}
98
176
99
177
impl<Op> ExtensiveInput<Op> for (i32, $fty)
100
178
where
101
179
Op: MathOp<RustArgs = Self, FTy = $fty>,
102
180
{
103
- fn get_cases(ctx: &CheckCtx) -> impl ExactSizeIterator<Item = Self> {
104
- let start = <$fty>::NEG_INFINITY;
105
- let end = <$fty>::INFINITY;
181
+ fn get_cases(ctx: &CheckCtx) -> (impl Iterator<Item = Self>, u64) {
182
+ let range0 = int_range(ctx, GeneratorKind::Extensive, 0);
183
+ let max_steps0 = iteration_count(ctx, GeneratorKind::Extensive, 0);
184
+ let max_steps1 = iteration_count(ctx, GeneratorKind::Extensive, 1);
185
+ match value_count::<Op::FTy>() {
186
+ Some(count1) if count1 <= max_steps1 => {
187
+ let (iter0, steps0) = linear_ints(range0, max_steps0);
188
+ let iter = iter0
189
+ .flat_map(move |first| all_values().map(move |second| (first, second)));
190
+ (EitherIter::A(iter), steps0.checked_mul(count1).unwrap())
191
+ }
192
+ _ => {
193
+ let start = <$fty>::NEG_INFINITY;
194
+ let end = <$fty>::INFINITY;
106
195
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);
196
+ let (iter0, steps0) = linear_ints(range0, max_steps0);
197
+ let (iter1, steps1) = logspace_steps::<Op>(start, end, max_steps1);
110
198
111
- let iter =
112
- iter0.flat_map(move |first| iter1.clone().map(move |second| (first, second)));
113
- let count = steps0.checked_mul(steps1).unwrap();
199
+ let iter = iter0.flat_map(move |first| {
200
+ iter1.clone().map(move |second| (first, second))
201
+ });
202
+ let count = steps0.checked_mul(steps1).unwrap();
114
203
115
- KnownSize::new(iter, count)
204
+ (EitherIter::B(iter), count)
205
+ }
206
+ }
116
207
}
117
208
}
118
209
119
210
impl<Op> ExtensiveInput<Op> for ($fty, i32)
120
211
where
121
212
Op: MathOp<RustArgs = Self, FTy = $fty>,
122
213
{
123
- fn get_cases(ctx: &CheckCtx) -> impl ExactSizeIterator<Item = Self> {
124
- let start = <$fty>::NEG_INFINITY;
125
- let end = <$fty>::INFINITY;
214
+ fn get_cases(ctx: &CheckCtx) -> (impl Iterator<Item = Self>, u64) {
215
+ let max_steps0 = iteration_count(ctx, GeneratorKind::Extensive, 0);
216
+ let range1 = int_range(ctx, GeneratorKind::Extensive, 1);
217
+ let max_steps1 = iteration_count(ctx, GeneratorKind::Extensive, 1);
218
+ match value_count::<Op::FTy>() {
219
+ Some(count0) if count0 <= max_steps0 => {
220
+ let (iter1, steps1) = linear_ints(range1, max_steps1);
221
+ let iter = all_values().flat_map(move |first| {
222
+ iter1.clone().map(move |second| (first, second))
223
+ });
224
+ (EitherIter::A(iter), count0.checked_mul(steps1).unwrap())
225
+ }
226
+ _ => {
227
+ let start = <$fty>::NEG_INFINITY;
228
+ let end = <$fty>::INFINITY;
126
229
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);
230
+ let (iter0, steps0) = logspace_steps::<Op>(start, end, max_steps0);
231
+ let (iter1, steps1) = linear_ints(range1, max_steps1);
130
232
131
- let iter =
132
- iter0.flat_map(move |first| iter1.clone().map(move |second| (first, second)));
133
- let count = steps0.checked_mul(steps1).unwrap();
233
+ let iter = iter0.flat_map(move |first| {
234
+ iter1.clone().map(move |second| (first, second))
235
+ });
236
+ let count = steps0.checked_mul(steps1).unwrap();
134
237
135
- KnownSize::new(iter, count)
238
+ (EitherIter::B(iter), count)
239
+ }
240
+ }
136
241
}
137
242
}
138
243
};
@@ -145,10 +250,10 @@ impl_extensive_input!(f64);
145
250
#[cfg(f128_enabled)]
146
251
impl_extensive_input!(f128);
147
252
148
- /// Create a test case iterator for extensive inputs.
253
+ /// Create a test case iterator for extensive inputs. Also returns the total test case count.
149
254
pub fn get_test_cases<Op>(
150
255
ctx: &CheckCtx,
151
- ) -> impl ExactSizeIterator <Item = Op::RustArgs> + Send + use<'_, Op>
256
+ ) -> ( impl Iterator <Item = Op::RustArgs> + Send + use<'_, Op>, u64)
152
257
where
153
258
Op: MathOp,
154
259
Op::RustArgs: ExtensiveInput<Op>,
0 commit comments