From 6ab7af98493aaeb77160ea0f0b7bddfbdf50153e Mon Sep 17 00:00:00 2001 From: Daniel Petri Rocha <33319791+danielpetri1@users.noreply.github.com> Date: Mon, 28 Jun 2021 16:02:49 +0200 Subject: [PATCH 1/4] Fixes point_search when unique is false This change implements point_search iteratively to ensure a stack overflow can not occur. It also makes the method use the passed unique parameter, as the recursive call was previously defaulting unique to true: itv = [(5...20), (15.6...20), (15.7...20), (15.7...20)] t = IntervalTree::Tree.new(itv) p t.search(15.7) #=> [5...20, 15.6...20, 15.7...20] p t.search(15.7, unique: false) #=> Should be [5...20, 15.6...20, 15.7...20, 15.7...20], not [5...20, 15.6...20, 15.7...20] again Additionally, result.uniq is invoked at the end of the method for faster query speeds. --- lib/interval_tree.rb | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/lib/interval_tree.rb b/lib/interval_tree.rb index f711bff..7540ff6 100644 --- a/lib/interval_tree.rb +++ b/lib/interval_tree.rb @@ -73,18 +73,25 @@ def center(intervals) ) / 2 end - def point_search(node, point, result, unique = true) - node.s_center.each do |k| - if k.include?(point) - result << k + def point_search(node, point, result, unique) + stack = [node] + + until stack.empty? + node = stack.pop + + node.s_center.each do |k| + if k.begin <= point && point < k.end + result << k + end + end + if node.left_node && ( point.to_r < node.x_center ) + stack.push(node.left_node) + + elsif node.right_node && ( point.to_r >= node.x_center ) + stack.push(node.right_node) + end + end - end - if node.left_node && ( point.to_r < node.x_center ) - point_search(node.left_node, point, []).each{|k|result << k} - end - if node.right_node && ( point.to_r >= node.x_center ) - point_search(node.right_node, point, []).each{|k|result << k} - end if unique result.uniq else From b6488cb331beda716d58bd668c29d35f316ea4ac Mon Sep 17 00:00:00 2001 From: Daniel Petri Rocha <33319791+danielpetri1@users.noreply.github.com> Date: Mon, 28 Jun 2021 16:10:08 +0200 Subject: [PATCH 2/4] Update interval_tree.rb Fixed point_search Indentation --- lib/interval_tree.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/interval_tree.rb b/lib/interval_tree.rb index 7540ff6..8be2872 100644 --- a/lib/interval_tree.rb +++ b/lib/interval_tree.rb @@ -91,7 +91,7 @@ def point_search(node, point, result, unique) stack.push(node.right_node) end - end + end if unique result.uniq else From a043f3415858c4505b7b687bff5b58fbe8a862b7 Mon Sep 17 00:00:00 2001 From: Daniel Petri Rocha <33319791+danielpetri1@users.noreply.github.com> Date: Mon, 28 Jun 2021 18:03:49 +0200 Subject: [PATCH 3/4] << is faster than push Co-authored-by: amarzot-yesware <83241806+amarzot-yesware@users.noreply.github.com> --- lib/interval_tree.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/interval_tree.rb b/lib/interval_tree.rb index 8be2872..53dba04 100644 --- a/lib/interval_tree.rb +++ b/lib/interval_tree.rb @@ -85,10 +85,10 @@ def point_search(node, point, result, unique) end end if node.left_node && ( point.to_r < node.x_center ) - stack.push(node.left_node) + stack << node.left_node elsif node.right_node && ( point.to_r >= node.x_center ) - stack.push(node.right_node) + stack << node.right_node end end From 2f8fabc66bb764a62b2b8b9b789d57b45eaeab17 Mon Sep 17 00:00:00 2001 From: Daniel Petri Rocha Date: Thu, 15 Jul 2021 12:14:34 +0200 Subject: [PATCH 4/4] Specs for uniq --- spec/interval_tree_spec.rb | 40 +++++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/spec/interval_tree_spec.rb b/spec/interval_tree_spec.rb index f225617..669066f 100644 --- a/spec/interval_tree_spec.rb +++ b/spec/interval_tree_spec.rb @@ -276,9 +276,18 @@ def initialize(l, r, value = nil) end end - context 'with unique defaulting to true' do - context 'given intervals with duplicates' do - it 'returns the duplicates in the result' do + context 'with unique defaulting to true, given intervals with duplicates' do + context 'given [(1...3), (3...5), (3...9), (4...8), (4...8)] and a query by (3...5)]' do + it 'removes the duplicates in the result' do + itv = [(1...3), (3...5), (3...9), (4...8), (4...8)] + t = IntervalTree::Tree.new(itv) + results = t.search((3...5)) + expect(results).to match_array([(3...5), (3...9), (4...8)]) + end + end + + context 'given [(0...3), (1...4), (3...5), (0...3)] and a query by (2)' do + it 'removes the duplicates in the result' do itv = [(0...3), (1...4), (3...5), (0...3)] t = IntervalTree::Tree.new(itv) results = t.search(2) @@ -303,6 +312,31 @@ def initialize(l, r, value = nil) expect(results).to match_array([(2...4)]) end end + + context 'given [(0...5), (1...5), (3...5), (3...5)] and a query by (3)' do + it 'returns [(0...5), (1...5), (3...5), (3...5)]' do + itvs = [(0...5), (1...5), (3...5), (3...5)] + results = IntervalTree::Tree.new(itvs).search(3, unique: false) + expect(results).to match_array([(0...5), (1...5), (3...5), (3...5)]) + end + end + + context 'given [(0...3), (1...4), (3...4), (3...4), (3...5)] and a query by (3)' do + it 'returns [(1...4), (3...4), (3...4), (3...5)]' do + itvs = [(0...3), (1...4), (3...4), (3...4), (3...5)] + results = IntervalTree::Tree.new(itvs).search(3, unique: false) + expect(results).to match_array([(1...4), (3...4), (3...4), (3...5)]) + end + end + + context 'given [(0...2), (0...2), (1...2), (1...2), (2...5)] and a query by (1)' do + it 'returns [(0...2), (0...2), (1...2), (1...2)]' do + itvs = [(0...2), (0...2), (1...2), (1...2), (2...5)] + results = IntervalTree::Tree.new(itvs).search(1, unique: false) + expect(results).to match_array([(0...2), (0...2), (1...2), (1...2)]) + end + end + end context "when concerned with performance" do