#![deny(missing_docs)]
#![doc = include_str!("../README.md")]
use std::{
cmp::Reverse, default::Default, fmt, iter::FromIterator, iter::FusedIterator, num::Wrapping,
};
type Bucket<K, V> = Vec<(K, V)>;
#[derive(Clone)]
pub struct RadixHeapMap<K, V> {
len: usize,
top: Option<K>,
buckets: Vec<Bucket<K, V>>,
initial: Bucket<K, V>,
}
impl<K: Radix + Ord + Copy, V> RadixHeapMap<K, V> {
pub fn new() -> RadixHeapMap<K, V> {
RadixHeapMap {
len: 0,
top: None,
buckets: (0..=K::RADIX_BITS).map(|_| Bucket::default()).collect(),
initial: Bucket::default(),
}
}
pub fn new_at(top: K) -> RadixHeapMap<K, V> {
RadixHeapMap {
len: 0,
top: Some(top),
buckets: (0..=K::RADIX_BITS).map(|_| Bucket::default()).collect(),
initial: Bucket::default(),
}
}
pub fn clear(&mut self) {
self.len = 0;
self.top = None;
self.initial.clear();
for bucket in &mut self.buckets {
bucket.clear();
}
}
pub fn clear_to(&mut self, top: K) {
self.clear();
self.top = Some(top);
}
pub fn constrain(&mut self) {
let repush = if self.top.is_some() {
let index = self
.buckets
.iter()
.enumerate()
.find(|&(_, bucket)| !bucket.is_empty())
.map(|(i, _)| i);
match index {
None | Some(0) => None,
Some(index) => {
let (buckets, rest) = self.buckets.split_at_mut(index);
Some((buckets, &mut rest[0]))
}
}
} else if !self.initial.is_empty() {
Some((&mut self.buckets[..], &mut self.initial))
} else {
None
};
if let Some((buckets, repush)) = repush {
let top = *repush
.iter()
.map(|(k, _)| k)
.max()
.expect("Expected non-empty bucket");
self.top = Some(top);
repush.drain(..).for_each(|(key, value)| {
buckets[key.radix_distance(&top) as usize].push((key, value))
});
}
}
#[inline]
pub fn push(&mut self, key: K, value: V) {
let bucket = if let Some(top) = self.top {
assert!(key <= top, "Key must be lower or equal to current top key");
&mut self.buckets[key.radix_distance(&top) as usize]
} else {
&mut self.initial
};
bucket.push((key, value));
self.len += 1;
}
#[inline]
pub fn pop(&mut self) -> Option<(K, V)> {
let mut constrained = false;
loop {
let pop = self.buckets[0].pop();
if pop.is_some() {
self.len -= 1;
return pop;
} else if constrained {
return pop;
} else {
constrained = true;
self.constrain()
}
}
}
#[inline]
pub fn len(&self) -> usize {
self.len
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[inline]
pub fn top(&self) -> Option<K> {
self.top
}
pub fn shrink_to_fit(&mut self) {
self.initial.shrink_to_fit();
for bucket in &mut self.buckets {
bucket.shrink_to_fit();
}
}
pub fn iter(&self) -> Iter<K, V> {
Iter {
cur_bucket: self.initial.iter(),
buckets: self.buckets.iter(),
size: self.len,
}
}
pub fn keys(&self) -> Keys<K, V> {
Keys(self.iter())
}
pub fn values(&self) -> Values<K, V> {
Values(self.iter())
}
}
impl<K: Radix + Ord + Copy, V> Default for RadixHeapMap<K, V> {
fn default() -> RadixHeapMap<K, V> {
RadixHeapMap::new()
}
}
impl<K: Radix + Ord + Copy, V> FromIterator<(K, V)> for RadixHeapMap<K, V> {
fn from_iter<I>(iter: I) -> RadixHeapMap<K, V>
where
I: IntoIterator<Item = (K, V)>,
{
let mut heap = RadixHeapMap::new();
for (k, v) in iter {
heap.push(k, v);
}
heap
}
}
impl<K: Radix + Ord + Copy, V> Extend<(K, V)> for RadixHeapMap<K, V> {
fn extend<I>(&mut self, iter: I)
where
I: IntoIterator<Item = (K, V)>,
{
for (k, v) in iter {
self.push(k, v);
}
}
}
impl<'a, K: Radix + Ord + Copy + 'a, V: Copy + 'a> Extend<&'a (K, V)> for RadixHeapMap<K, V> {
fn extend<I>(&mut self, iter: I)
where
I: IntoIterator<Item = &'a (K, V)>,
{
for &(k, v) in iter {
self.push(k, v);
}
}
}
impl<K: Radix + Ord + Copy + fmt::Debug, V: fmt::Debug> fmt::Debug for RadixHeapMap<K, V> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_list().entries(self.iter()).finish()
}
}
#[derive(Clone)]
pub struct IntoIter<K, V> {
cur_bucket: std::vec::IntoIter<(K, V)>,
buckets: std::vec::IntoIter<Bucket<K, V>>,
size: usize,
}
impl<K, V> Iterator for IntoIter<K, V> {
type Item = (K, V);
fn next(&mut self) -> Option<Self::Item> {
loop {
if let pair @ Some(_) = self.cur_bucket.next() {
self.size -= 1;
return pair;
} else {
self.cur_bucket = self.buckets.next()?.into_iter();
}
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.size, Some(self.size))
}
fn for_each<F>(self, mut f: F)
where
F: FnMut(Self::Item),
{
self.cur_bucket.for_each(&mut f);
self.buckets.for_each(|b| b.into_iter().for_each(&mut f));
}
}
impl<K, V> ExactSizeIterator for IntoIter<K, V> {}
impl<K, V> FusedIterator for IntoIter<K, V> {}
#[derive(Clone)]
pub struct Iter<'a, K, V> {
cur_bucket: std::slice::Iter<'a, (K, V)>,
buckets: std::slice::Iter<'a, Bucket<K, V>>,
size: usize,
}
impl<'a, K, V> Iterator for Iter<'a, K, V> {
type Item = &'a (K, V);
fn next(&mut self) -> Option<Self::Item> {
loop {
if let pair @ Some(_) = self.cur_bucket.next() {
self.size -= 1;
return pair;
} else {
self.cur_bucket = self.buckets.next()?.iter();
}
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.size, Some(self.size))
}
fn for_each<F>(self, mut f: F)
where
F: FnMut(Self::Item),
{
self.cur_bucket.for_each(&mut f);
self.buckets.for_each(|b| b.iter().for_each(&mut f));
}
}
impl<'a, K, V> ExactSizeIterator for Iter<'a, K, V> {}
impl<'a, K, V> FusedIterator for Iter<'a, K, V> {}
#[derive(Clone)]
pub struct Keys<'a, K, V>(Iter<'a, K, V>);
impl<'a, K, V> Iterator for Keys<'a, K, V> {
type Item = &'a K;
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(|(k, _)| k)
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
fn for_each<F>(self, mut f: F)
where
F: FnMut(Self::Item),
{
self.0.for_each(|(k, _)| f(k))
}
}
impl<'a, K, V> ExactSizeIterator for Keys<'a, K, V> {}
impl<'a, K, V> FusedIterator for Keys<'a, K, V> {}
#[derive(Clone)]
pub struct Values<'a, K, V>(Iter<'a, K, V>);
impl<'a, K, V> Iterator for Values<'a, K, V> {
type Item = &'a V;
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(|(_, v)| v)
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
fn for_each<F>(self, mut f: F)
where
F: FnMut(Self::Item),
{
self.0.for_each(|(_, v)| f(v))
}
}
impl<'a, K, V> ExactSizeIterator for Values<'a, K, V> {}
impl<'a, K, V> FusedIterator for Values<'a, K, V> {}
impl<K: Radix + Ord + Copy, V> IntoIterator for RadixHeapMap<K, V> {
type Item = (K, V);
type IntoIter = IntoIter<K, V>;
fn into_iter(self) -> Self::IntoIter {
IntoIter {
cur_bucket: self.initial.into_iter(),
buckets: self.buckets.into_iter(),
size: self.len,
}
}
}
impl<'a, K: Radix + Ord + Copy, V> IntoIterator for &'a RadixHeapMap<K, V> {
type Item = &'a (K, V);
type IntoIter = Iter<'a, K, V>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
pub trait Radix {
fn radix_similarity(&self, other: &Self) -> u32;
fn radix_distance(&self, other: &Self) -> u32 {
Self::RADIX_BITS - self.radix_similarity(other)
}
const RADIX_BITS: u32;
}
macro_rules! radix_wrapper_impl {
($t:ident) => {
impl<T: Radix> Radix for $t<T> {
#[inline]
fn radix_similarity(&self, other: &$t<T>) -> u32 {
self.0.radix_similarity(&other.0)
}
const RADIX_BITS: u32 = T::RADIX_BITS;
}
};
}
radix_wrapper_impl!(Reverse);
radix_wrapper_impl!(Wrapping);
macro_rules! radix_int_impl {
($t:ty) => {
impl Radix for $t {
#[inline]
fn radix_similarity(&self, other: &$t) -> u32 {
(self ^ other).leading_zeros()
}
const RADIX_BITS: u32 = (std::mem::size_of::<$t>() * 8) as u32;
}
};
}
radix_int_impl!(i8);
radix_int_impl!(i16);
radix_int_impl!(i32);
radix_int_impl!(i64);
radix_int_impl!(i128);
radix_int_impl!(isize);
radix_int_impl!(u8);
radix_int_impl!(u16);
radix_int_impl!(u32);
radix_int_impl!(u64);
radix_int_impl!(u128);
radix_int_impl!(usize);
#[cfg(feature = "ordered-float")]
macro_rules! radix_float_impl {
($t:ty, $bits:ty, $wrapper:path) => {
impl Radix for $wrapper {
#[inline]
fn radix_similarity(&self, other: &$wrapper) -> u32 {
let self_bits: $bits = self.to_bits();
let other_bits: $bits = other.to_bits();
self_bits.radix_similarity(&other_bits)
}
const RADIX_BITS: u32 = <$bits>::RADIX_BITS;
}
};
}
#[cfg(feature = "ordered-float")]
radix_float_impl!(f32, u32, ordered_float::NotNan<f32>);
#[cfg(feature = "ordered-float")]
radix_float_impl!(f64, u64, ordered_float::NotNan<f64>);
impl Radix for () {
#[inline]
fn radix_similarity(&self, _: &()) -> u32 {
0
}
const RADIX_BITS: u32 = 0;
}
macro_rules! radix_tuple_impl {
($(
$Tuple:ident {
$(($idx:tt) -> $T:ident)+
}
)+) => {
$(
impl<$($T:Radix),+> Radix for ($($T,)+) {
#[inline]
fn radix_similarity(&self, other: &($($T,)+)) -> u32 {
let similarity = 0;
$(
let s = self.$idx.radix_similarity(&other.$idx);
let similarity = similarity + s;
if s < <$T as Radix>::RADIX_BITS { return similarity }
)+
return similarity;
}
const RADIX_BITS: u32 = 0 $(+<$T as Radix>::RADIX_BITS)+;
}
)+
}
}
radix_tuple_impl! {
Tuple1 {
(0) -> A
}
Tuple2 {
(0) -> A
(1) -> B
}
Tuple3 {
(0) -> A
(1) -> B
(2) -> C
}
Tuple4 {
(0) -> A
(1) -> B
(2) -> C
(3) -> D
}
Tuple5 {
(0) -> A
(1) -> B
(2) -> C
(3) -> D
(4) -> E
}
Tuple6 {
(0) -> A
(1) -> B
(2) -> C
(3) -> D
(4) -> E
(5) -> F
}
Tuple7 {
(0) -> A
(1) -> B
(2) -> C
(3) -> D
(4) -> E
(5) -> F
(6) -> G
}
Tuple8 {
(0) -> A
(1) -> B
(2) -> C
(3) -> D
(4) -> E
(5) -> F
(6) -> G
(7) -> H
}
Tuple9 {
(0) -> A
(1) -> B
(2) -> C
(3) -> D
(4) -> E
(5) -> F
(6) -> G
(7) -> H
(8) -> I
}
Tuple10 {
(0) -> A
(1) -> B
(2) -> C
(3) -> D
(4) -> E
(5) -> F
(6) -> G
(7) -> H
(8) -> I
(9) -> J
}
Tuple11 {
(0) -> A
(1) -> B
(2) -> C
(3) -> D
(4) -> E
(5) -> F
(6) -> G
(7) -> H
(8) -> I
(9) -> J
(10) -> K
}
Tuple12 {
(0) -> A
(1) -> B
(2) -> C
(3) -> D
(4) -> E
(5) -> F
(6) -> G
(7) -> H
(8) -> I
(9) -> J
(10) -> K
(11) -> L
}
}
#[cfg(test)]
mod tests {
extern crate quickcheck;
use self::quickcheck::{quickcheck, TestResult};
use super::Radix;
use super::RadixHeapMap;
use std::cmp::Reverse;
#[test]
fn radix_dist() {
assert!(4u32.radix_distance(&2) == 3);
assert!(3u32.radix_distance(&2) == 1);
assert!(2u32.radix_distance(&2) == 0);
assert!(1u32.radix_distance(&2) == 2);
assert!(0u32.radix_distance(&2) == 2);
}
#[test]
fn clear() {
let mut heap = RadixHeapMap::new();
heap.push(0u32, 'a');
heap.clear();
assert!(heap.pop().is_none());
}
#[test]
fn push_pop() {
let mut heap = RadixHeapMap::new();
heap.push(0u32, 'a');
heap.push(3, 'b');
heap.push(2, 'c');
assert!(heap.len() == 3);
assert!(!heap.is_empty());
assert!(heap.pop() == Some((3, 'b')));
assert!(heap.pop() == Some((2, 'c')));
assert!(heap.pop() == Some((0, 'a')));
assert!(heap.pop() == None);
assert!(heap.len() == 0);
assert!(heap.is_empty());
}
#[test]
fn rev_push_pop() {
let mut heap = RadixHeapMap::new();
heap.push(Reverse(0), 'a');
heap.push(Reverse(3), 'b');
heap.push(Reverse(2), 'c');
assert!(heap.len() == 3);
assert!(!heap.is_empty());
assert!(heap.pop() == Some((Reverse(0), 'a')));
assert!(heap.pop() == Some((Reverse(2), 'c')));
assert!(heap.pop() == Some((Reverse(3), 'b')));
assert!(heap.pop() == None);
assert!(heap.len() == 0);
assert!(heap.is_empty());
}
#[test]
#[should_panic]
fn push_pop_panic() {
let mut heap = RadixHeapMap::new();
heap.push(0u32, 'a');
heap.push(3, 'b');
assert!(heap.pop() == Some((3, 'b')));
heap.push(4, 'd');
}
#[test]
fn sort() {
fn prop<T: Ord + Radix + Copy>(mut xs: Vec<T>) -> bool {
let mut heap: RadixHeapMap<_, _> =
xs.iter().enumerate().map(|(i, &d)| (d, i)).collect();
xs.sort();
while xs.pop() == heap.pop().map(|(k, _)| k) {
if xs.is_empty() {
return true;
}
}
return false;
}
quickcheck(prop as fn(Vec<()>) -> bool);
quickcheck(prop as fn(Vec<u32>) -> bool);
quickcheck(prop as fn(Vec<i32>) -> bool);
quickcheck(prop as fn(Vec<(u32, i32)>) -> bool);
quickcheck(prop as fn(Vec<u8>) -> bool);
quickcheck(prop as fn(Vec<i16>) -> bool);
quickcheck(prop as fn(Vec<(i64, usize)>) -> bool);
quickcheck(prop as fn(Vec<i128>) -> bool);
quickcheck(prop as fn(Vec<u128>) -> bool);
}
#[cfg(feature = "ordered-float")]
#[test]
fn sort_float() {
fn prop(xs: Vec<f32>) -> TestResult {
if xs.iter().any(|x| x.is_nan()) {
return TestResult::discard();
}
let mut xs: Vec<_> = xs
.into_iter()
.map(|x| ordered_float::NotNan::new(x).unwrap())
.collect();
xs.sort();
let mut heap: RadixHeapMap<_, _> =
xs.iter().enumerate().map(|(i, &d)| (d, i)).collect();
while xs.pop() == heap.pop().map(|(k, _)| k) {
if xs.is_empty() {
return TestResult::passed();
}
}
return TestResult::failed();
}
quickcheck(prop as fn(Vec<f32>) -> TestResult);
}
#[test]
fn iter_yeilds_all_elements() {
fn prop<T: Ord + Radix + Copy>(mut xs: Vec<T>) -> TestResult {
let heap = xs.iter().map(|&d| (d, ())).collect::<RadixHeapMap<_, _>>();
for (k, ()) in heap.iter() {
for i in 0..xs.len() {
if xs[i] == *k {
xs.remove(i);
break;
}
}
}
if xs.is_empty() {
TestResult::passed()
} else {
TestResult::failed()
}
}
quickcheck(prop as fn(Vec<u32>) -> TestResult);
quickcheck(prop as fn(Vec<i32>) -> TestResult);
quickcheck(prop as fn(Vec<(u32, i32)>) -> TestResult);
quickcheck(prop as fn(Vec<u8>) -> TestResult);
quickcheck(prop as fn(Vec<i16>) -> TestResult);
quickcheck(prop as fn(Vec<(i64, usize)>) -> TestResult);
}
#[test]
fn into_iter_inital() {
let mut heap = RadixHeapMap::new();
heap.push(1, 2);
heap.push(5, 2);
let mut vec: Vec<_> = heap.into_iter().collect();
vec.sort();
assert_eq!(vec, vec![(1, 2), (5, 2)]);
}
#[test]
fn into_iter() {
let mut heap = RadixHeapMap::new();
heap.push(1, 2);
heap.push(5, 4);
heap.push(7, 1);
assert_eq!(Some((7, 1)), heap.pop());
let mut vec: Vec<_> = heap.into_iter().collect();
vec.sort();
assert_eq!(vec, vec![(1, 2), (5, 4)]);
}
}