cycle_sort/
cycle_sort.rs

1#![deny(missing_docs)]
2
3use core::cmp::Ordering;
4use core::mem::{self, ManuallyDrop};
5use core::ptr;
6
7use crate::util;
8
9/// Sorts a slice using the elements' natural ordering and returns the
10/// number of writes made.
11///
12/// # Examples
13///
14/// ```
15/// # use cycle_sort::cycle_sort;
16/// let mut a = [1, 4, 1, 5, 9, 2];
17/// let     w = cycle_sort(&mut a);
18///
19/// assert_eq!(a, [1, 1, 2, 4, 5, 9]);
20/// assert_eq!(w, 5);
21/// ```
22#[inline]
23pub fn cycle_sort<T>(slice: &mut [T]) -> usize
24where
25    T: Ord,
26{
27    cycle_impl(slice, &|a, b| a.lt(b))
28}
29
30/// Sorts a slice using a comparator function and returns the number of
31/// writes made.
32///
33/// # Examples
34///
35/// ```
36/// # use cycle_sort::cycle_sort_by;
37/// // reverse sorting
38/// let mut a = ["davidii", "demissa", "deltoidea", "decapetala", "dahurica"];
39/// let     w = cycle_sort_by(&mut a, &|a, b| b.cmp(&a));
40///
41/// assert_eq!(a, ["demissa", "deltoidea", "decapetala", "davidii", "dahurica"]);
42/// assert_eq!(w, 4);
43/// ```
44#[inline]
45pub fn cycle_sort_by<T, F>(slice: &mut [T], compare: &F) -> usize
46where
47    F: Fn(&T, &T) -> Ordering,
48{
49    cycle_impl(slice, &|a, b| compare(a, b) == Ordering::Less)
50}
51
52/// Sorts a slice with a key extraction function and returns the number of
53/// writes made.
54///
55/// # Examples
56///
57/// ```
58/// # use cycle_sort::cycle_sort_by_key;
59/// // sort by length
60/// let mut a = ["zwölf", "zzxjoanw", "zymbel"];
61/// let     w = cycle_sort_by_key(&mut a, &|s| s.len());
62///
63/// assert_eq!(a, ["zwölf", "zymbel", "zzxjoanw"]);
64/// assert_eq!(w, 2);
65/// ```
66#[inline]
67pub fn cycle_sort_by_key<T, F, U>(slice: &mut [T], key: &F) -> usize
68where
69    F: Fn(&T) -> U,
70    U: Ord,
71{
72    cycle_impl(slice, &|a, b| key(a).lt(&key(b)))
73}
74
75fn cycle_impl<T, F>(slice: &mut [T], is_less: &F) -> usize
76where
77    F: Fn(&T, &T) -> bool,
78{
79    let length = slice.len();
80
81    // check if sorting is necessary
82    if mem::size_of::<T>() == 0 || length < 2 {
83        return 0;
84    }
85
86    let mut writes = 0;
87
88    for src in 0..length - 1 {
89        let mut tmp = unsafe { ManuallyDrop::new(ptr::read(&slice[src])) };
90        let mut dst = src;
91
92        // count number of elements in `slice[src..]` strictly less than `tmp`
93        for i in src + 1..length {
94            if is_less(&slice[i], &tmp) {
95                dst += 1;
96            }
97        }
98
99        // tmp is in correct position, nothing to do
100        if dst == src {
101            continue;
102        }
103
104        // place `tmp` after any possible duplicates
105        while util::are_equal(&*tmp, &slice[dst], is_less) {
106            dst += 1;
107        }
108
109        // put `tmp` into correct position
110        mem::swap(&mut *tmp, &mut slice[dst]);
111        writes += 1;
112
113        // find correct position for whatever element was in `tmp`'s position
114        // and loop until we're back at `tmp`'s original position
115        while dst != src {
116            dst = src;
117
118            for i in src + 1..length {
119                if is_less(&slice[i], &tmp) {
120                    dst += 1;
121                }
122            }
123
124            while util::are_equal(&*tmp, &slice[dst], is_less) {
125                dst += 1;
126            }
127
128            mem::swap(&mut *tmp, &mut slice[dst]);
129            writes += 1;
130        }
131    }
132
133    writes
134}
135
136#[cfg(test)]
137mod tests {
138    use crate::cycle_sort;
139
140    extern crate std;
141    use std::string::String;
142    use std::vec::Vec;
143
144    use rand::{distributions::Alphanumeric, seq::SliceRandom, thread_rng, Rng};
145
146    macro_rules! assert_sorted {
147        ($x:expr) => {
148            assert!($x.windows(2).all(|w| w[0] <= w[1]))
149        };
150    }
151
152    #[test]
153    fn zero_sized_elements() {
154        const SIZE: usize = 1100;
155
156        let mut array = [(); SIZE];
157
158        for length in (0..10).chain(1000..SIZE + 1) {
159            let mut slice = &mut array[..length];
160            let writes = cycle_sort(&mut slice);
161
162            assert_eq!(writes, 0);
163        }
164    }
165
166    #[test]
167    fn basic_sort() {
168        const SIZE: usize = 110;
169
170        let mut array = [0_i32; SIZE];
171        let mut rng = thread_rng();
172
173        for length in (0..20).chain(100..SIZE + 1) {
174            let mut slice = &mut array[..length];
175
176            for _ in 0..10 {
177                rng.fill(slice);
178                cycle_sort(&mut slice);
179
180                assert_sorted!(slice);
181            }
182        }
183    }
184
185    #[test]
186    fn sort_strings() {
187        const SIZE: usize = 128;
188        const LENGTH: usize = 128;
189
190        let mut rng = thread_rng();
191        let mut vec: Vec<String> = Vec::with_capacity(SIZE);
192
193        for _ in 0..10 {
194            vec.clear();
195
196            // generate `SIZE` strings of length `LENGTH` with random `char`s
197            for _ in 0..SIZE {
198                vec.push(rng.sample_iter(&Alphanumeric).take(LENGTH).collect());
199            }
200
201            // shuffle and sort strings
202            vec.as_mut_slice().shuffle(&mut rng);
203            cycle_sort(vec.as_mut_slice());
204
205            assert_sorted!(vec.as_slice());
206        }
207    }
208
209    #[test]
210    fn many_duplicates() {
211        const SIZE: usize = 100;
212
213        let mut array = [0_u8; SIZE];
214        let mut rng = thread_rng();
215
216        for length in 80..SIZE {
217            let mut slice = &mut array[..length];
218
219            for divisor in &[11, 13, 17, 19] {
220                for _ in 0..10 {
221                    rng.fill(slice);
222                    for x in slice.iter_mut() {
223                        *x %= divisor;
224                    }
225
226                    cycle_sort(&mut slice);
227
228                    assert_sorted!(slice);
229                }
230            }
231        }
232    }
233
234    #[test]
235    fn correct_writes() {
236        const SIZE: usize = 25;
237
238        let mut array = [0; SIZE];
239
240        for i in 0..SIZE {
241            array[i] = i;
242        }
243
244        let mut rng = thread_rng();
245
246        for length in 1..SIZE + 1 {
247            let mut slice = &mut array[..length];
248
249            for _ in 0..10 {
250                slice.shuffle(&mut rng);
251
252                let expect = slice.iter().enumerate().filter(|&(i, v)| i != *v).count();
253                let writes = cycle_sort(&mut slice);
254
255                assert_sorted!(slice);
256                assert_eq!(writes, expect);
257            }
258        }
259    }
260}