Skip to main content

dashu_float/
log.rs

1use dashu_base::{
2    utils::{next_down, next_up},
3    AbsOrd,
4    Approximation::*,
5    EstimatedLog2, Sign,
6};
7use dashu_int::IBig;
8
9use crate::{
10    error::{assert_finite, assert_limited_precision, FpError, FpResult},
11    fbig::FBig,
12    math::cache::{reborrow_cache, ConstCache},
13    repr::{Context, Repr, Word},
14    round::{Round, Rounded},
15    utils::ceil_usize,
16};
17use core::cmp::Ordering;
18
19impl<const B: Word> EstimatedLog2 for Repr<B> {
20    // currently a Word has at most 64 bits, so log2() < f32::MAX
21    fn log2_bounds(&self) -> (f32, f32) {
22        if self.significand.is_zero() {
23            return (f32::NEG_INFINITY, f32::NEG_INFINITY);
24        }
25
26        // log(s*B^e) = log(s) + e*log(B)
27        let (logs_lb, logs_ub) = self.significand.log2_bounds();
28        let (logb_lb, logb_ub) = if B.is_power_of_two() {
29            let log = B.trailing_zeros() as f32;
30            (log, log)
31        } else {
32            B.log2_bounds()
33        };
34        let e = self.exponent as f32;
35        let (lb, ub) = if self.exponent >= 0 {
36            (logs_lb + e * logb_lb, logs_ub + e * logb_ub)
37        } else {
38            (logs_lb + e * logb_ub, logs_ub + e * logb_lb)
39        };
40        (next_down(lb), next_up(ub))
41    }
42
43    fn log2_est(&self) -> f32 {
44        let logs = self.significand.log2_est();
45        let logb = if B.is_power_of_two() {
46            B.trailing_zeros() as f32
47        } else {
48            B.log2_est()
49        };
50        logs + self.exponent as f32 * logb
51    }
52}
53
54impl<R: Round, const B: Word> EstimatedLog2 for FBig<R, B> {
55    #[inline]
56    fn log2_bounds(&self) -> (f32, f32) {
57        self.repr.log2_bounds()
58    }
59
60    #[inline]
61    fn log2_est(&self) -> f32 {
62        self.repr.log2_est()
63    }
64}
65
66impl<R: Round, const B: Word> FBig<R, B> {
67    /// Calculate the natural logarithm function (`log(x)`) on the float number.
68    ///
69    /// # Examples
70    ///
71    /// ```
72    /// # use core::str::FromStr;
73    /// # use dashu_base::ParseError;
74    /// # use dashu_float::DBig;
75    /// let a = DBig::from_str("1.234")?;
76    /// assert_eq!(a.ln(), DBig::from_str("0.2103")?);
77    /// # Ok::<(), ParseError>(())
78    /// ```
79    #[inline]
80    pub fn ln(&self) -> Self {
81        self.context.unwrap_fp(self.context.ln(&self.repr, None))
82    }
83
84    /// Calculate the natural logarithm function (`log(x+1)`) on the float number
85    ///
86    /// # Examples
87    ///
88    /// ```
89    /// # use core::str::FromStr;
90    /// # use dashu_base::ParseError;
91    /// # use dashu_float::DBig;
92    /// let a = DBig::from_str("0.1234")?;
93    /// assert_eq!(a.ln_1p(), DBig::from_str("0.11636")?);
94    /// # Ok::<(), ParseError>(())
95    /// ```
96    #[inline]
97    pub fn ln_1p(&self) -> Self {
98        self.context.unwrap_fp(self.context.ln_1p(&self.repr, None))
99    }
100
101    /// Calculate the base-2 logarithm function (`log2(x)`) on the float number.
102    ///
103    /// This is evaluated as `ln(x) / ln(2)` at an elevated working precision and rounded
104    /// once, so it is correctly rounded to this number's precision (unlike
105    /// [`log2_bounds`][dashu_base::EstimatedLog2::log2_bounds], which only gives an
106    /// f32-precision magnitude estimate).
107    ///
108    /// # Examples
109    ///
110    /// ```
111    /// # use core::str::FromStr;
112    /// # use dashu_base::ParseError;
113    /// # use dashu_float::DBig;
114    /// let a = DBig::from_str("8.0")?;
115    /// assert_eq!(a.log2(), DBig::from_str("3")?);
116    /// # Ok::<(), ParseError>(())
117    /// ```
118    #[inline]
119    pub fn log2(&self) -> Self {
120        self.context.unwrap_fp(self.context.log2(&self.repr, None))
121    }
122}
123
124impl<R: Round> Context<R> {
125    /// Calculate log(2)
126    ///
127    /// The precision of the output will be larger than self.precision
128    #[inline]
129    fn ln2<const B: Word>(&self, cache: Option<&mut ConstCache>) -> FBig<R, B> {
130        if let Some(c) = cache {
131            return c.ln2::<B, R>(self.precision);
132        }
133        // log(2) = 4L(6) + 2L(99)
134        // see formula (24) from Gourdon, Xavier, and Pascal Sebah.
135        // "The Logarithmic Constant: Log 2." (2004)
136        4 * self.iacoth(6.into()) + 2 * self.iacoth(99.into())
137    }
138
139    /// Calculate log(10)
140    ///
141    /// The precision of the output will be larger than self.precision
142    #[inline]
143    fn ln10<const B: Word>(&self, cache: Option<&mut ConstCache>) -> FBig<R, B> {
144        if let Some(c) = cache {
145            return c.ln10::<B, R>(self.precision);
146        }
147        // log(10) = log(2) + log(5) = 3log(2) + 2L(9)
148        3 * self.ln2(None) + 2 * self.iacoth(9.into())
149    }
150
151    /// Calculate log(B), for internal use only
152    ///
153    /// The precision of the output will be larger than self.precision
154    #[inline]
155    pub(crate) fn ln_base<const B: Word>(&self, cache: Option<&mut ConstCache>) -> FBig<R, B> {
156        if let Some(c) = cache {
157            return c.ln_base::<B, R>(self.precision);
158        }
159        match B {
160            2 => self.ln2(None),
161            10 => self.ln10(None),
162            i if i.is_power_of_two() => self.ln2(None) * i.trailing_zeros(),
163            _ => self.unwrap_fp(self.ln(&Repr::new(Repr::<B>::BASE.into(), 0), None)),
164        }
165    }
166
167    /// Calculate L(n) = acoth(n) = atanh(1/n) = 1/2 log((n+1)/(n-1)), given by the
168    /// series
169    ///
170    /// ```text
171    ///                1     n + 1              1
172    ///   atanh(1/n) = — log(—————) = Σ   ——————————————————
173    ///                2     n - 1   i≥0 n^(2i+1) · (2i+1)
174    /// ```
175    ///
176    /// This method is intended to be used in logarithm calculation,
177    /// so the precision of the output will be larger than desired precision.
178    ///
179    /// Evaluated by binary splitting (see [`iacoth_bs`][crate::math::cache::iacoth_bs]):
180    /// the exact integer tree state `(P, Q, T)` over `[1, N)` satisfies
181    /// `L(n) = (Q + T)/(n·Q)`, with `Q` kept at O(p) digits by the ratio-form
182    /// term recurrence.
183    fn iacoth<const B: Word>(&self, n: IBig) -> FBig<R, B> {
184        let n: u32 = (&n).try_into().expect("iacoth argument must fit in u32");
185
186        // number of series terms until r_k < B^{-p}:  (2k+1)·log_B(n) > p.
187        // The count is generously over-provisioned, so a truncating cast stands in
188        // for a ceiling.
189        let log_b_n = n.log2_est() / B.log2_est();
190        let num_terms = (self.precision as f32 / (2.0 * log_b_n)) as usize + 10;
191
192        let (_p, q, t) = crate::math::cache::iacoth_bs(n, 1, num_terms + 1);
193
194        // L(n) = (Q + T) / (n·Q). Extra guard digits absorb the division's rounding
195        // (the binary-splitting state is exact, so only this single round loses anything).
196        let guard_digits = ceil_usize(self.precision.log2_est() / B.log2_est());
197        let work_context = Self::new(self.precision + guard_digits + 2);
198
199        let num = work_context.convert_int::<B>(q.as_ibig() + &t).value();
200        let denom = work_context.convert_int::<B>(IBig::from(n) * &q).value();
201        num / denom
202    }
203
204    /// Calculate the natural logarithm function (`log(x)`) on the float number under this context.
205    ///
206    /// # Examples
207    ///
208    /// ```
209    /// # use core::str::FromStr;
210    /// # use dashu_base::ParseError;
211    /// # use dashu_float::DBig;
212    /// use dashu_base::Approximation::*;
213    /// use dashu_float::{Context, round::{mode::HalfAway, Rounding::*}};
214    ///
215    /// let context = Context::<HalfAway>::new(2);
216    /// let a = DBig::from_str("1.234")?;
217    /// assert_eq!(context.ln(&a.repr(), None), Ok(Inexact(DBig::from_str("0.21")?, NoOp)));
218    /// # Ok::<(), ParseError>(())
219    /// ```
220    #[inline]
221    pub fn ln<const B: Word>(
222        &self,
223        x: &Repr<B>,
224        cache: Option<&mut ConstCache>,
225    ) -> FpResult<FBig<R, B>> {
226        if x.is_infinite() {
227            return Err(FpError::InfiniteInput);
228        }
229        if x.significand.is_zero() {
230            // ln(±0) = -inf (a value, not an error)
231            return Ok(Exact(FBig::new(Repr::neg_infinity(), *self)));
232        }
233        if x.sign() == Sign::Negative {
234            return Err(FpError::OutOfDomain);
235        }
236        Ok(self.ln_internal(x, false, cache))
237    }
238
239    /// Calculate the base-2 logarithm function (`log2(x)`) on the float number under this
240    /// context.
241    ///
242    /// This is evaluated as `ln(x) / ln(2)` at an elevated working precision and rounded
243    /// once, so it is correctly rounded to this context's precision (unlike
244    /// [`log2_bounds`][dashu_base::EstimatedLog2::log2_bounds], which only gives an
245    /// f32-precision magnitude estimate).
246    ///
247    /// # Examples
248    ///
249    /// ```
250    /// # use core::str::FromStr;
251    /// # use dashu_base::ParseError;
252    /// # use dashu_float::DBig;
253    /// use dashu_base::Approximation::*;
254    /// use dashu_float::{Context, round::mode::HalfEven};
255    ///
256    /// let context = Context::<HalfEven>::new(8);
257    /// let a = DBig::from_str("10")?;
258    /// // log2(10) ≈ 3.3219281
259    /// assert_eq!(context.log2(&a.repr(), None).unwrap().value(), DBig::from_str("3.3219281")?);
260    /// # Ok::<(), ParseError>(())
261    /// ```
262    #[inline]
263    pub fn log2<const B: Word>(
264        &self,
265        x: &Repr<B>,
266        cache: Option<&mut ConstCache>,
267    ) -> FpResult<FBig<R, B>> {
268        if x.is_infinite() {
269            return Err(FpError::InfiniteInput);
270        }
271        if x.significand.is_zero() {
272            // log2(±0) = -inf (a value, not an error)
273            return Ok(Exact(FBig::new(Repr::neg_infinity(), *self)));
274        }
275        if x.sign() == Sign::Negative {
276            return Err(FpError::OutOfDomain);
277        }
278        Ok(self.log2_internal(x, cache))
279    }
280
281    /// Calculate the natural logarithm function (`log(x+1)`) on the float number under this context.
282    ///
283    /// # Examples
284    ///
285    /// ```
286    /// # use core::str::FromStr;
287    /// # use dashu_base::ParseError;
288    /// # use dashu_float::DBig;
289    /// use dashu_base::Approximation::*;
290    /// use dashu_float::{Context, round::{mode::HalfAway, Rounding::*}};
291    ///
292    /// let context = Context::<HalfAway>::new(2);
293    /// let a = DBig::from_str("0.1234")?;
294    /// assert_eq!(context.ln_1p(&a.repr(), None), Ok(Inexact(DBig::from_str("0.12")?, AddOne)));
295    /// # Ok::<(), ParseError>(())
296    /// ```
297    #[inline]
298    pub fn ln_1p<const B: Word>(
299        &self,
300        x: &Repr<B>,
301        cache: Option<&mut ConstCache>,
302    ) -> FpResult<FBig<R, B>> {
303        if x.is_infinite() {
304            return Err(FpError::InfiniteInput);
305        }
306        // Domain of ln_1p is x > -1. x == -1 gives -inf; x < -1 is out of domain.
307        if x.sign() == Sign::Negative && !x.significand.is_zero() {
308            match FBig::<R, B>::new(x.clone(), *self).abs_cmp(&FBig::ONE) {
309                Ordering::Greater => return Err(FpError::OutOfDomain), // x < -1
310                Ordering::Equal => return Ok(Exact(FBig::new(Repr::neg_infinity(), *self))),
311                _ => {}
312            }
313        }
314        Ok(self.ln_internal(x, true, cache))
315    }
316
317    fn ln_internal<const B: Word>(
318        &self,
319        x: &Repr<B>,
320        one_plus: bool,
321        mut cache: Option<&mut ConstCache>,
322    ) -> Rounded<FBig<R, B>> {
323        assert_finite(x);
324
325        // Exact special cases first: they need no rounding, so a precision-0 (unlimited)
326        // value such as `FBig::ONE` or the one from `try_from(0.0)` must still resolve
327        // ln/ln_1p exactly rather than tripping the limited-precision assertion below.
328        if !one_plus && x.is_one() {
329            return Exact(FBig::ZERO); // ln(1) = +0
330        }
331        if one_plus && x.significand.is_zero() {
332            // ln_1p(±0) = ±0
333            let zero = if x.is_neg_zero() {
334                FBig::new(Repr::neg_zero(), *self)
335            } else {
336                FBig::ZERO
337            };
338            return Exact(zero);
339        }
340
341        assert_limited_precision(self.precision);
342
343        // A simple algorithm:
344        // - let log(x) = log(x/2^s) + slog2 where s = floor(log2(x))
345        // - such that x*2^s is close to but larger than 1 (and x*2^s < 2)
346        let guard_digits = ceil_usize(self.precision.log2_est() / B.log2_est()) + 2;
347        let mut work_precision = self.precision + guard_digits + one_plus as usize;
348        let context = Context::<R>::new(work_precision);
349        let x = FBig::new(context.repr_round_ref(x).value(), context);
350
351        // When one_plus is true and |x| < 1/B, the input is fed into the Maclaurin without scaling
352        let no_scaling = one_plus && x.log2_est() < -B.log2_est();
353
354        let (s, mut x_scaled) = if no_scaling {
355            (0, x)
356        } else {
357            let x = if one_plus { x + FBig::ONE } else { x };
358
359            let log2 = x.log2_bounds().0;
360            let s = log2 as isize - (log2 < 0.) as isize; // floor(log2(x))
361
362            let x_scaled = if B == 2 {
363                x >> s
364            } else if s > 0 {
365                x / (IBig::ONE << s as usize)
366            } else {
367                x * (IBig::ONE << (-s) as usize)
368            };
369            debug_assert!(x_scaled >= FBig::<R, B>::ONE);
370            (s, x_scaled)
371        };
372
373        if s < 0 || x_scaled.repr.sign() == Sign::Negative {
374            // when s or x_scaled is negative, the final addition is actually a subtraction,
375            // therefore we need to double the precision to get the correct result
376            work_precision += self.precision;
377            x_scaled.context.precision = work_precision;
378        };
379        let work_context = Context::new(work_precision);
380
381        // after the number is scaled to nearly one, use Maclaurin series on log(x) = 2atanh(z):
382        // let z = (x-1)/(x+1) < 1, log(x) = 2atanh(z) = 2Σ(z²ⁱ⁺¹/(2i+1)) for i = 1,3,5,...
383        // similar to iacoth, the required iterations stop at i = -p/2log_B(z), and we need log_B(i) guard bits
384        let z = if no_scaling {
385            let d = &x_scaled + (FBig::ONE + FBig::ONE);
386            x_scaled / d
387        } else {
388            (&x_scaled - FBig::ONE) / (x_scaled + FBig::ONE)
389        };
390        let z2 = z.sqr();
391        let mut pow = z.clone();
392        let mut sum = z;
393
394        let mut k: usize = 3;
395        loop {
396            pow *= &z2;
397
398            let increase = &pow / work_context.convert_int::<B>(k.into()).value();
399            if increase.abs_cmp(&sum.sub_ulp()).is_le() {
400                break;
401            }
402
403            sum += increase;
404            k += 2;
405        }
406
407        // compose the logarithm of the original number
408        let result: FBig<R, B> = if no_scaling {
409            2 * sum
410        } else {
411            2 * sum + s * work_context.ln2::<B>(reborrow_cache(&mut cache))
412        };
413        result.with_precision(self.precision)
414    }
415
416    /// `log2(x) = ln(x) / ln(2)`, correctly rounded to this context's precision.
417    ///
418    /// Both `ln(x)` and `ln(2)` are evaluated at a working precision above the target and
419    /// the quotient is rounded once. The result magnitude `|log2(x)| ≈ |floor(log2 x)|`
420    /// grows with the operand, which tracks the division's error amplification, so the
421    /// per-ulp error is ~`B^(target - work)`: a few guard digits certify the final round
422    /// across the whole magnitude range (no manual scaling needed).
423    fn log2_internal<const B: Word>(
424        &self,
425        x: &Repr<B>,
426        mut cache: Option<&mut ConstCache>,
427    ) -> Rounded<FBig<R, B>> {
428        assert_finite(x);
429        if x.is_one() {
430            return Exact(FBig::ZERO); // log2(1) = +0 (exact; also covers unlimited precision)
431        }
432
433        let guard = ceil_usize(self.precision.log2_est() / B.log2_est()) + 3;
434        let work = Context::<R>::new(self.precision + guard);
435        let ln_x = work
436            .ln_internal(x, false, reborrow_cache(&mut cache))
437            .value();
438        let ln2 = work.ln2::<B>(reborrow_cache(&mut cache));
439        (ln_x / ln2).with_precision(self.precision)
440    }
441}
442
443#[cfg(test)]
444mod tests {
445    use super::*;
446    use crate::round::mode;
447
448    #[test]
449    fn test_ln_zero_is_neg_infinity() {
450        let ctx = Context::<mode::HalfEven>::new(53);
451        let r = ctx.ln::<2>(&Repr::<2>::zero(), None).unwrap().value();
452        assert!(r.repr().is_infinite());
453        assert_eq!(r.repr().sign(), Sign::Negative);
454    }
455
456    #[test]
457    fn test_log2_domain() {
458        let ctx = Context::<mode::HalfEven>::new(53);
459        // log2(±0) = -inf (a value, not an error)
460        let r = ctx.log2::<2>(&Repr::<2>::zero(), None).unwrap().value();
461        assert!(r.repr().is_infinite());
462        assert_eq!(r.repr().sign(), Sign::Negative);
463        // log2(negative) is out of domain
464        assert_eq!(ctx.log2::<2>(&Repr::new(IBig::from(-1), 0), None), Err(FpError::OutOfDomain));
465    }
466
467    #[test]
468    fn test_log2_powers_of_two() {
469        // log2(2^k) = k, including log2(1) = 0. The only magnitude estimate available
470        // (log2_bounds) is f32-precision, so deriving directed log2 from it gives a small
471        // *negative* value for log2(1) and is loose by many ULPs elsewhere; the
472        // correctly-rounded log2 returns the exact integer value for every power of two
473        // (log2(1) is tagged Exact via the shortcut; other powers are value-exact).
474        let ctx = Context::<mode::HalfEven>::new(53);
475        for k in [0usize, 1, 2, 3, 10, 50, 100, 200, 1000] {
476            let x = Repr::new(IBig::from(1) << k, 0); // 2^k
477            let r = ctx.log2::<2>(&x, None).unwrap();
478            let expected = ctx.convert_int::<2>(IBig::from(k)).value();
479            assert_eq!(r.value(), expected, "log2(2^{k}) = {k}");
480        }
481        // log2(1) = 0 is certified Exact via the special-case shortcut.
482        assert!(matches!(ctx.log2::<2>(&Repr::new(IBig::from(1), 0), None).unwrap(), Exact(_)));
483    }
484
485    #[test]
486    fn test_log2_correctly_rounded() {
487        // log2(x) must equal the correctly-rounded ln(x)/ln(2) oracle (computed at much
488        // higher precision then rounded down) across magnitude ranges — tiny, normal, and
489        // huge inputs, in both base 2 and base 10.
490        let p = 30;
491        let ctx = Context::<mode::HalfEven>::new(p);
492        let hi = Context::<mode::HalfEven>::new(200);
493
494        let cases_base2 = [
495            Repr::new(IBig::from(3), 0),
496            Repr::new(IBig::from(10), 0),
497            Repr::new(IBig::from(1), -24), // near the fuzzer's 2^-24 region
498            Repr::new(IBig::from(1) << 50, 0), // huge
499            Repr::new(IBig::from(1), -50), // tiny
500        ];
501        for x in cases_base2 {
502            let got = ctx.log2::<2>(&x, None).unwrap().value();
503            let want = (hi.ln::<2>(&x, None).unwrap().value() / hi.ln2::<2>(None))
504                .with_precision(p)
505                .value();
506            assert_eq!(got, want, "base-2 log2 mismatch for x={x:?}");
507        }
508
509        // base 10: log2(x) = ln(x)/ln(2) with a decimal significand
510        let x = Repr::new(IBig::from(123456), 0);
511        let got = ctx.log2::<10>(&x, None).unwrap().value();
512        let want = (hi.ln::<10>(&x, None).unwrap().value() / hi.ln2::<10>(None))
513            .with_precision(p)
514            .value();
515        assert_eq!(got, want, "base-10 log2 mismatch");
516    }
517
518    #[test]
519    fn test_log2_directed_bounds() {
520        // Down rounds toward -inf, Up toward +inf, so the correctly-rounded (HalfEven)
521        // value must lie within [Down, Up].
522        use crate::round::mode::{Down, Up};
523        let p = 20;
524        let mid = Context::<mode::HalfEven>::new(p);
525        for x in [Repr::new(IBig::from(10), 0), Repr::new(IBig::from(3), 13)] {
526            let down = Context::<Down>::new(p).log2::<2>(&x, None).unwrap().value();
527            let up = Context::<Up>::new(p).log2::<2>(&x, None).unwrap().value();
528            let half = mid.log2::<2>(&x, None).unwrap().value();
529            assert!(down <= half, "down <= half failed for x={x:?}: {down:?} vs {half:?}");
530            assert!(half <= up, "half <= up failed for x={x:?}: {half:?} vs {up:?}");
531            assert!(down <= up, "down <= up failed for x={x:?}");
532        }
533    }
534
535    #[test]
536    fn test_iacoth() {
537        let context = Context::<mode::Zero>::new(10);
538        let binary_6 = context.iacoth::<2>(6.into()).with_precision(10).value();
539        assert_eq!(binary_6.repr.significand, IBig::from(689));
540        let decimal_6 = context.iacoth::<10>(6.into()).with_precision(10).value();
541        assert_eq!(decimal_6.repr.significand, IBig::from(1682361183));
542
543        let context = Context::<mode::Zero>::new(40);
544        let decimal_6 = context.iacoth::<10>(6.into()).with_precision(40).value();
545        assert_eq!(
546            decimal_6.repr.significand,
547            IBig::from_str_radix("1682361183106064652522967051084960450557", 10).unwrap()
548        );
549
550        let context = Context::<mode::Zero>::new(201);
551        let binary_6 = context.iacoth::<2>(6.into()).with_precision(201).value();
552        assert_eq!(
553            binary_6.repr.significand,
554            IBig::from_str_radix(
555                "2162760151454160450909229890833066944953539957685348083415205",
556                10
557            )
558            .unwrap()
559        );
560    }
561
562    #[test]
563    fn test_ln2_ln10() {
564        let context = Context::<mode::Zero>::new(45);
565        let decimal_ln2 = context.ln2::<10>(None).with_precision(45).value();
566        assert_eq!(
567            decimal_ln2.repr.significand,
568            IBig::from_str_radix("693147180559945309417232121458176568075500134", 10).unwrap()
569        );
570        let decimal_ln10 = context.ln10::<10>(None).with_precision(45).value();
571        assert_eq!(
572            decimal_ln10.repr.significand,
573            IBig::from_str_radix("230258509299404568401799145468436420760110148", 10).unwrap()
574        );
575
576        let context = Context::<mode::Zero>::new(180);
577        let binary_ln2 = context.ln2::<2>(None).with_precision(180).value();
578        assert_eq!(
579            binary_ln2.repr.significand,
580            IBig::from_str_radix("1062244963371879310175186301324412638028404515790072203", 10)
581                .unwrap()
582        );
583        let binary_ln10 = context.ln10::<2>(None).with_precision(180).value();
584        assert_eq!(
585            binary_ln10.repr.significand,
586            IBig::from_str_radix("882175346869410758689845931257775553286341791676474847", 10)
587                .unwrap()
588        );
589    }
590}