1use std::cmp::Ordering;
2
3use crate::btic::{Btic, NEG_INF, POS_INF};
4use crate::certainty::Certainty;
5use crate::granularity::Granularity;
6
7pub fn intersection(a: &Btic, b: &Btic) -> Option<Btic> {
17 let lo = a.lo().max(b.lo());
18 let hi = a.hi().min(b.hi());
19
20 if lo >= hi {
22 return None;
23 }
24
25 let (lo_gran, lo_cert) = pick_bound_meta(a, b, BoundSide::Lo, Ordering::Greater);
26 let (hi_gran, hi_cert) = pick_bound_meta(a, b, BoundSide::Hi, Ordering::Less);
27
28 let meta = build_result_meta(lo, hi, lo_gran, hi_gran, lo_cert, hi_cert);
29 Btic::new(lo, hi, meta).ok()
30}
31
32pub fn span(a: &Btic, b: &Btic) -> Btic {
38 let lo = a.lo().min(b.lo());
39 let hi = a.hi().max(b.hi());
40
41 let (lo_gran, lo_cert) = pick_bound_meta(a, b, BoundSide::Lo, Ordering::Less);
42 let (hi_gran, hi_cert) = pick_bound_meta(a, b, BoundSide::Hi, Ordering::Greater);
43
44 let meta = build_result_meta(lo, hi, lo_gran, hi_gran, lo_cert, hi_cert);
45 Btic::new(lo, hi, meta).expect("span of two valid intervals must be valid")
46}
47
48pub fn gap(a: &Btic, b: &Btic) -> Option<Btic> {
56 let gap_lo = a.hi().min(b.hi());
57 let gap_hi = a.lo().max(b.lo());
58
59 if gap_lo >= gap_hi {
61 return None;
62 }
63
64 let (lo_gran, lo_cert) = pick_bound_meta(a, b, BoundSide::Hi, Ordering::Less);
66 let (hi_gran, hi_cert) = pick_bound_meta(a, b, BoundSide::Lo, Ordering::Greater);
68
69 let meta = build_result_meta(gap_lo, gap_hi, lo_gran, hi_gran, lo_cert, hi_cert);
70 Btic::new(gap_lo, gap_hi, meta).ok()
71}
72
73#[derive(Clone, Copy)]
78enum BoundSide {
79 Lo,
80 Hi,
81}
82
83fn bound_meta(btic: &Btic, side: BoundSide) -> (Granularity, Certainty) {
85 match side {
86 BoundSide::Lo => (btic.lo_granularity(), btic.lo_certainty()),
87 BoundSide::Hi => (btic.hi_granularity(), btic.hi_certainty()),
88 }
89}
90
91fn bound_val(btic: &Btic, side: BoundSide) -> i64 {
93 match side {
94 BoundSide::Lo => btic.lo(),
95 BoundSide::Hi => btic.hi(),
96 }
97}
98
99fn pick_bound_meta(
109 a: &Btic,
110 b: &Btic,
111 side: BoundSide,
112 pick: std::cmp::Ordering,
113) -> (Granularity, Certainty) {
114 debug_assert_ne!(
115 pick,
116 std::cmp::Ordering::Equal,
117 "pick_bound_meta requires Greater or Less"
118 );
119
120 let va = bound_val(a, side);
121 let vb = bound_val(b, side);
122 let (ga, ca) = bound_meta(a, side);
123 let (gb, cb) = bound_meta(b, side);
124
125 match va.cmp(&vb) {
126 std::cmp::Ordering::Equal => (ga.finer(gb), ca.least_certain(cb)),
127 ord if ord == pick => (ga, ca),
128 _ => (gb, cb),
129 }
130}
131
132fn build_result_meta(
134 lo: i64,
135 hi: i64,
136 lo_gran: Granularity,
137 hi_gran: Granularity,
138 lo_cert: Certainty,
139 hi_cert: Certainty,
140) -> u64 {
141 let (lg, lc) = if lo == NEG_INF {
142 (Granularity::Millisecond, Certainty::Definite)
143 } else {
144 (lo_gran, lo_cert)
145 };
146 let (hg, hc) = if hi == POS_INF {
147 (Granularity::Millisecond, Certainty::Definite)
148 } else {
149 (hi_gran, hi_cert)
150 };
151 Btic::build_meta(lg, hg, lc, hc)
152}
153
154#[cfg(test)]
155mod tests {
156 use super::*;
157
158 fn make(lo: i64, hi: i64) -> Btic {
159 let meta = Btic::build_meta(
160 Granularity::Millisecond,
161 Granularity::Millisecond,
162 Certainty::Definite,
163 Certainty::Definite,
164 );
165 Btic::new(lo, hi, meta).unwrap()
166 }
167
168 fn make_with_gran(lo: i64, hi: i64, lg: Granularity, hg: Granularity) -> Btic {
169 let meta = Btic::build_meta(lg, hg, Certainty::Definite, Certainty::Definite);
170 Btic::new(lo, hi, meta).unwrap()
171 }
172
173 fn make_with_cert(lo: i64, hi: i64, lc: Certainty, hc: Certainty) -> Btic {
174 let meta = Btic::build_meta(Granularity::Millisecond, Granularity::Millisecond, lc, hc);
175 Btic::new(lo, hi, meta).unwrap()
176 }
177
178 #[test]
181 fn intersection_overlapping() {
182 let a = make(100, 300);
183 let b = make(200, 400);
184 let r = intersection(&a, &b).unwrap();
185 assert_eq!(r.lo(), 200);
186 assert_eq!(r.hi(), 300);
187 }
188
189 #[test]
190 fn intersection_contained() {
191 let a = make(100, 400);
192 let b = make(200, 300);
193 let r = intersection(&a, &b).unwrap();
194 assert_eq!(r.lo(), 200);
195 assert_eq!(r.hi(), 300);
196 }
197
198 #[test]
199 fn intersection_identical() {
200 let a = make(100, 200);
201 let r = intersection(&a, &a).unwrap();
202 assert_eq!(r.lo(), 100);
203 assert_eq!(r.hi(), 200);
204 }
205
206 #[test]
207 fn intersection_disjoint() {
208 let a = make(100, 200);
209 let b = make(300, 400);
210 assert!(intersection(&a, &b).is_none());
211 }
212
213 #[test]
214 fn intersection_adjacent_is_none() {
215 let a = make(100, 200);
216 let b = make(200, 300);
217 assert!(intersection(&a, &b).is_none()); }
219
220 #[test]
221 fn intersection_granularity_inherited() {
222 let a = make_with_gran(100, 300, Granularity::Month, Granularity::Year);
223 let b = make_with_gran(200, 400, Granularity::Day, Granularity::Day);
224 let r = intersection(&a, &b).unwrap();
225 assert_eq!(r.lo_granularity(), Granularity::Day);
227 assert_eq!(r.hi_granularity(), Granularity::Year);
229 }
230
231 #[test]
232 fn intersection_equal_bounds_finer_granularity() {
233 let a = make_with_gran(100, 300, Granularity::Year, Granularity::Year);
234 let b = make_with_gran(100, 300, Granularity::Day, Granularity::Day);
235 let r = intersection(&a, &b).unwrap();
236 assert_eq!(r.lo_granularity(), Granularity::Day);
238 assert_eq!(r.hi_granularity(), Granularity::Day);
239 }
240
241 #[test]
242 fn intersection_certainty_inherited() {
243 let a = make_with_cert(100, 300, Certainty::Definite, Certainty::Approximate);
244 let b = make_with_cert(200, 400, Certainty::Uncertain, Certainty::Definite);
245 let r = intersection(&a, &b).unwrap();
246 assert_eq!(r.lo_certainty(), Certainty::Uncertain);
248 assert_eq!(r.hi_certainty(), Certainty::Approximate);
250 }
251
252 #[test]
253 fn intersection_with_sentinel() {
254 let a = Btic::new(
255 NEG_INF,
256 300,
257 Btic::build_meta(
258 Granularity::Millisecond,
259 Granularity::Day,
260 Certainty::Definite,
261 Certainty::Definite,
262 ),
263 )
264 .unwrap();
265 let b = make(100, 400);
266 let r = intersection(&a, &b).unwrap();
267 assert_eq!(r.lo(), 100);
268 assert_eq!(r.hi(), 300);
269 }
270
271 #[test]
274 fn span_overlapping() {
275 let a = make(100, 300);
276 let b = make(200, 400);
277 let r = span(&a, &b);
278 assert_eq!(r.lo(), 100);
279 assert_eq!(r.hi(), 400);
280 }
281
282 #[test]
283 fn span_disjoint() {
284 let a = make(100, 200);
285 let b = make(300, 400);
286 let r = span(&a, &b);
287 assert_eq!(r.lo(), 100);
288 assert_eq!(r.hi(), 400);
289 }
290
291 #[test]
292 fn span_contained() {
293 let a = make(100, 400);
294 let b = make(200, 300);
295 let r = span(&a, &b);
296 assert_eq!(r.lo(), 100);
297 assert_eq!(r.hi(), 400);
298 }
299
300 #[test]
301 fn span_identical() {
302 let a = make(100, 200);
303 let r = span(&a, &a);
304 assert_eq!(r.lo(), 100);
305 assert_eq!(r.hi(), 200);
306 }
307
308 #[test]
309 fn span_granularity_inherited() {
310 let a = make_with_gran(100, 300, Granularity::Month, Granularity::Year);
311 let b = make_with_gran(200, 400, Granularity::Day, Granularity::Day);
312 let r = span(&a, &b);
313 assert_eq!(r.lo_granularity(), Granularity::Month);
315 assert_eq!(r.hi_granularity(), Granularity::Day);
317 }
318
319 #[test]
320 fn span_with_sentinel() {
321 let a = Btic::new(
322 NEG_INF,
323 200,
324 Btic::build_meta(
325 Granularity::Millisecond,
326 Granularity::Day,
327 Certainty::Definite,
328 Certainty::Definite,
329 ),
330 )
331 .unwrap();
332 let b = make(100, 400);
333 let r = span(&a, &b);
334 assert_eq!(r.lo(), NEG_INF);
335 assert_eq!(r.hi(), 400);
336 assert_eq!(r.lo_granularity(), Granularity::Millisecond);
338 assert_eq!(r.lo_certainty(), Certainty::Definite);
339 }
340
341 #[test]
344 fn gap_disjoint_with_space() {
345 let a = make(100, 200);
346 let b = make(300, 400);
347 let r = gap(&a, &b).unwrap();
348 assert_eq!(r.lo(), 200); assert_eq!(r.hi(), 300); }
351
352 #[test]
353 fn gap_disjoint_reversed() {
354 let a = make(300, 400);
355 let b = make(100, 200);
356 let r = gap(&a, &b).unwrap();
357 assert_eq!(r.lo(), 200);
358 assert_eq!(r.hi(), 300);
359 }
360
361 #[test]
362 fn gap_overlapping_returns_none() {
363 let a = make(100, 300);
364 let b = make(200, 400);
365 assert!(gap(&a, &b).is_none());
366 }
367
368 #[test]
369 fn gap_adjacent_returns_none() {
370 let a = make(100, 200);
371 let b = make(200, 300);
372 assert!(gap(&a, &b).is_none());
373 }
374
375 #[test]
376 fn gap_contained_returns_none() {
377 let a = make(100, 400);
378 let b = make(200, 300);
379 assert!(gap(&a, &b).is_none());
380 }
381
382 #[test]
383 fn gap_granularity_inherited() {
384 let a = make_with_gran(100, 200, Granularity::Month, Granularity::Year);
385 let b = make_with_gran(300, 400, Granularity::Day, Granularity::Day);
386 let r = gap(&a, &b).unwrap();
387 assert_eq!(r.lo_granularity(), Granularity::Year);
389 assert_eq!(r.hi_granularity(), Granularity::Day);
391 }
392}