Skip to main content

solverforge_scoring/stream/joiner/
overlapping.rs

1// Overlapping joiner for interval overlap detection.
2
3use std::marker::PhantomData;
4
5use super::Joiner;
6
7/* Creates a joiner that matches when two intervals overlap.
8
9Two intervals [start_a, end_a) and [start_b, end_b) overlap if:
10- start_a < end_b AND start_b < end_a
11
12This uses half-open intervals: the start is inclusive, the end is exclusive.
13
14# Example
15
16```
17use solverforge_scoring::stream::joiner::{Joiner, overlapping};
18
19#[derive(Clone)]
20struct Shift { start: i64, end: i64 }
21
22let overlap = overlapping(
23|s: &Shift| s.start,
24|s: &Shift| s.end,
25|s: &Shift| s.start,
26|s: &Shift| s.end
27);
28
29// Overlapping shifts: [0, 10) and [5, 15) overlap at [5, 10)
30assert!(overlap.matches(
31&Shift { start: 0, end: 10 },
32&Shift { start: 5, end: 15 }
33));
34
35// Non-overlapping shifts: [0, 10) and [10, 20) touch but don't overlap
36assert!(!overlap.matches(
37&Shift { start: 0, end: 10 },
38&Shift { start: 10, end: 20 }
39));
40
41// Non-overlapping shifts: [0, 5) and [10, 15) are disjoint
42assert!(!overlap.matches(
43&Shift { start: 0, end: 5 },
44&Shift { start: 10, end: 15 }
45));
46```
47*/
48pub fn overlapping<A, B, T, Fsa, Fea, Fsb, Feb>(
49    start_a: Fsa,
50    end_a: Fea,
51    start_b: Fsb,
52    end_b: Feb,
53) -> OverlappingJoiner<Fsa, Fea, Fsb, Feb, T>
54where
55    T: Ord,
56    Fsa: Fn(&A) -> T + Send + Sync,
57    Fea: Fn(&A) -> T + Send + Sync,
58    Fsb: Fn(&B) -> T + Send + Sync,
59    Feb: Fn(&B) -> T + Send + Sync,
60{
61    OverlappingJoiner {
62        start_a,
63        end_a,
64        start_b,
65        end_b,
66        _phantom: PhantomData,
67    }
68}
69
70/* A joiner that matches when two intervals overlap.
71
72Created by the [`overlapping()`] function.
73*/
74pub struct OverlappingJoiner<Fsa, Fea, Fsb, Feb, T> {
75    start_a: Fsa,
76    end_a: Fea,
77    start_b: Fsb,
78    end_b: Feb,
79    _phantom: PhantomData<fn() -> T>,
80}
81
82impl<A, B, T, Fsa, Fea, Fsb, Feb> Joiner<A, B> for OverlappingJoiner<Fsa, Fea, Fsb, Feb, T>
83where
84    T: Ord,
85    Fsa: Fn(&A) -> T + Send + Sync,
86    Fea: Fn(&A) -> T + Send + Sync,
87    Fsb: Fn(&B) -> T + Send + Sync,
88    Feb: Fn(&B) -> T + Send + Sync,
89{
90    #[inline]
91    fn matches(&self, a: &A, b: &B) -> bool {
92        let start_a = (self.start_a)(a);
93        let end_a = (self.end_a)(a);
94        let start_b = (self.start_b)(b);
95        let end_b = (self.end_b)(b);
96
97        // Half-open interval overlap: [start_a, end_a) ∩ [start_b, end_b) ≠ ∅
98        // iff start_a < end_b AND start_b < end_a
99        start_a < end_b && start_b < end_a
100    }
101}