range_list 0.1.0 → 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.
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