use crate::bounded::{NormalizedLnArg, OpenUnitInterval};
use crate::error::{Error, Result};
use crate::ops::hyperbolic::atanh_open;
use crate::traits::CordicNumber;
#[must_use]
#[cfg_attr(feature = "verify-no-panic", no_panic::no_panic)]
pub fn exp<T: CordicNumber>(x: T) -> T {
let zero = T::zero();
let one = T::one();
let ln2 = T::ln_2();
if x == zero {
return one;
}
#[allow(clippy::cast_possible_wrap, reason = "total_bits bounded by type size")]
let max_shift = (T::total_bits() - 1) as i32;
let scale = x.div(ln2).to_i32();
if scale > max_shift {
return T::max_value();
}
if scale < -max_shift {
return zero;
}
let r = x.saturating_sub(T::from_num(scale).saturating_mul(ln2));
let mut p = one;
if T::frac_bits() >= 24 {
p = one.saturating_add(r.div(T::from_num(12)).saturating_mul(p));
p = one.saturating_add(r.div(T::from_num(11)).saturating_mul(p));
p = one.saturating_add(r.div(T::from_num(10)).saturating_mul(p));
p = one.saturating_add(r.div(T::from_num(9)).saturating_mul(p));
p = one.saturating_add(r.div(T::from_num(8)).saturating_mul(p));
}
p = one.saturating_add(r.div(T::from_num(7)).saturating_mul(p));
p = one.saturating_add(r.div(T::from_num(6)).saturating_mul(p));
p = one.saturating_add(r.div(T::from_num(5)).saturating_mul(p));
p = one.saturating_add(r.div(T::from_num(4)).saturating_mul(p));
p = one.saturating_add(r.div(T::from_num(3)).saturating_mul(p));
p = one.saturating_add(r.div(T::from_num(2)).saturating_mul(p));
let exp_r = one.saturating_add(r.saturating_mul(p));
#[allow(clippy::cast_sign_loss, reason = "scale >= 0 checked before cast")]
match scale.cmp(&0) {
core::cmp::Ordering::Greater => {
let shift = scale as u32;
let headroom = T::max_value() >> shift;
if exp_r > headroom {
T::max_value()
} else {
exp_r << shift
}
}
core::cmp::Ordering::Less => exp_r >> ((-scale) as u32),
core::cmp::Ordering::Equal => exp_r,
}
}
#[must_use = "returns the natural logarithm result which should be handled"]
#[cfg_attr(feature = "verify-no-panic", no_panic::no_panic)]
pub fn ln<T: CordicNumber>(x: T) -> Result<T> {
let zero = T::zero();
let one = T::one();
let two = T::two();
if x <= zero {
return Err(Error::domain("ln", "positive value"));
}
if x == one {
return Ok(zero);
}
let ln2 = T::ln_2();
let mut normalized = x;
let mut k_ln2 = zero;
let half = T::half();
let mut i = 0;
while normalized > two && i < 128 {
normalized = normalized >> 1;
k_ln2 = k_ln2.saturating_add(ln2);
i += 1;
}
i = 0;
while normalized < half && i < 128 {
normalized = normalized.saturating_add(normalized);
k_ln2 = k_ln2.saturating_sub(ln2);
i += 1;
}
let norm = NormalizedLnArg::from_normalized(normalized);
let arg = OpenUnitInterval::from_normalized_ln_arg(norm);
let atanh_val = atanh_open(arg);
let ln_normalized = atanh_val.saturating_add(atanh_val);
Ok(ln_normalized.saturating_add(k_ln2))
}
#[must_use = "returns the base-2 logarithm result which should be handled"]
#[cfg_attr(feature = "verify-no-panic", no_panic::no_panic)]
pub fn log2<T: CordicNumber>(x: T) -> Result<T> {
let ln_x = ln(x)?;
let ln_2 = T::ln_2();
Ok(ln_x.div(ln_2))
}
#[must_use = "returns the base-10 logarithm result which should be handled"]
#[cfg_attr(feature = "verify-no-panic", no_panic::no_panic)]
pub fn log10<T: CordicNumber>(x: T) -> Result<T> {
let ln_x = ln(x)?;
let ln_10 = T::ln_10();
Ok(ln_x.div(ln_10))
}
#[must_use]
#[cfg_attr(feature = "verify-no-panic", no_panic::no_panic)]
pub fn pow2<T: CordicNumber>(x: T) -> T {
let ln_2 = T::ln_2();
exp(x.saturating_mul(ln_2))
}