Skip to main content

btree_range_map/
range.rs

1pub use range_traits::{Measure, PartialEnum};
2use std::{
3	cmp::PartialOrd,
4	ops::{Bound, RangeBounds},
5};
6
7mod bound;
8mod ordering;
9
10mod any;
11mod from_excluded;
12mod from_excluded_to;
13mod from_excluded_to_included;
14
15pub use any::*;
16pub use bound::*;
17pub use from_excluded::*;
18pub use from_excluded_to::*;
19pub use from_excluded_to_included::*;
20pub use ordering::*;
21
22/// Types that can be interpreted as ranges.
23pub trait AsRange: Sized {
24	/// Type of the elements of the range.
25	type Item: Measure + PartialEnum;
26
27	/// Start bound of the range.
28	fn start(&self) -> Bound<&Self::Item>;
29
30	/// End bound of the range.
31	fn end(&self) -> Bound<&Self::Item>;
32
33	fn is_empty(&self) -> bool {
34		is_range_empty(self.start(), self.end())
35	}
36
37	fn intersects<R: AsRange>(&self, other: &R) -> bool
38	where
39		Self::Item: PartialOrd<R::Item> + Measure<R::Item>,
40	{
41		matches!(
42			self.range_partial_cmp(other),
43			Some(RangeOrdering::Intersecting(_, _))
44		)
45	}
46
47	fn connected_to<R: AsRange>(&self, other: &R) -> bool
48	where
49		Self::Item: PartialOrd<R::Item> + Measure<R::Item>,
50	{
51		match self.range_partial_cmp(other) {
52			Some(RangeOrdering::Intersecting(_, _)) => true,
53			Some(RangeOrdering::Before(connected)) => connected,
54			Some(RangeOrdering::After(connected)) => connected,
55			_ => false,
56		}
57	}
58
59	fn intersected_with<'a, R: AsRange<Item = Self::Item>>(
60		&'a self,
61		other: &'a R,
62	) -> AnyRange<&'a Self::Item>
63	where
64		Self::Item: PartialOrd + Measure,
65	{
66		AnyRange {
67			start: max_bound(self.start(), other.start(), true),
68			end: min_bound(self.end(), other.end(), false),
69		}
70	}
71
72	fn without<'a, R: AsRange<Item = Self::Item>>(
73		&'a self,
74		other: &'a R,
75	) -> Difference<&'a Self::Item>
76	where
77		Self::Item: PartialOrd + Measure,
78	{
79		let left = match invert_bound(other.start()) {
80			Some(inverted_other_start) => {
81				if !is_range_empty(self.start(), inverted_other_start) {
82					Some(AnyRange {
83						start: self.start(),
84						end: inverted_other_start,
85					})
86				} else {
87					None
88				}
89			}
90			None => None,
91		};
92
93		let right = match invert_bound(other.end()) {
94			Some(inverted_other_end) => {
95				if !is_range_empty(inverted_other_end, self.end()) {
96					Some(AnyRange {
97						start: inverted_other_end,
98						end: self.end(),
99					})
100				} else {
101					None
102				}
103			}
104			None => None,
105		};
106
107		match (left, right) {
108			(Some(left), None) => Difference::Before(
109				left,
110				Directed::End(left.end) >= Directed::Start(other.start()),
111			),
112			(None, Some(right)) => Difference::After(
113				right,
114				Directed::Start(right.start) <= Directed::End(other.end()),
115			),
116			(Some(left), Some(right)) => Difference::Split(left, right),
117			(None, None) => Difference::Empty,
118		}
119	}
120
121	fn product<'a, R: AsRange<Item = Self::Item>>(&'a self, other: &'a R) -> Product<&'a Self::Item>
122	where
123		Self::Item: PartialOrd + Measure,
124	{
125		let before = match crop_right(self, other.start()) {
126			Some(self_before) => Some(ProductArg::Subject(self_before)),
127			None => crop_right(other, self.start()).map(ProductArg::Object),
128		};
129
130		let intersection = self.intersected_with(other);
131		let intersection = if is_range_empty(intersection.start, intersection.end) {
132			None
133		} else {
134			Some(intersection)
135		};
136
137		let after = match crop_left(self, other.end()) {
138			Some(self_after) => Some(ProductArg::Subject(self_after)),
139			None => crop_left(other, self.end()).map(ProductArg::Object),
140		};
141
142		Product {
143			before,
144			intersection,
145			after,
146		}
147	}
148}
149
150pub trait IntoRange: AsRange {
151	fn into_range(self) -> AnyRange<Self::Item>;
152}
153
154fn crop_left<'a, R: AsRange>(
155	range: &'a R,
156	other_end: Bound<&'a R::Item>,
157) -> Option<AnyRange<&'a R::Item>> {
158	match invert_bound(other_end) {
159		Some(inverted_other_end) => {
160			let max_start = max_bound(range.start(), inverted_other_end, true);
161			if !is_range_empty(max_start, range.end()) {
162				Some(AnyRange {
163					start: inverted_other_end,
164					end: range.end(),
165				})
166			} else {
167				None
168			}
169		}
170		None => None,
171	}
172}
173
174fn crop_right<'a, R: AsRange>(
175	range: &'a R,
176	other_start: Bound<&'a R::Item>,
177) -> Option<AnyRange<&'a R::Item>> {
178	match invert_bound(other_start) {
179		Some(inverted_other_start) => {
180			let min_end = min_bound(range.end(), inverted_other_start, false);
181			if !is_range_empty(range.start(), min_end) {
182				Some(AnyRange {
183					start: range.start(),
184					end: min_end,
185				})
186			} else {
187				None
188			}
189		}
190		None => None,
191	}
192}
193
194/// Part of the result of a `product` operation.
195#[derive(Debug)]
196pub enum ProductArg<T> {
197	/// A part of the subject, `self`.
198	Subject(AnyRange<T>),
199
200	/// A part of the object, `other`.
201	Object(AnyRange<T>),
202}
203
204impl<T: Clone> ProductArg<&T> {
205	pub fn cloned(&self) -> ProductArg<T> {
206		match self {
207			ProductArg::Subject(range) => ProductArg::Subject(range.cloned()),
208			ProductArg::Object(range) => ProductArg::Object(range.cloned()),
209		}
210	}
211}
212
213/// Result of a `product` operation.
214#[derive(Debug)]
215pub struct Product<T> {
216	/// What is left of `self` and `other` before their intersection.
217	pub before: Option<ProductArg<T>>,
218
219	/// The intersection of `self` and `other`, if not empty.
220	pub intersection: Option<AnyRange<T>>,
221
222	/// What is left of `self` and `other` after their intersection.
223	pub after: Option<ProductArg<T>>,
224}
225
226impl<T: Clone> Product<&T> {
227	pub fn cloned(&self) -> Product<T> {
228		Product {
229			before: self.before.as_ref().map(|r| r.cloned()),
230			intersection: self.intersection.as_ref().map(|r| r.cloned()),
231			after: self.after.as_ref().map(|r| r.cloned()),
232		}
233	}
234}
235
236pub enum RelativePosition {
237	Before,
238	After,
239}
240
241/// Result of a `without` operation.
242pub enum Difference<T> {
243	/// The end of the range may intersects `other`. The boolean is set to true if it does.
244	Before(AnyRange<T>, bool),
245
246	/// The begining of the range may intersects `other`. The boolean is set to true if it does.
247	After(AnyRange<T>, bool),
248
249	/// The `other` range if fully included.
250	Split(AnyRange<T>, AnyRange<T>),
251
252	/// The range is fully included in `other`.
253	Empty,
254}
255
256macro_rules! singleton_range {
257	($ty:ident) => {
258		impl AsRange for $ty {
259			type Item = Self;
260
261			fn start(&self) -> Bound<&Self::Item> {
262				Bound::Included(self)
263			}
264
265			fn end(&self) -> Bound<&Self::Item> {
266				Bound::Included(self)
267			}
268		}
269
270		impl IntoRange for $ty {
271			fn into_range(self) -> AnyRange<Self::Item> {
272				AnyRange::new(Bound::Included(self), Bound::Included(self))
273			}
274		}
275	};
276}
277
278singleton_range!(u8);
279singleton_range!(i8);
280singleton_range!(u16);
281singleton_range!(i16);
282singleton_range!(u32);
283singleton_range!(i32);
284singleton_range!(u64);
285singleton_range!(i64);
286// singleton_range!(u128);
287// singleton_range!(i128);
288singleton_range!(usize);
289// singleton_range!(isize);
290singleton_range!(f32);
291singleton_range!(f64);
292singleton_range!(char);
293
294macro_rules! standard_range {
295	($ty:path, |$this:ident| $into_range:expr) => {
296		impl<T: Measure + PartialEnum> AsRange for $ty {
297			type Item = T;
298
299			fn start(&self) -> Bound<&Self::Item> {
300				self.start_bound()
301			}
302
303			fn end(&self) -> Bound<&Self::Item> {
304				self.end_bound()
305			}
306		}
307
308		impl<T: Measure + PartialEnum> IntoRange for $ty {
309			fn into_range($this) -> AnyRange<Self::Item> {
310				$into_range
311			}
312		}
313	};
314}
315
316standard_range!(std::ops::Range<T>, |self| AnyRange::new(
317	Bound::Included(self.start),
318	Bound::Excluded(self.end)
319));
320standard_range!(std::ops::RangeInclusive<T>, |self| {
321	let (a, b) = self.into_inner();
322	AnyRange::new(Bound::Included(a), Bound::Included(b))
323});
324standard_range!(std::ops::RangeFrom<T>, |self| AnyRange::new(
325	Bound::Included(self.start),
326	Bound::Unbounded
327));
328standard_range!(std::ops::RangeTo<T>, |self| AnyRange::new(
329	Bound::Unbounded,
330	Bound::Excluded(self.end)
331));
332standard_range!(std::ops::RangeToInclusive<T>, |self| AnyRange::new(
333	Bound::Unbounded,
334	Bound::Included(self.end)
335));
336standard_range!(AnyRange<T>, |self| self);
337standard_range!(RangeFromExcluded<T>, |self| AnyRange::new(
338	Bound::Excluded(self.start),
339	Bound::Unbounded
340));
341standard_range!(RangeFromExcludedTo<T>, |self| AnyRange::new(
342	Bound::Excluded(self.start),
343	Bound::Excluded(self.end)
344));
345standard_range!(RangeFromExcludedToIncluded<T>, |self| AnyRange::new(
346	Bound::Excluded(self.start),
347	Bound::Included(self.end)
348));
349
350#[inline(always)]
351fn is_range_empty<T, U>(start: Bound<&T>, end: Bound<&U>) -> bool
352where
353	T: PartialOrd<U> + Measure<U> + PartialEnum,
354	U: PartialEnum,
355{
356	Directed::Start(start) > Directed::End(end)
357}
358
359#[cfg(test)]
360mod tests {
361	use crate::RangeSet;
362
363	use super::*;
364	use std::cmp::Ordering;
365
366	macro_rules! make_bound {
367		([= $v:literal ..]) => {
368			Directed::Start(Bound::Included(&$v))
369		};
370		([$v:literal ..]) => {
371			Directed::Start(Bound::Excluded(&$v))
372		};
373		([~ ..]) => {
374			Directed::Start(Bound::Unbounded)
375		};
376		([..= $v:literal]) => {
377			Directed::End(Bound::Included(&$v))
378		};
379		([.. $v:literal]) => {
380			Directed::End(Bound::Excluded(&$v))
381		};
382		([.. ~]) => {
383			Directed::End(Bound::Unbounded)
384		};
385	}
386
387	macro_rules! test_bound_cmp {
388		(@assert $ty:ty, $a:tt, $b:tt, $expected:ident) => {
389			assert_eq!(<Directed<Bound<&$ty>> as PartialOrd>::partial_cmp(&make_bound!($a), &make_bound!($b)), Some(Ordering::$expected));
390		};
391		($ty:ty, $a:tt < $b:tt) => {
392			test_bound_cmp!(@assert $ty, $a, $b, Less)
393		};
394		($ty:ty, $a:tt == $b:tt) => {
395			test_bound_cmp!(@assert $ty, $a, $b, Equal)
396		};
397		($ty:ty, $a:tt > $b:tt) => {
398			test_bound_cmp!(@assert $ty, $a, $b, Greater)
399		}
400	}
401
402	#[test]
403	fn issue_2() {
404		let k = AnyRange {
405			start: Bound::Excluded(0u32),
406			end: Bound::Unbounded,
407		};
408		assert!(!k.is_empty());
409
410		let mut ids: RangeSet<u32> = RangeSet::new();
411		ids.insert(0u32);
412
413		let mut gaps = ids.gaps();
414		assert_eq!(
415			gaps.next().unwrap().cloned(),
416			AnyRange::new(Bound::Excluded(0), Bound::Unbounded)
417		);
418		assert_eq!(gaps.next().map(AnyRange::cloned), None);
419	}
420
421	#[test]
422	fn unsigned_integer_bound_partial_less() {
423		test_bound_cmp!(u32, [=0..] < [=1..]);
424		test_bound_cmp!(u32, [=0..] < [0..]);
425		test_bound_cmp!(u32, [=0..] < [..=1]);
426		test_bound_cmp!(u32, [=0..] < [..2]);
427		test_bound_cmp!(u32, [=0..] < [..~]);
428
429		test_bound_cmp!(u32, [0..] < [=2..]);
430		test_bound_cmp!(u32, [0..] < [1..]);
431		test_bound_cmp!(u32, [0..] < [..=2]);
432		test_bound_cmp!(u32, [0..] < [..3]);
433		test_bound_cmp!(u32, [0..] < [..~]);
434
435		test_bound_cmp!(u32, [~..] < [..=0]);
436		test_bound_cmp!(u32, [~..] < [..~]);
437
438		test_bound_cmp!(u32, [..=0] < [=1..]);
439		test_bound_cmp!(u32, [..=0] < [0..]);
440		test_bound_cmp!(u32, [..=0] < [..=1]);
441		test_bound_cmp!(u32, [..=0] < [..2]);
442		test_bound_cmp!(u32, [..=0] < [..~]);
443
444		test_bound_cmp!(u32, [..1] < [=1..]);
445		test_bound_cmp!(u32, [..1] < [0..]);
446		test_bound_cmp!(u32, [..1] < [..=1]);
447		test_bound_cmp!(u32, [..1] < [..2]);
448		test_bound_cmp!(u32, [..0] < [..~]);
449	}
450
451	#[test]
452	fn unsigned_integer_bound_partial_eq() {
453		test_bound_cmp!(u32, [~..] == [=0..]);
454	}
455
456	#[test]
457	fn unsigned_integer_bound_partial_greater() {
458		test_bound_cmp!(u32, [~..] > [..0]);
459	}
460
461	#[test]
462	fn integer_bound_partial_less() {
463		test_bound_cmp!(i32, [=0..] < [=1..]);
464		test_bound_cmp!(i32, [=0..] < [0..]);
465		test_bound_cmp!(i32, [=0..] < [..=1]);
466		test_bound_cmp!(i32, [=0..] < [..2]);
467		test_bound_cmp!(i32, [=0..] < [..~]);
468
469		test_bound_cmp!(i32, [0..] < [=2..]);
470		test_bound_cmp!(i32, [0..] < [1..]);
471		test_bound_cmp!(i32, [0..] < [..=2]);
472		test_bound_cmp!(i32, [0..] < [..3]);
473		test_bound_cmp!(i32, [0..] < [..~]);
474		test_bound_cmp!(i32, [-2_147_483_648i32..] < [..~]);
475
476		test_bound_cmp!(i32, [~..] < [=0..]);
477		test_bound_cmp!(i32, [~..] < [..=0]);
478		test_bound_cmp!(i32, [~..] < [..0]);
479		test_bound_cmp!(i32, [~..] < [..~]);
480
481		test_bound_cmp!(i32, [..=0] < [=1..]);
482		test_bound_cmp!(i32, [..=0] < [0..]);
483		test_bound_cmp!(i32, [..=0] < [..=1]);
484		test_bound_cmp!(i32, [..=0] < [..2]);
485		test_bound_cmp!(i32, [..=0] < [..~]);
486
487		test_bound_cmp!(i32, [..1] < [=1..]);
488		test_bound_cmp!(i32, [..1] < [0..]);
489		test_bound_cmp!(i32, [..1] < [..=1]);
490		test_bound_cmp!(i32, [..1] < [..2]);
491		test_bound_cmp!(i32, [..0] < [..~]);
492	}
493
494	#[test]
495	fn integer_bound_partial_eq() {
496		test_bound_cmp!(i32, [=0..] == [=0..]);
497		test_bound_cmp!(i32, [=1..] == [0..]);
498		test_bound_cmp!(i32, [=0..] == [..=0]);
499		test_bound_cmp!(i32, [=0..] == [..1]);
500
501		test_bound_cmp!(i32, [0..] == [=1..]);
502		test_bound_cmp!(i32, [0..] == [0..]);
503		test_bound_cmp!(i32, [0..] == [..=1]);
504		test_bound_cmp!(i32, [0..] == [..2]);
505
506		test_bound_cmp!(i32, [~..] == [~..]);
507
508		test_bound_cmp!(i32, [..=0] == [=0..]);
509		test_bound_cmp!(i32, [..=1] == [0..]);
510		test_bound_cmp!(i32, [..=0] == [..=0]);
511		test_bound_cmp!(i32, [..=0] == [..1]);
512
513		test_bound_cmp!(i32, [..1] == [=0..]);
514		test_bound_cmp!(i32, [..2] == [0..]);
515		test_bound_cmp!(i32, [..1] == [..=0]);
516		test_bound_cmp!(i32, [..0] == [..0]);
517
518		test_bound_cmp!(i32, [..~] == [..~]);
519	}
520
521	#[test]
522	fn integer_bound_partial_greater() {
523		test_bound_cmp!(i32, [=1..] > [=0..]);
524		test_bound_cmp!(i32, [0..] > [=0..]);
525		test_bound_cmp!(i32, [..=1] > [=0..]);
526		test_bound_cmp!(i32, [..2] > [=0..]);
527		test_bound_cmp!(i32, [..~] > [=0..]);
528
529		test_bound_cmp!(i32, [=2..] > [0..]);
530		test_bound_cmp!(i32, [1..] > [0..]);
531		test_bound_cmp!(i32, [..=2] > [0..]);
532		test_bound_cmp!(i32, [..3] > [0..]);
533		test_bound_cmp!(i32, [..~] > [0..]);
534
535		test_bound_cmp!(i32, [=0..] > [~..]);
536		test_bound_cmp!(i32, [..=0] > [~..]);
537		test_bound_cmp!(i32, [..0] > [~..]);
538		test_bound_cmp!(i32, [..~] > [~..]);
539
540		test_bound_cmp!(i32, [=1..] > [..=0]);
541		test_bound_cmp!(i32, [0..] > [..=0]);
542		test_bound_cmp!(i32, [..=1] > [..=0]);
543		test_bound_cmp!(i32, [..2] > [..=0]);
544		test_bound_cmp!(i32, [..~] > [..=0]);
545
546		test_bound_cmp!(i32, [=1..] > [..1]);
547		test_bound_cmp!(i32, [0..] > [..1]);
548		test_bound_cmp!(i32, [..=1] > [..1]);
549		test_bound_cmp!(i32, [..2] > [..1]);
550		test_bound_cmp!(i32, [..~] > [..0]);
551	}
552
553	#[test]
554	fn float_bound_partial_less() {
555		test_bound_cmp!(f32, [=0.0..] < [=1.0..]);
556		test_bound_cmp!(f32, [=0.0..] < [0.0..]);
557		test_bound_cmp!(f32, [=0.0..] < [..=1.0]);
558		test_bound_cmp!(f32, [=0.0..] < [..2.0]);
559		test_bound_cmp!(f32, [=0.0..] < [..~]);
560
561		test_bound_cmp!(f32, [0.0..] < [=2.0..]);
562		test_bound_cmp!(f32, [0.0..] < [1.0..]);
563		test_bound_cmp!(f32, [0.0..] < [..1.0]); // different from the int behavior
564		test_bound_cmp!(f32, [0.0..] < [..=2.0]);
565		test_bound_cmp!(f32, [0.0..] < [..3.0]);
566		test_bound_cmp!(f32, [0.0..] < [..~]);
567
568		test_bound_cmp!(f32, [~..] < [=0.0..]);
569		test_bound_cmp!(f32, [~..] < [..=0.0]);
570		test_bound_cmp!(f32, [~..] < [..0.0]);
571		test_bound_cmp!(f32, [~..] < [..~]);
572
573		test_bound_cmp!(f32, [..=0.0] < [=1.0..]);
574		test_bound_cmp!(f32, [..=0.0] < [0.0..]);
575		test_bound_cmp!(f32, [..=0.0] < [..=1.0]);
576		test_bound_cmp!(f32, [..=0.0] < [..2.0]);
577		test_bound_cmp!(f32, [..=0.0] < [..~]);
578
579		test_bound_cmp!(f32, [..1.0] < [=1.0..]);
580		test_bound_cmp!(f32, [..1.0] < [1.0..]);
581		test_bound_cmp!(f32, [..1.0] < [..=1.0]);
582		test_bound_cmp!(f32, [..1.0] < [..2.0]);
583		test_bound_cmp!(f32, [..0.0] < [..~]);
584	}
585
586	#[test]
587	fn float_bound_partial_eq() {
588		test_bound_cmp!(f32, [=0.0..] == [=0.0..]);
589		test_bound_cmp!(f32, [=1.0..] > [0.0..]); // different from the int behavior
590		test_bound_cmp!(f32, [=0.0..] == [..=0.0]);
591		test_bound_cmp!(f32, [=0.0..] < [..1.0]); // different from the int behavior
592
593		test_bound_cmp!(f32, [0.0..] < [=1.0..]); // different from the int behavior
594		test_bound_cmp!(f32, [0.0..] == [0.0..]);
595		test_bound_cmp!(f32, [0.0..] < [..=1.0]); // different from the int behavior
596		test_bound_cmp!(f32, [0.0..] < [..2.0]); // different from the int behavior
597
598		test_bound_cmp!(f32, [~..] == [~..]);
599
600		test_bound_cmp!(f32, [..=0.0] == [=0.0..]);
601		test_bound_cmp!(f32, [..=1.0] > [0.0..]); // different from the int behavior
602		test_bound_cmp!(f32, [..=0.0] == [..=0.0]);
603		test_bound_cmp!(f32, [..=0.0] < [..1.0]); // different from the int behavior
604
605		test_bound_cmp!(f32, [..1.0] > [=0.0..]); // different from the int behavior
606		test_bound_cmp!(f32, [..2.0] > [0.0..]); // different from the int behavior
607		test_bound_cmp!(f32, [..1.0] > [..=0.0]); // different from the int behavior
608		test_bound_cmp!(f32, [..0.0] == [..0.0]);
609
610		test_bound_cmp!(f32, [..~] == [..~]);
611	}
612
613	#[test]
614	fn float_bound_partial_greater() {
615		test_bound_cmp!(f32, [=1.0..] > [=0.0..]);
616		test_bound_cmp!(f32, [0.0..] > [=0.0..]);
617		test_bound_cmp!(f32, [..=1.0] > [=0.0..]);
618		test_bound_cmp!(f32, [..2.0] > [=0.0..]);
619		test_bound_cmp!(f32, [..~] > [=0.0..]);
620
621		test_bound_cmp!(f32, [=2.0..] > [0.0..]);
622		test_bound_cmp!(f32, [1.0..] > [0.0..]);
623		test_bound_cmp!(f32, [..1.0] > [0.0..]); // different from the int behavior
624		test_bound_cmp!(f32, [..=2.0] > [0.0..]);
625		test_bound_cmp!(f32, [..3.0] > [0.0..]);
626		test_bound_cmp!(f32, [..~] > [0.0..]);
627
628		test_bound_cmp!(f32, [=0.0..] > [~..]);
629		test_bound_cmp!(f32, [..=0.0] > [~..]);
630		test_bound_cmp!(f32, [..0.0] > [~..]);
631		test_bound_cmp!(f32, [..~] > [~..]);
632
633		test_bound_cmp!(f32, [=1.0..] > [..=0.0]);
634		test_bound_cmp!(f32, [0.0..] > [..=0.0]);
635		test_bound_cmp!(f32, [..=1.0] > [..=0.0]);
636		test_bound_cmp!(f32, [..2.0] > [..=0.0]);
637		test_bound_cmp!(f32, [..~] > [..=0.0]);
638
639		test_bound_cmp!(f32, [=1.0..] > [..1.0]);
640		test_bound_cmp!(f32, [1.0..] > [..1.0]);
641		test_bound_cmp!(f32, [..=1.0] > [..1.0]);
642		test_bound_cmp!(f32, [..2.0] > [..1.0]);
643		test_bound_cmp!(f32, [..~] > [..0.0]);
644	}
645
646	#[test]
647	fn int_intersection() {
648		assert!((0..10).intersects(&(5..100)));
649	}
650
651	// Intersecting ranges are connected.
652	#[test]
653	fn int_connected_intersection() {
654		assert!((0..10).connected_to(&(5..100)));
655	}
656
657	#[test]
658	fn int_connected() {
659		assert!((0..10).connected_to(&(10..20)));
660		assert!((10..20).connected_to(&(0..10)));
661		assert!((0..=10).connected_to(&(RangeFromExcludedTo::new(10, 20))));
662	}
663
664	#[test]
665	fn int_disconnected() {
666		assert!(!(0..10).connected_to(&(11..20)));
667		assert!(!(11..20).connected_to(&(0..10)));
668		assert!(!(0..10).connected_to(&(RangeFromExcludedTo::new(10, 20))));
669	}
670
671	#[test]
672	fn float_connected() {
673		assert!((0.0..10.0).connected_to(&(10.0..20.0)));
674		assert!((0.0..=10.0).connected_to(&(RangeFromExcludedTo::new(10.0, 20.0))));
675	}
676
677	#[test]
678	fn float_disconnected() {
679		assert!(!(0.0..10.0).connected_to(&(RangeFromExcludedTo::new(10.0, 20.0))));
680		assert!(!(..10.0).connected_to(&(RangeFromExcludedTo::new(10.0, 20.0))));
681		assert!(!(0.0..10.0).connected_to(&(RangeFromExcluded::new(10.0))));
682		assert!(!(..10.0).connected_to(&(RangeFromExcluded::new(10.0))));
683	}
684}