use crate::builder::{Buildable, Builder};
use crate::linkedlistgraph::LinkedListGraph;
use crate::traits::IndexDigraph;
use crate::vec::{EdgeVec, NodeVec};
use crate::num::traits::NumAssign;
#[allow(clippy::cognitive_complexity)]
pub fn max_weight_branching<'a, G, W>(g: &'a G, weights: &EdgeVec<'a, &'a G, W>) -> Vec<G::Edge>
where
G: IndexDigraph<'a>,
W: NumAssign + Ord + Copy,
{
let mut inarcs = NodeVec::new(g, None);
for e in g.edges() {
let u = g.snk(e);
let w = weights[e];
if let Some(max_arc) = inarcs[u] {
if weights[max_arc] < w {
inarcs[u] = Some(e)
}
} else if w > W::zero() {
inarcs[u] = Some(e)
}
}
let mut newnodes = NodeVec::new(g, None);
let mut newg = LinkedListGraph::<usize>::new_builder();
let mut label = NodeVec::new(g, 0);
let mut diffweights = NodeVec::new(g, W::zero());
for u in g.nodes() {
if label[u] != 0 {
continue;
}
let mut v = u;
while label[v] == 0 {
label[v] = 1;
if let Some(e) = inarcs[v] {
v = g.src(e);
} else {
break;
}
}
if let Some(e) = inarcs[v] {
if label[v] == 1 {
let mut minweight = weights[e];
let mut w = g.src(e);
while w != v {
let e = inarcs[w].unwrap();
if weights[e] < minweight {
minweight = weights[e];
}
w = g.src(e);
}
let contracted_node = newg.add_node();
newnodes[v] = Some(contracted_node);
diffweights[v] = weights[e] - minweight;
label[v] = 2;
let mut w = g.src(e);
while w != v {
newnodes[w] = Some(contracted_node);
label[w] = 2;
let e = inarcs[w].unwrap();
diffweights[w] = weights[e] - minweight;
w = g.src(e);
}
}
}
let mut v = u;
while label[v] == 1 {
newnodes[v] = Some(newg.add_node());
label[v] = 2;
if let Some(e) = inarcs[v] {
v = g.src(e);
} else {
break;
}
}
}
if newg.num_nodes() == g.num_nodes() {
return inarcs.iter().filter_map(|(_, &e)| e).collect();
}
let mut newweights = vec![];
let mut newarcs = vec![];
for e in g.edges() {
let newu = newnodes[g.src(e)].unwrap();
let newv = newnodes[g.snk(e)].unwrap();
if newu != newv && weights[e] > W::zero() {
newg.add_edge(newu, newv);
newarcs.push(e);
newweights.push(weights[e] - diffweights[g.snk(e)]);
}
}
let newg = newg.into_graph();
let newweights = EdgeVec::new_from_vec(&newg, newweights);
let newbranching = max_weight_branching(&newg, &newweights);
let mut branching = vec![];
let newarcs = EdgeVec::new_from_vec(&newg, newarcs);
for newa in newbranching {
let e = newarcs[newa];
branching.push(e);
let u = g.snk(e);
label[u] = 3;
if let Some(inarc) = inarcs[u] {
if inarc != e {
let mut v = g.src(inarc);
while v != u {
label[v] = 3;
let e = inarcs[v].unwrap();
branching.push(e);
v = g.src(e);
}
}
}
}
for u in g.nodes() {
if label[u] == 2 {
label[u] = 3;
if let Some(e) = inarcs[u] {
let mut minarc = e;
let mut v = g.src(e);
while label[v] != 3 {
label[v] = 3;
if let Some(e) = inarcs[v] {
if weights[e] >= weights[minarc] {
branching.push(e);
} else {
branching.push(minarc);
minarc = e;
}
v = g.src(e);
} else {
break;
}
}
}
}
}
branching
}
#[cfg(test)]
mod tests {
use crate::branching::max_weight_branching;
use crate::vec::EdgeVec;
use crate::{Buildable, Builder, LinkedListGraph};
#[test]
fn test_branching1() {
let mut g = LinkedListGraph::<usize>::new_builder();
let mut weights = vec![];
let nodes = g.add_nodes(9);
for &(u, v, c) in [
(1, 4, 17u32),
(1, 5, 5),
(1, 3, 18),
(2, 1, 21),
(2, 6, 17),
(2, 7, 12),
(3, 2, 21),
(3, 8, 15),
(4, 9, 12),
(5, 2, 12),
(5, 4, 12),
(6, 5, 4),
(6, 7, 13),
(7, 3, 14),
(7, 8, 12),
(8, 9, 18),
(9, 1, 19),
(9, 3, 15),
]
.iter()
{
g.add_edge(nodes[u - 1], nodes[v - 1]);
weights.push(c);
}
let g = g.into_graph();
let weights = EdgeVec::new_from_vec(&g, weights);
let branching = max_weight_branching(&g, &weights);
assert_eq!(branching.iter().fold(0, |acc, &e| acc + weights[e]), 131);
}
#[test]
fn test_branching2() {
let mut g = LinkedListGraph::<usize>::new_builder();
let mut weights = vec![];
let nodes = g.add_nodes(9);
for &(u, v, c) in [
(2, 1, 3),
(1, 3, 4),
(6, 3, 3),
(6, 7, 1),
(7, 4, 3),
(1, 2, 10),
(4, 1, 5),
(3, 4, 5),
(4, 5, 2),
(4, 6, 4),
(5, 6, 2),
]
.iter()
{
g.add_edge(nodes[u - 1], nodes[v - 1]);
weights.push(c);
}
let g = g.into_graph();
let weights = EdgeVec::new_from_vec(&g, weights);
let branching = max_weight_branching(&g, &weights);
assert_eq!(branching.iter().fold(0, |acc, &e| acc + weights[e]), 28);
}
}