use core::fmt;
use crate::float::{Float, RoundingMode};
const DOWN: RoundingMode = RoundingMode::TowardNegative;
const UP: RoundingMode = RoundingMode::TowardPositive;
#[derive(Clone)]
pub struct Interval {
lo: Float,
hi: Float,
precision: u64,
}
fn fmin(a: Float, b: Float) -> Float {
if a <= b { a } else { b }
}
fn fmax(a: Float, b: Float) -> Float {
if a >= b { a } else { b }
}
impl Interval {
pub fn new(lo: Float, hi: Float, precision: u64) -> Interval {
Interval { lo, hi, precision }
}
pub fn point(x: Float) -> Interval {
let precision = x.precision();
Interval {
lo: x.clone(),
hi: x,
precision,
}
}
pub fn from_rational(r: &crate::rational::Rational, precision: u64) -> Interval {
Interval {
lo: Float::from_rational(r, precision, DOWN),
hi: Float::from_rational(r, precision, UP),
precision,
}
}
#[inline]
pub fn lower(&self) -> &Float {
&self.lo
}
#[inline]
pub fn upper(&self) -> &Float {
&self.hi
}
#[inline]
pub fn precision(&self) -> u64 {
self.precision
}
pub fn contains_zero(&self) -> bool {
let zero = Float::zero(self.precision);
self.lo <= zero && zero <= self.hi
}
pub fn width(&self) -> Float {
self.hi.sub(&self.lo, self.precision, UP)
}
pub fn midpoint(&self) -> Float {
self.lo
.add(&self.hi, self.precision, RoundingMode::Nearest)
.mul(
&Float::from_f64(0.5, self.precision, RoundingMode::Nearest),
self.precision,
RoundingMode::Nearest,
)
}
fn prec(&self, rhs: &Interval) -> u64 {
self.precision.max(rhs.precision)
}
pub fn add(&self, rhs: &Interval) -> Interval {
let p = self.prec(rhs);
Interval {
lo: self.lo.add(&rhs.lo, p, DOWN),
hi: self.hi.add(&rhs.hi, p, UP),
precision: p,
}
}
pub fn sub(&self, rhs: &Interval) -> Interval {
let p = self.prec(rhs);
Interval {
lo: self.lo.sub(&rhs.hi, p, DOWN),
hi: self.hi.sub(&rhs.lo, p, UP),
precision: p,
}
}
pub fn neg(&self) -> Interval {
Interval {
lo: self.hi.neg(),
hi: self.lo.neg(),
precision: self.precision,
}
}
pub fn mul(&self, rhs: &Interval) -> Interval {
let p = self.prec(rhs);
let ends = [
(&self.lo, &rhs.lo),
(&self.lo, &rhs.hi),
(&self.hi, &rhs.lo),
(&self.hi, &rhs.hi),
];
let mut lo = self.lo.mul(&rhs.lo, p, DOWN);
let mut hi = self.lo.mul(&rhs.lo, p, UP);
for (a, b) in ends {
lo = fmin(lo, a.mul(b, p, DOWN));
hi = fmax(hi, a.mul(b, p, UP));
}
Interval {
lo,
hi,
precision: p,
}
}
pub fn div(&self, rhs: &Interval) -> Interval {
assert!(
!rhs.contains_zero(),
"Interval::div: divisor interval contains zero"
);
let p = self.prec(rhs);
let ends = [
(&self.lo, &rhs.lo),
(&self.lo, &rhs.hi),
(&self.hi, &rhs.lo),
(&self.hi, &rhs.hi),
];
let mut lo = self.lo.div(&rhs.lo, p, DOWN);
let mut hi = self.lo.div(&rhs.lo, p, UP);
for (a, b) in ends {
lo = fmin(lo, a.div(b, p, DOWN));
hi = fmax(hi, a.div(b, p, UP));
}
Interval {
lo,
hi,
precision: p,
}
}
pub fn sqrt(&self) -> Interval {
Interval {
lo: self.lo.sqrt(self.precision, DOWN),
hi: self.hi.sqrt(self.precision, UP),
precision: self.precision,
}
}
pub fn intersect(&self, rhs: &Interval) -> Option<Interval> {
let p = self.prec(rhs);
let lo = fmax(self.lo.clone(), rhs.lo.clone());
let hi = fmin(self.hi.clone(), rhs.hi.clone());
if lo <= hi {
Some(Interval {
lo,
hi,
precision: p,
})
} else {
None
}
}
pub fn hull(&self, rhs: &Interval) -> Interval {
Interval {
lo: fmin(self.lo.clone(), rhs.lo.clone()),
hi: fmax(self.hi.clone(), rhs.hi.clone()),
precision: self.prec(rhs),
}
}
}
impl fmt::Display for Interval {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "[{}, {}]", self.lo, self.hi)
}
}
impl fmt::Debug for Interval {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Interval[{:?}, {:?}]", self.lo, self.hi)
}
}
macro_rules! iv_binop {
($tr:ident, $m:ident, $atr:ident, $am:ident) => {
impl core::ops::$tr for Interval {
type Output = Interval;
#[inline]
fn $m(self, rhs: Interval) -> Interval {
Interval::$m(&self, &rhs)
}
}
impl core::ops::$tr<&Interval> for &Interval {
type Output = Interval;
#[inline]
fn $m(self, rhs: &Interval) -> Interval {
Interval::$m(self, rhs)
}
}
impl core::ops::$atr for Interval {
#[inline]
fn $am(&mut self, rhs: Interval) {
*self = Interval::$m(self, &rhs);
}
}
};
}
iv_binop!(Add, add, AddAssign, add_assign);
iv_binop!(Sub, sub, SubAssign, sub_assign);
iv_binop!(Mul, mul, MulAssign, mul_assign);
iv_binop!(Div, div, DivAssign, div_assign);
impl core::ops::Neg for Interval {
type Output = Interval;
#[inline]
fn neg(self) -> Interval {
Interval::neg(&self)
}
}