#![cfg(any(feature = "d57", feature = "wide"))]
use crate::types::widths::wide_trig_d57 as core;
use crate::support::rounding::RoundingMode;
use crate::wide_int::Int192;
const M: u32 = 512;
#[cfg(feature = "std")]
::std::thread_local! {
static TABLE_CACHE: ::core::cell::RefCell<alloc::vec::Vec<(u32, alloc::vec::Vec<core::W>)>> =
const { ::core::cell::RefCell::new(alloc::vec::Vec::new()) };
}
#[cfg(feature = "std")]
fn table_entry(w: u32, j_idx: usize) -> core::W {
TABLE_CACHE.with(|c| {
{
let cache = c.borrow();
for (cw, tbl) in cache.iter() {
if *cw == w {
return tbl[j_idx];
}
}
}
let tbl = compute_table(w);
let entry = tbl[j_idx];
c.borrow_mut().push((w, tbl));
entry
})
}
#[cfg(not(feature = "std"))]
fn table_entry(w: u32, j_idx: usize) -> core::W {
compute_table(w)[j_idx]
}
fn compute_table(w: u32) -> alloc::vec::Vec<core::W> {
let mut out = alloc::vec::Vec::with_capacity(M as usize);
let l2 = core::ln2(w);
out.push(core::one(w)); for j in 1..M {
let cj_w = (l2 * core::lit(j as u128)) / core::lit(M as u128);
out.push(core::exp_fixed(cj_w, w));
}
out
}
#[inline]
#[must_use]
pub(crate) fn exp_strict<const SCALE: u32>(raw: Int192, mode: RoundingMode) -> Int192 {
if raw == Int192::ZERO {
let ten: Int192 = crate::wide_int::wide_cast::<u128, Int192>(10);
return ten.pow(SCALE);
}
let w = SCALE + core::GUARD;
let v_w = core::to_work(raw);
let one_w = core::one(w);
let pow10_w = one_w;
let l2 = core::ln2(w);
let k = core::round_to_nearest_int(core::div_cached(v_w, l2, pow10_w), w);
let k_l2 = if k >= 0 {
l2 * core::lit(k as u128)
} else {
-(l2 * core::lit((-k) as u128))
};
let s = v_w - k_l2;
let j_signed = core::round_to_nearest_int(
core::div_cached(s * core::lit(M as u128), l2, pow10_w),
w,
);
let cj_signed_w = if j_signed >= 0 {
(l2 * core::lit(j_signed as u128)) / core::lit(M as u128)
} else {
-((l2 * core::lit((-j_signed) as u128)) / core::lit(M as u128))
};
let delta = s - cj_signed_w;
let (j_idx, k_adj) = if j_signed >= 0 {
(j_signed as u32, 0i128)
} else {
((j_signed + M as i128) as u32, -1i128)
};
debug_assert!(j_idx < M, "exp_strict d57 s45..=56: table index out of range");
let mut sum = one_w + delta;
let mut term = delta;
let mut n: u128 = 2;
loop {
term = core::mul_cached(term, delta, pow10_w) / core::lit(n);
if term == core::zero() {
break;
}
sum = sum + term;
n += 1;
if n > 200 {
break;
}
}
let exp_cj = table_entry(w, j_idx as usize);
let exp_s = core::mul_cached(exp_cj, sum, pow10_w);
let k_total = k + k_adj;
let result = if k_total >= 0 {
let shift = k_total as u32;
debug_assert!(
core::bit_length(exp_s) + shift < core::W::BITS,
"exp_strict d57 s45..=56: result overflows the representable range",
);
exp_s << shift
} else {
let neg_k = (-k_total) as u32;
if neg_k as u128 >= core::bit_length(exp_s) as u128 {
core::zero()
} else {
exp_s >> neg_k
}
};
core::round_to_storage_with(result, w, SCALE, mode)
}