1
- //! This tests the `Integer::isqrt` methods.
2
-
3
1
macro_rules! tests {
4
- ( $( $SignedT : ident $UnsignedT : ident) ,+) => {
2
+ ( $( $T : ident $isqrt_consistency_check_fn_macro : ident) ,+) => {
5
3
$(
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) ;
39
6
40
7
// 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`:
45
9
//
10
+ // * `<$T>::MIN` (for signed types, no nonnegative value can
11
+ // negate to `<$T>::MIN)
46
12
// * the first and last 128 nonnegative values
47
13
// * powers of two, minus one
48
14
// * 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:
75
15
//
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
+ //
213
23
// Check that the following produce the correct values from
214
24
// `isqrt` and that `checked_isqrt` produces the same numeric
215
25
// value as `isqrt`:
216
26
//
217
- // * the first and last 128 values
27
+ // * the first and last 128 nonnegative values
218
28
// * powers of two, minus one
219
29
// * powers of two
220
30
#[ test]
221
31
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
+
222
37
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) )
226
41
{
227
42
isqrt_consistency_check( n) ;
228
43
229
- let sqrt_n = n. isqrt( ) ;
44
+ let isqrt_n = n. isqrt( ) ;
230
45
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} ."
233
48
) ;
234
49
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} )."
237
52
) ;
238
53
}
239
54
}
@@ -252,7 +67,7 @@ macro_rules! tests {
252
67
#[ test]
253
68
// Skip this test on Miri, as it takes too long to run.
254
69
#[ cfg( not( miri) ) ]
255
- fn test_isqrt_extended ( ) {
70
+ fn isqrt_extended ( ) {
256
71
// The correct value is worked out by using the fact that
257
72
// the nth nonzero perfect square is the sum of the first n
258
73
// odd numbers:
@@ -274,7 +89,7 @@ macro_rules! tests {
274
89
// That means we can add the square root of this perfect
275
90
// square once to get about halfway to the next perfect
276
91
// 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
278
93
// one, then we can add one to get to the next perfect
279
94
// square.
280
95
//
@@ -285,8 +100,8 @@ macro_rules! tests {
285
100
// * the current perfect square
286
101
// * about halfway to the next perfect square
287
102
// * 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 {
290
105
isqrt_consistency_check( n) ;
291
106
assert_eq!(
292
107
n. isqrt( ) ,
@@ -321,13 +136,13 @@ macro_rules! tests {
321
136
// * the current perfect square
322
137
// * the current perfect square, minus one
323
138
// * 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( ) ;
328
143
let mut n = maximum_sqrt * maximum_sqrt;
329
144
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( ) {
331
146
isqrt_consistency_check( n) ;
332
147
assert_eq!(
333
148
n. isqrt( ) ,
@@ -362,4 +177,66 @@ macro_rules! tests {
362
177
} ;
363
178
}
364
179
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