Depth first search adapted for identification of cut-edges and cut-nodes.
    
    @type  graph: graph, digraph
    @param graph: Graph
    
    @type  spanning_tree: dictionary
    @param spanning_tree: Spanning tree being built for the graph by DFS.

    @type  pre: dictionary
    @param pre: Graph's preordering.
    
    @type  low: dictionary
    @param low: Associates to each node, the preordering index of the node of lowest preordering
    accessible from the given node.

    @type  reply: list
    @param reply: List of cut-edges.
    
    @type  node: node
    @param node: Node to be explored by DFS.

def _cut_dfs(graph, spanning_tree, pre, low, reply, node):
    """
    Depth first search adapted for identification of cut-edges and cut-nodes.
    
    @type  graph: graph, digraph
    @param graph: Graph
    
    @type  spanning_tree: dictionary
    @param spanning_tree: Spanning tree being built for the graph by DFS.

    @type  pre: dictionary
    @param pre: Graph's preordering.
    
    @type  low: dictionary
    @param low: Associates to each node, the preordering index of the node of lowest preordering
    accessible from the given node.

    @type  reply: list
    @param reply: List of cut-edges.
    
    @type  node: node
    @param node: Node to be explored by DFS.
    """
    pre[node] = pre[None]
    low[node] = pre[None]
    pre[None] = pre[None] + 1
    
    for each in graph[node]:
        if (each not in pre):
            spanning_tree[each] = node
            _cut_dfs(graph, spanning_tree, pre, low, reply, each)
            if (low[node] > low[each]):
                low[node] = low[each]
            if (low[each] == pre[each]):
                reply.append((node, each))
        elif (low[node] > pre[each] and spanning_tree[node] != each):
            low[node] = pre[each]