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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f27b8cd8551942e4ff09f2cc32c525e7fd5724cdbea8e322203cd01df681fb46
4
- data.tar.gz: c24ffbe52e439561c2010fccdf67f68d9700703b23c4ed45d34c143ba5e6de46
3
+ metadata.gz: 7ea59292d297f5583f7a9ceddaf8194cbd33704180c3e43f15fe87358c8d02d5
4
+ data.tar.gz: f8ea601a80d2261c1b869aa57699bd0c71d048b4a94b25b978d67d1636d426b5
5
5
  SHA512:
6
- metadata.gz: 62a1a66f4607c3e17424d8bd76cd8371626e8ff0d64961f189f09534fb33cc6e60537e821b3bb743389057db0c76a4e5e83cd7cbe8d3a6872c6c808891237f16
7
- data.tar.gz: 9fb515423d8048f124e15a88cb3c5b718e7d273694bb8e9fe128ce2e37ebda636ba1da330af406abfc57d8566686fa71eff91d88c7af1eee55cd9b5b4ad75ad1
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.0.0)
5
- treemap (~> 1.0)
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 offers `add`, `remove`, `print`, `containsAll?`, `containsAny?` methods, and offers the `Enumerable` ability,
7
- you can handle ranges more easily.
8
+ RangeList use AVL Tree processing internally, so range operations are very fast.
8
9
 
9
10
  [![Gem Version](https://badge.fury.io/rb/range_list.svg)](https://rubygems.org/gems/range_list)
10
11
  [![Documentation](https://img.shields.io/badge/docs-YARD-blue.svg)](https://rubydoc.info/gems/range_list)
11
12
  [![pipeline status](https://gitlab.com/songhuangcn/range_list/badges/main/pipeline.svg)](https://gitlab.com/songhuangcn/range_list/-/commits/main)
12
- [![coverage report](https://gitlab.com/songhuangcn/range_list/badges/main/coverage.svg)](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
- range_list.each { |range_start, range_end| puts "#{range_start} <= x < #{range_end}" } # print all range in order
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class RangeList
4
- VERSION = "1.0.0"
4
+ VERSION = "1.3.0"
5
5
  end
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
- @tree = TreeMap.new
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
- start_floor_entry = tree.floor_entry(range[0])
26
- range_start = if !start_floor_entry.nil? && start_floor_entry.value >= range[0]
27
- start_floor_entry.key
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
- end_floor_entry = tree.floor_entry(range[1])
34
- range_end = if !end_floor_entry.nil? && end_floor_entry.value >= range[1]
35
- end_floor_entry.value
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
- tree.put(range_start, range_end)
42
+ avl_tree.put(range_start, range_end)
42
43
 
43
44
  # Remove keys between range, exclude start, include end.
44
- between_maps = tree.sub_map(range[0], false, range[1], true)
45
- between_maps.keys.each { |key| tree.remove(key) }
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 entry
60
- end_lower_entry = tree.lower_entry(range[1])
61
- if !end_lower_entry.nil? && end_lower_entry.value > range[1]
62
- tree.put(range[1], end_lower_entry.value)
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 entry
66
- start_lower_entry = tree.lower_entry(range[0])
67
- if !start_lower_entry.nil? && start_lower_entry.value > range[0]
68
- tree.put(start_lower_entry.key, range[0])
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
- between_maps = tree.sub_map(range[0], true, range[1], false)
73
- between_maps.keys.each { |key| tree.remove(key) }
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
- puts to_a.map { |k, v| "[#{k}, #{v})" }.join(" ")
83
+ info = map { |k, v| "[#{k}, #{v})" }.join(" ")
84
+ puts info
82
85
  end
83
86
 
84
- # Returns true if this set contains all elements of the range.
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
- start_floor_entry = tree.floor_entry(range[0])
92
- !start_floor_entry.nil? && start_floor_entry.value >= range[1]
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 this set contains any element of the range.
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
- lower_floor_entry = tree.lower_entry(range[1])
103
- !lower_floor_entry.nil? && lower_floor_entry.value > range[0]
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
- # Iterating an empty tree will raise an error,
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 :tree
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 "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.0.0
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-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