Skip to content

Commit 234ecff

Browse files
authored
Merge pull request #42 from escalonn/issue41
Resolve issue #41
2 parents 709ab20 + a35b875 commit 234ecff

File tree

3 files changed

+185
-4
lines changed

3 files changed

+185
-4
lines changed

intervaltree/node.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -372,10 +372,7 @@ def get_new_s_center():
372372
if iv.contains_point(new_x_center): yield iv
373373

374374
# Create a new node with the largest x_center possible.
375-
child = Node.from_intervals(get_new_s_center())
376-
# [iv for iv in self.s_center if iv.contains_point(child_x_center)]
377-
# )
378-
child.x_center = new_x_center
375+
child = Node(new_x_center, get_new_s_center())
379376
self.s_center -= child.s_center
380377

381378
#print('Pop hit! Returning child = {}'.format(

test/data/issue41_orig.py

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
"""
2+
Manually create the original tree that revealed the bug. I have to
3+
do this, since with later code changes, the original sequence of
4+
insertions and removals might not recreate that tree exactly, with
5+
the same internal structure.
6+
7+
Node<961, depth=2, balance=0>
8+
Interval(961, 986, 1)
9+
<: Node<871, depth=1, balance=0>
10+
Interval(860, 917, 1)
11+
Interval(860, 917, 2)
12+
Interval(860, 917, 3)
13+
Interval(860, 917, 4)
14+
Interval(871, 917, 1)
15+
Interval(871, 917, 2)
16+
Interval(871, 917, 3)
17+
>: Node<1047, depth=1, balance=0>
18+
Interval(1047, 1064, 1)
19+
Interval(1047, 1064, 2)
20+
"""
21+
from intervaltree import IntervalTree, Interval
22+
from intervaltree.node import Node
23+
24+
data = [
25+
(860, 917, 1), #0
26+
(860, 917, 2), #1
27+
(860, 917, 3), #2
28+
(860, 917, 4), #3
29+
(871, 917, 1), #4
30+
(871, 917, 2), #5
31+
(871, 917, 3), #6
32+
(961, 986, 1), #7
33+
(1047, 1064, 1), #8
34+
(1047, 1064, 2), #9
35+
]
36+
37+
def tree():
38+
t = IntervalTree.from_tuples(data)
39+
# Node<961, depth=2, balance=0>
40+
# Interval(961, 986, 1)
41+
root = Node()
42+
root.x_center = 961
43+
root.s_center = set([Interval(*data[7])])
44+
root.depth = 2
45+
root.balance = 0
46+
47+
# <: Node<871, depth=1, balance=0>
48+
# Interval(860, 917, 1)
49+
# Interval(860, 917, 2)
50+
# Interval(860, 917, 3)
51+
# Interval(860, 917, 4)
52+
# Interval(871, 917, 1)
53+
# Interval(871, 917, 2)
54+
# Interval(871, 917, 3)
55+
n = root.left_node = Node()
56+
n.x_center = 871
57+
n.s_center = set(Interval(*tup) for tup in data[:7])
58+
n.depth = 1
59+
n.balance = 0
60+
61+
# >: Node<1047, depth=1, balance=0>
62+
# Interval(1047, 1064, 1)
63+
# Interval(1047, 1064, 2)
64+
n = root.right_node = Node()
65+
n.x_center = 1047
66+
n.s_center = set(Interval(*tup) for tup in data[8:])
67+
n.depth = 1
68+
n.balance = 0
69+
70+
structure = root.print_structure(tostring=True)
71+
# root.print_structure()
72+
assert structure == """\
73+
Node<961, depth=2, balance=0>
74+
Interval(961, 986, 1)
75+
<: Node<871, depth=1, balance=0>
76+
Interval(860, 917, 1)
77+
Interval(860, 917, 2)
78+
Interval(860, 917, 3)
79+
Interval(860, 917, 4)
80+
Interval(871, 917, 1)
81+
Interval(871, 917, 2)
82+
Interval(871, 917, 3)
83+
>: Node<1047, depth=1, balance=0>
84+
Interval(1047, 1064, 1)
85+
Interval(1047, 1064, 2)
86+
"""
87+
t.top_node = root
88+
t.verify()
89+
return t
90+
91+
if __name__ == "__main__":
92+
tree().print_structure()

test/issue41_test.py

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
"""
2+
intervaltree: A mutable, self-balancing interval tree for Python 2 and 3.
3+
Queries may be by point, by range overlap, or by range envelopment.
4+
5+
Test module: IntervalTree, removal of intervals
6+
Submitted as issue #41 (Interval removal breaks this tree) by escalonn
7+
8+
Copyright 2013-2015 Chaim-Leib Halbert
9+
10+
Licensed under the Apache License, Version 2.0 (the "License");
11+
you may not use this file except in compliance with the License.
12+
You may obtain a copy of the License at
13+
14+
http://www.apache.org/licenses/LICENSE-2.0
15+
16+
Unless required by applicable law or agreed to in writing, software
17+
distributed under the License is distributed on an "AS IS" BASIS,
18+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19+
See the License for the specific language governing permissions and
20+
limitations under the License.
21+
"""
22+
from __future__ import absolute_import
23+
from intervaltree import IntervalTree
24+
from test.intervaltrees import trees
25+
import pytest
26+
27+
28+
def test_original_sequence():
29+
t = IntervalTree()
30+
t.addi(860, 917, 1)
31+
t.addi(860, 917, 2)
32+
t.addi(860, 917, 3)
33+
t.addi(860, 917, 4)
34+
t.addi(871, 917, 1)
35+
t.addi(871, 917, 2)
36+
t.addi(871, 917, 3) # Value inserted here
37+
t.addi(961, 986, 1)
38+
t.addi(1047, 1064, 1)
39+
t.addi(1047, 1064, 2)
40+
t.removei(961, 986, 1)
41+
t.removei(871, 917, 3) # Deleted here
42+
43+
44+
def test_debug_sequence():
45+
t = IntervalTree()
46+
t.addi(860, 917, 1)
47+
t.verify()
48+
t.addi(860, 917, 2)
49+
t.verify()
50+
t.addi(860, 917, 3)
51+
t.verify()
52+
t.addi(860, 917, 4)
53+
t.verify()
54+
t.addi(871, 917, 1)
55+
t.verify()
56+
t.addi(871, 917, 2)
57+
t.verify()
58+
t.addi(871, 917, 3) # Value inserted here
59+
t.verify()
60+
t.addi(961, 986, 1)
61+
t.verify()
62+
t.addi(1047, 1064, 1)
63+
t.verify()
64+
t.addi(1047, 1064, 2)
65+
t.verify()
66+
t.removei(961, 986, 1)
67+
t.verify()
68+
t.removei(871, 917, 3) # Deleted here
69+
t.verify()
70+
71+
72+
def test_orig_structure():
73+
"""
74+
Reconstruct the original tree just before the removals, then
75+
perform the removals.
76+
"""
77+
t = trees['issue41_orig']()
78+
# t.print_structure()
79+
t.verify()
80+
81+
t.removei(961, 986, 1)
82+
# t.print_structure()
83+
t.verify()
84+
85+
t.removei(871, 917, 3)
86+
# t.print_structure()
87+
t.verify()
88+
89+
90+
if __name__ == "__main__":
91+
# pytest.main([__file__, '-v'])
92+
test_orig_structure()

0 commit comments

Comments
 (0)