use core::ops::{Div, DivAssign, Rem, RemAssign};
use oxinum_core::{OxiNumError, OxiNumResult, Sign};
use super::float::{BigFloat, RoundingMode};
use super::nonfinite::{nonfinite_binop, nonfinite_propagate, BinOp};
const DIV_GUARD_BITS: u32 = 8;
impl BigFloat {
pub fn div_ref(&self, other: &BigFloat) -> OxiNumResult<BigFloat> {
self.div_ref_with_mode(other, RoundingMode::HalfEven)
}
pub fn div_ref_with_mode(
&self,
other: &BigFloat,
mode: RoundingMode,
) -> OxiNumResult<BigFloat> {
if let Some(result) = nonfinite_propagate(self, other, BinOp::Div) {
return Ok(result);
}
if other.is_zero() {
return Err(OxiNumError::DivByZero);
}
let target_prec = self.precision.max(other.precision);
if self.is_zero() {
return Ok(BigFloat::zero(target_prec));
}
let out_sign = if self.sign == other.sign {
Sign::Positive
} else {
Sign::Negative
};
let shift_bits = (target_prec + DIV_GUARD_BITS) as u64;
let scaled = self.mantissa.shl_bits(shift_bits);
let quotient = &scaled / &other.mantissa;
let exp_after_div = self
.exponent
.saturating_sub(other.exponent)
.saturating_sub(shift_bits as i64);
Ok(BigFloat::from_parts(
out_sign,
quotient,
exp_after_div,
target_prec,
mode,
))
}
}
fn div_ieee(a: &BigFloat, b: &BigFloat) -> BigFloat {
if let Some(result) = nonfinite_binop(a, b, BinOp::Div) {
return result;
}
a.div_ref(b)
.expect("div_ieee: finite non-zero divisor guaranteed by nonfinite_binop guard")
}
impl Div<&BigFloat> for &BigFloat {
type Output = BigFloat;
#[inline]
fn div(self, rhs: &BigFloat) -> BigFloat {
div_ieee(self, rhs)
}
}
impl Div<BigFloat> for BigFloat {
type Output = BigFloat;
#[inline]
fn div(self, rhs: BigFloat) -> BigFloat {
div_ieee(&self, &rhs)
}
}
impl Div<&BigFloat> for BigFloat {
type Output = BigFloat;
#[inline]
fn div(self, rhs: &BigFloat) -> BigFloat {
div_ieee(&self, rhs)
}
}
impl Div<BigFloat> for &BigFloat {
type Output = BigFloat;
#[inline]
fn div(self, rhs: BigFloat) -> BigFloat {
div_ieee(self, &rhs)
}
}
impl DivAssign<&BigFloat> for BigFloat {
#[inline]
fn div_assign(&mut self, rhs: &BigFloat) {
*self = div_ieee(self, rhs);
}
}
impl DivAssign<BigFloat> for BigFloat {
#[inline]
fn div_assign(&mut self, rhs: BigFloat) {
*self = div_ieee(self, &rhs);
}
}
fn rem_core(a: &BigFloat, b: &BigFloat) -> BigFloat {
if a.is_nan() || b.is_nan() || a.is_infinite() || b.is_zero() {
return BigFloat::nan(a.precision.max(b.precision));
}
if b.is_infinite() {
return a.clone();
}
if a.is_zero() {
return BigFloat::zero(a.precision.max(b.precision));
}
let prec = a.precision.max(b.precision);
let q = div_ieee(a, b);
let q_trunc = q.trunc_to_integer(prec, RoundingMode::ToZero);
a.clone() - q_trunc * b.clone()
}
impl BigFloat {
fn trunc_to_integer(&self, prec: u32, mode: RoundingMode) -> Self {
if self.is_zero() {
return BigFloat::zero(prec);
}
if self.exponent >= 0 {
return self.clone().with_precision(prec, mode);
}
let frac_bits = (-self.exponent) as u64;
let mant_bits = self.mantissa.bit_length();
if frac_bits >= mant_bits {
return BigFloat::zero(prec);
}
let int_mantissa = self.mantissa.shr_bits(frac_bits);
BigFloat::from_parts(self.sign, int_mantissa, 0, prec, mode)
}
}
impl Rem<&BigFloat> for &BigFloat {
type Output = BigFloat;
#[inline]
fn rem(self, rhs: &BigFloat) -> BigFloat {
rem_core(self, rhs)
}
}
impl Rem<BigFloat> for BigFloat {
type Output = BigFloat;
#[inline]
fn rem(self, rhs: BigFloat) -> BigFloat {
rem_core(&self, &rhs)
}
}
impl Rem<&BigFloat> for BigFloat {
type Output = BigFloat;
#[inline]
fn rem(self, rhs: &BigFloat) -> BigFloat {
rem_core(&self, rhs)
}
}
impl Rem<BigFloat> for &BigFloat {
type Output = BigFloat;
#[inline]
fn rem(self, rhs: BigFloat) -> BigFloat {
rem_core(self, &rhs)
}
}
impl RemAssign<&BigFloat> for BigFloat {
#[inline]
fn rem_assign(&mut self, rhs: &BigFloat) {
*self = rem_core(self, rhs);
}
}
impl RemAssign<BigFloat> for BigFloat {
#[inline]
fn rem_assign(&mut self, rhs: BigFloat) {
*self = rem_core(self, &rhs);
}
}