radix-heap-0.4.2/.cargo_vcs_info.json0000644000000001360000000000100130610ustar { "git": { "sha1": "1b20603bf00300c157b1f90a31d7c452312f3207" }, "path_in_vcs": "" }radix-heap-0.4.2/.github/workflows/ci.yml000064400000000000000000000004050072674642500164130ustar 00000000000000name: CI on: [push] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 - uses: icepuma/rust-action@1.56.0 with: args: cargo fmt -- --check && cargo clippy -- -Dwarnings && cargo test --all-features radix-heap-0.4.2/.gitignore000064400000000000000000000004530072674642500136730ustar 00000000000000# Compiled files *.o *.so *.rlib *.dll # Executables *.exe # Generated by Cargo /target/ # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock Cargo.lock # Perf sampling data perf.data*radix-heap-0.4.2/Cargo.toml0000644000000020160000000000100110560ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "radix-heap" version = "0.4.2" authors = ["Mike Pedersen "] description = "Fast monotone priority queues" readme = "README.md" keywords = ["heap"] categories = ["data-structures"] license = "MIT" repository = "https://github.com/mpdn/radix-heap" [package.metadata.docs.rs] all-features = true [lib] bench = false [[bench]] name = "bench" harness = false [dependencies.ordered-float] version = "2.8.0" optional = true [dev-dependencies.criterion] version = "0.3.5" [dev-dependencies.quickcheck] version = "1.0.3" radix-heap-0.4.2/Cargo.toml.orig000064400000000000000000000010310072674642500145630ustar 00000000000000[package] authors = ["Mike Pedersen "] categories = ["data-structures"] description = "Fast monotone priority queues" keywords = ["heap"] license = "MIT" name = "radix-heap" readme = "README.md" repository = "https://github.com/mpdn/radix-heap" version = "0.4.2" edition = "2018" [lib] bench = false [[bench]] harness = false name = "bench" [dependencies.ordered-float] version = "2.8.0" optional = true [dev-dependencies] criterion = "0.3.5" quickcheck = "1.0.3" [package.metadata.docs.rs] all-features = trueradix-heap-0.4.2/LICENSE000064400000000000000000000020700072674642500127050ustar 00000000000000The MIT License (MIT) Copyright (c) 2016 Mike Pedersen 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 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. radix-heap-0.4.2/README.md000064400000000000000000000043230072674642500131620ustar 00000000000000# radix-heap Fast monotone priority queues. A monotone priority queue is a priority queue that requires the extracted elements follow a monotonic sequence. This means that, for a max-radix-heap, you cannot insert an element into a radix heap that is larger than the last extracted element. The key of the last extracted element is called the "top" key of the radix heap. Thus any value pushed onto the heap must be less than or equal to the top key. In return for this restriction, the radix heap provides two major benefits: - Inserts are `O(1)` and popping an element is amortized `O(log m)` where `m` is the difference between a popped key and the top key at the time the element was inserted. Note that this is independent of the number of elements in the radix heap. This means that for workloads where this difference is bounded by a constant, the radix heap has O(1) pop. - It is trivial to implement first-in-last-out order for equal keys in a radix heap. When implementing pathfinding, this corresponds to "tie-breaking" which can significantly improve performance. This is also possible to implement with a binary heap, but comes for free with a radix heap. - A radix heap has generally better cache coherence than a binary heap. # Performance Here is a summary of the benchmarks from running them on my machine: ```text astar_radix time: [2.6594 us 2.6622 us 2.6651 us] astar_binary time: [5.3698 us 5.3762 us 5.3827 us] pushpop_radix time: [97.601 us 97.784 us 97.987 us] pushpop_binary time: [507.28 us 507.44 us 507.60 us] ``` `astar` is a benchmark using a map from the [2D Pathfinding Banchmarks](https://movingai.com/benchmarks/grids.html). `pushpop` is a more heap-focused benchmark where values are repeatedly pushed and popped off a heap. # Example ``` extern crate radix_heap; let mut heap = radix_heap::RadixHeapMap::new(); heap.push(7, 'a'); heap.push(2, 'b'); heap.push(9, 'c'); assert!(heap.top() == None); assert!(heap.pop() == Some((9, 'c'))); assert!(heap.top() == Some(9)); assert!(heap.pop() == Some((7, 'a'))); assert!(heap.top() == Some(7)); assert!(heap.pop() == Some((2, 'b'))); assert!(heap.top() == Some(2)); assert!(heap.pop() == None); ``` radix-heap-0.4.2/benches/bench.rs000064400000000000000000000137460072674642500147500ustar 00000000000000use std::cmp::Reverse; use std::collections::BinaryHeap; use std::io::BufRead; use criterion::{black_box, Bencher, Criterion}; use criterion::{criterion_group, criterion_main}; use radix_heap::RadixHeapMap; type Pos = (u32, u32); struct Bool2D { height: u32, width: u32, values: Vec, } impl Bool2D { fn new(height: u32, width: u32) -> Bool2D { Bool2D { height, width, values: vec![false; (height * width) as usize], } } fn parse_map(mut bytes: &[u8]) -> Bool2D { let mut line = String::new(); bytes.read_line(&mut line).unwrap(); assert_eq!(line, "type octile\n"); line.clear(); bytes.read_line(&mut line).unwrap(); let mut words = line.split_whitespace(); assert_eq!(words.next(), Some("height")); let height: u32 = words.next().unwrap().parse().unwrap(); line.clear(); bytes.read_line(&mut line).unwrap(); let mut words = line.split_whitespace(); assert_eq!(words.next(), Some("width")); let width: u32 = words.next().unwrap().parse().unwrap(); line.clear(); bytes.read_line(&mut line).unwrap(); assert_eq!(line, "map\n"); line.clear(); let mut values = Vec::new(); for _ in 0..height { bytes.read_line(&mut line).unwrap(); values.extend(line.as_bytes().iter().map(|&x| x == b'.')); line.clear(); } Bool2D { height, width, values, } } fn clear(&mut self) { self.values.fill(false); } fn get_mut(&mut self, pos: Pos) -> &mut bool { &mut self.values[(pos.0 * self.height + pos.1) as usize] } } fn astar(b: &mut Bencher) { let from = (40, 75); let to = (20, 10); let expected_distance = 85; let map = Bool2D::parse_map(include_bytes!("den203d.map")); let mut visited = Bool2D::new(map.height, map.width); let manhattan = |pos: Pos| (pos.0.max(to.0) - pos.0.min(to.0)) + (pos.1.max(to.1) - pos.1.min(to.1)); let mut heap = H::new(); b.iter(|| { heap.clear(); visited.clear(); heap.push(AStarEntry { pos: from, cost: 0, full_cost: manhattan(from), }); loop { if let Some(AStarEntry { pos, cost, .. }) = heap.pop() { if pos == to { assert_eq!(black_box(cost), expected_distance); break; } for neighbor in [ (pos.0 + 1, pos.1), (pos.0, pos.1 + 1), (pos.0, pos.1 - 1), (pos.0 - 1, pos.1), ] { let visited = visited.get_mut(neighbor); if !*visited { let neighbor_cost = cost + 1; heap.push(AStarEntry { pos: neighbor, cost: neighbor_cost, full_cost: neighbor_cost + manhattan(neighbor), }); *visited = true; } } } else { panic!("no path") } } }); } struct AStarEntry { pos: Pos, cost: u32, full_cost: u32, } impl PartialOrd for AStarEntry { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } impl Ord for AStarEntry { fn cmp(&self, other: &Self) -> std::cmp::Ordering { other .full_cost .cmp(&self.full_cost) .then_with(|| self.cost.cmp(&other.cost)) } } impl PartialEq for AStarEntry { fn eq(&self, other: &Self) -> bool { self.full_cost == other.full_cost && self.cost == other.cost } } impl Eq for AStarEntry {} trait AStarHeap { fn new() -> Self; fn clear(&mut self); fn push(&mut self, entry: AStarEntry); fn pop(&mut self) -> Option; } impl AStarHeap for RadixHeapMap, (Pos, u32)> { #[inline] fn new() -> Self { RadixHeapMap::new() } #[inline] fn clear(&mut self) { self.clear() } #[inline] fn push(&mut self, entry: AStarEntry) { self.push(Reverse(entry.full_cost), (entry.pos, entry.cost)) } #[inline] fn pop(&mut self) -> Option { self.pop() .map(|(Reverse(full_cost), (pos, cost))| AStarEntry { pos, cost, full_cost, }) } } impl AStarHeap for BinaryHeap { #[inline] fn new() -> Self { BinaryHeap::new() } #[inline] fn clear(&mut self) { self.clear(); } #[inline] fn push(&mut self, entry: AStarEntry) { self.push(entry) } #[inline] fn pop(&mut self) -> Option { self.pop() } } fn pushpop_radix(b: &mut Bencher) { let mut heap = RadixHeapMap::::new(); b.iter(|| { heap.push(0, ()); for _ in 0..10000 { let (n, _) = heap.pop().unwrap(); for i in 0..4 { heap.push(n - i, ()); } } heap.clear(); }); } fn pushpop_binary(b: &mut Bencher) { let mut heap = BinaryHeap::::new(); b.iter(|| { heap.push(0); for _ in 0..10000 { let n = heap.pop().unwrap(); for i in 0..4 { heap.push(n - i); } } heap.clear(); }); } fn criterion_benchmark(c: &mut Criterion) { c.bench_function( "astar_radix", astar::, (Pos, u32)>>, ); c.bench_function("astar_binary", astar::>); c.bench_function("pushpop_radix", pushpop_radix); c.bench_function("pushpop_binary", pushpop_binary); } criterion_group!(benches, criterion_benchmark); criterion_main!(benches); radix-heap-0.4.2/benches/den203d.map000064400000000000000000000161510072674642500151520ustar 00000000000000type octile height 77 width 93 map @@@@@@@@@@@@@@@@@@@@TTTTTTTT..TTTTTTT@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@TTTTTTTT..TTTTTTT@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@TTTTT.....TTT..TT@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@TTT............TT@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@TTTT...........TT@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@TTTT...........TT@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@TTTT............T@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@TT.............TT@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@TT............TTT@@@@@@@@@@@@@@@@@@@TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT@@@@ @@@@@@@@@@@@@@@@@@@@TTTT..........TTT@@@@@@@@@@@@@@@@@@@TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT@@@@ @@@@@@@@@@@@@@@@@@@@TTTTT.........TTT@@@@@@@@@@@@@@@@@@@TTTTT...........................T@@@@ @@@@@@@@@@@@@@@@@@@@TTTTT.........TTT@@@@@@@@@@@@@@@@@@@TTTT............................T@@@@ @@@@@@@@@@@@@@@@@@@@TTTTT........TTTT@@@@@@@@@@@@@@@@@@@TT...............................@@@@ @@@@@@@@@@@@@@@@@@@@TTTTT........TTTT@@@@@@@@@@@@@@@@@@@TT...............................@@@@ @@@@@@@@@@@@@@@@@@@@TTTTTT.......TTTT@@@@@@@@@@@@@@@@@@@TT..............................T@@@@ @@@@@@@@@@@@@@@@@@@@TTTTTTTT..TTTTTTT@@@@@@@@@@@@@@@@@@@TT.....TTTTT..TTT...............T@@@@ TTTTTTTTTTTTTTTTTTTTTTTTTTTT..TTTTTTTTTTTTTTTTTTTTTTTTTTTT.....TTTTT..TTTTTTTTTTTTTTTTTTT@@@@ TTTTTTTTTTTTTTTTTTTTTTTTTTTT..TTTTTTTTTTTTTTTTTTTTTTTTTTTT.....TTTTT..TTTTTTTTTTTTTTTTTTT@@@@ TT....TT...TTT.......TTTTT.....TTT....T...T..........TTTTT......TT........TTTTTT.......TT@@@@ TT........TTTT.........TTT.....TTT........T.........TTTTTT......TT........TTTTTT.......TT@@@@ TT........TTTT.........TTT.....TTT.T......T.TTTTTT...TTTTT......TT........TTTTTT......TTT@@@@ TT........TTTT.........TTT.....TTT....................TTTT......TT.....................TT@@@@ TT........TTTT.........TTT.....TTT......................TT......TT......................T@@@@ TT......TT.....TTT.....TTT.....TTT......................TT......TT......................T@@@@ TT......TT.....TTT.....TTT.....TTT....................TTTT......TT......................TTTTT TT.....................TTT.....TTTTT.................TTTTT......TT......................TTTTT TT.....................TTT.....TTTTTT................TTTTT......TT.............TT.......TTTTT .......................TTT.....TTTTTT................TTTTT......TT.........T.TTTT.......TTTTT .......................TTT.....TTTTTT..................TTT......TT.........TTTTT........TTTTT .......................TTT.....TTTTT....................TT......TT.........TTTTTT.......TTTTT TT.....................TTT.....TTTTT....................TT......TT.........TTTTTT.......TTTTT TT.......TT............TTT.....TTTTT..................TTTT......TT..........TTTT........TTTTT TT....TTTTTT...........TTT.....TTTT..................TTTTT......TT...........TT.TTTTTTTTTTTTT TTT...TTTTTT...........TTT.....TTTT..................TTTTT......TTT.............T@@@@@@@@@@@@ TTT....TTTTTT..........TTT.....TTTT...................T.TT......TTT.............T@@@@@@@@@@@@ TT.....TTTTTT..........TT......TTT......................TT......TT............TTT@@@@@@@@@@@@ TT.....TTTT....................TTT......................TT......TT............TTT@@@@@@@@@@@@ TT.....TTTT....................TTT..T...................TT......TTTT..........TTT@@@@@@@@@@@@ TTTT...................TTT.....TTTT.....................TT......TTTT..........TTT@@@@@@@@@@@@ TTTT...................TTT.....TTTT.....TTTT..TTT.......TT......TTTT............T@@@@@@@@@@@@ TTTT....TT....TTTT.....TTT.....TTTTTTTTTTTTT..TTTTTTTTTTTT......TTTTTTTTTTTT..TTT@@@@@@@@@@@@ TT......TT....TTTT.....TTT.....TTTTTTTTTTTTT..TTTTTTTTTTTT......T@@@@@@@@@@@@@@@@@@@@@@@@@@@@ TT..............TT.....TTT..............................TT......T@@@@@@@@@@@@@@@@@@@@@@@@@@@@ TT.....................TTT......................................T@@@@@@@@@@@@@@@@@@@@@@@@@@@@ TT.....................TTT......................................T@@@@@@@@@@@@@@@@@@@@@@@@@@@@ TT.....................TTT......................................T@@@@@@@@@@@@@@@@@@@@@@@@@@@@ TT......TTTTTTTTTT...TTTTT......................................T@@@@@@@@@@@@@@@@@@@@@@@@@@@@ TT......TTTTTTTTTT...TTTTTTTTTTTTTTT..TTTTTTTTTTTTTTTTTTTT......T@@@@@@@@@@@@@@@@@@@@@@@@@@@@ TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT..TTTTTTTTTTTTTTTTTTTT......T@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@TTTT..TTTTTTTTTTTTTTTTTTTT......T@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@TT.............TTTTTTT.TTT......T@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@TT.............TTTTTTT.TTT......T@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@TT................TTTT.TTT......T@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@TT................TTTT.TTT......T@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@TT................TTTT.TTT......T@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@TT.....................TTT......T@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@TT.....................TTT......T@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@TT.....................TTT......T@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@TT...........T.........TTT......T@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@TT........TTTT.........TTT......T@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@TT........TTTT.........TTT......T@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@TT.....................TTT......T@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@TT.....................TTT......T@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@TT..............T......TTT......T@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@TT.............TTT.....TTT......T@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@TT.............TT......TTT......T@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@TT.....................TTT......T@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@TT.....................TTT......T@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@TTT....................TTT.TTTT.T@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@TTTT...................TTT.TTTT.T@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@TTTT.......TTTT........TTT.TTTTTT@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@TTT........TTTT........TTT..TTT@T@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@TT......TTTTTTTTTTTT..TTTTTTTTTTT@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@TT.TTTT.T@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@TT.TTTTTT@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@TT.TTTTTT@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@TTTTTTTTT@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ radix-heap-0.4.2/src/lib.rs000064400000000000000000000511640072674642500136130ustar 00000000000000#![deny(missing_docs)] #![doc = include_str!("../README.md")] use std::{ cmp::Reverse, default::Default, fmt, iter::FromIterator, iter::FusedIterator, num::Wrapping, }; type Bucket = Vec<(K, V)>; /// A montone priority queue implemented using a radix heap. /// /// This will be a max-heap. /// /// See the [module documentation](index.html) for more information. /// /// It is a logic error for a key to be modified in such a way that the /// item's ordering relative to any other item, as determined by the `Ord` /// trait, changes while it is in the heap. This is normally only possible /// through `Cell`, `RefCell`, global state, I/O, or unsafe code. #[derive(Clone)] pub struct RadixHeapMap { len: usize, /// The current top key, or none if one is not set yet. top: Option, /// The K::RADIX_BITS + 1 number of buckets the items can land in. /// /// TODO: when rust supports associated consts as array sizes, use a fixed /// array instead of a vec. buckets: Vec>, /// The initial entries before a top key is found. initial: Bucket, } impl RadixHeapMap { /// Create an empty `RadixHeapMap` pub fn new() -> RadixHeapMap { RadixHeapMap { len: 0, top: None, buckets: (0..=K::RADIX_BITS).map(|_| Bucket::default()).collect(), initial: Bucket::default(), } } /// Create an empty `RadixHeapMap` with the top key set to a specific /// value. /// /// This can be more efficient if you have a known minimum bound of the /// items being pushed to the heap. pub fn new_at(top: K) -> RadixHeapMap { RadixHeapMap { len: 0, top: Some(top), buckets: (0..=K::RADIX_BITS).map(|_| Bucket::default()).collect(), initial: Bucket::default(), } } /// Drops all items form the `RadixHeapMap` and sets the top key to `None`. pub fn clear(&mut self) { self.len = 0; self.top = None; self.initial.clear(); for bucket in &mut self.buckets { bucket.clear(); } } /// Drop all items from the `RadixHeapMap` and sets the top key to a /// specific value. /// /// This can be more efficient if you have a known maximum bound of the /// items being pushed to the heap. pub fn clear_to(&mut self, top: K) { self.clear(); self.top = Some(top); } /// Sets the top value to the current maximum key value in the heap 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)) }); } } /// Pushes a new key value pair onto the heap. /// /// Panics /// ------ /// Panics if the key is larger than the current top key. #[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; } /// Remove the greatest element from the heap and returns it, or `None` if /// empty. /// /// If there is a tie between multiple elements, the last inserted element /// will be popped first. /// /// This will set the top key to the extracted key. #[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() } } } /// Returns the number of elements in the heap #[inline] pub fn len(&self) -> usize { self.len } /// Returns true if there is no elements in the heap #[inline] pub fn is_empty(&self) -> bool { self.len() == 0 } /// The current top value. All keys pushed onto the heap must be smaller than this value. #[inline] pub fn top(&self) -> Option { self.top } /// Discards as much additional capacity as possible. pub fn shrink_to_fit(&mut self) { self.initial.shrink_to_fit(); for bucket in &mut self.buckets { bucket.shrink_to_fit(); } } /// Returns an iterator of all key-value pairs in the RadixHeapMap in arbitrary order pub fn iter(&self) -> Iter { Iter { cur_bucket: self.initial.iter(), buckets: self.buckets.iter(), size: self.len, } } /// Returns an iterator of all keys in the RadixHeapMap in arbitrary order pub fn keys(&self) -> Keys { Keys(self.iter()) } /// Returns an iterator of all values in the RadixHeapMap in arbitrary order pub fn values(&self) -> Values { Values(self.iter()) } } impl Default for RadixHeapMap { fn default() -> RadixHeapMap { RadixHeapMap::new() } } impl FromIterator<(K, V)> for RadixHeapMap { fn from_iter(iter: I) -> RadixHeapMap where I: IntoIterator, { let mut heap = RadixHeapMap::new(); for (k, v) in iter { heap.push(k, v); } heap } } impl Extend<(K, V)> for RadixHeapMap { fn extend(&mut self, iter: I) where I: IntoIterator, { 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 { fn extend(&mut self, iter: I) where I: IntoIterator, { for &(k, v) in iter { self.push(k, v); } } } impl fmt::Debug for RadixHeapMap { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_list().entries(self.iter()).finish() } } /// An owning iterator over key-value pairs in a RadixHeapMap. #[derive(Clone)] pub struct IntoIter { cur_bucket: std::vec::IntoIter<(K, V)>, buckets: std::vec::IntoIter>, size: usize, } impl Iterator for IntoIter { type Item = (K, V); fn next(&mut self) -> Option { 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) { (self.size, Some(self.size)) } fn for_each(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 ExactSizeIterator for IntoIter {} impl FusedIterator for IntoIter {} /// An iterator over key-value pairs in a RadixHeapMap. #[derive(Clone)] pub struct Iter<'a, K, V> { cur_bucket: std::slice::Iter<'a, (K, V)>, buckets: std::slice::Iter<'a, Bucket>, size: usize, } impl<'a, K, V> Iterator for Iter<'a, K, V> { type Item = &'a (K, V); fn next(&mut self) -> Option { 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) { (self.size, Some(self.size)) } fn for_each(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> {} /// An iterator over keys in a RadixHeapMap. #[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.0.next().map(|(k, _)| k) } fn size_hint(&self) -> (usize, Option) { self.0.size_hint() } fn for_each(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> {} /// An iterator over values in a RadixHeapMap. #[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.0.next().map(|(_, v)| v) } fn size_hint(&self) -> (usize, Option) { self.0.size_hint() } fn for_each(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 IntoIterator for RadixHeapMap { type Item = (K, V); type IntoIter = IntoIter; 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 { type Item = &'a (K, V); type IntoIter = Iter<'a, K, V>; fn into_iter(self) -> Self::IntoIter { self.iter() } } /// A number that can be compared using radix distance pub trait Radix { /// The number of high bits in a row that this and `other` has in common /// /// Eg. the radix similarity of 001001 and 000001 is 2 because they share /// the 2 high bits. fn radix_similarity(&self, other: &Self) -> u32; /// Opposite of `radix_similarity`. If `radix_distance` returns 0, then `radix_similarity` /// returns `radix_bits` and vice versa. fn radix_distance(&self, other: &Self) -> u32 { Self::RADIX_BITS - self.radix_similarity(other) } /// The value returned by `radix_similarty` if all bits are equal const RADIX_BITS: u32; } macro_rules! radix_wrapper_impl { ($t:ident) => { impl Radix for $t { #[inline] fn radix_similarity(&self, other: &$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); #[cfg(feature = "ordered-float")] radix_float_impl!(f64, u64, ordered_float::NotNan); 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(mut xs: Vec) -> 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) -> bool); quickcheck(prop as fn(Vec) -> bool); quickcheck(prop as fn(Vec<(u32, i32)>) -> bool); quickcheck(prop as fn(Vec) -> bool); quickcheck(prop as fn(Vec) -> bool); quickcheck(prop as fn(Vec<(i64, usize)>) -> bool); quickcheck(prop as fn(Vec) -> bool); quickcheck(prop as fn(Vec) -> bool); } #[cfg(feature = "ordered-float")] #[test] fn sort_float() { fn prop(xs: Vec) -> 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) -> TestResult); } #[test] fn iter_yeilds_all_elements() { fn prop(mut xs: Vec) -> TestResult { let heap = xs.iter().map(|&d| (d, ())).collect::>(); // Check that the iterator yields all elements inside the heap 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) -> TestResult); quickcheck(prop as fn(Vec) -> TestResult); quickcheck(prop as fn(Vec<(u32, i32)>) -> TestResult); quickcheck(prop as fn(Vec) -> TestResult); quickcheck(prop as fn(Vec) -> 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)]); } }