use std::cmp::Ordering;
use crate::btic::{Btic, NEG_INF, POS_INF};
use crate::certainty::Certainty;
use crate::granularity::Granularity;
pub fn intersection(a: &Btic, b: &Btic) -> Option<Btic> {
let lo = a.lo().max(b.lo());
let hi = a.hi().min(b.hi());
if lo >= hi {
return None;
}
let (lo_gran, lo_cert) = pick_bound_meta(a, b, BoundSide::Lo, Ordering::Greater);
let (hi_gran, hi_cert) = pick_bound_meta(a, b, BoundSide::Hi, Ordering::Less);
let meta = build_result_meta(lo, hi, lo_gran, hi_gran, lo_cert, hi_cert);
Btic::new(lo, hi, meta).ok()
}
pub fn span(a: &Btic, b: &Btic) -> Btic {
let lo = a.lo().min(b.lo());
let hi = a.hi().max(b.hi());
let (lo_gran, lo_cert) = pick_bound_meta(a, b, BoundSide::Lo, Ordering::Less);
let (hi_gran, hi_cert) = pick_bound_meta(a, b, BoundSide::Hi, Ordering::Greater);
let meta = build_result_meta(lo, hi, lo_gran, hi_gran, lo_cert, hi_cert);
Btic::new(lo, hi, meta).expect("span of two valid intervals must be valid")
}
pub fn gap(a: &Btic, b: &Btic) -> Option<Btic> {
let gap_lo = a.hi().min(b.hi());
let gap_hi = a.lo().max(b.lo());
if gap_lo >= gap_hi {
return None;
}
let (lo_gran, lo_cert) = pick_bound_meta(a, b, BoundSide::Hi, Ordering::Less);
let (hi_gran, hi_cert) = pick_bound_meta(a, b, BoundSide::Lo, Ordering::Greater);
let meta = build_result_meta(gap_lo, gap_hi, lo_gran, hi_gran, lo_cert, hi_cert);
Btic::new(gap_lo, gap_hi, meta).ok()
}
#[derive(Clone, Copy)]
enum BoundSide {
Lo,
Hi,
}
fn bound_meta(btic: &Btic, side: BoundSide) -> (Granularity, Certainty) {
match side {
BoundSide::Lo => (btic.lo_granularity(), btic.lo_certainty()),
BoundSide::Hi => (btic.hi_granularity(), btic.hi_certainty()),
}
}
fn bound_val(btic: &Btic, side: BoundSide) -> i64 {
match side {
BoundSide::Lo => btic.lo(),
BoundSide::Hi => btic.hi(),
}
}
fn pick_bound_meta(
a: &Btic,
b: &Btic,
side: BoundSide,
pick: std::cmp::Ordering,
) -> (Granularity, Certainty) {
let va = bound_val(a, side);
let vb = bound_val(b, side);
let (ga, ca) = bound_meta(a, side);
let (gb, cb) = bound_meta(b, side);
match va.cmp(&vb) {
ord if ord == pick => (ga, ca),
std::cmp::Ordering::Equal => (ga.finer(gb), ca.least_certain(cb)),
_ => (gb, cb),
}
}
fn build_result_meta(
lo: i64,
hi: i64,
lo_gran: Granularity,
hi_gran: Granularity,
lo_cert: Certainty,
hi_cert: Certainty,
) -> u64 {
let (lg, lc) = if lo == NEG_INF {
(Granularity::Millisecond, Certainty::Definite)
} else {
(lo_gran, lo_cert)
};
let (hg, hc) = if hi == POS_INF {
(Granularity::Millisecond, Certainty::Definite)
} else {
(hi_gran, hi_cert)
};
Btic::build_meta(lg, hg, lc, hc)
}
#[cfg(test)]
mod tests {
use super::*;
fn make(lo: i64, hi: i64) -> Btic {
let meta = Btic::build_meta(
Granularity::Millisecond,
Granularity::Millisecond,
Certainty::Definite,
Certainty::Definite,
);
Btic::new(lo, hi, meta).unwrap()
}
fn make_with_gran(lo: i64, hi: i64, lg: Granularity, hg: Granularity) -> Btic {
let meta = Btic::build_meta(lg, hg, Certainty::Definite, Certainty::Definite);
Btic::new(lo, hi, meta).unwrap()
}
fn make_with_cert(lo: i64, hi: i64, lc: Certainty, hc: Certainty) -> Btic {
let meta = Btic::build_meta(Granularity::Millisecond, Granularity::Millisecond, lc, hc);
Btic::new(lo, hi, meta).unwrap()
}
#[test]
fn intersection_overlapping() {
let a = make(100, 300);
let b = make(200, 400);
let r = intersection(&a, &b).unwrap();
assert_eq!(r.lo(), 200);
assert_eq!(r.hi(), 300);
}
#[test]
fn intersection_contained() {
let a = make(100, 400);
let b = make(200, 300);
let r = intersection(&a, &b).unwrap();
assert_eq!(r.lo(), 200);
assert_eq!(r.hi(), 300);
}
#[test]
fn intersection_identical() {
let a = make(100, 200);
let r = intersection(&a, &a).unwrap();
assert_eq!(r.lo(), 100);
assert_eq!(r.hi(), 200);
}
#[test]
fn intersection_disjoint() {
let a = make(100, 200);
let b = make(300, 400);
assert!(intersection(&a, &b).is_none());
}
#[test]
fn intersection_adjacent_is_none() {
let a = make(100, 200);
let b = make(200, 300);
assert!(intersection(&a, &b).is_none()); }
#[test]
fn intersection_granularity_inherited() {
let a = make_with_gran(100, 300, Granularity::Month, Granularity::Year);
let b = make_with_gran(200, 400, Granularity::Day, Granularity::Day);
let r = intersection(&a, &b).unwrap();
assert_eq!(r.lo_granularity(), Granularity::Day);
assert_eq!(r.hi_granularity(), Granularity::Year);
}
#[test]
fn intersection_equal_bounds_finer_granularity() {
let a = make_with_gran(100, 300, Granularity::Year, Granularity::Year);
let b = make_with_gran(100, 300, Granularity::Day, Granularity::Day);
let r = intersection(&a, &b).unwrap();
assert_eq!(r.lo_granularity(), Granularity::Day);
assert_eq!(r.hi_granularity(), Granularity::Day);
}
#[test]
fn intersection_certainty_inherited() {
let a = make_with_cert(100, 300, Certainty::Definite, Certainty::Approximate);
let b = make_with_cert(200, 400, Certainty::Uncertain, Certainty::Definite);
let r = intersection(&a, &b).unwrap();
assert_eq!(r.lo_certainty(), Certainty::Uncertain);
assert_eq!(r.hi_certainty(), Certainty::Approximate);
}
#[test]
fn intersection_with_sentinel() {
let a = Btic::new(
NEG_INF,
300,
Btic::build_meta(
Granularity::Millisecond,
Granularity::Day,
Certainty::Definite,
Certainty::Definite,
),
)
.unwrap();
let b = make(100, 400);
let r = intersection(&a, &b).unwrap();
assert_eq!(r.lo(), 100);
assert_eq!(r.hi(), 300);
}
#[test]
fn span_overlapping() {
let a = make(100, 300);
let b = make(200, 400);
let r = span(&a, &b);
assert_eq!(r.lo(), 100);
assert_eq!(r.hi(), 400);
}
#[test]
fn span_disjoint() {
let a = make(100, 200);
let b = make(300, 400);
let r = span(&a, &b);
assert_eq!(r.lo(), 100);
assert_eq!(r.hi(), 400);
}
#[test]
fn span_contained() {
let a = make(100, 400);
let b = make(200, 300);
let r = span(&a, &b);
assert_eq!(r.lo(), 100);
assert_eq!(r.hi(), 400);
}
#[test]
fn span_identical() {
let a = make(100, 200);
let r = span(&a, &a);
assert_eq!(r.lo(), 100);
assert_eq!(r.hi(), 200);
}
#[test]
fn span_granularity_inherited() {
let a = make_with_gran(100, 300, Granularity::Month, Granularity::Year);
let b = make_with_gran(200, 400, Granularity::Day, Granularity::Day);
let r = span(&a, &b);
assert_eq!(r.lo_granularity(), Granularity::Month);
assert_eq!(r.hi_granularity(), Granularity::Day);
}
#[test]
fn span_with_sentinel() {
let a = Btic::new(
NEG_INF,
200,
Btic::build_meta(
Granularity::Millisecond,
Granularity::Day,
Certainty::Definite,
Certainty::Definite,
),
)
.unwrap();
let b = make(100, 400);
let r = span(&a, &b);
assert_eq!(r.lo(), NEG_INF);
assert_eq!(r.hi(), 400);
assert_eq!(r.lo_granularity(), Granularity::Millisecond);
assert_eq!(r.lo_certainty(), Certainty::Definite);
}
#[test]
fn gap_disjoint_with_space() {
let a = make(100, 200);
let b = make(300, 400);
let r = gap(&a, &b).unwrap();
assert_eq!(r.lo(), 200); assert_eq!(r.hi(), 300); }
#[test]
fn gap_disjoint_reversed() {
let a = make(300, 400);
let b = make(100, 200);
let r = gap(&a, &b).unwrap();
assert_eq!(r.lo(), 200);
assert_eq!(r.hi(), 300);
}
#[test]
fn gap_overlapping_returns_none() {
let a = make(100, 300);
let b = make(200, 400);
assert!(gap(&a, &b).is_none());
}
#[test]
fn gap_adjacent_returns_none() {
let a = make(100, 200);
let b = make(200, 300);
assert!(gap(&a, &b).is_none());
}
#[test]
fn gap_contained_returns_none() {
let a = make(100, 400);
let b = make(200, 300);
assert!(gap(&a, &b).is_none());
}
#[test]
fn gap_granularity_inherited() {
let a = make_with_gran(100, 200, Granularity::Month, Granularity::Year);
let b = make_with_gran(300, 400, Granularity::Day, Granularity::Day);
let r = gap(&a, &b).unwrap();
assert_eq!(r.lo_granularity(), Granularity::Year);
assert_eq!(r.hi_granularity(), Granularity::Day);
}
}