Compare commits

..

No commits in common. "3d9162725322d4df669d3ad127d9bbfdf2090cf7" and "b4ca806d768e94506565ff6e56d315bfb282f3c1" have entirely different histories.

14 changed files with 39 additions and 343 deletions

View file

@ -25,11 +25,6 @@ class Queue[Data](ABC):
:return: The next item in the queue
"""
def pop(self) -> Data:
"""
:return: The next item in the queue
"""
class PureQueue[Data: Comparable](Queue[Data]):
"""
@ -41,13 +36,18 @@ class PureQueue[Data: Comparable](Queue[Data]):
:param items: An optional list of priorities with which to initialize the queue
"""
def pop(self) -> Data:
"""
:return: The next item in the queue
"""
def insert(self, item: Data) -> None:
"""
:param item: Item to insert into the queue
"""
class PairedQueue[Data, Priority: Comparable](Queue[Data]):
class KeyedQueue[Data, Priority: Comparable](Queue[Data]):
"""
A min-queue that allows arbitrary data associated with some priority, allowing duplicates of both data and priority.
"""
@ -87,9 +87,3 @@ class IndexedQueue[Data: Hashable, Priority: Comparable](Queue[Data]):
"""
:param key: The item to delete from the queue
"""
def __contains__(self, key: Data) -> bool:
"""
:param key: The item to check the existence of
:return: Whether the item is in the queue
"""

View file

@ -1,12 +0,0 @@
/// Data structures for the "indexed" min-queues, supporting priority updates and arbitrary removals, but no duplicates
use super::{item::Item, pure::PureBacking};
/// A data structure usable for backing an "indexed" queue
pub trait IndexedBacking<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
fn remove(data: D) -> bool;
}

12
src/backing/keyed/mod.rs Normal file
View file

@ -0,0 +1,12 @@
/// Data structures for the "keyed" min-queues, supporting priority updates and arbitrary removals, but no duplicates
use super::{item::Item, pure::PureBacking};
/// A data structure usable for backing a "keyed" queue
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
fn remove(data: D) -> bool;
}

View file

@ -1,3 +1,3 @@
pub mod indexed;
pub mod item;
pub mod keyed;
pub mod pure;

View file

@ -1,6 +1,5 @@
/// Data structures for the "pure" min-queues, supporting duplicates but no arbitrary updates
mod binary_heap;
pub use binary_heap::BinaryHeap;
pub mod binary_heap;
/// A data structure usable for backing a "pure" queue
pub trait PureBacking<T: Ord + Send + Sync>: Send + Sync {

View file

@ -2,13 +2,11 @@ pub mod backing;
pub mod queue;
use pyo3::prelude::*;
use queue::{IndexedQueue, PairedQueue, PureQueue};
use queue::pure::PureQueue;
/// Bindings for the Rust queue implementations
#[pymodule]
fn pyority_queue(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_class::<PureQueue>()?;
m.add_class::<PairedQueue>()?;
m.add_class::<IndexedQueue>()?;
Ok(())
}

View file

@ -1,12 +0,0 @@
use pyo3::prelude::*;
#[pyclass]
pub struct IndexedQueue {}
#[pymethods]
impl IndexedQueue {
#[new]
fn new() -> Self {
Self {}
}
}

View file

@ -1,7 +1 @@
mod indexed;
mod paired;
mod pure;
pub use indexed::IndexedQueue;
pub use paired::PairedQueue;
pub use pure::PureQueue;
pub mod pure;

View file

@ -1,25 +0,0 @@
// A "pure" priority queue that supports duplicates, but not arbitrary deletions or weight updates
use crate::backing::{
item::Item,
pure::{BinaryHeap, PureBacking},
};
use pyo3::prelude::*;
#[pyclass]
pub struct PairedQueue {
backing: Box<dyn PureBacking<Item<Py<PyAny>, f64>>>,
}
#[pymethods]
impl PairedQueue {
#[new]
fn new() -> Self {
Self {
backing: Box::new(BinaryHeap::new()),
}
}
fn __len__(self_: PyRef<'_, Self>) -> usize {
self_.backing.len()
}
}

View file

@ -1,12 +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 {}
pub struct PureQueue {
backing: Box<dyn PureBacking<Item<Py<PyAny>, f64>>>,
}
#[pymethods]
impl PureQueue {
#[new]
fn new() -> Self {
Self {}
Self {
backing: Box::new(BinaryHeap::new()),
}
}
fn __len__(self_: PyRef<'_, Self>) -> usize {
self_.backing.len()
}
}

View file

@ -1,132 +0,0 @@
from typing import Any, TYPE_CHECKING
import pytest
from pyority_queue import IndexedQueue
if TYPE_CHECKING:
from pyority_queue import Comparable
type IndexedQueueInitializer = dict[Any, Comparable] | list[tuple[Any, Comparable]] | tuple[tuple[Any, Comparable], ...]
def test_empty_creation():
queue = IndexedQueue()
assert len(queue) == 0
@pytest.mark.parametrize("items", (
[],
[("a", 0), ("b", 1), ("c", 2)],
((0, 0), (1, 1), (2, 2)),
((0.0, 0.0), (1.0, 1.0), (2.0, 2.0)),
((lambda: None, 0)),
((Exception(), 0.0)),
(([], -1)),
{},
{"a": 0, "b": 1, "c": 2},
))
def test_creation(items: IndexedQueueInitializer):
queue = IndexedQueue()
assert len(queue) == len(items)
@pytest.mark.parametrize("items", (
[],
((0, 0),),
((-1, -1), (3, 3)),
))
def test_iteration(items: IndexedQueueInitializer):
queue = IndexedQueue(items)
assert len(list(queue)) == len(items)
@pytest.mark.parametrize("items", (
[],
(("a", -3), ("b", 5)),
(("c", 3.0), ("b", 2.0), ("a", 1.0)),
(("c", 3), ("f", 6), ("h", 8), ("e", 5), ("g", 7), ("d", 4), ("b", 2), ("a", 0)),
))
def test_sorting(items: IndexedQueueInitializer):
queue = IndexedQueue(items)
in_order = list(queue)
assert in_order == sorted(in_order)
def test_insertion():
queue = IndexedQueue({"a": 1, "b": 2, "c": 3})
queue["d"] = 4
queue["e"] = 5
assert list(queue) == ["a", "b", "c", "d", "e"]
def test_removal():
queue = IndexedQueue({"a": 1, "b": 2, "c": 3})
assert queue.pop() == "a"
assert queue.pop() == "b"
assert queue.pop() == "c"
assert len(queue) == 0
def test_mixed_removal():
queue = IndexedQueue({"a": 1, "b": 2, "c": 3})
assert queue.pop() == "a"
assert list(queue) == ["b", "c"]
def test_empty_removal():
queue = IndexedQueue()
with pytest.raises(IndexError):
queue.pop()
def test_duplicates():
queue = IndexedQueue[str, int]((("a", 0), ("a", 0), ("a", 2)))
queue["b"] = 1
queue["b"] = 3
assert list(queue) == ["a", "b"]
def test_deletion():
queue = IndexedQueue({"a": 1, "b": 2, "c": 3})
del queue["a"]
del queue["b"]
assert list(queue) == ["c"]
def test_empty_deletion():
queue = IndexedQueue()
with pytest.raises(KeyError):
del queue["a"]
def test_in():
queue = IndexedQueue({"a": 1, "b": 2, "c": 3})
assert "a" in queue
assert "b" in queue
assert "c" in queue
assert "d" not in queue
del queue["b"]
assert "b" not in queue
def test_mixed_iteration():
queue = IndexedQueue({"a": 1, "b": 2, "c": 3})
results = []
for char in queue:
results.append(char)
if len(queue):
queue[queue.pop()] = 10
assert results == ["a", "c", "b"]
def test_iteration_deletion():
queue = IndexedQueue({"a": 1, "b": 2, "c": 3})
results = []
for char in queue:
results.append(char)
if "b" in queue:
del queue["b"]
assert results == ["a", "c"]

View file

@ -1,97 +0,0 @@
from typing import Any, TYPE_CHECKING
import pytest
from pyority_queue import PairedQueue
if TYPE_CHECKING:
from pyority_queue import Comparable
type PairedQueueInitializer = dict[Any, Comparable] | list[tuple[Any, Comparable]] | tuple[tuple[Any, Comparable], ...]
def test_empty_creation():
queue = PairedQueue()
assert len(queue) == 0
@pytest.mark.parametrize("items", (
[],
[("a", 0), ("b", 1), ("c", 2)],
((0, 0), (1, 1), (2, 2)),
((0.0, 0.0), (1.0, 1.0), (2.0, 2.0)),
((lambda: None, 0)),
((Exception(), 0.0)),
(([], -1)),
{},
{"a": 0, "b": 1, "c": 2},
))
def test_creation(items: PairedQueueInitializer):
queue = PairedQueue()
assert len(queue) == len(items)
@pytest.mark.parametrize("items", (
[],
((0, 0),),
((-1, -1), (3, 3)),
))
def test_iteration(items: PairedQueueInitializer):
queue = PairedQueue(items)
assert len(list(queue)) == len(items)
@pytest.mark.parametrize("items", (
[],
(("a", -3), ("b", 5)),
(("c", 3.0), ("b", 2.0), ("a", 1.0)),
(("c", 3), ("f", 6), ("h", 8), ("e", 5), ("g", 7), ("d", 4), ("b", 2), ("a", 0)),
))
def test_sorting(items: PairedQueueInitializer):
queue = PairedQueue(items)
in_order = list(queue)
assert in_order == sorted(in_order)
def test_insertion():
queue = PairedQueue({"a": 1, "b": 2, "c": 3})
queue["d"] = 4
queue["e"] = 5
assert list(queue) == ["a", "b", "c", "d", "e"]
def test_removal():
queue = PairedQueue[str, int]({"a": 1, "b": 2, "c": 3})
assert queue.pop() == "a"
assert queue.pop() == "b"
assert queue.pop() == "c"
assert len(queue) == 0
def test_mixed_removal():
queue = PairedQueue[str, int]({"a": 1, "b": 2, "c": 3})
assert queue.pop() == "a"
assert list(queue) == ["b", "c"]
def test_empty_removal():
queue = PairedQueue()
with pytest.raises(IndexError):
queue.pop()
def test_duplicates():
queue = PairedQueue[str, int]((("a", 0), ("a", 0), ("a", 2)))
queue["b"] = 1
queue["b"] = 3
assert list(queue) == ["a", "a", "b", "a", "b"]
def test_mixed_iteration():
queue = PairedQueue[str, int]({"a": 1, "b": 2, "c": 3})
results = []
for char in queue:
results.append(char)
if len(queue):
queue[queue.pop()] = 10
assert results == ["a", "c", "b"]

View file

@ -1,6 +1,6 @@
#[cfg(test)]
mod tests {
use pyority_queue::backing::pure::{BinaryHeap, PureBacking};
use pyority_queue::backing::pure::{binary_heap::BinaryHeap, PureBacking};
#[test]
fn test_pure_binary_heap_manual_creation() {

View file

@ -1,13 +1,9 @@
from typing import TYPE_CHECKING
import pytest
from pyority_queue import PureQueue
if TYPE_CHECKING:
from pyority_queue import Comparable
type PureQueueInitializer = list[Comparable] | tuple[Comparable, ...]
type PureQueueInitializer = list[int | float] | tuple[int | float, ...]
def test_empty_creation():
@ -20,7 +16,6 @@ def test_creation(items: PureQueueInitializer):
queue = PureQueue()
assert len(queue) == len(items)
@pytest.mark.parametrize("items", ([], (0,), (-1, 3), range(100)))
def test_iteration(items: PureQueueInitializer):
queue = PureQueue(items)
@ -41,39 +36,8 @@ def test_insertion():
assert list(queue) == [0, 2, 3, 4, 6, 7, 8]
def test_removal():
queue = PureQueue[int]((4, 2, 8, 6))
assert queue.pop() == 2
assert queue.pop() == 4
assert queue.pop() == 6
assert queue.pop() == 8
assert len(queue) == 0
def test_mixed_removal():
queue = PureQueue[int]((4, 2, 8, 6))
assert queue.pop() == 2
assert list(queue) == [4, 6, 8]
def test_empty_removal():
queue = PureQueue()
with pytest.raises(IndexError):
queue.pop()
def test_duplicates():
queue = PureQueue[int]((0, 0, 0, 5, 5))
queue.insert(3)
queue.insert(3)
assert list(queue) == [0, 0, 0, 3, 3, 5, 5]
def test_mixed_iteration():
queue = PureQueue[int]((4, 2, 8, 6))
results = []
for number in queue:
results.append(number)
if len(queue):
queue.insert(queue.pop() * 2)
assert results == [2, 6, 8, 16]