range_list 1.0.0 → 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.
- checksums.yaml +4 -4
- data/Gemfile.lock +3 -3
- data/README.md +7 -4
- data/lib/range_list/avl_tree/abstract_adapter.rb +28 -0
- data/lib/range_list/avl_tree/rbtree_adapter.rb +39 -0
- data/lib/range_list/version.rb +1 -1
- data/lib/range_list.rb +53 -38
- data/range_list.gemspec +1 -1
- metadata +7 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7ea59292d297f5583f7a9ceddaf8194cbd33704180c3e43f15fe87358c8d02d5
|
4
|
+
data.tar.gz: f8ea601a80d2261c1b869aa57699bd0c71d048b4a94b25b978d67d1636d426b5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 52b7dd400bb94273b9e0ff1702db7d5bd328f88e425322ba3838cc859e4629f0f5f2776bcedec7f73ba90ca0936b8f661fd435bde89bf67bbfdede88c93f62b5
|
7
|
+
data.tar.gz: af2aee3e2b4c37cdffbe89518423ea037b3348db50c9bfeff3b27259f6ff54f055265c3cbfb7c5ec7a9bc7410baa475e1d2f59be0259a5061b2e5197bd8e7280
|
data/Gemfile.lock
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
range_list (1.
|
5
|
-
|
4
|
+
range_list (1.3.0)
|
5
|
+
rbtree (~> 0.4.5)
|
6
6
|
|
7
7
|
GEM
|
8
8
|
remote: https://rubygems.org/
|
@@ -15,6 +15,7 @@ GEM
|
|
15
15
|
ast (~> 2.4.1)
|
16
16
|
rainbow (3.1.1)
|
17
17
|
rake (13.0.6)
|
18
|
+
rbtree (0.4.5)
|
18
19
|
regexp_parser (2.2.1)
|
19
20
|
rexml (3.2.5)
|
20
21
|
rubocop (1.25.1)
|
@@ -44,7 +45,6 @@ GEM
|
|
44
45
|
standard (1.7.2)
|
45
46
|
rubocop (= 1.25.1)
|
46
47
|
rubocop-performance (= 1.13.2)
|
47
|
-
treemap (1.0.3)
|
48
48
|
unicode-display_width (2.1.0)
|
49
49
|
webrick (1.7.0)
|
50
50
|
yard (0.9.27)
|
data/README.md
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
# RangeList
|
2
2
|
|
3
|
+
RangeList is a library that can handle ranges quickly and appropriately.
|
4
|
+
|
3
5
|
A pair of integers define a range, for example: [1, 5), this range includes integers: 1, 2, 3, and 4.
|
4
6
|
A range list is an aggregate of these ranges: [1, 5), [10, 11), [100, 201).
|
5
7
|
|
6
|
-
RangeList
|
7
|
-
you can handle ranges more easily.
|
8
|
+
RangeList use AVL Tree processing internally, so range operations are very fast.
|
8
9
|
|
9
10
|
[](https://rubygems.org/gems/range_list)
|
10
11
|
[](https://rubydoc.info/gems/range_list)
|
11
12
|
[](https://gitlab.com/songhuangcn/range_list/-/commits/main)
|
12
|
-
[](https://gitlab.com/songhuangcn/range_list/-/commits/main)
|
13
13
|
|
14
14
|
## Installation
|
15
15
|
|
@@ -45,8 +45,11 @@ range_list.contains_any?([8, 10]) # false
|
|
45
45
|
range_list.contains_any?([8, 11]) # true
|
46
46
|
range_list.contains_all?([8, 11]) # false
|
47
47
|
range_list.contains_all?([10, 11]) # true
|
48
|
+
range_list.contains?(5) # false
|
49
|
+
range_list.contains?(10) # true
|
48
50
|
|
49
|
-
|
51
|
+
# RangeList has the `Enumerable` ability, feel free to use `each`, `map`, `to_a`, `min`, `max` etc.
|
52
|
+
range_list.each { |range_start, range_end| puts "#{range_start} <= x < #{range_end}" }
|
50
53
|
|
51
54
|
range_list.remove([0, 100])
|
52
55
|
range_list.print #
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class RangeList
|
4
|
+
# @!visibility private
|
5
|
+
module AvlTree
|
6
|
+
class AbstractAdapter
|
7
|
+
def put(key, value)
|
8
|
+
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
|
9
|
+
end
|
10
|
+
|
11
|
+
def lower_entry(key)
|
12
|
+
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
|
13
|
+
end
|
14
|
+
|
15
|
+
def sub_map(from_key, to_key)
|
16
|
+
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
|
17
|
+
end
|
18
|
+
|
19
|
+
def remove(key)
|
20
|
+
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
|
21
|
+
end
|
22
|
+
|
23
|
+
def each(&block)
|
24
|
+
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rbtree"
|
4
|
+
require_relative "abstract_adapter"
|
5
|
+
|
6
|
+
class RangeList
|
7
|
+
# @!visibility private
|
8
|
+
module AvlTree
|
9
|
+
class RBTreeAdapter < AbstractAdapter
|
10
|
+
def initialize
|
11
|
+
@rbtree = RBTree.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def put(key, value)
|
15
|
+
rbtree[key] = value
|
16
|
+
end
|
17
|
+
|
18
|
+
def lower_entry(key)
|
19
|
+
rbtree.upper_bound(key)
|
20
|
+
end
|
21
|
+
|
22
|
+
def sub_map(from_key, to_key)
|
23
|
+
rbtree.bound(from_key, to_key).to_a
|
24
|
+
end
|
25
|
+
|
26
|
+
def remove(key)
|
27
|
+
rbtree.delete(key)
|
28
|
+
end
|
29
|
+
|
30
|
+
def each(&block)
|
31
|
+
rbtree.each(&block)
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
attr_reader :rbtree
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/range_list/version.rb
CHANGED
data/lib/range_list.rb
CHANGED
@@ -1,20 +1,21 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "treemap"
|
4
3
|
require_relative "range_list/version"
|
5
4
|
require_relative "range_list/errors"
|
5
|
+
require_relative "range_list/avl_tree/rbtree_adapter"
|
6
6
|
|
7
7
|
class RangeList
|
8
8
|
include Enumerable
|
9
9
|
|
10
10
|
def initialize
|
11
|
-
@
|
11
|
+
@avl_tree = AvlTree::RBTreeAdapter.new
|
12
12
|
end
|
13
13
|
|
14
14
|
# Add range into current range list.
|
15
15
|
# @param range [Array<Integer>] the range, first element is range start, second is range end.
|
16
16
|
# Range end need be greater or equal than range start.
|
17
17
|
# @return [RangeList]
|
18
|
+
# @raise [ArgumentError] when argement invalid
|
18
19
|
def add(range)
|
19
20
|
validate_range!(range)
|
20
21
|
|
@@ -22,27 +23,27 @@ class RangeList
|
|
22
23
|
return self if empty_range?(range)
|
23
24
|
|
24
25
|
# Get real range start.
|
25
|
-
|
26
|
-
range_start = if !
|
27
|
-
|
26
|
+
start_lower_range = avl_tree.lower_entry(range[0])
|
27
|
+
range_start = if !start_lower_range.nil? && start_lower_range[1] >= range[0]
|
28
|
+
start_lower_range[0]
|
28
29
|
else
|
29
30
|
range[0]
|
30
31
|
end
|
31
32
|
|
32
33
|
# Get real range end.
|
33
|
-
|
34
|
-
range_end = if !
|
35
|
-
|
34
|
+
end_lower_range = avl_tree.lower_entry(range[1])
|
35
|
+
range_end = if !end_lower_range.nil? && end_lower_range[1] >= range[1]
|
36
|
+
end_lower_range[1]
|
36
37
|
else
|
37
38
|
range[1]
|
38
39
|
end
|
39
40
|
|
40
41
|
# Insert or replace new range.
|
41
|
-
|
42
|
+
avl_tree.put(range_start, range_end)
|
42
43
|
|
43
44
|
# Remove keys between range, exclude start, include end.
|
44
|
-
|
45
|
-
|
45
|
+
sub_range = avl_tree.sub_map(range[0] + 1, range[1])
|
46
|
+
sub_range.each { |range_start, _range_end| avl_tree.remove(range_start) }
|
46
47
|
|
47
48
|
self
|
48
49
|
end
|
@@ -50,27 +51,28 @@ class RangeList
|
|
50
51
|
# Remove range from current range list.
|
51
52
|
# @param (see #add)
|
52
53
|
# @return [RangeList]
|
54
|
+
# @raise (see #add)
|
53
55
|
def remove(range)
|
54
56
|
validate_range!(range)
|
55
57
|
|
56
58
|
# Return when range is empty.
|
57
59
|
return self if empty_range?(range)
|
58
60
|
|
59
|
-
# Insert end lower
|
60
|
-
|
61
|
-
if !
|
62
|
-
|
61
|
+
# Insert end lower range, `-1` means not include.
|
62
|
+
end_lower_range = avl_tree.lower_entry(range[1] - 1)
|
63
|
+
if !end_lower_range.nil? && end_lower_range[1] > range[1]
|
64
|
+
avl_tree.put(range[1], end_lower_range[1])
|
63
65
|
end
|
64
66
|
|
65
|
-
# Relace start lower
|
66
|
-
|
67
|
-
if !
|
68
|
-
|
67
|
+
# Relace start lower range, `-1` means not include.
|
68
|
+
start_lower_range = avl_tree.lower_entry(range[0] - 1)
|
69
|
+
if !start_lower_range.nil? && start_lower_range[1] > range[0]
|
70
|
+
avl_tree.put(start_lower_range[0], range[0])
|
69
71
|
end
|
70
72
|
|
71
73
|
# Remove keys between range, include start, exclude end
|
72
|
-
|
73
|
-
|
74
|
+
sub_range = avl_tree.sub_map(range[0], range[1] - 1)
|
75
|
+
sub_range.each { |range_start, _range_end| avl_tree.remove(range_start) }
|
74
76
|
|
75
77
|
self
|
76
78
|
end
|
@@ -78,38 +80,57 @@ class RangeList
|
|
78
80
|
# Print current range list.
|
79
81
|
# @return [void]
|
80
82
|
def print
|
81
|
-
|
83
|
+
info = map { |k, v| "[#{k}, #{v})" }.join(" ")
|
84
|
+
puts info
|
82
85
|
end
|
83
86
|
|
84
|
-
# Returns true if
|
87
|
+
# Returns true if current ranges contains all elements of the range.
|
85
88
|
# @param (see #add)
|
86
89
|
# @return [Boolean]
|
87
90
|
# @note return false if the argument range is empty
|
91
|
+
# @raise (see #add)
|
88
92
|
def contains_all?(range)
|
93
|
+
validate_range!(range)
|
94
|
+
|
95
|
+
# Return false when range is empty.
|
89
96
|
return false if empty_range?(range)
|
90
97
|
|
91
|
-
|
92
|
-
!
|
98
|
+
start_lower_range = avl_tree.lower_entry(range[0])
|
99
|
+
!start_lower_range.nil? && start_lower_range[1] >= range[1]
|
93
100
|
end
|
94
101
|
|
95
|
-
# Returns true if
|
102
|
+
# Returns true if current ranges contains any element of the range.
|
96
103
|
# @param (see #add)
|
97
104
|
# @return [Boolean]
|
98
105
|
# @note return false if the argument range is empty
|
106
|
+
# @raise (see #add)
|
99
107
|
def contains_any?(range)
|
108
|
+
validate_range!(range)
|
109
|
+
|
110
|
+
# Return false when range is empty.
|
100
111
|
return false if empty_range?(range)
|
101
112
|
|
102
|
-
|
103
|
-
|
113
|
+
# `-1` means not include.
|
114
|
+
end_lower_range = avl_tree.lower_entry(range[1] - 1)
|
115
|
+
!end_lower_range.nil? && end_lower_range[1] > range[0]
|
116
|
+
end
|
117
|
+
|
118
|
+
# Returns true if current ranges contains the element.
|
119
|
+
# @param element [Integer] the range element
|
120
|
+
# @return [Boolean]
|
121
|
+
# @raise (see #add)
|
122
|
+
def contains?(element)
|
123
|
+
raise ArgumentError, "`element` should be `Integer` type." unless element.is_a?(Integer)
|
124
|
+
|
125
|
+
lower_entry = avl_tree.lower_entry(element)
|
126
|
+
!lower_entry.nil? && lower_entry[1] > element
|
104
127
|
end
|
105
128
|
|
106
129
|
# Give RangeList iterative ability.
|
107
130
|
# @yield [range_start, range_end] give the range element to the block
|
108
131
|
def each(&block)
|
109
132
|
if block
|
110
|
-
|
111
|
-
# see https://github.com/davidkellis/treemap/issues/1
|
112
|
-
tree.empty? ? self : tree.each(&block)
|
133
|
+
avl_tree.each(&block)
|
113
134
|
else
|
114
135
|
enum_for(:each)
|
115
136
|
end
|
@@ -120,15 +141,9 @@ class RangeList
|
|
120
141
|
to_a.inspect
|
121
142
|
end
|
122
143
|
|
123
|
-
# Return the current range as an array.
|
124
|
-
# @return [Array]
|
125
|
-
def to_a
|
126
|
-
tree.empty? ? [] : tree.to_a
|
127
|
-
end
|
128
|
-
|
129
144
|
private
|
130
145
|
|
131
|
-
attr_reader :
|
146
|
+
attr_reader :avl_tree
|
132
147
|
|
133
148
|
def validate_range!(range)
|
134
149
|
raise ArgumentError, "`range` should be `Array` type." unless range.is_a?(Array)
|
data/range_list.gemspec
CHANGED
@@ -34,7 +34,7 @@ Gem::Specification.new do |spec|
|
|
34
34
|
|
35
35
|
# Uncomment to register a new dependency of your gem
|
36
36
|
# spec.add_dependency "example-gem", "~> 1.0"
|
37
|
-
spec.add_dependency "
|
37
|
+
spec.add_dependency "rbtree", "~> 0.4.5"
|
38
38
|
|
39
39
|
# For more information and examples about making a new gem, check out our
|
40
40
|
# guide at: https://bundler.io/guides/creating_gem.html
|
metadata
CHANGED
@@ -1,29 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: range_list
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.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-
|
11
|
+
date: 2022-02-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: rbtree
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 0.4.5
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: 0.4.5
|
27
27
|
description:
|
28
28
|
email:
|
29
29
|
- songhuangcn@gmail.com
|
@@ -37,6 +37,8 @@ files:
|
|
37
37
|
- README.md
|
38
38
|
- Rakefile
|
39
39
|
- lib/range_list.rb
|
40
|
+
- lib/range_list/avl_tree/abstract_adapter.rb
|
41
|
+
- lib/range_list/avl_tree/rbtree_adapter.rb
|
40
42
|
- lib/range_list/errors.rb
|
41
43
|
- lib/range_list/version.rb
|
42
44
|
- range_list.gemspec
|