use std::collections::BinaryHeap;
use std::mem;
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub(super) struct RevWalkWorkItem<P, T> {
pub pos: P,
pub value: T,
}
#[derive(Clone)]
pub(super) struct RevWalkQueue<P, T> {
items: BinaryHeap<RevWalkWorkItem<P, T>>,
scratch_item: Option<RevWalkWorkItem<P, T>>,
min_pos: P,
}
impl<P: Ord, T: Ord> RevWalkQueue<P, T> {
pub fn with_min_pos(min_pos: P) -> Self {
Self {
items: BinaryHeap::new(),
scratch_item: None,
min_pos,
}
}
#[cfg_attr(not(test), expect(dead_code))]
pub fn len(&self) -> usize {
self.items.len() + usize::from(self.scratch_item.is_some())
}
pub fn push(&mut self, pos: P, value: T) {
if pos < self.min_pos {
return;
}
self.push_item(RevWalkWorkItem { pos, value });
}
fn push_item(&mut self, new: RevWalkWorkItem<P, T>) {
if let Some(next) = self.scratch_item.as_mut() {
if new < *next {
self.items.push(new);
} else {
let next = mem::replace(next, new);
self.items.push(next);
}
} else if let Some(next) = self.items.peek() {
if new < *next {
self.items.push(new);
} else {
self.scratch_item = Some(new);
}
} else {
self.scratch_item = Some(new);
}
}
pub fn extend(&mut self, positions: impl IntoIterator<Item = P>, value: T)
where
T: Clone,
{
for pos in positions {
self.push(pos, value.clone());
}
}
pub fn peek(&self) -> Option<&RevWalkWorkItem<P, T>> {
self.scratch_item.as_ref().or_else(|| self.items.peek())
}
pub fn pop(&mut self) -> Option<RevWalkWorkItem<P, T>> {
let next = self.scratch_item.take().or_else(|| self.items.pop())?;
Some(next)
}
pub fn pop_if(
&mut self,
predicate: impl FnOnce(&RevWalkWorkItem<P, T>) -> bool,
) -> Option<RevWalkWorkItem<P, T>> {
let next = self.peek()?;
predicate(next).then(|| self.pop().unwrap())
}
pub fn pop_eq(&mut self, pos: &P) -> Option<RevWalkWorkItem<P, T>> {
self.pop_if(|next| next.pos == *pos)
}
pub fn skip_while_eq(&mut self, pos: &P) {
while self.pop_eq(pos).is_some() {
continue;
}
}
}
#[cfg(test)]
mod tests {
use assert_matches::assert_matches;
use super::*;
#[test]
fn test_push_pop_in_forward_order() {
let mut queue: RevWalkQueue<u32, ()> = RevWalkQueue::with_min_pos(0);
queue.push(0, ());
assert!(queue.scratch_item.is_some());
assert_eq!(queue.items.len(), 0);
queue.push(1, ());
assert!(queue.scratch_item.is_some());
assert_eq!(queue.items.len(), 1);
assert_matches!(queue.pop(), Some(RevWalkWorkItem { pos: 1, .. }));
assert!(queue.scratch_item.is_none());
assert_eq!(queue.items.len(), 1);
queue.push(2, ());
assert!(queue.scratch_item.is_some());
assert_eq!(queue.items.len(), 1);
assert_matches!(queue.pop(), Some(RevWalkWorkItem { pos: 2, .. }));
assert!(queue.scratch_item.is_none());
assert_eq!(queue.items.len(), 1);
assert_matches!(queue.pop(), Some(RevWalkWorkItem { pos: 0, .. }));
assert!(queue.scratch_item.is_none());
assert_eq!(queue.items.len(), 0);
assert_matches!(queue.pop(), None);
}
#[test]
fn test_push_pop_in_reverse_order() {
let mut queue: RevWalkQueue<u32, ()> = RevWalkQueue::with_min_pos(0);
queue.push(2, ());
assert!(queue.scratch_item.is_some());
assert_eq!(queue.items.len(), 0);
queue.push(1, ());
assert!(queue.scratch_item.is_some());
assert_eq!(queue.items.len(), 1);
assert_matches!(queue.pop(), Some(RevWalkWorkItem { pos: 2, .. }));
assert!(queue.scratch_item.is_none());
assert_eq!(queue.items.len(), 1);
queue.push(0, ());
assert!(queue.scratch_item.is_none());
assert_eq!(queue.items.len(), 2);
assert_matches!(queue.pop(), Some(RevWalkWorkItem { pos: 1, .. }));
assert!(queue.scratch_item.is_none());
assert_eq!(queue.items.len(), 1);
assert_matches!(queue.pop(), Some(RevWalkWorkItem { pos: 0, .. }));
assert!(queue.scratch_item.is_none());
assert_eq!(queue.items.len(), 0);
assert_matches!(queue.pop(), None);
}
}