ruby_binary_search 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: e8f5f86b8bbbf1618e3d795de3f817bb753372c7f24005301385f7da40c720ea
4
+ data.tar.gz: 433b0100900bf1bd1846d10e785acf63c83505adfc91d5424f783360eb8504fd
5
+ SHA512:
6
+ metadata.gz: f3e41db0b009ae95f1f3ac5b12018a6df9f475ef7d483a5ba395a26c32a2f0a301297b5b12ee4815a803fc26ee25a3b6709fc3466a8b677082723e90e260569c
7
+ data.tar.gz: 5eb34f6900de0ffc7681d5a50989abf6182de46b93342f234f63716c1cabbcc7b2f58cf74bb67a5bd6de5ac9eacbb0cb002d514abca66b45a094e8efec765992
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format progress
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,13 @@
1
+ inherit_gem:
2
+ rubocop-rails_config:
3
+ - config/rails.yml
4
+
5
+ Style/ClassAndModuleChildren:
6
+ EnforcedStyle: nested
7
+
8
+ Lint/Debugger:
9
+ Enabled: true
10
+
11
+ Style/StringLiterals:
12
+ Enabled: true
13
+ EnforcedStyle: single_quotes
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2024-07-18
4
+
5
+ - Initial release
@@ -0,0 +1,132 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ We as members, contributors, and leaders pledge to make participation in our
6
+ community a harassment-free experience for everyone, regardless of age, body
7
+ size, visible or invisible disability, ethnicity, sex characteristics, gender
8
+ identity and expression, level of experience, education, socio-economic status,
9
+ nationality, personal appearance, race, caste, color, religion, or sexual
10
+ identity and orientation.
11
+
12
+ We pledge to act and interact in ways that contribute to an open, welcoming,
13
+ diverse, inclusive, and healthy community.
14
+
15
+ ## Our Standards
16
+
17
+ Examples of behavior that contributes to a positive environment for our
18
+ community include:
19
+
20
+ * Demonstrating empathy and kindness toward other people
21
+ * Being respectful of differing opinions, viewpoints, and experiences
22
+ * Giving and gracefully accepting constructive feedback
23
+ * Accepting responsibility and apologizing to those affected by our mistakes,
24
+ and learning from the experience
25
+ * Focusing on what is best not just for us as individuals, but for the overall
26
+ community
27
+
28
+ Examples of unacceptable behavior include:
29
+
30
+ * The use of sexualized language or imagery, and sexual attention or advances of
31
+ any kind
32
+ * Trolling, insulting or derogatory comments, and personal or political attacks
33
+ * Public or private harassment
34
+ * Publishing others' private information, such as a physical or email address,
35
+ without their explicit permission
36
+ * Other conduct which could reasonably be considered inappropriate in a
37
+ professional setting
38
+
39
+ ## Enforcement Responsibilities
40
+
41
+ Community leaders are responsible for clarifying and enforcing our standards of
42
+ acceptable behavior and will take appropriate and fair corrective action in
43
+ response to any behavior that they deem inappropriate, threatening, offensive,
44
+ or harmful.
45
+
46
+ Community leaders have the right and responsibility to remove, edit, or reject
47
+ comments, commits, code, wiki edits, issues, and other contributions that are
48
+ not aligned to this Code of Conduct, and will communicate reasons for moderation
49
+ decisions when appropriate.
50
+
51
+ ## Scope
52
+
53
+ This Code of Conduct applies within all community spaces, and also applies when
54
+ an individual is officially representing the community in public spaces.
55
+ Examples of representing our community include using an official email address,
56
+ posting via an official social media account, or acting as an appointed
57
+ representative at an online or offline event.
58
+
59
+ ## Enforcement
60
+
61
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
62
+ reported to the community leaders responsible for enforcement at
63
+ [INSERT CONTACT METHOD].
64
+ All complaints will be reviewed and investigated promptly and fairly.
65
+
66
+ All community leaders are obligated to respect the privacy and security of the
67
+ reporter of any incident.
68
+
69
+ ## Enforcement Guidelines
70
+
71
+ Community leaders will follow these Community Impact Guidelines in determining
72
+ the consequences for any action they deem in violation of this Code of Conduct:
73
+
74
+ ### 1. Correction
75
+
76
+ **Community Impact**: Use of inappropriate language or other behavior deemed
77
+ unprofessional or unwelcome in the community.
78
+
79
+ **Consequence**: A private, written warning from community leaders, providing
80
+ clarity around the nature of the violation and an explanation of why the
81
+ behavior was inappropriate. A public apology may be requested.
82
+
83
+ ### 2. Warning
84
+
85
+ **Community Impact**: A violation through a single incident or series of
86
+ actions.
87
+
88
+ **Consequence**: A warning with consequences for continued behavior. No
89
+ interaction with the people involved, including unsolicited interaction with
90
+ those enforcing the Code of Conduct, for a specified period of time. This
91
+ includes avoiding interactions in community spaces as well as external channels
92
+ like social media. Violating these terms may lead to a temporary or permanent
93
+ ban.
94
+
95
+ ### 3. Temporary Ban
96
+
97
+ **Community Impact**: A serious violation of community standards, including
98
+ sustained inappropriate behavior.
99
+
100
+ **Consequence**: A temporary ban from any sort of interaction or public
101
+ communication with the community for a specified period of time. No public or
102
+ private interaction with the people involved, including unsolicited interaction
103
+ with those enforcing the Code of Conduct, is allowed during this period.
104
+ Violating these terms may lead to a permanent ban.
105
+
106
+ ### 4. Permanent Ban
107
+
108
+ **Community Impact**: Demonstrating a pattern of violation of community
109
+ standards, including sustained inappropriate behavior, harassment of an
110
+ individual, or aggression toward or disparagement of classes of individuals.
111
+
112
+ **Consequence**: A permanent ban from any sort of public interaction within the
113
+ community.
114
+
115
+ ## Attribution
116
+
117
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage],
118
+ version 2.1, available at
119
+ [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
120
+
121
+ Community Impact Guidelines were inspired by
122
+ [Mozilla's code of conduct enforcement ladder][Mozilla CoC].
123
+
124
+ For answers to common questions about this code of conduct, see the FAQ at
125
+ [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
126
+ [https://www.contributor-covenant.org/translations][translations].
127
+
128
+ [homepage]: https://www.contributor-covenant.org
129
+ [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
130
+ [Mozilla CoC]: https://github.com/mozilla/diversity
131
+ [FAQ]: https://www.contributor-covenant.org/faq
132
+ [translations]: https://www.contributor-covenant.org/translations
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2024 sebi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,104 @@
1
+ # BinarySearch 🌳🔍
2
+
3
+ Welcome to BinarySearch, a gem that brings the power of Red-Black Trees to your Ruby projects! 🚀
4
+
5
+ ## What is BinarySearch? 🤔
6
+
7
+ BinarySearch is a Ruby gem that implements a self-balancing binary search tree using the Red-Black Tree algorithm. It provides a list-like interface with blazing-fast search, insertion, and deletion operations. 💨
8
+
9
+ ## Why BinarySearch? 🌟
10
+
11
+ - **Efficiency**: Operations like search, insert, and delete are O(log n), making it much faster than standard arrays for large datasets. 📈
12
+ - **Self-balancing**: The Red-Black Tree ensures that the tree remains balanced, maintaining consistent performance even with frequent modifications. ⚖️
13
+ - **Sorted storage**: Elements are always stored in sorted order, making it perfect for applications that require sorted data. 📊
14
+ - **Flexible**: Supports common list operations like push, pop, shift, and unshift, as well as set operations like union and intersection. 🛠️
15
+
16
+ ## Installation 💻
17
+
18
+ Add this line to your application's Gemfile:
19
+
20
+ ```ruby
21
+ gem 'binary_search'
22
+ ```
23
+
24
+ And then execute:
25
+ ```bash
26
+ bundle install
27
+ ```
28
+
29
+ ## Usage 🚀
30
+ Here's a quick example of how to use BinarySearch:
31
+
32
+ ```ruby
33
+ require 'binary_search'
34
+
35
+ # Create a new list
36
+ list = BinarySearch::List.new([3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5])
37
+
38
+ # Get the sorted array
39
+ puts list.to_a # Output: [1, 1, 2, 3, 3, 4, 5, 5, 5, 6, 9]
40
+
41
+ # Check if a value exists
42
+ puts list.include?(4) # Output: true
43
+
44
+ # Remove all instances of a value
45
+ list.delete(1)
46
+ puts list.to_a # Output: [2, 3, 3, 4, 5, 5, 5, 6, 9]
47
+
48
+ # Add a new value
49
+ list.insert(7)
50
+ puts list.to_a # Output: [2, 3, 3, 4, 5, 5, 5, 6, 7, 9]
51
+
52
+ # Get the minimum and maximum values
53
+ puts list.min # Output: 2
54
+ puts list.max # Output: 9
55
+ ```
56
+ Custom objects
57
+ ```ruby
58
+ require 'binary_search'
59
+ class Person
60
+ attr_accessor :name, :age
61
+
62
+ def initialize(name, age)
63
+ @name = name
64
+ @age = age
65
+ end
66
+
67
+ def <=>(other)
68
+ @age <=> other.age
69
+ end
70
+ end
71
+
72
+
73
+ list = BinarySearch::List.new([
74
+ Person.new('Alice', 25),
75
+ Person.new('Bob', 30),
76
+ Person.new('Charlie', 20),
77
+ Person.new('David', 35)
78
+ ])
79
+
80
+ puts list.to_a.map(&:name) # Output: ["Charlie", "Alice", "Bob", "David"]
81
+ ```
82
+
83
+ ## Why is BinarySearch better than normal search? 🏆
84
+
85
+ - Speed: For large datasets, binary search is significantly faster than linear search. While a normal array search takes O(n) time, BinarySearch takes only O(log n) time. 🐇
86
+ - Always sorted: The list is always maintained in sorted order, which is useful for many applications and algorithms. 📑
87
+ - Efficient insertions and deletions: Unlike normal arrays where insertions and deletions can be O(n) operations, BinarySearch performs these in O(log n) time. 🔄
88
+ - Memory efficiency: Red-Black Trees are more memory-efficient than hash tables for certain types of data and operations. 💾
89
+ - Range queries: BinarySearch makes it easy to perform range queries efficiently, which can be cumbersome with normal arrays. 🎯
90
+
91
+ ## Development 🛠️
92
+
93
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
94
+ To install this gem onto your local machine, run `bundle exec rake install`.
95
+
96
+ ## Contributing 🤝
97
+
98
+ Bug reports and pull requests are welcome on GitHub at https://github.com/sebyx07/binary_search. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the code of conduct.
99
+
100
+ ## License 📄
101
+ The gem is available as open source under the terms of the MIT License.
102
+
103
+ ## Code of Conduct 🤓
104
+ Everyone interacting in the BinarySearch project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: :spec
data/lefthook.yml ADDED
@@ -0,0 +1,8 @@
1
+ pre-commit:
2
+ commands:
3
+ rubocop:
4
+ run: bundle exec rubocop -A
5
+ skip:
6
+ - merge
7
+ - rebase
8
+ stage_fixed: true
@@ -0,0 +1,526 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BinarySearch
4
+ # A self-balancing binary search tree implementation of a list
5
+ #
6
+ # This class provides a list-like interface backed by a Red-Black Tree,
7
+ # which ensures O(log n) time complexity for most operations.
8
+ #
9
+ # @example Creating and using a BinarySearch::List
10
+ # list = BinarySearch::List.new([3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5])
11
+ # list.to_a # => [1, 1, 2, 3, 3, 4, 5, 5, 5, 6, 9]
12
+ # list.include?(4) # => true
13
+ # list.delete(1) # Removes all instances of 1
14
+ # list.to_a # => [2, 3, 3, 4, 5, 5, 5, 6, 9]
15
+ class List
16
+ include Enumerable
17
+
18
+ # Initialize a new BinarySearch::List
19
+ #
20
+ # @param from [Array] An array to initialize the list with
21
+ #
22
+ # @example Create an empty list
23
+ # list = BinarySearch::List.new
24
+ #
25
+ # @example Create a list from an array
26
+ # list = BinarySearch::List.new([1, 2, 3, 4, 5])
27
+ def initialize(from = [])
28
+ @tree = BinarySearch::RedBlackTree.new
29
+ build_tree(from)
30
+ end
31
+
32
+ # Insert a value into the list
33
+ #
34
+ # This method inserts a value into the list, maintaining the sorted order.
35
+ # It has a time complexity of O(log n).
36
+ #
37
+ # @param value [Object] The value to insert
38
+ # @return [BinarySearch::List] The list object (for method chaining)
39
+ #
40
+ # @example Insert a value
41
+ # list.insert(4) # => #<BinarySearch::List: ...>
42
+ # list << 5 # => #<BinarySearch::List: ...>
43
+ def insert(value)
44
+ @tree.insert(value)
45
+ @size = nil
46
+ self
47
+ end
48
+ alias_method :append, :insert
49
+ alias_method :push, :insert
50
+ alias_method :<<, :insert
51
+
52
+ # Delete all occurrences of a value from the list
53
+ #
54
+ # This method removes all instances of the specified value from the list.
55
+ # It has a time complexity of O(log n) for each deletion.
56
+ #
57
+ # @param value [Object] The value to delete
58
+ # @return [Boolean] True if any elements were deleted, false otherwise
59
+ #
60
+ # @example Delete a value
61
+ # list = BinarySearch::List.new([1, 2, 2, 3, 4])
62
+ # list.delete(2) # => true
63
+ # list.to_a # => [1, 3, 4]
64
+ def delete(value)
65
+ deleted = false
66
+ while @tree.find(value)
67
+ @tree.remove(value)
68
+ @size -= 1 if @size
69
+ deleted = true
70
+ end
71
+ deleted
72
+ end
73
+
74
+ # Check if a value is in the list
75
+ #
76
+ # This method checks if the list contains the specified value.
77
+ # It has a time complexity of O(log n).
78
+ #
79
+ # @param value [Object] The value to check for
80
+ # @return [Boolean] True if the value is in the list, false otherwise
81
+ #
82
+ # @example Check for a value
83
+ # list = BinarySearch::List.new([1, 2, 3, 4, 5])
84
+ # list.include?(3) # => true
85
+ # list.include?(6) # => false
86
+ def include?(value)
87
+ !@tree.find(value).nil?
88
+ end
89
+
90
+ # Convert the list to an array
91
+ #
92
+ # This method returns an array representation of the list in sorted order.
93
+ # It has a time complexity of O(n).
94
+ #
95
+ # @return [Array] An array representation of the list
96
+ #
97
+ # @example Convert to array
98
+ # list = BinarySearch::List.new([3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5])
99
+ # list.to_a # => [1, 1, 2, 3, 3, 4, 5, 5, 5, 6, 9]
100
+ def to_a
101
+ inorder_traversal(@tree.root)
102
+ end
103
+
104
+ # Get the size of the list
105
+ #
106
+ # This method returns the number of elements in the list.
107
+ # It has a time complexity of O(1) if the size is cached, or O(n) otherwise.
108
+ #
109
+ # @return [Integer] The number of elements in the list
110
+ #
111
+ # @example Get the size
112
+ # list = BinarySearch::List.new([1, 2, 3, 4, 5])
113
+ # list.size # => 5
114
+ def size
115
+ @size ||= inorder_traversal(@tree.root).size
116
+ end
117
+
118
+ # Check if the list is empty
119
+ #
120
+ # This method checks if the list contains no elements.
121
+ # It has a time complexity of O(1).
122
+ #
123
+ # @return [Boolean] True if the list is empty, false otherwise
124
+ #
125
+ # @example Check if empty
126
+ # list = BinarySearch::List.new
127
+ # list.empty? # => true
128
+ # list.insert(1)
129
+ # list.empty? # => false
130
+ def empty?
131
+ @tree.root.nil?
132
+ end
133
+
134
+ # Access elements by index or range
135
+ #
136
+ # This method allows accessing elements by index or range, similar to Array.
137
+ # It has a time complexity of O(n) in the worst case.
138
+ #
139
+ # @param arg [Integer, Range] The index or range to access
140
+ # @return [Object, BinarySearch::List, nil] The element(s) at the given index or range, or nil if out of bounds
141
+ # @raise [ArgumentError] If the argument is not an Integer or Range
142
+ #
143
+ # @example Access by index
144
+ # list = BinarySearch::List.new([1, 2, 3, 4, 5])
145
+ # list[2] # => 3
146
+ #
147
+ # @example Access by range
148
+ # list[1..3] # => #<BinarySearch::List: [2, 3, 4]>
149
+ def [](arg)
150
+ case arg
151
+ when Integer
152
+ return nil if arg < 0 || arg >= size
153
+ to_a[arg]
154
+ when Range
155
+ start = arg.begin
156
+ finish = arg.end
157
+ start = size + start if start < 0
158
+ finish = size + finish if finish < 0
159
+ finish -= 1 if arg.exclude_end?
160
+
161
+ return nil if start < 0 || start >= size
162
+
163
+ result = []
164
+ (start..finish).each do |i|
165
+ break if i >= size
166
+ result << to_a[i]
167
+ end
168
+ self.class.new(result)
169
+ else
170
+ raise ArgumentError, "Invalid argument type: #{arg.class}"
171
+ end
172
+ end
173
+
174
+ # Iterate over each element in the list
175
+ #
176
+ # This method yields each element in the list to the given block.
177
+ # It has a time complexity of O(n).
178
+ #
179
+ # @yield [Object] Gives each element in the list to the block
180
+ # @return [Enumerator] If no block is given
181
+ #
182
+ # @example Iterate over elements
183
+ # list = BinarySearch::List.new([1, 2, 3, 4, 5])
184
+ # list.each { |x| puts x } # Prints each number on a new line
185
+ def each(&block)
186
+ to_a.each(&block)
187
+ end
188
+
189
+ # Clear all elements from the list
190
+ #
191
+ # This method removes all elements from the list, leaving it empty.
192
+ # It has a time complexity of O(1).
193
+ #
194
+ # @return [BinarySearch::List] The empty list object
195
+ #
196
+ # @example Clear the list
197
+ # list = BinarySearch::List.new([1, 2, 3, 4, 5])
198
+ # list.clear # => #<BinarySearch::List: []>
199
+ # list.empty? # => true
200
+ def clear
201
+ @tree = BinarySearch::RedBlackTree.new
202
+ @size = 0
203
+ self
204
+ end
205
+
206
+ # Get the first element in the list
207
+ #
208
+ # This method returns the smallest element in the list.
209
+ # It has a time complexity of O(log n).
210
+ #
211
+ # @return [Object, nil] The first element, or nil if the list is empty
212
+ #
213
+ # @example Get the first element
214
+ # list = BinarySearch::List.new([3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5])
215
+ # list.first # => 1
216
+ def first
217
+ return nil if empty?
218
+ leftmost_node(@tree.root).key
219
+ end
220
+
221
+ # Get the last element in the list
222
+ #
223
+ # This method returns the largest element in the list.
224
+ # It has a time complexity of O(log n).
225
+ #
226
+ # @return [Object, nil] The last element, or nil if the list is empty
227
+ #
228
+ # @example Get the last element
229
+ # list = BinarySearch::List.new([3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5])
230
+ # list.last # => 9
231
+ def last
232
+ return nil if empty?
233
+ rightmost_node(@tree.root).key
234
+ end
235
+
236
+ # Remove and return the last element in the list
237
+ #
238
+ # This method removes and returns the largest element in the list.
239
+ # It has a time complexity of O(log n).
240
+ #
241
+ # @return [Object, nil] The last element, or nil if the list is empty
242
+ #
243
+ # @example Remove the last element
244
+ # list = BinarySearch::List.new([1, 2, 3, 4, 5])
245
+ # list.pop # => 5
246
+ # list.to_a # => [1, 2, 3, 4]
247
+ def pop
248
+ return nil if empty?
249
+ last_value = last
250
+ @tree.remove(last_value)
251
+ @size -= 1 if @size
252
+ last_value
253
+ end
254
+
255
+ # Remove and return the first element in the list
256
+ #
257
+ # This method removes and returns the smallest element in the list.
258
+ # It has a time complexity of O(log n).
259
+ #
260
+ # @return [Object, nil] The first element, or nil if the list is empty
261
+ #
262
+ # @example Remove the first element
263
+ # list = BinarySearch::List.new([1, 2, 3, 4, 5])
264
+ # list.shift # => 1
265
+ # list.to_a # => [2, 3, 4, 5]
266
+ def shift
267
+ return nil if empty?
268
+ first_value = first
269
+ @tree.remove(first_value)
270
+ @size -= 1 if @size
271
+ first_value
272
+ end
273
+
274
+ # Insert a value at the beginning of the list
275
+ #
276
+ # This method inserts a value at the beginning of the list.
277
+ # It has a time complexity of O(log n).
278
+ #
279
+ # @param value [Object] The value to insert
280
+ # @return [BinarySearch::List] The list object (for method chaining)
281
+ #
282
+ # @example Insert at the beginning
283
+ # list = BinarySearch::List.new([2, 3, 4, 5])
284
+ # list.unshift(1) # => #<BinarySearch::List: [1, 2, 3, 4, 5]>
285
+ def unshift(value)
286
+ insert(value)
287
+ self
288
+ end
289
+
290
+ # Get the maximum value in the list
291
+ #
292
+ # This method returns the largest element in the list.
293
+ # It has a time complexity of O(log n).
294
+ #
295
+ # @return [Object, nil] The maximum value, or nil if the list is empty
296
+ #
297
+ # @example Get the maximum value
298
+ # list = BinarySearch::List.new([3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5])
299
+ # list.max # => 9
300
+ def max
301
+ last
302
+ end
303
+
304
+ # Get the minimum value in the list
305
+ #
306
+ # This method returns the smallest element in the list.
307
+ # It has a time complexity of O(log n).
308
+ #
309
+ # @return [Object, nil] The minimum value, or nil if the list is empty
310
+ #
311
+ # @example Get the minimum value
312
+ # list = BinarySearch::List.new([3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5])
313
+ # list.min # => 1
314
+ def min
315
+ first
316
+ end
317
+
318
+ # Calculate the sum of all elements in the list
319
+ #
320
+ # This method returns the sum of all elements in the list.
321
+ # It has a time complexity of O(n).
322
+ #
323
+ # @return [Numeric] The sum of all elements
324
+ #
325
+ # @example Calculate the sum
326
+ # list = BinarySearch::List.new([1, 2, 3, 4, 5])
327
+ # list.sum # => 15
328
+ def sum
329
+ to_a.sum
330
+ end
331
+
332
+ # Find the first element that satisfies the given condition
333
+ #
334
+ # This method returns the first element for which the given block returns true.
335
+ # It has a time complexity of O(n) in the worst case.
336
+ #
337
+ # @yield [Object] Gives each element to the block
338
+ # @return [Object, nil] The first element for which the block returns true, or nil if none found
339
+ #
340
+ # @example Find an element
341
+ # list = BinarySearch::List.new([1, 2, 3, 4, 5])
342
+ # list.find { |x| x > 3 } # => 4
343
+ def find
344
+ each { |element| return element if yield(element) }
345
+ nil
346
+ end
347
+
348
+ # Create a new list with duplicate elements removed
349
+ #
350
+ # This method returns a new list with all duplicate elements removed.
351
+ # It has a time complexity of O(n log n).
352
+ #
353
+ # @return [BinarySearch::List] A new list with unique elements
354
+ #
355
+ # @example Remove duplicates
356
+ # list = BinarySearch::List.new([1, 2, 2, 3, 3, 3, 4, 5])
357
+ # list.uniq.to_a # => [1, 2, 3, 4, 5]
358
+ def uniq
359
+ self.class.new(to_a.uniq)
360
+ end
361
+
362
+ # Concatenate two lists
363
+ #
364
+ # This method returns a new list containing all elements from both lists.
365
+ # It has a time complexity of O(n + m), where n and m are the sizes of the lists.
366
+ #
367
+ # @param other [BinarySearch::List] The list to concatenate
368
+ # @return [BinarySearch::List] A new list containing elements from both lists
369
+ #
370
+ # @example Concatenate lists
371
+ # list1 = BinarySearch::List.new([1, 2, 3])
372
+ # list2 = BinarySearch::List.new([4, 5, 6])
373
+ # (list1 + list2).to_a # => [1, 2, 3, 4, 5, 6]
374
+ def +(other)
375
+ self.class.new(to_a + other.to_a)
376
+ end
377
+
378
+ # Compute the difference between two lists
379
+ #
380
+ # This method returns a new list containing elements that are in the current list
381
+ # but not in the other list, taking into account the number of occurrences of each element.
382
+ # It has a time complexity of O(n log n + m log m), where n and m are the sizes of the lists.
383
+ #
384
+ # @param other [BinarySearch::List] The list to subtract
385
+ # @return [BinarySearch::List] A new list containing elements in this list but not in the other
386
+ #
387
+ # @example Compute the difference
388
+ # list1 = BinarySearch::List.new([1, 2, 2, 3, 4, 5])
389
+ # list2 = BinarySearch::List.new([2, 4, 6])
390
+ # (list1 - list2).to_a # => [1, 2, 3, 5]
391
+ def -(other)
392
+ result = self.class.new
393
+ self_counts = Hash.new(0)
394
+ other_counts = Hash.new(0)
395
+
396
+ self.each { |item| self_counts[item] += 1 }
397
+ other.each { |item| other_counts[item] += 1 }
398
+
399
+ self_counts.each do |item, count|
400
+ diff = count - other_counts[item]
401
+ diff.times { result.insert(item) } if diff > 0
402
+ end
403
+
404
+ result
405
+ end
406
+
407
+ # Compute the intersection of two lists
408
+ #
409
+ # This method returns a new list containing elements common to both lists,
410
+ # taking into account the number of occurrences of each element.
411
+ # It has a time complexity of O(n log n + m log m), where n and m are the sizes of the lists.
412
+ #
413
+ # @param other [BinarySearch::List] The list to intersect with
414
+ # @return [BinarySearch::List] A new list containing elements common to both lists
415
+ #
416
+ # @example Compute the intersection
417
+ # list1 = BinarySearch::List.new([1, 2, 2, 3, 4, 5])
418
+ # list2 = BinarySearch::List.new([2, 2, 4, 6])
419
+ # (list1 & list2).to_a # => [2, 2, 4]
420
+ def &(other)
421
+ self.class.new(to_a & other.to_a)
422
+ end
423
+
424
+ # Compute the union of two lists
425
+ #
426
+ # This method returns a new list containing unique elements from both lists.
427
+ # It has a time complexity of O(n log n + m log m), where n and m are the sizes of the lists.
428
+ #
429
+ # @param other [BinarySearch::List] The list to unite with
430
+ # @return [BinarySearch::List] A new list containing unique elements from both lists
431
+ #
432
+ # @example Compute the union
433
+ # list1 = BinarySearch::List.new([1, 2, 3, 4])
434
+ # list2 = BinarySearch::List.new([3, 4, 5, 6])
435
+ # (list1 | list2).to_a # => [1, 2, 3, 4, 5, 6]
436
+ def |(other)
437
+ self.class.new(to_a | other.to_a)
438
+ end
439
+
440
+ # Compare two lists for equality
441
+ #
442
+ # This method checks if two lists have the same elements in the same order.
443
+ # It has a time complexity of O(n), where n is the size of the lists.
444
+ #
445
+ # @param other [Object] The object to compare with
446
+ # @return [Boolean] True if the lists are equal, false otherwise
447
+ #
448
+ # @example Compare lists
449
+ # list1 = BinarySearch::List.new([1, 2, 3, 4, 5])
450
+ # list2 = BinarySearch::List.new([1, 2, 3, 4, 5])
451
+ # list3 = BinarySearch::List.new([5, 4, 3, 2, 1])
452
+ # list1 == list2 # => true
453
+ # list1 == list3 # => false
454
+ def ==(other)
455
+ return false unless other.is_a?(BinarySearch::List)
456
+ return true if self.equal?(other)
457
+ self.to_a == other.to_a
458
+ end
459
+
460
+ # Provide a string representation of the list
461
+ #
462
+ # This method returns a concise string representation of the list,
463
+ # showing the class name and the size of the list.
464
+ #
465
+ # @return [String] A string representation of the list
466
+ #
467
+ # @example Inspect the list
468
+ # list = BinarySearch::List.new([1, 2, 3, 4, 5])
469
+ # list.inspect # => "#<BinarySearch::List: (5 elements)>"
470
+ def inspect
471
+ "#<#{self.class}: (#{size} elements)>"
472
+ end
473
+
474
+ private
475
+ # Build the tree from an initial list
476
+ #
477
+ # This method inserts each element from the initial list into the tree.
478
+ # It has a time complexity of O(n log n), where n is the size of the initial list.
479
+ #
480
+ # @param list [Array] The initial list of elements
481
+ # @return [void]
482
+ def build_tree(list)
483
+ list.each { |item| @tree.insert(item) }
484
+ @size = list.size
485
+ end
486
+
487
+ # Perform an inorder traversal of the tree
488
+ #
489
+ # This method traverses the tree in-order and returns an array of the elements.
490
+ # It has a time complexity of O(n), where n is the number of nodes in the tree.
491
+ #
492
+ # @param node [BinarySearch::RedBlackTree::Node] The current node
493
+ # @param result [Array] The result array
494
+ # @return [Array] An array of elements in sorted order
495
+ def inorder_traversal(node, result = [])
496
+ return result if node.nil?
497
+ inorder_traversal(node.left, result)
498
+ result << node.key
499
+ inorder_traversal(node.right, result)
500
+ end
501
+
502
+ # Find the leftmost node in a subtree
503
+ #
504
+ # This method finds the node with the smallest key in the given subtree.
505
+ # It has a time complexity of O(log n) in a balanced tree.
506
+ #
507
+ # @param node [BinarySearch::RedBlackTree::Node] The root of the subtree
508
+ # @return [BinarySearch::RedBlackTree::Node] The leftmost node
509
+ def leftmost_node(node)
510
+ return node if node.left.nil?
511
+ leftmost_node(node.left)
512
+ end
513
+
514
+ # Find the rightmost node in a subtree
515
+ #
516
+ # This method finds the node with the largest key in the given subtree.
517
+ # It has a time complexity of O(log n) in a balanced tree.
518
+ #
519
+ # @param node [BinarySearch::RedBlackTree::Node] The root of the subtree
520
+ # @return [BinarySearch::RedBlackTree::Node] The rightmost node
521
+ def rightmost_node(node)
522
+ return node if node.right.nil?
523
+ rightmost_node(node.right)
524
+ end
525
+ end
526
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BinarySearch
4
+ class RedBlackTree
5
+ # Represents a node in the Red-Black Tree
6
+ #
7
+ # A node contains a key, color, references to its left and right children,
8
+ # and a reference to its parent. The color is used to maintain the balance
9
+ # properties of the Red-Black Tree.
10
+ Node = Struct.new('Node', :key, :color, :left, :right, :parent) do
11
+ # Creates a new Node
12
+ #
13
+ # @param key [Comparable] The key stored in the node
14
+ # @param color [Symbol] The color of the node (:red or :black)
15
+ # @param left [Node, nil] The left child of the node
16
+ # @param right [Node, nil] The right child of the node
17
+ # @param parent [Node, nil] The parent of the node
18
+ def initialize(key, color = :red, left = nil, right = nil, parent = nil)
19
+ super(key, color, left, right, parent)
20
+ end
21
+
22
+ # Checks if the node is black
23
+ #
24
+ # @return [Boolean] true if the node is black, false otherwise
25
+ def black?
26
+ color == :black
27
+ end
28
+
29
+ # Checks if the node is red
30
+ #
31
+ # @return [Boolean] true if the node is red, false otherwise
32
+ def red?
33
+ color == :red
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,385 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BinarySearch
4
+ # Implements a Red-Black Tree, a self-balancing binary search tree
5
+ #
6
+ # A Red-Black Tree is a type of self-balancing binary search tree that maintains
7
+ # balance through the use of node colors (red and black) and a set of properties:
8
+ #
9
+ # 1. Every node is either red or black.
10
+ # 2. The root is black.
11
+ # 3. Every leaf (NIL) is black.
12
+ # 4. If a node is red, then both its children are black.
13
+ # 5. For each node, all simple paths from the node to descendant leaves contain the
14
+ # same number of black nodes.
15
+ #
16
+ # These properties ensure that the tree remains approximately balanced during
17
+ # insertions and deletions, guaranteeing O(log n) time complexity for basic
18
+ # operations like search, insert, and delete.
19
+ class RedBlackTree
20
+ # @return [Node, nil] The root node of the tree
21
+ attr_reader :root
22
+
23
+ # Initializes an empty Red-Black Tree
24
+ def initialize
25
+ @root = nil
26
+ end
27
+
28
+ # Inserts a new key into the tree
29
+ #
30
+ # The insertion process involves:
31
+ # 1. Performing a standard BST insertion
32
+ # 2. Coloring the new node red
33
+ # 3. Rebalancing the tree to maintain Red-Black properties
34
+ #
35
+ # @param key [Comparable] The key to insert
36
+ # @return [void]
37
+ def insert(key)
38
+ new_node = Node.new(key)
39
+ if @root.nil?
40
+ @root = new_node
41
+ @root.color = :black
42
+ else
43
+ current = @root
44
+ parent = nil
45
+ while current
46
+ parent = current
47
+ comparison = key <=> current.key
48
+ case comparison
49
+ when -1
50
+ current = current.left
51
+ when 1
52
+ current = current.right
53
+ else
54
+ # For duplicates, we'll add to the right
55
+ current = current.right
56
+ end
57
+ end
58
+ new_node.parent = parent
59
+ comparison = key <=> parent.key
60
+ if comparison <= 0
61
+ parent.left = new_node
62
+ else
63
+ parent.right = new_node
64
+ end
65
+ fix_insert(new_node)
66
+ end
67
+ end
68
+
69
+ # Removes a key from the tree
70
+ #
71
+ # The removal process involves:
72
+ # 1. Finding the node to be removed
73
+ # 2. If the node has two children, replacing it with its successor
74
+ # 3. Removing the node (or its successor)
75
+ # 4. Rebalancing the tree if the removed node was black
76
+ #
77
+ # @param key [Comparable] The key to remove
78
+ # @return [Node, nil] The removed node, or nil if the key was not found
79
+ def remove(key)
80
+ node = find(key)
81
+ return nil unless node
82
+
83
+ remove_node(node)
84
+ end
85
+
86
+ # Updates a key in the tree
87
+ #
88
+ # This operation ensures that the tree structure remains valid after updating a key.
89
+ # It's implemented as a removal of the old key followed by an insertion of the new key.
90
+ #
91
+ # @param old_key [Comparable] The key to update
92
+ # @param new_key [Comparable] The new key value
93
+ # @return [Boolean, nil] true if updated, false if new_key already exists, nil if old_key not found
94
+ def update(old_key, new_key)
95
+ node = find(old_key)
96
+ return nil unless node
97
+ return false if find(new_key)
98
+
99
+ node.key = new_key
100
+ true
101
+ end
102
+
103
+ # Finds a node with the given key
104
+ #
105
+ # This method performs a standard binary search tree lookup.
106
+ #
107
+ # @param key [Comparable] The key to find
108
+ # @return [Node, nil] The node with the given key, or nil if not found
109
+ def find(key)
110
+ current = @root
111
+ while current
112
+ comparison = key <=> current.key
113
+ return current if comparison == 0
114
+ current = comparison < 0 ? current.left : current.right
115
+ end
116
+ nil
117
+ end
118
+
119
+ private
120
+ # Fixes the tree after insertion to maintain Red-Black properties
121
+ #
122
+ # This method is called after every insertion to ensure that the Red-Black
123
+ # properties are maintained. It performs color changes and rotations as necessary.
124
+ #
125
+ # @param node [Node] The newly inserted node
126
+ # @return [void]
127
+ def fix_insert(node)
128
+ while node.parent&.red?
129
+ if node.parent == node.parent.parent&.left
130
+ uncle = node.parent.parent&.right
131
+ if uncle&.red?
132
+ node.parent.color = :black
133
+ uncle.color = :black
134
+ node.parent.parent.color = :red
135
+ node = node.parent.parent
136
+ else
137
+ if node == node.parent.right
138
+ node = node.parent
139
+ left_rotate(node)
140
+ end
141
+ node.parent.color = :black
142
+ node.parent.parent.color = :red
143
+ right_rotate(node.parent.parent)
144
+ end
145
+ else
146
+ uncle = node.parent.parent&.left
147
+ if uncle&.red?
148
+ node.parent.color = :black
149
+ uncle.color = :black
150
+ node.parent.parent.color = :red
151
+ node = node.parent.parent
152
+ else
153
+ if node == node.parent.left
154
+ node = node.parent
155
+ right_rotate(node)
156
+ end
157
+ node.parent.color = :black
158
+ node.parent.parent.color = :red
159
+ left_rotate(node.parent.parent)
160
+ end
161
+ end
162
+ end
163
+ @root.color = :black
164
+ end
165
+
166
+ # Performs a left rotation on the given node
167
+ #
168
+ # A left rotation is a local operation in a search tree that changes the structure
169
+ # of the tree while preserving the search tree properties of the nodes involved.
170
+ #
171
+ # @param x [Node] The node to rotate
172
+ # @return [void]
173
+ def left_rotate(x)
174
+ y = x.right
175
+ x.right = y.left
176
+ y.left.parent = x if y.left
177
+ y.parent = x.parent
178
+ if x.parent.nil?
179
+ @root = y
180
+ elsif x == x.parent.left
181
+ x.parent.left = y
182
+ else
183
+ x.parent.right = y
184
+ end
185
+ y.left = x
186
+ x.parent = y
187
+ end
188
+
189
+ # Performs a right rotation on the given node
190
+ #
191
+ # A right rotation is the mirror operation of a left rotation.
192
+ #
193
+ # @param y [Node] The node to rotate
194
+ # @return [void]
195
+ def right_rotate(y)
196
+ x = y.left
197
+ y.left = x.right
198
+ x.right.parent = y if x.right
199
+ x.parent = y.parent
200
+ if y.parent.nil?
201
+ @root = x
202
+ elsif y == y.parent.right
203
+ y.parent.right = x
204
+ else
205
+ y.parent.left = x
206
+ end
207
+ x.right = y
208
+ y.parent = x
209
+ end
210
+
211
+ # Removes a node from the tree
212
+ #
213
+ # This method handles the actual removal of a node and calls the necessary
214
+ # fix-up routines to maintain the Red-Black properties.
215
+ #
216
+ # @param node [Node] The node to remove
217
+ # @return [void]
218
+ def remove_node(node)
219
+ if node.left && node.right
220
+ successor = minimum(node.right)
221
+ node.key = successor.key
222
+ remove_node(successor)
223
+ else
224
+ child = node.left || node.right
225
+ if node.black?
226
+ if child&.red?
227
+ child.color = :black
228
+ else
229
+ delete_case1(node)
230
+ end
231
+ end
232
+ replace_node(node, child)
233
+ end
234
+ @root.color = :black if @root
235
+ end
236
+
237
+ # Finds the minimum node in a subtree
238
+ #
239
+ # This method is used in the deletion process to find the successor of a node.
240
+ #
241
+ # @param node [Node] The root of the subtree
242
+ # @return [Node] The minimum node
243
+ def minimum(node)
244
+ node = node.left while node.left
245
+ node
246
+ end
247
+
248
+ # Replaces one node with another in the tree
249
+ #
250
+ # This method is a helper for the removal process, updating the necessary
251
+ # parent-child relationships.
252
+ #
253
+ # @param old [Node] The node to be replaced
254
+ # @param new [Node, nil] The replacement node
255
+ # @return [void]
256
+ def replace_node(old, new)
257
+ if old.parent.nil?
258
+ @root = new
259
+ elsif old == old.parent.left
260
+ old.parent.left = new
261
+ else
262
+ old.parent.right = new
263
+ end
264
+ new.parent = old.parent if new
265
+ end
266
+
267
+ # Handles case 1 of the delete fixup
268
+ #
269
+ # The delete fixup cases are part of the algorithm to maintain Red-Black
270
+ # properties after a black node is removed from the tree.
271
+ #
272
+ # @param node [Node] The node being processed
273
+ # @return [void]
274
+ def delete_case1(node)
275
+ delete_case2(node) if node.parent
276
+ end
277
+
278
+ # Handles case 2 of the delete fixup
279
+ #
280
+ # @param node [Node] The node being processed
281
+ # @return [void]
282
+ def delete_case2(node)
283
+ sibling = get_sibling(node)
284
+ return if sibling.nil? || node.parent.nil?
285
+ if sibling.red?
286
+ node.parent.color = :red
287
+ sibling.color = :black
288
+ if node == node.parent.left
289
+ left_rotate(node.parent)
290
+ else
291
+ right_rotate(node.parent)
292
+ end
293
+ end
294
+ delete_case3(node)
295
+ end
296
+
297
+ # Handles case 3 of the delete fixup
298
+ #
299
+ # @param node [Node] The node being processed
300
+ # @return [void]
301
+ def delete_case3(node)
302
+ sibling = get_sibling(node)
303
+ return if sibling.nil? || node.parent.nil?
304
+ if node.parent.black? && sibling.black? &&
305
+ (!sibling.left || sibling.left.black?) &&
306
+ (!sibling.right || sibling.right.black?)
307
+ sibling.color = :red
308
+ delete_case1(node.parent)
309
+ else
310
+ delete_case4(node)
311
+ end
312
+ end
313
+
314
+ # Handles case 4 of the delete fixup
315
+ #
316
+ # @param node [Node] The node being processed
317
+ # @return [void]
318
+ def delete_case4(node)
319
+ sibling = get_sibling(node)
320
+ return if sibling.nil? || node.parent.nil?
321
+ if node.parent.red? && sibling.black? &&
322
+ (!sibling.left || sibling.left.black?) &&
323
+ (!sibling.right || sibling.right.black?)
324
+ sibling.color = :red
325
+ node.parent.color = :black
326
+ else
327
+ delete_case5(node)
328
+ end
329
+ end
330
+
331
+ # Handles case 5 of the delete fixup
332
+ #
333
+ # @param node [Node] The node being processed
334
+ # @return [void]
335
+ def delete_case5(node)
336
+ sibling = get_sibling(node)
337
+ return if sibling.nil? || node.parent.nil?
338
+ if sibling.black?
339
+ if node == node.parent.left &&
340
+ (!sibling.right || sibling.right.black?) &&
341
+ sibling.left&.red?
342
+ sibling.color = :red
343
+ sibling.left.color = :black
344
+ right_rotate(sibling)
345
+ elsif node == node.parent.right &&
346
+ (!sibling.left || sibling.left.black?) &&
347
+ sibling.right&.red?
348
+ sibling.color = :red
349
+ sibling.right.color = :black
350
+ left_rotate(sibling)
351
+ end
352
+ end
353
+ delete_case6(node)
354
+ end
355
+
356
+ # Handles case 6 of the delete fixup
357
+ #
358
+ # @param node [Node] The node being processed
359
+ # @return [void]
360
+ def delete_case6(node)
361
+ sibling = get_sibling(node)
362
+ return if sibling.nil? || node.parent.nil?
363
+ sibling.color = node.parent.color
364
+ node.parent.color = :black
365
+ if node == node.parent.left
366
+ sibling.right.color = :black if sibling.right
367
+ left_rotate(node.parent)
368
+ else
369
+ sibling.left.color = :black if sibling.left
370
+ right_rotate(node.parent)
371
+ end
372
+ end
373
+
374
+ # Gets the sibling of a node
375
+ #
376
+ # This helper method is used in the delete fixup process.
377
+ #
378
+ # @param node [Node] The node whose sibling to find
379
+ # @return [Node, nil] The sibling node, or nil if there is no sibling
380
+ def get_sibling(node)
381
+ return nil if node.parent.nil?
382
+ node == node.parent.left ? node.parent.right : node.parent.left
383
+ end
384
+ end
385
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BinarySearch
4
+ VERSION = '1.0.0'
5
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'set'
4
+ require 'singleton'
5
+
6
+ Dir[File.join(__dir__, 'binary_search', '**', '*.rb')].each { |file| require file }
7
+
8
+ module BinarySearch
9
+ class Error < StandardError; end
10
+ end
metadata ADDED
@@ -0,0 +1,60 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ruby_binary_search
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - sebi
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2024-08-03 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Binary search list implemented in ruby using red-black self-balancing
14
+ tree
15
+ email:
16
+ - gore.sebyx@yahoo.com
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - ".rspec"
22
+ - ".rubocop.yml"
23
+ - CHANGELOG.md
24
+ - CODE_OF_CONDUCT.md
25
+ - LICENSE.txt
26
+ - README.md
27
+ - Rakefile
28
+ - lefthook.yml
29
+ - lib/binary_search/list.rb
30
+ - lib/binary_search/red_black_tree.rb
31
+ - lib/binary_search/red_black_tree/node.rb
32
+ - lib/binary_search/version.rb
33
+ - lib/ruby_binary_search.rb
34
+ homepage: https://github.com/sebyx07/ruby-binary-search
35
+ licenses:
36
+ - MIT
37
+ metadata:
38
+ allowed_push_host: https://rubygems.org
39
+ homepage_uri: https://github.com/sebyx07/ruby-binary-search
40
+ source_code_uri: https://github.com/sebyx07/ruby-binary-search
41
+ post_install_message:
42
+ rdoc_options: []
43
+ require_paths:
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: 3.0.0
50
+ required_rubygems_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ requirements: []
56
+ rubygems_version: 3.5.11
57
+ signing_key:
58
+ specification_version: 4
59
+ summary: Binary search list implemented in ruby using red-black self-balancing tree
60
+ test_files: []