├── .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 | --------------------------------------------------------------------------------