use alloc::collections::VecDeque;
use alloc::{vec, vec::Vec};
use core::hash::Hash;
use core::ops::Sub;
use hashbrown::HashMap;
use super::{dijkstra, spfa::spfa_loop};
pub use super::{BoundedMeasure, NegativeCycle};
use crate::visit::{EdgeRef, IntoEdges, IntoNodeIdentifiers, NodeIndexable, Visitable};
#[cfg(feature = "rayon")]
use core::marker::{Send, Sync};
#[allow(clippy::type_complexity)]
pub fn johnson<G, F, K>(
graph: G,
mut edge_cost: F,
) -> Result<HashMap<(G::NodeId, G::NodeId), K>, NegativeCycle>
where
G: IntoEdges + IntoNodeIdentifiers + NodeIndexable + Visitable,
G::NodeId: Eq + Hash,
F: FnMut(G::EdgeRef) -> K,
K: BoundedMeasure + Copy + Sub<K, Output = K>,
{
let reweight = johnson_reweight(graph, &mut edge_cost)?;
let reweight = reweight.as_slice();
let node_bound = graph.node_bound();
let ix = |i| graph.to_index(i);
let mut distance_map: HashMap<(G::NodeId, G::NodeId), K> =
HashMap::with_capacity(node_bound * node_bound);
let mut new_cost = |edge: G::EdgeRef| {
let (sum, _overflow) = edge_cost(edge).overflowing_add(reweight[ix(edge.source())]);
debug_assert!(!_overflow);
sum - reweight[ix(edge.target())]
};
for source in graph.node_identifiers() {
for (target, dist) in dijkstra(graph, source, None, &mut new_cost) {
distance_map.insert(
(source, target),
dist + reweight[ix(target)] - reweight[ix(source)],
);
}
}
Ok(distance_map)
}
#[cfg(feature = "rayon")]
#[allow(clippy::type_complexity)]
pub fn parallel_johnson<G, F, K>(
graph: G,
mut edge_cost: F,
) -> Result<HashMap<(G::NodeId, G::NodeId), K>, NegativeCycle>
where
G: IntoEdges + IntoNodeIdentifiers + NodeIndexable + Visitable + Sync,
G::NodeId: Eq + Hash + Send,
F: Fn(G::EdgeRef) -> K + Sync,
K: BoundedMeasure + Copy + Sub<K, Output = K> + Send + Sync,
{
use rayon::iter::{IntoParallelIterator, ParallelIterator};
let reweight = johnson_reweight(graph, &mut edge_cost)?;
let reweight = reweight.as_slice();
let node_bound = graph.node_bound();
let ix = |i| graph.to_index(i);
let new_cost = |edge: G::EdgeRef| {
let (sum, _overflow) = edge_cost(edge).overflowing_add(reweight[ix(edge.source())]);
debug_assert!(!_overflow);
sum - reweight[ix(edge.target())]
};
let distance_map = (0..node_bound)
.into_par_iter()
.flat_map_iter(|s| {
let source = graph.from_index(s);
dijkstra(graph, source, None, new_cost)
.into_iter()
.map(move |(target, dist)| {
(
(source, target),
dist + reweight[ix(target)] - reweight[ix(source)],
)
})
})
.collect::<HashMap<(G::NodeId, G::NodeId), K>>();
Ok(distance_map)
}
fn johnson_reweight<G, F, K>(graph: G, mut edge_cost: F) -> Result<Vec<K>, NegativeCycle>
where
G: IntoEdges + IntoNodeIdentifiers + NodeIndexable + Visitable,
G::NodeId: Eq + Hash,
F: FnMut(G::EdgeRef) -> K,
K: BoundedMeasure + Copy + Sub<K, Output = K>,
{
let node_bound = graph.node_bound();
let reweight = vec![K::default(); node_bound];
let mut queue: VecDeque<G::NodeId> = VecDeque::with_capacity(node_bound);
queue.extend(graph.node_identifiers());
let in_queue = vec![true; node_bound];
spfa_loop(graph, reweight, None, queue, in_queue, &mut edge_cost).map(|(dists, _)| dists)
}