use crate::double::{NormDouble, SemiDouble};
use crate::traits::{CastFrom as _, CastInto as _, FloatConsts, Int as _, Like};
pub(crate) trait ReduceHalfMulPi<L = Like<Self>>: FloatConsts {
fn pi_ex() -> SemiDouble<Self>;
}
pub(crate) fn reduce_half_mul_pi<F: ReduceHalfMulPi>(x: F) -> (u8, NormDouble<F>) {
let xexp = x.exponent();
if xexp < -F::Exp::TWO {
let scale = F::exp2i_fast(F::Exp::cast_from(F::MANT_BITS));
let descale = F::exp2i_fast(-F::Exp::cast_from(F::MANT_BITS));
let sx = SemiDouble::new(x * scale);
let y = (sx * F::pi_ex()).pmul1(descale).to_norm();
(0, y)
} else if xexp == -F::Exp::TWO {
let fpart = x - F::half().copysign(x);
let n = if x.sign() { 3 } else { 1 };
let fpart = SemiDouble::new(fpart);
let y = (fpart * F::pi_ex()).to_norm();
(n, y)
} else if xexp < F::Exp::cast_from(F::MANT_BITS) {
let shift: u8 = (F::Exp::cast_from(F::MANT_BITS) - (xexp + F::Exp::ONE)).cast_into();
let fmask = !(F::Raw::MAX << shift);
let xraw = x.to_raw();
let fpart = xraw & fmask;
let ipart = xraw & !fmask;
let mut ipart_i = x.mant() >> shift;
let mut fpart_f = x - F::from_raw(ipart);
if fpart > (fmask / F::Raw::TWO) {
fpart_f = fpart_f - F::half().copysign(x);
ipart_i += F::Raw::ONE;
}
let mut n: u8 = ipart_i.cast_into();
if x.sign() {
n = n.wrapping_neg();
}
let fpart = SemiDouble::new(fpart_f);
let y = (fpart * F::pi_ex()).to_norm();
(n & 3, y)
} else if xexp == F::Exp::cast_from(F::MANT_BITS) {
let n: u8 = ((x.to_raw() & F::Raw::ONE) << 1).cast_into();
if x.sign() {
(n, NormDouble::ZERO)
} else {
(n.wrapping_neg() & 3, NormDouble::ZERO)
}
} else {
(0, NormDouble::ZERO)
}
}