Skip to main content

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