Sort a given set of nodes from a graph based on the following rules:

        # - if A -> B and not B -> A, then we have A < B
        # - if A -> B and B -> A, then the ordering is undefined

        Following the above rules gives us a quasi-topological sorting of nodes in the graph. It also works for cyclic
        graphs.

        :param networkx.DiGraph graph: A local transition graph of the function.
        :param iterable nodes: A list of nodes to sort. None if you want to sort all nodes inside the graph.
        :return: A list of ordered nodes.
        :rtype: list

def quasi_topological_sort_nodes(graph, nodes=None):
        """
        Sort a given set of nodes from a graph based on the following rules:

        # - if A -> B and not B -> A, then we have A < B
        # - if A -> B and B -> A, then the ordering is undefined

        Following the above rules gives us a quasi-topological sorting of nodes in the graph. It also works for cyclic
        graphs.

        :param networkx.DiGraph graph: A local transition graph of the function.
        :param iterable nodes: A list of nodes to sort. None if you want to sort all nodes inside the graph.
        :return: A list of ordered nodes.
        :rtype: list
        """

        # fast path for single node graphs
        if graph.number_of_nodes() == 1:
            return graph.nodes()

        # make a copy to the graph since we are gonna modify it
        graph_copy = networkx.DiGraph()

        # find all strongly connected components in the graph
        sccs = [ scc for scc in networkx.strongly_connected_components(graph) if len(scc) > 1 ]

        # collapse all strongly connected components
        for src, dst in graph.edges():
            scc_index = CFGUtils._components_index_node(sccs, src)
            if scc_index is not None:
                src = SCCPlaceholder(scc_index)
            scc_index = CFGUtils._components_index_node(sccs, dst)
            if scc_index is not None:
                dst = SCCPlaceholder(scc_index)

            if isinstance(src, SCCPlaceholder) and isinstance(dst, SCCPlaceholder) and src == dst:
                continue
            if src == dst:
                continue

            graph_copy.add_edge(src, dst)

        # add loners
        out_degree_zero_nodes = [node for (node, degree) in graph.out_degree() if degree == 0]
        for node in out_degree_zero_nodes:
            if graph.in_degree(node) == 0:
                graph_copy.add_node(node)

        # topological sort on acyclic graph `graph_copy`
        tmp_nodes = networkx.topological_sort(graph_copy)

        ordered_nodes = [ ]
        for n in tmp_nodes:
            if isinstance(n, SCCPlaceholder):
                CFGUtils._append_scc(graph, ordered_nodes, sccs[n.scc_id])
            else:
                ordered_nodes.append(n)

        if nodes is None:
            return ordered_nodes

        nodes = set(nodes)
        ordered_nodes = [ n for n in ordered_nodes if n in nodes ]
        return ordered_nodes