use axiolid_guarantees::Sign;
use crate::arith::Arith;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Interval {
lo: f64,
hi: f64,
}
impl Interval {
pub const WHOLE: Self = Self {
lo: f64::NEG_INFINITY,
hi: f64::INFINITY,
};
#[must_use]
pub fn point(value: f64) -> Self {
if value.is_finite() {
Self {
lo: value,
hi: value,
}
} else {
Self::WHOLE
}
}
#[must_use]
pub const fn lo(self) -> f64 {
self.lo
}
#[must_use]
pub const fn hi(self) -> f64 {
self.hi
}
#[must_use]
pub fn contains(self, value: f64) -> bool {
self.lo <= value && value <= self.hi
}
pub(crate) fn from_bounds(lo: f64, hi: f64) -> Self {
if lo.is_nan() || hi.is_nan() || lo > hi {
return Self::WHOLE;
}
Self { lo, hi }
}
#[must_use]
pub fn quotient(self, divisor: Self) -> Self {
if divisor.lo <= 0.0 && divisor.hi >= 0.0 || divisor.lo.is_nan() || divisor.hi.is_nan() {
return Self::WHOLE;
}
let q = [
self.lo / divisor.lo,
self.lo / divisor.hi,
self.hi / divisor.lo,
self.hi / divisor.hi,
];
if q.iter().any(|v| v.is_nan()) {
return Self::WHOLE;
}
let lo = q.iter().copied().fold(f64::INFINITY, f64::min);
let hi = q.iter().copied().fold(f64::NEG_INFINITY, f64::max);
Self::outward(lo, hi)
}
#[must_use]
pub fn disjoint(self, other: Self) -> bool {
self.hi < other.lo || other.hi < self.lo
}
fn outward(lo: f64, hi: f64) -> Self {
if lo.is_nan() || hi.is_nan() {
return Self::WHOLE;
}
Self {
lo: lo.next_down(),
hi: hi.next_up(),
}
}
}
impl Arith for Interval {
fn from_f64(value: f64) -> Self {
Self::point(value)
}
fn from_dyadic(value: &crate::dyadic::Dyadic) -> Self {
value.enclosure()
}
fn add(&self, other: &Self) -> Self {
Self::outward(self.lo + other.lo, self.hi + other.hi)
}
fn sub(&self, other: &Self) -> Self {
Self::outward(self.lo - other.hi, self.hi - other.lo)
}
fn mul(&self, other: &Self) -> Self {
let products = [
self.lo * other.lo,
self.lo * other.hi,
self.hi * other.lo,
self.hi * other.hi,
];
if products.iter().any(|p| p.is_nan()) {
return Self::WHOLE;
}
let lo = products.iter().copied().fold(f64::INFINITY, f64::min);
let hi = products.iter().copied().fold(f64::NEG_INFINITY, f64::max);
Self::outward(lo, hi)
}
fn neg(&self) -> Self {
Self {
lo: -self.hi,
hi: -self.lo,
}
}
fn sign(&self) -> Option<Sign> {
if self.lo > 0.0 {
Some(Sign::Positive)
} else if self.hi < 0.0 {
Some(Sign::Negative)
} else if self.lo == 0.0 && self.hi == 0.0 {
Some(Sign::Zero)
} else {
None
}
}
fn square(&self) -> Self {
if self.lo >= 0.0 || self.hi <= 0.0 {
return self.mul(self);
}
let top = (self.lo * self.lo).max(self.hi * self.hi);
if top.is_nan() {
return Self::WHOLE;
}
Self {
lo: 0.0,
hi: top.next_up(),
}
}
fn sqrt_enclosure(&self) -> Option<Self> {
if self.lo.is_nan() || self.lo < 0.0 {
return None;
}
Some(Self {
lo: self.lo.sqrt().next_down().max(0.0),
hi: self.hi.sqrt().next_up(),
})
}
}