11use std:: fmt;
22use std:: ops:: RangeInclusive ;
33
4- use libm:: support:: MinInt ;
4+ use libm:: support:: { Float , MinInt } ;
55
66use crate :: domain:: HasDomain ;
7- use crate :: gen:: KnownSize ;
87use crate :: op:: OpITy ;
98use crate :: run_cfg:: { int_range, iteration_count} ;
10- use crate :: { CheckCtx , GeneratorKind , MathOp , logspace} ;
9+ use crate :: { CheckCtx , GeneratorKind , MathOp , linear_ints , logspace} ;
1110
1211/// Generate a sequence of inputs that either cover the domain in completeness (for smaller float
1312/// types and single argument functions) or provide evenly spaced inputs across the domain with
1413/// approximately `u32::MAX` total iterations.
1514pub 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 ) ;
1716}
1817
1918/// Construct an iterator from `logspace` and also calculate the total number of steps expected
2019/// for that iterator.
2120fn logspace_steps < Op > (
2221 start : Op :: FTy ,
2322 end : Op :: FTy ,
24- ctx : & CheckCtx ,
25- argnum : usize ,
23+ max_steps : u64 ,
2624) -> ( impl Iterator < Item = Op :: FTy > + Clone , u64 )
2725where
2826 Op : MathOp ,
2927 OpITy < Op > : TryFrom < u64 , Error : fmt:: Debug > ,
28+ u64 : TryFrom < OpITy < Op > , Error : fmt:: Debug > ,
3029 RangeInclusive < OpITy < Op > > : Iterator ,
3130{
32- let max_steps = iteration_count ( ctx, GeneratorKind :: Extensive , argnum) ;
3331 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+ }
3543
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 ;
4046
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) )
4277}
4378
4479macro_rules! impl_extensive_input {
@@ -48,91 +83,161 @@ macro_rules! impl_extensive_input {
4883 Op : MathOp <RustArgs = Self , FTy = $fty>,
4984 Op : HasDomain <Op :: FTy >,
5085 {
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+ }
57103 }
58104 }
59105
60106 impl <Op > ExtensiveInput <Op > for ( $fty, $fty)
61107 where
62108 Op : MathOp <RustArgs = Self , FTy = $fty>,
63109 {
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+ }
73132 }
74133 }
75134
76135 impl <Op > ExtensiveInput <Op > for ( $fty, $fty, $fty)
77136 where
78137 Op : MathOp <RustArgs = Self , FTy = $fty>,
79138 {
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+ }
96174 }
97175 }
98176
99177 impl <Op > ExtensiveInput <Op > for ( i32 , $fty)
100178 where
101179 Op : MathOp <RustArgs = Self , FTy = $fty>,
102180 {
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 ;
106195
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) ;
110198
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( ) ;
114203
115- KnownSize :: new( iter, count)
204+ ( EitherIter :: B ( iter) , count)
205+ }
206+ }
116207 }
117208 }
118209
119210 impl <Op > ExtensiveInput <Op > for ( $fty, i32 )
120211 where
121212 Op : MathOp <RustArgs = Self , FTy = $fty>,
122213 {
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 ;
126229
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) ;
130232
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( ) ;
134237
135- KnownSize :: new( iter, count)
238+ ( EitherIter :: B ( iter) , count)
239+ }
240+ }
136241 }
137242 }
138243 } ;
@@ -145,10 +250,10 @@ impl_extensive_input!(f64);
145250#[ cfg( f128_enabled) ]
146251impl_extensive_input ! ( f128) ;
147252
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.
149254pub fn get_test_cases < Op > (
150255 ctx : & CheckCtx ,
151- ) -> impl ExactSizeIterator < Item = Op :: RustArgs > + Send + use < ' _ , Op >
256+ ) -> ( impl Iterator < Item = Op :: RustArgs > + Send + use < ' _ , Op > , u64 )
152257where
153258 Op : MathOp ,
154259 Op :: RustArgs : ExtensiveInput < Op > ,
0 commit comments