auto merge of #11064 : huonw/rust/vec-sort, r=alexcrichton

This uses quite a bit of unsafe code for speed and failure safety, and allocates `2*n` temporary storage.

[Performance](https://gist.github.com/huonw/5547f2478380288a28c2):

|      n |      new | priority_queue |   quick3 |
|-------:|---------:|---------------:|---------:|
|      5 |      200 |            155 |      106 |
|    100 |     6490 |           8750 |     5810 |
|  10000 |  1300000 |        1790000 |  1060000 |
| 100000 | 16700000 |       23600000 | 12700000 |
| sorted |   520000 |        1380000 | 53900000 |
|  trend |  1310000 |        1690000 |  1100000 |

(The times are in nanoseconds, having subtracted the set-up time (i.e. the `just_generate` bench target).)

I imagine that there is still significant room for improvement, particularly because both priority_queue and quick3 are doing a static call via `Ord` or `TotalOrd` for the comparisons, while this is using a (boxed) closure.

Also, this code does not `clone`, unlike `quick_sort3`; and is stable, unlike both of the others.
This commit is contained in:
bors 2013-12-22 00:41:39 -08:00
commit 55cbef611a
19 changed files with 493 additions and 1303 deletions

View file

@ -28,8 +28,6 @@ use std::io;
use std::io::fs;
use std::path::is_sep;
use sort;
/**
* An iterator that yields Paths from the filesystem that match a particular
* pattern - see the `glob` function for more details.
@ -149,9 +147,8 @@ impl Iterator<Path> for GlobIterator {
fn list_dir_sorted(path: &Path) -> ~[Path] {
match io::result(|| fs::readdir(path)) {
Ok(children) => {
let mut children = children;
sort::quick_sort(children, |p1, p2| p2.filename() <= p1.filename());
Ok(mut children) => {
children.sort_by(|p1, p2| p2.filename().cmp(&p1.filename()));
children
}
Err(..) => ~[]
@ -771,4 +768,3 @@ mod test {
assert!(Pattern::new("a/b").matches_path(&Path::new("a/b")));
}
}

View file

@ -61,8 +61,6 @@ pub mod ringbuf;
pub mod priority_queue;
pub mod smallintmap;
pub mod sort;
pub mod dlist;
pub mod treemap;
pub mod btree;

View file

@ -213,7 +213,6 @@ impl<T: Ord> Extendable<T> for PriorityQueue<T> {
#[cfg(test)]
mod tests {
use sort::merge_sort;
use priority_queue::PriorityQueue;
#[test]
@ -231,7 +230,8 @@ mod tests {
#[test]
fn test_top_and_pop() {
let data = ~[2u, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1];
let mut sorted = merge_sort(data, |x, y| x.le(y));
let mut sorted = data.clone();
sorted.sort();
let mut heap = PriorityQueue::from_vec(data);
while !heap.is_empty() {
assert_eq!(heap.top(), sorted.last());
@ -311,11 +311,14 @@ mod tests {
assert_eq!(heap.len(), 5);
}
fn check_to_vec(data: ~[int]) {
fn check_to_vec(mut data: ~[int]) {
let heap = PriorityQueue::from_vec(data.clone());
assert_eq!(merge_sort(heap.clone().to_vec(), |x, y| x.le(y)),
merge_sort(data, |x, y| x.le(y)));
assert_eq!(heap.to_sorted_vec(), merge_sort(data, |x, y| x.le(y)));
let mut v = heap.clone().to_vec();
v.sort();
data.sort();
assert_eq!(v, data);
assert_eq!(heap.to_sorted_vec(), data);
}
#[test]

File diff suppressed because it is too large Load diff

View file

@ -10,7 +10,6 @@
#[allow(missing_doc)];
use sort;
use std::cmp;
use std::hashmap;
use std::io;
@ -20,6 +19,25 @@ use std::util;
// NB: this can probably be rewritten in terms of num::Num
// to be less f64-specific.
fn f64_cmp(x: f64, y: f64) -> Ordering {
// arbitrarily decide that NaNs are larger than everything.
if y.is_nan() {
Less
} else if x.is_nan() {
Greater
} else if x < y {
Less
} else if x == y {
Equal
} else {
Greater
}
}
fn f64_sort(v: &mut [f64]) {
v.sort_by(|x: &f64, y: &f64| f64_cmp(*x, *y));
}
/// Trait that provides simple descriptive statistics on a univariate set of numeric samples.
pub trait Stats {
@ -240,13 +258,13 @@ impl<'a> Stats for &'a [f64] {
fn percentile(self, pct: f64) -> f64 {
let mut tmp = self.to_owned();
sort::tim_sort(tmp);
f64_sort(tmp);
percentile_of_sorted(tmp, pct)
}
fn quartiles(self) -> (f64,f64,f64) {
let mut tmp = self.to_owned();
sort::tim_sort(tmp);
f64_sort(tmp);
let a = percentile_of_sorted(tmp, 25.0);
let b = percentile_of_sorted(tmp, 50.0);
let c = percentile_of_sorted(tmp, 75.0);
@ -291,7 +309,7 @@ fn percentile_of_sorted(sorted_samples: &[f64],
/// See: http://en.wikipedia.org/wiki/Winsorising
pub fn winsorize(samples: &mut [f64], pct: f64) {
let mut tmp = samples.to_owned();
sort::tim_sort(tmp);
f64_sort(tmp);
let lo = percentile_of_sorted(tmp, pct);
let hi = percentile_of_sorted(tmp, 100.0-pct);
for samp in samples.mut_iter() {

View file

@ -21,7 +21,6 @@ use getopts::groups;
use json::ToJson;
use json;
use serialize::Decodable;
use sort;
use stats::Stats;
use stats;
use term;
@ -38,7 +37,6 @@ use std::to_str::ToStr;
use std::f64;
use std::os;
// The name of a test. By convention this follows the rules for rust
// paths; i.e. it should be a series of identifiers separated by double
// colons. This way if some test runner wants to arrange the tests
@ -488,7 +486,7 @@ impl<T: Writer> ConsoleTestState<T> {
for f in self.failures.iter() {
failures.push(f.name.to_str());
}
sort::tim_sort(failures);
failures.sort();
for name in failures.iter() {
self.write_plain(format!(" {}\n", name.to_str()));
}
@ -839,10 +837,7 @@ pub fn filter_tests(
};
// Sort the tests alphabetically
fn lteq(t1: &TestDescAndFn, t2: &TestDescAndFn) -> bool {
t1.desc.name.to_str() < t2.desc.name.to_str()
}
sort::quick_sort(filtered, lteq);
filtered.sort_by(|t1, t2| t1.desc.name.to_str().cmp(&t2.desc.name.to_str()));
// Shard the remaining tests, if sharding requested.
match opts.test_shard {

View file

@ -151,7 +151,6 @@ Additional help:
}
pub fn describe_warnings() {
use extra::sort::Sort;
println("
Available lint options:
-W <foo> Warn about <foo>
@ -164,7 +163,7 @@ Available lint options:
let mut lint_dict = lint_dict.move_iter()
.map(|(k, v)| (v, k))
.collect::<~[(lint::LintSpec, &'static str)]>();
lint_dict.qsort();
lint_dict.sort();
let mut max_key = 0;
for &(_, name) in lint_dict.iter() {

View file

@ -18,7 +18,6 @@ use metadata::decoder;
use metadata::loader;
use std::hashmap::HashMap;
use extra;
use syntax::ast;
use syntax::parse::token::ident_interner;
@ -174,7 +173,7 @@ pub fn find_extern_mod_stmt_cnum(cstore: &CStore,
cstore.extern_mod_crate_map.find(&emod_id).map(|x| *x)
}
#[deriving(Clone)]
#[deriving(Clone, TotalEq, TotalOrd)]
struct crate_hash {
name: @str,
vers: @str,
@ -198,16 +197,14 @@ pub fn get_dep_hashes(cstore: &CStore) -> ~[@str] {
});
}
let sorted = extra::sort::merge_sort(result, |a, b| {
(a.name, a.vers, a.hash) <= (b.name, b.vers, b.hash)
});
result.sort();
debug!("sorted:");
for x in sorted.iter() {
for x in result.iter() {
debug!(" hash[{}]: {}", x.name, x.hash);
}
sorted.map(|ch| ch.hash)
result.map(|ch| ch.hash)
}
impl crate_metadata {

View file

@ -30,7 +30,6 @@ use std::util;
use std::vec;
use extra::serialize::Encodable;
use extra;
use syntax::abi::AbiSet;
use syntax::ast::*;
@ -1532,7 +1531,7 @@ fn encode_crate_deps(ecx: &EncodeContext,
});
// Sort by cnum
extra::sort::quick_sort(deps, |kv1, kv2| kv1.cnum <= kv2.cnum);
deps.sort_by(|kv1, kv2| kv1.cnum.cmp(&kv2.cnum));
// Sanity-check the crate numbers
let mut expected_cnum = 1;

View file

@ -21,7 +21,6 @@ use util::ppaux::ty_to_str;
use std::iter;
use std::num;
use std::vec;
use extra::sort;
use syntax::ast::*;
use syntax::ast_util::{unguarded_pat, walk_pat};
use syntax::codemap::{Span, dummy_sp, Spanned};
@ -454,7 +453,7 @@ fn missing_ctor(cx: &MatchCheckCtxt,
ty::ty_unboxed_vec(..) | ty::ty_evec(..) => {
// Find the lengths and slices of all vector patterns.
let vec_pat_lens = m.iter().filter_map(|r| {
let mut vec_pat_lens = m.iter().filter_map(|r| {
match r[0].node {
PatVec(ref before, ref slice, ref after) => {
Some((before.len() + after.len(), slice.is_some()))
@ -465,21 +464,19 @@ fn missing_ctor(cx: &MatchCheckCtxt,
// Sort them by length such that for patterns of the same length,
// those with a destructured slice come first.
let mut sorted_vec_lens = sort::merge_sort(vec_pat_lens,
|&(len1, slice1), &(len2, slice2)| {
if len1 == len2 {
slice1 > slice2
} else {
len1 <= len2
}
}
);
sorted_vec_lens.dedup();
vec_pat_lens.sort_by(|&(len1, slice1), &(len2, slice2)| {
if len1 == len2 {
slice2.cmp(&slice1)
} else {
len1.cmp(&len2)
}
});
vec_pat_lens.dedup();
let mut found_slice = false;
let mut next = 0;
let mut missing = None;
for &(length, slice) in sorted_vec_lens.iter() {
for &(length, slice) in vec_pat_lens.iter() {
if length != next {
missing = Some(next);
break;

View file

@ -63,7 +63,7 @@ use syntax::{ast, ast_util, visit};
use syntax::ast_util::IdVisitingOperation;
use syntax::visit::Visitor;
#[deriving(Clone, Eq)]
#[deriving(Clone, Eq, Ord, TotalEq, TotalOrd)]
pub enum lint {
ctypes,
unused_imports,
@ -110,20 +110,16 @@ pub fn level_to_str(lv: level) -> &'static str {
}
}
#[deriving(Clone, Eq, Ord)]
#[deriving(Clone, Eq, Ord, TotalEq, TotalOrd)]
pub enum level {
allow, warn, deny, forbid
}
#[deriving(Clone, Eq)]
#[deriving(Clone, Eq, Ord, TotalEq, TotalOrd)]
pub struct LintSpec {
default: level,
lint: lint,
desc: &'static str,
default: level
}
impl Ord for LintSpec {
fn lt(&self, other: &LintSpec) -> bool { self.default < other.default }
}
pub type LintDict = HashMap<&'static str, LintSpec>;

View file

@ -73,7 +73,6 @@ use std::libc::c_uint;
use std::vec;
use std::local_data;
use extra::time;
use extra::sort;
use syntax::ast::Name;
use syntax::ast_map::{path, path_elt_to_str, path_name, path_pretty_name};
use syntax::ast_util::{local_def, is_local};
@ -3163,10 +3162,9 @@ pub fn trans_crate(sess: session::Session,
println!("n_inlines: {}", ccx.stats.n_inlines);
println!("n_closures: {}", ccx.stats.n_closures);
println("fn stats:");
sort::quick_sort(ccx.stats.fn_stats,
|&(_, _, insns_a), &(_, _, insns_b)| {
insns_a > insns_b
});
ccx.stats.fn_stats.sort_by(|&(_, _, insns_a), &(_, _, insns_b)| insns_b.cmp(&insns_a));
for tuple in ccx.stats.fn_stats.iter() {
match *tuple {
(ref name, ms, insns) => {

View file

@ -45,8 +45,6 @@ use std::vec;
use extra::arc::Arc;
use extra::json::ToJson;
use extra::sort;
use syntax::ast;
use syntax::attr;
@ -900,44 +898,44 @@ fn item_module(w: &mut Writer, cx: &Context,
debug!("{:?}", items);
let mut indices = vec::from_fn(items.len(), |i| i);
fn lt(i1: &clean::Item, i2: &clean::Item, idx1: uint, idx2: uint) -> bool {
fn cmp(i1: &clean::Item, i2: &clean::Item, idx1: uint, idx2: uint) -> Ordering {
if shortty(i1) == shortty(i2) {
return i1.name < i2.name;
return i1.name.cmp(&i2.name);
}
match (&i1.inner, &i2.inner) {
(&clean::ViewItemItem(ref a), &clean::ViewItemItem(ref b)) => {
match (&a.inner, &b.inner) {
(&clean::ExternMod(..), _) => true,
(_, &clean::ExternMod(..)) => false,
_ => idx1 < idx2,
(&clean::ExternMod(..), _) => Less,
(_, &clean::ExternMod(..)) => Greater,
_ => idx1.cmp(&idx2),
}
}
(&clean::ViewItemItem(..), _) => true,
(_, &clean::ViewItemItem(..)) => false,
(&clean::ModuleItem(..), _) => true,
(_, &clean::ModuleItem(..)) => false,
(&clean::StructItem(..), _) => true,
(_, &clean::StructItem(..)) => false,
(&clean::EnumItem(..), _) => true,
(_, &clean::EnumItem(..)) => false,
(&clean::StaticItem(..), _) => true,
(_, &clean::StaticItem(..)) => false,
(&clean::ForeignFunctionItem(..), _) => true,
(_, &clean::ForeignFunctionItem(..)) => false,
(&clean::ForeignStaticItem(..), _) => true,
(_, &clean::ForeignStaticItem(..)) => false,
(&clean::TraitItem(..), _) => true,
(_, &clean::TraitItem(..)) => false,
(&clean::FunctionItem(..), _) => true,
(_, &clean::FunctionItem(..)) => false,
(&clean::TypedefItem(..), _) => true,
(_, &clean::TypedefItem(..)) => false,
_ => idx1 < idx2,
(&clean::ViewItemItem(..), _) => Less,
(_, &clean::ViewItemItem(..)) => Greater,
(&clean::ModuleItem(..), _) => Less,
(_, &clean::ModuleItem(..)) => Greater,
(&clean::StructItem(..), _) => Less,
(_, &clean::StructItem(..)) => Greater,
(&clean::EnumItem(..), _) => Less,
(_, &clean::EnumItem(..)) => Greater,
(&clean::StaticItem(..), _) => Less,
(_, &clean::StaticItem(..)) => Greater,
(&clean::ForeignFunctionItem(..), _) => Less,
(_, &clean::ForeignFunctionItem(..)) => Greater,
(&clean::ForeignStaticItem(..), _) => Less,
(_, &clean::ForeignStaticItem(..)) => Greater,
(&clean::TraitItem(..), _) => Less,
(_, &clean::TraitItem(..)) => Greater,
(&clean::FunctionItem(..), _) => Less,
(_, &clean::FunctionItem(..)) => Greater,
(&clean::TypedefItem(..), _) => Less,
(_, &clean::TypedefItem(..)) => Greater,
_ => idx1.cmp(&idx2),
}
}
debug!("{:?}", indices);
sort::quick_sort(indices, |&i1, &i2| lt(&items[i1], &items[i2], i1, i2));
indices.sort_by(|&i1, &i2| cmp(&items[i1], &items[i2], i1, i2));
debug!("{:?}", indices);
let mut curty = "";
@ -1532,7 +1530,7 @@ fn build_sidebar(m: &clean::Module) -> HashMap<~str, ~[~str]> {
}
for (_, items) in map.mut_iter() {
sort::quick_sort(*items, |i1, i2| i1 < i2);
items.sort();
}
return map;
}

View file

@ -79,7 +79,8 @@ pub use tuple::{Tuple1, Tuple2, Tuple3, Tuple4};
pub use tuple::{Tuple5, Tuple6, Tuple7, Tuple8};
pub use tuple::{Tuple9, Tuple10, Tuple11, Tuple12};
pub use vec::{ImmutableEqVector, ImmutableTotalOrdVector, ImmutableCopyableVector};
pub use vec::{OwnedVector, OwnedCopyableVector,OwnedEqVector, MutableVector};
pub use vec::{OwnedVector, OwnedCopyableVector,OwnedEqVector};
pub use vec::{MutableVector, MutableTotalOrdVector};
pub use vec::{Vector, VectorVector, CopyableVector, ImmutableVector};
// Reexported runtime types

View file

@ -1921,6 +1921,151 @@ impl<T:Eq> OwnedEqVector<T> for ~[T] {
}
}
fn merge_sort<T>(v: &mut [T], compare: |&T, &T| -> Ordering) {
// warning: this wildly uses unsafe.
static INSERTION: uint = 8;
let len = v.len();
// allocate some memory to use as scratch memory, we keep the
// length 0 so we can keep shallow copies of the contents of `v`
// without risking the dtors running on an object twice if
// `compare` fails.
let mut working_space = with_capacity(2 * len);
// these both are buffers of length `len`.
let mut buf_dat = working_space.as_mut_ptr();
let mut buf_tmp = unsafe {buf_dat.offset(len as int)};
// length `len`.
let buf_v = v.as_ptr();
// step 1. sort short runs with insertion sort. This takes the
// values from `v` and sorts them into `buf_dat`, leaving that
// with sorted runs of length INSERTION.
// We could hardcode the sorting comparisons here, and we could
// manipulate/step the pointers themselves, rather than repeatedly
// .offset-ing.
for start in range_step(0, len, INSERTION) {
// start <= i <= len;
for i in range(start, cmp::min(start + INSERTION, len)) {
// j satisfies: start <= j <= i;
let mut j = i as int;
unsafe {
// `i` is in bounds.
let read_ptr = buf_v.offset(i as int);
// find where to insert, we need to do strict <,
// rather than <=, to maintain stability.
// start <= j - 1 < len, so .offset(j - 1) is in
// bounds.
while j > start as int &&
compare(&*read_ptr, &*buf_dat.offset(j - 1)) == Less {
j -= 1;
}
// shift everything to the right, to make space to
// insert this value.
// j + 1 could be `len` (for the last `i`), but in
// that case, `i == j` so we don't copy. The
// `.offset(j)` is always in bounds.
ptr::copy_memory(buf_dat.offset(j + 1),
buf_dat.offset(j),
i - j as uint);
ptr::copy_nonoverlapping_memory(buf_dat.offset(j), read_ptr, 1);
}
}
}
// step 2. merge the sorted runs.
let mut width = INSERTION;
while width < len {
// merge the sorted runs of length `width` in `buf_dat` two at
// a time, placing the result in `buf_tmp`.
// 0 <= start <= len.
for start in range_step(0, len, 2 * width) {
// manipulate pointers directly for speed (rather than
// using a `for` loop with `range` and `.offset` inside
// that loop).
unsafe {
// the end of the first run & start of the
// second. Offset of `len` is defined, since this is
// precisely one byte past the end of the object.
let right_start = buf_dat.offset(cmp::min(start + width, len) as int);
// end of the second. Similar reasoning to the above re safety.
let right_end_idx = cmp::min(start + 2 * width, len);
let right_end = buf_dat.offset(right_end_idx as int);
// the pointers to the elements under consideration
// from the two runs.
// both of these are in bounds.
let mut left = buf_dat.offset(start as int);
let mut right = right_start;
// where we're putting the results, it is a run of
// length `2*width`, so we step it once for each step
// of either `left` or `right`. `buf_tmp` has length
// `len`, so these are in bounds.
let mut out = buf_tmp.offset(start as int);
let out_end = buf_tmp.offset(right_end_idx as int);
while out < out_end {
// Either the left or the right run are exhausted,
// so just copy the remainder from the other run
// and move on; this gives a huge speed-up (order
// of 25%) for mostly sorted vectors (the best
// case).
if left == right_start {
// the number remaining in this run.
let elems = (right_end as uint - right as uint) / mem::size_of::<T>();
ptr::copy_nonoverlapping_memory(out, right, elems);
break;
} else if right == right_end {
let elems = (right_start as uint - left as uint) / mem::size_of::<T>();
ptr::copy_nonoverlapping_memory(out, left, elems);
break;
}
// check which side is smaller, and that's the
// next element for the new run.
// `left < right_start` and `right < right_end`,
// so these are valid.
let to_copy = if compare(&*left, &*right) == Greater {
step(&mut right)
} else {
step(&mut left)
};
ptr::copy_nonoverlapping_memory(out, to_copy, 1);
step(&mut out);
}
}
}
util::swap(&mut buf_dat, &mut buf_tmp);
width *= 2;
}
// write the result to `v` in one go, so that there are never two copies
// of the same object in `v`.
unsafe {
ptr::copy_nonoverlapping_memory(v.as_mut_ptr(), buf_dat, len);
}
// increment the pointer, returning the old pointer.
#[inline(always)]
unsafe fn step<T>(ptr: &mut *mut T) -> *mut T {
let old = *ptr;
*ptr = ptr.offset(1);
old
}
}
/// Extension methods for vectors such that their elements are
/// mutable.
pub trait MutableVector<'a, T> {
@ -2020,6 +2165,25 @@ pub trait MutableVector<'a, T> {
/// Reverse the order of elements in a vector, in place
fn reverse(self);
/// Sort the vector, in place, using `compare` to compare
/// elements.
///
/// This sort is `O(n log n)` worst-case and stable, but allocates
/// approximately `2 * n`, where `n` is the length of `self`.
///
/// # Example
///
/// ```rust
/// let mut v = [5, 4, 1, 3, 2];
/// v.sort(|a, b| a.cmp(b));
/// assert_eq!(v, [1, 2, 3, 4, 5]);
///
/// // reverse sorting
/// v.sort(|a, b| b.cmp(a));
/// assert_eq!(v, [5, 4, 3, 2, 1]);
/// ```
fn sort_by(self, compare: |&T, &T| -> Ordering);
/**
* Consumes `src` and moves as many elements as it can into `self`
* from the range [start,end).
@ -2163,6 +2327,11 @@ impl<'a,T> MutableVector<'a, T> for &'a mut [T] {
}
}
#[inline]
fn sort_by(self, compare: |&T, &T| -> Ordering) {
merge_sort(self, compare)
}
#[inline]
fn move_from(self, mut src: ~[T], start: uint, end: uint) -> uint {
for (a, b) in self.mut_iter().zip(src.mut_slice(start, end).mut_iter()) {
@ -2216,6 +2385,32 @@ impl<'a, T:Clone> MutableCloneableVector<T> for &'a mut [T] {
}
}
/// Methods for mutable vectors with orderable elements, such as
/// in-place sorting.
pub trait MutableTotalOrdVector<T> {
/// Sort the vector, in place.
///
/// This is equivalent to `self.sort_by(std::vec::SortForward)`.
///
/// # Example
///
/// ```rust
/// use std::vec;
///
/// let mut v = [-5, 4, 1, -3, 2];
///
/// v.sort();
/// assert_eq!(v, [-5, -3, 1, 2, 4]);
/// ```
fn sort(self);
}
impl<'a, T: TotalOrd> MutableTotalOrdVector<T> for &'a mut [T] {
#[inline]
fn sort(self) {
self.sort_by(|a,b| a.cmp(b))
}
}
/**
* Constructs a vector from an unsafe pointer to a buffer
*
@ -2686,6 +2881,7 @@ mod tests {
use vec::*;
use cmp::*;
use prelude::*;
use rand::{Rng, task_rng};
fn square(n: uint) -> uint { n * n }
@ -3279,6 +3475,64 @@ mod tests {
assert!(v3.is_empty());
}
#[test]
fn test_sort() {
for len in range(4u, 25) {
for _ in range(0, 100) {
let mut v = task_rng().gen_vec::<uint>(len);
let mut v1 = v.clone();
v.sort();
assert!(v.windows(2).all(|w| w[0] <= w[1]));
v1.sort_by(|a, b| a.cmp(b));
assert!(v1.windows(2).all(|w| w[0] <= w[1]));
v1.sort_by(|a, b| b.cmp(a));
assert!(v1.windows(2).all(|w| w[0] >= w[1]));
}
}
// shouldn't fail/crash
let mut v: [uint, .. 0] = [];
v.sort();
let mut v = [0xDEADBEEF];
v.sort();
assert_eq!(v, [0xDEADBEEF]);
}
#[test]
fn test_sort_stability() {
for len in range(4, 25) {
for _ in range(0 , 10) {
let mut counts = [0, .. 10];
// create a vector like [(6, 1), (5, 1), (6, 2), ...],
// where the first item of each tuple is random, but
// the second item represents which occurrence of that
// number this element is, i.e. the second elements
// will occur in sorted order.
let mut v = range(0, len).map(|_| {
let n = task_rng().gen::<uint>() % 10;
counts[n] += 1;
(n, counts[n])
}).to_owned_vec();
// only sort on the first element, so an unstable sort
// may mix up the counts.
v.sort_by(|&(a,_), &(b,_)| a.cmp(&b));
// this comparison includes the count (the second item
// of the tuple), so elements with equal first items
// will need to be ordered with increasing
// counts... i.e. exactly asserting that this sort is
// stable.
assert!(v.windows(2).all(|w| w[0] <= w[1]));
}
}
}
#[test]
fn test_partition() {
assert_eq!((~[]).partition(|x: &int| *x < 3), (~[], ~[]));
@ -4102,10 +4356,11 @@ mod bench {
use extra::test::BenchHarness;
use iter::range;
use vec;
use vec::VectorVector;
use vec::{VectorVector, MutableTotalOrdVector};
use option::*;
use ptr;
use rand::{weak_rng, Rng};
use mem;
#[bench]
fn iterator(bh: &mut BenchHarness) {
@ -4306,4 +4561,43 @@ mod bench {
}
})
}
#[bench]
fn sort_random_small(bh: &mut BenchHarness) {
let mut rng = weak_rng();
bh.iter(|| {
let mut v: ~[u64] = rng.gen_vec(5);
v.sort();
});
bh.bytes = 5 * mem::size_of::<u64>() as u64;
}
#[bench]
fn sort_random_medium(bh: &mut BenchHarness) {
let mut rng = weak_rng();
bh.iter(|| {
let mut v: ~[u64] = rng.gen_vec(100);
v.sort();
});
bh.bytes = 100 * mem::size_of::<u64>() as u64;
}
#[bench]
fn sort_random_large(bh: &mut BenchHarness) {
let mut rng = weak_rng();
bh.iter(|| {
let mut v: ~[u64] = rng.gen_vec(10000);
v.sort();
});
bh.bytes = 10000 * mem::size_of::<u64>() as u64;
}
#[bench]
fn sort_sorted(bh: &mut BenchHarness) {
let mut v = vec::from_fn(10000, |i| i);
bh.iter(|| {
v.sort();
});
bh.bytes = (v.len() * mem::size_of_val(&v[0])) as u64;
}
}

View file

@ -10,8 +10,6 @@
// Functions dealing with attributes and meta items
use extra;
use ast;
use ast::{Attribute, Attribute_, MetaItem, MetaWord, MetaNameValue, MetaList};
use codemap::{Span, Spanned, spanned, dummy_spanned};
@ -205,7 +203,7 @@ pub fn sort_meta_items(items: &[@MetaItem]) -> ~[@MetaItem] {
.map(|&mi| (mi.name(), mi))
.collect::<~[(@str, @MetaItem)]>();
extra::sort::quick_sort(v, |&(a, _), &(b, _)| a <= b);
v.sort_by(|&(a, _), &(b, _)| a.cmp(&b));
// There doesn't seem to be a more optimal way to do this
v.move_iter().map(|(_, m)| {

View file

@ -15,7 +15,6 @@
extern mod extra;
use extra::sort;
use std::cmp::Ord;
use std::comm;
use std::hashmap::HashMap;
@ -27,35 +26,32 @@ use std::task;
use std::util;
use std::vec;
fn f64_cmp(x: f64, y: f64) -> Ordering {
// arbitrarily decide that NaNs are larger than everything.
if y.is_nan() {
Less
} else if x.is_nan() {
Greater
} else if x < y {
Less
} else if x == y {
Equal
} else {
Greater
}
}
// given a map, print a sorted version of it
fn sort_and_fmt(mm: &HashMap<~[u8], uint>, total: uint) -> ~str {
fn pct(xx: uint, yy: uint) -> f64 {
return (xx as f64) * 100.0 / (yy as f64);
}
fn le_by_val<TT:Clone,
UU:Clone + Ord>(
kv0: &(TT,UU),
kv1: &(TT,UU))
-> bool {
let (_, v0) = (*kv0).clone();
let (_, v1) = (*kv1).clone();
return v0 >= v1;
}
fn le_by_key<TT:Clone + Ord,
UU:Clone>(
kv0: &(TT,UU),
kv1: &(TT,UU))
-> bool {
let (k0, _) = (*kv0).clone();
let (k1, _) = (*kv1).clone();
return k0 <= k1;
}
// sort by key, then by value
fn sortKV<TT:Clone + Ord, UU:Clone + Ord>(orig: ~[(TT,UU)]) -> ~[(TT,UU)] {
return sort::merge_sort(sort::merge_sort(orig, le_by_key), le_by_val);
fn sortKV(mut orig: ~[(~[u8],f64)]) -> ~[(~[u8],f64)] {
orig.sort_by(|&(ref a, _), &(ref b, _)| a.cmp(b));
orig.sort_by(|&(_, a), &(_, b)| f64_cmp(b, a));
orig
}
let mut pairs = ~[];

View file

@ -9,7 +9,6 @@ use std::libc::{stat, strlen};
use std::ptr::null;
use std::unstable::intrinsics::init;
use std::vec::{reverse};
use extra::sort::quick_sort3;
static LINE_LEN: uint = 80;
static TABLE: [u8, ..4] = [ 'A' as u8, 'C' as u8, 'G' as u8, 'T' as u8 ];
@ -267,7 +266,7 @@ fn print_frequencies(frequencies: &Table, frame: i32) {
for frequencies.each |entry| {
vector.push((entry.code, entry.count));
}
quick_sort3(vector);
vector.sort();
let mut total_count = 0;
for vector.each |&(_, count)| {

View file

@ -0,0 +1,89 @@
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::task;
use std::rand::{task_rng, Rng};
static MAX_LEN: uint = 20;
static mut drop_counts: [uint, .. MAX_LEN] = [0, .. MAX_LEN];
static mut clone_count: uint = 0;
#[deriving(Rand, Ord, TotalEq, TotalOrd)]
struct DropCounter { x: uint, clone_num: uint }
impl Clone for DropCounter {
fn clone(&self) -> DropCounter {
let num = unsafe { clone_count };
unsafe { clone_count += 1; }
DropCounter {
x: self.x,
clone_num: num
}
}
}
impl Drop for DropCounter {
fn drop(&mut self) {
unsafe {
// Rand creates some with arbitrary clone_nums
if self.clone_num < MAX_LEN {
drop_counts[self.clone_num] += 1;
}
}
}
}
pub fn main() {
// len can't go above 64.
for len in range(2u, MAX_LEN) {
for _ in range(0, 10) {
let main = task_rng().gen_vec::<DropCounter>(len);
// work out the total number of comparisons required to sort
// this array...
let mut count = 0;
main.clone().sort_by(|a, b| { count += 1; a.cmp(b) });
// ... and then fail on each and every single one.
for fail_countdown in range(0, count) {
// refresh the counters.
unsafe {
drop_counts = [0, .. MAX_LEN];
clone_count = 0;
}
let v = main.clone();
task::try(proc() {
let mut v = v;
let mut fail_countdown = fail_countdown;
v.sort_by(|a, b| {
if fail_countdown == 0 {
fail!()
}
fail_countdown -= 1;
a.cmp(b)
})
});
// check that the number of things dropped is exactly
// what we expect (i.e. the contents of `v`).
unsafe {
for (i, &c) in drop_counts.iter().enumerate() {
let expected = if i < len {1} else {0};
assert!(c == expected,
"found drop count == {} for i == {}, len == {}",
c, i, len);
}
}
}
}
}
}