def dfs_edges(node, type_): ''' Iterative DFS, based on http://www.ics.uci.edu/~eppstein/PADS/DFS.py Returns forward and reverse edges. Also returns root node in correct order for pre- (FORWARD) and post- (BACKWARD) ordering. ``type_`` selects which values are iterated over. Children which are not of this type are flagged with LEAF. ''' while isinstance(node, type_): try: stack = [(node, iter(node), ROOT)] yield node, node, FORWARD | ROOT visited = set() visited = fallback_add(visited, node) while stack: parent, children, p_type = stack[-1] try: child = next(children) if isinstance(child, type_): if safe_in(child, visited, False): yield parent, child, NON_TREE else: stack.append((child, iter(child), NODE)) yield parent, child, FORWARD | NODE visited = fallback_add(visited, child) else: stack.append((child, empty(), LEAF)) yield parent, child, FORWARD | LEAF except StopIteration: stack.pop() if stack: yield stack[-1][0], parent, BACKWARD | p_type yield node, node, BACKWARD | ROOT return except Reset: yield # in response to the throw (ignored by caller)
def loops(node, type_): ''' Return all loops from the given node. Each loop is a list that starts and ends with the given node. ''' stack = [[node]] known = {node} # avoid getting lost in sub-loops while stack: ancestors = stack.pop() parent = ancestors[-1] if isinstance(parent, type_): for child in parent: family = list(ancestors) family.append(child) if child is node: yield family else: if not safe_in(child, known): stack.append(family) known = fallback_add(known, child)