use crate::graph::graph::{Graph, NodeId};
use std::collections::HashMap;
use std::fmt::Debug;
use std::hash::Hash;
use std::ops::Add;
pub use crate::core::algo::shortest_path::ShortestPathError;
#[deprecated(
since = "3.0.0",
note = "superseded by `xgraph::core::ShortestPath`, which is implemented for every \
graph view — including filtered ones — and carries no unused type parameters"
)]
pub trait ShortestPath<W, N, E>
where
W: Add<Output = W> + PartialOrd + Copy + Default,
N: Clone + Eq + Hash + Debug,
E: Clone + Default + Debug,
{
fn dijkstra(&self, start: NodeId) -> Result<HashMap<NodeId, W>, ShortestPathError>;
#[allow(clippy::type_complexity)]
fn dijkstra_with_predecessors(
&self,
start: NodeId,
) -> Result<(HashMap<NodeId, W>, HashMap<NodeId, NodeId>), ShortestPathError>;
fn shortest_path_to(
&self,
start: NodeId,
target: NodeId,
) -> Result<Option<(Vec<NodeId>, W)>, ShortestPathError>;
}
#[allow(deprecated)]
impl<W, N, E> ShortestPath<W, N, E> for Graph<W, N, E>
where
W: Add<Output = W> + PartialOrd + Copy + Default,
N: Clone + Eq + Hash + Debug,
E: Clone + Default + Debug,
{
fn dijkstra(&self, start: NodeId) -> Result<HashMap<NodeId, W>, ShortestPathError> {
crate::core::ShortestPath::dijkstra(self, start)
}
fn dijkstra_with_predecessors(
&self,
start: NodeId,
) -> Result<(HashMap<NodeId, W>, HashMap<NodeId, NodeId>), ShortestPathError> {
crate::core::ShortestPath::dijkstra_with_predecessors(self, start)
}
fn shortest_path_to(
&self,
start: NodeId,
target: NodeId,
) -> Result<Option<(Vec<NodeId>, W)>, ShortestPathError> {
crate::core::ShortestPath::shortest_path_to(self, start, target)
}
}
#[cfg(test)]
#[allow(deprecated)]
mod tests {
use super::*;
use crate::core::view::NodeId;
#[test]
fn test_dijkstra_basic() {
let mut graph = Graph::<u32, (), ()>::new(false);
let n0 = graph.add_node(());
let n1 = graph.add_node(());
let n2 = graph.add_node(());
graph.add_edge(n0, n1, 2, ()).unwrap();
graph.add_edge(n1, n2, 3, ()).unwrap();
let distances = graph.dijkstra(n0).unwrap();
assert_eq!(distances[&n0], 0);
assert_eq!(distances[&n1], 2);
assert_eq!(distances[&n2], 5);
}
#[test]
fn test_unreachable_node() {
let mut graph = Graph::<u64, (), ()>::new(true);
let n0 = graph.add_node(());
let n1 = graph.add_node(());
let distances = graph.dijkstra(n0).unwrap();
assert_eq!(distances.get(&n1), None);
}
#[test]
fn test_invalid_start_node() {
let graph = Graph::<u32, (), ()>::new(false);
assert!(matches!(
graph.dijkstra(NodeId::from_index(999)),
Err(ShortestPathError::InvalidStartNode(id)) if id == NodeId::from_index(999)
));
}
#[test]
fn test_dijkstra_with_float_weights() {
let mut graph = Graph::<f64, (), ()>::new(false);
let n0 = graph.add_node(());
let n1 = graph.add_node(());
let n2 = graph.add_node(());
graph.add_edge(n0, n1, 1.5, ()).unwrap();
graph.add_edge(n1, n2, 2.25, ()).unwrap();
graph.add_edge(n0, n2, 10.0, ()).unwrap();
let distances = graph.dijkstra(n0).unwrap();
assert_eq!(distances[&n2], 3.75);
}
#[test]
fn test_shortest_path_reconstruction() {
let mut graph = Graph::<u32, (), ()>::new(false);
let n0 = graph.add_node(());
let n1 = graph.add_node(());
let n2 = graph.add_node(());
let n3 = graph.add_node(());
graph.add_edge(n0, n1, 1, ()).unwrap();
graph.add_edge(n1, n3, 1, ()).unwrap();
graph.add_edge(n0, n2, 5, ()).unwrap();
graph.add_edge(n2, n3, 5, ()).unwrap();
assert_eq!(
graph.shortest_path_to(n0, n3).unwrap(),
Some((vec![n0, n1, n3], 2))
);
assert_eq!(graph.shortest_path_to(n0, n0).unwrap(), Some((vec![n0], 0)));
}
#[test]
fn test_unreachable_target_is_none_not_an_error() {
let mut graph = Graph::<u32, (), ()>::new(true);
let n0 = graph.add_node(());
let n1 = graph.add_node(());
assert_eq!(graph.shortest_path_to(n0, n1).unwrap(), None);
assert!(matches!(
graph.shortest_path_to(n0, NodeId::from_index(999)),
Err(ShortestPathError::InvalidTargetNode(id)) if id == NodeId::from_index(999)
));
}
#[test]
fn test_negative_weight_is_rejected() {
let mut graph = Graph::<i32, (), ()>::new(true);
let n0 = graph.add_node(());
let n1 = graph.add_node(());
graph.add_edge(n0, n1, -3, ()).unwrap();
assert!(matches!(
graph.dijkstra(n0),
Err(ShortestPathError::NegativeWeight { .. })
));
}
#[test]
fn test_nan_weight_is_rejected() {
let mut graph = Graph::<f64, (), ()>::new(true);
let n0 = graph.add_node(());
let n1 = graph.add_node(());
graph.add_edge(n0, n1, f64::NAN, ()).unwrap();
assert!(matches!(
graph.dijkstra(n0),
Err(ShortestPathError::IncomparableWeight { .. })
));
}
}