Skip to main content

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}