Skip to main content

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