range_list 1.1.0 → 1.3.1

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: cbae62498893749767a5138f59caacd3de650bf62f566859d2ff16fc69e04a73
4
- data.tar.gz: b5317ec465420963e3d392f89f22e2bf3a7a662a569c82084b4cbe5383eaab32
3
+ metadata.gz: bac3ba3fced62c57e3d3fafa0f1999667172d7083defe6ec6404ca8ecfb83af0
4
+ data.tar.gz: 12708960354b2930dd49ff8ee80e8b809340c8b8447953baac65351a914ad73b
5
5
  SHA512:
6
- metadata.gz: 9f39a299e682ca947eeb1518cdba35eaad08b806433a849f1651ead445a7602fcb7c290611b8c3fa2601994003b74f3edbbe7909967d02064316db27d0d03a46
7
- data.tar.gz: 286f03c1ecc1de6b5d0b5d4d271b0fcb2d3ea2f56c5a146d26e66b68a0fe168e0a8d093e781bb7c4a4551501682e8ea4534495a86f669470b73db52aa48a2028
6
+ metadata.gz: ebfdde12a2eca743326ffd44db017c23ff1ed796af325b4c0c0b1953cee76c427dd1a0ebf3d91e3a5674a2767332925881978292569fed01fca9ee30f5f55c28
7
+ data.tar.gz: dc711a4f3cf7a4157b11d9b0d7976f5411801f330daf7d501c427518434b3d3eb113f6d7aa3a65f019662bf230cf7f50f67b18266e4cc217ff3d6b2315d2c28b
data/Gemfile.lock CHANGED
@@ -1,8 +1,8 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- range_list (1.1.0)
5
- treemap (~> 1.0)
4
+ range_list (1.3.1)
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
@@ -10,7 +10,6 @@ RangeList use AVL Tree processing internally, so range operations are very fast.
10
10
  [![Gem Version](https://badge.fury.io/rb/range_list.svg)](https://rubygems.org/gems/range_list)
11
11
  [![Documentation](https://img.shields.io/badge/docs-YARD-blue.svg)](https://rubydoc.info/gems/range_list)
12
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)
14
13
 
15
14
  ## Installation
16
15
 
@@ -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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class RangeList
4
- VERSION = "1.1.0"
4
+ VERSION = "1.3.1"
5
5
  end
data/lib/range_list.rb CHANGED
@@ -1,14 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "treemap"
4
- require_relative "range_list/version"
5
3
  require_relative "range_list/errors"
4
+ require_relative "range_list/avl_tree/rbtree_adapter"
6
5
 
7
6
  class RangeList
8
7
  include Enumerable
9
8
 
10
9
  def initialize
11
- @tree = TreeMap.new
10
+ @avl_tree = AvlTree::RBTreeAdapter.new
12
11
  end
13
12
 
14
13
  # Add range into current range list.
@@ -18,32 +17,30 @@ class RangeList
18
17
  # @raise [ArgumentError] when argement invalid
19
18
  def add(range)
20
19
  validate_range!(range)
21
-
22
- # Return when range is empty.
23
20
  return self if empty_range?(range)
24
21
 
25
22
  # Get real range start.
26
- start_floor_entry = tree.floor_entry(range[0])
27
- range_start = if !start_floor_entry.nil? && start_floor_entry.value >= range[0]
28
- start_floor_entry.key
23
+ start_lower_range = avl_tree.lower_entry(range[0])
24
+ range_start = if !start_lower_range.nil? && start_lower_range[1] >= range[0]
25
+ start_lower_range[0]
29
26
  else
30
27
  range[0]
31
28
  end
32
29
 
33
30
  # Get real range end.
34
- end_floor_entry = tree.floor_entry(range[1])
35
- range_end = if !end_floor_entry.nil? && end_floor_entry.value >= range[1]
36
- end_floor_entry.value
31
+ end_lower_range = avl_tree.lower_entry(range[1])
32
+ range_end = if !end_lower_range.nil? && end_lower_range[1] >= range[1]
33
+ end_lower_range[1]
37
34
  else
38
35
  range[1]
39
36
  end
40
37
 
41
38
  # Insert or replace new range.
42
- tree.put(range_start, range_end)
39
+ avl_tree.put(range_start, range_end)
43
40
 
44
41
  # Remove keys between range, exclude start, include end.
45
- between_maps = tree.sub_map(range[0], false, range[1], true)
46
- between_maps.keys.each { |key| tree.remove(key) }
42
+ sub_range = avl_tree.sub_map(range[0] + 1, range[1])
43
+ sub_range.each { |range_start, _range_end| avl_tree.remove(range_start) }
47
44
 
48
45
  self
49
46
  end
@@ -54,25 +51,23 @@ class RangeList
54
51
  # @raise (see #add)
55
52
  def remove(range)
56
53
  validate_range!(range)
57
-
58
- # Return when range is empty.
59
54
  return self if empty_range?(range)
60
55
 
61
- # Insert end lower entry
62
- end_lower_entry = tree.lower_entry(range[1])
63
- if !end_lower_entry.nil? && end_lower_entry.value > range[1]
64
- tree.put(range[1], end_lower_entry.value)
56
+ # Insert end lower range, `-1` means not include.
57
+ end_lower_range = avl_tree.lower_entry(range[1] - 1)
58
+ if !end_lower_range.nil? && end_lower_range[1] > range[1]
59
+ avl_tree.put(range[1], end_lower_range[1])
65
60
  end
66
61
 
67
- # Relace start lower entry
68
- start_lower_entry = tree.lower_entry(range[0])
69
- if !start_lower_entry.nil? && start_lower_entry.value > range[0]
70
- tree.put(start_lower_entry.key, range[0])
62
+ # Relace start lower range, `-1` means not include.
63
+ start_lower_range = avl_tree.lower_entry(range[0] - 1)
64
+ if !start_lower_range.nil? && start_lower_range[1] > range[0]
65
+ avl_tree.put(start_lower_range[0], range[0])
71
66
  end
72
67
 
73
68
  # Remove keys between range, include start, exclude end
74
- between_maps = tree.sub_map(range[0], true, range[1], false)
75
- between_maps.keys.each { |key| tree.remove(key) }
69
+ sub_range = avl_tree.sub_map(range[0], range[1] - 1)
70
+ sub_range.each { |range_start, _range_end| avl_tree.remove(range_start) }
76
71
 
77
72
  self
78
73
  end
@@ -80,7 +75,8 @@ class RangeList
80
75
  # Print current range list.
81
76
  # @return [void]
82
77
  def print
83
- puts map { |k, v| "[#{k}, #{v})" }.join(" ")
78
+ info = map { |k, v| "[#{k}, #{v})" }.join(" ")
79
+ puts info
84
80
  end
85
81
 
86
82
  # Returns true if current ranges contains all elements of the range.
@@ -90,12 +86,10 @@ class RangeList
90
86
  # @raise (see #add)
91
87
  def contains_all?(range)
92
88
  validate_range!(range)
93
-
94
- # Return false when range is empty.
95
89
  return false if empty_range?(range)
96
90
 
97
- start_floor_entry = tree.floor_entry(range[0])
98
- !start_floor_entry.nil? && start_floor_entry.value >= range[1]
91
+ start_lower_range = avl_tree.lower_entry(range[0])
92
+ !start_lower_range.nil? && start_lower_range[1] >= range[1]
99
93
  end
100
94
 
101
95
  # Returns true if current ranges contains any element of the range.
@@ -105,12 +99,11 @@ class RangeList
105
99
  # @raise (see #add)
106
100
  def contains_any?(range)
107
101
  validate_range!(range)
108
-
109
- # Return false when range is empty.
110
102
  return false if empty_range?(range)
111
103
 
112
- end_lower_entry = tree.lower_entry(range[1])
113
- !end_lower_entry.nil? && end_lower_entry.value > range[0]
104
+ # `-1` means not include.
105
+ end_lower_range = avl_tree.lower_entry(range[1] - 1)
106
+ !end_lower_range.nil? && end_lower_range[1] > range[0]
114
107
  end
115
108
 
116
109
  # Returns true if current ranges contains the element.
@@ -118,19 +111,19 @@ class RangeList
118
111
  # @return [Boolean]
119
112
  # @raise (see #add)
120
113
  def contains?(element)
121
- raise ArgumentError, "`element` should be `Integer` type." unless element.is_a?(Integer)
114
+ validate_element!(element)
122
115
 
123
- floor_entry = tree.floor_entry(element)
124
- !floor_entry.nil? && floor_entry.value > element
116
+ lower_entry = avl_tree.lower_entry(element)
117
+ !lower_entry.nil? && lower_entry[1] > element
125
118
  end
126
119
 
127
120
  # Give RangeList iterative ability.
121
+ # @return [RangeList, Enumerator]
128
122
  # @yield [range_start, range_end] give the range element to the block
129
123
  def each(&block)
130
124
  if block
131
- # Iterating an empty tree will raise an error,
132
- # see https://github.com/davidkellis/treemap/issues/1
133
- tree.empty? ? self : tree.each(&block)
125
+ avl_tree.each(&block)
126
+ self
134
127
  else
135
128
  enum_for(:each)
136
129
  end
@@ -138,12 +131,16 @@ class RangeList
138
131
 
139
132
  # @!visibility private
140
133
  def inspect
141
- to_a.inspect
134
+ data = map { |k, v| "[#{k}, #{v})" }.join(", ")
135
+ "[#{data}]"
142
136
  end
143
137
 
138
+ # @!visibility private
139
+ alias_method :to_s, :inspect
140
+
144
141
  private
145
142
 
146
- attr_reader :tree
143
+ attr_reader :avl_tree
147
144
 
148
145
  def validate_range!(range)
149
146
  raise ArgumentError, "`range` should be `Array` type." unless range.is_a?(Array)
@@ -151,6 +148,10 @@ class RangeList
151
148
  raise ArgumentError, "All elements of `range` should be `Integer`." unless range.all? { |item| item.is_a?(Integer) }
152
149
  end
153
150
 
151
+ def validate_element!(element)
152
+ raise ArgumentError, "`element` should be `Integer` type." unless element.is_a?(Integer)
153
+ end
154
+
154
155
  def empty_range?(range)
155
156
  range[0] >= range[1]
156
157
  end
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 "treemap", "~> 1.0"
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.1.0
4
+ version: 1.3.1
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-26 00:00:00.000000000 Z
11
+ date: 2022-02-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: treemap
14
+ name: rbtree
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.0'
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: '1.0'
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