use std::{collections::VecDeque, ops::Sub};
use hashbrown::HashSet;
use crate::{algo::measures::PositiveMeasure, prelude::*};
fn residual_capacity<'a, G: ?Sized, K, F>(
network: &'a G,
edge: <G as GraphBasics<'a>>::EdgeIndex,
vertex: G::NodeIndex,
flow: K,
edge_weight: F,
) -> Option<K>
where
G: DiGraphProperties<'a>,
K: PositiveMeasure + Sub<Output = K>,
F: Fn(<G as GraphBasics<'a>>::EdgeIndex) -> K,
{
if network.source(edge)?.contains(&vertex) {
Some(flow)
} else if network.target(edge)?.contains(&vertex) {
return Some(edge_weight(edge) - flow);
} else {
panic!("Illegal endpoint.");
}
}
fn other_endpoint<'a, G: ?Sized>(
network: &'a G,
edge: <G as GraphBasics<'a>>::EdgeIndex,
vertex: G::NodeIndex,
) -> Option<HashSet<G::NodeIndex>>
where
G: DiGraphProperties<'a>,
{
if network.source(edge)?.contains(&vertex) {
network.target(edge)
} else if network.target(edge)?.contains(&vertex) {
network.source(edge)
} else {
panic!("Illegal endpoint.");
}
}
fn has_augmented_path<'a, G: ?Sized, K, F>(
network: &'a G,
source: G::NodeIndex,
destination: G::NodeIndex,
edge_to: &mut [Option<(G::EdgeIndex, G::NodeIndex)>],
flows: &[K],
edge_weight: F,
) -> Option<bool>
where
G: DiGraphProperties<'a> + GraphBasics<'a> + CommonProperties<'a, Directed>,
K: Sub<Output = K> + PositiveMeasure,
F: Fn(<G as GraphBasics<'a>>::EdgeIndex) -> K,
{
let mut visited = network.visit_map();
let mut queue = VecDeque::new();
visited.set(source.into(), true);
queue.push_back(source);
while let Some(vertex) = queue.pop_front() {
let out_edges = network.in_edges(vertex)?;
let in_edges = network.out_edges(vertex)?;
for edge in out_edges.union(&in_edges) {
let nb = other_endpoint(network, *edge, vertex)?;
for next in nb {
let residual_cap =
residual_capacity(network, *edge, next, flows[(*edge).into()], &edge_weight)?;
if !visited.contains(next.into()) && (residual_cap > K::zero()) {
visited.set(next.into(), true);
edge_to[next.into()] = Some((*edge, vertex));
if destination == next {
return Some(true);
}
queue.push_back(next);
}
}
}
}
Some(false)
}
fn adjust_residual_flow<'a, G: ?Sized, K>(
network: &'a G,
edge: <G as GraphBasics<'a>>::EdgeIndex,
vertex: G::NodeIndex,
flow: K,
delta: K,
) -> Option<K>
where
G: DiGraphProperties<'a>,
K: Sub<Output = K> + PositiveMeasure,
{
Some(if network.source(edge)?.contains(&vertex) {
flow - delta
} else if network.target(edge)?.contains(&vertex) {
flow + delta
} else {
panic!("Illegal endpoint.");
})
}
pub fn ford_fulkerson<'a, G: ?Sized, K, F>(
network: &'a G,
source: G::NodeIndex,
destination: G::NodeIndex,
edge_weight: F,
) -> Option<(K, Vec<K>)>
where
F: Fn(<G as GraphBasics<'a>>::EdgeIndex) -> K,
G: DiGraphProperties<'a> + CommonProperties<'a, Directed>,
K: Sub<Output = K> + PositiveMeasure,
{
let mut edge_to = vec![None; network.node_count()];
let mut flows = vec![K::zero(); network.edge_count()];
let mut max_flow = K::zero();
while has_augmented_path(
network,
source,
destination,
&mut edge_to,
&flows,
&edge_weight,
)? {
let mut path_flow = K::max();
let mut vertex = destination;
while let Some((edge, prev)) = edge_to[vertex.into()] {
let residual_capacity =
residual_capacity(network, edge, vertex, flows[edge.into()], &edge_weight)?;
path_flow = if path_flow > residual_capacity {
residual_capacity
} else {
path_flow
};
vertex = prev;
}
let mut vertex = destination;
while let Some((edge, prev)) = edge_to[vertex.into()] {
flows[edge.into()] =
adjust_residual_flow(network, edge, vertex, flows[edge.into()], path_flow)?;
vertex = prev;
}
max_flow = max_flow + path_flow;
}
Some((max_flow, flows))
}