malachite_base/num/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::num::arithmetic::traits::{CeilingLogBase2, CheckedLogBase2, FloorLogBase2};
10use crate::num::basic::integers::PrimitiveInt;
11use crate::num::basic::unsigneds::PrimitiveUnsigned;
12use crate::num::conversion::traits::SciMantissaAndExponent;
13use crate::num::logic::traits::{LeadingZeros, TrailingZeros};
14
15fn floor_log_base_2<T: PrimitiveUnsigned>(x: T) -> u64 {
16    assert!(x != T::ZERO, "Cannot take the base-2 logarithm of 0.");
17    x.significant_bits() - 1
18}
19
20fn ceiling_log_base_2<T: PrimitiveUnsigned>(x: T) -> u64 {
21    let floor_log_base_2 = x.floor_log_base_2();
22    if x.is_power_of_2() {
23        floor_log_base_2
24    } else {
25        floor_log_base_2 + 1
26    }
27}
28
29fn checked_log_base_2<T: PrimitiveInt>(x: T) -> Option<u64> {
30    assert!(x != T::ZERO, "Cannot take the base-2 logarithm of 0.");
31    let leading_zeros = LeadingZeros::leading_zeros(x);
32    let trailing_zeros = TrailingZeros::trailing_zeros(x);
33    if leading_zeros + trailing_zeros == T::WIDTH - 1 {
34        Some(trailing_zeros)
35    } else {
36        None
37    }
38}
39
40macro_rules! impl_log_base_2_unsigned {
41    ($t:ident) => {
42        impl FloorLogBase2 for $t {
43            type Output = u64;
44
45            /// Returns the floor of the base-2 logarithm of a positive integer.
46            ///
47            /// $f(x) = \lfloor\log_2 x\rfloor$.
48            ///
49            /// # Worst-case complexity
50            /// Constant time and additional memory.
51            ///
52            /// # Panics
53            /// Panics if `self` is 0.
54            ///
55            /// # Examples
56            /// See [here](super::log_base_2#floor_log_base_2).
57            #[inline]
58            fn floor_log_base_2(self) -> u64 {
59                // TODO use ilog2 once stabilized
60                floor_log_base_2(self)
61            }
62        }
63
64        impl CeilingLogBase2 for $t {
65            type Output = u64;
66
67            /// Returns the ceiling of the base-2 logarithm of a positive integer.
68            ///
69            /// $f(x) = \lceil\log_2 x\rceil$.
70            ///
71            /// # Worst-case complexity
72            /// Constant time and additional memory.
73            ///
74            /// # Panics
75            /// Panics if `self` is 0.
76            ///
77            /// # Examples
78            /// See [here](super::log_base_2#ceiling_log_base_2).
79            #[inline]
80            fn ceiling_log_base_2(self) -> u64 {
81                ceiling_log_base_2(self)
82            }
83        }
84
85        impl CheckedLogBase2 for $t {
86            type Output = u64;
87
88            /// Returns the base-2 logarithm of a positive integer. If the integer is not a power of
89            /// 2, `None` is returned.
90            ///
91            /// $$
92            /// f(x) = \\begin{cases}
93            ///     \operatorname{Some}(\log_2 x) & \text{if} \\quad \log_2 x \in \Z, \\\\
94            ///     \operatorname{None} & \textrm{otherwise}.
95            /// \\end{cases}
96            /// $$
97            ///
98            /// # Worst-case complexity
99            /// Constant time and additional memory.
100            ///
101            /// # Panics
102            /// Panics if `self` is 0.
103            ///
104            /// # Examples
105            /// See [here](super::log_base_2#checked_log_base_2).
106            #[inline]
107            fn checked_log_base_2(self) -> Option<u64> {
108                checked_log_base_2(self)
109            }
110        }
111    };
112}
113apply_to_unsigneds!(impl_log_base_2_unsigned);
114
115macro_rules! impl_log_base_2_primitive_float {
116    ($t:ident) => {
117        impl FloorLogBase2 for $t {
118            type Output = i64;
119
120            /// Returns the floor of the base-2 logarithm of a positive float.
121            ///
122            /// $f(x) = \lfloor\log_2 x\rfloor$.
123            ///
124            /// # Worst-case complexity
125            /// Constant time and additional memory.
126            ///
127            /// # Panics
128            /// Panics if `self` is infinite, `NaN`, or less than or equal to zero.
129            ///
130            /// # Examples
131            /// See [here](super::log_base_2#floor_log_base_2).
132            #[inline]
133            fn floor_log_base_2(self) -> i64 {
134                assert!(self > 0.0);
135                self.sci_exponent()
136            }
137        }
138
139        impl CeilingLogBase2 for $t {
140            type Output = i64;
141
142            /// Returns the ceiling of the base-2 logarithm of a positive float.
143            ///
144            /// $f(x) = \lceil\log_2 x\rceil$.
145            ///
146            /// # Worst-case complexity
147            /// Constant time and additional memory.
148            ///
149            /// # Panics
150            /// Panics if `self` is infinite, `NaN`, or less than or equal to zero.
151            ///
152            /// # Examples
153            /// See [here](super::log_base_2#ceiling_log_base_2).
154            #[inline]
155            fn ceiling_log_base_2(self) -> i64 {
156                assert!(self > 0.0);
157                let (mantissa, exponent) = self.sci_mantissa_and_exponent();
158                if mantissa == 1.0 {
159                    exponent
160                } else {
161                    exponent + 1
162                }
163            }
164        }
165
166        impl CheckedLogBase2 for $t {
167            type Output = i64;
168
169            /// Returns the base-2 logarithm of a positive float If the float is not a power of 2,
170            /// `None` is returned.
171            ///
172            /// $$
173            /// f(x) = \\begin{cases}
174            ///     \operatorname{Some}(\log_2 x) & \text{if} \\quad \log_2 x \in \Z, \\\\
175            ///     \operatorname{None} & \textrm{otherwise}.
176            /// \\end{cases}
177            /// $$
178            ///
179            /// # Worst-case complexity
180            /// Constant time and additional memory.
181            ///
182            /// # Panics
183            /// Panics if `self` is infinite, `NaN`, or less than or equal to zero.
184            ///
185            /// # Examples
186            /// See [here](super::log_base_2#checked_log_base_2).
187            #[inline]
188            fn checked_log_base_2(self) -> Option<i64> {
189                assert!(self > 0.0);
190                let (mantissa, exponent) = self.sci_mantissa_and_exponent();
191                if mantissa == 1.0 {
192                    Some(exponent)
193                } else {
194                    None
195                }
196            }
197        }
198    };
199}
200apply_to_primitive_floats!(impl_log_base_2_primitive_float);