use alloc::collections::BinaryHeap;
use core::hash::Hash;
use hashbrown::hash_map::{
Entry::{Occupied, Vacant},
HashMap,
};
use crate::algo::Measure;
use crate::scored::MinScored;
use crate::visit::{EdgeRef, IntoEdges, IntoEdgesDirected, VisitMap, Visitable};
use crate::Direction;
pub fn dijkstra<G, F, K>(
graph: G,
start: G::NodeId,
goal: Option<G::NodeId>,
edge_cost: F,
) -> HashMap<G::NodeId, K>
where
G: IntoEdges + Visitable,
G::NodeId: Eq + Hash,
F: FnMut(G::EdgeRef) -> K,
K: Measure + Copy,
{
with_dynamic_goal(graph, start, |node| goal.as_ref() == Some(node), edge_cost).scores
}
pub struct AlgoResult<N, K> {
pub scores: HashMap<N, K>,
pub goal_node: Option<N>,
}
pub fn with_dynamic_goal<G, GoalFn, CostFn, K>(
graph: G,
start: G::NodeId,
mut goal_fn: GoalFn,
mut edge_cost: CostFn,
) -> AlgoResult<G::NodeId, K>
where
G: IntoEdges + Visitable,
G::NodeId: Eq + Hash,
GoalFn: FnMut(&G::NodeId) -> bool,
CostFn: FnMut(G::EdgeRef) -> K,
K: Measure + Copy,
{
let mut visited = graph.visit_map();
let mut scores = HashMap::new();
let mut visit_next = BinaryHeap::new();
let zero_score = K::default();
scores.insert(start, zero_score);
visit_next.push(MinScored(zero_score, start));
let mut goal_node = None;
while let Some(MinScored(node_score, node)) = visit_next.pop() {
if visited.is_visited(&node) {
continue;
}
if goal_fn(&node) {
goal_node = Some(node);
break;
}
for edge in graph.edges(node) {
let next = edge.target();
if visited.is_visited(&next) {
continue;
}
let next_score = node_score + edge_cost(edge);
match scores.entry(next) {
Occupied(ent) => {
if next_score < *ent.get() {
*ent.into_mut() = next_score;
visit_next.push(MinScored(next_score, next));
}
}
Vacant(ent) => {
ent.insert(next_score);
visit_next.push(MinScored(next_score, next));
}
}
}
visited.visit(node);
}
AlgoResult { scores, goal_node }
}
pub fn bidirectional_dijkstra<G, F, K>(
graph: G,
start: G::NodeId,
goal: G::NodeId,
mut edge_cost: F,
) -> Option<K>
where
G: Visitable + IntoEdgesDirected,
G::NodeId: Eq + Hash,
F: FnMut(G::EdgeRef) -> K,
K: Measure + Copy,
{
let mut forward_visited = graph.visit_map();
let mut forward_distance = HashMap::new();
forward_distance.insert(start, K::default());
let mut backward_visited = graph.visit_map();
let mut backward_distance = HashMap::new();
backward_distance.insert(goal, K::default());
let mut forward_heap = BinaryHeap::new();
let mut backward_heap = BinaryHeap::new();
forward_heap.push(MinScored(K::default(), start));
backward_heap.push(MinScored(K::default(), goal));
let mut best_value = None;
while !forward_heap.is_empty() && !backward_heap.is_empty() {
let MinScored(_, u) = forward_heap.pop().unwrap();
let MinScored(_, v) = backward_heap.pop().unwrap();
forward_visited.visit(u);
backward_visited.visit(v);
let distance_to_u = forward_distance[&u];
let distance_to_v = backward_distance[&v];
for edge in graph.edges_directed(u, Direction::Outgoing) {
let x = edge.target();
let current_edge_cost = edge_cost(edge);
if !forward_visited.is_visited(&x) {
let next_score = distance_to_u + current_edge_cost;
match forward_distance.entry(x) {
Occupied(entry) => {
if next_score < *entry.get() {
*entry.into_mut() = next_score;
forward_heap.push(MinScored(next_score, x));
}
}
Vacant(entry) => {
entry.insert(next_score);
forward_heap.push(MinScored(next_score, x));
}
}
}
if !backward_visited.is_visited(&x) {
continue;
}
let potential_best_value = distance_to_u + current_edge_cost + backward_distance[&x];
let improves_best_value = match best_value {
None => true,
Some(current_best_value) => potential_best_value < current_best_value,
};
if improves_best_value {
best_value = Some(potential_best_value);
}
}
for edge in graph.edges_directed(v, Direction::Incoming) {
let x = edge.source();
let edge_cost = edge_cost(edge);
if !backward_visited.is_visited(&x) {
let next_score = distance_to_v + edge_cost;
match backward_distance.entry(x) {
Occupied(entry) => {
if next_score < *entry.get() {
*entry.into_mut() = next_score;
backward_heap.push(MinScored(next_score, x));
}
}
Vacant(entry) => {
entry.insert(next_score);
backward_heap.push(MinScored(next_score, x));
}
}
}
if !forward_visited.is_visited(&x) {
continue;
}
let potential_best_value = distance_to_v + edge_cost + forward_distance[&x];
let improves_best_value = match best_value {
None => true,
Some(mu) => potential_best_value < mu,
};
if improves_best_value {
best_value = Some(potential_best_value);
}
}
if let Some(best_value) = best_value {
if distance_to_u + distance_to_v >= best_value {
return Some(best_value);
}
}
}
None
}