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