1- //! This tests the `Integer::isqrt` methods.
2-
31macro_rules! tests {
4- ( $( $SignedT : ident $UnsignedT : ident) ,+) => {
2+ ( $( $T : ident $isqrt_consistency_check_fn_macro : ident) ,+) => {
53 $(
6- mod $SignedT {
7- /// This takes an input and, if it's nonnegative or
8- #[ doc = concat!( "`" , stringify!( $SignedT) , "::MIN`," ) ]
9- /// checks that `isqrt` and `checked_isqrt` produce equivalent
10- /// results for that input and for the negative of that input.
11- fn isqrt_consistency_check( n: $SignedT) {
12- // `$SignedT::MIN` will be negative, so we don't want to handle `n` as if it's nonnegative.
13- if n >= 0 {
14- assert_eq!(
15- Some ( n. isqrt( ) ) ,
16- n. checked_isqrt( ) ,
17- "`{n}.checked_isqrt()` should match `Some({n}.isqrt())`." ,
18- ) ;
19- }
20-
21- // `wrapping_neg` so that `$SignedT::MIN` will negate to
22- // itself rather than panicking.
23- let negative_n = n. wrapping_neg( ) ;
24-
25- // `negative_n` should be negative, but `n` could be zero,
26- // so make sure not to check that one.
27- if negative_n < 0 {
28- assert_eq!(
29- negative_n. checked_isqrt( ) ,
30- None ,
31- "`({negative_n}).checked_isqrt()` should be `None`, as {negative_n} is negative." ,
32- ) ;
33-
34- :: std:: panic:: catch_unwind( :: core:: panic:: AssertUnwindSafe ( || ( -n) . isqrt( ) ) ) . expect_err(
35- & format!( "`({negative_n}).isqrt()` should have panicked, as {negative_n} is negative." )
36- ) ;
37- }
38- }
4+ mod $T {
5+ $isqrt_consistency_check_fn_macro!( $T) ;
396
407 // Check that the following produce the correct values from
41- // `isqrt` and that `checked_isqrt` produces the same numeric
42- // value as `isqrt`. Check also that their negative versions
43- // and `$SignedT::MIN` produce a panic from `isqrt` and `None`
44- // from `checked_isqrt`:
8+ // `isqrt`:
459 //
10+ // * `<$T>::MIN` (for signed types, no nonnegative value can
11+ // negate to `<$T>::MIN)
4612 // * the first and last 128 nonnegative values
4713 // * powers of two, minus one
4814 // * powers of two
49- #[ test]
50- fn isqrt( ) {
51- // Check the minimum value because there's no positive
52- // value that can be negated into the minimum value.
53- isqrt_consistency_check( $SignedT:: MIN ) ;
54-
55- for n in ( 0 ..=127 )
56- . chain( $SignedT:: MAX - 127 ..=$SignedT:: MAX )
57- . chain( ( 0 ..$SignedT:: BITS - 1 ) . map( |exponent| ( 1 << exponent) - 1 ) )
58- . chain( ( 0 ..$SignedT:: BITS - 1 ) . map( |exponent| 1 << exponent) )
59- {
60- isqrt_consistency_check( n) ;
61-
62- let sqrt_n = n. isqrt( ) ;
63- assert!(
64- sqrt_n * sqrt_n <= n,
65- "The integer square root of {n} should be lower than {sqrt_n} (the current return value of `{n}.isqrt()`)."
66- ) ;
67- assert!(
68- ( sqrt_n + 1 ) . checked_mul( sqrt_n + 1 ) . map( |higher_than_n| n < higher_than_n) . unwrap_or( true ) ,
69- "The integer square root of {n} should be higher than {sqrt_n} (the current return value of `{n}.isqrt()`)."
70- ) ;
71- }
72- }
73-
74- // Check the square roots of:
7515 //
76- // * the first 1,024 perfect squares
77- // * halfway between each of the first 1,024 perfect squares
78- // and the next perfect square
79- // * the next perfect square after the each of the first 1,024
80- // perfect squares, minus one
81- // * the last 1,024 perfect squares
82- // * the last 1,024 perfect squares, minus one
83- // * halfway between each of the last 1,024 perfect squares
84- // and the previous perfect square
85- #[ test]
86- // Skip this test on Miri, as it takes too long to run.
87- #[ cfg( not( miri) ) ]
88- fn isqrt_extended( ) {
89- // The correct value is worked out by using the fact that
90- // the nth nonzero perfect square is the sum of the first n
91- // odd numbers:
92- //
93- // 1 = 1
94- // 4 = 1 + 3
95- // 9 = 1 + 3 + 5
96- // 16 = 1 + 3 + 5 + 7
97- //
98- // Note also that the last odd number added in is two times
99- // the square root of the previous perfect square, plus
100- // one:
101- //
102- // 1 = 2*0 + 1
103- // 3 = 2*1 + 1
104- // 5 = 2*2 + 1
105- // 7 = 2*3 + 1
106- //
107- // That means we can add the square root of this perfect
108- // square once to get about halfway to the next perfect
109- // square, then we can add the square root of this perfect
110- // square again to get to the next perfect square minus
111- // one, then we can add one to get to the next perfect
112- // square.
113- //
114- // This allows us to, for each of the first 1,024 perfect
115- // squares, test that the square roots of the following are
116- // all correct and equal to each other:
117- //
118- // * the current perfect square
119- // * about halfway to the next perfect square
120- // * the next perfect square, minus one
121- let mut n: $SignedT = 0 ;
122- for sqrt_n in 0 ..1_024 . min( ( 1_u128 << ( ( $SignedT:: BITS - 1 ) /2 ) ) - 1 ) as $SignedT {
123- isqrt_consistency_check( n) ;
124- assert_eq!(
125- n. isqrt( ) ,
126- sqrt_n,
127- "`{sqrt_n}.pow(2).isqrt()` should be {sqrt_n}."
128- ) ;
129-
130- n += sqrt_n;
131- isqrt_consistency_check( n) ;
132- assert_eq!(
133- n. isqrt( ) ,
134- sqrt_n,
135- "{n} is about halfway between `{sqrt_n}.pow(2)` and `{}.pow(2)`, so `{n}.isqrt()` should be {sqrt_n}." ,
136- sqrt_n + 1
137- ) ;
138-
139- n += sqrt_n;
140- isqrt_consistency_check( n) ;
141- assert_eq!(
142- n. isqrt( ) ,
143- sqrt_n,
144- "`({}.pow(2) - 1).isqrt()` should be {sqrt_n}." ,
145- sqrt_n + 1
146- ) ;
147-
148- n += 1 ;
149- }
150-
151- // Similarly, for each of the last 1,024 perfect squares,
152- // check:
153- //
154- // * the current perfect square
155- // * the current perfect square, minus one
156- // * about halfway to the previous perfect square
157-
158- // `MAX`'s `isqrt` return value verified in `isqrt` test
159- // function above.
160- let maximum_sqrt = $SignedT:: MAX . isqrt( ) ;
161- let mut n = maximum_sqrt * maximum_sqrt;
162-
163- for sqrt_n in ( maximum_sqrt - 1_024 . min( ( 1_u128 << ( ( $SignedT:: BITS - 1 ) /2 ) ) - 1 ) as $SignedT..maximum_sqrt) . rev( ) {
164- isqrt_consistency_check( n) ;
165- assert_eq!(
166- n. isqrt( ) ,
167- sqrt_n + 1 ,
168- "`{0}.pow(2).isqrt()` should be {0}." ,
169- sqrt_n + 1
170- ) ;
171-
172- n -= 1 ;
173- isqrt_consistency_check( n) ;
174- assert_eq!(
175- n. isqrt( ) ,
176- sqrt_n,
177- "`({}.pow(2) - 1).isqrt()` should be {sqrt_n}." ,
178- sqrt_n + 1
179- ) ;
180-
181- n -= sqrt_n;
182- isqrt_consistency_check( n) ;
183- assert_eq!(
184- n. isqrt( ) ,
185- sqrt_n,
186- "{n} is about halfway between `{sqrt_n}.pow(2)` and `{}.pow(2)`, so `{n}.isqrt()` should be {sqrt_n}." ,
187- sqrt_n + 1
188- ) ;
189-
190- n -= sqrt_n;
191- }
192- }
193- }
194-
195- mod $UnsignedT {
196- /// This takes an input and, if it's nonzero, checks that
197- /// `isqrt` produces the same numeric value for both
198- #[ doc = concat!( "`" , stringify!( $UnsignedT) , "` and " ) ]
199- #[ doc = concat!( "`NonZero<" , stringify!( $UnsignedT) , ">`." ) ]
200- fn isqrt_consistency_check( n: $UnsignedT) {
201- if n > 0 {
202- assert_eq!(
203- n. isqrt( ) ,
204- :: core:: num:: NonZero :: <$UnsignedT>:: new( n)
205- . expect( "Cannot create a new `NonZero` value from a nonzero value" )
206- . isqrt( )
207- . get( ) ,
208- "`{n}.isqrt` should match `NonZero`'s `{n}.isqrt().get()`." ,
209- ) ;
210- }
211- }
212-
16+ // For signed types, check that `checked_isqrt` and
17+ // `isqrt` either produce the same numeric value or
18+ // respectively produce `None` and a panic.
19+ //
20+ // For unsigned types check that `isqrt` produces the same
21+ // numeric value for `$T` and `NonZero<$T>`.
22+ //
21323 // Check that the following produce the correct values from
21424 // `isqrt` and that `checked_isqrt` produces the same numeric
21525 // value as `isqrt`:
21626 //
217- // * the first and last 128 values
27+ // * the first and last 128 nonnegative values
21828 // * powers of two, minus one
21929 // * powers of two
22030 #[ test]
22131 fn isqrt( ) {
32+ // Check the minimum value because, for signed types,
33+ // there's no nonnegative value that can be negated into
34+ // the minimum value.
35+ isqrt_consistency_check( $T:: MIN ) ;
36+
22237 for n in ( 0 ..=127 )
223- . chain( $UnsignedT :: MAX - 127 ..=$UnsignedT :: MAX )
224- . chain( ( 0 ..$UnsignedT :: BITS ) . map( |exponent| ( 1 << exponent) - 1 ) )
225- . chain( ( 0 ..$UnsignedT :: BITS ) . map( |exponent| 1 << exponent) )
38+ . chain( <$T> :: MAX - 127 ..=<$T> :: MAX )
39+ . chain( ( 0 ..<$T> :: MAX . count_ones ( ) ) . map( |exponent| ( 1 << exponent) - 1 ) )
40+ . chain( ( 0 ..<$T> :: MAX . count_ones ( ) ) . map( |exponent| 1 << exponent) )
22641 {
22742 isqrt_consistency_check( n) ;
22843
229- let sqrt_n = n. isqrt( ) ;
44+ let isqrt_n = n. isqrt( ) ;
23045 assert!(
231- sqrt_n * sqrt_n <= n,
232- "The integer square root of {n} should be lower than {sqrt_n} (the current return value of `{n}.isqrt()`) ."
46+ isqrt_n . checked_mul ( isqrt_n ) . map ( |isqrt_n_squared| isqrt_n_squared <= n) . unwrap_or ( false ) ,
47+ "` {n}.isqrt()` should be lower than {isqrt_n} ."
23348 ) ;
23449 assert!(
235- ( sqrt_n + 1 ) . checked_mul( sqrt_n + 1 ) . map( |higher_than_n | n < higher_than_n ) . unwrap_or( true ) ,
236- "The integer square root of {n} should be higher than {sqrt_n} (the current return value of `{n}.isqrt()` )."
50+ ( isqrt_n + 1 ) . checked_mul( isqrt_n + 1 ) . map( |isqrt_n_plus_1_squared | n < isqrt_n_plus_1_squared ) . unwrap_or( true ) ,
51+ "` {n}.isqrt()` should be higher than {isqrt_n} )."
23752 ) ;
23853 }
23954 }
@@ -252,7 +67,7 @@ macro_rules! tests {
25267 #[ test]
25368 // Skip this test on Miri, as it takes too long to run.
25469 #[ cfg( not( miri) ) ]
255- fn test_isqrt_extended ( ) {
70+ fn isqrt_extended ( ) {
25671 // The correct value is worked out by using the fact that
25772 // the nth nonzero perfect square is the sum of the first n
25873 // odd numbers:
@@ -274,7 +89,7 @@ macro_rules! tests {
27489 // That means we can add the square root of this perfect
27590 // square once to get about halfway to the next perfect
27691 // square, then we can add the square root of this perfect
277- // square again to get to the next perfect square minus
92+ // square again to get to the next perfect square, minus
27893 // one, then we can add one to get to the next perfect
27994 // square.
28095 //
@@ -285,8 +100,8 @@ macro_rules! tests {
285100 // * the current perfect square
286101 // * about halfway to the next perfect square
287102 // * the next perfect square, minus one
288- let mut n: $UnsignedT = 0 ;
289- for sqrt_n in 0 ..1_024 . min( ( 1_u128 << ( $UnsignedT :: BITS /2 ) ) - 1 ) as $UnsignedT {
103+ let mut n: $T = 0 ;
104+ for sqrt_n in 0 ..1_024 . min( ( 1_u128 << ( <$T> :: MAX . count_ones ( ) /2 ) ) - 1 ) as $T {
290105 isqrt_consistency_check( n) ;
291106 assert_eq!(
292107 n. isqrt( ) ,
@@ -321,13 +136,13 @@ macro_rules! tests {
321136 // * the current perfect square
322137 // * the current perfect square, minus one
323138 // * about halfway to the previous perfect square
324-
325- // `MAX`'s `isqrt` return value verified in `isqrt` test
326- // function above.
327- let maximum_sqrt = $UnsignedT :: MAX . isqrt( ) ;
139+ //
140+ // `MAX`'s `isqrt` return value is verified in the `isqrt`
141+ // test function above.
142+ let maximum_sqrt = <$T> :: MAX . isqrt( ) ;
328143 let mut n = maximum_sqrt * maximum_sqrt;
329144
330- for sqrt_n in ( maximum_sqrt - 1_024 . min( ( 1_u128 << ( $UnsignedT :: BITS /2 ) ) - 1 ) as $UnsignedT ..maximum_sqrt) . rev( ) {
145+ for sqrt_n in ( maximum_sqrt - 1_024 . min( ( 1_u128 << ( <$T> :: MAX . count_ones ( ) /2 ) ) - 1 ) as $T ..maximum_sqrt) . rev( ) {
331146 isqrt_consistency_check( n) ;
332147 assert_eq!(
333148 n. isqrt( ) ,
@@ -362,4 +177,66 @@ macro_rules! tests {
362177 } ;
363178}
364179
365- tests ! ( i8 u8 , i16 u16 , i32 u32 , i64 u64 , i128 u128 , isize usize ) ;
180+ macro_rules! signed_check {
181+ ( $T: ident) => {
182+ /// This takes an input and, if it's nonnegative or
183+ #[ doc = concat!( "`" , stringify!( $T) , "::MIN`," ) ]
184+ /// checks that `isqrt` and `checked_isqrt` produce equivalent
185+ /// results for that input and for the negative of that input.
186+ fn isqrt_consistency_check( n: $T) {
187+ // `<$T>::MIN` will be negative, so ignore it in this nonnegative
188+ // section.
189+ if n >= 0 {
190+ assert_eq!(
191+ Some ( n. isqrt( ) ) ,
192+ n. checked_isqrt( ) ,
193+ "`{n}.checked_isqrt()` should match `Some({n}.isqrt())`." ,
194+ ) ;
195+ }
196+
197+ // `wrapping_neg` so that `$SignedT::MIN` will negate to
198+ // itself rather than panicking.
199+ let negative_n = n. wrapping_neg( ) ;
200+
201+ // Zero negated will still be nonnegative, so ignore it in this
202+ // negative section.
203+ if negative_n < 0 {
204+ assert_eq!(
205+ negative_n. checked_isqrt( ) ,
206+ None ,
207+ "`({negative_n}).checked_isqrt()` should be `None`, as {negative_n} is negative." ,
208+ ) ;
209+
210+ :: std:: panic:: catch_unwind( :: core:: panic:: AssertUnwindSafe ( || ( -n) . isqrt( ) ) ) . expect_err(
211+ & format!( "`({negative_n}).isqrt()` should have panicked, as {negative_n} is negative." )
212+ ) ;
213+ }
214+ }
215+ } ;
216+ }
217+
218+ macro_rules! unsigned_check {
219+ ( $T: ident) => {
220+ /// This takes an input and, if it's nonzero, checks that
221+ /// `isqrt` produces the same numeric value for both
222+ #[ doc = concat!( "`" , stringify!( $T) , "` and " ) ]
223+ #[ doc = concat!( "`NonZero<" , stringify!( $T) , ">`." ) ]
224+ fn isqrt_consistency_check( n: $T) {
225+ // Zero cannot be turned into a `NonZero` value, so ignore it in
226+ // this nonzero section.
227+ if n > 0 {
228+ assert_eq!(
229+ n. isqrt( ) ,
230+ :: core:: num:: NonZero :: <$T>:: new( n)
231+ . expect( "Was not able to create a new `NonZero` value from a nonzero value" )
232+ . isqrt( )
233+ . get( ) ,
234+ "`{n}.isqrt` should match `NonZero`'s `{n}.isqrt().get()`." ,
235+ ) ;
236+ }
237+ }
238+ } ;
239+ }
240+
241+ tests ! ( i8 signed_check, i16 signed_check, i32 signed_check, i64 signed_check, i128 signed_check) ;
242+ tests ! ( u8 unsigned_check, u16 unsigned_check, u32 unsigned_check, u64 unsigned_check, u128 unsigned_check) ;
0 commit comments