1use crate::cbig::CBig;
4use crate::repr::{combine_parts, exact, reborrow_cache, riemann, CfpResult, Context};
5use dashu_float::round::Round;
6use dashu_float::{ConstCache, FBig, Repr};
7use dashu_int::Word;
8
9const LOG_GUARD: usize = 14;
11
12impl<R: Round> Context<R> {
13 pub fn log<const B: Word>(
19 &self,
20 z: &CBig<R, B>,
21 mut cache: Option<&mut ConstCache>,
22 ) -> CfpResult<R, B> {
23 if z.is_zero() {
24 return Ok(exact(
26 FBig::from_repr(Repr::neg_infinity(), self.float()),
27 FBig::from_repr(Repr::zero(), self.float()),
28 ));
29 }
30 if z.is_infinite() {
31 return Ok(riemann(*self)); }
33
34 let gctx = self.guard(LOG_GUARD);
35 let p = self.precision();
36 let r = gctx.hypot(z.re(), z.im())?.value();
38 let ln_r = gctx.ln(r.repr(), reborrow_cache(&mut cache))?.value();
39 let arg = gctx
41 .atan2(z.im(), z.re(), reborrow_cache(&mut cache))?
42 .value();
43 let re = ln_r.with_precision(p);
44 let im = arg.with_precision(p);
45 Ok(combine_parts(re, im))
46 }
47}
48
49impl<R: Round, const B: Word> CBig<R, B> {
50 #[inline]
56 pub fn ln(&self) -> Self {
57 self.context().unwrap_cfp(self.context().log(self, None))
58 }
59}
60
61#[cfg(test)]
62mod tests {
63 use super::*;
64 use dashu_base::{Abs, AbsOrd, Sign};
65 use dashu_float::round::mode;
66
67 type C = CBig<mode::HalfAway, 10>;
68 type F = FBig<mode::HalfAway, 10>;
69
70 fn c(re: i32, im: i32) -> C {
71 let mk = |v: i32| -> F { F::from(v).with_precision(53).value() };
72 CBig::from_parts(mk(re), mk(im))
73 }
74
75 fn within(a: &F, b: &F, k: u32) -> bool {
76 if a == b {
77 return true;
78 }
79 let diff = (a.clone() - b.clone()).abs();
80 diff.abs_cmp(&(a.ulp() * F::from(k))).is_le()
81 }
82
83 #[test]
84 fn ln_one_is_zero() {
85 assert!(C::ONE.ln() == C::ZERO);
86 }
87
88 #[test]
89 fn ln_exp_roundtrip() {
90 let z = c(1, 1);
92 let l = z.exp().ln();
93 let (zr, zi) = z.into_parts();
94 let (lr, li) = l.into_parts();
95 assert!(within(&zr, &lr, 16));
96 assert!(within(&zi, &li, 16));
97 }
98
99 #[test]
100 fn ln_zero_is_neg_infinity() {
101 let l = C::ZERO.ln();
102 assert!(l.re().is_infinite());
103 assert_eq!(l.re().sign(), Sign::Negative);
104 }
105}