fixed_bigint/heapless/
roots_impl.rs1use super::HeaplessBigInt;
12use crate::MachineWord;
13use const_num_traits::{CarryingMul, CheckedPow, Nct};
14use num_integer::Roots;
15use num_traits::{FromPrimitive, One, Zero};
16
17impl<T, const CAP: usize> Roots for HeaplessBigInt<T, CAP, Nct>
18where
19 T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
20{
21 fn nth_root(&self, n: u32) -> Self {
22 if n == 0 {
23 panic!("nth_root: n must be non-zero");
24 }
25 if Zero::is_zero(self) {
27 return *self;
28 }
29 if One::is_one(self) || n == 1 {
30 return *self;
31 }
32
33 let width = self.len();
34 let bit_len = self.bit_length();
35 if n as usize > bit_len {
37 return <Self as One>::one().widened(width);
38 }
39
40 let one_w = <Self as One>::one().widened(width);
41 let initial_exp = (bit_len as u32).div_ceil(n).max(1);
42 let mut x = one_w << (initial_exp as usize);
43
44 let n_val = width_const(n, width);
49 let n_minus_1 = width_const(n - 1, width);
50
51 loop {
52 let quotient = match CheckedPow::checked_pow(x, n - 1) {
55 Some(p) if Zero::is_zero(&p) => break,
56 Some(p) => *self / p,
57 None => Self::new_zero_with_len(width),
58 };
59 let numerator = x * n_minus_1 + quotient;
60 let x_new = numerator / n_val;
61 if x_new >= x {
62 break;
63 }
64 x = x_new;
65 }
66
67 while CheckedPow::checked_pow(x, n).is_none_or(|p| p > *self) {
70 x -= one_w;
71 }
72 let mut x_plus_one = x + one_w;
73 while CheckedPow::checked_pow(x_plus_one, n).is_some_and(|p| p <= *self) {
74 x += one_w;
75 x_plus_one = x + one_w;
76 }
77
78 x
79 }
80}
81
82fn width_const<T, const CAP: usize>(value: u32, width: u16) -> HeaplessBigInt<T, CAP, Nct>
86where
87 T: MachineWord,
88{
89 <HeaplessBigInt<T, CAP, Nct> as FromPrimitive>::from_u64(value as u64)
92 .expect("width_const: value fits the carrier")
93 .widened(width)
94}
95
96#[cfg(test)]
97mod tests {
98 use super::HeaplessBigInt;
99 use num_integer::Roots;
100
101 type H = HeaplessBigInt<u8, 8>;
102
103 #[test]
104 fn sqrt_cbrt_nth() {
105 assert_eq!(H::from(16u8).sqrt(), H::from(4u8));
106 assert_eq!(H::from(15u8).sqrt(), H::from(3u8));
107 assert_eq!(H::from(27u8).cbrt(), H::from(3u8));
108 assert_eq!(H::from(63u8).cbrt(), H::from(3u8));
109 assert_eq!(H::from(81u8).nth_root(4), H::from(3u8));
110 assert_eq!(H::from(42u8).nth_root(1), H::from(42u8));
111 assert_eq!(H::from(2u8).nth_root(100), H::from(1u8));
113 }
114
115 #[test]
116 #[should_panic(expected = "nth_root: n must be non-zero")]
117 fn nth_root_zero_n_panics() {
118 H::from(16u8).nth_root(0);
119 }
120
121 #[test]
123 fn nth_root_preserves_width() {
124 let n = H::from(10000u16).widened(8);
125 let r = n.sqrt();
126 assert_eq!(r, H::from(100u8));
127 assert_eq!(r.len(), 8);
128 }
129
130 #[test]
131 fn root_correctness_small_range() {
132 for x in 1..=200u16 {
133 let xi = H::from(x);
134 let s = xi.sqrt();
135 assert!(
136 s.pow(2) <= xi && (s + H::from(1u8)).pow(2) > xi,
137 "sqrt({x})"
138 );
139 let c = xi.cbrt();
140 assert!(
141 c.pow(3) <= xi && (c + H::from(1u8)).pow(3) > xi,
142 "cbrt({x})"
143 );
144 }
145 }
146
147 #[test]
151 fn sqrt_of_max_at_exact_width_does_not_panic() {
152 type H1 = HeaplessBigInt<u32, 1>; assert_eq!(H1::from(u32::MAX).sqrt(), H1::from(0xFFFFu32));
154 }
155
156 #[test]
159 fn sqrt_narrow_word_tiny_cap() {
160 type H1 = HeaplessBigInt<u8, 1>; assert_eq!(H1::from(196u8).sqrt(), H1::from(14u8));
162 assert_eq!(H1::from(255u8).sqrt(), H1::from(15u8));
163 assert_eq!(H1::from(196u8).sqrt().len(), 1);
164 }
165}