ancestor.py
91 lines
| 2.6 KiB
| text/x-python
|
PythonLexer
/ mercurial / ancestor.py
Matt Mackall
|
r3135 | # ancestor.py - generic DAG ancestor algorithm for mercurial | ||
# | ||||
# Copyright 2006 Matt Mackall <mpm@selenic.com> | ||||
# | ||||
Martin Geisler
|
r8225 | # This software may be used and distributed according to the terms of the | ||
Matt Mackall
|
r10263 | # GNU General Public License version 2 or any later version. | ||
Matt Mackall
|
r3135 | |||
import heapq | ||||
def ancestor(a, b, pfunc): | ||||
""" | ||||
Matt Mackall
|
r13554 | Returns the common ancestor of a and b that is furthest from a | ||
root (as measured by longest path) or None if no ancestor is | ||||
found. If there are multiple common ancestors at the same | ||||
distance, the first one found is returned. | ||||
Matt Mackall
|
r3135 | |||
Sune Foldager
|
r9915 | pfunc must return a list of parent vertices for a given vertex | ||
Matt Mackall
|
r3135 | """ | ||
if a == b: | ||||
return a | ||||
Matt Mackall
|
r11418 | a, b = sorted([a, b]) | ||
Matt Mackall
|
r3135 | # find depth from root of all ancestors | ||
Matt Mackall
|
r13554 | # depth is stored as a negative for heapq | ||
Nicolas Dumazet
|
r7882 | parentcache = {} | ||
Matt Mackall
|
r3135 | visit = [a, b] | ||
depth = {} | ||||
while visit: | ||||
vertex = visit[-1] | ||||
pl = pfunc(vertex) | ||||
Nicolas Dumazet
|
r7882 | parentcache[vertex] = pl | ||
Matt Mackall
|
r3135 | if not pl: | ||
depth[vertex] = 0 | ||||
visit.pop() | ||||
else: | ||||
for p in pl: | ||||
Matt Mackall
|
r12401 | if p == a or p == b: # did we find a or b as a parent? | ||
Matt Mackall
|
r3135 | return p # we're done | ||
if p not in depth: | ||||
visit.append(p) | ||||
if visit[-1] == vertex: | ||||
Matt Mackall
|
r13554 | # -(maximum distance of parents + 1) | ||
Matt Mackall
|
r3135 | depth[vertex] = min([depth[p] for p in pl]) - 1 | ||
visit.pop() | ||||
# traverse ancestors in order of decreasing distance from root | ||||
def ancestors(vertex): | ||||
h = [(depth[vertex], vertex)] | ||||
Benoit Boissinot
|
r8465 | seen = set() | ||
Matt Mackall
|
r3135 | while h: | ||
d, n = heapq.heappop(h) | ||||
if n not in seen: | ||||
Benoit Boissinot
|
r8465 | seen.add(n) | ||
Matt Mackall
|
r3135 | yield (d, n) | ||
Nicolas Dumazet
|
r7882 | for p in parentcache[n]: | ||
Matt Mackall
|
r3135 | heapq.heappush(h, (depth[p], p)) | ||
def generations(vertex): | ||||
Benoit Boissinot
|
r8465 | sg, s = None, set() | ||
Thomas Arendsen Hein
|
r3673 | for g, v in ancestors(vertex): | ||
Matt Mackall
|
r3135 | if g != sg: | ||
if sg: | ||||
yield sg, s | ||||
Benoit Boissinot
|
r8465 | sg, s = g, set((v,)) | ||
Matt Mackall
|
r3135 | else: | ||
Benoit Boissinot
|
r8465 | s.add(v) | ||
Matt Mackall
|
r3135 | yield sg, s | ||
x = generations(a) | ||||
y = generations(b) | ||||
gx = x.next() | ||||
gy = y.next() | ||||
# increment each ancestor list until it is closer to root than | ||||
# the other, or they match | ||||
try: | ||||
while 1: | ||||
if gx[0] == gy[0]: | ||||
for v in gx[1]: | ||||
if v in gy[1]: | ||||
return v | ||||
gy = y.next() | ||||
gx = x.next() | ||||
elif gx[0] > gy[0]: | ||||
gy = y.next() | ||||
else: | ||||
gx = x.next() | ||||
except StopIteration: | ||||
return None | ||||