Skip to main content

btree_range_map/range/
ordering.rs

1use super::{AsBound, AsRange, Directed, Measure};
2use range_traits::{MaybeBounded, PartialEnum};
3use std::{
4	cmp::{Ordering, PartialOrd},
5	ops::Bound,
6};
7
8#[derive(Debug)]
9pub enum RangeOrdering {
10	Before(bool),
11	Intersecting(bool, bool),
12	After(bool),
13}
14
15impl RangeOrdering {
16	pub fn is_before(&self, connected: bool) -> bool {
17		match self {
18			RangeOrdering::Before(c) => !*c || !connected,
19			_ => false,
20		}
21	}
22
23	pub fn is_after(&self, connected: bool) -> bool {
24		match self {
25			RangeOrdering::After(c) => !*c || !connected,
26			_ => false,
27		}
28	}
29
30	pub fn matches(&self, connected: bool) -> bool {
31		match self {
32			RangeOrdering::Before(c) => *c && connected,
33			RangeOrdering::After(c) => *c && connected,
34			RangeOrdering::Intersecting(_, _) => true,
35		}
36	}
37}
38
39pub trait RangePartialOrd<T = Self> {
40	fn range_partial_cmp<R: AsRange<Item = T>>(&self, other: &R) -> Option<RangeOrdering>;
41}
42
43impl<R: AsRange, U> RangePartialOrd<U> for R
44where
45	R::Item: PartialOrd<U> + Measure<U>,
46	U: PartialEnum,
47{
48	fn range_partial_cmp<S: AsRange<Item = U>>(&self, other: &S) -> Option<RangeOrdering> {
49		match direct_bound_partial_cmp(self.start(), other.start(), true) {
50			Some(BoundOrdering::Included(limit_before)) => {
51				match inverse_bound_partial_cmp(self.start(), other.end(), false) {
52					Some(BoundOrdering::Included(_)) => {
53						match direct_bound_partial_cmp(self.end(), other.end(), false) {
54							Some(BoundOrdering::Included(limit_after)) => {
55								Some(RangeOrdering::Intersecting(limit_before, limit_after))
56							}
57							Some(BoundOrdering::Excluded(_)) => {
58								Some(RangeOrdering::Intersecting(limit_before, false))
59							}
60							None => None,
61						}
62					}
63					Some(BoundOrdering::Excluded(limit_after)) => {
64						Some(RangeOrdering::After(limit_after))
65					}
66					None => None,
67				}
68			}
69			Some(BoundOrdering::Excluded(_)) => {
70				match inverse_bound_partial_cmp(self.end(), other.start(), true) {
71					Some(BoundOrdering::Included(_)) => {
72						match direct_bound_partial_cmp(self.end(), other.end(), false) {
73							Some(BoundOrdering::Included(limit_after)) => {
74								Some(RangeOrdering::Intersecting(false, limit_after))
75							}
76							Some(BoundOrdering::Excluded(_)) => {
77								Some(RangeOrdering::Intersecting(false, false))
78							}
79							None => None,
80						}
81					}
82					Some(BoundOrdering::Excluded(limit_before)) => {
83						Some(RangeOrdering::Before(limit_before))
84					}
85					None => None,
86				}
87			}
88			None => None,
89		}
90	}
91}
92
93pub enum BoundOrdering {
94	Included(bool),
95	Excluded(bool),
96}
97
98pub trait BoundPartialOrd<T = Self> {
99	fn bound_partial_cmp<B: AsBound<Item = T>>(&self, other: &Directed<B>)
100	-> Option<BoundOrdering>;
101}
102
103impl<B: AsBound, U> BoundPartialOrd<U> for Directed<B>
104where
105	B::Item: PartialOrd<U> + Measure<U> + PartialEnum,
106	U: PartialEnum,
107{
108	fn bound_partial_cmp<C: AsBound<Item = U>>(
109		&self,
110		other: &Directed<C>,
111	) -> Option<BoundOrdering> {
112		match (self, other) {
113			(Directed::Start(a), Directed::Start(b)) => {
114				direct_bound_partial_cmp(a.bound(), b.bound(), true)
115			}
116			(Directed::Start(a), Directed::End(b)) => {
117				inverse_bound_partial_cmp(a.bound(), b.bound(), false)
118			}
119			(Directed::End(a), Directed::Start(b)) => {
120				inverse_bound_partial_cmp(a.bound(), b.bound(), true)
121			}
122			(Directed::End(a), Directed::End(b)) => {
123				direct_bound_partial_cmp(a.bound(), b.bound(), false)
124			}
125		}
126	}
127}
128
129pub trait BoundOrd<T = Self> {
130	fn bound_cmp<B: AsBound<Item = T>>(&self, other: &Directed<B>) -> BoundOrdering;
131}
132
133impl<B: AsBound> BoundOrd<B::Item> for Directed<B>
134where
135	B::Item: Ord + Measure + PartialEnum,
136{
137	fn bound_cmp<C: AsBound<Item = B::Item>>(&self, other: &Directed<C>) -> BoundOrdering {
138		match (self, other) {
139			(Directed::Start(a), Directed::Start(b)) => {
140				direct_bound_cmp(a.bound(), b.bound(), true)
141			}
142			(Directed::Start(a), Directed::End(b)) => {
143				inverse_bound_cmp(a.bound(), b.bound(), false)
144			}
145			(Directed::End(a), Directed::Start(b)) => inverse_bound_cmp(a.bound(), b.bound(), true),
146			(Directed::End(a), Directed::End(b)) => direct_bound_cmp(a.bound(), b.bound(), false),
147		}
148	}
149}
150
151pub enum Dist {
152	Equals,
153	Zero,
154	One,
155	More,
156}
157
158fn dist<T, U>(t: &T, u: &U) -> Dist
159where
160	T: PartialEnum + PartialEq<U>,
161{
162	if t == u {
163		return Dist::Equals;
164	}
165
166	if let Some(s) = t.succ() {
167		if s == *u {
168			return Dist::Zero;
169		} else {
170			match s.succ() {
171				Some(ss) if ss == *u => return Dist::One,
172				_ => (),
173			}
174		}
175	}
176
177	if let Some(s) = t.pred() {
178		if s == *u {
179			return Dist::Zero;
180		} else {
181			match s.pred() {
182				Some(ss) if ss == *u => return Dist::One,
183				_ => (),
184			}
185		}
186	}
187
188	Dist::More
189}
190
191fn distance_zero<T, U>(t: &T, u: &U) -> bool
192where
193	T: PartialEnum + PartialEq<U>,
194{
195	match t.succ() {
196		Some(s) if s == *u => return true,
197		_ => (),
198	}
199
200	match t.pred() {
201		Some(p) if p == *u => return true,
202		_ => (),
203	}
204
205	false
206}
207
208pub(crate) fn direct_bound_partial_cmp<T, U>(
209	b1: Bound<&T>,
210	b2: Bound<&U>,
211	start: bool,
212) -> Option<BoundOrdering>
213where
214	T: Measure<U> + PartialOrd<U> + PartialEnum,
215	U: PartialEnum,
216{
217	let included_ord = if start {
218		Ordering::Greater
219	} else {
220		Ordering::Less
221	};
222
223	match (b1, b2) {
224		(Bound::Included(v1), Bound::Included(v2)) => match v1.partial_cmp(v2) {
225			Some(Ordering::Equal) => Some(BoundOrdering::Included(true)),
226			Some(ord) if ord == included_ord => Some(BoundOrdering::Included(false)),
227			Some(_) => Some(BoundOrdering::Excluded(distance_zero(v1, v2))),
228			None => None,
229		},
230		(Bound::Included(v1), Bound::Excluded(v2)) => match v1.partial_cmp(v2) {
231			Some(Ordering::Equal) => Some(BoundOrdering::Excluded(true)),
232			Some(ord) if ord == included_ord => {
233				Some(BoundOrdering::Included(distance_zero(v1, v2)))
234			}
235			Some(_) => Some(BoundOrdering::Excluded(false)),
236			None => None,
237		},
238		(Bound::Included(v1), Bound::Unbounded) => Some(BoundOrdering::Included(
239			(start && U::min().map(|m| *v1 == m).unwrap_or(false))
240				|| (!start && U::max().map(|m| *v1 == m).unwrap_or(false)),
241		)),
242		(Bound::Excluded(v1), Bound::Included(v2)) => match v1.partial_cmp(v2) {
243			Some(Ordering::Equal) => Some(BoundOrdering::Included(false)),
244			Some(ord) if ord == included_ord => Some(BoundOrdering::Included(false)),
245			Some(_) => match dist(v1, v2) {
246				Dist::Zero => Some(BoundOrdering::Included(true)),
247				Dist::One => Some(BoundOrdering::Excluded(true)),
248				_ => Some(BoundOrdering::Excluded(false)),
249			},
250			None => None,
251		},
252		(Bound::Excluded(v1), Bound::Excluded(v2)) => match v1.partial_cmp(v2) {
253			Some(Ordering::Equal) => Some(BoundOrdering::Included(true)),
254			Some(ord) if ord == included_ord => Some(BoundOrdering::Included(false)),
255			Some(_) => Some(BoundOrdering::Excluded(distance_zero(v1, v2))),
256			None => None,
257		},
258		(Bound::Excluded(_), Bound::Unbounded) => Some(BoundOrdering::Included(false)),
259		(Bound::Unbounded, Bound::Included(v2)) => {
260			if (start && U::min().map(|m| *v2 == m).unwrap_or(false))
261				|| (!start && U::max().map(|m| *v2 == m).unwrap_or(false))
262			{
263				Some(BoundOrdering::Included(true))
264			} else {
265				Some(BoundOrdering::Excluded(
266					(start
267						&& v2
268							.pred()
269							.and_then(|pred| U::min().map(|m| pred == m))
270							.unwrap_or(false))
271						|| (!start
272							&& v2
273								.succ()
274								.and_then(|succ| U::min().map(|m| succ == m))
275								.unwrap_or(false)),
276				))
277			}
278		}
279		(Bound::Unbounded, Bound::Excluded(_)) => Some(BoundOrdering::Excluded(false)),
280		(Bound::Unbounded, Bound::Unbounded) => Some(BoundOrdering::Included(true)),
281	}
282}
283
284pub(crate) fn direct_bound_cmp<T>(b1: Bound<&T>, b2: Bound<&T>, start: bool) -> BoundOrdering
285where
286	T: Measure + PartialEnum + Ord,
287{
288	let included_ord = if start {
289		Ordering::Greater
290	} else {
291		Ordering::Less
292	};
293
294	match (b1, b2) {
295		(Bound::Included(v1), Bound::Included(v2)) => match v1.cmp(v2) {
296			Ordering::Equal => BoundOrdering::Included(true),
297			ord if ord == included_ord => BoundOrdering::Included(false),
298			_ => BoundOrdering::Excluded(distance_zero(v1, v2)),
299		},
300		(Bound::Included(v1), Bound::Excluded(v2)) => match v1.cmp(v2) {
301			Ordering::Equal => BoundOrdering::Excluded(true),
302			ord if ord == included_ord => BoundOrdering::Included(distance_zero(v1, v2)),
303			_ => BoundOrdering::Excluded(false),
304		},
305		(Bound::Included(v1), Bound::Unbounded) => BoundOrdering::Included(
306			(start && Some(v1) == <T as MaybeBounded>::min().as_ref())
307				|| (!start && Some(v1) == <T as MaybeBounded>::max().as_ref()),
308		),
309		(Bound::Excluded(v1), Bound::Included(v2)) => match v1.cmp(v2) {
310			Ordering::Equal => BoundOrdering::Included(false),
311			ord if ord == included_ord => BoundOrdering::Included(false),
312			_ => match dist(v1, v2) {
313				Dist::Zero => BoundOrdering::Included(true),
314				Dist::One => BoundOrdering::Excluded(true),
315				_ => BoundOrdering::Excluded(false),
316			},
317		},
318		(Bound::Excluded(v1), Bound::Excluded(v2)) => match v1.cmp(v2) {
319			Ordering::Equal => BoundOrdering::Included(true),
320			ord if ord == included_ord => BoundOrdering::Included(false),
321			_ => BoundOrdering::Excluded(distance_zero(v1, v2)),
322		},
323		(Bound::Excluded(_), Bound::Unbounded) => BoundOrdering::Included(false),
324		(Bound::Unbounded, Bound::Included(v2)) => {
325			if (start && <T as MaybeBounded>::min().as_ref() == Some(v2))
326				|| (!start && <T as MaybeBounded>::max().as_ref() == Some(v2))
327			{
328				BoundOrdering::Included(true)
329			} else {
330				BoundOrdering::Excluded(
331					(start
332						&& v2
333							.pred()
334							.map(|pred| <T as MaybeBounded>::min() == Some(pred))
335							.unwrap_or(false))
336						|| (!start
337							&& v2
338								.succ()
339								.map(|succ| <T as MaybeBounded>::min() == Some(succ))
340								.unwrap_or(false)),
341				)
342			}
343		}
344		(Bound::Unbounded, Bound::Excluded(_)) => BoundOrdering::Excluded(false),
345		(Bound::Unbounded, Bound::Unbounded) => BoundOrdering::Included(true),
346	}
347}
348
349pub(crate) fn direct_bound_partial_eq<T, U>(b1: Bound<&T>, b2: Bound<&U>, start: bool) -> bool
350where
351	T: Measure<U> + PartialOrd<U> + PartialEnum,
352	U: PartialEnum,
353{
354	match direct_bound_partial_cmp(b1, b2, start) {
355		Some(BoundOrdering::Included(eq)) => eq,
356		_ => false,
357	}
358}
359
360// pub(crate) fn direct_bound_eq<T>(b1: Bound<&T>, b2: Bound<&T>, start: bool) -> bool where T: Measure + Ord {
361// 	match direct_bound_cmp(b1, b2, start) {
362// 		BoundOrdering::Included(eq) => eq,
363// 		_ => false
364// 	}
365// }
366
367pub(crate) fn inverse_bound_partial_cmp<T, U>(
368	b1: Bound<&T>,
369	b2: Bound<&U>,
370	b2_start: bool,
371) -> Option<BoundOrdering>
372where
373	T: Measure<U> + PartialOrd<U> + PartialEnum,
374	U: PartialEnum,
375{
376	let included_ord = if b2_start {
377		Ordering::Greater
378	} else {
379		Ordering::Less
380	};
381
382	match (b1, b2) {
383		(Bound::Included(v1), Bound::Included(v2)) => match v1.partial_cmp(v2) {
384			Some(Ordering::Equal) => Some(BoundOrdering::Included(true)),
385			Some(ord) if ord == included_ord => Some(BoundOrdering::Included(false)),
386			Some(_) => Some(BoundOrdering::Excluded(distance_zero(v1, v2))),
387			None => None,
388		},
389		(Bound::Included(v1), Bound::Excluded(v2)) => match v1.partial_cmp(v2) {
390			Some(Ordering::Equal) => Some(BoundOrdering::Excluded(true)),
391			Some(ord) if ord == included_ord => {
392				Some(BoundOrdering::Included(distance_zero(v1, v2)))
393			}
394			Some(_) => Some(BoundOrdering::Excluded(false)),
395			None => None,
396		},
397		(Bound::Included(_), Bound::Unbounded) => Some(BoundOrdering::Included(false)),
398		(Bound::Excluded(v1), Bound::Included(v2)) => match v1.partial_cmp(v2) {
399			Some(Ordering::Equal) => Some(BoundOrdering::Excluded(true)), // []v2=v1
400			Some(ord) if ord == included_ord => {
401				Some(BoundOrdering::Included(distance_zero(v1, v2)))
402			}
403			Some(_) => Some(BoundOrdering::Excluded(false)),
404			None => None,
405		},
406		(Bound::Excluded(v1), Bound::Excluded(v2)) => match v1.partial_cmp(v2) {
407			Some(Ordering::Equal) => Some(BoundOrdering::Excluded(false)),
408			Some(ord) if ord == included_ord => match dist(v1, v2) {
409				Dist::Zero => Some(BoundOrdering::Excluded(true)), // v2 [] v1
410				Dist::One => Some(BoundOrdering::Included(true)),  // v2 [ x ] v1
411				_ => Some(BoundOrdering::Included(false)),         // v2 [ x .. y ] v1
412			},
413			Some(_) => Some(BoundOrdering::Excluded(false)), // ] v1 v2 [
414			None => None,
415		},
416		(Bound::Excluded(v1), Bound::Unbounded) => {
417			if (!b2_start && U::max().map(|m| *v1 == m).unwrap_or(false))
418				|| (b2_start && U::min().map(|m| *v1 == m).unwrap_or(false))
419			{
420				Some(BoundOrdering::Excluded(true))
421			} else {
422				Some(BoundOrdering::Included(
423					(!b2_start
424						&& v1
425							.pred()
426							.map(|pred| U::min().map(|m| pred == m).unwrap_or(false))
427							.unwrap_or(false))
428						|| (b2_start
429							&& v1
430								.succ()
431								.map(|succ| U::max().map(|m| succ == m).unwrap_or(false))
432								.unwrap_or(false)),
433				))
434			}
435		}
436		(Bound::Unbounded, Bound::Included(_)) => Some(BoundOrdering::Included(false)),
437		(Bound::Unbounded, Bound::Excluded(v2)) => {
438			if (!b2_start && U::min().map(|m| *v2 == m).unwrap_or(false))
439				|| (b2_start && U::max().map(|m| *v2 == m).unwrap_or(false))
440			{
441				Some(BoundOrdering::Excluded(true))
442			} else {
443				Some(BoundOrdering::Included(
444					(!b2_start
445						&& v2
446							.pred()
447							.map(|pred| U::min().map(|m| pred == m).unwrap_or(false))
448							.unwrap_or(false))
449						|| (b2_start
450							&& v2
451								.succ()
452								.map(|succ| U::max().map(|m| succ == m).unwrap_or(false))
453								.unwrap_or(false)),
454				))
455			}
456		}
457		(Bound::Unbounded, Bound::Unbounded) => Some(BoundOrdering::Included(false)),
458	}
459}
460
461pub(crate) fn inverse_bound_cmp<T>(b1: Bound<&T>, b2: Bound<&T>, b2_start: bool) -> BoundOrdering
462where
463	T: Measure + Ord + PartialEnum,
464{
465	let included_ord = if b2_start {
466		Ordering::Greater
467	} else {
468		Ordering::Less
469	};
470
471	match (b1, b2) {
472		(Bound::Included(v1), Bound::Included(v2)) => match v1.cmp(v2) {
473			Ordering::Equal => BoundOrdering::Included(true),
474			ord if ord == included_ord => BoundOrdering::Included(false),
475			_ => BoundOrdering::Excluded(distance_zero(v1, v2)),
476		},
477		(Bound::Included(v1), Bound::Excluded(v2)) => match v1.cmp(v2) {
478			Ordering::Equal => BoundOrdering::Excluded(true),
479			ord if ord == included_ord => BoundOrdering::Included(distance_zero(v1, v2)),
480			_ => BoundOrdering::Excluded(false),
481		},
482		(Bound::Included(_), Bound::Unbounded) => BoundOrdering::Included(false),
483		(Bound::Excluded(v1), Bound::Included(v2)) => match v1.cmp(v2) {
484			Ordering::Equal => BoundOrdering::Excluded(true), // []v2=v1
485			ord if ord == included_ord => BoundOrdering::Included(distance_zero(v1, v2)),
486			_ => BoundOrdering::Excluded(false),
487		},
488		(Bound::Excluded(v1), Bound::Excluded(v2)) => match v1.cmp(v2) {
489			Ordering::Equal => BoundOrdering::Excluded(false),
490			ord if ord == included_ord => match dist(v1, v2) {
491				Dist::Zero => BoundOrdering::Excluded(true), // v2 [] v1
492				Dist::One => BoundOrdering::Included(true),  // v2 [ x ] v1
493				_ => BoundOrdering::Included(false),         // v2 [ x .. y ] v1
494			},
495			_ => BoundOrdering::Excluded(false), // ] v1 v2 [
496		},
497		(Bound::Excluded(v1), Bound::Unbounded) => {
498			if (!b2_start
499				&& <T as MaybeBounded>::max()
500					.map(|m| *v1 == m)
501					.unwrap_or(false))
502				|| (b2_start
503					&& <T as MaybeBounded>::min()
504						.map(|m| *v1 == m)
505						.unwrap_or(false))
506			{
507				BoundOrdering::Excluded(true)
508			} else {
509				BoundOrdering::Included(
510					(!b2_start
511						&& v1
512							.pred()
513							.map(|pred| {
514								<T as MaybeBounded>::min()
515									.map(|m| pred == m)
516									.unwrap_or(false)
517							})
518							.unwrap_or(false))
519						|| (b2_start
520							&& v1
521								.succ()
522								.map(|succ| {
523									<T as MaybeBounded>::max()
524										.map(|m| succ == m)
525										.unwrap_or(false)
526								})
527								.unwrap_or(false)),
528				)
529			}
530		}
531		(Bound::Unbounded, Bound::Included(_)) => BoundOrdering::Included(false),
532		(Bound::Unbounded, Bound::Excluded(v2)) => {
533			if (!b2_start
534				&& <T as MaybeBounded>::min()
535					.map(|m| *v2 == m)
536					.unwrap_or(false))
537				|| (b2_start
538					&& <T as MaybeBounded>::max()
539						.map(|m| *v2 == m)
540						.unwrap_or(false))
541			{
542				BoundOrdering::Excluded(true)
543			} else {
544				BoundOrdering::Included(
545					(!b2_start
546						&& v2
547							.pred()
548							.map(|pred| {
549								<T as MaybeBounded>::min()
550									.map(|m| pred == m)
551									.unwrap_or(false)
552							})
553							.unwrap_or(false))
554						|| (b2_start
555							&& v2
556								.succ()
557								.map(|succ| {
558									<T as MaybeBounded>::max()
559										.map(|m| succ == m)
560										.unwrap_or(false)
561								})
562								.unwrap_or(false)),
563				)
564			}
565		}
566		(Bound::Unbounded, Bound::Unbounded) => BoundOrdering::Included(false),
567	}
568}
569
570// fn inverse_bound_partial_eq<T, U>(b1: Bound<&T>, b2: Bound<&U>, start: bool) -> bool where T: Measure<U> + PartialOrd<U> {
571// 	match inverse_bound_partial_cmp(b1, b2, start) {
572// 		Some(BoundOrdering::Included(eq)) => eq,
573// 		_ => false
574// 	}
575// }