#[cfg(test)]
extern crate quickcheck;
use std::borrow::Cow;
pub trait DigitAt {
#[inline]
fn get_digit_at(&self, digit: usize) -> Option<u8>;
}
impl DigitAt for u8 {
#[inline]
fn get_digit_at(&self, digit: usize) -> Option<u8> {
if digit == 0 {
Some(*self)
} else {
None
}
}
}
impl DigitAt for u16 {
#[inline]
fn get_digit_at(&self, digit: usize) -> Option<u8> {
match digit {
0 => Some(((self & 0xFF00) >> 8) as u8),
1 => Some((self & 0xFF) as u8),
_ => None,
}
}
}
impl DigitAt for u32 {
#[inline]
fn get_digit_at(&self, digit: usize) -> Option<u8> {
match digit {
0 => Some(((self & 0xFF000000) >> 24) as u8),
1 => Some(((self & 0xFF0000) >> 16) as u8),
2 => Some(((self & 0xFF00) >> 8) as u8),
3 => Some((*self & 0xFF) as u8),
_ => None,
}
}
}
impl DigitAt for u64 {
#[inline]
fn get_digit_at(&self, digit: usize) -> Option<u8> {
match digit {
0 => Some(((self & 0xFF00000000000000) >> 56) as u8),
1 => Some(((self & 0xFF000000000000) >> 48) as u8),
2 => Some(((self & 0xFF0000000000) >> 40) as u8),
3 => Some(((self & 0xFF00000000) >> 32) as u8),
4 => Some(((self & 0xFF000000) >> 24) as u8),
5 => Some(((self & 0xFF0000) >> 16) as u8),
6 => Some(((self & 0xFF00) >> 8) as u8),
7 => Some((self & 0xFF) as u8),
_ => None,
}
}
}
impl<'a> DigitAt for &'a str {
#[inline]
fn get_digit_at(&self, digit: usize) -> Option<u8> {
if self.len() > digit {
Some(self.as_bytes()[digit])
} else {
None
}
}
}
impl<'a> DigitAt for String {
#[inline]
fn get_digit_at(&self, digit: usize) -> Option<u8> {
if self.len() > digit {
Some(self.as_bytes()[digit])
} else {
None
}
}
}
impl<'a> DigitAt for [u8] {
#[inline]
fn get_digit_at(&self, digit: usize) -> Option<u8> {
if self.len() > digit {
Some(self[digit])
} else {
None
}
}
}
impl<'a> DigitAt for &'a [u8] {
#[inline]
fn get_digit_at(&self, digit: usize) -> Option<u8> {
if self.len() > digit {
Some(self[digit])
} else {
None
}
}
}
impl<'a> DigitAt for Cow<'a, str> {
#[inline]
fn get_digit_at(&self, digit: usize) -> Option<u8> {
if self.len() > digit {
Some(self.as_bytes()[digit])
} else {
None
}
}
}
impl<'a> DigitAt for AsRef<DigitAt> {
#[inline]
fn get_digit_at(&self, digit: usize) -> Option<u8> {
self.as_ref().get_digit_at(digit)
}
}
pub trait AFSortable {
#[inline]
fn af_sort_unstable(&mut self);
}
impl<T> AFSortable for [T]
where
T: DigitAt + Ord,
{
#[inline]
fn af_sort_unstable(&mut self) {
sort_unstable_by(self, ident);
}
}
#[inline]
fn ident<T>(t: &T) -> &T {
&t
}
#[inline]
pub fn sort_unstable_by<T, O, S>(vec: &mut [T], sort_by: S)
where
O: Ord + DigitAt + ?Sized,
S: Fn(&T) -> &O,
{
sort_req(vec, &sort_by, 0);
}
fn sort_req<T, O, S>(vec: &mut [T], sort_by: &S, depth: usize)
where
O: Ord + DigitAt + ?Sized,
S: Fn(&T) -> &O,
{
if vec.len() <= 32 {
vec.sort_unstable_by(|e1, e2| sort_by(e1).cmp(sort_by(e2)));
return;
}
let mut min = u16::max_value();
let mut max = 0u16;
{
for elem in vec.iter() {
match sort_by(elem).get_digit_at(depth) {
Some(v) => {
let radix_val = v as u16;
if radix_val < min {
min = radix_val;
}
if radix_val > max {
max = radix_val;
}
}
None => (),
}
}
}
if min == u16::max_value() {
return;
}
let num_items = (max - min + 2) as usize;
let mut counts: Vec<usize> = vec![0usize; num_items as usize];
{
for elem in vec.iter() {
let radix_val = match sort_by(elem).get_digit_at(depth) {
Some(r) => r as u16 + 1 - min,
None => 0,
};
counts[radix_val as usize] += 1;
}
}
let mut offsets: Vec<usize> = vec![0usize; num_items as usize];
{
let mut sum = 0usize;
for i in 0..counts.len() {
offsets[i as usize] = sum;
sum += counts[i as usize];
}
}
{
let mut next_free = offsets.clone();
let mut block = 0usize;
let mut i = 0usize;
while block < counts.len() - 1 {
if i >= offsets[block as usize + 1] as usize {
block += 1;
} else {
let radix_val = match sort_by(&vec[i]).get_digit_at(depth) {
Some(r) => r as u16 + 1 - min,
None => 0,
};
if radix_val == block as u16 {
i += 1;
} else {
vec.swap(i as usize, next_free[radix_val as usize] as usize);
next_free[radix_val as usize] += 1;
}
}
}
}
{
for i in 1..offsets.len() - 1 {
sort_req(
&mut vec[offsets[i as usize] as usize..offsets[i as usize + 1] as usize],
sort_by,
depth + 1,
);
}
sort_req(&mut vec[offsets[offsets.len() - 1]..], sort_by, depth + 1);
}
}
#[cfg(test)]
mod tests {
use super::AFSortable;
use super::DigitAt;
use quickcheck::QuickCheck;
use std::borrow::Cow;
#[test]
fn sorts_strings_same_as_unstable() {
fn compare_sort(mut strings: Vec<String>) -> bool {
let mut copy = strings.clone();
copy.sort_unstable();
strings.af_sort_unstable();
strings == copy
}
QuickCheck::new()
.tests(50000)
.quickcheck(compare_sort as fn(Vec<String>) -> bool);
}
#[test]
fn sorts_cow_str_same_as_unstable() {
fn compare_sort(strings: Vec<String>) -> bool {
let mut cows: Vec<Cow<str>> = strings.into_iter().map(|s| Cow::Owned(s)).collect();
let mut copy = cows.clone();
copy.sort_unstable();
cows.af_sort_unstable();
cows == copy
}
QuickCheck::new()
.tests(50000)
.quickcheck(compare_sort as fn(Vec<String>) -> bool);
}
#[test]
fn sorts_u8_ref_same_as_unstable() {
fn compare_sort(nums: Vec<Vec<u8>>) -> bool {
let mut refs: Vec<&[u8]> = nums.iter().map(|i| i.as_slice()).collect();
let mut copy = refs.clone();
copy.sort_unstable();
refs.af_sort_unstable();
refs == copy
}
QuickCheck::new()
.tests(50000)
.quickcheck(compare_sort as fn(Vec<Vec<u8>>) -> bool);
}
#[test]
fn sorts_u8_same_as_unstable() {
fn compare_sort(mut nums: Vec<u8>) -> bool {
let mut copy = nums.clone();
copy.sort_unstable();
nums.af_sort_unstable();
nums == copy
}
QuickCheck::new()
.tests(50000)
.quickcheck(compare_sort as fn(Vec<u8>) -> bool);
}
#[test]
fn sorts_u16_same_as_unstable() {
fn compare_sort(mut nums: Vec<u16>) -> bool {
let mut copy = nums.clone();
copy.sort_unstable();
nums.af_sort_unstable();
nums == copy
}
QuickCheck::new()
.tests(50000)
.quickcheck(compare_sort as fn(Vec<u16>) -> bool);
}
#[test]
fn sorts_u32_same_as_unstable() {
fn compare_sort(mut nums: Vec<u32>) -> bool {
let mut copy = nums.clone();
copy.sort_unstable();
nums.af_sort_unstable();
nums == copy
}
QuickCheck::new()
.tests(50000)
.quickcheck(compare_sort as fn(Vec<u32>) -> bool);
}
#[test]
fn sorts_u64_same_as_unstable() {
fn compare_sort(mut nums: Vec<u64>) -> bool {
let mut copy = nums.clone();
copy.sort_unstable();
nums.af_sort_unstable();
nums == copy
}
QuickCheck::new()
.tests(50000)
.quickcheck(compare_sort as fn(Vec<u64>) -> bool);
}
#[test]
fn sorts_tuples_same_as_unstable() {
fn compare_sort(mut tuples: Vec<(String, u8)>) -> bool {
let mut copy = tuples.clone();
copy.sort_unstable_by(|t1, t2| t1.0.cmp(&t2.0));
super::sort_unstable_by(&mut tuples, |t| &t.0);
tuples.into_iter().map(|t| t.0).collect::<Vec<String>>()
== copy.into_iter().map(|t| t.0).collect::<Vec<String>>()
}
QuickCheck::new()
.tests(50000)
.quickcheck(compare_sort as fn(Vec<(String, u8)>) -> bool);
}
#[test]
fn correct_radix_for_u8() {
let num = 0x50u8;
assert_eq!(Some(num), num.get_digit_at(0));
assert_eq!(None, num.get_digit_at(1));
assert_eq!(None, num.get_digit_at(5));
}
#[test]
fn correct_radix_for_u16() {
let num = 0x3050u16;
assert_eq!(Some(0x30), num.get_digit_at(0));
assert_eq!(Some(0x50), num.get_digit_at(1));
assert_eq!(None, num.get_digit_at(2));
assert_eq!(None, num.get_digit_at(5));
}
#[test]
fn correct_radix_for_u32() {
let num = 0x70103050u32;
assert_eq!(Some(0x70), num.get_digit_at(0));
assert_eq!(Some(0x10), num.get_digit_at(1));
assert_eq!(Some(0x30), num.get_digit_at(2));
assert_eq!(Some(0x50), num.get_digit_at(3));
assert_eq!(None, num.get_digit_at(4));
assert_eq!(None, num.get_digit_at(7));
}
#[test]
fn correct_radix_for_u64() {
let num = 0x2040608070103050u64;
assert_eq!(Some(0x20), num.get_digit_at(0));
assert_eq!(Some(0x40), num.get_digit_at(1));
assert_eq!(Some(0x60), num.get_digit_at(2));
assert_eq!(Some(0x80), num.get_digit_at(3));
assert_eq!(Some(0x70), num.get_digit_at(4));
assert_eq!(Some(0x10), num.get_digit_at(5));
assert_eq!(Some(0x30), num.get_digit_at(6));
assert_eq!(Some(0x50), num.get_digit_at(7));
assert_eq!(None, num.get_digit_at(8));
assert_eq!(None, num.get_digit_at(13));
}
}