fast_interval_tree 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +34 -0
- data/README.md +70 -0
- data/Rakefile +6 -0
- data/fast_interval_tree.gemspec +31 -0
- data/lib/interval_tree.rb +145 -0
- data/lib/interval_tree/version.rb +3 -0
- metadata +87 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 42b8ee0cf97e339df2d7b54b29291f6a9365eab9bda925a92d935b2c5c23ac47
|
4
|
+
data.tar.gz: 90000f00518ca5f3a547e7990f9c57c7a8a72a35929b650f05b511dfa4aab2e1
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b4c99025fa3a0071bacc862ae2080e3236ad8052da0ce4556b0cc0db4c241b4a263b5df964e99f222084b3b96d3aa08098cf3197f02578acb2be8b6966da178f
|
7
|
+
data.tar.gz: 6756e05df8be87515476355806a0549736512cf1eb1243db7ceeb86c2042d89c2286daababaeae8fbaee97d6baeb63cf18834207fedb9d507458c3aefb539153
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
fast_interval_tree (0.2.0)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
diff-lcs (1.4.4)
|
10
|
+
rake (13.0.1)
|
11
|
+
rspec (3.10.0)
|
12
|
+
rspec-core (~> 3.10.0)
|
13
|
+
rspec-expectations (~> 3.10.0)
|
14
|
+
rspec-mocks (~> 3.10.0)
|
15
|
+
rspec-core (3.10.0)
|
16
|
+
rspec-support (~> 3.10.0)
|
17
|
+
rspec-expectations (3.10.0)
|
18
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
19
|
+
rspec-support (~> 3.10.0)
|
20
|
+
rspec-mocks (3.10.0)
|
21
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
22
|
+
rspec-support (~> 3.10.0)
|
23
|
+
rspec-support (3.10.0)
|
24
|
+
|
25
|
+
PLATFORMS
|
26
|
+
ruby
|
27
|
+
|
28
|
+
DEPENDENCIES
|
29
|
+
fast_interval_tree!
|
30
|
+
rake (~> 13.0.1)
|
31
|
+
rspec (~> 3.10)
|
32
|
+
|
33
|
+
BUNDLED WITH
|
34
|
+
1.17.3
|
data/README.md
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
# IntervalTree
|
2
|
+
|
3
|
+
An implementation of the centered interval tree algorithm in Ruby.
|
4
|
+
|
5
|
+
This is a fork of [https://github.com/misshie/interval-tree] until our (performance related PR)[https://github.com/misshie/interval-tree/pull/8] gets merged.
|
6
|
+
|
7
|
+
## See also
|
8
|
+
|
9
|
+
* description in Wikipedia http://en.wikipedia.org/wiki/Interval_tree
|
10
|
+
* an implementation in Python by Tyler Kahn http://forrst.com/posts/Interval_Tree_implementation_in_python-e0K (broken link)
|
11
|
+
|
12
|
+
## ChangeLog
|
13
|
+
|
14
|
+
### 2020-11-09, contribution by Brendan Weibrecht, Chris Nankervis and Thomas van der Pol
|
15
|
+
|
16
|
+
* Substantially improved performance when supplied with a large range as the search query.
|
17
|
+
|
18
|
+
### 2017-05-12, contribution by Sam Davies ( https://github.com/samphilipd )
|
19
|
+
|
20
|
+
* User can specify an option in search `unique: false` if s/he wants multiple matches to be returned.
|
21
|
+
|
22
|
+
### 2015-11-02, contribution by Carlos Alonso ( https://github.com/calonso )
|
23
|
+
|
24
|
+
* Improved centering
|
25
|
+
* Fixed searching: With some use cases with very large trees, the library fails to find intervals.
|
26
|
+
* Added rubygems structure to be able to be pushed as a gem
|
27
|
+
|
28
|
+
### 2013-04-06, contribution by Simeon Simeonov ( https://github.com/ssimeonov )
|
29
|
+
|
30
|
+
* **Range factory**: The current design allows for Range-compatible elements to be added except for the case where `Tree#ensure_exclusive_end` constructs a Range in a private method. In keeping with good design practices of containers such as Hash, this pull requests allows for a custom range factory to be provided to `Tree#initialize` while maintaining perfect backward compatibility.
|
31
|
+
Search in empty trees failing
|
32
|
+
* Adds a nil guard in `Tree#search` to protect against empty tree searches failing.
|
33
|
+
* **Cosmetic improvements**: Language & whitespace in specs, Gemfile addition, and .gitignore update
|
34
|
+
|
35
|
+
## Install
|
36
|
+
|
37
|
+
```bash
|
38
|
+
$ gem install fast_interval_tree
|
39
|
+
```
|
40
|
+
|
41
|
+
## Usage
|
42
|
+
|
43
|
+
See spec/interval_tree_spec.rb for details.
|
44
|
+
|
45
|
+
```ruby
|
46
|
+
require "interval_tree"
|
47
|
+
|
48
|
+
itv = [(0...3), (1...4), (3...5), (0...3)]
|
49
|
+
t = IntervalTree::Tree.new(itv)
|
50
|
+
p t.search(2) #=> [0...3, 1...4]
|
51
|
+
p t.search(2, unique: false) #=> [0...3, 0...3, 1...4]
|
52
|
+
p t.search(1...4) #=> [0...3, 1...4, 3...5]
|
53
|
+
```
|
54
|
+
|
55
|
+
## Note
|
56
|
+
|
57
|
+
Result intervals are always returned
|
58
|
+
in the "left-closed and right-open" style that can be expressed
|
59
|
+
by three-dotted Range object literals `(first...last)`
|
60
|
+
|
61
|
+
Two-dotted full-closed intervals `(first..last)` are also accepted and internally
|
62
|
+
converted to half-closed intervals.
|
63
|
+
|
64
|
+
## Copyright
|
65
|
+
|
66
|
+
**Author**: MISHIMA, Hiroyuki ( https://github.com/misshie ), Simeon Simeonov ( https://github.com/ssimeonov ), Carlos Alonso ( https://github.com/calonso ), Sam Davies ( https://github.com/samphilipd ), Brendan Weibrecht (https://github.com/ZimbiX), Chris Nankervis (https://github.com/chrisnankervis), Thomas van der Pol (https://github.com/tvanderpol).
|
67
|
+
|
68
|
+
**Copyright**: (c) 2011-2020 MISHIMA, Hiroyuki; Simeon Simeonov; Carlos Alonsol; Sam Davies; Brendan Weibrecht; Chris Nankervis; Thomas van der Pol
|
69
|
+
|
70
|
+
**License**: The MIT/X11 license
|
data/Rakefile
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
|
5
|
+
require_relative 'lib/interval_tree/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |s|
|
8
|
+
s.name = 'fast_interval_tree'
|
9
|
+
s.version = IntervalTree::VERSION
|
10
|
+
s.summary = 'A Ruby implementation of the Centered Interval Tree data structure'
|
11
|
+
s.description = <<-END.gsub(/^ {4}/, '')
|
12
|
+
A Ruby implementation of the Centered Interval Tree data structure.
|
13
|
+
See also: http://en.wikipedia.org/wiki/Interval_tree
|
14
|
+
END
|
15
|
+
s.authors = ['Hiroyuki Mishima', 'Simeon Simeonov', 'Carlos Alonso', 'Sam Davies', 'Brendan Weibrecht', 'Chris Nankervis', 'Thomas van der Pol']
|
16
|
+
s.email = ['brendan@weibrecht.net.au']
|
17
|
+
s.files = ['lib/interval_tree.rb']
|
18
|
+
s.homepage = 'https://github.com/greensync/interval-tree'
|
19
|
+
s.metadata['source_code_uri'] = 'https://github.com/greensync/interval-tree'
|
20
|
+
s.license = 'MIT'
|
21
|
+
|
22
|
+
# Specify which files should be added to the gem when it is released.
|
23
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
24
|
+
s.files = Dir.chdir(File.expand_path(__dir__)) do
|
25
|
+
Dir.glob('{bin,lib}/**/*') + %w[README.md Gemfile Gemfile.lock Rakefile fast_interval_tree.gemspec]
|
26
|
+
end
|
27
|
+
s.require_paths = ['lib']
|
28
|
+
|
29
|
+
s.add_development_dependency 'rspec', '~> 3.10'
|
30
|
+
s.add_development_dependency 'rake', '~> 13.0.1'
|
31
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
module IntervalTree
|
4
|
+
|
5
|
+
class Tree
|
6
|
+
def initialize(ranges, &range_factory)
|
7
|
+
range_factory = lambda { |l, r| (l ... r+1) } unless block_given?
|
8
|
+
ranges_excl = ensure_exclusive_end([ranges].flatten, range_factory)
|
9
|
+
@top_node = divide_intervals(ranges_excl)
|
10
|
+
end
|
11
|
+
attr_reader :top_node
|
12
|
+
|
13
|
+
def divide_intervals(intervals)
|
14
|
+
return nil if intervals.empty?
|
15
|
+
x_center = center(intervals)
|
16
|
+
s_center = Array.new
|
17
|
+
s_left = Array.new
|
18
|
+
s_right = Array.new
|
19
|
+
|
20
|
+
intervals.each do |k|
|
21
|
+
case
|
22
|
+
when k.last < x_center
|
23
|
+
s_left << k
|
24
|
+
when k.first > x_center
|
25
|
+
s_right << k
|
26
|
+
else
|
27
|
+
s_center << k
|
28
|
+
end
|
29
|
+
end
|
30
|
+
Node.new(x_center, s_center,
|
31
|
+
divide_intervals(s_left), divide_intervals(s_right))
|
32
|
+
end
|
33
|
+
|
34
|
+
# Search by range or point
|
35
|
+
DEFAULT_OPTIONS = {unique: true}
|
36
|
+
def search(query, options = {})
|
37
|
+
options = DEFAULT_OPTIONS.merge(options)
|
38
|
+
|
39
|
+
return nil unless @top_node
|
40
|
+
|
41
|
+
if query.respond_to?(:first)
|
42
|
+
result = top_node.search(query)
|
43
|
+
options[:unique] ? result.uniq : result
|
44
|
+
else
|
45
|
+
point_search(self.top_node, query, [], options[:unique])
|
46
|
+
end
|
47
|
+
.sort_by{|x|[x.first, x.last]}
|
48
|
+
end
|
49
|
+
|
50
|
+
def ==(other)
|
51
|
+
top_node == other.top_node
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def ensure_exclusive_end(ranges, range_factory)
|
57
|
+
ranges.map do |range|
|
58
|
+
case
|
59
|
+
when !range.respond_to?(:exclude_end?)
|
60
|
+
range
|
61
|
+
when range.exclude_end?
|
62
|
+
range
|
63
|
+
else
|
64
|
+
range_factory.call(range.first, range.end)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Note: Floors the result
|
70
|
+
def center(intervals)
|
71
|
+
(
|
72
|
+
intervals.map(&:begin).min +
|
73
|
+
intervals.map(&:end).max
|
74
|
+
) / 2
|
75
|
+
end
|
76
|
+
|
77
|
+
def point_search(node, point, result, unique = true)
|
78
|
+
node.s_center.each do |k|
|
79
|
+
if k.include?(point)
|
80
|
+
result << k
|
81
|
+
end
|
82
|
+
end
|
83
|
+
if node.left_node && ( point < node.x_center )
|
84
|
+
point_search(node.left_node, point, []).each{|k|result << k}
|
85
|
+
end
|
86
|
+
if node.right_node && ( point >= node.x_center )
|
87
|
+
point_search(node.right_node, point, []).each{|k|result << k}
|
88
|
+
end
|
89
|
+
if unique
|
90
|
+
result.uniq
|
91
|
+
else
|
92
|
+
result
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end # class Tree
|
96
|
+
|
97
|
+
class Node
|
98
|
+
def initialize(x_center, s_center, left_node, right_node)
|
99
|
+
@x_center = x_center
|
100
|
+
@s_center = s_center
|
101
|
+
@left_node = left_node
|
102
|
+
@right_node = right_node
|
103
|
+
end
|
104
|
+
attr_reader :x_center, :s_center, :left_node, :right_node
|
105
|
+
|
106
|
+
def ==(other)
|
107
|
+
x_center == other.x_center &&
|
108
|
+
s_center == other.s_center &&
|
109
|
+
left_node == other.left_node &&
|
110
|
+
right_node == other.right_node
|
111
|
+
end
|
112
|
+
|
113
|
+
# Search by range only
|
114
|
+
def search(query)
|
115
|
+
search_s_center(query) +
|
116
|
+
(query.begin < x_center && left_node&.search(query) || []) +
|
117
|
+
(query.end > x_center && right_node&.search(query) || [])
|
118
|
+
end
|
119
|
+
|
120
|
+
private
|
121
|
+
|
122
|
+
def search_s_center(query)
|
123
|
+
s_center.select do |k|
|
124
|
+
(
|
125
|
+
# k is entirely contained within the query
|
126
|
+
(k.begin >= query.begin) &&
|
127
|
+
(k.end <= query.end)
|
128
|
+
) || (
|
129
|
+
# k's start overlaps with the query
|
130
|
+
(k.begin >= query.begin) &&
|
131
|
+
(k.begin < query.end)
|
132
|
+
) || (
|
133
|
+
# k's end overlaps with the query
|
134
|
+
(k.end > query.begin) &&
|
135
|
+
(k.end <= query.end)
|
136
|
+
) || (
|
137
|
+
# k is bigger than the query
|
138
|
+
(k.begin < query.begin) &&
|
139
|
+
(k.end > query.end)
|
140
|
+
)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end # class Node
|
144
|
+
|
145
|
+
end # module IntervalTree
|
metadata
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fast_interval_tree
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Hiroyuki Mishima
|
8
|
+
- Simeon Simeonov
|
9
|
+
- Carlos Alonso
|
10
|
+
- Sam Davies
|
11
|
+
- Brendan Weibrecht
|
12
|
+
- Chris Nankervis
|
13
|
+
- Thomas van der Pol
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
date: 2020-11-09 00:00:00.000000000 Z
|
18
|
+
dependencies:
|
19
|
+
- !ruby/object:Gem::Dependency
|
20
|
+
name: rspec
|
21
|
+
requirement: !ruby/object:Gem::Requirement
|
22
|
+
requirements:
|
23
|
+
- - "~>"
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: '3.10'
|
26
|
+
type: :development
|
27
|
+
prerelease: false
|
28
|
+
version_requirements: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - "~>"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '3.10'
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: rake
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: 13.0.1
|
40
|
+
type: :development
|
41
|
+
prerelease: false
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 13.0.1
|
47
|
+
description: |
|
48
|
+
A Ruby implementation of the Centered Interval Tree data structure.
|
49
|
+
See also: http://en.wikipedia.org/wiki/Interval_tree
|
50
|
+
email:
|
51
|
+
- brendan@weibrecht.net.au
|
52
|
+
executables: []
|
53
|
+
extensions: []
|
54
|
+
extra_rdoc_files: []
|
55
|
+
files:
|
56
|
+
- Gemfile
|
57
|
+
- Gemfile.lock
|
58
|
+
- README.md
|
59
|
+
- Rakefile
|
60
|
+
- fast_interval_tree.gemspec
|
61
|
+
- lib/interval_tree.rb
|
62
|
+
- lib/interval_tree/version.rb
|
63
|
+
homepage: https://github.com/greensync/interval-tree
|
64
|
+
licenses:
|
65
|
+
- MIT
|
66
|
+
metadata:
|
67
|
+
source_code_uri: https://github.com/greensync/interval-tree
|
68
|
+
post_install_message:
|
69
|
+
rdoc_options: []
|
70
|
+
require_paths:
|
71
|
+
- lib
|
72
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0'
|
82
|
+
requirements: []
|
83
|
+
rubygems_version: 3.1.4
|
84
|
+
signing_key:
|
85
|
+
specification_version: 4
|
86
|
+
summary: A Ruby implementation of the Centered Interval Tree data structure
|
87
|
+
test_files: []
|