use crate::fx::FxHashSet;
use crate::graph::{DirectedGraph, WithNumNodes, WithNumEdges, WithSuccessors, GraphSuccessors};
use crate::graph::vec_graph::VecGraph;
use crate::indexed_vec::{Idx, IndexVec};
use std::ops::Range;
#[cfg(test)]
mod tests;
pub struct Sccs<N: Idx, S: Idx> {
    
    
    scc_indices: IndexVec<N, S>,
    
    scc_data: SccData<S>,
}
struct SccData<S: Idx> {
    
    
    ranges: IndexVec<S, Range<usize>>,
    
    
    
    all_successors: Vec<S>,
}
impl<N: Idx, S: Idx> Sccs<N, S> {
    pub fn new(graph: &(impl DirectedGraph<Node = N> + WithNumNodes + WithSuccessors)) -> Self {
        SccsConstruction::construct(graph)
    }
    
    pub fn num_sccs(&self) -> usize {
        self.scc_data.len()
    }
    
    pub fn all_sccs(&self) -> impl Iterator<Item = S> {
        (0 .. self.scc_data.len()).map(S::new)
    }
    
    pub fn scc(&self, r: N) -> S {
        self.scc_indices[r]
    }
    
    pub fn successors(&self, scc: S) -> &[S] {
        self.scc_data.successors(scc)
    }
    
    pub fn reverse(&self) -> VecGraph<S> {
        VecGraph::new(
            self.num_sccs(),
            self.all_sccs()
                .flat_map(|source| self.successors(source).iter().map(move |&target| {
                    (target, source)
                }))
                .collect(),
        )
    }
}
impl<N: Idx, S: Idx> DirectedGraph for Sccs<N, S> {
    type Node = S;
}
impl<N: Idx, S: Idx> WithNumNodes for Sccs<N, S> {
    fn num_nodes(&self) -> usize {
        self.num_sccs()
    }
}
impl<N: Idx, S: Idx> WithNumEdges for Sccs<N, S> {
    fn num_edges(&self) -> usize {
        self.scc_data.all_successors.len()
    }
}
impl<N: Idx, S: Idx> GraphSuccessors<'graph> for Sccs<N, S> {
    type Item = S;
    type Iter = std::iter::Cloned<std::slice::Iter<'graph, S>>;
}
impl<N: Idx, S: Idx> WithSuccessors for Sccs<N, S> {
    fn successors<'graph>(
        &'graph self,
        node: S
    ) -> <Self as GraphSuccessors<'graph>>::Iter {
        self.successors(node).iter().cloned()
    }
}
impl<S: Idx> SccData<S> {
    
    fn len(&self) -> usize {
        self.ranges.len()
    }
    
    fn successors(&self, scc: S) -> &[S] {
        
        
        let range = &self.ranges[scc];
        &self.all_successors[range.start..range.end]
    }
    
    
    fn create_scc(&mut self, successors: impl IntoIterator<Item = S>) -> S {
        
        
        let all_successors_start = self.all_successors.len();
        self.all_successors.extend(successors);
        let all_successors_end = self.all_successors.len();
        debug!(
            "create_scc({:?}) successors={:?}",
            self.ranges.len(),
            &self.all_successors[all_successors_start..all_successors_end],
        );
        self.ranges.push(all_successors_start..all_successors_end)
    }
}
struct SccsConstruction<'c, G: DirectedGraph + WithNumNodes + WithSuccessors, S: Idx> {
    graph: &'c G,
    
    
    
    node_states: IndexVec<G::Node, NodeState<G::Node, S>>,
    
    node_stack: Vec<G::Node>,
    
    
    
    
    successors_stack: Vec<S>,
    
    
    
    
    duplicate_set: FxHashSet<S>,
    scc_data: SccData<S>,
}
#[derive(Copy, Clone, Debug)]
enum NodeState<N, S> {
    
    
    
    
    NotVisited,
    
    
    
    
    
    BeingVisited { depth: usize },
    
    InCycle { scc_index: S },
    
    
    
    
    
    InCycleWith { parent: N },
}
#[derive(Copy, Clone, Debug)]
enum WalkReturn<S> {
    Cycle { min_depth: usize },
    Complete { scc_index: S },
}
impl<'c, G, S> SccsConstruction<'c, G, S>
where
    G: DirectedGraph + WithNumNodes + WithSuccessors,
    S: Idx,
{
    
    
    
    
    
    
    
    
    
    
    
    
    fn construct(graph: &'c G) -> Sccs<G::Node, S> {
        let num_nodes = graph.num_nodes();
        let mut this = Self {
            graph,
            node_states: IndexVec::from_elem_n(NodeState::NotVisited, num_nodes),
            node_stack: Vec::with_capacity(num_nodes),
            successors_stack: Vec::new(),
            scc_data: SccData {
                ranges: IndexVec::new(),
                all_successors: Vec::new(),
            },
            duplicate_set: FxHashSet::default(),
        };
        let scc_indices = (0..num_nodes)
            .map(G::Node::new)
            .map(|node| match this.walk_node(0, node) {
                WalkReturn::Complete { scc_index } => scc_index,
                WalkReturn::Cycle { min_depth } => panic!(
                    "`walk_node(0, {:?})` returned cycle with depth {:?}",
                    node, min_depth
                ),
            })
            .collect();
        Sccs {
            scc_indices,
            scc_data: this.scc_data,
        }
    }
    
    
    
    
    
    
    
    
    
    
    
    fn walk_node(&mut self, depth: usize, node: G::Node) -> WalkReturn<S> {
        debug!("walk_node(depth = {:?}, node = {:?})", depth, node);
        match self.find_state(node) {
            NodeState::InCycle { scc_index } => WalkReturn::Complete { scc_index },
            NodeState::BeingVisited { depth: min_depth } => WalkReturn::Cycle { min_depth },
            NodeState::NotVisited => self.walk_unvisited_node(depth, node),
            NodeState::InCycleWith { parent } => panic!(
                "`find_state` returned `InCycleWith({:?})`, which ought to be impossible",
                parent
            ),
        }
    }
    
    
    
    
    
    fn find_state(&mut self, r: G::Node) -> NodeState<G::Node, S> {
        debug!("find_state(r = {:?} in state {:?})", r, self.node_states[r]);
        match self.node_states[r] {
            NodeState::InCycle { scc_index } => NodeState::InCycle { scc_index },
            NodeState::BeingVisited { depth } => NodeState::BeingVisited { depth },
            NodeState::NotVisited => NodeState::NotVisited,
            NodeState::InCycleWith { parent } => {
                let parent_state = self.find_state(parent);
                debug!("find_state: parent_state = {:?}", parent_state);
                match parent_state {
                    NodeState::InCycle { .. } => {
                        self.node_states[r] = parent_state;
                        parent_state
                    }
                    NodeState::BeingVisited { depth } => {
                        self.node_states[r] = NodeState::InCycleWith {
                            parent: self.node_stack[depth],
                        };
                        parent_state
                    }
                    NodeState::NotVisited | NodeState::InCycleWith { .. } => {
                        panic!("invalid parent state: {:?}", parent_state)
                    }
                }
            }
        }
    }
    
    fn walk_unvisited_node(&mut self, depth: usize, node: G::Node) -> WalkReturn<S> {
        debug!(
            "walk_unvisited_node(depth = {:?}, node = {:?})",
            depth, node
        );
        debug_assert!(match self.node_states[node] {
            NodeState::NotVisited => true,
            _ => false,
        });
        
        self.node_states[node] = NodeState::BeingVisited { depth };
        self.node_stack.push(node);
        
        
        
        let mut min_depth = depth;
        let mut min_cycle_root = node;
        let successors_len = self.successors_stack.len();
        for successor_node in self.graph.successors(node) {
            debug!(
                "walk_unvisited_node: node = {:?} successor_ode = {:?}",
                node, successor_node
            );
            match self.walk_node(depth + 1, successor_node) {
                WalkReturn::Cycle {
                    min_depth: successor_min_depth,
                } => {
                    
                    assert!(successor_min_depth <= depth);
                    if successor_min_depth < min_depth {
                        debug!(
                            "walk_unvisited_node: node = {:?} successor_min_depth = {:?}",
                            node, successor_min_depth
                        );
                        min_depth = successor_min_depth;
                        min_cycle_root = successor_node;
                    }
                }
                WalkReturn::Complete {
                    scc_index: successor_scc_index,
                } => {
                    
                    
                    debug!(
                        "walk_unvisited_node: node = {:?} successor_scc_index = {:?}",
                        node, successor_scc_index
                    );
                    self.successors_stack.push(successor_scc_index);
                }
            }
        }
        
        let r = self.node_stack.pop();
        debug_assert_eq!(r, Some(node));
        
        
        if min_depth == depth {
            
            
            let deduplicated_successors = {
                let duplicate_set = &mut self.duplicate_set;
                duplicate_set.clear();
                self.successors_stack
                    .drain(successors_len..)
                    .filter(move |&i| duplicate_set.insert(i))
            };
            let scc_index = self.scc_data.create_scc(deduplicated_successors);
            self.node_states[node] = NodeState::InCycle { scc_index };
            WalkReturn::Complete { scc_index }
        } else {
            
            
            
            self.node_states[node] = NodeState::InCycleWith {
                parent: min_cycle_root,
            };
            WalkReturn::Cycle { min_depth }
        }
    }
}