use crate::cbig::CBig;
use crate::repr::{combine_parts, exact, reborrow_cache, riemann, CfpResult, Context};
use dashu_float::round::Round;
use dashu_float::{ConstCache, FBig, Repr};
use dashu_int::Word;
const LOG_GUARD: usize = 14;
impl<R: Round> Context<R> {
pub fn log<const B: Word>(
&self,
z: &CBig<R, B>,
mut cache: Option<&mut ConstCache>,
) -> CfpResult<R, B> {
if z.is_zero() {
return Ok(exact(
FBig::from_repr(Repr::neg_infinity(), self.float()),
FBig::from_repr(Repr::zero(), self.float()),
));
}
if z.is_infinite() {
return Ok(riemann(*self)); }
let gctx = self.guard(LOG_GUARD);
let p = self.precision();
let r = gctx.hypot(z.re(), z.im())?.value();
let ln_r = gctx.ln(r.repr(), reborrow_cache(&mut cache))?.value();
let arg = gctx
.atan2(z.im(), z.re(), reborrow_cache(&mut cache))?
.value();
let re = ln_r.with_precision(p);
let im = arg.with_precision(p);
Ok(combine_parts(re, im))
}
}
impl<R: Round, const B: Word> CBig<R, B> {
#[inline]
pub fn ln(&self) -> Self {
self.context().unwrap_cfp(self.context().log(self, None))
}
}
#[cfg(test)]
mod tests {
use super::*;
use dashu_base::{Abs, AbsOrd, Sign};
use dashu_float::round::mode;
type C = CBig<mode::HalfAway, 10>;
type F = FBig<mode::HalfAway, 10>;
fn c(re: i32, im: i32) -> C {
let mk = |v: i32| -> F { F::from(v).with_precision(53).value() };
CBig::from_parts(mk(re), mk(im))
}
fn within(a: &F, b: &F, k: u32) -> bool {
if a == b {
return true;
}
let diff = (a.clone() - b.clone()).abs();
diff.abs_cmp(&(a.ulp() * F::from(k))).is_le()
}
#[test]
fn ln_one_is_zero() {
assert!(C::ONE.ln() == C::ZERO);
}
#[test]
fn ln_exp_roundtrip() {
let z = c(1, 1);
let l = z.exp().ln();
let (zr, zi) = z.into_parts();
let (lr, li) = l.into_parts();
assert!(within(&zr, &lr, 16));
assert!(within(&zi, &li, 16));
}
#[test]
fn ln_zero_is_neg_infinity() {
let l = C::ZERO.ln();
assert!(l.re().is_infinite());
assert_eq!(l.re().sign(), Sign::Negative);
}
}