malachite_q/arithmetic/root.rs
1// Copyright © 2025 Mikhail Hogrefe
2//
3// This file is part of Malachite.
4//
5// Malachite is free software: you can redistribute it and/or modify it under the terms of the GNU
6// Lesser General Public License (LGPL) as published by the Free Software Foundation; either version
7// 3 of the License, or (at your option) any later version. See <https://www.gnu.org/licenses/>.
8
9use crate::Rational;
10use malachite_base::num::arithmetic::traits::{CheckedRoot, Reciprocal, UnsignedAbs};
11use malachite_base::num::logic::traits::SignificantBits;
12use malachite_nz::integer::Integer;
13
14impl CheckedRoot<u64> for Rational {
15 type Output = Self;
16
17 /// Returns the the $n$th root of a [`Rational`], or `None` if the [`Rational`] is not a perfect
18 /// $n$th power. The [`Rational`] is taken by value.
19 ///
20 /// $$
21 /// f(x, n) = \\begin{cases}
22 /// \operatorname{Some}(sqrt\[n\]{x}) & \text{if} \\quad \sqrt\[n\]{x} \in \mathbb{Q}, \\\\
23 /// \operatorname{None} & \textrm{otherwise}.
24 /// \\end{cases}
25 /// $$
26 ///
27 /// # Worst-case complexity
28 /// $T(n) = O(n (\log n)^2 \log\log n)$
29 ///
30 /// $M(n) = O(n \log n)$
31 ///
32 /// where $T$ is time, $M$ is additional memory, and $n$ is `self.significant_bits()`.
33 ///
34 /// # Panics
35 /// Panics if `exp` is zero, or if `exp` is even and `self` is negative.
36 ///
37 /// # Examples
38 /// ```
39 /// use malachite_base::num::arithmetic::traits::CheckedRoot;
40 /// use malachite_base::strings::ToDebugString;
41 /// use malachite_q::Rational;
42 ///
43 /// assert_eq!(
44 /// Rational::from(999i32).checked_root(3u64).to_debug_string(),
45 /// "None"
46 /// );
47 /// assert_eq!(
48 /// Rational::from(1000i32).checked_root(3u64).to_debug_string(),
49 /// "Some(10)"
50 /// );
51 /// assert_eq!(
52 /// Rational::from(1001i32).checked_root(3u64).to_debug_string(),
53 /// "None"
54 /// );
55 /// assert_eq!(
56 /// Rational::from(-1000i32)
57 /// .checked_root(3u64)
58 /// .to_debug_string(),
59 /// "Some(-10)"
60 /// );
61 /// assert_eq!(
62 /// Rational::from_signeds(22, 7)
63 /// .checked_root(3u64)
64 /// .to_debug_string(),
65 /// "None"
66 /// );
67 /// assert_eq!(
68 /// Rational::from_signeds(27, 8)
69 /// .checked_root(3u64)
70 /// .to_debug_string(),
71 /// "Some(3/2)"
72 /// );
73 /// assert_eq!(
74 /// Rational::from_signeds(-27, 8)
75 /// .checked_root(3u64)
76 /// .to_debug_string(),
77 /// "Some(-3/2)"
78 /// );
79 /// ```
80 fn checked_root(self, pow: u64) -> Option<Self> {
81 let sign = self >= 0;
82 let (n, d) = self.into_numerator_and_denominator();
83 let root_n;
84 let root_d;
85 if n.significant_bits() <= d.significant_bits() {
86 root_n = Integer::from_sign_and_abs(sign, n).checked_root(pow)?;
87 root_d = d.checked_root(pow)?;
88 } else {
89 root_d = d.checked_root(pow)?;
90 root_n = Integer::from_sign_and_abs(sign, n).checked_root(pow)?;
91 }
92 Some(Self {
93 sign: root_n >= 0,
94 numerator: root_n.unsigned_abs(),
95 denominator: root_d,
96 })
97 }
98}
99
100impl CheckedRoot<u64> for &Rational {
101 type Output = Rational;
102
103 /// Returns the the $n$th root of a [`Rational`], or `None` if the [`Rational`] is not a perfect
104 /// $n$th power. The [`Rational`] is taken by reference.
105 ///
106 /// $$
107 /// f(x, n) = \\begin{cases}
108 /// \operatorname{Some}(sqrt\[n\]{x}) & \text{if} \\quad \sqrt\[n\]{x} \in \mathbb{Q}, \\\\
109 /// \operatorname{None} & \textrm{otherwise}.
110 /// \\end{cases}
111 /// $$
112 ///
113 /// # Worst-case complexity
114 /// $T(n) = O(n (\log n)^2 \log\log n)$
115 ///
116 /// $M(n) = O(n \log n)$
117 ///
118 /// where $T$ is time, $M$ is additional memory, and $n$ is `self.significant_bits()`.
119 ///
120 /// # Panics
121 /// Panics if `exp` is zero, or if `exp` is even and `self` is negative.
122 ///
123 /// # Examples
124 /// ```
125 /// use malachite_base::num::arithmetic::traits::CheckedRoot;
126 /// use malachite_base::strings::ToDebugString;
127 /// use malachite_q::Rational;
128 ///
129 /// assert_eq!(
130 /// Rational::from(999i32).checked_root(3u64).to_debug_string(),
131 /// "None"
132 /// );
133 /// assert_eq!(
134 /// Rational::from(1000i32).checked_root(3u64).to_debug_string(),
135 /// "Some(10)"
136 /// );
137 /// assert_eq!(
138 /// Rational::from(1001i32).checked_root(3u64).to_debug_string(),
139 /// "None"
140 /// );
141 /// assert_eq!(
142 /// Rational::from(-1000i32)
143 /// .checked_root(3u64)
144 /// .to_debug_string(),
145 /// "Some(-10)"
146 /// );
147 /// assert_eq!(
148 /// Rational::from_signeds(22, 7)
149 /// .checked_root(3u64)
150 /// .to_debug_string(),
151 /// "None"
152 /// );
153 /// assert_eq!(
154 /// Rational::from_signeds(27, 8)
155 /// .checked_root(3u64)
156 /// .to_debug_string(),
157 /// "Some(3/2)"
158 /// );
159 /// assert_eq!(
160 /// Rational::from_signeds(-27, 8)
161 /// .checked_root(3u64)
162 /// .to_debug_string(),
163 /// "Some(-3/2)"
164 /// );
165 /// ```
166 fn checked_root(self, pow: u64) -> Option<Rational> {
167 let (n, d) = self.numerator_and_denominator_ref();
168 let root_n;
169 let root_d;
170 if n.significant_bits() <= d.significant_bits() {
171 root_n = Integer::from_sign_and_abs_ref(*self >= 0, n).checked_root(pow)?;
172 root_d = d.checked_root(pow)?;
173 } else {
174 root_d = d.checked_root(pow)?;
175 root_n = Integer::from_sign_and_abs_ref(*self >= 0, n).checked_root(pow)?;
176 }
177 Some(Rational {
178 sign: root_n >= 0,
179 numerator: root_n.unsigned_abs(),
180 denominator: root_d,
181 })
182 }
183}
184
185impl CheckedRoot<i64> for Rational {
186 type Output = Self;
187
188 /// Returns the the $n$th root of a [`Rational`], or `None` if the [`Rational`] is not a perfect
189 /// $n$th power. The [`Rational`] is taken by value.
190 ///
191 /// $$
192 /// f(x, n) = \\begin{cases}
193 /// \operatorname{Some}(sqrt\[n\]{x}) & \text{if} \\quad \sqrt\[n\]{x} \in \mathbb{Q}, \\\\
194 /// \operatorname{None} & \textrm{otherwise}.
195 /// \\end{cases}
196 /// $$
197 ///
198 /// # Worst-case complexity
199 /// $T(n) = O(n (\log n)^2 \log\log n)$
200 ///
201 /// $M(n) = O(n \log n)$
202 ///
203 /// where $T$ is time, $M$ is additional memory, and $n$ is `self.significant_bits()`.
204 ///
205 /// # Panics
206 /// Panics if `exp` is zero, if `exp` is even and `self` is negative, or if `self` is zero and
207 /// `exp` is negative.
208 ///
209 /// # Examples
210 /// ```
211 /// use malachite_base::num::arithmetic::traits::CheckedRoot;
212 /// use malachite_base::strings::ToDebugString;
213 /// use malachite_q::Rational;
214 ///
215 /// assert_eq!(
216 /// Rational::from(999i32).checked_root(3i64).to_debug_string(),
217 /// "None"
218 /// );
219 /// assert_eq!(
220 /// Rational::from(1000i32).checked_root(3i64).to_debug_string(),
221 /// "Some(10)"
222 /// );
223 /// assert_eq!(
224 /// Rational::from(1001i32).checked_root(3i64).to_debug_string(),
225 /// "None"
226 /// );
227 /// assert_eq!(
228 /// Rational::from(-1000i32)
229 /// .checked_root(3i64)
230 /// .to_debug_string(),
231 /// "Some(-10)"
232 /// );
233 /// assert_eq!(
234 /// Rational::from_signeds(22, 7)
235 /// .checked_root(3i64)
236 /// .to_debug_string(),
237 /// "None"
238 /// );
239 /// assert_eq!(
240 /// Rational::from_signeds(27, 8)
241 /// .checked_root(3i64)
242 /// .to_debug_string(),
243 /// "Some(3/2)"
244 /// );
245 /// assert_eq!(
246 /// Rational::from_signeds(-27, 8)
247 /// .checked_root(3i64)
248 /// .to_debug_string(),
249 /// "Some(-3/2)"
250 /// );
251 ///
252 /// assert_eq!(
253 /// Rational::from(1000i32)
254 /// .checked_root(-3i64)
255 /// .to_debug_string(),
256 /// "Some(1/10)"
257 /// );
258 /// assert_eq!(
259 /// Rational::from_signeds(-27, 8)
260 /// .checked_root(-3i64)
261 /// .to_debug_string(),
262 /// "Some(-2/3)"
263 /// );
264 /// ```
265 fn checked_root(self, pow: i64) -> Option<Self> {
266 let u_pow = pow.unsigned_abs();
267 if pow >= 0 {
268 self.checked_root(u_pow)
269 } else {
270 self.checked_root(u_pow).map(Self::reciprocal)
271 }
272 }
273}
274
275impl CheckedRoot<i64> for &Rational {
276 type Output = Rational;
277
278 /// Returns the the $n$th root of a [`Rational`], or `None` if the [`Rational`] is not a perfect
279 /// $n$th power. The [`Rational`] is taken by reference.
280 ///
281 /// $$
282 /// f(x, n) = \\begin{cases}
283 /// \operatorname{Some}(sqrt\[n\]{x}) & \text{if} \\quad \sqrt\[n\]{x} \in \mathbb{Q}, \\\\
284 /// \operatorname{None} & \textrm{otherwise}.
285 /// \\end{cases}
286 /// $$
287 ///
288 /// # Worst-case complexity
289 /// $T(n) = O(n (\log n)^2 \log\log n)$
290 ///
291 /// $M(n) = O(n \log n)$
292 ///
293 /// where $T$ is time, $M$ is additional memory, and $n$ is `self.significant_bits()`.
294 ///
295 /// # Panics
296 /// Panics if `exp` is zero, if `exp` is even and `self` is negative, or if `self` is zero and
297 /// `exp` is negative.
298 ///
299 /// # Examples
300 /// ```
301 /// use malachite_base::num::arithmetic::traits::CheckedRoot;
302 /// use malachite_base::strings::ToDebugString;
303 /// use malachite_q::Rational;
304 ///
305 /// assert_eq!(
306 /// (&Rational::from(999i32))
307 /// .checked_root(3i64)
308 /// .to_debug_string(),
309 /// "None"
310 /// );
311 /// assert_eq!(
312 /// (&Rational::from(1000i32))
313 /// .checked_root(3i64)
314 /// .to_debug_string(),
315 /// "Some(10)"
316 /// );
317 /// assert_eq!(
318 /// (&Rational::from(1001i32))
319 /// .checked_root(3i64)
320 /// .to_debug_string(),
321 /// "None"
322 /// );
323 /// assert_eq!(
324 /// (&Rational::from(-1000i32))
325 /// .checked_root(3i64)
326 /// .to_debug_string(),
327 /// "Some(-10)"
328 /// );
329 /// assert_eq!(
330 /// (&Rational::from_signeds(22, 7))
331 /// .checked_root(3i64)
332 /// .to_debug_string(),
333 /// "None"
334 /// );
335 /// assert_eq!(
336 /// (&Rational::from_signeds(27, 8))
337 /// .checked_root(3i64)
338 /// .to_debug_string(),
339 /// "Some(3/2)"
340 /// );
341 /// assert_eq!(
342 /// (&Rational::from_signeds(-27, 8))
343 /// .checked_root(3i64)
344 /// .to_debug_string(),
345 /// "Some(-3/2)"
346 /// );
347 ///
348 /// assert_eq!(
349 /// (&Rational::from(1000i32))
350 /// .checked_root(-3i64)
351 /// .to_debug_string(),
352 /// "Some(1/10)"
353 /// );
354 /// assert_eq!(
355 /// (&Rational::from_signeds(-27, 8))
356 /// .checked_root(-3i64)
357 /// .to_debug_string(),
358 /// "Some(-2/3)"
359 /// );
360 /// ```
361 fn checked_root(self, pow: i64) -> Option<Rational> {
362 let u_pow = pow.unsigned_abs();
363 if pow >= 0 {
364 self.checked_root(u_pow)
365 } else {
366 self.checked_root(u_pow).map(Rational::reciprocal)
367 }
368 }
369}