version_ranges/
lib.rs

1// SPDX-License-Identifier: MPL-2.0
2
3//! This crate contains a performance-optimized type for generic version ranges and operations on
4//! them.
5//!
6//! [`Ranges`] can represent version selectors such as `(>=1, <2) OR (==3) OR (>4)`. Internally,
7//! it is an ordered list of contiguous intervals (segments) with inclusive, exclusive or open-ended
8//! ends, similar to a `Vec<(Bound<T>, Bound<T>)>`.
9//!
10//! You can construct a basic range from one of the following build blocks. All other ranges are
11//! concatenation, union, and complement of these basic ranges.
12//!  - [empty()](Ranges::empty): No version
13//!  - [full()](Ranges::full): All versions
14//!  - [singleton(v)](Ranges::singleton): Only the version v exactly
15//!  - [higher_than(v)](Ranges::higher_than): All versions `v <= versions`
16//!  - [strictly_higher_than(v)](Ranges::strictly_higher_than): All versions `v < versions`
17//!  - [lower_than(v)](Ranges::lower_than): All versions `versions <= v`
18//!  - [strictly_lower_than(v)](Ranges::strictly_lower_than): All versions `versions < v`
19//!  - [between(v1, v2)](Ranges::between): All versions `v1 <= versions < v2`
20//!
21//! [`Ranges`] is generic over any type that implements [`Ord`] + [`Clone`] and can represent all
22//! kinds of slices with ordered coordinates, not just version ranges. While built as a
23//! performance-critical piece of [pubgrub](https://github.com/pubgrub-rs/pubgrub), it can be
24//! adopted for other domains, too.
25//!
26//! Note that there are limitations to the equality implementation: Given a `Ranges<u32>`,
27//! the segments `(Unbounded, Included(42u32))` and `(Included(0), Included(42u32))` as well as
28//! `(Included(1), Included(5))` and  `(Included(1), Included(3)) + (Included(4), Included(5))`
29//! are reported as unequal, even though the match the same versions: We can't tell that there isn't
30//! a version between `0` and `-inf` or `3` and `4` respectively.
31//!
32//! ## Optional features
33//!
34//! * `serde`: serialization and deserialization for the version range, given that the version type
35//!   also supports it.
36//! * `proptest`: Exports are proptest strategy for [`Ranges<u32>`].
37
38use std::borrow::Borrow;
39use std::cmp::Ordering;
40use std::fmt::{Debug, Display, Formatter};
41use std::ops::Bound::{self, Excluded, Included, Unbounded};
42use std::ops::RangeBounds;
43
44#[cfg(any(feature = "proptest", test))]
45use proptest::prelude::*;
46use smallvec::{smallvec, SmallVec};
47
48/// Ranges represents multiple intervals of a continuous range of monotone increasing values.
49///
50/// Internally, [`Ranges`] are an ordered list of segments, where segment is a bounds pair.
51///
52/// Invariants:
53/// 1. The segments are sorted, from lowest to highest (through `Ord`).
54/// 2. Each segment contains at least one version (start < end).
55/// 3. There is at least one version between two segments.
56///
57/// These ensure that equivalent instances have an identical representation, which is important
58/// for `Eq` and `Hash`. Note that this representation cannot strictly guaranty equality of
59/// [`Ranges`] with equality of its representation without also knowing the nature of the underlying
60/// versions. In particular, if the version space is discrete, different representations, using
61/// different types of bounds (exclusive/inclusive) may correspond to the same set of existing
62/// versions. It is a tradeoff we acknowledge, but which makes representations of continuous version
63/// sets more accessible, to better handle features like pre-releases and other types of version
64/// modifiers. For example, `[(Included(3u32), Excluded(7u32))]` and
65/// `[(Included(3u32), Included(6u32))]` refer to the same version set, since there is no version
66/// between 6 and 7, which this crate doesn't know about.
67
68#[derive(Debug, Clone, Eq, PartialEq, Hash)]
69#[cfg_attr(feature = "serde", derive(serde::Serialize))]
70#[cfg_attr(feature = "serde", serde(transparent))]
71pub struct Ranges<V> {
72    /// Profiling in <https://github.com/pubgrub-rs/pubgrub/pull/262#discussion_r1804276278> showed
73    /// that a single stack entry is the most efficient. This is most likely due to `Interval<V>`
74    /// being large.
75    segments: SmallVec<[Interval<V>; 1]>,
76}
77
78// TODO: Replace the tuple type with a custom enum inlining the bounds to reduce the type's size.
79type Interval<V> = (Bound<V>, Bound<V>);
80
81impl<V> Ranges<V> {
82    /// Empty set of versions.
83    pub fn empty() -> Self {
84        Self {
85            segments: SmallVec::new(),
86        }
87    }
88
89    /// Set of all possible versions
90    pub fn full() -> Self {
91        Self {
92            segments: smallvec![(Unbounded, Unbounded)],
93        }
94    }
95
96    /// Set of all versions higher or equal to some version
97    pub fn higher_than(v: impl Into<V>) -> Self {
98        Self {
99            segments: smallvec![(Included(v.into()), Unbounded)],
100        }
101    }
102
103    /// Set of all versions higher to some version
104    pub fn strictly_higher_than(v: impl Into<V>) -> Self {
105        Self {
106            segments: smallvec![(Excluded(v.into()), Unbounded)],
107        }
108    }
109
110    /// Set of all versions lower to some version
111    pub fn strictly_lower_than(v: impl Into<V>) -> Self {
112        Self {
113            segments: smallvec![(Unbounded, Excluded(v.into()))],
114        }
115    }
116
117    /// Set of all versions lower or equal to some version
118    pub fn lower_than(v: impl Into<V>) -> Self {
119        Self {
120            segments: smallvec![(Unbounded, Included(v.into()))],
121        }
122    }
123
124    /// Set of versions greater or equal to `v1` but less than `v2`.
125    pub fn between(v1: impl Into<V>, v2: impl Into<V>) -> Self {
126        Self {
127            segments: smallvec![(Included(v1.into()), Excluded(v2.into()))],
128        }
129    }
130
131    /// Whether the set is empty, i.e. it has not ranges
132    pub fn is_empty(&self) -> bool {
133        self.segments.is_empty()
134    }
135}
136
137impl<V: Clone> Ranges<V> {
138    /// Set containing exactly one version
139    pub fn singleton(v: impl Into<V>) -> Self {
140        let v = v.into();
141        Self {
142            segments: smallvec![(Included(v.clone()), Included(v))],
143        }
144    }
145
146    /// Returns the complement, which contains everything not included in `self`.
147    pub fn complement(&self) -> Self {
148        match self.segments.first() {
149            // Complement of ∅ is ∞
150            None => Self::full(),
151
152            // Complement of ∞ is ∅
153            Some((Unbounded, Unbounded)) => Self::empty(),
154
155            // First high bound is +∞
156            Some((Included(v), Unbounded)) => Self::strictly_lower_than(v.clone()),
157            Some((Excluded(v), Unbounded)) => Self::lower_than(v.clone()),
158
159            Some((Unbounded, Included(v))) => {
160                Self::negate_segments(Excluded(v.clone()), &self.segments[1..])
161            }
162            Some((Unbounded, Excluded(v))) => {
163                Self::negate_segments(Included(v.clone()), &self.segments[1..])
164            }
165            Some((Included(_), Included(_)))
166            | Some((Included(_), Excluded(_)))
167            | Some((Excluded(_), Included(_)))
168            | Some((Excluded(_), Excluded(_))) => Self::negate_segments(Unbounded, &self.segments),
169        }
170    }
171
172    /// Helper function performing the negation of intervals in segments.
173    fn negate_segments(start: Bound<V>, segments: &[Interval<V>]) -> Self {
174        let mut complement_segments = SmallVec::new();
175        let mut start = start;
176        for (v1, v2) in segments {
177            complement_segments.push((
178                start,
179                match v1 {
180                    Included(v) => Excluded(v.clone()),
181                    Excluded(v) => Included(v.clone()),
182                    Unbounded => unreachable!(),
183                },
184            ));
185            start = match v2 {
186                Included(v) => Excluded(v.clone()),
187                Excluded(v) => Included(v.clone()),
188                Unbounded => Unbounded,
189            }
190        }
191        if !matches!(start, Unbounded) {
192            complement_segments.push((start, Unbounded));
193        }
194
195        Self {
196            segments: complement_segments,
197        }
198    }
199}
200
201impl<V: Ord> Ranges<V> {
202    /// If self contains exactly a single version, return it, otherwise, return [None].
203    pub fn as_singleton(&self) -> Option<&V> {
204        match self.segments.as_slice() {
205            [(Included(v1), Included(v2))] => {
206                if v1 == v2 {
207                    Some(v1)
208                } else {
209                    None
210                }
211            }
212            _ => None,
213        }
214    }
215
216    /// Convert to something that can be used with
217    /// [BTreeMap::range](std::collections::BTreeMap::range).
218    /// All versions contained in self, will be in the output,
219    /// but there may be versions in the output that are not contained in self.
220    /// Returns None if the range is empty.
221    pub fn bounding_range(&self) -> Option<(Bound<&V>, Bound<&V>)> {
222        self.segments.first().map(|(start, _)| {
223            let end = self
224                .segments
225                .last()
226                .expect("if there is a first element, there must be a last element");
227            (start.as_ref(), end.1.as_ref())
228        })
229    }
230
231    /// Returns true if self contains the specified value.
232    pub fn contains<Q>(&self, version: &Q) -> bool
233    where
234        V: Borrow<Q>,
235        Q: ?Sized + PartialOrd,
236    {
237        self.segments
238            .binary_search_by(|segment| {
239                // We have to reverse because we need the segment wrt to the version, while
240                // within bounds tells us the version wrt to the segment.
241                within_bounds(version, segment).reverse()
242            })
243            // An equal interval is one that contains the version
244            .is_ok()
245    }
246
247    /// Returns true if self contains the specified values.
248    ///
249    /// The `versions` iterator must be sorted.
250    /// Functionally equivalent to `versions.map(|v| self.contains(v))`.
251    /// Except it runs in `O(size_of_range + len_of_versions)` not `O(size_of_range * len_of_versions)`
252    pub fn contains_many<'s, I, BV>(&'s self, versions: I) -> impl Iterator<Item = bool> + 's
253    where
254        I: Iterator<Item = BV> + 's,
255        BV: Borrow<V> + 's,
256    {
257        #[cfg(debug_assertions)]
258        let mut last: Option<BV> = None;
259        versions.scan(0, move |i, v| {
260            #[cfg(debug_assertions)]
261            {
262                if let Some(l) = last.as_ref() {
263                    assert!(
264                        l.borrow() <= v.borrow(),
265                        "`contains_many` `versions` argument incorrectly sorted"
266                    );
267                }
268            }
269            while let Some(segment) = self.segments.get(*i) {
270                match within_bounds(v.borrow(), segment) {
271                    Ordering::Less => return Some(false),
272                    Ordering::Equal => return Some(true),
273                    Ordering::Greater => *i += 1,
274                }
275            }
276            #[cfg(debug_assertions)]
277            {
278                last = Some(v);
279            }
280            Some(false)
281        })
282    }
283
284    /// Construct a simple range from anything that impls [RangeBounds] like `v1..v2`.
285    pub fn from_range_bounds<R, IV>(bounds: R) -> Self
286    where
287        R: RangeBounds<IV>,
288        IV: Clone + Into<V>,
289    {
290        let start = match bounds.start_bound() {
291            Included(v) => Included(v.clone().into()),
292            Excluded(v) => Excluded(v.clone().into()),
293            Unbounded => Unbounded,
294        };
295        let end = match bounds.end_bound() {
296            Included(v) => Included(v.clone().into()),
297            Excluded(v) => Excluded(v.clone().into()),
298            Unbounded => Unbounded,
299        };
300        if valid_segment(&start, &end) {
301            Self {
302                segments: smallvec![(start, end)],
303            }
304        } else {
305            Self::empty()
306        }
307    }
308
309    /// See [`Ranges`] for the invariants checked.
310    fn check_invariants(self) -> Self {
311        if cfg!(debug_assertions) {
312            for p in self.segments.as_slice().windows(2) {
313                assert!(end_before_start_with_gap(&p[0].1, &p[1].0));
314            }
315            for (s, e) in self.segments.iter() {
316                assert!(valid_segment(s, e));
317            }
318        }
319        self
320    }
321}
322
323/// Implementing `PartialOrd` for start `Bound` of an interval.
324///
325/// Legend: `∞` is unbounded, `[1,2]` is `>=1,<=2`, `]1,2[` is `>1,<2`.
326///
327/// ```text
328/// left:   ∞-------]
329/// right:    [-----]
330/// left is smaller, since it starts earlier.
331///
332/// left:   [-----]
333/// right:  ]-----]
334/// left is smaller, since it starts earlier.
335/// ```
336fn cmp_bounds_start<V: PartialOrd>(left: Bound<&V>, right: Bound<&V>) -> Option<Ordering> {
337    Some(match (left, right) {
338        // left:   ∞-----
339        // right:  ∞-----
340        (Unbounded, Unbounded) => Ordering::Equal,
341        // left:     [---
342        // right:  ∞-----
343        (Included(_left), Unbounded) => Ordering::Greater,
344        // left:     ]---
345        // right:  ∞-----
346        (Excluded(_left), Unbounded) => Ordering::Greater,
347        // left:   ∞-----
348        // right:    [---
349        (Unbounded, Included(_right)) => Ordering::Less,
350        // left:   [----- OR [----- OR   [-----
351        // right:    [--- OR [----- OR [---
352        (Included(left), Included(right)) => left.partial_cmp(right)?,
353        (Excluded(left), Included(right)) => match left.partial_cmp(right)? {
354            // left:   ]-----
355            // right:    [---
356            Ordering::Less => Ordering::Less,
357            // left:   ]-----
358            // right:  [---
359            Ordering::Equal => Ordering::Greater,
360            // left:     ]---
361            // right:  [-----
362            Ordering::Greater => Ordering::Greater,
363        },
364        // left:   ∞-----
365        // right:    ]---
366        (Unbounded, Excluded(_right)) => Ordering::Less,
367        (Included(left), Excluded(right)) => match left.partial_cmp(right)? {
368            // left:   [-----
369            // right:    ]---
370            Ordering::Less => Ordering::Less,
371            // left:   [-----
372            // right:  ]---
373            Ordering::Equal => Ordering::Less,
374            // left:     [---
375            // right:  ]-----
376            Ordering::Greater => Ordering::Greater,
377        },
378        // left:   ]----- OR ]----- OR   ]---
379        // right:    ]--- OR ]----- OR ]-----
380        (Excluded(left), Excluded(right)) => left.partial_cmp(right)?,
381    })
382}
383
384/// Implementing `PartialOrd` for end `Bound` of an interval.
385///
386/// We flip the unbounded ranges from `-∞` to `∞`, while `V`-valued bounds checks remain the same.
387///
388/// Legend: `∞` is unbounded, `[1,2]` is `>=1,<=2`, `]1,2[` is `>1,<2`.
389///
390/// ```text
391/// left:   [--------∞
392/// right:  [-----]
393/// left is greater, since it starts earlier.
394///
395/// left:   [-----[
396/// right:  [-----]
397/// left is smaller, since it ends earlier.
398/// ```
399fn cmp_bounds_end<V: PartialOrd>(left: Bound<&V>, right: Bound<&V>) -> Option<Ordering> {
400    Some(match (left, right) {
401        // left:   -----∞
402        // right:  -----∞
403        (Unbounded, Unbounded) => Ordering::Equal,
404        // left:   ---]
405        // right:  -----∞
406        (Included(_left), Unbounded) => Ordering::Less,
407        // left:   ---[
408        // right:  -----∞
409        (Excluded(_left), Unbounded) => Ordering::Less,
410        // left:  -----∞
411        // right: ---]
412        (Unbounded, Included(_right)) => Ordering::Greater,
413        // left:   -----] OR -----] OR ---]
414        // right:    ---] OR -----] OR -----]
415        (Included(left), Included(right)) => left.partial_cmp(right)?,
416        (Excluded(left), Included(right)) => match left.partial_cmp(right)? {
417            // left:   ---[
418            // right:  -----]
419            Ordering::Less => Ordering::Less,
420            // left:   -----[
421            // right:  -----]
422            Ordering::Equal => Ordering::Less,
423            // left:   -----[
424            // right:  ---]
425            Ordering::Greater => Ordering::Greater,
426        },
427        (Unbounded, Excluded(_right)) => Ordering::Greater,
428        (Included(left), Excluded(right)) => match left.partial_cmp(right)? {
429            // left:   ---]
430            // right:  -----[
431            Ordering::Less => Ordering::Less,
432            // left:   -----]
433            // right:  -----[
434            Ordering::Equal => Ordering::Greater,
435            // left:   -----]
436            // right:  ---[
437            Ordering::Greater => Ordering::Greater,
438        },
439        // left:   -----[ OR -----[ OR ---[
440        // right:  ---[   OR -----[ OR -----[
441        (Excluded(left), Excluded(right)) => left.partial_cmp(right)?,
442    })
443}
444
445impl<V: PartialOrd> PartialOrd for Ranges<V> {
446    /// A simple ordering scheme where we zip the segments and compare all bounds in order. If all
447    /// bounds are equal, the longer range is considered greater. (And if all zipped bounds are
448    /// equal and we have the same number of segments, the ranges are equal).
449    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
450        for (left, right) in self.segments.iter().zip(other.segments.iter()) {
451            let start_cmp = cmp_bounds_start(left.start_bound(), right.start_bound())?;
452            if start_cmp != Ordering::Equal {
453                return Some(start_cmp);
454            }
455            let end_cmp = cmp_bounds_end(left.end_bound(), right.end_bound())?;
456            if end_cmp != Ordering::Equal {
457                return Some(end_cmp);
458            }
459        }
460        Some(self.segments.len().cmp(&other.segments.len()))
461    }
462}
463
464impl<V: Ord> Ord for Ranges<V> {
465    fn cmp(&self, other: &Self) -> Ordering {
466        self.partial_cmp(other)
467            .expect("PartialOrd must be `Some(Ordering)` for types that implement `Ord`")
468    }
469}
470
471/// The ordering of the version wrt to the interval.
472/// ```text
473///      |-------|
474///   ^      ^      ^
475///   less   equal  greater
476/// ```
477fn within_bounds<Q, V>(version: &Q, segment: &Interval<V>) -> Ordering
478where
479    V: Borrow<Q>,
480    Q: ?Sized + PartialOrd,
481{
482    let below_lower_bound = match segment {
483        (Excluded(start), _) => version <= start.borrow(),
484        (Included(start), _) => version < start.borrow(),
485        (Unbounded, _) => false,
486    };
487    if below_lower_bound {
488        return Ordering::Less;
489    }
490    let below_upper_bound = match segment {
491        (_, Unbounded) => true,
492        (_, Included(end)) => version <= end.borrow(),
493        (_, Excluded(end)) => version < end.borrow(),
494    };
495    if below_upper_bound {
496        return Ordering::Equal;
497    }
498    Ordering::Greater
499}
500
501/// A valid segment is one where at least one version fits between start and end
502fn valid_segment<T: PartialOrd>(start: &Bound<T>, end: &Bound<T>) -> bool {
503    match (start, end) {
504        // Singleton interval are allowed
505        (Included(s), Included(e)) => s <= e,
506        (Included(s), Excluded(e)) => s < e,
507        (Excluded(s), Included(e)) => s < e,
508        (Excluded(s), Excluded(e)) => s < e,
509        (Unbounded, _) | (_, Unbounded) => true,
510    }
511}
512
513/// The end of one interval is before the start of the next one, so they can't be concatenated
514/// into a single interval. The `union` method calling with both intervals and then the intervals
515/// switched. If either is true, the intervals are separate in the union and if both are false, they
516/// are merged.
517/// ```text
518/// True for these two:
519///  |----|
520///                |-----|
521///       ^ end    ^ start
522/// False for these two:
523///  |----|
524///     |-----|
525/// Here it depends: If they both exclude the position they share, there is a version in between
526/// them that blocks concatenation
527///  |----|
528///       |-----|
529/// ```
530fn end_before_start_with_gap<V: PartialOrd>(end: &Bound<V>, start: &Bound<V>) -> bool {
531    match (end, start) {
532        (_, Unbounded) => false,
533        (Unbounded, _) => false,
534        (Included(left), Included(right)) => left < right,
535        (Included(left), Excluded(right)) => left < right,
536        (Excluded(left), Included(right)) => left < right,
537        (Excluded(left), Excluded(right)) => left <= right,
538    }
539}
540
541fn left_start_is_smaller<V: PartialOrd>(left: Bound<V>, right: Bound<V>) -> bool {
542    match (left, right) {
543        (Unbounded, _) => true,
544        (_, Unbounded) => false,
545        (Included(l), Included(r)) => l <= r,
546        (Excluded(l), Excluded(r)) => l <= r,
547        (Included(l), Excluded(r)) => l <= r,
548        (Excluded(l), Included(r)) => l < r,
549    }
550}
551
552fn left_end_is_smaller<V: PartialOrd>(left: Bound<V>, right: Bound<V>) -> bool {
553    match (left, right) {
554        (_, Unbounded) => true,
555        (Unbounded, _) => false,
556        (Included(l), Included(r)) => l <= r,
557        (Excluded(l), Excluded(r)) => l <= r,
558        (Excluded(l), Included(r)) => l <= r,
559        (Included(l), Excluded(r)) => l < r,
560    }
561}
562
563/// Group adjacent versions locations.
564///
565/// ```text
566/// [None, 3, 6, 7, None] -> [(3, 7)]
567/// [3, 6, 7, None] -> [(None, 7)]
568/// [3, 6, 7] -> [(None, None)]
569/// [None, 1, 4, 7, None, None, None, 8, None, 9] -> [(1, 7), (8, 8), (9, None)]
570/// ```
571fn group_adjacent_locations(
572    mut locations: impl Iterator<Item = Option<usize>>,
573) -> impl Iterator<Item = (Option<usize>, Option<usize>)> {
574    // If the first version matched, then the lower bound of that segment is not needed
575    let mut seg = locations.next().flatten().map(|ver| (None, Some(ver)));
576    std::iter::from_fn(move || {
577        for ver in locations.by_ref() {
578            if let Some(ver) = ver {
579                // As long as were still matching versions, we keep merging into the currently matching segment
580                seg = Some((seg.map_or(Some(ver), |(s, _)| s), Some(ver)));
581            } else {
582                // If we have found a version that doesn't match, then right the merge segment and prepare for a new one.
583                if seg.is_some() {
584                    return seg.take();
585                }
586            }
587        }
588        // If the last version matched, then write out the merged segment but the upper bound is not needed.
589        seg.take().map(|(s, _)| (s, None))
590    })
591}
592
593impl<V: Ord + Clone> Ranges<V> {
594    /// Computes the union of this `Ranges` and another.
595    pub fn union(&self, other: &Self) -> Self {
596        let mut output = SmallVec::new();
597        let mut accumulator: Option<(&Bound<_>, &Bound<_>)> = None;
598        let mut left_iter = self.segments.iter().peekable();
599        let mut right_iter = other.segments.iter().peekable();
600        loop {
601            let smaller_interval = match (left_iter.peek(), right_iter.peek()) {
602                (Some((left_start, left_end)), Some((right_start, right_end))) => {
603                    if left_start_is_smaller(left_start.as_ref(), right_start.as_ref()) {
604                        left_iter.next();
605                        (left_start, left_end)
606                    } else {
607                        right_iter.next();
608                        (right_start, right_end)
609                    }
610                }
611                (Some((left_start, left_end)), None) => {
612                    left_iter.next();
613                    (left_start, left_end)
614                }
615                (None, Some((right_start, right_end))) => {
616                    right_iter.next();
617                    (right_start, right_end)
618                }
619                (None, None) => break,
620            };
621
622            if let Some(accumulator_) = accumulator {
623                if end_before_start_with_gap(accumulator_.1, smaller_interval.0) {
624                    output.push((accumulator_.0.clone(), accumulator_.1.clone()));
625                    accumulator = Some(smaller_interval);
626                } else {
627                    let accumulator_end = match (accumulator_.1, smaller_interval.1) {
628                        (_, Unbounded) | (Unbounded, _) => &Unbounded,
629                        (Included(l), Excluded(r) | Included(r)) if l == r => accumulator_.1,
630                        (Included(l) | Excluded(l), Included(r) | Excluded(r)) => {
631                            if l > r {
632                                accumulator_.1
633                            } else {
634                                smaller_interval.1
635                            }
636                        }
637                    };
638                    accumulator = Some((accumulator_.0, accumulator_end));
639                }
640            } else {
641                accumulator = Some(smaller_interval)
642            }
643        }
644
645        if let Some(accumulator) = accumulator {
646            output.push((accumulator.0.clone(), accumulator.1.clone()));
647        }
648
649        Self { segments: output }.check_invariants()
650    }
651
652    /// Computes the intersection of two sets of versions.
653    pub fn intersection(&self, other: &Self) -> Self {
654        let mut output = SmallVec::new();
655        let mut left_iter = self.segments.iter().peekable();
656        let mut right_iter = other.segments.iter().peekable();
657        // By the definition of intersection any point that is matched by the output
658        // must have a segment in each of the inputs that it matches.
659        // Therefore, every segment in the output must be the intersection of a segment from each of the inputs.
660        // It would be correct to do the "O(n^2)" thing, by computing the intersection of every segment from one input
661        // with every segment of the other input, and sorting the result.
662        // We can avoid the sorting by generating our candidate segments with an increasing `end` value.
663        while let Some(((left_start, left_end), (right_start, right_end))) =
664            left_iter.peek().zip(right_iter.peek())
665        {
666            // The next smallest `end` value is going to come from one of the inputs.
667            let left_end_is_smaller = left_end_is_smaller(left_end.as_ref(), right_end.as_ref());
668            // Now that we are processing `end` we will never have to process any segment smaller than that.
669            // We can ensure that the input that `end` came from is larger than `end` by advancing it one step.
670            // `end` is the smaller available input, so we know the other input is already larger than `end`.
671            // Note: We can call `other_iter.next_if( == end)`, but the ends lining up is rare enough that
672            // it does not end up being faster in practice.
673            let (other_start, end) = if left_end_is_smaller {
674                left_iter.next();
675                (right_start, left_end)
676            } else {
677                right_iter.next();
678                (left_start, right_end)
679            };
680            // `start` will either come from the input `end` came from or the other input, whichever one is larger.
681            // The intersection is invalid if `start` > `end`.
682            // But, we already know that the segments in our input are valid.
683            // So we do not need to check if the `start` from the input `end` came from is smaller than `end`.
684            // If the `other_start` is larger than end, then the intersection will be invalid.
685            if !valid_segment(other_start, end) {
686                // Note: We can call `this_iter.next_if(!valid_segment(other_start, this_end))` in a loop.
687                // But the checks make it slower for the benchmarked inputs.
688                continue;
689            }
690            let start = match (left_start, right_start) {
691                (Included(l), Included(r)) => Included(std::cmp::max(l, r)),
692                (Excluded(l), Excluded(r)) => Excluded(std::cmp::max(l, r)),
693
694                (Included(i), Excluded(e)) | (Excluded(e), Included(i)) => {
695                    if i <= e {
696                        Excluded(e)
697                    } else {
698                        Included(i)
699                    }
700                }
701                (s, Unbounded) | (Unbounded, s) => s.as_ref(),
702            };
703            // Now we clone and push a new segment.
704            // By dealing with references until now we ensure that NO cloning happens when we reject the segment.
705            output.push((start.cloned(), end.clone()))
706        }
707
708        Self { segments: output }.check_invariants()
709    }
710
711    /// Return true if there can be no `V` so that `V` is contained in both `self` and `other`.
712    ///
713    /// Note that we don't know that set of all existing `V`s here, so we only check if the segments
714    /// are disjoint, not if no version is contained in both.
715    pub fn is_disjoint(&self, other: &Self) -> bool {
716        // The operation is symmetric
717        let mut left_iter = self.segments.iter().peekable();
718        let mut right_iter = other.segments.iter().peekable();
719
720        while let Some((left, right)) = left_iter.peek().zip(right_iter.peek()) {
721            if !valid_segment(&right.start_bound(), &left.end_bound()) {
722                left_iter.next();
723            } else if !valid_segment(&left.start_bound(), &right.end_bound()) {
724                right_iter.next();
725            } else {
726                return false;
727            }
728        }
729
730        // The remaining element(s) can't intersect anymore
731        true
732    }
733
734    /// Return true if any `V` that is contained in `self` is also contained in `other`.
735    ///
736    /// Note that we don't know that set of all existing `V`s here, so we only check if all
737    /// segments `self` are contained in a segment of `other`.
738    pub fn subset_of(&self, other: &Self) -> bool {
739        let mut containing_iter = other.segments.iter();
740        let mut subset_iter = self.segments.iter();
741        let Some(mut containing_elem) = containing_iter.next() else {
742            // As long as we have subset elements, we need containing elements
743            return subset_iter.next().is_none();
744        };
745
746        for subset_elem in subset_iter {
747            // Check if the current containing element ends before the subset element.
748            // There needs to be another containing element for our subset element in this case.
749            while !valid_segment(&subset_elem.start_bound(), &containing_elem.end_bound()) {
750                if let Some(containing_elem_) = containing_iter.next() {
751                    containing_elem = containing_elem_;
752                } else {
753                    return false;
754                };
755            }
756
757            let start_contained =
758                left_start_is_smaller(containing_elem.start_bound(), subset_elem.start_bound());
759
760            if !start_contained {
761                // The start element is not contained
762                return false;
763            }
764
765            let end_contained =
766                left_end_is_smaller(subset_elem.end_bound(), containing_elem.end_bound());
767
768            if !end_contained {
769                // The end element is not contained
770                return false;
771            }
772        }
773
774        true
775    }
776
777    /// Returns a simpler representation that contains the same versions.
778    ///
779    /// For every one of the Versions provided in versions the existing range and the simplified range will agree on whether it is contained.
780    /// The simplified version may include or exclude versions that are not in versions as the implementation wishes.
781    ///
782    /// If none of the versions are contained in the original than the range will be returned unmodified.
783    /// If the range includes a single version, it will be returned unmodified.
784    /// If all the versions are contained in the original than the range will be simplified to `full`.
785    ///
786    /// If the given versions are not sorted the correctness of this function is not guaranteed.
787    pub fn simplify<'s, I, BV>(&self, versions: I) -> Self
788    where
789        I: Iterator<Item = BV> + 's,
790        BV: Borrow<V> + 's,
791    {
792        // Do not simplify singletons
793        if self.as_singleton().is_some() {
794            return self.clone();
795        }
796
797        #[cfg(debug_assertions)]
798        let mut last: Option<BV> = None;
799        // Return the segment index in the range for each version in the range, None otherwise
800        let version_locations = versions.scan(0, move |i, v| {
801            #[cfg(debug_assertions)]
802            {
803                if let Some(l) = last.as_ref() {
804                    assert!(
805                        l.borrow() <= v.borrow(),
806                        "`simplify` `versions` argument incorrectly sorted"
807                    );
808                }
809            }
810            while let Some(segment) = self.segments.get(*i) {
811                match within_bounds(v.borrow(), segment) {
812                    Ordering::Less => return Some(None),
813                    Ordering::Equal => return Some(Some(*i)),
814                    Ordering::Greater => *i += 1,
815                }
816            }
817            #[cfg(debug_assertions)]
818            {
819                last = Some(v);
820            }
821            Some(None)
822        });
823        let mut kept_segments = group_adjacent_locations(version_locations).peekable();
824
825        // Do not return null sets
826        if kept_segments.peek().is_none() {
827            return self.clone();
828        }
829
830        self.keep_segments(kept_segments)
831    }
832
833    /// Create a new range with a subset of segments at given location bounds.
834    ///
835    /// Each new segment is constructed from a pair of segments, taking the
836    /// start of the first and the end of the second.
837    fn keep_segments(
838        &self,
839        kept_segments: impl Iterator<Item = (Option<usize>, Option<usize>)>,
840    ) -> Ranges<V> {
841        let mut segments = SmallVec::new();
842        for (s, e) in kept_segments {
843            segments.push((
844                s.map_or(Unbounded, |s| self.segments[s].0.clone()),
845                e.map_or(Unbounded, |e| self.segments[e].1.clone()),
846            ));
847        }
848        Self { segments }.check_invariants()
849    }
850
851    /// Iterate over the parts of the range.
852    pub fn iter(&self) -> impl Iterator<Item = (&Bound<V>, &Bound<V>)> {
853        self.segments.iter().map(|(start, end)| (start, end))
854    }
855}
856
857// Newtype to avoid leaking our internal representation.
858pub struct RangesIter<V>(smallvec::IntoIter<[Interval<V>; 1]>);
859
860impl<V> Iterator for RangesIter<V> {
861    type Item = Interval<V>;
862
863    fn next(&mut self) -> Option<Self::Item> {
864        self.0.next()
865    }
866
867    fn size_hint(&self) -> (usize, Option<usize>) {
868        (self.0.len(), Some(self.0.len()))
869    }
870}
871
872impl<V> ExactSizeIterator for RangesIter<V> {}
873
874impl<V> DoubleEndedIterator for RangesIter<V> {
875    fn next_back(&mut self) -> Option<Self::Item> {
876        self.0.next_back()
877    }
878}
879
880impl<V> IntoIterator for Ranges<V> {
881    type Item = (Bound<V>, Bound<V>);
882    // Newtype to avoid leaking our internal representation.
883    type IntoIter = RangesIter<V>;
884
885    fn into_iter(self) -> Self::IntoIter {
886        RangesIter(self.segments.into_iter())
887    }
888}
889
890impl<V: Ord> FromIterator<(Bound<V>, Bound<V>)> for Ranges<V> {
891    /// Constructor from arbitrary, unsorted and potentially overlapping ranges.
892    ///
893    /// This is equivalent, but faster, to computing the [`Ranges::union`] of the
894    /// [`Ranges::from_range_bounds`] of each segment.
895    fn from_iter<T: IntoIterator<Item = (Bound<V>, Bound<V>)>>(iter: T) -> Self {
896        // We have three constraints we need to fulfil:
897        // 1. The segments are sorted, from lowest to highest (through `Ord`): By sorting.
898        // 2. Each segment contains at least one version (start < end): By skipping invalid
899        //    segments.
900        // 3. There is at least one version between two segments: By merging overlapping elements.
901        //
902        // Technically, the implementation has a O(n²) worst case complexity since we're inserting
903        // and removing. This has two motivations: One is that we don't have any performance
904        // critical usages of this method as of this writing, so we have no real world benchmark.
905        // The other is that we get the elements from an iterator, so to avoid moving elements
906        // around we would first need to build a different, sorted collection with extra
907        // allocation(s), before we could build our real segments. --Konsti
908
909        // For this implementation, we choose to only build a single smallvec and insert or remove
910        // in it, instead of e.g. collecting the segments into a sorted datastructure first and then
911        // construction the second smallvec without shifting.
912        let mut segments: SmallVec<[Interval<V>; 1]> = SmallVec::new();
913
914        for segment in iter {
915            if !valid_segment(&segment.start_bound(), &segment.end_bound()) {
916                continue;
917            }
918            // Find where to insert the new segment
919            let insertion_point = segments.partition_point(|elem: &Interval<V>| {
920                cmp_bounds_start(elem.start_bound(), segment.start_bound())
921                    .unwrap()
922                    .is_lt()
923            });
924            // Is it overlapping with the previous segment?
925            let previous_overlapping = insertion_point > 0
926                && !end_before_start_with_gap(
927                    &segments[insertion_point - 1].end_bound(),
928                    &segment.start_bound(),
929                );
930
931            // Is it overlapping with the following segment? We'll check if there's more than one
932            // overlap later.
933            let next_overlapping = insertion_point < segments.len()
934                && !end_before_start_with_gap(
935                    &segment.end_bound(),
936                    &segments[insertion_point].start_bound(),
937                );
938
939            match (previous_overlapping, next_overlapping) {
940                (true, true) => {
941                    // previous:  |------|
942                    // segment:       |------|
943                    // following:          |------|
944                    // final:     |---------------|
945                    //
946                    // OR
947                    //
948                    // previous:  |------|
949                    // segment:       |-----------|
950                    // following:          |----|
951                    // final:     |---------------|
952                    //
953                    // OR
954                    //
955                    // previous:  |------|
956                    // segment:       |----------------|
957                    // following:          |----|   |------|
958                    // final:     |------------------------|
959                    // We merge all three segments into one, which is effectively removing one of
960                    // two previously inserted and changing the bounds on the other.
961
962                    // Remove all elements covered by the final element
963                    let mut following = segments.remove(insertion_point);
964                    while insertion_point < segments.len()
965                        && !end_before_start_with_gap(
966                            &segment.end_bound(),
967                            &segments[insertion_point].start_bound(),
968                        )
969                    {
970                        following = segments.remove(insertion_point);
971                    }
972
973                    // Set end to max(segment.end, <last overlapping segment>.end)
974                    if cmp_bounds_end(segment.end_bound(), following.end_bound())
975                        .unwrap()
976                        .is_lt()
977                    {
978                        segments[insertion_point - 1].1 = following.1;
979                    } else {
980                        segments[insertion_point - 1].1 = segment.1;
981                    }
982                }
983                (true, false) => {
984                    // previous:  |------|
985                    // segment:       |------|
986                    // following:                |------|
987                    //
988                    // OR
989                    //
990                    // previous:  |----------|
991                    // segment:       |---|
992                    // following:                |------|
993                    //
994                    // final:     |----------|   |------|
995                    // We can reuse the existing element by extending it.
996
997                    // Set end to max(segment.end, <previous>.end)
998                    if cmp_bounds_end(
999                        segments[insertion_point - 1].end_bound(),
1000                        segment.end_bound(),
1001                    )
1002                    .unwrap()
1003                    .is_lt()
1004                    {
1005                        segments[insertion_point - 1].1 = segment.1;
1006                    }
1007                }
1008                (false, true) => {
1009                    // previous:  |------|
1010                    // segment:             |------|
1011                    // following:               |------|
1012                    // final:    |------|   |----------|
1013                    //
1014                    // OR
1015                    //
1016                    // previous:  |------|
1017                    // segment:             |----------|
1018                    // following:               |---|
1019                    // final:    |------|   |----------|
1020                    //
1021                    // OR
1022                    //
1023                    // previous:  |------|
1024                    // segment:             |------------|
1025                    // following:               |---|  |------|
1026                    //
1027                    // final:    |------|   |-----------------|
1028                    // We can reuse the existing element by extending it.
1029
1030                    // Remove all fully covered segments so the next element is the last one that
1031                    // overlaps.
1032                    while insertion_point + 1 < segments.len()
1033                        && !end_before_start_with_gap(
1034                            &segment.end_bound(),
1035                            &segments[insertion_point + 1].start_bound(),
1036                        )
1037                    {
1038                        // We know that the one after also overlaps, so we can drop the current
1039                        // following.
1040                        segments.remove(insertion_point);
1041                    }
1042
1043                    // Set end to max(segment.end, <last overlapping segment>.end)
1044                    if cmp_bounds_end(segments[insertion_point].end_bound(), segment.end_bound())
1045                        .unwrap()
1046                        .is_lt()
1047                    {
1048                        segments[insertion_point].1 = segment.1;
1049                    }
1050                    segments[insertion_point].0 = segment.0;
1051                }
1052                (false, false) => {
1053                    // previous:  |------|
1054                    // segment:             |------|
1055                    // following:                      |------|
1056                    //
1057                    // final:    |------|   |------|   |------|
1058
1059                    // This line is O(n), which makes the algorithm O(n²), but it should be good
1060                    // enough for now.
1061                    segments.insert(insertion_point, segment);
1062                }
1063            }
1064        }
1065
1066        Self { segments }.check_invariants()
1067    }
1068}
1069
1070// REPORT ######################################################################
1071
1072impl<V: Display + Eq> Display for Ranges<V> {
1073    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1074        if self.segments.is_empty() {
1075            write!(f, "∅")?;
1076        } else {
1077            for (idx, segment) in self.segments.iter().enumerate() {
1078                if idx > 0 {
1079                    write!(f, " | ")?;
1080                }
1081                match segment {
1082                    (Unbounded, Unbounded) => write!(f, "*")?,
1083                    (Unbounded, Included(v)) => write!(f, "<={v}")?,
1084                    (Unbounded, Excluded(v)) => write!(f, "<{v}")?,
1085                    (Included(v), Unbounded) => write!(f, ">={v}")?,
1086                    (Included(v), Included(b)) => {
1087                        if v == b {
1088                            write!(f, "{v}")?
1089                        } else {
1090                            write!(f, ">={v}, <={b}")?
1091                        }
1092                    }
1093                    (Included(v), Excluded(b)) => write!(f, ">={v}, <{b}")?,
1094                    (Excluded(v), Unbounded) => write!(f, ">{v}")?,
1095                    (Excluded(v), Included(b)) => write!(f, ">{v}, <={b}")?,
1096                    (Excluded(v), Excluded(b)) => write!(f, ">{v}, <{b}")?,
1097                };
1098            }
1099        }
1100        Ok(())
1101    }
1102}
1103
1104// SERIALIZATION ###############################################################
1105
1106#[cfg(feature = "serde")]
1107impl<'de, V: serde::Deserialize<'de>> serde::Deserialize<'de> for Ranges<V> {
1108    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
1109        // This enables conversion from the "old" discrete implementation of `Ranges` to the new
1110        // bounded one.
1111        //
1112        // Serialization is always performed in the new format.
1113        #[derive(serde::Deserialize)]
1114        #[serde(untagged)]
1115        enum EitherInterval<V> {
1116            B(Bound<V>, Bound<V>),
1117            D(V, Option<V>),
1118        }
1119
1120        let bounds: SmallVec<[EitherInterval<V>; 2]> =
1121            serde::Deserialize::deserialize(deserializer)?;
1122
1123        let mut segments = SmallVec::new();
1124        for i in bounds {
1125            match i {
1126                EitherInterval::B(l, r) => segments.push((l, r)),
1127                EitherInterval::D(l, Some(r)) => segments.push((Included(l), Excluded(r))),
1128                EitherInterval::D(l, None) => segments.push((Included(l), Unbounded)),
1129            }
1130        }
1131
1132        Ok(Ranges { segments })
1133    }
1134}
1135
1136/// Generate version sets from a random vector of deltas between randomly inclusive or exclusive
1137/// bounds.
1138#[cfg(any(feature = "proptest", test))]
1139pub fn proptest_strategy() -> impl Strategy<Value = Ranges<u32>> {
1140    (
1141        any::<bool>(),
1142        prop::collection::vec(any::<(u32, bool)>(), 0..10),
1143    )
1144        .prop_map(|(start_unbounded, deltas)| {
1145            let mut start = if start_unbounded {
1146                Some(Unbounded)
1147            } else {
1148                None
1149            };
1150            let mut largest: u32 = 0;
1151            let mut last_bound_was_inclusive = false;
1152            let mut segments = SmallVec::new();
1153            for (delta, inclusive) in deltas {
1154                // Add the offset to the current bound
1155                largest = match largest.checked_add(delta) {
1156                    Some(s) => s,
1157                    None => {
1158                        // Skip this offset, if it would result in a too large bound.
1159                        continue;
1160                    }
1161                };
1162
1163                let current_bound = if inclusive {
1164                    Included(largest)
1165                } else {
1166                    Excluded(largest)
1167                };
1168
1169                // If we already have a start bound, the next offset defines the complete range.
1170                // If we don't have a start bound, we have to generate one.
1171                if let Some(start_bound) = start.take() {
1172                    // If the delta from the start bound is 0, the only authorized configuration is
1173                    // Included(x), Included(x)
1174                    if delta == 0 && !(matches!(start_bound, Included(_)) && inclusive) {
1175                        start = Some(start_bound);
1176                        continue;
1177                    }
1178                    last_bound_was_inclusive = inclusive;
1179                    segments.push((start_bound, current_bound));
1180                } else {
1181                    // If the delta from the end bound of the last range is 0 and
1182                    // any of the last ending or current starting bound is inclusive,
1183                    // we skip the delta because they basically overlap.
1184                    if delta == 0 && (last_bound_was_inclusive || inclusive) {
1185                        continue;
1186                    }
1187                    start = Some(current_bound);
1188                }
1189            }
1190
1191            // If we still have a start bound, but didn't have enough deltas to complete another
1192            // segment, we add an unbounded upperbound.
1193            if let Some(start_bound) = start {
1194                segments.push((start_bound, Unbounded));
1195            }
1196
1197            Ranges { segments }.check_invariants()
1198        })
1199}
1200
1201#[cfg(test)]
1202pub mod tests {
1203    use proptest::prelude::*;
1204
1205    use super::*;
1206
1207    fn version_strat() -> impl Strategy<Value = u32> {
1208        any::<u32>()
1209    }
1210
1211    proptest! {
1212
1213        // Testing serde ----------------------------------
1214
1215        #[cfg(feature = "serde")]
1216        #[test]
1217        fn serde_round_trip(range in proptest_strategy()) {
1218            let s = ron::ser::to_string(&range).unwrap();
1219            let r = ron::de::from_str(&s).unwrap();
1220            assert_eq!(range, r);
1221        }
1222
1223        // Testing negate ----------------------------------
1224
1225        #[test]
1226        fn negate_is_different(range in proptest_strategy()) {
1227            assert_ne!(range.complement(), range);
1228        }
1229
1230        #[test]
1231        fn double_negate_is_identity(range in proptest_strategy()) {
1232            assert_eq!(range.complement().complement(), range);
1233        }
1234
1235        #[test]
1236        fn negate_contains_opposite(range in proptest_strategy(), version in version_strat()) {
1237            assert_ne!(range.contains(&version), range.complement().contains(&version));
1238        }
1239
1240        // Testing intersection ----------------------------
1241
1242        #[test]
1243        fn intersection_is_symmetric(r1 in proptest_strategy(), r2 in proptest_strategy()) {
1244            assert_eq!(r1.intersection(&r2), r2.intersection(&r1));
1245        }
1246
1247        #[test]
1248        fn intersection_with_any_is_identity(range in proptest_strategy()) {
1249            assert_eq!(Ranges::full().intersection(&range), range);
1250        }
1251
1252        #[test]
1253        fn intersection_with_none_is_none(range in proptest_strategy()) {
1254            assert_eq!(Ranges::empty().intersection(&range), Ranges::empty());
1255        }
1256
1257        #[test]
1258        fn intersection_is_idempotent(r1 in proptest_strategy(), r2 in proptest_strategy()) {
1259            assert_eq!(r1.intersection(&r2).intersection(&r2), r1.intersection(&r2));
1260        }
1261
1262        #[test]
1263        fn intersection_is_associative(r1 in proptest_strategy(), r2 in proptest_strategy(), r3 in proptest_strategy()) {
1264            assert_eq!(r1.intersection(&r2).intersection(&r3), r1.intersection(&r2.intersection(&r3)));
1265        }
1266
1267        #[test]
1268        fn intesection_of_complements_is_none(range in proptest_strategy()) {
1269            assert_eq!(range.complement().intersection(&range), Ranges::empty());
1270        }
1271
1272        #[test]
1273        fn intesection_contains_both(r1 in proptest_strategy(), r2 in proptest_strategy(), version in version_strat()) {
1274            assert_eq!(r1.intersection(&r2).contains(&version), r1.contains(&version) && r2.contains(&version));
1275        }
1276
1277        // Testing union -----------------------------------
1278
1279        #[test]
1280        fn union_of_complements_is_any(range in proptest_strategy()) {
1281            assert_eq!(range.complement().union(&range), Ranges::full());
1282        }
1283
1284        #[test]
1285        fn union_contains_either(r1 in proptest_strategy(), r2 in proptest_strategy(), version in version_strat()) {
1286            assert_eq!(r1.union(&r2).contains(&version), r1.contains(&version) || r2.contains(&version));
1287        }
1288
1289        #[test]
1290        fn is_disjoint_through_intersection(r1 in proptest_strategy(), r2 in proptest_strategy()) {
1291            let disjoint_def = r1.intersection(&r2) == Ranges::empty();
1292            assert_eq!(r1.is_disjoint(&r2), disjoint_def);
1293        }
1294
1295        #[test]
1296        fn subset_of_through_intersection(r1 in proptest_strategy(), r2 in proptest_strategy()) {
1297            let disjoint_def = r1.intersection(&r2) == r1;
1298            assert_eq!(r1.subset_of(&r2), disjoint_def);
1299        }
1300
1301        #[test]
1302        fn union_through_intersection(r1 in proptest_strategy(), r2 in proptest_strategy()) {
1303            let union_def = r1
1304                .complement()
1305                .intersection(&r2.complement())
1306                .complement()
1307                .check_invariants();
1308            assert_eq!(r1.union(&r2), union_def);
1309        }
1310
1311        // Testing contains --------------------------------
1312
1313        #[test]
1314        fn always_contains_exact(version in version_strat()) {
1315            assert!(Ranges::<u32>::singleton(version).contains(&version));
1316        }
1317
1318        #[test]
1319        fn contains_negation(range in proptest_strategy(), version in version_strat()) {
1320            assert_ne!(range.contains(&version), range.complement().contains(&version));
1321        }
1322
1323        #[test]
1324        fn contains_intersection(range in proptest_strategy(), version in version_strat()) {
1325            assert_eq!(range.contains(&version), range.intersection(&Ranges::singleton(version)) != Ranges::empty());
1326        }
1327
1328        #[test]
1329        fn contains_bounding_range(range in proptest_strategy(), version in version_strat()) {
1330            if range.contains(&version) {
1331                assert!(range.bounding_range().map(|b| b.contains(&version)).unwrap_or(false));
1332            }
1333        }
1334
1335        #[test]
1336        fn from_range_bounds(range in any::<(Bound<u32>, Bound<u32>)>(), version in version_strat()) {
1337            let rv: Ranges<_> = Ranges::<u32>::from_range_bounds(range);
1338            assert_eq!(range.contains(&version), rv.contains(&version));
1339        }
1340
1341        #[test]
1342        fn from_range_bounds_round_trip(range in any::<(Bound<u32>, Bound<u32>)>()) {
1343            let rv: Ranges<u32> = Ranges::from_range_bounds(range);
1344            let rv2: Ranges<u32> = rv.bounding_range().map(Ranges::from_range_bounds::<_, u32>).unwrap_or_else(Ranges::empty);
1345            assert_eq!(rv, rv2);
1346        }
1347
1348        #[test]
1349        fn contains(range in proptest_strategy(), versions in proptest::collection::vec(version_strat(), ..30)) {
1350            for v in versions {
1351                assert_eq!(range.contains(&v), range.segments.iter().any(|s| RangeBounds::contains(s, &v)));
1352            }
1353        }
1354
1355        #[test]
1356        fn contains_many(range in proptest_strategy(), mut versions in proptest::collection::vec(version_strat(), ..30)) {
1357            versions.sort();
1358            assert_eq!(versions.len(), range.contains_many(versions.iter()).count());
1359            for (a, b) in versions.iter().zip(range.contains_many(versions.iter())) {
1360                assert_eq!(range.contains(a), b);
1361            }
1362        }
1363
1364        #[test]
1365        fn simplify(range in proptest_strategy(), mut versions in proptest::collection::vec(version_strat(), ..30)) {
1366            versions.sort();
1367            let simp = range.simplify(versions.iter());
1368
1369            for v in versions {
1370                assert_eq!(range.contains(&v), simp.contains(&v));
1371            }
1372            assert!(simp.segments.len() <= range.segments.len())
1373        }
1374
1375        #[test]
1376        fn from_iter_valid(segments in proptest::collection::vec(any::<(Bound<u32>, Bound<u32>)>(), ..30)) {
1377            let mut expected = Ranges::empty();
1378            for segment in &segments {
1379                expected = expected.union(&Ranges::from_range_bounds(*segment));
1380            }
1381            let actual =  Ranges::from_iter(segments.clone());
1382            assert_eq!(expected, actual, "{segments:?}");
1383        }
1384    }
1385
1386    #[test]
1387    fn contains_many_can_take_owned() {
1388        let range: Ranges<u8> = Ranges::singleton(1);
1389        let versions = vec![1, 2, 3];
1390        // Check that iter can be a Cow
1391        assert_eq!(
1392            range.contains_many(versions.iter()).count(),
1393            range
1394                .contains_many(versions.iter().map(std::borrow::Cow::Borrowed))
1395                .count()
1396        );
1397        // Check that iter can be a V
1398        assert_eq!(
1399            range.contains_many(versions.iter()).count(),
1400            range.contains_many(versions.into_iter()).count()
1401        );
1402    }
1403
1404    #[test]
1405    fn contains_can_take_owned() {
1406        let range: Ranges<Box<u8>> = Ranges::singleton(1);
1407        let version = 1;
1408
1409        assert_eq!(range.contains(&Box::new(version)), range.contains(&version));
1410        let range: Ranges<String> = Ranges::singleton(1.to_string());
1411        let version = 1.to_string();
1412        assert_eq!(range.contains(&version), range.contains("1"));
1413    }
1414
1415    #[test]
1416    fn simplify_can_take_owned() {
1417        let range: Ranges<u8> = Ranges::singleton(1);
1418        let versions = vec![1, 2, 3];
1419        // Check that iter can be a Cow
1420        assert_eq!(
1421            range.simplify(versions.iter()),
1422            range.simplify(versions.iter().map(std::borrow::Cow::Borrowed))
1423        );
1424        // Check that iter can be a V
1425        assert_eq!(
1426            range.simplify(versions.iter()),
1427            range.simplify(versions.into_iter())
1428        );
1429    }
1430
1431    #[test]
1432    fn version_ord() {
1433        let versions: &[Ranges<u32>] = &[
1434            Ranges::strictly_lower_than(1u32),
1435            Ranges::lower_than(1u32),
1436            Ranges::singleton(1u32),
1437            Ranges::between(1u32, 3u32),
1438            Ranges::higher_than(1u32),
1439            Ranges::strictly_higher_than(1u32),
1440            Ranges::singleton(2u32),
1441            Ranges::singleton(2u32).union(&Ranges::singleton(3u32)),
1442            Ranges::singleton(2u32)
1443                .union(&Ranges::singleton(3u32))
1444                .union(&Ranges::singleton(4u32)),
1445            Ranges::singleton(2u32).union(&Ranges::singleton(4u32)),
1446            Ranges::singleton(3u32),
1447        ];
1448
1449        let mut versions_sorted = versions.to_vec();
1450        versions_sorted.sort();
1451        assert_eq!(versions_sorted, versions);
1452
1453        // Check that the sorting isn't just stable because we're returning equal.
1454        let mut version_reverse_sorted = versions.to_vec();
1455        version_reverse_sorted.reverse();
1456        version_reverse_sorted.sort();
1457        assert_eq!(version_reverse_sorted, versions);
1458    }
1459}