├── .gitignore
├── .travis.yml
├── Makefile
├── README.md
├── UNLICENSE
├── cargo-lite.conf
├── ctags.rust
├── session.vim
└── src
├── bench.rs
├── lib.rs
└── test.rs
/.gitignore:
--------------------------------------------------------------------------------
1 | doc
2 | .*.swp
3 | *.rlib
4 | sort-test
5 | tags
6 | bench.json
7 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: c
2 | before_install:
3 | - yes | sudo add-apt-repository ppa:hansjorg/rust
4 | - sudo apt-get update
5 | install:
6 | - sudo apt-get install rust-nightly
7 | script:
8 | - git clone git://github.com/BurntSushi/quickcheck
9 | && rustc --crate-type lib ./quickcheck/src/lib.rs
10 | && rustc -L . --crate-type lib ./src/lib.rs
11 |
12 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | RUST_CFG ?= --cfg sorted --cfg same --cfg micro --cfg small --cfg medium --cfg large
2 | RUST_PATH ?= $(HOME)/.rust/lib/x86_64-unknown-linux-gnu
3 |
4 | compile:
5 | rustc -L $(RUST_PATH) ./src/lib.rs
6 |
7 | install:
8 | cargo-lite install
9 |
10 | tags:
11 | ctags --recurse --options=ctags.rust --languages=Rust
12 |
13 | docs:
14 | rm -rf doc
15 | rustdoc src/lib.rs
16 | # WTF is rustdoc doing?
17 | chmod 755 doc
18 | in-dir doc fix-perms
19 | rscp ./doc/* gopher:~/www/burntsushi.net/rustdoc/
20 |
21 | test: sort-test
22 | RUST_TEST_TASKS=1 RUST_LOG=quickcheck,sorts ./sort-test
23 |
24 | sort-test: src/lib.rs src/test.rs src/bench.rs
25 | rustc -L $(RUST_PATH) -O --test src/lib.rs -o sort-test
26 |
27 | bench: bench-test
28 | RUST_TEST_TASKS=1 RUST_LOG=quickcheck,sorts ./sort-test --bench --save-metrics=bench.json
29 |
30 | bench-test: src/lib.rs src/test.rs src/bench.rs
31 | rustc -L $(RUST_PATH) -O --test $(RUST_CFG) src/lib.rs -o sort-test
32 |
33 | test-clean:
34 | rm -rf ./sort-test
35 |
36 | clean: test-clean
37 | rm -f *.rlib
38 |
39 | push:
40 | git push origin master
41 | git push github master
42 |
43 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | **Deprecated:** This is pre-rust 1.0 code. A majority of it will not compile or
2 | otherwise work in modern Rust. It is kept here for legacy but is not recommended
3 | to learn from or use.
4 |
5 | A comparison of different sort algorithms in Rust. This includes mergesort,
6 | quicksort, heapsort, insertion sort, selection sort, bubble sort and even bogo
7 | sort. The library comes with benchmarks for vectors of different sizes and for
8 | vectors that are already sorted or vectors where all elements are equivalent.
9 | This library also comes with QuickCheck tests that check whether the result of
10 | a sorting algorithm is sorted. Each algorithm is also checked that it is
11 | stable/unstable depending on the algorithm used.
12 |
13 | There is some documentation of the API: http://burntsushi.net/rustdoc/sorts
14 |
15 | Many of the implementations were done with inspiration from the relevant
16 | Wikipedia articles.
17 |
18 | Tests can be run with `make test`. Benchmarks can be run with `make bench`.
19 | Since they can take a long time to run, here are all benchmarks on my machine.
20 | My specs: Intel i3930K (12 threads) with 32GB of memory. Compiled with `-O`.
21 |
22 | Note that sorting algorithms with average case `O(n^2)` or worse complexity are
23 | not benchmarked on the `medium` and `large` sizes.
24 |
25 | The `std` sort is the one from Rust's standard library.
26 |
27 | ```
28 | micro_bubble 119 ns/iter (+/- 22)
29 | micro_heapsort_down 169 ns/iter (+/- 16)
30 | micro_heapsort_up 173 ns/iter (+/- 13)
31 | micro_insertion 124 ns/iter (+/- 33)
32 | micro_mergesort 282 ns/iter (+/- 7)
33 | micro_mergesort_insertion 184 ns/iter (+/- 40)
34 | micro_quicksort_dumb 235 ns/iter (+/- 63)
35 | micro_quicksort_insertion 129 ns/iter (+/- 21)
36 | micro_quicksort_smart 234 ns/iter (+/- 31)
37 | micro_selection 133 ns/iter (+/- 9)
38 | micro_std 121 ns/iter (+/- 36)
39 | small_bubble 11306 ns/iter (+/- 1622)
40 | small_heapsort_down 2782 ns/iter (+/- 101)
41 | small_heapsort_up 2790 ns/iter (+/- 99)
42 | small_insertion 6396 ns/iter (+/- 964)
43 | small_mergesort 3263 ns/iter (+/- 99)
44 | small_mergesort_insertion 2521 ns/iter (+/- 192)
45 | small_quicksort_dumb 2679 ns/iter (+/- 264)
46 | small_quicksort_insertion 2454 ns/iter (+/- 263)
47 | small_quicksort_smart 2627 ns/iter (+/- 213)
48 | small_selection 6436 ns/iter (+/- 354)
49 | small_std 2370 ns/iter (+/- 198)
50 | medium_heapsort_down 664567 ns/iter (+/- 3372)
51 | medium_heapsort_up 708254 ns/iter (+/- 2642)
52 | medium_mergesort 982694 ns/iter (+/- 3843)
53 | medium_mergesort_insertion 886573 ns/iter (+/- 4352)
54 | medium_quicksort_dumb 686721 ns/iter (+/- 24446)
55 | medium_quicksort_insertion 678892 ns/iter (+/- 15685)
56 | medium_quicksort_smart 690518 ns/iter (+/- 18175)
57 | medium_std 494699 ns/iter (+/- 1971)
58 | large_heapsort_down 10174136 ns/iter (+/- 187066)
59 | large_heapsort_up 10659817 ns/iter (+/- 134254)
60 | large_mergesort 12287202 ns/iter (+/- 53687)
61 | large_mergesort_insertion 11349007 ns/iter (+/- 37731)
62 | large_quicksort_dumb 8286579 ns/iter (+/- 163110)
63 | large_quicksort_insertion 8223799 ns/iter (+/- 188353)
64 | large_quicksort_smart 8307287 ns/iter (+/- 154722)
65 | large_std 6113112 ns/iter (+/- 23370)
66 | same_bogo 3883 ns/iter (+/- 6)
67 | same_bubble 4394 ns/iter (+/- 8)
68 | same_heapsort_down 10125 ns/iter (+/- 14)
69 | same_heapsort_up 11256 ns/iter (+/- 499)
70 | same_insertion 4923 ns/iter (+/- 8)
71 | same_mergesort 37884 ns/iter (+/- 575)
72 | same_mergesort_insertion 25502 ns/iter (+/- 45)
73 | same_quicksort_dumb 693800 ns/iter (+/- 1310)
74 | same_quicksort_insertion 695169 ns/iter (+/- 1461)
75 | same_quicksort_smart 695203 ns/iter (+/- 932)
76 | same_selection 536685 ns/iter (+/- 1853)
77 | same_std 15378 ns/iter (+/- 45)
78 | sorted_bogo 3982 ns/iter (+/- 13)
79 | sorted_bubble 4396 ns/iter (+/- 77)
80 | sorted_heapsort_down 44740 ns/iter (+/- 1003)
81 | sorted_heapsort_up 50721 ns/iter (+/- 150)
82 | sorted_insertion 4881 ns/iter (+/- 15)
83 | sorted_mergesort 37888 ns/iter (+/- 1429)
84 | sorted_mergesort_insertion 25596 ns/iter (+/- 64)
85 | sorted_quicksort_dumb 561639 ns/iter (+/- 1818)
86 | sorted_quicksort_insertion 25458 ns/iter (+/- 92)
87 | sorted_quicksort_smart 25330 ns/iter (+/- 122)
88 | sorted_selection 536804 ns/iter (+/- 2146)
89 | sorted_std 15362 ns/iter (+/- 66)
90 | ```
91 |
92 |
--------------------------------------------------------------------------------
/UNLICENSE:
--------------------------------------------------------------------------------
1 | This is free and unencumbered software released into the public domain.
2 |
3 | Anyone is free to copy, modify, publish, use, compile, sell, or
4 | distribute this software, either in source code form or as a compiled
5 | binary, for any purpose, commercial or non-commercial, and by any
6 | means.
7 |
8 | In jurisdictions that recognize copyright laws, the author or authors
9 | of this software dedicate any and all copyright interest in the
10 | software to the public domain. We make this dedication for the benefit
11 | of the public at large and to the detriment of our heirs and
12 | successors. We intend this dedication to be an overt act of
13 | relinquishment in perpetuity of all present and future rights to this
14 | software under copyright law.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 | OTHER DEALINGS IN THE SOFTWARE.
23 |
24 | For more information, please refer to
25 |
--------------------------------------------------------------------------------
/cargo-lite.conf:
--------------------------------------------------------------------------------
1 | deps = [
2 | ["--git", "http://github.com/BurntSushi/quickcheck"],
3 | ]
4 |
5 | [build]
6 | crate_root = "src/lib.rs"
7 | crate_type = "library"
8 |
9 |
--------------------------------------------------------------------------------
/ctags.rust:
--------------------------------------------------------------------------------
1 | --langdef=Rust
2 | --langmap=Rust:.rs
3 | --regex-Rust=/^[ \t]*(#\[[^\]]\][ \t]*)*(pub[ \t]+)?(extern[ \t]+)?("[^"]+"[ \t]+)?(unsafe[ \t]+)?fn[ \t]+([a-zA-Z0-9_]+)/\6/f,functions,function definitions/
4 | --regex-Rust=/^[ \t]*(pub[ \t]+)?type[ \t]+([a-zA-Z0-9_]+)/\2/T,types,type definitions/
5 | --regex-Rust=/^[ \t]*(pub[ \t]+)?enum[ \t]+([a-zA-Z0-9_]+)/\2/g,enum,enumeration names/
6 | --regex-Rust=/^[ \t]*(pub[ \t]+)?struct[ \t]+([a-zA-Z0-9_]+)/\2/s,structure names/
7 | --regex-Rust=/^[ \t]*(pub[ \t]+)?mod[ \t]+([a-zA-Z0-9_]+)/\2/m,modules,module names/
8 | --regex-Rust=/^[ \t]*(pub[ \t]+)?static[ \t]+([a-zA-Z0-9_]+)/\2/c,consts,static constants/
9 | --regex-Rust=/^[ \t]*(pub[ \t]+)?trait[ \t]+([a-zA-Z0-9_]+)/\2/t,traits,traits/
10 | --regex-Rust=/^[ \t]*(pub[ \t]+)?impl([ \t\n]+<.*>)?[ \t]+([a-zA-Z0-9_]+)/\3/i,impls,trait implementations/
11 | --regex-Rust=/^[ \t]*macro_rules![ \t]+([a-zA-Z0-9_]+)/\1/d,macros,macro definitions/
12 |
--------------------------------------------------------------------------------
/session.vim:
--------------------------------------------------------------------------------
1 | au BufWritePost *.rs silent!make tags > /dev/null 2>&1
2 |
--------------------------------------------------------------------------------
/src/bench.rs:
--------------------------------------------------------------------------------
1 | #![allow(dead_code)]
2 | #![allow(unused_imports)]
3 |
4 | use rand::{Rng, task_rng};
5 | use stdtest::BenchHarness;
6 |
7 | use super::heap;
8 | use super::merge;
9 | use super::quick;
10 | use super::{bogo, insertion, bubble, selection};
11 |
12 | static SIZE_SORTED: uint = 1000;
13 | static SIZE_SAME: uint = 1000;
14 | static SIZE_MICRO: uint = 10;
15 | static SIZE_SMALL: uint = 100;
16 | static SIZE_MEDIUM: uint = 10000;
17 | static SIZE_LARGE: uint = 100000;
18 |
19 | macro_rules! defbench(
20 | ($name:ident, $sorter:expr, $gen:ident) => (
21 | #[bench]
22 | #[cfg($gen)]
23 | fn $name(b: &mut BenchHarness) {
24 | let xs = $gen();
25 | b.iter(|| $sorter(xs.clone()))
26 | }
27 | );
28 | )
29 |
30 | fn std_sort(xs: &mut [int]) { xs.sort() }
31 | fn micro() -> ~[int] { task_rng().gen_vec(SIZE_MICRO) }
32 | fn small() -> ~[int] { task_rng().gen_vec(SIZE_SMALL) }
33 | fn medium() -> ~[int] { task_rng().gen_vec(SIZE_MEDIUM) }
34 | fn large() -> ~[int] { task_rng().gen_vec(SIZE_LARGE) }
35 | fn sorted() -> ~[int] {
36 | let mut xs = task_rng().gen_vec(SIZE_SORTED);
37 | xs.sort();
38 | return xs
39 | }
40 | fn same() -> ~[int] {
41 | let x: int = task_rng().gen();
42 | ::std::iter::Repeat::new(x).take(SIZE_SAME).collect()
43 | }
44 |
45 | defbench!(sorted_std, std_sort, sorted)
46 | defbench!(sorted_bogo, bogo, sorted)
47 | defbench!(sorted_insertion, insertion, sorted)
48 | defbench!(sorted_bubble, bubble, sorted)
49 | defbench!(sorted_selection, selection, sorted)
50 | defbench!(sorted_mergesort, merge::sort, sorted)
51 | defbench!(sorted_mergesort_insertion, merge::insertion, sorted)
52 | defbench!(sorted_heapsort_up, heap::up, sorted)
53 | defbench!(sorted_heapsort_down, heap::down, sorted)
54 | defbench!(sorted_quicksort_dumb, quick::dumb, sorted)
55 | defbench!(sorted_quicksort_smart, quick::smart, sorted)
56 | defbench!(sorted_quicksort_insertion, quick::insertion, sorted)
57 |
58 | defbench!(same_std, std_sort, same)
59 | defbench!(same_bogo, bogo, same)
60 | defbench!(same_insertion, insertion, same)
61 | defbench!(same_bubble, bubble, same)
62 | defbench!(same_selection, selection, same)
63 | defbench!(same_mergesort, merge::sort, same)
64 | defbench!(same_mergesort_insertion, merge::insertion, same)
65 | defbench!(same_heapsort_up, heap::up, same)
66 | defbench!(same_heapsort_down, heap::down, same)
67 | defbench!(same_quicksort_dumb, quick::dumb, same)
68 | defbench!(same_quicksort_smart, quick::smart, same)
69 | defbench!(same_quicksort_insertion, quick::insertion, same)
70 |
71 | defbench!(micro_std, std_sort, micro)
72 | defbench!(micro_insertion, insertion, micro)
73 | defbench!(micro_bubble, bubble, micro)
74 | defbench!(micro_selection, selection, micro)
75 | defbench!(micro_mergesort, merge::sort, micro)
76 | defbench!(micro_mergesort_insertion, merge::insertion, micro)
77 | defbench!(micro_heapsort_up, heap::up, micro)
78 | defbench!(micro_heapsort_down, heap::down, micro)
79 | defbench!(micro_quicksort_dumb, quick::dumb, micro)
80 | defbench!(micro_quicksort_smart, quick::smart, micro)
81 | defbench!(micro_quicksort_insertion, quick::insertion, micro)
82 |
83 | defbench!(small_std, std_sort, small)
84 | defbench!(small_insertion, insertion, small)
85 | defbench!(small_bubble, bubble, small)
86 | defbench!(small_selection, selection, small)
87 | defbench!(small_mergesort, merge::sort, small)
88 | defbench!(small_mergesort_insertion, merge::insertion, small)
89 | defbench!(small_heapsort_up, heap::up, small)
90 | defbench!(small_heapsort_down, heap::down, small)
91 | defbench!(small_quicksort_dumb, quick::dumb, small)
92 | defbench!(small_quicksort_smart, quick::smart, small)
93 | defbench!(small_quicksort_insertion, quick::insertion, small)
94 |
95 | // We stop benchmarking O(n^2) (and greater) sorting algorithms here. They
96 | // are just too slow.
97 |
98 | defbench!(medium_std, std_sort, medium)
99 | defbench!(medium_mergesort, merge::sort, medium)
100 | defbench!(medium_mergesort_insertion, merge::insertion, medium)
101 | defbench!(medium_heapsort_up, heap::up, medium)
102 | defbench!(medium_heapsort_down, heap::down, medium)
103 | defbench!(medium_quicksort_dumb, quick::dumb, medium)
104 | defbench!(medium_quicksort_smart, quick::smart, medium)
105 | defbench!(medium_quicksort_insertion, quick::insertion, medium)
106 |
107 | defbench!(large_std, std_sort, large)
108 | defbench!(large_mergesort, merge::sort, large)
109 | defbench!(large_mergesort_insertion, merge::insertion, large)
110 | defbench!(large_heapsort_up, heap::up, large)
111 | defbench!(large_heapsort_down, heap::down, large)
112 | defbench!(large_quicksort_dumb, quick::dumb, large)
113 | defbench!(large_quicksort_smart, quick::smart, large)
114 | defbench!(large_quicksort_insertion, quick::insertion, large)
115 |
116 |
--------------------------------------------------------------------------------
/src/lib.rs:
--------------------------------------------------------------------------------
1 | #![crate_id = "sorts#0.1.0"]
2 | #![crate_type = "lib"]
3 | #![license = "UNLICENSE"]
4 | #![doc(html_root_url = "http://burntsushi.net/rustdoc/rust-sorts")]
5 |
6 | #![feature(phase)]
7 | #![feature(macro_rules)]
8 |
9 | //! A collection of sorting algorithms with tests and benchmarks.
10 |
11 | #[phase(syntax, link)] extern crate log;
12 | extern crate stdtest = "test";
13 | extern crate quickcheck;
14 | extern crate rand;
15 |
16 | use rand::Rng; // why do I need this?
17 |
18 | #[cfg(test)]
19 | mod bench;
20 |
21 | #[cfg(test)]
22 | mod test;
23 |
24 | pub static INSERTION_THRESHOLD: uint = 16;
25 |
26 | /// The `bogo` sort is the simplest but worst sorting algorithm. It shuffles
27 | /// the given input until it is sorted. Its worst case space complexity is
28 | /// `O(n)` but its time complexity is *unbounded*.
29 | pub fn bogo(xs: &mut [T]) {
30 | fn is_sorted(xs: &[T]) -> bool {
31 | for win in xs.windows(2) {
32 | if win[0] > win[1] {
33 | return false
34 | }
35 | }
36 | true
37 | }
38 | let rng = &mut rand::task_rng();
39 | while !is_sorted(xs) {
40 | rng.shuffle_mut(xs);
41 | }
42 | }
43 |
44 | /// Classic in place insertion sort. Worst case time complexity is `O(n^2)`.
45 | pub fn insertion(xs: &mut [T]) {
46 | let (mut i, len) = (1, xs.len());
47 | while i < len {
48 | let mut j = i;
49 | while j > 0 && xs[j-1] > xs[j] {
50 | xs.swap(j, j-1);
51 | j = j - 1;
52 | }
53 | i = i + 1;
54 | }
55 | }
56 |
57 | /// Classic in place bubble sort. Worst case time complexity is `O(n^2)`.
58 | pub fn bubble(xs: &mut [T]) {
59 | let mut n = xs.len();
60 | while n > 0 {
61 | let mut newn = 0;
62 | let mut i = 1;
63 | while i < n {
64 | if xs[i-1] > xs[i] {
65 | xs.swap(i-1, i);
66 | newn = i;
67 | }
68 | i = i + 1;
69 | }
70 | n = newn;
71 | }
72 | }
73 |
74 | /// Classic in place selection sort. Worst case time complexity is `O(n^2)`.
75 | /// Note that this is an *unstable* implementation.
76 | pub fn selection(xs: &mut [T]) {
77 | let (mut i, len) = (0, xs.len());
78 | while i < len {
79 | let (mut j, mut cur_min) = (i + 1, i);
80 | while j < len {
81 | if xs[j] < xs[cur_min] {
82 | cur_min = j;
83 | }
84 | j = j + 1;
85 | }
86 | xs.swap(i, cur_min);
87 | i = i + 1;
88 | }
89 | }
90 |
91 | pub mod quick {
92 | use super::INSERTION_THRESHOLD;
93 |
94 | /// Standard in-place quicksort that always uses the first element as
95 | /// a pivot. Average time complexity is `O(nlogn)` and its space complexity
96 | /// is `O(1)` (limited to vectors of size `N`, which is the maximum number
97 | /// expressible with a `uint`).
98 | pub fn dumb(xs: &mut [T]) {
99 | fn pivot(_: &[T]) -> uint { 0 }
100 | qsort(xs, pivot)
101 | }
102 |
103 |
104 | /// Standard in-place quicksort that uses the median of the first, middle
105 | /// and last elements in each vector for the pivot.
106 | /// Average time complexity is `O(nlogn)` and its space complexity
107 | /// is `O(1)` (limited to vectors of size `N`, which is the maximum number
108 | /// expressible with a `uint`).
109 | ///
110 | /// This seems to have the same performance characteristics as the `dumb`
111 | /// quicksort, except when the input is almost sorted where intelligently
112 | /// choosing a pivot helps by at least an order of magnitude. (This is
113 | /// because an almost-sorted vector given to the `dumb` quicksort provokes
114 | /// worse case `O(n^2)` performance, whereas picking a pivot intelligently
115 | /// helps keep it closer to the average `O(nlogn)` performance.)
116 | pub fn smart(xs: &mut [T]) {
117 | qsort(xs, smart_pivot)
118 | }
119 |
120 | pub fn insertion(xs: &mut [T]) {
121 | if xs.len() <= 1 {
122 | return
123 | }
124 | let p = smart_pivot(xs);
125 | let p = partition(xs, p);
126 |
127 | if p <= INSERTION_THRESHOLD {
128 | super::insertion(xs.mut_slice_to(p))
129 | } else {
130 | qsort(xs.mut_slice_to(p), smart_pivot);
131 | }
132 | if xs.len() - p+1 <= INSERTION_THRESHOLD {
133 | super::insertion(xs.mut_slice_from(p+1))
134 | } else {
135 | qsort(xs.mut_slice_from(p+1), smart_pivot);
136 | }
137 | }
138 |
139 | fn qsort(xs: &mut [T], pivot: fn(&[T]) -> uint) {
140 | if xs.len() <= 1 {
141 | return
142 | }
143 | let p = pivot(xs);
144 | let p = partition(xs, p);
145 | qsort(xs.mut_slice_to(p), pivot);
146 | qsort(xs.mut_slice_from(p+1), pivot);
147 | }
148 |
149 | fn partition(xs: &mut [T], p: uint) -> uint {
150 | if xs.len() <= 1 {
151 | return p
152 | }
153 |
154 | let lasti = xs.len() - 1;
155 | let (mut i, mut nextp) = (0, 0);
156 | xs.swap(lasti, p);
157 | while i < lasti {
158 | if xs[i] <= xs[lasti] {
159 | xs.swap(i, nextp);
160 | nextp = nextp + 1;
161 | }
162 | i = i + 1;
163 | }
164 | xs.swap(nextp, lasti);
165 | nextp
166 | }
167 |
168 | fn smart_pivot(xs: &[T]) -> uint {
169 | let (l, r) = (0, xs.len() - 1);
170 | let m = l + ((r - l) / 2);
171 | let (left, middle, right) = (&xs[l], &xs[m], &xs[r]);
172 | if middle >= left && middle <= right {
173 | m
174 | } else if left >= middle && left <= right {
175 | l
176 | } else {
177 | r
178 | }
179 | }
180 | }
181 |
182 | pub mod heap {
183 | pub fn up(xs: &mut [T]) {
184 | sort(xs, heapify_up);
185 | }
186 |
187 | pub fn down(xs: &mut [T]) {
188 | sort(xs, heapify_down);
189 | }
190 |
191 | fn sort(xs: &mut [T], heapify: fn(&mut [T])) {
192 | if xs.len() <= 1 {
193 | return
194 | }
195 |
196 | heapify(xs);
197 | let mut end = xs.len() - 1;
198 | while end > 0 {
199 | xs.swap(end, 0);
200 | end = end - 1;
201 | sift_down(xs, 0, end);
202 | }
203 | }
204 |
205 | fn heapify_down(xs: &mut [T]) {
206 | let last = xs.len() - 1;
207 | let mut start = 1 + ((last - 1) / 2);
208 | while start > 0 {
209 | start = start - 1;
210 | sift_down(xs, start, last);
211 | }
212 | }
213 |
214 | fn sift_down(xs: &mut [T], start: uint, end: uint) {
215 | let mut root = start;
216 | while root * 2 + 1 <= end {
217 | let child = root * 2 + 1;
218 | let mut swap = root;
219 | if xs[swap] < xs[child] {
220 | swap = child
221 | }
222 | if child + 1 <= end && xs[swap] < xs[child+1] {
223 | swap = child + 1
224 | }
225 |
226 | if swap == root {
227 | return
228 | }
229 | xs.swap(root, swap);
230 | root = swap;
231 | }
232 | }
233 |
234 | fn heapify_up(xs: &mut [T]) {
235 | let mut end = 1;
236 | while end < xs.len() {
237 | sift_up(xs, 0, end);
238 | end = end + 1;
239 | }
240 | }
241 |
242 | fn sift_up(xs: &mut [T], start: uint, end: uint) {
243 | let mut child = end;
244 | while child > start {
245 | let parent = (child - 1) / 2;
246 | if xs[parent] >= xs[child] {
247 | return
248 | }
249 | xs.swap(parent, child);
250 | child = parent;
251 | }
252 | }
253 | }
254 |
255 | pub mod merge {
256 | use std::cmp::min;
257 | use std::slice::MutableCloneableVector;
258 |
259 | use super::INSERTION_THRESHOLD;
260 |
261 | /// A stable mergesort with worst case `O(nlogn)` performance. This
262 | /// particular implementation has `O(n)` complexity. Unfortunately, the
263 | /// constant factor is pretty high.
264 | ///
265 | /// (See Rust's standard library `sort` function for a better mergesort
266 | /// which uses unsafe, I think.)
267 | pub fn sort(xs: &mut [T]) {
268 | let (len, mut width) = (xs.len(), 1);
269 | let mut buf = xs.to_owned();
270 | while width < len {
271 | let mut start = 0;
272 | while start < len {
273 | let mid = min(len, start + width);
274 | let end = min(len, start + 2 * width);
275 | merge(xs, buf, start, mid, end);
276 | start = start + 2 * width;
277 | }
278 | width = width * 2;
279 | xs.copy_from(buf);
280 | }
281 | }
282 |
283 | pub fn insertion(xs: &mut [T]) {
284 | let (len, mut width) = (xs.len(), INSERTION_THRESHOLD);
285 | let mut i = 0;
286 | while i < len {
287 | let upto = min(len, i + INSERTION_THRESHOLD);
288 | super::insertion(xs.mut_slice(i, upto));
289 | i = i + INSERTION_THRESHOLD;
290 | }
291 |
292 | let mut buf = xs.to_owned();
293 | while width < len {
294 | let mut start = 0;
295 | while start < len {
296 | let mid = min(len, start + width);
297 | let end = min(len, start + 2 * width);
298 | merge(xs, buf, start, mid, end);
299 | start = start + 2 * width;
300 | }
301 | width = width * 2;
302 | xs.copy_from(buf);
303 | }
304 | }
305 |
306 | fn merge
307 | (xs: &mut [T], buf: &mut [T], l: uint, r: uint, e: uint) {
308 | let (mut il, mut ir) = (l, r);
309 | let mut i = l;
310 | while i < e {
311 | if il < r && (ir >= e || xs[il] <= xs[ir]) {
312 | buf[i] = xs[il].clone();
313 | il = il + 1;
314 | } else {
315 | buf[i] = xs[ir].clone();
316 | ir = ir + 1;
317 | }
318 | i = i + 1;
319 | }
320 | }
321 | }
322 |
--------------------------------------------------------------------------------
/src/test.rs:
--------------------------------------------------------------------------------
1 | use quickcheck::quickcheck;
2 | use super::heap;
3 | use super::merge;
4 | use super::quick;
5 |
6 | macro_rules! defsamelen(
7 | ($name:ident, $sorter:expr) => (
8 | #[test]
9 | fn $name() {
10 | fn prop(mut xs: Vec) -> bool {
11 | let len = xs.len();
12 | $sorter(xs.as_mut_slice());
13 | len == xs.len()
14 | }
15 | quickcheck(prop);
16 | }
17 | );
18 | )
19 |
20 | macro_rules! defsorted(
21 | ($name:ident, $sorter:expr) => (
22 | #[test]
23 | fn $name() {
24 | fn prop(mut xs: Vec) -> bool {
25 | $sorter(xs.as_mut_slice());
26 | is_sorted(xs.as_slice())
27 | }
28 | quickcheck(prop);
29 | }
30 | );
31 | )
32 |
33 | macro_rules! defstable(
34 | ($name:ident, $sorter:expr) => (
35 | #[test]
36 | fn $name() {
37 | fn prop(xs: Vec) -> bool {
38 | let mut sxs = stable_annotate(xs.as_slice());
39 | $sorter(sxs.as_mut_slice());
40 | is_stable(sxs.as_slice())
41 | }
42 | quickcheck(prop);
43 | }
44 | );
45 | )
46 |
47 | macro_rules! defunstable(
48 | ($name:ident, $sorter:expr) => (
49 | #[test]
50 | #[should_fail]
51 | fn $name() {
52 | fn prop(xs: Vec) -> bool {
53 | let mut sxs = stable_annotate(xs.as_slice());
54 | $sorter(sxs.as_mut_slice());
55 | is_stable(sxs.as_slice())
56 | }
57 | quickcheck(prop);
58 | }
59 | );
60 | )
61 |
62 | defsamelen!(samelen_std, std_sort)
63 | defsamelen!(samelen_insertion, super::insertion)
64 | defsamelen!(samelen_bubble, super::bubble)
65 | defsamelen!(samelen_selection, super::selection)
66 | defsamelen!(samelen_merge, merge::sort)
67 | defsamelen!(samelen_merge_insertion, merge::insertion)
68 | defsamelen!(samelen_heapsort_up, heap::up)
69 | defsamelen!(samelen_heapsort_down, heap::down)
70 | defsamelen!(samelen_quicksort_dumb, quick::dumb)
71 | defsamelen!(samelen_quicksort_smart, quick::smart)
72 | defsamelen!(samelen_quicksort_insertion, quick::insertion)
73 |
74 | defsorted!(sorted_std, std_sort)
75 | defsorted!(sorted_insertion, super::insertion)
76 | defsorted!(sorted_bubble, super::bubble)
77 | defsorted!(sorted_selection, super::selection)
78 | defsorted!(sorted_merge, merge::sort)
79 | defsorted!(sorted_merge_insertion, merge::insertion)
80 | defsorted!(sorted_heapsort_up, heap::up)
81 | defsorted!(sorted_heapsort_down, heap::down)
82 | defsorted!(sorted_quicksort_dumb, quick::dumb)
83 | defsorted!(sorted_quicksort_smart, quick::smart)
84 | defsorted!(sorted_quicksort_insertion, quick::insertion)
85 |
86 | defstable!(stable_std, std_sort)
87 | defstable!(stable_insertion, super::insertion)
88 | defstable!(stable_bubble, super::bubble)
89 | defstable!(stable_merge, merge::sort)
90 | defstable!(stable_merge_insertion, merge::insertion)
91 |
92 | defunstable!(unstable_selection, super::selection)
93 | defunstable!(unstable_heapsort_up, heap::up)
94 | defunstable!(unstable_heapsort_down, heap::down)
95 | defunstable!(unstable_quicksort_dumb, quick::dumb)
96 | defunstable!(unstable_quicksort_smart, quick::smart)
97 | defunstable!(unstable_quicksort_insertion, quick::insertion)
98 |
99 | fn std_sort(xs: &mut [T]) {
100 | xs.sort()
101 | }
102 |
103 | fn is_sorted(xs: &[T]) -> bool {
104 | for win in xs.windows(2) {
105 | if win[0] > win[1] {
106 | return false
107 | }
108 | }
109 | true
110 | }
111 |
112 | #[deriving(Show, Clone)]
113 | struct Pair {
114 | val: int,
115 | vestigial: uint,
116 | }
117 |
118 | impl Eq for Pair {
119 | fn eq(&self, other: &Pair) -> bool {
120 | self.val == other.val
121 | }
122 | }
123 |
124 | impl Ord for Pair {
125 | fn lt(&self, other: &Pair) -> bool {
126 | self.val < other.val
127 | }
128 | }
129 |
130 | impl TotalEq for Pair {}
131 |
132 | impl TotalOrd for Pair {
133 | fn cmp(&self, other: &Pair) -> Ordering {
134 | self.val.cmp(&other.val)
135 | }
136 | }
137 |
138 | fn stable_annotate(xs: &[int]) -> Vec {
139 | let mut pairs = vec!();
140 | for (i, &x) in xs.iter().enumerate() {
141 | pairs.push(Pair { val: x, vestigial: i, })
142 | }
143 | pairs
144 | }
145 |
146 | fn is_stable(xs: &[Pair]) -> bool {
147 | fn vestigial_groups(xs: &[Pair]) -> Vec> {
148 | let mut groups: Vec> = vec!();
149 | let mut current: Vec = vec!();
150 | for (i, &x) in xs.iter().enumerate() {
151 | if i == 0 {
152 | current.push(x.vestigial);
153 | continue
154 | }
155 | if xs[i-1].val == x.val {
156 | current.push(x.vestigial)
157 | } else {
158 | groups.push(current);
159 | current = vec!(x.vestigial);
160 | }
161 | }
162 | if current.len() > 0 {
163 | groups.push(current)
164 | }
165 | groups
166 | }
167 | let groups = vestigial_groups(xs.as_slice());
168 | // debug!("BEFORE GROUPS: {}", xs);
169 | // debug!("GROUPS: {}", groups);
170 | groups.move_iter().all(|g| is_sorted(g.as_slice()))
171 | }
172 |
--------------------------------------------------------------------------------