A helper function for determining the condensed
            representation of the tree. That is, one that does not hold
            all of the internal nodes of the graph. The results will be
            stored in ContourTree.superNodes and ContourTree.superArcs.
            These two can be used to potentially speed up queries by
            limiting the searching on the graph to only nodes on these
            super arcs.

def _identifySuperGraph(self):
        """ A helper function for determining the condensed
            representation of the tree. That is, one that does not hold
            all of the internal nodes of the graph. The results will be
            stored in ContourTree.superNodes and ContourTree.superArcs.
            These two can be used to potentially speed up queries by
            limiting the searching on the graph to only nodes on these
            super arcs.
        """

        if self.debug:
            sys.stdout.write("Condensing Graph: ")
            start = time.clock()

        G = nx.DiGraph()
        G.add_edges_from(self.edges)

        if self.short_circuit:
            self.superNodes = G.nodes()
            self.superArcs = G.edges()
            # There should be a way to populate this from the data we
            # have...
            return

        self.augmentedEdges = {}
        N = len(self.Y)
        processed = np.zeros(N)
        for node in range(N):

            # We can short circuit this here, since some of the nodes
            # will be handled within the while loops below.
            if processed[node]:
                continue

            # Loop through each internal node (see if below for
            # determining what is internal), trace up and down to a
            # node's first non-internal node in either direction
            # removing all of the internal nodes and pushing them into a
            # list. This list (removedNodes) will be put into a
            # dictionary keyed on the endpoints of the final super arc.
            if G.in_degree(node) == 1 and G.out_degree(node) == 1:
                # The sorted list of nodes that will be condensed by
                # this super arc
                removedNodes = []

                # Trace down to a non-internal node

                lower_link = list(G.in_edges(node))[0][0]
                while (
                    G.in_degree(lower_link) == 1
                    and G.out_degree(lower_link) == 1
                ):
                    new_lower_link = list(G.in_edges(lower_link))[0][0]
                    G.add_edge(new_lower_link, node)
                    G.remove_node(lower_link)
                    removedNodes.append(lower_link)
                    lower_link = new_lower_link

                removedNodes.reverse()
                removedNodes.append(node)

                # Trace up to a non-internal node
                upper_link = list(G.out_edges(node))[0][1]
                while (
                    G.in_degree(upper_link) == 1
                    and G.out_degree(upper_link) == 1
                ):
                    new_upper_link = list(G.out_edges(upper_link))[0][1]
                    G.add_edge(node, new_upper_link)
                    G.remove_node(upper_link)
                    removedNodes.append(upper_link)
                    upper_link = new_upper_link

                G.add_edge(lower_link, upper_link)
                G.remove_node(node)

                self.augmentedEdges[(lower_link, upper_link)] = removedNodes

                # This is to help speed up the process by skipping nodes
                # we have already condensed, and to prevent us from not
                # being able to find nodes that have already been
                # removed.
                processed[removedNodes] = 1

        self.superNodes = G.nodes()
        self.superArcs = G.edges()

        if self.debug:
            end = time.clock()
            sys.stdout.write("%f s\n" % (end - start))