diff --git a/pyority_queue.pyi b/pyority_queue.pyi index 8ee487a..e76f406 100644 --- a/pyority_queue.pyi +++ b/pyority_queue.pyi @@ -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 - """ diff --git a/src/backing/indexed/mod.rs b/src/backing/indexed/mod.rs deleted file mode 100644 index ebc1ad5..0000000 --- a/src/backing/indexed/mod.rs +++ /dev/null @@ -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: - PureBacking> -{ - /// Update an item's priority - fn update(data: D, priority: P) -> Result<(), ()>; - /// Remove an item from the queue - fn remove(data: D) -> bool; -} diff --git a/src/backing/keyed/mod.rs b/src/backing/keyed/mod.rs new file mode 100644 index 0000000..e9bc311 --- /dev/null +++ b/src/backing/keyed/mod.rs @@ -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: + PureBacking> +{ + /// Update an item's priority + fn update(data: D, priority: P) -> Result<(), ()>; + /// Remove an item from the queue + fn remove(data: D) -> bool; +} diff --git a/src/backing/mod.rs b/src/backing/mod.rs index 1c34cd3..f66e16c 100644 --- a/src/backing/mod.rs +++ b/src/backing/mod.rs @@ -1,3 +1,3 @@ -pub mod indexed; pub mod item; +pub mod keyed; pub mod pure; diff --git a/src/backing/pure/mod.rs b/src/backing/pure/mod.rs index a8b0259..9c43b14 100644 --- a/src/backing/pure/mod.rs +++ b/src/backing/pure/mod.rs @@ -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: Send + Sync { diff --git a/src/lib.rs b/src/lib.rs index a43efe3..64be5b4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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::()?; - m.add_class::()?; - m.add_class::()?; Ok(()) } diff --git a/src/queue/indexed.rs b/src/queue/indexed.rs deleted file mode 100644 index 086ad0f..0000000 --- a/src/queue/indexed.rs +++ /dev/null @@ -1,12 +0,0 @@ -use pyo3::prelude::*; - -#[pyclass] -pub struct IndexedQueue {} - -#[pymethods] -impl IndexedQueue { - #[new] - fn new() -> Self { - Self {} - } -} diff --git a/src/queue/mod.rs b/src/queue/mod.rs index 17366d1..9ec004d 100644 --- a/src/queue/mod.rs +++ b/src/queue/mod.rs @@ -1,7 +1 @@ -mod indexed; -mod paired; -mod pure; - -pub use indexed::IndexedQueue; -pub use paired::PairedQueue; -pub use pure::PureQueue; +pub mod pure; diff --git a/src/queue/paired.rs b/src/queue/paired.rs deleted file mode 100644 index ab837cf..0000000 --- a/src/queue/paired.rs +++ /dev/null @@ -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, f64>>>, -} - -#[pymethods] -impl PairedQueue { - #[new] - fn new() -> Self { - Self { - backing: Box::new(BinaryHeap::new()), - } - } - - fn __len__(self_: PyRef<'_, Self>) -> usize { - self_.backing.len() - } -} diff --git a/src/queue/pure.rs b/src/queue/pure.rs index 8cf4912..4c03a26 100644 --- a/src/queue/pure.rs +++ b/src/queue/pure.rs @@ -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, f64>>>, +} #[pymethods] impl PureQueue { #[new] fn new() -> Self { - Self {} + Self { + backing: Box::new(BinaryHeap::new()), + } + } + + fn __len__(self_: PyRef<'_, Self>) -> usize { + self_.backing.len() } } diff --git a/tests/indexed_queue.py b/tests/indexed_queue.py deleted file mode 100644 index c829962..0000000 --- a/tests/indexed_queue.py +++ /dev/null @@ -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"] - - diff --git a/tests/paired_queue.py b/tests/paired_queue.py deleted file mode 100644 index 2f88d7b..0000000 --- a/tests/paired_queue.py +++ /dev/null @@ -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"] diff --git a/tests/pure_binary_heap.rs b/tests/pure_binary_heap.rs index 9daeaff..597d6eb 100644 --- a/tests/pure_binary_heap.rs +++ b/tests/pure_binary_heap.rs @@ -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() { diff --git a/tests/pure_queue.py b/tests/pure_queue.py index 559fe9f..ce32069 100644 --- a/tests/pure_queue.py +++ b/tests/pure_queue.py @@ -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]