keyed_priority_queue/lib.rs
1//! This is priority queue that supports elements priority modification and early removal.
2//!
3//! It uses HashMap and own implementation of binary heap to achieve this.
4//!
5//! Each entry has associated *key* and *priority*.
6//! Keys must be unique, and hashable; priorities must implement Ord trait.
7//!
8//! Popping returns element with biggest priority.
9//! Pushing adds element to queue.
10//! Also it is possible to change priority or remove item by key.
11//!
12//! Pop, push, change priority, remove by key have ***O(log n)*** time complexity;
13//! peek, lookup by key are ***O(1)***.
14//!
15//! # Examples
16//!
17//! This is implementation of [A* algorithm][a_star] for 2D grid.
18//! Each cell in grid has the cost.
19//! This algorithm finds shortest path to target using heuristics.
20//!
21//! Let open set be the set of position where algorithm can move in next step.
22//! Sometimes better path for node in open set is found
23//! so the priority of it needs to be updated with new value.
24//!
25//! This example shows how to change priority in [`KeyedPriorityQueue`] when needed.
26//!
27//! [a_star]: https://en.wikipedia.org/wiki/A*_search_algorithm
28//! [`KeyedPriorityQueue`]: struct.KeyedPriorityQueue.html
29//!
30//! ```
31//! use keyed_priority_queue::{KeyedPriorityQueue, Entry};
32//! use std::cmp::Reverse;
33//! use std::collections::HashSet;
34//! use std::ops::Index;
35//!
36//! struct Field {
37//! rows: usize,
38//! columns: usize,
39//! costs: Box<[u32]>,
40//! }
41//!
42//! #[derive(Eq, PartialEq, Debug, Hash, Copy, Clone)]
43//! struct Position {
44//! row: usize,
45//! column: usize,
46//! }
47//!
48//! impl Index<Position> for Field {
49//! type Output = u32;
50//!
51//! fn index(&self, index: Position) -> &Self::Output {
52//! &self.costs[self.columns * index.row + index.column]
53//! }
54//! }
55//!
56//! // From cell we can move upper, right, bottom and left
57//! fn get_neighbors(pos: Position, field: &Field) -> Vec<Position> {
58//! let mut items = Vec::with_capacity(4);
59//! if pos.row > 0 {
60//! items.push(Position { row: pos.row - 1, column: pos.column });
61//! }
62//! if pos.row + 1 < field.rows {
63//! items.push(Position { row: pos.row + 1, column: pos.column });
64//! }
65//! if pos.column > 0 {
66//! items.push(Position { row: pos.row, column: pos.column - 1 });
67//! }
68//! if pos.column + 1 < field.columns {
69//! items.push(Position { row: pos.row, column: pos.column + 1 });
70//! }
71//! items
72//! }
73//!
74//! fn find_path(start: Position, target: Position, field: &Field) -> Option<u32> {
75//! if start == target {
76//! return Some(field[start]);
77//! }
78//! let calc_heuristic = |pos: Position| -> u32 {
79//! ((target.row as isize - pos.row as isize).abs()
80//! + (target.column as isize - pos.column as isize).abs()) as u32
81//! };
82//!
83//! // Already handled this points
84//! let mut closed_set: HashSet<Position> = HashSet::new();
85//! // Positions sortered by total cost and real cost.
86//! // We prefer items with lower real cost if total ones are same.
87//! #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
88//! struct Cost {
89//! total: u32,
90//! real: u32,
91//! }
92//! // Queue that contains all nodes that available for next step
93//! // Min-queue required so Reverse struct used as priority.
94//! let mut available = KeyedPriorityQueue::<Position, Reverse<Cost>>::new();
95//! available.push(
96//! start,
97//! Reverse(Cost {
98//! total: calc_heuristic(start),
99//! real: 0,
100//! }),
101//! );
102//! while let Some((current_pos, Reverse(current_cost))) = available.pop() {
103//! // We have reached target
104//! if current_pos == target {
105//! return Some(current_cost.real);
106//! }
107//!
108//! closed_set.insert(current_pos);
109//!
110//! for next in get_neighbors(current_pos, &field).into_iter()
111//! .filter(|x| !closed_set.contains(x))
112//! {
113//! let real = field[next] + current_cost.real;
114//! let total = current_cost.real + calc_heuristic(next);
115//! let cost = Cost { total, real };
116//! // Entire this interaction will make only one hash lookup
117//! match available.entry(next) {
118//! Entry::Vacant(entry) => {
119//! // Add new position to queue
120//! entry.set_priority(Reverse(cost));
121//! }
122//! Entry::Occupied(entry) if *entry.get_priority() < Reverse(cost) => {
123//! // Have found better path to node in queue
124//! entry.set_priority(Reverse(cost));
125//! }
126//! _ => { /* Have found worse path. */ }
127//! };
128//! }
129//! }
130//! None
131//! }
132//!
133//!let field = Field {
134//! rows: 4,
135//! columns: 4,
136//! costs: vec![
137//! 1, 3, 3, 6, //
138//! 4, 4, 3, 8, //
139//! 3, 1, 2, 4, //
140//! 4, 8, 9, 4, //
141//! ].into_boxed_slice(),
142//!};
143//!
144//!let start = Position { row: 0, column: 0 };
145//!let end = Position { row: 3, column: 3 };
146//!assert_eq!(find_path(start, end, &field), Some(18));
147//! ```
148//!
149
150mod editable_binary_heap;
151mod keyed_priority_queue;
152mod mediator;
153
154pub use crate::keyed_priority_queue::{
155 Entry, KeyedPriorityQueue, KeyedPriorityQueueBorrowIter, KeyedPriorityQueueIterator,
156 OccupiedEntry, SetPriorityNotFoundError, VacantEntry,
157};
158
159#[doc = include_str!("../../Readme.md")]
160#[cfg(doctest)]
161pub struct ReadmeDoctests;