use alloc::{vec, vec::Vec};
use core::hash::Hash;
use hashbrown::HashMap;
use crate::algo::{BoundedMeasure, NegativeCycle};
use crate::visit::{
EdgeRef, GraphProp, IntoEdgeReferences, IntoNodeIdentifiers, NodeCompactIndexable,
};
#[allow(clippy::type_complexity, clippy::needless_range_loop)]
pub fn floyd_warshall<G, F, K>(
graph: G,
edge_cost: F,
) -> Result<HashMap<(G::NodeId, G::NodeId), K>, NegativeCycle>
where
G: NodeCompactIndexable + IntoEdgeReferences + IntoNodeIdentifiers + GraphProp,
G::NodeId: Eq + Hash,
F: FnMut(G::EdgeRef) -> K,
K: BoundedMeasure + Copy,
{
let num_of_nodes = graph.node_count();
let mut m_dist = Some(vec![vec![K::max(); num_of_nodes]; num_of_nodes]);
_floyd_warshall_path(graph, edge_cost, &mut m_dist, &mut None)?;
let mut distance_map: HashMap<(G::NodeId, G::NodeId), K> =
HashMap::with_capacity(num_of_nodes * num_of_nodes);
if let Some(dist) = m_dist {
for i in 0..num_of_nodes {
for j in 0..num_of_nodes {
distance_map.insert((graph.from_index(i), graph.from_index(j)), dist[i][j]);
}
}
}
Ok(distance_map)
}
#[allow(clippy::type_complexity, clippy::needless_range_loop)]
pub fn floyd_warshall_path<G, F, K>(
graph: G,
edge_cost: F,
) -> Result<(HashMap<(G::NodeId, G::NodeId), K>, Vec<Vec<Option<usize>>>), NegativeCycle>
where
G: NodeCompactIndexable + IntoEdgeReferences + IntoNodeIdentifiers + GraphProp,
G::NodeId: Eq + Hash,
F: FnMut(G::EdgeRef) -> K,
K: BoundedMeasure + Copy,
{
let num_of_nodes = graph.node_count();
let mut m_dist = Some(vec![vec![K::max(); num_of_nodes]; num_of_nodes]);
let mut m_prev = Some(vec![vec![None; num_of_nodes]; num_of_nodes]);
_floyd_warshall_path(graph, edge_cost, &mut m_dist, &mut m_prev)?;
let mut distance_map = HashMap::with_capacity(num_of_nodes * num_of_nodes);
if let Some(dist) = m_dist {
for i in 0..num_of_nodes {
for j in 0..num_of_nodes {
distance_map.insert((graph.from_index(i), graph.from_index(j)), dist[i][j]);
}
}
}
Ok((distance_map, m_prev.unwrap()))
}
fn set_object<K: Clone>(m_dist: &mut Option<Vec<Vec<K>>>, i: usize, j: usize, value: K) {
if let Some(dist) = m_dist {
dist[i][j] = value;
}
}
fn is_greater<K: PartialOrd>(
m_dist: &mut Option<Vec<Vec<K>>>,
i: usize,
j: usize,
value: K,
) -> bool {
if let Some(dist) = m_dist {
return dist[i][j] > value;
}
false
}
fn _floyd_warshall_path<G, F, K>(
graph: G,
mut edge_cost: F,
m_dist: &mut Option<Vec<Vec<K>>>,
m_prev: &mut Option<Vec<Vec<Option<usize>>>>,
) -> Result<(), NegativeCycle>
where
G: NodeCompactIndexable + IntoEdgeReferences + IntoNodeIdentifiers + GraphProp,
G::NodeId: Eq + Hash,
F: FnMut(G::EdgeRef) -> K,
K: BoundedMeasure + Copy,
{
let num_of_nodes = graph.node_count();
for edge in graph.edge_references() {
let source = graph.to_index(edge.source());
let target = graph.to_index(edge.target());
let cost = edge_cost(edge);
if is_greater(m_dist, source, target, cost) {
set_object(m_dist, source, target, cost);
set_object(m_prev, source, target, Some(source));
if !graph.is_directed() {
set_object(m_dist, target, source, cost);
set_object(m_prev, target, source, Some(target));
}
}
}
for node in graph.node_identifiers() {
let index = graph.to_index(node);
set_object(m_dist, index, index, K::default());
set_object(m_prev, index, index, Some(index));
}
for k in 0..num_of_nodes {
for i in 0..num_of_nodes {
for j in 0..num_of_nodes {
if let Some(dist) = m_dist {
let (result, overflow) = dist[i][k].overflowing_add(dist[k][j]);
if !overflow && dist[i][j] > result {
dist[i][j] = result;
if let Some(prev) = m_prev {
prev[i][j] = prev[k][j];
}
}
}
}
}
}
for i in 0..num_of_nodes {
if let Some(dist) = m_dist {
if dist[i][i] < K::default() {
return Err(NegativeCycle(()));
}
}
}
Ok(())
}