mod bipartite;
mod shared;
mod solver;
mod tree;
use alloc::vec::Vec;
use num_traits::AsPrimitive;
use solver::minimum_cost_balanced_flow_impl;
use crate::traits::{Number, PositiveInteger, SparseValuedMatrix2D};
pub trait MinimumCostBalancedFlow: SparseValuedMatrix2D + Sized
where
Self::Value: PositiveInteger,
Self::RowIndex: PositiveInteger,
Self::ColumnIndex: PositiveInteger,
{
fn minimum_cost_balanced_flow<C>(
&self,
vertex_budgets: &[Self::Value],
edge_costs: &C,
) -> Vec<(Self::RowIndex, Self::ColumnIndex, Self::Value)>
where
C: SparseValuedMatrix2D<RowIndex = Self::RowIndex, ColumnIndex = Self::ColumnIndex>,
C::Value: Number + AsPrimitive<i64>,
{
minimum_cost_balanced_flow_impl(self, vertex_budgets, edge_costs)
}
}
impl<M> MinimumCostBalancedFlow for M
where
M: SparseValuedMatrix2D,
M::Value: PositiveInteger,
M::RowIndex: PositiveInteger,
M::ColumnIndex: PositiveInteger,
{
}
#[cfg(test)]
mod tests {
use super::{
bipartite::bipartite_minimum_cost_balanced_flow, shared::OriginalEdge,
tree::tree_minimum_cost_balanced_flow,
};
#[test]
fn test_private_tree_helper_handles_empty_input() {
assert!(tree_minimum_cost_balanced_flow(0, &[], &[]).is_empty());
}
#[test]
#[cfg(feature = "std")]
fn test_private_tree_helper_panics_on_disconnected_input() {
let edges = [OriginalEdge { u: 0, v: 1, capacity: 1, cost: 3 }];
let result =
std::panic::catch_unwind(|| tree_minimum_cost_balanced_flow(3, &edges, &[1, 1, 1]));
assert!(result.is_err());
}
#[test]
#[cfg(feature = "std")]
fn test_private_bipartite_helper_panics_on_invalid_coloring() {
let edges = [OriginalEdge { u: 0, v: 1, capacity: 1, cost: 2 }];
let result = std::panic::catch_unwind(|| {
bipartite_minimum_cost_balanced_flow(2, &edges, &[1, 1], &[0, 0])
});
assert!(result.is_err());
}
#[test]
fn test_private_bipartite_helper_solves_simple_case() {
let edges = [OriginalEdge { u: 0, v: 1, capacity: 2, cost: 5 }];
let flow = bipartite_minimum_cost_balanced_flow(2, &edges, &[2, 2], &[0, 1]);
assert_eq!(flow, vec![(0, 1, 2)]);
}
}