1 # Bugs Everywhere, a distributed bugtracker
2 # Copyright (C) 2008-2009 Gianluca Montecchi <gian@grys.it>
3 # W. Trevor King <wking@drexel.edu>
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 2 of the License, or
8 # (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License along
16 # with this program; if not, write to the Free Software Foundation, Inc.,
17 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 Define a traversable tree structure.
24 if libbe.TESTING == True:
34 >>> i = Tree(); i.n = "i"
35 >>> h = Tree([i]); h.n = "h"
36 >>> f = Tree([h]); f.n = "f"
37 >>> e = Tree(); e.n = "e"
38 >>> c = Tree([f,e]); c.n = "c"
39 >>> g = Tree(); g.n = "g"
40 >>> d = Tree([g]); d.n = "d"
41 >>> b = Tree([d]); b.n = "b"
42 >>> a = Tree(); a.n = "a"
48 >>> a.sort(key=lambda node : -node.branch_len())
49 >>> "".join([node.n for node in a.traverse()])
51 >>> a.sort(key=lambda node : node.branch_len())
52 >>> "".join([node.n for node in a.traverse()])
54 >>> "".join([node.n for node in a.traverse(depth_first=False)])
56 >>> for depth,node in a.thread():
57 ... print "%*s" % (2*depth+1, node.n)
67 >>> for depth,node in a.thread(flatten=True):
68 ... print "%*s" % (2*depth+1, node.n)
78 >>> a.has_descendant(g)
80 >>> c.has_descendant(g)
82 >>> a.has_descendant(a)
84 >>> a.has_descendant(a, match_self=True)
87 def __cmp__(self, other):
88 return cmp(id(self), id(other))
90 def __eq__(self, other):
91 return self.__cmp__(other) == 0
93 def __ne__(self, other):
94 return self.__cmp__(other) != 0
98 Exhaustive search every time == SLOW.
100 Use only on small trees, or reimplement by overriding
101 child-addition methods to allow accurate caching.
107 this method returns 5.
112 return 1 + max([child.branch_len() for child in self])
114 def sort(self, *args, **kwargs):
116 This method can be slow, e.g. on a branch_len() sort, since a
117 node at depth N from the root has it's branch_len() method
120 list.sort(self, *args, **kwargs)
122 child.sort(*args, **kwargs)
124 def traverse(self, depth_first=True):
126 Note: you might want to sort() your tree first.
128 if depth_first == True:
131 for descendant in child.traverse():
133 else: # breadth first, Wikipedia algorithm
134 # http://en.wikipedia.org/wiki/Breadth-first_search
136 while len(queue) > 0:
141 def thread(self, flatten=False):
143 When flatten==False, the depth of any node is one greater than
144 the depth of its parent. That way the inheritance is
145 explicit, but you can end up with highly indented threads.
147 When flatten==True, the depth of any node is only greater than
148 the depth of its parent when there is a branch, and the node
149 is not the last child. This can lead to ancestry ambiguity,
150 but keeps the total indentation down. E.g.
154 would both produce (after sorting by branch_len())
162 stack = [] # ancestry of the current node
166 for node in self.traverse(depth_first=True):
167 while len(stack) > 0 \
168 and id(node) not in [id(c) for c in stack[-1]]:
177 depth = depthDict[id(parent)]
178 if len(parent) > 1 and node != parent[-1]:
180 depthDict[id(node)] = depth
184 def has_descendant(self, descendant, depth_first=True, match_self=False):
185 if descendant == self:
187 for d in self.traverse(depth_first):
192 if libbe.TESTING == True:
193 suite = doctest.DocTestSuite()