sorted_containers 0.1.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +3 -0
- data/README.md +20 -12
- 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
|