1#[cfg(feature = "num-bigint")]
4use crate::tables::{CUBIC_MODULI, CUBIC_RESIDUAL, QUAD_MODULI, QUAD_RESIDUAL};
5use crate::traits::{BitTest, ExactRoots};
6
7use num_integer::Roots;
8
9#[cfg(feature = "num-bigint")]
10use num_bigint::{BigInt, BigUint, ToBigInt};
11#[cfg(feature = "num-bigint")]
12use num_traits::{One, Signed, ToPrimitive, Zero};
13
14macro_rules! impl_bittest_prim {
15 ($($T:ty)*) => {$(
16 impl BitTest for $T {
17 #[inline]
18 fn bits(&self) -> usize {
19 (<$T>::BITS - self.leading_zeros()) as usize
20 }
21 #[inline]
22 fn bit(&self, position: usize) -> bool {
23 self & (1 << position) > 0
24 }
25 #[inline]
26 fn trailing_zeros(&self) -> usize {
27 <$T>::trailing_zeros(*self) as usize
28 }
29 }
30 )*}
31}
32impl_bittest_prim!(u8 u16 u32 u64 u128 usize);
33
34#[cfg(feature = "num-bigint")]
35impl BitTest for BigUint {
36 fn bit(&self, position: usize) -> bool {
37 self.bit(position as u64)
38 }
39 fn bits(&self) -> usize {
40 BigUint::bits(self) as usize
41 }
42 #[inline]
43 fn trailing_zeros(&self) -> usize {
44 match BigUint::trailing_zeros(self) {
45 Some(a) => a as usize,
46 None => 0,
47 }
48 }
49}
50
51macro_rules! impl_exactroot_prim {
52 ($($T:ty)*) => {$(
53 impl ExactRoots for $T {
54 fn nth_root_exact(&self, n: u32) -> Option<Self> {
55 if self < &0 && n % 2 == 0 {
57 return None;
58 }
59 let r = self.nth_root(n);
60 if &r.clone().pow(n) == self {
61 Some(r)
62 } else {
63 None
64 }
65 }
66 fn sqrt_exact(&self) -> Option<Self> {
67 if self < &0 { return None; }
68 let shift = self.trailing_zeros();
69
70 if shift & 1 == 1 { return None; }
72 if (self >> shift) & 7 != 1 { return None; }
73 self.nth_root_exact(2)
74 }
75 }
76 )*};
77 }
80impl_exactroot_prim!(u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize);
81
82#[cfg(feature = "num-bigint")]
83impl ExactRoots for BigUint {
84 fn sqrt_exact(&self) -> Option<Self> {
85 if self.is_zero() {
87 return Some(BigUint::zero());
88 }
89 if let Some(v) = self.to_u64() {
90 return v.sqrt_exact().map(BigUint::from);
91 }
92
93 let shift = self.trailing_zeros().unwrap();
95 if shift & 1 == 1 {
96 return None;
97 }
98 if !((self >> shift) & BigUint::from(7u8)).is_one() {
99 return None;
100 }
101
102 #[cfg(not(feature = "big-table"))]
104 for (m, res) in QUAD_MODULI.iter().zip(QUAD_RESIDUAL) {
105 if (res >> ((self % m).to_u8().unwrap() & 63)) & 1 == 0 {
107 return None;
108 }
109 }
110 #[cfg(feature = "big-table")]
111 for (m, res) in QUAD_MODULI.iter().zip(QUAD_RESIDUAL) {
112 let rem = (self % m).to_u16().unwrap();
113 if (res[(rem / 64) as usize] >> (rem % 64)) & 1 == 0 {
114 return None;
115 }
116 }
117
118 self.nth_root_exact(2)
119 }
120
121 fn cbrt_exact(&self) -> Option<Self> {
122 if self.is_zero() {
124 return Some(BigUint::zero());
125 }
126 if let Some(v) = self.to_u64() {
127 return v.cbrt_exact().map(BigUint::from);
128 }
129
130 let shift = self.trailing_zeros().unwrap();
132 if shift % 3 != 0 {
133 return None;
134 }
135
136 #[cfg(not(feature = "big-table"))]
138 for (m, res) in CUBIC_MODULI.iter().zip(CUBIC_RESIDUAL) {
139 if (res >> (self % m).to_u8().unwrap()) & 1 == 0 {
140 return None;
141 }
142 }
143 #[cfg(feature = "big-table")]
144 for (m, res) in CUBIC_MODULI.iter().zip(CUBIC_RESIDUAL) {
145 let rem = (self % m).to_u16().unwrap();
146 if (res[(rem / 64) as usize] >> (rem % 64)) & 1 == 0 {
147 return None;
148 }
149 }
150
151 self.nth_root_exact(3)
152 }
153}
154
155#[cfg(feature = "num-bigint")]
156impl ExactRoots for BigInt {
157 fn nth_root_exact(&self, n: u32) -> Option<Self> {
158 if self.is_negative() && n % 2 == 0 {
160 return None;
161 }
162
163 if self.is_negative() {
166 self.magnitude()
167 .nth_root_exact(n)
168 .and_then(|u| u.to_bigint())
169 .map(|v| -v)
170 } else {
171 self.magnitude()
172 .nth_root_exact(n)
173 .and_then(|u| u.to_bigint())
174 }
175 }
176 fn sqrt_exact(&self) -> Option<Self> {
177 self.to_biguint()
178 .and_then(|u| u.sqrt_exact())
179 .and_then(|u| u.to_bigint())
180 }
181 fn cbrt_exact(&self) -> Option<Self> {
182 self.magnitude()
183 .cbrt_exact()
184 .and_then(|u| u.to_bigint())
185 .map(|v| if self.is_negative() { -v } else { v })
186 }
187}
188
189#[cfg(test)]
190mod tests {
191 use super::*;
192
193 #[test]
194 fn exact_root_test() {
195 assert!(ExactRoots::sqrt_exact(&3u8).is_none());
197 assert!(matches!(ExactRoots::sqrt_exact(&4u8), Some(2)));
198 assert!(matches!(ExactRoots::sqrt_exact(&9u8), Some(3)));
199 assert!(ExactRoots::sqrt_exact(&18u8).is_none());
200 assert!(ExactRoots::sqrt_exact(&3i8).is_none());
201 assert!(matches!(ExactRoots::sqrt_exact(&4i8), Some(2)));
202 assert!(matches!(ExactRoots::sqrt_exact(&9i8), Some(3)));
203 assert!(ExactRoots::sqrt_exact(&18i8).is_none());
204
205 for _ in 0..100 {
207 let x = rand::random::<u32>();
208 assert_eq!(
209 ExactRoots::sqrt_exact(&x),
210 ExactRoots::nth_root_exact(&x, 2)
211 );
212 assert_eq!(
213 ExactRoots::cbrt_exact(&x),
214 ExactRoots::nth_root_exact(&x, 3)
215 );
216 let x = rand::random::<i32>();
217 assert_eq!(
218 ExactRoots::cbrt_exact(&x),
219 ExactRoots::nth_root_exact(&x, 3)
220 );
221 }
222 for _ in 0..100 {
224 let x = u64::from(rand::random::<u32>());
225 assert!(matches!(ExactRoots::sqrt_exact(&(x * x)), Some(v) if v == x));
226 let x = i64::from(rand::random::<i16>());
227 assert!(matches!(ExactRoots::cbrt_exact(&(x * x * x)), Some(v) if v == x));
228 }
229 for _ in 0..100 {
231 let x = u64::from(rand::random::<u32>());
232 let y = u64::from(rand::random::<u32>());
233 if x == y {
234 continue;
235 }
236 assert!(ExactRoots::sqrt_exact(&(x * y)).is_none());
237 }
238
239 #[cfg(feature = "num-bigint")]
240 {
241 use num_bigint::RandBigInt;
242 let mut rng = rand::thread_rng();
243 for _ in 0..10 {
245 let x = rng.gen_biguint(150);
246 assert_eq!(
247 ExactRoots::sqrt_exact(&x),
248 ExactRoots::nth_root_exact(&x, 2)
249 );
250 assert_eq!(
251 ExactRoots::cbrt_exact(&x),
252 ExactRoots::nth_root_exact(&x, 3)
253 );
254 let x = rng.gen_bigint(150);
255 assert_eq!(
256 ExactRoots::cbrt_exact(&x),
257 ExactRoots::nth_root_exact(&x, 3)
258 );
259 }
260 for _ in 0..10 {
262 let x = rng.gen_biguint(150);
263 assert!(matches!(ExactRoots::sqrt_exact(&(&x * &x)), Some(v) if v == x));
264 let x = rng.gen_biguint(150);
265 assert!(
266 matches!(ExactRoots::cbrt_exact(&(&x * &x * &x)), Some(v) if v == x),
267 "failed at {}",
268 x
269 );
270 }
271 for _ in 0..10 {
273 let x = rng.gen_biguint(150);
274 let y = rng.gen_biguint(150);
275 if x == y {
276 continue;
277 }
278 assert!(ExactRoots::sqrt_exact(&(x * y)).is_none());
279 }
280 }
281 }
282
283 #[test]
284 fn test_nth_root_exact_negative_even_root() {
285 let result = (-1i32).nth_root_exact(2);
288 assert!(
289 result.is_none(),
290 "nth_root_exact(2) should return None for negative numbers"
291 );
292
293 let result = (-4i32).nth_root_exact(2);
295 assert!(
296 result.is_none(),
297 "nth_root_exact(2) should return None for negative numbers"
298 );
299
300 let result = (-8i32).nth_root_exact(4);
301 assert!(
302 result.is_none(),
303 "nth_root_exact(4) should return None for negative numbers"
304 );
305
306 let result = (-8i32).nth_root_exact(3);
308 assert_eq!(
309 result,
310 Some(-2),
311 "nth_root_exact(3) should work for negative numbers"
312 );
313
314 let result = (-27i32).nth_root_exact(3);
315 assert_eq!(
316 result,
317 Some(-3),
318 "nth_root_exact(3) should work for negative numbers"
319 );
320 }
321
322 #[test]
323 fn test_nth_root_exact_all_signed_types() {
324 assert_eq!((-1i8).nth_root_exact(2), None);
326 assert_eq!((-1i16).nth_root_exact(2), None);
327 assert_eq!((-1i32).nth_root_exact(2), None);
328 assert_eq!((-1i64).nth_root_exact(2), None);
329 assert_eq!((-1i128).nth_root_exact(2), None);
330 assert_eq!((-1isize).nth_root_exact(2), None);
331
332 assert_eq!((-8i32).nth_root_exact(3), Some(-2));
334 assert_eq!((-32i32).nth_root_exact(5), Some(-2));
335
336 assert_eq!(16i32.nth_root_exact(4), Some(2));
338 assert_eq!(32i32.nth_root_exact(5), Some(2));
339 }
340
341 #[test]
342 #[cfg(feature = "num-bigint")]
343 fn test_nth_root_exact_bigint_negative() {
344 use num_bigint::BigInt;
345
346 assert_eq!(BigInt::from(-1).nth_root_exact(2), None);
348 assert_eq!(BigInt::from(-16).nth_root_exact(4), None);
349
350 assert_eq!(BigInt::from(-8).nth_root_exact(3), Some(BigInt::from(-2)));
352 assert_eq!(
353 BigInt::from(-1000000000i64).nth_root_exact(3),
354 Some(BigInt::from(-1000i32))
355 );
356 }
357}