1pub fn maybe_iroot(value: isize, exp: usize) -> Option<isize> {
14 if exp == 1 {
15 return Some(value);
16 }
17 if exp == 0 {
18 return Some(1);
19 }
20 let (value, coeff) = if value >= 0 {
21 (value, 1)
22 } else if exp % 2 == 1 {
23 (-value, -1)
24 } else {
25 return None;
27 };
28 pos_maybe_iroot(value as usize, exp).map(|v| coeff * (v as isize))
29}
30
31#[inline(always)]
33fn pos_maybe_iroot(value: usize, exp: usize) -> Option<usize> {
34 if exp == 1 {
35 return Some(value);
36 }
37 if exp == 0 {
38 return Some(1);
39 }
40 match value {
41 0 => Some(0),
42 1 => Some(1),
43 _ => {
44 let exp = exp as u32;
45
46 let mut lower = 1usize;
48
49 let mut upper = value.isqrt();
50 if exp == 2 && value == upper.pow(2) {
51 return Some(upper);
53 }
54 upper += 1;
55
56 loop {
57 if lower + 1 >= upper {
58 return None;
60 }
61 let candidate = (lower + upper) / 2;
62
63 match candidate.checked_pow(exp) {
64 None => {
65 upper = candidate - 1;
67 }
68 Some(pow) => {
69 if pow == value {
70 return Some(candidate);
72 } else if pow > value {
73 upper = candidate;
75 } else {
76 lower = candidate;
78 }
79 }
80 }
81 }
82 }
83 }
84}
85
86#[cfg(test)]
87mod tests {
88 use super::*;
89
90 #[test]
91 fn test_maybe_iroot() {
92 assert_eq!(maybe_iroot(0, 0), Some(1));
93 assert_eq!(maybe_iroot(0, 2), Some(0));
94
95 assert_eq!(maybe_iroot(4 * 4 * 4, 3), Some(4));
96
97 assert_eq!(maybe_iroot(1, 2), Some(1));
98 assert_eq!(maybe_iroot(4, 2), Some(2));
99 assert_eq!(maybe_iroot(8, 3), Some(2));
100 assert_eq!(maybe_iroot(27, 3), Some(3));
101
102 assert_eq!(maybe_iroot(5, 1), Some(5));
104 assert_eq!(maybe_iroot(-7, 1), Some(-7));
105
106 assert_eq!(maybe_iroot(-8, 3), Some(-2));
107 assert_eq!(maybe_iroot(-16, 4), None);
108 assert_eq!(maybe_iroot(16, 4), Some(2));
109 assert_eq!(maybe_iroot(-1, 3), Some(-1));
110 assert_eq!(maybe_iroot(-1, 2), None);
111
112 let too_big = isize::MAX.isqrt() + 1;
114 assert_eq!(maybe_iroot(too_big, 2), None);
115 assert_eq!(maybe_iroot(too_big, 6), None);
116 }
117}