multi_range 1.0.0 → 1.3.1

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.
data/Rakefile CHANGED
@@ -1,10 +1,10 @@
1
- require 'bundler/gem_tasks'
2
- require 'rake/testtask'
3
-
4
- Rake::TestTask.new(:test) do |t|
5
- t.libs << 'test'
6
- t.libs << 'lib'
7
- t.test_files = FileList['test/**/*_test.rb']
8
- end
9
-
10
- task :default => :test
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << 'test'
6
+ t.libs << 'lib'
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ end
9
+
10
+ task :default => :test
data/bin/console CHANGED
@@ -1,14 +1,14 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'bundler/setup'
4
- require 'multi_range'
5
-
6
- # You can add fixtures and/or initialization code here to make experimenting
7
- # with your gem easier. You can also use a different console, if you like.
8
-
9
- # (If you use this, don't forget to add pry to your Gemfile!)
10
- # require "pry"
11
- # Pry.start
12
-
13
- require 'irb'
14
- IRB.start
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'multi_range'
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require 'irb'
14
+ IRB.start
data/bin/setup CHANGED
@@ -1,8 +1,8 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
- IFS=$'\n\t'
4
- set -vx
5
-
6
- bundle install --gemfile=gemfiles/4.2.gemfile
7
-
8
- # Do any other automated setup that you need to do here
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install --gemfile=gemfiles/4.2.gemfile
7
+
8
+ # Do any other automated setup that you need to do here
data/gemfiles/Gemfile CHANGED
@@ -1,9 +1,9 @@
1
- source 'https://rubygems.org'
2
-
3
- # Specify your gem's dependencies in rails_or.gemspec
4
-
5
- group :test do
6
- gem 'simplecov'
7
- end
8
-
9
- gemspec :path => '../'
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rails_or.gemspec
4
+
5
+ group :test do
6
+ gem 'simplecov'
7
+ end
8
+
9
+ gemspec :path => '../'
@@ -1,10 +1,10 @@
1
- source 'https://rubygems.org'
2
-
3
- # Specify your gem's dependencies in rails_or.gemspec
4
-
5
- group :test do
6
- gem 'simplecov'
7
- gem 'minitest', '~> 5.11.3'
8
- end
9
-
10
- gemspec :path => '../'
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rails_or.gemspec
4
+
5
+ group :test do
6
+ gem 'simplecov'
7
+ gem 'minitest', '~> 5.11.3'
8
+ end
9
+
10
+ gemspec :path => '../'
data/lib/multi_range.rb CHANGED
@@ -1,143 +1,174 @@
1
- # frozen_string_literal: true
2
-
3
- require 'multi_range/version'
4
- require 'roulette-wheel-selection'
5
-
6
- if not Range.method_defined?(:size)
7
- warn "Please backports Range#size method to use multi_range gem.\n" \
8
- "You can use backports gem and add the following lines to your program:\n" \
9
- "require 'backports/1.9.2/float/infinity'\n" \
10
- "require 'backports/2.0.0/range/size'"
11
- end
12
-
13
- if not Enumerable.method_defined?(:to_h)
14
- warn "Please backports Enumerable#to_h method to use multi_range gem.\n" \
15
- "You can use backports gem and add the following lines to your program:\n" \
16
- "require 'backports/2.1.0/enumerable/to_h'"
17
- end
18
-
19
- class MultiRange
20
- INDEX_WITH_DEFAULT = Object.new
21
-
22
- attr_reader :ranges
23
-
24
- def initialize(ranges)
25
- @ranges = ranges.map{|s| s.is_a?(Integer) ? s..s : s }.sort_by(&:begin).freeze
26
- end
27
-
28
- def flatten
29
- return MultiRange.new([]) if @ranges.size == 0
30
-
31
- new_ranges = []
32
- current_range = nil
33
-
34
- @ranges.each do |range|
35
- next current_range = range if current_range == nil
36
- next if range.max <= current_range.max
37
-
38
- if current_range.max + 1 < range.min
39
- new_ranges << current_range
40
- current_range = range
41
- else
42
- current_range = current_range.min..range.max
43
- end
44
- end
45
-
46
- new_ranges << current_range
47
- return MultiRange.new(new_ranges)
48
- end
49
-
50
- def -(other)
51
- if other.is_a?(MultiRange)
52
- new_multi_range = dup
53
- other.ranges.each{|range| new_multi_range -= range }
54
- return new_multi_range
55
- end
56
-
57
- new_ranges = @ranges.dup
58
-
59
- return MultiRange.new(new_ranges) if new_ranges.empty?
60
- return MultiRange.new(new_ranges) if other.min > @ranges.last.max # 大於最大值
61
- return MultiRange.new(new_ranges) if other.max < @ranges.first.min # 小於最小值
62
-
63
- changed_size = 0
64
- @ranges.each_with_index do |range, idx|
65
- next if other.min > range.max # 大於這個 range
66
- break if other.max < range.min # 小於這個 range
67
-
68
- sub_range1 = range.min...other.min
69
- sub_range2 = (other.max + 1)..range.max
70
-
71
- sub_ranges = []
72
- sub_ranges << sub_range1 if sub_range1.any?
73
- sub_ranges << sub_range2 if sub_range2.any?
74
-
75
- new_ranges[idx + changed_size, 1] = sub_ranges
76
- changed_size += sub_ranges.size - 1
77
- break if other.max <= range.max # 沒有超過一個 range 的範圍
78
- end
79
-
80
- return MultiRange.new(new_ranges)
81
- end
82
-
83
- def |(other)
84
- other_ranges = other.is_a?(MultiRange) ? other.ranges : [other]
85
- return MultiRange.new(@ranges + other_ranges).flatten
86
- end
87
-
88
- def overlaps?(other)
89
- multi_range = flatten
90
- return multi_range.size != (multi_range - other).size
91
- end
92
-
93
- def sample
94
- range = RouletteWheelSelection.sample(@ranges.map{|s| [s, s.size] }.to_h)
95
- return nil if range == nil
96
- return rand(range.max - range.min) + range.min
97
- end
98
-
99
- def size
100
- @ranges.inject(0){|sum, v| sum + v.size }
101
- end
102
-
103
- def any?
104
- @ranges.any?
105
- end
106
-
107
- def index_with(default = INDEX_WITH_DEFAULT)
108
- if block_given?
109
- fail ArgumentError, 'wrong number of arguments (given 1, expected 0)' if default != INDEX_WITH_DEFAULT
110
- return map{|s| [s, yield(s)] }.to_h
111
- end
112
-
113
- return to_enum(:index_with){ size } if default == INDEX_WITH_DEFAULT
114
- return map{|s| [s, default] }.to_h
115
- end
116
-
117
- def each
118
- return to_enum(:each){ size } if !block_given?
119
-
120
- ranges.each do |range|
121
- range.each{|s| yield(s) }
122
- end
123
- end
124
-
125
- def map
126
- return to_enum(:map){ size } if !block_given?
127
- return each.map{|s| yield(s) }
128
- end
129
-
130
- def to_a
131
- each.to_a
132
- end
133
-
134
- def min
135
- range = @ranges.first
136
- return range.min if range
137
- end
138
-
139
- def max
140
- range = @ranges.last
141
- return range.max if range
142
- end
143
- end
1
+ # frozen_string_literal: true
2
+
3
+ require 'multi_range/version'
4
+ require 'roulette-wheel-selection'
5
+
6
+ if not Range.method_defined?(:size)
7
+ warn "Please backports Range#size method to use multi_range gem.\n" \
8
+ "You can use backports gem and add the following lines to your program:\n" \
9
+ "require 'backports/1.9.2/float/infinity'\n" \
10
+ "require 'backports/2.0.0/range/size'"
11
+ end
12
+
13
+ if not Enumerable.method_defined?(:to_h)
14
+ warn "Please backports Enumerable#to_h method to use multi_range gem.\n" \
15
+ "You can use backports gem and add the following lines to your program:\n" \
16
+ "require 'backports/2.1.0/enumerable/to_h'"
17
+ end
18
+
19
+ class MultiRange
20
+ INDEX_WITH_DEFAULT = Object.new
21
+
22
+ attr_reader :ranges
23
+
24
+ def initialize(ranges)
25
+ @ranges = ranges.map{|s| s.is_a?(Numeric) ? s..s : s }.sort_by(&:begin).freeze
26
+ @is_float = @ranges.any?{|range| range.begin.is_a?(Float) || range.end.is_a?(Float) }
27
+ end
28
+
29
+ def merge_overlaps(merge_same_value = true)
30
+ return MultiRange.new([]) if @ranges.size == 0
31
+
32
+ new_ranges = []
33
+ current_range = nil
34
+
35
+ @ranges.each do |range|
36
+ next current_range = range if current_range == nil
37
+ next if range.end <= current_range.end
38
+
39
+ if can_combine?(current_range, range, merge_same_value)
40
+ current_range = range.exclude_end? ? current_range.begin...range.end : current_range.begin..range.end
41
+ else
42
+ new_ranges << current_range
43
+ current_range = range
44
+ end
45
+ end
46
+
47
+ new_ranges << current_range
48
+ return MultiRange.new(new_ranges)
49
+ end
50
+
51
+ def -(other)
52
+ return difference_with_other_multi_range(other) if other.is_a?(MultiRange)
53
+
54
+ new_ranges = @ranges.dup
55
+ return MultiRange.new(new_ranges) if not overlaps_with_range?(other)
56
+
57
+ changed_size = 0
58
+ @ranges.each_with_index do |range, idx|
59
+ next if other.begin > range.end # 大於這個 range
60
+ break if other.end < range.begin # 小於這個 range
61
+
62
+ sub_ranges = possible_sub_ranges_of(range, other)
63
+ new_ranges[idx + changed_size, 1] = sub_ranges
64
+ changed_size += sub_ranges.size - 1
65
+ break if other.end <= range.end # 沒有超過一個 range 的範圍
66
+ end
67
+
68
+ return MultiRange.new(new_ranges)
69
+ end
70
+
71
+ def |(other)
72
+ other_ranges = other.is_a?(MultiRange) ? other.ranges : [other]
73
+ return MultiRange.new(@ranges + other_ranges).merge_overlaps
74
+ end
75
+
76
+ def overlaps?(other)
77
+ multi_range = merge_overlaps
78
+ return multi_range.ranges != (multi_range - other).ranges
79
+ end
80
+
81
+ def sample
82
+ range = RouletteWheelSelection.sample(@ranges.map{|s| [s, s.size] }.to_h)
83
+ return nil if range == nil
84
+ return rand(range.max - range.min + 1) + range.min
85
+ end
86
+
87
+ def size
88
+ @ranges.inject(0){|sum, v| sum + v.size }
89
+ end
90
+
91
+ def any?
92
+ @ranges.any?
93
+ end
94
+
95
+ def index_with(default = INDEX_WITH_DEFAULT)
96
+ if block_given?
97
+ fail ArgumentError, 'wrong number of arguments (given 1, expected 0)' if default != INDEX_WITH_DEFAULT
98
+ return map{|s| [s, yield(s)] }.to_h
99
+ end
100
+
101
+ return to_enum(:index_with){ size } if default == INDEX_WITH_DEFAULT
102
+ return map{|s| [s, default] }.to_h
103
+ end
104
+
105
+ def each
106
+ return to_enum(:each){ size } if !block_given?
107
+
108
+ ranges.each do |range|
109
+ range.each{|s| yield(s) }
110
+ end
111
+ end
112
+
113
+ def map
114
+ return to_enum(:map){ size } if !block_given?
115
+ return each.map{|s| yield(s) }
116
+ end
117
+
118
+ def to_a
119
+ each.to_a
120
+ end
121
+
122
+ def min
123
+ range = @ranges.first
124
+ return range.min if range
125
+ end
126
+
127
+ def max
128
+ range = @ranges.last
129
+ return range.max if range
130
+ end
131
+
132
+ def contain_overlaps?
133
+ merge_overlaps(false).ranges != ranges
134
+ end
135
+
136
+ private
137
+
138
+ # make sure that range1.begin <= range2.begin
139
+ def can_combine?(range1, range2, merge_same_value)
140
+ return merge_same_value if range1.end == range2.begin and range1.exclude_end?
141
+ return range1.end >= range2.begin if @is_float
142
+ return range1.end + 1 >= range2.begin
143
+ end
144
+
145
+ def difference_with_other_multi_range(other)
146
+ new_multi_range = dup
147
+ other.ranges.each{|range| new_multi_range -= range }
148
+ return new_multi_range
149
+ end
150
+
151
+ def possible_sub_ranges_of(range, other)
152
+ sub_range1 = range.begin...other.begin
153
+
154
+ sub_range2_begin = if other.exclude_end?
155
+ other.end
156
+ else
157
+ other.end + (other.end.is_a?(Float) ? Float::EPSILON : 1)
158
+ end
159
+
160
+ sub_range2 = range.exclude_end? ? sub_range2_begin...range.end : sub_range2_begin..range.end
161
+
162
+ sub_ranges = []
163
+ sub_ranges << sub_range1 if sub_range1.begin <= sub_range1.end
164
+ sub_ranges << sub_range2 if sub_range2.begin <= sub_range2.end
165
+ return sub_ranges
166
+ end
167
+
168
+ def overlaps_with_range?(range)
169
+ return false if @ranges.empty?
170
+ return false if range.begin > @ranges.last.end # larger than maxinum
171
+ return false if range.end < @ranges.first.begin # smaller than mininum
172
+ return true
173
+ end
174
+ end
@@ -1,5 +1,5 @@
1
- # frozen_string_literal: true
2
-
3
- class MultiRange
4
- VERSION = '1.0.0'
5
- end
1
+ # frozen_string_literal: true
2
+
3
+ class MultiRange
4
+ VERSION = '1.3.1'
5
+ end
data/multi_range.gemspec CHANGED
@@ -1,43 +1,43 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
3
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'multi_range/version'
5
-
6
- Gem::Specification.new do |spec|
7
- spec.name = 'multi_range'
8
- spec.version = MultiRange::VERSION
9
- spec.authors = ['khiav reoy']
10
- spec.email = ['mrtmrt15xn@yahoo.com.tw']
11
-
12
- spec.summary = 'Provides cross-rails methods for you to upgrade rails, backport features, create easy-to-maintain gems, and so on.'
13
- spec.description = 'Provides cross-rails methods for you to upgrade rails, backport features, create easy-to-maintain gems, and so on.'
14
- spec.homepage = 'https://github.com/khiav223577/multi_range'
15
- spec.license = 'MIT'
16
-
17
- # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
18
- # delete this section to allow pushing this gem to any host.
19
- # if spec.respond_to?(:metadata)
20
- # spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com'"
21
- # else
22
- # raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
23
- # end
24
-
25
- spec.files = `git ls-files -z`.split("\x0").reject{|f| f.match(%r{^(test|spec|features)/}) }
26
- spec.bindir = 'exe'
27
- spec.executables = spec.files.grep(%r{^exe/}){|f| File.basename(f) }
28
- spec.require_paths = ['lib']
29
- spec.metadata = {
30
- 'homepage_uri' => 'https://github.com/khiav223577/multi_range',
31
- 'changelog_uri' => 'https://github.com/khiav223577/multi_range/blob/master/CHANGELOG.md',
32
- 'source_code_uri' => 'https://github.com/khiav223577/multi_range',
33
- 'documentation_uri' => 'https://www.rubydoc.info/gems/multi_range',
34
- 'bug_tracker_uri' => 'https://github.com/khiav223577/multi_range/issues',
35
- }
36
-
37
- spec.add_development_dependency 'bundler', '>= 1.17', '< 3.x'
38
- spec.add_development_dependency 'rake', '>= 10.5.0'
39
- spec.add_development_dependency 'minitest', '~> 5.0'
40
- spec.add_development_dependency 'backports', '~> 3.15.0'
41
-
42
- spec.add_dependency 'roulette-wheel-selection', '~> 1.1.1'
43
- end
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'multi_range/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'multi_range'
8
+ spec.version = MultiRange::VERSION
9
+ spec.authors = ['khiav reoy']
10
+ spec.email = ['mrtmrt15xn@yahoo.com.tw']
11
+
12
+ spec.summary = 'Allow you to manipulate a group of ranges. Such as merging overlapping ranges, doing ranges union and difference.'
13
+ spec.description = 'Allow you to manipulate a group of ranges. Such as merging overlapping ranges, doing ranges union and difference.'
14
+ spec.homepage = 'https://github.com/khiav223577/multi_range'
15
+ spec.license = 'MIT'
16
+
17
+ # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
18
+ # delete this section to allow pushing this gem to any host.
19
+ # if spec.respond_to?(:metadata)
20
+ # spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com'"
21
+ # else
22
+ # raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
23
+ # end
24
+
25
+ spec.files = `git ls-files -z`.split("\x0").reject{|f| f.match(%r{^(test|spec|features)/}) }
26
+ spec.bindir = 'exe'
27
+ spec.executables = spec.files.grep(%r{^exe/}){|f| File.basename(f) }
28
+ spec.require_paths = ['lib']
29
+ spec.metadata = {
30
+ 'homepage_uri' => 'https://github.com/khiav223577/multi_range',
31
+ 'changelog_uri' => 'https://github.com/khiav223577/multi_range/blob/master/CHANGELOG.md',
32
+ 'source_code_uri' => 'https://github.com/khiav223577/multi_range',
33
+ 'documentation_uri' => 'https://www.rubydoc.info/gems/multi_range',
34
+ 'bug_tracker_uri' => 'https://github.com/khiav223577/multi_range/issues',
35
+ }
36
+
37
+ spec.add_development_dependency 'bundler', '>= 1.17', '< 3.x'
38
+ spec.add_development_dependency 'rake', '>= 10.5.0'
39
+ spec.add_development_dependency 'minitest', '~> 5.0'
40
+ spec.add_development_dependency 'backports', '~> 3.15.0'
41
+
42
+ spec.add_dependency 'roulette-wheel-selection', '~> 1.1.1'
43
+ end