use alloc::vec::Vec;
use num_traits::AsPrimitive;
use crate::traits::{PositiveInteger, SparseValuedMatrix2D};
const VTX_S: usize = 0;
const VTX_T: usize = 1;
#[inline]
fn prim(v: usize) -> usize {
v ^ 1
}
#[inline]
fn virt_x(i: usize) -> usize {
2 * (i + 1)
}
#[inline]
fn virt_y(i: usize) -> usize {
2 * (i + 1) + 1
}
#[inline]
fn orig(v: usize) -> usize {
(v - 2) / 2
}
const NO_VERTEX: usize = usize::MAX;
const BLOSSOM_BASE: usize = usize::MAX - 1;
const TREE_NOT_IN: u8 = 0;
const TREE_IN_T: u8 = 1;
const TREE_IN_TP: u8 = 2;
const TREE_IN_BLOSSOM: u8 = 3;
#[inline]
fn is_s_reachable(t: u8) -> bool {
t == TREE_IN_T || t == TREE_IN_BLOSSOM
}
const NO_EDGE: usize = usize::MAX;
#[derive(Clone, Copy)]
struct SwitchEdge {
vert1: usize,
vert2: usize,
edge_idx: usize,
}
impl SwitchEdge {
const NONE: Self = Self { vert1: NO_VERTEX, vert2: NO_VERTEX, edge_idx: NO_EDGE };
}
fn find_edge_idx_in_adj(adj: &[Vec<(usize, usize)>], i: usize, j: usize) -> Option<usize> {
for &(neighbor, eidx) in &adj[i] {
if neighbor == j {
return Some(eidx);
}
}
None
}
pub(super) struct KocayState<'a, M: SparseValuedMatrix2D + ?Sized> {
_marker: core::marker::PhantomData<&'a M>,
n: usize,
adj: Vec<Vec<(usize, usize)>>,
edge_cap: Vec<usize>,
edge_flow: Vec<usize>,
st_cap: Vec<usize>,
st_flow: Vec<usize>,
edge_on_path: Vec<bool>,
st_on_path: Vec<bool>,
base_ptr: Vec<usize>,
switch_edge: Vec<SwitchEdge>,
tree: Vec<u8>,
scan_q: Vec<usize>,
path_u: Vec<usize>,
path_v: Vec<usize>,
}
impl<'a, M: SparseValuedMatrix2D + ?Sized> KocayState<'a, M>
where
M::Value: PositiveInteger,
M::RowIndex: PositiveInteger,
M::ColumnIndex: PositiveInteger,
{
pub(super) fn new_with_initial_flow(
matrix: &'a M,
vertex_budgets: &[M::Value],
initial_flow: &[(M::RowIndex, M::ColumnIndex, M::Value)],
) -> Self {
let n_rows: usize = matrix.number_of_rows().as_();
let n = n_rows;
let nv = 2 * n + 2;
let mut adj: Vec<Vec<(usize, usize)>> = vec![Vec::new(); n];
let mut edge_cap: Vec<usize> = Vec::new();
for i in matrix.row_indices() {
let iu: usize = i.as_();
for (col, val) in matrix.sparse_row(i).zip(matrix.sparse_row_values(i)) {
let ju: usize = col.as_();
if ju <= iu {
continue; }
let cap: usize = val.as_();
if cap == 0 {
continue;
}
let eidx = edge_cap.len();
adj[iu].push((ju, eidx));
adj[ju].push((iu, eidx));
edge_cap.push(cap);
}
}
let num_edges = edge_cap.len();
let st_cap: Vec<usize> = vertex_budgets.iter().map(|b| (*b).as_()).collect();
let mut edge_flow = vec![0usize; num_edges];
let mut st_flow = vec![0usize; n];
for &(row, col, flow) in initial_flow {
let r: usize = row.as_();
let c: usize = col.as_();
let f: usize = flow.as_();
assert!(r < c, "initial_flow requires row < col, got ({r}, {c})");
assert!(f > 0, "initial_flow values must be > 0, got flow=0 on ({r}, {c})");
let eidx = find_edge_idx_in_adj(&adj, r, c)
.unwrap_or_else(|| panic!("initial_flow references non-existent edge ({r}, {c})"));
assert!(
f <= edge_cap[eidx],
"initial_flow {f} exceeds edge capacity {} on ({r}, {c})",
edge_cap[eidx]
);
assert!(edge_flow[eidx] == 0, "initial_flow contains duplicate edge ({r}, {c})");
edge_flow[eidx] = f;
st_flow[r] += f;
st_flow[c] += f;
}
for v in 0..n {
assert!(
st_flow[v] <= st_cap[v],
"initial_flow total at vertex {v} ({}) exceeds budget ({})",
st_flow[v],
st_cap[v]
);
}
Self {
_marker: core::marker::PhantomData,
n,
adj,
edge_cap,
edge_flow,
st_cap,
st_flow,
edge_on_path: vec![false; num_edges],
st_on_path: vec![false; n],
base_ptr: vec![NO_VERTEX; nv],
switch_edge: vec![SwitchEdge::NONE; nv],
tree: vec![TREE_NOT_IN; nv],
scan_q: Vec::with_capacity(nv),
path_u: Vec::with_capacity(nv),
path_v: Vec::with_capacity(nv),
}
}
pub(super) fn solve(mut self) -> Vec<(M::RowIndex, M::ColumnIndex, M::Value)> {
if self.n == 0 {
return Vec::new();
}
loop {
if !self.balanced_network_search() {
break;
}
self.reinit();
}
self.into_flow_triples()
}
fn into_flow_triples(self) -> Vec<(M::RowIndex, M::ColumnIndex, M::Value)> {
let mut triples = Vec::new();
for (i, neighbors) in self.adj.iter().enumerate() {
for &(j, eidx) in neighbors {
if j <= i {
continue; }
let flow = self.edge_flow[eidx];
if flow > 0 {
let i_idx = M::RowIndex::try_from(i).ok().expect("valid vertex index");
let j_idx = M::ColumnIndex::try_from(j).ok().expect("valid vertex index");
let f_val = M::Value::try_from(flow).ok().expect("valid flow value");
triples.push((i_idx, j_idx, f_val));
}
}
}
triples
}
fn reinit(&mut self) {
for idx in 0..self.scan_q.len() {
let u = self.scan_q[idx];
let v = prim(u);
self.base_ptr[u] = NO_VERTEX;
self.base_ptr[v] = NO_VERTEX;
self.switch_edge[u] = SwitchEdge::NONE;
self.switch_edge[v] = SwitchEdge::NONE;
self.tree[u] = TREE_NOT_IN;
self.tree[v] = TREE_NOT_IN;
}
self.scan_q.clear();
}
fn find_base(&mut self, u: usize) -> usize {
if self.base_ptr[u] == NO_VERTEX {
return NO_VERTEX;
}
let mut root = u;
while self.base_ptr[root] != BLOSSOM_BASE {
root = self.base_ptr[root];
}
let mut cur = u;
while self.base_ptr[cur] != BLOSSOM_BASE {
let next = self.base_ptr[cur];
self.base_ptr[cur] = root;
cur = next;
}
root
}
fn rescap(&self, u: usize, v: usize, eidx: usize) -> usize {
if eidx == NO_EDGE {
if u == VTX_S {
let i = orig(v);
self.st_cap[i] - self.st_flow[i]
} else if v == VTX_S {
let i = orig(u);
self.st_flow[i]
} else if v == VTX_T {
let i = orig(u);
self.st_cap[i] - self.st_flow[i]
} else {
assert_eq!(u, VTX_T);
let i = orig(v);
self.st_flow[i]
}
} else if u >= 2 && u.is_multiple_of(2) {
self.edge_cap[eidx] - self.edge_flow[eidx]
} else {
self.edge_flow[eidx]
}
}
fn rescap_mark(&mut self, u: usize, v: usize, eidx: usize) -> usize {
if eidx == NO_EDGE {
let i = if u == VTX_S || v == VTX_T {
if u == VTX_S { orig(v) } else { orig(u) }
} else {
if v == VTX_S { orig(u) } else { orig(v) }
};
if self.st_on_path[i] {
self.rescap(u, v, eidx) / 2
} else {
self.st_on_path[i] = true;
self.rescap(u, v, eidx)
}
} else if self.edge_on_path[eidx] {
self.rescap(u, v, eidx) / 2
} else {
self.edge_on_path[eidx] = true;
self.rescap(u, v, eidx)
}
}
fn find_edge_idx(&self, i: usize, j: usize) -> usize {
for &(neighbor, eidx) in &self.adj[i] {
if neighbor == j {
return eidx;
}
}
NO_EDGE
}
fn edge_idx_for_virtual(&self, u: usize, v: usize) -> usize {
if u <= 1 || v <= 1 {
return NO_EDGE;
}
let i = orig(u);
let j = orig(v);
self.find_edge_idx(i, j)
}
fn clear_path_marks(&mut self) {
for m in &mut self.edge_on_path {
*m = false;
}
for m in &mut self.st_on_path {
*m = false;
}
}
fn find_path_cap(&mut self, x: usize, y: usize) -> usize {
let mut delta = usize::MAX;
let mut stack: Vec<(usize, usize)> = Vec::new();
stack.push((x, y));
while let Some((x, y)) = stack.pop() {
let se = self.switch_edge[y];
let w = se.vert1;
let z = se.vert2;
let eidx = se.edge_idx;
let cap = self.rescap_mark(w, z, eidx);
delta = delta.min(cap);
if w != x {
stack.push((x, w));
}
if z != y {
stack.push((prim(y), prim(z)));
}
}
delta
}
fn augment_edge(&mut self, u: usize, v: usize, eidx: usize, delta: usize) {
if eidx == NO_EDGE {
let is_forward = (u == VTX_S) || (v == VTX_T);
let i = if u == VTX_S || v == VTX_T {
if u == VTX_S { orig(v) } else { orig(u) }
} else {
if v == VTX_S { orig(u) } else { orig(v) }
};
if is_forward {
self.st_flow[i] += delta;
} else {
self.st_flow[i] -= delta;
}
} else {
if u >= 2 && u.is_multiple_of(2) {
self.edge_flow[eidx] += delta;
} else {
self.edge_flow[eidx] -= delta;
}
}
}
fn pull_flow(&mut self, delta: usize) {
let mut stack: Vec<(usize, usize, bool)> = Vec::new();
stack.push((VTX_S, VTX_T, false));
while let Some((x, y, reverse)) = stack.pop() {
let se = self.switch_edge[y];
let w = se.vert1;
let z = se.vert2;
let eidx = se.edge_idx;
self.augment_edge(w, z, eidx, delta);
if reverse {
if w != x {
stack.push((x, w, true));
}
if z != y {
stack.push((prim(y), prim(z), false));
}
} else {
if z != y {
stack.push((prim(y), prim(z), true));
}
if w != x {
stack.push((x, w, false));
}
}
}
}
fn balanced_network_search(&mut self) -> bool {
self.scan_q.clear();
self.scan_q.push(VTX_S);
self.base_ptr[VTX_T] = VTX_S;
self.base_ptr[VTX_S] = BLOSSOM_BASE;
self.tree[VTX_S] = TREE_IN_T;
let mut k = 0;
while k < self.scan_q.len() {
let u = self.scan_q[k];
let mut b_u = self.find_base(u);
if u == VTX_S {
for i in 0..self.n {
if self.st_cap[i] > self.st_flow[i] {
let v = virt_x(i);
if self.process_neighbor(u, v, NO_EDGE, &mut b_u) {
return true;
}
}
}
} else if u >= 2 && u.is_multiple_of(2) {
let i = orig(u);
if self.st_flow[i] > 0 && self.process_neighbor(u, VTX_S, NO_EDGE, &mut b_u) {
return true;
}
let adj_i = self.adj[i].clone();
for &(j, eidx) in &adj_i {
if self.edge_cap[eidx] > self.edge_flow[eidx] {
let v = virt_y(j);
if self.process_neighbor(u, v, eidx, &mut b_u) {
return true;
}
}
}
} else if u >= 2 {
let i = orig(u);
if self.st_cap[i] > self.st_flow[i]
&& self.process_neighbor(u, VTX_T, NO_EDGE, &mut b_u)
{
return true;
}
let adj_i = self.adj[i].clone();
for &(j, eidx) in &adj_i {
if self.edge_flow[eidx] > 0 {
let v = virt_x(j);
if self.process_neighbor(u, v, eidx, &mut b_u) {
return true;
}
}
}
}
k += 1;
}
false
}
fn process_neighbor(&mut self, u: usize, v: usize, eidx: usize, b_u: &mut usize) -> bool {
if self.switch_edge[u].vert1 == v
&& self.switch_edge[u].vert2 == u
&& self.switch_edge[u].edge_idx == eidx
{
return false;
}
let b_v = self.find_base(v);
if b_v == NO_VERTEX {
self.scan_q.push(v);
self.tree[v] = TREE_IN_T;
self.tree[prim(v)] = TREE_IN_TP;
self.switch_edge[v] = SwitchEdge { vert1: u, vert2: v, edge_idx: eidx };
self.base_ptr[prim(v)] = v;
self.base_ptr[v] = BLOSSOM_BASE;
return false;
}
if is_s_reachable(self.tree[prim(v)]) && *b_u != b_v {
let pu = prim(u);
let pv = prim(v);
let comp_eidx = self.edge_idx_for_virtual(pu, pv);
if self.switch_edge[pu].vert1 == pv
&& self.switch_edge[pu].vert2 == pu
&& self.switch_edge[pu].edge_idx == comp_eidx
{
return false;
}
let w = self.make_blossom(u, v, eidx, *b_u, b_v);
*b_u = w;
if prim(w) == VTX_T {
self.clear_path_marks();
let delta = self.find_path_cap(VTX_S, VTX_T);
if delta > 0 {
self.pull_flow(delta);
return true;
}
}
}
false
}
fn find_path_to_s(&mut self, mut x: usize, path: &mut Vec<usize>) {
path.clear();
path.push(x);
while x != VTX_S {
x = self.find_base(self.switch_edge[x].vert1);
path.push(x);
}
}
fn make_blossom(&mut self, u: usize, v: usize, iuv: usize, b_u: usize, b_v: usize) -> usize {
let mut pu = core::mem::take(&mut self.path_u);
let mut pv = core::mem::take(&mut self.path_v);
self.find_path_to_s(b_u, &mut pu);
self.find_path_to_s(b_v, &mut pv);
let mut match_count = 0;
let min_len = pu.len().min(pv.len());
while match_count < min_len
&& pu[pu.len() - 1 - match_count] == pv[pv.len() - 1 - match_count]
{
match_count += 1;
}
let mut pu_contract_end = pu.len() - match_count;
let mut w = pu[pu_contract_end];
let pv_suffix_start = pv.len() - match_count;
while w != VTX_S {
let se = self.switch_edge[w];
if se.vert1 == NO_VERTEX {
break;
}
let rc = self.rescap(se.vert1, w, se.edge_idx);
if rc < 2 {
break;
}
let parent_base = self.find_base(se.vert1);
if parent_base == NO_VERTEX || parent_base == w {
break;
}
pu_contract_end += 1;
w = parent_base;
}
for k in (0..pu_contract_end).rev() {
let z = pu[k];
self.base_ptr[z] = w;
self.base_ptr[prim(z)] = w;
let pz = prim(z);
if !is_s_reachable(self.tree[pz]) {
let comp_eidx = self.edge_idx_for_virtual(prim(v), prim(u));
self.switch_edge[pz] =
SwitchEdge { vert1: prim(v), vert2: prim(u), edge_idx: comp_eidx };
self.scan_q.push(pz);
self.tree[pz] = TREE_IN_BLOSSOM;
}
}
for k in (0..pv_suffix_start).rev() {
let z = pv[k];
self.base_ptr[z] = w;
self.base_ptr[prim(z)] = w;
let pz = prim(z);
if !is_s_reachable(self.tree[pz]) {
self.switch_edge[pz] = SwitchEdge { vert1: u, vert2: v, edge_idx: iuv };
self.scan_q.push(pz);
self.tree[pz] = TREE_IN_BLOSSOM;
}
}
let pw = prim(w);
if !is_s_reachable(self.tree[pw]) {
self.switch_edge[pw] = SwitchEdge { vert1: u, vert2: v, edge_idx: iuv };
self.scan_q.push(pw);
self.tree[pw] = TREE_IN_BLOSSOM;
}
self.path_u = pu;
self.path_v = pv;
w
}
}