##// END OF EJS Templates
ancestor.missingancestors: turn into a state-keeping class...
Siddharth Agarwal -
r23334:59e6e5dd default
parent child Browse files
Show More
@@ -134,83 +134,95 b' def ancestors(pfunc, *orignodes):'
134 return gca
134 return gca
135 return deepest(gca)
135 return deepest(gca)
136
136
137 def missingancestors(revs, bases, pfunc):
137 class incrementalmissingancestors(object):
138 """Return all the ancestors of revs that are not ancestors of bases.
138 '''persistent state used to calculate missing ancestors incrementally
139
140 This may include elements from revs.
141
139
142 Equivalent to the revset (::revs - ::bases). Revs are returned in
140 Although similar in spirit to lazyancestors below, this is a separate class
143 revision number order, which is a topological order.
141 because trying to support contains and missingancestors operations with the
144
142 same internal data structures adds needless complexity.'''
145 revs and bases should both be iterables. pfunc must return a list of
143 def __init__(self, pfunc, bases):
146 parent revs for a given revs.
144 self.bases = set(bases)
147 """
145 if not self.bases:
146 self.bases.add(nullrev)
147 self.pfunc = pfunc
148
148
149 revsvisit = set(revs)
149 def missingancestors(self, revs):
150 basesvisit = set(bases)
150 '''return all the ancestors of revs that are not ancestors of self.bases
151 if not basesvisit:
151
152 basesvisit.add(nullrev)
152 This may include elements from revs.
153 bothvisit = revsvisit.intersection(basesvisit)
154 revsvisit.difference_update(bothvisit)
155 if not revsvisit:
156 return []
157 start = max(max(revsvisit), max(basesvisit))
158 # At this point, we hold the invariants that:
159 # - revsvisit is the set of nodes we know are an ancestor of at least one
160 # of the nodes in revs
161 # - basesvisit is the same for bases
162 # - bothvisit is the set of nodes we know are ancestors of at least one of
163 # the nodes in revs and one of the nodes in bases, and that are smaller
164 # than curr. bothvisit and revsvisit are mutually exclusive, but bothvisit
165 # is a subset of basesvisit.
166 # Now we walk down in reverse topo order, adding parents of nodes already
167 # visited to the sets while maintaining the invariants. When a node is found
168 # in both revsvisit and basesvisit, it is removed from revsvisit and added
169 # to bothvisit. When revsvisit becomes empty, there are no more ancestors of
170 # revs that aren't also ancestors of bases, so exit.
171
153
172 missing = []
154 Equivalent to the revset (::revs - ::self.bases). Revs are returned in
173 for curr in xrange(start, nullrev, -1):
155 revision number order, which is a topological order.'''
156 revsvisit = set(revs)
157 basesvisit = self.bases
158 pfunc = self.pfunc
159 bothvisit = revsvisit.intersection(basesvisit)
160 revsvisit.difference_update(bothvisit)
174 if not revsvisit:
161 if not revsvisit:
175 break
162 return []
176
163
177 if curr in bothvisit:
164 start = max(max(revsvisit), max(basesvisit))
178 bothvisit.remove(curr)
165 # At this point, we hold the invariants that:
179 # curr's parents might have made it into revsvisit through another
166 # - revsvisit is the set of nodes we know are an ancestor of at least
180 # path
167 # one of the nodes in revs
181 for p in pfunc(curr):
168 # - basesvisit is the same for bases
182 revsvisit.discard(p)
169 # - bothvisit is the set of nodes we know are ancestors of at least one
183 basesvisit.add(p)
170 # of the nodes in revs and one of the nodes in bases. bothvisit and
184 bothvisit.add(p)
171 # revsvisit are mutually exclusive, but bothvisit is a subset of
185 continue
172 # basesvisit.
173 # Now we walk down in reverse topo order, adding parents of nodes
174 # already visited to the sets while maintaining the invariants. When a
175 # node is found in both revsvisit and basesvisit, it is removed from
176 # revsvisit and added to bothvisit. When revsvisit becomes empty, there
177 # are no more ancestors of revs that aren't also ancestors of bases, so
178 # exit.
179
180 missing = []
181 for curr in xrange(start, nullrev, -1):
182 if not revsvisit:
183 break
186
184
187 if curr in revsvisit:
185 if curr in bothvisit:
188 missing.append(curr)
186 bothvisit.remove(curr)
189 revsvisit.remove(curr)
187 # curr's parents might have made it into revsvisit through
190 thisvisit = revsvisit
188 # another path
191 othervisit = basesvisit
189 for p in pfunc(curr):
192 elif curr in basesvisit:
190 revsvisit.discard(p)
193 thisvisit = basesvisit
191 basesvisit.add(p)
194 othervisit = revsvisit
192 bothvisit.add(p)
195 else:
193 continue
196 # not an ancestor of revs or bases: ignore
197 continue
198
194
199 for p in pfunc(curr):
195 if curr in revsvisit:
200 if p == nullrev:
196 missing.append(curr)
201 pass
197 revsvisit.remove(curr)
202 elif p in othervisit or p in bothvisit:
198 thisvisit = revsvisit
203 # p is implicitly in thisvisit. This means p is or should be
199 othervisit = basesvisit
204 # in bothvisit
200 elif curr in basesvisit:
205 revsvisit.discard(p)
201 thisvisit = basesvisit
206 basesvisit.add(p)
202 othervisit = revsvisit
207 bothvisit.add(p)
208 else:
203 else:
209 # visit later
204 # not an ancestor of revs or bases: ignore
210 thisvisit.add(p)
205 continue
211
206
212 missing.reverse()
207 for p in pfunc(curr):
213 return missing
208 if p == nullrev:
209 pass
210 elif p in othervisit or p in bothvisit:
211 # p is implicitly in thisvisit. This means p is or should be
212 # in bothvisit
213 revsvisit.discard(p)
214 basesvisit.add(p)
215 bothvisit.add(p)
216 else:
217 # visit later
218 thisvisit.add(p)
219
220 missing.reverse()
221 return missing
222
223 def missingancestors(revs, bases, pfunc):
224 inc = incrementalmissingancestors(pfunc, bases)
225 return inc.missingancestors(revs)
214
226
215 class lazyancestors(object):
227 class lazyancestors(object):
216 def __init__(self, pfunc, revs, stoprev=0, inclusive=False):
228 def __init__(self, pfunc, revs, stoprev=0, inclusive=False):
@@ -88,7 +88,8 b' def test_missingancestors(seed, rng):'
88 revs = samplerevs(graphnodes)
88 revs = samplerevs(graphnodes)
89
89
90 # fast algorithm
90 # fast algorithm
91 h = ancestor.missingancestors(revs, bases, graph.__getitem__)
91 inc = ancestor.incrementalmissingancestors(graph.__getitem__, bases)
92 h = inc.missingancestors(revs)
92 # reference slow algorithm
93 # reference slow algorithm
93 r = naivemissingancestors(ancs, revs, bases)
94 r = naivemissingancestors(ancs, revs, bases)
94 if h != r:
95 if h != r:
General Comments 0
You need to be logged in to leave comments. Login now