use super::reduce_pi_2_large::reduce_pi_2_large;
use crate::traits::{CastInto as _, Float, FloatConsts, Int as _, Like};
pub(crate) trait ReducePi2<L = Like<Self>>: FloatConsts {
fn frac_pi_2_hi() -> Self;
fn frac_pi_2_hiex() -> Self;
fn frac_pi_2_mi() -> Self;
fn frac_pi_2_miex() -> Self;
fn frac_pi_2_lo() -> Self;
fn frac_pi_2_loex() -> Self;
fn max_reduce_pi_2_medium() -> Self;
const REDUCE_PI_2_MEDIUM_TH1: Self::Exp;
const REDUCE_PI_2_MEDIUM_TH2: Self::Exp;
type SrcChunks: Sized + AsRef<[u32]>;
fn reduce_pi_2_prepare(x: Self) -> (Self::SrcChunks, i16, usize);
fn reduce_pi_2_compress(qp: &[u64; 20], qe: i16, ih: u32, jz: usize) -> (Self, Self);
}
pub(crate) fn reduce_pi_2<F: ReducePi2>(x: F) -> (u8, F, F) {
let xabs = x.abs();
if xabs <= F::frac_pi_4() {
(0, x, F::ZERO)
} else if xabs < F::max_reduce_pi_2_medium() {
reduce_pi_2_medium(x)
} else {
let (x_chunks, e0, jk) = F::reduce_pi_2_prepare(x);
let mut qp: [u64; 20] = [0; 20];
let (ih, jz, n, qe) = reduce_pi_2_large(x_chunks.as_ref(), e0, jk, &mut qp);
let (y_hi, y_lo) = F::reduce_pi_2_compress(&qp, qe, ih, jz);
if x.sign() {
(n.wrapping_neg() & 3, -y_hi, -y_lo)
} else {
(n & 3, y_hi, y_lo)
}
}
}
fn reduce_pi_2_medium<F: ReducePi2>(x: F) -> (u8, F, F) {
let (f_n, n) = round_fi(x * F::frac_2_pi());
let xexp = x.exponent();
let mut r = x - f_n * F::frac_pi_2_hi();
let mut w = f_n * F::frac_pi_2_hiex();
let mut y0 = (r - w).purify();
if (xexp - y0.exponent()) > F::REDUCE_PI_2_MEDIUM_TH1 {
let t = r;
w = f_n * F::frac_pi_2_mi();
r = (t - w).purify();
w = f_n * F::frac_pi_2_miex() - ((t - r) - w);
y0 = (r - w).purify();
if (xexp - y0.exponent()) > F::REDUCE_PI_2_MEDIUM_TH2 {
let t = r;
w = f_n * F::frac_pi_2_lo();
r = (t - w).purify();
w = f_n * F::frac_pi_2_loex() - ((t - r) - w);
y0 = (r - w).purify();
}
}
let y1 = (r - y0).purify() - w;
(n as u8 & 3, y0, y1)
}
pub(super) fn round_fi<F: Float>(x: F) -> (F, i32) {
let e = x.raw_exp();
if e < F::EXP_OFFSET {
(F::one().copysign(x), 1 - (i32::from(x.sign()) << 1))
} else {
let shift = F::RawExp::from(F::MANT_BITS) - (e - F::EXP_OFFSET);
let imask = F::Raw::MAX << shift;
let fmask = !imask;
let xraw = x.to_raw();
let fpart = xraw & fmask;
let mut ipart_raw = xraw & !fmask;
let mut ipart_i: i32 = (x.mant() >> shift).cast_into();
if fpart > (fmask / F::Raw::TWO) {
ipart_raw += fmask + F::Raw::ONE;
ipart_i += 1;
}
let ipart_f = F::from_raw(ipart_raw);
if x.sign() {
ipart_i = -ipart_i;
}
(ipart_f, ipart_i)
}
}