order_stat/lib.rs
1//! Calculate order statistics.
2//!
3//! This crates allows one to compute the `k`th smallest element in
4//! (expected) linear time, and estimate a median element via the
5//! median-of-medians algorithm.
6//!
7//! [Source](https://github.com/huonw/order-stat)
8//!
9//! # Installation
10//!
11//! Ensure your `Cargo.toml` contains:
12//!
13//! ```toml
14//! [dependencies]
15//! order-stat = "0.1"
16//! ```
17//!
18//! # Examples
19//!
20//! The `kth` function allows computing order statistics of slices of
21//! `Ord` types.
22//!
23//! ```rust
24//! let mut v = [4, 1, 3, 2, 0];
25//!
26//! println!("the 2nd smallest element is {}", // 1
27//! order_stat::kth(&mut v, 1));
28//! ```
29//!
30//! The `kth_by` function takes an arbitrary closure, designed for
31//! order statistics of slices of floating point and more general
32//! comparisons.
33//!
34//! ```rust
35//! let mut v = [4.0, 1.0, 3.0, 2.0, 0.0];
36//!
37//! println!("the 3rd smallest element is {}", // 2
38//! order_stat::kth_by(&mut v, 2, |x, y| x.partial_cmp(y).unwrap()));
39//! ```
40//!
41//! ```rust
42//! #[derive(Debug)]
43//! struct Foo(i32);
44//!
45//! let mut v = [Foo(4), Foo(1), Foo(3), Foo(2), Foo(0)];
46//!
47//! println!("the element with the 4th smallest field is {:?}", // Foo(3)
48//! order_stat::kth_by(&mut v, 3, |x, y| x.0.cmp(&y.0)));
49//! ```
50//!
51//! The `median_of_medians` function gives an approximation to the
52//! median of a slice of an `Ord` type.
53//!
54//! ```rust
55//! let mut v = [4, 1, 3, 2, 0];
56//!
57//! println!("{} is close to the median",
58//! order_stat::median_of_medians(&mut v).1);
59//! ```
60//!
61//! It also has a `median_of_medians_by` variant to work with
62//! non-`Ord` types and more general comparisons.
63//!
64//! ```rust
65//! let mut v = [4.0, 1.0, 3.0, 2.0, 0.0];
66//!
67//! println!("{} is close to the median",
68//! order_stat::median_of_medians_by(&mut v, |x, y| x.partial_cmp(y).unwrap()).1);
69//! ```
70//!
71//! ```rust
72//! #[derive(Debug)]
73//! struct Foo(i32);
74//!
75//! let mut v = [Foo(4), Foo(1), Foo(3), Foo(2), Foo(0)];
76//!
77//! println!("{:?}'s field is close to the median of the fields",
78//! order_stat::median_of_medians_by(&mut v, |x, y| x.0.cmp(&y.0)).1);
79//! ```
80
81#![cfg_attr(all(test, feature = "unstable"), feature(test))]
82
83#[cfg(test)] extern crate rand;
84#[cfg(test)] extern crate quickcheck;
85#[cfg(all(test, feature = "unstable"))] extern crate test;
86
87use std::cmp::Ordering;
88
89#[cfg(all(test, feature = "unstable"))]
90#[macro_use]
91mod benches;
92
93mod floyd_rivest;
94mod quickselect;
95mod mom;
96
97/// Compute the `k`th order statistic (`k`th smallest element) of
98/// `array` via the Floyd-Rivest Algorithm[1].
99///
100/// The return value is the same as that returned by the following
101/// function (although the final order of `array` may differ):
102///
103/// ```rust
104/// fn kth_sort<T: Ord>(array: &mut [T], k: usize) -> &mut T {
105/// array.sort();
106/// &mut array[k]
107/// }
108/// ```
109///
110/// That is, `k` is zero-indexed, so the minimum corresponds to `k =
111/// 0` and the maximum `k = array.len() - 1`. Furthermore, `array` is
112/// mutated, placing the `k`th order statistic into `array[k]` and
113/// partitioning the remaining values so that smaller elements lie
114/// before and larger after.
115///
116/// If *n* is the length of `array`, `kth` operates with (expected)
117/// running time of *O(n)*, and a single query is usually much faster
118/// than sorting `array` (per `kth_sort`). However, if many order
119/// statistic queries need to be performed, it may be more efficient
120/// to sort and index directly.
121///
122/// For convenience, a reference to the requested order statistic,
123/// `array[k]`, is returned directly. It is also accessibly via
124/// `array` itself.
125///
126/// [1]: Robert W. Floyd and Ronald L. Rivest (1975). Algorithm 489:
127/// the algorithm SELECT—for finding the *i*th smallest of *n* elements
128/// [M1]. *Commun. ACM* **18**, 3,
129/// 173. doi:[10.1145/360680.360694](http://doi.acm.org/10.1145/360680.360694).
130///
131/// # Panics
132///
133/// If `k >= array.len()`, `kth` panics.
134///
135/// # Examples
136///
137/// ```rust
138/// let mut v = [10, 0, -10, 20];
139/// let kth = order_stat::kth(&mut v, 2);
140///
141/// assert_eq!(*kth, 10);
142/// ```
143///
144/// If the order of the original array, or position of the element is
145/// important, one can collect references to a temporary before querying.
146///
147/// ```rust
148/// use std::mem;
149///
150/// let mut v = [10, 0, -10, 20];
151///
152/// // compute the order statistic of an array of references (the Ord
153/// // impl defers to the internals, so this is correct)
154/// let kth = *order_stat::kth(&mut v.iter().collect::<Vec<&i32>>(), 2);
155///
156/// // the position is the difference between the start of the array
157/// // and the order statistic's location.
158/// let index = (kth as *const _ as usize - &v[0] as *const _ as usize) / mem::size_of_val(&v[0]);
159///
160/// assert_eq!(*kth, 10);
161/// assert_eq!(index, 0);
162/// ```
163pub fn kth<T: Ord>(array: &mut [T], k: usize) -> &mut T {
164 assert!(k < array.len(),
165 "order_stat::kth called with k = {} >= len = {}", k, array.len());
166 floyd_rivest::select(array, k, Ord::cmp);
167 &mut array[k]
168}
169
170/// Compute the element that is the `k`th order statistic in the
171/// ordering defined by `cmp` (that is, the `k`th element of `array.sort_by(cmp)`).
172///
173/// See special case `kth` for more details. It is equivalent to
174/// `kth_by(array, k, Ord::cmp)`.
175///
176/// # Panics
177///
178/// If `k >= array.len()`, `kth_by` panics.
179///
180/// # Examples
181///
182/// ```rust
183/// let mut v = [10.0, 0.0, -10.0, 20.0];
184/// // no NaNs, so partial_cmp works
185/// let kth = order_stat::kth_by(&mut v, 2, |x, y| x.partial_cmp(y).unwrap());
186///
187/// assert_eq!(*kth, 10.0);
188/// ```
189pub fn kth_by<T, F>(array: &mut [T], k: usize, cmp: F) -> &mut T
190 where F: FnMut(&T, &T) -> Ordering
191{
192 floyd_rivest::select(array, k, cmp);
193 &mut array[k]
194}
195
196pub use mom::{median_of_medians, median_of_medians_by};