sorted_containers 0.1.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +3 -0
- data/CHANGELOG.md +5 -1
- data/README.md +31 -17
- data/lib/sorted_containers/core_extensions.rb +56 -0
- data/lib/sorted_containers/sorted_array.rb +987 -208
- data/lib/sorted_containers/sorted_hash.rb +461 -53
- data/lib/sorted_containers/sorted_set.rb +310 -71
- data/lib/sorted_containers/version.rb +1 -1
- data/lib/sorted_containers.rb +1 -0
- metadata +4 -3
@@ -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
|
-
#
|
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
|
-
@
|
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
|
-
#
|
32
|
-
|
33
|
-
# Adds a value to the sorted array.
|
46
|
+
# Returns a new SortedArray populated with the given objects.
|
34
47
|
#
|
35
|
-
# @param
|
36
|
-
|
37
|
-
|
38
|
-
|
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
|
-
#
|
57
|
-
|
58
|
-
# Alias for add
|
54
|
+
# Returns a new SortedArray with the values from the union of the two arrays.
|
59
55
|
#
|
60
|
-
# @param
|
61
|
-
|
62
|
-
|
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
|
-
#
|
63
|
+
# When non-negative, multiplies returns a new Array with each value repeated `int` times.
|
66
64
|
#
|
67
|
-
# @
|
68
|
-
|
69
|
-
|
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
|
-
#
|
72
|
+
# Returns a new SortedArray with the values from both arrays.
|
73
73
|
#
|
74
|
-
# @
|
75
|
-
|
76
|
-
|
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
|
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
|
-
#
|
85
|
-
#
|
86
|
-
|
87
|
-
|
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
|
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
|
107
|
-
# @return [Integer]
|
108
|
-
def
|
109
|
-
return
|
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
|
-
|
97
|
+
each_with_index do |value, index|
|
98
|
+
return value <=> other[index] if value != other[index]
|
99
|
+
end
|
114
100
|
|
115
|
-
|
116
|
-
loc(pos, idx)
|
101
|
+
0
|
117
102
|
end
|
118
103
|
|
119
|
-
#
|
104
|
+
# Returns true if the arrays size and values are equal.
|
120
105
|
#
|
121
|
-
# @param
|
122
|
-
|
123
|
-
|
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
|
-
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
216
|
-
|
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
|
-
|
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
|
-
#
|
692
|
+
# Returns a new SortedArray that is a recursive flattening of the array.
|
232
693
|
#
|
233
|
-
#
|
234
|
-
|
235
|
-
|
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
|
-
|
238
|
-
|
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
|
-
#
|
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
|
-
#
|
725
|
+
# Checks if the sorted array contains a value.
|
244
726
|
#
|
245
|
-
# @
|
246
|
-
|
247
|
-
|
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
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
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
|
-
|
258
|
-
value
|
755
|
+
false
|
259
756
|
end
|
260
|
-
# rubocop:enable Metrics/MethodLength
|
261
757
|
|
262
|
-
#
|
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
|
-
#
|
766
|
+
# Retains those elements for which the block returns a truthy value; deletes all other elements; returns +self+
|
265
767
|
#
|
266
|
-
#
|
267
|
-
|
268
|
-
|
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
|
-
|
271
|
-
|
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
|
-
|
279
|
-
value
|
778
|
+
self
|
280
779
|
end
|
281
|
-
# rubocop:enable Metrics/MethodLength
|
282
780
|
|
283
|
-
#
|
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
|
-
# @
|
289
|
-
|
290
|
-
|
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
|
-
|
296
|
-
right_index = bisect_right(value)
|
297
|
-
right_index - left_index
|
787
|
+
@lists.last.last
|
298
788
|
end
|
299
789
|
|
300
|
-
#
|
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 [
|
303
|
-
def
|
304
|
-
|
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
|
-
#
|
811
|
+
# Returns an index to insert +value+ in the sorted list.
|
312
812
|
#
|
313
|
-
#
|
314
|
-
#
|
315
|
-
|
316
|
-
|
317
|
-
|
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
|
-
|
320
|
-
|
321
|
-
|
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
|
-
@
|
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
|
-
#
|
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
|
-
|
375
|
-
|
911
|
+
values = to_a
|
912
|
+
self.class.new(values, load_factor: @load_factor)
|
376
913
|
end
|
377
914
|
|
378
|
-
#
|
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
|
-
|
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.
|
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
|
-
#
|
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
|
-
|
431
|
-
|
432
|
-
|
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
|
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
|
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
|
-
@
|
575
|
-
elsif @
|
1344
|
+
@array_index.clear
|
1345
|
+
elsif @array_index.size.positive?
|
576
1346
|
child = @offset + pos
|
577
1347
|
while child.positive?
|
578
|
-
@
|
1348
|
+
@array_index[child] += 1
|
579
1349
|
child = (child - 1) >> 1
|
580
1350
|
end
|
581
|
-
@
|
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 @
|
1375
|
+
if @array_index.size.positive?
|
606
1376
|
child = @offset + pos
|
607
1377
|
while child.positive?
|
608
|
-
@
|
1378
|
+
@array_index[child] -= 1
|
609
1379
|
child = (child - 1) >> 1
|
610
1380
|
end
|
611
|
-
@
|
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
|
-
@
|
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
|
-
@
|
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
|
-
# @
|
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
|
-
@
|
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
|
-
@
|
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| @
|
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 @
|
1561
|
+
build_index if @array_index.empty?
|
792
1562
|
|
793
1563
|
pos = 0
|
794
1564
|
child = 1
|
795
|
-
len_index = @
|
1565
|
+
len_index = @array_index.size
|
796
1566
|
|
797
1567
|
while child < len_index
|
798
|
-
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 @
|
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 += @
|
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
|
-
|
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
|