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 +4 -4
- data/Gemfile +8 -0
- data/Gemfile.lock +67 -0
- data/README.md +45 -12
- data/lib/range_list/errors.rb +7 -0
- data/lib/range_list/version.rb +2 -2
- data/lib/range_list.rb +150 -3
- data/range_list.gemspec +2 -0
- metadata +20 -4
- data/sig/range_list.rbs +0 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c3bbec9e50fbfcf613d6371f9851c1410269819c738d362bbe0db73bf6ab1ed6
|
4
|
+
data.tar.gz: 48b42d5f37e67577cc92528b4f3e898b58f81f7781fa4ea388347572fb70d90a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c6f784320a3ab3b9b1c7714c9c7350aef3824b222a28ca0b1278722a728564b91c6c40faf3bbb8ea8a2926795daa56ca8f3db740cec6fd8833371bb60214ddd8
|
7
|
+
data.tar.gz: 98c20d0456e3c84432dfa9d3d09438c4887606dead589cfb1006407569b3c7f1330e0e3401fba5562b0c1c0c3dba408ad43fa0ea18f99232775ce7dcef69c3ee
|
data/Gemfile
CHANGED
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
|
-
|
3
|
+
RangeList is a library that can handle ranges quickly and appropriately.
|
4
4
|
|
5
|
-
|
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
|
-
|
8
|
+
RangeList use AVL Tree processing internally, so range operations are very fast.
|
8
9
|
|
9
|
-
|
10
|
+
[](https://rubygems.org/gems/range_list)
|
11
|
+
[](https://rubydoc.info/gems/range_list)
|
12
|
+
[](https://gitlab.com/songhuangcn/range_list/-/commits/main)
|
13
|
+
[](https://gitlab.com/songhuangcn/range_list/-/commits/main)
|
10
14
|
|
11
|
-
|
12
|
-
gem 'range_list'
|
13
|
-
```
|
15
|
+
## Installation
|
14
16
|
|
15
|
-
|
17
|
+
Add gem to your application's Gemfile:
|
16
18
|
|
17
|
-
|
19
|
+
```shell
|
20
|
+
bundle add range_list
|
21
|
+
```
|
18
22
|
|
19
23
|
Or install it yourself as:
|
20
24
|
|
21
|
-
|
25
|
+
```shell
|
26
|
+
gem install range_list
|
27
|
+
```
|
22
28
|
|
23
29
|
## Usage
|
24
30
|
|
25
|
-
|
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
|
68
|
+
Bug reports and Merge requests are welcome on GitLab at https://gitlab.com/songhuangcn/range_list.
|
36
69
|
|
37
70
|
## License
|
38
71
|
|
data/lib/range_list/version.rb
CHANGED
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
|
-
|
6
|
-
|
7
|
-
|
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:
|
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-
|
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