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}