range_list 1.0.0 → 1.3.0

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: 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