use crate::adjacencies::{Adjacencies, Neighbors, OutEdges};
use crate::collections::BinHeap;
use crate::collections::{ItemMap, ItemPriQueue};
use crate::search::path_from_incomings;
use crate::traits::{Digraph, Graph};
use num_traits::Zero;
use std::cmp::Ordering;
use std::collections::HashMap;
use std::hash::Hash;
use std::marker::PhantomData;
use std::ops::{Add, Sub};
pub struct AStar<'a, A, D, W, M, P, H, Accum>
where
A: Adjacencies<'a>,
M: ItemMap<A::Node, Option<P::Item>>,
P: ItemPriQueue<A::Node, Data<A::Edge, D, H::Result>>,
D: Copy,
W: Fn(A::Edge) -> D,
H: AStarHeuristic<A::Node>,
H::Result: Copy,
Accum: Accumulator<D>,
{
adj: A,
nodes: M,
pqueue: P,
weights: W,
heur: H,
phantom: PhantomData<&'a (D, Accum)>,
}
#[derive(Clone)]
pub struct Data<E, D, H> {
pub incoming_edge: E,
pub distance: D,
lower: H,
}
impl<E, D, H> PartialEq for Data<E, D, H>
where
D: PartialEq,
{
fn eq(&self, data: &Self) -> bool {
self.distance.eq(&data.distance)
}
}
impl<E, D, H> PartialOrd for Data<E, D, H>
where
D: PartialOrd + Clone,
H: Add<D, Output = D> + Clone,
{
fn partial_cmp(&self, data: &Self) -> Option<Ordering> {
(self.lower.clone() + self.distance.clone()).partial_cmp(&(data.lower.clone() + data.distance.clone()))
}
}
pub trait AStarHeuristic<N> {
type Result: Copy + Default;
fn call(&self, u: N) -> Self::Result;
}
impl<F, N, H> AStarHeuristic<N> for F
where
F: Fn(N) -> H,
H: Copy + Default,
{
type Result = H;
fn call(&self, u: N) -> H {
(*self)(u)
}
}
pub trait Accumulator<T> {
fn accum(dist: T, weight: T) -> T;
}
pub struct SumAccumulator;
impl<T> Accumulator<T> for SumAccumulator
where
T: Add<Output = T>,
{
fn accum(dist: T, weight: T) -> T {
dist + weight
}
}
pub type DefaultMap<'a, A, D, H> = HashMap<
<A as Adjacencies<'a>>::Node,
Option<
<BinHeap<<A as Adjacencies<'a>>::Node, Data<<A as Adjacencies<'a>>::Edge, D, H>> as ItemPriQueue<
<A as Adjacencies<'a>>::Node,
Data<<A as Adjacencies<'a>>::Edge, D, H>,
>>::Item,
>,
>;
pub type DefaultPriQueue<'a, A, D, H> = BinHeap<<A as Adjacencies<'a>>::Node, Data<<A as Adjacencies<'a>>::Edge, D, H>>;
pub type DefaultData<ID, N, I, D> = (HashMap<N, I>, BinHeap<N, D, ID>);
pub type AStarDefault<'a, A, D, W, H> = AStar<
'a,
A,
D,
W,
DefaultMap<'a, A, D, <H as AStarHeuristic<<A as Adjacencies<'a>>::Node>>::Result>,
DefaultPriQueue<'a, A, D, <H as AStarHeuristic<<A as Adjacencies<'a>>::Node>>::Result>,
H,
SumAccumulator,
>;
pub fn start<'a, A, D, W, H>(adj: A, src: A::Node, weights: W, heur: H) -> AStarDefault<'a, A, D, W, H>
where
A: Adjacencies<'a>,
A::Node: Hash,
D: Copy + PartialOrd + Zero,
W: Fn(A::Edge) -> D,
H: AStarHeuristic<A::Node>,
H::Result: Add<D, Output = D>,
{
start_with_data(adj, src, weights, heur, DefaultData::default())
}
pub fn start_with_data<'a, A, D, W, H, M, P>(
adj: A,
src: A::Node,
weights: W,
heur: H,
data: (M, P),
) -> AStar<'a, A, D, W, M, P, H, SumAccumulator>
where
A: Adjacencies<'a>,
D: Copy + PartialOrd + Zero,
W: Fn(A::Edge) -> D,
H: AStarHeuristic<A::Node>,
H::Result: Add<D, Output = D>,
M: ItemMap<A::Node, Option<P::Item>>,
P: ItemPriQueue<A::Node, Data<A::Edge, D, H::Result>>,
{
start_generic(adj, src, weights, heur, data)
}
pub fn start_generic<'a, A, D, W, H, Accum, M, P>(
adj: A,
src: A::Node,
weights: W,
heur: H,
data: (M, P),
) -> AStar<'a, A, D, W, M, P, H, Accum>
where
A: Adjacencies<'a>,
D: Copy + PartialOrd + PartialOrd + Zero,
W: Fn(A::Edge) -> D,
H: AStarHeuristic<A::Node>,
H::Result: Add<D, Output = D>,
M: ItemMap<A::Node, Option<P::Item>>,
P: ItemPriQueue<A::Node, Data<A::Edge, D, H::Result>>,
Accum: Accumulator<D>,
{
let (mut nodes, mut pqueue) = data;
pqueue.clear();
nodes.clear();
nodes.insert(src, None);
for (e, v) in adj.neighs(src) {
let dist = Accum::accum(D::zero(), (weights)(e));
match nodes.get_mut(v) {
Some(Some(vitem)) => {
let (olddist, lower) = {
let data = pqueue.value(vitem);
(data.distance, data.lower)
};
if dist < olddist {
pqueue.decrease_key(
vitem,
Data {
incoming_edge: e,
distance: dist,
lower,
},
);
}
}
None => {
let item = pqueue.push(
v,
Data {
incoming_edge: e,
distance: dist,
lower: heur.call(v),
},
);
nodes.insert(v, Some(item));
}
_ => (), };
}
AStar {
adj,
nodes,
pqueue,
weights,
heur,
phantom: PhantomData,
}
}
impl<'a, A, D, W, M, P, H, Accum> Iterator for AStar<'a, A, D, W, M, P, H, Accum>
where
A: Adjacencies<'a>,
D: Copy + PartialOrd + Add<D, Output = D> + Sub<D, Output = D>,
W: Fn(A::Edge) -> D,
M: ItemMap<A::Node, Option<P::Item>>,
P: ItemPriQueue<A::Node, Data<A::Edge, D, H::Result>>,
H: AStarHeuristic<A::Node>,
H::Result: Add<D, Output = D>,
Accum: Accumulator<D>,
{
type Item = (A::Node, A::Edge, D);
fn next(&mut self) -> Option<Self::Item> {
if let Some((u, data)) = self.pqueue.pop_min() {
self.nodes.insert_or_replace(u, None);
let (d, incoming_edge) = (data.distance, data.incoming_edge);
for (e, v) in self.adj.neighs(u) {
let dist = Accum::accum(d, (self.weights)(e));
match self.nodes.get_mut(v) {
Some(Some(vitem)) => {
let (olddist, lower) = {
let data = self.pqueue.value(vitem);
(data.distance, data.lower)
};
if dist < olddist {
self.pqueue.decrease_key(
vitem,
Data {
incoming_edge: e,
distance: dist,
lower,
},
);
}
}
None => {
let item = self.pqueue.push(
v,
Data {
incoming_edge: e,
distance: dist,
lower: self.heur.call(v),
},
);
self.nodes.insert(v, Some(item));
}
_ => (), };
}
Some((u, incoming_edge, d))
} else {
None
}
}
}
impl<'a, A, D, W, M, P, H, Accum> AStar<'a, A, D, W, M, P, H, Accum>
where
A: Adjacencies<'a>,
D: Copy + PartialOrd + Add<D, Output = D> + Sub<D, Output = D>,
W: Fn(A::Edge) -> D,
M: ItemMap<A::Node, Option<P::Item>>,
P: ItemPriQueue<A::Node, Data<A::Edge, D, H::Result>>,
H: AStarHeuristic<A::Node>,
H::Result: Add<D, Output = D>,
Accum: Accumulator<D>,
{
pub fn run(&mut self) {
while self.next().is_some() {}
}
pub fn into_data(self) -> (M, P) {
(self.nodes, self.pqueue)
}
}
pub fn start_undirected<'a, G, D, W, H>(
g: &'a G,
src: G::Node<'a>,
weights: W,
heur: H,
) -> AStarDefault<'a, Neighbors<'a, G>, D, W, H>
where
G: Graph,
G::Node<'a>: Hash,
D: Copy + PartialOrd + Zero,
W: Fn(G::Edge<'a>) -> D,
H: AStarHeuristic<G::Node<'a>>,
H::Result: Add<D, Output = D>,
{
start(Neighbors(g), src, weights, heur)
}
pub fn find_undirected_path<'a, G, D, W, H>(
g: &'a G,
src: G::Node<'a>,
snk: G::Node<'a>,
weights: W,
heur: H,
) -> Option<(Vec<G::Edge<'a>>, D)>
where
G: Graph,
G::Node<'a>: Hash,
D: 'a + Copy + PartialOrd + Zero + Add<D, Output = D> + Sub<D, Output = D>,
W: Fn(G::Edge<'a>) -> D,
H: AStarHeuristic<G::Node<'a>>,
H::Result: Add<D, Output = D>,
{
if src == snk {
return Some((vec![], D::zero()));
}
let mut incoming_edges = HashMap::new();
for (u, e, d) in start_undirected(g, src, weights, heur) {
incoming_edges.insert(u, e);
if u == snk {
let mut path = path_from_incomings(snk, |u| {
incoming_edges
.get(&u)
.map(|&e| (e, g.enodes(e)))
.map(|(e, (v, w))| (e, if v == u { w } else { v }))
})
.collect::<Vec<_>>();
path.reverse();
return Some((path, d));
}
}
None
}
pub fn start_directed<'a, G, D, W, H>(
g: &'a G,
src: G::Node<'a>,
weights: W,
heur: H,
) -> AStarDefault<'a, OutEdges<'a, G>, D, W, H>
where
G: Digraph,
G::Node<'a>: Hash,
D: Copy + PartialOrd + Zero,
W: Fn(G::Edge<'a>) -> D,
H: AStarHeuristic<G::Node<'a>>,
H::Result: Add<D, Output = D>,
{
start(OutEdges(g), src, weights, heur)
}
pub fn find_directed_path<'a, G, D, W, H>(
g: &'a G,
src: G::Node<'a>,
snk: G::Node<'a>,
weights: W,
heur: H,
) -> Option<(Vec<G::Edge<'a>>, D)>
where
G: Digraph,
G::Node<'a>: Hash,
D: 'a + Copy + PartialOrd + Zero + Add<D, Output = D> + Sub<D, Output = D>,
W: Fn(G::Edge<'a>) -> D,
H: AStarHeuristic<G::Node<'a>>,
H::Result: Add<D, Output = D>,
{
if src == snk {
return Some((vec![], D::zero()));
}
let mut incoming_edges = HashMap::new();
for (u, e, d) in start_directed(g, src, weights, heur) {
incoming_edges.insert(u, e);
if u == snk {
let mut path =
path_from_incomings(snk, |u| incoming_edges.get(&u).map(|&e| (e, g.src(e)))).collect::<Vec<_>>();
path.reverse();
return Some((path, d));
}
}
None
}