Skip to content

Commit 31dc0db

Browse files
authored
Merge pull request #66 from garro95/extract-if
Implement Extract if
2 parents e24f5ee + 3c471ef commit 31dc0db

7 files changed

Lines changed: 376 additions & 6 deletions

File tree

src/double_priority_queue/iterators.rs

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,89 @@ use std::iter::*;
4040

4141
use crate::DoublePriorityQueue;
4242

43+
use super::Index;
44+
45+
/// An `Iterator` in arbitrary order which uses a `predicate` to determine if
46+
/// an element should be removed from the `DoublePriorityQueue`.
47+
///
48+
/// It can be obtained calling the [`extract_if`](DoublePriorityQueue::extract_if) method.
49+
///
50+
/// The `predicate` has mutable access to the `(item, priority)` pairs.
51+
///
52+
/// It can update the priorities of the elements in the queue and the items
53+
/// in a way that does not change the result of any of `hash` or `eq`.
54+
///
55+
/// When the iterator goes out of scope, the heap is rebuilt to restore the
56+
/// structural properties.
57+
#[cfg(feature = "std")]
58+
pub struct ExtractIf<'a, I: 'a, P: 'a, F, H: 'a = RandomState>
59+
where
60+
P: Ord,
61+
{
62+
pq: &'a mut DoublePriorityQueue<I, P, H>,
63+
predicate: F,
64+
idx: Index,
65+
}
66+
67+
#[cfg(not(feature = "std"))]
68+
pub struct ExtractIf<'a, I: 'a, P: 'a, F, H: 'a>
69+
where
70+
P: Ord,
71+
{
72+
pq: &'a mut DoublePriorityQueue<I, P, H>,
73+
predicate: F,
74+
idx: Index,
75+
}
76+
77+
impl<'a, I: 'a, P: 'a, F, H: 'a> ExtractIf<'a, I, P, F, H>
78+
where
79+
P: Ord,
80+
{
81+
pub(crate) fn new(pq: &'a mut DoublePriorityQueue<I, P, H>, predicate: F) -> Self {
82+
ExtractIf {
83+
pq,
84+
predicate,
85+
idx: Index(0),
86+
}
87+
}
88+
}
89+
90+
impl<'a, I: 'a, P: 'a, F, H: 'a> Iterator for ExtractIf<'a, I, P, F, H>
91+
where
92+
P: Ord,
93+
F: FnMut(&mut I, &mut P) -> bool,
94+
H: BuildHasher,
95+
{
96+
type Item = (I, P);
97+
fn next(&mut self) -> Option<Self::Item> {
98+
use indexmap::map::MutableKeys;
99+
100+
loop {
101+
let r: Option<bool> = self
102+
.pq
103+
.store
104+
.map
105+
.get_index_mut2(self.idx.0)
106+
.map(|(i, p)| (self.predicate)(i, p));
107+
108+
match r {
109+
Some(true) => return self.pq.store.swap_remove_index(self.idx),
110+
Some(false) => self.idx.0 += 1,
111+
None => return None,
112+
}
113+
}
114+
}
115+
}
116+
117+
impl<'a, I: 'a, P: 'a, F, H: 'a> Drop for ExtractIf<'a, I, P, F, H>
118+
where
119+
P: Ord,
120+
{
121+
fn drop(&mut self) {
122+
self.pq.heap_build();
123+
}
124+
}
125+
43126
/// A mutable iterator over the couples `(item, priority)` of the `DoublePriorityQueue`
44127
/// in arbitrary order.
45128
///

src/double_priority_queue/mod.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,29 @@ where
454454
self.heap_build();
455455
}
456456

457+
/// Returns an `Iterator` removing from the queue the `(item, priority)`
458+
/// pairs for which the `predicate` returns `true`, in arbitraty order.
459+
///
460+
/// The `predicate` receives mutable references to both the item and
461+
/// the priority.
462+
///
463+
/// It's a logical error to change the item in a way
464+
/// that changes the result of `Hash` or `Eq`.
465+
///
466+
/// The `predicate` can change the priority. If it returns `true`, the
467+
/// extracted pair will have the updated priority, otherwise, the
468+
/// heap structural property will be restored once the iterator is `Drop`ped.
469+
///
470+
/// # Example
471+
/// ```
472+
/// ```
473+
pub fn extract_if<F>(&mut self, predicate: F) -> ExtractIf<I, P, F, H>
474+
where
475+
F: FnMut(&mut I, &mut P) -> bool,
476+
{
477+
ExtractIf::new(self, predicate)
478+
}
479+
457480
/// Removes the item with the lowest priority from
458481
/// the priority queue if the predicate returns `true`.
459482
///

src/priority_queue/iterators.rs

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,89 @@ use std::iter::*;
4040

4141
use crate::PriorityQueue;
4242

43+
use super::Index;
44+
45+
/// An `Iterator` in arbitrary order which uses a `predicate` to determine if
46+
/// an element should be removed from the `PriorityQueue`.
47+
///
48+
/// It can be obtained calling the [`extract_if`](PriorityQueue::extract_if) method.
49+
///
50+
/// The `predicate` has mutable access to the `(item, priority)` pairs.
51+
///
52+
/// It can update the priorities of the elements in the queue and the items
53+
/// in a way that does not change the result of any of `hash` or `eq`.
54+
///
55+
/// When the iterator goes out of scope, the heap is rebuilt to restore the
56+
/// structural properties.
57+
#[cfg(feature = "std")]
58+
pub struct ExtractIf<'a, I: 'a, P: 'a, F, H: 'a = RandomState>
59+
where
60+
P: Ord,
61+
{
62+
pq: &'a mut PriorityQueue<I, P, H>,
63+
predicate: F,
64+
idx: Index,
65+
}
66+
67+
#[cfg(not(feature = "std"))]
68+
pub struct ExtractIf<'a, I: 'a, P: 'a, F, H: 'a>
69+
where
70+
P: Ord,
71+
{
72+
pq: &'a mut PriorityQueue<I, P, H>,
73+
predicate: F,
74+
idx: Index,
75+
}
76+
77+
impl<'a, I: 'a, P: 'a, F, H: 'a> ExtractIf<'a, I, P, F, H>
78+
where
79+
P: Ord,
80+
{
81+
pub(crate) fn new(pq: &'a mut PriorityQueue<I, P, H>, predicate: F) -> Self {
82+
ExtractIf {
83+
pq,
84+
predicate,
85+
idx: Index(0),
86+
}
87+
}
88+
}
89+
90+
impl<'a, I: 'a, P: 'a, F, H: 'a> Iterator for ExtractIf<'a, I, P, F, H>
91+
where
92+
P: Ord,
93+
F: FnMut(&mut I, &mut P) -> bool,
94+
H: BuildHasher,
95+
{
96+
type Item = (I, P);
97+
fn next(&mut self) -> Option<Self::Item> {
98+
use indexmap::map::MutableKeys;
99+
100+
loop {
101+
let r: Option<bool> = self
102+
.pq
103+
.store
104+
.map
105+
.get_index_mut2(self.idx.0)
106+
.map(|(i, p)| (self.predicate)(i, p));
107+
108+
match r {
109+
Some(true) => return self.pq.store.swap_remove_index(self.idx),
110+
Some(false) => self.idx.0 += 1,
111+
None => return None,
112+
}
113+
}
114+
}
115+
}
116+
117+
impl<'a, I: 'a, P: 'a, F, H: 'a> Drop for ExtractIf<'a, I, P, F, H>
118+
where
119+
P: Ord,
120+
{
121+
fn drop(&mut self) {
122+
self.pq.heap_build();
123+
}
124+
}
125+
43126
/// A mutable iterator over the couples `(item, priority)` of the `PriorityQueue`
44127
/// in arbitrary order.
45128
///

src/priority_queue/mod.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,25 @@ where
364364
self.heap_build();
365365
}
366366

367+
/// Returns an `Iterator` removing from the queue the `(item, priority)`
368+
/// pairs for which the `predicate` returns `true`, in arbitraty order.
369+
///
370+
/// The `predicate` receives mutable references to both the item and
371+
/// the priority.
372+
///
373+
/// It's a logical error to change the item in a way
374+
/// that changes the result of `Hash` or `Eq`.
375+
///
376+
/// The `predicate` can change the priority. If it returns `true`, the
377+
/// extracted pair will have the updated priority, otherwise, the
378+
/// heap structural property will be restored once the iterator is `Drop`ped.
379+
pub fn extract_if<F>(&mut self, predicate: F) -> ExtractIf<I, P, F, H>
380+
where
381+
F: FnMut(&mut I, &mut P) -> bool,
382+
{
383+
ExtractIf::new(self, predicate)
384+
}
385+
367386
/// Removes the item with the greatest priority from
368387
/// the priority queue if the `predicate` returns `true`.
369388
///

src/store.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,41 @@ impl<I, P, H> Store<I, P, H> {
267267
self.heap.swap(a.0, b.0);
268268
}
269269

270+
/// Remove and return the element at index `idx`
271+
/// and swap it with the last element keeping a consistent
272+
/// state.
273+
///
274+
/// Computes in **O(1)** time (average)
275+
pub fn swap_remove_index(&mut self, idx: Index) -> Option<(I, P)> {
276+
// swap_remove the position from the qp
277+
let position = self.qp.swap_remove(idx.0);
278+
self.size -= 1;
279+
280+
if idx.0 < self.size {
281+
// SAFETY: head validity checked on the previous line.
282+
// All positions point to valid heap items because we already
283+
// updated the qp.
284+
unsafe {
285+
*self.heap.get_unchecked_mut(self.qp.get_unchecked(idx.0).0) = idx;
286+
}
287+
}
288+
self.heap.swap_remove(position.0);
289+
// Fix indexes and swap remove the old heap head from the qp
290+
if position.0 < self.size {
291+
// SAFETY: position validity checked on the previous line.
292+
// Indexes still point to valid qp items because we didn't
293+
// remove anything from qp yet
294+
unsafe {
295+
*self
296+
.qp
297+
.get_unchecked_mut(self.heap.get_unchecked(position.0).0) = position;
298+
}
299+
}
300+
301+
// swap remove from the map and return to the client
302+
self.map.swap_remove_index(idx.0)
303+
}
304+
270305
/// Remove and return the element in position `position`
271306
/// and swap it with the last element keeping a consistent
272307
/// state.

tests/double_priority_queue.rs

Lines changed: 68 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -786,12 +786,11 @@ mod doublepq_tests {
786786
assert_eq!(pq.pop_max(), Some(("b", 20)));
787787

788788
/*
789-
As expected, this does not compile
789+
// As expected, this does not compile
790790
let iter_mut = pq.iter_mut();
791-
iter_mut.for_each(|(_, p)| {*p += 2});
792791
793-
assert_eq!(pq.pop_max(), Some(("f", 9)));
794-
*/
792+
assert_eq!(pq.pop_max(), Some(("a", 21)));
793+
iter_mut.for_each(|(_, p)| {*p += 2}); */
795794
}
796795

797796
#[test]
@@ -819,7 +818,6 @@ mod doublepq_tests {
819818
pq.push(Animal::new("bird".to_string(), true, false), 7);
820819
pq.push(Animal::new("fish".to_string(), false, true), 4);
821820
pq.push(Animal::new("cow".to_string(), false, false), 3);
822-
823821
pq.retain(|i, _| i.can_swim);
824822

825823
assert_eq!(
@@ -873,6 +871,71 @@ mod doublepq_tests {
873871
);
874872
}
875873

874+
#[test]
875+
fn extract_if() {
876+
#[derive(Hash, PartialEq, Eq, Debug)]
877+
struct Animal {
878+
name: String,
879+
can_fly: bool,
880+
can_swim: bool,
881+
}
882+
883+
impl Animal {
884+
pub fn new(name: String, can_fly: bool, can_swim: bool) -> Self {
885+
Animal {
886+
name,
887+
can_fly,
888+
can_swim,
889+
}
890+
}
891+
}
892+
893+
let mut pq = DoublePriorityQueue::new();
894+
pq.push(Animal::new("dog".to_string(), false, true), 1);
895+
pq.push(Animal::new("cat".to_string(), false, false), 2);
896+
pq.push(Animal::new("bird".to_string(), true, false), 7);
897+
pq.push(Animal::new("fish".to_string(), false, true), 4);
898+
pq.push(Animal::new("cow".to_string(), false, false), 3);
899+
900+
let swimming_animals: Vec<(Animal, i32)> = pq
901+
.extract_if(|i, p| {
902+
if i.can_fly {
903+
*p -= 18;
904+
return false;
905+
}
906+
907+
i.can_swim
908+
})
909+
.collect();
910+
911+
assert_eq!(
912+
swimming_animals,
913+
[
914+
(Animal::new("dog".to_string(), false, true), 1),
915+
(Animal::new("fish".to_string(), false, true), 4)
916+
]
917+
);
918+
assert_eq!(
919+
pq.pop_max(),
920+
Some((Animal::new("cow".to_string(), false, false), 3))
921+
);
922+
assert_eq!(
923+
pq.pop_max(),
924+
Some((Animal::new("cat".to_string(), false, false), 2))
925+
);
926+
assert_eq!(
927+
pq.pop_max(),
928+
Some((Animal::new("bird".to_string(), true, false), -11))
929+
);
930+
931+
/*
932+
// As expected, this does not compile
933+
let extract_if = pq.extract_if(|i, p| { i.can_fly });
934+
935+
assert_eq!(pq.pop_max(), None);
936+
extract_if.for_each(|(_, p)| println!("{:?}", p)); */
937+
}
938+
876939
#[test]
877940
fn into_sorted_iter() {
878941
let mut pq = DoublePriorityQueue::new();

0 commit comments

Comments
 (0)