malachite_base/num/arithmetic/log_base_power_of_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::num::arithmetic::traits::{
10 CeilingLogBasePowerOf2, CheckedLogBasePowerOf2, DivMod, DivRound, FloorLogBasePowerOf2,
11};
12use crate::num::basic::unsigneds::PrimitiveUnsigned;
13use crate::num::conversion::traits::{ExactFrom, SciMantissaAndExponent};
14use crate::rounding_modes::RoundingMode::*;
15
16#[cfg(feature = "test_build")]
17pub fn ceiling_log_base_power_of_2_naive<T: PrimitiveUnsigned>(x: T, pow: u64) -> u64 {
18 assert_ne!(x, T::ZERO);
19 assert_ne!(pow, 0);
20 if pow >= T::WIDTH {
21 return u64::from(x != T::ONE);
22 }
23 let mut result = 0;
24 let mut p = T::ONE;
25 while p < x {
26 let highest_possible = p.leading_zeros() < pow;
27 result += 1;
28 if highest_possible {
29 break;
30 }
31 p <<= pow;
32 }
33 result
34}
35
36fn floor_log_base_power_of_2<T: PrimitiveUnsigned>(x: T, pow: u64) -> u64 {
37 assert!(x != T::ZERO, "Cannot take the base-2 logarithm of 0.");
38 assert_ne!(pow, 0);
39 (x.significant_bits() - 1) / pow
40}
41
42fn ceiling_log_base_power_of_2<T: PrimitiveUnsigned>(x: T, pow: u64) -> u64 {
43 assert!(x != T::ZERO, "Cannot take the base-2 logarithm of 0.");
44 assert_ne!(pow, 0);
45 let (floor_log, rem) = (x.significant_bits() - 1).div_mod(pow);
46 if rem == 0 && T::is_power_of_2(&x) {
47 floor_log
48 } else {
49 floor_log + 1
50 }
51}
52
53fn checked_log_base_power_of_2<T: PrimitiveUnsigned>(x: T, pow: u64) -> Option<u64> {
54 assert!(x != T::ZERO, "Cannot take the base-2 logarithm of 0.");
55 assert_ne!(pow, 0);
56 let (floor_log, rem) = (x.significant_bits() - 1).div_mod(pow);
57 if rem == 0 && T::is_power_of_2(&x) {
58 Some(floor_log)
59 } else {
60 None
61 }
62}
63
64macro_rules! impl_log_base_power_of_2_unsigned {
65 ($t:ident) => {
66 impl FloorLogBasePowerOf2<u64> for $t {
67 type Output = u64;
68
69 /// Returns the floor of the base-$2^k$ logarithm of a positive integer.
70 ///
71 /// $f(x, k) = \lfloor\log_{2^k} x\rfloor$.
72 ///
73 /// # Worst-case complexity
74 /// Constant time and additional memory.
75 ///
76 /// # Panics
77 /// Panics if `self` is infinite, `NaN`, or less than or equal to zero, or if `pow` is
78 /// zero.
79 ///
80 /// # Examples
81 /// See [here](super::log_base_power_of_2#floor_log_base_power_of_2).
82 #[inline]
83 fn floor_log_base_power_of_2(self, pow: u64) -> u64 {
84 floor_log_base_power_of_2(self, pow)
85 }
86 }
87
88 impl CeilingLogBasePowerOf2<u64> for $t {
89 type Output = u64;
90
91 /// Returns the ceiling of the base-$2^k$ logarithm of a positive integer.
92 ///
93 /// $f(x, k) = \lceil\log_{2^k} x\rceil$.
94 ///
95 /// # Worst-case complexity
96 /// Constant time and additional memory.
97 ///
98 /// # Panics
99 /// Panics if `self` is infinite, `NaN`, or less than or equal to zero, or if `pow` is
100 /// zero.
101 ///
102 /// # Examples
103 /// See [here](super::log_base_power_of_2#ceiling_log_base_power_of_2).
104 #[inline]
105 fn ceiling_log_base_power_of_2(self, pow: u64) -> u64 {
106 ceiling_log_base_power_of_2(self, pow)
107 }
108 }
109
110 impl CheckedLogBasePowerOf2<u64> for $t {
111 type Output = u64;
112
113 /// Returns the base-$2^k$ logarithm of a positive integer. If the integer is not a
114 /// power of $2^k$, `None` is returned.
115 ///
116 /// $$
117 /// f(x, k) = \\begin{cases}
118 /// \operatorname{Some}(\log_{2^k} x) & \text{if} \\quad \log_{2^k} x \in \Z, \\\\
119 /// \operatorname{None} & \textrm{otherwise}.
120 /// \\end{cases}
121 /// $$
122 ///
123 /// # Worst-case complexity
124 /// Constant time and additional memory.
125 ///
126 /// # Panics
127 /// Panics if `self` is infinite, `NaN`, or less than or equal to zero, or if `pow` is
128 /// zero.
129 ///
130 /// # Examples
131 /// See [here](super::log_base_power_of_2#ceiling_log_base_power_of_2).
132 #[inline]
133 fn checked_log_base_power_of_2(self, pow: u64) -> Option<u64> {
134 checked_log_base_power_of_2(self, pow)
135 }
136 }
137 };
138}
139apply_to_unsigneds!(impl_log_base_power_of_2_unsigned);
140
141macro_rules! impl_log_base_power_of_2_primitive_float {
142 ($t:ident) => {
143 impl FloorLogBasePowerOf2<u64> for $t {
144 type Output = i64;
145
146 /// Returns the floor of the base-$2^k$ logarithm of a positive float.
147 ///
148 /// $f(x, k) = \lfloor\log_{2^k} x\rfloor$.
149 ///
150 /// # Worst-case complexity
151 /// Constant time and additional memory.
152 ///
153 /// # Panics
154 /// Panics if `self` or `pow` are 0.
155 ///
156 /// # Examples
157 /// See [here](super::log_base_power_of_2#floor_log_base_power_of_2).
158 #[inline]
159 fn floor_log_base_power_of_2(self, pow: u64) -> i64 {
160 assert!(self > 0.0);
161 self.sci_exponent().div_round(i64::exact_from(pow), Floor).0
162 }
163 }
164
165 impl CeilingLogBasePowerOf2<u64> for $t {
166 type Output = i64;
167
168 /// Returns the ceiling of the base-$2^k$ logarithm of a positive float.
169 ///
170 /// $f(x, k) = \lceil\log_{2^k} x\rceil$.
171 ///
172 /// # Worst-case complexity
173 /// Constant time and additional memory.
174 ///
175 /// # Panics
176 /// Panics if `self` or `pow` are 0.
177 ///
178 /// # Examples
179 /// See [here](super::log_base_power_of_2#ceiling_log_base_power_of_2).
180 #[inline]
181 fn ceiling_log_base_power_of_2(self, pow: u64) -> i64 {
182 assert!(self > 0.0);
183 let (mantissa, exponent) = self.sci_mantissa_and_exponent();
184 let exact = mantissa == 1.0;
185 let (q, r) = exponent.div_mod(i64::exact_from(pow));
186 if exact && r == 0 { q } else { q + 1 }
187 }
188 }
189
190 impl CheckedLogBasePowerOf2<u64> for $t {
191 type Output = i64;
192
193 /// Returns the base-$2^k$ logarithm of a positive float. If the float is not a power of
194 /// $2^k$, `None` is returned.
195 ///
196 /// $$
197 /// f(x, k) = \\begin{cases}
198 /// \operatorname{Some}(\log_{2^k} x) & \text{if} \\quad \log_{2^k} x \in \Z, \\\\
199 /// \operatorname{None} & \textrm{otherwise}.
200 /// \\end{cases}
201 /// $$
202 ///
203 /// # Worst-case complexity
204 /// Constant time and additional memory.
205 ///
206 /// # Panics
207 /// Panics if `self` or `pow` are 0.
208 ///
209 /// # Examples
210 /// See [here](super::log_base_power_of_2#checked_log_base_power_of_2).
211 #[inline]
212 fn checked_log_base_power_of_2(self, pow: u64) -> Option<i64> {
213 assert!(self > 0.0);
214 let (mantissa, exponent) = self.sci_mantissa_and_exponent();
215 if mantissa != 1.0 {
216 return None;
217 }
218 let (q, r) = exponent.div_mod(i64::exact_from(pow));
219 if r == 0 { Some(q) } else { None }
220 }
221 }
222 };
223}
224apply_to_primitive_floats!(impl_log_base_power_of_2_primitive_float);