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