use crate::classify::*;
use crate::interval::*;
use crate::simd::*;
use core::arch::x86_64::*;
use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
impl Neg for Interval {
type Output = Self;
fn neg(self) -> Self {
Self {
rep: swap(self.rep),
}
}
}
impl Add for Interval {
type Output = Self;
fn add(self, rhs: Self) -> Self {
let _r = RoundUpContext::new();
let x = self.rep;
let y = rhs.rep;
Self { rep: add(x, y) }
}
}
impl Sub for Interval {
type Output = Self;
fn sub(self, rhs: Self) -> Self {
let _r = RoundUpContext::new();
let x = self.rep;
let y = swap(rhs.rep);
Self { rep: add(x, y) }
}
}
impl Mul for Interval {
type Output = Self;
#[allow(clippy::many_single_char_names)]
fn mul(self, rhs: Self) -> Self {
match (self.classify() << 4) | rhs.classify() {
C_E_E | C_E_M | C_E_N0 | C_E_N1 | C_E_P0 | C_E_P1 | C_E_Z | C_M_E | C_N0_E | C_N1_E
| C_P0_E | C_P1_E | C_Z_E => Self::empty(),
C_M_Z | C_N0_Z | C_N1_Z | C_P0_Z | C_P1_Z | C_Z_M | C_Z_N0 | C_Z_N1 | C_Z_P0
| C_Z_P1 | C_Z_Z => Self::zero(),
C_M_M => {
let _r = RoundUpContext::new();
let x = dup_lo(self.rep);
let y = swap(rhs.rep);
let xy = mul(x, y);
let z = dup_hi(self.rep);
let w = rhs.rep;
let zw = mul(z, w);
let r = unsafe { _mm_max_pd(xy, zw) };
Self { rep: r }
}
C_M_N0 | C_M_N1 => {
let _r = RoundUpContext::new();
let x = swap(self.rep);
let y = dup_lo(rhs.rep);
Self { rep: mul(x, y) }
}
C_M_P0 | C_M_P1 => {
let _r = RoundUpContext::new();
let x = self.rep;
let y = dup_hi(rhs.rep);
Self { rep: mul(x, y) }
}
C_N0_M | C_N1_M => {
let _r = RoundUpContext::new();
let x = dup_lo(self.rep);
let y = swap(rhs.rep);
Self { rep: mul(x, y) }
}
C_N0_N0 | C_N0_N1 | C_N1_N0 | C_N1_N1 => {
let _r = RoundUpContext::new();
let x0 = swap(self.rep);
let x = negate_lo(x0);
let y = swap(rhs.rep);
Self { rep: mul(x, y) }
}
C_N0_P0 | C_N0_P1 | C_N1_P0 | C_N1_P1 => {
let _r = RoundUpContext::new();
let x = self.rep;
let y0 = negate_lo(rhs.rep);
let y = swap(y0);
Self { rep: mul(x, y) }
}
C_P0_M | C_P1_M => {
let _r = RoundUpContext::new();
let x = dup_hi(self.rep);
let y = rhs.rep;
Self { rep: mul(x, y) }
}
C_P0_N0 | C_P0_N1 | C_P1_N0 | C_P1_N1 => {
let _r = RoundUpContext::new();
let x0 = negate_lo(self.rep);
let x = swap(x0);
let y = rhs.rep;
Self { rep: mul(x, y) }
}
C_P0_P0 | C_P0_P1 | C_P1_P0 | C_P1_P1 => {
let _r = RoundUpContext::new();
let x = self.rep;
let y = negate_lo(rhs.rep);
Self { rep: mul(x, y) }
}
_ => unreachable!(),
}
}
}
impl Div for Interval {
type Output = Self;
fn div(self, rhs: Self) -> Self {
match (self.classify() << 4) | rhs.classify() {
C_E_E | C_E_M | C_E_N0 | C_E_N1 | C_E_P0 | C_E_P1 | C_E_Z | C_M_E | C_M_Z | C_N0_E
| C_N0_Z | C_N1_E | C_N1_Z | C_P0_E | C_P0_Z | C_P1_E | C_P1_Z | C_Z_E | C_Z_Z => {
Self::empty()
}
C_M_M | C_M_N0 | C_M_P0 | C_N0_M | C_N1_M | C_P0_M | C_P1_M => Self::entire(),
C_Z_M | C_Z_N0 | C_Z_N1 | C_Z_P0 | C_Z_P1 => Self::zero(),
C_M_N1 => {
let _r = RoundUpContext::new();
let x = swap(self.rep);
let y0 = swap(rhs.rep);
let y1 = negate_lo(y0);
let y = dup_lo(y1);
Self { rep: div(x, y) }
}
C_M_P1 => {
let _r = RoundUpContext::new();
let x = self.rep;
let y0 = negate_lo(rhs.rep);
let y = dup_lo(y0);
Self { rep: div(x, y) }
}
C_N0_N0 | C_N1_N0 => {
let _r = RoundUpContext::new();
let x = swap(self.rep);
let y = rhs.rep;
Self {
rep: set_hi_inf(div(x, y)),
}
}
C_N0_N1 | C_N1_N1 => {
let _r = RoundUpContext::new();
let x0 = negate_lo(self.rep);
let x = swap(x0);
let y = rhs.rep;
Self { rep: div(x, y) }
}
C_N0_P0 | C_N1_P0 => {
let _r = RoundUpContext::new();
let x = self.rep;
let y = rhs.rep;
Self {
rep: set_lo_inf(div(x, y)),
}
}
C_N0_P1 | C_N1_P1 => {
let _r = RoundUpContext::new();
let x = self.rep;
let y = negate_lo(rhs.rep);
Self { rep: div(x, y) }
}
C_P0_N0 | C_P1_N0 => {
let _r = RoundUpContext::new();
let x = swap(self.rep);
let y = swap(rhs.rep);
Self {
rep: set_lo_inf(div(x, y)),
}
}
C_P0_N1 | C_P1_N1 => {
let _r = RoundUpContext::new();
let x0 = swap(self.rep);
let x = negate_lo(x0);
let y = swap(rhs.rep);
Self { rep: div(x, y) }
}
C_P0_P0 | C_P1_P0 => {
let _r = RoundUpContext::new();
let x = self.rep;
let y = swap(rhs.rep);
Self {
rep: set_hi_inf(div(x, y)),
}
}
C_P0_P1 | C_P1_P1 => {
let _r = RoundUpContext::new();
let x = self.rep;
let y0 = negate_lo(rhs.rep);
let y = swap(y0);
Self { rep: div(x, y) }
}
_ => unreachable!(),
}
}
}
impl Neg for DecoratedInterval {
type Output = Self;
fn neg(self) -> Self {
if self.is_nai() {
return self;
}
Self::set_dec(-self.x, self.d)
}
}
impl Add for DecoratedInterval {
type Output = Self;
fn add(self, rhs: Self) -> Self {
#[allow(clippy::suspicious_arithmetic_impl)]
if self.is_nai() || rhs.is_nai() {
return Self::nai();
}
Self::set_dec(self.x + rhs.x, self.d.min(rhs.d))
}
}
impl Sub for DecoratedInterval {
type Output = Self;
fn sub(self, rhs: Self) -> Self {
#[allow(clippy::suspicious_arithmetic_impl)]
if self.is_nai() || rhs.is_nai() {
return Self::nai();
}
Self::set_dec(self.x - rhs.x, self.d.min(rhs.d))
}
}
impl Mul for DecoratedInterval {
type Output = Self;
fn mul(self, rhs: Self) -> Self {
#[allow(clippy::suspicious_arithmetic_impl)]
if self.is_nai() || rhs.is_nai() {
return Self::nai();
}
Self::set_dec(self.x * rhs.x, self.d.min(rhs.d))
}
}
impl Div for DecoratedInterval {
type Output = Self;
fn div(self, rhs: Self) -> Self {
#[allow(clippy::suspicious_arithmetic_impl)]
if self.is_nai() || rhs.is_nai() {
return Self::nai();
}
let d = if rhs.x.contains(0.0) {
Decoration::Trv
} else {
self.d.min(rhs.d)
};
Self::set_dec(self.x / rhs.x, d)
}
}
macro_rules! impl_op_assign {
($OpAssign:ident, $op_assign:ident, $op:ident) => {
impl $OpAssign for Interval {
fn $op_assign(&mut self, rhs: Self) {
*self = self.$op(rhs);
}
}
impl $OpAssign for DecoratedInterval {
fn $op_assign(&mut self, rhs: Self) {
*self = self.$op(rhs);
}
}
};
}
impl_op_assign!(AddAssign, add_assign, add);
impl_op_assign!(SubAssign, sub_assign, sub);
impl_op_assign!(MulAssign, mul_assign, mul);
impl_op_assign!(DivAssign, div_assign, div);
#[cfg(test)]
mod tests {
use super::*;
use crate::{dec_interval, interval};
type DI = DecoratedInterval;
type I = Interval;
#[test]
fn add_assign() {
let mut i = interval!(3.0, 4.0).unwrap();
i += interval!(1.0, 2.0).unwrap();
assert_eq!(i, interval!(4.0, 6.0).unwrap());
let mut i = dec_interval!(3.0, 4.0).unwrap();
i += dec_interval!(1.0, 2.0).unwrap();
assert_eq!(i, dec_interval!(4.0, 6.0).unwrap());
}
#[test]
fn sub_assign() {
let mut i = interval!(3.0, 4.0).unwrap();
i -= interval!(1.0, 2.0).unwrap();
assert_eq!(i, interval!(1.0, 3.0).unwrap());
let mut i = dec_interval!(3.0, 4.0).unwrap();
i -= dec_interval!(1.0, 2.0).unwrap();
assert_eq!(i, dec_interval!(1.0, 3.0).unwrap());
}
#[test]
fn mul_assign() {
let mut i = interval!(3.0, 4.0).unwrap();
i *= interval!(1.0, 2.0).unwrap();
assert_eq!(i, interval!(3.0, 8.0).unwrap());
let mut i = dec_interval!(3.0, 4.0).unwrap();
i *= dec_interval!(1.0, 2.0).unwrap();
assert_eq!(i, dec_interval!(3.0, 8.0).unwrap());
}
#[test]
fn div_assign() {
let mut i = interval!(3.0, 4.0).unwrap();
i /= interval!(1.0, 2.0).unwrap();
assert_eq!(i, interval!(1.5, 4.0).unwrap());
let mut i = dec_interval!(3.0, 4.0).unwrap();
i /= dec_interval!(1.0, 2.0).unwrap();
assert_eq!(i, dec_interval!(1.5, 4.0).unwrap());
}
#[test]
fn empty() {
assert!((-I::empty()).is_empty());
assert!((I::empty() + I::PI).is_empty());
assert!((I::PI + I::empty()).is_empty());
assert!((I::empty() - I::PI).is_empty());
assert!((I::PI - I::empty()).is_empty());
assert!((I::empty() * I::PI).is_empty());
assert!((I::PI * I::empty()).is_empty());
assert!((I::empty() / I::PI).is_empty());
assert!((I::PI / I::empty()).is_empty());
assert!((-DI::empty()).is_empty());
assert!((DI::empty() + DI::PI).is_empty());
assert!((DI::PI + DI::empty()).is_empty());
assert!((DI::empty() - DI::PI).is_empty());
assert!((DI::PI - DI::empty()).is_empty());
assert!((DI::empty() * DI::PI).is_empty());
assert!((DI::PI * DI::empty()).is_empty());
assert!((DI::empty() / DI::PI).is_empty());
assert!((DI::PI / DI::empty()).is_empty());
}
#[test]
fn nai() {
assert!((-DI::nai()).is_nai());
assert!((DI::nai() + DI::PI).is_nai());
assert!((DI::PI + DI::nai()).is_nai());
assert!((DI::nai() - DI::PI).is_nai());
assert!((DI::PI - DI::nai()).is_nai());
assert!((DI::nai() * DI::PI).is_nai());
assert!((DI::PI * DI::nai()).is_nai());
assert!((DI::nai() / DI::PI).is_nai());
assert!((DI::PI / DI::nai()).is_nai());
}
}