use crate::{interval::*, simd::*};
impl Interval {
/// Returns the (greatest) lower bound of `self`.
///
/// Equivalently, it returns $a$ if $\self = \[a, b\]$ is nonempty; otherwise, $+∞$.
///
/// # Examples
///
/// ```
/// use inari::*;
/// assert_eq!(const_interval!(-2.0, 3.0).inf(), -2.0);
/// assert_eq!(Interval::EMPTY.inf(), f64::INFINITY);
/// assert_eq!(Interval::ENTIRE.inf(), f64::NEG_INFINITY);
/// ```
///
/// See also: [`Interval::sup`].
pub fn inf(self) -> f64 {
let x = self.inf_raw();
if x.is_nan() {
// The empty interval.
f64::INFINITY
} else if x == 0.0 {
-0.0
} else {
x
}
}
/// Returns the magnitude of `self` if `self` is nonempty; otherwise, NaN.
///
/// The magnitude of a nonempty interval $𝒙 = \[a, b\]$ is defined as follows:
///
/// $$
/// \operatorname{mag}(𝒙) = \sup\\{|x| ∣ x ∈ 𝒙\\} = \max(|a|, |b|).
/// $$
///
/// # Examples
///
/// ```
/// use inari::*;
/// assert_eq!(const_interval!(-2.0, 3.0).mag(), 3.0);
/// assert!(Interval::EMPTY.mag().is_nan());
/// assert_eq!(Interval::ENTIRE.mag(), f64::INFINITY);
/// ```
///
/// See also: [`Interval::mig`].
pub fn mag(self) -> f64 {
let abs = abs(self.rep);
extract0(max(abs, swap(abs)))
}
/// Returns the midpoint of `self` if `self` is nonempty; otherwise, NaN.
/// For nonempty cases, the following values are returned.
///
/// - If $\self = \[-∞, +∞\]$, zero is returned.
/// - If $\self = \[-∞, b\]$ where $b < +∞$, `f64::MIN` is returned.
/// - If $\self = \[a, +∞\]$ where $a > -∞$, `f64::MAX` is returned.
/// - If `self` is bounded, $\operatorname{mid}(\self)$ rounded to the nearest `f64` value is returned.
///
/// The midpoint of a nonempty interval $𝒙 = \[a, b\]$ is defined as follows:
///
/// $$
/// \operatorname{mid}(𝒙) = \frac{1}{2}(a + b).
/// $$
///
/// # Examples
///
/// ```
/// use inari::*;
/// assert_eq!(const_interval!(-2.0, 3.0).mid(), 0.5);
/// assert_eq!(const_interval!(f64::NEG_INFINITY, 3.0).mid(), f64::MIN);
/// assert_eq!(const_interval!(-2.0, f64::INFINITY).mid(), f64::MAX);
/// assert!(Interval::EMPTY.mid().is_nan());
/// assert_eq!(Interval::ENTIRE.mid(), 0.0);
/// ```
///
/// See also: [`Interval::rad`].
// See Table VII in https://doi.org/10.1145/2493882 for the implementation.
pub fn mid(self) -> f64 {
let a = self.inf_raw();
let b = self.sup_raw();
match (a == f64::NEG_INFINITY, b == f64::INFINITY) {
(false, false) => {
let mid = 0.5 * (a + b);
if mid.is_infinite() {
0.5 * a + 0.5 * b
} else if mid == 0.0 {
0.0
} else {
mid
}
}
(false, true) => f64::MAX,
(true, false) => f64::MIN,
(true, true) => 0.0,
}
}
/// Returns the mignitude of `self` if `self` is nonempty; otherwise, NaN.
///
/// The mignitude of a nonempty interval $𝒙 = \[a, b\]$ is defined as follows:
///
/// $$
/// \operatorname{mig}(𝒙) = \inf\\{|x| ∣ x ∈ 𝒙\\} = \begin{cases}
/// \min(|a|, |b|) & \text{if } \sgn(a) = \sgn(b), \\\\
/// 0 & \text{otherwise}.
/// \end{cases}
/// $$
///
/// # Examples
///
/// ```
/// use inari::*;
/// assert_eq!(const_interval!(-2.0, 3.0).mig(), 0.0);
/// assert_eq!(const_interval!(2.0, 3.0).mig(), 2.0);
/// assert!(Interval::EMPTY.mig().is_nan());
/// assert_eq!(Interval::ENTIRE.mig(), 0.0);
/// ```
///
/// See also: [`Interval::mag`].
pub fn mig(self) -> f64 {
let zero = splat(0.0);
let contains_zero = all(ge(self.rep, zero));
if contains_zero {
return 0.0;
}
let abs = abs(self.rep);
extract0(min(abs, swap(abs)))
}
/// Returns the radius of `self` if `self` is nonempty; otherwise, NaN.
/// The result $r$ is the smallest `f64` number that satisfies
/// $\self ⊆ \[m - r, m + r\]$ where $m$ is the `f64` value returned by `self.mid()`.
///
/// The radius of a nonempty interval $𝒙 = \[a, b\]$ is defined as follows:
///
/// $$
/// \operatorname{rad}(𝒙) = \frac{1}{2}(b - a).
/// $$
///
/// # Examples
///
/// ```
/// use inari::*;
/// assert_eq!(const_interval!(-2.0, 3.0).rad(), 2.5);
/// assert!(Interval::EMPTY.rad().is_nan());
/// assert_eq!(Interval::ENTIRE.rad(), f64::INFINITY);
/// ```
///
/// See also: [`Interval::mid`].
pub fn rad(self) -> f64 {
let m = self.mid();
f64::max(sub1_ru(m, self.inf_raw()), sub1_ru(self.sup_raw(), m))
}
/// Returns the (least) upper bound of `self`.
///
/// Equivalently, it returns $b$ if $\self = \[a, b\]$ is nonempty; otherwise, $-∞$.
///
/// # Examples
///
/// ```
/// use inari::*;
/// assert_eq!(const_interval!(-2.0, 3.0).sup(), 3.0);
/// assert_eq!(Interval::EMPTY.sup(), f64::NEG_INFINITY);
/// assert_eq!(Interval::ENTIRE.sup(), f64::INFINITY);
/// ```
///
/// See also: [`Interval::inf`].
pub fn sup(self) -> f64 {
let x = self.sup_raw();
if x.is_nan() {
// The empty interval.
f64::NEG_INFINITY
} else if x == 0.0 {
0.0
} else {
x
}
}
/// Returns the width of `self` if `self` is nonempty; otherwise, NaN.
/// The result is rounded toward $+∞$.
///
/// The width of a nonempty interval $𝒙 = \[a, b\]$ is defined as follows:
///
/// $$
/// \operatorname{wid}(𝒙) = b - a.
/// $$
///
/// # Examples
///
/// ```
/// use inari::*;
/// assert_eq!(const_interval!(-2.0, 3.0).wid(), 5.0);
/// assert_eq!(const_interval!(-1.0, f64::MAX).wid(), f64::INFINITY);
/// assert!(Interval::EMPTY.wid().is_nan());
/// assert_eq!(Interval::ENTIRE.wid(), f64::INFINITY);
/// ```
pub fn wid(self) -> f64 {
let wid = sub1_ru(self.sup_raw(), self.inf_raw());
if wid == 0.0 {
0.0
} else {
wid
}
}
}
macro_rules! impl_dec {
($f:ident) => {
pub fn $f(self) -> f64 {
if self.is_nai() {
return f64::NAN;
}
self.x.$f()
}
};
}
impl DecInterval {
impl_dec!(inf);
impl_dec!(mag);
impl_dec!(mid);
impl_dec!(mig);
impl_dec!(rad);
impl_dec!(sup);
impl_dec!(wid);
}
#[cfg(test)]
mod tests {
use crate::*;
#[test]
fn inf() {
assert!(const_interval!(0.0, 0.0).inf().is_sign_negative());
assert!(const_interval!(0.0, -0.0).inf().is_sign_negative());
assert!(const_interval!(-0.0, 0.0).inf().is_sign_negative());
assert!(const_interval!(-0.0, -0.0).inf().is_sign_negative());
}
#[test]
fn mag() {
assert!(const_interval!(0.0, 0.0).mag().is_sign_positive());
assert!(const_interval!(0.0, -0.0).mag().is_sign_positive());
assert!(const_interval!(-0.0, 0.0).mag().is_sign_positive());
assert!(const_interval!(-0.0, -0.0).mag().is_sign_positive());
}
#[test]
fn mid() {
assert!(const_interval!(0.0, 0.0).mid().is_sign_positive());
assert!(const_interval!(0.0, -0.0).mid().is_sign_positive());
assert!(const_interval!(-0.0, 0.0).mid().is_sign_positive());
assert!(const_interval!(-0.0, -0.0).mid().is_sign_positive());
}
#[test]
fn mig() {
assert!(const_interval!(0.0, 0.0).mig().is_sign_positive());
assert!(const_interval!(0.0, -0.0).mig().is_sign_positive());
assert!(const_interval!(-0.0, 0.0).mig().is_sign_positive());
assert!(const_interval!(-0.0, -0.0).mig().is_sign_positive());
}
#[test]
fn rad() {
assert!(const_interval!(0.0, 0.0).rad().is_sign_positive());
assert!(const_interval!(0.0, -0.0).rad().is_sign_positive());
assert!(const_interval!(-0.0, 0.0).rad().is_sign_positive());
assert!(const_interval!(-0.0, -0.0).rad().is_sign_positive());
}
#[test]
fn sup() {
assert!(const_interval!(0.0, 0.0).sup().is_sign_positive());
assert!(const_interval!(0.0, -0.0).sup().is_sign_positive());
assert!(const_interval!(-0.0, 0.0).sup().is_sign_positive());
assert!(const_interval!(-0.0, -0.0).sup().is_sign_positive());
}
#[test]
fn wid() {
assert!(const_interval!(0.0, 0.0).wid().is_sign_positive());
assert!(const_interval!(0.0, -0.0).wid().is_sign_positive());
assert!(const_interval!(-0.0, 0.0).wid().is_sign_positive());
assert!(const_interval!(-0.0, -0.0).wid().is_sign_positive());
// Check if the result is rounded up.
assert_eq!(
const_interval!(-f64::MIN_POSITIVE, f64::MAX).wid(),
f64::INFINITY
);
}
}