malachite_base/num/arithmetic/log_base.rs
1// Copyright © 2026 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::{CeilingLogBase, CheckedLogBase, FloorLogBase};
10use crate::num::basic::unsigneds::PrimitiveUnsigned;
11
12fn floor_log_base_naive<T: PrimitiveUnsigned>(x: T, base: T) -> u64 {
13 assert_ne!(x, T::ZERO);
14 assert!(base > T::ONE);
15 let mut result = 0;
16 let mut p = T::ONE;
17 // loop always executes at least once
18 while p <= x {
19 result += 1;
20 if let Some(next_p) = p.checked_mul(base) {
21 p = next_p;
22 } else {
23 break;
24 }
25 }
26 result - 1
27}
28
29private_test_fn! {ceiling_log_base_naive<T: PrimitiveUnsigned>(x: T, base: T) -> u64 {
30 assert_ne!(x, T::ZERO);
31 assert!(base > T::ONE);
32 let mut result = 0;
33 let mut p = T::ONE;
34 while p < x {
35 result += 1;
36 if let Some(next_p) = p.checked_mul(base) {
37 p = next_p;
38 } else {
39 break;
40 }
41 }
42 result
43}}
44
45private_test_fn! {checked_log_base_naive<T: PrimitiveUnsigned>(x: T, base: T) -> Option<u64> {
46 assert_ne!(x, T::ZERO);
47 assert!(base > T::ONE);
48 let mut result = 0;
49 let mut p = T::ONE;
50 while p < x {
51 result += 1;
52 p = p.checked_mul(base)?;
53 }
54 if p == x {
55 Some(result)
56 } else {
57 None
58 }
59}}
60
61fn floor_log_base<T: PrimitiveUnsigned>(x: T, base: T) -> u64 {
62 if let Some(log_base) = base.checked_log_base_2() {
63 x.floor_log_base_power_of_2(log_base)
64 } else {
65 floor_log_base_naive(x, base)
66 }
67}
68
69fn ceiling_log_base<T: PrimitiveUnsigned>(x: T, base: T) -> u64 {
70 if let Some(log_base) = base.checked_log_base_2() {
71 x.ceiling_log_base_power_of_2(log_base)
72 } else {
73 ceiling_log_base_naive(x, base)
74 }
75}
76
77fn checked_log_base<T: PrimitiveUnsigned>(x: T, base: T) -> Option<u64> {
78 if let Some(log_base) = base.checked_log_base_2() {
79 x.checked_log_base_power_of_2(log_base)
80 } else {
81 checked_log_base_naive(x, base)
82 }
83}
84
85macro_rules! impl_log_base_unsigned {
86 ($t:ident) => {
87 impl FloorLogBase for $t {
88 type Output = u64;
89
90 /// Returns the floor of the base-$b$ logarithm of a positive integer.
91 ///
92 /// $f(x, b) = \lfloor\log_b x\rfloor$.
93 ///
94 /// # Worst-case complexity
95 /// $T(n) = O(n)$
96 ///
97 /// $M(n) = O(1)$
98 ///
99 /// where $T$ is time, $M$ is additional memory, and $n$ is `self.significant_bits() /
100 /// base.significant_bits()`: the loop multiplies by the base once per unit of the
101 /// logarithm, which is $O(n)$; the power-of-2 fast path is $O(1)$.
102 ///
103 /// # Panics
104 /// Panics if `self` is 0 or `base` is less than 2.
105 ///
106 /// # Examples
107 /// See [here](super::log_base#floor_log_base).
108 #[inline]
109 fn floor_log_base(self, base: $t) -> u64 {
110 // TODO use ilog once stabilized
111 floor_log_base(self, base)
112 }
113 }
114
115 impl CeilingLogBase for $t {
116 type Output = u64;
117
118 /// Returns the ceiling of the base-$b$ logarithm of a positive integer.
119 ///
120 /// $f(x, b) = \lceil\log_b x\rceil$.
121 ///
122 /// # Worst-case complexity
123 /// $T(n) = O(n)$
124 ///
125 /// $M(n) = O(1)$
126 ///
127 /// where $T$ is time, $M$ is additional memory, and $n$ is `self.significant_bits() /
128 /// base.significant_bits()`: the loop multiplies by the base once per unit of the
129 /// logarithm, which is $O(n)$; the power-of-2 fast path is $O(1)$.
130 ///
131 /// # Panics
132 /// Panics if `self` is 0 or `base` is less than 2.
133 ///
134 /// # Examples
135 /// See [here](super::log_base#ceiling_log_base).
136 #[inline]
137 fn ceiling_log_base(self, base: $t) -> u64 {
138 ceiling_log_base(self, base)
139 }
140 }
141
142 impl CheckedLogBase for $t {
143 type Output = u64;
144
145 /// Returns the base-$b$ logarithm of a positive integer. If the integer is not a power
146 /// of $b$, `None` is returned.
147 ///
148 /// $$
149 /// f(x, b) = \\begin{cases}
150 /// \operatorname{Some}(\log_b x) & \text{if} \\quad \log_b x \in \Z, \\\\
151 /// \operatorname{None} & \textrm{otherwise}.
152 /// \\end{cases}
153 /// $$
154 ///
155 /// # Worst-case complexity
156 /// $T(n) = O(n)$
157 ///
158 /// $M(n) = O(1)$
159 ///
160 /// where $T$ is time, $M$ is additional memory, and $n$ is `self.significant_bits() /
161 /// base.significant_bits()`: the loop multiplies by the base once per unit of the
162 /// logarithm, which is $O(n)$; the power-of-2 fast path is $O(1)$.
163 ///
164 /// # Panics
165 /// Panics if `self` is 0 or `base` is less than 2.
166 ///
167 /// # Examples
168 /// See [here](super::log_base#checked_log_base).
169 #[inline]
170 fn checked_log_base(self, base: $t) -> Option<u64> {
171 checked_log_base(self, base)
172 }
173 }
174 };
175}
176apply_to_unsigneds!(impl_log_base_unsigned);