use core::{
cmp::{max, min},
ops::{Rem, RemAssign},
};
use crate::{
abs_bits, abs_bits_sticky, exp_bits, f256, norm_bit, sign_bits_hi,
signif, BigUInt, BinEncAnySpecial, DivRem, HiLo, FRACTION_BITS,
HI_EXP_MASK, HI_FRACTION_BITS, MAX_HI, SIGNIFICAND_BITS, U256, U512,
};
#[inline]
pub(crate) fn rem(x: f256, y: f256) -> f256 {
let mut abs_bits_x = abs_bits(&x);
let mut abs_bits_y = abs_bits(&y);
let abs_bits_sticky_x = abs_bits_sticky(&abs_bits_x);
let abs_bits_sticky_y = abs_bits_sticky(&abs_bits_y);
if (abs_bits_sticky_x, abs_bits_sticky_y).any_special() {
let max_abs_bits_sticky = max(abs_bits_sticky_x, abs_bits_sticky_y);
if max_abs_bits_sticky > HI_EXP_MASK
|| abs_bits_sticky_y == 0
|| abs_bits_sticky_x == HI_EXP_MASK
{
return f256::NAN;
}
return x;
}
if abs_bits_x < abs_bits_y {
return x;
}
let mut exp_bits_x = exp_bits(&abs_bits_x);
let norm_bit_x = norm_bit(&abs_bits_x);
let mut signif_x = signif(&abs_bits_x);
let mut exp_bits_y = exp_bits(&abs_bits_y);
let norm_bit_y = norm_bit(&abs_bits_y);
let mut signif_y = signif(&abs_bits_y);
if signif_x == signif_y {
return f256::ZERO;
}
let n_bits = exp_bits_x + norm_bit_y - exp_bits_y - norm_bit_x;
let sh = n_bits % U256::BITS;
let mut t = U512::from_hi_lo(U256::ZERO, signif_x);
t <<= sh;
let mut abs_bits_z = t % signif_y;
for _ in 0..n_bits >> 8 {
t = U512::from_hi_lo(abs_bits_z, U256::ZERO);
abs_bits_z = t % signif_y;
if abs_bits_z.is_zero() {
break;
}
}
if abs_bits_z.is_zero() {
return f256::ZERO;
}
let shift_z = min(
FRACTION_BITS - abs_bits_z.msb(),
exp_bits_y.saturating_sub(1),
);
abs_bits_z <<= shift_z;
let exp_bits_z_m1 = (exp_bits_y - shift_z).saturating_sub(1);
abs_bits_z.hi.0 += (exp_bits_z_m1 as u128) << HI_FRACTION_BITS;
f256 {
bits: U256::new(sign_bits_hi(&x) | abs_bits_z.hi.0, abs_bits_z.lo.0),
}
}
impl Rem for f256 {
type Output = Self;
fn rem(self, rhs: Self) -> Self::Output {
rem(self, rhs)
}
}
forward_ref_binop!(impl Rem, rem);
forward_op_assign!(impl RemAssign, rem_assign, Rem, rem);