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]
fn exp2_exact_pin(raw: i128, scale: u32) -> Option<i128> {
let one_s = 10i128.checked_pow(scale)?;
if raw % one_s != 0 {
return None;
}
let k = raw / one_s;
if k == 0 {
return Some(one_s);
}
let kk = k.unsigned_abs();
if k > 0 {
let mut v: i128 = one_s;
for _ in 0..kk {
v = v.checked_mul(2)?;
}
Some(v)
} else {
if kk > scale as u128 {
return None;
}
let mut v = 10i128.checked_pow(scale - kk as u32)?;
for _ in 0..kk {
v = v.checked_mul(5)?;
}
Some(v)
}
}
#[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);
}
if let Some(pinned) = exp2_exact_pin(raw, scale) {
return pinned;
}
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)
}