use crate::algos::ln::ln_series_2limb::STRICT_GUARD;
use crate::algos::support::wide_trig_core::WideTrigCore;
use crate::algos::trig::trig_series_2limb::{atan_fixed, sin_cos_fixed, sin_fixed, to_fixed};
use crate::int::types::Int;
use crate::support::rounding::RoundingMode;
#[inline]
#[must_use]
pub(crate) fn sin_schoolbook<C: WideTrigCore, const SCALE: u32>(
raw: C::Storage,
mode: RoundingMode,
) -> C::Storage {
C::round_to_storage_directed(C::GUARD, SCALE, mode, &mut |guard| {
C::sin_fixed::<SCALE>(C::to_work_scaled(raw, guard), SCALE + guard)
})
}
#[inline]
#[must_use]
pub(crate) fn cos_schoolbook<C: WideTrigCore, const SCALE: u32>(
raw: C::Storage,
mode: RoundingMode,
) -> C::Storage {
C::round_to_storage_directed(C::GUARD, SCALE, mode, &mut |guard| {
C::cos_fixed::<SCALE>(C::to_work_scaled(raw, guard), SCALE + guard)
})
}
#[inline]
#[must_use]
pub(crate) fn tan_schoolbook<C: WideTrigCore, const SCALE: u32>(
raw: C::Storage,
mode: RoundingMode,
) -> C::Storage {
C::round_to_storage_directed(C::GUARD, SCALE, mode, &mut |guard| {
let w = SCALE + guard;
let (sin_w, cos_w) = C::sin_cos_fixed::<SCALE>(C::to_work_scaled(raw, guard), w);
if cos_w == C::zero() {
panic!("schoolbook tan: cosine is zero (argument is an odd multiple of pi/2)");
}
C::div(sin_w, cos_w, w)
})
}
#[inline]
#[must_use]
pub(crate) fn atan_schoolbook<C: WideTrigCore, const SCALE: u32>(
raw: C::Storage,
mode: RoundingMode,
) -> C::Storage {
C::round_to_storage_directed(C::GUARD, SCALE, mode, &mut |guard| {
C::atan_fixed::<SCALE>(C::to_work_scaled(raw, guard), SCALE + guard)
})
}
#[inline]
#[must_use]
fn sin_schoolbook_raw<const SCALE: u32>(raw: i128, mode: RoundingMode) -> i128 {
if raw == 0 {
return 0;
}
let w = SCALE + STRICT_GUARD;
sin_fixed(to_fixed(raw), w)
.round_to_i128_with(w, SCALE, mode)
.unwrap_or_else(|| crate::support::diagnostics::overflow_panic_with_scale("sin", SCALE))
}
#[inline]
#[must_use]
fn cos_schoolbook_raw<const SCALE: u32>(raw: i128, mode: RoundingMode) -> i128 {
let w = SCALE + STRICT_GUARD;
let (_s, c) = sin_cos_fixed(to_fixed(raw), w);
c.round_to_i128_with(w, SCALE, mode)
.unwrap_or_else(|| crate::support::diagnostics::overflow_panic_with_scale("cos", SCALE))
}
#[inline]
#[must_use]
fn tan_schoolbook_raw<const SCALE: u32>(raw: i128, mode: RoundingMode) -> i128 {
if raw == 0 {
return 0;
}
let w = SCALE + STRICT_GUARD;
let (s, c) = sin_cos_fixed(to_fixed(raw), w);
if c.is_zero() {
panic!("schoolbook tan: cosine is zero (argument is an odd multiple of pi/2)");
}
s.div(c, w)
.round_to_i128_with(w, SCALE, mode)
.unwrap_or_else(|| crate::support::diagnostics::overflow_panic_with_scale("tan", SCALE))
}
#[inline]
#[must_use]
fn atan_schoolbook_raw<const SCALE: u32>(raw: i128, mode: RoundingMode) -> i128 {
use crate::types::consts::DecimalConstants;
if raw == 0 {
return 0;
}
let one_bits: i128 = 10_i128.pow(SCALE);
if crate::support::rounding::is_nearest_mode(mode) {
if raw == one_bits {
return <crate::D<Int<2>, SCALE> as DecimalConstants>::quarter_pi().0.as_i128();
}
if raw == -one_bits {
return -<crate::D<Int<2>, SCALE> as DecimalConstants>::quarter_pi().0.as_i128();
}
}
let w = SCALE + STRICT_GUARD;
atan_fixed(to_fixed(raw), w)
.round_to_i128_with(w, SCALE, mode)
.unwrap_or_else(|| crate::support::diagnostics::overflow_panic_with_scale("atan", SCALE))
}
#[inline]
#[must_use]
pub(crate) fn sin_schoolbook_narrow<const SCALE: u32>(raw: Int<2>, mode: RoundingMode) -> Int<2> {
Int::<2>::from_i128(sin_schoolbook_raw::<SCALE>(raw.as_i128(), mode))
}
#[inline]
#[must_use]
pub(crate) fn cos_schoolbook_narrow<const SCALE: u32>(raw: Int<2>, mode: RoundingMode) -> Int<2> {
Int::<2>::from_i128(cos_schoolbook_raw::<SCALE>(raw.as_i128(), mode))
}
#[inline]
#[must_use]
pub(crate) fn tan_schoolbook_narrow<const SCALE: u32>(raw: Int<2>, mode: RoundingMode) -> Int<2> {
Int::<2>::from_i128(tan_schoolbook_raw::<SCALE>(raw.as_i128(), mode))
}
#[inline]
#[must_use]
pub(crate) fn atan_schoolbook_narrow<const SCALE: u32>(raw: Int<2>, mode: RoundingMode) -> Int<2> {
Int::<2>::from_i128(atan_schoolbook_raw::<SCALE>(raw.as_i128(), mode))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::D;
use crate::support::rounding::RoundingMode;
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 NARROW_TRIG_INPUTS: [i128; 11] = [
0,
500_000_000_000,
750_000_000_000,
1_000_000_000_000,
1_200_000_000_000,
3_000_000_000_000,
7_000_000_000_000,
-500_000_000_000,
-1_200_000_000_000,
-7_000_000_000_000,
13_000_000_000_000,
];
#[test]
fn sin_schoolbook_narrow_matches_routed_kernel() {
for &raw in &NARROW_TRIG_INPUTS {
for &mode in &MODES {
let school = sin_schoolbook_narrow::<S38>(d38(raw).0, mode);
let routed = d38(raw).sin_strict_with(mode).0;
assert_eq!(
school, routed,
"sin schoolbook != routed at raw={raw} mode={mode:?}"
);
}
}
}
#[test]
fn cos_schoolbook_narrow_matches_routed_kernel() {
for &raw in &NARROW_TRIG_INPUTS {
for &mode in &MODES {
let school = cos_schoolbook_narrow::<S38>(d38(raw).0, mode);
let routed = d38(raw).cos_strict_with(mode).0;
assert_eq!(
school, routed,
"cos schoolbook != routed at raw={raw} mode={mode:?}"
);
}
}
}
#[test]
fn tan_schoolbook_narrow_matches_routed_kernel() {
for &raw in &NARROW_TRIG_INPUTS {
for &mode in &MODES {
let school = tan_schoolbook_narrow::<S38>(d38(raw).0, mode);
let routed = d38(raw).tan_strict_with(mode).0;
assert_eq!(
school, routed,
"tan schoolbook != routed at raw={raw} mode={mode:?}"
);
}
}
}
#[test]
fn cos_near_extremum_directed_rounding_s28() {
const FLOOR: i128 = 9_999_999_999_999_999_999_999_999_999; const UP: i128 = 10_000_000_000_000_000_000_000_000_000; for &raw in &[
31_415_926_535_897_932_384_626_433_832_795_i128,
-31_415_926_535_897_932_384_626_433_832_795_i128,
] {
for &mode in &MODES {
let want = match mode {
RoundingMode::Trunc | RoundingMode::Floor => FLOOR,
_ => UP,
};
let got = D::<Int<2>, 28>(Int::<2>::from_i128(raw))
.cos_strict_with(mode)
.0
.as_i128();
assert_eq!(got, want, "cos near-extremum mis-rounded raw={raw} mode={mode:?}");
}
}
}
#[test]
fn atan_schoolbook_narrow_matches_routed_kernel() {
let one_bits: i128 = 10_i128.pow(S38);
for &raw in &NARROW_TRIG_INPUTS {
if raw == one_bits || raw == -one_bits {
continue;
}
for &mode in &MODES {
let school = atan_schoolbook_narrow::<S38>(d38(raw).0, mode);
let routed = d38(raw).atan_strict_with(mode).0;
assert_eq!(
school, routed,
"atan schoolbook != routed at raw={raw} mode={mode:?}"
);
}
}
}
#[test]
fn atan_schoolbook_narrow_endpoint_pi_over_4_is_correctly_rounded() {
const PI_OVER_4_S12: i128 = 785_398_163_397;
let one_bits: i128 = 10_i128.pow(S38);
for &mode in &[
RoundingMode::HalfToEven,
RoundingMode::HalfAwayFromZero,
RoundingMode::HalfTowardZero,
RoundingMode::Trunc,
RoundingMode::Floor,
] {
assert_eq!(
atan_schoolbook_narrow::<S38>(d38(one_bits).0, mode),
Int::<2>::from_i128(PI_OVER_4_S12),
"atan(1) schoolbook not correctly-rounded pi/4 at mode={mode:?}"
);
}
for &mode in &[
RoundingMode::HalfToEven,
RoundingMode::HalfAwayFromZero,
RoundingMode::HalfTowardZero,
RoundingMode::Trunc,
RoundingMode::Ceiling,
] {
assert_eq!(
atan_schoolbook_narrow::<S38>(d38(-one_bits).0, mode),
Int::<2>::from_i128(-PI_OVER_4_S12),
"atan(-1) schoolbook not correctly-rounded -pi/4 at 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 raw(units: i128) -> Int<3> {
Int::<3>::from_i128(units * 10_i128.pow(10))
}
const INPUTS9: [i128; 9] = [
0,
500_000_000,
750_000_000,
1_000_000_000,
1_200_000_000,
3_000_000_000,
7_000_000_000,
-1_200_000_000,
13_000_000_000,
];
#[test]
fn sin_cos_tan_atan_schoolbook_match_routed() {
for &u in &INPUTS9 {
let r = raw(u);
for &mode in &MODES {
assert_eq!(
sin_schoolbook::<Core, S>(r, mode),
D::<Int<3>, S>(r).sin_strict_with(mode).0,
"D57 sin schoolbook != routed at units={u} mode={mode:?}"
);
assert_eq!(
cos_schoolbook::<Core, S>(r, mode),
D::<Int<3>, S>(r).cos_strict_with(mode).0,
"D57 cos schoolbook != routed at units={u} mode={mode:?}"
);
assert_eq!(
tan_schoolbook::<Core, S>(r, mode),
D::<Int<3>, S>(r).tan_strict_with(mode).0,
"D57 tan schoolbook != routed at units={u} mode={mode:?}"
);
assert_eq!(
atan_schoolbook::<Core, S>(r, mode),
D::<Int<3>, S>(r).atan_strict_with(mode).0,
"D57 atan schoolbook != routed at units={u} mode={mode:?}"
);
}
}
}
}
#[cfg(any(feature = "d307", feature = "wide", feature = "x-wide"))]
mod wide_d307 {
use super::*;
use crate::types::widths::wide_trig_d307::Core;
const S: u32 = 30;
fn raw(units: i128) -> Int<16> {
Int::<16>::from_i128(units * 10_i128.pow(21))
}
const INPUTS9: [i128; 7] = [
0,
500_000_000,
1_200_000_000,
3_000_000_000,
7_000_000_000,
-1_200_000_000,
13_000_000_000,
];
#[test]
fn sin_cos_tan_atan_schoolbook_match_routed() {
for &u in &INPUTS9 {
let r = raw(u);
for &mode in &MODES {
assert_eq!(
sin_schoolbook::<Core, S>(r, mode),
D::<Int<16>, S>(r).sin_strict_with(mode).0,
"D307 sin schoolbook != routed at units={u} mode={mode:?}"
);
assert_eq!(
cos_schoolbook::<Core, S>(r, mode),
D::<Int<16>, S>(r).cos_strict_with(mode).0,
"D307 cos schoolbook != routed at units={u} mode={mode:?}"
);
assert_eq!(
tan_schoolbook::<Core, S>(r, mode),
D::<Int<16>, S>(r).tan_strict_with(mode).0,
"D307 tan schoolbook != routed at units={u} mode={mode:?}"
);
assert_eq!(
atan_schoolbook::<Core, S>(r, mode),
D::<Int<16>, S>(r).atan_strict_with(mode).0,
"D307 atan schoolbook != routed at units={u} mode={mode:?}"
);
}
}
}
}
}