stats_ci/
interval.rs

1//!
2//! Implements the [`Interval`] enum, which represents a confidence interval
3//! over a partially ordered type
4//!
5//! Note that floating point numbers are only partially ordered because of `NaN` values.
6//!
7
8use num_traits::float::FloatCore;
9use num_traits::Num;
10use std::ops::RangeBounds;
11use std::ops::{Add, Div, Mul, Neg, Sub};
12use std::ops::{RangeFrom, RangeInclusive, RangeToInclusive};
13
14/// Interval over a partially ordered type (NB: floating point numbers are only partially ordered because of `NaN` values).
15/// The interval is defined by its lower and upper bounds. One-sided intervals (with a single concrete bound) are also supported.
16/// In this crate, intervals are considered inclusive of their (finite) bounds.
17///
18/// ## Type parameters
19///
20/// * `T`: The type over which the interval is defined. It must be partially ordered.
21///
22/// ## Variants
23///
24/// * `TwoSided(T, T)`: Two-sided interval with lower and upper bounds. The interval is defined as [low, high]. The bounds are included in the interval.
25/// * `UpperOneSided(T)`: Upper one-sided interval with a lower bound. The interval is defined as [low, +∞). The lower bound is included in the interval.
26/// * `LowerOneSided(T)`: Lower one-sided interval with an upper bound. The interval is defined as (-∞, high]. The upper bound is included in the interval.
27///
28/// Intervals support various operations that depend on the type `T` over which they are defined.
29///
30/// ## Operations
31///
32/// ### Creation
33///
34/// * [`Self::new(low, high)`](#method.new): Create a new interval from its left and right bounds for ordered types with equality.
35/// * [`Self::new_upper(low)`](#method.new_upper): Create a new upper one-sided interval from its left bound.
36/// * [`Self::new_lower(high)`](#method.new_lower): Create a new lower one-sided interval from its right bound.
37///
38/// ### Accessors
39///
40/// * [`Self::low()`](#method.low): Get the lower bound of the interval (if any) for partially ordered types.
41/// * [`Self::high()`](#method.high): Get the upper bound of the interval (if any) for partially ordered types.
42/// * [`Self::low_f()`](#method.low_f): Get the lower bound of the interval (if any) for floating point types.
43/// * [`Self::high_f()`](#method.high_f): Get the upper bound of the interval (if any) for floating point types.
44/// * [`Self::low_i()`](#method.low_i): Get the lower bound of the interval (if any) for signed integer types.
45/// * [`Self::high_i()`](#method.high_i): Get the upper bound of the interval (if any) for signed integer types.
46/// * [`Self::low_u()`](#method.low_u): Get the lower bound of the interval (if any) for unsigned integer types.
47/// * [`Self::high_u()`](#method.high_u): Get the upper bound of the interval (if any) for unsigned integer types.
48/// * [`Self::low_as_ref()`](#method.low_as_ref): Get a reference to the lower bound of the interval (if any).
49/// * [`Self::high_as_ref()`](#method.high_as_ref): Get a reference to the upper bound of the interval (if any).
50/// * [`Self::left()`](#method.left): Get the left bound of the interval (if any).
51/// * [`Self::right()`](#method.right): Get the right bound of the interval (if any).
52///
53/// ### Characteristics
54///
55/// * [`Self::is_two_sided()`](#method.is_two_sided): Test whether the interval is two-sided.
56/// * [`Self::is_one_sided()`](#method.is_one_sided): Test whether the interval is one-sided.
57/// * [`Self::is_upper()`](#method.is_upper): Test whether the interval is an upper one-sided interval.
58/// * [`Self::is_lower()`](#method.is_lower): Test whether the interval is a lower one-sided interval.
59/// * [`Self::is_degenerate()`](#method.is_degenerate): Test whether the interval is degenerate.
60///
61/// ### Comparison
62///
63/// * [`Self::intersects(other)`](#method.intersects): Test whether the interval intersects another interval.
64/// * [`Self::is_included_in(other)`](#method.is_included_in): Test whether the interval is included in another interval.
65/// * [`Self::includes(other)`](#method.includes): Test whether the interval includes another interval.
66/// * [`Self::contains(x)`](#method.contains): Test whether the interval contains a value.
67/// * approximate equality with [`approx`](https://docs.rs/approx/0.3.3/approx/) if the `approx` feature is enabled.
68///
69/// ### Operators with a scalar value
70///
71/// * [`Self::mul(rhs)`](#method.mul): Multiply the interval by a value.
72/// * [`Self::div(rhs)`](#method.div): Divide the interval by a value.
73/// * [`Self::add(rhs)`](#method.add): Add a value to the interval.
74/// * [`Self::sub(rhs)`](#method.sub): Subtract a value from the interval.
75///
76/// ### Operators with another interval
77///
78/// * [`Self::relative_to(reference)`](#method.relative_to): Given two intervals, compute the relative interval compared to the reference (argument). The relative interval is defined as the interval of the ratios of the two intervals.
79///
80/// ### Conversions
81///
82/// * [`Self::try_from(value)`](#method.try_from): Create a new interval from a tuple of bounds. The first element of the tuple is the lower bound, the second element is the upper bound. If the lower bound is greater than the upper bound, an error is returned.
83/// * [`Self::from(range)`](#method.from): Create a new interval from a range. The range must be bounded. If the lower bound is greater than the upper bound, an error is returned.
84///
85/// ### Display
86///
87/// * [`Self::fmt()`](#method.fmt): Format the interval as a string.
88///  
89/// # Examples
90///
91/// ## Creation
92/// ```
93/// use stats_ci::*;
94/// let interval = Interval::new(0., 10.)?;
95/// let interval = Interval::new_upper(0.);
96/// let interval = Interval::new_lower(10.);
97/// # Ok::<(),stats_ci::error::IntervalError>(())
98/// ```
99///
100/// ## Accessors
101/// ```
102/// # use stats_ci::*;
103/// let interval = Interval::new(0., 10.)?;
104/// assert_eq!(interval.low(), Some(0.));
105/// assert_eq!(interval.high(), Some(10.));
106/// assert_eq!(interval.low_f(), 0.);
107/// assert_eq!(interval.high_f(), 10.);
108/// assert_eq!(interval.width(), Some(10.));
109/// assert_eq!(interval.is_one_sided(), false);
110/// assert_eq!(interval.is_two_sided(), true);
111/// assert_eq!(interval.is_upper(), false);
112/// assert_eq!(interval.is_lower(), false);
113/// assert_eq!(interval.is_degenerate(), false);
114/// # Ok::<(),stats_ci::error::IntervalError>(())
115/// ```
116///
117/// ## Comparison
118/// ```
119/// # use stats_ci::*;
120/// let interval = Interval::new(0., 10.)?;
121/// let interval2 = Interval::new(8., 15.)?;
122/// let interval3 = Interval::new(2., 5.)?;
123/// assert!(interval.intersects(&interval2));
124/// assert!(interval3.is_included_in(&interval));
125/// assert!(interval.includes(&interval3));
126/// assert!(interval3 < interval2);
127/// assert!(interval == Interval::new(0., 10.)?);
128/// assert!(interval.contains(&5.));
129/// assert!(!interval.contains(&20.));
130/// # Ok::<(),stats_ci::error::IntervalError>(())
131/// ```
132///
133/// ## Operations
134/// ```
135/// # use stats_ci::*;
136/// let interval = Interval::new(2., 4.)?;
137/// assert_eq!(interval * 2., Interval::new(4., 8.)?);
138/// assert_eq!(interval + 2., Interval::new(4., 6.)?);
139/// assert_eq!(interval - 2., Interval::new(0., 2.)?);
140/// assert_eq!(interval / 2., Interval::new(1., 2.)?);
141/// # Ok::<(),stats_ci::error::IntervalError>(())
142/// ```
143///
144/// ## Conversions
145/// ```
146/// # use stats_ci::*;
147/// let interval = Interval::new(2., 4.)?;
148/// assert_eq!(interval, Interval::try_from(2. ..= 4.)?);
149/// assert_eq!(interval, Interval::try_from((2., 4.))?);
150/// assert_eq!(interval, Interval::try_from((Some(2.), Some(4.)))?);
151/// assert_eq!(Interval::from(..= 10.), Interval::new_lower(10.));
152/// assert_eq!(Interval::from(2. ..), Interval::new_upper(2.));
153/// assert_eq!(format!("{}", interval), String::from("[2, 4]"));
154/// assert_eq!(format!("{}", Interval::new_lower(3.)), String::from("(<-,3]"));
155/// assert_eq!(format!("{}", Interval::new_upper(2.)), String::from("[2,->)"));
156/// # Ok::<(),stats_ci::error::IntervalError>(())
157/// ```
158///
159#[derive(Debug, PartialEq)]
160#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
161pub enum Interval<T>
162where
163    T: PartialOrd,
164{
165    ///
166    /// Two-sided interval with lower and upper bounds.
167    /// The interval is defined as [low, high].
168    /// The bounds are included in the interval.
169    ///
170    TwoSided(T, T), // [T, T]
171
172    ///
173    /// Upper one-sided interval with a lower bound.
174    /// The interval is defined as [low, +∞).
175    /// The lower bound is included in the interval.
176    ///
177    UpperOneSided(T), // [T, +inf)
178
179    ///
180    /// Lower one-sided interval with an upper bound.
181    /// The interval is defined as (-∞, high].
182    /// The upper bound is included in the interval.
183    ///
184    LowerOneSided(T), // (-inf, T]
185}
186
187impl<T: PartialOrd> Interval<T> {
188    ///
189    /// Create a new interval from its left and right bounds for ordered types with equality.
190    ///
191    /// # Examples
192    ///
193    /// ```
194    /// # use stats_ci::Interval;
195    /// let interval = Interval::new(0., 1.)?;
196    /// assert_eq!(interval.low(), Some(0.));
197    /// assert_eq!(interval.high(), Some(1.));
198    /// let interval2 = Interval::new("A", "Z")?;
199    /// assert_eq!(interval2.low(), Some("A"));
200    /// assert_eq!(interval2.high(), Some("Z"));
201    /// let interval3 = Interval::new(0, 0_usize)?;
202    /// assert_eq!(interval3.low(), Some(0));
203    /// assert_eq!(interval3.high(), Some(0));
204    /// # Ok::<(),stats_ci::error::IntervalError>(())
205    /// ```
206    pub fn new(low: T, high: T) -> Result<Self, IntervalError> {
207        if low > high {
208            Err(IntervalError::InvalidBounds)
209        } else {
210            Ok(Interval::TwoSided(low, high))
211        }
212    }
213
214    ///
215    /// Create a new upper one-sided interval from its left bound.
216    /// The interval is defined as [low, +∞).
217    /// The bound is included in the interval.
218    ///
219    /// # Examples
220    ///
221    /// The interval below represents [0., +∞).
222    /// ```
223    /// # use stats_ci::Interval;
224    /// let interval = Interval::new_upper(0.);
225    /// assert_eq!(interval.low(), Some(0.));
226    /// assert_eq!(interval.high(), None);
227    /// # Ok::<(),stats_ci::error::IntervalError>(())
228    /// ```
229    ///
230    pub fn new_upper(low: T) -> Self {
231        Interval::UpperOneSided(low)
232    }
233
234    ///
235    /// Create a new lower one-sided interval from its right bound.
236    /// The interval is defined as (-∞, high].
237    /// The bound is included in the interval.
238    ///
239    /// # Examples
240    ///
241    /// The interval below represents (-∞, 1.]
242    /// ```
243    /// # use stats_ci::Interval;
244    /// let interval = Interval::new_lower(1.);
245    /// assert_eq!(interval.low(), None);
246    /// assert_eq!(interval.high(), Some(1.));
247    /// # Ok::<(),stats_ci::error::IntervalError>(())
248    /// ```
249    ///
250    pub fn new_lower(high: T) -> Self {
251        Interval::LowerOneSided(high)
252    }
253
254    ///
255    /// Test whether the interval is two-sided.
256    ///
257    pub fn is_two_sided(&self) -> bool {
258        matches!(self, Interval::TwoSided(_, _))
259    }
260
261    ///
262    /// Test whether the interval is one-sided.
263    ///
264    pub fn is_one_sided(&self) -> bool {
265        !self.is_two_sided()
266    }
267
268    ///
269    /// Test whether the interval is an upper one-sided interval.
270    ///
271    pub fn is_upper(&self) -> bool {
272        matches!(self, Interval::UpperOneSided(_))
273    }
274
275    ///
276    /// Test whether the interval is a lower one-sided interval.
277    ///
278    pub fn is_lower(&self) -> bool {
279        matches!(self, Interval::LowerOneSided(_))
280    }
281
282    ///
283    /// Test whether the interval contains a value.
284    ///
285    /// # Examples
286    ///
287    /// ```
288    /// # use stats_ci::Interval;
289    /// let interval = Interval::new(0., 1.)?;
290    /// assert!(interval.contains(&0.5));
291    /// assert!(!interval.contains(&2.));
292    /// # Ok::<(),stats_ci::error::IntervalError>(())
293    /// ```
294    ///
295    pub fn contains(&self, x: &T) -> bool {
296        match self {
297            Interval::TwoSided(low, high) => low <= x && x <= high,
298            Interval::UpperOneSided(low) => low <= x,
299            Interval::LowerOneSided(high) => x <= high,
300        }
301    }
302
303    ///
304    /// Test whether the interval intersects another interval.
305    /// Two intervals are considered to intersect even if they only have a single point in common (e.g., one of their bounds).
306    ///
307    /// # Examples
308    ///
309    /// ```
310    /// # use stats_ci::Interval;
311    /// let interval = Interval::new(0., 1.)?;
312    /// let interval2 = Interval::new(0.5, 1.5)?;
313    /// assert!(interval.intersects(&interval2));
314    /// let interval3 = Interval::new(2., 3.)?;
315    /// assert!(!interval.intersects(&interval3));
316    /// # Ok::<(),stats_ci::error::IntervalError>(())
317    /// ```
318    ///
319    pub fn intersects(&self, other: &Self) -> bool {
320        match (self, other) {
321            (Interval::UpperOneSided(_), Interval::UpperOneSided(_)) => true,
322            (Interval::LowerOneSided(_), Interval::LowerOneSided(_)) => true,
323            (Interval::UpperOneSided(x), Interval::LowerOneSided(y) | Interval::TwoSided(_, y)) => {
324                x <= y
325            }
326            (Interval::LowerOneSided(x), Interval::UpperOneSided(y) | Interval::TwoSided(_, y)) => {
327                x <= y
328            }
329            (Interval::TwoSided(x, y), Interval::UpperOneSided(z) | Interval::LowerOneSided(z)) => {
330                x <= z && z <= y
331            }
332            (Interval::TwoSided(x, y), Interval::TwoSided(a, b)) => x <= b && a <= y,
333        }
334    }
335
336    ///
337    /// Test whether the interval is included in another interval.
338    ///
339    /// The inclusion is not strict, i.e. an interval is included in itself.
340    ///
341    pub fn is_included_in(&self, other: &Self) -> bool {
342        other.includes(self)
343    }
344
345    ///
346    /// Test whether the interval includes another interval.
347    ///
348    /// The inclusion is not strict, i.e. an interval includes itself.
349    ///
350    pub fn includes(&self, other: &Self) -> bool {
351        match (self, other) {
352            (Interval::UpperOneSided(x), Interval::UpperOneSided(y)) => x <= y,
353            (Interval::LowerOneSided(x), Interval::LowerOneSided(y)) => x >= y,
354            (Interval::UpperOneSided(x), Interval::TwoSided(y, _)) => x <= y,
355            (Interval::LowerOneSided(x), Interval::TwoSided(_, y)) => x >= y,
356            (Interval::TwoSided(x, y), Interval::TwoSided(a, b)) => x <= a && b <= y,
357            (Interval::UpperOneSided(_), Interval::LowerOneSided(_))
358            | (Interval::LowerOneSided(_), Interval::UpperOneSided(_))
359            | (Interval::TwoSided(_, _), Interval::UpperOneSided(_))
360            | (Interval::TwoSided(_, _), Interval::LowerOneSided(_)) => false,
361        }
362    }
363
364    ///
365    /// Get the left bound of the interval (if any).
366    ///
367    pub fn left(&self) -> Option<&T> {
368        match self {
369            Interval::UpperOneSided(x) | Interval::TwoSided(x, _) => Some(x),
370            Interval::LowerOneSided(_) => None,
371        }
372    }
373
374    ///
375    /// Get the right bound of the interval (if any).
376    ///
377    pub fn right(&self) -> Option<&T> {
378        match self {
379            Interval::LowerOneSided(x) | Interval::TwoSided(_, x) => Some(x),
380            Interval::UpperOneSided(_) => None,
381        }
382    }
383}
384
385impl<T: PartialOrd + PartialEq> Interval<T> {
386    ///
387    /// Test whether the interval is degenerate.
388    /// A degenerate interval is an interval with a single point.
389    /// For example, the interval [0, 0] is degenerate.
390    ///
391    pub fn is_degenerate(&self) -> bool {
392        match self {
393            Interval::TwoSided(x, y) => x == y,
394            _ => false,
395        }
396    }
397}
398
399impl<T: PartialOrd + Clone> Interval<T> {
400    ///
401    /// Get the lower bound of the interval (if any) for partially ordered types.
402    ///
403    /// This function clones the bound. If cloning is an issue, use [`Self::low_as_ref()`] instead.
404    ///
405    pub fn low(&self) -> Option<T> {
406        self.left().cloned()
407    }
408
409    ///
410    /// Get the upper bound of the interval (if any) for partially ordered types.
411    ///
412    /// This function clones the bound. If cloning is an issue, use [`Self::high_as_ref()`] instead.
413    ///
414    pub fn high(&self) -> Option<T> {
415        self.right().cloned()
416    }
417}
418
419impl<T: num_traits::Float> Interval<T> {
420    ///
421    /// Get the lower bound of the interval (if any) for floating point types.
422    /// This function returns the negative infinite value for `T` for lower one-sided intervals.
423    ///
424    pub fn low_f(&self) -> T {
425        match self {
426            Interval::TwoSided(low, _) => *low,
427            Interval::UpperOneSided(low) => *low,
428            Interval::LowerOneSided(_) => T::neg_infinity(),
429        }
430    }
431
432    ///
433    /// Get the upper bound of the interval (if any) for floating point types.
434    /// This function returns the infinite value for `T` for upper one-sided intervals.
435    ///
436    pub fn high_f(&self) -> T {
437        match self {
438            Interval::TwoSided(_, high) => *high,
439            Interval::UpperOneSided(_) => T::infinity(),
440            Interval::LowerOneSided(high) => *high,
441        }
442    }
443
444    ///
445    /// Given two intervals, compute the relative interval compared to the reference (argument).
446    /// The relative interval is defined as the interval of the ratios of the two intervals.
447    ///
448    /// E.g., for two two-sided intervals \\( [x, y] \\) and reference \\( [a, b] \\), the relative interval is \\( [(x-b)/b, (y-a)/a] \\).
449    ///
450    pub fn relative_to(&self, reference: &Interval<T>) -> Interval<T> {
451        match (reference, self) {
452            (Interval::TwoSided(a, b), _) if a.is_zero() || b.is_zero() => {
453                panic!("Cannot compute relative interval to a zero interval");
454            }
455            (Interval::LowerOneSided(a) | Interval::UpperOneSided(a), _) if a.is_zero() => {
456                panic!("Cannot compute relative interval to a zero interval");
457            }
458            (&Interval::TwoSided(a, b), &Interval::TwoSided(x, y)) => {
459                Interval::TwoSided((x - b) / b, (y - a) / a)
460            }
461            (
462                &Interval::UpperOneSided(a) | &Interval::TwoSided(a, _),
463                &Interval::LowerOneSided(y) | &Interval::TwoSided(_, y),
464            ) => Interval::LowerOneSided((y - a) / a),
465            (
466                &Interval::LowerOneSided(b) | &Interval::TwoSided(_, b),
467                &Interval::UpperOneSided(x) | &Interval::TwoSided(x, _),
468            ) => Interval::UpperOneSided((x - b) / b),
469            (&Interval::UpperOneSided(_), &Interval::UpperOneSided(_))
470            | (&Interval::LowerOneSided(_), &Interval::LowerOneSided(_)) => {
471                panic!(
472                    "Cannot compute relative interval to one-sided interval with same direction"
473                );
474            }
475        }
476    }
477}
478
479impl<T: num_traits::PrimInt + num_traits::Signed> Interval<T> {
480    ///
481    /// Get the lower bound of the interval (if any) for signed integer types.
482    /// This function returns the minimal value for `T` for lower one-sided intervals.
483    ///
484    pub fn low_i(&self) -> T {
485        match self {
486            Interval::TwoSided(low, _) => *low,
487            Interval::UpperOneSided(low) => *low,
488            Interval::LowerOneSided(_) => <T>::min_value(),
489        }
490    }
491
492    ///
493    /// Get the upper bound of the interval (if any) for signed integer types.
494    /// This function returns the maximal value for `T` for upper one-sided intervals.
495    ///
496    pub fn high_i(&self) -> T {
497        match self {
498            Interval::TwoSided(_, high) => *high,
499            Interval::UpperOneSided(_) => <T>::max_value(),
500            Interval::LowerOneSided(high) => *high,
501        }
502    }
503}
504impl<T: num_traits::PrimInt + num_traits::Unsigned> Interval<T> {
505    ///
506    /// Get the lower bound of the interval (if any) for unsigned integer types.
507    /// This function returns `0` for lower one-sided intervals.
508    ///
509    pub fn low_u(&self) -> T {
510        match self {
511            Interval::TwoSided(low, _) => *low,
512            Interval::UpperOneSided(low) => *low,
513            Interval::LowerOneSided(_) => <T>::min_value(),
514        }
515    }
516
517    ///
518    /// Get the upper bound of the interval (if any) for unsigned integer types.
519    /// This function returns the maximum value for `T` for upper one-sided intervals.
520    ///
521    pub fn high_u(&self) -> T {
522        match self {
523            Interval::TwoSided(_, high) => *high,
524            Interval::UpperOneSided(_) => <T>::max_value(),
525            Interval::LowerOneSided(high) => *high,
526        }
527    }
528}
529
530impl<T: PartialOrd> Interval<T> {
531    ///
532    /// Get a reference to the lower bound of the interval (if any).
533    ///
534    /// See also [`Self::low()`] if cloning is not an issue.
535    ///
536    pub fn low_as_ref(&self) -> Option<&T> {
537        self.left()
538    }
539
540    ///
541    /// Get a reference to the upper bound of the interval (if any).
542    ///
543    /// See also [`Self::high()`] if cloning is not an issue.
544    ///
545    pub fn high_as_ref(&self) -> Option<&T> {
546        self.right()
547    }
548}
549impl<T: PartialOrd + Copy> Interval<T> {
550    fn applied<F>(&self, f_low: F, f_high: F) -> Self
551    where
552        F: FnOnce(T) -> T,
553    {
554        match self {
555            Interval::TwoSided(low, high) => Interval::TwoSided(f_low(*low), f_high(*high)),
556            Interval::LowerOneSided(low) => Interval::UpperOneSided(f_low(*low)),
557            Interval::UpperOneSided(high) => Interval::LowerOneSided(f_high(*high)),
558        }
559    }
560
561    fn applied_both<F>(&self, f: F) -> Self
562    where
563        F: Fn(T) -> T,
564    {
565        self.applied(&f, &f)
566    }
567}
568
569#[cfg(feature = "approx")]
570impl<T: approx::AbsDiffEq + PartialOrd> approx::AbsDiffEq for Interval<T>
571where
572    T::Epsilon: Copy,
573{
574    type Epsilon = T::Epsilon;
575
576    fn default_epsilon() -> T::Epsilon {
577        T::default_epsilon()
578    }
579
580    fn abs_diff_eq(&self, other: &Self, epsilon: T::Epsilon) -> bool {
581        match (self, other) {
582            (Interval::TwoSided(a, b), Interval::TwoSided(x, y)) => {
583                T::abs_diff_eq(a, x, epsilon) && T::abs_diff_eq(b, y, epsilon)
584            }
585            (Interval::UpperOneSided(a), Interval::UpperOneSided(x)) => {
586                T::abs_diff_eq(a, x, epsilon)
587            }
588            (Interval::LowerOneSided(b), Interval::LowerOneSided(y)) => {
589                T::abs_diff_eq(b, y, epsilon)
590            }
591            _ => false,
592        }
593    }
594}
595
596#[cfg(feature = "approx")]
597impl<T: approx::RelativeEq + PartialOrd> approx::RelativeEq for Interval<T>
598where
599    T::Epsilon: Copy,
600{
601    fn default_max_relative() -> T::Epsilon {
602        T::default_max_relative()
603    }
604
605    fn relative_eq(&self, other: &Self, epsilon: T::Epsilon, max_relative: T::Epsilon) -> bool {
606        match (self, other) {
607            (Interval::TwoSided(a, b), Interval::TwoSided(x, y)) => {
608                T::relative_eq(a, x, epsilon, max_relative)
609                    && T::relative_eq(b, y, epsilon, max_relative)
610            }
611            (Interval::UpperOneSided(a), Interval::UpperOneSided(x)) => {
612                T::relative_eq(a, x, epsilon, max_relative)
613            }
614            (Interval::LowerOneSided(b), Interval::LowerOneSided(y)) => {
615                T::relative_eq(b, y, epsilon, max_relative)
616            }
617            _ => false,
618        }
619    }
620}
621
622#[cfg(feature = "approx")]
623impl<T: approx::UlpsEq + PartialOrd> approx::UlpsEq for Interval<T>
624where
625    T::Epsilon: Copy,
626{
627    fn default_max_ulps() -> u32 {
628        T::default_max_ulps()
629    }
630
631    fn ulps_eq(&self, other: &Self, epsilon: T::Epsilon, max_ulps: u32) -> bool {
632        match (self, other) {
633            (Interval::TwoSided(a, b), Interval::TwoSided(x, y)) => {
634                T::ulps_eq(a, x, epsilon, max_ulps) && T::ulps_eq(b, y, epsilon, max_ulps)
635            }
636            (Interval::UpperOneSided(a), Interval::UpperOneSided(x)) => {
637                T::ulps_eq(a, x, epsilon, max_ulps)
638            }
639            (Interval::LowerOneSided(b), Interval::LowerOneSided(y)) => {
640                T::ulps_eq(b, y, epsilon, max_ulps)
641            }
642            _ => false,
643        }
644    }
645}
646
647impl<F: Mul<F, Output = F> + PartialOrd + Copy> Mul<F> for Interval<F> {
648    type Output = Self;
649
650    fn mul(self, rhs: F) -> Self::Output {
651        self.applied_both(|x| x * rhs)
652    }
653}
654
655impl<F: Div<F, Output = F> + PartialOrd + Copy> Div<F> for Interval<F> {
656    type Output = Self;
657
658    fn div(self, rhs: F) -> Self::Output {
659        self.applied_both(|x| x / rhs)
660    }
661}
662
663impl<F: Add<F, Output = F> + PartialOrd + Copy> Add<F> for Interval<F> {
664    type Output = Self;
665
666    fn add(self, rhs: F) -> Self::Output {
667        self.applied_both(|x| x + rhs)
668    }
669}
670
671impl<F: Sub<F, Output = F> + PartialOrd + Copy> Sub<F> for Interval<F> {
672    type Output = Self;
673
674    fn sub(self, rhs: F) -> Self::Output {
675        self.applied_both(|x| x - rhs)
676    }
677}
678
679impl<F: Neg<Output = F> + PartialOrd + Copy> Neg for Interval<F> {
680    type Output = Self;
681
682    fn neg(self) -> Self::Output {
683        self.applied_both(|x| -x)
684    }
685}
686
687impl<F: Num + PartialOrd + Copy> Add for Interval<F> {
688    type Output = Self;
689
690    fn add(self, rhs: Self) -> Self::Output {
691        match (self, rhs) {
692            (Interval::TwoSided(a, b), Interval::TwoSided(x, y)) => {
693                Interval::TwoSided(a + x, b + y)
694            }
695            (Interval::TwoSided(a, _) | Interval::UpperOneSided(a), Interval::UpperOneSided(x)) => {
696                Interval::UpperOneSided(a + x)
697            }
698            (Interval::TwoSided(_, b) | Interval::LowerOneSided(b), Interval::LowerOneSided(y)) => {
699                Interval::LowerOneSided(b + y)
700            }
701            (Interval::UpperOneSided(a), Interval::TwoSided(x, _)) => {
702                Interval::UpperOneSided(a + x)
703            }
704            (Interval::LowerOneSided(b), Interval::TwoSided(_, y)) => {
705                Interval::LowerOneSided(b + y)
706            }
707            (Interval::UpperOneSided(_), Interval::LowerOneSided(_))
708            | (Interval::LowerOneSided(_), Interval::UpperOneSided(_)) => {
709                panic!("Cannot add one-sided intervals with different directions (all values interval)")
710            }
711        }
712    }
713}
714
715impl<F: Num + PartialOrd + Copy> Sub for Interval<F> {
716    type Output = Self;
717
718    fn sub(self, rhs: Self) -> Self::Output {
719        match (self, rhs) {
720            (Interval::TwoSided(a, b), Interval::TwoSided(x, y)) => {
721                Interval::TwoSided(a - y, b - x)
722            }
723            (Interval::TwoSided(_, b) | Interval::LowerOneSided(b), Interval::UpperOneSided(x)) => {
724                Interval::LowerOneSided(b - x)
725            }
726            (Interval::TwoSided(a, _) | Interval::UpperOneSided(a), Interval::LowerOneSided(y)) => {
727                Interval::UpperOneSided(a - y)
728            }
729            (Interval::UpperOneSided(a), Interval::TwoSided(_, y)) => {
730                Interval::UpperOneSided(a - y)
731            }
732            (Interval::LowerOneSided(b), Interval::TwoSided(x, _)) => {
733                Interval::LowerOneSided(b - x)
734            }
735            (Interval::UpperOneSided(_), Interval::UpperOneSided(_))
736            | (Interval::LowerOneSided(_), Interval::LowerOneSided(_)) => {
737                panic!(
738                    "Cannot subtract one-sided intervals of the same directions (empty interval)"
739                )
740            }
741        }
742    }
743}
744
745impl<T: PartialOrd> TryFrom<(T, T)> for Interval<T> {
746    type Error = IntervalError;
747
748    ///
749    /// Create a new interval from a tuple of bounds.
750    /// The first element of the tuple is the lower bound, the second element is the upper bound.
751    /// If the lower bound is greater than the upper bound, an error is returned.
752    ///
753    fn try_from(value: (T, T)) -> Result<Self, Self::Error> {
754        if value.0 <= value.1 {
755            Interval::new(value.0, value.1)
756        } else {
757            Err(IntervalError::InvalidBounds)
758        }
759    }
760}
761
762impl<T: PartialOrd> TryFrom<(Option<T>, Option<T>)> for Interval<T> {
763    type Error = IntervalError;
764
765    ///
766    /// Create a new interval from a tuple of optional bounds.
767    /// The first element of the tuple is the lower bound, the second element is the upper bound.
768    /// If one of the bounds is `None`, the interval is one-sided.
769    /// If both bounds are `None`, an error is returned.
770    ///
771    fn try_from(value: (Option<T>, Option<T>)) -> Result<Self, Self::Error> {
772        match value {
773            (Some(low), Some(high)) => Interval::new(low, high),
774            (Some(low), None) => Ok(Interval::new_upper(low)),
775            (None, Some(high)) => Ok(Interval::new_lower(high)),
776            (None, None) => Err(IntervalError::EmptyInterval),
777        }
778    }
779}
780
781impl<T: PartialOrd + Clone> From<Interval<T>> for (Option<T>, Option<T>) {
782    ///
783    /// Convert an interval to a tuple of optional bounds.
784    /// The first element of the tuple is the lower bound, the second element is the upper bound.
785    /// If the interval is one-sided, one of the bounds is `None`,
786    ///
787    fn from(interval: Interval<T>) -> Self {
788        match interval {
789            Interval::TwoSided(low, high) => (Some(low), Some(high)),
790            Interval::UpperOneSided(low) => (Some(low), None),
791            Interval::LowerOneSided(high) => (None, Some(high)),
792        }
793    }
794}
795
796macro_rules! impl_for_ints {
797    ( $( $x:ty ),+ ) => {
798        $(
799            impl From<Interval<$x>> for ($x, $x) {
800                fn from(value: Interval<$x>) -> Self {
801                    match value {
802                        Interval::TwoSided(low, high) => (low, high),
803                        Interval::UpperOneSided(low) => (low, <$x>::max_value()),
804                        Interval::LowerOneSided(high) => (<$x>::min_value(), high),
805                    }
806                }
807            }
808        )*
809    };
810}
811impl_for_ints!(i8, i16, i32, i64, i128, u8, u16, u32, u64, u128, isize, usize);
812
813macro_rules! impl_for_floats {
814    ( $( $x:ty ),+ ) => {
815        $(
816            impl From<Interval<$x>> for ($x, $x) {
817                fn from(value: Interval<$x>) -> Self {
818                    match value {
819                        Interval::TwoSided(low, high) => (low, high),
820                        Interval::UpperOneSided(low) => (low, <$x>::infinity()),
821                        Interval::LowerOneSided(high) => (<$x>::neg_infinity(), high),
822                    }
823                }
824            }
825        )*
826    };
827}
828impl_for_floats!(f32, f64);
829
830impl<T: PartialOrd> TryFrom<RangeInclusive<T>> for Interval<T> {
831    type Error = IntervalError;
832
833    /// Create an interval from an inclusive range.
834    /// The range must be non-empty or the function will return an error.
835    /// ```
836    /// use stats_ci::Interval;
837    /// use std::ops::RangeInclusive;
838    /// let interval = Interval::try_from(1..=2);
839    /// assert!(interval.is_ok());
840    /// assert_eq!(interval.unwrap(), Interval::new(1, 2).unwrap());
841    /// ```
842    ///
843    fn try_from(range: RangeInclusive<T>) -> Result<Self, Self::Error> {
844        let (start, end) = range.into_inner();
845        Interval::new(start, end)
846    }
847}
848
849impl<T: PartialOrd> From<RangeFrom<T>> for Interval<T> {
850    ///
851    /// Create an upper one-sided interval from a range starting from a given value.
852    ///
853    /// ```
854    /// use stats_ci::Interval;
855    /// use std::ops::RangeFrom;
856    /// let interval = Interval::from(1..);
857    /// assert_eq!(interval, Interval::new_upper(1));
858    /// ```
859    ///
860    fn from(range: RangeFrom<T>) -> Self {
861        Interval::new_upper(range.start)
862    }
863}
864
865impl<T: PartialOrd> From<RangeToInclusive<T>> for Interval<T> {
866    ///
867    /// Create a lower one-sided interval from a range ending at a given value.
868    ///
869    /// ```
870    /// use stats_ci::Interval;
871    /// use std::ops::RangeToInclusive;
872    /// let interval = Interval::from(..=1);
873    /// assert_eq!(interval, Interval::new_lower(1));
874    /// ```
875    ///
876    fn from(range: RangeToInclusive<T>) -> Self {
877        Interval::new_lower(range.end)
878    }
879}
880
881impl<T: PartialOrd> RangeBounds<T> for Interval<T> {
882    fn start_bound(&self) -> std::ops::Bound<&T> {
883        match self.left() {
884            Some(low) => std::ops::Bound::Included(low),
885            None => std::ops::Bound::Unbounded,
886        }
887    }
888
889    fn end_bound(&self) -> std::ops::Bound<&T> {
890        match self.right() {
891            Some(high) => std::ops::Bound::Excluded(high),
892            None => std::ops::Bound::Unbounded,
893        }
894    }
895}
896
897impl<T: PartialOrd + Sub<Output = T> + num_traits::Zero + Clone> Interval<T> {
898    ///
899    /// Compute the width of the interval.
900    /// If the interval is one-sided, the function returns `None`.
901    ///
902    pub fn width(&self) -> Option<T> {
903        match self {
904            Interval::LowerOneSided(_) | Interval::UpperOneSided(_) => None,
905            Interval::TwoSided(low, high) => Some(high.clone() - low.clone()),
906        }
907    }
908}
909
910impl<T: PartialOrd + Clone> Clone for Interval<T> {
911    fn clone(&self) -> Self {
912        match self {
913            Interval::TwoSided(low, high) => Interval::TwoSided(low.clone(), high.clone()),
914            Interval::UpperOneSided(low) => Interval::UpperOneSided(low.clone()),
915            Interval::LowerOneSided(high) => Interval::LowerOneSided(high.clone()),
916        }
917    }
918}
919
920impl<T: PartialOrd + Copy> Copy for Interval<T> {}
921
922use std::fmt::Display;
923impl<T: PartialOrd + Display> Display for Interval<T> {
924    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
925        match self {
926            Interval::TwoSided(low, high) => write!(f, "[{}, {}]", low, high),
927            Interval::UpperOneSided(low) => write!(f, "[{},->)", low),
928            Interval::LowerOneSided(high) => write!(f, "(<-,{}]", high),
929        }
930    }
931}
932
933use std::hash::Hash;
934impl<T: PartialOrd + Hash> Hash for Interval<T> {
935    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
936        match self {
937            Interval::TwoSided(low, high) => {
938                0.hash(state);
939                low.hash(state);
940                high.hash(state);
941            }
942            Interval::UpperOneSided(low) => {
943                1.hash(state);
944                low.hash(state);
945            }
946            Interval::LowerOneSided(high) => {
947                2.hash(state);
948                high.hash(state);
949            }
950        }
951    }
952}
953
954impl<T: PartialOrd> AsRef<Self> for Interval<T> {
955    fn as_ref(&self) -> &Self {
956        self
957    }
958}
959
960impl<T: PartialOrd> PartialOrd for Interval<T> {
961    ///
962    /// Compare two intervals.
963    /// Given two intervals `a` and `b`, `a < b` if and only if the upper bound of `a` is less than the lower bound of `b`.
964    /// Although interval bounds are inclusive, two intervals that overlap only at a single bound are considered ordered.
965    /// E.g., intervals `[x,y]` is considered less than `[a,b]` if `y==a` and `x<b`.
966    ///
967    /// # Examples
968    /// ```
969    /// # fn main() -> stats_ci::CIResult<()> {
970    /// # use std::cmp::Ordering;
971    /// # use stats_ci::Interval;
972    /// let a = Interval::new(0, 10)?;
973    /// let b = Interval::new(10, 20)?;
974    /// let c = Interval::new(11, 20)?;
975    /// let d = Interval::new(0, 10)?;
976    /// let e = Interval::new_upper(10);
977    /// assert_eq!(a.partial_cmp(&b), Some(Ordering::Less));
978    /// assert_eq!(a.partial_cmp(&c), Some(Ordering::Less));
979    /// assert_eq!(a.partial_cmp(&d), Some(Ordering::Equal));
980    /// assert_eq!(a.partial_cmp(&e), Some(Ordering::Less));
981    /// assert_eq!(c.partial_cmp(&a), Some(Ordering::Greater));
982    /// assert_eq!(b.partial_cmp(&c), None);
983    /// # Ok(())
984    /// # }
985    /// ```
986    ///
987    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
988        use std::cmp::Ordering::*;
989        match (self, other) {
990            (xy, ab) if xy == ab => Some(Equal),
991            (
992                Interval::UpperOneSided(low) | Interval::TwoSided(low, _),
993                Interval::LowerOneSided(high) | Interval::TwoSided(_, high),
994            ) if low >= high => Some(Greater),
995            (
996                Interval::LowerOneSided(high) | Interval::TwoSided(_, high),
997                Interval::UpperOneSided(low) | Interval::TwoSided(low, _),
998            ) if low >= high => Some(Less),
999            _ => None,
1000        }
1001    }
1002}
1003
1004///
1005/// An error type for interval creation.
1006///
1007#[allow(missing_docs)]
1008#[derive(thiserror::Error, Debug)]
1009pub enum IntervalError {
1010    #[error("Invalid bounds: the left bound is greater than the right bound")]
1011    InvalidBounds,
1012
1013    #[error("Empty interval")]
1014    EmptyInterval,
1015}
1016
1017/*
1018 *      #   # #   # ### #####   ##### #####  #### #####  ####
1019 *      #   # ##  #  #    #       #   #     #       #   #
1020 *      #   # # # #  #    #       #   ###    ###    #    ###
1021 *      #   # #  ##  #    #       #   #         #   #       #
1022 *       ###  #   # ###   #       #   ##### ####    #   ####
1023 */
1024
1025#[cfg(test)]
1026mod tests {
1027    use super::*;
1028
1029    #[test]
1030    fn test_interval_new() -> Result<(), IntervalError> {
1031        let interval = Interval::new(0., 1.)?;
1032        assert_eq!(interval.low(), Some(0.));
1033        assert_eq!(interval.high(), Some(1.));
1034        assert_eq!(interval.low_f(), 0.);
1035        assert_eq!(interval.high_f(), 1.);
1036        assert!(interval.contains(&0.5));
1037        assert!(!interval.contains(&2.));
1038        assert!(!interval.is_degenerate());
1039        assert!(interval.is_two_sided());
1040        assert!(!interval.is_one_sided());
1041        assert!(!interval.is_lower());
1042        assert!(!interval.is_upper());
1043
1044        let interval = Interval::new_lower(0.);
1045        assert_eq!(interval.low(), None);
1046        assert_eq!(interval.high(), Some(0.));
1047        assert_eq!(interval.low_f(), f64::NEG_INFINITY);
1048        assert_ne!(interval.low_f(), f64::MIN);
1049        assert_eq!(interval.high_f(), 0.);
1050        assert!(!interval.is_degenerate());
1051        assert!(!interval.is_two_sided());
1052        assert!(interval.is_one_sided());
1053        assert!(interval.is_lower());
1054        assert!(!interval.is_upper());
1055
1056        let interval = Interval::new_upper(0.);
1057        assert_eq!(interval.low(), Some(0.));
1058        assert_eq!(interval.high(), None);
1059        assert_eq!(interval.low_f(), 0.);
1060        assert_eq!(interval.high_f(), f64::INFINITY);
1061        assert_ne!(interval.high_f(), f64::MAX);
1062        assert!(!interval.is_degenerate());
1063        assert!(!interval.is_two_sided());
1064        assert!(interval.is_one_sided());
1065        assert!(!interval.is_lower());
1066        assert!(interval.is_upper());
1067
1068        let interval = Interval::new(10, 20)?;
1069        assert_eq!(interval.low(), Some(10));
1070        assert_eq!(interval.high(), Some(20));
1071        assert_eq!(interval.low_i(), 10);
1072        assert_eq!(interval.high_i(), 20);
1073        assert!(interval.contains(&15));
1074        assert!(!interval.contains(&30));
1075        assert!(!interval.is_degenerate());
1076        assert!(interval.is_two_sided());
1077        assert!(!interval.is_one_sided());
1078        assert!(!interval.is_lower());
1079        assert!(!interval.is_upper());
1080
1081        let interval = Interval::new_lower(10_i64);
1082        assert_eq!(interval.low(), None);
1083        assert_eq!(interval.high(), Some(10));
1084        assert_eq!(interval.low_i(), std::i64::MIN);
1085        assert_eq!(interval.high_i(), 10);
1086        assert!(!interval.is_degenerate());
1087        assert!(!interval.is_two_sided());
1088        assert!(interval.is_one_sided());
1089        assert!(interval.is_lower());
1090        assert!(!interval.is_upper());
1091
1092        let interval = Interval::new_lower(10_usize);
1093        assert_eq!(interval.low(), None);
1094        assert_eq!(interval.high(), Some(10));
1095        assert_eq!(interval.low_u(), 0);
1096        assert_eq!(interval.high_u(), 10);
1097        assert!(!interval.is_degenerate());
1098        assert!(!interval.is_two_sided());
1099        assert!(interval.is_one_sided());
1100        assert!(interval.is_lower());
1101        assert!(!interval.is_upper());
1102
1103        let interval = Interval::new(10, 10)?;
1104        assert_eq!(interval.low(), Some(10));
1105        assert_eq!(interval.high(), Some(10));
1106        assert!(interval.is_degenerate());
1107        assert!(interval.is_two_sided());
1108        assert!(!interval.is_one_sided());
1109        assert!(!interval.is_lower());
1110        assert!(!interval.is_upper());
1111        Ok(())
1112    }
1113
1114    #[test]
1115    fn test_interval_contains() -> Result<(), IntervalError> {
1116        let interval = Interval::new(0., 1.)?;
1117        assert!(interval.contains(&0.5));
1118        assert!(!interval.contains(&2.));
1119        Ok(())
1120    }
1121
1122    #[test]
1123    fn test_interval_includes() -> Result<(), IntervalError> {
1124        let interval1 = Interval::new(0., 10.)?;
1125        let interval2 = Interval::new(0., 1.)?;
1126        let interval3 = Interval::new(0., 10.)?;
1127        let interval4 = Interval::new(1., 11.)?;
1128        let interval5 = Interval::new(10., 20.)?;
1129        let interval6 = Interval::new_upper(0.);
1130        let interval7 = Interval::new_upper(11.);
1131        let interval8 = Interval::new_upper(20.);
1132        let interval9 = Interval::new_lower(10.);
1133        let interval10 = Interval::new_lower(1.);
1134        let interval11 = Interval::new_lower(-1.);
1135
1136        assert!(interval1.includes(&interval2));
1137        assert!(interval1.includes(&interval3));
1138        assert!(!interval1.includes(&interval4));
1139        assert!(!interval1.includes(&interval5));
1140        assert!(!interval1.includes(&interval6));
1141        assert!(!interval1.includes(&interval7));
1142        assert!(!interval1.includes(&interval8));
1143        assert!(!interval1.includes(&interval9));
1144        assert!(!interval1.includes(&interval10));
1145
1146        assert!(!interval2.includes(&interval1));
1147        assert!(interval3.includes(&interval1));
1148        assert!(!interval4.includes(&interval1));
1149        assert!(!interval5.includes(&interval1));
1150        assert!(interval6.includes(&interval1));
1151        assert!(!interval7.includes(&interval1));
1152        assert!(!interval8.includes(&interval1));
1153        assert!(interval9.includes(&interval1));
1154        assert!(!interval10.includes(&interval1));
1155        assert!(!interval11.includes(&interval1));
1156
1157        Ok(())
1158    }
1159
1160    #[test]
1161    fn test_interval_compare() -> Result<(), IntervalError> {
1162        use std::cmp::Ordering::*;
1163
1164        let interval1 = Interval::new(0., 10.)?;
1165        let interval2 = Interval::new(0., 1.)?;
1166        let interval3 = Interval::new(0., 10.)?;
1167        let interval4 = Interval::new(1., 11.)?;
1168        let interval5 = Interval::new(10., 20.)?;
1169        let interval6 = Interval::new_upper(0.);
1170        let interval7 = Interval::new_upper(11.);
1171        let interval8 = Interval::new_upper(20.);
1172        let interval9 = Interval::new_lower(10.);
1173        let interval10 = Interval::new_lower(1.);
1174        let interval11 = Interval::new_lower(-1.);
1175
1176        assert_eq!(interval1.partial_cmp(&interval2), None);
1177        assert_eq!(interval1.partial_cmp(&interval3), Some(Equal));
1178        assert_eq!(interval1.partial_cmp(&interval4), None);
1179        assert_eq!(interval1.partial_cmp(&interval5), Some(Less));
1180        assert_eq!(interval1.partial_cmp(&interval6), None);
1181        assert_eq!(interval1.partial_cmp(&interval7), Some(Less));
1182        assert_eq!(interval1.partial_cmp(&interval8), Some(Less));
1183        assert_eq!(interval1.partial_cmp(&interval9), None);
1184        assert_eq!(interval1.partial_cmp(&interval10), None);
1185        assert_eq!(interval1.partial_cmp(&interval11), Some(Greater));
1186
1187        Ok(())
1188    }
1189
1190    #[test]
1191    fn test_interval_from_range() -> Result<(), IntervalError> {
1192        let interval = Interval::try_from(0..=3)?;
1193        assert_eq!(interval, Interval::new(0, 3)?);
1194        assert_eq!(interval.low(), Some(0));
1195        assert_eq!(interval.high(), Some(3));
1196        assert!(interval.contains(&1));
1197        assert!(!interval.contains(&10));
1198
1199        let interval = Interval::from(10..);
1200        assert_eq!(interval, Interval::new_upper(10));
1201        assert_eq!(interval.low(), Some(10));
1202        assert_eq!(interval.high(), None);
1203        assert!(interval.contains(&10));
1204        assert!(interval.contains(&100));
1205        assert!(!interval.contains(&0));
1206
1207        let interval = Interval::from(..=10);
1208        assert_eq!(interval, Interval::new_lower(10));
1209        assert_eq!(interval.low(), None);
1210        assert_eq!(interval.high(), Some(10));
1211        assert!(interval.contains(&10));
1212        assert!(!interval.contains(&100));
1213        assert!(interval.contains(&0));
1214
1215        Ok(())
1216    }
1217
1218    #[test]
1219    fn test_special_case() {
1220        assert!(Interval::new(10, 10).is_ok());
1221        assert!(Interval::new(10, 8).is_err());
1222    }
1223
1224    #[test]
1225    fn test_interval_intersection() -> Result<(), IntervalError> {
1226        let interval1 = Interval::new(0, 10)?;
1227        let interval2 = Interval::new(5, 15)?;
1228        let interval3 = Interval::new(10, 20)?;
1229        let interval4 = Interval::new(15, 25)?;
1230
1231        assert!(interval1.intersects(&interval2));
1232        assert!(interval2.intersects(&interval1));
1233        assert!(interval2.intersects(&interval3));
1234        assert!(interval3.intersects(&interval2));
1235        assert!(interval3.intersects(&interval4));
1236        assert!(interval4.intersects(&interval3));
1237
1238        // intervals are assumed to be inclusive
1239        assert!(interval1.intersects(&interval3));
1240        assert!(interval3.intersects(&interval1));
1241
1242        assert!(!interval1.intersects(&interval4));
1243        assert!(!interval4.intersects(&interval1));
1244
1245        Ok(())
1246    }
1247
1248    #[test]
1249    fn test_interval_equality() -> Result<(), IntervalError> {
1250        let interval1 = Interval::new(0, 10)?;
1251        let interval2 = Interval::new(0, 10)?;
1252        let interval3 = Interval::new(0, 11)?;
1253        let interval4 = Interval::new(1, 10)?;
1254        let interval5 = Interval::new(1, 11)?;
1255
1256        assert_eq!(interval1, interval2);
1257        assert_ne!(interval1, interval3);
1258        assert_ne!(interval1, interval4);
1259        assert_ne!(interval1, interval5);
1260
1261        Ok(())
1262    }
1263
1264    #[test]
1265    fn test_width() -> Result<(), IntervalError> {
1266        assert_eq!(Interval::new(0, 10)?.width(), Some(10));
1267        assert_eq!(Interval::new(0, 0)?.width(), Some(0));
1268        assert_eq!(Interval::new(-10, 0)?.width(), Some(10));
1269        assert_eq!(Interval::new(-10, -10)?.width(), Some(0));
1270
1271        Ok(())
1272    }
1273
1274    #[test]
1275    fn test_from() -> Result<(), IntervalError> {
1276        let interval = Interval::try_from(0..=10)?;
1277        assert_eq!(interval.low(), Some(0));
1278        assert_eq!(interval.high(), Some(10));
1279
1280        let tuple: (i32, i32) = interval.into();
1281        assert_eq!(tuple, (0, 10));
1282
1283        let tuple: (f64, f64) = Interval::new_lower(0.).into();
1284        let (lo, hi) = tuple;
1285        assert!(lo.is_infinite());
1286        assert!(lo.is_sign_negative());
1287        assert_eq!(hi, 0.);
1288
1289        let tuple: (f64, f64) = Interval::new_upper(0.).into();
1290        assert_eq!(tuple, (0., f64::INFINITY));
1291        assert_eq!(tuple, (0., f64::infinity()));
1292
1293        let tuple: (f64, f64) = Interval::new_lower(0.).into();
1294        assert_eq!(tuple, (f64::NEG_INFINITY, 0.));
1295        assert_eq!(tuple, (f64::neg_infinity(), 0.));
1296
1297        let tuple: (usize, usize) = Interval::new_lower(10).into();
1298        assert_eq!(tuple, (0, 10));
1299
1300        let tuple: (usize, usize) = Interval::new_upper(10).into();
1301        assert_eq!(tuple, (10, usize::MAX));
1302        Ok(())
1303    }
1304
1305    #[test]
1306    fn test_send() {
1307        fn assert_send<T: Send>() {}
1308        assert_send::<Interval<f64>>();
1309    }
1310
1311    #[test]
1312    fn test_sync() {
1313        fn assert_sync<T: Sync>() {}
1314        assert_sync::<Interval<f64>>();
1315    }
1316
1317    #[test]
1318    fn test_approx() {
1319        use approx::*;
1320
1321        let interval1 = Interval::new(0., 10.).unwrap();
1322        let interval2 = Interval::new(1e-7, 10.000000001).unwrap();
1323        assert!(interval1.abs_diff_eq(&interval2, 1e-6));
1324        assert_abs_diff_eq!(interval1, interval2, epsilon = 1e-6);
1325    }
1326}