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