use crate::cbig::CBig;
use crate::repr::{combine_parts, exact, reborrow_cache, riemann, CfpResult, Context};
use dashu_base::Approximation::*;
use dashu_base::{BitTest, Sign};
use dashu_float::round::Round;
use dashu_float::{ConstCache, FBig, FpError};
use dashu_int::{IBig, Word};
const EXP_GUARD: usize = 14;
const POWF_GUARD: usize = 22;
impl<R: Round> Context<R> {
pub fn exp<const B: Word>(
&self,
z: &CBig<R, B>,
mut cache: Option<&mut ConstCache>,
) -> CfpResult<R, B> {
if z.is_zero() {
return Ok(exact(FBig::ONE, FBig::ZERO));
}
if z.is_infinite() {
if z.im().is_infinite() {
return Err(FpError::Indeterminate); }
return if z.re().sign() == Sign::Positive {
Ok(riemann(*self))
} else {
Ok(exact(FBig::ZERO, FBig::ZERO))
};
}
let gctx = self.guard(EXP_GUARD);
let p = self.precision();
let ex = gctx.exp(z.re(), reborrow_cache(&mut cache))?.value();
let (sin_y, cos_y) = gctx.sin_cos(z.im(), reborrow_cache(&mut cache));
let cos_y = cos_y?.value();
let sin_y = sin_y?.value();
let re = gctx.mul(ex.repr(), cos_y.repr())?.value().with_precision(p);
let im = gctx.mul(ex.repr(), sin_y.repr())?.value().with_precision(p);
Ok(combine_parts(re, im))
}
pub fn powi<const B: Word>(&self, z: &CBig<R, B>, exp: IBig) -> CfpResult<R, B> {
let (sign, n) = exp.into_parts();
if n.is_zero() {
return Ok(Exact(CBig::ONE));
}
let negative = sign == Sign::Negative;
let bitlen = n.bit_len();
let mut acc = z.clone();
for i in (0..bitlen - 1).rev() {
acc = self.sqr(&acc)?.value();
if n.bit(i) {
acc = self.mul(&acc, z)?.value();
}
}
if negative {
self.inv(&acc)
} else {
Ok(Exact(acc))
}
}
pub fn powf<const B: Word>(
&self,
base: &CBig<R, B>,
w: &CBig<R, B>,
mut cache: Option<&mut ConstCache>,
) -> CfpResult<R, B> {
if w.is_zero() {
return Ok(Exact(CBig::ONE)); }
let gctx = Context::new(self.precision() + POWF_GUARD);
let log_z = gctx.log(base, reborrow_cache(&mut cache))?.value();
let wlogz = gctx.mul(w, &log_z)?.value();
let hi = gctx.exp(&wlogz, reborrow_cache(&mut cache))?.value();
let p = self.precision();
let (re, im) = hi.into_parts();
Ok(combine_parts(re.with_precision(p), im.with_precision(p)))
}
}
impl<R: Round, const B: Word> CBig<R, B> {
#[inline]
pub fn exp(&self) -> Self {
self.context().unwrap_cfp(self.context().exp(self, None))
}
#[inline]
pub fn powi(&self, exp: IBig) -> Self {
self.context().unwrap_cfp(self.context().powi(self, exp))
}
#[inline]
pub fn powf(&self, w: &Self) -> Self {
self.context()
.unwrap_cfp(self.context().powf(self, w, None))
}
}
#[cfg(test)]
mod tests {
use super::*;
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))
}
#[test]
fn exp_zero_is_one() {
assert!(C::ZERO.exp() == C::ONE);
}
#[test]
fn exp_one_is_e() {
let e = C::ONE.exp();
let (re, _im) = e.into_parts();
assert!(re > F::from(2));
assert!(re < F::from(3));
}
#[test]
fn exp_pi_i_is_neg_one() {
use dashu_base::{Abs, AbsOrd};
let pi = F::from_parts(31415926535897932i64.into(), -16)
.with_precision(60)
.value();
let z = CBig::from_parts(F::ZERO, pi);
let (re, im) = z.exp().into_parts();
let re_err = (re + F::ONE).abs();
let tol = F::from_parts(1.into(), -12);
assert!(re_err.abs_cmp(&tol).is_le());
assert!(im.abs_cmp(&tol).is_le());
}
#[test]
fn exp_pos_infinity_is_riemann() {
let inf = CBig::from(F::INFINITY);
let r = inf.exp();
assert!(r.re().is_infinite());
assert!(r.im().is_pos_zero());
}
#[test]
fn powi_zero_is_one() {
assert!(c(3, 4).powi(0.into()) == C::ONE);
}
#[test]
fn powi_one_is_self() {
let z = c(3, 4);
assert!(z.powi(1.into()) == z);
}
#[test]
fn powi_two_is_sqr() {
let z = c(1, 2);
assert!(z.powi(2.into()) == z.sqr());
}
#[test]
fn powi_negative_is_inv() {
let z = c(3, 4);
let r = z.powi((-1).into());
let one = &z * &r;
assert!(one == C::ONE);
}
#[test]
fn powf_zero_exponent_is_one() {
assert!(c(3, 4).powf(&C::ZERO) == C::ONE);
assert!(C::ZERO.powf(&C::ZERO) == C::ONE);
}
#[test]
fn powf_one_exponent_is_self() {
let z = c(2, 1);
assert!(z.powf(&C::ONE) == z);
}
}