#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum RangeOverlap {
AContainsB,
AInsideB,
AEndsInB,
AStartsInB,
AEqualsB,
None
}
impl RangeOverlap {
pub fn has_overlap(&self) -> bool {
if let Self::None = self {
false
} else {
true
}
}
}
pub fn excl_classify<T: PartialOrd>(a_start: T, a_end: T, b_start: T, b_end: T) -> RangeOverlap {
if a_start == b_start && a_end == b_end {
RangeOverlap::AEqualsB
} else if a_start <= b_start && a_end >= b_end {
RangeOverlap::AContainsB
} else if a_start < b_start && a_end > b_start && a_end <= b_end {
RangeOverlap::AEndsInB
} else if a_start > b_start && a_start < b_end && a_end > b_end {
RangeOverlap::AStartsInB
} else if a_start >= b_end || b_start >= a_end {
RangeOverlap::None
} else {
RangeOverlap::AInsideB
}
}
pub fn incl_classify<T: PartialOrd>(a_start: T, a_end: T, b_start: T, b_end: T) -> RangeOverlap {
if a_start == b_start && a_end == b_end {
RangeOverlap::AEqualsB
} else if a_start <= b_start && a_end >= b_end {
RangeOverlap::AContainsB
} else if a_start < b_start && a_end >= b_start && a_end <= b_end {
RangeOverlap::AEndsInB
} else if a_start > b_start && a_start <= b_end && a_end > b_end {
RangeOverlap::AStartsInB
} else if a_start >= b_end || b_start >= a_end {
RangeOverlap::None
} else {
RangeOverlap::AInsideB
}
}
pub fn classify_any<T: PartialOrd>(a_start: Option<T>, a_end: Option<T>, b_start: Option<T>, b_end: Option<T>, inclusive: bool) -> RangeOverlap {
match (a_start, a_end, b_start, b_end, inclusive) {
(None, None, None, None, _) => RangeOverlap::AEqualsB,
(None, None, None, Some(_), _) => RangeOverlap::AContainsB,
(None, None, Some(_), None, _) => RangeOverlap::AContainsB,
(None, None, Some(_), Some(_), _) => RangeOverlap::AContainsB,
(None, Some(_), None, None, _) => RangeOverlap::AInsideB,
(None, Some(ea), None, Some(eb), _) => {
if ea == eb {
RangeOverlap::AEqualsB
} else if ea < eb {
RangeOverlap::AInsideB
} else {
RangeOverlap::AContainsB
}
},
(None, Some(ea), Some(sb), None, false) => {
if ea <= sb {
RangeOverlap::None
} else {
RangeOverlap::AEndsInB
}
},
(None, Some(ea), Some(sb), None, true) => {
if ea < sb {
RangeOverlap::None
} else {
RangeOverlap::AEndsInB
}
},
(None, Some(ea), Some(sb), Some(eb), false) => {
if ea <= sb {
RangeOverlap::None
} else if ea > sb && ea < eb {
RangeOverlap::AEndsInB
} else {
RangeOverlap::AContainsB
}
},
(None, Some(ea), Some(sb), Some(eb), true) => {
if ea < sb {
RangeOverlap::None
} else if ea >= sb && ea < eb {
RangeOverlap::AEndsInB
} else {
RangeOverlap::AContainsB
}
},
(Some(_), None, None, None, _) => RangeOverlap::AInsideB,
(Some(sa), None, None, Some(eb), false) => {
if sa >= eb {
RangeOverlap::None
} else {
RangeOverlap::AStartsInB
}
},
(Some(sa), None, None, Some(eb), true) => {
if sa > eb {
RangeOverlap::None
} else {
RangeOverlap::AStartsInB
}
},
(Some(sa), None, Some(sb), None, _) => {
if sa == sb {
RangeOverlap::AEqualsB
} else if sa < sb {
RangeOverlap::AContainsB
} else {
RangeOverlap::AInsideB
}
},
(Some(sa), None, Some(sb), Some(eb), false) => {
if sa <= sb {
RangeOverlap::AContainsB
} else if sa < eb {
RangeOverlap::AStartsInB
} else {
RangeOverlap::None
}
},
(Some(sa), None, Some(sb), Some(eb), true) => {
if sa <= sb {
RangeOverlap::AContainsB
} else if sa <= eb {
RangeOverlap::AStartsInB
} else {
RangeOverlap::None
}
},
(Some(_), Some(_), None, None, _) => RangeOverlap::AInsideB,
(Some(sa), Some(ea), None, Some(eb), false) => {
if eb <= sa {
RangeOverlap::None
} else if ea <= eb {
RangeOverlap::AInsideB
} else {
RangeOverlap::AStartsInB
}
},
(Some(sa), Some(ea), None, Some(eb), true) => {
if eb < sa {
RangeOverlap::None
} else if ea <= eb {
RangeOverlap::AInsideB
} else {
RangeOverlap::AStartsInB
}
},
(Some(sa), Some(ea), Some(sb), None, false) => {
if sb >= ea {
RangeOverlap::None
} else if sa >= sb {
RangeOverlap::AInsideB
} else {
RangeOverlap::AEndsInB
}
},
(Some(sa), Some(ea), Some(sb), None, true) => {
if sb > ea {
RangeOverlap::None
} else if sa >= sb {
RangeOverlap::AInsideB
} else {
RangeOverlap::AEndsInB
}
},
(Some(sa), Some(ea), Some(sb), Some(eb), false) => {
excl_classify(sa, ea, sb, eb)
},
(Some(sa), Some(ea), Some(sb), Some(eb), true) => {
incl_classify(sa, ea, sb, eb)
},
}
}
pub fn has_excl_overlap<T: PartialOrd>(a_start: T, a_end: T, b_start: T, b_end: T) -> bool {
excl_classify(a_start, a_end, b_start, b_end).has_overlap()
}
pub fn has_incl_overlap<T: PartialOrd>(a_start: T, a_end: T, b_start: T, b_end: T) -> bool {
incl_classify(a_start, a_end, b_start, b_end).has_overlap()
}
pub fn has_open_excl_overlap<T: PartialOrd>(a_start: Option<T>, a_end: Option<T>, b_start: Option<T>, b_end: Option<T>) -> bool {
classify_any(a_start, a_end, b_start, b_end, false).has_overlap()
}
pub fn has_open_incl_overlap<T: PartialOrd>(a_start: Option<T>, a_end: Option<T>, b_start: Option<T>, b_end: Option<T>) -> bool {
classify_any(a_start, a_end, b_start, b_end, true).has_overlap()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_open_range_exclusive_bool() {
let r1_start = 1;
let r1_end = 20;
let r2_before = -20;
let r2_before2 = -10;
let r2_between = 10;
let r2_after = 30;
let r2_after2 = 40;
assert_eq!(has_open_excl_overlap(Some(r1_start), None, Some(r2_before), None), true);
assert_eq!(has_open_excl_overlap(Some(r1_start), None, Some(r2_between), None), true);
assert_eq!(has_open_excl_overlap(Some(r1_start), None, Some(r2_after), None), true);
assert_eq!(has_open_excl_overlap(Some(r2_before), None, Some(r1_start), None), true);
assert_eq!(has_open_excl_overlap(Some(r2_between), None, Some(r1_start), None), true);
assert_eq!(has_open_excl_overlap(Some(r2_after), None, Some(r1_start), None), true);
assert_eq!(has_open_excl_overlap(Some(r1_start), Some(r1_end), Some(r2_before), None), true);
assert_eq!(has_open_excl_overlap(Some(r1_start), Some(r1_end), Some(r2_between), None), true);
assert_eq!(has_open_excl_overlap(Some(r1_start), Some(r1_end), Some(r2_after), None), false);
assert_eq!(has_open_excl_overlap(Some(r2_before), None, Some(r1_start), Some(r1_end)), true);
assert_eq!(has_open_excl_overlap(Some(r2_between), None, Some(r1_start), Some(r1_end)), true);
assert_eq!(has_open_excl_overlap(Some(r2_after), None, Some(r1_start), Some(r1_end)), false);
assert_eq!(has_open_excl_overlap(Some(r1_start), Some(r1_end), Some(r2_before), Some(r2_before2)), false);
assert_eq!(has_open_excl_overlap(Some(r1_start), Some(r1_end), Some(r2_before), Some(r2_between)), true);
assert_eq!(has_open_excl_overlap(Some(r1_start), Some(r1_end), Some(r2_between), Some(r2_after)), true);
assert_eq!(has_open_excl_overlap(Some(r1_start), Some(r1_end), Some(r2_after), Some(r2_after2)), false);
assert_eq!(has_open_excl_overlap(Some(r2_before), Some(r2_before2), Some(r1_start), Some(r1_end)), false);
assert_eq!(has_open_excl_overlap(Some(r2_before), Some(r2_between), Some(r1_start), Some(r1_end)), true);
assert_eq!(has_open_excl_overlap(Some(r2_between), Some(r2_after), Some(r1_start), Some(r1_end)), true);
assert_eq!(has_open_excl_overlap(Some(r2_after), Some(r2_after2), Some(r1_start), Some(r1_end)), false);
}
#[test]
fn test_open_range_exclusive_classification() {
assert_eq!(classify_any(Some(1), Some(10), Some(1), Some(10), false), RangeOverlap::AEqualsB);
assert_eq!(classify_any(None, Some(10), None, Some(10), false), RangeOverlap::AEqualsB);
assert_eq!(classify_any(Some(1), None, Some(1), None, false), RangeOverlap::AEqualsB);
assert_eq!(classify_any::<i32>(None, None, None, None, false), RangeOverlap::AEqualsB);
assert_eq!(classify_any(Some(1), Some(100), Some(50), Some(60), false), RangeOverlap::AContainsB);
assert_eq!(classify_any(None, Some(100), Some(50), Some(60), false), RangeOverlap::AContainsB);
assert_eq!(classify_any(Some(1), None, Some(50), Some(60), false), RangeOverlap::AContainsB);
assert_eq!(classify_any(None, None, Some(50), Some(60), false), RangeOverlap::AContainsB);
assert_eq!(classify_any(None, Some(100), None, Some(60), false), RangeOverlap::AContainsB);
assert_eq!(classify_any(Some(1), None, Some(50), None, false), RangeOverlap::AContainsB);
assert_eq!(classify_any(None, None, None, Some(60), false), RangeOverlap::AContainsB);
assert_eq!(classify_any(None, None, Some(50), None, false), RangeOverlap::AContainsB);
assert_eq!(classify_any(None, Some(50), Some(1), Some(50), false), RangeOverlap::AContainsB);
assert_eq!(classify_any(Some(1), None, Some(1), Some(50), false), RangeOverlap::AContainsB);
assert_eq!(classify_any(Some(10), Some(50), Some(10), Some(20), false), RangeOverlap::AContainsB);
assert_eq!(classify_any(Some(10), Some(50), Some(40), Some(50), false), RangeOverlap::AContainsB);
assert_eq!(classify_any(None, Some(75), None, None, false), RangeOverlap::AInsideB);
assert_eq!(classify_any(Some(50), None, None, None, false), RangeOverlap::AInsideB);
assert_eq!(classify_any(Some(50), Some(60), Some(1), Some(100), false), RangeOverlap::AInsideB);
assert_eq!(classify_any(Some(50), Some(60), None, Some(100), false), RangeOverlap::AInsideB);
assert_eq!(classify_any(Some(50), Some(60), Some(1), None, false), RangeOverlap::AInsideB);
assert_eq!(classify_any(None, Some(60), None, Some(100), false), RangeOverlap::AInsideB);
assert_eq!(classify_any(Some(50), None, Some(1), None, false), RangeOverlap::AInsideB);
assert_eq!(classify_any(Some(1), Some(50), Some(1), None, false), RangeOverlap::AInsideB);
assert_eq!(classify_any(Some(1), Some(50), None, Some(50), false), RangeOverlap::AInsideB);
assert_eq!(classify_any(Some(1), Some(335), Some(1), None, false), RangeOverlap::AInsideB);
assert_eq!(classify_any(Some(336), Some(366), Some(183), Some(366), false), RangeOverlap::AInsideB);
assert_eq!(classify_any(Some(1), Some(75), Some(25), Some(99), false), RangeOverlap::AEndsInB);
assert_eq!(classify_any(None, Some(75), Some(25), Some(99), false), RangeOverlap::AEndsInB);
assert_eq!(classify_any(None, Some(75), Some(25), None, false), RangeOverlap::AEndsInB);
assert_eq!(classify_any(Some(50), Some(99), Some(1), Some(75), false), RangeOverlap::AStartsInB);
assert_eq!(classify_any(Some(50), None, Some(1), Some(75), false), RangeOverlap::AStartsInB);
assert_eq!(classify_any(Some(50), None, None, Some(75), false), RangeOverlap::AStartsInB);
assert_eq!(classify_any(Some(1), Some(25), Some(50), Some(75), false), RangeOverlap::None);
assert_eq!(classify_any(Some(50), Some(75), Some(1), Some(25), false), RangeOverlap::None);
assert_eq!(classify_any(None, Some(25), Some(50), Some(99), false), RangeOverlap::None);
assert_eq!(classify_any(Some(1), Some(25), Some(50), None, false), RangeOverlap::None);
}
#[test]
fn test_open_range_inclusive_classification() {
assert_eq!(classify_any(Some(1), Some(10), Some(1), Some(10), true), RangeOverlap::AEqualsB);
assert_eq!(classify_any(None, Some(10), None, Some(10), true), RangeOverlap::AEqualsB);
assert_eq!(classify_any(Some(1), None, Some(1), None, true), RangeOverlap::AEqualsB);
assert_eq!(classify_any::<i32>(None, None, None, None, true), RangeOverlap::AEqualsB);
assert_eq!(classify_any(Some(1), Some(100), Some(50), Some(60), true), RangeOverlap::AContainsB);
assert_eq!(classify_any(None, Some(100), Some(50), Some(60), true), RangeOverlap::AContainsB);
assert_eq!(classify_any(Some(1), None, Some(50), Some(60), true), RangeOverlap::AContainsB);
assert_eq!(classify_any(None, None, Some(50), Some(60), true), RangeOverlap::AContainsB);
assert_eq!(classify_any(None, Some(100), None, Some(60), true), RangeOverlap::AContainsB);
assert_eq!(classify_any(Some(1), None, Some(50), None, true), RangeOverlap::AContainsB);
assert_eq!(classify_any(None, None, None, Some(60), true), RangeOverlap::AContainsB);
assert_eq!(classify_any(None, None, Some(50), None, true), RangeOverlap::AContainsB);
assert_eq!(classify_any(None, Some(50), Some(1), Some(50), true), RangeOverlap::AContainsB);
assert_eq!(classify_any(Some(1), None, Some(1), Some(50), true), RangeOverlap::AContainsB);
assert_eq!(classify_any(Some(10), Some(50), Some(10), Some(20), true), RangeOverlap::AContainsB);
assert_eq!(classify_any(Some(10), Some(50), Some(40), Some(50), true), RangeOverlap::AContainsB);
assert_eq!(classify_any(None, Some(75), None, None, true), RangeOverlap::AInsideB);
assert_eq!(classify_any(Some(50), None, None, None, true), RangeOverlap::AInsideB);
assert_eq!(classify_any(Some(50), Some(60), Some(1), Some(100), true), RangeOverlap::AInsideB);
assert_eq!(classify_any(Some(50), Some(60), None, Some(100), true), RangeOverlap::AInsideB);
assert_eq!(classify_any(Some(50), Some(60), Some(1), None, true), RangeOverlap::AInsideB);
assert_eq!(classify_any(None, Some(60), None, Some(100), true), RangeOverlap::AInsideB);
assert_eq!(classify_any(Some(50), None, Some(1), None, true), RangeOverlap::AInsideB);
assert_eq!(classify_any(Some(1), Some(50), Some(1), None, true), RangeOverlap::AInsideB);
assert_eq!(classify_any(Some(1), Some(50), None, Some(50), true), RangeOverlap::AInsideB);
assert_eq!(classify_any(Some(1), Some(335), Some(1), None, true), RangeOverlap::AInsideB);
assert_eq!(classify_any(Some(336), Some(366), Some(183), Some(366), true), RangeOverlap::AInsideB);
assert_eq!(classify_any(Some(1), Some(75), Some(25), Some(99), true), RangeOverlap::AEndsInB);
assert_eq!(classify_any(None, Some(75), Some(25), Some(99), true), RangeOverlap::AEndsInB);
assert_eq!(classify_any(None, Some(75), Some(25), None, true), RangeOverlap::AEndsInB);
assert_eq!(classify_any(Some(50), Some(99), Some(1), Some(75), true), RangeOverlap::AStartsInB);
assert_eq!(classify_any(Some(50), None, Some(1), Some(75), true), RangeOverlap::AStartsInB);
assert_eq!(classify_any(Some(50), None, None, Some(75), true), RangeOverlap::AStartsInB);
assert_eq!(classify_any(Some(1), Some(25), Some(50), Some(75), true), RangeOverlap::None);
assert_eq!(classify_any(Some(50), Some(75), Some(1), Some(25), true), RangeOverlap::None);
assert_eq!(classify_any(None, Some(25), Some(50), Some(99), true), RangeOverlap::None);
assert_eq!(classify_any(Some(1), Some(25), Some(50), None, true), RangeOverlap::None);
}
#[test]
fn test_exclusive_vs_inclusive() {
assert_eq!(excl_classify(1, 5, 5, 10), RangeOverlap::None);
assert_eq!(incl_classify(1, 5, 5, 10), RangeOverlap::AEndsInB);
assert_eq!(excl_classify(10, 15, 5, 10), RangeOverlap::None);
assert_eq!(incl_classify(10, 15, 5, 10), RangeOverlap::AStartsInB);
assert_eq!(classify_any(None, Some(10), Some(10), None, false), RangeOverlap::None);
assert_eq!(classify_any(None, Some(10), Some(10), None, true), RangeOverlap::AEndsInB);
assert_eq!(classify_any(None, Some(10), Some(10), Some(20), false), RangeOverlap::None);
assert_eq!(classify_any(None, Some(10), Some(10), Some(20), true), RangeOverlap::AEndsInB);
assert_eq!(classify_any(Some(1), None, None, Some(1), false), RangeOverlap::None);
assert_eq!(classify_any(Some(1), None, None, Some(1), true), RangeOverlap::AStartsInB);
assert_eq!(classify_any(Some(1), None, Some(-5), Some(1), false), RangeOverlap::None);
assert_eq!(classify_any(Some(1), None, Some(-5), Some(1), true), RangeOverlap::AStartsInB);
assert_eq!(classify_any(Some(1), Some(10), None, Some(1), false), RangeOverlap::None);
assert_eq!(classify_any(Some(1), Some(10), None, Some(1), true), RangeOverlap::AStartsInB);
assert_eq!(classify_any(Some(1), Some(10), Some(10), None, false), RangeOverlap::None);
assert_eq!(classify_any(Some(1), Some(10), Some(10), None, true), RangeOverlap::AEndsInB);
}
}