malachite_nz/natural/arithmetic/log_base.rs
1// Copyright © 2026 Mikhail Hogrefe
2//
3// Uses code adopted from the FLINT Library.
4//
5// Copyright © 2011 Sebastian Pancratz
6//
7// Copyright © 2011 Fredrik Johansson
8//
9// This file is part of Malachite.
10//
11// Malachite is free software: you can redistribute it and/or modify it under the terms of the GNU
12// Lesser General Public License (LGPL) as published by the Free Software Foundation; either version
13// 3 of the License, or (at your option) any later version. See <https://www.gnu.org/licenses/>.
14
15use crate::natural::Natural;
16use core::cmp::Ordering::*;
17use malachite_base::num::arithmetic::traits::{
18 CeilingLogBase, CeilingLogBasePowerOf2, CheckedLogBase, CheckedLogBase2,
19 CheckedLogBasePowerOf2, DivExactAssign, FloorLogBase, FloorLogBasePowerOf2, Pow,
20};
21use malachite_base::num::basic::traits::One;
22use malachite_base::num::conversion::traits::RoundingFrom;
23use malachite_base::num::conversion::traits::SciMantissaAndExponent;
24use malachite_base::rounding_modes::RoundingMode::*;
25
26impl Natural {
27 /// Calculates the approximate natural logarithm of a nonzero [`Natural`].
28 ///
29 /// $f(x) = (1+\varepsilon)(\ln x)$, where $|\varepsilon| < 2^{-52}.$
30 ///
31 /// # Worst-case complexity
32 /// $T(n) = O(n)$
33 ///
34 /// $M(n) = O(1)$
35 ///
36 /// where $T$ is time, $M$ is additional memory, and $n$ is `self.significant_bits()`.
37 ///
38 /// # Examples
39 /// ```
40 /// use malachite_base::num::arithmetic::traits::Pow;
41 /// use malachite_base::num::float::NiceFloat;
42 /// use malachite_nz::natural::Natural;
43 ///
44 /// assert_eq!(
45 /// NiceFloat(Natural::from(10u32).approx_ln()),
46 /// NiceFloat(2.3025850929940455)
47 /// );
48 /// assert_eq!(
49 /// NiceFloat(Natural::from(10u32).pow(10000).approx_ln()),
50 /// NiceFloat(23025.850929940454)
51 /// );
52 /// ```
53 ///
54 /// This is equivalent to `fmpz_dlog` from `fmpz/dlog.c`, FLINT 2.7.1.
55 pub fn approx_ln(&self) -> f64 {
56 assert_ne!(*self, 0);
57 let (mantissa, exponent): (f64, u64) = self.sci_mantissa_and_exponent();
58 libm::log(mantissa) + (exponent as f64) * core::f64::consts::LN_2
59 }
60}
61
62// # Worst-case complexity
63// $T(n) = O(n \log n \log\log n)$
64//
65// $M(n) = O(n \log n)$
66//
67// where $T$ is time, $M$ is additional memory, and $n$ is `x.significant_bits()`.
68fn log_base_helper(x: &Natural, base: &Natural) -> (u64, bool) {
69 assert_ne!(*x, 0);
70 assert!(*base > 1);
71 if *x == 1 {
72 return (0, true);
73 } else if x < base {
74 return (0, false);
75 }
76 let mut log = u64::rounding_from(x.approx_ln() / base.approx_ln(), Floor).0;
77 let mut power = base.pow(log);
78 match power.cmp(x) {
79 Equal => (log, true),
80 Less => loop {
81 power *= base;
82 match power.cmp(x) {
83 Equal => {
84 return (log + 1, true);
85 }
86 Less => {
87 log += 1;
88 }
89 Greater => {
90 return (log, false);
91 }
92 }
93 },
94 Greater => loop {
95 power.div_exact_assign(base);
96 match power.cmp(x) {
97 Equal => {
98 return (log - 1, true);
99 }
100 Less => {
101 return (log - 1, false);
102 }
103 Greater => {
104 log -= 1;
105 }
106 }
107 },
108 }
109}
110
111// # Worst-case complexity
112// $T(n) = O(n \log n \log\log n)$
113//
114// $M(n) = O(n \log n)$
115//
116// where $T$ is time, $M$ is additional memory, and $n$ is `x.significant_bits()`.
117//
118// Also returns base^p and p, where base^p is close to x.
119pub(crate) fn log_base_helper_with_pow(x: &Natural, base: &Natural) -> (u64, bool, Natural, u64) {
120 assert_ne!(*x, 0);
121 assert!(*base > 1);
122 if *x == 1 {
123 return (0, true, Natural::ONE, 0);
124 } else if x < base {
125 return (0, false, Natural::ONE, 0);
126 }
127 let mut log = (x.approx_ln() / base.approx_ln()) as u64;
128 let mut power = base.pow(log);
129 match power.cmp(x) {
130 Equal => (log, true, power, log),
131 Less => loop {
132 power *= base;
133 match power.cmp(x) {
134 Equal => {
135 log += 1;
136 return (log, true, power, log);
137 }
138 Less => {
139 log += 1;
140 }
141 Greater => {
142 return (log, false, power, log + 1);
143 }
144 }
145 },
146 Greater => loop {
147 power.div_exact_assign(base);
148 match power.cmp(x) {
149 Equal => {
150 log -= 1;
151 return (log, true, power, log);
152 }
153 Less => {
154 log -= 1;
155 return (log, false, power, log);
156 }
157 Greater => {
158 log -= 1;
159 }
160 }
161 },
162 }
163}
164
165impl FloorLogBase<&Natural> for &Natural {
166 type Output = u64;
167
168 /// Returns the floor of the base-$b$ logarithm of a positive [`Natural`].
169 ///
170 /// $f(x, b) = \lfloor\log_b x\rfloor$.
171 ///
172 /// # Worst-case complexity
173 /// $T(n) = O(n \log n \log\log n)$
174 ///
175 /// $M(n) = O(n \log n)$
176 ///
177 /// where $T$ is time, $M$ is additional memory, and $n$ is `x.significant_bits()`.
178 ///
179 /// # Panics
180 /// Panics if `self` is 0 or `base` is less than 2.
181 ///
182 /// # Examples
183 /// ```
184 /// use malachite_base::num::arithmetic::traits::FloorLogBase;
185 /// use malachite_nz::natural::Natural;
186 ///
187 /// assert_eq!(Natural::from(80u32).floor_log_base(&Natural::from(3u32)), 3);
188 /// assert_eq!(Natural::from(81u32).floor_log_base(&Natural::from(3u32)), 4);
189 /// assert_eq!(Natural::from(82u32).floor_log_base(&Natural::from(3u32)), 4);
190 /// assert_eq!(
191 /// Natural::from(4294967296u64).floor_log_base(&Natural::from(10u32)),
192 /// 9
193 /// );
194 /// ```
195 ///
196 /// This is equivalent to `fmpz_flog` from `fmpz/flog.c`, FLINT 2.7.1.
197 fn floor_log_base(self, base: &Natural) -> u64 {
198 if let Some(log_base) = base.checked_log_base_2() {
199 return self.floor_log_base_power_of_2(log_base);
200 }
201 log_base_helper(self, base).0
202 }
203}
204
205impl CeilingLogBase<&Natural> for &Natural {
206 type Output = u64;
207
208 /// Returns the ceiling of the base-$b$ logarithm of a positive [`Natural`].
209 ///
210 /// $f(x, b) = \lceil\log_b x\rceil$.
211 ///
212 /// # Worst-case complexity
213 /// $T(n) = O(n \log n \log\log n)$
214 ///
215 /// $M(n) = O(n \log n)$
216 ///
217 /// where $T$ is time, $M$ is additional memory, and $n$ is `x.significant_bits()`.
218 ///
219 /// # Panics
220 /// Panics if `self` is 0 or `base` is less than 2.
221 ///
222 /// # Examples
223 /// ```
224 /// use malachite_base::num::arithmetic::traits::CeilingLogBase;
225 /// use malachite_nz::natural::Natural;
226 ///
227 /// assert_eq!(
228 /// Natural::from(80u32).ceiling_log_base(&Natural::from(3u32)),
229 /// 4
230 /// );
231 /// assert_eq!(
232 /// Natural::from(81u32).ceiling_log_base(&Natural::from(3u32)),
233 /// 4
234 /// );
235 /// assert_eq!(
236 /// Natural::from(82u32).ceiling_log_base(&Natural::from(3u32)),
237 /// 5
238 /// );
239 /// assert_eq!(
240 /// Natural::from(4294967296u64).ceiling_log_base(&Natural::from(10u32)),
241 /// 10
242 /// );
243 /// ```
244 ///
245 /// This is equivalent to `fmpz_clog` from `fmpz/clog.c`, FLINT 2.7.1.
246 fn ceiling_log_base(self, base: &Natural) -> u64 {
247 if let Some(log_base) = base.checked_log_base_2() {
248 return self.ceiling_log_base_power_of_2(log_base);
249 }
250 let (log, exact) = log_base_helper(self, base);
251 if exact { log } else { log + 1 }
252 }
253}
254
255impl CheckedLogBase<&Natural> for &Natural {
256 type Output = u64;
257
258 /// Returns the base-$b$ logarithm of a positive [`Natural`]. If the [`Natural`] is not a power
259 /// of $b$, then `None` is returned.
260 ///
261 /// $$
262 /// f(x, b) = \\begin{cases}
263 /// \operatorname{Some}(\log_b x) & \text{if} \\quad \log_b x \in \Z, \\\\
264 /// \operatorname{None} & \textrm{otherwise}.
265 /// \\end{cases}
266 /// $$
267 ///
268 /// # Worst-case complexity
269 /// $T(n) = O(n \log n \log\log n)$
270 ///
271 /// $M(n) = O(n \log n)$
272 ///
273 /// where $T$ is time, $M$ is additional memory, and $n$ is `x.significant_bits()`.
274 ///
275 /// # Panics
276 /// Panics if `self` is 0 or `base` is less than 2.
277 ///
278 /// # Examples
279 /// ```
280 /// use malachite_base::num::arithmetic::traits::CheckedLogBase;
281 /// use malachite_nz::natural::Natural;
282 ///
283 /// assert_eq!(
284 /// Natural::from(80u32).checked_log_base(&Natural::from(3u32)),
285 /// None
286 /// );
287 /// assert_eq!(
288 /// Natural::from(81u32).checked_log_base(&Natural::from(3u32)),
289 /// Some(4)
290 /// );
291 /// assert_eq!(
292 /// Natural::from(82u32).checked_log_base(&Natural::from(3u32)),
293 /// None
294 /// );
295 /// assert_eq!(
296 /// Natural::from(4294967296u64).checked_log_base(&Natural::from(10u32)),
297 /// None
298 /// );
299 /// ```
300 fn checked_log_base(self, base: &Natural) -> Option<u64> {
301 if let Some(log_base) = base.checked_log_base_2() {
302 return self.checked_log_base_power_of_2(log_base);
303 }
304 let (log, exact) = log_base_helper(self, base);
305 if exact { Some(log) } else { None }
306 }
307}