├── .github └── workflows │ └── rust.yml ├── .gitignore ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE ├── README.md ├── build_and_run.sh ├── docs ├── COPYRIGHT.txt ├── FiraSans-LICENSE.txt ├── FiraSans-Medium.woff ├── FiraSans-Regular.woff ├── Heuristica-Italic.woff ├── Heuristica-LICENSE.txt ├── LICENSE-APACHE.txt ├── LICENSE-MIT.txt ├── SourceCodePro-LICENSE.txt ├── SourceCodePro-Regular.woff ├── SourceCodePro-Semibold.woff ├── SourceSerifPro-Bold.woff ├── SourceSerifPro-LICENSE.txt ├── SourceSerifPro-Regular.woff ├── dmsort │ ├── dmsort │ │ ├── fn.sort.html │ │ ├── fn.sort_by.html │ │ ├── fn.sort_by_key.html │ │ ├── fn.sort_copy.html │ │ ├── sort.v.html │ │ ├── sort_by.v.html │ │ ├── sort_by_key.v.html │ │ └── sort_copy.v.html │ ├── fn.sort.html │ ├── fn.sort_by.html │ ├── fn.sort_by_key.html │ ├── fn.sort_copy.html │ ├── index.html │ ├── sidebar-items.js │ ├── sort.v.html │ ├── sort_by.v.html │ ├── sort_by_key.v.html │ └── sort_copy.v.html ├── jquery.js ├── main.css ├── main.js ├── normalize.css ├── pdqsort │ ├── fn.sort.html │ ├── fn.sort_by.html │ ├── fn.sort_by_key.html │ ├── index.html │ ├── sidebar-items.js │ ├── sort.v.html │ ├── sort_by.v.html │ └── sort_by_key.v.html ├── rustdoc.css ├── search-index.js └── src │ ├── dmsort │ ├── dmsort.rs.html │ ├── lib.rs.html │ └── src │ │ ├── dmsort.rs.html │ │ └── lib.rs.html │ └── pdqsort │ └── lib.rs.html ├── examples └── example.rs ├── images ├── comparisons_1000_i32.png ├── comparisons_1000_string.png ├── comparisons_100_i32.png ├── comparisons_100_string.png ├── comparisons_100k_i32.png ├── comparisons_100k_string.png ├── comparisons_10M_i32.png ├── comparisons_10k_i32.png ├── comparisons_10k_string.png ├── comparisons_1M_i32.png ├── comparisons_1M_string.png ├── example.png ├── speedup_1000_i32.png ├── speedup_1000_string.png ├── speedup_100_i32.png ├── speedup_100_string.png ├── speedup_100k_i32.png ├── speedup_100k_string.png ├── speedup_10M_i32.png ├── speedup_10k_i32.png ├── speedup_10k_string.png ├── speedup_1M_i32.png └── speedup_1M_string.png ├── src ├── dmsort.rs └── lib.rs └── tests ├── benchmark.rs └── tests.rs /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | 3 | name: Continuous integration 4 | 5 | jobs: 6 | check: 7 | name: Check 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | - uses: actions-rs/toolchain@v1 12 | with: 13 | profile: minimal 14 | toolchain: stable 15 | override: true 16 | - uses: actions-rs/cargo@v1 17 | with: 18 | command: check 19 | 20 | test: 21 | name: Test Suite 22 | runs-on: ubuntu-latest 23 | steps: 24 | - uses: actions/checkout@v2 25 | - uses: actions-rs/toolchain@v1 26 | with: 27 | profile: minimal 28 | toolchain: stable 29 | override: true 30 | - uses: actions-rs/cargo@v1 31 | with: 32 | command: test 33 | 34 | miri: 35 | name: Miri 36 | runs-on: ubuntu-latest 37 | env: 38 | MIRIFLAGS: -Zmiri-tag-raw-pointers 39 | steps: 40 | - uses: actions/checkout@v2 41 | - uses: actions-rs/toolchain@v1 42 | with: 43 | profile: minimal 44 | toolchain: nightly 45 | components: miri 46 | override: true 47 | - uses: actions-rs/cargo@v1 48 | with: 49 | command: miri 50 | args: test 51 | 52 | fmt: 53 | name: Rustfmt 54 | runs-on: ubuntu-latest 55 | steps: 56 | - uses: actions/checkout@v2 57 | - uses: actions-rs/toolchain@v1 58 | with: 59 | profile: minimal 60 | toolchain: stable 61 | override: true 62 | - run: rustup component add rustfmt 63 | - uses: actions-rs/cargo@v1 64 | with: 65 | command: fmt 66 | args: --all -- --check 67 | 68 | clippy: 69 | name: Clippy 70 | runs-on: ubuntu-latest 71 | steps: 72 | - uses: actions/checkout@v2 73 | - uses: actions-rs/toolchain@v1 74 | with: 75 | profile: minimal 76 | toolchain: stable 77 | override: true 78 | - run: rustup component add clippy 79 | - uses: actions-rs/cargo@v1 80 | with: 81 | command: clippy 82 | args: -- -D warnings 83 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | *.lock 3 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 1.0.2 - 2022-06-19 2 | Fix some bugs found by Miri (https://github.com/emilk/drop-merge-sort/pull/21). 3 | 4 | # 1.0.0 - 2018-07-18 5 | Replace dependency on Crate [pdqsort](https://github.com/orlp/pdqsort) with [`sort_unstable`](https://doc.rust-lang.org/std/primitive.slice.html#method.sort_unstable). 6 | 7 | # 0.1.3 - 2017-05-08 8 | Update pdqsort to 0.1.2 and update pbr to fix compilation issue due to https://github.com/rust-lang/rust/issues/40952#issuecomment-293720927 9 | 10 | # 0.1.2 - 2017-02-05 11 | Update pdqsort to 0.1.1 12 | 13 | # 0.1.1 - 2017-01-06 14 | Minor adjustments for faster sorting of shorter lists. 15 | 16 | # 0.1.0 - 2017-01-05 17 | Made crate available on cargo.io. 18 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dmsort" 3 | version = "1.0.2" 4 | authors = ["Emil Ernerfeldt "] 5 | description = """ 6 | Fast adaptive sorting for when most of your data is already in order. 7 | dmsort can be 2-5 times faster than Rust's default sort when more than 80% of the elements are already in order. 8 | """ 9 | repository = "https://github.com/emilk/drop-merge-sort" 10 | readme = "README.md" 11 | keywords = ["sorting", "sort", "adaptive"] 12 | categories = ["algorithms"] 13 | license = "MIT" 14 | include = [ "**/*.rs", "Cargo.toml", "LICENSE"] 15 | 16 | [lib] 17 | name = "dmsort" 18 | path = "src/lib.rs" 19 | test = false 20 | bench = false 21 | 22 | [dev-dependencies] 23 | gnuplot = "0.0.22" 24 | pbr = "1" 25 | rand = "0.7" 26 | time = "0.1" 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | Copyright (c) 2017 Emil Ernerfeldt 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Latest version](https://img.shields.io/crates/v/dmsort.svg)](https://crates.io/crates/dmsort) 2 | [![Documentation](https://docs.rs/dmsort/badge.svg)](https://docs.rs/dmsort) 3 | 4 | # Abstract 5 | This is an implementation of a novel [adaptive sorting](https://en.wikipedia.org/wiki/Adaptive_sort) algorithm optimized for nearly-sorted data. Drop-Merge sort is especially useful for when >80% of the data is already in-order, and the unsorted elements are evenly distributed. An example use-case would be re-sorting an already sorted list after minor modifications. 6 | 7 | Drop-Merge sort is 2-5 times faster than quicksort when sorting long lists (10k elements or more) in cases where >80% of the data is already in order, while being considerably simpler to implement than other adaptive sorting algorithms. 8 | 9 | With `N` elements in the list where `K` elements are out-of-order, Drop-Merge sort performs `O(N + K⋅log(K))` comparisons and use `O(K)` extra memory. 10 | 11 | Definition: X% of elements are in order if the [Longest Nondecreasing Subsequence](https://en.wikipedia.org/wiki/Longest_increasing_subsequence) contains X% of the elements. 12 | 13 | # Getting started with the crate 14 | Add this to `Cargo.toml`: 15 | ``` 16 | [dependencies] 17 | dmsort = "1.0.0" 18 | ``` 19 | 20 | And then use it: 21 | ``` rust 22 | extern crate dmsort; 23 | 24 | fn main() { 25 | let mut numbers : Vec = vec!(0, 1, 6, 7, 2, 3, 4, 5); 26 | dmsort::sort(&mut numbers); 27 | assert_eq!(numbers, vec!(0, 1, 2, 3, 4, 5, 6, 7)); 28 | } 29 | ``` 30 | 31 | [Full dmsort documentation](https://emilk.github.io/drop-merge-sort/dmsort/index.html). 32 | 33 | # Performance 34 | To test the performance of this algorithm, I generate almost-sorted data like this (pseudo-code): 35 | 36 | ``` 37 | function generate_test_data(length, disorder_factor) -> Vec { 38 | result = Vec::new() 39 | for i in 0..length { 40 | if random_float() < disorder_factor { 41 | result.push(random_integer_in_range(0, length)) 42 | } else { 43 | result.push(i) 44 | } 45 | } 46 | return result 47 | } 48 | ``` 49 | 50 | I then benchmarked Drop-Merge sort against: 51 | 52 | * the default Rust sorting algorithm ([Vec::sort](https://doc.rust-lang.org/beta/std/vec/struct.Vec.html#method.sort), a [stable sorting algorithm](https://github.com/rust-lang/rust/pull/38192) based loosely on [Timsort](https://en.wikipedia.org/wiki/Timsort)) 53 | * [pdqsort (pattern-defeating quicksort)](https://github.com/stjepang/pdqsort) 54 | * [quickersort (dual-pivot quicksort)](https://github.com/notriddle/quickersort) 55 | 56 | The benchmark was run on an MacBook Pro Retina (Early 2013) using `rustc 1.15.0 (10893a9a3 2017-01-19)`. 57 | 58 | ![Comparing Drop-Merge sort](images/comparisons_1M_i32.png) 59 | ![Comparing Drop-Merge sort](images/comparisons_1M_string.png) 60 | 61 | We can see that all four algorithms manages to exploit almost-sorted data, but Drop-Merge sort wins out when the disorder factor is less than 30% (more than 70% of the elements are in order). 62 | 63 | It also behaves well when the data becomes more random, and at its worst it is still only ~30% slower than pdqsort (which Drop-Merge sort uses as fallback). 64 | 65 | Here is another view of the data for 0-50% disorder: 66 | 67 | ![Speedup over quicksort](images/speedup_1M_i32.png) 68 | 69 | Here we can see that we get 5x speedup over quicksort when 99% of the elements are in order, and a 2x speedup when 85% of the elements are in order. 70 | 71 | When the disorder is above 30% (less than 70% of the elements are in order), Drop-Merge sort is slower than its competitors. 72 | 73 | # Algorithm details 74 | ## Background 75 | The paper [Item Retention Improvements to Dropsort, a Lossy Sorting Algorithm](http://micsymposium.org/mics_2011_proceedings/mics2011_submission_13.pdf) by Abram Jackson and Ryan McCulloch introduced improvements to the esoteric "lossy" sorting algorithm known as [*Dropsort*](http://www.dangermouse.net/esoteric/dropsort.html). In Dropsort, out-of-order elements are simply "dropped" (i.e. removed). In the paper, Jackson et al. introduced improvements to Dropsort which improved the detection of out-of-order elements so that more elements would be kept, and fewer dropped. 76 | 77 | Although the paper does not spell it out, it does in fact describe a fast `O(N)` algorithm for finding an approximate solution to the [Longest Nondecreasing Subsequence](https://en.wikipedia.org/wiki/Longest_increasing_subsequence) (LNS) problem. The algorithm is very effective when a majority of elements in a list is in order (part of the LNS). 78 | 79 | Example input, where most of the data is in order: 80 | 81 | ![Example of almost-sorted data](images/example.png) 82 | 83 | ## Drop-Merge sort 84 | The main idea in Drop-Merge sort is this: 85 | 86 | * Use the methods described in the Jackson et al. paper to find the Longest Nondecreasing Subsequence (LNS). 87 | * Keep the LNS and drop the outliers into a separate list. 88 | * Sort the list of dropped outliers using a standard sorting algorithm ([`sort_unstable`](https://doc.rust-lang.org/std/primitive.slice.html#method.sort_unstable) in this implementation). 89 | * Merge the outliers back into the main list of the already sorted LNS. 90 | 91 | Thus despite its heritage, Drop-Merge sort is a *lossless* sorting algorithm (the normal kind). 92 | 93 | ## Finding the Longest Nondecreasing Subsequence 94 | The implementation uses the idea of *memory* from the Jackson et al. paper to detect *mistakes* - elements that probably should have been dropped instead of accepted. Consider the following sequence of numbers: 95 | 96 | `0 1 12 3 4 5 6 7 8 9` 97 | 98 | The naïve Dropsort algorithm will accept `0 1 12` and then drop the remaining values (as they are all smaller than 12). The idea of memory is to detect when we have dropped a certain number of elements in a row. When this happens, we roll-back and drop the last element before the long run of drops. This is a form of back-tracking which solves the above problem. In the case of the input above, the algorithm will drop the `12` and keep all other elements. In this implementation, we consider the dropping of 8 elements in a row to be the cutoff point at which we roll-back and undo. This contains the assumption that there will be no more than 8 outliers in a row. It is a very simple method that works well in practice. 99 | 100 | Drop-Merge sort will keep in-order elements in situ, moving them towards the start of the list. This helps keep the memory use down and the performance up. 101 | 102 | If the elements are not ordered Drop-Merge sort can be around 10% slower than a traditional sorting algorithm. Therefore Drop-Merge sort will try to detect such disordered input and abort, falling back to the standard sorting algorithm. 103 | 104 | ## Comparison to other adaptive sorting algorithms 105 | An adaptive sorting algorithm is one that can exploit existing order. These algorithms ranges from the complicated to the simple. 106 | 107 | On the complicated end there is the famous [Smoothsort](https://en.wikipedia.org/wiki/Smoothsort), which seems, however, to be quite unpopular - probably due to its complexity. I failed to find a good implementation of Smoothsort to compare Drop-Merge sort against. [Timsort](https://en.wikipedia.org/wiki/Timsort) is a more modern and popular adaptive sorting algorithm. It needs long spans of nondecreasing elements to compete with the performance of Drop-Merge sort. The standard Rust sort uses a variant of Timsort, and as you can see from the performance comparisons, Drop-Merge sort wins for the nearly-sorted cases for which it was designed. 108 | 109 | On the simple end of the spectrum there are `O(N²)` algorithms that perform extremely well when there are only one or two elements out of place, or the list is very short (a few hundred elements at most). Examples include [Insertion sort](https://en.wikipedia.org/wiki/Insertion_sort) and [Coctail sort](https://en.wikipedia.org/wiki/Cocktail_shaker_sort). 110 | 111 | Drop-Merge sort finds an interesting middle-ground – it is reasonably simple (less than 100 lines of code), yet manages to perform well for long lists. Note, however, that Drop-Merge sort depends on another sorting algorithm (e.g. quick-sort) for sorting the out-of-order elements. 112 | 113 | ## When Drop-Merge sort works best 114 | * Less than 20-30% of the elements out of order AND these are randomly distributed in the data (not clumped). 115 | * You have a lot of data (10k elements or more, and definitively when you get into the millions). 116 | 117 | 118 | ## Limitations and future work 119 | Drop-Merge sort is not stable, which means it will not keep the order of equal elements. 120 | 121 | Drop-Merge sort does not sort [in-situ](https://en.wikipedia.org/wiki/In-place_algorithm), but will use `O(K)` extra memory, where `K` is the number of elements out-of-order. 122 | 123 | The algorithms uses `recency=8` which means it can handle no more than 8 outliers in a row. This number was chosen by experimentation, and could perhaps be adjusted dynamically for increased performance. 124 | 125 | # Other implementations 126 | * C++: [https://github.com/adrian17/cpp-drop-merge-sort](https://github.com/adrian17/cpp-drop-merge-sort) 127 | 128 | 129 | ## TODO 130 | * Set up TravisCI 131 | -------------------------------------------------------------------------------- /build_and_run.sh: -------------------------------------------------------------------------------- 1 | set -e 2 | rustup default stable 3 | # rustup default nightly 4 | 5 | # export RUST_BACKTRACE=1 6 | cargo test 7 | 8 | # Publish documentation in docs/ folder to be compatible with github pages. 9 | # It can be found on https://emilk.github.io/drop-merge-sort/dmsort/index.html 10 | cargo doc && rm -rf docs/ && cp -r target/doc docs 11 | 12 | # The slow benchmarks that make the plots: 13 | time cargo test --release -- --nocapture --ignored 14 | 15 | rustc --version 16 | -------------------------------------------------------------------------------- /docs/COPYRIGHT.txt: -------------------------------------------------------------------------------- 1 | These documentation pages include resources by third parties. This copyright 2 | file applies only to those resources. The following third party resources are 3 | included, and carry their own copyright notices and license terms: 4 | 5 | * Fira Sans (FiraSans-Regular.woff, FiraSans-Medium.woff): 6 | 7 | Copyright (c) 2014, Mozilla Foundation https://mozilla.org/ 8 | with Reserved Font Name Fira Sans. 9 | 10 | Copyright (c) 2014, Telefonica S.A. 11 | 12 | Licensed under the SIL Open Font License, Version 1.1. 13 | See FiraSans-LICENSE.txt. 14 | 15 | * Heuristica (Heuristica-Italic.woff): 16 | 17 | Copyright 1989, 1991 Adobe Systems Incorporated. All rights reserved. 18 | Utopia is either a registered trademark or trademark of Adobe Systems 19 | Incorporated in the United States and/or other countries. Used under 20 | license. 21 | 22 | Copyright 2006 Han The Thanh, Vntopia font family, http://vntex.sf.net 23 | 24 | Copyright (c) 2008-2012, Andrey V. Panov (panov@canopus.iacp.dvo.ru), 25 | with Reserved Font Name Heuristica. 26 | 27 | Licensed under the SIL Open Font License, Version 1.1. 28 | See Heuristica-LICENSE.txt. 29 | 30 | * jQuery (jquery-2.1.4.min.js): 31 | 32 | Copyright 2005, 2015 jQuery Foundation, Inc. 33 | Licensed under the MIT license (see LICENSE-MIT.txt). 34 | 35 | * rustdoc.css, main.js, and playpen.js: 36 | 37 | Copyright 2015 The Rust Developers. 38 | Licensed under the Apache License, Version 2.0 (see LICENSE-APACHE.txt) or 39 | the MIT license (LICENSE-MIT.txt) at your option. 40 | 41 | * normalize.css: 42 | 43 | Copyright (c) Nicolas Gallagher and Jonathan Neal. 44 | Licensed under the MIT license (see LICENSE-MIT.txt). 45 | 46 | * Source Code Pro (SourceCodePro-Regular.woff, SourceCodePro-Semibold.woff): 47 | 48 | Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), 49 | with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark 50 | of Adobe Systems Incorporated in the United States and/or other countries. 51 | 52 | Licensed under the SIL Open Font License, Version 1.1. 53 | See SourceCodePro-LICENSE.txt. 54 | 55 | * Source Serif Pro (SourceSerifPro-Regular.woff, SourceSerifPro-Bold.woff): 56 | 57 | Copyright 2014 Adobe Systems Incorporated (http://www.adobe.com/), with 58 | Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of 59 | Adobe Systems Incorporated in the United States and/or other countries. 60 | 61 | Licensed under the SIL Open Font License, Version 1.1. 62 | See SourceSerifPro-LICENSE.txt. 63 | 64 | This copyright file is intended to be distributed with rustdoc output. 65 | -------------------------------------------------------------------------------- /docs/FiraSans-LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, Mozilla Foundation https://mozilla.org/ 2 | with Reserved Font Name Fira Sans. 3 | 4 | Copyright (c) 2014, Mozilla Foundation https://mozilla.org/ 5 | with Reserved Font Name Fira Mono. 6 | 7 | Copyright (c) 2014, Telefonica S.A. 8 | 9 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 10 | This license is copied below, and is also available with a FAQ at: 11 | http://scripts.sil.org/OFL 12 | 13 | 14 | ----------------------------------------------------------- 15 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 16 | ----------------------------------------------------------- 17 | 18 | PREAMBLE 19 | The goals of the Open Font License (OFL) are to stimulate worldwide 20 | development of collaborative font projects, to support the font creation 21 | efforts of academic and linguistic communities, and to provide a free and 22 | open framework in which fonts may be shared and improved in partnership 23 | with others. 24 | 25 | The OFL allows the licensed fonts to be used, studied, modified and 26 | redistributed freely as long as they are not sold by themselves. The 27 | fonts, including any derivative works, can be bundled, embedded, 28 | redistributed and/or sold with any software provided that any reserved 29 | names are not used by derivative works. The fonts and derivatives, 30 | however, cannot be released under any other type of license. The 31 | requirement for fonts to remain under this license does not apply 32 | to any document created using the fonts or their derivatives. 33 | 34 | DEFINITIONS 35 | "Font Software" refers to the set of files released by the Copyright 36 | Holder(s) under this license and clearly marked as such. This may 37 | include source files, build scripts and documentation. 38 | 39 | "Reserved Font Name" refers to any names specified as such after the 40 | copyright statement(s). 41 | 42 | "Original Version" refers to the collection of Font Software components as 43 | distributed by the Copyright Holder(s). 44 | 45 | "Modified Version" refers to any derivative made by adding to, deleting, 46 | or substituting -- in part or in whole -- any of the components of the 47 | Original Version, by changing formats or by porting the Font Software to a 48 | new environment. 49 | 50 | "Author" refers to any designer, engineer, programmer, technical 51 | writer or other person who contributed to the Font Software. 52 | 53 | PERMISSION & CONDITIONS 54 | Permission is hereby granted, free of charge, to any person obtaining 55 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 56 | redistribute, and sell modified and unmodified copies of the Font 57 | Software, subject to the following conditions: 58 | 59 | 1) Neither the Font Software nor any of its individual components, 60 | in Original or Modified Versions, may be sold by itself. 61 | 62 | 2) Original or Modified Versions of the Font Software may be bundled, 63 | redistributed and/or sold with any software, provided that each copy 64 | contains the above copyright notice and this license. These can be 65 | included either as stand-alone text files, human-readable headers or 66 | in the appropriate machine-readable metadata fields within text or 67 | binary files as long as those fields can be easily viewed by the user. 68 | 69 | 3) No Modified Version of the Font Software may use the Reserved Font 70 | Name(s) unless explicit written permission is granted by the corresponding 71 | Copyright Holder. This restriction only applies to the primary font name as 72 | presented to the users. 73 | 74 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 75 | Software shall not be used to promote, endorse or advertise any 76 | Modified Version, except to acknowledge the contribution(s) of the 77 | Copyright Holder(s) and the Author(s) or with their explicit written 78 | permission. 79 | 80 | 5) The Font Software, modified or unmodified, in part or in whole, 81 | must be distributed entirely under this license, and must not be 82 | distributed under any other license. The requirement for fonts to 83 | remain under this license does not apply to any document created 84 | using the Font Software. 85 | 86 | TERMINATION 87 | This license becomes null and void if any of the above conditions are 88 | not met. 89 | 90 | DISCLAIMER 91 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 92 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 93 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 94 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 95 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 96 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 97 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 98 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 99 | OTHER DEALINGS IN THE FONT SOFTWARE. 100 | -------------------------------------------------------------------------------- /docs/FiraSans-Medium.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilk/drop-merge-sort/ccef37cca71d2b538b1f17856a458f5cd74dfaf0/docs/FiraSans-Medium.woff -------------------------------------------------------------------------------- /docs/FiraSans-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilk/drop-merge-sort/ccef37cca71d2b538b1f17856a458f5cd74dfaf0/docs/FiraSans-Regular.woff -------------------------------------------------------------------------------- /docs/Heuristica-Italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilk/drop-merge-sort/ccef37cca71d2b538b1f17856a458f5cd74dfaf0/docs/Heuristica-Italic.woff -------------------------------------------------------------------------------- /docs/Heuristica-LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 1989, 1991 Adobe Systems Incorporated. All rights reserved. 2 | Utopia is either a registered trademark or trademark of Adobe Systems 3 | Incorporated in the United States and/or other countries. Used under 4 | license. 5 | 6 | Copyright 2006 Han The Thanh, Vntopia font family, http://vntex.sf.net 7 | 8 | Copyright (c) 2008-2012, Andrey V. Panov (panov@canopus.iacp.dvo.ru), 9 | with Reserved Font Name Heuristica. 10 | 11 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 12 | This license is copied below, and is also available with a FAQ at: 13 | http://scripts.sil.org/OFL 14 | 15 | 16 | ----------------------------------------------------------- 17 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 18 | ----------------------------------------------------------- 19 | 20 | PREAMBLE 21 | The goals of the Open Font License (OFL) are to stimulate worldwide 22 | development of collaborative font projects, to support the font creation 23 | efforts of academic and linguistic communities, and to provide a free and 24 | open framework in which fonts may be shared and improved in partnership 25 | with others. 26 | 27 | The OFL allows the licensed fonts to be used, studied, modified and 28 | redistributed freely as long as they are not sold by themselves. The 29 | fonts, including any derivative works, can be bundled, embedded, 30 | redistributed and/or sold with any software provided that any reserved 31 | names are not used by derivative works. The fonts and derivatives, 32 | however, cannot be released under any other type of license. The 33 | requirement for fonts to remain under this license does not apply 34 | to any document created using the fonts or their derivatives. 35 | 36 | DEFINITIONS 37 | "Font Software" refers to the set of files released by the Copyright 38 | Holder(s) under this license and clearly marked as such. This may 39 | include source files, build scripts and documentation. 40 | 41 | "Reserved Font Name" refers to any names specified as such after the 42 | copyright statement(s). 43 | 44 | "Original Version" refers to the collection of Font Software components as 45 | distributed by the Copyright Holder(s). 46 | 47 | "Modified Version" refers to any derivative made by adding to, deleting, 48 | or substituting -- in part or in whole -- any of the components of the 49 | Original Version, by changing formats or by porting the Font Software to a 50 | new environment. 51 | 52 | "Author" refers to any designer, engineer, programmer, technical 53 | writer or other person who contributed to the Font Software. 54 | 55 | PERMISSION & CONDITIONS 56 | Permission is hereby granted, free of charge, to any person obtaining 57 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 58 | redistribute, and sell modified and unmodified copies of the Font 59 | Software, subject to the following conditions: 60 | 61 | 1) Neither the Font Software nor any of its individual components, 62 | in Original or Modified Versions, may be sold by itself. 63 | 64 | 2) Original or Modified Versions of the Font Software may be bundled, 65 | redistributed and/or sold with any software, provided that each copy 66 | contains the above copyright notice and this license. These can be 67 | included either as stand-alone text files, human-readable headers or 68 | in the appropriate machine-readable metadata fields within text or 69 | binary files as long as those fields can be easily viewed by the user. 70 | 71 | 3) No Modified Version of the Font Software may use the Reserved Font 72 | Name(s) unless explicit written permission is granted by the corresponding 73 | Copyright Holder. This restriction only applies to the primary font name as 74 | presented to the users. 75 | 76 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 77 | Software shall not be used to promote, endorse or advertise any 78 | Modified Version, except to acknowledge the contribution(s) of the 79 | Copyright Holder(s) and the Author(s) or with their explicit written 80 | permission. 81 | 82 | 5) The Font Software, modified or unmodified, in part or in whole, 83 | must be distributed entirely under this license, and must not be 84 | distributed under any other license. The requirement for fonts to 85 | remain under this license does not apply to any document created 86 | using the Font Software. 87 | 88 | TERMINATION 89 | This license becomes null and void if any of the above conditions are 90 | not met. 91 | 92 | DISCLAIMER 93 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 94 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 95 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 96 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 97 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 98 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 99 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 100 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 101 | OTHER DEALINGS IN THE FONT SOFTWARE. 102 | -------------------------------------------------------------------------------- /docs/LICENSE-APACHE.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /docs/LICENSE-MIT.txt: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /docs/SourceCodePro-LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries. 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | 5 | This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /docs/SourceCodePro-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilk/drop-merge-sort/ccef37cca71d2b538b1f17856a458f5cd74dfaf0/docs/SourceCodePro-Regular.woff -------------------------------------------------------------------------------- /docs/SourceCodePro-Semibold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilk/drop-merge-sort/ccef37cca71d2b538b1f17856a458f5cd74dfaf0/docs/SourceCodePro-Semibold.woff -------------------------------------------------------------------------------- /docs/SourceSerifPro-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilk/drop-merge-sort/ccef37cca71d2b538b1f17856a458f5cd74dfaf0/docs/SourceSerifPro-Bold.woff -------------------------------------------------------------------------------- /docs/SourceSerifPro-LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2014 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries. 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | 5 | This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /docs/SourceSerifPro-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilk/drop-merge-sort/ccef37cca71d2b538b1f17856a458f5cd74dfaf0/docs/SourceSerifPro-Regular.woff -------------------------------------------------------------------------------- /docs/dmsort/dmsort/fn.sort.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |

Redirecting to ../../dmsort/fn.sort.html...

8 | 9 | 10 | -------------------------------------------------------------------------------- /docs/dmsort/dmsort/fn.sort_by.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |

Redirecting to ../../dmsort/fn.sort_by.html...

8 | 9 | 10 | -------------------------------------------------------------------------------- /docs/dmsort/dmsort/fn.sort_by_key.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |

Redirecting to ../../dmsort/fn.sort_by_key.html...

8 | 9 | 10 | -------------------------------------------------------------------------------- /docs/dmsort/dmsort/fn.sort_copy.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |

Redirecting to ../../dmsort/fn.sort_copy.html...

8 | 9 | 10 | -------------------------------------------------------------------------------- /docs/dmsort/dmsort/sort.v.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |

Redirecting to fn.sort.html...

8 | 9 | 10 | -------------------------------------------------------------------------------- /docs/dmsort/dmsort/sort_by.v.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |

Redirecting to fn.sort_by.html...

8 | 9 | 10 | -------------------------------------------------------------------------------- /docs/dmsort/dmsort/sort_by_key.v.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |

Redirecting to fn.sort_by_key.html...

8 | 9 | 10 | -------------------------------------------------------------------------------- /docs/dmsort/dmsort/sort_copy.v.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |

Redirecting to fn.sort_copy.html...

8 | 9 | 10 | -------------------------------------------------------------------------------- /docs/dmsort/fn.sort.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | dmsort::sort - Rust 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 26 | 27 | 28 | 29 | 33 | 34 | 44 | 45 |
46 |

Function dmsort::sort 47 | 48 | [] 49 | 50 | [src]

51 |
pub fn sort<T: Ord>(slice: &mut [T])

Sorts the elements using the Ord trait.

52 | 53 |

Examples

54 |
 55 | let mut numbers : Vec<i32> = vec!(0, 1, 6, 7, 2, 3, 4, 5);
 56 | dmsort::sort(&mut numbers);
 57 | assert_eq!(numbers, vec!(0, 1, 2, 3, 4, 5, 6, 7));
58 |
59 | 60 | 61 | 62 | 63 | 108 | 109 | 110 | 111 | 115 | 116 | 117 | 118 | 119 | -------------------------------------------------------------------------------- /docs/dmsort/fn.sort_by.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | dmsort::sort_by - Rust 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 26 | 27 | 28 | 29 | 33 | 34 | 44 | 45 |
46 |

Function dmsort::sort_by 47 | 48 | [] 49 | 50 | [src]

51 |
pub fn sort_by<T, F>(slice: &mut [T], compare: F) where F: FnMut(&T, &T) -> Ordering

Sorts the elements using the given compare function.

52 | 53 |

Examples

54 |
 55 | let mut numbers : Vec<i32> = vec!(0, 1, 6, 7, 2, 3, 4, 5);
 56 | dmsort::sort_by(&mut numbers, |a, b| b.cmp(a));
 57 | assert_eq!(numbers, vec!(7, 6, 5, 4, 3, 2, 1, 0));
58 |
59 | 60 | 61 | 62 | 63 | 108 | 109 | 110 | 111 | 115 | 116 | 117 | 118 | 119 | -------------------------------------------------------------------------------- /docs/dmsort/fn.sort_by_key.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | dmsort::sort_by_key - Rust 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 26 | 27 | 28 | 29 | 33 | 34 | 44 | 45 |
46 |

Function dmsort::sort_by_key 47 | 48 | [] 49 | 50 | [src]

51 |
pub fn sort_by_key<T, K, F>(slice: &mut [T], key: F) where K: Ord, F: FnMut(&T) -> K

Sorts the elements using the given key function.

52 | 53 |

Examples

54 |
 55 | let mut numbers : Vec<i32> = vec!(0, 1, 6, 7, 2, 3, 4, 5);
 56 | dmsort::sort_by_key(&mut numbers, |x| -x);
 57 | assert_eq!(numbers, vec!(7, 6, 5, 4, 3, 2, 1, 0));
58 |
59 | 60 | 61 | 62 | 63 | 108 | 109 | 110 | 111 | 115 | 116 | 117 | 118 | 119 | -------------------------------------------------------------------------------- /docs/dmsort/fn.sort_copy.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | dmsort::sort_copy - Rust 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 26 | 27 | 28 | 29 | 33 | 34 | 44 | 45 |
46 |

Function dmsort::sort_copy 47 | 48 | [] 49 | 50 | [src]

51 |
pub fn sort_copy<T: Copy + Ord>(slice: &mut [T]) -> usize

UNSTABLE! FOR INTERNAL USE ONLY.

52 |
53 | 54 | 55 | 56 | 57 | 102 | 103 | 104 | 105 | 109 | 110 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /docs/dmsort/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | dmsort - Rust 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 26 | 27 | 28 | 29 | 33 | 34 | 44 | 45 |
46 |

Crate dmsort 47 | 48 | [] 49 | 50 | [src]

51 |

Drop-Merge sort created and implemented by Emil Ernerfeldt.

52 | 53 |

Drop-Merge sort is an adaptive, unstable sorting algorithm designed for nearly-sorted data. 54 | An example use-case would be re-sorting an already sorted list after minor modifications.

55 | 56 |

Drop-Merge sort is especially useful for:

57 | 58 |
    59 |
  • Long lists (>10k elements)
  • 60 |
  • Where >80% of the data is already in-order
  • 61 |
  • The unsorted elements are evenly distributed.
  • 62 |
63 | 64 |

Expected number of comparisons is O(N + K * log(K)) where K is the number of elements not in order. 65 | Expected memory usage is O(K). 66 | Works best when K < 0.2 * N. 67 | The out-of-order elements are expected to be randomly distributed (NOT clumped).

68 | 69 |

Examples

70 |
 71 | extern crate dmsort;
 72 | 
 73 | fn main() {
 74 |     let mut numbers : Vec<i32> = vec!(0, 1, 6, 7, 2, 3, 4, 5);
 75 | 
 76 |     // Sort with custom key:
 77 |     dmsort::sort_by_key(&mut numbers, |x| -x);
 78 |     assert_eq!(numbers, vec!(7, 6, 5, 4, 3, 2, 1, 0));
 79 | 
 80 |     // Sort with Ord trait:
 81 |     dmsort::sort(&mut numbers);
 82 |     assert_eq!(numbers, vec!(0, 1, 2, 3, 4, 5, 6, 7));
 83 | 
 84 |     // Sort with custom compare:
 85 |     dmsort::sort_by(&mut numbers, |a, b| b.cmp(a));
 86 |     assert_eq!(numbers, vec!(7, 6, 5, 4, 3, 2, 1, 0));
 87 | }
88 |

Functions

89 | 90 | 91 | 93 | 96 | 97 | 98 | 100 | 103 | 104 | 105 | 107 | 110 | 111 | 112 | 114 | 117 |
sort 94 |

Sorts the elements using the Ord trait.

95 |
sort_by 101 |

Sorts the elements using the given compare function.

102 |
sort_by_key 108 |

Sorts the elements using the given key function.

109 |
sort_copy 115 |

UNSTABLE! FOR INTERNAL USE ONLY.

116 |
118 | 119 | 120 | 121 | 122 | 167 | 168 | 169 | 170 | 174 | 175 | 176 | 177 | 178 | -------------------------------------------------------------------------------- /docs/dmsort/sidebar-items.js: -------------------------------------------------------------------------------- 1 | initSidebarItems({"fn":[["sort","Sorts the elements using the Ord trait. # Examples ``` let mut numbers : Vec = vec!(0, 1, 6, 7, 2, 3, 4, 5); dmsort::sort(&mut numbers); assert_eq!(numbers, vec!(0, 1, 2, 3, 4, 5, 6, 7)); ```"],["sort_by","Sorts the elements using the given compare function. # Examples ``` let mut numbers : Vec = vec!(0, 1, 6, 7, 2, 3, 4, 5); dmsort::sort_by(&mut numbers, |a, b| b.cmp(a)); assert_eq!(numbers, vec!(7, 6, 5, 4, 3, 2, 1, 0)); ```"],["sort_by_key","Sorts the elements using the given key function. # Examples ``` let mut numbers : Vec = vec!(0, 1, 6, 7, 2, 3, 4, 5); dmsort::sort_by_key(&mut numbers, |x| -x); assert_eq!(numbers, vec!(7, 6, 5, 4, 3, 2, 1, 0)); ```"],["sort_copy","UNSTABLE! FOR INTERNAL USE ONLY."]]}); -------------------------------------------------------------------------------- /docs/dmsort/sort.v.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |

Redirecting to fn.sort.html...

8 | 9 | 10 | -------------------------------------------------------------------------------- /docs/dmsort/sort_by.v.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |

Redirecting to fn.sort_by.html...

8 | 9 | 10 | -------------------------------------------------------------------------------- /docs/dmsort/sort_by_key.v.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |

Redirecting to fn.sort_by_key.html...

8 | 9 | 10 | -------------------------------------------------------------------------------- /docs/dmsort/sort_copy.v.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |

Redirecting to fn.sort_copy.html...

8 | 9 | 10 | -------------------------------------------------------------------------------- /docs/main.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 The Rust Project Developers. See the COPYRIGHT 3 | * file at the top-level directory of this distribution and at 4 | * http://rust-lang.org/COPYRIGHT. 5 | * 6 | * Licensed under the Apache License, Version 2.0 or the MIT license 8 | * , at your 9 | * option. This file may not be copied, modified, or distributed 10 | * except according to those terms. 11 | */ 12 | 13 | /* General structure and fonts */ 14 | 15 | body { 16 | background-color: white; 17 | color: black; 18 | } 19 | 20 | h1, h2, h3:not(.impl):not(.method):not(.type):not(.tymethod), h4:not(.method):not(.type):not(.tymethod) { 21 | color: black; 22 | } 23 | h1.fqn { 24 | border-bottom-color: #D5D5D5; 25 | } 26 | h2, h3:not(.impl):not(.method):not(.type):not(.tymethod), h4:not(.method):not(.type):not(.tymethod) { 27 | border-bottom-color: #DDDDDD; 28 | } 29 | .in-band { 30 | background-color: white; 31 | } 32 | 33 | div.stability > em > code { 34 | background-color: initial; 35 | } 36 | 37 | .docblock code, .docblock-short code { 38 | background-color: #F5F5F5; 39 | } 40 | pre { 41 | background-color: #F5F5F5; 42 | } 43 | 44 | .sidebar .location { 45 | background: #e1e1e1; 46 | color: #333; 47 | } 48 | 49 | .block a:hover { 50 | background: #F5F5F5; 51 | } 52 | 53 | .line-numbers span { color: #c67e2d; } 54 | .line-numbers .line-highlighted { 55 | background-color: #f6fdb0 !important; 56 | } 57 | 58 | :target { background: #FDFFD3; } 59 | .content .highlighted { 60 | color: #000 !important; 61 | background-color: #ccc; 62 | } 63 | .content .highlighted a, .content .highlighted span { color: #000 !important; } 64 | .content .highlighted.trait { background-color: #fece7e; } 65 | .content .highlighted.mod { background-color: #afc6e4; } 66 | .content .highlighted.enum { background-color: #b4d1b9; } 67 | .content .highlighted.struct { background-color: #e7b1a0; } 68 | .content .highlighted.fn { background-color: #c6afb3; } 69 | .content .highlighted.method { background-color: #c6afb3; } 70 | .content .highlighted.tymethod { background-color: #c6afb3; } 71 | .content .highlighted.type { background-color: #c6afb3; } 72 | 73 | .docblock h1, .docblock h2, .docblock h3, .docblock h4, .docblock h5 { 74 | border-bottom-color: #DDD; 75 | } 76 | 77 | .docblock table { 78 | border-color: #ddd; 79 | } 80 | 81 | .docblock table td { 82 | border-top-color: #ddd; 83 | border-bottom-color: #ddd; 84 | } 85 | 86 | .docblock table th { 87 | border-top-color: #ddd; 88 | border-bottom-color: #ddd; 89 | } 90 | 91 | .content span.primitive, .content a.primitive, .block a.current.primitive { color: #39a7bf; } 92 | .content span.externcrate, 93 | .content span.mod, .content a.mod, .block a.current.mod { color: #4d76ae; } 94 | .content span.fn, .content a.fn, .block a.current.fn, 95 | .content span.method, .content a.method, .block a.current.method, 96 | .content span.tymethod, .content a.tymethod, .block a.current.tymethod, 97 | .content .fnname { color: #8c6067; } 98 | 99 | pre.rust .comment { color: #8E908C; } 100 | pre.rust .doccomment { color: #4D4D4C; } 101 | 102 | nav { 103 | border-bottom-color: #e0e0e0; 104 | } 105 | nav.main .current { 106 | border-top-color: #000; 107 | border-bottom-color: #000; 108 | } 109 | nav.main .separator { 110 | border: 1px solid #000; 111 | } 112 | a { 113 | color: #000; 114 | } 115 | 116 | .docblock a, .docblock-short a, .stability a { 117 | color: #3873AD; 118 | } 119 | 120 | a.test-arrow { 121 | color: #f5f5f5; 122 | } 123 | 124 | .content span.trait, .content a.trait, .block a.current.trait { color: #7c5af3; } 125 | 126 | .search-input { 127 | color: #555; 128 | box-shadow: 0 0 0 1px #e0e0e0, 0 0 0 2px transparent; 129 | background-color: white; 130 | } 131 | 132 | em.stab.unstable { background: #FFF5D6; border-color: #FFC600; } 133 | em.stab.deprecated { background: #F3DFFF; border-color: #7F0087; } 134 | -------------------------------------------------------------------------------- /docs/normalize.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v3.0.0 | MIT License | git.io/normalize */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0} 2 | -------------------------------------------------------------------------------- /docs/pdqsort/fn.sort.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | pdqsort::sort - Rust 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 26 | 27 | 28 | 29 | 33 | 34 | 44 | 45 |
46 |

Function pdqsort::sort 47 | 48 | [] 49 | 50 | [src]

51 |
pub fn sort<T>(v: &mut [T]) where T: Ord

Sorts a slice.

52 | 53 |

This sort is in-place, unstable, and O(n log n) worst-case.

54 | 55 |

The implementation is based on Orson Peters' pattern-defeating quicksort.

56 | 57 |

Examples

58 |
 59 | extern crate pdqsort;
 60 | 
 61 | let mut v = [-5, 4, 1, -3, 2];
 62 | pdqsort::sort(&mut v);
 63 | assert!(v == [-5, -3, 1, 2, 4]);
64 |
65 | 66 | 67 | 68 | 69 | 114 | 115 | 116 | 117 | 121 | 122 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /docs/pdqsort/fn.sort_by.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | pdqsort::sort_by - Rust 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 26 | 27 | 28 | 29 | 33 | 34 | 44 | 45 |
46 |

Function pdqsort::sort_by 47 | 48 | [] 49 | 50 | [src]

51 |
pub fn sort_by<T, F>(v: &mut [T], compare: F) where F: FnMut(&T, &T) -> Ordering

Sorts a slice using compare to compare elements.

52 | 53 |

This sort is in-place, unstable, and O(n log n) worst-case.

54 | 55 |

The implementation is based on Orson Peters' pattern-defeating quicksort.

56 | 57 |

Examples

58 |
 59 | extern crate pdqsort;
 60 | 
 61 | let mut v = [5, 4, 1, 3, 2];
 62 | pdqsort::sort_by(&mut v, |a, b| a.cmp(b));
 63 | assert!(v == [1, 2, 3, 4, 5]);
 64 | 
 65 | // reverse sorting
 66 | pdqsort::sort_by(&mut v, |a, b| b.cmp(a));
 67 | assert!(v == [5, 4, 3, 2, 1]);
68 |
69 | 70 | 71 | 72 | 73 | 118 | 119 | 120 | 121 | 125 | 126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /docs/pdqsort/fn.sort_by_key.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | pdqsort::sort_by_key - Rust 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 26 | 27 | 28 | 29 | 33 | 34 | 44 | 45 |
46 |

Function pdqsort::sort_by_key 47 | 48 | [] 49 | 50 | [src]

51 |
pub fn sort_by_key<T, B, F>(v: &mut [T], f: F) where F: FnMut(&T) -> B, B: Ord

Sorts a slice using f to extract a key to compare elements by.

52 | 53 |

This sort is in-place, unstable, and O(n log n) worst-case.

54 | 55 |

The implementation is based on Orson Peters' pattern-defeating quicksort.

56 | 57 |

Examples

58 |
 59 | extern crate pdqsort;
 60 | 
 61 | let mut v = [-5i32, 4, 1, -3, 2];
 62 | pdqsort::sort_by_key(&mut v, |k| k.abs());
 63 | assert!(v == [1, 2, -3, 4, -5]);
64 |
65 | 66 | 67 | 68 | 69 | 114 | 115 | 116 | 117 | 121 | 122 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /docs/pdqsort/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | pdqsort - Rust 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 26 | 27 | 28 | 29 | 33 | 34 | 44 | 45 |
46 |

Crate pdqsort 47 | 48 | [] 49 | 50 | [src]

51 |

Pattern-defeating quicksort.

52 | 53 |

This sort is significantly faster than the standard sort in Rust. In particular, it sorts 54 | random arrays of integers approximately 40% faster. The key drawback is that it is an unstable 55 | sort (i.e. may reorder equal elements). However, in most cases stability doesn't matter anyway.

56 | 57 |

The algorithm was designed by Orson Peters and first published at: 58 | https://github.com/orlp/pdqsort

59 | 60 |

Quoting it's designer: "Pattern-defeating quicksort (pdqsort) is a novel sorting algorithm 61 | that combines the fast average case of randomized quicksort with the fast worst case of 62 | heapsort, while achieving linear time on inputs with certain patterns. pdqsort is an extension 63 | and improvement of David Musser's introsort."

64 | 65 |

Properties

66 |
    67 |
  • Best-case running time is O(n).
  • 68 |
  • Worst-case running time is O(n log n).
  • 69 |
  • Unstable, i.e. may reorder equal elements.
  • 70 |
  • Does not allocate additional memory.
  • 71 |
  • Uses #![no_std].
  • 72 |
73 | 74 |

Examples

75 |
 76 | extern crate pdqsort;
 77 | 
 78 | let mut v = [-5i32, 4, 1, -3, 2];
 79 | 
 80 | pdqsort::sort(&mut v);
 81 | assert!(v == [-5, -3, 1, 2, 4]);
 82 | 
 83 | pdqsort::sort_by(&mut v, |a, b| b.cmp(a));
 84 | assert!(v == [4, 2, 1, -3, -5]);
 85 | 
 86 | pdqsort::sort_by_key(&mut v, |k| k.abs());
 87 | assert!(v == [1, 2, -3, 4, -5]);
88 |

Functions

89 | 90 | 91 | 93 | 96 | 97 | 98 | 100 | 103 | 104 | 105 | 107 | 110 |
sort 94 |

Sorts a slice.

95 |
sort_by 101 |

Sorts a slice using compare to compare elements.

102 |
sort_by_key 108 |

Sorts a slice using f to extract a key to compare elements by.

109 |
111 | 112 | 113 | 114 | 115 | 160 | 161 | 162 | 163 | 167 | 168 | 169 | 170 | 171 | -------------------------------------------------------------------------------- /docs/pdqsort/sidebar-items.js: -------------------------------------------------------------------------------- 1 | initSidebarItems({"fn":[["sort","Sorts a slice."],["sort_by","Sorts a slice using `compare` to compare elements."],["sort_by_key","Sorts a slice using `f` to extract a key to compare elements by."]]}); -------------------------------------------------------------------------------- /docs/pdqsort/sort.v.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |

Redirecting to fn.sort.html...

8 | 9 | 10 | -------------------------------------------------------------------------------- /docs/pdqsort/sort_by.v.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |

Redirecting to fn.sort_by.html...

8 | 9 | 10 | -------------------------------------------------------------------------------- /docs/pdqsort/sort_by_key.v.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |

Redirecting to fn.sort_by_key.html...

8 | 9 | 10 | -------------------------------------------------------------------------------- /docs/rustdoc.css: -------------------------------------------------------------------------------- 1 | @import "normalize.css"; 2 | 3 | /** 4 | * Copyright 2013 The Rust Project Developers. See the COPYRIGHT 5 | * file at the top-level directory of this distribution and at 6 | * http://rust-lang.org/COPYRIGHT. 7 | * 8 | * Licensed under the Apache License, Version 2.0 or the MIT license 10 | * , at your 11 | * option. This file may not be copied, modified, or distributed 12 | * except according to those terms. 13 | */ 14 | 15 | /* See FiraSans-LICENSE.txt for the Fira Sans license. */ 16 | @font-face { 17 | font-family: 'Fira Sans'; 18 | font-style: normal; 19 | font-weight: 400; 20 | src: local('Fira Sans'), url("FiraSans-Regular.woff") format('woff'); 21 | } 22 | @font-face { 23 | font-family: 'Fira Sans'; 24 | font-style: normal; 25 | font-weight: 500; 26 | src: local('Fira Sans Medium'), url("FiraSans-Medium.woff") format('woff'); 27 | } 28 | 29 | /* See SourceSerifPro-LICENSE.txt for the Source Serif Pro license and 30 | * Heuristica-LICENSE.txt for the Heuristica license. */ 31 | @font-face { 32 | font-family: 'Source Serif Pro'; 33 | font-style: normal; 34 | font-weight: 400; 35 | src: local('Source Serif Pro'), url("SourceSerifPro-Regular.woff") format('woff'); 36 | } 37 | @font-face { 38 | font-family: 'Source Serif Pro'; 39 | font-style: italic; 40 | font-weight: 400; 41 | src: url("Heuristica-Italic.woff") format('woff'); 42 | } 43 | @font-face { 44 | font-family: 'Source Serif Pro'; 45 | font-style: normal; 46 | font-weight: 700; 47 | src: local('Source Serif Pro Bold'), url("SourceSerifPro-Bold.woff") format('woff'); 48 | } 49 | 50 | /* See SourceCodePro-LICENSE.txt for the Source Code Pro license. */ 51 | @font-face { 52 | font-family: 'Source Code Pro'; 53 | font-style: normal; 54 | font-weight: 400; 55 | /* Avoid using locally installed font because bad versions are in circulation: 56 | * see https://github.com/rust-lang/rust/issues/24355 */ 57 | src: url("SourceCodePro-Regular.woff") format('woff'); 58 | } 59 | @font-face { 60 | font-family: 'Source Code Pro'; 61 | font-style: normal; 62 | font-weight: 600; 63 | src: url("SourceCodePro-Semibold.woff") format('woff'); 64 | } 65 | 66 | * { 67 | -webkit-box-sizing: border-box; 68 | -moz-box-sizing: border-box; 69 | box-sizing: border-box; 70 | } 71 | 72 | /* General structure and fonts */ 73 | 74 | body { 75 | font: 16px/1.4 "Source Serif Pro", Georgia, Times, "Times New Roman", serif; 76 | margin: 0; 77 | position: relative; 78 | padding: 10px 15px 20px 15px; 79 | 80 | -webkit-font-feature-settings: "kern", "liga"; 81 | -moz-font-feature-settings: "kern", "liga"; 82 | font-feature-settings: "kern", "liga"; 83 | } 84 | 85 | h1 { 86 | font-size: 1.5em; 87 | } 88 | h2 { 89 | font-size: 1.4em; 90 | } 91 | h3 { 92 | font-size: 1.3em; 93 | } 94 | h1, h2, h3:not(.impl):not(.method):not(.type):not(.tymethod), h4:not(.method):not(.type):not(.tymethod) { 95 | font-weight: 500; 96 | margin: 20px 0 15px 0; 97 | padding-bottom: 6px; 98 | } 99 | h1.fqn { 100 | border-bottom: 1px dashed; 101 | margin-top: 0; 102 | position: relative; 103 | } 104 | h2, h3:not(.impl):not(.method):not(.type):not(.tymethod), h4:not(.method):not(.type):not(.tymethod) { 105 | border-bottom: 1px solid; 106 | } 107 | h3.impl, h3.method, h4.method, h3.type, h4.type { 108 | font-weight: 600; 109 | margin-top: 10px; 110 | margin-bottom: 10px; 111 | position: relative; 112 | } 113 | h3.impl, h3.method, h3.type { 114 | margin-top: 15px; 115 | } 116 | h1, h2, h3, h4, .sidebar, a.source, .search-input, .content table :not(code)>a, .collapse-toggle { 117 | font-family: "Fira Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; 118 | } 119 | 120 | ol, ul { 121 | padding-left: 25px; 122 | } 123 | ul ul, ol ul, ul ol, ol ol { 124 | margin-bottom: 0; 125 | } 126 | 127 | p { 128 | margin: 0 0 .6em 0; 129 | } 130 | 131 | code, pre { 132 | font-family: "Source Code Pro", Menlo, Monaco, Consolas, "DejaVu Sans Mono", Inconsolata, monospace; 133 | white-space: pre-wrap; 134 | } 135 | .docblock code, .docblock-short code { 136 | border-radius: 3px; 137 | padding: 0 0.2em; 138 | } 139 | .docblock pre code, .docblock-short pre code { 140 | padding: 0; 141 | } 142 | pre { 143 | padding: 14px; 144 | } 145 | 146 | .source pre { 147 | padding: 20px; 148 | } 149 | 150 | img { 151 | max-width: 100%; 152 | } 153 | 154 | .content.source { 155 | margin-top: 50px; 156 | max-width: none; 157 | overflow: visible; 158 | margin-left: 0px; 159 | min-width: 70em; 160 | } 161 | 162 | nav.sub { 163 | font-size: 16px; 164 | text-transform: uppercase; 165 | } 166 | 167 | .sidebar { 168 | width: 200px; 169 | position: absolute; 170 | left: 0; 171 | top: 0; 172 | min-height: 100%; 173 | } 174 | 175 | .content, nav { max-width: 960px; } 176 | 177 | /* Everything else */ 178 | 179 | .js-only, .hidden { display: none !important; } 180 | 181 | .sidebar { 182 | padding: 10px; 183 | } 184 | .sidebar img { 185 | margin: 20px auto; 186 | display: block; 187 | } 188 | 189 | .sidebar .location { 190 | font-size: 17px; 191 | margin: 30px 0 20px 0; 192 | text-align: center; 193 | } 194 | 195 | .location a:first-child { font-weight: 500; } 196 | 197 | .block { 198 | padding: 0 10px; 199 | margin-bottom: 14px; 200 | } 201 | .block h2, .block h3 { 202 | margin-top: 0; 203 | margin-bottom: 8px; 204 | text-align: center; 205 | } 206 | .block ul, .block li { 207 | margin: 0; 208 | padding: 0; 209 | list-style: none; 210 | } 211 | 212 | .block a { 213 | display: block; 214 | text-overflow: ellipsis; 215 | overflow: hidden; 216 | line-height: 15px; 217 | padding: 7px 5px; 218 | font-size: 14px; 219 | font-weight: 300; 220 | transition: border 500ms ease-out; 221 | } 222 | 223 | .content { 224 | padding: 15px 0; 225 | } 226 | 227 | .content.source pre.rust { 228 | white-space: pre; 229 | overflow: auto; 230 | padding-left: 0; 231 | } 232 | .content pre.line-numbers { 233 | float: left; 234 | border: none; 235 | position: relative; 236 | 237 | -webkit-user-select: none; 238 | -moz-user-select: none; 239 | -ms-user-select: none; 240 | user-select: none; 241 | } 242 | .line-numbers span { cursor: pointer; } 243 | 244 | .docblock-short p { 245 | display: inline; 246 | } 247 | 248 | .docblock-short.nowrap { 249 | display: block; 250 | overflow: hidden; 251 | white-space: nowrap; 252 | text-overflow: ellipsis; 253 | } 254 | 255 | .docblock-short p { 256 | overflow: hidden; 257 | text-overflow: ellipsis; 258 | margin: 0; 259 | } 260 | .docblock-short code { white-space: nowrap; } 261 | 262 | .docblock h1, .docblock h2, .docblock h3, .docblock h4, .docblock h5 { 263 | border-bottom: 1px solid; 264 | } 265 | 266 | .docblock h1 { font-size: 1.3em; } 267 | .docblock h2 { font-size: 1.15em; } 268 | .docblock h3, .docblock h4, .docblock h5 { font-size: 1em; } 269 | 270 | .docblock { 271 | margin-left: 24px; 272 | } 273 | 274 | .content .out-of-band { 275 | font-size: 23px; 276 | margin: 0px; 277 | padding: 0px; 278 | text-align: right; 279 | display: inline-block; 280 | font-weight: normal; 281 | position: absolute; 282 | right: 0; 283 | } 284 | 285 | h3.impl > .out-of-band { 286 | font-size: 21px; 287 | } 288 | 289 | h4 > code, h3 > code, .invisible > code { 290 | position: inherit; 291 | } 292 | 293 | .in-band, code { 294 | z-index: 5; 295 | } 296 | 297 | .invisible { 298 | background: rgba(0, 0, 0, 0); 299 | width: 100%; 300 | display: inline-block; 301 | } 302 | 303 | .content .in-band { 304 | margin: 0px; 305 | padding: 0px; 306 | display: inline-block; 307 | } 308 | 309 | #main { position: relative; } 310 | #main > .since { 311 | top: inherit; 312 | font-family: "Fira Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; 313 | } 314 | 315 | .content table { 316 | border-spacing: 0 5px; 317 | border-collapse: separate; 318 | } 319 | .content td { vertical-align: top; } 320 | .content td:first-child { padding-right: 20px; } 321 | .content td p:first-child { margin-top: 0; } 322 | .content td h1, .content td h2 { margin-left: 0; font-size: 1.1em; } 323 | 324 | .docblock table { 325 | border: 1px solid; 326 | margin: .5em 0; 327 | border-collapse: collapse; 328 | width: 100%; 329 | } 330 | 331 | .docblock table td { 332 | padding: .5em; 333 | border-top: 1px dashed; 334 | border-bottom: 1px dashed; 335 | } 336 | 337 | .docblock table th { 338 | padding: .5em; 339 | text-align: left; 340 | border-top: 1px solid; 341 | border-bottom: 1px solid; 342 | } 343 | 344 | .fields + table { 345 | margin-bottom: 1em; 346 | } 347 | 348 | .content .item-list { 349 | list-style-type: none; 350 | padding: 0; 351 | } 352 | 353 | .content .item-list li { margin-bottom: 3px; } 354 | 355 | .content .multi-column { 356 | -moz-column-count: 5; 357 | -moz-column-gap: 2.5em; 358 | -webkit-column-count: 5; 359 | -webkit-column-gap: 2.5em; 360 | column-count: 5; 361 | column-gap: 2.5em; 362 | } 363 | .content .multi-column li { width: 100%; display: inline-block; } 364 | 365 | .content .method { 366 | font-size: 1em; 367 | position: relative; 368 | } 369 | /* Shift "where ..." part of method or fn definition down a line */ 370 | .content .method .where, 371 | .content .fn .where, 372 | .content .where.fmt-newline { 373 | display: block; 374 | } 375 | /* Bit of whitespace to indent it */ 376 | .content .method .where::before, 377 | .content .fn .where::before, 378 | .content .where.fmt-newline::before { 379 | content: ' '; 380 | } 381 | 382 | .content .methods > div { margin-left: 40px; } 383 | 384 | .content .impl-items .docblock, .content .impl-items .stability { 385 | margin-left: 40px; 386 | } 387 | .content .impl-items .method, .content .impl-items > .type { 388 | margin-left: 20px; 389 | } 390 | 391 | .content .stability code { 392 | font-size: 90%; 393 | } 394 | 395 | /* Shift where in trait listing down a line */ 396 | pre.trait .where::before { 397 | content: '\a '; 398 | } 399 | 400 | nav { 401 | border-bottom: 1px solid; 402 | padding-bottom: 10px; 403 | margin-bottom: 10px; 404 | } 405 | nav.main { 406 | padding: 20px 0; 407 | text-align: center; 408 | } 409 | nav.main .current { 410 | border-top: 1px solid; 411 | border-bottom: 1px solid; 412 | } 413 | nav.main .separator { 414 | border: 1px solid; 415 | display: inline-block; 416 | height: 23px; 417 | margin: 0 20px; 418 | } 419 | nav.sum { text-align: right; } 420 | nav.sub form { display: inline; } 421 | 422 | nav.sub, .content { 423 | margin-left: 230px; 424 | } 425 | 426 | a { 427 | text-decoration: none; 428 | background: transparent; 429 | } 430 | 431 | .docblock a:hover, .docblock-short a:hover, .stability a { 432 | text-decoration: underline; 433 | } 434 | 435 | .content span.enum, .content a.enum, .block a.current.enum { color: #5e9766; } 436 | .content span.struct, .content a.struct, .block a.current.struct { color: #df3600; } 437 | .content span.type, .content a.type, .block a.current.type { color: #e57300; } 438 | .content span.macro, .content a.macro, .block a.current.macro { color: #068000; } 439 | .block a.current.crate { font-weight: 500; } 440 | 441 | .search-input { 442 | width: 100%; 443 | /* Override Normalize.css: we have margins and do 444 | not want to overflow - the `moz` attribute is necessary 445 | until Firefox 29, too early to drop at this point */ 446 | -moz-box-sizing: border-box !important; 447 | box-sizing: border-box !important; 448 | outline: none; 449 | border: none; 450 | border-radius: 1px; 451 | margin-top: 5px; 452 | padding: 10px 16px; 453 | font-size: 17px; 454 | transition: border-color 300ms ease; 455 | transition: border-radius 300ms ease-in-out; 456 | transition: box-shadow 300ms ease-in-out; 457 | } 458 | 459 | .search-input:focus { 460 | border-color: #66afe9; 461 | border-radius: 2px; 462 | border: 0; 463 | outline: 0; 464 | box-shadow: 0 0 8px #078dd8; 465 | } 466 | 467 | .search-results .desc { 468 | white-space: nowrap; 469 | text-overflow: ellipsis; 470 | overflow: hidden; 471 | display: block; 472 | } 473 | 474 | .search-results a { 475 | display: block; 476 | } 477 | 478 | .content .search-results td:first-child { padding-right: 0; } 479 | .content .search-results td:first-child a { padding-right: 10px; } 480 | 481 | tr.result span.primitive::after { content: ' (primitive type)'; font-style: italic; color: black; 482 | } 483 | 484 | body.blur > :not(#help) { 485 | filter: blur(8px); 486 | -webkit-filter: blur(8px); 487 | opacity: .7; 488 | } 489 | 490 | #help { 491 | width: 100%; 492 | height: 100vh; 493 | position: fixed; 494 | top: 0; 495 | left: 0; 496 | display: flex; 497 | justify-content: center; 498 | align-items: center; 499 | } 500 | #help > div { 501 | flex: 0 0 auto; 502 | background: #e9e9e9; 503 | box-shadow: 0 0 6px rgba(0,0,0,.2); 504 | width: 550px; 505 | height: 330px; 506 | border: 1px solid #bfbfbf; 507 | } 508 | #help dt { 509 | float: left; 510 | border-radius: 4px; 511 | border: 1px solid #bfbfbf; 512 | background: #fff; 513 | width: 23px; 514 | text-align: center; 515 | clear: left; 516 | display: block; 517 | margin-top: -1px; 518 | } 519 | #help dd { margin: 5px 33px; } 520 | #help .infos { padding-left: 0; } 521 | #help h1, #help h2 { margin-top: 0; } 522 | #help > div div { 523 | width: 50%; 524 | float: left; 525 | padding: 20px; 526 | } 527 | 528 | em.stab { 529 | display: inline-block; 530 | border-width: 1px; 531 | border-style: solid; 532 | padding: 3px; 533 | margin-bottom: 5px; 534 | font-size: 90%; 535 | font-style: normal; 536 | } 537 | em.stab p { 538 | display: inline; 539 | } 540 | 541 | .module-item .stab { 542 | border-width: 0; 543 | padding: 0; 544 | margin: 0; 545 | background: inherit !important; 546 | } 547 | 548 | .module-item.unstable { 549 | opacity: 0.65; 550 | } 551 | 552 | .since { 553 | font-weight: normal; 554 | font-size: initial; 555 | color: grey; 556 | position: absolute; 557 | right: 0; 558 | top: 0; 559 | } 560 | 561 | .variants_table { 562 | width: 100%; 563 | } 564 | 565 | .variants_table tbody tr td:first-child { 566 | width: 1%; /* make the variant name as small as possible */ 567 | } 568 | 569 | td.summary-column { 570 | width: 100%; 571 | } 572 | 573 | .summary { 574 | padding-right: 0px; 575 | } 576 | 577 | .line-numbers :target { background-color: transparent; } 578 | 579 | /* Code highlighting */ 580 | pre.rust .kw { color: #8959A8; } 581 | pre.rust .kw-2, pre.rust .prelude-ty { color: #4271AE; } 582 | pre.rust .number, pre.rust .string { color: #718C00; } 583 | pre.rust .self, pre.rust .bool-val, pre.rust .prelude-val, 584 | pre.rust .attribute, pre.rust .attribute .ident { color: #C82829; } 585 | pre.rust .macro, pre.rust .macro-nonterminal { color: #3E999F; } 586 | pre.rust .lifetime { color: #B76514; } 587 | pre.rust .question-mark { 588 | color: #ff9011; 589 | font-weight: bold; 590 | } 591 | 592 | pre.rust { position: relative; } 593 | a.test-arrow { 594 | background-color: rgba(78, 139, 202, 0.2); 595 | display: inline-block; 596 | position: absolute; 597 | padding: 5px 10px 5px 10px; 598 | border-radius: 5px; 599 | font-size: 130%; 600 | top: 5px; 601 | right: 5px; 602 | } 603 | a.test-arrow:hover{ 604 | background-color: #4e8bca; 605 | text-decoration: none; 606 | } 607 | 608 | .section-header:hover a:after { 609 | content: '\2002\00a7\2002'; 610 | } 611 | 612 | .section-header:hover a { 613 | text-decoration: none; 614 | } 615 | 616 | .section-header a { 617 | color: inherit; 618 | } 619 | 620 | .collapse-toggle { 621 | font-weight: 300; 622 | position: absolute; 623 | left: -23px; 624 | color: #999; 625 | top: 0; 626 | } 627 | 628 | .toggle-wrapper > .collapse-toggle { 629 | left: -24px; 630 | margin-top: 0px; 631 | } 632 | 633 | .toggle-wrapper { 634 | position: relative; 635 | } 636 | 637 | .toggle-wrapper.collapsed { 638 | height: 1em; 639 | transition: height .2s; 640 | } 641 | 642 | .collapse-toggle > .inner { 643 | display: inline-block; 644 | width: 1.2ch; 645 | text-align: center; 646 | } 647 | 648 | .toggle-label { 649 | color: #999; 650 | } 651 | 652 | .ghost { 653 | display: none; 654 | } 655 | 656 | .ghost + .since { 657 | position: initial; 658 | display: table-cell; 659 | } 660 | 661 | .since + .srclink { 662 | display: table-cell; 663 | padding-left: 10px; 664 | } 665 | 666 | span.since { 667 | position: initial; 668 | font-size: 20px; 669 | margin-right: 5px; 670 | } 671 | 672 | .toggle-wrapper > .collapse-toggle { 673 | left: 0; 674 | } 675 | 676 | .variant + .toggle-wrapper > a { 677 | margin-top: 5px; 678 | } 679 | 680 | .sub-variant, .sub-variant > h3 { 681 | margin-top: 0 !important; 682 | } 683 | 684 | .enum > .toggle-wrapper + .docblock, .struct > .toggle-wrapper + .docblock { 685 | margin-left: 30px; 686 | margin-bottom: 20px; 687 | margin-top: 5px; 688 | } 689 | 690 | .enum > .collapsed, .struct > .collapsed { 691 | margin-bottom: 25px; 692 | } 693 | 694 | .enum .variant, .struct .structfield { 695 | display: block; 696 | } 697 | 698 | .attributes { 699 | display: block; 700 | margin: 0px 0px 0px 30px !important; 701 | } 702 | .toggle-attributes.collapsed { 703 | margin-bottom: 5px; 704 | } 705 | 706 | :target > code { 707 | background: #FDFFD3; 708 | opacity: 1; 709 | } 710 | 711 | /* Media Queries */ 712 | 713 | @media (max-width: 700px) { 714 | body { 715 | padding-top: 0px; 716 | } 717 | 718 | .sidebar { 719 | height: 40px; 720 | min-height: 40px; 721 | width: 100%; 722 | margin: 0px; 723 | padding: 0px; 724 | position: static; 725 | } 726 | 727 | .sidebar .location { 728 | float: right; 729 | margin: 0px; 730 | padding: 3px 10px 1px 10px; 731 | min-height: 39px; 732 | background: inherit; 733 | text-align: left; 734 | font-size: 24px; 735 | } 736 | 737 | .sidebar .location:empty { 738 | padding: 0; 739 | } 740 | 741 | .sidebar img { 742 | width: 35px; 743 | margin-top: 5px; 744 | margin-bottom: 0px; 745 | float: left; 746 | } 747 | 748 | nav.sub { 749 | margin: 0 auto; 750 | } 751 | 752 | .sidebar .block { 753 | display: none; 754 | } 755 | 756 | .content { 757 | margin-left: 0px; 758 | } 759 | 760 | .content .in-band { 761 | width: 100%; 762 | } 763 | 764 | .content .out-of-band { 765 | display: none; 766 | } 767 | 768 | .toggle-wrapper > .collapse-toggle { 769 | left: 0px; 770 | } 771 | 772 | .toggle-wrapper { 773 | height: 1.5em; 774 | } 775 | } 776 | 777 | @media print { 778 | nav.sub, .content .out-of-band, .collapse-toggle { 779 | display: none; 780 | } 781 | } 782 | -------------------------------------------------------------------------------- /docs/search-index.js: -------------------------------------------------------------------------------- 1 | var searchIndex = {}; 2 | searchIndex["dmsort"] = {"doc":"Drop-Merge sort created and implemented by Emil Ernerfeldt.","items":[[5,"sort","dmsort","Sorts the elements using the Ord trait.\n# Examples\n```\nlet mut numbers : Vec<i32> = vec!(0, 1, 6, 7, 2, 3, 4, 5);\ndmsort::sort(&mut numbers);\nassert_eq!(numbers, vec!(0, 1, 2, 3, 4, 5, 6, 7));\n```",null,null],[5,"sort_by","","Sorts the elements using the given compare function.\n# Examples\n```\nlet mut numbers : Vec<i32> = vec!(0, 1, 6, 7, 2, 3, 4, 5);\ndmsort::sort_by(&mut numbers, |a, b| b.cmp(a));\nassert_eq!(numbers, vec!(7, 6, 5, 4, 3, 2, 1, 0));\n```",null,null],[5,"sort_by_key","","Sorts the elements using the given key function.\n# Examples\n```\nlet mut numbers : Vec<i32> = vec!(0, 1, 6, 7, 2, 3, 4, 5);\ndmsort::sort_by_key(&mut numbers, |x| -x);\nassert_eq!(numbers, vec!(7, 6, 5, 4, 3, 2, 1, 0));\n```",null,null],[5,"sort_copy","","UNSTABLE! FOR INTERNAL USE ONLY.",null,null]],"paths":[]}; 3 | searchIndex["pdqsort"] = {"doc":"Pattern-defeating quicksort.","items":[[5,"sort","pdqsort","Sorts a slice.",null,null],[5,"sort_by_key","","Sorts a slice using `f` to extract a key to compare elements by.",null,null],[5,"sort_by","","Sorts a slice using `compare` to compare elements.",null,null]],"paths":[]}; 4 | initSearch(searchIndex); 5 | -------------------------------------------------------------------------------- /docs/src/dmsort/lib.rs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | lib.rs.html -- source 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 26 | 27 | 28 | 29 | 33 | 34 | 44 | 45 |
 1
 46 |  2
 47 |  3
 48 |  4
 49 |  5
 50 |  6
 51 |  7
 52 |  8
 53 |  9
 54 | 10
 55 | 11
 56 | 12
 57 | 13
 58 | 14
 59 | 15
 60 | 16
 61 | 17
 62 | 18
 63 | 19
 64 | 20
 65 | 21
 66 | 22
 67 | 23
 68 | 24
 69 | 25
 70 | 26
 71 | 27
 72 | 28
 73 | 29
 74 | 30
 75 | 31
 76 | 32
 77 | 33
 78 | 34
 79 | 35
 80 | 36
 81 | 37
 82 | 38
 83 | 39
 84 | 40
 85 | 41
 86 | 42
 87 | 43
 88 | 
 89 | //! Drop-Merge sort created and implemented by Emil Ernerfeldt.
 90 | //!
 91 | //! Drop-Merge sort is an adaptive, unstable sorting algorithm designed for nearly-sorted data.
 92 | //! An example use-case would be re-sorting an already sorted list after minor modifications.
 93 | //!
 94 | //! Drop-Merge sort is especially useful for:
 95 | //!
 96 | //! * Long lists (>10k elements)
 97 | //! * Where >80% of the data is already in-order
 98 | //! * The unsorted elements are evenly distributed.
 99 | //!
100 | //! Expected number of comparisons is `O(N + K * log(K))` where `K` is the number of elements not in order.
101 | //! Expected memory usage is `O(K)`.
102 | //! Works best when `K < 0.2 * N`.
103 | //! The out-of-order elements are expected to be randomly distributed (NOT clumped).
104 | //!
105 | //! # Examples
106 | //! ```
107 | //! extern crate dmsort;
108 | //!
109 | //! fn main() {
110 | //! 	let mut numbers : Vec<i32> = vec!(0, 1, 6, 7, 2, 3, 4, 5);
111 | //!
112 | //! 	// Sort with custom key:
113 | //! 	dmsort::sort_by_key(&mut numbers, |x| -x);
114 | //! 	assert_eq!(numbers, vec!(7, 6, 5, 4, 3, 2, 1, 0));
115 | //!
116 | //! 	// Sort with Ord trait:
117 | //! 	dmsort::sort(&mut numbers);
118 | //! 	assert_eq!(numbers, vec!(0, 1, 2, 3, 4, 5, 6, 7));
119 | //!
120 | //! 	// Sort with custom compare:
121 | //! 	dmsort::sort_by(&mut numbers, |a, b| b.cmp(a));
122 | //! 	assert_eq!(numbers, vec!(7, 6, 5, 4, 3, 2, 1, 0));
123 | //! }
124 | //! ```
125 | 
126 | pub use dmsort::{sort, sort_by, sort_by_key};
127 | 
128 | /// For in module-level testing only. TODO: this shouldn't be public.
129 | pub use dmsort::sort_copy;
130 | 
131 | mod dmsort;
132 | 
133 |
134 | 135 | 136 | 137 | 138 | 183 | 184 | 185 | 186 | 190 | 191 | 192 | 193 | 194 | -------------------------------------------------------------------------------- /docs/src/dmsort/src/lib.rs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | lib.rs.html -- source 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 26 | 27 | 28 | 29 | 33 | 34 | 44 | 45 |
 1
 46 |  2
 47 |  3
 48 |  4
 49 |  5
 50 |  6
 51 |  7
 52 |  8
 53 |  9
 54 | 10
 55 | 11
 56 | 12
 57 | 13
 58 | 14
 59 | 15
 60 | 16
 61 | 17
 62 | 18
 63 | 19
 64 | 20
 65 | 21
 66 | 22
 67 | 23
 68 | 24
 69 | 25
 70 | 26
 71 | 27
 72 | 28
 73 | 29
 74 | 30
 75 | 31
 76 | 32
 77 | 33
 78 | 34
 79 | 35
 80 | 36
 81 | 37
 82 | 38
 83 | 39
 84 | 40
 85 | 
 86 | //! Drop-Merge sort created and implemented by Emil Ernerfeldt.
 87 | //!
 88 | //! Drop-Merge sort is an adaptive unstable sorting algorithm.
 89 | //! It is designed for nearly-sorted data.
 90 | //! Drop-Merge sort is especially useful for when >80% of the data is already in-order,
 91 | //! and the unsorted elements are evenly distributed.
 92 | //! An example use-case would be re-sorting an already sorted list after minor modifications.
 93 | //!
 94 | //! Expected number of comparisons is `O(N + K * log(K))` where `K` is the number of elements not in order.
 95 | //! Expected memory usage is `O(K)`.
 96 | //! Works best for when `K < 0.2 * N`.
 97 | //! The out-of-order elements are expected to be randomly distributed (NOT clumped).
 98 | //!
 99 | //! # Examples
100 | //! ```
101 | //! extern crate dmsort;
102 | //!
103 | //! fn main() {
104 | //! 	let mut numbers : Vec<i32> = vec!(0, 1, 6, 7, 2, 3, 4, 5);
105 | //!
106 | //! 	// Sort with custom key:
107 | //! 	dmsort::sort_by_key(&mut numbers, |x| -x);
108 | //! 	assert_eq!(numbers, vec!(7, 6, 5, 4, 3, 2, 1, 0));
109 | //!
110 | //! 	// Sort with Ord trait:
111 | //! 	dmsort::sort(&mut numbers);
112 | //! 	assert_eq!(numbers, vec!(0, 1, 2, 3, 4, 5, 6, 7));
113 | //!
114 | //! 	// Sort with custom compare:
115 | //! 	dmsort::sort_by(&mut numbers, |a, b| b.cmp(a));
116 | //! 	assert_eq!(numbers, vec!(7, 6, 5, 4, 3, 2, 1, 0));
117 | //! }
118 | //! ```
119 | 
120 | pub use dmsort::{sort, sort_by, sort_by_key};
121 | 
122 | /// For in module-level testing only. TODO: this shouldn't be public.
123 | pub use dmsort::{sort_copy};
124 | 
125 | mod dmsort;
126 | 
127 |
128 | 129 | 130 | 131 | 132 | 177 | 178 | 179 | 180 | 184 | 185 | 186 | 187 | 188 | -------------------------------------------------------------------------------- /examples/example.rs: -------------------------------------------------------------------------------- 1 | extern crate dmsort; 2 | 3 | fn main() { 4 | let mut numbers: Vec = vec![0, 1, 6, 7, 2, 3, 4, 5]; 5 | 6 | // Sort with custom key: 7 | dmsort::sort_by_key(&mut numbers, |x| -x); 8 | assert_eq!(numbers, vec![7, 6, 5, 4, 3, 2, 1, 0]); 9 | 10 | // Sort with Ord trait: 11 | dmsort::sort(&mut numbers); 12 | assert_eq!(numbers, vec![0, 1, 2, 3, 4, 5, 6, 7]); 13 | 14 | // Sort with custom compare: 15 | dmsort::sort_by(&mut numbers, |a, b| b.cmp(a)); 16 | assert_eq!(numbers, vec![7, 6, 5, 4, 3, 2, 1, 0]); 17 | } 18 | -------------------------------------------------------------------------------- /images/comparisons_1000_i32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilk/drop-merge-sort/ccef37cca71d2b538b1f17856a458f5cd74dfaf0/images/comparisons_1000_i32.png -------------------------------------------------------------------------------- /images/comparisons_1000_string.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilk/drop-merge-sort/ccef37cca71d2b538b1f17856a458f5cd74dfaf0/images/comparisons_1000_string.png -------------------------------------------------------------------------------- /images/comparisons_100_i32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilk/drop-merge-sort/ccef37cca71d2b538b1f17856a458f5cd74dfaf0/images/comparisons_100_i32.png -------------------------------------------------------------------------------- /images/comparisons_100_string.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilk/drop-merge-sort/ccef37cca71d2b538b1f17856a458f5cd74dfaf0/images/comparisons_100_string.png -------------------------------------------------------------------------------- /images/comparisons_100k_i32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilk/drop-merge-sort/ccef37cca71d2b538b1f17856a458f5cd74dfaf0/images/comparisons_100k_i32.png -------------------------------------------------------------------------------- /images/comparisons_100k_string.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilk/drop-merge-sort/ccef37cca71d2b538b1f17856a458f5cd74dfaf0/images/comparisons_100k_string.png -------------------------------------------------------------------------------- /images/comparisons_10M_i32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilk/drop-merge-sort/ccef37cca71d2b538b1f17856a458f5cd74dfaf0/images/comparisons_10M_i32.png -------------------------------------------------------------------------------- /images/comparisons_10k_i32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilk/drop-merge-sort/ccef37cca71d2b538b1f17856a458f5cd74dfaf0/images/comparisons_10k_i32.png -------------------------------------------------------------------------------- /images/comparisons_10k_string.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilk/drop-merge-sort/ccef37cca71d2b538b1f17856a458f5cd74dfaf0/images/comparisons_10k_string.png -------------------------------------------------------------------------------- /images/comparisons_1M_i32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilk/drop-merge-sort/ccef37cca71d2b538b1f17856a458f5cd74dfaf0/images/comparisons_1M_i32.png -------------------------------------------------------------------------------- /images/comparisons_1M_string.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilk/drop-merge-sort/ccef37cca71d2b538b1f17856a458f5cd74dfaf0/images/comparisons_1M_string.png -------------------------------------------------------------------------------- /images/example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilk/drop-merge-sort/ccef37cca71d2b538b1f17856a458f5cd74dfaf0/images/example.png -------------------------------------------------------------------------------- /images/speedup_1000_i32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilk/drop-merge-sort/ccef37cca71d2b538b1f17856a458f5cd74dfaf0/images/speedup_1000_i32.png -------------------------------------------------------------------------------- /images/speedup_1000_string.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilk/drop-merge-sort/ccef37cca71d2b538b1f17856a458f5cd74dfaf0/images/speedup_1000_string.png -------------------------------------------------------------------------------- /images/speedup_100_i32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilk/drop-merge-sort/ccef37cca71d2b538b1f17856a458f5cd74dfaf0/images/speedup_100_i32.png -------------------------------------------------------------------------------- /images/speedup_100_string.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilk/drop-merge-sort/ccef37cca71d2b538b1f17856a458f5cd74dfaf0/images/speedup_100_string.png -------------------------------------------------------------------------------- /images/speedup_100k_i32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilk/drop-merge-sort/ccef37cca71d2b538b1f17856a458f5cd74dfaf0/images/speedup_100k_i32.png -------------------------------------------------------------------------------- /images/speedup_100k_string.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilk/drop-merge-sort/ccef37cca71d2b538b1f17856a458f5cd74dfaf0/images/speedup_100k_string.png -------------------------------------------------------------------------------- /images/speedup_10M_i32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilk/drop-merge-sort/ccef37cca71d2b538b1f17856a458f5cd74dfaf0/images/speedup_10M_i32.png -------------------------------------------------------------------------------- /images/speedup_10k_i32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilk/drop-merge-sort/ccef37cca71d2b538b1f17856a458f5cd74dfaf0/images/speedup_10k_i32.png -------------------------------------------------------------------------------- /images/speedup_10k_string.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilk/drop-merge-sort/ccef37cca71d2b538b1f17856a458f5cd74dfaf0/images/speedup_10k_string.png -------------------------------------------------------------------------------- /images/speedup_1M_i32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilk/drop-merge-sort/ccef37cca71d2b538b1f17856a458f5cd74dfaf0/images/speedup_1M_i32.png -------------------------------------------------------------------------------- /images/speedup_1M_string.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilk/drop-merge-sort/ccef37cca71d2b538b1f17856a458f5cd74dfaf0/images/speedup_1M_string.png -------------------------------------------------------------------------------- /src/dmsort.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Emil Ernerfeldt 2 | 3 | use std::cmp::Ordering; 4 | use std::ptr; 5 | 6 | // ---------------------------------------------------------------------------- 7 | 8 | /// This speeds up well-ordered input by quite a lot. 9 | const DOUBLE_COMPARISONS: bool = true; 10 | 11 | /// Low RECENCY = faster when there is low disorder (a lot of order). 12 | /// High RECENCY = more resilient against long stretches of noise. 13 | /// If RECENCY is too small we are more dependent on nice data/luck. 14 | const RECENCY: usize = 8; 15 | 16 | /// Back-track several elements at once. This is helpful when there are big clumps out-of-order. 17 | const FAST_BACKTRACKING: bool = true; 18 | 19 | /// Break early if we notice that the input is not ordered enough. 20 | const EARLY_OUT: bool = true; 21 | 22 | /// Test for early-out when we have processed len / `EARLY_OUT_TEST_AT` elements. 23 | const EARLY_OUT_TEST_AT: usize = 4; 24 | 25 | /// If more than this percentage of elements have been dropped, we abort. 26 | const EARLY_OUT_DISORDER_FRACTION: f32 = 0.60; 27 | 28 | // ---------------------------------------------------------------------------- 29 | 30 | /// This is the readable reference implementation that only works for Copy types. 31 | /// Returns the number of dropped elements for diagnostic purposes. 32 | fn sort_copy_by(slice: &mut [T], mut compare: F) -> usize 33 | where 34 | T: Copy, 35 | F: FnMut(&T, &T) -> Ordering, 36 | { 37 | if slice.len() < 2 { 38 | return slice.len(); 39 | } 40 | 41 | // ------------------------------------------------------------------------ 42 | // First step: heuristically find the Longest Nondecreasing Subsequence (LNS). 43 | // The LNS is shifted into slice[..write] while slice[write..] will be left unchanged. 44 | // Elements not part of the LNS will be put in the "dropped" vector. 45 | 46 | let mut dropped = Vec::new(); 47 | let mut num_dropped_in_row = 0; 48 | let mut write = 0; // Index of where to write the next element to keep. 49 | let mut read = 0; // Index of the input stream. 50 | let mut iteration = 0; 51 | let ealy_out_stop = slice.len() / EARLY_OUT_TEST_AT; 52 | 53 | while read < slice.len() { 54 | iteration += 1; 55 | if EARLY_OUT 56 | && iteration == ealy_out_stop 57 | && dropped.len() as f32 > read as f32 * EARLY_OUT_DISORDER_FRACTION 58 | { 59 | // We have seen a lot of the elements and dropped a lot of them. 60 | // This doesn't look good. Abort. 61 | for (i, &element) in dropped.iter().enumerate() { 62 | slice[write + i] = element; 63 | } 64 | slice.sort_unstable_by(|a, b| compare(a, b)); 65 | return dropped.len() * EARLY_OUT_TEST_AT; // Just an estimate. 66 | } 67 | 68 | if write == 0 || compare(&slice[read], &slice[write - 1]) != Ordering::Less { 69 | // The element is order - keep it: 70 | slice[write] = slice[read]; 71 | read += 1; 72 | write += 1; 73 | num_dropped_in_row = 0; 74 | } else { 75 | // The next element is smaller than the last stored one. 76 | // The question is - should we drop the new element, or was accepting the previous element a mistake? 77 | 78 | /* 79 | Check this situation: 80 | . 0 1 2 3 9 5 6 7 (the 9 is a one-off) 81 | . | | 82 | . | read 83 | . write - 1 84 | Checking this improves performance because we catch common problems earlier (without back-tracking). 85 | */ 86 | if DOUBLE_COMPARISONS 87 | && num_dropped_in_row == 0 88 | && 2 <= write 89 | && compare(&slice[read], &slice[write - 2]) != Ordering::Less 90 | { 91 | // Quick undo: drop previously accepted element, and overwrite with new one: 92 | dropped.push(slice[write - 1]); 93 | slice[write - 1] = slice[read]; 94 | read += 1; 95 | continue; 96 | } 97 | 98 | if num_dropped_in_row < RECENCY { 99 | // Drop it: 100 | dropped.push(slice[read]); 101 | read += 1; 102 | num_dropped_in_row += 1; 103 | } else { 104 | /* 105 | We accepted something num_dropped_in_row elements back that made us drop all RECENCY subsequent items. 106 | Accepting that element was obviously a mistake - so let's undo it! 107 | 108 | Example problem (RECENCY = 3): 0 1 12 3 4 5 6 109 | 0 1 12 is accepted. 3, 4, 5 will be rejected because they are larger than the last kept item (12). 110 | When we get to 5 we reach num_dropped_in_row == RECENCY. 111 | This will trigger an undo where we drop the 12. 112 | When we again go to 3, we will keep it because it is larger than the last kept item (1). 113 | 114 | Example worst-case (RECENCY = 3): ...100 101 102 103 104 1 2 3 4 5 .... 115 | 100-104 is accepted. When we get to 3 we reach num_dropped_in_row == RECENCY. 116 | We drop 104 and reset the read by RECENCY. We restart, and then we drop again. 117 | This can lead us to backtracking RECENCY number of elements 118 | as many times as the leading non-decreasing subsequence is long. 119 | */ 120 | 121 | // Undo dropping the last num_dropped_in_row elements: 122 | let trunc_to_length = dropped.len() - num_dropped_in_row; 123 | dropped.truncate(trunc_to_length); 124 | read -= num_dropped_in_row; 125 | 126 | let mut num_backtracked = 1; 127 | write -= 1; 128 | 129 | if FAST_BACKTRACKING { 130 | // Back-track until we can accept at least one of the recently dropped elements: 131 | let max_of_dropped = slice[read..(read + num_dropped_in_row + 1)] 132 | .iter() 133 | .max_by(|a, b| compare(a, b)) 134 | .unwrap(); 135 | 136 | while 1 <= write && compare(max_of_dropped, &slice[write - 1]) == Ordering::Less 137 | { 138 | num_backtracked += 1; 139 | write -= 1; 140 | } 141 | } 142 | 143 | // Drop the back-tracked elements: 144 | dropped.extend_from_slice(&slice[write..(write + num_backtracked)]); 145 | 146 | num_dropped_in_row = 0; 147 | } 148 | } 149 | } 150 | 151 | let num_dropped = dropped.len(); 152 | 153 | // ------------------------------------------------------------------------ 154 | // Second step: sort the dropped elements: 155 | 156 | dropped.sort_unstable_by(|a, b| compare(a, b)); 157 | 158 | // ------------------------------------------------------------------------ 159 | // Third step: merge slice[..write] and `dropped`: 160 | 161 | let mut back = slice.len(); 162 | 163 | while let Some(&last_dropped) = dropped.last() { 164 | while 0 < write && compare(&last_dropped, &slice[write - 1]) == Ordering::Less { 165 | slice[back - 1] = slice[write - 1]; 166 | back -= 1; 167 | write -= 1; 168 | } 169 | slice[back - 1] = last_dropped; 170 | back -= 1; 171 | dropped.pop(); 172 | } 173 | 174 | num_dropped 175 | } 176 | 177 | /// UNSTABLE! FOR INTERNAL USE ONLY. 178 | pub fn sort_copy(slice: &mut [T]) -> usize { 179 | sort_copy_by(slice, |a, b| a.cmp(b)) 180 | } 181 | 182 | // ---------------------------------------------------------------------------- 183 | 184 | // A note about protecting us from stack unwinding: 185 | // 186 | // If our compare function panics we need to make sure all objects are put back into slice 187 | // so they can be properly destroyed by the caller. 188 | // 189 | // This is done by temporarily bit-copying the data into the dropped vector 190 | // and copying them back if there is a panic. 191 | // 192 | struct DmSorter<'a, T: 'a> { 193 | /// The slice we are sorting 194 | slice: &'a mut [T], 195 | 196 | /// Temporary storage of dropped elements. 197 | dropped: Vec, 198 | 199 | /// Index in self.slice of where to write the next element to keep. 200 | write: usize, 201 | // slice[write..(write + dropped.len())] is a gap. The elements can be found in dropped 202 | } 203 | 204 | impl<'a, T> Drop for DmSorter<'a, T> { 205 | fn drop(&mut self) { 206 | if self.dropped.is_empty() { 207 | return; 208 | } 209 | unsafe { 210 | // This code will only run on stack-unwind (panic). 211 | 212 | // Move back all elements into the slice: 213 | ptr::copy_nonoverlapping( 214 | self.dropped.as_ptr(), 215 | self.slice.as_mut_ptr().add(self.write), 216 | self.dropped.len(), 217 | ); 218 | 219 | // Make sure the objects aren't destroyed when self.dropped is dropped (avoid-double-free). 220 | self.dropped.set_len(0); 221 | } 222 | } 223 | } 224 | 225 | #[inline(always)] 226 | unsafe fn unsafe_push(vec: &mut Vec, value: &T) { 227 | let old_len = vec.len(); 228 | vec.reserve(1); 229 | ptr::copy_nonoverlapping(value, vec.as_mut_ptr().add(old_len), 1); 230 | vec.set_len(old_len + 1); 231 | } 232 | 233 | #[inline(always)] 234 | unsafe fn unsafe_copy(slice: &mut [T], source: usize, dest: usize) { 235 | let ptr = slice.as_mut_ptr(); 236 | ptr::copy_nonoverlapping(ptr.add(source), ptr.add(dest), 1); 237 | } 238 | 239 | fn sort_move_by(slice: &mut [T], mut compare: F) 240 | where 241 | F: FnMut(&T, &T) -> Ordering, 242 | { 243 | unsafe { 244 | if slice.len() < 2 { 245 | return; 246 | } 247 | 248 | let mut s = DmSorter { 249 | slice, 250 | dropped: Vec::new(), 251 | write: 0, 252 | }; 253 | 254 | // ------------------------------------------------------------------------ 255 | 256 | let mut num_dropped_in_row = 0; 257 | let mut read = 0; 258 | let mut iteration = 0; 259 | let ealy_out_stop = s.slice.len() / EARLY_OUT_TEST_AT; 260 | 261 | while read < s.slice.len() { 262 | iteration += 1; 263 | if EARLY_OUT 264 | && iteration == ealy_out_stop 265 | && s.dropped.len() as f32 > read as f32 * EARLY_OUT_DISORDER_FRACTION 266 | { 267 | // We have seen a lot of the elements and dropped a lot of them. 268 | // This doesn't look good. Abort. 269 | ptr::copy_nonoverlapping( 270 | s.dropped.as_ptr(), 271 | &mut s.slice[s.write], 272 | s.dropped.len(), 273 | ); 274 | s.dropped.set_len(0); 275 | s.slice.sort_unstable_by(|a, b| compare(a, b)); 276 | return; 277 | } 278 | 279 | if s.write == 0 280 | || compare( 281 | s.slice.get_unchecked(read), 282 | s.slice.get_unchecked(s.write - 1), 283 | ) != Ordering::Less 284 | { 285 | // The element is order - keep it: 286 | if read != s.write { 287 | unsafe_copy(s.slice, read, s.write); 288 | } 289 | read += 1; 290 | s.write += 1; 291 | num_dropped_in_row = 0; 292 | } else { 293 | if DOUBLE_COMPARISONS 294 | && num_dropped_in_row == 0 295 | && 2 <= s.write 296 | && compare( 297 | s.slice.get_unchecked(read), 298 | s.slice.get_unchecked(s.write - 2), 299 | ) != Ordering::Less 300 | { 301 | // Quick undo: drop previously accepted element, and overwrite with new one: 302 | unsafe_push(&mut s.dropped, s.slice.get_unchecked(s.write - 1)); 303 | unsafe_copy(s.slice, read, s.write - 1); 304 | read += 1; 305 | continue; 306 | } 307 | 308 | if num_dropped_in_row < RECENCY { 309 | // Drop it: 310 | unsafe_push(&mut s.dropped, s.slice.get_unchecked(read)); 311 | read += 1; 312 | num_dropped_in_row += 1; 313 | } else { 314 | // Undo dropping the last num_dropped_in_row elements: 315 | let trunc_to_length = s.dropped.len() - num_dropped_in_row; 316 | s.dropped.set_len(trunc_to_length); 317 | read -= num_dropped_in_row; 318 | 319 | let mut num_backtracked = 1; 320 | s.write -= 1; 321 | 322 | if FAST_BACKTRACKING { 323 | // Back-track until we can accept at least one of the recently dropped elements: 324 | let max_of_dropped = s.slice[read..(read + num_dropped_in_row + 1)] 325 | .iter() 326 | .max_by(|a, b| compare(a, b)) 327 | .unwrap(); 328 | 329 | while 1 <= s.write 330 | && compare(max_of_dropped, s.slice.get_unchecked(s.write - 1)) 331 | == Ordering::Less 332 | { 333 | num_backtracked += 1; 334 | s.write -= 1; 335 | } 336 | } 337 | 338 | // Append s.slice[read..(read + num_backtracked)] to s.dropped: 339 | { 340 | let old_len = s.dropped.len(); 341 | s.dropped.reserve(num_backtracked); 342 | ptr::copy_nonoverlapping( 343 | s.slice.as_ptr().add(s.write), 344 | s.dropped.as_mut_ptr().add(old_len), 345 | num_backtracked, 346 | ); 347 | s.dropped.set_len(old_len + num_backtracked); 348 | } 349 | 350 | num_dropped_in_row = 0; 351 | } 352 | } 353 | } 354 | 355 | // ------------------------------------------------------------------------ 356 | 357 | s.dropped.sort_unstable_by(|a, b| compare(a, b)); 358 | 359 | // ------------------------------------------------------------------------ 360 | // Merge: 361 | 362 | let mut back = s.slice.len(); 363 | 364 | loop { 365 | let old_len = s.dropped.len(); 366 | if old_len == 0 { 367 | break; 368 | } 369 | { 370 | let last_dropped = s.dropped.get_unchecked(old_len - 1); 371 | while 0 < s.write 372 | && compare(last_dropped, s.slice.get_unchecked(s.write - 1)) == Ordering::Less 373 | { 374 | unsafe_copy(s.slice, s.write - 1, back - 1); 375 | back -= 1; 376 | s.write -= 1; 377 | } 378 | ptr::copy_nonoverlapping(last_dropped, s.slice.get_unchecked_mut(back - 1), 1); 379 | } 380 | back -= 1; 381 | s.dropped.set_len(old_len - 1); 382 | } 383 | } 384 | } 385 | 386 | // ---------------------------------------------------------------------------- 387 | 388 | /// Sorts the elements using the given compare function. 389 | /// # Examples 390 | /// ``` 391 | /// let mut numbers : Vec = vec!(0, 1, 6, 7, 2, 3, 4, 5); 392 | /// dmsort::sort_by(&mut numbers, |a, b| b.cmp(a)); 393 | /// assert_eq!(numbers, vec!(7, 6, 5, 4, 3, 2, 1, 0)); 394 | /// ``` 395 | pub fn sort_by(slice: &mut [T], compare: F) 396 | where 397 | F: FnMut(&T, &T) -> Ordering, 398 | { 399 | sort_move_by(slice, compare); 400 | } 401 | 402 | /// Sorts the elements using the given key function. 403 | /// # Examples 404 | /// ``` 405 | /// let mut numbers : Vec = vec!(0, 1, 6, 7, 2, 3, 4, 5); 406 | /// dmsort::sort_by_key(&mut numbers, |x| -x); 407 | /// assert_eq!(numbers, vec!(7, 6, 5, 4, 3, 2, 1, 0)); 408 | /// ``` 409 | pub fn sort_by_key(slice: &mut [T], mut key: F) 410 | where 411 | K: Ord, 412 | F: FnMut(&T) -> K, 413 | { 414 | sort_by(slice, |a, b| key(a).cmp(&key(b))); 415 | } 416 | 417 | /// Sorts the elements using the Ord trait. 418 | /// # Examples 419 | /// ``` 420 | /// let mut numbers : Vec = vec!(0, 1, 6, 7, 2, 3, 4, 5); 421 | /// dmsort::sort(&mut numbers); 422 | /// assert_eq!(numbers, vec!(0, 1, 2, 3, 4, 5, 6, 7)); 423 | /// ``` 424 | pub fn sort(slice: &mut [T]) { 425 | sort_move_by(slice, |a, b| a.cmp(b)); 426 | } 427 | 428 | // ---------------------------------------------------------------------------- 429 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Drop-Merge sort created and implemented by Emil Ernerfeldt. 2 | //! 3 | //! Drop-Merge sort is an adaptive, unstable sorting algorithm designed for nearly-sorted data. 4 | //! An example use-case would be re-sorting an already sorted list after minor modifications. 5 | //! 6 | //! Drop-Merge sort is especially useful for: 7 | //! 8 | //! * Long lists (>10k elements) 9 | //! * Where >80% of the data is already in-order 10 | //! * The unsorted elements are evenly distributed. 11 | //! 12 | //! Expected number of comparisons is `O(N + K * log(K))` where `K` is the number of elements not in order. 13 | //! Expected memory usage is `O(K)`. 14 | //! Works best when `K < 0.2 * N`. 15 | //! The out-of-order elements are expected to be randomly distributed (NOT clumped). 16 | //! 17 | //! # Examples 18 | //! ``` 19 | //! extern crate dmsort; 20 | //! 21 | //! fn main() { 22 | //! let mut numbers : Vec = vec!(0, 1, 6, 7, 2, 3, 4, 5); 23 | //! 24 | //! // Sort with custom key: 25 | //! dmsort::sort_by_key(&mut numbers, |x| -x); 26 | //! assert_eq!(numbers, vec!(7, 6, 5, 4, 3, 2, 1, 0)); 27 | //! 28 | //! // Sort with Ord trait: 29 | //! dmsort::sort(&mut numbers); 30 | //! assert_eq!(numbers, vec!(0, 1, 2, 3, 4, 5, 6, 7)); 31 | //! 32 | //! // Sort with custom compare: 33 | //! dmsort::sort_by(&mut numbers, |a, b| b.cmp(a)); 34 | //! assert_eq!(numbers, vec!(7, 6, 5, 4, 3, 2, 1, 0)); 35 | //! } 36 | //! ``` 37 | 38 | pub use dmsort::{sort, sort_by, sort_by_key}; 39 | 40 | /// For in module-level testing only. TODO: this shouldn't be public. 41 | pub use dmsort::sort_copy; 42 | 43 | mod dmsort; 44 | -------------------------------------------------------------------------------- /tests/benchmark.rs: -------------------------------------------------------------------------------- 1 | extern crate dmsort; 2 | 3 | extern crate gnuplot; 4 | extern crate pbr; 5 | extern crate rand; 6 | extern crate time; 7 | 8 | use pbr::ProgressBar; 9 | use rand::{rngs::StdRng, Rng}; 10 | 11 | static BENCH_RESOLUTION_START: usize = 10; 12 | static BENCH_RESOLUTION_END: usize = 99 * 5; 13 | static BENCH_RESOLUTION_CUTOFF: f32 = 0.01; 14 | 15 | // ---------------------------------------------------------------------------- 16 | 17 | type Integer = i32; 18 | 19 | /// Returns a mostly-sorted array with `disorder_factor` fraction of elements with random values. 20 | fn generate_integers(rng: &mut StdRng, length: usize, disorder_factor: f32) -> Vec { 21 | (0..length) 22 | .map(|i| { 23 | if rng.gen::() < disorder_factor { 24 | #[allow(clippy::unnecessary_cast)] 25 | rng.gen_range(0 as Integer, length as Integer) 26 | } else { 27 | i as Integer 28 | } 29 | }) 30 | .collect() 31 | } 32 | 33 | fn generate_strings(rng: &mut StdRng, length: usize, disorder_factor: f32) -> Vec { 34 | generate_integers(rng, length, disorder_factor) 35 | .iter() 36 | .map(|&x| format!("{:0100}", x)) 37 | .collect() 38 | } 39 | 40 | fn time_sort_ms( 41 | num_best_of: usize, 42 | unsorted: &[T], 43 | mut sorter: Sorter, 44 | ) -> (f32, Vec) 45 | where 46 | Sorter: FnMut(&mut Vec), 47 | { 48 | let mut best_ns = None; 49 | let mut sorted = Vec::new(); 50 | 51 | for _ in 0..num_best_of { 52 | let mut vec_clone: Vec = unsorted.to_vec(); 53 | let start_time_ns = time::precise_time_ns(); 54 | sorter(&mut vec_clone); 55 | let duration_ns = time::precise_time_ns() - start_time_ns; 56 | if best_ns == None || duration_ns < best_ns.unwrap() { 57 | best_ns = Some(duration_ns); 58 | } 59 | sorted = vec_clone; 60 | } 61 | 62 | (best_ns.unwrap() as f32 / 1000000.0, sorted) 63 | } 64 | 65 | /// Benchmark at these disorders: 66 | fn get_bench_disorders() -> Vec { 67 | fn remap(x: usize, in_min: usize, in_max: usize, out_min: f32, out_max: f32) -> f32 { 68 | out_min + (out_max - out_min) * ((x - in_min) as f32) / ((in_max - in_min) as f32) 69 | } 70 | 71 | (0..BENCH_RESOLUTION_START) 72 | .map(|x| remap(x, 0, BENCH_RESOLUTION_START, 0.0, BENCH_RESOLUTION_CUTOFF)) 73 | .chain( 74 | (0..(BENCH_RESOLUTION_END + 1)) 75 | .map(|x| remap(x, 0, BENCH_RESOLUTION_END, BENCH_RESOLUTION_CUTOFF, 1.0)), 76 | ) 77 | .collect() 78 | } 79 | 80 | fn benchmark_and_plot( 81 | rng: &mut StdRng, 82 | num_best_of: usize, 83 | length: usize, 84 | length_str: &str, 85 | element_type_short: &str, 86 | element_type_long: &str, 87 | mut generator: G, 88 | ) where 89 | T: std::fmt::Debug + Clone + std::cmp::Ord, 90 | G: FnMut(&mut StdRng, usize, f32) -> Vec, 91 | { 92 | let bench_disorders = get_bench_disorders(); 93 | let mut pb = ProgressBar::new(bench_disorders.len() as u64); 94 | pb.message(&format!( 95 | "Benchmarking {} {}: ", 96 | length_str, element_type_long 97 | )); 98 | 99 | let mut std_ms_list = vec![]; 100 | let mut pdq_ms_list = vec![]; 101 | let mut dmsort_ms_list = vec![]; 102 | let mut dmsort_speedup_list = vec![]; 103 | 104 | for &disorder_factor in &bench_disorders { 105 | let vec = generator(rng, length, disorder_factor); 106 | let (std_ms, std_sorted) = time_sort_ms(num_best_of, &vec, |x| x.sort()); 107 | let (pdq_ms, pdq_sorted) = time_sort_ms(num_best_of, &vec, |x| x.sort_unstable()); 108 | let (dmsort_ms, dmsort_sorted) = time_sort_ms(num_best_of, &vec, |x| dmsort::sort(x)); 109 | 110 | let fastest_competitor_ms = std_ms.min(pdq_ms); 111 | 112 | assert_eq!(pdq_sorted, std_sorted); 113 | assert_eq!(dmsort_sorted, std_sorted); 114 | 115 | std_ms_list.push(std_ms); 116 | pdq_ms_list.push(pdq_ms); 117 | dmsort_ms_list.push(dmsort_ms); 118 | dmsort_speedup_list.push(fastest_competitor_ms / dmsort_ms); 119 | 120 | pb.inc(); 121 | } 122 | println!(); 123 | 124 | let disorder_percentages: Vec = bench_disorders.iter().map(|x| x * 100.0).collect(); 125 | 126 | use gnuplot::*; 127 | { 128 | let mut figure = Figure::new(); 129 | figure.set_terminal( 130 | "pngcairo", 131 | &format!( 132 | "images/comparisons_{}_{}.png", 133 | length_str, element_type_short 134 | ), 135 | ); 136 | figure 137 | .axes2d() 138 | .set_legend( 139 | Graph(1.0), 140 | Graph(0.05), 141 | &[Placement(AlignRight, AlignBottom)], 142 | &[], 143 | ) 144 | .set_border(false, &[Left, Bottom], &[]) 145 | .set_x_ticks( 146 | Some((Auto, 0)), 147 | &[Mirror(false), Format("%.0f %%")], 148 | &[TextColor("#808080")], 149 | ) 150 | .set_y_ticks(Some((Auto, 0)), &[Mirror(false)], &[TextColor("#808080")]) 151 | .set_x_grid(true) 152 | .set_y_grid(true) 153 | .set_title( 154 | &format!("Sorting {} {}", length_str, element_type_long), 155 | &[], 156 | ) 157 | .set_x_label("Disorder", &[]) 158 | .set_y_label("ms", &[]) 159 | .lines( 160 | &disorder_percentages, 161 | &std_ms_list, 162 | &[Caption("Vec::sort"), Color("#FF0000"), LineWidth(2.0)], 163 | ) 164 | .lines( 165 | &disorder_percentages, 166 | &pdq_ms_list, 167 | &[Caption("pdqsort"), Color("#BBBB00"), LineWidth(2.0)], 168 | ) 169 | .lines( 170 | &disorder_percentages, 171 | &dmsort_ms_list, 172 | &[Caption("Drop-Merge Sort"), Color("#4444FF"), LineWidth(2.0)], 173 | ); 174 | figure.show(); 175 | } 176 | { 177 | let mut figure = Figure::new(); 178 | figure.set_terminal( 179 | "pngcairo", 180 | &format!("images/speedup_{}_{}.png", length_str, element_type_short), 181 | ); 182 | figure 183 | .axes2d() 184 | .set_legend( 185 | Graph(1.0), 186 | Graph(0.05), 187 | &[Placement(AlignRight, AlignBottom)], 188 | &[], 189 | ) 190 | .set_border(false, &[Left, Bottom], &[]) 191 | .set_x_ticks( 192 | Some((Auto, 0)), 193 | &[Mirror(false), Format("%.0f %%")], 194 | &[TextColor("#808080")], 195 | ) 196 | .set_y_ticks(Some((Auto, 0)), &[Mirror(false)], &[TextColor("#808080")]) 197 | .set_x_grid(true) 198 | .set_y_grid(true) 199 | .set_title( 200 | &format!( 201 | "Drop-Merge sort speedup when sorting {} {}", 202 | length_str, element_type_long 203 | ), 204 | &[], 205 | ) 206 | .set_x_label("Disorder", &[]) 207 | .set_y_label("Speedup over fastest competitor", &[]) 208 | .set_x_range(Fix(0.0), Fix(50.0)) 209 | .set_y_range(Fix(0.0), Fix(8.0)) 210 | .lines(&[0.0, 100.0], &[1.0, 1.0], &[Color("#606060")]) 211 | .lines( 212 | &disorder_percentages, 213 | &dmsort_speedup_list, 214 | &[Color("#4444FF"), LineWidth(2.0)], 215 | ); 216 | figure.show(); 217 | } 218 | } 219 | 220 | /// Benchmark worst-case input for Drop-Merge sort 221 | #[allow(clippy::stable_sort_primitive)] 222 | fn bench_evil() { 223 | let evil_input: Vec<_> = (100..1000000).chain(0..100).collect(); 224 | let (std_ms, std_sorted) = time_sort_ms(10, &evil_input, |x| x.sort()); 225 | let (pdq_ms, pdq_sorted) = time_sort_ms(10, &evil_input, |x| x.sort_unstable()); 226 | let (drop_ms, drop_sorted) = time_sort_ms(10, &evil_input, |x| dmsort::sort(x)); 227 | // let (drop_ms, drop_sorted) = time_sort_ms(10, &evil_input, |x| {dmsort::sort_copy(x); ()}); 228 | 229 | assert_eq!(std_sorted, drop_sorted); 230 | assert_eq!(std_sorted, pdq_sorted); 231 | println!("Worst-case input:"); 232 | println!("std::sort: {} ms", std_ms); 233 | println!("pdqsort: {} ms", pdq_ms); 234 | println!("Drop-Merge sort: {} ms", drop_ms); 235 | } 236 | 237 | #[test] 238 | #[ignore] 239 | #[rustfmt::skip] 240 | fn benchmarks() { 241 | bench_evil(); 242 | 243 | use rand::SeedableRng; 244 | let seed = [0; 32]; 245 | let mut rng: StdRng = StdRng::from_seed(seed); 246 | 247 | benchmark_and_plot(&mut rng, 1000, 100, "100", "i32", "32-bit integers", generate_integers); 248 | benchmark_and_plot(&mut rng, 1000, 100, "100", "string", "100-byte strings", generate_strings); 249 | benchmark_and_plot(&mut rng, 1000, 1_000, "1000", "i32", "32-bit integers", generate_integers); 250 | benchmark_and_plot(&mut rng, 100, 1_000, "1000", "string", "100-byte strings", generate_strings); 251 | benchmark_and_plot(&mut rng, 100, 10_000, "10k", "i32", "32-bit integers", generate_integers); 252 | benchmark_and_plot(&mut rng, 10, 10_000, "10k", "string", "100-byte strings", generate_strings); 253 | benchmark_and_plot(&mut rng, 10, 100_000, "100k", "i32", "32-bit integers", generate_integers); 254 | benchmark_and_plot(&mut rng, 3, 100_000, "100k", "string", "100-byte strings", generate_strings); 255 | benchmark_and_plot(&mut rng, 5, 1_000_000, "1M", "i32", "32-bit integers", generate_integers); 256 | benchmark_and_plot(&mut rng, 1, 1_000_000, "1M", "string", "100-byte strings", generate_strings); 257 | benchmark_and_plot(&mut rng, 1, 10_000_000, "10M", "i32", "32-bit integers", generate_integers); 258 | } 259 | -------------------------------------------------------------------------------- /tests/tests.rs: -------------------------------------------------------------------------------- 1 | extern crate dmsort; 2 | 3 | use std::cell::RefCell; 4 | use std::collections::BTreeSet; 5 | use std::panic; 6 | 7 | #[test] 8 | fn simple_tests() { 9 | fn test_type(unsorted: Vec) { 10 | let mut dm_sorted = unsorted.clone(); 11 | dmsort::sort(&mut dm_sorted); 12 | 13 | let mut std_sorted = unsorted.clone(); 14 | std_sorted.sort(); 15 | 16 | assert_eq!(dm_sorted, std_sorted, "FAIL with input {:?}", unsorted); 17 | } 18 | 19 | fn test(list: Vec) { 20 | test_type(list.iter().map(|&x| format!("{:02}", x)).collect()); 21 | test_type(list); 22 | } 23 | 24 | test(vec![]); 25 | test(vec![0]); 26 | test(vec![0, 1]); 27 | test(vec![1, 0]); 28 | test(vec![0, 1, 2]); 29 | test(vec![0, 2, 1]); 30 | test(vec![1, 0, 2]); 31 | test(vec![1, 2, 0]); 32 | test(vec![2, 0, 1]); 33 | test(vec![2, 1, 0]); 34 | test(vec![0, 1, 3, 2, 4, -5, 6, 7, 8, 9]); 35 | test(vec![0, 1, 10, 3, 4, 5, 6, 7, 8, 9]); 36 | test(vec![10, 1, 2, 3, 4, 5, 6, 7, 8, 9]); 37 | test(vec![0, 0, 2, 3, 4, 1, 6, 1, 8, 9]); 38 | test(vec![ 39 | 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 40 | ]); 41 | test(vec![ 42 | 20, 21, 2, 23, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 43 | ]); 44 | } 45 | 46 | #[test] 47 | fn test_unwind() { 48 | // The purpose of this test is to make sure that if there is a panic in the compare function 49 | // then we will unwind the stack in such a way that the slice we are sorting 50 | // contains all elements it had when called (but maybe in a different, partially-sorted order). 51 | // This is crucial in order to prevent double-frees. 52 | // 53 | struct TestSortType<'a> { 54 | id: usize, 55 | dropped: &'a RefCell>, 56 | } 57 | impl<'a> Drop for TestSortType<'a> { 58 | fn drop(&mut self) { 59 | let did_insert = self.dropped.borrow_mut().insert(self.id); 60 | assert!(did_insert, "Double-free of {}", self.id); 61 | } 62 | } 63 | 64 | for break_after_this_many_comparisons in 0..14 { 65 | let scheuled_panic_code: String = String::from("This is a scheduled panic"); 66 | 67 | let dropped = RefCell::new(BTreeSet::new()); 68 | 69 | let catch_result = panic::catch_unwind(panic::AssertUnwindSafe(|| { 70 | let mut data = [ 71 | TestSortType { 72 | id: 0, 73 | dropped: &dropped, 74 | }, 75 | TestSortType { 76 | id: 1, 77 | dropped: &dropped, 78 | }, 79 | TestSortType { 80 | id: 5, 81 | dropped: &dropped, 82 | }, 83 | TestSortType { 84 | id: 6, 85 | dropped: &dropped, 86 | }, 87 | TestSortType { 88 | id: 2, 89 | dropped: &dropped, 90 | }, 91 | TestSortType { 92 | id: 3, 93 | dropped: &dropped, 94 | }, 95 | TestSortType { 96 | id: 4, 97 | dropped: &dropped, 98 | }, 99 | ]; 100 | let mut num_comparisons = 0; 101 | 102 | dmsort::sort_by(&mut data, |a, b| { 103 | if num_comparisons == break_after_this_many_comparisons { 104 | panic!("{}", scheuled_panic_code.clone()); 105 | } 106 | num_comparisons += 1; 107 | a.id.cmp(&b.id) 108 | }); 109 | panic!( 110 | "We where supposed to abort after {} comparisons, but we only did {}", 111 | break_after_this_many_comparisons, num_comparisons 112 | ); 113 | })); 114 | 115 | // Make sure we did panic: 116 | assert!(catch_result.is_err()); 117 | 118 | // Make sure we panicked for the right reason: 119 | let error = catch_result.err().unwrap(); 120 | assert!(error.is::()); 121 | assert_eq!( 122 | *error.downcast_ref::().unwrap(), 123 | scheuled_panic_code 124 | ); 125 | 126 | // Make sure we dropped all objects: 127 | assert_eq!(dropped.borrow_mut().len(), 7); 128 | } 129 | } 130 | --------------------------------------------------------------------------------