multi_range 0.0.3 → 1.3.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.
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
@@ -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
@@ -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 => '../'
@@ -1,103 +1,163 @@
1
- require 'multi_range/version'
2
- require 'roulette-wheel-selection'
3
-
4
- if not Range.method_defined?(:size)
5
- warn "Please backports Range#size method to use multi_range gem.\n" \
6
- "You can use backports gem and add the following lines to your program:\n" \
7
- "require 'backports/1.9.2/float/infinity'\n" \
8
- "require 'backports/2.0.0/range/size'"
9
- end
10
-
11
- if not Enumerable.method_defined?(:to_h)
12
- warn "Please backports Enumerable#to_h method to use multi_range gem.\n" \
13
- "You can use backports gem and add the following lines to your program:\n" \
14
- "require 'backports/2.1.0/enumerable/to_h'"
15
- end
16
-
17
- class MultiRange
18
- INDEX_WITH_DEFAULT = Object.new
19
-
20
- attr_reader :ranges
21
-
22
- def initialize(ranges) # range 要由小到大排序,且各 range 不能重疊
23
- @ranges = ranges.map{|s| s.is_a?(Integer) ? s..s : s }.freeze
24
- end
25
-
26
- def -(other)
27
- new_ranges = @ranges.dup
28
-
29
- return MultiRange.new(new_ranges) if new_ranges.empty?
30
- return MultiRange.new(new_ranges) if other.min > @ranges.last.max # 大於最大值
31
- return MultiRange.new(new_ranges) if other.max < @ranges.first.min # 小於最小值
32
-
33
- changed_size = 0
34
- @ranges.each_with_index do |range, idx|
35
- next if other.min > range.max # 大於這個 range
36
- break if other.max < range.min # 小於這個 range
37
-
38
- sub_range1 = range.min...other.min
39
- sub_range2 = (other.max + 1)..range.max
40
-
41
- sub_ranges = []
42
- sub_ranges << sub_range1 if sub_range1.any?
43
- sub_ranges << sub_range2 if sub_range2.any?
44
-
45
- new_ranges[idx + changed_size, 1] = sub_ranges
46
- changed_size += sub_ranges.size - 1
47
- break if other.max <= range.max # 沒有超過一個 range 的範圍
48
- end
49
-
50
- return MultiRange.new(new_ranges)
51
- end
52
-
53
- def sample
54
- range = RouletteWheelSelection.sample(@ranges.map{|s| [s, s.size] }.to_h)
55
- return nil if range == nil
56
- return rand(range.max - range.min) + range.min
57
- end
58
-
59
- def size
60
- @ranges.inject(0){|sum, v| sum + v.size }
61
- end
62
-
63
- def any?
64
- @ranges.any?
65
- end
66
-
67
- def index_with(default = INDEX_WITH_DEFAULT)
68
- if block_given?
69
- fail ArgumentError, 'wrong number of arguments (given 1, expected 0)' if default != INDEX_WITH_DEFAULT
70
- return map{|s| [s, yield(s)] }.to_h
71
- end
72
-
73
- return to_enum(:index_with){ size } if default == INDEX_WITH_DEFAULT
74
- return map{|s| [s, default] }.to_h
75
- end
76
-
77
- def each
78
- return to_enum(:each){ size } if !block_given?
79
-
80
- ranges.each do |range|
81
- range.each{|s| yield(s) }
82
- end
83
- end
84
-
85
- def map
86
- return to_enum(:map){ size } if !block_given?
87
- return each.map{|s| yield(s) }
88
- end
89
-
90
- def to_a
91
- each.to_a
92
- end
93
-
94
- def min
95
- range = @ranges.first
96
- return range.min if range
97
- end
98
-
99
- def max
100
- range = @ranges.last
101
- return range.max if range
102
- end
103
- 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
+ if other.is_a?(MultiRange)
53
+ new_multi_range = dup
54
+ other.ranges.each{|range| new_multi_range -= range }
55
+ return new_multi_range
56
+ end
57
+
58
+ new_ranges = @ranges.dup
59
+
60
+ return MultiRange.new(new_ranges) if new_ranges.empty?
61
+ return MultiRange.new(new_ranges) if other.begin > @ranges.last.end # 大於最大值
62
+ return MultiRange.new(new_ranges) if other.end < @ranges.first.begin # 小於最小值
63
+
64
+ changed_size = 0
65
+ @ranges.each_with_index do |range, idx|
66
+ next if other.begin > range.end # 大於這個 range
67
+ break if other.end < range.begin # 小於這個 range
68
+
69
+ sub_range1 = range.begin...other.begin
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
+
82
+ new_ranges[idx + changed_size, 1] = sub_ranges
83
+ changed_size += sub_ranges.size - 1
84
+ break if other.end <= range.end # 沒有超過一個 range 的範圍
85
+ end
86
+
87
+ return MultiRange.new(new_ranges)
88
+ end
89
+
90
+ def |(other)
91
+ other_ranges = other.is_a?(MultiRange) ? other.ranges : [other]
92
+ return MultiRange.new(@ranges + other_ranges).merge_overlaps
93
+ end
94
+
95
+ def overlaps?(other)
96
+ multi_range = merge_overlaps
97
+ return multi_range.ranges != (multi_range - other).ranges
98
+ end
99
+
100
+ def sample
101
+ range = RouletteWheelSelection.sample(@ranges.map{|s| [s, s.size] }.to_h)
102
+ return nil if range == nil
103
+ return rand(range.max - range.min) + range.min
104
+ end
105
+
106
+ def size
107
+ @ranges.inject(0){|sum, v| sum + v.size }
108
+ end
109
+
110
+ def any?
111
+ @ranges.any?
112
+ end
113
+
114
+ def index_with(default = INDEX_WITH_DEFAULT)
115
+ if block_given?
116
+ fail ArgumentError, 'wrong number of arguments (given 1, expected 0)' if default != INDEX_WITH_DEFAULT
117
+ return map{|s| [s, yield(s)] }.to_h
118
+ end
119
+
120
+ return to_enum(:index_with){ size } if default == INDEX_WITH_DEFAULT
121
+ return map{|s| [s, default] }.to_h
122
+ end
123
+
124
+ def each
125
+ return to_enum(:each){ size } if !block_given?
126
+
127
+ ranges.each do |range|
128
+ range.each{|s| yield(s) }
129
+ end
130
+ end
131
+
132
+ def map
133
+ return to_enum(:map){ size } if !block_given?
134
+ return each.map{|s| yield(s) }
135
+ end
136
+
137
+ def to_a
138
+ each.to_a
139
+ end
140
+
141
+ def min
142
+ range = @ranges.first
143
+ return range.min if range
144
+ end
145
+
146
+ def max
147
+ range = @ranges.last
148
+ return range.max if range
149
+ end
150
+
151
+ def contain_overlaps?
152
+ merge_overlaps(false).ranges != ranges
153
+ end
154
+
155
+ private
156
+
157
+ # make sure that range1.begin <= range2.begin
158
+ def can_combine?(range1, range2, merge_same_value)
159
+ return merge_same_value if range1.end == range2.begin and range1.exclude_end?
160
+ return range1.end >= range2.begin if @is_float
161
+ return range1.end + 1 >= range2.begin
162
+ end
163
+ end
@@ -1,3 +1,5 @@
1
- class MultiRange
2
- VERSION = '0.0.3'
3
- end
1
+ # frozen_string_literal: true
2
+
3
+ class MultiRange
4
+ VERSION = '1.3.0'
5
+ end
@@ -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