malachite_q/arithmetic/log_base_2.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 core::cmp::Ordering::*;
11use malachite_base::num::arithmetic::traits::{
12 CeilingLogBase2, CheckedLogBase2, FloorLogBase2, IsPowerOf2,
13};
14use malachite_base::num::conversion::traits::ExactFrom;
15use malachite_base::num::logic::traits::SignificantBits;
16
17impl Rational {
18 /// Returns the floor of the base-2 logarithm of the absolute value of a nonzero [`Rational`].
19 ///
20 /// $f(x) = \lfloor\log_2 |x|\rfloor$.
21 ///
22 /// # Worst-case complexity
23 /// $T(n) = O(n)$
24 ///
25 /// $M(n) = O(1)$
26 ///
27 /// where $T$ is time, $M$ is additional memory, and $n$ is `self.significant_bits()`.
28 ///
29 /// # Panics
30 /// Panics if `self` is zero.
31 ///
32 /// # Examples
33 /// ```
34 /// use malachite_q::Rational;
35 ///
36 /// assert_eq!(Rational::from(3u32).floor_log_base_2_abs(), 1);
37 /// assert_eq!(Rational::from_signeds(1, 3).floor_log_base_2_abs(), -2);
38 /// assert_eq!(Rational::from_signeds(1, 4).floor_log_base_2_abs(), -2);
39 /// assert_eq!(Rational::from_signeds(1, 5).floor_log_base_2_abs(), -3);
40 ///
41 /// assert_eq!(Rational::from(-3).floor_log_base_2_abs(), 1);
42 /// assert_eq!(Rational::from_signeds(-1, 3).floor_log_base_2_abs(), -2);
43 /// assert_eq!(Rational::from_signeds(-1, 4).floor_log_base_2_abs(), -2);
44 /// assert_eq!(Rational::from_signeds(-1, 5).floor_log_base_2_abs(), -3);
45 /// ```
46 pub fn floor_log_base_2_abs(&self) -> i64 {
47 let exponent = i64::exact_from(self.numerator.significant_bits())
48 - i64::exact_from(self.denominator.significant_bits());
49 if self.numerator.cmp_normalized(&self.denominator) == Less {
50 exponent - 1
51 } else {
52 exponent
53 }
54 }
55
56 /// Returns the ceiling of the base-2 logarithm of the absolute value of a nonzero [`Rational`].
57 ///
58 /// $f(x) = \lfloor\log_2 |x|\rfloor$.
59 ///
60 /// # Worst-case complexity
61 /// $T(n) = O(n)$
62 ///
63 /// $M(n) = O(1)$
64 ///
65 /// where $T$ is time, $M$ is additional memory, and $n$ is `self.significant_bits()`.
66 ///
67 /// # Panics
68 /// Panics if `self` less than or equal to zero.
69 ///
70 /// # Examples
71 /// ```
72 /// use malachite_q::Rational;
73 ///
74 /// assert_eq!(Rational::from(3u32).ceiling_log_base_2_abs(), 2);
75 /// assert_eq!(Rational::from_signeds(1, 3).ceiling_log_base_2_abs(), -1);
76 /// assert_eq!(Rational::from_signeds(1, 4).ceiling_log_base_2_abs(), -2);
77 /// assert_eq!(Rational::from_signeds(1, 5).ceiling_log_base_2_abs(), -2);
78 ///
79 /// assert_eq!(Rational::from(-3).ceiling_log_base_2_abs(), 2);
80 /// assert_eq!(Rational::from_signeds(-1, 3).ceiling_log_base_2_abs(), -1);
81 /// assert_eq!(Rational::from_signeds(-1, 4).ceiling_log_base_2_abs(), -2);
82 /// assert_eq!(Rational::from_signeds(-1, 5).ceiling_log_base_2_abs(), -2);
83 /// ```
84 pub fn ceiling_log_base_2_abs(&self) -> i64 {
85 let exponent = i64::exact_from(self.numerator.significant_bits())
86 - i64::exact_from(self.denominator.significant_bits());
87 if self.numerator.cmp_normalized(&self.denominator) == Greater {
88 exponent + 1
89 } else {
90 exponent
91 }
92 }
93}
94
95impl FloorLogBase2 for &Rational {
96 type Output = i64;
97
98 /// Returns the floor of the base-2 logarithm of a positive [`Rational`].
99 ///
100 /// $f(x) = \lfloor\log_2 x\rfloor$.
101 ///
102 /// # Worst-case complexity
103 /// $T(n) = O(n)$
104 ///
105 /// $M(n) = O(1)$
106 ///
107 /// where $T$ is time, $M$ is additional memory, and $n$ is `self.significant_bits()`.
108 ///
109 /// # Panics
110 /// Panics if `self` less than or equal to zero.
111 ///
112 /// # Examples
113 /// ```
114 /// use malachite_base::num::arithmetic::traits::FloorLogBase2;
115 /// use malachite_q::Rational;
116 ///
117 /// assert_eq!(Rational::from(3u32).floor_log_base_2(), 1);
118 /// assert_eq!(Rational::from_signeds(1, 3).floor_log_base_2(), -2);
119 /// assert_eq!(Rational::from_signeds(1, 4).floor_log_base_2(), -2);
120 /// assert_eq!(Rational::from_signeds(1, 5).floor_log_base_2(), -3);
121 /// ```
122 #[inline]
123 fn floor_log_base_2(self) -> i64 {
124 assert!(*self > 0u32);
125 self.floor_log_base_2_abs()
126 }
127}
128
129impl CeilingLogBase2 for &Rational {
130 type Output = i64;
131
132 /// Returns the ceiling of the base-2 logarithm of a positive [`Rational`].
133 ///
134 /// $f(x) = \lfloor\log_2 x\rfloor$.
135 ///
136 /// # Worst-case complexity
137 /// $T(n) = O(n)$
138 ///
139 /// $M(n) = O(1)$
140 ///
141 /// where $T$ is time, $M$ is additional memory, and $n$ is `self.significant_bits()`.
142 ///
143 /// # Panics
144 /// Panics if `self` less than or equal to zero.
145 ///
146 /// # Examples
147 /// ```
148 /// use malachite_base::num::arithmetic::traits::CeilingLogBase2;
149 /// use malachite_q::Rational;
150 ///
151 /// assert_eq!(Rational::from(3u32).ceiling_log_base_2(), 2);
152 /// assert_eq!(Rational::from_signeds(1, 3).ceiling_log_base_2(), -1);
153 /// assert_eq!(Rational::from_signeds(1, 4).ceiling_log_base_2(), -2);
154 /// assert_eq!(Rational::from_signeds(1, 5).ceiling_log_base_2(), -2);
155 /// ```
156 #[inline]
157 fn ceiling_log_base_2(self) -> i64 {
158 assert!(*self > 0u32);
159 self.ceiling_log_base_2_abs()
160 }
161}
162
163impl CheckedLogBase2 for &Rational {
164 type Output = i64;
165
166 /// Returns the base-2 logarithm of a positive [`Rational`]. If the [`Rational`] is not a power
167 /// of 2, then `None` is returned.
168 ///
169 /// $$
170 /// f(x) = \\begin{cases}
171 /// \operatorname{Some}(\log_2 x) & \text{if} \\quad \log_2 x \in \Z, \\\\
172 /// \operatorname{None} & \textrm{otherwise}.
173 /// \\end{cases}
174 /// $$
175 ///
176 /// # Worst-case complexity
177 /// $T(n) = O(n)$
178 ///
179 /// $M(n) = O(1)$
180 ///
181 /// where $T$ is time, $M$ is additional memory, and $n$ is `self.significant_bits()`.
182 ///
183 /// # Panics
184 /// Panics if `self` is less than or equal to zero.
185 ///
186 /// # Examples
187 /// ```
188 /// use malachite_base::num::arithmetic::traits::CheckedLogBase2;
189 /// use malachite_q::Rational;
190 ///
191 /// assert_eq!(Rational::from(3u32).checked_log_base_2(), None);
192 /// assert_eq!(Rational::from_signeds(1, 3).checked_log_base_2(), None);
193 /// assert_eq!(Rational::from_signeds(1, 4).checked_log_base_2(), Some(-2));
194 /// assert_eq!(Rational::from_signeds(1, 5).checked_log_base_2(), None);
195 /// ```
196 fn checked_log_base_2(self) -> Option<i64> {
197 assert!(*self > 0u32);
198 if self.denominator == 1u32 && self.numerator.is_power_of_2() {
199 Some(i64::exact_from(self.numerator.significant_bits()) - 1)
200 } else if self.numerator == 1u32 && self.denominator.is_power_of_2() {
201 Some(1 - i64::exact_from(self.denominator.significant_bits()))
202 } else {
203 None
204 }
205 }
206}