sorted_containers 0.1.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,8 +2,23 @@
2
2
 
3
3
  # The SortedContainers module provides data structures for sorted collections.
4
4
  # rubocop:disable Metrics/ClassLength
5
+ require "English"
5
6
  module SortedContainers
6
- # The SortedArray class is a sorted array implementation.
7
+ # SortedArray is an array that maintains its elements in sorted order.
8
+ #
9
+ # It contains most of the same methods as a regular Array, but some methods
10
+ # have been removed because they would not make sense for a sorted array. For
11
+ # example, the #rotate, #shuffle, and #reverse methods have been removed.
12
+ #
13
+ # SortedArray also has the additional following methods:
14
+ # - #add
15
+ # - #update
16
+ # - #bisect_left
17
+ # - #bisect_right
18
+ #
19
+ # There are also methods that have been obtimized using the sorted nature of
20
+ # the array. For example, the #include? method has been optimized to use
21
+ # binary search.
7
22
  class SortedArray
8
23
  include Enumerable
9
24
 
@@ -11,7 +26,7 @@ module SortedContainers
11
26
  # Sublists are split when they exceed 2 * load_factor
12
27
  DEFAULT_LOAD_FACTOR = 1000
13
28
 
14
- attr_reader :size
29
+ attr_reader :size, :load_factor
15
30
  alias length size
16
31
 
17
32
  # Initializes a new SortedArray object.
@@ -21,128 +36,91 @@ module SortedContainers
21
36
  def initialize(iterable = [], load_factor: DEFAULT_LOAD_FACTOR)
22
37
  @lists = []
23
38
  @maxes = []
24
- @index = []
39
+ @array_index = []
25
40
  @offset = 0
26
41
  @load_factor = load_factor
27
42
  @size = 0
28
43
  update(iterable)
29
44
  end
30
45
 
31
- # rubocop:disable Metrics/MethodLength
32
-
33
- # Adds a value to the sorted array.
46
+ # Returns a new SortedArray populated with the given objects.
34
47
  #
35
- # @param value [Object] The value to add.
36
- def add(value)
37
- if @maxes.empty?
38
- @lists.append([value])
39
- @maxes.append(value)
40
- else
41
- pos = internal_bisect_right(@maxes, value)
42
-
43
- if pos == @maxes.size
44
- pos -= 1
45
- @lists[pos].push(value)
46
- @maxes[pos] = value
47
- else
48
- sub_pos = internal_bisect_right(@lists[pos], value)
49
- @lists[pos].insert(sub_pos, value)
50
- end
51
- expand(pos)
52
- end
53
- @size += 1
48
+ # @param args [Array] The objects to populate the array with.
49
+ # @return [SortedArray] The populated array.
50
+ def self.[](*args)
51
+ new(args)
54
52
  end
55
53
 
56
- # rubocop:enable Metrics/MethodLength
57
-
58
- # Alias for add
54
+ # Returns a new SortedArray with the values from the union of the two arrays.
59
55
  #
60
- # @param value [Object] The value to add.
61
- def <<(value)
62
- add(value)
56
+ # @param other [SortedArray] The other array to union with.
57
+ # @return [SortedArray] The union of the two arrays.
58
+ def intersection(other)
59
+ self.class.new(to_a & other.to_a, load_factor: @load_factor)
63
60
  end
61
+ alias & intersection
64
62
 
65
- # Returns a string representation of the sorted array.
63
+ # When non-negative, multiplies returns a new Array with each value repeated `int` times.
66
64
  #
67
- # @return [String] A string representation of the sorted array.
68
- def to_s
69
- "SortedArray(#{to_a})"
65
+ # @param num [Integer] The integer to multiply the array by.
66
+ # @return [SortedArray] The multiplied sorted array.
67
+ def multiply(num)
68
+ self.class.new(to_a * num, load_factor: @load_factor)
70
69
  end
70
+ alias * multiply
71
71
 
72
- # Checks if Array is empty
72
+ # Returns a new SortedArray with the values from both arrays.
73
73
  #
74
- # @return [Boolean]
75
- def empty?
76
- @size.zero?
74
+ # @param other [SortedArray] The other array to add.
75
+ # @return [SortedArray] The combined array.
76
+ def +(other)
77
+ self.class.new(to_a + other.to_a, load_factor: @load_factor)
77
78
  end
78
79
 
79
- # Returns an index to insert `value` in the sorted list.
80
- #
81
- # If the `value` is already present, the insertion point will be before
82
- # (to the left of) any existing values.
80
+ # Returns a new SortedArray with the values from the difference of the two arrays.
83
81
  #
84
- # Runtime complexity: `O(log(n))` -- approximate.
85
- #
86
- # @param value [Object] The value to insert.
87
- # @return [Integer] The index to insert the value.
88
- def bisect_left(value)
89
- return 0 if @maxes.empty?
90
-
91
- pos = internal_bisect_left(@maxes, value)
92
-
93
- return @size if pos == @maxes.size
94
-
95
- idx = internal_bisect_left(@lists[pos], value)
96
- loc(pos, idx)
82
+ # @param other [SortedArray] The other array to subtract.
83
+ # @return [SortedArray] The difference of the two arrays.
84
+ def difference(other)
85
+ self.class.new(to_a - other.to_a, load_factor: @load_factor)
97
86
  end
87
+ alias - difference
98
88
 
99
- # Returns an index to insert `value` in the sorted list.
100
- #
101
- # If the `value` is already present, the insertion point will be after
102
- # (to the right of) any existing values.
103
- #
104
- # Runtime complexity: `O(log(n))` -- approximate.
89
+ # Returns -1, 0, or 1 if +self+ is less than, equal to, or greater than other. For each index +i+ in +self+,
90
+ # evaluates +self[i] <=> other[i]+
105
91
  #
106
- # @param value [Object] The value to insert.
107
- # @return [Integer] The index to insert the value.
108
- def bisect_right(value)
109
- return 0 if @maxes.empty?
110
-
111
- pos = internal_bisect_right(@maxes, value)
92
+ # @param other [SortedArray] The other array to compare.
93
+ # @return [Integer] -1, 0, or 1 as self is less than, equal to, or greater than other.
94
+ def <=>(other)
95
+ return size <=> other.size if size != other.size
112
96
 
113
- return @size if pos == @maxes.size
97
+ each_with_index do |value, index|
98
+ return value <=> other[index] if value != other[index]
99
+ end
114
100
 
115
- idx = internal_bisect_right(@lists[pos], value)
116
- loc(pos, idx)
101
+ 0
117
102
  end
118
103
 
119
- # Deletes a value from the sorted array.
104
+ # Returns true if the arrays size and values are equal.
120
105
  #
121
- # @param value [Object] The value to delete.
122
- def delete(value)
123
- return if @maxes.empty?
124
-
125
- pos = internal_bisect_left(@maxes, value)
126
-
127
- return if pos == @maxes.size
128
-
129
- idx = internal_bisect_left(@lists[pos], value)
106
+ # @param other [SortedArray] The other array to compare.
107
+ # @return [Boolean] True if the arrays are equal, false otherwise.
108
+ def ==(other)
109
+ return false unless other.is_a?(SortedArray)
130
110
 
131
- internal_delete(pos, idx) if @lists[pos][idx] == value
132
- end
133
-
134
- # Tries to match the behavior of Array#[]
135
- # alias for slice
136
- #
137
- # @param args [Integer, Range, Enumerator::ArithmeticSequence] The index or range of values to retrieve.
138
- # @return [Object, Array] The value or values at the specified index or range.
139
- def [](*args)
140
- slice(*args)
111
+ size == other.size && each_with_index.all? { |value, index| value == other[index] }
141
112
  end
142
113
 
143
114
  # rubocop:disable Metrics/MethodLength
144
115
 
145
- # Tries to match the behavior of Array#slice
116
+ # Returns elements from array at the specified index or range. Does not modify the array.
117
+ #
118
+ # If a single index is provided, returns the value at that index.
119
+ #
120
+ # If a range is provided, returns the values in that range.
121
+ #
122
+ # If a start index and length are provided, returns the values starting from the start index and
123
+ # continuing for the given length.
146
124
  #
147
125
  # @param args [Integer, Range, Enumerator::ArithmeticSequence] The index or range of values to retrieve.
148
126
  # @return [Object, Array] The value or values at the specified index or range.
@@ -167,13 +145,22 @@ module SortedContainers
167
145
  raise ArgumentError, "wrong number of arguments (given #{args.size}, expected 1..2)"
168
146
  end
169
147
  end
148
+ alias [] slice
149
+
170
150
  # rubocop:enable Metrics/MethodLength
171
151
 
172
152
  # rubocop:disable Metrics/MethodLength
173
153
  # rubocop:disable Metrics/AbcSize
174
154
  # rubocop:disable Metrics/CyclomaticComplexity
175
155
 
176
- # Tries to match the behavior of Array#slice!
156
+ # Removes elements from array at the specified index or range and returns them. Modifies the array.
157
+ #
158
+ # If a single index is provided, returns the value at that index.
159
+ #
160
+ # If a range is provided, returns the values in that range.
161
+ #
162
+ # If a start index and length are provided, returns the values starting from the start index and
163
+ # continuing for the given length.
177
164
  #
178
165
  # @param args [Integer, Range, Enumerator::ArithmeticSequence] The index or range of values to retrieve.
179
166
  # @return [Object, Array] The value or values at the specified index or range.
@@ -210,14 +197,488 @@ module SortedContainers
210
197
  # rubocop:enable Metrics/AbcSize
211
198
  # rubocop:enable Metrics/CyclomaticComplexity
212
199
 
213
- # Retrieves the last value in the sorted array.
200
+ # rubocop:disable Metrics/AbcSize
201
+ # rubocop:disable Metrics/CyclomaticComplexity
202
+ # rubocop:disable Metrics/MethodLength
203
+ # rubocop:disable Metrics/PerceivedComplexity
204
+
205
+ # Sets the value at the specified index or range.
206
+ #
207
+ # If a single index is provided, sets the value at that index.
208
+ #
209
+ # If a range is provided, sets the values in that range.
210
+ #
211
+ # If a start index and length are provided, sets the values starting from the start index and
212
+ # continuing for the given length.
213
+ #
214
+ # @overload []=(index, value)
215
+ # @param index [Integer] The index of the value to set.
216
+ # @param value [Object] The value to set.
217
+ # @overload []=(range, value)
218
+ # @param range [Range] The range of values to set.
219
+ # @param value [Object] The value to set.
220
+ # @overload []=(start, length, value)
221
+ # @param start [Integer] The index to start from.
222
+ # @param length [Integer] The length of the values to set.
223
+ # @param value [Object, Array] The value or values to set.
224
+ # @return [Object, Array] The value or values at the specified index or range.
225
+ def []=(*args)
226
+ raise ArgumentError, "wrong number of arguments (given #{args.size}, expected 2..3)" if args.size < 2
227
+
228
+ value = args.pop
229
+ case args.size
230
+ when 1
231
+ if args[0].is_a?(Integer)
232
+ index = args[0]
233
+ delete_at(index)
234
+ add(value)
235
+ elsif args[0].is_a?(Range)
236
+ range = args[0]
237
+ values = get_values_from_range(range)
238
+ values.each { |val| delete(val) }
239
+ if value.is_a?(Array)
240
+ value.each { |val| add(val) }
241
+ else
242
+ add(value)
243
+ end
244
+ else
245
+ raise TypeError, "no implicit conversion of #{args[0].class} into Integer"
246
+ end
247
+ when 2
248
+ start, length = args
249
+ values = get_values_from_start_and_length(start, length)
250
+ values.each { |val| delete(val) }
251
+ add(value)
252
+ else
253
+ raise ArgumentError, "wrong number of arguments (given #{args.size}, expected 2..3)"
254
+ end
255
+ end
256
+
257
+ # rubocop:enable Metrics/AbcSize
258
+ # rubocop:enable Metrics/CyclomaticComplexity
259
+ # rubocop:enable Metrics/MethodLength
260
+ # rubocop:enable Metrics/PerceivedComplexity
261
+
262
+ # Calculates the set of unambiguous abbreviations for the strings in +self+.
263
+ #
264
+ # require 'abbrev'
265
+ # SortedArray.new(%w{ car cone }).abbrev
266
+ # #=> {"car"=>"car", "ca"=>"car", "cone"=>"cone", "con"=>"cone", "co"=>"cone"}
267
+ #
268
+ # The optional +pattern+ parameter is a pattern or a string. Only input
269
+ # strings that match the pattern or start with the string are included in the
270
+ # output hash.
271
+ #
272
+ # SortedArray.new(%w{ fast boat day }).abbrev(/^.a/)
273
+ # #=> {"fast"=>"fast", "fas"=>"fast", "fa"=>"fast", "day"=>"day", "da"=>"day"}
274
+ #
275
+ # @param pattern [Regexp, String] The pattern to match.
276
+ # @return [Hash] The set of unambiguous abbreviations.
277
+ # See also Abbrev.abbrev
278
+ def abbrev(pattern = nil)
279
+ to_a.abbrev(pattern)
280
+ end
281
+
282
+ # rubocop:disable Metrics/MethodLength
283
+ # rubocop:disable Metrics/AbcSize
284
+
285
+ # Adds a value to the sorted array.
286
+ #
287
+ # @param value [Object] The value to add.
288
+ def add(value)
289
+ if @maxes.empty?
290
+ @lists.append([value])
291
+ @maxes.append(value)
292
+ else
293
+ raise ArgumentError, "value must be comparable" unless @maxes[0] <=> value
294
+
295
+ pos = internal_bisect_right(@maxes, value)
296
+
297
+ if pos == @maxes.size
298
+ pos -= 1
299
+ @lists[pos].push(value)
300
+ @maxes[pos] = value
301
+ else
302
+ sub_pos = internal_bisect_right(@lists[pos], value)
303
+ @lists[pos].insert(sub_pos, value)
304
+ end
305
+ expand(pos)
306
+ end
307
+ @size += 1
308
+ self
309
+ end
310
+ alias << add
311
+ alias push add
312
+ alias append add
313
+
314
+ # rubocop:enable Metrics/MethodLength
315
+ # rubocop:enable Metrics/AbcSize
316
+
317
+ # Returns the first element in +self+ that is an +Array+ whose first element +==+ +obj+:
318
+ #
319
+ # @param obj [Object] The object to search for.
320
+ # @return [Array] The first element in +self+ that is an +Array+ whose first element +==+ +obj+.
321
+ def assoc(obj)
322
+ index = bsearch_index { |x| x.is_a?(Array) && x.first >= obj }
323
+ index.nil? ? nil : self[index]
324
+ end
325
+
326
+ # Returns the element at +Integer+ offset +index+; does not modify +self+.
327
+ # If +index+ is negative, counts from the end of +self+.
328
+ # Returns +nil+ if the +index+ is out of range.
329
+ # Will raise +TypeError+ if the +index+ is not an +Integer+.
330
+ #
331
+ # @param index [Integer] The index of the value to retrieve.
332
+ # @return [Object] The value at the specified index.
333
+ def at(index)
334
+ raise TypeError, "no implicit conversion of #{index.class} into Integer" unless index.is_a?(Integer)
335
+
336
+ self[index.to_i]
337
+ end
338
+
339
+ # Returns an element from +self+ selected by a binary search.
340
+ #
341
+ # @yield [value] The block to search with.
342
+ # @return [Object] The value selected by the binary search.
343
+ def bsearch(&block)
344
+ index_result = bsearch_index(&block)
345
+
346
+ return nil if index_result.nil?
347
+
348
+ self[index_result]
349
+ end
350
+
351
+ # Returns an index of an element from +self+ selected by a binary search.
352
+ #
353
+ # @yield [value] The block to search with.
354
+ # @return [Integer] The index of the value selected by the binary search.
355
+ def bsearch_index(&block)
356
+ return nil if @maxes.empty?
357
+
358
+ pos = @maxes.bsearch_index(&block)
359
+
360
+ return nil if pos.nil?
361
+
362
+ idx = @lists[pos].bsearch_index(&block)
363
+ loc(pos, idx)
364
+ end
365
+
366
+ # Clears the sorted array, removing all values.
367
+ #
368
+ # @return [SortedArray] The cleared sorted array.
369
+ def clear
370
+ @lists.map(&:clear)
371
+ @lists.clear
372
+ @maxes.clear
373
+ @array_index.clear
374
+ @offset = 0
375
+ @size = 0
376
+ self
377
+ end
378
+
379
+ # Calls the block, if given, with each element of +self+;
380
+ # returns +self+ after the block has been executed.
381
+ #
382
+ # If no block is given, returns an Enumerator.
383
+ #
384
+ # @yield [value] The block to map with.
385
+ # @return [SortedArray, Enumerator] The mapped array.
386
+ def map!
387
+ return to_enum(:map!) unless block_given?
388
+
389
+ new_values = []
390
+ # rubocop:disable Style/MapIntoArray
391
+ each { |value| new_values << yield(value) }
392
+ # rubocop:enable Style/MapIntoArray
393
+ clear
394
+ # Experimitation shows that it's faster to add all values at once
395
+ # rather than adding them one by one
396
+ update(new_values)
397
+ self
398
+ end
399
+ alias collect! map!
400
+
401
+ # Adds the elements of one or more arrays to the SortedArray.
402
+ #
403
+ # @param other_arrays [Array] The arrays to concatenate.
404
+ # @return [SortedArray] +self+. The SortedArray with the concatenated values.
405
+ def concat(*other_arrays)
406
+ other_arrays.each do |array|
407
+ update(array)
408
+ end
409
+ self
410
+ end
411
+
412
+ # Returns the count of elements, based on an argument or block criterion, if given.
413
+ # With no argument and no block given, returns the number of elements:
414
+ # With argument object given, returns the number of elements that are == to object:
415
+ # Uses binary search to find the first and last index of the value.
416
+ #
417
+ # @param value [Object] The value to count.
418
+ # @yield [value] The block to count with.
419
+ # @return [Integer] The count of elements.
420
+ def count(value = nil)
421
+ # If block is given, we call super to use the Enumerable#count method
422
+ return super() if block_given?
423
+ return @size unless value
424
+
425
+ left_index = bisect_left(value)
426
+ right_index = bisect_right(value)
427
+ right_index - left_index
428
+ end
429
+
430
+ # Deletes a value from the sorted array.
431
+ #
432
+ # @param value [Object] The value to delete.
433
+ def delete(value)
434
+ return if @maxes.empty?
435
+
436
+ pos = internal_bisect_left(@maxes, value)
437
+
438
+ return if pos == @maxes.size
439
+
440
+ idx = internal_bisect_left(@lists[pos], value)
441
+
442
+ internal_delete(pos, idx) if @lists[pos][idx] == value
443
+ end
444
+
445
+ # Deletes the value at the specified index.
446
+ #
447
+ # @param index [Integer] The index of the value to delete.
448
+ def delete_at(index)
449
+ return nil if index.abs >= @size
450
+
451
+ pos, idx = pos(index)
452
+ internal_delete(pos, idx)
453
+ end
454
+
455
+ # Removes each element from the array for which block evaluates to true.
456
+ #
457
+ # @yield [value] The block to delete with.
458
+ # @return [SortedArray] +self+. The array with the deleted values.
459
+ def delete_if
460
+ return to_enum(:delete_if) unless block_given?
461
+
462
+ to_delete = []
463
+ each do |value|
464
+ to_delete << value if yield(value)
465
+ end
466
+ to_delete.each { |value| delete(value) }
467
+ self
468
+ end
469
+
470
+ # Finds and returns the object in nested objects that is specified by +index+ and +identifiers+.
471
+ # The nested objects may be instances of various classes. See Dig methods.
472
+ #
473
+ # @param index [Integer] The index of the value to retrieve.
474
+ # @param identifiers [Array] The identifiers to retrieve the value.
475
+ # @return [Object] The value at the specified index.
476
+ def dig(index, *identifiers)
477
+ result = self[index]
478
+ return nil if result.nil?
479
+
480
+ identifiers.each do |identifier|
481
+ raise TypeError, "#{result.class} does not have #dig method" unless result.respond_to?(:dig)
482
+
483
+ # rubocop:disable Style/SingleArgumentDig
484
+ result = result.dig(identifier)
485
+ # rubocop:enable Style/SingleArgumentDig
486
+ return nil if result.nil?
487
+ end
488
+
489
+ result
490
+ end
491
+
492
+ # rubocop:disable Naming/MethodParameterName
493
+
494
+ # Returns a new SortedArray containing all but the first +n+ elements, where +n+ is a non-negative integer.
495
+ # Does not modify the +self+.
496
+ #
497
+ # @param n [Integer] The number of elements to drop.
498
+ # @return [SortedArray] The array with the dropped values.
499
+ def drop(n)
500
+ raise ArgumentError, "attempt to drop negative size" if n.negative?
501
+ return self.class.new(load_factor: @load_factor) if n >= @size
502
+
503
+ self.class.new(to_a.drop(n), load_factor: @load_factor)
504
+ end
505
+
506
+ # rubocop:enable Naming/MethodParameterName
507
+
508
+ # Returns a new SortedArray containing all but the first elements for which the block returns true.
509
+ # Does not modify the +self+.
510
+ # If no block is given, an Enumerator is returned instead.
511
+ #
512
+ # @yield [value] The block to drop with.
513
+ # @return [SortedArray, Enumerator] The array with the dropped values.
514
+ def drop_while
515
+ return to_enum(:drop_while) unless block_given?
516
+
517
+ self.class.new(super(), load_factor: @load_factor)
518
+ end
519
+
520
+ # Iterates over each value in the sorted array.
521
+ #
522
+ # @yield [value] Gives each value to the block.
523
+ # @return [Enumerator] If no block is given, an Enumerator is returned.
524
+ def each(&block)
525
+ return to_enum(:each) unless block_given?
526
+
527
+ @lists.each do |sublist|
528
+ sublist.each(&block)
529
+ end
530
+ end
531
+
532
+ # Iterates over each index in the sorted array.
533
+ #
534
+ # @yield [index] Gives each index to the block.
535
+ # @return [Enumerator] If no block is given, an Enumerator is returned.
536
+ def each_index(&block)
537
+ return to_enum(:each_index) unless block_given?
538
+
539
+ 0.upto(@size - 1, &block)
540
+ end
541
+
542
+ # Checks if Array is empty
543
+ #
544
+ # @return [Boolean]
545
+ def empty?
546
+ @size.zero?
547
+ end
548
+
549
+ # Returns +true+ if for every element in +self+ there is a corresponding element in +other+
550
+ # that are equal using +Object#eql?+.
551
+ #
552
+ # This method is different from method +SortedArray#==+,
553
+ # which compares using method +Object#==+
554
+ #
555
+ # @param other [SortedArray] The other array to compare.
556
+ # @return [Boolean] True if the arrays are equal, false otherwise.
557
+ def eql?(other)
558
+ return false unless other.is_a?(SortedArray)
559
+
560
+ size == other.size && each_with_index.all? { |value, index| value.eql?(other[index]) }
561
+ end
562
+
563
+ # Returns the element at the specified index, or returns a default value if the index is out of range.
564
+ # Raises an IndexError if the index is out of range and no default is given.
565
+ # If a block is given, it is called with the index. The block will supplant the default value if both are given.
566
+ #
567
+ # @param index [Integer] The index of the value to retrieve.
568
+ # @param args [Object] The default value to return if the index is out of range.
569
+ def fetch(index, *args)
570
+ return self[index] if index.abs < @size
571
+
572
+ return yield(index) if block_given?
573
+
574
+ return args[0] if args.size.positive?
575
+
576
+ raise IndexError, "index #{index} outside of array bounds: #{-size}...#{size}"
577
+ end
578
+
579
+ # rubocop:disable Metrics/AbcSize
580
+ # rubocop:disable Metrics/CyclomaticComplexity
581
+ # rubocop:disable Metrics/MethodLength
582
+
583
+ # Fills the array with the given value.
584
+ #
585
+ # @overload fill(value)
586
+ # @param value [Object] The value to fill the array with.
587
+ # @overload fill(value, start)
588
+ # @param value [Object] The value to fill the array with.
589
+ # @param start [Integer] The index to start filling from.
590
+ # @overload fill(value, start, length)
591
+ # @param value [Object] The value to fill the array with.
592
+ # @param start [Integer] The index to start filling from.
593
+ # @param length [Integer] The length of the values to fill.
594
+ # @overload fill(value, range)
595
+ # @param value [Object] The value to fill the array with.
596
+ # @param range [Range] The range of values to fill.
597
+ # @overload fill
598
+ # @yield [index] The block to fill with.
599
+ # @overload fill(start)
600
+ # @param start [Integer] The index to start filling from.
601
+ # @yield [index] The block to fill with.
602
+ # @overload fill(start, length)
603
+ # @param start [Integer] The index to start filling from.
604
+ # @param length [Integer] The length of the values to fill.
605
+ # @yield [index] The block to fill with.
606
+ # @overload fill(range)
607
+ # @param range [Range] The range of values to fill.
608
+ # @yield [index] The block to fill with.
609
+ # @return [SortedArray] +self+. The filled array.
610
+ def fill(*args, &block)
611
+ unless block_given?
612
+ value = args.shift
613
+ block = proc { value }
614
+ end
615
+
616
+ case args.size
617
+ when 0
618
+ fill_range_unsafe(0..@size - 1, block)
619
+ when 1
620
+ if args[0].is_a?(Integer)
621
+ start = args[0]
622
+ start += @size if start.negative?
623
+ fill_range_unsafe(start..@size - 1, block)
624
+ elsif args[0].is_a?(Range)
625
+ fill_range_unsafe(args[0], block)
626
+ end
627
+ when 2
628
+ start, length = args
629
+ start += @size if start.negative?
630
+ fill_range_unsafe(start...(start + length), block)
631
+ end
632
+
633
+ sort! # resort will re-initialize the index and maxes
634
+ self
635
+ end
636
+
637
+ # rubocop:enable Metrics/AbcSize
638
+ # rubocop:enable Metrics/CyclomaticComplexity
639
+ # rubocop:enable Metrics/MethodLength
640
+
641
+ # Calls the block, if given, with each element of +self+;
642
+ # returns +self+ with the elements for which the block returns a truthy value.
643
+ #
644
+ # If no block is given, returns an Enumerator.
645
+ #
646
+ # @yield [value] The block to filter with.
647
+ # @return [SortedArray, Enumerator] The filtered array.
648
+ def select!
649
+ return to_enum(:select!) unless block_given?
650
+
651
+ indexes_to_delete = []
652
+ each_with_index do |value, index|
653
+ indexes_to_delete << index unless yield(value)
654
+ end
655
+ indexes_to_delete.reverse.each { |index| delete_at(index) }
656
+ self
657
+ end
658
+ alias filter! select!
659
+
660
+ # Returns the first index of the value in the sorted array, or returns
661
+ # the first index that returns true when passed to the block.
214
662
  #
215
- # @return [Object] The last value in the array.
216
- def last
663
+ # This method will binary search if value is given,
664
+ # if a block is given, it will iterate through the array.
665
+ #
666
+ # @overload find_index(value)
667
+ # @param value [Object] The value to find.
668
+ # @overload find_index
669
+ # @yield [value] The block to find with.
670
+ # @return [Integer] The index of the value.
671
+ def find_index(value = nil)
217
672
  return nil if @size.zero?
218
673
 
219
- @lists.last.last
674
+ if block_given?
675
+ each_with_index { |val, idx| return idx if yield(val) }
676
+ nil
677
+ else
678
+ bsearch_index { |val| val >= value }
679
+ end
220
680
  end
681
+ alias index find_index
221
682
 
222
683
  # Retrieves the first value in the sorted array.
223
684
  #
@@ -228,99 +689,173 @@ module SortedContainers
228
689
  @lists.first.first
229
690
  end
230
691
 
231
- # Deletes the value at the specified index.
692
+ # Returns a new SortedArray that is a recursive flattening of the array.
232
693
  #
233
- # @param index [Integer] The index of the value to delete.
234
- def delete_at(index)
235
- return nil if index.abs >= @size
694
+ # Each non-array element is unchanged, and each array element is recursively flattened.
695
+ # When the optional level argument is given, the recursion is limited to that level.
696
+ #
697
+ # @param level [Integer] The level to flatten to.
698
+ # @return [SortedArray] The flattened array.
699
+ def flatten(level = nil)
700
+ self.class.new(to_a.flatten(level), load_factor: @load_factor)
701
+ end
236
702
 
237
- pos, idx = pos(index)
238
- internal_delete(pos, idx)
703
+ # Flattens the array in place.
704
+ #
705
+ # Each non-array element is unchanged, and each array element is recursively flattened.
706
+ # When the optional level argument is given, the recursion is limited to that level.
707
+ #
708
+ # @param level [Integer] The level to flatten to.
709
+ # @return [SortedArray] +self+. The flattened array.
710
+ def flatten!(level = nil)
711
+ values = to_a.flatten(level)
712
+ clear
713
+ update(values)
714
+ self
239
715
  end
240
716
 
241
- # rubocop:disable Metrics/MethodLength
717
+ # Returns the integer hash value for the sorted array.
718
+ # Two arrays with the same content will have the same hash value.
719
+ #
720
+ # @return [Integer] The hash value.
721
+ def hash
722
+ @lists.hash
723
+ end
242
724
 
243
- # Pops the last value from the sorted array.
725
+ # Checks if the sorted array contains a value.
244
726
  #
245
- # @return [Object] The last value in the array.
246
- def pop
247
- return nil if @size.zero?
727
+ # @param value [Object] The value to check.
728
+ # @return [Boolean] True if the value is found, false otherwise.
729
+ def include?(value)
730
+ i = internal_bisect_left(@maxes, value)
731
+ return false if i == @maxes.size
248
732
 
249
- value = @lists.last.pop
250
- if @lists.last.empty?
251
- @lists.pop
252
- @maxes.pop
253
- @index.clear
254
- else
255
- @maxes[-1] = @lists.last.last
733
+ sublist = @lists[i]
734
+ idx = internal_bisect_left(sublist, value)
735
+ idx < sublist.size && sublist[idx] == value
736
+ end
737
+
738
+ # Returns a string representation of the sorted array.
739
+ #
740
+ # @return [String] A string representation of the sorted array.
741
+ def inspect
742
+ "#<#{self.class} size=#{@size} array_index=#{@array_index} " \
743
+ "offset=#{@offset} maxes=#{@maxes} items=#{@lists}>"
744
+ end
745
+
746
+ # Returns +true+ if the SortedArray and +other+ have at least one element in common, otherwise returns +false+
747
+ # Elements are compared using +eql?+
748
+ #
749
+ # @param other [SortedArray] The other array to compare.
750
+ # @return [Boolean] +true+ if the array and +other+ have at least one element in common, otherwise +false+
751
+ def intersect?(other)
752
+ each do |value|
753
+ return true if other.include_eql?(value)
256
754
  end
257
- @size -= 1
258
- value
755
+ false
259
756
  end
260
- # rubocop:enable Metrics/MethodLength
261
757
 
262
- # rubocop:disable Metrics/MethodLength
758
+ # Returns a +String+ formed by joining each element of the array with the given separator.
759
+ #
760
+ # @param separator [String] The separator to join the elements with.
761
+ # @return [String] The joined string.
762
+ def join(separator = $OUTPUT_FIELD_SEPARATOR)
763
+ to_a.join(separator)
764
+ end
263
765
 
264
- # Shifts the first value from the sorted array.
766
+ # Retains those elements for which the block returns a truthy value; deletes all other elements; returns +self+
265
767
  #
266
- # @return [Object] The first value in the array.
267
- def shift
268
- return nil if @size.zero?
768
+ # Returns an Enumerator if no block is given.
769
+ #
770
+ # @yield [value] The block to keep with.
771
+ # @return [SortedArray, Enumerator] The array with the kept values.
772
+ def keep_if
773
+ return to_enum(:keep_if) unless block_given?
269
774
 
270
- value = @lists.first.shift
271
- if @lists.first.empty?
272
- @lists.shift
273
- @maxes.shift
274
- @index.clear
275
- else
276
- @maxes[0] = @lists.first.first
775
+ (@size - 1).downto(0) do |index|
776
+ delete_at(index) unless yield(self[index])
277
777
  end
278
- @size -= 1
279
- value
778
+ self
280
779
  end
281
- # rubocop:enable Metrics/MethodLength
282
780
 
283
- # Returns the count of elements, based on an argument or block criterion, if given.
284
- # With no argument and no block given, returns the number of elements:
285
- # With argument object given, returns the number of elements that are == to object:
286
- # Uses binary search to find the first and last index of the value.
781
+ # Retrieves the last value in the sorted array.
287
782
  #
288
- # @param value [Object] The value to count.
289
- # @yield [value] The block to count with.
290
- # @return [Integer] The count of elements.
291
- def count(value = nil)
292
- return super() if block_given?
293
- return @size unless value
783
+ # @return [Object] The last value in the array.
784
+ def last
785
+ return nil if @size.zero?
294
786
 
295
- left_index = bisect_left(value)
296
- right_index = bisect_right(value)
297
- right_index - left_index
787
+ @lists.last.last
298
788
  end
299
789
 
300
- # Clears the sorted array, removing all values.
790
+ # Replaces the contents of +self+ with the contents of +other+.
791
+ #
792
+ # @param other [SortedArray] The other array to replace with.
793
+ # @return [SortedArray] +self+. The replaced array.
794
+ def replace(other)
795
+ @lists = other.lists.map(&:dup)
796
+ @maxes = other.maxes.dup
797
+ @array_index = other.array_index.dup
798
+ @offset = other.offset
799
+ @load_factor = other.load_factor
800
+ @size = other.size
801
+ self
802
+ end
803
+
804
+ # Returns a string representation of the sorted array.
301
805
  #
302
- # @return [void]
303
- def clear
304
- @lists.clear
305
- @maxes.clear
306
- @index.clear
307
- @offset = 0
308
- @size = 0
806
+ # @return [String] A string representation of the sorted array.
807
+ def to_s
808
+ "SortedArray(#{to_a})"
309
809
  end
310
810
 
311
- # Checks if the sorted array contains a value.
811
+ # Returns an index to insert +value+ in the sorted list.
312
812
  #
313
- # @param value [Object] The value to check.
314
- # @return [Boolean] True if the value is found, false otherwise.
315
- def include?(value)
316
- i = internal_bisect_left(@maxes, value)
317
- return false if i == @maxes.size
813
+ # If the +value+ is already present, the insertion point will be before
814
+ # (to the left of) any existing values.
815
+ #
816
+ # Runtime complexity: +O(log(n))+ -- approximate.
817
+ #
818
+ # @param value [Object] The value to insert.
819
+ # @return [Integer] The index to insert the value.
820
+ def bisect_left(value)
821
+ return 0 if @maxes.empty?
318
822
 
319
- sublist = @lists[i]
320
- idx = internal_bisect_left(sublist, value)
321
- idx < sublist.size && sublist[idx] == value
823
+ pos = internal_bisect_left(@maxes, value)
824
+
825
+ return @size if pos == @maxes.size
826
+
827
+ idx = internal_bisect_left(@lists[pos], value)
828
+ loc(pos, idx)
829
+ end
830
+
831
+ # Returns an index to insert +value+ in the sorted list.
832
+ #
833
+ # If the +value+ is already present, the insertion point will be after
834
+ # (to the right of) any existing values.
835
+ #
836
+ # Runtime complexity: +O(log(n))+ -- approximate.
837
+ #
838
+ # @param value [Object] The value to insert.
839
+ # @return [Integer] The index to insert the value.
840
+ def bisect_right(value)
841
+ return 0 if @maxes.empty?
842
+
843
+ pos = internal_bisect_right(@maxes, value)
844
+
845
+ return @size if pos == @maxes.size
846
+
847
+ idx = internal_bisect_right(@lists[pos], value)
848
+ loc(pos, idx)
322
849
  end
323
850
 
851
+ # Shifts the first value from the sorted array.
852
+ #
853
+ # @return [Object] The first value in the array.
854
+ def shift
855
+ return nil if @size.zero?
856
+
857
+ delete_at(0)
858
+ end
324
859
  # rubocop:disable Metrics/MethodLength
325
860
  # rubocop:disable Metrics/AbcSize
326
861
 
@@ -355,7 +890,7 @@ module SortedContainers
355
890
  @size = values.length
356
891
 
357
892
  # Clear the index as it might be outdated
358
- @index.clear
893
+ @array_index.clear
359
894
  end
360
895
  # rubocop:enable Metrics/MethodLength
361
896
  # rubocop:enable Metrics/AbcSize
@@ -364,22 +899,27 @@ module SortedContainers
364
899
  #
365
900
  # @return [Array] An array representation of the sorted array.
366
901
  def to_a
367
- @lists.flatten
902
+ @lists.flatten(1)
368
903
  end
904
+ alias to_ary to_a
369
905
 
370
- # Array is already sorted. Duplicates the sorted array and returns it.
906
+ # Creates a new SortedArray and resorts the values.
907
+ # Usefull when the values are modified and the array needs to be resorted.
371
908
  #
372
909
  # @return [SortedArray] The sorted array.
373
910
  def sort
374
- # No need to sort, already sorted
375
- dup
911
+ values = to_a
912
+ self.class.new(values, load_factor: @load_factor)
376
913
  end
377
914
 
378
- # Returns self, as the array is already sorted.
915
+ # Resorts the values in the array.
916
+ # Usefull when the values are modified and the array needs to be resorted.
379
917
  #
380
918
  # @return [SortedArray] The sorted array.
381
919
  def sort!
382
- # No need to sort, already sorted
920
+ values = to_a
921
+ clear
922
+ update(values)
383
923
  self
384
924
  end
385
925
 
@@ -391,25 +931,13 @@ module SortedContainers
391
931
  new_instance = self.class.new
392
932
  new_instance.lists = @lists.map(&:dup)
393
933
  new_instance.maxes = @maxes.dup
394
- new_instance.index = @index.dup
934
+ new_instance.array_index = @array_index.dup
395
935
  new_instance.offset = @offset
396
936
  new_instance.load_factor = @load_factor
397
937
  new_instance.size = @size
398
938
  new_instance
399
939
  end
400
940
 
401
- # When non-negative, multiplies returns a new Array with each value repeated `int` times.
402
- #
403
- # @param num [Integer] The integer to multiply the array by.
404
- # @return [SortedArray] The multiplied sorted array.
405
- def multiply(num)
406
- values = @lists.flatten * num
407
- new_instance = self.class.new
408
- new_instance.update(values)
409
- new_instance
410
- end
411
- alias * multiply
412
-
413
941
  # Returns the maximum value in the sorted array.
414
942
  #
415
943
  # @return [Object] The maximum value in the array.
@@ -424,15 +952,238 @@ module SortedContainers
424
952
  @lists.first&.first
425
953
  end
426
954
 
427
- # Iterates over each value in the sorted array.
955
+ # Returns a 2-element array containing the minimum and maximum values in the sorted array.
956
+ # If a block is given, the result of the block is computed for each value in the array,
957
+ # and the minimum and maximum values are computed from the result.
958
+ #
959
+ # @yield [value] The block to compute with.
960
+ # @return [Array] A 2-element array containing the minimum and maximum values.
961
+ def minmax
962
+ if block_given?
963
+ each.reduce([nil, nil]) do |(min_value, max_value), value|
964
+ min_value = value if min_value.nil? || yield(value, min_value).negative?
965
+ max_value = value if max_value.nil? || yield(value, max_value).positive?
966
+ [min_value, max_value]
967
+ end
968
+ else
969
+ [min, max]
970
+ end
971
+ end
972
+
973
+ # Packs the values in the array into a binary sequence.
974
+ # @see Array#pack
975
+ #
976
+ # @param template [String] The template to pack the values with.
977
+ # @param buffer [String] The buffer to pack the values into.
978
+ # @return [String] The packed values.
979
+ def pack(template, buffer: nil)
980
+ to_a.pack(template, buffer: buffer)
981
+ end
982
+
983
+ # rubocop:disable Naming/MethodParameterName
984
+ # rubocop:disable Metrics/MethodLength
985
+
986
+ # Pops the last value from the sorted array.
987
+ #
988
+ # @param n [Integer] The number of values to pop.
989
+ # @return [Object] The last value in the array.
990
+ def pop(n = nil)
991
+ return nil if @size.zero?
992
+
993
+ if n.nil?
994
+ index = @size - 1
995
+ delete_at(index)
996
+ else
997
+ values = []
998
+ n.times do
999
+ return values if @size.zero?
1000
+
1001
+ index = @size - 1
1002
+ values.prepend(delete_at(index))
1003
+ end
1004
+ values
1005
+ end
1006
+ end
1007
+
1008
+ # rubocop:enable Naming/MethodParameterName
1009
+ # rubocop:enable Metrics/MethodLength
1010
+
1011
+ # Computes and returns or yields all combinations of elements from all the Arrays,
1012
+ # including both +self+ and +other_arrays+.
1013
+ #
1014
+ # @param other_arrays [SortedArray] The arrays to combine with.
1015
+ # @yield [value] The block to combine with.
1016
+ # @return [SortedArray] The combined array.
1017
+ def product(*other_arrays, &block)
1018
+ arrays = other_arrays.map(&:to_a)
1019
+ self.class.new(
1020
+ to_a.product(*arrays, &block),
1021
+ load_factor: @load_factor
1022
+ )
1023
+ end
1024
+
1025
+ # Returns the first element in +self+ that is an +Array+ whose second element +==+ +obj+:
1026
+ #
1027
+ # Time complexity: O(n)
1028
+ #
1029
+ # @param obj [Object] The object to search for.
1030
+ # @return [Array] The first element in +self+ that is an +Array+ whose second element +==+ +obj+.
1031
+ def rassoc(obj)
1032
+ index = find_index { |x| x.is_a?(Array) && x[1] >= obj }
1033
+ index.nil? ? nil : self[index]
1034
+ end
1035
+
1036
+ # Deletes every element of +self+ for which block evaluates to true.
1037
+ #
1038
+ # Returns +self+ if any changes were made, otherwise returns +nil+.
1039
+ #
1040
+ # Returns an Enumerator if no block is given.
1041
+ #
1042
+ # @yield [value] The block to reject with.
1043
+ # @return [SortedArray, Enumerator] The rejected array.
1044
+ def reject!
1045
+ return to_enum(:reject!) unless block_given?
1046
+
1047
+ indexes_to_delete = []
1048
+ each_with_index do |value, index|
1049
+ indexes_to_delete << index if yield(value)
1050
+ end
1051
+ return nil if indexes_to_delete.empty?
1052
+
1053
+ indexes_to_delete.reverse.each { |index| delete_at(index) }
1054
+ self
1055
+ end
1056
+
1057
+ # Iterates over the sorted array in reverse order.
428
1058
  #
429
1059
  # @yield [value] Gives each value to the block.
430
- def each(&block)
431
- @lists.each do |sublist|
432
- sublist.each(&block)
1060
+ # @return [Enumerator] If no block is given, an Enumerator is returned.
1061
+ def reverse_each(&block)
1062
+ return to_enum(:reverse_each) unless block_given?
1063
+
1064
+ @lists.reverse_each do |sublist|
1065
+ sublist.reverse_each(&block)
433
1066
  end
434
1067
  end
435
1068
 
1069
+ # rubocop:disable Metrics/AbcSize
1070
+ # rubocop:disable Metrics/CyclomaticComplexity
1071
+ # rubocop:disable Metrics/MethodLength
1072
+ # rubocop:disable Metrics/PerceivedComplexity
1073
+
1074
+ # Returns the index of the last element for which object +==+ element.
1075
+ #
1076
+ # Returns an Enumerator if no block or value is given.
1077
+ #
1078
+ # When a block is given but no value, the block is used to find the last element.
1079
+ #
1080
+ # @overload rindex(value)
1081
+ # @param value [Object] The value to find.
1082
+ # @overload rindex
1083
+ # @yield [value] The block to find with.
1084
+ # @return [Integer] The index of the value.
1085
+ def rindex(value = nil)
1086
+ return to_enum(:rindex, value) unless block_given? || value
1087
+ return nil if @size.zero?
1088
+
1089
+ if value.nil?
1090
+ reverse_each.with_index do |val, idx|
1091
+ return @size - idx - 1 if yield(val)
1092
+ end
1093
+ nil
1094
+ else
1095
+ warn "given block not used" if block_given?
1096
+ index = bisect_right(value)
1097
+ self[index - 1] == value ? index - 1 : nil
1098
+ end
1099
+ end
1100
+
1101
+ # rubocop:enable Metrics/AbcSize
1102
+ # rubocop:enable Metrics/CyclomaticComplexity
1103
+ # rubocop:enable Metrics/MethodLength
1104
+ # rubocop:enable Metrics/PerceivedComplexity
1105
+
1106
+ # rubocop:disable Naming/MethodParameterName
1107
+
1108
+ # Returns random elements from +self+.
1109
+ #
1110
+ # If +n+ is given, returns an array of +n+ random elements.
1111
+ # If +n+ is not given, returns a single random element.
1112
+ #
1113
+ # @param n [Integer] The number of random elements to return.
1114
+ # @param random [Random] The random number generator to use.
1115
+ # @return [Object, Array] The random element(s).
1116
+ def sample(n = nil, random: Random)
1117
+ return nil if @size.zero?
1118
+
1119
+ if n.nil?
1120
+ index = random.rand(@size)
1121
+ self[index]
1122
+ else
1123
+ raise ArgumentError, "negative sample number" if n.negative?
1124
+
1125
+ n.times.map { self[random.rand(@size)] }
1126
+ end
1127
+ end
1128
+
1129
+ # rubocop:enable Naming/MethodParameterName
1130
+
1131
+ # Returns a new SortedArray that is the union of +self+ and +other_arrays+.
1132
+ # Duplicates are removed.
1133
+ #
1134
+ # @param other_arrays [Array] The arrays to union with.
1135
+ # @return [SortedArray] The unioned array.
1136
+ def union(*other_arrays)
1137
+ self.class.new(to_a | other_arrays.flatten, load_factor: @load_factor)
1138
+ end
1139
+ alias | union
1140
+
1141
+ # Returns a new SortedArray containing the unique values in +self+.
1142
+ #
1143
+ # @return [SortedArray] The unique array.
1144
+ def uniq(&block)
1145
+ self.class.new(to_a.uniq(&block), load_factor: @load_factor)
1146
+ end
1147
+
1148
+ # Removes duplicate values from the sorted array.
1149
+ # Returns +self+ if any changes were made, otherwise returns +nil+.
1150
+ # If a block is given, it will use the block to determine uniqueness.
1151
+ #
1152
+ # @yield [value] The block to determine uniqueness.
1153
+ # @return [SortedArray, nil] The array with the unique values.
1154
+ def uniq!(&block)
1155
+ values = to_a.uniq(&block)
1156
+
1157
+ return nil if values.size == @size
1158
+
1159
+ clear
1160
+ update(values)
1161
+ self
1162
+ end
1163
+
1164
+ # Returns a new SortedArray containing the values from the given indexes and ranges.
1165
+ #
1166
+ # @param indexes [Integer, Range] The indexes and ranges to get values from.
1167
+ # @return [Array] The values from the given indexes and ranges.
1168
+ def values_at(*indexes)
1169
+ indexes.map do |index|
1170
+ if index.is_a?(Range)
1171
+ get_values_from_range(index)
1172
+ else
1173
+ get_value_at_index(index)
1174
+ end
1175
+ end.flatten
1176
+ end
1177
+
1178
+ # Combines each element of the sorted array with the corresponding elements from other arrays.
1179
+ #
1180
+ # @param other_arrays [Array] The other arrays to zip with the sorted array.
1181
+ # @param block [Proc] An optional block to apply to each zipped element.
1182
+ # @return [SortedArray] A new SortedArray containing the zipped elements.
1183
+ def zip(*other_arrays, &block)
1184
+ self.class.new(to_a.zip(*other_arrays, &block), load_factor: @load_factor)
1185
+ end
1186
+
436
1187
  private
437
1188
 
438
1189
  # Performs a left bisect on the array.
@@ -441,7 +1192,7 @@ module SortedContainers
441
1192
  # @param value [Object] The value to bisect with.
442
1193
  # @return [Integer] The index where the value should be inserted.
443
1194
  def internal_bisect_left(array, value)
444
- array.bsearch_index { |x| x >= value } || array.size
1195
+ array.bsearch_index { |x| (x <=> value) >= 0 } || array.size
445
1196
  end
446
1197
 
447
1198
  # Performs a right bisect on the array.
@@ -450,7 +1201,26 @@ module SortedContainers
450
1201
  # @param value [Object] The value to bisect with.
451
1202
  # @return [Integer] The index where the value should be inserted.
452
1203
  def internal_bisect_right(array, value)
453
- array.bsearch_index { |x| x > value } || array.length
1204
+ array.bsearch_index { |x| (x <=> value) == 1 } || array.length
1205
+ end
1206
+
1207
+ # Updates the index within the range with the block.
1208
+ # Does not update index or maxes. Distrupts the sorted order.
1209
+ #
1210
+ # @param range [Range] The range to update.
1211
+ # @param block [Proc] The block that takes the index and returns the value.
1212
+ def fill_range_unsafe(range, block)
1213
+ range.each { |index| internal_change_unsafe(index, block.call(index)) }
1214
+ end
1215
+
1216
+ # Updates the element at index with the value.
1217
+ # Does not update index or maxes. Distrupts the sorted order.
1218
+ #
1219
+ # @param index [Integer] The index of the value to update.
1220
+ # @param value [Object] The value to update.
1221
+ def internal_change_unsafe(index, value)
1222
+ pos, idx = pos(index)
1223
+ @lists[pos][idx] = value
454
1224
  end
455
1225
 
456
1226
  # Gets the value at a given index. Supports negative indices.
@@ -571,14 +1341,14 @@ module SortedContainers
571
1341
  @maxes[pos] = @lists[pos].last
572
1342
  @lists.insert(pos + 1, half)
573
1343
  @maxes.insert(pos + 1, half.last)
574
- @index.clear
575
- elsif @index.size.positive?
1344
+ @array_index.clear
1345
+ elsif @array_index.size.positive?
576
1346
  child = @offset + pos
577
1347
  while child.positive?
578
- @index[child] += 1
1348
+ @array_index[child] += 1
579
1349
  child = (child - 1) >> 1
580
1350
  end
581
- @index[0] += 1
1351
+ @array_index[0] += 1
582
1352
  end
583
1353
  end
584
1354
  # rubocop:enable Metrics/AbcSize
@@ -602,13 +1372,13 @@ module SortedContainers
602
1372
  if len_list > (@load_factor >> 1)
603
1373
  @maxes[pos] = list.last
604
1374
 
605
- if @index.size.positive?
1375
+ if @array_index.size.positive?
606
1376
  child = @offset + pos
607
1377
  while child.positive?
608
- @index[child] -= 1
1378
+ @array_index[child] -= 1
609
1379
  child = (child - 1) >> 1
610
1380
  end
611
- @index[0] -= 1
1381
+ @array_index[0] -= 1
612
1382
  end
613
1383
  elsif @lists.length > 1
614
1384
  pos += 1 if pos.zero?
@@ -619,7 +1389,7 @@ module SortedContainers
619
1389
 
620
1390
  @lists.delete_at(pos)
621
1391
  @maxes.delete_at(pos)
622
- @index.clear
1392
+ @array_index.clear
623
1393
 
624
1394
  expand(prev)
625
1395
  elsif len_list.positive?
@@ -627,7 +1397,7 @@ module SortedContainers
627
1397
  else
628
1398
  @lists.delete_at(pos)
629
1399
  @maxes.delete_at(pos)
630
- @index.clear
1400
+ @array_index.clear
631
1401
  end
632
1402
  value
633
1403
  end
@@ -663,7 +1433,7 @@ module SortedContainers
663
1433
  #
664
1434
  # Finally, the index is built by concatenating these lists together:
665
1435
  #
666
- # @index = [14, 5, 9, 3, 2, 4, 5]
1436
+ # @array_index = [14, 5, 9, 3, 2, 4, 5]
667
1437
  #
668
1438
  # An offset storing the start of the first row is also stored:
669
1439
  #
@@ -677,7 +1447,7 @@ module SortedContainers
677
1447
 
678
1448
  # Early return if there is only one sublist
679
1449
  if row0.length == 1
680
- @index = row0
1450
+ @array_index = row0
681
1451
  @offset = 0
682
1452
  return
683
1453
  end
@@ -691,7 +1461,7 @@ module SortedContainers
691
1461
 
692
1462
  # Return early if only one row is needed
693
1463
  if row1.length == 1
694
- @index = row1 + row0
1464
+ @array_index = row1 + row0
695
1465
  @offset = 1
696
1466
  return
697
1467
  end
@@ -708,7 +1478,7 @@ module SortedContainers
708
1478
  end
709
1479
 
710
1480
  # Flatten the tree into the index array
711
- tree.reverse_each { |level| @index.concat(level) }
1481
+ tree.reverse_each { |level| @array_index.concat(level) }
712
1482
  @offset = (the_size * 2) - 1
713
1483
  end
714
1484
  # rubocop:enable Metrics/AbcSize
@@ -788,14 +1558,14 @@ module SortedContainers
788
1558
 
789
1559
  return 0, idx if idx < @lists[0].size
790
1560
 
791
- build_index if @index.empty?
1561
+ build_index if @array_index.empty?
792
1562
 
793
1563
  pos = 0
794
1564
  child = 1
795
- len_index = @index.size
1565
+ len_index = @array_index.size
796
1566
 
797
1567
  while child < len_index
798
- index_child = @index[child]
1568
+ index_child = @array_index[child]
799
1569
 
800
1570
  if idx < index_child
801
1571
  pos = child
@@ -823,7 +1593,7 @@ module SortedContainers
823
1593
  def loc(pos, idx)
824
1594
  return idx if pos.zero?
825
1595
 
826
- build_index if @index.empty?
1596
+ build_index if @array_index.empty?
827
1597
 
828
1598
  # Increment pos to point in the index to @lists[pos].size.
829
1599
  total = 0
@@ -835,7 +1605,7 @@ module SortedContainers
835
1605
 
836
1606
  # Right-child nodes are at even indices. At such indices
837
1607
  # account the total below the left child node.
838
- total += @index[pos - 1] if pos.even?
1608
+ total += @array_index[pos - 1] if pos.even?
839
1609
 
840
1610
  # Advance pos to the parent node.
841
1611
  pos = (pos - 1) >> 1
@@ -846,7 +1616,16 @@ module SortedContainers
846
1616
 
847
1617
  protected
848
1618
 
849
- attr_accessor :lists, :maxes, :index, :offset, :load_factor
1619
+ # Checks if the sorted array includes a value using eql?.
1620
+ #
1621
+ # @param value [Object] The value to check.
1622
+ # @return [Boolean] True if the value is found, false otherwise.
1623
+ def include_eql?(value)
1624
+ index = find_index(value)
1625
+ index ? self[index].eql?(value) : false
1626
+ end
1627
+
1628
+ attr_accessor :lists, :maxes, :array_index, :offset
850
1629
 
851
1630
  attr_writer :size
852
1631
  end