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)) || (!start
271						&& v2
272							.succ()
273							.and_then(|succ| U::min().map(|m| succ == m))
274							.unwrap_or(false)),
275				))
276			}
277		}
278		(Bound::Unbounded, Bound::Excluded(_)) => Some(BoundOrdering::Excluded(false)),
279		(Bound::Unbounded, Bound::Unbounded) => Some(BoundOrdering::Included(true)),
280	}
281}
282
283pub(crate) fn direct_bound_cmp<T>(b1: Bound<&T>, b2: Bound<&T>, start: bool) -> BoundOrdering
284where
285	T: Measure + PartialEnum + Ord,
286{
287	let included_ord = if start {
288		Ordering::Greater
289	} else {
290		Ordering::Less
291	};
292
293	match (b1, b2) {
294		(Bound::Included(v1), Bound::Included(v2)) => match v1.cmp(v2) {
295			Ordering::Equal => BoundOrdering::Included(true),
296			ord if ord == included_ord => BoundOrdering::Included(false),
297			_ => BoundOrdering::Excluded(distance_zero(v1, v2)),
298		},
299		(Bound::Included(v1), Bound::Excluded(v2)) => match v1.cmp(v2) {
300			Ordering::Equal => BoundOrdering::Excluded(true),
301			ord if ord == included_ord => BoundOrdering::Included(distance_zero(v1, v2)),
302			_ => BoundOrdering::Excluded(false),
303		},
304		(Bound::Included(v1), Bound::Unbounded) => BoundOrdering::Included(
305			(start && Some(v1) == <T as MaybeBounded>::min().as_ref())
306				|| (!start && Some(v1) == <T as MaybeBounded>::max().as_ref()),
307		),
308		(Bound::Excluded(v1), Bound::Included(v2)) => match v1.cmp(v2) {
309			Ordering::Equal => BoundOrdering::Included(false),
310			ord if ord == included_ord => BoundOrdering::Included(false),
311			_ => match dist(v1, v2) {
312				Dist::Zero => BoundOrdering::Included(true),
313				Dist::One => BoundOrdering::Excluded(true),
314				_ => BoundOrdering::Excluded(false),
315			},
316		},
317		(Bound::Excluded(v1), Bound::Excluded(v2)) => match v1.cmp(v2) {
318			Ordering::Equal => BoundOrdering::Included(true),
319			ord if ord == included_ord => BoundOrdering::Included(false),
320			_ => BoundOrdering::Excluded(distance_zero(v1, v2)),
321		},
322		(Bound::Excluded(_), Bound::Unbounded) => BoundOrdering::Included(false),
323		(Bound::Unbounded, Bound::Included(v2)) => {
324			if (start && <T as MaybeBounded>::min().as_ref() == Some(v2))
325				|| (!start && <T as MaybeBounded>::max().as_ref() == Some(v2))
326			{
327				BoundOrdering::Included(true)
328			} else {
329				BoundOrdering::Excluded(
330					(start
331						&& v2
332							.pred()
333							.map(|pred| <T as MaybeBounded>::min() == Some(pred))
334							.unwrap_or(false)) || (!start
335						&& v2
336							.succ()
337							.map(|succ| <T as MaybeBounded>::min() == Some(succ))
338							.unwrap_or(false)),
339				)
340			}
341		}
342		(Bound::Unbounded, Bound::Excluded(_)) => BoundOrdering::Excluded(false),
343		(Bound::Unbounded, Bound::Unbounded) => BoundOrdering::Included(true),
344	}
345}
346
347pub(crate) fn direct_bound_partial_eq<T, U>(b1: Bound<&T>, b2: Bound<&U>, start: bool) -> bool
348where
349	T: Measure<U> + PartialOrd<U> + PartialEnum,
350	U: PartialEnum,
351{
352	match direct_bound_partial_cmp(b1, b2, start) {
353		Some(BoundOrdering::Included(eq)) => eq,
354		_ => false,
355	}
356}
357
358// pub(crate) fn direct_bound_eq<T>(b1: Bound<&T>, b2: Bound<&T>, start: bool) -> bool where T: Measure + Ord {
359// 	match direct_bound_cmp(b1, b2, start) {
360// 		BoundOrdering::Included(eq) => eq,
361// 		_ => false
362// 	}
363// }
364
365pub(crate) fn inverse_bound_partial_cmp<T, U>(
366	b1: Bound<&T>,
367	b2: Bound<&U>,
368	b2_start: bool,
369) -> Option<BoundOrdering>
370where
371	T: Measure<U> + PartialOrd<U> + PartialEnum,
372	U: PartialEnum,
373{
374	let included_ord = if b2_start {
375		Ordering::Greater
376	} else {
377		Ordering::Less
378	};
379
380	match (b1, b2) {
381		(Bound::Included(v1), Bound::Included(v2)) => match v1.partial_cmp(v2) {
382			Some(Ordering::Equal) => Some(BoundOrdering::Included(true)),
383			Some(ord) if ord == included_ord => Some(BoundOrdering::Included(false)),
384			Some(_) => Some(BoundOrdering::Excluded(distance_zero(v1, v2))),
385			None => None,
386		},
387		(Bound::Included(v1), Bound::Excluded(v2)) => match v1.partial_cmp(v2) {
388			Some(Ordering::Equal) => Some(BoundOrdering::Excluded(true)),
389			Some(ord) if ord == included_ord => {
390				Some(BoundOrdering::Included(distance_zero(v1, v2)))
391			}
392			Some(_) => Some(BoundOrdering::Excluded(false)),
393			None => None,
394		},
395		(Bound::Included(_), Bound::Unbounded) => Some(BoundOrdering::Included(false)),
396		(Bound::Excluded(v1), Bound::Included(v2)) => match v1.partial_cmp(v2) {
397			Some(Ordering::Equal) => Some(BoundOrdering::Excluded(true)), // []v2=v1
398			Some(ord) if ord == included_ord => {
399				Some(BoundOrdering::Included(distance_zero(v1, v2)))
400			}
401			Some(_) => Some(BoundOrdering::Excluded(false)),
402			None => None,
403		},
404		(Bound::Excluded(v1), Bound::Excluded(v2)) => match v1.partial_cmp(v2) {
405			Some(Ordering::Equal) => Some(BoundOrdering::Excluded(false)),
406			Some(ord) if ord == included_ord => match dist(v1, v2) {
407				Dist::Zero => Some(BoundOrdering::Excluded(true)), // v2 [] v1
408				Dist::One => Some(BoundOrdering::Included(true)),  // v2 [ x ] v1
409				_ => Some(BoundOrdering::Included(false)),         // v2 [ x .. y ] v1
410			},
411			Some(_) => Some(BoundOrdering::Excluded(false)), // ] v1 v2 [
412			None => None,
413		},
414		(Bound::Excluded(v1), Bound::Unbounded) => {
415			if (!b2_start && U::max().map(|m| *v1 == m).unwrap_or(false))
416				|| (b2_start && U::min().map(|m| *v1 == m).unwrap_or(false))
417			{
418				Some(BoundOrdering::Excluded(true))
419			} else {
420				Some(BoundOrdering::Included(
421					(!b2_start
422						&& v1
423							.pred()
424							.map(|pred| U::min().map(|m| pred == m).unwrap_or(false))
425							.unwrap_or(false)) || (b2_start
426						&& v1
427							.succ()
428							.map(|succ| U::max().map(|m| succ == m).unwrap_or(false))
429							.unwrap_or(false)),
430				))
431			}
432		}
433		(Bound::Unbounded, Bound::Included(_)) => Some(BoundOrdering::Included(false)),
434		(Bound::Unbounded, Bound::Excluded(v2)) => {
435			if (!b2_start && U::min().map(|m| *v2 == m).unwrap_or(false))
436				|| (b2_start && U::max().map(|m| *v2 == m).unwrap_or(false))
437			{
438				Some(BoundOrdering::Excluded(true))
439			} else {
440				Some(BoundOrdering::Included(
441					(!b2_start
442						&& v2
443							.pred()
444							.map(|pred| U::min().map(|m| pred == m).unwrap_or(false))
445							.unwrap_or(false)) || (b2_start
446						&& v2
447							.succ()
448							.map(|succ| U::max().map(|m| succ == m).unwrap_or(false))
449							.unwrap_or(false)),
450				))
451			}
452		}
453		(Bound::Unbounded, Bound::Unbounded) => Some(BoundOrdering::Included(false)),
454	}
455}
456
457pub(crate) fn inverse_bound_cmp<T>(b1: Bound<&T>, b2: Bound<&T>, b2_start: bool) -> BoundOrdering
458where
459	T: Measure + Ord + PartialEnum,
460{
461	let included_ord = if b2_start {
462		Ordering::Greater
463	} else {
464		Ordering::Less
465	};
466
467	match (b1, b2) {
468		(Bound::Included(v1), Bound::Included(v2)) => match v1.cmp(v2) {
469			Ordering::Equal => BoundOrdering::Included(true),
470			ord if ord == included_ord => BoundOrdering::Included(false),
471			_ => BoundOrdering::Excluded(distance_zero(v1, v2)),
472		},
473		(Bound::Included(v1), Bound::Excluded(v2)) => match v1.cmp(v2) {
474			Ordering::Equal => BoundOrdering::Excluded(true),
475			ord if ord == included_ord => BoundOrdering::Included(distance_zero(v1, v2)),
476			_ => BoundOrdering::Excluded(false),
477		},
478		(Bound::Included(_), Bound::Unbounded) => BoundOrdering::Included(false),
479		(Bound::Excluded(v1), Bound::Included(v2)) => match v1.cmp(v2) {
480			Ordering::Equal => BoundOrdering::Excluded(true), // []v2=v1
481			ord if ord == included_ord => BoundOrdering::Included(distance_zero(v1, v2)),
482			_ => BoundOrdering::Excluded(false),
483		},
484		(Bound::Excluded(v1), Bound::Excluded(v2)) => match v1.cmp(v2) {
485			Ordering::Equal => BoundOrdering::Excluded(false),
486			ord if ord == included_ord => match dist(v1, v2) {
487				Dist::Zero => BoundOrdering::Excluded(true), // v2 [] v1
488				Dist::One => BoundOrdering::Included(true),  // v2 [ x ] v1
489				_ => BoundOrdering::Included(false),         // v2 [ x .. y ] v1
490			},
491			_ => BoundOrdering::Excluded(false), // ] v1 v2 [
492		},
493		(Bound::Excluded(v1), Bound::Unbounded) => {
494			if (!b2_start
495				&& <T as MaybeBounded>::max()
496					.map(|m| *v1 == m)
497					.unwrap_or(false))
498				|| (b2_start
499					&& <T as MaybeBounded>::min()
500						.map(|m| *v1 == m)
501						.unwrap_or(false))
502			{
503				BoundOrdering::Excluded(true)
504			} else {
505				BoundOrdering::Included(
506					(!b2_start
507						&& v1
508							.pred()
509							.map(|pred| {
510								<T as MaybeBounded>::min()
511									.map(|m| pred == m)
512									.unwrap_or(false)
513							})
514							.unwrap_or(false)) || (b2_start
515						&& v1
516							.succ()
517							.map(|succ| {
518								<T as MaybeBounded>::max()
519									.map(|m| succ == m)
520									.unwrap_or(false)
521							})
522							.unwrap_or(false)),
523				)
524			}
525		}
526		(Bound::Unbounded, Bound::Included(_)) => BoundOrdering::Included(false),
527		(Bound::Unbounded, Bound::Excluded(v2)) => {
528			if (!b2_start
529				&& <T as MaybeBounded>::min()
530					.map(|m| *v2 == m)
531					.unwrap_or(false))
532				|| (b2_start
533					&& <T as MaybeBounded>::max()
534						.map(|m| *v2 == m)
535						.unwrap_or(false))
536			{
537				BoundOrdering::Excluded(true)
538			} else {
539				BoundOrdering::Included(
540					(!b2_start
541						&& v2
542							.pred()
543							.map(|pred| {
544								<T as MaybeBounded>::min()
545									.map(|m| pred == m)
546									.unwrap_or(false)
547							})
548							.unwrap_or(false)) || (b2_start
549						&& v2
550							.succ()
551							.map(|succ| {
552								<T as MaybeBounded>::max()
553									.map(|m| succ == m)
554									.unwrap_or(false)
555							})
556							.unwrap_or(false)),
557				)
558			}
559		}
560		(Bound::Unbounded, Bound::Unbounded) => BoundOrdering::Included(false),
561	}
562}
563
564// fn inverse_bound_partial_eq<T, U>(b1: Bound<&T>, b2: Bound<&U>, start: bool) -> bool where T: Measure<U> + PartialOrd<U> {
565// 	match inverse_bound_partial_cmp(b1, b2, start) {
566// 		Some(BoundOrdering::Included(eq)) => eq,
567// 		_ => false
568// 	}
569// }