[pypy-svn] r23404 - in pypy/dist/pypy/tool/algo: . test
arigo at codespeak.net
arigo at codespeak.net
Thu Feb 16 14:52:47 CET 2006
Author: arigo
Date: Thu Feb 16 14:52:43 2006
New Revision: 23404
Added:
pypy/dist/pypy/tool/algo/test/test_unionref.py (contents, props changed)
pypy/dist/pypy/tool/algo/unionref.py (contents, props changed)
Log:
A decentralized version of the unionfind algorithm. Not used anywhere in PyPy
so far.
Added: pypy/dist/pypy/tool/algo/test/test_unionref.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/tool/algo/test/test_unionref.py Thu Feb 16 14:52:43 2006
@@ -0,0 +1,55 @@
+from pypy.tool.algo.unionref import UnionRef, UnionDict
+
+def test_ref():
+ x = object()
+ ref = UnionRef(x)
+ assert ref() is x
+ assert ref == ref
+ assert ref != UnionRef(x)
+
+def test_merge():
+ d1 = {1: '1'}
+ d2 = {2: '2'}
+ d3 = {3: '3'}
+ d4 = {4: '4'}
+ r1 = UnionRef(d1)
+ r2 = UnionRef(d2)
+ r3 = UnionRef(d3)
+ r4 = UnionRef(d4)
+ r1.merge(r1)
+ assert r1 != r2 != r3 != r4
+ r1.merge(r2)
+ assert r1() is r2() == {1: '1', 2: '2'}
+ assert r1 == r2
+ r3.merge(r4)
+ assert r3() is r4() == {3: '3', 4: '4'}
+ assert r1 != r3
+ assert r2 != r3
+ assert r1 != r4
+ assert r2 != r4
+ r1.merge(r4)
+ assert r1() is r2() is r3() is r4() == {1: '1', 2: '2', 3: '3', 4: '4'}
+ assert r1 == r2 == r3 == r4
+
+def test_uniondict():
+ k1 = object()
+ k2 = object()
+ k3 = object()
+ k4 = object()
+ d = UnionDict()
+ d[k1] = {1: '1'}
+ d[k2] = {2: '2'}
+ d[k3] = {3: '3'}
+ d[k4] = {4: '4'}
+ assert d[k1] == {1: '1'}
+ assert d[k2] == {2: '2'}
+ assert d[k3] == {3: '3'}
+ assert d[k4] == {4: '4'}
+ assert len(d) == 4
+ d.merge(k1, k2)
+ d.merge(k3, k4)
+ assert d[k1] is d[k2] == {1: '1', 2: '2'}
+ assert d[k3] is d[k4] == {3: '3', 4: '4'}
+ d.merge(k1, k4)
+ assert d[k1] is d[k2] is d[k3] is d[k4] == {1: '1', 2: '2', 3: '3', 4: '4'}
+ assert len(d) == 4
Added: pypy/dist/pypy/tool/algo/unionref.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/tool/algo/unionref.py Thu Feb 16 14:52:43 2006
@@ -0,0 +1,150 @@
+"""
+ref = UnionRef(x) -> creates a reference to x, such that ref() is x.
+
+Two references can be merged: ref.merge(ref2) make ref and ref2 interchangeable.
+After a merge, ref() is ref2(). This is done by asking the two older objects
+that ref and ref2 pointed to how they should be merged. The point is that
+large equivalence relations can be built this way:
+
+ >>> ref1.merge(ref2)
+ >>> ref3.merge(ref4)
+ >>> ref1() is ref4()
+ False
+ >>> ref2.merge(ref3)
+ >>> ref1() is ref4()
+ True
+
+By default, two objects x and y are merged by calling x.update(y).
+"""
+
+import UserDict
+
+
+class UnionRef(object):
+ __slots__ = ('_obj', '_parent', '_weight')
+
+ def __init__(self, obj):
+ "Build a new reference to 'obj'."
+ self._obj = obj
+ self._parent = None
+ self._weight = 1
+
+ def __call__(self):
+ "Return the 'obj' that self currently references."
+ return self._findrep()._obj
+
+ def _findrep(self):
+ p = self._parent
+ if p:
+ if p._parent:
+ # this linked list is unnecessarily long, shorten it
+ path = [self]
+ while p._parent:
+ path.append(p)
+ p = p._parent
+ for q in path:
+ q._parent = p
+ return p
+ return self
+
+ def merge(self, other, union=None):
+ "Merge two references. After a.merge(b), a() and b() are identical."
+ self = self ._findrep()
+ other = other._findrep()
+ if self is not other:
+ w1 = self ._weight
+ w2 = other._weight
+ if w1 < w2:
+ self, other = other, self
+ self._weight = w1 + w2
+ other._parent = self
+ o = other._obj
+ del other._obj
+ if union is not None:
+ self._obj = union(self._obj, o)
+ else:
+ self.update(o)
+ return self
+
+ def update(self, obj):
+ "Merge 'obj' in self. Default implementation, can be overridden."
+ self._obj.update(obj)
+
+ def __hash__(self):
+ raise TypeError("UnionRef objects are unhashable")
+
+ def __eq__(self, other):
+ return (isinstance(other, UnionRef) and
+ self._findrep() is other._findrep())
+
+ def __ne__(self, other):
+ return not (self == other)
+
+
+class UnionDict(object, UserDict.DictMixin):
+ """Mapping class whose items can be unified. Conceptually, instead of
+ a set of (key, value) pairs, this is a set of ({keys}, value) pairs.
+ The method merge(key1, key2) merges the two pairs containing, respectively,
+ key1 and key2.
+ """
+ _slots = ('_data',)
+
+ def __init__(self, dict=None, **kwargs):
+ self._data = {}
+ if dict is not None:
+ self.update(dict)
+ if len(kwargs):
+ self.update(kwargs)
+
+ def merge(self, key1, key2, union=None):
+ self._data[key1] = self._data[key1].merge(self._data[key2], union)
+
+ def copy(self):
+ result = UnionDictionary()
+ newrefs = {}
+ for key, valueref in self._data.iteritems():
+ valueref = valueref._findrep()
+ try:
+ newref = newrefs[valueref]
+ except KeyError:
+ newref = newrefs[valueref] = UnionRef(valueref())
+ result._data[key] = newref
+ return result
+
+ def __repr__(self):
+ return "<UnionDictionary at %s>" % id(self)
+
+ def __getitem__(self, key):
+ return self._data[key]()
+
+ def __setitem__(self, key, value):
+ self._data[key] = UnionRef(value)
+
+ def __delitem__(self, key):
+ del self._data[key]
+
+ def keys(self):
+ return self._data.keys()
+
+ def has_key(self, key):
+ return key in self._data
+
+ def __contains__(self, key):
+ return key in self._data
+
+ def __iter__(self):
+ return iter(self._data)
+
+ def iteritems(self):
+ for key, valueref in self._data.iteritems():
+ yield (key, valueref())
+
+ def clear(self):
+ self._data.clear()
+
+ def popitem(self):
+ key, valueref = self._data.popitem()
+ return key, valueref()
+
+ def __len__(self):
+ return len(self._data)
More information about the Pypy-commit
mailing list