static
void getBackwardReach(const NGHolder &g, ReportID report, u32 lag,
                      map<s32, CharReach> &look) {
    ue2::flat_set<NFAVertex> curr, next;

    for (auto v : inv_adjacent_vertices_range(g.accept, g)) {
        if (contains(g[v].reports, report)) {
            curr.insert(v);
        }
    }

    for (u32 i = lag + 1; i <= MAX_BACK_LEN; i++) {
        if (curr.empty() || contains(curr, g.start) ||
            contains(curr, g.startDs)) {
            break;
        }

        next.clear();
        CharReach cr;

        for (auto v : curr) {
            assert(!is_special(v, g));
            cr |= g[v].char_reach;
            insert(&next, inv_adjacent_vertices(v, g));
        }

        assert(cr.any());
        look[0 - i] |= cr;
        curr.swap(next);
    }
}
static
bool couldEndLiteral(const ue2_literal &s, NFAVertex initial,
                     const NGHolder &h) {
    ue2::flat_set<NFAVertex> curr, next;
    curr.insert(initial);

    for (auto it = s.rbegin(), ite = s.rend(); it != ite; ++it) {
        const CharReach &cr_s = *it;
        bool matched = false;
        next.clear();

        for (auto v : curr) {
            if (v == h.start) {
                // We can't see what we had before the start, so we must assume
                // the literal could overlap with it.
                return true;
            }
            const CharReach &cr_v = h[v].char_reach;
            if (overlaps(cr_v, cr_s)) {
                insert(&next, inv_adjacent_vertices(v, h));
                matched = true;
            }
        }

        if (!matched) {
            return false;
        }

        curr.swap(next);
    }

    return true;
}
static
bool getTransientPrefixReach(const NGHolder &g, u32 lag,
                             map<s32, CharReach> &look) {
    if (in_degree(g.accept, g) != 1) {
        DEBUG_PRINTF("more than one accept\n");
        return false;
    }

    // Must be a floating chain wired to startDs.
    if (!hasSingleFloatingStart(g)) {
        DEBUG_PRINTF("not a single floating start\n");
        return false;
    }

    NFAVertex v = *(inv_adjacent_vertices(g.accept, g).first);
    u32 i = lag + 1;
    while (v != g.startDs) {
        DEBUG_PRINTF("i=%u, v=%u\n", i, g[v].index);
        if (is_special(v, g)) {
            DEBUG_PRINTF("special\n");
            return false;
        }

        look[0 - i] = g[v].char_reach;

        NFAVertex next = NGHolder::null_vertex();
        for (auto u : inv_adjacent_vertices_range(v, g)) {
            if (u == g.start) {
                continue; // Benign, checked by hasSingleFloatingStart
            }
            if (next == NGHolder::null_vertex()) {
                next = u;
                continue;
            }
            DEBUG_PRINTF("branch\n");
            return false;
        }

        if (next == NGHolder::null_vertex() || next == v) {
            DEBUG_PRINTF("no predecessor or only self-loop\n");
            // This graph is malformed -- all vertices in a graph that makes it
            // to this analysis should have predecessors.
            assert(0);
            return false;
        }

        v = next;
        i++;
    }

    DEBUG_PRINTF("done\n");
    return true;
}
    static void find_connected(TaskSystemType& task_system,
                            const typename TaskSystemType::ClusterIdType& curr_clust_id,
                            std::list<typename TaskSystemType::ClusterIdType>& connected_comps, int nr_of_connected) {

        typedef typename TaskSystemType::GraphType GraphType;
        typedef typename TaskSystemType::ClusterType ClusterType;
        typedef typename TaskSystemType::ClusterIdType ClusterIdType;
        typedef typename TaskSystemType::inv_adjacency_iterator inv_adjacency_iterator;
        typedef typename TaskSystemType::adjacency_iterator adjacency_iterator;

        GraphType& sys_graph = task_system.sys_graph;

        ClusterType& curr_clust = sys_graph[curr_clust_id];
        if(!curr_clust.is_valid())
            return;

        inv_adjacency_iterator parent_iter, parent_end;
        boost::tie(parent_iter, parent_end) = inv_adjacent_vertices(curr_clust_id, sys_graph);

        for ( ; parent_iter != parent_end; ++parent_iter) {
            const ClusterIdType& curr_parent_id = *parent_iter;
            // ClusterType& curr_parent = sys_graph[curr_parent_id];

            find_connected(task_system, curr_parent_id, connected_comps, nr_of_connected);
        }


        if(curr_clust.group != 0)
            std::cout << "Already visited node " << curr_clust.index_list << std::endl;
        else {
            connected_comps.push_back(curr_clust_id);
            curr_clust.group = nr_of_connected;
            curr_clust.valid = false;
        }

        adjacency_iterator child_iter, child_end;
        boost::tie(child_iter, child_end) = adjacent_vertices(curr_clust_id, sys_graph);

        for ( ; child_iter != child_end; ++child_iter) {
            const ClusterIdType& curr_child_id = *child_iter;
            // ClusterType& curr_child = sys_graph[curr_child_id];

            find_connected(task_system, curr_child_id, connected_comps, nr_of_connected);
        }

    }
    static void apply(TaskSystemType& task_system) {

        typedef typename TaskSystemType::GraphType GraphType;
        typedef typename TaskSystemType::ClusterLevels ClusterLevels;
        typedef typename TaskSystemType::ClusterType ClusterType;
        typedef typename TaskSystemType::ClusterIdType ClusterIdType;
        typedef typename TaskSystemType::inv_adjacency_iterator inv_adjacency_iterator;
        typedef typename ClusterLevels::value_type SameLevelClusterIdsType;

        GraphType& sys_graph = task_system.sys_graph;
        ClusterLevels& clusters_by_level = task_system.clusters_by_level;


        if(task_system.levels_valid == false)
            task_system.update_node_levels();


        typename ClusterLevels::iterator level_iter = clusters_by_level.begin();
        /*! Skip the first level. Which contains only the root node and some invlaidated clusters.*/
        ++level_iter;
        /*! Skip the second level as well. All nodes here have root as parent.*/
        ++level_iter;

        int level_number = 2;
        for( ;level_iter != clusters_by_level.end(); ++level_iter, ++level_number) {
            SameLevelClusterIdsType& current_level = *level_iter;

            typename SameLevelClusterIdsType::iterator clustid_iter = current_level.begin();
            for( ;clustid_iter != current_level.end(); ++clustid_iter) {
                ClusterIdType& curr_clust_id = *clustid_iter;
                ClusterType& curr_clust = sys_graph[curr_clust_id];

                if(!curr_clust.is_valid()) {
                    continue;
                }

                if(in_degree(curr_clust_id, sys_graph) == 1) {
                    continue;
                }

                std::vector<ClusterIdType> parent_ids;
                inv_adjacency_iterator parent_iter, parent_end;
                boost::tie(parent_iter, parent_end) = inv_adjacent_vertices(curr_clust_id, sys_graph);
                const ClusterIdType& main_parent_id = *parent_iter;
                ClusterType& main_parent = sys_graph[main_parent_id];

                /*! start from the second parent.*/
                parent_iter++;
                for ( ; parent_iter != parent_end; ++parent_iter) {
                    const ClusterIdType& other_parent_id = *parent_iter;
                    ClusterType& other_parent = sys_graph[other_parent_id];
                    /*! Don't merge different level parents for now.*/
                    if(other_parent.level == main_parent.level)
                        parent_ids.push_back(*parent_iter);
                }

                typename std::vector<ClusterIdType>::iterator id_iter;
                for(id_iter = parent_ids.begin(); id_iter != parent_ids.end(); ++id_iter) {
                    task_system.concat_same_level_clusters(main_parent_id, *id_iter);
                }




            }

        }


        task_system.levels_valid = false;

    }
    static void apply(TaskSystemType& task_system,
                        const typename TaskSystemType::ClusterIdType& dest_id,
                        const typename TaskSystemType::ClusterIdType& src_id) {

        typedef typename TaskSystemType::GraphType GraphType;
        typedef typename TaskSystemType::ClusterType ClusterType;
        typedef typename TaskSystemType::adjacency_iterator adjacency_iterator;
        typedef typename TaskSystemType::inv_adjacency_iterator inv_adjacency_iterator;

        GraphType& sys_graph = task_system.sys_graph;

        if(dest_id == src_id)
            return;

        ClusterType& dest = sys_graph[dest_id];
        ClusterType& src = sys_graph[src_id];

        if(dest.level > src.level) {
                std::cout << "trying to add edge : " << dest.level << " -> " << src.level << std::endl;
        }

        // std::cout << "Trying merege "  << dest.index_list << " and " << src.index_list << std::endl;

        typename ClusterType::iterator task_iter;
        for(task_iter = src.begin(); task_iter != src.end(); ++task_iter) {
            dest.add_task(*task_iter);
        }

        adjacency_iterator src_child_iter, src_child_end, curr_src_child_iter;
        boost::tie(src_child_iter, src_child_end) = adjacent_vertices(src_id, sys_graph);
        while (src_child_iter != src_child_end) {

			/*! Increment before erase. Apparently erasing an edge invalidates the vertex iterators in VS.
			  something is going on inside boost that I don't know yet. Or VS is just being VS as ususal.
			  (the edge container is in fact a set, but that should matter only if we iterate over ages.
			  well apparently not :) )*/
			curr_src_child_iter = src_child_iter;
			++src_child_iter;

            if(dest_id != *curr_src_child_iter) {
                boost::add_edge(dest_id, *curr_src_child_iter, sys_graph);
            }
            else {
                std::cout << "trying to add edge : " << sys_graph[dest_id].index_list << " -> " << sys_graph[*curr_src_child_iter].index_list << std::endl;
            }
            boost::remove_edge(src_id, *curr_src_child_iter, sys_graph);

        }

        inv_adjacency_iterator src_parent_iter, src_parent_end, curr_src_parent_iter;
        boost::tie(src_parent_iter, src_parent_end) = inv_adjacent_vertices(src_id, sys_graph);
        while (src_parent_iter != src_parent_end) {

			/*! Increment before erase. Apparently erasing an edge invalidates the vertex iterators in VS.
			  something is going on inside boost that I don't know yet. Or VS is just being VS as ususal.
			  (the edge container is in fact a set, but that should matter only if we iterate over ages.
			  well apparently not :) )*/
			curr_src_parent_iter = src_parent_iter;
			++src_parent_iter;

            if(*curr_src_parent_iter != dest_id) {
                boost::add_edge(*curr_src_parent_iter, dest_id, sys_graph);
            }
            else {
                std::cout << "trying to add edge : " << sys_graph[*curr_src_parent_iter].index_list << " -> " << sys_graph[dest_id].index_list << std::endl;
            }
            boost::remove_edge(*curr_src_parent_iter, src_id, sys_graph);
        }

		// boost::clear_vertex(src_id, sys_graph);
        boost::remove_vertex(src_id, sys_graph);

    }