use crate::{interval::*, classify::*};
use forward_ref::*;
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
impl Neg for Interval {
type Output = Self;
fn neg(self) -> Self {
Self {
inf: -self.sup,
sup: -self.inf,
}
}
}
forward_ref_unop!(impl Neg, neg for Interval);
impl Add for Interval {
type Output = Self;
fn add(self, rhs: Self) -> Self {
Self {
inf: self.inf + rhs.inf,
sup: self.sup + rhs.sup,
}
}
}
forward_ref_binop!(impl Add, add for Interval, Interval);
impl Sub for Interval {
type Output = Self;
fn sub(self, rhs: Self) -> Self {
Self {
inf: self.inf - rhs.sup,
sup: self.sup - rhs.inf,
}
}
}
forward_ref_binop!(impl Sub, sub for Interval, Interval);
impl Mul for Interval {
type Output = Self;
#[allow(clippy::many_single_char_names)]
fn mul(self, rhs: Self) -> Self {
let (a, b) = (self.inf, self.sup);
let (c, d) = (rhs.inf, rhs.sup);
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 => {
Self { inf: f64::min(a * d, b * c), sup: f64::max(a * c, b * d) }
}
M_N0 | M_N1 => {
Self { inf: b * c, sup: a * c }
}
M_P0 | M_P1 => {
Self { inf: a * d, sup: b * d }
}
N0_M | N1_M => {
Self { inf: a * d, sup: a * c }
}
N0_N0 | N0_N1 | N1_N0 | N1_N1 => {
Self { inf: b * d, sup: a * c }
}
N0_P0 | N0_P1 | N1_P0 | N1_P1 => {
Self { inf: a * d, sup: b * c }
}
P0_M | P1_M => {
Self { inf: b * c, sup: b * d }
}
P0_N0 | P0_N1 | P1_N0 | P1_N1 => {
Self { inf: b * c, sup: a * d }
}
P0_P0 | P0_P1 | P1_P0 | P1_P1 => {
Self { inf: a * c, sup: b * d }
}
}
}
}
forward_ref_binop!(impl Mul, mul for Interval, Interval);
impl Div for Interval {
type Output = Self;
fn div(self, rhs: Self) -> Self {
let (a, b) = (self.inf, self.sup);
let (c, d) = (rhs.inf, rhs.sup);
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 => {
Self { inf: b / d, sup: a / d }
}
M_P1 => {
Self { inf: a / c, sup: b / c }
}
N0_N0 | N1_N0 => {
Self { inf: b / c, sup: f64::INFINITY }
}
N0_N1 | N1_N1 => {
Self { inf: b / c, sup: a / d }
}
N0_P0 | N1_P0 => {
Self { inf: f64::NEG_INFINITY, sup: b / d }
}
N0_P1 | N1_P1 => {
Self { inf: a / c, sup: b / d }
}
P0_N0 | P1_N0 => {
Self { inf: f64::NEG_INFINITY, sup: a / c }
}
P0_N1 | P1_N1 => {
Self { inf: b / d, sup: a / c }
}
P0_P0 | P1_P0 => {
Self { inf: a / d, sup: f64::INFINITY }
}
P0_P1 | P1_P1 => {
Self { inf: a / d, sup: b / c }
}
}
}
}
forward_ref_binop!(impl Div, div for Interval, Interval);
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);
}
}
forward_ref_op_assign!(impl $OpAssign, $op_assign for Interval, Interval);
};
}
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 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));
}
#[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));
}
#[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));
}
#[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));
}
#[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());
}
#[allow(clippy::op_ref)]
#[test]
fn ref_type_args() {
const E: I = I::EMPTY;
let _ = -&E;
let _ = &E + E;
let _ = E + &E;
let _ = &E + &E;
let _ = &E - E;
let _ = E - &E;
let _ = &E - &E;
let _ = &E * E;
let _ = E * &E;
let _ = &E * &E;
let _ = &E / E;
let _ = E / &E;
let _ = &E / &E;
let mut e = I::EMPTY;
e += &E;
e -= &E;
e *= &E;
e /= &E;
}
}