use std::cmp::Reverse;
use std::collections::HashMap;
use std::hash::Hash;
use super::numtraits::IntoOrd;
pub(super) struct Frontier<T, W> {
queue: Vec<(T, W)>,
index: HashMap<T, usize>,
}
impl<T, W> Frontier<T, W>
where
T: Clone + Ord + Eq + Hash,
W: Clone + PartialOrd + IntoOrd,
{
pub fn new() -> Frontier<T, W> {
Frontier {
queue: Vec::new(),
index: HashMap::new(),
}
}
fn key_fn(item: &(T, W)) -> impl Ord {
let (t, w) = item;
(Reverse(w.clone().into_ord()), t.clone())
}
fn insertion_pos(&self, item: &(T, W)) -> usize {
match self.queue.binary_search_by_key(&Self::key_fn(item), Self::key_fn) {
Ok(index) | Err(index) => index,
}
}
pub fn push(&mut self, tag: T, weight: W) {
let item = (tag.clone(), weight);
let index = self.insertion_pos(&item);
self.queue.insert(index, item);
self.index.iter_mut().for_each(|(_, old_idx)| {
if *old_idx >= index {
*old_idx += 1;
}
});
self.index.insert(tag, index);
}
pub fn pop(&mut self) -> Option<(T, W)> {
if let Some((tag, weight)) = self.queue.pop() {
self.index.remove(&tag);
Some((tag, weight))
} else {
None
}
}
pub fn try_insert_or_decrease_cost(&mut self, tag: &T, new_cost: W) -> bool {
if let Some(&i) = self.index.get(tag) {
if new_cost < self.queue[i].1 {
self.queue.remove(i);
self.index.remove(tag);
self.index.iter_mut().for_each(|(_, old_idx)| {
if *old_idx >= i {
*old_idx -= 1;
}
});
let new_item = (tag.clone(), new_cost);
let new_index = self.insertion_pos(&new_item);
self.queue.insert(new_index, new_item);
self.index.iter_mut().for_each(|(_, old_idx)| {
if *old_idx >= new_index {
*old_idx += 1;
}
});
self.index.insert(tag.clone(), new_index);
true
} else {
false
}
} else {
self.push(tag.clone(), new_cost);
true
}
}
}
#[test]
fn test_push_pop() {
let mut f = Frontier::new();
assert_eq!(f.pop(), None);
f.push("N", 1.0);
assert_eq!(f.pop(), Some(("N", 1.0)));
assert_eq!(f.pop(), None);
f.push("A", 1.0);
f.push("B", 2.0);
assert_eq!(f.pop(), Some(("A", 1.0)));
assert_eq!(f.pop(), Some(("B", 2.0)));
assert_eq!(f.pop(), None);
f.push("X", 2.0);
f.push("Y", 1.0);
assert_eq!(f.pop(), Some(("Y", 1.0)));
assert_eq!(f.pop(), Some(("X", 2.0)));
assert_eq!(f.pop(), None);
}
#[test]
fn test_decrease_cost() {
let mut f = Frontier::new();
assert_eq!(f.pop(), None);
assert_eq!(f.try_insert_or_decrease_cost(&"A", 1.0), true);
assert_eq!(f.pop(), Some(("A", 1.0)));
assert_eq!(f.pop(), None);
f.push("B", 2.0);
assert_eq!(f.try_insert_or_decrease_cost(&"A", 1.0), true);
assert_eq!(f.pop(), Some(("A", 1.0)));
assert_eq!(f.pop(), Some(("B", 2.0)));
assert_eq!(f.pop(), None);
assert_eq!(f.try_insert_or_decrease_cost(&"A", 1.0), true);
assert_eq!(f.try_insert_or_decrease_cost(&"B", 2.0), true);
assert_eq!(f.pop(), Some(("A", 1.0)));
assert_eq!(f.pop(), Some(("B", 2.0)));
assert_eq!(f.pop(), None);
f.push("X", 1.0);
assert_eq!(f.try_insert_or_decrease_cost(&"X", 2.0), false);
assert_eq!(f.pop(), Some(("X", 1.0)));
assert_eq!(f.pop(), None);
f.push("Y", 2.0);
assert_eq!(f.try_insert_or_decrease_cost(&"Y", 1.0), true);
assert_eq!(f.pop(), Some(("Y", 1.0)));
assert_eq!(f.pop(), None);
f.push("X", 1.0);
f.push("Y", 3.0);
assert_eq!(f.try_insert_or_decrease_cost(&"Y", 2.0), true);
assert_eq!(f.pop(), Some(("X", 1.0)));
assert_eq!(f.pop(), Some(("Y", 2.0)));
assert_eq!(f.pop(), None);
f.push("X", 2.0);
f.push("Y", 3.0);
assert_eq!(f.try_insert_or_decrease_cost(&"Y", 1.0), true);
assert_eq!(f.pop(), Some(("Y", 1.0)));
assert_eq!(f.pop(), Some(("X", 2.0)));
assert_eq!(f.pop(), None);
}