Skip to content

Commit 99e0287

Browse files
committed
Fix nth_root_exact panic on negative even roots
Reproduces the panic that occurs when calling nth_root_exact with even roots on negative numbers, which should return None instead of panicking. closes: #25
1 parent 75e7ea8 commit 99e0287

1 file changed

Lines changed: 108 additions & 0 deletions

File tree

src/integer.rs

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
use crate::tables::{CUBIC_MODULI, CUBIC_RESIDUAL, QUAD_MODULI, QUAD_RESIDUAL};
55
use crate::traits::{BitTest, ExactRoots};
66

7+
use num_integer::Roots;
8+
79
#[cfg(feature = "num-bigint")]
810
use num_bigint::{BigInt, BigUint, ToBigInt};
911
#[cfg(feature = "num-bigint")]
@@ -49,6 +51,18 @@ impl BitTest for BigUint {
4951
macro_rules! impl_exactroot_prim {
5052
($($T:ty)*) => {$(
5153
impl ExactRoots for $T {
54+
fn nth_root_exact(&self, n: u32) -> Option<Self> {
55+
// For even roots of negative numbers, return None instead of panicking
56+
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+
}
5266
fn sqrt_exact(&self) -> Option<Self> {
5367
if self < &0 { return None; }
5468
let shift = self.trailing_zeros();
@@ -140,6 +154,25 @@ impl ExactRoots for BigUint {
140154

141155
#[cfg(feature = "num-bigint")]
142156
impl ExactRoots for BigInt {
157+
fn nth_root_exact(&self, n: u32) -> Option<Self> {
158+
// For even roots of negative numbers, return None instead of panicking
159+
if self.is_negative() && n % 2 == 0 {
160+
return None;
161+
}
162+
163+
// For odd roots, handle negative numbers by taking the root of the magnitude
164+
// and then applying the sign
165+
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+
}
143176
fn sqrt_exact(&self) -> Option<Self> {
144177
self.to_biguint()
145178
.and_then(|u| u.sqrt_exact())
@@ -246,4 +279,79 @@ mod tests {
246279
}
247280
}
248281
}
282+
283+
#[test]
284+
fn test_nth_root_exact_negative_even_root() {
285+
// Test for issue #25: nth_root_exact(2) should return None for negative numbers
286+
// instead of panicking
287+
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+
// Test other negative numbers with even roots
294+
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+
// Test that odd roots of negative numbers still work
307+
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+
// Test all signed integer types with even roots of negative numbers
325+
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+
// Test odd roots work correctly
333+
assert_eq!((-8i32).nth_root_exact(3), Some(-2));
334+
assert_eq!((-32i32).nth_root_exact(5), Some(-2));
335+
336+
// Test positive cases still work
337+
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+
// Test even roots return None for negative BigInt
347+
assert_eq!(BigInt::from(-1).nth_root_exact(2), None);
348+
assert_eq!(BigInt::from(-16).nth_root_exact(4), None);
349+
350+
// Test odd roots work for negative BigInt
351+
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+
}
249357
}

0 commit comments

Comments
 (0)