Browse Source

Working on getting the RBTree working. Intersections are busted.

git-svn-id: https://bucket.mit.edu/svn/nilm/nilmdb@11380 ddd99763-3ecb-0310-9145-efcb8ce7c51f
tags/bxinterval-return
Jim Paris 9 years ago
parent
commit
7211217f40
6 changed files with 69 additions and 27 deletions
  1. +0
    -2
      nilmdb/RedBlackTree.cc
  2. +11
    -9
      nilmdb/interval.py
  3. +31
    -5
      nilmdb/rbtree.py
  4. +3
    -3
      setup.cfg
  5. +12
    -2
      tests/test_interval.py
  6. +12
    -6
      tests/test_rbtree.py

+ 0
- 2
nilmdb/RedBlackTree.cc View File

@@ -581,7 +581,6 @@ RedBlackEntry * RedBlackTree::DeleteNode(RedBlackTreeNode * z){
/**/
/***********************************************************************/

/*
TemplateStack<RedBlackTreeNode *> * RedBlackTree::Enumerate(int low,
int high) {
TemplateStack<RedBlackTreeNode *> * enumResultStack =
@@ -604,4 +603,3 @@ TemplateStack<RedBlackTreeNode *> * RedBlackTree::Enumerate(int low,
}
return(enumResultStack);
}
*/

+ 11
- 9
nilmdb/interval.py View File

@@ -221,7 +221,10 @@ class IntervalSet(object):

Removes an interval from the set. Must exist exactly
as provided -- cannot remove a subset of an existing interval."""
self.tree.delete(rbtree.RBNode(other))
i = self.tree.find(other.start, other.end)
if i is None:
raise IntervalError("interval " + str(other) + " not in tree")
self.tree.delete(i)
return self

def __add__(self, other):
@@ -240,16 +243,16 @@ class IntervalSet(object):
out = IntervalSet()

if not isinstance(other, IntervalSet):
other = [ other ]

for x in other:
for i in self.intersection(x):
for i in self.intersection(other):
out.tree.insert(rbtree.RBNode(i))
else:
for x in other:
for i in self.intersection(x):
out.tree.insert(rbtree.RBNode(i))

return out

def intersection(self, interval):
### PROBABLY WRONG
"""
Compute a sequence of intervals that correspond to the
intersection between `self` and the provided interval.
@@ -261,11 +264,10 @@ class IntervalSet(object):
"""
if not isinstance(interval, Interval):
raise TypeError("bad type")
node = self.tree.find_left(interval.start, interval.end)
for n in self.tree.inorder(node):
for n in self.tree.intersect(interval.start, interval.end):
i = n.obj
if i:
if i.start > interval.start and i.end < interval.end:
if i.start >= interval.start and i.end <= interval.end:
yield i
elif i.start > interval.end:
break


+ 31
- 5
nilmdb/rbtree.py View File

@@ -286,7 +286,9 @@ class RBTree(object):

def render_dot(self, title = "RBTree"):
"""Render the entire RBTree as a dot graph"""
return "digraph rbtree {\n" + self.__render_dot_node(self.root) + "}\n";
return ("digraph rbtree {\n"
+ self.__render_dot_node(self.root.left)
+ "}\n");

def render_dot_live(self, title = "RBTree"):
"""Render the entire RBTree as a dot graph, live GTK view"""
@@ -295,7 +297,9 @@ class RBTree(object):
sys.path.append("/usr/share/xdot")
import xdot
xdot.Pen.highlighted = lambda pen: pen
s = "digraph rbtree {\n" + self.__render_dot_node(self.root) + "}\n";
s = ("digraph rbtree {\n"
+ self.__render_dot_node(self.root)
+ "}\n");
window = xdot.DotWindow()
window.set_dotcode(s)
window.set_title(title + " - any key to close")
@@ -309,13 +313,13 @@ class RBTree(object):

# Walking, searching
def __iter__(self):
return self.inorder(self.root)
return self.inorder(self.root.left)

def inorder(self, x = None):
"""Generator that performs an inorder walk for the tree
starting at RBNode x"""
if x is None:
x = self.root
x = self.root.left
while x.left is not self.nil:
x = x.left
while x is not self.nil:
@@ -327,7 +331,7 @@ class RBTree(object):
Also returns the largest node less than or equal to key,
and the smallest node greater or equal to than key."""
if x is None:
x = self.root
x = self.root.left
largest = self.nil
smallest = self.nil
while x is not self.nil:
@@ -364,3 +368,25 @@ class RBTree(object):
"""Find node with the largest key <= (start,end), or None"""
y = self.__find_all(start, end, x)[2]
return y if y is not self.nil else None

# Intersections
def intersect(self, start, end):
"""Generator that returns nodes that overlap the given
(start,end) range, for the tree rooted at RBNode x.

NOTE: this assumes non-overlapping intervals."""
# Start with the leftmost node before the starting point
n = self.find_left(start, start)
# If we didn't find one, look for the leftmode node before the
# ending point instead.
if n is None:
n = self.find_left(end, end)
# If we still didn't find it, there are no intervals that intersect.
if n is None:
return none

# Now yield this node and all successors until their endpoints

if False:
yield
return

+ 3
- 3
setup.cfg View File

@@ -2,7 +2,7 @@
# note: the value doesn't matter, that's why they're empty here
nocapture=
nologcapture= # comment to see cherrypy logs on failure
#with-coverage=
with-coverage=
cover-inclusive=
cover-package=nilmdb
cover-erase=
@@ -12,8 +12,8 @@ stop=
verbosity=2
#tests=tests/test_cmdline.py
#tests=tests/test_layout.py
tests=tests/test_rbtree.py
#tests=tests/test_interval.py
#tests=tests/test_rbtree.py
tests=tests/test_interval.py
#tests=tests/test_client.py
#tests=tests/test_timestamper.py
#tests=tests/test_serializer.py


+ 12
- 2
tests/test_interval.py View File

@@ -200,6 +200,10 @@ class TestInterval:
makeset(" [-----] ") ==
makeset(" [--] "))

assert(makeset(" [--] [--]") &
makeset(" [------] ") ==
makeset(" [-] [-] "))

assert(makeset(" [---]") &
makeset(" [--] ") ==
makeset(" "))
@@ -265,9 +269,10 @@ class TestIntervalDB:

class TestIntervalTree:

#@unittest.skip("needs GTK")
def test_interval_tree(self):
import random
random.seed(1234)

# make a set of 500 intervals
iset = IntervalSet()
j = 500
@@ -280,8 +285,13 @@ class TestIntervalTree:
if random.randint(0,1):
iset -= Interval(i, i+1)

# try removing an interval that doesn't exist
with assert_raises(IntervalError):
iset -= Interval(1234,5678)

# show the graph
iset.tree.render_dot_live()
if False:
iset.tree.render_dot_live()

class TestIntervalSpeed:
@unittest.skip("this is slow")


+ 12
- 6
tests/test_rbtree.py View File

@@ -11,6 +11,8 @@ from nilmdb.rbtree import RBTree, RBNode
from test_helpers import *
import unittest

render = False

class TestRBTree:
def test_rbtree(self):
rb = RBTree()
@@ -22,7 +24,6 @@ class TestRBTree:
# Verify that the dot isn't too big.
assert(len(s.splitlines()) < 30)

#@unittest.skip("needs GTK")
def test_rbtree_big(self):
import random
random.seed(1234)
@@ -34,7 +35,8 @@ class TestRBTree:
rb.insert(RBNode(None, i, i+1))

# show the graph
rb.render_dot_live("in-order insert")
if render:
rb.render_dot_live("in-order insert")

# remove about half of them
for i in random.sample(xrange(j),j):
@@ -42,7 +44,8 @@ class TestRBTree:
rb.delete(rb.find(i, i+1))

# show the graph
rb.render_dot_live("in-order insert, random delete")
if render:
rb.render_dot_live("in-order insert, random delete")

# make a set of 500 intervals, inserted at random
rb = RBTree()
@@ -51,7 +54,8 @@ class TestRBTree:
rb.insert(RBNode(None, i, i+1))

# show the graph
rb.render_dot_live("random insert")
if render:
rb.render_dot_live("random insert")

# remove about half of them
for i in random.sample(xrange(j),j):
@@ -59,11 +63,13 @@ class TestRBTree:
rb.delete(rb.find(i, i+1))

# show the graph
rb.render_dot_live("random insert, random delete")
if render:
rb.render_dot_live("random insert, random delete")

# in-order insert of 250 more
for i in xrange(250):
rb.insert(RBNode(None, i+500, i+501))

# show the graph
rb.render_dot_live("random insert, random delete, in-order insert")
if render:
rb.render_dot_live("random insert, random delete, in-order insert")

Loading…
Cancel
Save