use crate::{InitialisationError, InitialisationErrorKind};
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub struct Limits {
lower: f64,
upper: f64,
}
impl Limits {
pub const fn new(lower: f64, upper: f64) -> Result<Self, InitialisationError> {
if (upper - lower).abs() > f64::MAX || (upper + lower).abs() > f64::MAX {
let kind = InitialisationErrorKind::IntegrationRangeTooLarge { lower, upper };
let err = InitialisationError::new(kind);
Err(err)
} else {
let out = Self { lower, upper };
Ok(out)
}
}
pub(crate) const fn new_unchecked(lower: f64, upper: f64) -> Self {
Self { lower, upper }
}
#[must_use]
pub const fn lower(&self) -> f64 {
self.lower
}
#[must_use]
pub const fn upper(&self) -> f64 {
self.upper
}
pub(crate) const fn centre(&self) -> f64 {
(self.upper).midpoint(self.lower)
}
pub(crate) const fn width(&self) -> f64 {
2.0 * self.half_width()
}
pub(crate) const fn half_width(&self) -> f64 {
(self.upper).midpoint(-self.lower)
}
#[must_use]
pub const fn bisect(&self) -> [Self; 2] {
let upper = self.upper();
let lower = self.lower();
let midpoint = self.centre();
[
Self::new_unchecked(lower, midpoint),
Self::new_unchecked(midpoint, upper),
]
}
#[inline]
pub(crate) fn subinterval_too_small(&self) -> bool {
let lower = self.lower();
let upper = self.upper();
let midpoint = self.centre();
let eps = f64::EPSILON;
let min = f64::MIN_POSITIVE;
let tmp = (1.0 + 100.0 * eps) * (midpoint.abs() + 1000.0 * min);
lower.abs() <= tmp && upper.abs() <= tmp
}
pub fn scale(self, scale: f64) -> Result<Self, InitialisationError> {
let lower = self.lower() * scale;
let upper = self.upper() * scale;
Limits::new(lower, upper)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_bisection() {
let limit = Limits::new(0.0, 1.0).unwrap();
let [lower, upper] = limit.bisect();
assert_eq!(lower, Limits::new(0.0, 0.5).unwrap());
assert_eq!(upper, Limits::new(0.5, 1.0).unwrap());
}
}