1use dashu_base::{CubicRoot, CubicRootRem, Sign, SquareRoot, SquareRootRem};
2
3use crate::{
4 error::{panic_root_negative, panic_root_zeroth},
5 ibig::IBig,
6 ubig::UBig,
7};
8
9impl UBig {
10 #[inline]
27 pub fn nth_root(&self, n: usize) -> UBig {
28 UBig(self.repr().nth_root(n))
29 }
30}
31
32impl SquareRoot for UBig {
33 type Output = UBig;
34 #[inline]
35 fn sqrt(&self) -> Self::Output {
36 UBig(self.repr().sqrt())
37 }
38}
39
40impl SquareRootRem for UBig {
41 type Output = UBig;
42 #[inline]
43 fn sqrt_rem(&self) -> (Self, Self) {
44 let (s, r) = self.repr().sqrt_rem();
45 (UBig(s), UBig(r))
46 }
47}
48
49impl CubicRoot for UBig {
50 type Output = UBig;
51 #[inline]
52 fn cbrt(&self) -> Self::Output {
53 self.nth_root(3)
54 }
55}
56
57impl CubicRootRem for UBig {
58 type Output = UBig;
59 #[inline]
60 fn cbrt_rem(&self) -> (Self::Output, Self) {
61 let c = self.nth_root(3);
62 let r = self - c.pow(3);
63 (c, r)
64 }
65}
66
67impl IBig {
68 #[inline]
83 pub fn nth_root(&self, n: usize) -> IBig {
84 if n == 0 {
85 panic_root_zeroth()
86 }
87
88 let (sign, mag) = self.as_sign_repr();
89 if sign == Sign::Negative && n % 2 == 0 {
90 panic_root_negative()
91 }
92
93 IBig(mag.nth_root(n).with_sign(sign))
94 }
95}
96
97impl SquareRoot for IBig {
98 type Output = UBig;
99 #[inline]
100 fn sqrt(&self) -> UBig {
101 let (sign, mag) = self.as_sign_repr();
102 if sign == Sign::Negative {
103 panic_root_negative()
104 }
105 UBig(mag.sqrt())
106 }
107}
108
109impl CubicRoot for IBig {
110 type Output = IBig;
111 #[inline]
112 fn cbrt(&self) -> IBig {
113 let (sign, mag) = self.as_sign_repr();
114 if sign == Sign::Negative {
115 panic_root_negative()
116 }
117 IBig(mag.nth_root(3).with_sign(sign))
118 }
119}
120
121mod repr {
122 use super::*;
123 use crate::{
124 add,
125 arch::word::Word,
126 buffer::Buffer,
127 memory::MemoryAllocation,
128 mul,
129 primitive::{extend_word, shrink_dword, WORD_BITS, WORD_BITS_USIZE},
130 repr::{
131 Repr,
132 TypedReprRef::{self, *},
133 },
134 root, shift, shift_ops,
135 };
136 use dashu_base::{SquareRoot, SquareRootRem};
137
138 impl<'a> TypedReprRef<'a> {
139 #[inline]
140 pub fn sqrt(self) -> Repr {
141 match self {
142 RefSmall(dw) => {
143 if let Some(w) = shrink_dword(dw) {
144 Repr::from_word(w.sqrt() as Word)
145 } else {
146 Repr::from_word(dw.sqrt())
147 }
148 }
149 RefLarge(words) => sqrt_rem_large(words, true).0,
150 }
151 }
152
153 #[inline]
154 pub fn sqrt_rem(self) -> (Repr, Repr) {
155 match self {
156 RefSmall(dw) => {
157 if let Some(w) = shrink_dword(dw) {
158 let (s, r) = w.sqrt_rem();
159 (Repr::from_word(s as Word), Repr::from_word(r))
160 } else {
161 let (s, r) = dw.sqrt_rem();
162 (Repr::from_word(s), Repr::from_dword(r))
163 }
164 }
165 RefLarge(words) => sqrt_rem_large(words, false),
166 }
167 }
168 }
169
170 fn sqrt_rem_large(words: &[Word], root_only: bool) -> (Repr, Repr) {
171 let shift = WORD_BITS_USIZE * (words.len() & 1)
174 + (words.last().unwrap().leading_zeros() & !1) as usize;
175 let n = (words.len() + 1) / 2;
176 let mut buffer = shift_ops::repr::shl_large_ref(words, shift).into_buffer();
177 let mut out = Buffer::allocate(n);
178 out.push_zeros(n);
179
180 let mut allocation = MemoryAllocation::new(root::memory_requirement_sqrt_rem(n));
181 let r_top = root::sqrt_rem(&mut out, &mut buffer, &mut allocation.memory());
182
183 if shift != 0 {
186 if !root_only {
189 let s0 = out[0] & ((1 << (shift / 2)) - 1);
190 let c1 = mul::add_mul_word_in_place(&mut buffer[..n], 2 * s0, &out);
191 let c2 =
192 add::sub_dword_in_place(&mut buffer[..n], extend_word(s0) * extend_word(s0));
193 buffer[n] = r_top as Word + c1 - c2 as Word;
194 }
195
196 let _ = shift::shr_in_place(&mut out, shift as u32 / 2);
198 if !root_only {
199 if shift >= WORD_BITS_USIZE {
206 shift::shr_in_place_one_word(&mut buffer);
207 buffer.truncate(n);
208 } else {
209 buffer.truncate(n + 1);
210 }
211 let _ = shift::shr_in_place(&mut buffer, shift as u32 % WORD_BITS);
212 }
213 } else if !root_only {
214 buffer[n] = r_top as Word;
215 buffer.truncate(n + 1);
216 }
217
218 (Repr::from_buffer(out), Repr::from_buffer(buffer))
219 }
220
221 impl<'a> TypedReprRef<'a> {
222 pub fn nth_root(self, n: usize) -> Repr {
223 match n {
224 0 => panic_root_zeroth(),
225 1 => return Repr::from_ref(self),
226 2 => return self.sqrt(),
227 _ => {}
228 }
229
230 let bits = self.bit_len();
231 if bits <= n {
232 return Repr::one();
233 }
234
235 let (repr, remaining) = reduce_by_small_factors(self, n);
240 if remaining == 1 {
241 return repr;
242 }
243 let typed = repr.as_typed();
244 if remaining == 2 {
245 return typed.sqrt();
246 }
247 newton_nth_root(typed, remaining)
248 }
249 }
250
251 fn reduce_by_small_factors(num: TypedReprRef<'_>, mut n: usize) -> (Repr, usize) {
254 let twos = n.trailing_zeros() as usize;
256 n >>= twos;
257 let mut repr = if twos == 0 {
258 Repr::from_ref(num)
259 } else {
260 let mut r = num.sqrt();
261 for _ in 1..twos {
262 r = r.as_typed().sqrt();
263 }
264 r
265 };
266
267 while n % 3 == 0 {
269 repr = newton_nth_root(repr.as_typed(), 3);
270 n /= 3;
271 }
272 while n % 5 == 0 {
274 repr = newton_nth_root(repr.as_typed(), 5);
275 n /= 5;
276 }
277 while n % 7 == 0 {
279 repr = newton_nth_root(repr.as_typed(), 7);
280 n /= 7;
281 }
282
283 (repr, n)
284 }
285
286 fn newton_nth_root(num: TypedReprRef<'_>, n: usize) -> Repr {
288 debug_assert!(n > 2);
289 let nm1 = n - 1;
290 let mut guess = UBig::ONE << (num.bit_len() / n); let next = |x: &UBig| {
292 let y = UBig(num / x.pow(nm1).into_repr());
293 (y + x * nm1) / n
294 };
295
296 let mut fixpoint = next(&guess);
297 while fixpoint > guess {
299 guess = fixpoint;
300 fixpoint = next(&guess);
301 }
302 while fixpoint < guess {
303 guess = fixpoint;
304 fixpoint = next(&guess);
305 }
306 guess.0
307 }
308}