range_list 0.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 60084bab9a2a689bd360d45fbd94f6915159f20779edfa4137758f5c29bb6e89
4
- data.tar.gz: 897265da656540c840314e498cc4bb792f0a384c54cc3f27f62a42506a8d5b1b
3
+ metadata.gz: c3bbec9e50fbfcf613d6371f9851c1410269819c738d362bbe0db73bf6ab1ed6
4
+ data.tar.gz: 48b42d5f37e67577cc92528b4f3e898b58f81f7781fa4ea388347572fb70d90a
5
5
  SHA512:
6
- metadata.gz: db4e3df7dc8e9be8da2c490050d3fd8b9f4b0b58f88581a32ca788d54b53fdef9f2d4a1d1d4bacacc22f7bdcdf095e4749240afe8b6b25e69b5963f45cc76d2e
7
- data.tar.gz: b8d0050152b5ac4221b41fc9223a845be3c8636d70a85a204c9e7bad455f155f3aa119ea0e635eb71982015918449de1da062d89ad8c8f30acf7e845769a3502
6
+ metadata.gz: c6f784320a3ab3b9b1c7714c9c7350aef3824b222a28ca0b1278722a728564b91c6c40faf3bbb8ea8a2926795daa56ca8f3db740cec6fd8833371bb60214ddd8
7
+ data.tar.gz: 98c20d0456e3c84432dfa9d3d09438c4887606dead589cfb1006407569b3c7f1330e0e3401fba5562b0c1c0c3dba408ad43fa0ea18f99232775ce7dcef69c3ee
data/Gemfile CHANGED
@@ -8,3 +8,11 @@ gemspec
8
8
  gem "rake", "~> 13.0"
9
9
 
10
10
  gem "minitest", "~> 5.0"
11
+
12
+ gem "standard", "~> 1.7"
13
+
14
+ gem "yard", "~> 0.9.27"
15
+
16
+ gem "simplecov", "~> 0.21.2"
17
+
18
+ gem "simplecov-cobertura", "~> 2.1"
data/Gemfile.lock ADDED
@@ -0,0 +1,67 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ range_list (1.2.0)
5
+ rbtree (~> 0.4.5)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ ast (2.4.2)
11
+ docile (1.4.0)
12
+ minitest (5.15.0)
13
+ parallel (1.21.0)
14
+ parser (3.1.1.0)
15
+ ast (~> 2.4.1)
16
+ rainbow (3.1.1)
17
+ rake (13.0.6)
18
+ rbtree (0.4.5)
19
+ regexp_parser (2.2.1)
20
+ rexml (3.2.5)
21
+ rubocop (1.25.1)
22
+ parallel (~> 1.10)
23
+ parser (>= 3.1.0.0)
24
+ rainbow (>= 2.2.2, < 4.0)
25
+ regexp_parser (>= 1.8, < 3.0)
26
+ rexml
27
+ rubocop-ast (>= 1.15.1, < 2.0)
28
+ ruby-progressbar (~> 1.7)
29
+ unicode-display_width (>= 1.4.0, < 3.0)
30
+ rubocop-ast (1.16.0)
31
+ parser (>= 3.1.1.0)
32
+ rubocop-performance (1.13.2)
33
+ rubocop (>= 1.7.0, < 2.0)
34
+ rubocop-ast (>= 0.4.0)
35
+ ruby-progressbar (1.11.0)
36
+ simplecov (0.21.2)
37
+ docile (~> 1.1)
38
+ simplecov-html (~> 0.11)
39
+ simplecov_json_formatter (~> 0.1)
40
+ simplecov-cobertura (2.1.0)
41
+ rexml
42
+ simplecov (~> 0.19)
43
+ simplecov-html (0.12.3)
44
+ simplecov_json_formatter (0.1.4)
45
+ standard (1.7.2)
46
+ rubocop (= 1.25.1)
47
+ rubocop-performance (= 1.13.2)
48
+ unicode-display_width (2.1.0)
49
+ webrick (1.7.0)
50
+ yard (0.9.27)
51
+ webrick (~> 1.7.0)
52
+
53
+ PLATFORMS
54
+ ruby
55
+ x86_64-darwin-19
56
+
57
+ DEPENDENCIES
58
+ minitest (~> 5.0)
59
+ rake (~> 13.0)
60
+ range_list!
61
+ simplecov (~> 0.21.2)
62
+ simplecov-cobertura (~> 2.1)
63
+ standard (~> 1.7)
64
+ yard (~> 0.9.27)
65
+
66
+ BUNDLED WITH
67
+ 2.3.8
data/README.md CHANGED
@@ -1,28 +1,61 @@
1
1
  # RangeList
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/range_list`. To experiment with that code, run `bin/console` for an interactive prompt.
3
+ RangeList is a library that can handle ranges quickly and appropriately.
4
4
 
5
- TODO: Delete this and the text above, and describe your gem
5
+ A pair of integers define a range, for example: [1, 5), this range includes integers: 1, 2, 3, and 4.
6
+ A range list is an aggregate of these ranges: [1, 5), [10, 11), [100, 201).
6
7
 
7
- ## Installation
8
+ RangeList use AVL Tree processing internally, so range operations are very fast.
8
9
 
9
- Add this line to your application's Gemfile:
10
+ [![Gem Version](https://badge.fury.io/rb/range_list.svg)](https://rubygems.org/gems/range_list)
11
+ [![Documentation](https://img.shields.io/badge/docs-YARD-blue.svg)](https://rubydoc.info/gems/range_list)
12
+ [![pipeline status](https://gitlab.com/songhuangcn/range_list/badges/main/pipeline.svg)](https://gitlab.com/songhuangcn/range_list/-/commits/main)
13
+ [![coverage report](https://gitlab.com/songhuangcn/range_list/badges/main/coverage.svg)](https://gitlab.com/songhuangcn/range_list/-/commits/main)
10
14
 
11
- ```ruby
12
- gem 'range_list'
13
- ```
15
+ ## Installation
14
16
 
15
- And then execute:
17
+ Add gem to your application's Gemfile:
16
18
 
17
- $ bundle install
19
+ ```shell
20
+ bundle add range_list
21
+ ```
18
22
 
19
23
  Or install it yourself as:
20
24
 
21
- $ gem install range_list
25
+ ```shell
26
+ gem install range_list
27
+ ```
22
28
 
23
29
  ## Usage
24
30
 
25
- TODO: Write usage instructions here
31
+ ```shell
32
+ range_list = RangeList.new
33
+ range_list.add([1, 5]).add([10, 20])
34
+ range_list.print # [1, 5) [10, 20)
35
+
36
+ range_list.add([20, 20])
37
+ range_list.print # [1, 5) [10, 20)
38
+
39
+ range_list.add([15, 21])
40
+ range_list.print # [1, 5) [10, 21)
41
+
42
+ range_list.remove([15, 21])
43
+ range_list.print # [1, 5) [10, 15)
44
+
45
+ range_list.contains_any?([8, 10]) # false
46
+ range_list.contains_any?([8, 11]) # true
47
+ range_list.contains_all?([8, 11]) # false
48
+ range_list.contains_all?([10, 11]) # true
49
+ range_list.contains?(5) # false
50
+ range_list.contains?(10) # true
51
+
52
+ # RangeList has the `Enumerable` ability, feel free to use `each`, `map`, `to_a`, `min`, `max` etc.
53
+ range_list.each { |range_start, range_end| puts "#{range_start} <= x < #{range_end}" }
54
+
55
+ range_list.remove([0, 100])
56
+ range_list.print #
57
+ range_list.to_a # []
58
+ ```
26
59
 
27
60
  ## Development
28
61
 
@@ -32,7 +65,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
32
65
 
33
66
  ## Contributing
34
67
 
35
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/range_list.
68
+ Bug reports and Merge requests are welcome on GitLab at https://gitlab.com/songhuangcn/range_list.
36
69
 
37
70
  ## License
38
71
 
@@ -0,0 +1,7 @@
1
+ class RangeList
2
+ # RangeList base error.
3
+ class Error < StandardError; end
4
+
5
+ # When the error raised, you should see the doc and check your argument.
6
+ class ArgumentError < Error; end
7
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module RangeList
4
- VERSION = "0.1.0"
3
+ class RangeList
4
+ VERSION = "1.2.0"
5
5
  end
data/lib/range_list.rb CHANGED
@@ -1,8 +1,155 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "rbtree"
3
4
  require_relative "range_list/version"
5
+ require_relative "range_list/errors"
4
6
 
5
- module RangeList
6
- class Error < StandardError; end
7
- # Your code goes here...
7
+ class RangeList
8
+ include Enumerable
9
+
10
+ def initialize
11
+ @tree = RBTree.new
12
+ end
13
+
14
+ # Add range into current range list.
15
+ # @param range [Array<Integer>] the range, first element is range start, second is range end.
16
+ # Range end need be greater or equal than range start.
17
+ # @return [RangeList]
18
+ # @raise [ArgumentError] when argement invalid
19
+ def add(range)
20
+ validate_range!(range)
21
+
22
+ # Return when range is empty.
23
+ return self if empty_range?(range)
24
+
25
+ # Get real range start.
26
+ start_floor_entry = tree.upper_bound(range[0])
27
+ range_start = if !start_floor_entry.nil? && start_floor_entry[1] >= range[0]
28
+ start_floor_entry[0]
29
+ else
30
+ range[0]
31
+ end
32
+
33
+ # Get real range end.
34
+ end_floor_entry = tree.upper_bound(range[1])
35
+ range_end = if !end_floor_entry.nil? && end_floor_entry[1] >= range[1]
36
+ end_floor_entry[1]
37
+ else
38
+ range[1]
39
+ end
40
+
41
+ # Insert or replace new range.
42
+ tree[range_start] = range_end
43
+
44
+ # Remove keys between range, exclude start, include end.
45
+ between_maps = tree.bound(range[0] + 1, range[1])
46
+ between_maps.to_a.each { |key, _value| tree.delete(key) }
47
+
48
+ self
49
+ end
50
+
51
+ # Remove range from current range list.
52
+ # @param (see #add)
53
+ # @return [RangeList]
54
+ # @raise (see #add)
55
+ def remove(range)
56
+ validate_range!(range)
57
+
58
+ # Return when range is empty.
59
+ return self if empty_range?(range)
60
+
61
+ # Insert end lower entry
62
+ end_lower_entry = tree.upper_bound(range[1] - 1)
63
+ if !end_lower_entry.nil? && end_lower_entry[1] > range[1]
64
+ tree[range[1]] = end_lower_entry[1]
65
+ end
66
+
67
+ # Relace start lower entry
68
+ start_lower_entry = tree.upper_bound(range[0] - 1)
69
+ if !start_lower_entry.nil? && start_lower_entry[1] > range[0]
70
+ tree[start_lower_entry[0]] = range[0]
71
+ end
72
+
73
+ # Remove keys between range, include start, exclude end
74
+ between_maps = tree.bound(range[0], range[1] - 1)
75
+ between_maps.to_a.each { |key, _value| tree.delete(key) }
76
+
77
+ self
78
+ end
79
+
80
+ # Print current range list.
81
+ # @return [void]
82
+ def print
83
+ puts map { |k, v| "[#{k}, #{v})" }.join(" ")
84
+ end
85
+
86
+ # Returns true if current ranges contains all elements of the range.
87
+ # @param (see #add)
88
+ # @return [Boolean]
89
+ # @note return false if the argument range is empty
90
+ # @raise (see #add)
91
+ def contains_all?(range)
92
+ validate_range!(range)
93
+
94
+ # Return false when range is empty.
95
+ return false if empty_range?(range)
96
+
97
+ start_floor_entry = tree.upper_bound(range[0])
98
+ !start_floor_entry.nil? && start_floor_entry[1] >= range[1]
99
+ end
100
+
101
+ # Returns true if current ranges contains any element of the range.
102
+ # @param (see #add)
103
+ # @return [Boolean]
104
+ # @note return false if the argument range is empty
105
+ # @raise (see #add)
106
+ def contains_any?(range)
107
+ validate_range!(range)
108
+
109
+ # Return false when range is empty.
110
+ return false if empty_range?(range)
111
+
112
+ end_lower_entry = tree.upper_bound(range[1] - 1)
113
+ !end_lower_entry.nil? && end_lower_entry[1] > range[0]
114
+ end
115
+
116
+ # Returns true if current ranges contains the element.
117
+ # @param element [Integer] the range element
118
+ # @return [Boolean]
119
+ # @raise (see #add)
120
+ def contains?(element)
121
+ raise ArgumentError, "`element` should be `Integer` type." unless element.is_a?(Integer)
122
+
123
+ floor_entry = tree.upper_bound(element)
124
+ !floor_entry.nil? && floor_entry[1] > element
125
+ end
126
+
127
+ # Give RangeList iterative ability.
128
+ # @yield [range_start, range_end] give the range element to the block
129
+ def each(&block)
130
+ if block
131
+ tree.each(&block)
132
+ else
133
+ enum_for(:each)
134
+ end
135
+ end
136
+
137
+ # @!visibility private
138
+ def inspect
139
+ to_a.inspect
140
+ end
141
+
142
+ private
143
+
144
+ attr_reader :tree
145
+
146
+ def validate_range!(range)
147
+ raise ArgumentError, "`range` should be `Array` type." unless range.is_a?(Array)
148
+ raise ArgumentError, "`range` size should be equal to 2." unless range.size == 2
149
+ raise ArgumentError, "All elements of `range` should be `Integer`." unless range.all? { |item| item.is_a?(Integer) }
150
+ end
151
+
152
+ def empty_range?(range)
153
+ range[0] >= range[1]
154
+ end
8
155
  end
data/range_list.gemspec CHANGED
@@ -18,6 +18,7 @@ Gem::Specification.new do |spec|
18
18
 
19
19
  spec.metadata["homepage_uri"] = spec.homepage
20
20
  spec.metadata["source_code_uri"] = "https://gitlab.com/songhuangcn/range_list"
21
+ spec.metadata["documentation_uri"] = "https://rubydoc.info/gems/range_list"
21
22
  # spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
22
23
 
23
24
  # Specify which files should be added to the gem when it is released.
@@ -33,6 +34,7 @@ Gem::Specification.new do |spec|
33
34
 
34
35
  # Uncomment to register a new dependency of your gem
35
36
  # spec.add_dependency "example-gem", "~> 1.0"
37
+ spec.add_dependency "rbtree", "~> 0.4.5"
36
38
 
37
39
  # For more information and examples about making a new gem, check out our
38
40
  # guide at: https://bundler.io/guides/creating_gem.html
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: range_list
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Song Huang
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-02-25 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2022-02-26 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rbtree
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.4.5
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.4.5
13
27
  description:
14
28
  email:
15
29
  - songhuangcn@gmail.com
@@ -18,19 +32,21 @@ extensions: []
18
32
  extra_rdoc_files: []
19
33
  files:
20
34
  - Gemfile
35
+ - Gemfile.lock
21
36
  - LICENSE.txt
22
37
  - README.md
23
38
  - Rakefile
24
39
  - lib/range_list.rb
40
+ - lib/range_list/errors.rb
25
41
  - lib/range_list/version.rb
26
42
  - range_list.gemspec
27
- - sig/range_list.rbs
28
43
  homepage: https://gitlab.com/songhuangcn/range_list
29
44
  licenses:
30
45
  - MIT
31
46
  metadata:
32
47
  homepage_uri: https://gitlab.com/songhuangcn/range_list
33
48
  source_code_uri: https://gitlab.com/songhuangcn/range_list
49
+ documentation_uri: https://rubydoc.info/gems/range_list
34
50
  post_install_message:
35
51
  rdoc_options: []
36
52
  require_paths:
data/sig/range_list.rbs DELETED
@@ -1,4 +0,0 @@
1
- module RangeList
2
- VERSION: String
3
- # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
- end