sorted_containers 0.1.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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,486 @@ 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) || 0
359
+
360
+ idx = @lists[pos].bsearch_index(&block)
361
+ loc(pos, idx)
362
+ end
363
+
364
+ # Clears the sorted array, removing all values.
365
+ #
366
+ # @return [SortedArray] The cleared sorted array.
367
+ def clear
368
+ @lists.map(&:clear)
369
+ @lists.clear
370
+ @maxes.clear
371
+ @array_index.clear
372
+ @offset = 0
373
+ @size = 0
374
+ self
375
+ end
376
+
377
+ # Calls the block, if given, with each element of +self+;
378
+ # returns +self+ after the block has been executed.
379
+ #
380
+ # If no block is given, returns an Enumerator.
381
+ #
382
+ # @yield [value] The block to map with.
383
+ # @return [SortedArray, Enumerator] The mapped array.
384
+ def map!
385
+ return to_enum(:map!) unless block_given?
386
+
387
+ new_values = []
388
+ # rubocop:disable Style/MapIntoArray
389
+ each { |value| new_values << yield(value) }
390
+ # rubocop:enable Style/MapIntoArray
391
+ clear
392
+ # Experimitation shows that it's faster to add all values at once
393
+ # rather than adding them one by one
394
+ update(new_values)
395
+ self
396
+ end
397
+ alias collect! map!
398
+
399
+ # Adds the elements of one or more arrays to the SortedArray.
400
+ #
401
+ # @param other_arrays [Array] The arrays to concatenate.
402
+ # @return [SortedArray] +self+. The SortedArray with the concatenated values.
403
+ def concat(*other_arrays)
404
+ other_arrays.each do |array|
405
+ update(array)
406
+ end
407
+ self
408
+ end
409
+
410
+ # Returns the count of elements, based on an argument or block criterion, if given.
411
+ # With no argument and no block given, returns the number of elements:
412
+ # With argument object given, returns the number of elements that are == to object:
413
+ # Uses binary search to find the first and last index of the value.
414
+ #
415
+ # @param value [Object] The value to count.
416
+ # @yield [value] The block to count with.
417
+ # @return [Integer] The count of elements.
418
+ def count(value = nil)
419
+ # If block is given, we call super to use the Enumerable#count method
420
+ return super() if block_given?
421
+ return @size unless value
422
+
423
+ left_index = bisect_left(value)
424
+ right_index = bisect_right(value)
425
+ right_index - left_index
426
+ end
427
+
428
+ # Deletes a value from the sorted array.
429
+ #
430
+ # @param value [Object] The value to delete.
431
+ def delete(value)
432
+ return if @maxes.empty?
433
+
434
+ pos = internal_bisect_left(@maxes, value)
435
+
436
+ return if pos == @maxes.size
437
+
438
+ idx = internal_bisect_left(@lists[pos], value)
439
+
440
+ internal_delete(pos, idx) if @lists[pos][idx] == value
441
+ end
442
+
443
+ # Deletes the value at the specified index.
444
+ #
445
+ # @param index [Integer] The index of the value to delete.
446
+ def delete_at(index)
447
+ return nil if index.abs >= @size
448
+
449
+ pos, idx = pos(index)
450
+ internal_delete(pos, idx)
451
+ end
452
+
453
+ # Removes each element from the array for which block evaluates to true.
454
+ #
455
+ # @yield [value] The block to delete with.
456
+ # @return [SortedArray] +self+. The array with the deleted values.
457
+ def delete_if
458
+ return to_enum(:delete_if) unless block_given?
459
+
460
+ to_delete = []
461
+ each do |value|
462
+ to_delete << value if yield(value)
463
+ end
464
+ to_delete.each { |value| delete(value) }
465
+ self
466
+ end
467
+
468
+ # Finds and returns the object in nested objects that is specified by +index+ and +identifiers+.
469
+ # The nested objects may be instances of various classes. See Dig methods.
470
+ #
471
+ # @param index [Integer] The index of the value to retrieve.
472
+ # @param identifiers [Array] The identifiers to retrieve the value.
473
+ # @return [Object] The value at the specified index.
474
+ def dig(index, *identifiers)
475
+ result = self[index]
476
+ return nil if result.nil?
477
+
478
+ identifiers.each do |identifier|
479
+ raise TypeError, "#{result.class} does not have #dig method" unless result.respond_to?(:dig)
480
+
481
+ # rubocop:disable Style/SingleArgumentDig
482
+ result = result.dig(identifier)
483
+ # rubocop:enable Style/SingleArgumentDig
484
+ return nil if result.nil?
485
+ end
486
+
487
+ result
488
+ end
489
+
490
+ # rubocop:disable Naming/MethodParameterName
491
+
492
+ # Returns a new SortedArray containing all but the first +n+ elements, where +n+ is a non-negative integer.
493
+ # Does not modify the +self+.
494
+ #
495
+ # @param n [Integer] The number of elements to drop.
496
+ # @return [SortedArray] The array with the dropped values.
497
+ def drop(n)
498
+ raise ArgumentError, "attempt to drop negative size" if n.negative?
499
+ return self.class.new(load_factor: @load_factor) if n >= @size
500
+
501
+ self.class.new(to_a.drop(n), load_factor: @load_factor)
502
+ end
503
+
504
+ # rubocop:enable Naming/MethodParameterName
505
+
506
+ # Returns a new SortedArray containing all but the first elements for which the block returns true.
507
+ # Does not modify the +self+.
508
+ # If no block is given, an Enumerator is returned instead.
509
+ #
510
+ # @yield [value] The block to drop with.
511
+ # @return [SortedArray, Enumerator] The array with the dropped values.
512
+ def drop_while
513
+ return to_enum(:drop_while) unless block_given?
514
+
515
+ self.class.new(super(), load_factor: @load_factor)
516
+ end
517
+
518
+ # Iterates over each value in the sorted array.
519
+ #
520
+ # @yield [value] Gives each value to the block.
521
+ # @return [Enumerator] If no block is given, an Enumerator is returned.
522
+ def each(&block)
523
+ return to_enum(:each) unless block_given?
524
+
525
+ @lists.each do |sublist|
526
+ sublist.each(&block)
527
+ end
528
+ end
529
+
530
+ # Iterates over each index in the sorted array.
531
+ #
532
+ # @yield [index] Gives each index to the block.
533
+ # @return [Enumerator] If no block is given, an Enumerator is returned.
534
+ def each_index(&block)
535
+ return to_enum(:each_index) unless block_given?
536
+
537
+ 0.upto(@size - 1, &block)
538
+ end
539
+
540
+ # Checks if Array is empty
541
+ #
542
+ # @return [Boolean]
543
+ def empty?
544
+ @size.zero?
545
+ end
546
+
547
+ # Returns +true+ if for every element in +self+ there is a corresponding element in +other+
548
+ # that are equal using +Object#eql?+.
549
+ #
550
+ # This method is different from method +SortedArray#==+,
551
+ # which compares using method +Object#==+
552
+ #
553
+ # @param other [SortedArray] The other array to compare.
554
+ # @return [Boolean] True if the arrays are equal, false otherwise.
555
+ def eql?(other)
556
+ return false unless other.is_a?(SortedArray)
557
+
558
+ size == other.size && each_with_index.all? { |value, index| value.eql?(other[index]) }
559
+ end
560
+
561
+ # Returns the element at the specified index, or returns a default value if the index is out of range.
562
+ # Raises an IndexError if the index is out of range and no default is given.
563
+ # If a block is given, it is called with the index. The block will supplant the default value if both are given.
564
+ #
565
+ # @param index [Integer] The index of the value to retrieve.
566
+ # @param args [Object] The default value to return if the index is out of range.
567
+ def fetch(index, *args)
568
+ return self[index] if index.abs < @size
569
+
570
+ return yield(index) if block_given?
571
+
572
+ return args[0] if args.size.positive?
573
+
574
+ raise IndexError, "index #{index} outside of array bounds: #{-size}...#{size}"
575
+ end
576
+
577
+ # rubocop:disable Metrics/AbcSize
578
+ # rubocop:disable Metrics/CyclomaticComplexity
579
+ # rubocop:disable Metrics/MethodLength
580
+
581
+ # Fills the array with the given value.
582
+ #
583
+ # @overload fill(value)
584
+ # @param value [Object] The value to fill the array with.
585
+ # @overload fill(value, start)
586
+ # @param value [Object] The value to fill the array with.
587
+ # @param start [Integer] The index to start filling from.
588
+ # @overload fill(value, start, length)
589
+ # @param value [Object] The value to fill the array with.
590
+ # @param start [Integer] The index to start filling from.
591
+ # @param length [Integer] The length of the values to fill.
592
+ # @overload fill(value, range)
593
+ # @param value [Object] The value to fill the array with.
594
+ # @param range [Range] The range of values to fill.
595
+ # @overload fill
596
+ # @yield [index] The block to fill with.
597
+ # @overload fill(start)
598
+ # @param start [Integer] The index to start filling from.
599
+ # @yield [index] The block to fill with.
600
+ # @overload fill(start, length)
601
+ # @param start [Integer] The index to start filling from.
602
+ # @param length [Integer] The length of the values to fill.
603
+ # @yield [index] The block to fill with.
604
+ # @overload fill(range)
605
+ # @param range [Range] The range of values to fill.
606
+ # @yield [index] The block to fill with.
607
+ # @return [SortedArray] +self+. The filled array.
608
+ def fill(*args, &block)
609
+ unless block_given?
610
+ value = args.shift
611
+ block = proc { value }
612
+ end
613
+
614
+ case args.size
615
+ when 0
616
+ fill_range_unsafe(0..@size - 1, block)
617
+ when 1
618
+ if args[0].is_a?(Integer)
619
+ start = args[0]
620
+ start += @size if start.negative?
621
+ fill_range_unsafe(start..@size - 1, block)
622
+ elsif args[0].is_a?(Range)
623
+ fill_range_unsafe(args[0], block)
624
+ end
625
+ when 2
626
+ start, length = args
627
+ start += @size if start.negative?
628
+ fill_range_unsafe(start...(start + length), block)
629
+ end
630
+
631
+ sort! # resort will re-initialize the index and maxes
632
+ self
633
+ end
634
+
635
+ # rubocop:enable Metrics/AbcSize
636
+ # rubocop:enable Metrics/CyclomaticComplexity
637
+ # rubocop:enable Metrics/MethodLength
638
+
639
+ # Calls the block, if given, with each element of +self+;
640
+ # returns +self+ with the elements for which the block returns a truthy value.
641
+ #
642
+ # If no block is given, returns an Enumerator.
643
+ #
644
+ # @yield [value] The block to filter with.
645
+ # @return [SortedArray, Enumerator] The filtered array.
646
+ def select!
647
+ return to_enum(:select!) unless block_given?
648
+
649
+ indexes_to_delete = []
650
+ each_with_index do |value, index|
651
+ indexes_to_delete << index unless yield(value)
652
+ end
653
+ indexes_to_delete.reverse.each { |index| delete_at(index) }
654
+ self
655
+ end
656
+ alias filter! select!
657
+
658
+ # Returns the first index of the value in the sorted array, or returns
659
+ # the first index that returns true when passed to the block.
214
660
  #
215
- # @return [Object] The last value in the array.
216
- def last
661
+ # This method will binary search if value is given,
662
+ # if a block is given, it will iterate through the array.
663
+ #
664
+ # @overload find_index(value)
665
+ # @param value [Object] The value to find.
666
+ # @overload find_index
667
+ # @yield [value] The block to find with.
668
+ # @return [Integer] The index of the value.
669
+ def find_index(value = nil)
217
670
  return nil if @size.zero?
218
671
 
219
- @lists.last.last
672
+ if block_given?
673
+ each_with_index { |val, idx| return idx if yield(val) }
674
+ nil
675
+ else
676
+ bsearch_index { |val| val >= value }
677
+ end
220
678
  end
679
+ alias index find_index
221
680
 
222
681
  # Retrieves the first value in the sorted array.
223
682
  #
@@ -228,99 +687,173 @@ module SortedContainers
228
687
  @lists.first.first
229
688
  end
230
689
 
231
- # Deletes the value at the specified index.
690
+ # Returns a new SortedArray that is a recursive flattening of the array.
232
691
  #
233
- # @param index [Integer] The index of the value to delete.
234
- def delete_at(index)
235
- return nil if index.abs >= @size
692
+ # Each non-array element is unchanged, and each array element is recursively flattened.
693
+ # When the optional level argument is given, the recursion is limited to that level.
694
+ #
695
+ # @param level [Integer] The level to flatten to.
696
+ # @return [SortedArray] The flattened array.
697
+ def flatten(level = nil)
698
+ self.class.new(to_a.flatten(level), load_factor: @load_factor)
699
+ end
236
700
 
237
- pos, idx = pos(index)
238
- internal_delete(pos, idx)
701
+ # Flattens the array in place.
702
+ #
703
+ # Each non-array element is unchanged, and each array element is recursively flattened.
704
+ # When the optional level argument is given, the recursion is limited to that level.
705
+ #
706
+ # @param level [Integer] The level to flatten to.
707
+ # @return [SortedArray] +self+. The flattened array.
708
+ def flatten!(level = nil)
709
+ values = to_a.flatten(level)
710
+ clear
711
+ update(values)
712
+ self
239
713
  end
240
714
 
241
- # rubocop:disable Metrics/MethodLength
715
+ # Returns the integer hash value for the sorted array.
716
+ # Two arrays with the same content will have the same hash value.
717
+ #
718
+ # @return [Integer] The hash value.
719
+ def hash
720
+ @lists.hash
721
+ end
242
722
 
243
- # Pops the last value from the sorted array.
723
+ # Checks if the sorted array contains a value.
244
724
  #
245
- # @return [Object] The last value in the array.
246
- def pop
247
- return nil if @size.zero?
725
+ # @param value [Object] The value to check.
726
+ # @return [Boolean] True if the value is found, false otherwise.
727
+ def include?(value)
728
+ i = internal_bisect_left(@maxes, value)
729
+ return false if i == @maxes.size
248
730
 
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
731
+ sublist = @lists[i]
732
+ idx = internal_bisect_left(sublist, value)
733
+ idx < sublist.size && sublist[idx] == value
734
+ end
735
+
736
+ # Returns a string representation of the sorted array.
737
+ #
738
+ # @return [String] A string representation of the sorted array.
739
+ def inspect
740
+ "#<#{self.class} size=#{@size} array_index=#{@array_index} " \
741
+ "offset=#{@offset} maxes=#{@maxes} items=#{@lists}>"
742
+ end
743
+
744
+ # Returns +true+ if the SortedArray and +other+ have at least one element in common, otherwise returns +false+
745
+ # Elements are compared using +eql?+
746
+ #
747
+ # @param other [SortedArray] The other array to compare.
748
+ # @return [Boolean] +true+ if the array and +other+ have at least one element in common, otherwise +false+
749
+ def intersect?(other)
750
+ each do |value|
751
+ return true if other.include_eql?(value)
256
752
  end
257
- @size -= 1
258
- value
753
+ false
259
754
  end
260
- # rubocop:enable Metrics/MethodLength
261
755
 
262
- # rubocop:disable Metrics/MethodLength
756
+ # Returns a +String+ formed by joining each element of the array with the given separator.
757
+ #
758
+ # @param separator [String] The separator to join the elements with.
759
+ # @return [String] The joined string.
760
+ def join(separator = $OUTPUT_FIELD_SEPARATOR)
761
+ to_a.join(separator)
762
+ end
263
763
 
264
- # Shifts the first value from the sorted array.
764
+ # Retains those elements for which the block returns a truthy value; deletes all other elements; returns +self+
265
765
  #
266
- # @return [Object] The first value in the array.
267
- def shift
268
- return nil if @size.zero?
766
+ # Returns an Enumerator if no block is given.
767
+ #
768
+ # @yield [value] The block to keep with.
769
+ # @return [SortedArray, Enumerator] The array with the kept values.
770
+ def keep_if
771
+ return to_enum(:keep_if) unless block_given?
269
772
 
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
773
+ (@size - 1).downto(0) do |index|
774
+ delete_at(index) unless yield(self[index])
277
775
  end
278
- @size -= 1
279
- value
776
+ self
280
777
  end
281
- # rubocop:enable Metrics/MethodLength
282
778
 
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.
779
+ # Retrieves the last value in the sorted array.
287
780
  #
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
781
+ # @return [Object] The last value in the array.
782
+ def last
783
+ return nil if @size.zero?
294
784
 
295
- left_index = bisect_left(value)
296
- right_index = bisect_right(value)
297
- right_index - left_index
785
+ @lists.last.last
298
786
  end
299
787
 
300
- # Clears the sorted array, removing all values.
788
+ # Replaces the contents of +self+ with the contents of +other+.
789
+ #
790
+ # @param other [SortedArray] The other array to replace with.
791
+ # @return [SortedArray] +self+. The replaced array.
792
+ def replace(other)
793
+ @lists = other.lists.map(&:dup)
794
+ @maxes = other.maxes.dup
795
+ @array_index = other.array_index.dup
796
+ @offset = other.offset
797
+ @load_factor = other.load_factor
798
+ @size = other.size
799
+ self
800
+ end
801
+
802
+ # Returns a string representation of the sorted array.
301
803
  #
302
- # @return [void]
303
- def clear
304
- @lists.clear
305
- @maxes.clear
306
- @index.clear
307
- @offset = 0
308
- @size = 0
804
+ # @return [String] A string representation of the sorted array.
805
+ def to_s
806
+ "SortedArray(#{to_a})"
309
807
  end
310
808
 
311
- # Checks if the sorted array contains a value.
809
+ # Returns an index to insert +value+ in the sorted list.
312
810
  #
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
811
+ # If the +value+ is already present, the insertion point will be before
812
+ # (to the left of) any existing values.
813
+ #
814
+ # Runtime complexity: +O(log(n))+ -- approximate.
815
+ #
816
+ # @param value [Object] The value to insert.
817
+ # @return [Integer] The index to insert the value.
818
+ def bisect_left(value)
819
+ return 0 if @maxes.empty?
318
820
 
319
- sublist = @lists[i]
320
- idx = internal_bisect_left(sublist, value)
321
- idx < sublist.size && sublist[idx] == value
821
+ pos = internal_bisect_left(@maxes, value)
822
+
823
+ return @size if pos == @maxes.size
824
+
825
+ idx = internal_bisect_left(@lists[pos], value)
826
+ loc(pos, idx)
827
+ end
828
+
829
+ # Returns an index to insert +value+ in the sorted list.
830
+ #
831
+ # If the +value+ is already present, the insertion point will be after
832
+ # (to the right of) any existing values.
833
+ #
834
+ # Runtime complexity: +O(log(n))+ -- approximate.
835
+ #
836
+ # @param value [Object] The value to insert.
837
+ # @return [Integer] The index to insert the value.
838
+ def bisect_right(value)
839
+ return 0 if @maxes.empty?
840
+
841
+ pos = internal_bisect_right(@maxes, value)
842
+
843
+ return @size if pos == @maxes.size
844
+
845
+ idx = internal_bisect_right(@lists[pos], value)
846
+ loc(pos, idx)
322
847
  end
323
848
 
849
+ # Shifts the first value from the sorted array.
850
+ #
851
+ # @return [Object] The first value in the array.
852
+ def shift
853
+ return nil if @size.zero?
854
+
855
+ delete_at(0)
856
+ end
324
857
  # rubocop:disable Metrics/MethodLength
325
858
  # rubocop:disable Metrics/AbcSize
326
859
 
@@ -355,7 +888,7 @@ module SortedContainers
355
888
  @size = values.length
356
889
 
357
890
  # Clear the index as it might be outdated
358
- @index.clear
891
+ @array_index.clear
359
892
  end
360
893
  # rubocop:enable Metrics/MethodLength
361
894
  # rubocop:enable Metrics/AbcSize
@@ -364,22 +897,27 @@ module SortedContainers
364
897
  #
365
898
  # @return [Array] An array representation of the sorted array.
366
899
  def to_a
367
- @lists.flatten
900
+ @lists.flatten(1)
368
901
  end
902
+ alias to_ary to_a
369
903
 
370
- # Array is already sorted. Duplicates the sorted array and returns it.
904
+ # Creates a new SortedArray and resorts the values.
905
+ # Usefull when the values are modified and the array needs to be resorted.
371
906
  #
372
907
  # @return [SortedArray] The sorted array.
373
908
  def sort
374
- # No need to sort, already sorted
375
- dup
909
+ values = to_a
910
+ self.class.new(values, load_factor: @load_factor)
376
911
  end
377
912
 
378
- # Returns self, as the array is already sorted.
913
+ # Resorts the values in the array.
914
+ # Usefull when the values are modified and the array needs to be resorted.
379
915
  #
380
916
  # @return [SortedArray] The sorted array.
381
917
  def sort!
382
- # No need to sort, already sorted
918
+ values = to_a
919
+ clear
920
+ update(values)
383
921
  self
384
922
  end
385
923
 
@@ -391,25 +929,13 @@ module SortedContainers
391
929
  new_instance = self.class.new
392
930
  new_instance.lists = @lists.map(&:dup)
393
931
  new_instance.maxes = @maxes.dup
394
- new_instance.index = @index.dup
932
+ new_instance.array_index = @array_index.dup
395
933
  new_instance.offset = @offset
396
934
  new_instance.load_factor = @load_factor
397
935
  new_instance.size = @size
398
936
  new_instance
399
937
  end
400
938
 
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
939
  # Returns the maximum value in the sorted array.
414
940
  #
415
941
  # @return [Object] The maximum value in the array.
@@ -424,15 +950,238 @@ module SortedContainers
424
950
  @lists.first&.first
425
951
  end
426
952
 
427
- # Iterates over each value in the sorted array.
953
+ # Returns a 2-element array containing the minimum and maximum values in the sorted array.
954
+ # If a block is given, the result of the block is computed for each value in the array,
955
+ # and the minimum and maximum values are computed from the result.
956
+ #
957
+ # @yield [value] The block to compute with.
958
+ # @return [Array] A 2-element array containing the minimum and maximum values.
959
+ def minmax
960
+ if block_given?
961
+ each.reduce([nil, nil]) do |(min_value, max_value), value|
962
+ min_value = value if min_value.nil? || yield(value, min_value).negative?
963
+ max_value = value if max_value.nil? || yield(value, max_value).positive?
964
+ [min_value, max_value]
965
+ end
966
+ else
967
+ [min, max]
968
+ end
969
+ end
970
+
971
+ # Packs the values in the array into a binary sequence.
972
+ # @see Array#pack
973
+ #
974
+ # @param template [String] The template to pack the values with.
975
+ # @param buffer [String] The buffer to pack the values into.
976
+ # @return [String] The packed values.
977
+ def pack(template, buffer: nil)
978
+ to_a.pack(template, buffer: buffer)
979
+ end
980
+
981
+ # rubocop:disable Naming/MethodParameterName
982
+ # rubocop:disable Metrics/MethodLength
983
+
984
+ # Pops the last value from the sorted array.
985
+ #
986
+ # @param n [Integer] The number of values to pop.
987
+ # @return [Object] The last value in the array.
988
+ def pop(n = nil)
989
+ return nil if @size.zero?
990
+
991
+ if n.nil?
992
+ index = @size - 1
993
+ delete_at(index)
994
+ else
995
+ values = []
996
+ n.times do
997
+ return values if @size.zero?
998
+
999
+ index = @size - 1
1000
+ values.prepend(delete_at(index))
1001
+ end
1002
+ values
1003
+ end
1004
+ end
1005
+
1006
+ # rubocop:enable Naming/MethodParameterName
1007
+ # rubocop:enable Metrics/MethodLength
1008
+
1009
+ # Computes and returns or yields all combinations of elements from all the Arrays,
1010
+ # including both +self+ and +other_arrays+.
1011
+ #
1012
+ # @param other_arrays [SortedArray] The arrays to combine with.
1013
+ # @yield [value] The block to combine with.
1014
+ # @return [SortedArray] The combined array.
1015
+ def product(*other_arrays, &block)
1016
+ arrays = other_arrays.map(&:to_a)
1017
+ self.class.new(
1018
+ to_a.product(*arrays, &block),
1019
+ load_factor: @load_factor
1020
+ )
1021
+ end
1022
+
1023
+ # Returns the first element in +self+ that is an +Array+ whose second element +==+ +obj+:
1024
+ #
1025
+ # Time complexity: O(n)
1026
+ #
1027
+ # @param obj [Object] The object to search for.
1028
+ # @return [Array] The first element in +self+ that is an +Array+ whose second element +==+ +obj+.
1029
+ def rassoc(obj)
1030
+ index = find_index { |x| x.is_a?(Array) && x[1] >= obj }
1031
+ index.nil? ? nil : self[index]
1032
+ end
1033
+
1034
+ # Deletes every element of +self+ for which block evaluates to true.
1035
+ #
1036
+ # Returns +self+ if any changes were made, otherwise returns +nil+.
1037
+ #
1038
+ # Returns an Enumerator if no block is given.
1039
+ #
1040
+ # @yield [value] The block to reject with.
1041
+ # @return [SortedArray, Enumerator] The rejected array.
1042
+ def reject!
1043
+ return to_enum(:reject!) unless block_given?
1044
+
1045
+ indexes_to_delete = []
1046
+ each_with_index do |value, index|
1047
+ indexes_to_delete << index if yield(value)
1048
+ end
1049
+ return nil if indexes_to_delete.empty?
1050
+
1051
+ indexes_to_delete.reverse.each { |index| delete_at(index) }
1052
+ self
1053
+ end
1054
+
1055
+ # Iterates over the sorted array in reverse order.
428
1056
  #
429
1057
  # @yield [value] Gives each value to the block.
430
- def each(&block)
431
- @lists.each do |sublist|
432
- sublist.each(&block)
1058
+ # @return [Enumerator] If no block is given, an Enumerator is returned.
1059
+ def reverse_each(&block)
1060
+ return to_enum(:reverse_each) unless block_given?
1061
+
1062
+ @lists.reverse_each do |sublist|
1063
+ sublist.reverse_each(&block)
433
1064
  end
434
1065
  end
435
1066
 
1067
+ # rubocop:disable Metrics/AbcSize
1068
+ # rubocop:disable Metrics/CyclomaticComplexity
1069
+ # rubocop:disable Metrics/MethodLength
1070
+ # rubocop:disable Metrics/PerceivedComplexity
1071
+
1072
+ # Returns the index of the last element for which object +==+ element.
1073
+ #
1074
+ # Returns an Enumerator if no block or value is given.
1075
+ #
1076
+ # When a block is given but no value, the block is used to find the last element.
1077
+ #
1078
+ # @overload rindex(value)
1079
+ # @param value [Object] The value to find.
1080
+ # @overload rindex
1081
+ # @yield [value] The block to find with.
1082
+ # @return [Integer] The index of the value.
1083
+ def rindex(value = nil)
1084
+ return to_enum(:rindex, value) unless block_given? || value
1085
+ return nil if @size.zero?
1086
+
1087
+ if value.nil?
1088
+ reverse_each.with_index do |val, idx|
1089
+ return @size - idx - 1 if yield(val)
1090
+ end
1091
+ nil
1092
+ else
1093
+ warn "given block not used" if block_given?
1094
+ index = bisect_right(value)
1095
+ self[index - 1] == value ? index - 1 : nil
1096
+ end
1097
+ end
1098
+
1099
+ # rubocop:enable Metrics/AbcSize
1100
+ # rubocop:enable Metrics/CyclomaticComplexity
1101
+ # rubocop:enable Metrics/MethodLength
1102
+ # rubocop:enable Metrics/PerceivedComplexity
1103
+
1104
+ # rubocop:disable Naming/MethodParameterName
1105
+
1106
+ # Returns random elements from +self+.
1107
+ #
1108
+ # If +n+ is given, returns an array of +n+ random elements.
1109
+ # If +n+ is not given, returns a single random element.
1110
+ #
1111
+ # @param n [Integer] The number of random elements to return.
1112
+ # @param random [Random] The random number generator to use.
1113
+ # @return [Object, Array] The random element(s).
1114
+ def sample(n = nil, random: Random)
1115
+ return nil if @size.zero?
1116
+
1117
+ if n.nil?
1118
+ index = random.rand(@size)
1119
+ self[index]
1120
+ else
1121
+ raise ArgumentError, "negative sample number" if n.negative?
1122
+
1123
+ n.times.map { self[random.rand(@size)] }
1124
+ end
1125
+ end
1126
+
1127
+ # rubocop:enable Naming/MethodParameterName
1128
+
1129
+ # Returns a new SortedArray that is the union of +self+ and +other_arrays+.
1130
+ # Duplicates are removed.
1131
+ #
1132
+ # @param other_arrays [Array] The arrays to union with.
1133
+ # @return [SortedArray] The unioned array.
1134
+ def union(*other_arrays)
1135
+ self.class.new(to_a | other_arrays.flatten, load_factor: @load_factor)
1136
+ end
1137
+ alias | union
1138
+
1139
+ # Returns a new SortedArray containing the unique values in +self+.
1140
+ #
1141
+ # @return [SortedArray] The unique array.
1142
+ def uniq(&block)
1143
+ self.class.new(to_a.uniq(&block), load_factor: @load_factor)
1144
+ end
1145
+
1146
+ # Removes duplicate values from the sorted array.
1147
+ # Returns +self+ if any changes were made, otherwise returns +nil+.
1148
+ # If a block is given, it will use the block to determine uniqueness.
1149
+ #
1150
+ # @yield [value] The block to determine uniqueness.
1151
+ # @return [SortedArray, nil] The array with the unique values.
1152
+ def uniq!(&block)
1153
+ values = to_a.uniq(&block)
1154
+
1155
+ return nil if values.size == @size
1156
+
1157
+ clear
1158
+ update(values)
1159
+ self
1160
+ end
1161
+
1162
+ # Returns a new SortedArray containing the values from the given indexes and ranges.
1163
+ #
1164
+ # @param indexes [Integer, Range] The indexes and ranges to get values from.
1165
+ # @return [Array] The values from the given indexes and ranges.
1166
+ def values_at(*indexes)
1167
+ indexes.map do |index|
1168
+ if index.is_a?(Range)
1169
+ get_values_from_range(index)
1170
+ else
1171
+ get_value_at_index(index)
1172
+ end
1173
+ end.flatten
1174
+ end
1175
+
1176
+ # Combines each element of the sorted array with the corresponding elements from other arrays.
1177
+ #
1178
+ # @param other_arrays [Array] The other arrays to zip with the sorted array.
1179
+ # @param block [Proc] An optional block to apply to each zipped element.
1180
+ # @return [SortedArray] A new SortedArray containing the zipped elements.
1181
+ def zip(*other_arrays, &block)
1182
+ self.class.new(to_a.zip(*other_arrays, &block), load_factor: @load_factor)
1183
+ end
1184
+
436
1185
  private
437
1186
 
438
1187
  # Performs a left bisect on the array.
@@ -441,7 +1190,7 @@ module SortedContainers
441
1190
  # @param value [Object] The value to bisect with.
442
1191
  # @return [Integer] The index where the value should be inserted.
443
1192
  def internal_bisect_left(array, value)
444
- array.bsearch_index { |x| x >= value } || array.size
1193
+ array.bsearch_index { |x| (x <=> value) >= 0 } || array.size
445
1194
  end
446
1195
 
447
1196
  # Performs a right bisect on the array.
@@ -450,7 +1199,26 @@ module SortedContainers
450
1199
  # @param value [Object] The value to bisect with.
451
1200
  # @return [Integer] The index where the value should be inserted.
452
1201
  def internal_bisect_right(array, value)
453
- array.bsearch_index { |x| x > value } || array.length
1202
+ array.bsearch_index { |x| (x <=> value) == 1 } || array.length
1203
+ end
1204
+
1205
+ # Updates the index within the range with the block.
1206
+ # Does not update index or maxes. Distrupts the sorted order.
1207
+ #
1208
+ # @param range [Range] The range to update.
1209
+ # @param block [Proc] The block that takes the index and returns the value.
1210
+ def fill_range_unsafe(range, block)
1211
+ range.each { |index| internal_change_unsafe(index, block.call(index)) }
1212
+ end
1213
+
1214
+ # Updates the element at index with the value.
1215
+ # Does not update index or maxes. Distrupts the sorted order.
1216
+ #
1217
+ # @param index [Integer] The index of the value to update.
1218
+ # @param value [Object] The value to update.
1219
+ def internal_change_unsafe(index, value)
1220
+ pos, idx = pos(index)
1221
+ @lists[pos][idx] = value
454
1222
  end
455
1223
 
456
1224
  # Gets the value at a given index. Supports negative indices.
@@ -571,14 +1339,14 @@ module SortedContainers
571
1339
  @maxes[pos] = @lists[pos].last
572
1340
  @lists.insert(pos + 1, half)
573
1341
  @maxes.insert(pos + 1, half.last)
574
- @index.clear
575
- elsif @index.size.positive?
1342
+ @array_index.clear
1343
+ elsif @array_index.size.positive?
576
1344
  child = @offset + pos
577
1345
  while child.positive?
578
- @index[child] += 1
1346
+ @array_index[child] += 1
579
1347
  child = (child - 1) >> 1
580
1348
  end
581
- @index[0] += 1
1349
+ @array_index[0] += 1
582
1350
  end
583
1351
  end
584
1352
  # rubocop:enable Metrics/AbcSize
@@ -602,24 +1370,24 @@ module SortedContainers
602
1370
  if len_list > (@load_factor >> 1)
603
1371
  @maxes[pos] = list.last
604
1372
 
605
- if @index.size.positive?
1373
+ if @array_index.size.positive?
606
1374
  child = @offset + pos
607
1375
  while child.positive?
608
- @index[child] -= 1
1376
+ @array_index[child] -= 1
609
1377
  child = (child - 1) >> 1
610
1378
  end
611
- @index[0] -= 1
1379
+ @array_index[0] -= 1
612
1380
  end
613
1381
  elsif @lists.length > 1
614
1382
  pos += 1 if pos.zero?
615
1383
 
616
1384
  prev = pos - 1
617
- @lists[prev].concat(list)
1385
+ @lists[prev].concat(@lists[pos])
618
1386
  @maxes[prev] = @lists[prev].last
619
1387
 
620
1388
  @lists.delete_at(pos)
621
1389
  @maxes.delete_at(pos)
622
- @index.clear
1390
+ @array_index.clear
623
1391
 
624
1392
  expand(prev)
625
1393
  elsif len_list.positive?
@@ -627,7 +1395,7 @@ module SortedContainers
627
1395
  else
628
1396
  @lists.delete_at(pos)
629
1397
  @maxes.delete_at(pos)
630
- @index.clear
1398
+ @array_index.clear
631
1399
  end
632
1400
  value
633
1401
  end
@@ -663,7 +1431,7 @@ module SortedContainers
663
1431
  #
664
1432
  # Finally, the index is built by concatenating these lists together:
665
1433
  #
666
- # @index = [14, 5, 9, 3, 2, 4, 5]
1434
+ # @array_index = [14, 5, 9, 3, 2, 4, 5]
667
1435
  #
668
1436
  # An offset storing the start of the first row is also stored:
669
1437
  #
@@ -677,7 +1445,7 @@ module SortedContainers
677
1445
 
678
1446
  # Early return if there is only one sublist
679
1447
  if row0.length == 1
680
- @index = row0
1448
+ @array_index = row0
681
1449
  @offset = 0
682
1450
  return
683
1451
  end
@@ -691,7 +1459,7 @@ module SortedContainers
691
1459
 
692
1460
  # Return early if only one row is needed
693
1461
  if row1.length == 1
694
- @index = row1 + row0
1462
+ @array_index = row1 + row0
695
1463
  @offset = 1
696
1464
  return
697
1465
  end
@@ -708,7 +1476,7 @@ module SortedContainers
708
1476
  end
709
1477
 
710
1478
  # Flatten the tree into the index array
711
- tree.reverse_each { |level| @index.concat(level) }
1479
+ tree.reverse_each { |level| @array_index.concat(level) }
712
1480
  @offset = (the_size * 2) - 1
713
1481
  end
714
1482
  # rubocop:enable Metrics/AbcSize
@@ -788,14 +1556,14 @@ module SortedContainers
788
1556
 
789
1557
  return 0, idx if idx < @lists[0].size
790
1558
 
791
- build_index if @index.empty?
1559
+ build_index if @array_index.empty?
792
1560
 
793
1561
  pos = 0
794
1562
  child = 1
795
- len_index = @index.size
1563
+ len_index = @array_index.size
796
1564
 
797
1565
  while child < len_index
798
- index_child = @index[child]
1566
+ index_child = @array_index[child]
799
1567
 
800
1568
  if idx < index_child
801
1569
  pos = child
@@ -823,7 +1591,7 @@ module SortedContainers
823
1591
  def loc(pos, idx)
824
1592
  return idx if pos.zero?
825
1593
 
826
- build_index if @index.empty?
1594
+ build_index if @array_index.empty?
827
1595
 
828
1596
  # Increment pos to point in the index to @lists[pos].size.
829
1597
  total = 0
@@ -835,7 +1603,7 @@ module SortedContainers
835
1603
 
836
1604
  # Right-child nodes are at even indices. At such indices
837
1605
  # account the total below the left child node.
838
- total += @index[pos - 1] if pos.even?
1606
+ total += @array_index[pos - 1] if pos.even?
839
1607
 
840
1608
  # Advance pos to the parent node.
841
1609
  pos = (pos - 1) >> 1
@@ -846,7 +1614,16 @@ module SortedContainers
846
1614
 
847
1615
  protected
848
1616
 
849
- attr_accessor :lists, :maxes, :index, :offset, :load_factor
1617
+ # Checks if the sorted array includes a value using eql?.
1618
+ #
1619
+ # @param value [Object] The value to check.
1620
+ # @return [Boolean] True if the value is found, false otherwise.
1621
+ def include_eql?(value)
1622
+ index = find_index(value)
1623
+ index ? self[index].eql?(value) : false
1624
+ end
1625
+
1626
+ attr_accessor :lists, :maxes, :array_index, :offset
850
1627
 
851
1628
  attr_writer :size
852
1629
  end