multi_range 1.3.0 → 2.0.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 +4 -4
- data/.travis.yml +1 -5
- data/README.md +38 -2
- data/bin/console +0 -0
- data/bin/setup +1 -1
- data/lib/multi_range.rb +83 -24
- data/lib/multi_range/version.rb +1 -1
- data/multi_range.gemspec +4 -0
- metadata +31 -4
- data/gemfiles/ruby_1_8_7.gemfile +0 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8b6430c0f1e34bd416c5ac86b7427a287b980282604b7220a518feb8eb90e3d3
|
4
|
+
data.tar.gz: 01f8ca6da27bc8a960ef531e114cdfc15296fac6af91e4e588642a11b7b56ac7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a14a3bee4fece94aa0c39a8fcdd614dfcd94afe771a7075f84e33093cf3a0e5e8990029685f09bc89b13c93d1ce89b82bc527c0f120f43f0238e39e902fd6a28
|
7
|
+
data.tar.gz: 513bba26a2dee77e82720214102e9231390954e94cdf36cff610066578179ea67498bfd29576ceb9177362981f34baad2abdddca877f782dc0b47123523e71b0
|
data/.travis.yml
CHANGED
@@ -4,16 +4,12 @@ env:
|
|
4
4
|
- CC_TEST_REPORTER_ID=a2bb1313ac63f7ae7a7f13ac962870e3cdb0c345b6c60d2857807cc5153d7c3b
|
5
5
|
language: ruby
|
6
6
|
rvm:
|
7
|
+
- 2.0
|
7
8
|
- 2.2
|
8
9
|
- 2.6
|
9
10
|
- 2.7
|
10
11
|
gemfile:
|
11
12
|
- gemfiles/Gemfile
|
12
|
-
matrix:
|
13
|
-
include:
|
14
|
-
- dist: trusty
|
15
|
-
rvm: 1.8.7
|
16
|
-
gemfile: gemfiles/ruby_1_8_7.gemfile
|
17
13
|
before_install:
|
18
14
|
- if `ruby -e 'exit(RUBY_VERSION.to_f < 2.7)'`; then
|
19
15
|
gem i rubygems-update -v '< 3' && update_rubygems;
|
data/README.md
CHANGED
@@ -7,7 +7,10 @@
|
|
7
7
|
[](https://codeclimate.com/github/khiav223577/multi_range/coverage)
|
8
8
|
|
9
9
|
## Supports
|
10
|
-
|
10
|
+
|
11
|
+
- Ruby 2.0 ~ 2.7
|
12
|
+
|
13
|
+
For Ruby 1.8.x and 1.9.x, please use multi_range < 2.
|
11
14
|
|
12
15
|
## Installation
|
13
16
|
|
@@ -25,7 +28,7 @@ Or install it yourself as:
|
|
25
28
|
|
26
29
|
## Usage
|
27
30
|
|
28
|
-
Allow you to manipulate a group of ranges. Such as merging overlapping ranges, doing ranges union and difference.
|
31
|
+
Allow you to manipulate a group of ranges. Such as merging overlapping ranges, doing ranges union, intersection, and difference.
|
29
32
|
|
30
33
|
### Sample a number
|
31
34
|
```rb
|
@@ -72,6 +75,22 @@ multi_range.ranges
|
|
72
75
|
# => [1..6, 10..25, 30..30]
|
73
76
|
```
|
74
77
|
|
78
|
+
### Intersection ranges
|
79
|
+
|
80
|
+
```rb
|
81
|
+
multi_range = MultiRange.new([1..5])
|
82
|
+
multi_range &= 3..8
|
83
|
+
multi_range.ranges
|
84
|
+
# => [3..5]
|
85
|
+
```
|
86
|
+
|
87
|
+
```rb
|
88
|
+
multi_range = MultiRange.new([1..3, 5..10])
|
89
|
+
multi_range &= MultiRange.new([2..6, 8..9])
|
90
|
+
multi_range.ranges
|
91
|
+
# => [2..3, 5..6, 8..9]
|
92
|
+
```
|
93
|
+
|
75
94
|
### Merge overlaps
|
76
95
|
```rb
|
77
96
|
multi_range = MultiRange.new([1, 2, 4..6, 7, 8..12])
|
@@ -103,6 +122,23 @@ multi_range.overlaps?(MultiRange.new([6..8, 18..22]))
|
|
103
122
|
# => true
|
104
123
|
```
|
105
124
|
|
125
|
+
|
126
|
+
### Check if it contains overlaps
|
127
|
+
|
128
|
+
```rb
|
129
|
+
MultiRange.new([0..3, 5..10, 20..50]).contain_overlaps?
|
130
|
+
# => false
|
131
|
+
|
132
|
+
MultiRange.new([0...5, 5..10, 20..50]).contain_overlaps?
|
133
|
+
# => false
|
134
|
+
|
135
|
+
MultiRange.new([0..5, 5..10, 20..50]).contain_overlaps?
|
136
|
+
# => true
|
137
|
+
|
138
|
+
MultiRange.new([0...7, 5..10, 20..50]).contain_overlaps?
|
139
|
+
# => true
|
140
|
+
```
|
141
|
+
|
106
142
|
### Range-like interface
|
107
143
|
|
108
144
|
#### each
|
data/bin/console
CHANGED
File without changes
|
data/bin/setup
CHANGED
data/lib/multi_range.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'multi_range/version'
|
4
4
|
require 'roulette-wheel-selection'
|
5
|
+
require 'interval_tree'
|
5
6
|
|
6
7
|
if not Range.method_defined?(:size)
|
7
8
|
warn "Please backports Range#size method to use multi_range gem.\n" \
|
@@ -22,8 +23,17 @@ class MultiRange
|
|
22
23
|
attr_reader :ranges
|
23
24
|
|
24
25
|
def initialize(ranges)
|
25
|
-
|
26
|
-
|
26
|
+
if ranges.is_a? MultiRange
|
27
|
+
@ranges = ranges.ranges
|
28
|
+
@is_float = ranges.is_float?
|
29
|
+
else
|
30
|
+
@ranges = ranges.map{|s| s.is_a?(Numeric) ? s..s : s }.sort_by(&:begin).freeze
|
31
|
+
@is_float = @ranges.any?{|range| range.begin.is_a?(Float) || range.end.is_a?(Float) }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def is_float?
|
36
|
+
@is_float
|
27
37
|
end
|
28
38
|
|
29
39
|
def merge_overlaps(merge_same_value = true)
|
@@ -48,37 +58,41 @@ class MultiRange
|
|
48
58
|
return MultiRange.new(new_ranges)
|
49
59
|
end
|
50
60
|
|
51
|
-
def
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
61
|
+
def &(other)
|
62
|
+
other_ranges = MultiRange.new(other).merge_overlaps.ranges
|
63
|
+
tree = IntervalTree::Tree.new(other_ranges)
|
64
|
+
intersected_ranges = merge_overlaps.ranges.flat_map do |range|
|
65
|
+
matching_ranges_converted_to_exclusive = tree.search(range) || []
|
66
|
+
|
67
|
+
# The interval tree converts interval endings to exclusive, so we need to restore the original
|
68
|
+
matching_ranges = matching_ranges_converted_to_exclusive.map do |matching_range_converted_to_exclusive|
|
69
|
+
other_ranges.find do |other_range|
|
70
|
+
# Having merged overlaps in each multirange, there's no need to check the endings, since there will only be one range with each beginning
|
71
|
+
other_range.begin == matching_range_converted_to_exclusive.begin
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
matching_ranges.map do |matching_range|
|
76
|
+
intersect_two_ranges(range, matching_range)
|
77
|
+
end
|
56
78
|
end
|
79
|
+
MultiRange.new(intersected_ranges)
|
80
|
+
end
|
57
81
|
|
58
|
-
|
82
|
+
alias intersection &
|
59
83
|
|
60
|
-
|
61
|
-
return
|
62
|
-
|
84
|
+
def -(other)
|
85
|
+
return difference_with_other_multi_range(other) if other.is_a?(MultiRange)
|
86
|
+
|
87
|
+
new_ranges = @ranges.dup
|
88
|
+
return MultiRange.new(new_ranges) if not overlaps_with_range?(other)
|
63
89
|
|
64
90
|
changed_size = 0
|
65
91
|
@ranges.each_with_index do |range, idx|
|
66
92
|
next if other.begin > range.end # 大於這個 range
|
67
93
|
break if other.end < range.begin # 小於這個 range
|
68
94
|
|
69
|
-
|
70
|
-
|
71
|
-
sub_range2_begin = if other.exclude_end?
|
72
|
-
other.end
|
73
|
-
else
|
74
|
-
other.end + (other.end.is_a?(Float) ? Float::EPSILON : 1)
|
75
|
-
end
|
76
|
-
sub_range2 = range.exclude_end? ? sub_range2_begin...range.end : sub_range2_begin..range.end
|
77
|
-
|
78
|
-
sub_ranges = []
|
79
|
-
sub_ranges << sub_range1 if sub_range1.begin <= sub_range1.end
|
80
|
-
sub_ranges << sub_range2 if sub_range2.begin <= sub_range2.end
|
81
|
-
|
95
|
+
sub_ranges = possible_sub_ranges_of(range, other)
|
82
96
|
new_ranges[idx + changed_size, 1] = sub_ranges
|
83
97
|
changed_size += sub_ranges.size - 1
|
84
98
|
break if other.end <= range.end # 沒有超過一個 range 的範圍
|
@@ -87,11 +101,15 @@ class MultiRange
|
|
87
101
|
return MultiRange.new(new_ranges)
|
88
102
|
end
|
89
103
|
|
104
|
+
alias difference -
|
105
|
+
|
90
106
|
def |(other)
|
91
107
|
other_ranges = other.is_a?(MultiRange) ? other.ranges : [other]
|
92
108
|
return MultiRange.new(@ranges + other_ranges).merge_overlaps
|
93
109
|
end
|
94
110
|
|
111
|
+
alias union |
|
112
|
+
|
95
113
|
def overlaps?(other)
|
96
114
|
multi_range = merge_overlaps
|
97
115
|
return multi_range.ranges != (multi_range - other).ranges
|
@@ -160,4 +178,45 @@ class MultiRange
|
|
160
178
|
return range1.end >= range2.begin if @is_float
|
161
179
|
return range1.end + 1 >= range2.begin
|
162
180
|
end
|
181
|
+
|
182
|
+
def difference_with_other_multi_range(other)
|
183
|
+
new_multi_range = dup
|
184
|
+
other.ranges.each{|range| new_multi_range -= range }
|
185
|
+
return new_multi_range
|
186
|
+
end
|
187
|
+
|
188
|
+
def possible_sub_ranges_of(range, other)
|
189
|
+
sub_range1 = range.begin...other.begin
|
190
|
+
|
191
|
+
sub_range2_begin = if other.exclude_end?
|
192
|
+
other.end
|
193
|
+
else
|
194
|
+
other.end + (other.end.is_a?(Float) ? Float::EPSILON : 1)
|
195
|
+
end
|
196
|
+
|
197
|
+
sub_range2 = range.exclude_end? ? sub_range2_begin...range.end : sub_range2_begin..range.end
|
198
|
+
|
199
|
+
sub_ranges = []
|
200
|
+
sub_ranges << sub_range1 if sub_range1.begin <= sub_range1.end
|
201
|
+
sub_ranges << sub_range2 if sub_range2.begin <= sub_range2.end
|
202
|
+
return sub_ranges
|
203
|
+
end
|
204
|
+
|
205
|
+
def overlaps_with_range?(range)
|
206
|
+
return false if @ranges.empty?
|
207
|
+
return false if range.begin > @ranges.last.end # larger than maxinum
|
208
|
+
return false if range.end < @ranges.first.begin # smaller than mininum
|
209
|
+
return true
|
210
|
+
end
|
211
|
+
|
212
|
+
def intersect_two_ranges(range_a, range_b)
|
213
|
+
ranges = [range_a, range_b]
|
214
|
+
start = ranges.map(&:begin).max
|
215
|
+
finish = ranges.map(&:end).min
|
216
|
+
if ranges.sort_by { |range| [range.end, range.exclude_end? ? 1 : 0] }.first.exclude_end?
|
217
|
+
start...finish
|
218
|
+
else
|
219
|
+
start..finish
|
220
|
+
end
|
221
|
+
end
|
163
222
|
end
|
data/lib/multi_range/version.rb
CHANGED
data/multi_range.gemspec
CHANGED
@@ -34,10 +34,14 @@ Gem::Specification.new do |spec|
|
|
34
34
|
'bug_tracker_uri' => 'https://github.com/khiav223577/multi_range/issues',
|
35
35
|
}
|
36
36
|
|
37
|
+
spec.required_ruby_version = '>= 2.0'
|
38
|
+
|
37
39
|
spec.add_development_dependency 'bundler', '>= 1.17', '< 3.x'
|
38
40
|
spec.add_development_dependency 'rake', '>= 10.5.0'
|
39
41
|
spec.add_development_dependency 'minitest', '~> 5.0'
|
42
|
+
spec.add_development_dependency 'minitest-color', '~> 0.0.2'
|
40
43
|
spec.add_development_dependency 'backports', '~> 3.15.0'
|
41
44
|
|
42
45
|
spec.add_dependency 'roulette-wheel-selection', '~> 1.1.1'
|
46
|
+
spec.add_dependency 'fast_interval_tree', '~> 0.2.0'
|
43
47
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: multi_range
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- khiav reoy
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-11-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -58,6 +58,20 @@ dependencies:
|
|
58
58
|
- - "~>"
|
59
59
|
- !ruby/object:Gem::Version
|
60
60
|
version: '5.0'
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: minitest-color
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - "~>"
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: 0.0.2
|
68
|
+
type: :development
|
69
|
+
prerelease: false
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - "~>"
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: 0.0.2
|
61
75
|
- !ruby/object:Gem::Dependency
|
62
76
|
name: backports
|
63
77
|
requirement: !ruby/object:Gem::Requirement
|
@@ -86,6 +100,20 @@ dependencies:
|
|
86
100
|
- - "~>"
|
87
101
|
- !ruby/object:Gem::Version
|
88
102
|
version: 1.1.1
|
103
|
+
- !ruby/object:Gem::Dependency
|
104
|
+
name: fast_interval_tree
|
105
|
+
requirement: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - "~>"
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: 0.2.0
|
110
|
+
type: :runtime
|
111
|
+
prerelease: false
|
112
|
+
version_requirements: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - "~>"
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: 0.2.0
|
89
117
|
description: Allow you to manipulate a group of ranges. Such as merging overlapping
|
90
118
|
ranges, doing ranges union and difference.
|
91
119
|
email:
|
@@ -105,7 +133,6 @@ files:
|
|
105
133
|
- bin/console
|
106
134
|
- bin/setup
|
107
135
|
- gemfiles/Gemfile
|
108
|
-
- gemfiles/ruby_1_8_7.gemfile
|
109
136
|
- lib/multi_range.rb
|
110
137
|
- lib/multi_range/version.rb
|
111
138
|
- multi_range.gemspec
|
@@ -126,7 +153,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
126
153
|
requirements:
|
127
154
|
- - ">="
|
128
155
|
- !ruby/object:Gem::Version
|
129
|
-
version: '0'
|
156
|
+
version: '2.0'
|
130
157
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
131
158
|
requirements:
|
132
159
|
- - ">="
|