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}