malachite_q/arithmetic/sqrt.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::{CheckedSqrt, UnsignedAbs};
11use malachite_base::num::logic::traits::SignificantBits;
12use malachite_nz::integer::Integer;
13
14impl CheckedSqrt for Rational {
15 type Output = Self;
16
17 /// Returns the the square root of a [`Rational`], or `None` if it is not a perfect square. The
18 /// [`Rational`] is taken by value.
19 ///
20 /// $$
21 /// f(x) = \\begin{cases}
22 /// \operatorname{Some}(sqrt{x}) & \text{if} \\quad \sqrt{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 \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 `self` is negative.
36 ///
37 /// # Examples
38 /// ```
39 /// use malachite_base::num::arithmetic::traits::CheckedSqrt;
40 /// use malachite_base::strings::ToDebugString;
41 /// use malachite_q::Rational;
42 ///
43 /// assert_eq!(
44 /// Rational::from(99u8).checked_sqrt().to_debug_string(),
45 /// "None"
46 /// );
47 /// assert_eq!(
48 /// Rational::from(100u8).checked_sqrt().to_debug_string(),
49 /// "Some(10)"
50 /// );
51 /// assert_eq!(
52 /// Rational::from(101u8).checked_sqrt().to_debug_string(),
53 /// "None"
54 /// );
55 /// assert_eq!(
56 /// Rational::from_signeds(22, 7)
57 /// .checked_sqrt()
58 /// .to_debug_string(),
59 /// "None"
60 /// );
61 /// assert_eq!(
62 /// Rational::from_signeds(25, 9)
63 /// .checked_sqrt()
64 /// .to_debug_string(),
65 /// "Some(5/3)"
66 /// );
67 /// ```
68 fn checked_sqrt(self) -> Option<Self> {
69 assert!(self >= 0);
70 let (n, d) = self.into_numerator_and_denominator();
71 let sqrt_n;
72 let sqrt_d;
73 if n.significant_bits() <= d.significant_bits() {
74 sqrt_n = n.checked_sqrt()?;
75 sqrt_d = d.checked_sqrt()?;
76 } else {
77 sqrt_d = d.checked_sqrt()?;
78 sqrt_n = n.checked_sqrt()?;
79 }
80 Some(Self {
81 sign: sqrt_n >= 0,
82 numerator: sqrt_n,
83 denominator: sqrt_d,
84 })
85 }
86}
87
88impl CheckedSqrt for &Rational {
89 type Output = Rational;
90
91 /// Returns the the square root of a [`Rational`], or `None` if it is not a perfect square. The
92 /// [`Rational`] is taken by reference.
93 ///
94 /// $$
95 /// f(x) = \\begin{cases}
96 /// \operatorname{Some}(sqrt{x}) & \text{if} \\quad \sqrt{x} \in \mathbb{Q}, \\\\
97 /// \operatorname{None} & \textrm{otherwise}.
98 /// \\end{cases}
99 /// $$
100 ///
101 /// # Worst-case complexity
102 /// $T(n) = O(n \log n \log\log n)$
103 ///
104 /// $M(n) = O(n \log n)$
105 ///
106 /// where $T$ is time, $M$ is additional memory, and $n$ is `self.significant_bits()`.
107 ///
108 /// # Panics
109 /// Panics if `self` is negative.
110 ///
111 /// # Examples
112 /// ```
113 /// use malachite_base::num::arithmetic::traits::CheckedSqrt;
114 /// use malachite_base::strings::ToDebugString;
115 /// use malachite_q::Rational;
116 ///
117 /// assert_eq!(
118 /// (&Rational::from(99u8)).checked_sqrt().to_debug_string(),
119 /// "None"
120 /// );
121 /// assert_eq!(
122 /// (&Rational::from(100u8)).checked_sqrt().to_debug_string(),
123 /// "Some(10)"
124 /// );
125 /// assert_eq!(
126 /// (&Rational::from(101u8)).checked_sqrt().to_debug_string(),
127 /// "None"
128 /// );
129 /// assert_eq!(
130 /// (&Rational::from_signeds(22, 7))
131 /// .checked_sqrt()
132 /// .to_debug_string(),
133 /// "None"
134 /// );
135 /// assert_eq!(
136 /// (&Rational::from_signeds(25, 9))
137 /// .checked_sqrt()
138 /// .to_debug_string(),
139 /// "Some(5/3)"
140 /// );
141 /// ```
142 fn checked_sqrt(self) -> Option<Rational> {
143 let (n, d) = self.numerator_and_denominator_ref();
144 let sqrt_n;
145 let sqrt_d;
146 if n.significant_bits() <= d.significant_bits() {
147 sqrt_n = Integer::from_sign_and_abs_ref(*self >= 0, n).checked_sqrt()?;
148 sqrt_d = d.checked_sqrt()?;
149 } else {
150 sqrt_d = d.checked_sqrt()?;
151 sqrt_n = Integer::from_sign_and_abs_ref(*self >= 0, n).checked_sqrt()?;
152 }
153 Some(Rational {
154 sign: sqrt_n >= 0,
155 numerator: sqrt_n.unsigned_abs(),
156 denominator: sqrt_d,
157 })
158 }
159}