use crate::{classify::*, interval::*, simd::*};
use std::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 x = self.rep;
let y = rhs.rep;
Self { rep: add_ru(x, y) }
}
}
impl Sub for Interval {
type Output = Self;
fn sub(self, rhs: Self) -> Self {
let x = self.rep;
let y = swap(rhs.rep);
Self { rep: add_ru(x, y) }
}
}
impl Mul for Interval {
type Output = Self;
#[allow(clippy::many_single_char_names)]
fn mul(self, rhs: Self) -> Self {
use IntervalClass2::*;
match self.classify2(rhs) {
E_E | E_M | E_N0 | E_N1 | E_P0 | E_P1 | E_Z | M_E | N0_E | N1_E | P0_E | P1_E | Z_E => {
Self::EMPTY
}
M_Z | N0_Z | N1_Z | P0_Z | P1_Z | Z_M | Z_N0 | Z_N1 | Z_P0 | Z_P1 | Z_Z => Self::zero(),
M_M => {
let x = shuffle02(self.rep, self.rep);
let y = swap(rhs.rep);
let xy = mul_ru(x, y);
let z = shuffle13(self.rep, self.rep);
let w = rhs.rep;
let zw = mul_ru(z, w);
let r = max(xy, zw);
Self { rep: r }
}
M_N0 | M_N1 => {
let x = swap(self.rep);
let y = shuffle02(rhs.rep, rhs.rep);
Self { rep: mul_ru(x, y) }
}
M_P0 | M_P1 => {
let x = self.rep;
let y = shuffle13(rhs.rep, rhs.rep);
Self { rep: mul_ru(x, y) }
}
N0_M | N1_M => {
let x = shuffle02(self.rep, self.rep);
let y = swap(rhs.rep);
Self { rep: mul_ru(x, y) }
}
N0_N0 | N0_N1 | N1_N0 | N1_N1 => {
let x = swap(self.rep);
let x = neg0(x);
let y = swap(rhs.rep);
Self { rep: mul_ru(x, y) }
}
N0_P0 | N0_P1 | N1_P0 | N1_P1 => {
let x = self.rep;
let y = neg0(rhs.rep);
let y = swap(y);
Self { rep: mul_ru(x, y) }
}
P0_M | P1_M => {
let x = shuffle13(self.rep, self.rep);
let y = rhs.rep;
Self { rep: mul_ru(x, y) }
}
P0_N0 | P0_N1 | P1_N0 | P1_N1 => {
let x = neg0(self.rep);
let x = swap(x);
let y = rhs.rep;
Self { rep: mul_ru(x, y) }
}
P0_P0 | P0_P1 | P1_P0 | P1_P1 => {
let x = self.rep;
let y = neg0(rhs.rep);
Self { rep: mul_ru(x, y) }
}
}
}
}
impl Div for Interval {
type Output = Self;
fn div(self, rhs: Self) -> Self {
use IntervalClass2::*;
match self.classify2(rhs) {
E_E | E_M | E_N0 | E_N1 | E_P0 | E_P1 | E_Z | M_E | M_Z | N0_E | N0_Z | N1_E | N1_Z
| P0_E | P0_Z | P1_E | P1_Z | Z_E | Z_Z => Self::EMPTY,
M_M | M_N0 | M_P0 | N0_M | N1_M | P0_M | P1_M => Self::ENTIRE,
Z_M | Z_N0 | Z_N1 | Z_P0 | Z_P1 => Self::zero(),
M_N1 => {
let x = swap(self.rep);
let y = swap(rhs.rep);
let y = neg0(y);
let y = shuffle02(y, y);
Self { rep: div_ru(x, y) }
}
M_P1 => {
let x = self.rep;
let y = neg0(rhs.rep);
let y = shuffle02(y, y);
Self { rep: div_ru(x, y) }
}
N0_N0 | N1_N0 => {
let x = swap(self.rep);
let y = rhs.rep;
Self {
rep: shuffle02(div_ru(x, y), splat(f64::INFINITY)),
}
}
N0_N1 | N1_N1 => {
let x = neg0(self.rep);
let x = swap(x);
let y = rhs.rep;
Self { rep: div_ru(x, y) }
}
N0_P0 | N1_P0 => {
let x = self.rep;
let y = rhs.rep;
Self {
rep: shuffle03(splat(f64::INFINITY), div_ru(x, y)),
}
}
N0_P1 | N1_P1 => {
let x = self.rep;
let y = neg0(rhs.rep);
Self { rep: div_ru(x, y) }
}
P0_N0 | P1_N0 => {
let x = self.rep;
let y = rhs.rep;
Self {
rep: shuffle02(splat(f64::INFINITY), div_ru(x, y)),
}
}
P0_N1 | P1_N1 => {
let x = swap(self.rep);
let x = neg0(x);
let y = swap(rhs.rep);
Self { rep: div_ru(x, y) }
}
P0_P0 | P1_P0 => {
let x = self.rep;
let y = swap(rhs.rep);
Self {
rep: shuffle02(div_ru(x, y), splat(f64::INFINITY)),
}
}
P0_P1 | P1_P1 => {
let x = self.rep;
let y = neg0(rhs.rep);
let y = swap(y);
Self { rep: div_ru(x, y) }
}
}
}
}
impl Neg for DecInterval {
type Output = Self;
fn neg(self) -> Self {
if self.is_nai() {
return self;
}
Self::set_dec(-self.x, self.d)
}
}
impl Add for DecInterval {
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 DecInterval {
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 DecInterval {
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 DecInterval {
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 DecInterval {
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 crate::*;
use DecInterval as DI;
use Interval as I;
#[test]
fn add_assign() {
let mut i = const_interval!(3.0, 4.0);
i += const_interval!(1.0, 2.0);
assert_eq!(i, const_interval!(4.0, 6.0));
let mut i = const_dec_interval!(3.0, 4.0);
i += const_dec_interval!(1.0, 2.0);
assert_eq!(i, const_dec_interval!(4.0, 6.0));
}
#[test]
fn sub_assign() {
let mut i = const_interval!(3.0, 4.0);
i -= const_interval!(1.0, 2.0);
assert_eq!(i, const_interval!(1.0, 3.0));
let mut i = const_dec_interval!(3.0, 4.0);
i -= const_dec_interval!(1.0, 2.0);
assert_eq!(i, const_dec_interval!(1.0, 3.0));
}
#[test]
fn mul_assign() {
let mut i = const_interval!(3.0, 4.0);
i *= const_interval!(1.0, 2.0);
assert_eq!(i, const_interval!(3.0, 8.0));
let mut i = const_dec_interval!(3.0, 4.0);
i *= const_dec_interval!(1.0, 2.0);
assert_eq!(i, const_dec_interval!(3.0, 8.0));
}
#[test]
fn div_assign() {
let mut i = const_interval!(3.0, 4.0);
i /= const_interval!(1.0, 2.0);
assert_eq!(i, const_interval!(1.5, 4.0));
let mut i = const_dec_interval!(3.0, 4.0);
i /= const_dec_interval!(1.0, 2.0);
assert_eq!(i, const_dec_interval!(1.5, 4.0));
}
#[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());
}
}