use crate::algos::ln::fixed_d38::{STRICT_GUARD, wide_ln2};
use crate::algos::fixed_d38::Fixed;
use crate::support::rounding::RoundingMode;
pub(crate) fn exp_fixed(v_w: Fixed, w: u32) -> Fixed {
let one_w = Fixed { negative: false, mag: Fixed::pow10(w) };
let ln2 = wide_ln2(w);
let k = v_w.div(ln2, w).round_to_nearest_int(w);
let k_ln2 = if k >= 0 {
ln2.mul_u128(k as u128)
} else {
ln2.mul_u128((-k) as u128).neg()
};
let s = v_w.sub(k_ln2);
let p_bits = w.saturating_mul(3).saturating_add(1);
let mut n: u32 = 1;
while (n + 1) * (n + 1) <= p_bits {
n += 1;
}
let s_red = s.shr(n);
let mut sum = one_w.add(s_red);
let mut term = s_red;
let mut i: u128 = 2;
loop {
term = term.mul(s_red, w).div_small(i);
if term.is_zero() {
break;
}
sum = sum.add(term);
i += 1;
if i > 400 {
break;
}
}
for _ in 0..n {
sum = sum.mul(sum, w);
}
if k >= 0 {
let shift = k as u32;
assert!(sum.bit_length() + shift <= 256, "D38::exp: result overflows the representable range");
sum.shl(shift)
} else {
sum.shr((-k) as u32)
}
}
#[inline]
#[must_use]
pub(crate) fn exp_with(raw: i128, scale: u32, working_digits: u32, mode: RoundingMode) -> i128 {
if raw == 0 {
return 10_i128.pow(scale); }
let w = scale + working_digits;
let negative_input = raw < 0;
let v_w = Fixed::from_u128_mag(raw.unsigned_abs(), false)
.mul_u128(10u128.pow(working_digits));
let v_w = if negative_input { v_w.neg() } else { v_w };
exp_fixed(v_w, w)
.round_to_i128_with(w, scale, mode)
.unwrap_or_else(|| crate::support::diagnostics::overflow_panic_with_scale("exp kernel", scale))
}
#[inline]
#[must_use]
pub(crate) fn exp_strict<const SCALE: u32>(raw: i128, mode: RoundingMode) -> i128 {
if raw == 0 {
return 10_i128.pow(SCALE);
}
let w = SCALE + STRICT_GUARD;
let negative_input = raw < 0;
let v_w = Fixed::from_u128_mag(raw.unsigned_abs(), false)
.mul_u128(10u128.pow(STRICT_GUARD));
let v_w = if negative_input { v_w.neg() } else { v_w };
exp_fixed(v_w, w)
.round_to_i128_with(w, SCALE, mode)
.unwrap_or_else(|| crate::support::diagnostics::overflow_panic_with_scale("exp kernel", SCALE))
}
#[inline]
#[must_use]
pub(crate) fn exp2_with(raw: i128, scale: u32, working_digits: u32, mode: RoundingMode) -> i128 {
if raw == 0 {
return 10_i128.pow(scale);
}
let w = scale + working_digits;
let negative_input = raw < 0;
let v_w = Fixed::from_u128_mag(raw.unsigned_abs(), false)
.mul_u128(10u128.pow(working_digits));
let v_w = if negative_input { v_w.neg() } else { v_w };
let arg_w = v_w.mul(wide_ln2(w), w);
exp_fixed(arg_w, w)
.round_to_i128_with(w, scale, mode)
.unwrap_or_else(|| crate::support::diagnostics::overflow_panic_with_scale("D38::exp2", scale))
}
#[inline]
#[must_use]
pub(crate) fn exp2_strict<const SCALE: u32>(raw: i128, mode: RoundingMode) -> i128 {
exp2_with(raw, SCALE, STRICT_GUARD, mode)
}