use crate::error::{GraphalgError, GraphalgResult};
use crate::repr::weighted_graph::WeightedGraph;
use super::bellman_ford::bellman_ford;
use super::dijkstra::dijkstra;
pub fn johnson(g: &WeightedGraph) -> GraphalgResult<Vec<f64>> {
let n = g.n;
let mut aug = WeightedGraph::new(n + 1);
for (u, adj) in g.edges.iter().enumerate() {
for &(v, w) in adj {
aug.add_edge(u, v, w)?;
}
}
for v in 0..n {
aug.add_edge(n, v, 0.0)?;
}
let h_out = bellman_ford(&aug, n)?;
let h = h_out.dist;
let mut reweighted = WeightedGraph::new(n);
for (u, adj) in g.edges.iter().enumerate() {
for &(v, w) in adj {
let nw = w + h[u] - h[v];
if nw < 0.0 {
return Err(GraphalgError::NumericalInstability(
"reweighting produced negative edge".to_string(),
));
}
reweighted.add_edge(u, v, nw)?;
}
}
let mut dist = vec![f64::INFINITY; n * n];
for s in 0..n {
let out = dijkstra(&reweighted, s)?;
for t in 0..n {
if out.dist[t].is_finite() {
dist[s * n + t] = out.dist[t] - h[s] + h[t];
}
}
}
Ok(dist)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn johnson_matches_fw() {
let mut g = WeightedGraph::new(3);
g.add_edge(0, 1, 1.0).expect("ok");
g.add_edge(1, 2, 2.0).expect("ok");
g.add_edge(0, 2, 5.0).expect("ok");
let d = johnson(&g).expect("ok");
assert!((d[0 * 3 + 2] - 3.0).abs() < 1e-9);
}
}