use crate::algos::exp::exp_series_2limb::exp_fixed;
use crate::algos::ln::ln_series_2limb::{STRICT_GUARD, ln_fixed};
use crate::algos::support::fixed::Fixed;
#[cfg(feature = "_wide-support")]
use crate::algos::support::wide_trig_core::WideTrigCore;
use crate::algos::trig::trig_series_2limb::to_fixed;
use crate::int::types::Int;
use crate::support::rounding::RoundingMode;
#[cfg(feature = "_wide-support")]
#[inline]
fn hyper_tiny_pin<C: WideTrigCore, const SCALE: u32, const EXPANDING: bool>(
raw: C::Storage,
mode: RoundingMode,
) -> Option<C::Storage> {
let zero = C::storage_zero();
if raw == zero {
return None;
}
let thresh_exp = SCALE - SCALE.div_ceil(3);
let thresh = crate::consts::pow10::dispatch::<C::Storage>(thresh_exp);
let a = if raw < zero { zero - raw } else { raw };
if a > thresh {
return None;
}
let one = <C::Storage as crate::int::types::traits::BigInt>::from_i128(1);
Some(if EXPANDING {
crate::support::rounding::tiny_odd_expanding_directed(raw, zero, one, mode)
} else {
crate::support::rounding::tiny_odd_compressing_directed(raw, zero, one, mode)
})
}
#[cfg(feature = "_wide-support")]
#[inline]
fn tanh_k_lift<C: WideTrigCore, const SCALE: u32>(raw: C::Storage) -> u32 {
C::exp_result_int_digits(C::to_work_scaled(raw, 0), SCALE)
.min((SCALE + C::GUARD) / 2 + 2)
}
#[cfg(feature = "_wide-support")]
#[inline]
fn tanh_saturated<C: WideTrigCore, const SCALE: u32>(
raw: C::Storage,
mode: RoundingMode,
) -> Option<C::Storage>
where
<C::Wagm as crate::int::types::traits::BigInt>::Scratch:
crate::int::types::compute_limbs::ComputeLimbs,
{
use crate::algos::exp::exp_generic as eg;
let zero = C::storage_zero();
let neg = raw < zero;
let a = if neg { zero - raw } else { raw };
let sat_x = ((SCALE as u128 + C::GUARD as u128 + 2) * 100_000 / 86_859) as i128;
let over = a / crate::consts::pow10::dispatch::<C::Storage>(SCALE)
> <C::Storage as crate::int::types::traits::BigInt>::from_i128(sat_x);
if !over {
return None;
}
Some(
crate::algos::support::wide_trig_core::round_to_storage_directed_g::<C::Storage, C::Wagm>(
C::GUARD,
SCALE,
mode,
C::storage_max(),
C::storage_min(),
|guard| {
let w = SCALE + guard;
let sat = eg::one::<C::Wagm>(w)
- <C::Wagm as crate::int::types::traits::BigInt>::ONE;
if neg { eg::zero::<C::Wagm>() - sat } else { sat }
},
),
)
}
#[cfg(feature = "_wide-support")]
#[inline]
#[must_use]
pub(crate) fn sinh_schoolbook<C: WideTrigCore, const SCALE: u32>(
raw: C::Storage,
mode: RoundingMode,
) -> C::Storage
where
<C::Wagm as crate::int::types::traits::BigInt>::Scratch:
crate::int::types::compute_limbs::ComputeLimbs,
<C::Wexp 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::{
round_to_storage_widening_g, to_work_scaled_g,
};
if raw == C::storage_zero() {
return C::storage_zero();
}
if let Some(p) = hyper_tiny_pin::<C, SCALE, true>(raw, mode) {
return p;
}
let neg = raw < C::storage_zero();
let k_lift = C::exp_result_int_digits(C::to_work_scaled(raw, 0), SCALE);
let base_guard = C::GUARD + k_lift;
round_to_storage_widening_g::<C::Storage, C::Wagm, C::Wexp>(
base_guard,
SCALE,
mode,
true,
C::storage_max(),
C::storage_min(),
|guard| {
let w = SCALE + guard;
let sh = hyper_probe_g::<C, C::Wagm>(
raw,
guard,
w,
eg::sinh_pos::<C::Wagm>,
eg::sinh_pos::<C::Wexp>,
);
if neg { eg::zero::<C::Wagm>() - sh } else { sh }
},
|guard| {
let w = SCALE + guard;
let v = to_work_scaled_g::<C::Storage, C::Wexp>(raw, guard);
let av = if v < eg::zero::<C::Wexp>() { eg::zero::<C::Wexp>() - v } else { v };
let sh = eg::sinh_pos::<C::Wexp>(av, w);
if neg { eg::zero::<C::Wexp>() - sh } else { sh }
},
)
}
#[cfg(feature = "_wide-support")]
#[inline]
#[must_use]
pub(crate) fn cosh_schoolbook<C: WideTrigCore, const SCALE: u32>(
raw: C::Storage,
mode: RoundingMode,
) -> C::Storage
where
<C::Wagm as crate::int::types::traits::BigInt>::Scratch:
crate::int::types::compute_limbs::ComputeLimbs,
<C::Wexp 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::{
round_to_storage_widening_g, to_work_scaled_g,
};
if raw == C::storage_zero() {
return C::storage_one(SCALE);
}
let k_lift = C::exp_result_int_digits(C::to_work_scaled(raw, 0), SCALE);
let base_guard = C::GUARD + k_lift;
round_to_storage_widening_g::<C::Storage, C::Wagm, C::Wexp>(
base_guard,
SCALE,
mode,
true,
C::storage_max(),
C::storage_min(),
|guard| {
let w = SCALE + guard;
hyper_probe_g::<C, C::Wagm>(
raw,
guard,
w,
eg::cosh_pos::<C::Wagm>,
eg::cosh_pos::<C::Wexp>,
)
},
|guard| {
let w = SCALE + guard;
let v = to_work_scaled_g::<C::Storage, C::Wexp>(raw, guard);
let av = if v < eg::zero::<C::Wexp>() { eg::zero::<C::Wexp>() - v } else { v };
eg::cosh_pos::<C::Wexp>(av, w)
},
)
}
#[cfg(feature = "_wide-support")]
#[inline]
#[must_use]
pub(crate) fn tanh_schoolbook<C: WideTrigCore, const SCALE: u32>(
raw: C::Storage,
mode: RoundingMode,
) -> C::Storage
where
<C::Wagm as crate::int::types::traits::BigInt>::Scratch:
crate::int::types::compute_limbs::ComputeLimbs,
<C::Wexp 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::round_to_storage_directed_g;
if let Some(p) = hyper_tiny_pin::<C, SCALE, false>(raw, mode) {
return p;
}
if let Some(p) = tanh_saturated::<C, SCALE>(raw, mode) {
return p;
}
let neg = raw < C::storage_zero();
let base_guard = C::GUARD + tanh_k_lift::<C, SCALE>(raw);
round_to_storage_directed_g::<C::Storage, C::Wagm>(
base_guard,
SCALE,
mode,
C::storage_max(),
C::storage_min(),
|guard| {
let w = SCALE + guard;
let th = hyper_probe_g::<C, C::Wagm>(
raw,
guard,
w,
|av, w| {
let ex = eg::exp_fixed::<C::Wagm>(av, w);
let enx = eg::div::<C::Wagm>(eg::one::<C::Wagm>(w), ex, w);
eg::div::<C::Wagm>(ex - enx, ex + enx, w)
},
eg::tanh_pos::<C::Wexp>,
);
if neg { eg::zero::<C::Wagm>() - th } else { th }
},
)
}
#[cfg(feature = "_wide-support")]
#[inline]
#[must_use]
pub(crate) fn asinh_schoolbook<C: WideTrigCore, const SCALE: u32>(
raw: C::Storage,
mode: RoundingMode,
) -> C::Storage
where
<C::Wagm 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::{
round_to_storage_directed_decided_g, tiny_x_deep_directed_adjust, to_work_scaled_g,
};
if raw == C::storage_zero() {
return C::storage_zero();
}
if let Some(p) = hyper_tiny_pin::<C, SCALE, false>(raw, mode) {
return p;
}
let neg = raw < C::storage_zero();
let (r, decided) = round_to_storage_directed_decided_g::<C::Storage, C::Wagm>(
C::GUARD,
SCALE,
mode,
C::storage_max(),
C::storage_min(),
|guard| {
let w = SCALE + guard;
let ln2_w = ln2_at_rung::<C::Wagm>(w, SCALE + C::GUARD);
let one_w = eg::one::<C::Wagm>(w);
let v = to_work_scaled_g::<C::Storage, C::Wagm>(raw, guard);
let ax = if v < eg::zero::<C::Wagm>() { eg::zero::<C::Wagm>() - v } else { v };
let inner = if ax >= one_w {
let inv = eg::div::<C::Wagm>(one_w, ax, w);
let root = eg::sqrt_fixed::<C::Wagm>(one_w + eg::mul::<C::Wagm>(inv, inv, w), w);
eg::ln_fixed::<C::Wagm>(ax, w, ln2_w) + eg::ln_fixed::<C::Wagm>(one_w + root, w, ln2_w)
} else {
let root = eg::sqrt_fixed::<C::Wagm>(eg::mul::<C::Wagm>(ax, ax, w) + one_w, w);
eg::ln_fixed::<C::Wagm>(ax + root, w, ln2_w)
};
if neg { eg::zero::<C::Wagm>() - inner } else { inner }
},
);
tiny_x_deep_directed_adjust::<C::Storage, SCALE>(
r,
decided,
raw,
mode,
true,
<C::Wagm as crate::int::types::traits::BigInt>::BITS,
)
}
#[cfg(feature = "_wide-support")]
#[inline]
#[must_use]
pub(crate) fn acosh_schoolbook<C: WideTrigCore, const SCALE: u32>(
raw: C::Storage,
mode: RoundingMode,
) -> C::Storage
where
<C::Wagm 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::{
round_to_storage_directed_near_special_g, to_work_scaled_g,
};
{
let w0 = SCALE + C::GUARD;
if to_work_scaled_g::<C::Storage, C::Wagm>(raw, C::GUARD) < eg::one::<C::Wagm>(w0) {
panic!("schoolbook acosh: argument must be >= 1");
}
}
round_to_storage_directed_near_special_g::<C::Storage, C::Wagm>(
C::GUARD,
SCALE,
mode,
C::storage_max(),
C::storage_min(),
|guard| {
let w = SCALE + guard;
let one_w = eg::one::<C::Wagm>(w);
let v = to_work_scaled_g::<C::Storage, C::Wagm>(raw, guard);
let two_w = one_w + one_w;
if v >= two_w {
let inv = eg::div::<C::Wagm>(one_w, v, w);
let root = eg::sqrt_fixed::<C::Wagm>(one_w - eg::mul::<C::Wagm>(inv, inv, w), w);
C::ln_fixed_routed_agm::<SCALE>(v, w) + C::ln_fixed_routed_agm::<SCALE>(one_w + root, w)
} else {
let t = v - one_w;
let root = eg::sqrt_fixed::<C::Wagm>(eg::mul::<C::Wagm>(t, t + two_w, w), w);
eg::log1p_fixed::<C::Wagm>(t + root, w)
}
},
)
}
#[cfg(feature = "_wide-support")]
#[inline]
#[must_use]
pub(crate) fn atanh_schoolbook<C: WideTrigCore, const SCALE: u32>(
raw: C::Storage,
mode: RoundingMode,
) -> C::Storage
where
<C::Wagm 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::{
round_to_storage_directed_near_special_g, to_work_scaled_g,
};
{
let w0 = SCALE + C::GUARD;
let v0 = to_work_scaled_g::<C::Storage, C::Wagm>(raw, C::GUARD);
let ax0 = if v0 < eg::zero::<C::Wagm>() { eg::zero::<C::Wagm>() - v0 } else { v0 };
if ax0 >= eg::one::<C::Wagm>(w0) {
panic!("schoolbook atanh: argument out of domain (-1, 1)");
}
}
if let Some(p) = hyper_tiny_pin::<C, SCALE, true>(raw, mode) {
return p;
}
round_to_storage_directed_near_special_g::<C::Storage, C::Wagm>(
C::GUARD,
SCALE,
mode,
C::storage_max(),
C::storage_min(),
|guard| {
let w = SCALE + guard;
let one_w = eg::one::<C::Wagm>(w);
let v = to_work_scaled_g::<C::Storage, C::Wagm>(raw, guard);
(C::ln_fixed_routed_agm::<SCALE>(one_w + v, w)
- C::ln_fixed_routed_agm::<SCALE>(one_w - v, w))
>> 1
},
)
}
#[cfg(feature = "_wide-support")]
#[inline]
fn hyper_probe_g<C: WideTrigCore, Wk: crate::int::types::traits::BigInt>(
raw: C::Storage,
guard: u32,
w: u32,
f_rung: impl Fn(Wk, u32) -> Wk,
f_wide: impl Fn(C::Wexp, u32) -> C::Wexp,
) -> Wk
where
Wk::Scratch: crate::int::types::compute_limbs::ComputeLimbs,
<C::Wexp 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::to_work_scaled_g;
let v = to_work_scaled_g::<C::Storage, Wk>(raw, guard);
let av = if v < eg::zero::<Wk>() { eg::zero::<Wk>() - v } else { v };
if eg::exp_peak_fits::<Wk>(av, w) {
f_rung(av, w)
} else {
let v_e = to_work_scaled_g::<C::Storage, C::Wexp>(raw, guard);
let av_e = if v_e < eg::zero::<C::Wexp>() {
eg::zero::<C::Wexp>() - v_e
} else {
v_e
};
eg::resize_or_panic::<C::Wexp, Wk>(f_wide(av_e, w))
}
}
#[cfg(feature = "_wide-support")]
#[inline]
#[must_use]
pub(crate) fn sinh_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::Wexp 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::{
round_to_storage_widening_g, to_work_scaled_g,
};
if raw == C::storage_zero() {
return C::storage_zero();
}
if let Some(p) = hyper_tiny_pin::<C, SCALE, true>(raw, mode) {
return p;
}
let neg = raw < C::storage_zero();
let k_lift = C::exp_result_int_digits(C::to_work_scaled(raw, 0), SCALE);
let base_guard = C::GUARD + k_lift;
round_to_storage_widening_g::<C::Storage, Wk, C::Wexp>(
base_guard,
SCALE,
mode,
true,
C::storage_max(),
C::storage_min(),
|guard| {
let w = SCALE + guard;
let sh = hyper_probe_g::<C, Wk>(
raw,
guard,
w,
|av, w| eg::sinh_pos::<Wk>(av, w),
eg::sinh_pos::<C::Wexp>,
);
if neg { eg::zero::<Wk>() - sh } else { sh }
},
|guard| {
let w = SCALE + guard;
let v = to_work_scaled_g::<C::Storage, C::Wexp>(raw, guard);
let av = if v < eg::zero::<C::Wexp>() { eg::zero::<C::Wexp>() - v } else { v };
let sh = eg::sinh_pos::<C::Wexp>(av, w);
if neg { eg::zero::<C::Wexp>() - sh } else { sh }
},
)
}
#[cfg(feature = "_wide-support")]
#[inline]
#[must_use]
pub(crate) fn cosh_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::Wexp 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::{
round_to_storage_widening_g, to_work_scaled_g,
};
if raw == C::storage_zero() {
return C::storage_one(SCALE);
}
let k_lift = C::exp_result_int_digits(C::to_work_scaled(raw, 0), SCALE);
let base_guard = C::GUARD + k_lift;
round_to_storage_widening_g::<C::Storage, Wk, C::Wexp>(
base_guard,
SCALE,
mode,
true,
C::storage_max(),
C::storage_min(),
|guard| {
let w = SCALE + guard;
hyper_probe_g::<C, Wk>(
raw,
guard,
w,
|av, w| eg::cosh_pos::<Wk>(av, w),
eg::cosh_pos::<C::Wexp>,
)
},
|guard| {
let w = SCALE + guard;
let v = to_work_scaled_g::<C::Storage, C::Wexp>(raw, guard);
let av = if v < eg::zero::<C::Wexp>() { eg::zero::<C::Wexp>() - v } else { v };
eg::cosh_pos::<C::Wexp>(av, w)
},
)
}
#[cfg(feature = "_wide-support")]
#[inline]
#[must_use]
pub(crate) fn tanh_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::Wagm as crate::int::types::traits::BigInt>::Scratch:
crate::int::types::compute_limbs::ComputeLimbs,
<C::Wexp 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::round_to_storage_directed_widening_g;
if let Some(p) = hyper_tiny_pin::<C, SCALE, false>(raw, mode) {
return p;
}
if let Some(p) = tanh_saturated::<C, SCALE>(raw, mode) {
return p;
}
let neg = raw < C::storage_zero();
let base_guard = C::GUARD + tanh_k_lift::<C, SCALE>(raw);
round_to_storage_directed_widening_g::<C::Storage, Wk, C::Wagm>(
base_guard,
SCALE,
mode,
C::storage_max(),
C::storage_min(),
|guard| {
let w = SCALE + guard;
let th = hyper_probe_g::<C, Wk>(
raw,
guard,
w,
|av, w| {
let ex = eg::exp_fixed::<Wk>(av, w);
let enx = eg::div::<Wk>(eg::one::<Wk>(w), ex, w);
eg::div::<Wk>(ex - enx, ex + enx, w)
},
eg::tanh_pos::<C::Wexp>,
);
if neg { eg::zero::<Wk>() - th } else { th }
},
|guard| {
let w = SCALE + guard;
let th = hyper_probe_g::<C, C::Wagm>(
raw,
guard,
w,
|av, w| {
let ex = eg::exp_fixed::<C::Wagm>(av, w);
let enx = eg::div::<C::Wagm>(eg::one::<C::Wagm>(w), ex, w);
eg::div::<C::Wagm>(ex - enx, ex + enx, w)
},
eg::tanh_pos::<C::Wexp>,
);
if neg { eg::zero::<C::Wagm>() - th } else { th }
},
)
}
#[cfg(feature = "_wide-support")]
#[inline]
fn ln2_at_rung<Wk: crate::int::types::traits::BigInt>(w: u32, base_w: u32) -> Wk {
if w == base_w {
crate::consts::ln2_by_scale::<Wk>(base_w, crate::support::rounding::DEFAULT_ROUNDING_MODE)
} else {
crate::consts::ln2_by_working_scale::<Wk>(
w,
crate::support::rounding::DEFAULT_ROUNDING_MODE,
)
}
}
#[cfg(feature = "_wide-support")]
#[inline]
#[must_use]
pub(crate) fn asinh_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::Wagm 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::{
round_to_storage_directed_widening_decided_g, tiny_x_deep_directed_adjust, to_work_scaled_g,
};
if raw == C::storage_zero() {
return C::storage_zero();
}
if let Some(p) = hyper_tiny_pin::<C, SCALE, false>(raw, mode) {
return p;
}
let neg = raw < C::storage_zero();
let (r, decided) = round_to_storage_directed_widening_decided_g::<C::Storage, Wk, C::Wagm>(
C::GUARD,
SCALE,
mode,
C::storage_max(),
C::storage_min(),
|guard| {
let w = SCALE + guard;
let ln2_w = ln2_at_rung::<Wk>(w, SCALE + C::GUARD);
let one_w = eg::one::<Wk>(w);
let v = to_work_scaled_g::<C::Storage, Wk>(raw, guard);
let ax = if v < eg::zero::<Wk>() { eg::zero::<Wk>() - v } else { v };
let inner = if ax >= one_w {
let inv = eg::div::<Wk>(one_w, ax, w);
let root = eg::sqrt_fixed::<Wk>(one_w + eg::mul::<Wk>(inv, inv, w), w);
eg::ln_fixed::<Wk>(ax, w, ln2_w) + eg::ln_fixed::<Wk>(one_w + root, w, ln2_w)
} else {
let root = eg::sqrt_fixed::<Wk>(eg::mul::<Wk>(ax, ax, w) + one_w, w);
eg::ln_fixed::<Wk>(ax + root, w, ln2_w)
};
if neg { eg::zero::<Wk>() - inner } else { inner }
},
|guard| {
let w = SCALE + guard;
let ln2_w = ln2_at_rung::<C::Wagm>(w, SCALE + C::GUARD);
let one_w = eg::one::<C::Wagm>(w);
let v = to_work_scaled_g::<C::Storage, C::Wagm>(raw, guard);
let ax = if v < eg::zero::<C::Wagm>() { eg::zero::<C::Wagm>() - v } else { v };
let inner = if ax >= one_w {
let inv = eg::div::<C::Wagm>(one_w, ax, w);
let root = eg::sqrt_fixed::<C::Wagm>(one_w + eg::mul::<C::Wagm>(inv, inv, w), w);
eg::ln_fixed::<C::Wagm>(ax, w, ln2_w) + eg::ln_fixed::<C::Wagm>(one_w + root, w, ln2_w)
} else {
let root = eg::sqrt_fixed::<C::Wagm>(eg::mul::<C::Wagm>(ax, ax, w) + one_w, w);
eg::ln_fixed::<C::Wagm>(ax + root, w, ln2_w)
};
if neg { eg::zero::<C::Wagm>() - inner } else { inner }
},
);
tiny_x_deep_directed_adjust::<C::Storage, SCALE>(
r,
decided,
raw,
mode,
true,
<C::Wagm as crate::int::types::traits::BigInt>::BITS,
)
}
#[cfg(feature = "_wide-support")]
#[inline]
#[must_use]
pub(crate) fn acosh_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::Wagm 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::{
round_to_storage_directed_near_special_widening_g, to_work_scaled_g,
};
{
let w0 = SCALE + C::GUARD;
if to_work_scaled_g::<C::Storage, Wk>(raw, C::GUARD) < eg::one::<Wk>(w0) {
panic!("schoolbook acosh: argument must be >= 1");
}
}
round_to_storage_directed_near_special_widening_g::<C::Storage, Wk, C::Wagm>(
C::GUARD,
SCALE,
mode,
C::storage_max(),
C::storage_min(),
|guard| {
let w = SCALE + guard;
let ln2_w = ln2_at_rung::<Wk>(w, SCALE + C::GUARD);
let one_w = eg::one::<Wk>(w);
let v = to_work_scaled_g::<C::Storage, Wk>(raw, guard);
let two_w = one_w + one_w;
if v >= two_w {
let inv = eg::div::<Wk>(one_w, v, w);
let root = eg::sqrt_fixed::<Wk>(one_w - eg::mul::<Wk>(inv, inv, w), w);
eg::ln_fixed::<Wk>(v, w, ln2_w) + eg::ln_fixed::<Wk>(one_w + root, w, ln2_w)
} else {
let t = v - one_w;
let root = eg::sqrt_fixed::<Wk>(eg::mul::<Wk>(t, t + two_w, w), w);
eg::log1p_fixed::<Wk>(t + root, w)
}
},
|guard| {
let w = SCALE + guard;
let one_w = eg::one::<C::Wagm>(w);
let v = to_work_scaled_g::<C::Storage, C::Wagm>(raw, guard);
let two_w = one_w + one_w;
if v >= two_w {
let inv = eg::div::<C::Wagm>(one_w, v, w);
let root = eg::sqrt_fixed::<C::Wagm>(one_w - eg::mul::<C::Wagm>(inv, inv, w), w);
C::ln_fixed_routed_agm::<SCALE>(v, w) + C::ln_fixed_routed_agm::<SCALE>(one_w + root, w)
} else {
let t = v - one_w;
let root = eg::sqrt_fixed::<C::Wagm>(eg::mul::<C::Wagm>(t, t + two_w, w), w);
eg::log1p_fixed::<C::Wagm>(t + root, w)
}
},
)
}
#[cfg(feature = "_wide-support")]
#[inline]
#[must_use]
pub(crate) fn atanh_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::Wagm 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::{
round_to_storage_directed_near_special_widening_g, to_work_scaled_g,
};
{
let w0 = SCALE + C::GUARD;
let v0 = to_work_scaled_g::<C::Storage, Wk>(raw, C::GUARD);
let ax0 = if v0 < eg::zero::<Wk>() { eg::zero::<Wk>() - v0 } else { v0 };
if ax0 >= eg::one::<Wk>(w0) {
panic!("schoolbook atanh: argument out of domain (-1, 1)");
}
}
if let Some(p) = hyper_tiny_pin::<C, SCALE, true>(raw, mode) {
return p;
}
round_to_storage_directed_near_special_widening_g::<C::Storage, Wk, C::Wagm>(
C::GUARD,
SCALE,
mode,
C::storage_max(),
C::storage_min(),
|guard| {
let w = SCALE + guard;
let ln2_w = ln2_at_rung::<Wk>(w, SCALE + C::GUARD);
let one_w = eg::one::<Wk>(w);
let v = to_work_scaled_g::<C::Storage, Wk>(raw, guard);
(eg::ln_fixed::<Wk>(one_w + v, w, ln2_w) - eg::ln_fixed::<Wk>(one_w - v, w, ln2_w))
>> 1
},
|guard| {
let w = SCALE + guard;
let one_w = eg::one::<C::Wagm>(w);
let v = to_work_scaled_g::<C::Storage, C::Wagm>(raw, guard);
(C::ln_fixed_routed_agm::<SCALE>(one_w + v, w)
- C::ln_fixed_routed_agm::<SCALE>(one_w - v, w))
>> 1
},
)
}
#[inline]
fn one_fixed(w: u32) -> Fixed {
Fixed { negative: false, mag: Fixed::pow10(w) }
}
#[inline]
#[must_use]
fn sinh_schoolbook_raw<const SCALE: u32>(raw: i128, mode: RoundingMode) -> i128 {
if raw == 0 {
return 0;
}
let w = SCALE + STRICT_GUARD;
let v = to_fixed(raw);
let neg = raw < 0;
let av = Fixed { negative: false, mag: v.mag };
let ex = exp_fixed(av, w);
let one_w = one_fixed(w);
let enx = one_w.div(ex, w);
let sh = ex.sub(enx).halve();
let sh = if neg { sh.neg() } else { sh };
sh.round_to_i128_with(w, SCALE, mode)
.unwrap_or_else(|| crate::support::diagnostics::overflow_panic_with_scale("sinh", SCALE))
}
#[inline]
#[must_use]
fn cosh_schoolbook_raw<const SCALE: u32>(raw: i128, mode: RoundingMode) -> i128 {
if raw == 0 {
return 10_i128.pow(SCALE);
}
let w = SCALE + STRICT_GUARD;
let v = to_fixed(raw);
let av = Fixed { negative: false, mag: v.mag };
let ex = exp_fixed(av, w);
let one_w = one_fixed(w);
let enx = one_w.div(ex, w);
ex.add(enx)
.halve()
.round_to_i128_with(w, SCALE, mode)
.unwrap_or_else(|| crate::support::diagnostics::overflow_panic_with_scale("cosh", SCALE))
}
#[inline]
#[must_use]
fn tanh_schoolbook_raw<const SCALE: u32>(raw: i128, mode: RoundingMode) -> i128 {
if raw == 0 {
return 0;
}
let w = SCALE + STRICT_GUARD;
let one_w = one_fixed(w);
let neg = raw < 0;
let thr_x = (w as i128) * 1152 / 1000 + 2;
let saturated = one_w.sub(Fixed::from_u128_mag(1, false));
let th = if raw.abs() / 10_i128.pow(SCALE) > thr_x {
saturated
} else {
let v = to_fixed(raw);
let av = Fixed { negative: false, mag: v.mag };
let m = exp_fixed(av.double().neg(), w);
if m.is_zero() {
saturated
} else {
one_w.sub(m).div(one_w.add(m), w)
}
};
let th = if neg { th.neg() } else { th };
th.round_to_i128_with(w, SCALE, mode)
.unwrap_or_else(|| crate::support::diagnostics::overflow_panic_with_scale("tanh", SCALE))
}
#[inline]
#[must_use]
fn asinh_schoolbook_raw<const SCALE: u32>(raw: i128, mode: RoundingMode) -> i128 {
if raw == 0 {
return 0;
}
let w = SCALE + STRICT_GUARD;
let one_w = one_fixed(w);
let v = to_fixed(raw);
let ax = Fixed { negative: false, mag: v.mag };
let inner = if ax.ge_mag(one_w) {
let inv = one_w.div(ax, w);
let root = one_w.add(inv.mul(inv, w)).sqrt(w);
ln_fixed(ax, w).add(ln_fixed(one_w.add(root), w))
} else {
let root = ax.mul(ax, w).add(one_w).sqrt(w);
ln_fixed(ax.add(root), w)
};
let signed = if raw < 0 { inner.neg() } else { inner };
signed
.round_to_i128_with(w, SCALE, mode)
.unwrap_or_else(|| crate::support::diagnostics::overflow_panic_with_scale("asinh", SCALE))
}
#[inline]
#[must_use]
fn acosh_schoolbook_raw<const SCALE: u32>(raw: i128, mode: RoundingMode) -> i128 {
let one_bits: i128 = 10_i128.pow(SCALE);
if raw == one_bits {
return 0;
}
let w = SCALE + STRICT_GUARD;
let one_w = one_fixed(w);
let v = to_fixed(raw);
assert!(!v.negative && v.ge_mag(one_w), "acosh: argument must be >= 1");
let two_w = one_w.double();
let inner = if v.ge_mag(two_w) {
let inv = one_w.div(v, w);
let root = one_w.sub(inv.mul(inv, w)).sqrt(w);
ln_fixed(v, w).add(ln_fixed(one_w.add(root), w))
} else {
let root = v.mul(v, w).sub(one_w).sqrt(w);
ln_fixed(v.add(root), w)
};
inner
.round_to_i128_with(w, SCALE, mode)
.unwrap_or_else(|| crate::support::diagnostics::overflow_panic_with_scale("acosh", SCALE))
}
#[inline]
#[must_use]
fn atanh_schoolbook_raw<const SCALE: u32>(raw: i128, mode: RoundingMode) -> i128 {
if raw == 0 {
return 0;
}
let w = SCALE + STRICT_GUARD;
let one_w = one_fixed(w);
let v = to_fixed(raw);
let ax = Fixed { negative: false, mag: v.mag };
assert!(!ax.ge_mag(one_w), "atanh: argument out of domain (-1, 1)");
let ln_num = ln_fixed(one_w.add(v), w);
let ln_den = ln_fixed(one_w.sub(v), w);
ln_num
.sub(ln_den)
.halve()
.round_to_i128_with(w, SCALE, mode)
.unwrap_or_else(|| crate::support::diagnostics::overflow_panic_with_scale("atanh", SCALE))
}
#[inline]
#[must_use]
pub(crate) fn sinh_schoolbook_narrow<const SCALE: u32>(raw: Int<2>, mode: RoundingMode) -> Int<2> {
Int::<2>::from_i128(sinh_schoolbook_raw::<SCALE>(raw.as_i128(), mode))
}
#[inline]
#[must_use]
pub(crate) fn cosh_schoolbook_narrow<const SCALE: u32>(raw: Int<2>, mode: RoundingMode) -> Int<2> {
Int::<2>::from_i128(cosh_schoolbook_raw::<SCALE>(raw.as_i128(), mode))
}
#[inline]
#[must_use]
pub(crate) fn tanh_schoolbook_narrow<const SCALE: u32>(raw: Int<2>, mode: RoundingMode) -> Int<2> {
Int::<2>::from_i128(tanh_schoolbook_raw::<SCALE>(raw.as_i128(), mode))
}
#[inline]
#[must_use]
pub(crate) fn asinh_schoolbook_narrow<const SCALE: u32>(raw: Int<2>, mode: RoundingMode) -> Int<2> {
Int::<2>::from_i128(asinh_schoolbook_raw::<SCALE>(raw.as_i128(), mode))
}
#[inline]
#[must_use]
pub(crate) fn acosh_schoolbook_narrow<const SCALE: u32>(raw: Int<2>, mode: RoundingMode) -> Int<2> {
Int::<2>::from_i128(acosh_schoolbook_raw::<SCALE>(raw.as_i128(), mode))
}
#[inline]
#[must_use]
pub(crate) fn atanh_schoolbook_narrow<const SCALE: u32>(raw: Int<2>, mode: RoundingMode) -> Int<2> {
Int::<2>::from_i128(atanh_schoolbook_raw::<SCALE>(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 HYP_INPUTS: [i128; 9] = [
0,
1_000_000_000,
500_000_000_000,
1_000_000_000_000,
2_500_000_000_000,
-1_000_000_000,
-500_000_000_000,
-1_000_000_000_000,
-2_500_000_000_000,
];
#[test]
fn sinh_schoolbook_narrow_matches_routed_kernel() {
for &raw in &HYP_INPUTS {
for &mode in &MODES {
assert_eq!(
sinh_schoolbook_narrow::<S38>(d38(raw).0, mode),
d38(raw).sinh_strict_with(mode).0,
"sinh schoolbook != routed at raw={raw} mode={mode:?}"
);
}
}
}
#[test]
fn cosh_schoolbook_narrow_matches_routed_kernel() {
for &raw in &HYP_INPUTS {
for &mode in &MODES {
assert_eq!(
cosh_schoolbook_narrow::<S38>(d38(raw).0, mode),
d38(raw).cosh_strict_with(mode).0,
"cosh schoolbook != routed at raw={raw} mode={mode:?}"
);
}
}
}
#[test]
fn tanh_schoolbook_narrow_matches_routed_kernel() {
for &raw in &HYP_INPUTS {
for &mode in &MODES {
assert_eq!(
tanh_schoolbook_narrow::<S38>(d38(raw).0, mode),
d38(raw).tanh_strict_with(mode).0,
"tanh schoolbook != routed at raw={raw} mode={mode:?}"
);
}
}
}
#[test]
fn tanh_schoolbook_narrow_saturates_large_x_matches_routed() {
const LARGE: [i128; 4] = [
100_000_000_000_000, 5_000_000_000_000_000, -100_000_000_000_000, -5_000_000_000_000_000, ];
for &raw in &LARGE {
for &mode in &MODES {
assert_eq!(
tanh_schoolbook_narrow::<S38>(d38(raw).0, mode),
d38(raw).tanh_strict_with(mode).0,
"tanh saturation schoolbook != routed at raw={raw} mode={mode:?}"
);
}
}
}
#[test]
fn tanh_schoolbook_narrow_gap_band_high_scale_matches_routed() {
const S: u32 = 28;
const UNIT: i128 = 10_i128.pow(28);
const XS: [i128; 9] = [44, 48, 50, 55, 60, 66, 67, 70, 100];
for &x in &XS {
for &raw in &[x * UNIT, -x * UNIT] {
for &mode in &MODES {
assert_eq!(
tanh_schoolbook_narrow::<S>(Int::<2>::from_i128(raw), mode),
D::<Int<2>, S>(Int::<2>::from_i128(raw)).tanh_strict_with(mode).0,
"tanh gap-band schoolbook != routed at raw={raw} mode={mode:?}"
);
}
}
}
}
#[test]
fn asinh_schoolbook_narrow_matches_routed_kernel() {
const PTS: [i128; 9] = [
0,
500_000_000_000,
1_000_000_000_000,
2_500_000_000_000,
5_000_000_000_000,
-500_000_000_000,
-1_000_000_000_000,
-2_500_000_000_000,
-5_000_000_000_000,
];
for &raw in &PTS {
for &mode in &MODES {
assert_eq!(
asinh_schoolbook_narrow::<S38>(d38(raw).0, mode),
d38(raw).asinh_strict_with(mode).0,
"asinh schoolbook != routed at raw={raw} mode={mode:?}"
);
}
}
}
#[test]
fn acosh_schoolbook_narrow_matches_routed_kernel() {
const PTS: [i128; 5] = [
1_000_000_000_000,
1_200_000_000_000,
2_000_000_000_000,
3_000_000_000_000,
5_000_000_000_000,
];
for &raw in &PTS {
for &mode in &MODES {
assert_eq!(
acosh_schoolbook_narrow::<S38>(d38(raw).0, mode),
d38(raw).acosh_strict_with(mode).0,
"acosh schoolbook != routed at raw={raw} mode={mode:?}"
);
}
}
}
#[test]
fn atanh_schoolbook_narrow_matches_routed_kernel() {
const PTS: [i128; 7] = [
0,
250_000_000_000,
500_000_000_000,
900_000_000_000,
-250_000_000_000,
-500_000_000_000,
-900_000_000_000,
];
for &raw in &PTS {
for &mode in &MODES {
assert_eq!(
atanh_schoolbook_narrow::<S38>(d38(raw).0, mode),
d38(raw).atanh_strict_with(mode).0,
"atanh schoolbook != routed at raw={raw} mode={mode:?}"
);
}
}
}
#[test]
fn atanh_schoolbook_narrow_matches_routed_kernel_near_one() {
const S: u32 = 28;
const ONE: i128 = 10_i128.pow(28);
const NEAR_ONE: [i128; 8] = [
ONE - 1,
ONE - 10,
ONE - 100,
ONE - 10_000,
ONE - 1_000_000,
ONE - 100_000_000,
ONE - 10_000_000_000,
ONE / 2,
];
for &mag in &NEAR_ONE {
for &raw in &[mag, -mag] {
for &mode in &MODES {
assert_eq!(
atanh_schoolbook_narrow::<S>(Int::<2>::from_i128(raw), mode),
D::<Int<2>, S>(Int::<2>::from_i128(raw)).atanh_strict_with(mode).0,
"atanh near-1 schoolbook != routed at raw={raw} mode={mode:?}"
);
}
}
}
}
#[cfg(any(feature = "d115", feature = "wide"))]
mod wide_d115_defect_b {
use super::*;
#[test]
fn sinh_cosh_large_arg_in_range_d115_s0() {
let floor_mag = Int::<6>::TEN.pow(110);
for &x in &[257i128, 259, 263, 264, 265, 266] {
let pos = Int::<6>::from_i128(x);
let neg = Int::<6>::from_i128(-x);
for &mode in &MODES {
let c = D::<Int<6>, 0>(pos).cosh_strict_with(mode).0;
let s = D::<Int<6>, 0>(pos).sinh_strict_with(mode).0;
assert!(c > floor_mag, "cosh({x}) too small, mode {mode:?}");
assert!(s > floor_mag, "sinh({x}) too small, mode {mode:?}");
assert!(c >= s, "cosh({x}) < sinh({x}), mode {mode:?}");
assert_eq!(
D::<Int<6>, 0>(neg).cosh_strict_with(mode).0,
c,
"cosh(-{x}) != cosh({x}), mode {mode:?}"
);
let flipped = match mode {
RoundingMode::Floor => RoundingMode::Ceiling,
RoundingMode::Ceiling => RoundingMode::Floor,
m => m,
};
let s_flipped = D::<Int<6>, 0>(pos).sinh_strict_with(flipped).0;
assert_eq!(
D::<Int<6>, 0>(neg).sinh_strict_with(mode).0,
Int::<6>::ZERO - s_flipped,
"sinh(-{x}) != -sinh({x}) under the flipped mode, mode {mode:?}"
);
}
}
}
#[test]
fn cosh_large_arg_in_range_d115_s57() {
let raw = Int::<6>::from_i128(1_336_131_707_362_966_849_971_232_805)
* Int::<6>::TEN.pow(32);
let floor_mag = Int::<6>::TEN.pow(114);
for &mode in &MODES {
let c = D::<Int<6>, 57>(raw).cosh_strict_with(mode).0;
assert!(c > floor_mag, "cosh(133.61..) too small, mode {mode:?}");
assert_eq!(
D::<Int<6>, 57>(Int::<6>::ZERO - raw).cosh_strict_with(mode).0,
c,
"cosh(-133.61..) != cosh(133.61..), mode {mode:?}"
);
}
}
#[test]
fn exp_exp2_deep_negative_in_range_d115() {
let one_ulp = Int::<6>::from_i128(1);
for &x in &[-357i128, -391, -436, -1013, -1089] {
for &mode in &MODES {
let expect = if mode == RoundingMode::Ceiling {
one_ulp
} else {
Int::<6>::ZERO
};
let r0 = Int::<6>::from_i128(x);
assert_eq!(
D::<Int<6>, 0>(r0).exp_strict_with(mode).0,
expect,
"exp({x}) at s0, mode {mode:?}"
);
assert_eq!(
D::<Int<6>, 0>(r0).exp2_strict_with(mode).0,
expect,
"exp2({x}) at s0, mode {mode:?}"
);
let r50 = Int::<6>::from_i128(x) * Int::<6>::TEN.pow(50);
assert_eq!(
D::<Int<6>, 50>(r50).exp_strict_with(mode).0,
expect,
"exp({x}) at s50, mode {mode:?}"
);
assert_eq!(
D::<Int<6>, 50>(r50).exp2_strict_with(mode).0,
expect,
"exp2({x}) at s50, mode {mode:?}"
);
}
}
}
}
#[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))
}
#[test]
fn forward_hyper_schoolbook_match_routed() {
const INPUTS9: [i128; 7] = [
0,
1_000_000,
500_000_000,
1_000_000_000,
2_500_000_000,
-1_000_000_000,
-2_500_000_000,
];
for &u in &INPUTS9 {
let r = raw9(u);
for &mode in &MODES {
assert_eq!(
sinh_schoolbook::<Core, S>(r, mode),
D::<Int<3>, S>(r).sinh_strict_with(mode).0,
"D57 sinh schoolbook != routed at units={u} mode={mode:?}"
);
assert_eq!(
cosh_schoolbook::<Core, S>(r, mode),
D::<Int<3>, S>(r).cosh_strict_with(mode).0,
"D57 cosh schoolbook != routed at units={u} mode={mode:?}"
);
assert_eq!(
tanh_schoolbook::<Core, S>(r, mode),
D::<Int<3>, S>(r).tanh_strict_with(mode).0,
"D57 tanh schoolbook != routed at units={u} mode={mode:?}"
);
}
}
}
#[test]
fn inverse_hyper_schoolbook_match_routed() {
const SINPUTS: [i128; 7] = [
0,
500_000_000,
1_000_000_000,
2_500_000_000,
-500_000_000,
-1_000_000_000,
-2_500_000_000,
];
for &u in &SINPUTS {
let r = raw9(u);
for &mode in &MODES {
assert_eq!(
asinh_schoolbook::<Core, S>(r, mode),
D::<Int<3>, S>(r).asinh_strict_with(mode).0,
"D57 asinh schoolbook != routed at units={u} mode={mode:?}"
);
}
}
const TINPUTS: [i128; 5] = [
0,
250_000_000,
500_000_000,
900_000_000,
-500_000_000,
];
for &u in &TINPUTS {
let r = raw9(u);
for &mode in &MODES {
assert_eq!(
atanh_schoolbook::<Core, S>(r, mode),
D::<Int<3>, S>(r).atanh_strict_with(mode).0,
"D57 atanh schoolbook != routed at units={u} mode={mode:?}"
);
}
}
const AINPUTS: [i128; 4] = [
1_000_000_000,
1_200_000_000,
2_000_000_000,
3_000_000_000,
];
for &u in &AINPUTS {
let r = raw9(u);
for &mode in &MODES {
assert_eq!(
acosh_schoolbook::<Core, S>(r, mode),
D::<Int<3>, S>(r).acosh_strict_with(mode).0,
"D57 acosh schoolbook != routed at units={u} mode={mode:?}"
);
}
}
}
}
#[cfg(feature = "d462")]
mod deep_tiny_directed_d462 {
use super::*;
use crate::int::types::Int;
type Core = crate::types::widths::wide_trig_d462::Core;
const S: u32 = 461;
fn raw_pos() -> Int<24> {
Int::<24>::from_i128(3) * Int::<24>::from_i128(10).pow(344)
}
#[test]
fn asinh_3e117_d462_s461_deep_expands() {
let raw = raw_pos();
let zero = Int::<24>::from_i128(0);
let one = Int::<24>::from_i128(1);
let g = asinh_schoolbook::<Core, S>(raw, RoundingMode::HalfToEven);
assert_eq!(
asinh_schoolbook::<Core, S>(raw, RoundingMode::Ceiling),
g + one,
"asinh(+) Ceiling steps up"
);
assert_eq!(asinh_schoolbook::<Core, S>(raw, RoundingMode::Floor), g, "asinh(+) Floor stays");
assert_eq!(asinh_schoolbook::<Core, S>(raw, RoundingMode::Trunc), g, "asinh(+) Trunc stays");
let gn = asinh_schoolbook::<Core, S>(zero - raw, RoundingMode::HalfToEven);
assert_eq!(gn, zero - g, "asinh(-) nearest = -G");
assert_eq!(
asinh_schoolbook::<Core, S>(zero - raw, RoundingMode::Floor),
gn - one,
"asinh(-) Floor steps down"
);
assert_eq!(
asinh_schoolbook::<Core, S>(zero - raw, RoundingMode::Ceiling),
gn,
"asinh(-) Ceiling stays"
);
assert_eq!(asinh_schoolbook::<Core, S>(zero - raw, RoundingMode::Trunc), gn, "asinh(-) Trunc stays");
let x = D::<Int<24>, S>(raw);
for mode in MODES {
assert_eq!(
x.asinh_strict_with(mode).0,
asinh_schoolbook::<Core, S>(raw, mode),
"asinh public == tier {mode:?}"
);
}
}
}
}