sorted_containers 0.1.1 → 1.0.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,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