multi_range 0.0.2 → 1.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.
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,89 +1,154 @@
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 }
24
- end
25
-
26
- def -(other)
27
- new_ranges = @ranges.clone
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.new('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
- 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
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)
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 = other.exclude_end? ? other.end : other.end + (other.end.is_a?(Float) ? Float::EPSILON : 1)
72
+ sub_range2 = range.exclude_end? ? sub_range2_begin...range.end : sub_range2_begin..range.end
73
+
74
+ sub_ranges = []
75
+ sub_ranges << sub_range1 if sub_range1.begin <= sub_range1.end
76
+ sub_ranges << sub_range2 if sub_range2.begin <= sub_range2.end
77
+
78
+ new_ranges[idx + changed_size, 1] = sub_ranges
79
+ changed_size += sub_ranges.size - 1
80
+ break if other.end <= range.end # 沒有超過一個 range 的範圍
81
+ end
82
+
83
+ return MultiRange.new(new_ranges)
84
+ end
85
+
86
+ def |(other)
87
+ other_ranges = other.is_a?(MultiRange) ? other.ranges : [other]
88
+ return MultiRange.new(@ranges + other_ranges).merge_overlaps
89
+ end
90
+
91
+ def overlaps?(other)
92
+ multi_range = merge_overlaps
93
+ return multi_range.ranges != (multi_range - other).ranges
94
+ end
95
+
96
+ def sample
97
+ range = RouletteWheelSelection.sample(@ranges.map{|s| [s, s.size] }.to_h)
98
+ return nil if range == nil
99
+ return rand(range.max - range.min) + range.min
100
+ end
101
+
102
+ def size
103
+ @ranges.inject(0){|sum, v| sum + v.size }
104
+ end
105
+
106
+ def any?
107
+ @ranges.any?
108
+ end
109
+
110
+ def index_with(default = INDEX_WITH_DEFAULT)
111
+ if block_given?
112
+ fail ArgumentError, 'wrong number of arguments (given 1, expected 0)' if default != INDEX_WITH_DEFAULT
113
+ return map{|s| [s, yield(s)] }.to_h
114
+ end
115
+
116
+ return to_enum(:index_with){ size } if default == INDEX_WITH_DEFAULT
117
+ return map{|s| [s, default] }.to_h
118
+ end
119
+
120
+ def each
121
+ return to_enum(:each){ size } if !block_given?
122
+
123
+ ranges.each do |range|
124
+ range.each{|s| yield(s) }
125
+ end
126
+ end
127
+
128
+ def map
129
+ return to_enum(:map){ size } if !block_given?
130
+ return each.map{|s| yield(s) }
131
+ end
132
+
133
+ def to_a
134
+ each.to_a
135
+ end
136
+
137
+ def min
138
+ range = @ranges.first
139
+ return range.min if range
140
+ end
141
+
142
+ def max
143
+ range = @ranges.last
144
+ return range.max if range
145
+ end
146
+
147
+ private
148
+
149
+ # make sure that range1.begin <= range2.begin
150
+ def can_combine?(range1, range2)
151
+ return range1.end >= range2.begin if @is_float
152
+ return range1.end + 1 >= range2.begin
153
+ end
154
+ end
@@ -1,3 +1,5 @@
1
- class MultiRange
2
- VERSION = '0.0.2'
3
- end
1
+ # frozen_string_literal: true
2
+
3
+ class MultiRange
4
+ VERSION = '1.2.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