use std::collections::BinaryHeap;
use std::hash::Hash;
use petgraph::algo::Measure;
use petgraph::visit::{EdgeRef, IntoEdges, NodeIndexable, VisitMap, Visitable};
use crate::dictmap::*;
use crate::distancemap::DistanceMap;
use crate::min_scored::MinScored;
pub fn dijkstra<G, F, K, E, S>(
graph: G,
start: G::NodeId,
goal: Option<G::NodeId>,
mut edge_cost: F,
mut path: Option<&mut DictMap<G::NodeId, Vec<G::NodeId>>>,
) -> Result<S, E>
where
G: IntoEdges + Visitable + NodeIndexable,
G::NodeId: Eq + Hash,
F: FnMut(G::EdgeRef) -> Result<K, E>,
K: Measure + Copy,
S: DistanceMap<G::NodeId, K>,
{
let mut visited = graph.visit_map();
let mut scores: S = S::build(graph.node_bound());
let mut visit_next = BinaryHeap::new();
let zero_score = K::default();
scores.put_item(start, zero_score);
visit_next.push(MinScored(zero_score, start));
if let Some(path_mut) = &mut path {
path_mut.insert(start, vec![start]);
}
while let Some(MinScored(node_score, node)) = visit_next.pop() {
if visited.is_visited(&node) {
continue;
}
if goal.as_ref() == Some(&node) {
break;
}
for edge in graph.edges(node) {
let next = edge.target();
if visited.is_visited(&next) {
continue;
}
let cost = edge_cost(edge)?;
let next_score = node_score + cost;
match scores.get_item(next) {
Some(current_score) => {
if next_score < *current_score {
scores.put_item(next, next_score);
visit_next.push(MinScored(next_score, next));
if let Some(path_mut) = &mut path {
let mut node_path = path_mut.get(&node).unwrap().clone();
node_path.push(next);
path_mut.entry(next).and_modify(|new_vec| {
*new_vec = node_path;
});
}
}
}
None => {
scores.put_item(next, next_score);
visit_next.push(MinScored(next_score, next));
if let Some(path_mut) = &mut path {
let mut node_path = path_mut.get(&node).unwrap().clone();
node_path.push(next);
path_mut.entry(next).or_insert(node_path);
}
}
}
}
visited.visit(node);
}
Ok(scores)
}
#[cfg(test)]
mod tests {
use crate::dictmap::DictMap;
use crate::shortest_path::dijkstra;
use crate::Result;
use petgraph::prelude::*;
use petgraph::Graph;
#[test]
fn test_dijk() {
let mut g = Graph::new_undirected();
let a = g.add_node("A");
let b = g.add_node("B");
let c = g.add_node("C");
let d = g.add_node("D");
let e = g.add_node("E");
let f = g.add_node("F");
g.add_edge(a, b, 7);
g.add_edge(c, a, 9);
g.add_edge(a, d, 14);
g.add_edge(b, c, 10);
g.add_edge(d, c, 2);
g.add_edge(d, e, 9);
g.add_edge(b, f, 15);
g.add_edge(c, f, 11);
g.add_edge(e, f, 6);
println!("{g:?}");
let scores: Result<DictMap<NodeIndex, usize>> =
dijkstra(&g, a, None, |e| Ok(*e.weight()), None);
let exp_scores: DictMap<NodeIndex, usize> =
[(a, 0), (b, 7), (c, 9), (d, 11), (e, 20), (f, 20)]
.iter()
.cloned()
.collect();
assert_eq!(scores.unwrap(), exp_scores);
}
#[test]
fn test_dijk_with_goal() {
let mut g = Graph::new_undirected();
let a = g.add_node("A");
let b = g.add_node("B");
let c = g.add_node("C");
let d = g.add_node("D");
let e = g.add_node("E");
let f = g.add_node("F");
g.add_edge(a, b, 7);
g.add_edge(c, a, 9);
g.add_edge(a, d, 14);
g.add_edge(b, c, 10);
g.add_edge(d, c, 2);
g.add_edge(d, e, 9);
g.add_edge(b, f, 15);
g.add_edge(c, f, 11);
g.add_edge(e, f, 6);
println!("{g:?}");
let scores: Result<DictMap<NodeIndex, usize>> =
dijkstra(&g, a, Some(c), |e| Ok(*e.weight()), None);
assert_eq!(scores.unwrap()[&c], 9);
}
}