use ebi_arithmetic::ebi_number::Zero;
use indexmap::map::Entry::{Occupied, Vacant};
use std::cmp::Ordering;
use std::collections::BinaryHeap;
use std::hash::Hash;
use std::hash::BuildHasherDefault;
use std::ops::AddAssign;
use indexmap::IndexMap;
use rustc_hash::FxHasher;
type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>;
#[allow(clippy::needless_collect)]
fn reverse_path<N, V, F>(parents: &FxIndexMap<N, V>, mut parent: F, start: usize) -> Vec<N>
where
N: Eq + Hash + Clone,
F: FnMut(&V) -> usize,
{
let mut i = start;
let path = std::iter::from_fn(|| {
parents.get_index(i).map(|(node, value)| {
i = parent(value);
node
})
})
.collect::<Vec<&N>>();
path.into_iter().rev().cloned().collect()
}
#[allow(clippy::missing_panics_doc)]
#[allow(clippy::missing_panics_doc)]
pub fn astar<'a, N, C, FN, IN, FH, FS>(
start: &N,
mut successors: FN,
mut heuristic: FH,
mut success: FS,
) -> Option<(Vec<N>, C)>
where
N: Eq + Hash + Clone,
C: Zero + Ord + Clone + AddAssign,
FN: FnMut(&N) -> IN,
IN: IntoIterator<Item = (N, C)>,
FH: FnMut(&N) -> C,
FS: FnMut(&N) -> bool,
{
let mut to_see = BinaryHeap::new();
to_see.push(SmallestCostHolder {
estimated_cost: Zero::zero(),
cost: Zero::zero(),
index: 0,
});
let mut parents: FxIndexMap<N, (usize, C)> = FxIndexMap::default();
parents.insert(start.clone(), (usize::MAX, Zero::zero()));
while let Some(SmallestCostHolder { cost, index, .. }) = to_see.pop() {
let successors = {
let (node, &(_, ref c)) = parents.get_index(index).unwrap(); if success(node) {
let path = reverse_path(&parents, |&(p, _)| p, index);
return Some((path, cost));
}
if &cost > c {
continue;
}
successors(node)
};
for (successor, mut move_cost) in successors {
move_cost += cost.clone();
let new_cost = move_cost;
let h; let n; match parents.entry(successor) {
Vacant(e) => {
h = heuristic(e.key());
n = e.index();
e.insert((index, new_cost.clone()));
}
Occupied(mut e) => {
if e.get().1 > new_cost {
h = heuristic(e.key());
n = e.index();
e.insert((index, new_cost.clone()));
} else {
continue;
}
}
}
let mut estimated_cost = new_cost.clone();
estimated_cost += h;
to_see.push(SmallestCostHolder {
estimated_cost: estimated_cost,
cost: new_cost,
index: n,
});
}
}
None
}
struct SmallestCostHolder<K> {
estimated_cost: K,
cost: K,
index: usize,
}
impl<K: PartialEq> PartialEq for SmallestCostHolder<K> {
fn eq(&self, other: &Self) -> bool {
self.estimated_cost.eq(&other.estimated_cost) && self.cost.eq(&other.cost)
}
}
impl<K: PartialEq> Eq for SmallestCostHolder<K> {}
impl<K: Ord> PartialOrd for SmallestCostHolder<K> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<K: Ord> Ord for SmallestCostHolder<K> {
fn cmp(&self, other: &Self) -> Ordering {
match other.estimated_cost.cmp(&self.estimated_cost) {
Ordering::Equal => self.cost.cmp(&other.cost),
s => s,
}
}
}