index_sort/
lib.rs

1//! Generic sorting functions
2//!
3//! Provides generic implementations of basic sorting functions.  Because Rust's build in sorting
4//! is only available on slices and some data structures are not representable using slices, the
5//! following sorting functions operate on a generic indexable container.
6//!
7//! # Examples
8//!
9//! Sorting a vector
10//! ```
11//! use index_sort::merge_sort;
12//! let mut v : Vec<i32> = (0..1000).rev().collect();
13//! let rng = 0..v.len();
14//! merge_sort(&mut v[..], rng);
15//! ```
16//!
17//! Sorting a pair of vectors lexicographically
18//! ```
19//! use index_sort::quick_sort;
20//! let mut v1 : Vec<i32> = vec![5, 2, 1, 3, 6, 3];
21//! let mut v2 : Vec<i32> = vec![1, 4, 2, 5, 7, 0];
22//! let rng = 0..v1.len();
23//! let mut v = (v1, v2);
24//! quick_sort(&mut v, rng);
25//! ```
26//!
27//! This crate defines generic sorting algorithms that are not tied to Rust slices.  Instead sort
28//! is defined on types that provide swap and compare functions with integer indices.  The genesis
29//! of this crate was a need to lexicographically sort tuples of made of matched length vectors.
30//! Such a data structure cannot be sorted with standard Rust sort functions without creating a
31//! permutation vector as an intermediate step.
32//!
33//!
34
35use std::{cmp::Ordering, ops::Range};
36
37/// Sortable is defined for types that should be allowed to be sorted.
38pub trait Sortable {
39    /// exchanges the data at index `i` with the data at index `j`
40    fn swap(&mut self, i: usize, j: usize);
41
42    /// compares the data at index `i` with the data at index `j`
43    fn compare(&self, i: usize, j: usize) -> Ordering;
44}
45
46/// Slices of ordered elements are sortable
47impl<T: Ord> Sortable for [T] {
48    fn swap(&mut self, i: usize, j: usize) {
49        (*self).swap(i, j);
50    }
51
52    fn compare(&self, i: usize, j: usize) -> Ordering {
53        self[i].cmp(&self[j])
54    }
55}
56
57/// Vectors of ordered elements are sortable
58impl <T: Ord> Sortable for Vec<T> {
59    fn swap(&mut self, i: usize, j: usize) {
60        self[..].swap(i, j)
61    }
62    fn compare(&self, i: usize, j: usize) -> Ordering {
63        self[i].cmp(&self[j])
64    }
65}
66
67/// Tuples of sortable elements are sortable within their common range
68impl<S: Sortable, T: Sortable> Sortable for (S, T) {
69    fn swap(&mut self, i: usize, j: usize) {
70        self.0.swap(i, j);
71        self.1.swap(i, j);
72    }
73
74    fn compare(&self, i: usize, j: usize) -> Ordering {
75        let res = self.0.compare(i, j);
76        if res != Ordering::Equal {
77            return res;
78        }
79
80        self.1.compare(i, j)
81    }
82}
83
84/// sort the container in the specified range using the insertion sort algorithm
85pub fn insertion_sort<T>(container: &mut T, range: Range<usize>)
86where
87    T: Sortable + ?Sized,
88{
89    let mut i = range.start + 1;
90    while i < range.end {
91        let mut j = i;
92        while j > range.start && container.compare(j - 1, j) == Ordering::Greater {
93            container.swap(j, j - 1);
94            j -= 1;
95        }
96        i += 1;
97    }
98}
99
100fn lower_bound(container: &(impl Sortable + ?Sized), range: Range<usize>, pos: usize) -> usize {
101    let mut from = range.start;
102    let mut len = range.len();
103    while len > 0 {
104        let half = len / 2;
105        let middle = from + half;
106        if container.compare(middle, pos) == Ordering::Less {
107            from = middle + 1;
108            len -= half + 1;
109        } else {
110            len = half;
111        }
112    }
113    from
114}
115
116fn upper_bound(container: &(impl Sortable + ?Sized), range: Range<usize>, pos: usize) -> usize {
117    let mut from = range.start;
118    let mut len = range.len();
119    while len > 0 {
120        let half = len / 2;
121        let middle = from + half;
122        if container.compare(pos, middle) == Ordering::Less {
123            len = half;
124        } else {
125            from = middle + 1;
126            len -= half + 1;
127        }
128    }
129    from
130}
131
132const MERGESORT_NO_REC: usize = 16;
133
134fn in_place_merge(
135    container: &mut (impl Sortable + ?Sized),
136    from: usize,
137    mut mid: usize,
138    to: usize,
139) {
140    if from >= mid || mid >= to {
141        return;
142    }
143    if to - from == 2 {
144        if container.compare(mid, from) == Ordering::Less {
145            container.swap(from, mid)
146        }
147        return;
148    }
149
150    let first_cut;
151    let second_cut;
152
153    if mid - from > to - mid {
154        first_cut = from + (mid - from) / 2;
155        second_cut = lower_bound(container, mid..to, first_cut);
156    } else {
157        second_cut = mid + (to - mid) / 2;
158        first_cut = upper_bound(container, from..mid, second_cut);
159    }
160
161    let first2 = first_cut;
162    let middle2 = mid;
163    let last2 = second_cut;
164    if middle2 != first2 && middle2 != last2 {
165        let mut first1 = first2;
166        let mut last1 = middle2 - 1;
167        while first1 < last1 {
168            container.swap(first1, last1);
169            first1 += 1;
170            last1 -= 1;
171        }
172        first1 = middle2;
173        last1 = last2 - 1;
174        while first1 < last1 {
175            container.swap(first1, last1);
176            first1 += 1;
177            last1 -= 1;
178        }
179        first1 = first2;
180        last1 = last2 - 1;
181        while first1 < last1 {
182            container.swap(first1, last1);
183            first1 += 1;
184            last1 -= 1;
185        }
186    }
187
188    mid = first_cut + (second_cut - mid);
189    in_place_merge(container, from, first_cut, mid);
190    in_place_merge(container, mid, second_cut, to);
191}
192
193/// sort the container in the specified range using the merge sort algorithm
194pub fn merge_sort<T>(container: &mut T, range: Range<usize>)
195where
196    T: Sortable + ?Sized,
197{
198    let length = range.len();
199
200    // Insertion sort on smallest arrays
201    if length < MERGESORT_NO_REC {
202        insertion_sort(container, range);
203        return;
204    }
205
206    // Recursively sort halves
207    let mid = range.start + length / 2;
208    merge_sort(container, range.start..mid);
209    merge_sort(container, mid..range.end);
210
211    // If list is already sorted, nothing left to do. This is an
212    // optimization that results in faster sorts for nearly ordered lists.
213    if container.compare(mid - 1, mid) != Ordering::Greater {
214        return;
215    }
216
217    // Merge sorted halves
218    in_place_merge(container, range.start, mid, range.end);
219}
220
221/// Helper modified from a servo library: https://github.com/servo/rust-quicksort/blob/master/lib.rs
222/// Servo library was copied from https://sedgewick.io/wp-content/uploads/2022/03/2002QuicksortIsOptimal.pdf
223fn qs_helper<T>(container: &mut T, left: isize, right: isize)
224where
225    T: Sortable + ?Sized,
226{
227    if right <= left {
228        return;
229    }
230
231    // if range is small, use insertion sort instead
232    if right - left >= 20 {
233        insertion_sort(container, (left as usize)..(right as usize + 1));
234        return;
235    }
236
237    // pick pivot
238    {
239        let mid = left + (right - left)/2;
240        let pivot = if let Ordering::Equal | Ordering::Less = container.compare(left as usize, mid as usize) {
241            if let Ordering::Equal | Ordering::Less = container.compare(mid as usize, right as usize) {
242                // left mid right
243                mid
244            } else {
245                if let Ordering::Equal | Ordering::Less = container.compare(left as usize, right as usize) {
246                    // left right mid
247                    right
248                } else {
249                    // right left mid
250                    left
251                }
252            }
253        } else {
254            if let Ordering::Equal | Ordering::Less = container.compare(mid as usize, right as usize) {
255                if let Ordering::Equal | Ordering::Less = container.compare(left as usize, right as usize) {
256                    // mid left right
257                    left
258                } else {
259                    // mid right left
260                    right
261                }
262            } else {
263                // right mid left
264                mid
265            }
266        };
267
268        // swap pivot and right element
269        if pivot != right {
270            container.swap(pivot as usize, right as usize);
271        }
272    }
273
274    let mut i = left - 1;
275    let mut j = right;
276    let mut p = i;
277    let mut q = j;
278
279    let v = right;
280
281    loop {
282        i += 1;
283        while container.compare(i as usize, v as usize) == Ordering::Less {
284            i += 1
285        }
286
287        j -= 1;
288        while container.compare(v as usize, j as usize) == Ordering::Less {
289            if j == left {
290                break;
291            }
292            j -= 1;
293        }
294
295        if i >= j {
296            break;
297        }
298
299        container.swap(i as usize, j as usize);
300        if container.compare(i as usize, v as usize) == Ordering::Equal {
301            p += 1;
302            container.swap(p as usize, i as usize);
303        }
304        if container.compare(v as usize, j as usize) == Ordering::Equal {
305            q -= 1;
306            container.swap(j as usize, q as usize);
307        }
308    }
309
310    container.swap(i as usize, right as usize);
311    j = i - 1;
312    i += 1;
313    let mut k = left;
314    while k < p {
315        container.swap(k as usize, j as usize);
316        k += 1;
317        j -= 1;
318    }
319
320    k = right - 1;
321    while k > q {
322        container.swap(i as usize, k as usize);
323        k -= 1;
324        i += 1;
325    }
326
327    qs_helper(container, left, j);
328    qs_helper(container, i, right);
329}
330
331/// sort the container in the specified range using the quicksort algorithm
332pub fn quick_sort<T>(container: &mut T, range: Range<usize>)
333where
334    T: Sortable + ?Sized,
335{
336    qs_helper(container, range.start as isize, range.end as isize - 1);
337}
338
339#[cfg(test)]
340mod tests {
341
342    fn ordered_vec(n: i32) -> Vec<i32> {
343        (0..n).collect()
344    }
345
346    fn rev_ordered_vec(n: i32) -> Vec<i32> {
347        (0..n).into_iter().rev().collect()
348    }
349
350    fn alternating_vec(n: i32) -> Vec<i32> {
351        let mut res = Vec::new();
352        for i in 0..n {
353            res.push(2 * i);
354        }
355        for i in 0..n {
356            res.push(2 * i + 1);
357        }
358        res
359    }
360
361    fn low_center_vec(n: i32) -> Vec<i32> {
362        let mut res = Vec::new();
363        res.extend((0..n).into_iter().rev());
364        res.extend((0..n).into_iter());
365        res
366    }
367
368    fn vectors() -> Vec<Vec<i32>> {
369        let mut result = Vec::new();
370        for i in [10i32, 20, 30, 50, 100, 500, 1000] {
371            result.push(ordered_vec(i));
372            result.push(rev_ordered_vec(i));
373            result.push(alternating_vec(i));
374            result.push(low_center_vec(i));
375        }
376        result
377    }
378
379    fn is_sorted<T: Ord>(v: impl AsRef<[T]>) -> bool {
380        v.as_ref()
381            .iter()
382            .zip(v.as_ref().iter().skip(1))
383            .all(|(a, b)| a <= b)
384    }
385
386    #[test]
387    fn quicksort_vectors() {
388        for mut v in vectors() {
389            let rng = 0..v.len();
390            super::quick_sort(v.as_mut_slice(), rng);
391            assert!(is_sorted(v));
392        }
393    }
394
395    #[test]
396    fn mergesort_vectors() {
397        for mut v in vectors() {
398            let rng = 0..v.len();
399            super::merge_sort(v.as_mut_slice(), rng);
400            assert!(is_sorted(v));
401        }
402    }
403
404    #[test]
405    fn insertionsort_vectors() {
406        for mut v in vectors() {
407            let rng = 0..v.len();
408            super::insertion_sort(v.as_mut_slice(), rng);
409            assert!(is_sorted(v));
410        }
411    }
412}