diff --git a/src/double_priority_queue/mod.rs b/src/double_priority_queue/mod.rs index 8725f25..a62443a 100644 --- a/src/double_priority_queue/mod.rs +++ b/src/double_priority_queue/mod.rs @@ -39,9 +39,9 @@ use crate::core_iterators::*; use crate::store::{left, level, parent, right}; use crate::store::{Index, Position, Store}; use crate::TryReserveError; +use equivalent::Equivalent; use iterators::*; -use std::borrow::Borrow; use std::cmp::{Eq, Ord}; #[cfg(feature = "std")] use std::collections::hash_map::RandomState; @@ -720,8 +720,7 @@ where /// The operation is performed in **O(log(N))** time. pub fn change_priority(&mut self, item: &Q, new_priority: P) -> Option

where - I: Borrow, - Q: Eq + Hash + ?Sized, + Q: ?Sized + Equivalent + Hash, { self.store .change_priority(item, new_priority) @@ -741,8 +740,7 @@ where /// The operation is performed in **O(log(N))** time (worst case). pub fn change_priority_by(&mut self, item: &Q, priority_setter: F) -> bool where - I: Borrow, - Q: Eq + Hash + ?Sized, + Q: ?Sized + Equivalent + Hash, F: FnOnce(&mut P), { self.store @@ -756,8 +754,7 @@ where /// Get the priority of an item, or `None`, if the item is not in the queue pub fn get_priority(&self, item: &Q) -> Option<&P> where - I: Borrow, - Q: Eq + Hash + ?Sized, + Q: ?Sized + Equivalent + Hash, { self.store.get_priority(item) } @@ -767,8 +764,7 @@ where /// Returns `true` if `item` is in the queue, `false` if it is not. pub fn contains(&self, item: &Q) -> bool where - I: Borrow, - Q: Eq + Hash + ?Sized, + Q: ?Sized + Equivalent + Hash, { self.store.contains(item) } @@ -777,8 +773,7 @@ where /// or `None` if the item is not in the queue. pub fn get(&self, item: &Q) -> Option<(&I, &P)> where - I: Borrow, - Q: Eq + Hash + ?Sized, + Q: ?Sized + Equivalent + Hash, { self.store.get(item) } @@ -795,8 +790,7 @@ where /// [`change_priority_by`](DoublePriorityQueue::change_priority_by). pub fn get_mut(&mut self, item: &Q) -> Option<(&mut I, &P)> where - I: Borrow, - Q: Eq + Hash + ?Sized, + Q: ?Sized + Equivalent + Hash, { self.store.get_mut(item) } @@ -808,8 +802,7 @@ where /// The operation is performed in **O(log(N))** time (worst case). pub fn remove(&mut self, item: &Q) -> Option<(I, P)> where - I: Borrow, - Q: Eq + Hash + ?Sized, + Q: ?Sized + Equivalent + Hash, { self.store.remove(item).map(|(item, priority, pos)| { if pos.0 < self.len() { diff --git a/src/priority_queue/mod.rs b/src/priority_queue/mod.rs index 1981562..9ae088c 100644 --- a/src/priority_queue/mod.rs +++ b/src/priority_queue/mod.rs @@ -40,9 +40,9 @@ use crate::core_iterators::*; use crate::store::{left, parent, right}; use crate::store::{Index, Position, Store}; use crate::TryReserveError; +use equivalent::Equivalent; use iterators::*; -use std::borrow::Borrow; use std::cmp::{Eq, Ord}; #[cfg(feature = "std")] use std::collections::hash_map::RandomState; @@ -594,8 +594,7 @@ where /// The operation is performed in **O(log(N))** time. pub fn change_priority(&mut self, item: &Q, new_priority: P) -> Option

where - I: Borrow, - Q: Eq + Hash + ?Sized, + Q: ?Sized + Equivalent + Hash, { self.store .change_priority(item, new_priority) @@ -616,8 +615,7 @@ where /// The operation is performed in **O(log(N))** time (worst case). pub fn change_priority_by(&mut self, item: &Q, priority_setter: F) -> bool where - I: Borrow, - Q: Eq + Hash + ?Sized, + Q: ?Sized + Equivalent + Hash, F: FnOnce(&mut P), { self.store @@ -631,8 +629,7 @@ where /// Get the priority of an item, or `None`, if the item is not in the queue pub fn get_priority(&self, item: &Q) -> Option<&P> where - I: Borrow, - Q: Eq + Hash + ?Sized, + Q: ?Sized + Equivalent + Hash, { self.store.get_priority(item) } @@ -642,8 +639,7 @@ where /// Returns `true` if `item` is in the queue, `false` if it is not. pub fn contains(&self, item: &Q) -> bool where - I: Borrow, - Q: Eq + Hash + ?Sized, + Q: ?Sized + Equivalent + Hash, { self.store.contains(item) } @@ -652,8 +648,7 @@ where /// or `None` if the item is not in the queue. pub fn get(&self, item: &Q) -> Option<(&I, &P)> where - I: Borrow, - Q: Eq + Hash + ?Sized, + Q: ?Sized + Equivalent + Hash, { self.store.get(item) } @@ -670,8 +665,7 @@ where /// [`change_priority_by`](PriorityQueue::change_priority_by). pub fn get_mut(&mut self, item: &Q) -> Option<(&mut I, &P)> where - I: Borrow, - Q: Eq + Hash + ?Sized, + Q: ?Sized + Equivalent + Hash, { self.store.get_mut(item) } @@ -707,8 +701,7 @@ where /// The operation is performed in **O(log(N))** time (worst case). pub fn remove(&mut self, item: &Q) -> Option<(I, P)> where - I: Borrow, - Q: Eq + Hash + ?Sized, + Q: ?Sized + Equivalent + Hash, { self.store.remove(item).map(|(item, priority, pos)| { if pos.0 < self.len() { diff --git a/src/store.rs b/src/store.rs index b1e6887..13f1754 100644 --- a/src/store.rs +++ b/src/store.rs @@ -32,8 +32,8 @@ use std::vec::Vec; // as vec instead of the IndexMap use crate::core_iterators::*; use crate::TryReserveError; +use equivalent::Equivalent; -use std::borrow::Borrow; use std::cmp::{Eq, Ord}; #[cfg(feature = "std")] use std::collections::hash_map::RandomState; @@ -420,8 +420,7 @@ where /// The operation is performed in **O(log(N))** time. pub fn change_priority(&mut self, item: &Q, mut new_priority: P) -> Option<(P, Position)> where - I: Borrow, - Q: Eq + Hash + ?Sized, + Q: ?Sized + Equivalent + Hash, { let Store { map, qp, .. } = self; map.get_full_mut(item).map(|(index, _, p)| { @@ -436,8 +435,7 @@ where /// The operation is performed in **O(log(N))** time (worst case). pub fn change_priority_by(&mut self, item: &Q, priority_setter: F) -> Option where - I: Borrow, - Q: Eq + Hash + ?Sized, + Q: ?Sized + Equivalent + Hash, F: FnOnce(&mut P), { let Store { map, qp, .. } = self; @@ -450,8 +448,7 @@ where /// Get the priority of an item, or `None`, if the item is not in the queue pub fn get_priority(&self, item: &Q) -> Option<&P> where - I: Borrow, - Q: Eq + Hash + ?Sized, + Q: ?Sized + Equivalent + Hash, { self.map.get(item) } @@ -461,8 +458,7 @@ where /// Returns `true` if `item` is in the store, `false` if it is not. pub fn contains(&self, item: &Q) -> bool where - I: Borrow, - Q: Eq + Hash + ?Sized, + Q: ?Sized + Equivalent + Hash, { self.map.contains_key(item) } @@ -471,8 +467,7 @@ where /// or `None` if the item is not in the queue. pub fn get(&self, item: &Q) -> Option<(&I, &P)> where - I: Borrow, - Q: Eq + Hash + ?Sized, + Q: ?Sized + Equivalent + Hash, { self.map.get_full(item).map(|(_, k, v)| (k, v)) } @@ -488,16 +483,14 @@ where /// `change_priority_by`. pub fn get_mut(&mut self, item: &Q) -> Option<(&mut I, &P)> where - I: Borrow, - Q: Eq + Hash + ?Sized, + Q: ?Sized + Equivalent + Hash, { self.map.get_full_mut2(item).map(|(_, k, v)| (k, &*v)) } pub fn remove(&mut self, item: &Q) -> Option<(I, P, Position)> where - I: Borrow, - Q: Eq + Hash + ?Sized, + Q: ?Sized + Equivalent + Hash, { self.map.swap_remove_full(item).map(|(i, item, priority)| { let i = Index(i); diff --git a/tests/double_priority_queue.rs b/tests/double_priority_queue.rs index 7519d30..396c982 100644 --- a/tests/double_priority_queue.rs +++ b/tests/double_priority_queue.rs @@ -1057,6 +1057,145 @@ mod doublepq_tests { assert_eq!(dpq.pop_min(), Some(('c', 1))); } + #[test] + fn equivalent_blanket_implementation() { + let mut dpq = DoublePriorityQueue::new(); + + // Push with owned String + dpq.push("Alice".to_string(), 10); + dpq.push("Bob".to_string(), 20); + + // Test get_priority with &str (uses String's blanket Equivalent impl) + assert_eq!(dpq.get_priority("Alice"), Some(&10)); + assert_eq!(dpq.get_priority("Bob"), Some(&20)); + assert_eq!(dpq.get_priority("Charlie"), None); + + // Test contains with &str + assert!(dpq.contains("Alice")); + assert!(dpq.contains("Bob")); + assert!(!dpq.contains("Charlie")); + + // Test change_priority with &str + assert_eq!(dpq.change_priority("Alice", 25), Some(10)); + assert_eq!(dpq.get_priority("Alice"), Some(&25)); + + // Test change_priority_by with &str + assert!(dpq.change_priority_by("Alice", |p| *p += 5)); + assert_eq!(dpq.get_priority("Alice"), Some(&30)); + + // Test get with &str + let (item, priority) = dpq.get("Bob").unwrap(); + assert_eq!(item, "Bob"); + assert_eq!(*priority, 20); + + // Test get_mut with &str + let (item_mut, priority) = dpq.get_mut("Bob").unwrap(); + assert_eq!(item_mut, "Bob"); + assert_eq!(*priority, 20); + + // Test remove with &str + assert_eq!(dpq.remove("Bob"), Some(("Bob".to_string(), 20))); + assert!(!dpq.contains("Bob")); + } + + #[test] + fn equivalent_custom_implementation() { + use equivalent::Equivalent; + use std::hash::Hash; + + #[derive(Debug, PartialEq, Eq, Hash)] + struct Person { + id: u32, + name: String, + age: u16, + } + + #[derive(Debug, PartialEq, Eq, Hash)] + struct PersonView<'a> { + id: u32, + name: &'a str, + age: u16, + } + + impl<'a> Equivalent for PersonView<'a> { + fn equivalent(&self, key: &Person) -> bool { + self.id == key.id && self.name == key.name && self.age == key.age + } + } + + let mut dpq = DoublePriorityQueue::new(); + + // Create test persons + let alice = Person { + id: 1, + name: "Alice".to_string(), + age: 30, + }; + let bob = Person { + id: 2, + name: "Bob".to_string(), + age: 25, + }; + + // Push persons into queue + dpq.push(alice, 100); + dpq.push(bob, 200); + + // Create PersonView instances for querying + let alice_view = PersonView { + id: 1, + name: "Alice", + age: 30, + }; + let bob_view = PersonView { + id: 2, + name: "Bob", + age: 25, + }; + let charlie_view = PersonView { + id: 3, + name: "Charlie", + age: 35, + }; + + // Test get_priority with PersonView + assert_eq!(dpq.get_priority(&alice_view), Some(&100)); + assert_eq!(dpq.get_priority(&bob_view), Some(&200)); + assert_eq!(dpq.get_priority(&charlie_view), None); + + // Test contains with PersonView + assert!(dpq.contains(&alice_view)); + assert!(dpq.contains(&bob_view)); + assert!(!dpq.contains(&charlie_view)); + + // Test change_priority with PersonView + assert_eq!(dpq.change_priority(&alice_view, 300), Some(100)); + assert_eq!(dpq.get_priority(&alice_view), Some(&300)); + + // Test change_priority_by with PersonView + assert!(dpq.change_priority_by(&alice_view, |p| *p += 50)); + assert_eq!(dpq.get_priority(&alice_view), Some(&350)); + + // Test get with PersonView + let (person, priority) = dpq.get(&bob_view).unwrap(); + assert_eq!(person.name, "Bob"); + assert_eq!(*priority, 200); + + // Test get_mut with PersonView + let (person_mut, priority) = dpq.get_mut(&bob_view).unwrap(); + assert_eq!(person_mut.name, "Bob"); + assert_eq!(*priority, 200); + + // Test remove with PersonView + let removed = dpq.remove(&bob_view); + assert!(removed.is_some()); + let (removed_person, removed_priority) = removed.unwrap(); + assert_eq!(removed_person.id, 2); + assert_eq!(removed_person.name, "Bob"); + assert_eq!(removed_priority, 200); + assert!(!dpq.contains(&bob_view)); + } + #[test] fn user_test() { use priority_queue::PriorityQueue; diff --git a/tests/priority_queue.rs b/tests/priority_queue.rs index c4d519c..1a7a5b9 100644 --- a/tests/priority_queue.rs +++ b/tests/priority_queue.rs @@ -930,6 +930,145 @@ mod pqueue_tests { assert_eq!(pq.pop(), Some(('b', 5))); } + + #[test] + fn equivalent_blanket_implementation() { + let mut pq = PriorityQueue::new(); + + // Push with owned String + pq.push("Alice".to_string(), 10); + pq.push("Bob".to_string(), 20); + + // Test get_priority with &str (uses String's blanket Equivalent impl) + assert_eq!(pq.get_priority("Alice"), Some(&10)); + assert_eq!(pq.get_priority("Bob"), Some(&20)); + assert_eq!(pq.get_priority("Charlie"), None); + + // Test contains with &str + assert!(pq.contains("Alice")); + assert!(pq.contains("Bob")); + assert!(!pq.contains("Charlie")); + + // Test change_priority with &str + assert_eq!(pq.change_priority("Alice", 25), Some(10)); + assert_eq!(pq.get_priority("Alice"), Some(&25)); + + // Test change_priority_by with &str + assert!(pq.change_priority_by("Alice", |p| *p += 5)); + assert_eq!(pq.get_priority("Alice"), Some(&30)); + + // Test get with &str + let (item, priority) = pq.get("Bob").unwrap(); + assert_eq!(item, "Bob"); + assert_eq!(*priority, 20); + + // Test get_mut with &str + let (item_mut, priority) = pq.get_mut("Bob").unwrap(); + assert_eq!(item_mut, "Bob"); + assert_eq!(*priority, 20); + + // Test remove with &str + assert_eq!(pq.remove("Bob"), Some(("Bob".to_string(), 20))); + assert!(!pq.contains("Bob")); + } + + #[test] + fn equivalent_custom_implementation() { + use equivalent::Equivalent; + use std::hash::Hash; + + #[derive(Debug, PartialEq, Eq, Hash)] + struct Person { + id: u32, + name: String, + age: u16, + } + + #[derive(Debug, PartialEq, Eq, Hash)] + struct PersonView<'a> { + id: u32, + name: &'a str, + age: u16, + } + + impl<'a> Equivalent for PersonView<'a> { + fn equivalent(&self, key: &Person) -> bool { + self.id == key.id && self.name == key.name && self.age == key.age + } + } + + let mut pq = PriorityQueue::new(); + + // Create test persons + let alice = Person { + id: 1, + name: "Alice".to_string(), + age: 30, + }; + let bob = Person { + id: 2, + name: "Bob".to_string(), + age: 25, + }; + + // Push persons into queue + pq.push(alice, 100); + pq.push(bob, 200); + + // Create PersonView instances for querying + let alice_view = PersonView { + id: 1, + name: "Alice", + age: 30, + }; + let bob_view = PersonView { + id: 2, + name: "Bob", + age: 25, + }; + let charlie_view = PersonView { + id: 3, + name: "Charlie", + age: 35, + }; + + // Test get_priority with PersonView + assert_eq!(pq.get_priority(&alice_view), Some(&100)); + assert_eq!(pq.get_priority(&bob_view), Some(&200)); + assert_eq!(pq.get_priority(&charlie_view), None); + + // Test contains with PersonView + assert!(pq.contains(&alice_view)); + assert!(pq.contains(&bob_view)); + assert!(!pq.contains(&charlie_view)); + + // Test change_priority with PersonView + assert_eq!(pq.change_priority(&alice_view, 300), Some(100)); + assert_eq!(pq.get_priority(&alice_view), Some(&300)); + + // Test change_priority_by with PersonView + assert!(pq.change_priority_by(&alice_view, |p| *p += 50)); + assert_eq!(pq.get_priority(&alice_view), Some(&350)); + + // Test get with PersonView + let (person, priority) = pq.get(&bob_view).unwrap(); + assert_eq!(person.name, "Bob"); + assert_eq!(*priority, 200); + + // Test get_mut with PersonView + let (person_mut, priority) = pq.get_mut(&bob_view).unwrap(); + assert_eq!(person_mut.name, "Bob"); + assert_eq!(*priority, 200); + + // Test remove with PersonView + let removed = pq.remove(&bob_view); + assert!(removed.is_some()); + let (removed_person, removed_priority) = removed.unwrap(); + assert_eq!(removed_person.id, 2); + assert_eq!(removed_person.name, "Bob"); + assert_eq!(removed_priority, 200); + assert!(!pq.contains(&bob_view)); + } } #[cfg(all(feature = "serde", test))]