use crate::algos::ln::ln_series_2limb::STRICT_GUARD;
use crate::algos::support::fixed::Fixed;
use crate::algos::support::wide_trig_core::WideTrigCore;
use crate::algos::trig::trig_series_2limb::{atan2_kernel, atan_fixed, to_fixed, wide_half_pi};
use crate::int::types::Int;
use crate::support::rounding::RoundingMode;
#[inline]
fn asin_work<C: WideTrigCore, const SCALE: u32>(v: C::W, w: u32) -> C::W {
let one_w = C::one(w);
let abs_v = if v < C::zero() { C::zero() - v } else { v };
let half_w = one_w >> 1;
if abs_v == one_w {
let hp = C::half_pi::<SCALE>(w);
if v < C::zero() { C::zero() - hp } else { hp }
} else if abs_v <= half_w {
let denom = C::sqrt_fixed(one_w - C::mul(v, v, w), w);
C::atan_fixed::<SCALE>(C::div(v, denom, w), w)
} else {
let inner = (one_w - abs_v) >> 1;
let inner_sqrt = C::sqrt_fixed(inner, w);
let inner_denom = C::sqrt_fixed(one_w - C::mul(inner_sqrt, inner_sqrt, w), w);
let inner_asin = C::atan_fixed::<SCALE>(C::div(inner_sqrt, inner_denom, w), w);
let result_abs = C::half_pi::<SCALE>(w) - inner_asin - inner_asin;
if v < C::zero() { C::zero() - result_abs } else { result_abs }
}
}
#[inline]
#[must_use]
pub(crate) fn asin_schoolbook<C: WideTrigCore, const SCALE: u32>(
raw: C::Storage,
mode: RoundingMode,
) -> C::Storage
where
<C::W as crate::int::types::traits::BigInt>::Scratch:
crate::int::types::compute_limbs::ComputeLimbs,
{
use crate::algos::support::wide_trig_core::{
round_to_storage_directed_decided_g, tiny_x_deep_directed_adjust, tiny_x_linear_directed,
};
let w0 = SCALE + C::GUARD;
let one_w0 = C::one(w0);
let v0 = C::to_work(raw);
let abs_v0 = if v0 < C::zero() { C::zero() - v0 } else { v0 };
if abs_v0 > one_w0 {
panic!("schoolbook asin: argument out of domain [-1, 1]");
}
if let Some(v) = tiny_x_linear_directed::<C::Storage, SCALE>(raw, mode, true) {
return v;
}
let (r, decided) = round_to_storage_directed_decided_g::<C::Storage, C::W>(
C::GUARD,
SCALE,
mode,
C::storage_max(),
C::storage_min(),
|guard| asin_work::<C, SCALE>(C::to_work_scaled(raw, guard), SCALE + guard),
);
tiny_x_deep_directed_adjust::<C::Storage, SCALE>(
r,
decided,
raw,
mode,
false,
<C::W as crate::int::types::traits::BigInt>::BITS,
)
}
#[inline]
#[must_use]
pub(crate) fn acos_schoolbook<C: WideTrigCore, const SCALE: u32>(
raw: C::Storage,
mode: RoundingMode,
) -> C::Storage {
let w0 = SCALE + C::GUARD;
let one_w0 = C::one(w0);
let v0 = C::to_work(raw);
let abs_v0 = if v0 < C::zero() { C::zero() - v0 } else { v0 };
if abs_v0 > one_w0 {
panic!("schoolbook acos: argument out of domain [-1, 1]");
}
C::round_to_storage_directed(C::GUARD, SCALE, mode, &mut |guard| {
let w = SCALE + guard;
C::half_pi::<SCALE>(w) - asin_work::<C, SCALE>(C::to_work_scaled(raw, guard), w)
})
}
#[inline]
#[must_use]
pub(crate) fn atan2_schoolbook<C: WideTrigCore, const SCALE: u32>(
y_raw: C::Storage,
x_raw: C::Storage,
mode: RoundingMode,
) -> C::Storage
where
<C::W as crate::int::types::traits::BigInt>::Scratch:
crate::int::types::compute_limbs::ComputeLimbs,
{
use crate::algos::support::wide_trig_core::{
round_to_storage_directed_decided_g, tiny_x_deep_directed_adjust, tiny_x_linear_directed,
};
let zero = C::storage_zero();
let abs_y = if y_raw < zero { zero - y_raw } else { y_raw };
if !crate::support::rounding::is_nearest_mode(mode) && x_raw > zero && abs_y < x_raw {
let probe = |m: RoundingMode| {
round_to_storage_directed_decided_g::<C::Storage, C::W>(
C::GUARD,
SCALE,
m,
C::storage_max(),
C::storage_min(),
|guard| atan2_work::<C, SCALE>(y_raw, x_raw, guard),
)
.0
};
let r_f = probe(RoundingMode::Floor);
let r_c = probe(RoundingMode::Ceiling);
if r_f == r_c {
let g = r_f;
if let Some(v) = tiny_x_linear_directed::<C::Storage, SCALE>(g, mode, false) {
return v;
}
let stepped = tiny_x_deep_directed_adjust::<C::Storage, SCALE>(
g,
false,
g,
mode,
true,
<C::W as crate::int::types::traits::BigInt>::BITS,
);
if stepped != g {
return stepped;
}
return g;
}
return match mode {
RoundingMode::Ceiling => r_c,
RoundingMode::Floor => r_f,
RoundingMode::Trunc => {
if y_raw >= zero {
r_f
} else {
r_c
}
}
_ => unreachable!("directed mode"),
};
}
C::round_to_storage_directed(C::GUARD, SCALE, mode, &mut |guard| {
atan2_work::<C, SCALE>(y_raw, x_raw, guard)
})
}
#[inline]
fn atan2_work<C: WideTrigCore, const SCALE: u32>(
y_raw: C::Storage,
x_raw: C::Storage,
guard: u32,
) -> C::W {
let w = SCALE + guard;
let z = C::storage_zero();
if x_raw == z {
return if y_raw > z {
C::half_pi::<SCALE>(w)
} else if y_raw < z {
C::zero() - C::half_pi::<SCALE>(w)
} else {
C::zero()
};
}
let y = C::to_work_scaled(y_raw, guard);
let x = C::to_work_scaled(x_raw, guard);
let zero_w = C::zero();
let abs_y = if y < zero_w { zero_w - y } else { y };
let abs_x = if x < zero_w { zero_w - x } else { x };
let base = if abs_x >= abs_y {
C::atan_fixed::<SCALE>(C::div(y, x, w), w)
} else {
let inv = C::atan_fixed::<SCALE>(C::div(x, y, w), w);
let hp = C::half_pi::<SCALE>(w);
let same_sign = (y < zero_w) == (x < zero_w);
if same_sign { hp - inv } else { (zero_w - hp) - inv }
};
if x_raw > z {
base
} else if y_raw >= z {
base + C::pi::<SCALE>(w)
} else {
base - C::pi::<SCALE>(w)
}
}
#[cfg(feature = "_wide-support")]
#[inline]
fn asin_work_g<Wk: crate::int::types::traits::BigInt>(v: Wk, w: u32, pi_w: Wk) -> Wk
where
Wk::Scratch: crate::int::types::compute_limbs::ComputeLimbs,
{
use crate::algos::exp::exp_generic as eg;
let one_w = eg::one::<Wk>(w);
let zero = eg::zero::<Wk>();
let abs_v = if v < zero { zero - v } else { v };
let half_w = one_w >> 1;
if abs_v == one_w {
let hp = pi_w >> 1;
if v < zero { zero - hp } else { hp }
} else if abs_v <= half_w {
let denom = eg::sqrt_fixed::<Wk>(one_w - eg::mul::<Wk>(v, v, w), w);
crate::algos::trig::trig_generic::atan_fixed::<Wk>(eg::div::<Wk>(v, denom, w), w, pi_w)
} else {
let inner = (one_w - abs_v) >> 1;
let inner_sqrt = eg::sqrt_fixed::<Wk>(inner, w);
let inner_denom =
eg::sqrt_fixed::<Wk>(one_w - eg::mul::<Wk>(inner_sqrt, inner_sqrt, w), w);
let inner_asin = crate::algos::trig::trig_generic::atan_fixed::<Wk>(
eg::div::<Wk>(inner_sqrt, inner_denom, w),
w,
pi_w,
);
let result_abs = (pi_w >> 1) - inner_asin - inner_asin;
if v < zero { zero - result_abs } else { result_abs }
}
}
#[cfg(feature = "_wide-support")]
#[inline]
#[must_use]
pub(crate) fn asin_schoolbook_g<C: WideTrigCore, Wk: crate::int::types::traits::BigInt, const SCALE: u32>(
raw: C::Storage,
mode: RoundingMode,
) -> C::Storage
where
Wk::Scratch: crate::int::types::compute_limbs::ComputeLimbs,
<C::W as crate::int::types::traits::BigInt>::Scratch:
crate::int::types::compute_limbs::ComputeLimbs,
{
use crate::algos::exp::exp_generic as eg;
use crate::algos::support::wide_trig_core::{
pi_at_rung, round_to_storage_directed_widening_decided_g, tiny_x_deep_directed_adjust,
tiny_x_linear_directed, to_work_scaled_g,
};
let w0 = SCALE + C::GUARD;
let one_w0 = eg::one::<Wk>(w0);
let zero = eg::zero::<Wk>();
let v0 = to_work_scaled_g::<C::Storage, Wk>(raw, C::GUARD);
let abs_v0 = if v0 < zero { zero - v0 } else { v0 };
if abs_v0 > one_w0 {
panic!("schoolbook asin: argument out of domain [-1, 1]");
}
if let Some(v) = tiny_x_linear_directed::<C::Storage, SCALE>(raw, mode, true) {
return v;
}
let (r, decided) = round_to_storage_directed_widening_decided_g::<C::Storage, Wk, C::W>(
C::GUARD,
SCALE,
mode,
C::storage_max(),
C::storage_min(),
|guard| {
let w = SCALE + guard;
asin_work_g::<Wk>(
to_work_scaled_g::<C::Storage, Wk>(raw, guard),
w,
pi_at_rung::<Wk>(w, w0),
)
},
|guard| asin_work::<C, SCALE>(C::to_work_scaled(raw, guard), SCALE + guard),
);
tiny_x_deep_directed_adjust::<C::Storage, SCALE>(
r,
decided,
raw,
mode,
false,
<C::W as crate::int::types::traits::BigInt>::BITS,
)
}
#[cfg(feature = "_wide-support")]
#[inline]
#[must_use]
pub(crate) fn acos_schoolbook_g<C: WideTrigCore, Wk: crate::int::types::traits::BigInt, const SCALE: u32>(
raw: C::Storage,
mode: RoundingMode,
) -> C::Storage
where
Wk::Scratch: crate::int::types::compute_limbs::ComputeLimbs,
<C::W as crate::int::types::traits::BigInt>::Scratch:
crate::int::types::compute_limbs::ComputeLimbs,
{
use crate::algos::exp::exp_generic as eg;
use crate::algos::support::wide_trig_core::{
pi_at_rung, round_to_storage_directed_widening_g, to_work_scaled_g,
};
let w0 = SCALE + C::GUARD;
let one_w0 = eg::one::<Wk>(w0);
let zero = eg::zero::<Wk>();
let v0 = to_work_scaled_g::<C::Storage, Wk>(raw, C::GUARD);
let abs_v0 = if v0 < zero { zero - v0 } else { v0 };
if abs_v0 > one_w0 {
panic!("schoolbook acos: argument out of domain [-1, 1]");
}
round_to_storage_directed_widening_g::<C::Storage, Wk, C::W>(
C::GUARD,
SCALE,
mode,
C::storage_max(),
C::storage_min(),
|guard| {
let w = SCALE + guard;
let pi_w = pi_at_rung::<Wk>(w, w0);
(pi_w >> 1)
- asin_work_g::<Wk>(to_work_scaled_g::<C::Storage, Wk>(raw, guard), w, pi_w)
},
|guard| {
let w = SCALE + guard;
C::half_pi::<SCALE>(w) - asin_work::<C, SCALE>(C::to_work_scaled(raw, guard), w)
},
)
}
#[cfg(feature = "_wide-support")]
#[inline]
#[must_use]
pub(crate) fn atan2_schoolbook_g<C: WideTrigCore, Wk: crate::int::types::traits::BigInt, const SCALE: u32>(
y_raw: C::Storage,
x_raw: C::Storage,
mode: RoundingMode,
) -> C::Storage
where
Wk::Scratch: crate::int::types::compute_limbs::ComputeLimbs,
<C::W as crate::int::types::traits::BigInt>::Scratch:
crate::int::types::compute_limbs::ComputeLimbs,
{
use crate::algos::support::wide_trig_core::{
round_to_storage_directed_widening_g, tiny_x_deep_directed_adjust, tiny_x_linear_directed,
};
let w0 = SCALE + C::GUARD;
let zero = C::storage_zero();
let abs_y = if y_raw < zero { zero - y_raw } else { y_raw };
let walk = |m: RoundingMode| {
round_to_storage_directed_widening_g::<C::Storage, Wk, C::W>(
C::GUARD,
SCALE,
m,
C::storage_max(),
C::storage_min(),
|guard| atan2_work_g::<C, Wk, SCALE>(y_raw, x_raw, guard, w0),
|guard| atan2_work::<C, SCALE>(y_raw, x_raw, guard),
)
};
if !crate::support::rounding::is_nearest_mode(mode) && x_raw > zero && abs_y < x_raw {
let r_f = walk(RoundingMode::Floor);
let r_c = walk(RoundingMode::Ceiling);
if r_f == r_c {
let g = r_f;
if let Some(v) = tiny_x_linear_directed::<C::Storage, SCALE>(g, mode, false) {
return v;
}
let stepped = tiny_x_deep_directed_adjust::<C::Storage, SCALE>(
g,
false,
g,
mode,
true,
<C::W as crate::int::types::traits::BigInt>::BITS,
);
if stepped != g {
return stepped;
}
return g;
}
return match mode {
RoundingMode::Ceiling => r_c,
RoundingMode::Floor => r_f,
RoundingMode::Trunc => {
if y_raw >= zero {
r_f
} else {
r_c
}
}
_ => unreachable!("directed mode"),
};
}
walk(mode)
}
#[cfg(feature = "_wide-support")]
#[inline]
fn atan2_work_g<C: WideTrigCore, Wk: crate::int::types::traits::BigInt, const SCALE: u32>(
y_raw: C::Storage,
x_raw: C::Storage,
guard: u32,
base_w0: u32,
) -> Wk
where
Wk::Scratch: crate::int::types::compute_limbs::ComputeLimbs,
{
use crate::algos::exp::exp_generic as eg;
use crate::algos::support::wide_trig_core::{pi_at_rung, to_work_scaled_g};
use crate::algos::trig::trig_generic::atan_fixed;
let w = SCALE + guard;
let z = C::storage_zero();
let pi_w = pi_at_rung::<Wk>(w, base_w0);
if x_raw == z {
return if y_raw > z {
pi_w >> 1
} else if y_raw < z {
eg::zero::<Wk>() - (pi_w >> 1)
} else {
eg::zero::<Wk>()
};
}
let y = to_work_scaled_g::<C::Storage, Wk>(y_raw, guard);
let x = to_work_scaled_g::<C::Storage, Wk>(x_raw, guard);
let zero_w = eg::zero::<Wk>();
let abs_y = if y < zero_w { zero_w - y } else { y };
let abs_x = if x < zero_w { zero_w - x } else { x };
let base = if abs_x >= abs_y {
atan_fixed::<Wk>(eg::div::<Wk>(y, x, w), w, pi_w)
} else {
let inv = atan_fixed::<Wk>(eg::div::<Wk>(x, y, w), w, pi_w);
let hp = pi_w >> 1;
let same_sign = (y < zero_w) == (x < zero_w);
if same_sign { hp - inv } else { (zero_w - hp) - inv }
};
if x_raw > z {
base
} else if y_raw >= z {
base + pi_w
} else {
base - pi_w
}
}
#[inline]
fn asin_work_narrow(v: Fixed, w: u32) -> Fixed {
let one_w = Fixed { negative: false, mag: Fixed::pow10(w) };
let abs_v = Fixed { negative: false, mag: v.mag };
if abs_v == one_w {
let hp = wide_half_pi(w);
return if v.negative { hp.neg() } else { hp };
}
let half_w = one_w.halve();
if !abs_v.ge_mag(half_w) {
let denom = one_w.sub(v.mul(v, w)).sqrt(w);
atan_fixed(v.div(denom, w), w)
} else {
let inner = one_w.sub(abs_v).halve();
let inner_sqrt = inner.sqrt(w);
let inner_denom = one_w.sub(inner_sqrt.mul(inner_sqrt, w)).sqrt(w);
let inner_asin = atan_fixed(inner_sqrt.div(inner_denom, w), w);
let result_abs = wide_half_pi(w).sub(inner_asin).sub(inner_asin);
if v.negative { result_abs.neg() } else { result_abs }
}
}
#[inline]
#[must_use]
fn asin_schoolbook_raw<const SCALE: u32>(raw: i128, mode: RoundingMode) -> i128 {
if raw == 0 {
return 0;
}
let w = SCALE + STRICT_GUARD;
let one_w = Fixed { negative: false, mag: Fixed::pow10(w) };
let v = to_fixed(raw);
let abs_v = Fixed { negative: false, mag: v.mag };
assert!(
!(abs_v.ge_mag(one_w) && abs_v != one_w),
"asin: argument out of domain [-1, 1]"
);
match asin_work_narrow(v, w).round_to_i128_clear_of_tie(w, SCALE, mode) {
Some(v) => v.unwrap_or_else(|| {
crate::support::diagnostics::overflow_panic_with_scale("asin", SCALE)
}),
None => crate::algos::support::narrow_ziv::walk(STRICT_GUARD, SCALE, mode, |g| {
crate::algos::trig::trig_series_2limb::asin_ziv(raw, SCALE, g)
}),
}
}
#[inline]
#[must_use]
fn acos_schoolbook_raw<const SCALE: u32>(raw: i128, mode: RoundingMode) -> i128 {
use crate::types::consts::DecimalConstants;
if raw == 0 && crate::support::rounding::is_nearest_mode(mode) {
return <crate::D<Int<2>, SCALE> as DecimalConstants>::half_pi().0.as_i128();
}
let one_bits: i128 = 10_i128.pow(SCALE);
if raw == one_bits {
return 0;
}
if raw == -one_bits && crate::support::rounding::is_nearest_mode(mode) {
return <crate::D<Int<2>, SCALE> as DecimalConstants>::pi().0.as_i128();
}
let w = SCALE + STRICT_GUARD;
let one_w = Fixed { negative: false, mag: Fixed::pow10(w) };
let v = to_fixed(raw);
let abs_v = Fixed { negative: false, mag: v.mag };
assert!(
!(abs_v.ge_mag(one_w) && abs_v != one_w),
"acos: argument out of domain [-1, 1]"
);
match wide_half_pi(w)
.sub(asin_work_narrow(v, w))
.round_to_i128_clear_of_tie(w, SCALE, mode)
{
Some(v) => v.unwrap_or_else(|| {
crate::support::diagnostics::overflow_panic_with_scale("acos", SCALE)
}),
None => crate::algos::support::narrow_ziv::walk(STRICT_GUARD, SCALE, mode, |g| {
crate::algos::trig::trig_series_2limb::acos_ziv(raw, SCALE, g)
}),
}
}
#[inline]
#[must_use]
fn atan2_schoolbook_raw<const SCALE: u32>(y_raw: i128, x_raw: i128, mode: RoundingMode) -> i128 {
let w = SCALE + STRICT_GUARD;
match atan2_kernel(to_fixed(y_raw), to_fixed(x_raw), y_raw, w)
.round_to_i128_clear_of_tie(w, SCALE, mode)
{
Some(v) => v.unwrap_or_else(|| {
crate::support::diagnostics::overflow_panic_with_scale("atan2", SCALE)
}),
None => crate::algos::support::narrow_ziv::walk(STRICT_GUARD, SCALE, mode, |g| {
crate::algos::trig::trig_series_2limb::atan2_ziv(y_raw, x_raw, SCALE, g)
}),
}
}
#[inline]
#[must_use]
pub(crate) fn asin_schoolbook_narrow<const SCALE: u32>(raw: Int<2>, mode: RoundingMode) -> Int<2> {
Int::<2>::from_i128(asin_schoolbook_raw::<SCALE>(raw.as_i128(), mode))
}
#[inline]
#[must_use]
pub(crate) fn acos_schoolbook_narrow<const SCALE: u32>(raw: Int<2>, mode: RoundingMode) -> Int<2> {
Int::<2>::from_i128(acos_schoolbook_raw::<SCALE>(raw.as_i128(), mode))
}
#[inline]
#[must_use]
pub(crate) fn atan2_schoolbook_narrow<const SCALE: u32>(
y_raw: Int<2>,
x_raw: Int<2>,
mode: RoundingMode,
) -> Int<2> {
Int::<2>::from_i128(atan2_schoolbook_raw::<SCALE>(y_raw.as_i128(), x_raw.as_i128(), mode))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::D;
const MODES: [RoundingMode; 6] = [
RoundingMode::HalfToEven,
RoundingMode::HalfAwayFromZero,
RoundingMode::HalfTowardZero,
RoundingMode::Trunc,
RoundingMode::Floor,
RoundingMode::Ceiling,
];
const S38: u32 = 12;
fn d38(raw: i128) -> D<Int<2>, S38> {
D(Int::<2>::from_i128(raw))
}
const ASIN_INPUTS: [i128; 11] = [
0,
250_000_000_000,
500_000_000_000,
600_000_000_000,
900_000_000_000,
1_000_000_000_000,
-250_000_000_000,
-500_000_000_000,
-600_000_000_000,
-900_000_000_000,
-1_000_000_000_000,
];
#[test]
fn asin_schoolbook_narrow_matches_routed_kernel() {
for &raw in &ASIN_INPUTS {
for &mode in &MODES {
assert_eq!(
asin_schoolbook_narrow::<S38>(d38(raw).0, mode),
d38(raw).asin_strict_with(mode).0,
"asin schoolbook != routed at raw={raw} mode={mode:?}"
);
}
}
}
#[test]
fn acos_schoolbook_narrow_matches_routed_kernel() {
let one_bits: i128 = 10_i128.pow(S38);
for &raw in &ASIN_INPUTS {
if raw == 0 || raw == one_bits || raw == -one_bits {
continue;
}
for &mode in &MODES {
assert_eq!(
acos_schoolbook_narrow::<S38>(d38(raw).0, mode),
d38(raw).acos_strict_with(mode).0,
"acos schoolbook != routed at raw={raw} mode={mode:?}"
);
}
}
}
#[test]
fn acos_schoolbook_narrow_endpoints_are_correctly_rounded() {
use crate::types::consts::DecimalConstants;
let one_bits: i128 = 10_i128.pow(S38);
let half_pi = <D<Int<2>, S38> as DecimalConstants>::half_pi().0;
let pi = <D<Int<2>, S38> as DecimalConstants>::pi().0;
for mode in [RoundingMode::HalfToEven, RoundingMode::HalfAwayFromZero] {
assert_eq!(acos_schoolbook_narrow::<S38>(d38(0).0, mode), half_pi);
assert_eq!(acos_schoolbook_narrow::<S38>(d38(-one_bits).0, mode), pi);
}
for &mode in &MODES {
assert_eq!(acos_schoolbook_narrow::<S38>(d38(one_bits).0, mode), Int::<2>::from_i128(0));
assert_eq!(
acos_schoolbook_narrow::<S38>(d38(0).0, mode),
d38(0).acos_strict_with(mode).0,
"acos(0) schoolbook != routed at mode={mode:?}"
);
assert_eq!(
acos_schoolbook_narrow::<S38>(d38(-one_bits).0, mode),
d38(-one_bits).acos_strict_with(mode).0,
"acos(-1) schoolbook != routed at mode={mode:?}"
);
}
}
#[test]
fn atan2_schoolbook_narrow_matches_routed_kernel() {
const PTS: [(i128, i128); 9] = [
(1_000_000_000_000, 1_000_000_000_000),
(1_000_000_000_000, -1_000_000_000_000),
(-1_000_000_000_000, 1_000_000_000_000),
(-1_000_000_000_000, -1_000_000_000_000),
(1_000_000_000_000, 0),
(-1_000_000_000_000, 0),
(0, 1_000_000_000_000),
(500_000_000_000, 2_000_000_000_000),
(2_000_000_000_000, 500_000_000_000),
];
for &(y, x) in &PTS {
for &mode in &MODES {
assert_eq!(
atan2_schoolbook_narrow::<S38>(d38(y).0, d38(x).0, mode),
d38(y).atan2_strict_with(d38(x), mode).0,
"atan2 schoolbook != routed at y={y} x={x} mode={mode:?}"
);
}
}
}
#[cfg(feature = "d462")]
mod near_half_tail_d462 {
use super::*;
use crate::int::types::Int;
#[test]
fn asin_3e60_d462_s180_rounds_above_the_half() {
type Core = crate::types::widths::wide_trig_d462::Core;
let raw = Int::<24>::from_i128(3) * Int::<24>::from_i128(10).pow(120);
let expect_nearest = raw + Int::<24>::from_i128(5); let expect_floor = raw + Int::<24>::from_i128(4);
assert_eq!(
asin_schoolbook::<Core, 180>(raw, RoundingMode::HalfToEven),
expect_nearest,
"tier asin HalfToEven"
);
assert_eq!(
asin_schoolbook::<Core, 180>(raw, RoundingMode::Floor),
expect_floor,
"tier asin Floor"
);
assert_eq!(
asin_schoolbook::<Core, 180>(raw, RoundingMode::Ceiling),
expect_nearest,
"tier asin Ceiling"
);
let x = crate::D::<Int<24>, 180>(raw);
assert_eq!(
x.asin_strict_with(RoundingMode::HalfToEven).0,
expect_nearest,
"public asin HalfToEven"
);
for mode in MODES {
assert_eq!(
x.asin_strict_with(mode).0,
asin_schoolbook::<Core, 180>(raw, mode),
"public == tier at mode {mode:?}"
);
}
}
}
#[cfg(any(feature = "d462", feature = "d307", feature = "d616"))]
fn assert_atan2_tiny_single_step<C: WideTrigCore, const S: u32>(coeff: i128, big_k: u32)
where
<C::W as crate::int::types::traits::BigInt>::Scratch:
crate::int::types::compute_limbs::ComputeLimbs,
{
use crate::int::types::traits::BigInt;
let p = |n: u32| crate::consts::pow10::dispatch::<C::Storage>(n);
let one = <C::Storage as BigInt>::from_i128(1);
let y = <C::Storage as BigInt>::from_i128(coeff) * p(S - big_k);
let x = p(S); let g = atan2_schoolbook::<C, S>(y, x, RoundingMode::HalfToEven);
assert_eq!(
atan2_schoolbook::<C, S>(y, x, RoundingMode::HalfAwayFromZero),
g,
"nearest modes agree S={S} k={big_k} coeff={coeff}"
);
let j_min = S / big_k + 1;
let j_star = if j_min % 2 == 1 { j_min } else { j_min + 1 };
let expanding = j_star % 4 == 1;
let positive = coeff > 0;
let (floor_e, trunc_e, ceil_e) = match (expanding, positive) {
(true, true) => (g, g, g + one), (true, false) => (g - one, g, g), (false, true) => (g - one, g - one, g), (false, false) => (g, g + one, g + one), };
assert_eq!(
atan2_schoolbook::<C, S>(y, x, RoundingMode::Floor),
floor_e,
"Floor S={S} k={big_k} coeff={coeff} j*={j_star} exp={expanding}"
);
assert_eq!(
atan2_schoolbook::<C, S>(y, x, RoundingMode::Trunc),
trunc_e,
"Trunc S={S} k={big_k} coeff={coeff} j*={j_star} exp={expanding}"
);
assert_eq!(
atan2_schoolbook::<C, S>(y, x, RoundingMode::Ceiling),
ceil_e,
"Ceiling S={S} k={big_k} coeff={coeff} j*={j_star} exp={expanding}"
);
for m in [RoundingMode::Floor, RoundingMode::Trunc, RoundingMode::Ceiling] {
let r = atan2_schoolbook::<C, S>(y, x, m);
let d = if r < g { g - r } else { r - g };
assert!(d <= one, "single-step |r-G|<=1 S={S} k={big_k} coeff={coeff} mode={m:?}");
}
}
#[cfg(any(feature = "d924", feature = "d1232"))]
fn assert_atan2_offgrid_uses_walker<C: WideTrigCore, const S: u32>(coeff: i128, big_k: u32)
where
<C::W as crate::int::types::traits::BigInt>::Scratch:
crate::int::types::compute_limbs::ComputeLimbs,
{
use crate::algos::support::wide_trig_core::round_to_storage_directed_decided_g;
use crate::int::types::traits::BigInt;
let p = |n: u32| crate::consts::pow10::dispatch::<C::Storage>(n);
let one = <C::Storage as BigInt>::from_i128(1);
let y = <C::Storage as BigInt>::from_i128(coeff) * p(S - big_k);
let x = p(S);
let g = atan2_schoolbook::<C, S>(y, x, RoundingMode::HalfToEven);
let walker = |m: RoundingMode| {
round_to_storage_directed_decided_g::<C::Storage, C::W>(
C::GUARD,
S,
m,
C::storage_max(),
C::storage_min(),
|guard| atan2_work::<C, S>(y, x, guard),
)
.0
};
assert_ne!(
walker(RoundingMode::Floor),
walker(RoundingMode::Ceiling),
"off-grid Floor != Ceiling S={S} k={big_k} coeff={coeff}"
);
for m in [RoundingMode::Floor, RoundingMode::Trunc, RoundingMode::Ceiling] {
assert_eq!(
atan2_schoolbook::<C, S>(y, x, m),
walker(m),
"off-grid kernel==walker S={S} k={big_k} coeff={coeff} mode={m:?}"
);
let r = atan2_schoolbook::<C, S>(y, x, m);
let d = if r < g { g - r } else { r - g };
assert!(d <= one, "off-grid single-step S={S} k={big_k} coeff={coeff} mode={m:?}");
}
}
#[cfg(feature = "d462")]
mod tiny_directed_d462 {
use super::*;
use crate::int::types::Int;
type Core = crate::types::widths::wide_trig_d462::Core;
#[test]
fn atan2_tiny_linear_to_deep_sweep() {
assert_atan2_tiny_single_step::<Core, 120>(3, 117);
assert_atan2_tiny_single_step::<Core, 180>(3, 117);
assert_atan2_tiny_single_step::<Core, 231>(3, 117);
assert_atan2_tiny_single_step::<Core, 290>(3, 117);
assert_atan2_tiny_single_step::<Core, 346>(3, 117); assert_atan2_tiny_single_step::<Core, 400>(3, 117); assert_atan2_tiny_single_step::<Core, 461>(3, 117); assert_atan2_tiny_single_step::<Core, 120>(-3, 117); assert_atan2_tiny_single_step::<Core, 461>(-3, 117); assert_atan2_tiny_single_step::<Core, 76>(1, 38); assert_atan2_tiny_single_step::<Core, 113>(1, 38); assert_atan2_tiny_single_step::<Core, 76>(-1, 38); }
#[test]
fn atan2_public_eq_tier() {
check::<180>(3, 117); check::<461>(3, 117); fn check<const S: u32>(coeff: i128, big_k: u32) {
let p = |n: u32| Int::<24>::from_i128(10).pow(n);
let y = Int::<24>::from_i128(coeff) * p(S - big_k);
let x = p(S);
let yd = crate::D::<Int<24>, S>(y);
let xd = crate::D::<Int<24>, S>(x);
for mode in MODES {
assert_eq!(
yd.atan2_strict_with(xd, mode).0,
atan2_schoolbook::<Core, S>(y, x, mode),
"public==tier S={S} k={big_k} mode={mode:?}"
);
}
}
}
#[test]
fn atan2_nontiny_no_spurious_step() {
let one_val = Int::<24>::from_i128(10).pow(461);
let one = Int::<24>::from_i128(1);
let f = atan2_schoolbook::<Core, 461>(one_val, one_val, RoundingMode::Floor);
let n = atan2_schoolbook::<Core, 461>(one_val, one_val, RoundingMode::HalfToEven);
let c = atan2_schoolbook::<Core, 461>(one_val, one_val, RoundingMode::Ceiling);
assert!(f <= n && n <= c, "non-tiny atan2(1,1) directed ordered");
assert!(c - f <= one, "non-tiny atan2(1,1) Ceiling-Floor <= 1 ULP (no spurious step)");
let d = crate::D::<Int<24>, 461>(one_val);
for mode in MODES {
assert_eq!(
d.atan2_strict_with(d, mode).0,
atan2_schoolbook::<Core, 461>(one_val, one_val, mode),
"non-tiny public==tier mode={mode:?}"
);
}
}
}
#[cfg(feature = "d307")]
mod tiny_directed_d307 {
use super::*;
type Core = crate::types::widths::wide_trig_d307::Core;
#[test]
fn atan2_3e117_linear_sweep() {
assert_atan2_tiny_single_step::<Core, 120>(3, 117);
assert_atan2_tiny_single_step::<Core, 153>(3, 117);
assert_atan2_tiny_single_step::<Core, 230>(3, 117);
assert_atan2_tiny_single_step::<Core, 290>(3, 117);
assert_atan2_tiny_single_step::<Core, 306>(3, 117);
}
#[test]
fn atan2_3e70_linear_to_deep() {
assert_atan2_tiny_single_step::<Core, 200>(3, 70); assert_atan2_tiny_single_step::<Core, 250>(3, 70); assert_atan2_tiny_single_step::<Core, 290>(3, 70); }
}
#[cfg(feature = "d616")]
mod tiny_directed_d616 {
use super::*;
type Core = crate::types::widths::wide_trig_d616::Core;
#[test]
fn atan2_3e117_j7() {
assert_atan2_tiny_single_step::<Core, 615>(3, 117); assert_atan2_tiny_single_step::<Core, 600>(3, 117); assert_atan2_tiny_single_step::<Core, 615>(-3, 117); }
}
#[cfg(feature = "d924")]
mod offgrid_d924 {
use super::*;
#[test]
fn atan2_3e117_s923_j9_uses_walker() {
type Core = crate::types::widths::wide_trig_d924::Core;
assert_atan2_offgrid_uses_walker::<Core, 923>(3, 117); assert_atan2_offgrid_uses_walker::<Core, 923>(-3, 117); assert_atan2_offgrid_uses_walker::<Core, 900>(3, 117);
use crate::int::types::Int;
let p = |n: u32| Int::<48>::from_i128(10).pow(n);
let y = Int::<48>::from_i128(3) * p(923 - 117);
let x = p(923);
let yd = crate::D::<Int<48>, 923>(y);
let xd = crate::D::<Int<48>, 923>(x);
for mode in MODES {
assert_eq!(
yd.atan2_strict_with(xd, mode).0,
atan2_schoolbook::<Core, 923>(y, x, mode),
"d924 s923 public==tier mode={mode:?}"
);
}
}
}
#[cfg(feature = "d1232")]
mod offgrid_d1232 {
use super::*;
type Core = crate::types::widths::wide_trig_d1232::Core;
#[test]
fn atan2_3e117_deep_j11_uses_walker() {
assert_atan2_offgrid_uses_walker::<Core, 1231>(3, 117); assert_atan2_offgrid_uses_walker::<Core, 1231>(-3, 117); assert_atan2_offgrid_uses_walker::<Core, 1100>(3, 117); }
}
#[cfg(any(feature = "d57", feature = "wide"))]
mod wide_d57 {
use super::*;
use crate::types::widths::wide_trig_d57::Core;
const S: u32 = 19;
fn raw9(units: i128) -> Int<3> {
Int::<3>::from_i128(units * 10_i128.pow(10))
}
const INPUTS9: [i128; 9] = [
0,
250_000_000,
500_000_000,
600_000_000,
900_000_000,
1_000_000_000,
-500_000_000,
-900_000_000,
-1_000_000_000,
];
#[test]
fn asin_acos_atan2_schoolbook_match_routed() {
for &u in &INPUTS9 {
let r = raw9(u);
for &mode in &MODES {
assert_eq!(
asin_schoolbook::<Core, S>(r, mode),
D::<Int<3>, S>(r).asin_strict_with(mode).0,
"D57 asin schoolbook != routed at units={u} mode={mode:?}"
);
assert_eq!(
acos_schoolbook::<Core, S>(r, mode),
D::<Int<3>, S>(r).acos_strict_with(mode).0,
"D57 acos schoolbook != routed at units={u} mode={mode:?}"
);
}
}
const PTS: [(i128, i128); 5] = [
(1_000_000_000, 1_000_000_000),
(1_000_000_000, -1_000_000_000),
(-1_000_000_000, 1_000_000_000),
(-1_000_000_000, -1_000_000_000),
(500_000_000, 2_000_000_000),
];
for &(y, x) in &PTS {
let yr = raw9(y);
let xr = raw9(x);
for &mode in &MODES {
assert_eq!(
atan2_schoolbook::<Core, S>(yr, xr, mode),
D::<Int<3>, S>(yr).atan2_strict_with(D::<Int<3>, S>(xr), mode).0,
"D57 atan2 schoolbook != routed at y={y} x={x} mode={mode:?}"
);
}
}
}
}
}