#[derive(Debug)]
struct Vertex {
    name: u16,
    edges: Vec<u16>,
}

#[derive(Debug)]
pub struct Graph {
    index: Vec<u16>,
    vertices: Vec<Vertex>,
    stack: Vec<Vertex>,
}

impl Graph {
    pub fn new(g: &crate::Graph) -> Self {
        let mut vertices: Vec<_> = (0..g.num_vertices())
            .map(|i| Vertex {
                name: i,
                edges: Vec::new(),
            })
            .collect();

        for (start, s) in g.edge_list.iter().enumerate() {
            vertices[start].edges.extend_from_slice(s);
        }

        Self {
            index: (0..g.num_vertices()).collect(),
            vertices,
            stack: Vec::new(),
        }
    }

    fn is_empty(&self) -> bool {
        self.vertices.is_empty()
    }

    fn remove_vertex(&mut self, v: u16) -> &Vertex {
        let i = self.index[v as usize];
        // dbg!(&self.index, v, i);
        let vertex = self.vertices.swap_remove(i as usize);

        assert_eq!(vertex.name, v);

        if (i as usize) < self.vertices.len() {
            self.index[self.vertices[i as usize].name as usize] = i;
        }

        for &e in &vertex.edges {
            let i = self.index[e as usize];
            let p = self.vertices[i as usize]
                .edges
                .iter()
                .position(|&o| o == v)
                .unwrap();
            self.vertices[i as usize].edges.swap_remove(p);
        }
        // dbg!(&self.index);

        self.stack.push(vertex);

        self.stack.last().unwrap()
    }

    fn reinsert_vertices(&mut self, n: usize) -> impl Iterator<Item = &Vertex> {
        for _ in 0..n {
            let v = self.stack.pop().unwrap();
            for &e in &v.edges {
                let i = self.index[e as usize];
                self.vertices[i as usize].edges.push(v.name);
            }

            self.index[v.name as usize] = self.vertices.len() as u16;
            self.vertices.push(v);
        }

        self.vertices[(self.vertices.len() - n)..].iter()
    }

    fn get_vertex_with_max_degree_greater_equal(&self, min_degree: u16) -> Option<u16> {
        self.vertices
            .iter()
            .max_by_key(|&v| v.edges.len())
            .filter(|&v| v.edges.len() >= min_degree as usize)
            .map(|v| v.name)
    }

    fn get_vertex_with_degree(&self, degree: u16) -> Option<u16> {
        self.vertices
            .iter()
            .find(|&v| v.edges.len() == degree as usize)
            .map(|v| v.name)
    }

    fn get_edges(&self, v: u16) -> impl Iterator<Item = u16> + '_ {
        let i = self.index[v as usize];
        self.vertices[i as usize].edges.iter().copied()
    }

    fn get_edge(&self, v: u16) -> Option<u16> {
        self.get_edges(v).next()
    }

    fn get_degree(&self, v: u16) -> u16 {
        let i = self.index[v as usize];
        self.vertices[i as usize].edges.len() as u16
    }
}

pub fn binary_vertex(graph: &mut Graph, k: u16) -> Option<Vec<u16>> {
    let vertex = match graph.get_vertex_with_max_degree_greater_equal(2) {
        Some(vertex) => vertex,
        None => {
            return {
                let mut c = 0;
                while let Some(vertex) = graph.get_vertex_with_max_degree_greater_equal(1) {
                    graph.remove_vertex(vertex);
                    c += 1;
                }

                if c <= k {
                    Some(
                        graph
                            .reinsert_vertices(c as usize)
                            .map(|v| v.name)
                            .collect(),
                    )
                } else {
                    let _ = graph.reinsert_vertices(c as usize);
                    None
                }
            }
        }
    };

    if k == 0 {
        return None;
    }

    graph.remove_vertex(vertex);

    match binary_vertex(graph, k - 1) {
        Some(mut s) => {
            s.push(vertex);
            Some(s)
        }
        None => {
            let _ = graph.reinsert_vertices(1);
            let num_neighbors = graph.get_edges(vertex).count();

            if num_neighbors <= k as usize {
                let mut c = 0;
                while let Some(e) = graph.get_edge(vertex) {
                    graph.remove_vertex(e);
                    c += 1;
                }
                let mut r = binary_vertex(graph, k - c);
                if let Some(r) = &mut r {
                    r.extend(graph.reinsert_vertices(c as usize).map(|v| v.name));
                } else {
                    let _ = graph.reinsert_vertices(c as usize);
                }
                r
            } else {
                None
            }
        }
    }
}

pub fn reduction(graph: &mut Graph, k: u16) -> Option<Vec<u16>> {
    if let Some(vertex) = graph.get_vertex_with_degree(0) {
        graph.remove_vertex(vertex);

        let r = reduction(graph, k);

        let _ = graph.reinsert_vertices(1);

        r
    } else if k == 0 {
        if graph.is_empty() {
            Some(Vec::new())
        } else {
            None
        }
    } else if let Some(vertex) = graph.get_vertex_with_degree(1) {
        let neighbor = graph.get_edge(vertex).unwrap();

        graph.remove_vertex(neighbor);
        let mut r = reduction(graph, k - 1);

        let i = graph.reinsert_vertices(1);

        if let Some(r) = &mut r {
            r.extend(i.map(|v| v.name));
        }

        r
    } else if let Some(vertex) = graph.get_vertex_with_max_degree_greater_equal(2) {
        graph.remove_vertex(vertex);

        match reduction(graph, k - 1) {
            Some(mut s) => {
                let i = graph.reinsert_vertices(1);
                s.extend(i.map(|v| v.name));
                Some(s)
            }
            None => {
                let _ = graph.reinsert_vertices(1);
                let num_neighbors = graph.get_edges(vertex).count();

                if num_neighbors <= k as usize {
                    let mut c = 0;
                    while let Some(e) = graph.get_edge(vertex) {
                        graph.remove_vertex(e);
                        c += 1;
                    }
                    let mut r = reduction(graph, k - c);

                    let i = graph.reinsert_vertices(c as usize);
                    if let Some(r) = &mut r {
                        r.extend(i.map(|v| v.name));
                    }
                    r
                } else {
                    None
                }
            }
        }
    } else {
        unreachable!()
    }
}

pub fn reduction2(graph: &mut Graph, k: u16) -> Option<Vec<u16>> {
    if let Some(vertex) = graph.get_vertex_with_degree(0) {
        graph.remove_vertex(vertex);

        let r = reduction2(graph, k);

        let _ = graph.reinsert_vertices(1);

        r
    } else if graph.is_empty() {
        Some(Vec::new())
    } else if k == 0 {
        if graph.is_empty() {
            Some(Vec::new())
        } else {
            None
        }
    } else if let Some(vertex) = graph.get_vertex_with_degree(1) {
        let neighbor = graph.get_edge(vertex).unwrap();

        if k < 1 {
            return None;
        }

        graph.remove_vertex(neighbor);
        let mut r = reduction2(graph, k - 1);

        let i = graph.reinsert_vertices(1);
        if let Some(r) = &mut r {
            r.extend(i.map(|v| v.name));
        }

        r
    } else if let Some(vertex) = graph.get_vertex_with_degree(2) {
        let (a, b) = {
            let mut i = graph.get_edges(vertex);
            (i.next().unwrap(), i.next().unwrap())
        };

        if graph.get_edges(a).any(|n| n == b) {
            if k < 2 {
                return None;
            }
            graph.remove_vertex(a);
            graph.remove_vertex(b);

            let mut r = reduction2(graph, k - 2);

            let i = graph.reinsert_vertices(2);
            if let Some(r) = &mut r {
                r.extend(i.map(|v| v.name));
            }

            r
        } else if graph.get_degree(a) == 2
            && graph.get_degree(b) == 2
            && graph.get_edges(a).find(|&v| v != vertex).unwrap()
                == graph.get_edges(b).find(|&v| v != vertex).unwrap()
        {
            if k < 2 {
                return None;
            }
            let w = graph.get_edges(a).find(|&v| v != vertex).unwrap();

            graph.remove_vertex(vertex);
            graph.remove_vertex(w);

            let mut r = reduction2(graph, k - 2);

            let i = graph.reinsert_vertices(2);
            if let Some(r) = &mut r {
                r.extend(i.map(|v| v.name));
            }

            r
        } else {
            if k < 2 {
                return None;
            }
            graph.remove_vertex(a);
            graph.remove_vertex(b);

            match reduction2(graph, k - 2) {
                Some(mut s) => {
                    let i = graph.reinsert_vertices(2);
                    s.extend(i.map(|v| v.name));
                    Some(s)
                }
                None => {
                    let _ = graph.reinsert_vertices(2);

                    let mut c = 0;
                    while let Some(e) = graph.get_edge(a) {
                        graph.remove_vertex(e);
                        c += 1;
                    }
                    while let Some(e) = graph.get_edge(b) {
                        graph.remove_vertex(e);
                        c += 1;
                    }

                    if c <= k {
                        let mut r = reduction2(graph, k - c);

                        let i = graph.reinsert_vertices(c as usize);
                        if let Some(r) = &mut r {
                            r.extend(i.map(|v| v.name));
                        }
                        r
                    } else {
                        let _ = graph.reinsert_vertices(c as usize);
                        None
                    }
                }
            }
        }
    } else if let Some(vertex) = graph.get_vertex_with_max_degree_greater_equal(3) {
        if k < 1 {
            return None;
        }

        graph.remove_vertex(vertex);

        match reduction2(graph, k - 1) {
            Some(mut s) => {
                let i = graph.reinsert_vertices(1);
                s.extend(i.map(|v| v.name));
                Some(s)
            }
            None => {
                let _ = graph.reinsert_vertices(1);
                let num_neighbors = graph.get_edges(vertex).count();

                if num_neighbors <= k as usize {
                    let mut c = 0;
                    while let Some(e) = graph.get_edge(vertex) {
                        graph.remove_vertex(e);
                        c += 1;
                    }
                    let mut r = reduction2(graph, k - c);

                    let i = graph.reinsert_vertices(c as usize);
                    if let Some(r) = &mut r {
                        r.extend(i.map(|v| v.name));
                    }
                    r
                } else {
                    None
                }
            }
        }
    } else {
        unreachable!("{k}")
    }
}
