use hibitset::BitSet;
use std::{
    collections::{BinaryHeap, HashMap, HashSet, VecDeque},
    convert::TryInto,
    fmt::Debug,
    hash::Hash,
};
pub fn topological_sort<InIndex, OutIndex, F, OrderIndicator, FOrd, Set>(
    index_set: Set, predecessor_edges: F, order_indicator: FOrd,
) -> Vec<OutIndex>
where
    InIndex: Copy + Hash + Eq + PartialEq + Ord + TryInto<OutIndex>,
    <InIndex as TryInto<OutIndex>>::Error: Debug,
    F: Fn(InIndex) -> Vec<InIndex>,
    OrderIndicator: Ord,
    FOrd: Fn(InIndex) -> OrderIndicator,
    Set: SetLike<InIndex> + Default + Clone + IntoIterator<Item = InIndex>,
{
    let mut num_next_edges = HashMap::new();
    for me in index_set.clone() {
        num_next_edges.entry(me).or_insert(0);
        for prev in predecessor_edges(me) {
            if index_set.contains(&prev) {
                *num_next_edges.entry(prev).or_insert(0) += 1;
            }
        }
    }
    let mut candidates = BinaryHeap::new();
    let mut reversed_indices = Vec::new();
    for me in index_set.clone() {
        if num_next_edges[&me] == 0 {
            candidates.push((order_indicator(me), me));
        }
    }
    while let Some((_, me)) = candidates.pop() {
        reversed_indices.push(me.try_into().expect("index in range"));
        for prev in predecessor_edges(me) {
            if index_set.contains(&prev) {
                num_next_edges.entry(prev).and_modify(|e| *e -= 1);
                if num_next_edges[&prev] == 0 {
                    candidates.push((order_indicator(prev), prev));
                }
            }
        }
    }
    reversed_indices.reverse();
    reversed_indices
}
pub fn get_future<'a, InIndex, OutIndex, F, FStop, Set, Iter>(
    index_set: Iter, successor_edges: F, stop_condition: FStop,
) -> Set
where
    InIndex: 'a + Copy + TryInto<OutIndex>,
    <InIndex as TryInto<OutIndex>>::Error: Debug,
    OutIndex: 'a + Copy + Hash + Eq + PartialEq + Ord,
    F: Fn(InIndex) -> Vec<InIndex>,
    FStop: Fn(InIndex) -> bool,
    Set: SetLike<OutIndex> + Default,
    Iter: IntoIterator<Item = InIndex>,
{
    let mut queue = VecDeque::new();
    let mut visited = Set::default();
    for i in index_set {
        visited.insert(i.try_into().expect("index in range"));
        queue.push_back(i);
    }
    while let Some(x) = queue.pop_front() {
        for succ in successor_edges(x) {
            if stop_condition(succ) {
                continue;
            }
            let out_index = succ.try_into().expect("index in range");
            if !visited.contains(&out_index) {
                queue.push_back(succ);
                visited.insert(out_index);
            }
        }
    }
    visited
}
pub trait Graph {
    type NodeIndex: 'static + Copy + Hash + Eq + PartialEq + Ord;
}
pub trait DAG: Graph {
    fn predecessor_edges(
        &self, node_index: Self::NodeIndex,
    ) -> Vec<Self::NodeIndex>;
    fn topological_sort_with_order_indicator<OrderIndicator, FOrd, Set>(
        &self, index_set: Set, order_indicator: FOrd,
    ) -> Vec<Self::NodeIndex>
    where
        OrderIndicator: Ord,
        FOrd: Fn(Self::NodeIndex) -> OrderIndicator,
        Set: SetLike<Self::NodeIndex>
            + Default
            + Clone
            + IntoIterator<Item = Self::NodeIndex>,
    {
        topological_sort(
            index_set,
            |i| self.predecessor_edges(i),
            order_indicator,
        )
    }
    fn topological_sort<Set>(&self, index_set: Set) -> Vec<Self::NodeIndex>
    where Set: SetLike<Self::NodeIndex>
            + Default
            + Clone
            + IntoIterator<Item = Self::NodeIndex> {
        self.topological_sort_with_order_indicator(index_set, |_| true)
    }
}
pub trait RichDAG: DAG {
    fn successor_edges(
        &self, node_index: Self::NodeIndex,
    ) -> Vec<Self::NodeIndex>;
    fn get_future_with_stop_condition<FStop, Set, Iter>(
        &self, index_set: Iter, stop_condition: FStop,
    ) -> Set
    where
        FStop: Fn(Self::NodeIndex) -> bool,
        Set: SetLike<Self::NodeIndex> + Default,
        Iter: IntoIterator<Item = Self::NodeIndex>,
    {
        get_future(index_set, |i| self.successor_edges(i), stop_condition)
    }
    fn get_future<Set, Iter>(&self, index_set: Iter) -> Set
    where
        Set: SetLike<Self::NodeIndex> + Default,
        Iter: IntoIterator<Item = Self::NodeIndex>,
    {
        self.get_future_with_stop_condition(index_set, |_| false)
    }
}
pub trait TreeGraph: Graph {
    fn parent(&self, node_index: Self::NodeIndex) -> Option<Self::NodeIndex>;
    fn referees(&self, node_index: Self::NodeIndex) -> Vec<Self::NodeIndex>;
}
pub trait RichTreeGraph: TreeGraph {
    fn children(&self, node_index: Self::NodeIndex) -> Vec<Self::NodeIndex>;
    fn referrers(&self, node_index: Self::NodeIndex) -> Vec<Self::NodeIndex>;
}
impl<T: TreeGraph> DAG for T {
    fn predecessor_edges(
        &self, node_index: Self::NodeIndex,
    ) -> Vec<Self::NodeIndex> {
        let mut predecessor_edges = self.referees(node_index);
        if let Some(p) = self.parent(node_index) {
            predecessor_edges.push(p);
        }
        predecessor_edges
    }
}
impl<T: RichTreeGraph + DAG> RichDAG for T {
    fn successor_edges(
        &self, node_index: Self::NodeIndex,
    ) -> Vec<Self::NodeIndex> {
        let mut successor_edges = self.children(node_index);
        successor_edges.append(&mut self.referrers(node_index));
        successor_edges
    }
}
pub trait SetLike<T> {
    fn insert(&mut self, i: T) -> bool;
    fn contains(&self, i: &T) -> bool;
}
impl<T: Eq + Hash> SetLike<T> for HashSet<T> {
    fn insert(&mut self, i: T) -> bool { self.insert(i) }
    fn contains(&self, i: &T) -> bool { self.contains(i) }
}
impl SetLike<u32> for BitSet {
    fn insert(&mut self, i: u32) -> bool { self.add(i) }
    fn contains(&self, i: &u32) -> bool { self.contains(*i) }
}