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);