Get basic incomplete Python queue API working
This commit is contained in:
parent
661e1d220a
commit
0995e6db90
8 changed files with 69 additions and 45 deletions
|
@ -9,4 +9,4 @@ name = "pyority_queue"
|
|||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
pyo3 = "0.23.3"
|
||||
pyo3 = { version = "0.23.3", features = ["py-clone"] }
|
||||
|
|
|
@ -2,12 +2,13 @@ use std::cmp::Ordering;
|
|||
|
||||
/// Helper struct to associate an item with its priority
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Item<D, P: Ord> {
|
||||
// I mean I guess P should be Ord but I want to use f64 so whatever
|
||||
pub struct Item<D: Clone, P: PartialOrd + Clone> {
|
||||
data: D,
|
||||
priority: P,
|
||||
}
|
||||
|
||||
impl<D, P: Ord> Item<D, P> {
|
||||
impl<D: Clone, P: PartialOrd + Clone> Item<D, P> {
|
||||
/// Creates a new instance
|
||||
fn new(data: D, priority: P) -> Self {
|
||||
Self { data, priority }
|
||||
|
@ -20,22 +21,26 @@ impl<D, P: Ord> Item<D, P> {
|
|||
}
|
||||
|
||||
// The relevant Ord implementations are based just on the priority
|
||||
impl<D, P: Ord> Ord for Item<D, P> {
|
||||
impl<D: Clone, P: PartialOrd + Clone> Ord for Item<D, P> {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.priority.cmp(&other.priority)
|
||||
// Yeah this is bad design
|
||||
// My excuse is that i'm still learning Rust
|
||||
self.priority
|
||||
.partial_cmp(&other.priority)
|
||||
.unwrap_or(Ordering::Equal)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D, P: Ord> PartialOrd for Item<D, P> {
|
||||
impl<D: Clone, P: PartialOrd + Clone> PartialOrd for Item<D, P> {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
self.priority.partial_cmp(&other.priority)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D, P: Ord> PartialEq for Item<D, P> {
|
||||
impl<D: Clone, P: PartialOrd + Clone> PartialEq for Item<D, P> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.priority == other.priority
|
||||
}
|
||||
}
|
||||
|
||||
impl<D, P: Ord> Eq for Item<D, P> {}
|
||||
impl<D: Clone, P: PartialOrd + Clone> Eq for Item<D, P> {}
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
use super::{item::Item, pure::PureBacking};
|
||||
|
||||
/// A data structure usable for backing a "keyed" queue
|
||||
pub trait KeyedBacking<D, P: Ord>: PureBacking<Item<D, P>> {
|
||||
pub trait KeyedBacking<D: Clone + Send + Sync, P: Ord + Clone + Send + Sync>:
|
||||
PureBacking<Item<D, P>>
|
||||
{
|
||||
/// Update an item's priority
|
||||
fn update(data: D, priority: P) -> Result<(), ()>;
|
||||
/// Remove an item from the queue
|
||||
|
|
|
@ -4,7 +4,7 @@ use super::PureBacking;
|
|||
|
||||
/// A binary min-heap backed by an array
|
||||
#[derive(Debug)]
|
||||
pub struct BinaryHeap<T: Ord + Copy> {
|
||||
pub struct BinaryHeap<T: Ord + Clone + Send + Sync> {
|
||||
data: Vec<T>,
|
||||
}
|
||||
|
||||
|
@ -33,18 +33,23 @@ impl fmt::Display for SiftError {
|
|||
/// Whether a sift operation succeeded
|
||||
type SiftResult = Result<(), SiftError>;
|
||||
|
||||
impl<T: Ord + Copy> BinaryHeap<T> {
|
||||
impl<T: Ord + Clone + Send + Sync> BinaryHeap<T> {
|
||||
/// Instantiates a new (empty) binary heap
|
||||
pub fn new() -> Self {
|
||||
Self { data: vec![] }
|
||||
}
|
||||
|
||||
/// Fix an index representing a node with valid children but that may violate the heap property compared to its immediate parent
|
||||
fn sift_up(&mut self, i: usize) -> SiftResult {
|
||||
if i == 0 {
|
||||
// Base case, at root so nothing to do
|
||||
Ok(())
|
||||
} else if let Some(child) = self.data.get(i).copied() {
|
||||
} else if let Some(child) = self.data.get(i).cloned() {
|
||||
let parent_index = (i - 1) / 2;
|
||||
// Check if the heap property is violated
|
||||
if child < self.data[parent_index] {
|
||||
// Swap child with parent
|
||||
self.data[i] = self.data[parent_index];
|
||||
self.data[i] = self.data[parent_index].clone();
|
||||
self.data[parent_index] = child;
|
||||
|
||||
// Repeat process with parent
|
||||
|
@ -64,12 +69,12 @@ impl<T: Ord + Copy> BinaryHeap<T> {
|
|||
// Tried to sift a non-existent index
|
||||
Err(SiftError::new(i, self.data.len()))
|
||||
} else {
|
||||
if let Some(first_child) = self.data.get(i * 2 + 1).copied() {
|
||||
if let Some(first_child) = self.data.get(i * 2 + 1).cloned() {
|
||||
let smaller_child_index;
|
||||
let smaller_child;
|
||||
|
||||
// Find the smallest child and its index
|
||||
if let Some(second_child) = self.data.get(i * 2 + 2).copied() {
|
||||
if let Some(second_child) = self.data.get(i * 2 + 2).cloned() {
|
||||
// Both children, use the smaller one
|
||||
if first_child < second_child {
|
||||
smaller_child = first_child;
|
||||
|
@ -86,7 +91,7 @@ impl<T: Ord + Copy> BinaryHeap<T> {
|
|||
|
||||
if smaller_child < self.data[i] {
|
||||
// Swap parent with child
|
||||
self.data[smaller_child_index] = self.data[i];
|
||||
self.data[smaller_child_index] = self.data[i].clone();
|
||||
self.data[i] = smaller_child;
|
||||
|
||||
// Repeat process with child
|
||||
|
@ -103,23 +108,19 @@ impl<T: Ord + Copy> BinaryHeap<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Ord + Copy> FromIterator<T> for BinaryHeap<T> {
|
||||
impl<T: Ord + Clone + Send + Sync> FromIterator<T> for BinaryHeap<T> {
|
||||
fn from_iter<U: IntoIterator<Item = T>>(iter: U) -> Self {
|
||||
let mut this = Self {
|
||||
data: Vec::from_iter(iter),
|
||||
};
|
||||
for i in (0..=(this.data.len() / 2)).rev() {
|
||||
this.sift_down(i);
|
||||
this.sift_down(i).expect("Index error during heapify");
|
||||
}
|
||||
this
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Ord + Copy> PureBacking<T> for BinaryHeap<T> {
|
||||
fn new() -> Self {
|
||||
Self { data: vec![] }
|
||||
}
|
||||
|
||||
impl<T: Ord + Clone + Send + Sync> PureBacking<T> for BinaryHeap<T> {
|
||||
fn add(&mut self, item: T) {
|
||||
// Append item
|
||||
self.data.push(item);
|
||||
|
@ -132,22 +133,18 @@ impl<T: Ord + Copy> PureBacking<T> for BinaryHeap<T> {
|
|||
// No extra processing
|
||||
0 | 1 => self.data.pop(),
|
||||
_ => {
|
||||
let last = self
|
||||
// Get the original root
|
||||
let root = self.data[0].clone();
|
||||
|
||||
// Move final item to the root and sift down to regain heap property
|
||||
self.data[0] = self
|
||||
.data
|
||||
.pop()
|
||||
.expect("Vector claimed not to be empty but was");
|
||||
let root = self
|
||||
.data
|
||||
.get_mut(0)
|
||||
.expect("Vector claimed to have multiple items but didn't");
|
||||
|
||||
// Move final item to the root and sift down to regain heap property
|
||||
let best = *root;
|
||||
*root = last;
|
||||
self.sift_down(0).unwrap();
|
||||
|
||||
// Return original root
|
||||
Some(best)
|
||||
Some(root)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,9 +2,7 @@
|
|||
pub mod binary_heap;
|
||||
|
||||
/// A data structure usable for backing a "pure" queue
|
||||
pub trait PureBacking<T: Ord>: FromIterator<T> {
|
||||
/// Instantiates a new data structure
|
||||
fn new() -> Self;
|
||||
pub trait PureBacking<T: Ord + Send + Sync>: Send + Sync {
|
||||
/// Places an item into the queue
|
||||
fn add(&mut self, item: T);
|
||||
/// Removes the item with minimum priority, if it exists
|
||||
|
|
12
src/lib.rs
12
src/lib.rs
|
@ -1,16 +1,12 @@
|
|||
pub mod backing;
|
||||
pub mod queue;
|
||||
|
||||
use pyo3::prelude::*;
|
||||
use queue::pure::PureQueue;
|
||||
|
||||
/// Formats the sum of two numbers as string.
|
||||
#[pyfunction]
|
||||
fn sum_as_string(a: usize, b: usize) -> PyResult<String> {
|
||||
Ok((a + b).to_string())
|
||||
}
|
||||
|
||||
/// A Python module implemented in Rust.
|
||||
/// Bindings for the Rust queue implementations
|
||||
#[pymodule]
|
||||
fn pyority_queue(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||
m.add_function(wrap_pyfunction!(sum_as_string, m)?)?;
|
||||
m.add_class::<PureQueue>()?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
1
src/queue/mod.rs
Normal file
1
src/queue/mod.rs
Normal file
|
@ -0,0 +1 @@
|
|||
pub mod pure;
|
25
src/queue/pure.rs
Normal file
25
src/queue/pure.rs
Normal file
|
@ -0,0 +1,25 @@
|
|||
// A "pure" priority queue that supports duplicates, but not arbitrary deletions or weight updates
|
||||
use crate::backing::{
|
||||
item::Item,
|
||||
pure::{binary_heap::BinaryHeap, PureBacking},
|
||||
};
|
||||
use pyo3::prelude::*;
|
||||
|
||||
#[pyclass]
|
||||
pub struct PureQueue {
|
||||
backing: Box<dyn PureBacking<Item<Py<PyAny>, f64>>>,
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl PureQueue {
|
||||
#[new]
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
backing: Box::new(BinaryHeap::new()),
|
||||
}
|
||||
}
|
||||
|
||||
fn __len__(self_: PyRef<'_, Self>) -> usize {
|
||||
self_.backing.len()
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue