use crate::time_base::{IntervalT, TimeT, UtcT};
use crate::uto::Uto;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OverlapType {
Container,
Contained,
Overlap,
NoOverlap,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Tio {
inner: IntervalT,
}
impl Tio {
#[must_use]
pub const fn from_interval(interval: IntervalT) -> Self {
Self { inner: interval }
}
#[must_use]
pub const fn time_interval(self) -> IntervalT {
self.inner
}
#[must_use]
pub fn spans(self, uto: Uto) -> (OverlapType, Tio) {
let other_lo = uto.time().saturating_sub(uto.inaccuracy());
let other_hi = uto.time().saturating_add(uto.inaccuracy());
let a = self.inner;
let b = IntervalT {
lower_bound: other_lo,
upper_bound: other_hi,
};
compute_overlap(a, b)
}
#[must_use]
pub fn overlaps(self, other: Tio) -> (OverlapType, Tio) {
compute_overlap(self.inner, other.inner)
}
#[must_use]
pub fn time(self) -> Uto {
let lo = self.inner.lower_bound;
let hi = self.inner.upper_bound;
let midpoint = lo / 2 + hi / 2 + (lo % 2 + hi % 2) / 2;
let half_width = (hi - lo) / 2;
Uto::from_utc(UtcT::new(midpoint, half_width, 0))
}
}
fn compute_overlap(a: IntervalT, b: IntervalT) -> (OverlapType, Tio) {
if a.upper_bound < b.lower_bound {
let gap = IntervalT {
lower_bound: a.upper_bound,
upper_bound: b.lower_bound,
};
return (OverlapType::NoOverlap, Tio::from_interval(gap));
}
if b.upper_bound < a.lower_bound {
let gap = IntervalT {
lower_bound: b.upper_bound,
upper_bound: a.lower_bound,
};
return (OverlapType::NoOverlap, Tio::from_interval(gap));
}
if a.lower_bound <= b.lower_bound && a.upper_bound >= b.upper_bound {
return (OverlapType::Container, Tio::from_interval(b));
}
if b.lower_bound <= a.lower_bound && b.upper_bound >= a.upper_bound {
return (OverlapType::Contained, Tio::from_interval(a));
}
let overlap_lo = max_t(a.lower_bound, b.lower_bound);
let overlap_hi = min_t(a.upper_bound, b.upper_bound);
let overlap_interval = IntervalT {
lower_bound: overlap_lo,
upper_bound: overlap_hi,
};
(OverlapType::Overlap, Tio::from_interval(overlap_interval))
}
const fn max_t(a: TimeT, b: TimeT) -> TimeT {
if a > b { a } else { b }
}
const fn min_t(a: TimeT, b: TimeT) -> TimeT {
if a < b { a } else { b }
}
#[cfg(test)]
#[allow(clippy::expect_used)]
mod tests {
use super::*;
#[test]
fn time_interval_attribute_returns_constructor_value() {
let i = IntervalT::new(100, 200).expect("ok");
let tio = Tio::from_interval(i);
assert_eq!(tio.time_interval(), i);
}
#[test]
fn time_method_returns_uto_with_midpoint_and_half_width() {
let i = IntervalT::new(100, 200).expect("ok");
let tio = Tio::from_interval(i);
let uto = tio.time();
assert_eq!(uto.time(), 150);
assert_eq!(uto.inaccuracy(), 50);
}
#[test]
fn overlaps_otcontainer() {
let a = Tio::from_interval(IntervalT::new(100, 300).expect("ok"));
let b = Tio::from_interval(IntervalT::new(150, 250).expect("ok"));
let (kind, overlap) = a.overlaps(b);
assert_eq!(kind, OverlapType::Container);
assert_eq!(overlap.time_interval().lower_bound, 150);
assert_eq!(overlap.time_interval().upper_bound, 250);
}
#[test]
fn overlaps_otcontained() {
let a = Tio::from_interval(IntervalT::new(150, 250).expect("ok"));
let b = Tio::from_interval(IntervalT::new(100, 300).expect("ok"));
let (kind, overlap) = a.overlaps(b);
assert_eq!(kind, OverlapType::Contained);
assert_eq!(overlap.time_interval().lower_bound, 150);
assert_eq!(overlap.time_interval().upper_bound, 250);
}
#[test]
fn overlaps_partial() {
let a = Tio::from_interval(IntervalT::new(100, 200).expect("ok"));
let b = Tio::from_interval(IntervalT::new(150, 250).expect("ok"));
let (kind, overlap) = a.overlaps(b);
assert_eq!(kind, OverlapType::Overlap);
assert_eq!(overlap.time_interval().lower_bound, 150);
assert_eq!(overlap.time_interval().upper_bound, 200);
}
#[test]
fn overlaps_no_overlap() {
let a = Tio::from_interval(IntervalT::new(100, 200).expect("ok"));
let b = Tio::from_interval(IntervalT::new(300, 400).expect("ok"));
let (kind, gap) = a.overlaps(b);
assert_eq!(kind, OverlapType::NoOverlap);
assert_eq!(gap.time_interval().lower_bound, 200);
assert_eq!(gap.time_interval().upper_bound, 300);
}
#[test]
fn spans_uses_uto_inaccuracy_envelope() {
let a = Tio::from_interval(IntervalT::new(50, 250).expect("ok"));
let uto = Uto::new(150, 50, 0);
let (kind, overlap) = a.spans(uto);
assert_eq!(kind, OverlapType::Container);
assert_eq!(overlap.time_interval().lower_bound, 100);
assert_eq!(overlap.time_interval().upper_bound, 200);
}
}