#![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;
type Entry = (core::W, core::W);
#[cfg(feature = "std")]
::std::thread_local! {
static TABLE_CACHE: ::core::cell::RefCell<alloc::vec::Vec<(u32, alloc::vec::Vec<Entry>)>> =
const { ::core::cell::RefCell::new(alloc::vec::Vec::new()) };
}
#[cfg(feature = "std")]
fn table_entry(w: u32, j_idx: usize) -> Entry {
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) -> Entry {
compute_table(w)[j_idx]
}
fn compute_table(w: u32) -> alloc::vec::Vec<Entry> {
let mut out = alloc::vec::Vec::with_capacity((M + 1) as usize);
let pi_w = core::pi(w);
let step_denom = core::lit((4 * M) as u128);
out.push((core::zero(), core::one(w)));
for j in 1..=M {
let cj_w = (pi_w * core::lit(j as u128)) / step_denom;
out.push(core::sin_cos_fixed(cj_w, w));
}
out
}
#[derive(Copy, Clone)]
pub(crate) enum Which {
Sin,
Cos,
}
#[inline]
#[must_use]
pub(crate) fn sin_cos_strict<const SCALE: u32>(
raw: Int192,
mode: RoundingMode,
which: Which,
) -> Int192 {
if raw == Int192::ZERO {
return match which {
Which::Sin => Int192::ZERO,
Which::Cos => {
let ten: Int192 = crate::wide_int::wide_cast::<u128, Int192>(10);
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 pi_w = core::pi(w);
let half_pi_w = core::half_pi(w);
let k = core::round_to_nearest_int(core::div_cached(v_w, half_pi_w, pow10_w), w);
let k_half_pi = if k >= 0 {
half_pi_w * core::lit(k as u128)
} else {
-(half_pi_w * core::lit((-k) as u128))
};
let r = v_w - k_half_pi;
let four_m = core::lit((4 * M) as u128);
let j_signed = core::round_to_nearest_int(
core::div_cached(r * four_m, pi_w, pow10_w),
w,
);
let cj_signed_w = if j_signed >= 0 {
(pi_w * core::lit(j_signed as u128)) / four_m
} else {
-((pi_w * core::lit((-j_signed) as u128)) / four_m)
};
let delta = r - cj_signed_w;
let j_abs = j_signed.unsigned_abs() as u32;
debug_assert!(
j_abs <= M,
"sin_cos_strict d57 s44..=56: table index {j_abs} > M={M}"
);
let j_idx = if j_abs > M { M as usize } else { j_abs as usize };
let (sin_cj_abs, cos_cj) = table_entry(w, j_idx);
let sin_cj = if j_signed < 0 { -sin_cj_abs } else { sin_cj_abs };
let delta2 = core::mul_cached(delta, delta, pow10_w);
let sin_delta = {
let mut sum = delta;
let mut term = delta;
let mut k_term: u128 = 1;
loop {
term = core::mul_cached(term, delta2, pow10_w)
/ core::lit((2 * k_term) * (2 * k_term + 1));
if term == core::zero() {
break;
}
if k_term % 2 == 1 {
sum = sum - term;
} else {
sum = sum + term;
}
k_term += 1;
if k_term > 200 {
break;
}
}
sum
};
let cos_delta = {
let mut sum = one_w;
let mut term = one_w;
let mut k_term: u128 = 1;
loop {
term = core::mul_cached(term, delta2, pow10_w)
/ core::lit((2 * k_term - 1) * (2 * k_term));
if term == core::zero() {
break;
}
if k_term % 2 == 1 {
sum = sum - term;
} else {
sum = sum + term;
}
k_term += 1;
if k_term > 200 {
break;
}
}
sum
};
let sin_r = core::mul_cached(sin_cj, cos_delta, pow10_w)
+ core::mul_cached(cos_cj, sin_delta, pow10_w);
let cos_r = core::mul_cached(cos_cj, cos_delta, pow10_w)
- core::mul_cached(sin_cj, sin_delta, pow10_w);
let quadrant = ((k % 4) + 4) % 4;
let (sin_x, cos_x) = match quadrant {
0 => (sin_r, cos_r),
1 => (cos_r, -sin_r),
2 => (-sin_r, -cos_r),
3 => (-cos_r, sin_r),
_ => unreachable!(),
};
let result = match which {
Which::Sin => sin_x,
Which::Cos => cos_x,
};
core::round_to_storage_with(result, w, SCALE, mode)
}
#[inline]
#[must_use]
pub(crate) fn sin_strict<const SCALE: u32>(raw: Int192, mode: RoundingMode) -> Int192 {
sin_cos_strict::<SCALE>(raw, mode, Which::Sin)
}
#[inline]
#[must_use]
pub(crate) fn cos_strict<const SCALE: u32>(raw: Int192, mode: RoundingMode) -> Int192 {
sin_cos_strict::<SCALE>(raw, mode, Which::Cos)
}