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)
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 = set([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) safe_add(known, child)
def __call__(self, visitor): ''' Apply the visitor to the nodes in the graph, in postorder. ''' pending = {} for (parent, node, kind) in dfs_edges(self.__root, self.__type): if kind & POSTORDER: if safe_in(node, pending): args = pending[node] del pending[node] else: args = [] if parent not in pending: pending[parent] = [] visitor.node(node) if kind & LEAF: pending[parent].append(visitor.leaf(node)) elif kind & NON_TREE: pending[parent].append(visitor.loop(node)) else: pending[parent].append(visitor.constructor(*args)) return pending[self.__root][0]
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 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() safe_add(visited, node) # cannot track loops in unhashable objects while stack: parent, children, ptype = stack[-1] try: child = next(children) if isinstance(child, type_): if safe_in(child, visited, False): yield parent, child, NONTREE else: stack.append((child, iter(child), NODE)) yield parent, child, FORWARD | NODE safe_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 | ptype yield node, node, BACKWARD | ROOT return except Reset: yield # in response to the throw (ignored by caller)