use alloc::{collections::VecDeque, vec, vec::Vec};
use core::ops::Sub;
use crate::{
algo::PositiveMeasure,
data::DataMap,
prelude::Direction,
visit::{
EdgeCount, EdgeIndexable, EdgeRef, IntoEdges, IntoEdgesDirected, NodeCount, NodeIndexable,
VisitMap, Visitable,
},
};
fn residual_capacity<G>(
network: G,
edge: G::EdgeRef,
vertex: G::NodeId,
flow: G::EdgeWeight,
) -> G::EdgeWeight
where
G: NodeIndexable + IntoEdges,
G::EdgeWeight: Sub<Output = G::EdgeWeight> + PositiveMeasure,
{
if vertex == edge.source() {
flow
} else if vertex == edge.target() {
*edge.weight() - flow
} else {
let end_point = NodeIndexable::to_index(&network, vertex);
panic!("Illegal endpoint {}", end_point);
}
}
fn other_endpoint<G>(network: G, edge: G::EdgeRef, vertex: G::NodeId) -> G::NodeId
where
G: NodeIndexable + IntoEdges,
{
if vertex == edge.source() {
edge.target()
} else if vertex == edge.target() {
edge.source()
} else {
let end_point = NodeIndexable::to_index(&network, vertex);
panic!("Illegal endpoint {}", end_point);
}
}
fn has_augmented_path<G>(
network: G,
source: G::NodeId,
destination: G::NodeId,
edge_to: &mut [Option<G::EdgeRef>],
flows: &[G::EdgeWeight],
) -> bool
where
G: NodeCount + IntoEdgesDirected + NodeIndexable + EdgeIndexable + Visitable,
G::EdgeWeight: Sub<Output = G::EdgeWeight> + PositiveMeasure,
{
let mut visited = network.visit_map();
let mut queue = VecDeque::new();
visited.visit(source);
queue.push_back(source);
while let Some(vertex) = queue.pop_front() {
let out_edges = network.edges_directed(vertex, Direction::Outgoing);
let in_edges = network.edges_directed(vertex, Direction::Incoming);
for edge in out_edges.chain(in_edges) {
let next = other_endpoint(&network, edge, vertex);
let edge_index: usize = EdgeIndexable::to_index(&network, edge.id());
let residual_cap = residual_capacity(&network, edge, next, flows[edge_index]);
if !visited.is_visited(&next) && (residual_cap > G::EdgeWeight::zero()) {
visited.visit(next);
edge_to[NodeIndexable::to_index(&network, next)] = Some(edge);
if destination == next {
return true;
}
queue.push_back(next);
}
}
}
false
}
fn adjust_residual_flow<G>(
network: G,
edge: G::EdgeRef,
vertex: G::NodeId,
flow: G::EdgeWeight,
delta: G::EdgeWeight,
) -> G::EdgeWeight
where
G: NodeIndexable + IntoEdges,
G::EdgeWeight: Sub<Output = G::EdgeWeight> + PositiveMeasure,
{
if vertex == edge.source() {
flow - delta
} else if vertex == edge.target() {
flow + delta
} else {
let end_point = NodeIndexable::to_index(&network, vertex);
panic!("Illegal endpoint {}", end_point);
}
}
pub fn ford_fulkerson<G>(
network: G,
source: G::NodeId,
destination: G::NodeId,
) -> (G::EdgeWeight, Vec<G::EdgeWeight>)
where
G: NodeCount
+ EdgeCount
+ IntoEdgesDirected
+ EdgeIndexable
+ NodeIndexable
+ DataMap
+ Visitable,
G::EdgeWeight: Sub<Output = G::EdgeWeight> + PositiveMeasure,
{
let mut edge_to = vec![None; network.node_count()];
let mut flows = vec![G::EdgeWeight::zero(); network.edge_bound()];
let mut max_flow = G::EdgeWeight::zero();
while has_augmented_path(&network, source, destination, &mut edge_to, &flows) {
let mut path_flow = G::EdgeWeight::max();
let mut vertex = destination;
let mut vertex_index = NodeIndexable::to_index(&network, vertex);
while let Some(edge) = edge_to[vertex_index] {
let edge_index = EdgeIndexable::to_index(&network, edge.id());
let residual_capacity = residual_capacity(&network, edge, vertex, flows[edge_index]);
path_flow = if path_flow > residual_capacity {
residual_capacity
} else {
path_flow
};
vertex = other_endpoint(&network, edge, vertex);
vertex_index = NodeIndexable::to_index(&network, vertex);
}
let mut vertex = destination;
let mut vertex_index = NodeIndexable::to_index(&network, vertex);
while let Some(edge) = edge_to[vertex_index] {
let edge_index = EdgeIndexable::to_index(&network, edge.id());
flows[edge_index] =
adjust_residual_flow(&network, edge, vertex, flows[edge_index], path_flow);
vertex = other_endpoint(&network, edge, vertex);
vertex_index = NodeIndexable::to_index(&network, vertex);
}
max_flow = max_flow + path_flow;
}
(max_flow, flows)
}