malachite_base/num/arithmetic/
log_base.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::{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
29pub_test! {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
45pub_test! {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        if let Some(next_p) = p.checked_mul(base) {
53            p = next_p;
54        } else {
55            return None;
56        }
57    }
58    if p == x {
59        Some(result)
60    } else {
61        None
62    }
63}}
64
65fn floor_log_base<T: PrimitiveUnsigned>(x: T, base: T) -> u64 {
66    if let Some(log_base) = base.checked_log_base_2() {
67        x.floor_log_base_power_of_2(log_base)
68    } else {
69        floor_log_base_naive(x, base)
70    }
71}
72
73fn ceiling_log_base<T: PrimitiveUnsigned>(x: T, base: T) -> u64 {
74    if let Some(log_base) = base.checked_log_base_2() {
75        x.ceiling_log_base_power_of_2(log_base)
76    } else {
77        ceiling_log_base_naive(x, base)
78    }
79}
80
81fn checked_log_base<T: PrimitiveUnsigned>(x: T, base: T) -> Option<u64> {
82    if let Some(log_base) = base.checked_log_base_2() {
83        x.checked_log_base_power_of_2(log_base)
84    } else {
85        checked_log_base_naive(x, base)
86    }
87}
88
89macro_rules! impl_log_base_unsigned {
90    ($t:ident) => {
91        impl FloorLogBase for $t {
92            type Output = u64;
93
94            /// Returns the floor of the base-$b$ logarithm of a positive integer.
95            ///
96            /// $f(x, b) = \lfloor\log_b x\rfloor$.
97            ///
98            /// # Worst-case complexity
99            /// $T(n) = O(n)$
100            ///
101            /// $M(n) = O(1)$
102            ///
103            /// where $T$ is time, $M$ is additional memory, and $n$ is `self.significant_bits() /
104            /// base.significant_bits()`.
105            ///
106            /// # Panics
107            /// Panics if `self` is 0 or `base` is less than 2.
108            ///
109            /// # Examples
110            /// See [here](super::log_base#floor_log_base).
111            #[inline]
112            fn floor_log_base(self, base: $t) -> u64 {
113                // TODO use ilog once stabilized
114                floor_log_base(self, base)
115            }
116        }
117
118        impl CeilingLogBase for $t {
119            type Output = u64;
120
121            /// Returns the ceiling of the base-$b$ logarithm of a positive integer.
122            ///
123            /// $f(x, b) = \lceil\log_b x\rceil$.
124            ///
125            /// # Worst-case complexity
126            /// $T(n) = O(n)$
127            ///
128            /// $M(n) = O(1)$
129            ///
130            /// where $T$ is time, $M$ is additional memory, and $n$ is `self.significant_bits() /
131            /// base.significant_bits()`.
132            ///
133            /// # Panics
134            /// Panics if `self` is 0 or `base` is less than 2.
135            ///
136            /// # Examples
137            /// See [here](super::log_base#ceiling_log_base).
138            #[inline]
139            fn ceiling_log_base(self, base: $t) -> u64 {
140                ceiling_log_base(self, base)
141            }
142        }
143
144        impl CheckedLogBase for $t {
145            type Output = u64;
146
147            /// Returns the base-$b$ logarithm of a positive integer. If the integer is not a power
148            /// of $b$, `None` is returned.
149            ///
150            /// $$
151            /// f(x, b) = \\begin{cases}
152            ///     \operatorname{Some}(\log_b x) & \text{if} \\quad \log_b x \in \Z, \\\\
153            ///     \operatorname{None} & \textrm{otherwise}.
154            /// \\end{cases}
155            /// $$
156            ///
157            /// # Worst-case complexity
158            /// $T(n) = O(n)$
159            ///
160            /// $M(n) = O(1)$
161            ///
162            /// where $T$ is time, $M$ is additional memory, and $n$ is `self.significant_bits() /
163            /// base.significant_bits()`.
164            ///
165            /// # Panics
166            /// Panics if `self` is 0 or `base` is less than 2.
167            ///
168            /// # Examples
169            /// See [here](super::log_base#checked_log_base).
170            #[inline]
171            fn checked_log_base(self, base: $t) -> Option<u64> {
172                checked_log_base(self, base)
173            }
174        }
175    };
176}
177apply_to_unsigneds!(impl_log_base_unsigned);