use rand::Rng;
use std::convert::TryInto;

#[derive(Debug, Clone)]
pub struct Graph {
    pub nodes: Vec<u32>,
    pub edges: Vec<(u32, u32)>,
}

pub fn min_cut(mut g: Graph) -> u32 {
    let mut rng = rand::thread_rng();
    // println!("min_cut({:?})\n", g);
    while g.nodes.len() > 2 {
        // pick random edge (u, v)
        let edge_index = rng.gen_range(0, g.edges.len());
        let removed_edge = g.edges.swap_remove(edge_index);

        // merge u and v into a single node u
        let mut self_loops = vec![];
        let u = removed_edge.0;
        let v = removed_edge.1;
        for i in 0..g.edges.len() {
            // replace all v with u
            if g.edges[i].0 == v {
                g.edges[i].0 = u;
            }
            if g.edges[i].1 == v {
                g.edges[i].1 = u;
            }
            // make sure lower node comes first
            if g.edges[i].0 > g.edges[i].1 {
                g.edges[i] = (g.edges[i].1, g.edges[i].0);
            }

            if g.edges[i].0 == g.edges[i].1 {
                self_loops.push(i);
            }
        }

        // remove v from nodes
        g.nodes.pop(); // we only use the nodes.len()
                       // if let Some(i) = g.nodes.iter().position(|x| *x == v) {
                       //   	g.nodes.swap_remove(i);
                       // }

        // remove self-loops
        let mut self_loops_removed: usize = 0;
        for i in self_loops {
            g.edges.remove(i - self_loops_removed);
            self_loops_removed += 1;
        }
    }
    // return cut represented by the final two nodes
    g.edges.len().try_into().unwrap()
}
