use core::ops::{Add, AddAssign, Neg, Sub, SubAssign};
use oxinum_core::Sign;
use oxinum_int::native::BigUint;
use super::float::{BigFloat, RoundingMode};
use super::nonfinite::{nonfinite_propagate, BinOp};
impl BigFloat {
pub fn add_ref(&self, other: &BigFloat) -> BigFloat {
self.add_ref_with_mode(other, RoundingMode::HalfEven)
}
pub fn add_ref_with_mode(&self, other: &BigFloat, mode: RoundingMode) -> BigFloat {
if let Some(result) = nonfinite_propagate(self, other, BinOp::Add) {
return result;
}
let target_prec = self.precision.max(other.precision);
if self.is_zero() {
return other.clone().round_to_precision(target_prec, mode);
}
if other.is_zero() {
return self.clone().round_to_precision(target_prec, mode);
}
let (common_exp, lhs_mag, rhs_mag) = align_to_common_exp(self, other);
let (out_sign, out_mag) = match (self.sign, other.sign) {
(Sign::Positive, Sign::Positive) | (Sign::Negative, Sign::Negative) => {
(self.sign, &lhs_mag + &rhs_mag)
}
_ => {
if lhs_mag >= rhs_mag {
let diff = lhs_mag.checked_sub(&rhs_mag).unwrap_or_else(BigUint::zero);
(self.sign, diff)
} else {
let diff = rhs_mag.checked_sub(&lhs_mag).unwrap_or_else(BigUint::zero);
(other.sign, diff)
}
}
};
BigFloat::from_parts(out_sign, out_mag, common_exp, target_prec, mode)
}
pub fn sub_ref(&self, other: &BigFloat) -> BigFloat {
self.sub_ref_with_mode(other, RoundingMode::HalfEven)
}
pub fn sub_ref_with_mode(&self, other: &BigFloat, mode: RoundingMode) -> BigFloat {
self.add_ref_with_mode(&other.neg(), mode)
}
}
fn align_to_common_exp(a: &BigFloat, b: &BigFloat) -> (i64, BigUint, BigUint) {
if a.exponent == b.exponent {
(a.exponent, a.mantissa.clone(), b.mantissa.clone())
} else if a.exponent > b.exponent {
let shift = (a.exponent - b.exponent) as u64;
let lhs = a.mantissa.shl_bits(shift);
(b.exponent, lhs, b.mantissa.clone())
} else {
let shift = (b.exponent - a.exponent) as u64;
let rhs = b.mantissa.shl_bits(shift);
(a.exponent, a.mantissa.clone(), rhs)
}
}
impl Add<&BigFloat> for &BigFloat {
type Output = BigFloat;
#[inline]
fn add(self, rhs: &BigFloat) -> BigFloat {
self.add_ref(rhs)
}
}
impl Add<BigFloat> for BigFloat {
type Output = BigFloat;
#[inline]
fn add(self, rhs: BigFloat) -> BigFloat {
self.add_ref(&rhs)
}
}
impl Add<&BigFloat> for BigFloat {
type Output = BigFloat;
#[inline]
fn add(self, rhs: &BigFloat) -> BigFloat {
self.add_ref(rhs)
}
}
impl Add<BigFloat> for &BigFloat {
type Output = BigFloat;
#[inline]
fn add(self, rhs: BigFloat) -> BigFloat {
self.add_ref(&rhs)
}
}
impl Sub<&BigFloat> for &BigFloat {
type Output = BigFloat;
#[inline]
fn sub(self, rhs: &BigFloat) -> BigFloat {
self.sub_ref(rhs)
}
}
impl Sub<BigFloat> for BigFloat {
type Output = BigFloat;
#[inline]
fn sub(self, rhs: BigFloat) -> BigFloat {
self.sub_ref(&rhs)
}
}
impl Sub<&BigFloat> for BigFloat {
type Output = BigFloat;
#[inline]
fn sub(self, rhs: &BigFloat) -> BigFloat {
self.sub_ref(rhs)
}
}
impl Sub<BigFloat> for &BigFloat {
type Output = BigFloat;
#[inline]
fn sub(self, rhs: BigFloat) -> BigFloat {
self.sub_ref(&rhs)
}
}
impl AddAssign<&BigFloat> for BigFloat {
#[inline]
fn add_assign(&mut self, rhs: &BigFloat) {
*self = self.add_ref(rhs);
}
}
impl AddAssign<BigFloat> for BigFloat {
#[inline]
fn add_assign(&mut self, rhs: BigFloat) {
*self = self.add_ref(&rhs);
}
}
impl SubAssign<&BigFloat> for BigFloat {
#[inline]
fn sub_assign(&mut self, rhs: &BigFloat) {
*self = self.sub_ref(rhs);
}
}
impl SubAssign<BigFloat> for BigFloat {
#[inline]
fn sub_assign(&mut self, rhs: BigFloat) {
*self = self.sub_ref(&rhs);
}
}
impl Neg for BigFloat {
type Output = BigFloat;
#[inline]
fn neg(self) -> BigFloat {
BigFloat::neg(&self)
}
}
impl Neg for &BigFloat {
type Output = BigFloat;
#[inline]
fn neg(self) -> BigFloat {
BigFloat::neg(self)
}
}