Skip to main content

gtether/util/
priority.rs

1//! Data structures and traits used for determining priority ordering.
2//!
3//! In contrast with existing solutions like [`std::collections::BinaryHeap`], this module provides
4//! the capability to have "dynamic" priority, in that the priority for a value can change via
5//! interior mutability and the data structures in this module will do their best to accommodate.
6
7use std::collections::{BinaryHeap, VecDeque};
8use std::sync::{atomic, Arc};
9use educe::Educe;
10
11/// Trait describing something that provides a static priority value.
12///
13/// This trait is auto-implemented for anything that implements `Ord`, and is semantically
14/// equivalent to `Ord`. It exists only as a convenient marker trait, and to serve as a pair to
15/// [`HasDynamicPriority`].
16pub trait HasStaticPriority: Ord {}
17
18impl<P: Ord> HasStaticPriority for P {}
19
20/// Trait describing something that provides a dynamically changing priority value.
21///
22/// Implementors of this trait do not necessarily implement [Ord], as their ordering can change over
23/// time. However, the priority value that implementors yield _do_ implement [Ord], and can be
24/// safely used for ordering.
25///
26/// This trait is automatically implemented for many common types and wrappers.
27pub trait HasDynamicPriority {
28    /// Type of the priority value.
29    ///
30    /// Must implement [Ord].
31    type Value: Ord;
32
33    /// Priority value used for ordering.
34    fn priority(&self) -> Self::Value;
35}
36
37macro_rules! impl_priority_copy_self {
38    ($self_type:ty) => {
39        impl HasDynamicPriority for $self_type {
40            type Value = $self_type;
41
42            #[inline]
43            fn priority(&self) -> Self::Value {
44                *self
45            }
46        }
47    };
48}
49
50impl_priority_copy_self!(i8);
51impl_priority_copy_self!(i16);
52impl_priority_copy_self!(i32);
53impl_priority_copy_self!(i64);
54impl_priority_copy_self!(isize);
55impl_priority_copy_self!(u8);
56impl_priority_copy_self!(u16);
57impl_priority_copy_self!(u32);
58impl_priority_copy_self!(u64);
59impl_priority_copy_self!(usize);
60
61impl<P: HasDynamicPriority> HasDynamicPriority for Arc<P> {
62    type Value = P::Value;
63
64    #[inline]
65    fn priority(&self) -> Self::Value {
66        (**self).priority()
67    }
68}
69
70macro_rules! impl_priority_wrapper_method {
71    ($wrapper_type:ty, $method:ident) => {
72        impl<P: HasDynamicPriority> HasDynamicPriority for $wrapper_type {
73            type Value = P::Value;
74
75            #[inline]
76            fn priority(&self) -> Self::Value {
77                self.$method().priority()
78            }
79        }
80    };
81}
82
83impl_priority_wrapper_method!(parking_lot::Mutex<P>, lock);
84impl_priority_wrapper_method!(parking_lot::RwLock<P>, read);
85impl_priority_wrapper_method!(smol::lock::Mutex<P>, lock_blocking);
86impl_priority_wrapper_method!(smol::lock::RwLock<P>, read_blocking);
87
88macro_rules! impl_priority_atomic {
89    ($atomic_type:ty, $int_type:ty) => {
90        impl HasDynamicPriority for $atomic_type {
91            type Value = $int_type;
92
93            #[inline]
94            fn priority(&self) -> Self::Value {
95                self.load(atomic::Ordering::Relaxed)
96            }
97        }
98    };
99}
100
101impl_priority_atomic!(atomic::AtomicI8, i8);
102impl_priority_atomic!(atomic::AtomicI16, i16);
103impl_priority_atomic!(atomic::AtomicI32, i32);
104impl_priority_atomic!(atomic::AtomicI64, i64);
105impl_priority_atomic!(atomic::AtomicIsize, isize);
106impl_priority_atomic!(atomic::AtomicU8, u8);
107impl_priority_atomic!(atomic::AtomicU16, u16);
108impl_priority_atomic!(atomic::AtomicU32, u32);
109impl_priority_atomic!(atomic::AtomicU64, u64);
110impl_priority_atomic!(atomic::AtomicUsize, usize);
111
112/// Common priority queue logic.
113///
114/// This trait is implemented by the various types of priority queues in this module, such as:
115///  * [StaticPriorityQueue]
116///  * [DynamicPriorityQueue]
117///
118/// For more specific documentation and examples, see the implementor type.
119pub trait PriorityQueue<T> {
120    /// Returns the length of the queue.
121    fn len(&self) -> usize;
122
123    /// Checks if the queue is empty.
124    fn is_empty(&self) -> bool;
125
126    /// Returns the item in the queue with the highest priority, or `None` if the queue is empty.
127    ///
128    /// Time complexity depends on the implementation; see individual documentation for more.
129    fn peek(&self) -> Option<&T>;
130
131    /// Push an item onto the queue.
132    ///
133    /// Time complexity depends on the implementation; see individual documentation for more.
134    fn push(&mut self, value: T);
135
136    /// Removes the item with the highest priority and returns it, or `None` if the queue is empty.
137    ///
138    /// Time complexity depends on the implementation; see individual documentation for more.
139    fn pop(&mut self) -> Option<T>;
140
141    /// Compares `value` to the highest priority in the queue, and swaps with it if it is higher.
142    ///
143    /// Time complexity depends on the implementation; see individual documentation for more.
144    fn swap_if_higher(&mut self, value: T) -> T;
145}
146
147/// FIFO queue.
148///
149/// Uses a [VecDeque] internally, so most operations simply delegate to [VecDeque].
150#[derive(Educe)]
151#[educe(Default)]
152pub struct FifoQueue<T>(VecDeque<T>);
153
154impl<T> PriorityQueue<T> for FifoQueue<T> {
155    /// Returns the length of the queue.
156    ///
157    /// ```
158    /// use gtether::util::priority::{PriorityQueue, FifoQueue};
159    /// let queue = FifoQueue::<isize>::from([-2, 3]);
160    /// assert_eq!(queue.len(), 2);
161    /// ```
162    #[inline]
163    fn len(&self) -> usize {
164        self.0.len()
165    }
166
167    /// Checks if the queue is empty.
168    ///
169    /// ```
170    /// use gtether::util::priority::{PriorityQueue, FifoQueue};
171    /// let mut queue = FifoQueue::<isize>::default();
172    /// assert!(queue.is_empty());
173    ///
174    /// queue.push(0);
175    /// queue.push(-2);
176    /// queue.push(3);
177    /// assert!(!queue.is_empty());
178    /// ```
179    #[inline]
180    fn is_empty(&self) -> bool {
181        self.0.is_empty()
182    }
183
184    /// Returns the item in the queue with the highest priority, or `None` if the queue is empty.
185    ///
186    /// ```
187    /// use gtether::util::priority::{PriorityQueue, FifoQueue};
188    /// let mut queue = FifoQueue::<isize>::default();
189    /// assert_eq!(queue.peek(), None);
190    ///
191    /// queue.push(0);
192    /// queue.push(3);
193    /// queue.push(-2);
194    /// assert_eq!(queue.peek(), Some(&0));
195    /// ```
196    ///
197    /// # Time complexity
198    ///
199    /// Delegates to [`VecDeque::front()`]; cost should be _O_(1).
200    #[inline]
201    fn peek(&self) -> Option<&T> {
202        self.0.front()
203    }
204
205    /// Push an item onto the queue.
206    ///
207    /// ```
208    /// use gtether::util::priority::{PriorityQueue, FifoQueue};
209    /// let mut queue = FifoQueue::<isize>::default();
210    /// queue.push(0);
211    /// queue.push(3);
212    /// queue.push(-2);
213    ///
214    /// assert_eq!(queue.len(), 3);
215    /// assert_eq!(queue.peek(), Some(&0));
216    /// ```
217    ///
218    /// # Time complexity
219    ///
220    /// Delegates to [`VecDeque::push_back()`]; cost should be amortized _O_(1).
221    #[inline]
222    fn push(&mut self, value: T) {
223        self.0.push_back(value)
224    }
225
226    /// Removes the item with the highest priority and returns it, or `None` if the queue is empty.
227    ///
228    /// ```
229    /// use gtether::util::priority::{PriorityQueue, FifoQueue};
230    /// let mut queue = FifoQueue::<isize>::from([-2, 3]);
231    ///
232    /// assert_eq!(queue.pop(), Some(-2));
233    /// assert_eq!(queue.pop(), Some(3));
234    /// assert_eq!(queue.pop(), None);
235    /// ```
236    ///
237    /// # Time complexity
238    ///
239    /// Delegates to [`VecDeque::pop_front()`]; cost should be _O_(1).
240    #[inline]
241    fn pop(&mut self) -> Option<T> {
242        self.0.pop_front()
243    }
244
245    /// Never swaps `value`; effectively an identity function.
246    ///
247    /// For a FIFO queue, the front of the queue is always highest priority, but it is assumed that
248    /// the `value` being compared has already been popped, so it is always considered to be the
249    /// highest priority.
250    ///
251    /// ```
252    /// use gtether::util::priority::{PriorityQueue, FifoQueue};
253    /// let mut queue = FifoQueue::<isize>::from([-2, 3]);
254    ///
255    /// assert_eq!(queue.swap_if_higher(0), 0);
256    /// assert_eq!(queue.swap_if_higher(10), 10);
257    /// ```
258    ///
259    /// # Time complexity
260    ///
261    /// _O_(1).
262    #[inline]
263    fn swap_if_higher(&mut self, value: T) -> T {
264        value
265    }
266}
267
268impl<T> From<Vec<T>> for FifoQueue<T> {
269    #[inline]
270    fn from(value: Vec<T>) -> Self {
271        Self(VecDeque::from(value))
272    }
273}
274
275impl<T> FromIterator<T> for FifoQueue<T> {
276    #[inline]
277    fn from_iter<II: IntoIterator<Item=T>>(iter: II) -> Self {
278        Self::from(iter.into_iter().collect::<Vec<_>>())
279    }
280}
281
282impl<T, const N: usize> From<[T; N]> for FifoQueue<T> {
283    #[inline]
284    fn from(value: [T; N]) -> Self {
285        Self::from_iter(value)
286    }
287}
288
289/// Priority queue using [static priorities](HasStaticPriority).
290///
291/// Uses a [BinaryHeap] internally, so most operations simply delegate to [BinaryHeap].
292#[derive(Educe)]
293#[educe(Default)]
294pub struct StaticPriorityQueue<T: HasStaticPriority> {
295    inner: BinaryHeap<T>,
296}
297
298impl<T: HasStaticPriority> PriorityQueue<T> for StaticPriorityQueue<T> {
299    /// Returns the length of the queue.
300    ///
301    /// ```
302    /// use gtether::util::priority::{PriorityQueue, StaticPriorityQueue};
303    /// let queue = StaticPriorityQueue::<isize>::from([-2, 3]);
304    /// assert_eq!(queue.len(), 2);
305    /// ```
306    #[inline]
307    fn len(&self) -> usize {
308        self.inner.len()
309    }
310
311    /// Checks if the queue is empty.
312    ///
313    /// ```
314    /// use gtether::util::priority::{PriorityQueue, StaticPriorityQueue};
315    /// let mut queue = StaticPriorityQueue::<isize>::default();
316    /// assert!(queue.is_empty());
317    ///
318    /// queue.push(0);
319    /// queue.push(-2);
320    /// queue.push(3);
321    /// assert!(!queue.is_empty());
322    /// ```
323    #[inline]
324    fn is_empty(&self) -> bool {
325        self.inner.is_empty()
326    }
327
328    /// Returns the item in the queue with the highest priority, or `None` if the queue is empty.
329    ///
330    /// ```
331    /// use gtether::util::priority::{PriorityQueue, StaticPriorityQueue};
332    /// let mut queue = StaticPriorityQueue::<isize>::default();
333    /// assert_eq!(queue.peek(), None);
334    ///
335    /// queue.push(0);
336    /// queue.push(3);
337    /// queue.push(-2);
338    /// assert_eq!(queue.peek(), Some(&3));
339    /// ```
340    ///
341    /// # Time complexity
342    ///
343    /// Delegates to [`BinaryHeap::peek()`]; see that method for time complexity.
344    #[inline]
345    fn peek(&self) -> Option<&T> {
346        self.inner.peek()
347    }
348
349    /// Push an item onto the queue.
350    ///
351    /// ```
352    /// use gtether::util::priority::{PriorityQueue, StaticPriorityQueue};
353    /// let mut queue = StaticPriorityQueue::<isize>::default();
354    /// queue.push(0);
355    /// queue.push(3);
356    /// queue.push(-2);
357    ///
358    /// assert_eq!(queue.len(), 3);
359    /// assert_eq!(queue.peek(), Some(&3));
360    /// ```
361    ///
362    /// # Time complexity
363    ///
364    /// Delegates to [`BinaryHeap::push()`]; see that method for time complexity.
365    #[inline]
366    fn push(&mut self, value: T) {
367        self.inner.push(value)
368    }
369
370    /// Removes the item with the highest priority and returns it, or `None` if the queue is empty.
371    ///
372    /// ```
373    /// use gtether::util::priority::{PriorityQueue, StaticPriorityQueue};
374    /// let mut queue = StaticPriorityQueue::<isize>::from([-2, 3]);
375    ///
376    /// assert_eq!(queue.pop(), Some(3));
377    /// assert_eq!(queue.pop(), Some(-2));
378    /// assert_eq!(queue.pop(), None);
379    /// ```
380    ///
381    /// # Time complexity
382    ///
383    /// Delegates to [`BinaryHeap::pop()`]; see that method for time complexity.
384    #[inline]
385    fn pop(&mut self) -> Option<T> {
386        self.inner.pop()
387    }
388
389    /// Compares `value` to the highest priority in the queue, and swaps with it if it is higher.
390    ///
391    /// ```
392    /// use gtether::util::priority::{PriorityQueue, StaticPriorityQueue};
393    /// let mut queue = StaticPriorityQueue::<isize>::from([-2, 3]);
394    ///
395    /// assert_eq!(queue.swap_if_higher(0), 3);
396    /// assert_eq!(queue.swap_if_higher(10), 10);
397    /// ```
398    ///
399    /// # Time complexity
400    ///
401    /// This is equivalent to a `peek()`, comparison, `pop()` and `push()`, and inherits time
402    /// complexity appropriately. This means the worst is in general is _O_(log(_n_)), but can be as
403    /// bad as _O_(_n_) in accordance with [`BinaryHeap::push()`].
404    fn swap_if_higher(&mut self, value: T) -> T {
405        if let Some(highest) = self.inner.peek() && highest > &value {
406            let new_value = self.inner.pop().unwrap();
407            self.inner.push(value);
408            new_value
409        } else {
410            value
411        }
412    }
413}
414
415impl<T: HasStaticPriority> From<Vec<T>> for StaticPriorityQueue<T> {
416    #[inline]
417    fn from(value: Vec<T>) -> Self {
418        Self {
419            inner: BinaryHeap::from(value)
420        }
421    }
422}
423
424impl<T: HasStaticPriority> FromIterator<T> for StaticPriorityQueue<T> {
425    #[inline]
426    fn from_iter<II: IntoIterator<Item=T>>(iter: II) -> Self {
427        Self::from(iter.into_iter().collect::<Vec<_>>())
428    }
429}
430
431impl<T: HasStaticPriority, const N: usize> From<[T; N]> for StaticPriorityQueue<T> {
432    #[inline]
433    fn from(value: [T; N]) -> Self {
434        Self::from_iter(value)
435    }
436}
437
438/// Priority queue using [dynamic priorities](HasDynamicPriority).
439#[derive(Educe)]
440#[educe(Default)]
441pub struct DynamicPriorityQueue<T: HasDynamicPriority> {
442    inner: Vec<T>,
443}
444
445impl<T: HasDynamicPriority> DynamicPriorityQueue<T> {
446    fn find_highest(&self) -> Option<(usize, &T, T::Value)> {
447        let mut highest: Option<(usize, &T, T::Value)> = None;
448        for (idx, value) in self.inner.iter().enumerate() {
449            let priority = value.priority();
450            match highest {
451                Some((_, _, ref highest_priority)) => {
452                    if &priority > highest_priority {
453                        highest = Some((idx, value, priority));
454                    }
455                },
456                None => {
457                    highest = Some((idx, value, priority));
458                },
459            }
460        }
461        highest
462    }
463}
464
465impl<T: HasDynamicPriority> PriorityQueue<T> for DynamicPriorityQueue<T> {
466    /// Returns the length of the queue.
467    ///
468    /// ```
469    /// use gtether::util::priority::{PriorityQueue, DynamicPriorityQueue};
470    /// use std::sync::atomic::AtomicIsize;
471    /// let queue = DynamicPriorityQueue::<AtomicIsize>::from([AtomicIsize::new(-2), AtomicIsize::new(3)]);
472    /// assert_eq!(queue.len(), 2);
473    /// ```
474    #[inline]
475    fn len(&self) -> usize {
476        self.inner.len()
477    }
478
479    /// Checks if the queue is empty.
480    ///
481    /// ```
482    /// use gtether::util::priority::{PriorityQueue, DynamicPriorityQueue};
483    /// use std::sync::atomic::AtomicIsize;
484    /// let mut queue = DynamicPriorityQueue::<AtomicIsize>::default();
485    /// assert!(queue.is_empty());
486    ///
487    /// queue.push(AtomicIsize::new(0));
488    /// queue.push(AtomicIsize::new(-2));
489    /// queue.push(AtomicIsize::new(3));
490    /// assert!(!queue.is_empty());
491    /// ```
492    #[inline]
493    fn is_empty(&self) -> bool {
494        self.inner.is_empty()
495    }
496
497    /// Returns the item in the queue with the highest priority, or `None` if the queue is empty.
498    ///
499    /// ```
500    /// use gtether::util::priority::{PriorityQueue, DynamicPriorityQueue};
501    /// use std::sync::{Arc, atomic::{AtomicIsize, Ordering}};
502    /// let mut queue = DynamicPriorityQueue::<Arc<AtomicIsize>>::default();
503    /// assert!(queue.peek().is_none());
504    ///
505    /// let val_a = Arc::new(AtomicIsize::new(-2));
506    /// let val_b = Arc::new(AtomicIsize::new(3));
507    /// queue.push(val_a.clone());
508    /// queue.push(val_b.clone());
509    ///
510    /// {
511    ///     let val = queue.peek().expect("should be Some()");
512    ///     assert_eq!(val.load(Ordering::Relaxed), 3);
513    /// }
514    ///
515    /// {
516    ///     val_a.store(10, Ordering::Relaxed);
517    ///     let val = queue.peek().expect("should be Some()");
518    ///     assert_eq!(val.load(Ordering::Relaxed), 10);
519    /// }
520    /// ```
521    ///
522    /// # Time complexity
523    ///
524    /// Because priorities are dynamic, the entire queue must be iterated to find the highest
525    /// priority every time, making the cost _O_(_n_).
526    #[inline]
527    fn peek(&self) -> Option<&T> {
528        self.find_highest().map(|(_, value, _)| value)
529    }
530
531    /// Push an item onto the queue.
532    ///
533    /// ```
534    /// use gtether::util::priority::{PriorityQueue, DynamicPriorityQueue};
535    /// use std::sync::atomic::{AtomicIsize, Ordering};
536    /// let mut queue = DynamicPriorityQueue::<AtomicIsize>::default();
537    /// queue.push(AtomicIsize::new(0));
538    /// queue.push(AtomicIsize::new(-2));
539    /// queue.push(AtomicIsize::new(3));
540    ///
541    /// assert_eq!(queue.len(), 3);
542    /// let val = queue.peek().expect("should be Some()");
543    /// assert_eq!(val.load(Ordering::Relaxed), 3);
544    /// ```
545    ///
546    /// # Time complexity
547    ///
548    /// Takes amortized _O_(1) time. See [`Vec::push()`] for more.
549    #[inline]
550    fn push(&mut self, value: T) {
551        self.inner.push(value)
552    }
553
554    /// Removes the item with the highest priority and returns it, or `None` if the queue is empty.
555    ///
556    /// ```
557    /// use gtether::util::priority::{PriorityQueue, DynamicPriorityQueue};
558    /// use std::sync::{Arc, atomic::{AtomicIsize, Ordering}};
559    ///
560    /// let val_a = Arc::new(AtomicIsize::new(0));
561    /// let val_b = Arc::new(AtomicIsize::new(-2));
562    /// let val_c = Arc::new(AtomicIsize::new(3));
563    ///
564    /// let mut queue = DynamicPriorityQueue::<Arc<AtomicIsize>>::from([
565    ///     val_a.clone(),
566    ///     val_b.clone(),
567    ///     val_c.clone(),
568    /// ]);
569    ///
570    /// {
571    ///     let val = queue.pop().expect("should be Some()");
572    ///     assert_eq!(val.load(Ordering::Relaxed), 3);
573    /// }
574    ///
575    /// {
576    ///     val_b.store(10, Ordering::Relaxed);
577    ///     let val = queue.pop().expect("should be Some()");
578    ///     assert_eq!(val.load(Ordering::Relaxed), 10);
579    /// }
580    ///
581    /// {
582    ///     let val = queue.pop().expect("should be Some()");
583    ///     assert_eq!(val.load(Ordering::Relaxed), 0);
584    /// }
585    ///
586    /// assert!(queue.pop().is_none());
587    /// ```
588    ///
589    /// # Time complexity
590    ///
591    /// Because priorities are dynamic, the entire queue must be iterated to find the highest
592    /// priority every time, making the cost _O_(_n_).
593    #[inline]
594    fn pop(&mut self) -> Option<T> {
595        self.find_highest()
596            // Drop the value ref so that we can mutate self.inner
597            .map(|(idx, _, _)| idx)
598            .map(|idx| self.inner.swap_remove(idx))
599    }
600
601    /// Compares `value` to the highest priority in the queue, and swaps with it if it is higher.
602    ///
603    /// ```
604    /// use gtether::util::priority::{PriorityQueue, DynamicPriorityQueue};
605    /// use std::sync::{Arc, atomic::{AtomicIsize, Ordering}};
606    ///
607    /// let val_a = Arc::new(AtomicIsize::new(5));
608    /// let mut queue = DynamicPriorityQueue::<Arc<AtomicIsize>>::from([val_a.clone()]);
609    ///
610    /// let val_b = Arc::new(AtomicIsize::new(10));
611    /// {
612    ///     let val = queue.swap_if_higher(val_b.clone());
613    ///     assert_eq!(val.load(Ordering::Relaxed), 10);
614    /// }
615    ///
616    /// val_a.store(20, Ordering::Relaxed);
617    /// {
618    ///     let val = queue.swap_if_higher(val_b.clone());
619    ///     assert_eq!(val.load(Ordering::Relaxed), 20);
620    /// }
621    /// ```
622    ///
623    /// # Time complexity
624    ///
625    /// Because priorities are dynamic, the entire queue must be iterated to find the highest
626    /// priority every time, making the cost _O_(_n_).
627    ///
628    /// This method only searches for the highest priority once before comparing and swapping, so
629    /// it is faster than manually comparing with `peek()` and then calling `pop()` and `push()`.
630    fn swap_if_higher(&mut self, value: T) -> T {
631        let priority = value.priority();
632        let highest = self.find_highest().map(|(idx, _, priority)| (idx, priority));
633        if let Some((idx, highest_priority)) = highest && highest_priority > priority {
634            let new_value = self.inner.swap_remove(idx);
635            self.inner.push(value);
636            new_value
637        } else {
638            value
639        }
640    }
641}
642
643impl<T: HasDynamicPriority> From<Vec<T>> for DynamicPriorityQueue<T> {
644    #[inline]
645    fn from(value: Vec<T>) -> Self {
646        Self {
647            inner: value,
648        }
649    }
650}
651
652impl<T: HasDynamicPriority> FromIterator<T> for DynamicPriorityQueue<T> {
653    #[inline]
654    fn from_iter<II: IntoIterator<Item=T>>(iter: II) -> Self {
655        Self::from(iter.into_iter().collect::<Vec<_>>())
656    }
657}
658
659impl<T: HasDynamicPriority, const N: usize> From<[T; N]> for DynamicPriorityQueue<T> {
660    #[inline]
661    fn from(value: [T; N]) -> Self {
662        Self::from_iter(value)
663    }
664}