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/// Describes how one set relates to another.
82///
83/// [`SetRelation::Subset`] takes precedence when the left-hand set is empty and therefore also
84/// disjoint.
85#[derive(Clone, Copy, Debug, Eq, PartialEq)]
86pub enum SetRelation {
87    /// Every value in the left-hand set is also in the right-hand set.
88    Subset,
89    /// The two sets have no values in common.
90    Disjoint,
91    /// The sets overlap, but the left-hand set is not a subset of the right-hand set.
92    Overlapping,
93}
94
95// TODO: Replace the tuple type with a custom enum inlining the bounds to reduce the type's size.
96type Interval<V> = (Bound<V>, Bound<V>);
97
98impl<V> Ranges<V> {
99    /// Empty set of versions.
100    pub fn empty() -> Self {
101        Self {
102            segments: SmallVec::new(),
103        }
104    }
105
106    /// Set of all possible versions
107    pub fn full() -> Self {
108        Self {
109            segments: smallvec![(Unbounded, Unbounded)],
110        }
111    }
112
113    /// Set of all versions higher or equal to some version
114    pub fn higher_than(v: impl Into<V>) -> Self {
115        Self {
116            segments: smallvec![(Included(v.into()), Unbounded)],
117        }
118    }
119
120    /// Set of all versions higher to some version
121    pub fn strictly_higher_than(v: impl Into<V>) -> Self {
122        Self {
123            segments: smallvec![(Excluded(v.into()), Unbounded)],
124        }
125    }
126
127    /// Set of all versions lower to some version
128    pub fn strictly_lower_than(v: impl Into<V>) -> Self {
129        Self {
130            segments: smallvec![(Unbounded, Excluded(v.into()))],
131        }
132    }
133
134    /// Set of all versions lower or equal to some version
135    pub fn lower_than(v: impl Into<V>) -> Self {
136        Self {
137            segments: smallvec![(Unbounded, Included(v.into()))],
138        }
139    }
140
141    /// Set of versions greater or equal to `v1` but less than `v2`.
142    pub fn between(v1: impl Into<V>, v2: impl Into<V>) -> Self {
143        Self {
144            segments: smallvec![(Included(v1.into()), Excluded(v2.into()))],
145        }
146    }
147
148    /// Whether the set is empty, i.e. it has not ranges
149    pub fn is_empty(&self) -> bool {
150        self.segments.is_empty()
151    }
152}
153
154impl<V: Clone> Ranges<V> {
155    /// Set containing exactly one version
156    pub fn singleton(v: impl Into<V>) -> Self {
157        let v = v.into();
158        Self {
159            segments: smallvec![(Included(v.clone()), Included(v))],
160        }
161    }
162
163    /// Returns the complement, which contains everything not included in `self`.
164    pub fn complement(&self) -> Self {
165        match self.segments.first() {
166            // Complement of ∅ is ∞
167            None => Self::full(),
168
169            // Complement of ∞ is ∅
170            Some((Unbounded, Unbounded)) => Self::empty(),
171
172            // First high bound is +∞
173            Some((Included(v), Unbounded)) => Self::strictly_lower_than(v.clone()),
174            Some((Excluded(v), Unbounded)) => Self::lower_than(v.clone()),
175
176            Some((Unbounded, Included(v))) => {
177                Self::negate_segments(Excluded(v.clone()), &self.segments[1..])
178            }
179            Some((Unbounded, Excluded(v))) => {
180                Self::negate_segments(Included(v.clone()), &self.segments[1..])
181            }
182            Some((Included(_), Included(_)))
183            | Some((Included(_), Excluded(_)))
184            | Some((Excluded(_), Included(_)))
185            | Some((Excluded(_), Excluded(_))) => Self::negate_segments(Unbounded, &self.segments),
186        }
187    }
188
189    /// Helper function performing the negation of intervals in segments.
190    fn negate_segments(start: Bound<V>, segments: &[Interval<V>]) -> Self {
191        let mut complement_segments = SmallVec::new();
192        let mut start = start;
193        for (v1, v2) in segments {
194            complement_segments.push((
195                start,
196                match v1 {
197                    Included(v) => Excluded(v.clone()),
198                    Excluded(v) => Included(v.clone()),
199                    Unbounded => unreachable!(),
200                },
201            ));
202            start = match v2 {
203                Included(v) => Excluded(v.clone()),
204                Excluded(v) => Included(v.clone()),
205                Unbounded => Unbounded,
206            }
207        }
208        if !matches!(start, Unbounded) {
209            complement_segments.push((start, Unbounded));
210        }
211
212        Self {
213            segments: complement_segments,
214        }
215    }
216}
217
218impl<V: Ord> Ranges<V> {
219    /// If self contains exactly a single version, return it, otherwise, return [None].
220    pub fn as_singleton(&self) -> Option<&V> {
221        match self.segments.as_slice() {
222            [(Included(v1), Included(v2))] => {
223                if v1 == v2 {
224                    Some(v1)
225                } else {
226                    None
227                }
228            }
229            _ => None,
230        }
231    }
232
233    /// Convert to something that can be used with
234    /// [BTreeMap::range](std::collections::BTreeMap::range).
235    /// All versions contained in self, will be in the output,
236    /// but there may be versions in the output that are not contained in self.
237    /// Returns None if the range is empty.
238    pub fn bounding_range(&self) -> Option<(Bound<&V>, Bound<&V>)> {
239        self.segments.first().map(|(start, _)| {
240            let end = self
241                .segments
242                .last()
243                .expect("if there is a first element, there must be a last element");
244            (start.as_ref(), end.1.as_ref())
245        })
246    }
247
248    /// Returns true if self contains the specified value.
249    pub fn contains<Q>(&self, version: &Q) -> bool
250    where
251        V: Borrow<Q>,
252        Q: ?Sized + PartialOrd,
253    {
254        self.segments
255            .binary_search_by(|segment| {
256                // We have to reverse because we need the segment wrt to the version, while
257                // within bounds tells us the version wrt to the segment.
258                within_bounds(version, segment).reverse()
259            })
260            // An equal interval is one that contains the version
261            .is_ok()
262    }
263
264    /// Returns true if self contains the specified values.
265    ///
266    /// The `versions` iterator must be sorted.
267    /// Functionally equivalent to `versions.map(|v| self.contains(v))`.
268    /// Except it runs in `O(size_of_range + len_of_versions)` not `O(size_of_range * len_of_versions)`
269    pub fn contains_many<'s, I, BV>(&'s self, versions: I) -> impl Iterator<Item = bool> + 's
270    where
271        I: Iterator<Item = BV> + 's,
272        BV: Borrow<V> + 's,
273    {
274        #[cfg(debug_assertions)]
275        let mut last: Option<BV> = None;
276        versions.scan(0, move |i, v| {
277            #[cfg(debug_assertions)]
278            {
279                if let Some(l) = last.as_ref() {
280                    assert!(
281                        l.borrow() <= v.borrow(),
282                        "`contains_many` `versions` argument incorrectly sorted"
283                    );
284                }
285            }
286            while let Some(segment) = self.segments.get(*i) {
287                match within_bounds(v.borrow(), segment) {
288                    Ordering::Less => return Some(false),
289                    Ordering::Equal => return Some(true),
290                    Ordering::Greater => *i += 1,
291                }
292            }
293            #[cfg(debug_assertions)]
294            {
295                last = Some(v);
296            }
297            Some(false)
298        })
299    }
300
301    /// Construct a simple range from anything that impls [RangeBounds] like `v1..v2`.
302    pub fn from_range_bounds<R, IV>(bounds: R) -> Self
303    where
304        R: RangeBounds<IV>,
305        IV: Clone + Into<V>,
306    {
307        let start = match bounds.start_bound() {
308            Included(v) => Included(v.clone().into()),
309            Excluded(v) => Excluded(v.clone().into()),
310            Unbounded => Unbounded,
311        };
312        let end = match bounds.end_bound() {
313            Included(v) => Included(v.clone().into()),
314            Excluded(v) => Excluded(v.clone().into()),
315            Unbounded => Unbounded,
316        };
317        if valid_segment(&start, &end) {
318            Self {
319                segments: smallvec![(start, end)],
320            }
321        } else {
322            Self::empty()
323        }
324    }
325
326    /// See [`Ranges`] for the invariants checked.
327    fn check_invariants(self) -> Self {
328        if cfg!(debug_assertions) {
329            for p in self.segments.as_slice().windows(2) {
330                assert!(end_before_start_with_gap(&p[0].1, &p[1].0));
331            }
332            for (s, e) in self.segments.iter() {
333                assert!(valid_segment(s, e));
334            }
335        }
336        self
337    }
338}
339
340/// Implementing `PartialOrd` for start `Bound` of an interval.
341///
342/// Legend: `∞` is unbounded, `[1,2]` is `>=1,<=2`, `]1,2[` is `>1,<2`.
343///
344/// ```text
345/// left:   ∞-------]
346/// right:    [-----]
347/// left is smaller, since it starts earlier.
348///
349/// left:   [-----]
350/// right:  ]-----]
351/// left is smaller, since it starts earlier.
352/// ```
353fn cmp_bounds_start<V: PartialOrd>(left: Bound<&V>, right: Bound<&V>) -> Option<Ordering> {
354    Some(match (left, right) {
355        // left:   ∞-----
356        // right:  ∞-----
357        (Unbounded, Unbounded) => Ordering::Equal,
358        // left:     [---
359        // right:  ∞-----
360        (Included(_left), Unbounded) => Ordering::Greater,
361        // left:     ]---
362        // right:  ∞-----
363        (Excluded(_left), Unbounded) => Ordering::Greater,
364        // left:   ∞-----
365        // right:    [---
366        (Unbounded, Included(_right)) => Ordering::Less,
367        // left:   [----- OR [----- OR   [-----
368        // right:    [--- OR [----- OR [---
369        (Included(left), Included(right)) => left.partial_cmp(right)?,
370        (Excluded(left), Included(right)) => match left.partial_cmp(right)? {
371            // left:   ]-----
372            // right:    [---
373            Ordering::Less => Ordering::Less,
374            // left:   ]-----
375            // right:  [---
376            Ordering::Equal => Ordering::Greater,
377            // left:     ]---
378            // right:  [-----
379            Ordering::Greater => Ordering::Greater,
380        },
381        // left:   ∞-----
382        // right:    ]---
383        (Unbounded, Excluded(_right)) => Ordering::Less,
384        (Included(left), Excluded(right)) => match left.partial_cmp(right)? {
385            // left:   [-----
386            // right:    ]---
387            Ordering::Less => Ordering::Less,
388            // left:   [-----
389            // right:  ]---
390            Ordering::Equal => Ordering::Less,
391            // left:     [---
392            // right:  ]-----
393            Ordering::Greater => Ordering::Greater,
394        },
395        // left:   ]----- OR ]----- OR   ]---
396        // right:    ]--- OR ]----- OR ]-----
397        (Excluded(left), Excluded(right)) => left.partial_cmp(right)?,
398    })
399}
400
401/// Implementing `PartialOrd` for end `Bound` of an interval.
402///
403/// We flip the unbounded ranges from `-∞` to `∞`, while `V`-valued bounds checks remain the same.
404///
405/// Legend: `∞` is unbounded, `[1,2]` is `>=1,<=2`, `]1,2[` is `>1,<2`.
406///
407/// ```text
408/// left:   [--------∞
409/// right:  [-----]
410/// left is greater, since it starts earlier.
411///
412/// left:   [-----[
413/// right:  [-----]
414/// left is smaller, since it ends earlier.
415/// ```
416fn cmp_bounds_end<V: PartialOrd>(left: Bound<&V>, right: Bound<&V>) -> Option<Ordering> {
417    Some(match (left, right) {
418        // left:   -----∞
419        // right:  -----∞
420        (Unbounded, Unbounded) => Ordering::Equal,
421        // left:   ---]
422        // right:  -----∞
423        (Included(_left), Unbounded) => Ordering::Less,
424        // left:   ---[
425        // right:  -----∞
426        (Excluded(_left), Unbounded) => Ordering::Less,
427        // left:  -----∞
428        // right: ---]
429        (Unbounded, Included(_right)) => Ordering::Greater,
430        // left:   -----] OR -----] OR ---]
431        // right:    ---] OR -----] OR -----]
432        (Included(left), Included(right)) => left.partial_cmp(right)?,
433        (Excluded(left), Included(right)) => match left.partial_cmp(right)? {
434            // left:   ---[
435            // right:  -----]
436            Ordering::Less => Ordering::Less,
437            // left:   -----[
438            // right:  -----]
439            Ordering::Equal => Ordering::Less,
440            // left:   -----[
441            // right:  ---]
442            Ordering::Greater => Ordering::Greater,
443        },
444        (Unbounded, Excluded(_right)) => Ordering::Greater,
445        (Included(left), Excluded(right)) => match left.partial_cmp(right)? {
446            // left:   ---]
447            // right:  -----[
448            Ordering::Less => Ordering::Less,
449            // left:   -----]
450            // right:  -----[
451            Ordering::Equal => Ordering::Greater,
452            // left:   -----]
453            // right:  ---[
454            Ordering::Greater => Ordering::Greater,
455        },
456        // left:   -----[ OR -----[ OR ---[
457        // right:  ---[   OR -----[ OR -----[
458        (Excluded(left), Excluded(right)) => left.partial_cmp(right)?,
459    })
460}
461
462impl<V: PartialOrd> PartialOrd for Ranges<V> {
463    /// A simple ordering scheme where we zip the segments and compare all bounds in order. If all
464    /// bounds are equal, the longer range is considered greater. (And if all zipped bounds are
465    /// equal and we have the same number of segments, the ranges are equal).
466    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
467        for (left, right) in self.segments.iter().zip(other.segments.iter()) {
468            let start_cmp = cmp_bounds_start(left.start_bound(), right.start_bound())?;
469            if start_cmp != Ordering::Equal {
470                return Some(start_cmp);
471            }
472            let end_cmp = cmp_bounds_end(left.end_bound(), right.end_bound())?;
473            if end_cmp != Ordering::Equal {
474                return Some(end_cmp);
475            }
476        }
477        Some(self.segments.len().cmp(&other.segments.len()))
478    }
479}
480
481impl<V: Ord> Ord for Ranges<V> {
482    fn cmp(&self, other: &Self) -> Ordering {
483        self.partial_cmp(other)
484            .expect("PartialOrd must be `Some(Ordering)` for types that implement `Ord`")
485    }
486}
487
488/// The ordering of the version wrt to the interval.
489/// ```text
490///      |-------|
491///   ^      ^      ^
492///   less   equal  greater
493/// ```
494fn within_bounds<Q, V>(version: &Q, segment: &Interval<V>) -> Ordering
495where
496    V: Borrow<Q>,
497    Q: ?Sized + PartialOrd,
498{
499    let below_lower_bound = match segment {
500        (Excluded(start), _) => version <= start.borrow(),
501        (Included(start), _) => version < start.borrow(),
502        (Unbounded, _) => false,
503    };
504    if below_lower_bound {
505        return Ordering::Less;
506    }
507    let below_upper_bound = match segment {
508        (_, Unbounded) => true,
509        (_, Included(end)) => version <= end.borrow(),
510        (_, Excluded(end)) => version < end.borrow(),
511    };
512    if below_upper_bound {
513        return Ordering::Equal;
514    }
515    Ordering::Greater
516}
517
518/// A valid segment is one where at least one version fits between start and end
519fn valid_segment<T: PartialOrd>(start: &Bound<T>, end: &Bound<T>) -> bool {
520    match (start, end) {
521        // Singleton interval are allowed
522        (Included(s), Included(e)) => s <= e,
523        (Included(s), Excluded(e)) => s < e,
524        (Excluded(s), Included(e)) => s < e,
525        (Excluded(s), Excluded(e)) => s < e,
526        (Unbounded, _) | (_, Unbounded) => true,
527    }
528}
529
530/// The complementary bound at the same position: the end bound just below a start bound, or the
531/// start bound just above an end bound. `None` for [`Unbounded`], whose complementary side is
532/// empty.
533fn complement_bound<V>(bound: &Bound<V>) -> Option<Bound<&V>> {
534    match bound {
535        Included(version) => Some(Excluded(version)),
536        Excluded(version) => Some(Included(version)),
537        Unbounded => None,
538    }
539}
540
541/// The end of one interval is before the start of the next one, so they can't be concatenated
542/// into a single interval. The `union` method calling with both intervals and then the intervals
543/// switched. If either is true, the intervals are separate in the union and if both are false, they
544/// are merged.
545/// ```text
546/// True for these two:
547///  |----|
548///                |-----|
549///       ^ end    ^ start
550/// False for these two:
551///  |----|
552///     |-----|
553/// Here it depends: If they both exclude the position they share, there is a version in between
554/// them that blocks concatenation
555///  |----|
556///       |-----|
557/// ```
558fn end_before_start_with_gap<V: PartialOrd>(end: &Bound<V>, start: &Bound<V>) -> bool {
559    match (end, start) {
560        (_, Unbounded) => false,
561        (Unbounded, _) => false,
562        (Included(left), Included(right)) => left < right,
563        (Included(left), Excluded(right)) => left < right,
564        (Excluded(left), Included(right)) => left < right,
565        (Excluded(left), Excluded(right)) => left <= right,
566    }
567}
568
569fn left_start_is_smaller<V: PartialOrd>(left: Bound<V>, right: Bound<V>) -> bool {
570    match (left, right) {
571        (Unbounded, _) => true,
572        (_, Unbounded) => false,
573        (Included(l), Included(r)) => l <= r,
574        (Excluded(l), Excluded(r)) => l <= r,
575        (Included(l), Excluded(r)) => l <= r,
576        (Excluded(l), Included(r)) => l < r,
577    }
578}
579
580fn left_end_is_smaller<V: PartialOrd>(left: Bound<V>, right: Bound<V>) -> bool {
581    match (left, right) {
582        (_, Unbounded) => true,
583        (Unbounded, _) => false,
584        (Included(l), Included(r)) => l <= r,
585        (Excluded(l), Excluded(r)) => l <= r,
586        (Excluded(l), Included(r)) => l <= r,
587        (Included(l), Excluded(r)) => l < r,
588    }
589}
590
591/// Group adjacent versions locations.
592///
593/// ```text
594/// [None, 3, 6, 7, None] -> [(3, 7)]
595/// [3, 6, 7, None] -> [(None, 7)]
596/// [3, 6, 7] -> [(None, None)]
597/// [None, 1, 4, 7, None, None, None, 8, None, 9] -> [(1, 7), (8, 8), (9, None)]
598/// ```
599fn group_adjacent_locations(
600    mut locations: impl Iterator<Item = Option<usize>>,
601) -> impl Iterator<Item = (Option<usize>, Option<usize>)> {
602    // If the first version matched, then the lower bound of that segment is not needed
603    let mut seg = locations.next().flatten().map(|ver| (None, Some(ver)));
604    std::iter::from_fn(move || {
605        for ver in locations.by_ref() {
606            if let Some(ver) = ver {
607                // As long as were still matching versions, we keep merging into the currently matching segment
608                seg = Some((seg.map_or(Some(ver), |(s, _)| s), Some(ver)));
609            } else {
610                // If we have found a version that doesn't match, then right the merge segment and prepare for a new one.
611                if seg.is_some() {
612                    return seg.take();
613                }
614            }
615        }
616        // If the last version matched, then write out the merged segment but the upper bound is not needed.
617        seg.take().map(|(s, _)| (s, None))
618    })
619}
620
621impl<V: Ord + Clone> Ranges<V> {
622    /// Computes the union of this `Ranges` and another.
623    pub fn union(&self, other: &Self) -> Self {
624        let mut output = SmallVec::new();
625        let mut accumulator: Option<(&Bound<_>, &Bound<_>)> = None;
626        let mut left_iter = self.segments.iter().peekable();
627        let mut right_iter = other.segments.iter().peekable();
628        loop {
629            let smaller_interval = match (left_iter.peek(), right_iter.peek()) {
630                (Some((left_start, left_end)), Some((right_start, right_end))) => {
631                    if left_start_is_smaller(left_start.as_ref(), right_start.as_ref()) {
632                        left_iter.next();
633                        (left_start, left_end)
634                    } else {
635                        right_iter.next();
636                        (right_start, right_end)
637                    }
638                }
639                (Some((left_start, left_end)), None) => {
640                    left_iter.next();
641                    (left_start, left_end)
642                }
643                (None, Some((right_start, right_end))) => {
644                    right_iter.next();
645                    (right_start, right_end)
646                }
647                (None, None) => break,
648            };
649
650            if let Some(accumulator_) = accumulator {
651                if end_before_start_with_gap(accumulator_.1, smaller_interval.0) {
652                    output.push((accumulator_.0.clone(), accumulator_.1.clone()));
653                    accumulator = Some(smaller_interval);
654                } else {
655                    let accumulator_end = match (accumulator_.1, smaller_interval.1) {
656                        (_, Unbounded) | (Unbounded, _) => &Unbounded,
657                        (Included(l), Excluded(r) | Included(r)) if l == r => accumulator_.1,
658                        (Included(l) | Excluded(l), Included(r) | Excluded(r)) => {
659                            if l > r {
660                                accumulator_.1
661                            } else {
662                                smaller_interval.1
663                            }
664                        }
665                    };
666                    accumulator = Some((accumulator_.0, accumulator_end));
667                }
668            } else {
669                accumulator = Some(smaller_interval)
670            }
671        }
672
673        if let Some(accumulator) = accumulator {
674            output.push((accumulator.0.clone(), accumulator.1.clone()));
675        }
676
677        Self { segments: output }.check_invariants()
678    }
679
680    /// Computes the intersection of two sets of versions.
681    pub fn intersection(&self, other: &Self) -> Self {
682        let mut output = SmallVec::new();
683        let mut left_iter = self.segments.iter().peekable();
684        let mut right_iter = other.segments.iter().peekable();
685        // By the definition of intersection any point that is matched by the output
686        // must have a segment in each of the inputs that it matches.
687        // Therefore, every segment in the output must be the intersection of a segment from each of the inputs.
688        // It would be correct to do the "O(n^2)" thing, by computing the intersection of every segment from one input
689        // with every segment of the other input, and sorting the result.
690        // We can avoid the sorting by generating our candidate segments with an increasing `end` value.
691        while let Some(((left_start, left_end), (right_start, right_end))) =
692            left_iter.peek().zip(right_iter.peek())
693        {
694            // The next smallest `end` value is going to come from one of the inputs.
695            let left_end_is_smaller = left_end_is_smaller(left_end.as_ref(), right_end.as_ref());
696            // Now that we are processing `end` we will never have to process any segment smaller than that.
697            // We can ensure that the input that `end` came from is larger than `end` by advancing it one step.
698            // `end` is the smaller available input, so we know the other input is already larger than `end`.
699            // Note: We can call `other_iter.next_if( == end)`, but the ends lining up is rare enough that
700            // it does not end up being faster in practice.
701            let (other_start, end) = if left_end_is_smaller {
702                left_iter.next();
703                (right_start, left_end)
704            } else {
705                right_iter.next();
706                (left_start, right_end)
707            };
708            // `start` will either come from the input `end` came from or the other input, whichever one is larger.
709            // The intersection is invalid if `start` > `end`.
710            // But, we already know that the segments in our input are valid.
711            // So we do not need to check if the `start` from the input `end` came from is smaller than `end`.
712            // If the `other_start` is larger than end, then the intersection will be invalid.
713            if !valid_segment(other_start, end) {
714                // Note: We can call `this_iter.next_if(!valid_segment(other_start, this_end))` in a loop.
715                // But the checks make it slower for the benchmarked inputs.
716                continue;
717            }
718            let start = match (left_start, right_start) {
719                (Included(l), Included(r)) => Included(std::cmp::max(l, r)),
720                (Excluded(l), Excluded(r)) => Excluded(std::cmp::max(l, r)),
721
722                (Included(i), Excluded(e)) | (Excluded(e), Included(i)) => {
723                    if i <= e {
724                        Excluded(e)
725                    } else {
726                        Included(i)
727                    }
728                }
729                (s, Unbounded) | (Unbounded, s) => s.as_ref(),
730            };
731            // Now we clone and push a new segment.
732            // By dealing with references until now we ensure that NO cloning happens when we reject the segment.
733            output.push((start.cloned(), end.clone()))
734        }
735
736        Self { segments: output }.check_invariants()
737    }
738
739    /// Compute the difference of two sets of versions: the versions contained in `self` but not
740    /// in `other`.
741    ///
742    /// Equivalent to `self.intersection(&other.complement())`, without materializing the
743    /// complement.
744    pub fn difference(&self, other: &Self) -> Self {
745        let mut output = SmallVec::new();
746        let mut right_iter = other.segments.iter().peekable();
747        for (left_start, left_end) in &self.segments {
748            // The start of the part of the left segment not yet known to be covered. Bounds stay
749            // references until push, so cloning only happens for segments in the output.
750            let mut current_start = left_start.as_ref();
751            loop {
752                // Drop right segments that end before the uncovered part starts: they cannot
753                // overlap it, nor any later left segment.
754                // Ensures left start < right end
755                while let Some((_, right_end)) = right_iter.peek() {
756                    if valid_segment(&current_start, &right_end.as_ref()) {
757                        break;
758                    }
759                    right_iter.next();
760                }
761                let Some((right_start, right_end)) = right_iter.peek().copied() else {
762                    // No right segment reaches the uncovered part; all of it survives.
763                    output.push((current_start.cloned(), left_end.clone()));
764                    break;
765                };
766                // Ensures right start < left end
767                if !valid_segment(&right_start.as_ref(), &left_end.as_ref()) {
768                    // The next right segment starts after this left segment ends.
769                    output.push((current_start.cloned(), left_end.clone()));
770                    break;
771                }
772
773                // If left start < right start, left start to right start is the new segment.
774                if let Some(cut_end) = complement_bound(right_start) {
775                    if valid_segment(&current_start, &cut_end) {
776                        output.push((current_start.cloned(), cut_end.cloned()));
777                    }
778                }
779                let Some(next_start) = complement_bound(right_end) else {
780                    // The right segment is unbounded above, so it also covers every later
781                    // left segment.
782                    return Self { segments: output }.check_invariants();
783                };
784                // If right ends later, keep right for overlapping with future left segments,
785                // if left ends later, keep left for checking if it overlaps with future right
786                // segments.
787                if valid_segment(&next_start, &left_end.as_ref()) {
788                    // Continue with the part of the left segment above the right segment.
789                    current_start = next_start;
790                    right_iter.next();
791                } else {
792                    // The right segment overlaps the current left segment entirely, but this right
793                    // segment may also overlap the next left segment too, so keep it.
794                    break;
795                }
796            }
797        }
798
799        Self { segments: output }.check_invariants()
800    }
801
802    /// Return true if there can be no `V` so that `V` is contained in both `self` and `other`.
803    ///
804    /// Note that we don't know that set of all existing `V`s here, so we only check if the segments
805    /// are disjoint, not if no version is contained in both.
806    pub fn is_disjoint(&self, other: &Self) -> bool {
807        // The operation is symmetric
808        let mut left_iter = self.segments.iter().peekable();
809        let mut right_iter = other.segments.iter().peekable();
810
811        while let Some((left, right)) = left_iter.peek().zip(right_iter.peek()) {
812            if !valid_segment(&right.start_bound(), &left.end_bound()) {
813                left_iter.next();
814            } else if !valid_segment(&left.start_bound(), &right.end_bound()) {
815                right_iter.next();
816            } else {
817                return false;
818            }
819        }
820
821        // The remaining element(s) can't intersect anymore
822        true
823    }
824
825    /// Classifies `self` as a subset of, disjoint from, or partially overlapping with `other`.
826    ///
827    /// This combines [`Self::subset_of`] and [`Self::is_disjoint`] into a single traversal.
828    /// An empty `self` is classified as [`SetRelation::Subset`].
829    pub fn relation(&self, other: &Self) -> SetRelation {
830        // Equality is common for long accumulated ranges during PubGrub conflict resolution.
831        if self.segments.len() > 1
832            && self.segments.len() == other.segments.len()
833            && self.segments == other.segments
834        {
835            return SetRelation::Subset;
836        }
837
838        let mut other_iter = other.segments.iter().peekable();
839        let mut is_subset = true;
840        let mut overlaps = false;
841
842        for subset_elem in &self.segments {
843            while other_iter.peek().is_some_and(|containing_elem| {
844                !valid_segment(&subset_elem.start_bound(), &containing_elem.end_bound())
845            }) {
846                other_iter.next();
847            }
848
849            let Some(containing_elem) = other_iter.peek() else {
850                is_subset = false;
851                break;
852            };
853
854            if !valid_segment(&containing_elem.start_bound(), &subset_elem.end_bound()) {
855                is_subset = false;
856                continue;
857            }
858
859            overlaps = true;
860            if !left_start_is_smaller(containing_elem.start_bound(), subset_elem.start_bound())
861                || !left_end_is_smaller(subset_elem.end_bound(), containing_elem.end_bound())
862            {
863                is_subset = false;
864            }
865        }
866
867        if is_subset {
868            SetRelation::Subset
869        } else if overlaps {
870            SetRelation::Overlapping
871        } else {
872            SetRelation::Disjoint
873        }
874    }
875
876    /// Return true if any `V` that is contained in `self` is also contained in `other`.
877    ///
878    /// Note that we don't know that set of all existing `V`s here, so we only check if all
879    /// segments `self` are contained in a segment of `other`.
880    pub fn subset_of(&self, other: &Self) -> bool {
881        // Equality is common for long accumulated ranges during PubGrub conflict resolution.
882        if self.segments.len() > 1
883            && self.segments.len() == other.segments.len()
884            && self.segments == other.segments
885        {
886            return true;
887        }
888
889        let mut containing_iter = other.segments.iter();
890        let mut subset_iter = self.segments.iter();
891        let Some(mut containing_elem) = containing_iter.next() else {
892            // As long as we have subset elements, we need containing elements
893            return subset_iter.next().is_none();
894        };
895
896        for subset_elem in subset_iter {
897            // Check if the current containing element ends before the subset element.
898            // There needs to be another containing element for our subset element in this case.
899            while !valid_segment(&subset_elem.start_bound(), &containing_elem.end_bound()) {
900                if let Some(containing_elem_) = containing_iter.next() {
901                    containing_elem = containing_elem_;
902                } else {
903                    return false;
904                };
905            }
906
907            let start_contained =
908                left_start_is_smaller(containing_elem.start_bound(), subset_elem.start_bound());
909
910            if !start_contained {
911                // The start element is not contained
912                return false;
913            }
914
915            let end_contained =
916                left_end_is_smaller(subset_elem.end_bound(), containing_elem.end_bound());
917
918            if !end_contained {
919                // The end element is not contained
920                return false;
921            }
922        }
923
924        true
925    }
926
927    /// Returns a copy of this set where each segment is widened to the largest interval that
928    /// contains the same given versions, merging segments when no version separates them.
929    ///
930    /// A bound that excludes no existing version cannot influence which versions a set contains,
931    /// so each segment can extend outward up to, and excluding, the nearest version outside the
932    /// segment. For example, with the existing versions `1, 2, 3, 4`, the singleton `{2}` widens
933    /// to `(1, 3)`, and the union `{2} ∪ {3}` widens to `(1, 4)`.
934    ///
935    /// The result is a superset of the input: For every one of the given versions, input and
936    /// output agree on whether it is contained, while versions not in `versions` may be added,
937    /// but are never removed.
938    ///
939    /// See [`Ranges::narrow_versions`] for the display-oriented inverse.
940    ///
941    /// The `versions` slice must be sorted.
942    pub fn widen_versions<BV>(&self, versions: &[BV]) -> Self
943    where
944        BV: Borrow<V>,
945    {
946        debug_assert!(
947            versions.is_sorted_by(|l, r| l.borrow() <= r.borrow()),
948            "`widen_versions` `versions` argument incorrectly sorted"
949        );
950        let mut segments: SmallVec<[Interval<V>; 1]> = SmallVec::new();
951        for segment in &self.segments {
952            // The last version below the segment becomes the new exclusive start bound, the
953            // first version above the segment the new exclusive end bound.
954            let below =
955                versions.partition_point(|v| within_bounds(v.borrow(), segment) == Ordering::Less);
956            let start = if below == 0 {
957                Unbounded
958            } else {
959                Excluded(versions[below - 1].borrow().clone())
960            };
961            let not_above = below
962                + versions[below..]
963                    .partition_point(|v| within_bounds(v.borrow(), segment) != Ordering::Greater);
964            let end = if not_above == versions.len() {
965                Unbounded
966            } else {
967                Excluded(versions[not_above].borrow().clone())
968            };
969            // Merge with the previous segment unless a version separates them.
970            match segments.last_mut() {
971                Some(last) if !end_before_start_with_gap(&last.1, &start) => last.1 = end,
972                _ => segments.push((start, end)),
973            }
974        }
975        Self { segments }.check_invariants()
976    }
977
978    /// Returns a copy of this set where each segment's bounded ends are shrunk to inclusive
979    /// bounds on the outermost given versions the segment contains.
980    ///
981    /// This is the display-oriented inverse of [`Ranges::widen_versions`]: bounds that exclude
982    /// no existing version carry no information, so each segment can shrink to the first and
983    /// last version it contains. For example, with the existing versions `1, 2, 3, 4`, the
984    /// segment `(1, 3)` shrinks to `{2}`. Unbounded ends are kept, so a claim about all
985    /// versions beyond the given ones (e.g. versions not yet published) remains visible:
986    /// `(1, ∞)` shrinks to `[2, ∞)`, not `[2, 4]`. A segment that contains none of the given
987    /// versions is kept unchanged.
988    ///
989    /// The result is a subset of the input: For every one of the given versions, input and
990    /// output agree on whether it is contained, while versions not in `versions` may be
991    /// removed, but are never added.
992    ///
993    /// The `versions` slice must be sorted.
994    pub fn narrow_versions<BV>(&self, versions: &[BV]) -> Self
995    where
996        BV: Borrow<V>,
997    {
998        debug_assert!(
999            versions.is_sorted_by(|l, r| l.borrow() <= r.borrow()),
1000            "`narrow_versions` `versions` argument incorrectly sorted"
1001        );
1002        let mut segments: SmallVec<[Interval<V>; 1]> = SmallVec::new();
1003        for segment in &self.segments {
1004            // The first and last version inside the segment become the new inclusive bounds.
1005            let first =
1006                versions.partition_point(|v| within_bounds(v.borrow(), segment) == Ordering::Less);
1007            let last = first
1008                + versions[first..]
1009                    .partition_point(|v| within_bounds(v.borrow(), segment) != Ordering::Greater);
1010            if first == last {
1011                // The segment contains none of the versions, keep it unchanged.
1012                segments.push(segment.clone());
1013            } else {
1014                let start = match &segment.0 {
1015                    Unbounded => Unbounded,
1016                    _ => Included(versions[first].borrow().clone()),
1017                };
1018                let end = match &segment.1 {
1019                    Unbounded => Unbounded,
1020                    _ => Included(versions[last - 1].borrow().clone()),
1021                };
1022                segments.push((start, end));
1023            }
1024        }
1025        Self { segments }.check_invariants()
1026    }
1027
1028    /// Returns a simpler representation that contains the same versions.
1029    ///
1030    /// For every one of the Versions provided in versions the existing range and the simplified range will agree on whether it is contained.
1031    /// The simplified version may include or exclude versions that are not in versions as the implementation wishes.
1032    ///
1033    /// If none of the versions are contained in the original than the range will be returned unmodified.
1034    /// If the range includes a single version, it will be returned unmodified.
1035    /// If all the versions are contained in the original than the range will be simplified to `full`.
1036    ///
1037    /// If the given versions are not sorted the correctness of this function is not guaranteed.
1038    pub fn simplify<'s, I, BV>(&self, versions: I) -> Self
1039    where
1040        I: Iterator<Item = BV> + 's,
1041        BV: Borrow<V> + 's,
1042    {
1043        // Do not simplify singletons
1044        if self.as_singleton().is_some() {
1045            return self.clone();
1046        }
1047
1048        #[cfg(debug_assertions)]
1049        let mut last: Option<BV> = None;
1050        // Return the segment index in the range for each version in the range, None otherwise
1051        let version_locations = versions.scan(0, move |i, v| {
1052            #[cfg(debug_assertions)]
1053            {
1054                if let Some(l) = last.as_ref() {
1055                    assert!(
1056                        l.borrow() <= v.borrow(),
1057                        "`simplify` `versions` argument incorrectly sorted"
1058                    );
1059                }
1060            }
1061            while let Some(segment) = self.segments.get(*i) {
1062                match within_bounds(v.borrow(), segment) {
1063                    Ordering::Less => return Some(None),
1064                    Ordering::Equal => return Some(Some(*i)),
1065                    Ordering::Greater => *i += 1,
1066                }
1067            }
1068            #[cfg(debug_assertions)]
1069            {
1070                last = Some(v);
1071            }
1072            Some(None)
1073        });
1074        let mut kept_segments = group_adjacent_locations(version_locations).peekable();
1075
1076        // Do not return null sets
1077        if kept_segments.peek().is_none() {
1078            return self.clone();
1079        }
1080
1081        self.keep_segments(kept_segments)
1082    }
1083
1084    /// Create a new range with a subset of segments at given location bounds.
1085    ///
1086    /// Each new segment is constructed from a pair of segments, taking the
1087    /// start of the first and the end of the second.
1088    fn keep_segments(
1089        &self,
1090        kept_segments: impl Iterator<Item = (Option<usize>, Option<usize>)>,
1091    ) -> Ranges<V> {
1092        let mut segments = SmallVec::new();
1093        for (s, e) in kept_segments {
1094            segments.push((
1095                s.map_or(Unbounded, |s| self.segments[s].0.clone()),
1096                e.map_or(Unbounded, |e| self.segments[e].1.clone()),
1097            ));
1098        }
1099        Self { segments }.check_invariants()
1100    }
1101
1102    /// Iterate over the parts of the range.
1103    pub fn iter(&self) -> impl DoubleEndedIterator<Item = (Bound<&V>, Bound<&V>)> {
1104        self.segments
1105            .iter()
1106            .map(|(start, end)| (start.as_ref(), end.as_ref()))
1107    }
1108}
1109
1110// Newtype to avoid leaking our internal representation.
1111pub struct RangesIter<V>(smallvec::IntoIter<[Interval<V>; 1]>);
1112
1113impl<V> Iterator for RangesIter<V> {
1114    type Item = Interval<V>;
1115
1116    fn next(&mut self) -> Option<Self::Item> {
1117        self.0.next()
1118    }
1119
1120    fn size_hint(&self) -> (usize, Option<usize>) {
1121        (self.0.len(), Some(self.0.len()))
1122    }
1123}
1124
1125impl<V> ExactSizeIterator for RangesIter<V> {}
1126
1127impl<V> DoubleEndedIterator for RangesIter<V> {
1128    fn next_back(&mut self) -> Option<Self::Item> {
1129        self.0.next_back()
1130    }
1131}
1132
1133impl<V> IntoIterator for Ranges<V> {
1134    type Item = (Bound<V>, Bound<V>);
1135    // Newtype to avoid leaking our internal representation.
1136    type IntoIter = RangesIter<V>;
1137
1138    fn into_iter(self) -> Self::IntoIter {
1139        RangesIter(self.segments.into_iter())
1140    }
1141}
1142
1143impl<V: Ord> FromIterator<(Bound<V>, Bound<V>)> for Ranges<V> {
1144    /// Constructor from arbitrary, unsorted and potentially overlapping ranges.
1145    ///
1146    /// This is equivalent, but faster, to computing the [`Ranges::union`] of the
1147    /// [`Ranges::from_range_bounds`] of each segment.
1148    fn from_iter<T: IntoIterator<Item = (Bound<V>, Bound<V>)>>(iter: T) -> Self {
1149        // We have three constraints we need to fulfil:
1150        // 1. The segments are sorted, from lowest to highest (through `Ord`): By sorting.
1151        // 2. Each segment contains at least one version (start < end): By skipping invalid
1152        //    segments.
1153        // 3. There is at least one version between two segments: By merging overlapping elements.
1154        //
1155        // Technically, the implementation has a O(n²) worst case complexity since we're inserting
1156        // and removing. This has two motivations: One is that we don't have any performance
1157        // critical usages of this method as of this writing, so we have no real world benchmark.
1158        // The other is that we get the elements from an iterator, so to avoid moving elements
1159        // around we would first need to build a different, sorted collection with extra
1160        // allocation(s), before we could build our real segments. --Konsti
1161
1162        // For this implementation, we choose to only build a single smallvec and insert or remove
1163        // in it, instead of e.g. collecting the segments into a sorted datastructure first and then
1164        // construction the second smallvec without shifting.
1165        let mut segments: SmallVec<[Interval<V>; 1]> = SmallVec::new();
1166
1167        for segment in iter {
1168            if !valid_segment(&segment.start_bound(), &segment.end_bound()) {
1169                continue;
1170            }
1171            // Find where to insert the new segment
1172            let insertion_point = segments.partition_point(|elem: &Interval<V>| {
1173                cmp_bounds_start(elem.start_bound(), segment.start_bound())
1174                    .unwrap()
1175                    .is_lt()
1176            });
1177            // Is it overlapping with the previous segment?
1178            let previous_overlapping = insertion_point > 0
1179                && !end_before_start_with_gap(
1180                    &segments[insertion_point - 1].end_bound(),
1181                    &segment.start_bound(),
1182                );
1183
1184            // Is it overlapping with the following segment? We'll check if there's more than one
1185            // overlap later.
1186            let next_overlapping = insertion_point < segments.len()
1187                && !end_before_start_with_gap(
1188                    &segment.end_bound(),
1189                    &segments[insertion_point].start_bound(),
1190                );
1191
1192            match (previous_overlapping, next_overlapping) {
1193                (true, true) => {
1194                    // previous:  |------|
1195                    // segment:       |------|
1196                    // following:          |------|
1197                    // final:     |---------------|
1198                    //
1199                    // OR
1200                    //
1201                    // previous:  |------|
1202                    // segment:       |-----------|
1203                    // following:          |----|
1204                    // final:     |---------------|
1205                    //
1206                    // OR
1207                    //
1208                    // previous:  |------|
1209                    // segment:       |----------------|
1210                    // following:          |----|   |------|
1211                    // final:     |------------------------|
1212                    // We merge all three segments into one, which is effectively removing one of
1213                    // two previously inserted and changing the bounds on the other.
1214
1215                    // Remove all elements covered by the final element
1216                    let mut following = segments.remove(insertion_point);
1217                    while insertion_point < segments.len()
1218                        && !end_before_start_with_gap(
1219                            &segment.end_bound(),
1220                            &segments[insertion_point].start_bound(),
1221                        )
1222                    {
1223                        following = segments.remove(insertion_point);
1224                    }
1225
1226                    // Set end to max(segment.end, <last overlapping segment>.end)
1227                    if cmp_bounds_end(segment.end_bound(), following.end_bound())
1228                        .unwrap()
1229                        .is_lt()
1230                    {
1231                        segments[insertion_point - 1].1 = following.1;
1232                    } else {
1233                        segments[insertion_point - 1].1 = segment.1;
1234                    }
1235                }
1236                (true, false) => {
1237                    // previous:  |------|
1238                    // segment:       |------|
1239                    // following:                |------|
1240                    //
1241                    // OR
1242                    //
1243                    // previous:  |----------|
1244                    // segment:       |---|
1245                    // following:                |------|
1246                    //
1247                    // final:     |----------|   |------|
1248                    // We can reuse the existing element by extending it.
1249
1250                    // Set end to max(segment.end, <previous>.end)
1251                    if cmp_bounds_end(
1252                        segments[insertion_point - 1].end_bound(),
1253                        segment.end_bound(),
1254                    )
1255                    .unwrap()
1256                    .is_lt()
1257                    {
1258                        segments[insertion_point - 1].1 = segment.1;
1259                    }
1260                }
1261                (false, true) => {
1262                    // previous:  |------|
1263                    // segment:             |------|
1264                    // following:               |------|
1265                    // final:    |------|   |----------|
1266                    //
1267                    // OR
1268                    //
1269                    // previous:  |------|
1270                    // segment:             |----------|
1271                    // following:               |---|
1272                    // final:    |------|   |----------|
1273                    //
1274                    // OR
1275                    //
1276                    // previous:  |------|
1277                    // segment:             |------------|
1278                    // following:               |---|  |------|
1279                    //
1280                    // final:    |------|   |-----------------|
1281                    // We can reuse the existing element by extending it.
1282
1283                    // Remove all fully covered segments so the next element is the last one that
1284                    // overlaps.
1285                    while insertion_point + 1 < segments.len()
1286                        && !end_before_start_with_gap(
1287                            &segment.end_bound(),
1288                            &segments[insertion_point + 1].start_bound(),
1289                        )
1290                    {
1291                        // We know that the one after also overlaps, so we can drop the current
1292                        // following.
1293                        segments.remove(insertion_point);
1294                    }
1295
1296                    // Set end to max(segment.end, <last overlapping segment>.end)
1297                    if cmp_bounds_end(segments[insertion_point].end_bound(), segment.end_bound())
1298                        .unwrap()
1299                        .is_lt()
1300                    {
1301                        segments[insertion_point].1 = segment.1;
1302                    }
1303                    segments[insertion_point].0 = segment.0;
1304                }
1305                (false, false) => {
1306                    // previous:  |------|
1307                    // segment:             |------|
1308                    // following:                      |------|
1309                    //
1310                    // final:    |------|   |------|   |------|
1311
1312                    // This line is O(n), which makes the algorithm O(n²), but it should be good
1313                    // enough for now.
1314                    segments.insert(insertion_point, segment);
1315                }
1316            }
1317        }
1318
1319        Self { segments }.check_invariants()
1320    }
1321}
1322
1323// REPORT ######################################################################
1324
1325impl<V: Display + Eq> Display for Ranges<V> {
1326    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1327        if self.segments.is_empty() {
1328            write!(f, "∅")?;
1329        } else {
1330            for (idx, segment) in self.segments.iter().enumerate() {
1331                if idx > 0 {
1332                    write!(f, " | ")?;
1333                }
1334                match segment {
1335                    (Unbounded, Unbounded) => write!(f, "*")?,
1336                    (Unbounded, Included(v)) => write!(f, "<={v}")?,
1337                    (Unbounded, Excluded(v)) => write!(f, "<{v}")?,
1338                    (Included(v), Unbounded) => write!(f, ">={v}")?,
1339                    (Included(v), Included(b)) => {
1340                        if v == b {
1341                            write!(f, "=={v}")?
1342                        } else {
1343                            write!(f, ">={v}, <={b}")?
1344                        }
1345                    }
1346                    (Included(v), Excluded(b)) => write!(f, ">={v}, <{b}")?,
1347                    (Excluded(v), Unbounded) => write!(f, ">{v}")?,
1348                    (Excluded(v), Included(b)) => write!(f, ">{v}, <={b}")?,
1349                    (Excluded(v), Excluded(b)) => write!(f, ">{v}, <{b}")?,
1350                };
1351            }
1352        }
1353        Ok(())
1354    }
1355}
1356
1357// SERIALIZATION ###############################################################
1358
1359#[cfg(feature = "serde")]
1360impl<'de, V: serde::Deserialize<'de>> serde::Deserialize<'de> for Ranges<V> {
1361    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
1362        // This enables conversion from the "old" discrete implementation of `Ranges` to the new
1363        // bounded one.
1364        //
1365        // Serialization is always performed in the new format.
1366        #[derive(serde::Deserialize)]
1367        #[serde(untagged)]
1368        enum EitherInterval<V> {
1369            B(Bound<V>, Bound<V>),
1370            D(V, Option<V>),
1371        }
1372
1373        let bounds: SmallVec<[EitherInterval<V>; 2]> =
1374            serde::Deserialize::deserialize(deserializer)?;
1375
1376        let mut segments = SmallVec::new();
1377        for i in bounds {
1378            match i {
1379                EitherInterval::B(l, r) => segments.push((l, r)),
1380                EitherInterval::D(l, Some(r)) => segments.push((Included(l), Excluded(r))),
1381                EitherInterval::D(l, None) => segments.push((Included(l), Unbounded)),
1382            }
1383        }
1384
1385        Ok(Ranges { segments })
1386    }
1387}
1388
1389/// Generate version sets from a random vector of deltas between randomly inclusive or exclusive
1390/// bounds.
1391#[cfg(any(feature = "proptest", test))]
1392pub fn proptest_strategy() -> impl Strategy<Value = Ranges<u32>> {
1393    (
1394        any::<bool>(),
1395        prop::collection::vec(any::<(u32, bool)>(), 0..10),
1396    )
1397        .prop_map(|(start_unbounded, deltas)| {
1398            let mut start = if start_unbounded {
1399                Some(Unbounded)
1400            } else {
1401                None
1402            };
1403            let mut largest: u32 = 0;
1404            let mut last_bound_was_inclusive = false;
1405            let mut segments = SmallVec::new();
1406            for (delta, inclusive) in deltas {
1407                // Add the offset to the current bound
1408                largest = match largest.checked_add(delta) {
1409                    Some(s) => s,
1410                    None => {
1411                        // Skip this offset, if it would result in a too large bound.
1412                        continue;
1413                    }
1414                };
1415
1416                let current_bound = if inclusive {
1417                    Included(largest)
1418                } else {
1419                    Excluded(largest)
1420                };
1421
1422                // If we already have a start bound, the next offset defines the complete range.
1423                // If we don't have a start bound, we have to generate one.
1424                if let Some(start_bound) = start.take() {
1425                    // If the delta from the start bound is 0, the only authorized configuration is
1426                    // Included(x), Included(x)
1427                    if delta == 0 && !(matches!(start_bound, Included(_)) && inclusive) {
1428                        start = Some(start_bound);
1429                        continue;
1430                    }
1431                    last_bound_was_inclusive = inclusive;
1432                    segments.push((start_bound, current_bound));
1433                } else {
1434                    // If the delta from the end bound of the last range is 0 and
1435                    // any of the last ending or current starting bound is inclusive,
1436                    // we skip the delta because they basically overlap.
1437                    if delta == 0 && (last_bound_was_inclusive || inclusive) {
1438                        continue;
1439                    }
1440                    start = Some(current_bound);
1441                }
1442            }
1443
1444            // If we still have a start bound, but didn't have enough deltas to complete another
1445            // segment, we add an unbounded upperbound.
1446            if let Some(start_bound) = start {
1447                segments.push((start_bound, Unbounded));
1448            }
1449
1450            Ranges { segments }.check_invariants()
1451        })
1452}
1453
1454#[cfg(test)]
1455pub mod tests {
1456    use proptest::prelude::*;
1457
1458    use super::*;
1459
1460    fn version_strat() -> impl Strategy<Value = u32> {
1461        any::<u32>()
1462    }
1463
1464    proptest! {
1465
1466        // Testing serde ----------------------------------
1467
1468        #[cfg(feature = "serde")]
1469        #[test]
1470        fn serde_round_trip(range in proptest_strategy()) {
1471            let s = ron::ser::to_string(&range).unwrap();
1472            let r = ron::de::from_str(&s).unwrap();
1473            assert_eq!(range, r);
1474        }
1475
1476        // Testing negate ----------------------------------
1477
1478        #[test]
1479        fn negate_is_different(range in proptest_strategy()) {
1480            assert_ne!(range.complement(), range);
1481        }
1482
1483        #[test]
1484        fn double_negate_is_identity(range in proptest_strategy()) {
1485            assert_eq!(range.complement().complement(), range);
1486        }
1487
1488        #[test]
1489        fn negate_contains_opposite(range in proptest_strategy(), version in version_strat()) {
1490            assert_ne!(range.contains(&version), range.complement().contains(&version));
1491        }
1492
1493        // Testing difference ------------------------------
1494
1495        #[test]
1496        fn difference_is_intersection_with_complement(r1 in proptest_strategy(), r2 in proptest_strategy()) {
1497            assert_eq!(r1.difference(&r2), r1.intersection(&r2.complement()));
1498        }
1499
1500        #[test]
1501        fn difference_contains(r1 in proptest_strategy(), r2 in proptest_strategy(), version in version_strat()) {
1502            assert_eq!(
1503                r1.difference(&r2).contains(&version),
1504                r1.contains(&version) && !r2.contains(&version)
1505            );
1506        }
1507
1508        // Testing intersection ----------------------------
1509
1510        #[test]
1511        fn intersection_is_symmetric(r1 in proptest_strategy(), r2 in proptest_strategy()) {
1512            assert_eq!(r1.intersection(&r2), r2.intersection(&r1));
1513        }
1514
1515        #[test]
1516        fn intersection_with_any_is_identity(range in proptest_strategy()) {
1517            assert_eq!(Ranges::full().intersection(&range), range);
1518        }
1519
1520        #[test]
1521        fn intersection_with_none_is_none(range in proptest_strategy()) {
1522            assert_eq!(Ranges::empty().intersection(&range), Ranges::empty());
1523        }
1524
1525        #[test]
1526        fn intersection_is_idempotent(r1 in proptest_strategy(), r2 in proptest_strategy()) {
1527            assert_eq!(r1.intersection(&r2).intersection(&r2), r1.intersection(&r2));
1528        }
1529
1530        #[test]
1531        fn intersection_is_associative(r1 in proptest_strategy(), r2 in proptest_strategy(), r3 in proptest_strategy()) {
1532            assert_eq!(r1.intersection(&r2).intersection(&r3), r1.intersection(&r2.intersection(&r3)));
1533        }
1534
1535        #[test]
1536        fn intesection_of_complements_is_none(range in proptest_strategy()) {
1537            assert_eq!(range.complement().intersection(&range), Ranges::empty());
1538        }
1539
1540        #[test]
1541        fn intesection_contains_both(r1 in proptest_strategy(), r2 in proptest_strategy(), version in version_strat()) {
1542            assert_eq!(r1.intersection(&r2).contains(&version), r1.contains(&version) && r2.contains(&version));
1543        }
1544
1545        // Testing union -----------------------------------
1546
1547        #[test]
1548        fn union_of_complements_is_any(range in proptest_strategy()) {
1549            assert_eq!(range.complement().union(&range), Ranges::full());
1550        }
1551
1552        #[test]
1553        fn union_contains_either(r1 in proptest_strategy(), r2 in proptest_strategy(), version in version_strat()) {
1554            assert_eq!(r1.union(&r2).contains(&version), r1.contains(&version) || r2.contains(&version));
1555        }
1556
1557        #[test]
1558        fn is_disjoint_through_intersection(r1 in proptest_strategy(), r2 in proptest_strategy()) {
1559            let disjoint_def = r1.intersection(&r2) == Ranges::empty();
1560            assert_eq!(r1.is_disjoint(&r2), disjoint_def);
1561        }
1562
1563        #[test]
1564        fn subset_of_through_intersection(r1 in proptest_strategy(), r2 in proptest_strategy()) {
1565            let disjoint_def = r1.intersection(&r2) == r1;
1566            assert_eq!(r1.subset_of(&r2), disjoint_def);
1567        }
1568
1569        #[test]
1570        fn relation_through_subset_and_disjoint(r1 in proptest_strategy(), r2 in proptest_strategy()) {
1571            let relation_def = if r1.subset_of(&r2) {
1572                SetRelation::Subset
1573            } else if r1.is_disjoint(&r2) {
1574                SetRelation::Disjoint
1575            } else {
1576                SetRelation::Overlapping
1577            };
1578            assert_eq!(r1.relation(&r2), relation_def);
1579        }
1580
1581        #[test]
1582        fn union_through_intersection(r1 in proptest_strategy(), r2 in proptest_strategy()) {
1583            let union_def = r1
1584                .complement()
1585                .intersection(&r2.complement())
1586                .complement()
1587                .check_invariants();
1588            assert_eq!(r1.union(&r2), union_def);
1589        }
1590
1591        // Testing contains --------------------------------
1592
1593        #[test]
1594        fn always_contains_exact(version in version_strat()) {
1595            assert!(Ranges::<u32>::singleton(version).contains(&version));
1596        }
1597
1598        #[test]
1599        fn contains_negation(range in proptest_strategy(), version in version_strat()) {
1600            assert_ne!(range.contains(&version), range.complement().contains(&version));
1601        }
1602
1603        #[test]
1604        fn contains_intersection(range in proptest_strategy(), version in version_strat()) {
1605            assert_eq!(range.contains(&version), range.intersection(&Ranges::singleton(version)) != Ranges::empty());
1606        }
1607
1608        #[test]
1609        fn contains_bounding_range(range in proptest_strategy(), version in version_strat()) {
1610            if range.contains(&version) {
1611                assert!(range.bounding_range().map(|b| b.contains(&version)).unwrap_or(false));
1612            }
1613        }
1614
1615        #[test]
1616        fn from_range_bounds(range in any::<(Bound<u32>, Bound<u32>)>(), version in version_strat()) {
1617            let rv: Ranges<_> = Ranges::<u32>::from_range_bounds(range);
1618            assert_eq!(range.contains(&version), rv.contains(&version));
1619        }
1620
1621        #[test]
1622        fn from_range_bounds_round_trip(range in any::<(Bound<u32>, Bound<u32>)>()) {
1623            let rv: Ranges<u32> = Ranges::from_range_bounds(range);
1624            let rv2: Ranges<u32> = rv.bounding_range().map(Ranges::from_range_bounds::<_, u32>).unwrap_or_else(Ranges::empty);
1625            assert_eq!(rv, rv2);
1626        }
1627
1628        #[test]
1629        fn contains(range in proptest_strategy(), versions in proptest::collection::vec(version_strat(), ..30)) {
1630            for v in versions {
1631                assert_eq!(range.contains(&v), range.segments.iter().any(|s| RangeBounds::contains(s, &v)));
1632            }
1633        }
1634
1635        #[test]
1636        fn contains_many(range in proptest_strategy(), mut versions in proptest::collection::vec(version_strat(), ..30)) {
1637            versions.sort();
1638            assert_eq!(versions.len(), range.contains_many(versions.iter()).count());
1639            for (a, b) in versions.iter().zip(range.contains_many(versions.iter())) {
1640                assert_eq!(range.contains(a), b);
1641            }
1642        }
1643
1644        #[test]
1645        fn simplify(range in proptest_strategy(), mut versions in proptest::collection::vec(version_strat(), ..30)) {
1646            versions.sort();
1647            let simp = range.simplify(versions.iter());
1648
1649            for v in versions {
1650                assert_eq!(range.contains(&v), simp.contains(&v));
1651            }
1652            assert!(simp.segments.len() <= range.segments.len())
1653        }
1654
1655        #[test]
1656        fn widen_versions(range in proptest_strategy(), mut versions in proptest::collection::vec(version_strat(), ..30)) {
1657            versions.sort();
1658            let widened = range.widen_versions(&versions);
1659
1660            // The result is a superset of the input that agrees on all given versions.
1661            assert!(range.subset_of(&widened));
1662            for v in &versions {
1663                assert_eq!(range.contains(v), widened.contains(v));
1664            }
1665            // The operation is idempotent.
1666            assert_eq!(widened.widen_versions(&versions), widened);
1667        }
1668
1669        #[test]
1670        fn narrow_versions(range in proptest_strategy(), mut versions in proptest::collection::vec(version_strat(), ..30)) {
1671            versions.sort();
1672            let narrowed = range.narrow_versions(&versions);
1673
1674            // The result is a subset of the input that agrees on all given versions.
1675            assert!(narrowed.subset_of(&range));
1676            for v in &versions {
1677                assert_eq!(range.contains(v), narrowed.contains(v));
1678            }
1679            // The operation is idempotent.
1680            assert_eq!(narrowed.narrow_versions(&versions), narrowed);
1681            // Narrowing a widened set restores agreement on all given versions.
1682            let round_trip = range.widen_versions(&versions).narrow_versions(&versions);
1683            for v in &versions {
1684                assert_eq!(range.contains(v), round_trip.contains(v));
1685            }
1686        }
1687
1688        #[test]
1689        fn from_iter_valid(segments in proptest::collection::vec(any::<(Bound<u32>, Bound<u32>)>(), ..30)) {
1690            let mut expected = Ranges::empty();
1691            for segment in &segments {
1692                expected = expected.union(&Ranges::from_range_bounds(*segment));
1693            }
1694            let actual =  Ranges::from_iter(segments.clone());
1695            assert_eq!(expected, actual, "{segments:?}");
1696        }
1697    }
1698
1699    #[test]
1700    fn difference_ties_and_singletons() {
1701        fn check(left: Ranges<u32>, right: Ranges<u32>, expected: Ranges<u32>) {
1702            assert_eq!(left.difference(&right), expected, "{left} minus {right}");
1703            assert_eq!(
1704                left.difference(&right),
1705                left.intersection(&right.complement()),
1706                "{left} minus {right}"
1707            );
1708        }
1709
1710        // Touching bounds at a shared version, all four inclusivity combinations.
1711        check(
1712            Ranges::from_range_bounds(1u32..=5),
1713            Ranges::from_range_bounds(5u32..=9),
1714            Ranges::from_range_bounds(1u32..5),
1715        );
1716        check(
1717            Ranges::from_range_bounds(1u32..5),
1718            Ranges::from_range_bounds(5u32..=9),
1719            Ranges::from_range_bounds(1u32..5),
1720        );
1721        check(
1722            Ranges::from_range_bounds(1u32..=5),
1723            Ranges::from_range_bounds((Excluded(5u32), Included(9u32))),
1724            Ranges::from_range_bounds(1u32..=5),
1725        );
1726        check(
1727            Ranges::from_range_bounds(1u32..5),
1728            Ranges::from_range_bounds((Excluded(5u32), Excluded(9u32))),
1729            Ranges::from_range_bounds(1u32..5),
1730        );
1731
1732        // Singleton operands.
1733        check(
1734            Ranges::singleton(3u32),
1735            Ranges::from_range_bounds(1u32..=3),
1736            Ranges::empty(),
1737        );
1738        check(
1739            Ranges::from_range_bounds(1u32..=5),
1740            Ranges::singleton(3u32),
1741            Ranges::from_range_bounds(1u32..3)
1742                .union(&Ranges::from_range_bounds((Excluded(3u32), Included(5u32)))),
1743        );
1744
1745        // The singleton gap between two excluded-bound right segments survives.
1746        check(
1747            Ranges::from_range_bounds(0u32..=10),
1748            Ranges::from_range_bounds((Excluded(2u32), Excluded(4u32)))
1749                .union(&Ranges::from_range_bounds((Excluded(4u32), Excluded(6u32)))),
1750            Ranges::from_range_bounds(0u32..=2)
1751                .union(&Ranges::singleton(4u32))
1752                .union(&Ranges::from_range_bounds(6u32..=10)),
1753        );
1754
1755        // An unbounded-above right segment covers every later left segment.
1756        check(
1757            Ranges::from_range_bounds(0u32..=1)
1758                .union(&Ranges::from_range_bounds(5u32..=6))
1759                .union(&Ranges::from_range_bounds(8u32..=9)),
1760            Ranges::higher_than(5u32),
1761            Ranges::from_range_bounds(0u32..=1),
1762        );
1763
1764        // Empty and full operands.
1765        check(Ranges::full(), Ranges::empty(), Ranges::full());
1766        check(Ranges::empty(), Ranges::full(), Ranges::empty());
1767        check(Ranges::full(), Ranges::full(), Ranges::empty());
1768    }
1769
1770    #[test]
1771    fn contains_many_can_take_owned() {
1772        let range: Ranges<u8> = Ranges::singleton(1);
1773        let versions = vec![1, 2, 3];
1774        // Check that iter can be a Cow
1775        assert_eq!(
1776            range.contains_many(versions.iter()).count(),
1777            range
1778                .contains_many(versions.iter().map(std::borrow::Cow::Borrowed))
1779                .count()
1780        );
1781        // Check that iter can be a V
1782        assert_eq!(
1783            range.contains_many(versions.iter()).count(),
1784            range.contains_many(versions.into_iter()).count()
1785        );
1786    }
1787
1788    #[test]
1789    fn contains_can_take_owned() {
1790        let range: Ranges<Box<u8>> = Ranges::singleton(1);
1791        let version = 1;
1792
1793        assert_eq!(range.contains(&Box::new(version)), range.contains(&version));
1794        let range: Ranges<String> = Ranges::singleton(1.to_string());
1795        let version = 1.to_string();
1796        assert_eq!(range.contains(&version), range.contains("1"));
1797    }
1798
1799    #[test]
1800    fn widen_versions_extends_to_neighboring_versions() {
1801        let versions = [1u32, 2, 3, 5, 9];
1802        // A singleton widens up to, and excluding, the neighboring versions.
1803        assert_eq!(
1804            Ranges::singleton(3u32).widen_versions(&versions),
1805            Ranges::from_range_bounds((Excluded(2u32), Excluded(5u32)))
1806        );
1807        // Without a version above, the segment becomes unbounded.
1808        assert_eq!(
1809            Ranges::singleton(9u32).widen_versions(&versions),
1810            Ranges::strictly_higher_than(5u32)
1811        );
1812        // The union of singletons of adjacent versions merges into a single segment.
1813        let range: Ranges<u32> = Ranges::singleton(2u32).union(&Ranges::singleton(3u32));
1814        assert_eq!(
1815            range.widen_versions(&versions),
1816            Ranges::from_range_bounds((Excluded(1u32), Excluded(5u32)))
1817        );
1818        // A version separating two segments is preserved.
1819        let range: Ranges<u32> = Ranges::singleton(1u32).union(&Ranges::singleton(3u32));
1820        assert_eq!(
1821            range.widen_versions(&versions),
1822            Ranges::strictly_lower_than(2u32)
1823                .union(&Ranges::from_range_bounds((Excluded(2u32), Excluded(5u32))))
1824        );
1825    }
1826
1827    #[test]
1828    fn narrow_versions_shrinks_to_contained_versions() {
1829        let versions = [1u32, 2, 3, 5, 9];
1830        // A segment shrinks to the versions it contains, with inclusive bounds.
1831        assert_eq!(
1832            Ranges::from_range_bounds((Excluded(2u32), Excluded(5u32))).narrow_versions(&versions),
1833            Ranges::singleton(3u32)
1834        );
1835        // Unbounded ends are kept, only the bounded end shrinks.
1836        assert_eq!(
1837            Ranges::strictly_higher_than(2u32).narrow_versions(&versions),
1838            Ranges::higher_than(3u32)
1839        );
1840        assert_eq!(
1841            Ranges::<u32>::full().narrow_versions(&versions),
1842            Ranges::full()
1843        );
1844        // A segment containing no version is kept unchanged.
1845        let range = Ranges::from_range_bounds((Excluded(5u32), Excluded(9u32)));
1846        assert_eq!(range.narrow_versions(&versions), range);
1847    }
1848
1849    #[test]
1850    fn simplify_can_take_owned() {
1851        let range: Ranges<u8> = Ranges::singleton(1);
1852        let versions = vec![1, 2, 3];
1853        // Check that iter can be a Cow
1854        assert_eq!(
1855            range.simplify(versions.iter()),
1856            range.simplify(versions.iter().map(std::borrow::Cow::Borrowed))
1857        );
1858        // Check that iter can be a V
1859        assert_eq!(
1860            range.simplify(versions.iter()),
1861            range.simplify(versions.into_iter())
1862        );
1863    }
1864
1865    #[test]
1866    fn version_ord() {
1867        let versions: &[Ranges<u32>] = &[
1868            Ranges::strictly_lower_than(1u32),
1869            Ranges::lower_than(1u32),
1870            Ranges::singleton(1u32),
1871            Ranges::between(1u32, 3u32),
1872            Ranges::higher_than(1u32),
1873            Ranges::strictly_higher_than(1u32),
1874            Ranges::singleton(2u32),
1875            Ranges::singleton(2u32).union(&Ranges::singleton(3u32)),
1876            Ranges::singleton(2u32)
1877                .union(&Ranges::singleton(3u32))
1878                .union(&Ranges::singleton(4u32)),
1879            Ranges::singleton(2u32).union(&Ranges::singleton(4u32)),
1880            Ranges::singleton(3u32),
1881        ];
1882
1883        let mut versions_sorted = versions.to_vec();
1884        versions_sorted.sort();
1885        assert_eq!(versions_sorted, versions);
1886
1887        // Check that the sorting isn't just stable because we're returning equal.
1888        let mut version_reverse_sorted = versions.to_vec();
1889        version_reverse_sorted.reverse();
1890        version_reverse_sorted.sort();
1891        assert_eq!(version_reverse_sorted, versions);
1892    }
1893}