net-imap 0.5.6 → 0.6.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/Gemfile +7 -4
- data/lib/net/imap/command_data.rb +0 -68
- data/lib/net/imap/config/attr_inheritance.rb +14 -1
- data/lib/net/imap/config/attr_type_coercion.rb +35 -22
- data/lib/net/imap/config/attr_version_defaults.rb +93 -0
- data/lib/net/imap/config.rb +265 -95
- data/lib/net/imap/connection_state.rb +48 -0
- data/lib/net/imap/data_encoding.rb +77 -28
- data/lib/net/imap/errors.rb +33 -0
- data/lib/net/imap/esearch_result.rb +48 -3
- data/lib/net/imap/flags.rb +1 -1
- data/lib/net/imap/response_data.rb +2 -4
- data/lib/net/imap/response_parser.rb +8 -13
- data/lib/net/imap/response_reader.rb +73 -0
- data/lib/net/imap/search_result.rb +8 -3
- data/lib/net/imap/sequence_set.rb +1113 -431
- data/lib/net/imap/uidplus_data.rb +2 -63
- data/lib/net/imap/vanished_data.rb +10 -1
- data/lib/net/imap.rb +291 -81
- data/net-imap.gemspec +1 -1
- data/rakelib/string_prep_tables_generator.rb +4 -2
- metadata +7 -5
- data/lib/net/imap/data_lite.rb +0 -226
|
@@ -18,21 +18,9 @@ module Net
|
|
|
18
18
|
#
|
|
19
19
|
# == Creating sequence sets
|
|
20
20
|
#
|
|
21
|
-
# SequenceSet.new with no arguments creates an empty sequence set. Note
|
|
22
|
-
# that an empty sequence set is invalid in the \IMAP grammar.
|
|
23
|
-
#
|
|
24
|
-
# set = Net::IMAP::SequenceSet.new
|
|
25
|
-
# set.empty? #=> true
|
|
26
|
-
# set.valid? #=> false
|
|
27
|
-
# set.valid_string #!> raises DataFormatError
|
|
28
|
-
# set << 1..10
|
|
29
|
-
# set.empty? #=> false
|
|
30
|
-
# set.valid? #=> true
|
|
31
|
-
# set.valid_string #=> "1:10"
|
|
32
|
-
#
|
|
33
21
|
# SequenceSet.new may receive a single optional argument: a non-zero 32 bit
|
|
34
22
|
# unsigned integer, a range, a <tt>sequence-set</tt> formatted string,
|
|
35
|
-
# another
|
|
23
|
+
# another SequenceSet, a Set (containing only numbers or <tt>*</tt>), or an
|
|
36
24
|
# Array containing any of these (array inputs may be nested).
|
|
37
25
|
#
|
|
38
26
|
# set = Net::IMAP::SequenceSet.new(1)
|
|
@@ -48,30 +36,118 @@ module Net
|
|
|
48
36
|
# set = Net::IMAP::SequenceSet.new(1, 2, 3..7, 5, 6..10, 2048, 1024)
|
|
49
37
|
# set.valid_string #=> "1:10,55,1024:2048"
|
|
50
38
|
#
|
|
51
|
-
#
|
|
52
|
-
#
|
|
39
|
+
# SequenceSet.new with no arguments creates an empty sequence set. Note
|
|
40
|
+
# that an empty sequence set is invalid in the \IMAP grammar.
|
|
41
|
+
#
|
|
42
|
+
# set = Net::IMAP::SequenceSet.new
|
|
43
|
+
# set.empty? #=> true
|
|
44
|
+
# set.valid? #=> false
|
|
45
|
+
# set.valid_string #!> raises DataFormatError
|
|
46
|
+
# set << 1..10
|
|
47
|
+
# set.empty? #=> false
|
|
48
|
+
# set.valid? #=> true
|
|
49
|
+
# set.valid_string #=> "1:10"
|
|
50
|
+
#
|
|
51
|
+
# Using SequenceSet.new with another SequenceSet input behaves the same as
|
|
52
|
+
# calling #dup on the other set. The input's #string will be preserved.
|
|
53
|
+
#
|
|
54
|
+
# input = Net::IMAP::SequenceSet.new("1,2,3:7,5,6:10,2048,1024")
|
|
55
|
+
# copy = Net::IMAP::SequenceSet.new(input)
|
|
56
|
+
# input.valid_string #=> "1,2,3:7,5,6:10,2048,1024"
|
|
57
|
+
# copy.valid_string #=> "1,2,3:7,5,6:10,2048,1024"
|
|
58
|
+
# copy2 = input.dup # same as calling new with a SequenceSet input
|
|
59
|
+
# copy == input #=> true, same set membership
|
|
60
|
+
# copy.eql? input #=> true, same string value
|
|
61
|
+
# copy.equal? input #=> false, different objects
|
|
62
|
+
#
|
|
63
|
+
# copy.normalize!
|
|
64
|
+
# copy.valid_string #=> "1:10,1024,2048"
|
|
65
|
+
# copy == input #=> true, same set membership
|
|
66
|
+
# copy.eql? input #=> false, different string value
|
|
67
|
+
#
|
|
68
|
+
# copy << 999
|
|
69
|
+
# copy.valid_string #=> "1:10,999,1024,2048"
|
|
70
|
+
# copy == input #=> false, different set membership
|
|
71
|
+
# copy.eql? input #=> false, different string value
|
|
72
|
+
#
|
|
73
|
+
# Use Net::IMAP::SequenceSet() to coerce a single (optional) input.
|
|
74
|
+
# A SequenceSet input is returned without duplication, even when frozen.
|
|
75
|
+
#
|
|
76
|
+
# set = Net::IMAP::SequenceSet()
|
|
77
|
+
# set.string #=> nil
|
|
78
|
+
# set.frozen? #=> false
|
|
79
|
+
#
|
|
80
|
+
# # String order is preserved
|
|
81
|
+
# set = Net::IMAP::SequenceSet("1,2,3:7,5,6:10,2048,1024")
|
|
82
|
+
# set.valid_string #=> "1,2,3:7,5,6:10,2048,1024"
|
|
83
|
+
# set.frozen? #=> false
|
|
84
|
+
#
|
|
85
|
+
# # Other inputs are normalized
|
|
86
|
+
# set = Net::IMAP::SequenceSet([1, 2, [3..7, 5], 6..10, 2048, 1024])
|
|
87
|
+
# set.valid_string #=> "1:10,1024,2048"
|
|
88
|
+
# set.frozen? #=> false
|
|
89
|
+
#
|
|
90
|
+
# unfrozen = set
|
|
91
|
+
# frozen = set.dup.freeze
|
|
92
|
+
# unfrozen.equal? Net::IMAP::SequenceSet(unfrozen) #=> true
|
|
93
|
+
# frozen.equal? Net::IMAP::SequenceSet(frozen) #=> true
|
|
53
94
|
#
|
|
95
|
+
# Use ::[] to coerce one or more arguments into a valid frozen SequenceSet.
|
|
96
|
+
# A valid frozen SequenceSet is returned directly, without allocating a new
|
|
97
|
+
# object. ::[] will not create an invalid (empty) set.
|
|
98
|
+
#
|
|
99
|
+
# Net::IMAP::SequenceSet[] #!> raises ArgumentError
|
|
100
|
+
# Net::IMAP::SequenceSet[nil] #!> raises DataFormatError
|
|
101
|
+
# Net::IMAP::SequenceSet[""] #!> raises DataFormatError
|
|
102
|
+
#
|
|
103
|
+
# # String order is preserved
|
|
54
104
|
# set = Net::IMAP::SequenceSet["1,2,3:7,5,6:10,2048,1024"]
|
|
55
105
|
# set.valid_string #=> "1,2,3:7,5,6:10,2048,1024"
|
|
106
|
+
# set.frozen? #=> true
|
|
107
|
+
#
|
|
108
|
+
# # Other inputs are normalized
|
|
56
109
|
# set = Net::IMAP::SequenceSet[1, 2, [3..7, 5], 6..10, 2048, 1024]
|
|
57
|
-
# set.valid_string #=> "1:10,
|
|
110
|
+
# set.valid_string #=> "1:10,1024,2048"
|
|
111
|
+
# set.frozen? #=> true
|
|
112
|
+
#
|
|
113
|
+
# frozen = set
|
|
114
|
+
# unfrozen = set.dup
|
|
115
|
+
# frozen.equal? Net::IMAP::SequenceSet[frozen] #=> true
|
|
116
|
+
# unfrozen.equal? Net::IMAP::SequenceSet[unfrozen] #=> false
|
|
117
|
+
#
|
|
118
|
+
# Objects which respond to +to_sequence_set+ (such as SearchResult and
|
|
119
|
+
# ThreadMember) can be coerced to a SequenceSet with ::new, ::try_convert,
|
|
120
|
+
# ::[], or Net::IMAP::SequenceSet.
|
|
121
|
+
#
|
|
122
|
+
# search = imap.uid_search(["SUBJECT", "hello", "NOT", "SEEN"])
|
|
123
|
+
# seqset = Net::IMAP::SequenceSet(search) - already_fetched
|
|
124
|
+
# fetch = imap.uid_fetch(seqset, "FAST")
|
|
58
125
|
#
|
|
59
126
|
# == Ordered and Normalized sets
|
|
60
127
|
#
|
|
61
128
|
# Sometimes the order of the set's members is significant, such as with the
|
|
62
129
|
# +ESORT+, <tt>CONTEXT=SORT</tt>, and +UIDPLUS+ extensions. So, when a
|
|
63
|
-
# sequence set is created
|
|
64
|
-
# #string representation is preserved.
|
|
130
|
+
# sequence set is created from a single string (such as by the parser), that
|
|
131
|
+
# #string representation is preserved. Assigning a string with #string= or
|
|
132
|
+
# #replace will also preserve that string. Use #each_entry, #entries, or
|
|
133
|
+
# #each_ordered_number to enumerate the entries in their #string order.
|
|
134
|
+
# Hash equality (using #eql?) is based on the string representation.
|
|
135
|
+
#
|
|
136
|
+
# Internally, SequenceSet uses a normalized uint32 set representation which
|
|
137
|
+
# sorts and de-duplicates all numbers and coalesces adjacent or overlapping
|
|
138
|
+
# entries. Many methods use this sorted set representation for <tt>O(lg
|
|
139
|
+
# n)</tt> searches. Use #each_element, #elements, #each_range, #ranges,
|
|
140
|
+
# #each_number, or #numbers to enumerate the set in sorted order. Basic
|
|
141
|
+
# object equality (using #==) is based on set membership, without regard to
|
|
142
|
+
# #entry order or #string normalization.
|
|
65
143
|
#
|
|
66
|
-
#
|
|
67
|
-
# entries
|
|
68
|
-
#
|
|
69
|
-
# <tt>O(lg n)</tt> porformance. Use #entries or #each_entry to enumerate
|
|
70
|
-
# the set in its original order.
|
|
144
|
+
# Most modification methods reset #string to its #normalized form, so that
|
|
145
|
+
# #entries and #elements are identical. Use #append to preserve #entries
|
|
146
|
+
# order while modifying a set.
|
|
71
147
|
#
|
|
72
|
-
#
|
|
73
|
-
#
|
|
74
|
-
# #
|
|
148
|
+
# Non-normalized sets store both representations of the set, which can more
|
|
149
|
+
# than double memory usage. Very large sequence sets should avoid
|
|
150
|
+
# denormalizing methods (such as #append) unless order is significant.
|
|
75
151
|
#
|
|
76
152
|
# == Using <tt>*</tt>
|
|
77
153
|
#
|
|
@@ -107,12 +183,16 @@ module Net
|
|
|
107
183
|
#
|
|
108
184
|
# When a set includes <tt>*</tt>, some methods may have surprising behavior.
|
|
109
185
|
#
|
|
110
|
-
# For example, #complement treats <tt>*</tt> as its own
|
|
111
|
-
# the #intersection of a set and its #complement will always be empty.
|
|
112
|
-
#
|
|
113
|
-
#
|
|
114
|
-
#
|
|
115
|
-
#
|
|
186
|
+
# For example, #complement treats <tt>*</tt> as its own member. This way,
|
|
187
|
+
# the #intersection of a set and its #complement will always be empty. And
|
|
188
|
+
# <tt>*</tt> is sorted as greater than any other number in the set. This is
|
|
189
|
+
# not how an \IMAP server interprets the set: it will convert <tt>*</tt> to
|
|
190
|
+
# the number of messages in the mailbox, the +UID+ of the last message in
|
|
191
|
+
# the mailbox, or +UIDNEXT+, as appropriate. Several methods have an
|
|
192
|
+
# argument for how <tt>*</tt> should be interpreted.
|
|
193
|
+
#
|
|
194
|
+
# But, for example, this means that there may be overlap between a set and
|
|
195
|
+
# its complement after #limit is applied to each:
|
|
116
196
|
#
|
|
117
197
|
# ~Net::IMAP::SequenceSet["*"] == Net::IMAP::SequenceSet[1..(2**32-1)]
|
|
118
198
|
# ~Net::IMAP::SequenceSet[1..5] == Net::IMAP::SequenceSet["6:*"]
|
|
@@ -123,7 +203,7 @@ module Net
|
|
|
123
203
|
# (set.limit(max: 4) & (~set).limit(max: 4)).to_a => [4]
|
|
124
204
|
#
|
|
125
205
|
# When counting the number of numbers in a set, <tt>*</tt> will be counted
|
|
126
|
-
#
|
|
206
|
+
# as if it were equal to UINT32_MAX:
|
|
127
207
|
# UINT32_MAX = 2**32 - 1
|
|
128
208
|
# Net::IMAP::SequenceSet["*"].count => 1
|
|
129
209
|
# Net::IMAP::SequenceSet[1..UINT32_MAX - 1, :*].count => UINT32_MAX
|
|
@@ -132,6 +212,12 @@ module Net
|
|
|
132
212
|
# Net::IMAP::SequenceSet[UINT32_MAX, :*].count => 1
|
|
133
213
|
# Net::IMAP::SequenceSet[UINT32_MAX..].count => 1
|
|
134
214
|
#
|
|
215
|
+
# Use #cardinality to count the set members wxth <tt>*</tt> counted as a
|
|
216
|
+
# distinct member:
|
|
217
|
+
# Net::IMAP::SequenceSet[1..].cardinality #=> UINT32_MAX + 1
|
|
218
|
+
# Net::IMAP::SequenceSet[UINT32_MAX, :*].cardinality #=> 2
|
|
219
|
+
# Net::IMAP::SequenceSet[UINT32_MAX..].cardinality #=> 2
|
|
220
|
+
#
|
|
135
221
|
# == What's here?
|
|
136
222
|
#
|
|
137
223
|
# SequenceSet provides methods for:
|
|
@@ -149,6 +235,7 @@ module Net
|
|
|
149
235
|
# * ::new: Creates a new mutable sequence set, which may be empty (invalid).
|
|
150
236
|
# * ::try_convert: Calls +to_sequence_set+ on an object and verifies that
|
|
151
237
|
# the result is a SequenceSet.
|
|
238
|
+
# * Net::IMAP::SequenceSet(): Coerce an input using ::try_convert or ::new.
|
|
152
239
|
# * ::empty: Returns a frozen empty (invalid) SequenceSet.
|
|
153
240
|
# * ::full: Returns a frozen SequenceSet containing every possible number.
|
|
154
241
|
#
|
|
@@ -174,14 +261,13 @@ module Net
|
|
|
174
261
|
#
|
|
175
262
|
# <i>Set membership:</i>
|
|
176
263
|
# - #include? (aliased as #member?):
|
|
177
|
-
# Returns whether a given
|
|
178
|
-
# contained by the set.
|
|
264
|
+
# Returns whether a given element is contained by the set.
|
|
179
265
|
# - #include_star?: Returns whether the set contains <tt>*</tt>.
|
|
180
266
|
#
|
|
181
267
|
# <i>Minimum and maximum value elements:</i>
|
|
182
|
-
# - #min: Returns the
|
|
183
|
-
# - #max: Returns the
|
|
184
|
-
# - #minmax: Returns the
|
|
268
|
+
# - #min: Returns one or more of the lowest numbers in the set.
|
|
269
|
+
# - #max: Returns one or more of the highest numbers in the set.
|
|
270
|
+
# - #minmax: Returns the lowest and highest numbers in the set.
|
|
185
271
|
#
|
|
186
272
|
# <i>Accessing value by offset in sorted set:</i>
|
|
187
273
|
# - #[] (aliased as #slice): Returns the number or consecutive subset at a
|
|
@@ -195,8 +281,10 @@ module Net
|
|
|
195
281
|
# occurrence in entries.
|
|
196
282
|
#
|
|
197
283
|
# <i>Set cardinality:</i>
|
|
198
|
-
# - #
|
|
199
|
-
#
|
|
284
|
+
# - #cardinality: Returns the number of distinct members in the set.
|
|
285
|
+
# <tt>*</tt> is counted as its own member, distinct from UINT32_MAX.
|
|
286
|
+
# - #count: Returns the count of distinct numbers in the set.
|
|
287
|
+
# <tt>*</tt> is counted as equal to UINT32_MAX.
|
|
200
288
|
# - #empty?: Returns whether the set has no members. \IMAP syntax does not
|
|
201
289
|
# allow empty sequence sets.
|
|
202
290
|
# - #valid?: Returns whether the set has any members.
|
|
@@ -204,12 +292,18 @@ module Net
|
|
|
204
292
|
# <tt>*</tt>.
|
|
205
293
|
#
|
|
206
294
|
# <i>Denormalized properties:</i>
|
|
295
|
+
# - #normalized?: Returns whether #entries are sorted, deduplicated, and
|
|
296
|
+
# coalesced, and all #string entries are in normalized form.
|
|
207
297
|
# - #has_duplicates?: Returns whether the ordered entries repeat any
|
|
208
298
|
# numbers.
|
|
209
|
-
# - #
|
|
210
|
-
#
|
|
299
|
+
# - #size: Returns the total size of all #entries, including repeated
|
|
300
|
+
# numbers. <tt>*</tt> is counted as its own member, distinct from
|
|
301
|
+
# UINT32_MAX.
|
|
211
302
|
# - #count_with_duplicates: Returns the count of numbers in the ordered
|
|
212
|
-
# entries, including
|
|
303
|
+
# #entries, including repeated numbers. <tt>*</tt> is counted as
|
|
304
|
+
# equal to UINT32_MAX.
|
|
305
|
+
# - #count_duplicates: Returns the count of repeated numbers in the ordered
|
|
306
|
+
# #entries. <tt>*</tt> is counted as equal to UINT32_MAX.
|
|
213
307
|
#
|
|
214
308
|
# === Methods for Iterating
|
|
215
309
|
#
|
|
@@ -239,53 +333,64 @@ module Net
|
|
|
239
333
|
# These methods do not modify +self+.
|
|
240
334
|
#
|
|
241
335
|
# - #| (aliased as #union and #+): Returns a new set combining all members
|
|
242
|
-
# from +self+ with all members from the other
|
|
336
|
+
# from +self+ with all members from the other set.
|
|
243
337
|
# - #& (aliased as #intersection): Returns a new set containing all members
|
|
244
|
-
# common to +self+ and the other
|
|
338
|
+
# common to +self+ and the other set.
|
|
245
339
|
# - #- (aliased as #difference): Returns a copy of +self+ with all members
|
|
246
|
-
# in the other
|
|
340
|
+
# in the other set removed.
|
|
247
341
|
# - #^ (aliased as #xor): Returns a new set containing all members from
|
|
248
|
-
# +self+ and the other
|
|
342
|
+
# +self+ and the other set except those common to both.
|
|
249
343
|
# - #~ (aliased as #complement): Returns a new set containing all members
|
|
250
344
|
# that are not in +self+
|
|
345
|
+
# - #above: Return a copy of +self+ which only contains numbers above a
|
|
346
|
+
# given number.
|
|
347
|
+
# - #below: Return a copy of +self+ which only contains numbers below a
|
|
348
|
+
# given value.
|
|
251
349
|
# - #limit: Returns a copy of +self+ which has replaced <tt>*</tt> with a
|
|
252
350
|
# given maximum value and removed all members over that maximum.
|
|
253
351
|
#
|
|
254
352
|
# === Methods for Assigning
|
|
255
|
-
# These methods add or replace
|
|
353
|
+
# These methods add or replace numbers in +self+.
|
|
256
354
|
#
|
|
257
355
|
# <i>Normalized (sorted and coalesced):</i>
|
|
258
356
|
#
|
|
259
357
|
# These methods always update #string to be fully sorted and coalesced.
|
|
260
358
|
#
|
|
261
|
-
# - #add (aliased as #<<): Adds a given
|
|
262
|
-
# - #add?: If the given
|
|
359
|
+
# - #add (aliased as #<<): Adds a given element to the set; returns +self+.
|
|
360
|
+
# - #add?: If the given element is not fully included the set, adds it and
|
|
263
361
|
# returns +self+; otherwise, returns +nil+.
|
|
264
|
-
# - #merge:
|
|
265
|
-
#
|
|
362
|
+
# - #merge: In-place set #union. Adds all members of the given sets into
|
|
363
|
+
# this set; returns +self+.
|
|
364
|
+
# - #complement!: In-place set #complement. Replaces the contents of this
|
|
365
|
+
# set with its own #complement; returns +self+.
|
|
366
|
+
# - #xor!: In-place +XOR+ operation. Adds numbers that are unique to the
|
|
367
|
+
# other set and removes numbers that are common to both; returns +self+.
|
|
266
368
|
#
|
|
267
369
|
# <i>Order preserving:</i>
|
|
268
370
|
#
|
|
269
371
|
# These methods _may_ cause #string to not be sorted or coalesced.
|
|
270
372
|
#
|
|
271
|
-
# - #append: Adds
|
|
373
|
+
# - #append: Adds the given entry to the set, appending it to the existing
|
|
272
374
|
# string, and returns +self+.
|
|
273
375
|
# - #string=: Assigns a new #string value and replaces #elements to match.
|
|
274
376
|
# - #replace: Replaces the contents of the set with the contents
|
|
275
377
|
# of a given object.
|
|
276
378
|
#
|
|
277
379
|
# === Methods for Deleting
|
|
278
|
-
# These methods remove
|
|
380
|
+
# These methods remove numbers from +self+, and update #string to be fully
|
|
279
381
|
# sorted and coalesced.
|
|
280
382
|
#
|
|
281
383
|
# - #clear: Removes all elements in the set; returns +self+.
|
|
282
|
-
# - #delete: Removes a given
|
|
283
|
-
# - #delete?: If the given
|
|
384
|
+
# - #delete: Removes a given element from the set; returns +self+.
|
|
385
|
+
# - #delete?: If the given element is included in the set, removes it and
|
|
284
386
|
# returns it; otherwise, returns +nil+.
|
|
285
387
|
# - #delete_at: Removes the number at a given offset.
|
|
388
|
+
# - #intersect!: In-place set #intersection. Removes numbers that are not
|
|
389
|
+
# in the given set; returns +self+.
|
|
286
390
|
# - #slice!: Removes the number or consecutive numbers at a given offset or
|
|
287
391
|
# range of offsets.
|
|
288
|
-
# - #subtract: Removes
|
|
392
|
+
# - #subtract: In-place set #difference. Removes all members of the given
|
|
393
|
+
# sets from this set; returns +self+.
|
|
289
394
|
# - #limit!: Replaces <tt>*</tt> with a given maximum value and removes all
|
|
290
395
|
# members over that maximum; returns +self+.
|
|
291
396
|
#
|
|
@@ -315,23 +420,42 @@ module Net
|
|
|
315
420
|
STARS = [:*, ?*, -1].freeze
|
|
316
421
|
private_constant :STARS
|
|
317
422
|
|
|
423
|
+
INSPECT_MAX_LEN = 512
|
|
424
|
+
INSPECT_TRUNCATE_LEN = 16
|
|
425
|
+
private_constant :INSPECT_MAX_LEN, :INSPECT_TRUNCATE_LEN
|
|
426
|
+
|
|
427
|
+
# /(,\d+){100}\z/ is shockingly slow on huge strings.
|
|
428
|
+
# /(,\d{0,10}){100}\z/ is ok, but ironically, Regexp.linear_time? is false.
|
|
429
|
+
#
|
|
430
|
+
# This unrolls all nested quantifiers. It's much harder to read, but it's
|
|
431
|
+
# also the fastest out of all the versions I tested.
|
|
432
|
+
nz_uint32 = /[1-9](?:\d(?:\d(?:\d(?:\d(?:\d(?:\d(?:\d(?:\d(?:\d)?)?)?)?)?)?)?)?)?/
|
|
433
|
+
num_or_star = /#{nz_uint32}|\*/
|
|
434
|
+
entry = /#{num_or_star}(?::#{num_or_star})?/
|
|
435
|
+
entries = ([entry] * INSPECT_TRUNCATE_LEN).join(",")
|
|
436
|
+
INSPECT_ABRIDGED_HEAD_RE = /\A#{entries},/
|
|
437
|
+
INSPECT_ABRIDGED_TAIL_RE = /,#{entries}\z/
|
|
438
|
+
private_constant :INSPECT_ABRIDGED_HEAD_RE, :INSPECT_ABRIDGED_TAIL_RE
|
|
439
|
+
|
|
318
440
|
class << self
|
|
319
441
|
|
|
320
442
|
# :call-seq:
|
|
321
|
-
# SequenceSet[*
|
|
443
|
+
# SequenceSet[*inputs] -> valid frozen sequence set
|
|
444
|
+
#
|
|
445
|
+
# Returns a frozen SequenceSet, constructed from +inputs+.
|
|
322
446
|
#
|
|
323
|
-
#
|
|
447
|
+
# When only a single valid frozen SequenceSet is given, that same set is
|
|
448
|
+
# returned.
|
|
324
449
|
#
|
|
325
450
|
# An empty SequenceSet is invalid and will raise a DataFormatError.
|
|
326
451
|
#
|
|
327
452
|
# Use ::new to create a mutable or empty SequenceSet.
|
|
453
|
+
#
|
|
454
|
+
# Related: ::new, Net::IMAP::SequenceSet(), ::try_convert
|
|
328
455
|
def [](first, *rest)
|
|
329
456
|
if rest.empty?
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
else
|
|
333
|
-
new(first).validate.freeze
|
|
334
|
-
end
|
|
457
|
+
set = try_convert(first)&.validate
|
|
458
|
+
set&.frozen? ? set : (set&.dup || new(first).validate).freeze
|
|
335
459
|
else
|
|
336
460
|
new(first).merge(*rest).validate.freeze
|
|
337
461
|
end
|
|
@@ -344,12 +468,14 @@ module Net
|
|
|
344
468
|
# +to_sequence_set+, calls +obj.to_sequence_set+ and returns the result.
|
|
345
469
|
# Otherwise returns +nil+.
|
|
346
470
|
#
|
|
347
|
-
# If +obj.to_sequence_set+ doesn't return a SequenceSet
|
|
348
|
-
# raised.
|
|
471
|
+
# If +obj.to_sequence_set+ doesn't return a SequenceSet or +nil+, an
|
|
472
|
+
# exception is raised.
|
|
473
|
+
#
|
|
474
|
+
# Related: Net::IMAP::SequenceSet(), ::new, ::[]
|
|
349
475
|
def try_convert(obj)
|
|
350
476
|
return obj if obj.is_a?(SequenceSet)
|
|
351
477
|
return nil unless obj.respond_to?(:to_sequence_set)
|
|
352
|
-
obj = obj.to_sequence_set
|
|
478
|
+
return nil unless obj = obj.to_sequence_set
|
|
353
479
|
return obj if obj.is_a?(SequenceSet)
|
|
354
480
|
raise DataFormatError, "invalid object returned from to_sequence_set"
|
|
355
481
|
end
|
|
@@ -364,23 +490,96 @@ module Net
|
|
|
364
490
|
end
|
|
365
491
|
|
|
366
492
|
# Create a new SequenceSet object from +input+, which may be another
|
|
367
|
-
# SequenceSet, an IMAP formatted +sequence-set+ string, a
|
|
368
|
-
# range, <tt>:*</tt>,
|
|
369
|
-
#
|
|
370
|
-
#
|
|
371
|
-
|
|
493
|
+
# SequenceSet, an IMAP formatted +sequence-set+ string, a non-zero 32 bit
|
|
494
|
+
# unsigned integer, a range, <tt>:*</tt>, a Set of numbers or <tt>*</tt>,
|
|
495
|
+
# an object that responds to +to_sequence_set+ (such as SearchResult) or
|
|
496
|
+
# an Array of these (array inputs may be nested).
|
|
497
|
+
#
|
|
498
|
+
# set = Net::IMAP::SequenceSet.new(1)
|
|
499
|
+
# set.valid_string #=> "1"
|
|
500
|
+
# set = Net::IMAP::SequenceSet.new(1..100)
|
|
501
|
+
# set.valid_string #=> "1:100"
|
|
502
|
+
# set = Net::IMAP::SequenceSet.new(1...100)
|
|
503
|
+
# set.valid_string #=> "1:99"
|
|
504
|
+
# set = Net::IMAP::SequenceSet.new([1, 2, 5..])
|
|
505
|
+
# set.valid_string #=> "1:2,5:*"
|
|
506
|
+
# set = Net::IMAP::SequenceSet.new("1,2,3:7,5,6:10,2048,1024")
|
|
507
|
+
# set.valid_string #=> "1,2,3:7,5,6:10,2048,1024"
|
|
508
|
+
# set = Net::IMAP::SequenceSet.new(1, 2, 3..7, 5, 6..10, 2048, 1024)
|
|
509
|
+
# set.valid_string #=> "1:10,1024,2048"
|
|
510
|
+
#
|
|
511
|
+
# With no arguments (or +nil+) creates an empty sequence set. Note that
|
|
512
|
+
# an empty sequence set is invalid in the \IMAP grammar.
|
|
513
|
+
#
|
|
514
|
+
# set = Net::IMAP::SequenceSet.new
|
|
515
|
+
# set.empty? #=> true
|
|
516
|
+
# set.valid? #=> false
|
|
517
|
+
# set.valid_string #!> raises DataFormatError
|
|
518
|
+
# set << 1..10
|
|
519
|
+
# set.empty? #=> false
|
|
520
|
+
# set.valid? #=> true
|
|
521
|
+
# set.valid_string #=> "1:10"
|
|
522
|
+
#
|
|
523
|
+
# When +input+ is a SequenceSet, ::new behaves the same as calling #dup on
|
|
524
|
+
# that other set. The input's #string will be preserved.
|
|
525
|
+
#
|
|
526
|
+
# input = Net::IMAP::SequenceSet.new("1,2,3:7,5,6:10,2048,1024")
|
|
527
|
+
# copy = Net::IMAP::SequenceSet.new(input)
|
|
528
|
+
# input.valid_string #=> "1,2,3:7,5,6:10,2048,1024"
|
|
529
|
+
# copy.valid_string #=> "1,2,3:7,5,6:10,2048,1024"
|
|
530
|
+
# copy2 = input.dup # same as calling new with a SequenceSet input
|
|
531
|
+
# copy == input #=> true, same set membership
|
|
532
|
+
# copy.eql? input #=> true, same string value
|
|
533
|
+
# copy.equal? input #=> false, different objects
|
|
534
|
+
#
|
|
535
|
+
# copy.normalize!
|
|
536
|
+
# copy.valid_string #=> "1:10,1024,2048"
|
|
537
|
+
# copy == input #=> true, same set membership
|
|
538
|
+
# copy.eql? input #=> false, different string value
|
|
539
|
+
#
|
|
540
|
+
# copy << 999
|
|
541
|
+
# copy.valid_string #=> "1:10,999,1024,2048"
|
|
542
|
+
# copy == input #=> false, different set membership
|
|
543
|
+
# copy.eql? input #=> false, different string value
|
|
544
|
+
#
|
|
545
|
+
# === Alternative set creation methods
|
|
546
|
+
#
|
|
547
|
+
# * ::[] returns a frozen validated (non-empty) SequenceSet, without
|
|
548
|
+
# allocating a new object when the input is already a valid frozen
|
|
549
|
+
# SequenceSet.
|
|
550
|
+
# * Net::IMAP::SequenceSet() coerces an input to SequenceSet, without
|
|
551
|
+
# allocating a new object when the input is already a SequenceSet.
|
|
552
|
+
# * ::try_convert calls +to_sequence_set+ on inputs that support it and
|
|
553
|
+
# returns +nil+ for inputs that don't.
|
|
554
|
+
# * ::empty and ::full both return frozen singleton sets which can be
|
|
555
|
+
# combined with set operations (#|, #&, #^, #-, etc) to make new sets.
|
|
556
|
+
#
|
|
557
|
+
# See SequenceSet@Creating+sequence+sets.
|
|
558
|
+
def initialize(input = nil)
|
|
559
|
+
@set_data = new_set_data
|
|
560
|
+
@string = nil
|
|
561
|
+
replace(input) unless input.nil?
|
|
562
|
+
end
|
|
372
563
|
|
|
373
564
|
# Removes all elements and returns self.
|
|
374
|
-
def clear
|
|
565
|
+
def clear
|
|
566
|
+
modifying! # redundant check (normalizes the error message for JRuby)
|
|
567
|
+
set_data.clear
|
|
568
|
+
@string = nil
|
|
569
|
+
self
|
|
570
|
+
end
|
|
375
571
|
|
|
376
572
|
# Replace the contents of the set with the contents of +other+ and returns
|
|
377
573
|
# +self+.
|
|
378
574
|
#
|
|
379
|
-
# +other+ may be another SequenceSet
|
|
380
|
-
#
|
|
575
|
+
# +other+ may be another SequenceSet or any other object that would be
|
|
576
|
+
# accepted by ::new.
|
|
381
577
|
def replace(other)
|
|
382
578
|
case other
|
|
383
|
-
when SequenceSet then
|
|
579
|
+
when SequenceSet then
|
|
580
|
+
modifying! # short circuit before doing any work
|
|
581
|
+
@set_data = other.dup_set_data
|
|
582
|
+
@string = other.instance_variable_get(:@string)
|
|
384
583
|
when String then self.string = other
|
|
385
584
|
else clear; merge other
|
|
386
585
|
end
|
|
@@ -409,43 +608,51 @@ module Net
|
|
|
409
608
|
# If the set was created from a single string, it is not normalized. If
|
|
410
609
|
# the set is updated the string will be normalized.
|
|
411
610
|
#
|
|
412
|
-
# Related: #valid_string, #normalized_string, #to_s
|
|
413
|
-
def string; @string
|
|
611
|
+
# Related: #valid_string, #normalized_string, #to_s, #inspect
|
|
612
|
+
def string; @string || normalized_string if valid? end
|
|
414
613
|
|
|
415
614
|
# Returns an array with #normalized_string when valid and an empty array
|
|
416
615
|
# otherwise.
|
|
417
616
|
def deconstruct; valid? ? [normalized_string] : [] end
|
|
418
617
|
|
|
419
|
-
# Assigns a new string to #string and resets #elements to match.
|
|
420
|
-
#
|
|
421
|
-
#
|
|
618
|
+
# Assigns a new string to #string and resets #elements to match.
|
|
619
|
+
# Assigning +nil+ or an empty string are equivalent to calling #clear.
|
|
620
|
+
#
|
|
621
|
+
# Non-empty strings are validated but not normalized.
|
|
422
622
|
#
|
|
423
|
-
# Use #add or #
|
|
623
|
+
# Use #add, #merge, or #append to add a string to an existing set.
|
|
424
624
|
#
|
|
425
625
|
# Related: #replace, #clear
|
|
426
|
-
def string=(
|
|
427
|
-
if
|
|
626
|
+
def string=(input)
|
|
627
|
+
if input.nil?
|
|
428
628
|
clear
|
|
629
|
+
elsif (str = String.try_convert(input))
|
|
630
|
+
modifying! # short-circuit before parsing the string
|
|
631
|
+
entries = each_parsed_entry(str).to_a
|
|
632
|
+
clear
|
|
633
|
+
if normalized_entries?(entries)
|
|
634
|
+
replace_minmaxes entries.map!(&:minmax)
|
|
635
|
+
else
|
|
636
|
+
add_minmaxes entries.map!(&:minmax)
|
|
637
|
+
@string = -str
|
|
638
|
+
end
|
|
429
639
|
else
|
|
430
|
-
|
|
431
|
-
tuples = str_to_tuples str
|
|
432
|
-
@tuples, @string = [], -str
|
|
433
|
-
tuples_add tuples
|
|
640
|
+
raise ArgumentError, "expected a string or nil, got #{input.class}"
|
|
434
641
|
end
|
|
642
|
+
input
|
|
435
643
|
end
|
|
436
644
|
|
|
437
645
|
# Returns the \IMAP +sequence-set+ string representation, or an empty
|
|
438
646
|
# string when the set is empty. Note that an empty set is invalid in the
|
|
439
647
|
# \IMAP syntax.
|
|
440
648
|
#
|
|
441
|
-
# Related: #valid_string, #normalized_string, #
|
|
649
|
+
# Related: #string, #valid_string, #normalized_string, #inspect
|
|
442
650
|
def to_s; string || "" end
|
|
443
651
|
|
|
444
652
|
# Freezes and returns the set. A frozen SequenceSet is Ractor-safe.
|
|
445
653
|
def freeze
|
|
446
654
|
return self if frozen?
|
|
447
|
-
|
|
448
|
-
@tuples.each(&:freeze).freeze
|
|
655
|
+
freeze_set_data
|
|
449
656
|
super
|
|
450
657
|
end
|
|
451
658
|
|
|
@@ -467,7 +674,7 @@ module Net
|
|
|
467
674
|
# Related: #eql?, #normalize
|
|
468
675
|
def ==(other)
|
|
469
676
|
self.class == other.class &&
|
|
470
|
-
(to_s == other.to_s ||
|
|
677
|
+
(to_s == other.to_s || set_data == other.set_data)
|
|
471
678
|
end
|
|
472
679
|
|
|
473
680
|
# :call-seq: eql?(other) -> true or false
|
|
@@ -491,8 +698,9 @@ module Net
|
|
|
491
698
|
|
|
492
699
|
# :call-seq: self === other -> true | false | nil
|
|
493
700
|
#
|
|
494
|
-
# Returns whether +other+ is contained within the set.
|
|
495
|
-
#
|
|
701
|
+
# Returns whether +other+ is contained within the set. +other+ may be any
|
|
702
|
+
# object that would be accepted by ::new. Returns +nil+ if StandardError
|
|
703
|
+
# is raised while converting +other+ to a comparable type.
|
|
496
704
|
#
|
|
497
705
|
# Related: #cover?, #include?, #include_star?
|
|
498
706
|
def ===(other)
|
|
@@ -506,12 +714,12 @@ module Net
|
|
|
506
714
|
# Returns whether +other+ is contained within the set. +other+ may be any
|
|
507
715
|
# object that would be accepted by ::new.
|
|
508
716
|
#
|
|
509
|
-
# Related: #===, #include?, #include_star?
|
|
510
|
-
def cover?(other)
|
|
717
|
+
# Related: #===, #include?, #include_star?, #intersect?
|
|
718
|
+
def cover?(other) import_runs(other).none? { !include_run?(_1) } end
|
|
511
719
|
|
|
512
720
|
# Returns +true+ when a given number or range is in +self+, and +false+
|
|
513
|
-
# otherwise. Returns +
|
|
514
|
-
# <tt>*</tt
|
|
721
|
+
# otherwise. Returns +nil+ when +number+ isn't a valid SequenceSet
|
|
722
|
+
# element (Integer, Range, <tt>*</tt>, +sequence-set+ string).
|
|
515
723
|
#
|
|
516
724
|
# set = Net::IMAP::SequenceSet["5:10,100,111:115"]
|
|
517
725
|
# set.include? 1 #=> false
|
|
@@ -519,8 +727,8 @@ module Net
|
|
|
519
727
|
# set.include? 11..20 #=> false
|
|
520
728
|
# set.include? 100 #=> true
|
|
521
729
|
# set.include? 6 #=> true, covered by "5:10"
|
|
522
|
-
# set.include?
|
|
523
|
-
# set.include? "
|
|
730
|
+
# set.include? 6..9 #=> true, covered by "5:10"
|
|
731
|
+
# set.include? "6:9" #=> true, strings are parsed
|
|
524
732
|
# set.include? 4..9 #=> false, intersection is not sufficient
|
|
525
733
|
# set.include? "*" #=> false, use #limit to re-interpret "*"
|
|
526
734
|
# set.include? -1 #=> false, -1 is interpreted as "*"
|
|
@@ -529,16 +737,19 @@ module Net
|
|
|
529
737
|
# set.include? :* #=> true
|
|
530
738
|
# set.include? "*" #=> true
|
|
531
739
|
# set.include? -1 #=> true
|
|
532
|
-
# set.include?
|
|
533
|
-
# set.include?
|
|
740
|
+
# set.include?(200..) #=> true
|
|
741
|
+
# set.include?(100..) #=> false
|
|
534
742
|
#
|
|
535
|
-
# Related: #include_star?, #cover?,
|
|
536
|
-
def include?(element)
|
|
743
|
+
# Related: #include_star?, #cover?, #===, #intersect?
|
|
744
|
+
def include?(element)
|
|
745
|
+
run = import_run element rescue nil
|
|
746
|
+
!!include_run?(run) if run
|
|
747
|
+
end
|
|
537
748
|
|
|
538
749
|
alias member? include?
|
|
539
750
|
|
|
540
751
|
# Returns +true+ when the set contains <tt>*</tt>.
|
|
541
|
-
def include_star?;
|
|
752
|
+
def include_star?; max_num == STAR_INT end
|
|
542
753
|
|
|
543
754
|
# Returns +true+ if the set and a given object have any common elements,
|
|
544
755
|
# +false+ otherwise.
|
|
@@ -546,9 +757,9 @@ module Net
|
|
|
546
757
|
# Net::IMAP::SequenceSet["5:10"].intersect? "7,9,11" #=> true
|
|
547
758
|
# Net::IMAP::SequenceSet["5:10"].intersect? "11:33" #=> false
|
|
548
759
|
#
|
|
549
|
-
# Related: #intersection, #disjoint?
|
|
760
|
+
# Related: #intersection, #disjoint?, #cover?, #include?
|
|
550
761
|
def intersect?(other)
|
|
551
|
-
valid? &&
|
|
762
|
+
valid? && import_runs(other).any? { intersect_run? _1 }
|
|
552
763
|
end
|
|
553
764
|
alias overlap? intersect?
|
|
554
765
|
|
|
@@ -560,39 +771,66 @@ module Net
|
|
|
560
771
|
#
|
|
561
772
|
# Related: #intersection, #intersect?
|
|
562
773
|
def disjoint?(other)
|
|
563
|
-
empty? ||
|
|
774
|
+
empty? || import_runs(other).none? { intersect_run? _1 }
|
|
564
775
|
end
|
|
565
776
|
|
|
566
|
-
# :call-seq:
|
|
777
|
+
# :call-seq:
|
|
778
|
+
# max(star: :*) => integer or star or nil
|
|
779
|
+
# max(count) => SequenceSet
|
|
567
780
|
#
|
|
568
781
|
# Returns the maximum value in +self+, +star+ when the set includes
|
|
569
782
|
# <tt>*</tt>, or +nil+ when the set is empty.
|
|
570
|
-
|
|
571
|
-
|
|
783
|
+
#
|
|
784
|
+
# When +count+ is given, a new SequenceSet is returned, containing only
|
|
785
|
+
# the last +count+ numbers. An empty SequenceSet is returned when +self+
|
|
786
|
+
# is empty. (+star+ is ignored when +count+ is given.)
|
|
787
|
+
#
|
|
788
|
+
# Related: #min, #minmax, #slice
|
|
789
|
+
def max(count = nil, star: :*)
|
|
790
|
+
if count
|
|
791
|
+
slice(-[count, size].min..) || remain_frozen_empty
|
|
792
|
+
elsif (val = max_num)
|
|
793
|
+
val == STAR_INT ? star : val
|
|
794
|
+
end
|
|
572
795
|
end
|
|
573
796
|
|
|
574
|
-
# :call-seq:
|
|
797
|
+
# :call-seq:
|
|
798
|
+
# min(star: :*) => integer or star or nil
|
|
799
|
+
# min(count) => SequenceSet
|
|
575
800
|
#
|
|
576
801
|
# Returns the minimum value in +self+, +star+ when the only value in the
|
|
577
802
|
# set is <tt>*</tt>, or +nil+ when the set is empty.
|
|
578
|
-
|
|
579
|
-
|
|
803
|
+
#
|
|
804
|
+
# When +count+ is given, a new SequenceSet is returned, containing only
|
|
805
|
+
# the first +count+ numbers. An empty SequenceSet is returned when +self+
|
|
806
|
+
# is empty. (+star+ is ignored when +count+ is given.)
|
|
807
|
+
#
|
|
808
|
+
# Related: #max, #minmax, #slice
|
|
809
|
+
def min(count = nil, star: :*)
|
|
810
|
+
if count
|
|
811
|
+
slice(0...count) || remain_frozen_empty
|
|
812
|
+
elsif (val = min_num)
|
|
813
|
+
val != STAR_INT ? val : star
|
|
814
|
+
end
|
|
580
815
|
end
|
|
581
816
|
|
|
582
|
-
# :call-seq: minmax(star: :*) =>
|
|
817
|
+
# :call-seq: minmax(star: :*) => [min, max] or nil
|
|
583
818
|
#
|
|
584
819
|
# Returns a 2-element array containing the minimum and maximum numbers in
|
|
585
|
-
# +self+, or +nil+ when the set is empty.
|
|
820
|
+
# +self+, or +nil+ when the set is empty. +star+ is handled the same way
|
|
821
|
+
# as by #min and #max.
|
|
822
|
+
#
|
|
823
|
+
# Related: #min, #max
|
|
586
824
|
def minmax(star: :*); [min(star: star), max(star: star)] unless empty? end
|
|
587
825
|
|
|
588
826
|
# Returns false when the set is empty.
|
|
589
827
|
def valid?; !empty? end
|
|
590
828
|
|
|
591
829
|
# Returns true if the set contains no elements
|
|
592
|
-
def empty?;
|
|
830
|
+
def empty?; runs.empty? end
|
|
593
831
|
|
|
594
832
|
# Returns true if the set contains every possible element.
|
|
595
|
-
def full?;
|
|
833
|
+
def full?; set_data == FULL_SET_DATA end
|
|
596
834
|
|
|
597
835
|
# :call-seq:
|
|
598
836
|
# self + other -> sequence set
|
|
@@ -602,14 +840,19 @@ module Net
|
|
|
602
840
|
# Returns a new sequence set that has every number in the +other+ object
|
|
603
841
|
# added.
|
|
604
842
|
#
|
|
605
|
-
# +other+ may be any object that would be accepted by ::new
|
|
606
|
-
# bit unsigned integer, range, <tt>sequence-set</tt> formatted string,
|
|
607
|
-
# another sequence set, or an enumerable containing any of these.
|
|
843
|
+
# +other+ may be any object that would be accepted by ::new.
|
|
608
844
|
#
|
|
609
845
|
# Net::IMAP::SequenceSet["1:5"] | 2 | [4..6, 99]
|
|
610
846
|
# #=> Net::IMAP::SequenceSet["1:6,99"]
|
|
611
847
|
#
|
|
612
|
-
# Related: #add, #merge
|
|
848
|
+
# Related: #add, #merge, #&, #-, #^, #~
|
|
849
|
+
#
|
|
850
|
+
# ==== Set identities
|
|
851
|
+
#
|
|
852
|
+
# <tt>lhs | rhs</tt> is equivalent to:
|
|
853
|
+
# * <tt>rhs | lhs</tt> (commutative)
|
|
854
|
+
# * <tt>~(~lhs & ~rhs)</tt> (De Morgan's Law)
|
|
855
|
+
# * <tt>(lhs & rhs) ^ (lhs ^ rhs)</tt>
|
|
613
856
|
def |(other) remain_frozen dup.merge other end
|
|
614
857
|
alias :+ :|
|
|
615
858
|
alias union :|
|
|
@@ -621,14 +864,22 @@ module Net
|
|
|
621
864
|
# Returns a new sequence set built by duplicating this set and removing
|
|
622
865
|
# every number that appears in +other+.
|
|
623
866
|
#
|
|
624
|
-
# +other+ may be any object that would be accepted by ::new
|
|
625
|
-
# bit unsigned integer, range, <tt>sequence-set</tt> formatted string,
|
|
626
|
-
# another sequence set, or an enumerable containing any of these.
|
|
867
|
+
# +other+ may be any object that would be accepted by ::new.
|
|
627
868
|
#
|
|
628
869
|
# Net::IMAP::SequenceSet[1..5] - 2 - 4 - 6
|
|
629
870
|
# #=> Net::IMAP::SequenceSet["1,3,5"]
|
|
630
871
|
#
|
|
631
|
-
# Related: #subtract
|
|
872
|
+
# Related: #subtract, #|, #&, #^, #~
|
|
873
|
+
#
|
|
874
|
+
# ==== Set identities
|
|
875
|
+
#
|
|
876
|
+
# <tt>lhs - rhs</tt> is equivalent to:
|
|
877
|
+
# * <tt>~rhs - ~lhs</tt>
|
|
878
|
+
# * <tt>lhs & ~rhs</tt>
|
|
879
|
+
# * <tt>~(~lhs | rhs)</tt>
|
|
880
|
+
# * <tt>lhs & (lhs ^ rhs)</tt>
|
|
881
|
+
# * <tt>lhs ^ (lhs & rhs)</tt>
|
|
882
|
+
# * <tt>rhs ^ (lhs | rhs)</tt>
|
|
632
883
|
def -(other) remain_frozen dup.subtract other end
|
|
633
884
|
alias difference :-
|
|
634
885
|
|
|
@@ -639,17 +890,23 @@ module Net
|
|
|
639
890
|
# Returns a new sequence set containing only the numbers common to this
|
|
640
891
|
# set and +other+.
|
|
641
892
|
#
|
|
642
|
-
# +other+ may be any object that would be accepted by ::new
|
|
643
|
-
# bit unsigned integer, range, <tt>sequence-set</tt> formatted string,
|
|
644
|
-
# another sequence set, or an enumerable containing any of these.
|
|
893
|
+
# +other+ may be any object that would be accepted by ::new.
|
|
645
894
|
#
|
|
646
895
|
# Net::IMAP::SequenceSet[1..5] & [2, 4, 6]
|
|
647
896
|
# #=> Net::IMAP::SequenceSet["2,4"]
|
|
648
897
|
#
|
|
649
|
-
#
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
898
|
+
# Related: #intersect?, #|, #-, #^, #~
|
|
899
|
+
#
|
|
900
|
+
# ==== Set identities
|
|
901
|
+
#
|
|
902
|
+
# <tt>lhs & rhs</tt> is equivalent to:
|
|
903
|
+
# * <tt>rhs & lhs</tt> (commutative)
|
|
904
|
+
# * <tt>~(~lhs | ~rhs)</tt> (De Morgan's Law)
|
|
905
|
+
# * <tt>lhs - ~rhs</tt>
|
|
906
|
+
# * <tt>lhs - (lhs - rhs)</tt>
|
|
907
|
+
# * <tt>lhs - (lhs ^ rhs)</tt>
|
|
908
|
+
# * <tt>lhs ^ (lhs - rhs)</tt>
|
|
909
|
+
def &(other) remain_frozen dup.intersect! other end
|
|
653
910
|
alias intersection :&
|
|
654
911
|
|
|
655
912
|
# :call-seq:
|
|
@@ -659,16 +916,22 @@ module Net
|
|
|
659
916
|
# Returns a new sequence set containing numbers that are exclusive between
|
|
660
917
|
# this set and +other+.
|
|
661
918
|
#
|
|
662
|
-
# +other+ may be any object that would be accepted by ::new
|
|
663
|
-
# bit unsigned integer, range, <tt>sequence-set</tt> formatted string,
|
|
664
|
-
# another sequence set, or an enumerable containing any of these.
|
|
919
|
+
# +other+ may be any object that would be accepted by ::new.
|
|
665
920
|
#
|
|
666
921
|
# Net::IMAP::SequenceSet[1..5] ^ [2, 4, 6]
|
|
667
922
|
# #=> Net::IMAP::SequenceSet["1,3,5:6"]
|
|
668
923
|
#
|
|
669
|
-
#
|
|
670
|
-
#
|
|
671
|
-
|
|
924
|
+
# Related: #|, #&, #-, #~
|
|
925
|
+
#
|
|
926
|
+
# ==== Set identities
|
|
927
|
+
#
|
|
928
|
+
# <tt>lhs ^ rhs</tt> is equivalent to:
|
|
929
|
+
# * <tt>rhs ^ lhs</tt> (commutative)
|
|
930
|
+
# * <tt>~lhs ^ ~rhs</tt>
|
|
931
|
+
# * <tt>(lhs | rhs) - (lhs & rhs)</tt>
|
|
932
|
+
# * <tt>(lhs - rhs) | (rhs - lhs)</tt>
|
|
933
|
+
# * <tt>(lhs ^ other) ^ (other ^ rhs)</tt>
|
|
934
|
+
def ^(other) remain_frozen dup.xor! other end
|
|
672
935
|
alias xor :^
|
|
673
936
|
|
|
674
937
|
# :call-seq:
|
|
@@ -685,21 +948,30 @@ module Net
|
|
|
685
948
|
# ~Net::IMAP::SequenceSet["6:99,223:*"]
|
|
686
949
|
# #=> Net::IMAP::SequenceSet["1:5,100:222"]
|
|
687
950
|
#
|
|
688
|
-
# Related: #complement
|
|
951
|
+
# Related: #complement!, #|, #&, #-, #^
|
|
952
|
+
#
|
|
953
|
+
# ==== Set identities
|
|
954
|
+
#
|
|
955
|
+
# <tt>~set</tt> is equivalent to:
|
|
956
|
+
# * <tt>full - set</tt>, where "full" is Net::IMAP::SequenceSet.full
|
|
689
957
|
def ~; remain_frozen dup.complement! end
|
|
690
958
|
alias complement :~
|
|
691
959
|
|
|
692
960
|
# :call-seq:
|
|
693
|
-
# add(
|
|
961
|
+
# add(element) -> self
|
|
694
962
|
# self << other -> self
|
|
695
963
|
#
|
|
696
964
|
# Adds a range or number to the set and returns +self+.
|
|
697
965
|
#
|
|
698
966
|
# #string will be regenerated. Use #merge to add many elements at once.
|
|
699
967
|
#
|
|
700
|
-
#
|
|
701
|
-
|
|
702
|
-
|
|
968
|
+
# Use #append to append new elements to #string. See
|
|
969
|
+
# SequenceSet@Ordered+and+Normalized+sets.
|
|
970
|
+
#
|
|
971
|
+
# Related: #add?, #merge, #union, #append
|
|
972
|
+
def add(element)
|
|
973
|
+
modifying! # short-circuit before import_run
|
|
974
|
+
add_run import_run element
|
|
703
975
|
normalize!
|
|
704
976
|
end
|
|
705
977
|
alias << add
|
|
@@ -708,29 +980,73 @@ module Net
|
|
|
708
980
|
#
|
|
709
981
|
# Unlike #add, #merge, or #union, the new value is appended to #string.
|
|
710
982
|
# This may result in a #string which has duplicates or is out-of-order.
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
983
|
+
#
|
|
984
|
+
# set = Net::IMAP::SequenceSet.new
|
|
985
|
+
# set.append(1..2) # => Net::IMAP::SequenceSet("1:2")
|
|
986
|
+
# set.append(5) # => Net::IMAP::SequenceSet("1:2,5")
|
|
987
|
+
# set.append(4) # => Net::IMAP::SequenceSet("1:2,5,4")
|
|
988
|
+
# set.append(3) # => Net::IMAP::SequenceSet("1:2,5,4,3")
|
|
989
|
+
# set.append(2) # => Net::IMAP::SequenceSet("1:2,5,4,3,2")
|
|
990
|
+
#
|
|
991
|
+
# If +entry+ is a string, it will be converted into normal form.
|
|
992
|
+
#
|
|
993
|
+
# set = Net::IMAP::SequenceSet("4:5,1:2")
|
|
994
|
+
# set.append("6:6") # => Net::IMAP::SequenceSet("4:5,1:2,6")
|
|
995
|
+
# set.append("9:8") # => Net::IMAP::SequenceSet("4:5,1:2,6,8:9")
|
|
996
|
+
#
|
|
997
|
+
# If +entry+ adjacently follows the last entry, they will coalesced:
|
|
998
|
+
# set = Net::IMAP::SequenceSet.new("2,1,9:10")
|
|
999
|
+
# set.append(11..12) # => Net::IMAP::SequenceSet("2,1,9:12")
|
|
1000
|
+
#
|
|
1001
|
+
# Non-normalized sets store the string <em>in addition to</em> an internal
|
|
1002
|
+
# normalized uint32 set representation. This can more than double memory
|
|
1003
|
+
# usage, so large sets should avoid using #append unless preserving order
|
|
1004
|
+
# is required. See SequenceSet@Ordered+and+Normalized+sets.
|
|
1005
|
+
#
|
|
1006
|
+
# Related: #add, #merge, #union
|
|
1007
|
+
def append(entry)
|
|
1008
|
+
modifying! # short-circuit before import_minmax
|
|
1009
|
+
minmax = import_minmax entry
|
|
1010
|
+
adj = minmax.first - 1
|
|
1011
|
+
if @string.nil? && (runs.empty? || max_num <= adj)
|
|
1012
|
+
# append to elements or coalesce with last element
|
|
1013
|
+
add_minmax minmax
|
|
1014
|
+
return self
|
|
1015
|
+
elsif @string.nil?
|
|
1016
|
+
# generate string for out-of-order append
|
|
1017
|
+
head, comma = normalized_string, ","
|
|
1018
|
+
else
|
|
1019
|
+
# @string already exists... maybe coalesce with last entry
|
|
1020
|
+
head, comma, last_entry = @string.rpartition(",")
|
|
1021
|
+
last_min, last_max = import_minmax last_entry
|
|
1022
|
+
if last_max == adj
|
|
1023
|
+
# coalesce with last entry
|
|
1024
|
+
minmax[0] = last_min
|
|
1025
|
+
else
|
|
1026
|
+
# append to existing string
|
|
1027
|
+
head, comma = @string, ","
|
|
1028
|
+
end
|
|
1029
|
+
end
|
|
1030
|
+
entry = export_minmax minmax
|
|
1031
|
+
add_minmax minmax
|
|
1032
|
+
@string = -"#{head}#{comma}#{entry}"
|
|
718
1033
|
self
|
|
719
1034
|
end
|
|
720
1035
|
|
|
721
|
-
# :call-seq: add?(
|
|
1036
|
+
# :call-seq: add?(element) -> self or nil
|
|
722
1037
|
#
|
|
723
1038
|
# Adds a range or number to the set and returns +self+. Returns +nil+
|
|
724
|
-
# when the
|
|
1039
|
+
# when the element is already included in the set.
|
|
725
1040
|
#
|
|
726
1041
|
# #string will be regenerated. Use #merge to add many elements at once.
|
|
727
1042
|
#
|
|
728
1043
|
# Related: #add, #merge, #union, #include?
|
|
729
|
-
def add?(
|
|
730
|
-
|
|
1044
|
+
def add?(element)
|
|
1045
|
+
modifying! # short-circuit before include?
|
|
1046
|
+
add element unless include? element
|
|
731
1047
|
end
|
|
732
1048
|
|
|
733
|
-
# :call-seq: delete(
|
|
1049
|
+
# :call-seq: delete(element) -> self
|
|
734
1050
|
#
|
|
735
1051
|
# Deletes the given range or number from the set and returns +self+.
|
|
736
1052
|
#
|
|
@@ -738,8 +1054,9 @@ module Net
|
|
|
738
1054
|
# many elements at once.
|
|
739
1055
|
#
|
|
740
1056
|
# Related: #delete?, #delete_at, #subtract, #difference
|
|
741
|
-
def delete(
|
|
742
|
-
|
|
1057
|
+
def delete(element)
|
|
1058
|
+
modifying! # short-circuit before import_run
|
|
1059
|
+
subtract_run import_run element
|
|
743
1060
|
normalize!
|
|
744
1061
|
end
|
|
745
1062
|
|
|
@@ -775,16 +1092,17 @@ module Net
|
|
|
775
1092
|
# #string will be regenerated after deletion.
|
|
776
1093
|
#
|
|
777
1094
|
# Related: #delete, #delete_at, #subtract, #difference, #disjoint?
|
|
778
|
-
def delete?(
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
1095
|
+
def delete?(element)
|
|
1096
|
+
modifying! # short-circuit before import_minmax
|
|
1097
|
+
minmax = import_minmax element
|
|
1098
|
+
if minmax.first == minmax.last
|
|
1099
|
+
return unless include_minmax? minmax
|
|
1100
|
+
subtract_minmax minmax
|
|
783
1101
|
normalize!
|
|
784
|
-
|
|
1102
|
+
export_num minmax.first
|
|
785
1103
|
else
|
|
786
1104
|
copy = dup
|
|
787
|
-
|
|
1105
|
+
subtract_minmax minmax
|
|
788
1106
|
normalize!
|
|
789
1107
|
copy if copy.subtract(self).valid?
|
|
790
1108
|
end
|
|
@@ -816,37 +1134,34 @@ module Net
|
|
|
816
1134
|
#
|
|
817
1135
|
# Related: #slice, #delete_at, #delete, #delete?, #subtract, #difference
|
|
818
1136
|
def slice!(index, length = nil)
|
|
1137
|
+
modifying! # short-circuit before slice
|
|
819
1138
|
deleted = slice(index, length) and subtract deleted
|
|
820
1139
|
deleted
|
|
821
1140
|
end
|
|
822
1141
|
|
|
823
|
-
# Merges all of the elements that appear in any of
|
|
824
|
-
# set, and returns +self+.
|
|
1142
|
+
# In-place set #union. Merges all of the elements that appear in any of
|
|
1143
|
+
# the +sets+ into this set, and returns +self+.
|
|
825
1144
|
#
|
|
826
|
-
# The +
|
|
827
|
-
# non-zero 32 bit unsigned integers, ranges, <tt>sequence-set</tt>
|
|
828
|
-
# formatted strings, other sequence sets, or enumerables containing any of
|
|
829
|
-
# these.
|
|
1145
|
+
# The +sets+ may be any objects that would be accepted by ::new.
|
|
830
1146
|
#
|
|
831
|
-
# #string will be regenerated after all
|
|
1147
|
+
# #string will be regenerated after all sets have been merged.
|
|
832
1148
|
#
|
|
833
1149
|
# Related: #add, #add?, #union
|
|
834
|
-
def merge(*
|
|
835
|
-
|
|
1150
|
+
def merge(*sets)
|
|
1151
|
+
modifying! # short-circuit before import_runs
|
|
1152
|
+
add_runs import_runs sets
|
|
836
1153
|
normalize!
|
|
837
1154
|
end
|
|
838
1155
|
|
|
839
|
-
# Removes all of the elements that appear in
|
|
840
|
-
#
|
|
1156
|
+
# In-place set #difference. Removes all of the elements that appear in
|
|
1157
|
+
# any of the given +sets+ from this set, and returns +self+.
|
|
841
1158
|
#
|
|
842
|
-
# The +
|
|
843
|
-
# non-zero 32 bit unsigned integers, ranges, <tt>sequence-set</tt>
|
|
844
|
-
# formatted strings, other sequence sets, or enumerables containing any of
|
|
845
|
-
# these.
|
|
1159
|
+
# The +sets+ may be any objects that would be accepted by ::new.
|
|
846
1160
|
#
|
|
847
1161
|
# Related: #difference
|
|
848
|
-
def subtract(*
|
|
849
|
-
|
|
1162
|
+
def subtract(*sets)
|
|
1163
|
+
modifying! # short-circuit before import_runs
|
|
1164
|
+
subtract_runs import_runs sets
|
|
850
1165
|
normalize!
|
|
851
1166
|
end
|
|
852
1167
|
|
|
@@ -858,21 +1173,21 @@ module Net
|
|
|
858
1173
|
# This is useful when the given order is significant, for example in a
|
|
859
1174
|
# ESEARCH response to IMAP#sort.
|
|
860
1175
|
#
|
|
1176
|
+
# See SequenceSet@Ordered+and+Normalized+sets.
|
|
1177
|
+
#
|
|
861
1178
|
# Related: #each_entry, #elements
|
|
862
1179
|
def entries; each_entry.to_a end
|
|
863
1180
|
|
|
864
1181
|
# Returns an array of ranges and integers and <tt>:*</tt>.
|
|
865
1182
|
#
|
|
866
1183
|
# The returned elements are sorted and coalesced, even when the input
|
|
867
|
-
# #string is not. <tt>*</tt> will sort last. See #normalize
|
|
1184
|
+
# #string is not. <tt>*</tt> will sort last. See #normalize,
|
|
1185
|
+
# SequenceSet@Ordered+and+Normalized+sets.
|
|
868
1186
|
#
|
|
869
1187
|
# By itself, <tt>*</tt> translates to <tt>:*</tt>. A range containing
|
|
870
1188
|
# <tt>*</tt> translates to an endless range. Use #limit to translate both
|
|
871
1189
|
# cases to a maximum value.
|
|
872
1190
|
#
|
|
873
|
-
# The returned elements will be sorted and coalesced, even when the input
|
|
874
|
-
# #string is not. <tt>*</tt> will sort last. See #normalize.
|
|
875
|
-
#
|
|
876
1191
|
# Net::IMAP::SequenceSet["2,5:9,6,*,12:11"].elements
|
|
877
1192
|
# #=> [2, 5..9, 11..12, :*]
|
|
878
1193
|
#
|
|
@@ -883,15 +1198,13 @@ module Net
|
|
|
883
1198
|
# Returns an array of ranges
|
|
884
1199
|
#
|
|
885
1200
|
# The returned elements are sorted and coalesced, even when the input
|
|
886
|
-
# #string is not. <tt>*</tt> will sort last. See #normalize
|
|
1201
|
+
# #string is not. <tt>*</tt> will sort last. See #normalize,
|
|
1202
|
+
# SequenceSet@Ordered+and+Normalized+sets.
|
|
887
1203
|
#
|
|
888
1204
|
# <tt>*</tt> translates to an endless range. By itself, <tt>*</tt>
|
|
889
1205
|
# translates to <tt>:*..</tt>. Use #limit to set <tt>*</tt> to a maximum
|
|
890
1206
|
# value.
|
|
891
1207
|
#
|
|
892
|
-
# The returned ranges will be sorted and coalesced, even when the input
|
|
893
|
-
# #string is not. <tt>*</tt> will sort last. See #normalize.
|
|
894
|
-
#
|
|
895
1208
|
# Net::IMAP::SequenceSet["2,5:9,6,*,12:11"].ranges
|
|
896
1209
|
# #=> [2..2, 5..9, 11..12, :*..]
|
|
897
1210
|
# Net::IMAP::SequenceSet["123,999:*,456:789"].ranges
|
|
@@ -903,7 +1216,7 @@ module Net
|
|
|
903
1216
|
# Returns a sorted array of all of the number values in the sequence set.
|
|
904
1217
|
#
|
|
905
1218
|
# The returned numbers are sorted and de-duplicated, even when the input
|
|
906
|
-
# #string is not. See #normalize.
|
|
1219
|
+
# #string is not. See #normalize, SequenceSet@Ordered+and+Normalized+sets.
|
|
907
1220
|
#
|
|
908
1221
|
# Net::IMAP::SequenceSet["2,5:9,6,12:11"].numbers
|
|
909
1222
|
# #=> [2, 5, 6, 7, 8, 9, 11, 12]
|
|
@@ -935,54 +1248,34 @@ module Net
|
|
|
935
1248
|
# no sorting, deduplication, or coalescing. When #string is in its
|
|
936
1249
|
# normalized form, this will yield the same values as #each_element.
|
|
937
1250
|
#
|
|
1251
|
+
# See SequenceSet@Ordered+and+Normalized+sets.
|
|
1252
|
+
#
|
|
938
1253
|
# Related: #entries, #each_element
|
|
939
1254
|
def each_entry(&block) # :yields: integer or range or :*
|
|
940
1255
|
return to_enum(__method__) unless block_given?
|
|
941
|
-
|
|
1256
|
+
each_entry_run do yield export_run_entry _1 end
|
|
942
1257
|
end
|
|
943
1258
|
|
|
944
1259
|
# Yields each number or range (or <tt>:*</tt>) in #elements to the block
|
|
945
1260
|
# and returns self. Returns an enumerator when called without a block.
|
|
946
1261
|
#
|
|
947
1262
|
# The returned numbers are sorted and de-duplicated, even when the input
|
|
948
|
-
# #string is not. See #normalize.
|
|
1263
|
+
# #string is not. See #normalize, SequenceSet@Ordered+and+Normalized+sets.
|
|
949
1264
|
#
|
|
950
1265
|
# Related: #elements, #each_entry
|
|
951
1266
|
def each_element # :yields: integer or range or :*
|
|
952
1267
|
return to_enum(__method__) unless block_given?
|
|
953
|
-
|
|
954
|
-
self
|
|
955
|
-
end
|
|
956
|
-
|
|
957
|
-
private
|
|
958
|
-
|
|
959
|
-
def each_entry_tuple(&block)
|
|
960
|
-
return to_enum(__method__) unless block_given?
|
|
961
|
-
if @string
|
|
962
|
-
@string.split(",") do block.call str_to_tuple _1 end
|
|
963
|
-
else
|
|
964
|
-
@tuples.each(&block)
|
|
965
|
-
end
|
|
1268
|
+
runs.each do yield export_run_entry _1 end
|
|
966
1269
|
self
|
|
967
1270
|
end
|
|
968
1271
|
|
|
969
|
-
def tuple_to_entry((min, max))
|
|
970
|
-
if min == STAR_INT then :*
|
|
971
|
-
elsif max == STAR_INT then min..
|
|
972
|
-
elsif min == max then min
|
|
973
|
-
else min..max
|
|
974
|
-
end
|
|
975
|
-
end
|
|
976
|
-
|
|
977
|
-
public
|
|
978
|
-
|
|
979
1272
|
# Yields each range in #ranges to the block and returns self.
|
|
980
1273
|
# Returns an enumerator when called without a block.
|
|
981
1274
|
#
|
|
982
1275
|
# Related: #ranges
|
|
983
1276
|
def each_range # :yields: range
|
|
984
1277
|
return to_enum(__method__) unless block_given?
|
|
985
|
-
|
|
1278
|
+
minmaxes.each do |min, max|
|
|
986
1279
|
if min == STAR_INT then yield :*..
|
|
987
1280
|
elsif max == STAR_INT then yield min..
|
|
988
1281
|
else yield min..max
|
|
@@ -1001,7 +1294,7 @@ module Net
|
|
|
1001
1294
|
def each_number(&block) # :yields: integer
|
|
1002
1295
|
return to_enum(__method__) unless block_given?
|
|
1003
1296
|
raise RangeError, '%s contains "*"' % [self.class] if include_star?
|
|
1004
|
-
|
|
1297
|
+
minmaxes.each do each_number_in_minmax _1, _2, &block end
|
|
1005
1298
|
self
|
|
1006
1299
|
end
|
|
1007
1300
|
|
|
@@ -1015,16 +1308,7 @@ module Net
|
|
|
1015
1308
|
def each_ordered_number(&block)
|
|
1016
1309
|
return to_enum(__method__) unless block_given?
|
|
1017
1310
|
raise RangeError, '%s contains "*"' % [self.class] if include_star?
|
|
1018
|
-
|
|
1019
|
-
end
|
|
1020
|
-
|
|
1021
|
-
private def each_number_in_tuple(min, max, &block)
|
|
1022
|
-
if min == STAR_INT then yield :*
|
|
1023
|
-
elsif min == max then yield min
|
|
1024
|
-
elsif max != STAR_INT then (min..max).each(&block)
|
|
1025
|
-
else
|
|
1026
|
-
raise RangeError, "#{SequenceSet} cannot enumerate range with '*'"
|
|
1027
|
-
end
|
|
1311
|
+
each_entry_minmax do each_number_in_minmax _1, _2, &block end
|
|
1028
1312
|
end
|
|
1029
1313
|
|
|
1030
1314
|
# Returns a Set with all of the #numbers in the sequence set.
|
|
@@ -1036,35 +1320,103 @@ module Net
|
|
|
1036
1320
|
# Related: #elements, #ranges, #numbers
|
|
1037
1321
|
def to_set; Set.new(numbers) end
|
|
1038
1322
|
|
|
1039
|
-
# Returns the
|
|
1323
|
+
# Returns the number of members in the set.
|
|
1324
|
+
#
|
|
1325
|
+
# Unlike #count, <tt>"*"</tt> is considered to be distinct from
|
|
1326
|
+
# <tt>2³² - 1</tt> (the maximum 32-bit unsigned integer value).
|
|
1327
|
+
#
|
|
1328
|
+
# set = Net::IMAP::SequenceSet[1..10]
|
|
1329
|
+
# set.count #=> 10
|
|
1330
|
+
# set.cardinality #=> 10
|
|
1331
|
+
#
|
|
1332
|
+
# set = Net::IMAP::SequenceSet["4294967295,*"]
|
|
1333
|
+
# set.count #=> 1
|
|
1334
|
+
# set.cardinality #=> 2
|
|
1335
|
+
#
|
|
1336
|
+
# set = Net::IMAP::SequenceSet[1..]
|
|
1337
|
+
# set.count #=> 4294967295
|
|
1338
|
+
# set.cardinality #=> 4294967296
|
|
1339
|
+
#
|
|
1340
|
+
# Related: #count, #count_with_duplicates
|
|
1341
|
+
def cardinality = minmaxes.sum(runs.count) { _2 - _1 }
|
|
1342
|
+
|
|
1343
|
+
# Returns the count of distinct #numbers in the set.
|
|
1344
|
+
#
|
|
1345
|
+
# Unlike #cardinality, <tt>"*"</tt> is considered to be equal to
|
|
1346
|
+
# <tt>2³² - 1</tt> (the maximum 32-bit unsigned integer value).
|
|
1040
1347
|
#
|
|
1041
|
-
#
|
|
1042
|
-
#
|
|
1348
|
+
# set = Net::IMAP::SequenceSet[1..10]
|
|
1349
|
+
# set.count #=> 10
|
|
1350
|
+
# set.cardinality #=> 10
|
|
1043
1351
|
#
|
|
1044
|
-
#
|
|
1352
|
+
# set = Net::IMAP::SequenceSet["4294967295,*"]
|
|
1353
|
+
# set.count #=> 1
|
|
1354
|
+
# set.cardinality #=> 2
|
|
1355
|
+
#
|
|
1356
|
+
# set = Net::IMAP::SequenceSet[1..]
|
|
1357
|
+
# set.count #=> 4294967295
|
|
1358
|
+
# set.cardinality #=> 4294967296
|
|
1359
|
+
#
|
|
1360
|
+
# Related: #cardinality, #count_with_duplicates
|
|
1045
1361
|
def count
|
|
1046
|
-
|
|
1047
|
-
(include_star? && include?(UINT32_MAX) ? -1 : 0)
|
|
1362
|
+
cardinality + (include_star? && include?(UINT32_MAX) ? -1 : 0)
|
|
1048
1363
|
end
|
|
1049
1364
|
|
|
1050
|
-
alias size count
|
|
1051
|
-
|
|
1052
1365
|
# Returns the count of numbers in the ordered #entries, including any
|
|
1053
1366
|
# repeated numbers.
|
|
1054
1367
|
#
|
|
1055
|
-
#
|
|
1056
|
-
#
|
|
1057
|
-
#
|
|
1058
|
-
#
|
|
1059
|
-
#
|
|
1060
|
-
#
|
|
1368
|
+
# When #string is normalized, this returns the same as #count. Like
|
|
1369
|
+
# #count, <tt>"*"</tt> is considered to be equal to <tt>2³² - 1</tt> (the
|
|
1370
|
+
# maximum 32-bit unsigned integer value).
|
|
1371
|
+
#
|
|
1372
|
+
# In a range, <tt>"*"</tt> is _not_ considered a duplicate:
|
|
1373
|
+
# set = Net::IMAP::SequenceSet["4294967295:*"]
|
|
1374
|
+
# set.count_with_duplicates #=> 1
|
|
1375
|
+
# set.size #=> 2
|
|
1376
|
+
# set.count #=> 1
|
|
1377
|
+
# set.cardinality #=> 2
|
|
1378
|
+
#
|
|
1379
|
+
# In a separate entry, <tt>"*"</tt> _is_ considered a duplicate:
|
|
1380
|
+
# set = Net::IMAP::SequenceSet["4294967295,*"]
|
|
1381
|
+
# set.count_with_duplicates #=> 2
|
|
1382
|
+
# set.size #=> 2
|
|
1383
|
+
# set.count #=> 1
|
|
1384
|
+
# set.cardinality #=> 2
|
|
1385
|
+
#
|
|
1386
|
+
# Related: #count, #cardinality, #size, #count_duplicates,
|
|
1387
|
+
# #has_duplicates?, #entries
|
|
1061
1388
|
def count_with_duplicates
|
|
1062
1389
|
return count unless @string
|
|
1063
|
-
|
|
1390
|
+
each_entry_minmax.sum {|min, max|
|
|
1064
1391
|
max - min + ((max == STAR_INT && min != STAR_INT) ? 0 : 1)
|
|
1065
1392
|
}
|
|
1066
1393
|
end
|
|
1067
1394
|
|
|
1395
|
+
# Returns the combined size of the ordered #entries, including any
|
|
1396
|
+
# repeated numbers.
|
|
1397
|
+
#
|
|
1398
|
+
# When #string is normalized, this returns the same as #cardinality.
|
|
1399
|
+
# Like #cardinality, <tt>"*"</tt> is considered to be be distinct from
|
|
1400
|
+
# <tt>2³² - 1</tt> (the maximum 32-bit unsigned integer value).
|
|
1401
|
+
#
|
|
1402
|
+
# set = Net::IMAP::SequenceSet["4294967295:*"]
|
|
1403
|
+
# set.size #=> 2
|
|
1404
|
+
# set.count_with_duplicates #=> 1
|
|
1405
|
+
# set.count #=> 1
|
|
1406
|
+
# set.cardinality #=> 2
|
|
1407
|
+
#
|
|
1408
|
+
# set = Net::IMAP::SequenceSet["4294967295,*"]
|
|
1409
|
+
# set.size #=> 2
|
|
1410
|
+
# set.count_with_duplicates #=> 2
|
|
1411
|
+
# set.count #=> 1
|
|
1412
|
+
# set.cardinality #=> 2
|
|
1413
|
+
#
|
|
1414
|
+
# Related: #cardinality, #count_with_duplicates, #count, #entries
|
|
1415
|
+
def size
|
|
1416
|
+
return cardinality unless @string
|
|
1417
|
+
each_entry_minmax.sum {|min, max| max - min + 1 }
|
|
1418
|
+
end
|
|
1419
|
+
|
|
1068
1420
|
# Returns the count of repeated numbers in the ordered #entries, the
|
|
1069
1421
|
# difference between #count_with_duplicates and #count.
|
|
1070
1422
|
#
|
|
@@ -1082,7 +1434,7 @@ module Net
|
|
|
1082
1434
|
#
|
|
1083
1435
|
# Always returns +false+ when #string is normalized.
|
|
1084
1436
|
#
|
|
1085
|
-
# Related: #entries, #count_with_duplicates, #count_duplicates
|
|
1437
|
+
# Related: #entries, #count_with_duplicates, #count_duplicates
|
|
1086
1438
|
def has_duplicates?
|
|
1087
1439
|
return false unless @string
|
|
1088
1440
|
count_with_duplicates != count
|
|
@@ -1093,10 +1445,10 @@ module Net
|
|
|
1093
1445
|
#
|
|
1094
1446
|
# Related: #[], #at, #find_ordered_index
|
|
1095
1447
|
def find_index(number)
|
|
1096
|
-
number =
|
|
1097
|
-
|
|
1448
|
+
number = import_num number
|
|
1449
|
+
each_minmax_with_index(minmaxes) do |min, max, idx_min|
|
|
1098
1450
|
number < min and return nil
|
|
1099
|
-
number <= max and return
|
|
1451
|
+
number <= max and return export_num(idx_min + (number - min))
|
|
1100
1452
|
end
|
|
1101
1453
|
nil
|
|
1102
1454
|
end
|
|
@@ -1106,38 +1458,15 @@ module Net
|
|
|
1106
1458
|
#
|
|
1107
1459
|
# Related: #find_index
|
|
1108
1460
|
def find_ordered_index(number)
|
|
1109
|
-
number =
|
|
1110
|
-
|
|
1461
|
+
number = import_num number
|
|
1462
|
+
each_minmax_with_index(each_entry_minmax) do |min, max, idx_min|
|
|
1111
1463
|
if min <= number && number <= max
|
|
1112
|
-
return
|
|
1464
|
+
return export_num(idx_min + (number - min))
|
|
1113
1465
|
end
|
|
1114
1466
|
end
|
|
1115
1467
|
nil
|
|
1116
1468
|
end
|
|
1117
1469
|
|
|
1118
|
-
private
|
|
1119
|
-
|
|
1120
|
-
def each_tuple_with_index(tuples)
|
|
1121
|
-
idx_min = 0
|
|
1122
|
-
tuples.each do |min, max|
|
|
1123
|
-
idx_max = idx_min + (max - min)
|
|
1124
|
-
yield min, max, idx_min, idx_max
|
|
1125
|
-
idx_min = idx_max + 1
|
|
1126
|
-
end
|
|
1127
|
-
idx_min
|
|
1128
|
-
end
|
|
1129
|
-
|
|
1130
|
-
def reverse_each_tuple_with_index(tuples)
|
|
1131
|
-
idx_max = -1
|
|
1132
|
-
tuples.reverse_each do |min, max|
|
|
1133
|
-
yield min, max, (idx_min = idx_max - (max - min)), idx_max
|
|
1134
|
-
idx_max = idx_min - 1
|
|
1135
|
-
end
|
|
1136
|
-
idx_max
|
|
1137
|
-
end
|
|
1138
|
-
|
|
1139
|
-
public
|
|
1140
|
-
|
|
1141
1470
|
# :call-seq: at(index) -> integer or nil
|
|
1142
1471
|
#
|
|
1143
1472
|
# Returns the number at the given +index+ in the sorted set, without
|
|
@@ -1148,7 +1477,7 @@ module Net
|
|
|
1148
1477
|
#
|
|
1149
1478
|
# Related: #[], #slice, #ordered_at
|
|
1150
1479
|
def at(index)
|
|
1151
|
-
|
|
1480
|
+
seek_number_in_minmaxes(minmaxes, index)
|
|
1152
1481
|
end
|
|
1153
1482
|
|
|
1154
1483
|
# :call-seq: ordered_at(index) -> integer or nil
|
|
@@ -1161,21 +1490,7 @@ module Net
|
|
|
1161
1490
|
#
|
|
1162
1491
|
# Related: #[], #slice, #ordered_at
|
|
1163
1492
|
def ordered_at(index)
|
|
1164
|
-
|
|
1165
|
-
end
|
|
1166
|
-
|
|
1167
|
-
private def lookup_number_by_tuple_index(tuples, index)
|
|
1168
|
-
index = Integer(index.to_int)
|
|
1169
|
-
if index.negative?
|
|
1170
|
-
reverse_each_tuple_with_index(tuples) do |min, max, idx_min, idx_max|
|
|
1171
|
-
idx_min <= index and return from_tuple_int(min + (index - idx_min))
|
|
1172
|
-
end
|
|
1173
|
-
else
|
|
1174
|
-
each_tuple_with_index(tuples) do |min, _, idx_min, idx_max|
|
|
1175
|
-
index <= idx_max and return from_tuple_int(min + (index - idx_min))
|
|
1176
|
-
end
|
|
1177
|
-
end
|
|
1178
|
-
nil
|
|
1493
|
+
seek_number_in_minmaxes(each_entry_minmax, index)
|
|
1179
1494
|
end
|
|
1180
1495
|
|
|
1181
1496
|
# :call-seq:
|
|
@@ -1226,33 +1541,58 @@ module Net
|
|
|
1226
1541
|
|
|
1227
1542
|
alias slice :[]
|
|
1228
1543
|
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1544
|
+
# Returns a copy of +self+ which only contains the numbers above +num+.
|
|
1545
|
+
#
|
|
1546
|
+
# Net::IMAP::SequenceSet["5,10:22,50"].above(10) # to_s => "11:22,50"
|
|
1547
|
+
# Net::IMAP::SequenceSet["5,10:22,50"].above(20) # to_s => "21:22,50
|
|
1548
|
+
# Net::IMAP::SequenceSet["5,10:22,50"].above(30) # to_s => "50"
|
|
1549
|
+
#
|
|
1550
|
+
# This returns the same result as #intersection with <tt>((num+1)..)</tt>
|
|
1551
|
+
# or #difference with <tt>(..num)</tt>.
|
|
1552
|
+
#
|
|
1553
|
+
# Net::IMAP::SequenceSet["5,10:22,50"] & (11..) # to_s => "11:22,50"
|
|
1554
|
+
# Net::IMAP::SequenceSet["5,10:22,50"] - (..10) # to_s => "11:22,50"
|
|
1555
|
+
# Net::IMAP::SequenceSet["5,10:22,50"] & (21..) # to_s => "21:22,50"
|
|
1556
|
+
# Net::IMAP::SequenceSet["5,10:22,50"] - (..20) # to_s => "21:22,50"
|
|
1557
|
+
#
|
|
1558
|
+
# Related: #above, #-, #&
|
|
1559
|
+
def above(num)
|
|
1560
|
+
NumValidator.valid_nz_number?(num) or
|
|
1561
|
+
raise ArgumentError, "not a valid sequence set number"
|
|
1562
|
+
difference(..num)
|
|
1237
1563
|
end
|
|
1238
1564
|
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1565
|
+
# Returns a copy of +self+ which only contains numbers below +num+.
|
|
1566
|
+
#
|
|
1567
|
+
# Net::IMAP::SequenceSet["5,10:22,50"].below(10) # to_s => "5"
|
|
1568
|
+
# Net::IMAP::SequenceSet["5,10:22,50"].below(20) # to_s => "5,10:19"
|
|
1569
|
+
# Net::IMAP::SequenceSet["5,10:22,50"].below(30) # to_s => "5,10:22"
|
|
1570
|
+
#
|
|
1571
|
+
# This returns the same result as #intersection with <tt>(..(num-1))</tt>
|
|
1572
|
+
# or #difference with <tt>(num..)</tt>.
|
|
1573
|
+
#
|
|
1574
|
+
# Net::IMAP::SequenceSet["5,10:22,50"] & (..9) # to_s => "5"
|
|
1575
|
+
# Net::IMAP::SequenceSet["5,10:22,50"] - (10..) # to_s => "5"
|
|
1576
|
+
# Net::IMAP::SequenceSet["5,10:22,50"] & (..19) # to_s => "5,10:19"
|
|
1577
|
+
# Net::IMAP::SequenceSet["5,10:22,50"] - (20..) # to_s => "5,10:19"
|
|
1578
|
+
#
|
|
1579
|
+
# When the set does not contain <tt>*</tt>, #below is identical to #limit
|
|
1580
|
+
# with <tt>max: num - 1</tt>. When the set does contain <tt>*</tt>,
|
|
1581
|
+
# #below always drops it from the result. Use #limit when the IMAP
|
|
1582
|
+
# semantics for <tt>*</tt> must be enforced.
|
|
1583
|
+
#
|
|
1584
|
+
# Net::IMAP::SequenceSet["5,10:22,50"].below(30) # to_s => "5,10:22"
|
|
1585
|
+
# Net::IMAP::SequenceSet["5,10:22,50"].limit(max: 29) # to_s => "5,10:22"
|
|
1586
|
+
# Net::IMAP::SequenceSet["5,10:22,*"].below(30) # to_s => "5,10:22"
|
|
1587
|
+
# Net::IMAP::SequenceSet["5,10:22,*"].limit(max: 29) # to_s => "5,10:22,29"
|
|
1588
|
+
#
|
|
1589
|
+
# Related: #above, #-, #&, #limit
|
|
1590
|
+
def below(num)
|
|
1591
|
+
NumValidator.valid_nz_number?(num) or
|
|
1592
|
+
raise ArgumentError, "not a valid sequence set number"
|
|
1593
|
+
difference(num..)
|
|
1252
1594
|
end
|
|
1253
1595
|
|
|
1254
|
-
public
|
|
1255
|
-
|
|
1256
1596
|
# Returns a frozen SequenceSet with <tt>*</tt> converted to +max+, numbers
|
|
1257
1597
|
# and ranges over +max+ removed, and ranges containing +max+ converted to
|
|
1258
1598
|
# end at +max+.
|
|
@@ -1270,8 +1610,9 @@ module Net
|
|
|
1270
1610
|
# Net::IMAP::SequenceSet["500:*"].limit(max: 37)
|
|
1271
1611
|
# #=> Net::IMAP::SequenceSet["37"]
|
|
1272
1612
|
#
|
|
1613
|
+
# Related: #limit!
|
|
1273
1614
|
def limit(max:)
|
|
1274
|
-
max =
|
|
1615
|
+
max = import_num(max)
|
|
1275
1616
|
if empty? then self.class.empty
|
|
1276
1617
|
elsif !include_star? && max < min then self.class.empty
|
|
1277
1618
|
elsif max(star: STAR_INT) <= max then frozen? ? self : dup.freeze
|
|
@@ -1284,76 +1625,206 @@ module Net
|
|
|
1284
1625
|
#
|
|
1285
1626
|
# Related: #limit
|
|
1286
1627
|
def limit!(max:)
|
|
1628
|
+
modifying! # short-circuit before querying
|
|
1287
1629
|
star = include_star?
|
|
1288
|
-
max =
|
|
1289
|
-
|
|
1290
|
-
|
|
1630
|
+
max = import_num(max)
|
|
1631
|
+
subtract_minmax [max + 1, STAR_INT]
|
|
1632
|
+
add_minmax [max, max ] if star
|
|
1291
1633
|
normalize!
|
|
1292
1634
|
end
|
|
1293
1635
|
|
|
1294
1636
|
# :call-seq: complement! -> self
|
|
1295
1637
|
#
|
|
1296
|
-
#
|
|
1297
|
-
# possible values _except_ for those
|
|
1638
|
+
# In-place set #complement. Replaces the contents of this set with its
|
|
1639
|
+
# own #complement. It will contain all possible values _except_ for those
|
|
1640
|
+
# currently in the set.
|
|
1298
1641
|
#
|
|
1299
1642
|
# Related: #complement
|
|
1300
1643
|
def complement!
|
|
1644
|
+
modifying! # short-circuit before querying
|
|
1301
1645
|
return replace(self.class.full) if empty?
|
|
1302
1646
|
return clear if full?
|
|
1303
|
-
flat =
|
|
1647
|
+
flat = minmaxes.flat_map { [_1 - 1, _2 + 1] }
|
|
1304
1648
|
if flat.first < 1 then flat.shift else flat.unshift 1 end
|
|
1305
1649
|
if STAR_INT < flat.last then flat.pop else flat.push STAR_INT end
|
|
1306
|
-
|
|
1650
|
+
replace_minmaxes flat.each_slice(2).to_a
|
|
1307
1651
|
normalize!
|
|
1308
1652
|
end
|
|
1309
1653
|
|
|
1310
|
-
#
|
|
1654
|
+
# In-place set #intersection. Removes any elements that are missing from
|
|
1655
|
+
# +other+ from this set, keeping only the #intersection, and returns
|
|
1656
|
+
# +self+.
|
|
1657
|
+
#
|
|
1658
|
+
# +other+ can be any object that would be accepted by ::new.
|
|
1659
|
+
#
|
|
1660
|
+
# set = Net::IMAP::SequenceSet.new(1..5)
|
|
1661
|
+
# set.intersect! [2, 4, 6]
|
|
1662
|
+
# set #=> Net::IMAP::SequenceSet("2,4")
|
|
1663
|
+
#
|
|
1664
|
+
# Related: #intersection, #intersect?
|
|
1665
|
+
def intersect!(other)
|
|
1666
|
+
modifying! # short-circuit before processing input
|
|
1667
|
+
subtract SequenceSet.new(other).complement!
|
|
1668
|
+
end
|
|
1669
|
+
|
|
1670
|
+
# In-place set #xor. Adds any numbers in +other+ that are missing from
|
|
1671
|
+
# this set, removes any numbers in +other+ that are already in this set,
|
|
1672
|
+
# and returns +self+.
|
|
1673
|
+
#
|
|
1674
|
+
# +other+ can be any object that would be accepted by ::new.
|
|
1675
|
+
#
|
|
1676
|
+
# set = Net::IMAP::SequenceSet.new(1..5)
|
|
1677
|
+
# set.xor! [2, 4, 6]
|
|
1678
|
+
# set #=> Net::IMAP::SequenceSet["1,3,5:6"]
|
|
1679
|
+
#
|
|
1680
|
+
# Related: #xor, #merge, #subtract
|
|
1681
|
+
def xor!(other)
|
|
1682
|
+
modifying! # short-circuit before processing input
|
|
1683
|
+
other = SequenceSet.new(other)
|
|
1684
|
+
copy = dup
|
|
1685
|
+
merge(other).subtract(other.subtract(copy.complement!))
|
|
1686
|
+
end
|
|
1687
|
+
|
|
1688
|
+
# Returns whether #string is fully normalized: entries have been sorted,
|
|
1689
|
+
# deduplicated, and coalesced, and all entries are in normal form. See
|
|
1690
|
+
# SequenceSet@Ordered+and+Normalized+sets.
|
|
1691
|
+
#
|
|
1692
|
+
# Net::IMAP::SequenceSet["1,3,5"].normalized? #=> true
|
|
1693
|
+
# Net::IMAP::SequenceSet["20:30"].normalized? #=> true
|
|
1694
|
+
#
|
|
1695
|
+
# Net::IMAP::SequenceSet["3,5,1"].normalized? #=> false, not sorted
|
|
1696
|
+
# Net::IMAP::SequenceSet["1,2,3"].normalized? #=> false, not coalesced
|
|
1697
|
+
# Net::IMAP::SequenceSet["1:5,2"].normalized? #=> false, repeated number
|
|
1698
|
+
#
|
|
1699
|
+
# Net::IMAP::SequenceSet["1:1"].normalized? #=> false, number as range
|
|
1700
|
+
# Net::IMAP::SequenceSet["5:1"].normalized? #=> false, backwards range
|
|
1701
|
+
#
|
|
1702
|
+
# Returns +true+ if (and only if) #string is equal to #normalized_string:
|
|
1703
|
+
# seqset = Net::IMAP::SequenceSet["1:3,5"]
|
|
1704
|
+
# seqset.string #=> "1:3,5"
|
|
1705
|
+
# seqset.normalized_string #=> "1:3,5"
|
|
1706
|
+
# seqset.entries #=> [1..3, 5]
|
|
1707
|
+
# seqset.elements #=> [1..3, 5]
|
|
1708
|
+
# seqset.normalized? #=> true
|
|
1709
|
+
#
|
|
1710
|
+
# seqset = Net::IMAP::SequenceSet["3,1,2"]
|
|
1711
|
+
# seqset.string #=> "3,1,2"
|
|
1712
|
+
# seqset.normalized_string #=> "1:3"
|
|
1713
|
+
# seqset.entries #=> [3, 1, 2]
|
|
1714
|
+
# seqset.elements #=> [1..3]
|
|
1715
|
+
# seqset.normalized? #=> false
|
|
1716
|
+
#
|
|
1717
|
+
# Can return +false+ even when #entries and #elements are the same:
|
|
1718
|
+
# seqset = Net::IMAP::SequenceSet["5:1"]
|
|
1719
|
+
# seqset.string #=> "5:1"
|
|
1720
|
+
# seqset.normalized_string #=> "1:5"
|
|
1721
|
+
# seqset.entries #=> [1..5]
|
|
1722
|
+
# seqset.elements #=> [1..5]
|
|
1723
|
+
# seqset.normalized? #=> false
|
|
1724
|
+
#
|
|
1725
|
+
# Note that empty sets are normalized, even though they are not #valid?:
|
|
1726
|
+
# seqset = Net::IMAP::SequenceSet.empty
|
|
1727
|
+
# seqset.normalized? #=> true
|
|
1728
|
+
# seqset.valid? #=> false
|
|
1729
|
+
#
|
|
1730
|
+
# Related: #normalize, #normalize!, #normalized_string
|
|
1731
|
+
def normalized?
|
|
1732
|
+
@string.nil? || normal_string?(@string)
|
|
1733
|
+
end
|
|
1734
|
+
|
|
1735
|
+
# Returns a SequenceSet with a normalized string representation: entries
|
|
1736
|
+
# have been sorted, deduplicated, and coalesced, and all entries
|
|
1737
|
+
# are in normal form. Returns +self+ for frozen normalized sets, and a
|
|
1738
|
+
# normalized duplicate otherwise.
|
|
1311
1739
|
#
|
|
1312
|
-
#
|
|
1313
|
-
# overlapping elements will be merged into a single larger range.
|
|
1740
|
+
# See SequenceSet@Ordered+and+Normalized+sets.
|
|
1314
1741
|
#
|
|
1315
1742
|
# Net::IMAP::SequenceSet["1:5,3:7,10:9,10:11"].normalize
|
|
1316
1743
|
# #=> Net::IMAP::SequenceSet["1:7,9:11"]
|
|
1317
1744
|
#
|
|
1318
|
-
# Related: #normalize!, #normalized_string
|
|
1745
|
+
# Related: #normalize!, #normalized_string, #normalized?
|
|
1319
1746
|
def normalize
|
|
1320
|
-
|
|
1321
|
-
return self if frozen? && str == string
|
|
1322
|
-
remain_frozen dup.instance_exec { @string = str&.-@; self }
|
|
1747
|
+
frozen? && normalized? ? self : remain_frozen(dup.normalize!)
|
|
1323
1748
|
end
|
|
1324
1749
|
|
|
1325
1750
|
# Resets #string to be sorted, deduplicated, and coalesced. Returns
|
|
1326
|
-
# +self+.
|
|
1751
|
+
# +self+. See SequenceSet@Ordered+and+Normalized+sets.
|
|
1327
1752
|
#
|
|
1328
|
-
# Related: #normalize, #normalized_string
|
|
1753
|
+
# Related: #normalize, #normalized_string, #normalized?
|
|
1329
1754
|
def normalize!
|
|
1755
|
+
modifying! # redundant check (normalizes the error message for JRuby)
|
|
1330
1756
|
@string = nil
|
|
1331
1757
|
self
|
|
1332
1758
|
end
|
|
1333
1759
|
|
|
1334
1760
|
# Returns a normalized +sequence-set+ string representation, sorted
|
|
1335
1761
|
# and deduplicated. Adjacent or overlapping elements will be merged into
|
|
1336
|
-
# a single larger range.
|
|
1762
|
+
# a single larger range. See SequenceSet@Ordered+and+Normalized+sets.
|
|
1337
1763
|
#
|
|
1338
1764
|
# Net::IMAP::SequenceSet["1:5,3:7,10:9,10:11"].normalized_string
|
|
1339
1765
|
# #=> "1:7,9:11"
|
|
1340
1766
|
#
|
|
1341
|
-
#
|
|
1767
|
+
# Returns +nil+ when the set is empty.
|
|
1768
|
+
#
|
|
1769
|
+
# Related: #normalize!, #normalize, #string, #to_s, #normalized?
|
|
1342
1770
|
def normalized_string
|
|
1343
|
-
|
|
1771
|
+
export_runs(runs) unless runs.empty?
|
|
1344
1772
|
end
|
|
1345
1773
|
|
|
1774
|
+
# Returns an inspection string for the SequenceSet.
|
|
1775
|
+
#
|
|
1776
|
+
# Net::IMAP::SequenceSet.new.inspect
|
|
1777
|
+
# #=> "Net::IMAP::SequenceSet()"
|
|
1778
|
+
#
|
|
1779
|
+
# Net::IMAP::SequenceSet(1..5, 1024, 15, 2000).inspect
|
|
1780
|
+
# #=> 'Net::IMAP::SequenceSet("1:5,15,1024,2000")'
|
|
1781
|
+
#
|
|
1782
|
+
# Frozen sets have slightly different output:
|
|
1783
|
+
#
|
|
1784
|
+
# Net::IMAP::SequenceSet.empty.inspect
|
|
1785
|
+
# #=> "Net::IMAP::SequenceSet.empty"
|
|
1786
|
+
#
|
|
1787
|
+
# Net::IMAP::SequenceSet[1..5, 1024, 15, 2000].inspect
|
|
1788
|
+
# #=> 'Net::IMAP::SequenceSet["1:5,15,1024,2000"]'
|
|
1789
|
+
#
|
|
1790
|
+
# Large sets (by number of #entries) have abridged output, with only the
|
|
1791
|
+
# first and last entries:
|
|
1792
|
+
#
|
|
1793
|
+
# Net::IMAP::SequenceSet(((1..5000) % 2).to_a).inspect
|
|
1794
|
+
# #=> #<Net::IMAP::SequenceSet 2500 entries "1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31,...(2468 entries omitted)...,4969,4971,4973,4975,4977,4979,4981,4983,4985,4987,4989,4991,4993,4995,4997,4999">
|
|
1795
|
+
#
|
|
1796
|
+
# Related: #to_s, #string
|
|
1346
1797
|
def inspect
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1798
|
+
case (count = count_entries)
|
|
1799
|
+
when 0
|
|
1800
|
+
(frozen? ? "%s.empty" : "%s()") % [self.class]
|
|
1801
|
+
when ..INSPECT_MAX_LEN
|
|
1802
|
+
(frozen? ? "%s[%p]" : "%s(%p)") % [self.class, to_s]
|
|
1351
1803
|
else
|
|
1352
|
-
|
|
1804
|
+
if @string
|
|
1805
|
+
head = @string[INSPECT_ABRIDGED_HEAD_RE]
|
|
1806
|
+
tail = @string[INSPECT_ABRIDGED_TAIL_RE]
|
|
1807
|
+
else
|
|
1808
|
+
head = export_runs(runs.first(INSPECT_TRUNCATE_LEN)) + ","
|
|
1809
|
+
tail = "," + export_runs(runs.last(INSPECT_TRUNCATE_LEN))
|
|
1810
|
+
end
|
|
1811
|
+
'#<%s %d entries "%s...(%d entries omitted)...%s"%s>' % [
|
|
1812
|
+
self.class, count,
|
|
1813
|
+
head, count - INSPECT_TRUNCATE_LEN * 2, tail,
|
|
1814
|
+
frozen? ? " (frozen)" : "",
|
|
1815
|
+
]
|
|
1353
1816
|
end
|
|
1354
1817
|
end
|
|
1355
1818
|
|
|
1356
|
-
|
|
1819
|
+
##
|
|
1820
|
+
# :method: to_sequence_set
|
|
1821
|
+
# :call-seq: to_sequence_set -> self
|
|
1822
|
+
#
|
|
1823
|
+
# Returns +self+
|
|
1824
|
+
#
|
|
1825
|
+
# Related: ::try_convert
|
|
1826
|
+
|
|
1827
|
+
# :nodoc: (work around rdoc bug)
|
|
1357
1828
|
alias to_sequence_set itself
|
|
1358
1829
|
|
|
1359
1830
|
# Unstable API: currently for internal use only (Net::IMAP#validate_data)
|
|
@@ -1367,49 +1838,69 @@ module Net
|
|
|
1367
1838
|
imap.__send__(:put_string, valid_string)
|
|
1368
1839
|
end
|
|
1369
1840
|
|
|
1841
|
+
# For YAML serialization
|
|
1842
|
+
def encode_with(coder) # :nodoc:
|
|
1843
|
+
# we can perfectly reconstruct from the string
|
|
1844
|
+
coder['string'] = to_s
|
|
1845
|
+
end
|
|
1846
|
+
|
|
1847
|
+
# For YAML deserialization
|
|
1848
|
+
def init_with(coder) # :nodoc:
|
|
1849
|
+
@set_data = new_set_data
|
|
1850
|
+
self.string = coder['string']
|
|
1851
|
+
end
|
|
1852
|
+
|
|
1853
|
+
# :stopdoc:
|
|
1370
1854
|
protected
|
|
1371
1855
|
|
|
1372
|
-
attr_reader :
|
|
1856
|
+
attr_reader :set_data
|
|
1857
|
+
|
|
1858
|
+
alias runs set_data
|
|
1859
|
+
alias minmaxes runs
|
|
1373
1860
|
|
|
1374
1861
|
private
|
|
1375
1862
|
|
|
1376
1863
|
def remain_frozen(set) frozen? ? set.freeze : set end
|
|
1864
|
+
def remain_frozen_empty; frozen? ? SequenceSet.empty : SequenceSet.new end
|
|
1377
1865
|
|
|
1378
1866
|
# frozen clones are shallow copied
|
|
1379
1867
|
def initialize_clone(other)
|
|
1380
|
-
other.
|
|
1868
|
+
@set_data = other.dup_set_data unless other.frozen?
|
|
1869
|
+
super
|
|
1381
1870
|
end
|
|
1382
1871
|
|
|
1383
1872
|
def initialize_dup(other)
|
|
1384
|
-
@
|
|
1385
|
-
@string = other.string&.-@
|
|
1873
|
+
@set_data = other.dup_set_data
|
|
1386
1874
|
super
|
|
1387
1875
|
end
|
|
1388
1876
|
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1877
|
+
######################################################################{{{2
|
|
1878
|
+
# Import methods
|
|
1879
|
+
|
|
1880
|
+
def import_minmax(input)
|
|
1881
|
+
entry = input_try_convert input
|
|
1882
|
+
case entry
|
|
1883
|
+
when *STARS, Integer then [int = import_num(entry), int]
|
|
1884
|
+
when Range then import_range_minmax(entry)
|
|
1885
|
+
when String then parse_minmax(entry)
|
|
1395
1886
|
else
|
|
1396
|
-
raise DataFormatError, "expected number or range, got %p" % [
|
|
1887
|
+
raise DataFormatError, "expected number or range, got %p" % [input]
|
|
1397
1888
|
end
|
|
1398
1889
|
end
|
|
1890
|
+
alias import_run import_minmax
|
|
1399
1891
|
|
|
1400
|
-
def
|
|
1401
|
-
|
|
1402
|
-
case
|
|
1403
|
-
when *STARS, Integer, Range then [
|
|
1404
|
-
when String then
|
|
1405
|
-
when SequenceSet then
|
|
1406
|
-
when Set then
|
|
1407
|
-
when Array then
|
|
1892
|
+
def import_runs(input)
|
|
1893
|
+
set = input_try_convert input
|
|
1894
|
+
case set
|
|
1895
|
+
when *STARS, Integer, Range then [import_run(set)]
|
|
1896
|
+
when String then parse_runs set
|
|
1897
|
+
when SequenceSet then set.runs
|
|
1898
|
+
when Set then set.map { [import_num(_1)] * 2 }
|
|
1899
|
+
when Array then set.flat_map { import_runs _1 }
|
|
1408
1900
|
when nil then []
|
|
1409
1901
|
else
|
|
1410
|
-
raise DataFormatError,
|
|
1411
|
-
|
|
1412
|
-
"got %p" % [obj]
|
|
1902
|
+
raise DataFormatError, "expected nz-number, range, '*', Set, Array; " \
|
|
1903
|
+
"got %p" % [input]
|
|
1413
1904
|
end
|
|
1414
1905
|
end
|
|
1415
1906
|
|
|
@@ -1422,9 +1913,9 @@ module Net
|
|
|
1422
1913
|
input
|
|
1423
1914
|
end
|
|
1424
1915
|
|
|
1425
|
-
def
|
|
1426
|
-
first =
|
|
1427
|
-
last =
|
|
1916
|
+
def import_range_minmax(range)
|
|
1917
|
+
first = import_num(range.begin || 1)
|
|
1918
|
+
last = import_num(range.end || :*)
|
|
1428
1919
|
last -= 1 if range.exclude_end? && range.end && last != STAR_INT
|
|
1429
1920
|
unless first <= last
|
|
1430
1921
|
raise DataFormatError, "invalid range for sequence-set: %p" % [range]
|
|
@@ -1432,67 +1923,260 @@ module Net
|
|
|
1432
1923
|
[first, last]
|
|
1433
1924
|
end
|
|
1434
1925
|
|
|
1435
|
-
def
|
|
1436
|
-
def
|
|
1926
|
+
def import_num(obj) STARS.include?(obj) ? STAR_INT : nz_number(obj) end
|
|
1927
|
+
def nz_number(num) = NumValidator.coerce_nz_number(num)
|
|
1928
|
+
|
|
1929
|
+
######################################################################{{{2
|
|
1930
|
+
# Export methods
|
|
1931
|
+
|
|
1932
|
+
def export_num(num) num == STAR_INT ? :* : num end
|
|
1933
|
+
|
|
1934
|
+
def export_minmaxes(minmaxes)
|
|
1935
|
+
-minmaxes.map { export_minmax _1 }.join(",")
|
|
1936
|
+
end
|
|
1937
|
+
|
|
1938
|
+
def export_minmax(minmax) minmax.uniq.map { export_num _1 }.join(":") end
|
|
1939
|
+
|
|
1940
|
+
alias export_runs export_minmaxes
|
|
1941
|
+
alias export_run export_minmax
|
|
1942
|
+
|
|
1943
|
+
def export_minmax_entry((min, max))
|
|
1944
|
+
if min == STAR_INT then :*
|
|
1945
|
+
elsif max == STAR_INT then min..
|
|
1946
|
+
elsif min == max then min
|
|
1947
|
+
else min..max
|
|
1948
|
+
end
|
|
1949
|
+
end
|
|
1950
|
+
alias export_run_entry export_minmax_entry
|
|
1951
|
+
|
|
1952
|
+
def each_number_in_minmax(min, max, &block)
|
|
1953
|
+
if min == STAR_INT then yield :*
|
|
1954
|
+
elsif min == max then yield min
|
|
1955
|
+
elsif max != STAR_INT then (min..max).each(&block)
|
|
1956
|
+
else
|
|
1957
|
+
raise RangeError, "#{SequenceSet} cannot enumerate range with '*'"
|
|
1958
|
+
end
|
|
1959
|
+
end
|
|
1960
|
+
|
|
1961
|
+
######################################################################{{{2
|
|
1962
|
+
# Parse methods
|
|
1437
1963
|
|
|
1438
|
-
def
|
|
1439
|
-
def
|
|
1440
|
-
|
|
1964
|
+
def parse_runs(str) str.split(",", -1).map! { parse_run _1 } end
|
|
1965
|
+
def parse_minmax(str) parse_entry(str).minmax end
|
|
1966
|
+
alias parse_run parse_minmax
|
|
1967
|
+
|
|
1968
|
+
def parse_entry(str)
|
|
1441
1969
|
raise DataFormatError, "invalid sequence set string" if str.empty?
|
|
1442
|
-
str.split(":", 2).map! {
|
|
1970
|
+
str.split(":", 2).map! { import_num _1 }
|
|
1971
|
+
end
|
|
1972
|
+
|
|
1973
|
+
# yields validated but unsorted [num] or [num, num]
|
|
1974
|
+
def each_parsed_entry(str)
|
|
1975
|
+
return to_enum(__method__, str) unless block_given?
|
|
1976
|
+
str&.split(",", -1) do |entry| yield parse_entry(entry) end
|
|
1977
|
+
end
|
|
1978
|
+
|
|
1979
|
+
def normal_string?(str) normalized_entries? each_parsed_entry str end
|
|
1980
|
+
|
|
1981
|
+
def normalized_entries?(entries)
|
|
1982
|
+
max = nil
|
|
1983
|
+
entries.each do |first, last|
|
|
1984
|
+
return false if last && last <= first # 1:1 or 2:1
|
|
1985
|
+
return false if max && first <= max + 1 # 2,1 or 1,1 or 1,2
|
|
1986
|
+
max = last || first
|
|
1987
|
+
end
|
|
1988
|
+
true
|
|
1989
|
+
end
|
|
1990
|
+
|
|
1991
|
+
######################################################################{{{2
|
|
1992
|
+
# Ordered entry methods
|
|
1993
|
+
|
|
1994
|
+
def count_entries
|
|
1995
|
+
@string ? @string.count(",") + 1 : runs.count
|
|
1996
|
+
end
|
|
1997
|
+
|
|
1998
|
+
def each_entry_minmax(&block)
|
|
1999
|
+
return to_enum(__method__) unless block_given?
|
|
2000
|
+
if @string
|
|
2001
|
+
@string.split(",") do block.call parse_minmax _1 end
|
|
2002
|
+
else
|
|
2003
|
+
minmaxes.each(&block)
|
|
2004
|
+
end
|
|
2005
|
+
self
|
|
1443
2006
|
end
|
|
2007
|
+
alias each_entry_run each_entry_minmax
|
|
2008
|
+
|
|
2009
|
+
######################################################################{{{2
|
|
2010
|
+
# Search methods
|
|
1444
2011
|
|
|
1445
|
-
def
|
|
2012
|
+
def include_minmax?((min, max)) bsearch_range(min)&.cover?(min..max) end
|
|
1446
2013
|
|
|
1447
|
-
def
|
|
1448
|
-
range =
|
|
2014
|
+
def intersect_minmax?((min, max))
|
|
2015
|
+
range = bsearch_range(min) and
|
|
1449
2016
|
range.include?(min) || range.include?(max) || (min..max).cover?(range)
|
|
1450
2017
|
end
|
|
1451
2018
|
|
|
2019
|
+
alias include_run? include_minmax?
|
|
2020
|
+
alias intersect_run? intersect_minmax?
|
|
2021
|
+
|
|
2022
|
+
def bsearch_index(num) = minmaxes.bsearch_index { _2 >= num }
|
|
2023
|
+
def bsearch_minmax(num) = minmaxes.bsearch { _2 >= num }
|
|
2024
|
+
def bsearch_range(num) = (min, max = bsearch_minmax(num)) && (min..max)
|
|
2025
|
+
|
|
2026
|
+
######################################################################{{{2
|
|
2027
|
+
# Number indexing methods
|
|
2028
|
+
|
|
2029
|
+
def seek_number_in_minmaxes(minmaxes, index)
|
|
2030
|
+
index = Integer(index.to_int)
|
|
2031
|
+
if index.negative?
|
|
2032
|
+
reverse_each_minmax_with_index(minmaxes) do |min, max, idx_min, idx_max|
|
|
2033
|
+
idx_min <= index and return export_num(min + (index - idx_min))
|
|
2034
|
+
end
|
|
2035
|
+
else
|
|
2036
|
+
each_minmax_with_index(minmaxes) do |min, _, idx_min, idx_max|
|
|
2037
|
+
index <= idx_max and return export_num(min + (index - idx_min))
|
|
2038
|
+
end
|
|
2039
|
+
end
|
|
2040
|
+
nil
|
|
2041
|
+
end
|
|
2042
|
+
|
|
2043
|
+
def each_minmax_with_index(minmaxes)
|
|
2044
|
+
idx_min = 0
|
|
2045
|
+
minmaxes.each do |min, max|
|
|
2046
|
+
idx_max = idx_min + (max - min)
|
|
2047
|
+
yield min, max, idx_min, idx_max
|
|
2048
|
+
idx_min = idx_max + 1
|
|
2049
|
+
end
|
|
2050
|
+
idx_min
|
|
2051
|
+
end
|
|
2052
|
+
|
|
2053
|
+
def reverse_each_minmax_with_index(minmaxes)
|
|
2054
|
+
idx_max = -1
|
|
2055
|
+
minmaxes.reverse_each do |min, max|
|
|
2056
|
+
yield min, max, (idx_min = idx_max - (max - min)), idx_max
|
|
2057
|
+
idx_max = idx_min - 1
|
|
2058
|
+
end
|
|
2059
|
+
idx_max
|
|
2060
|
+
end
|
|
2061
|
+
|
|
2062
|
+
def slice_length(start, length)
|
|
2063
|
+
start = Integer(start.to_int)
|
|
2064
|
+
length = Integer(length.to_int)
|
|
2065
|
+
raise ArgumentError, "length must be positive" unless length.positive?
|
|
2066
|
+
last = start + length - 1 unless start.negative? && start.abs <= length
|
|
2067
|
+
slice_range(start..last)
|
|
2068
|
+
end
|
|
2069
|
+
|
|
2070
|
+
def slice_range(range)
|
|
2071
|
+
first = range.begin || 0
|
|
2072
|
+
last = range.end || -1
|
|
2073
|
+
if range.exclude_end?
|
|
2074
|
+
return remain_frozen_empty if last.zero?
|
|
2075
|
+
last -= 1 if range.end && last != STAR_INT
|
|
2076
|
+
end
|
|
2077
|
+
if (first * last).positive? && last < first
|
|
2078
|
+
remain_frozen_empty
|
|
2079
|
+
elsif (min = at(first))
|
|
2080
|
+
max = at(last)
|
|
2081
|
+
max = :* if max.nil?
|
|
2082
|
+
if max == :* then self & (min..)
|
|
2083
|
+
elsif min <= max then self & (min..max)
|
|
2084
|
+
else remain_frozen_empty
|
|
2085
|
+
end
|
|
2086
|
+
end
|
|
2087
|
+
end
|
|
2088
|
+
|
|
2089
|
+
######################################################################{{{2
|
|
2090
|
+
# Core set data create/freeze/dup primitives
|
|
2091
|
+
|
|
2092
|
+
def new_set_data = []
|
|
2093
|
+
def freeze_set_data = set_data.each(&:freeze).freeze
|
|
2094
|
+
def dup_set_data = set_data.map { _1.dup }
|
|
2095
|
+
protected :dup_set_data
|
|
2096
|
+
|
|
2097
|
+
######################################################################{{{2
|
|
2098
|
+
# Core set data query/enumeration primitives
|
|
2099
|
+
|
|
2100
|
+
def min_num = minmaxes.first&.first
|
|
2101
|
+
def max_num = minmaxes.last&.last
|
|
2102
|
+
|
|
2103
|
+
def min_at(idx) = minmaxes[idx][0]
|
|
2104
|
+
def max_at(idx) = minmaxes[idx][1]
|
|
2105
|
+
|
|
2106
|
+
######################################################################{{{2
|
|
2107
|
+
# Core set data modification primitives
|
|
2108
|
+
|
|
2109
|
+
def set_min_at(idx, min) = minmaxes[idx][0] = min
|
|
2110
|
+
def set_max_at(idx, max) = minmaxes[idx][1] = max
|
|
2111
|
+
def replace_minmaxes(other) = minmaxes.replace(other)
|
|
2112
|
+
def append_minmax(min, max) = minmaxes << [min, max]
|
|
2113
|
+
def insert_minmax(idx, min, max) = minmaxes.insert idx, [min, max]
|
|
2114
|
+
def delete_run_at(idx) = runs.delete_at(idx)
|
|
2115
|
+
def slice_runs!(...) = runs.slice!(...)
|
|
2116
|
+
def truncate_runs!(idx) = runs.slice!(idx..)
|
|
2117
|
+
|
|
2118
|
+
######################################################################{{{2
|
|
2119
|
+
# Update methods
|
|
2120
|
+
|
|
1452
2121
|
def modifying!
|
|
1453
2122
|
if frozen?
|
|
1454
2123
|
raise FrozenError, "can't modify frozen #{self.class}: %p" % [self]
|
|
1455
2124
|
end
|
|
1456
2125
|
end
|
|
1457
2126
|
|
|
1458
|
-
def
|
|
1459
|
-
|
|
2127
|
+
def add_minmaxes(minmaxes)
|
|
2128
|
+
minmaxes.each do |minmax|
|
|
2129
|
+
add_minmax minmax
|
|
2130
|
+
end
|
|
2131
|
+
self
|
|
2132
|
+
end
|
|
2133
|
+
|
|
2134
|
+
def subtract_minmaxes(minmaxes)
|
|
2135
|
+
minmaxes.each do |minmax|
|
|
2136
|
+
subtract_minmax minmax
|
|
2137
|
+
end
|
|
2138
|
+
self
|
|
2139
|
+
end
|
|
1460
2140
|
|
|
1461
2141
|
#
|
|
1462
|
-
# --|=====| |=====new
|
|
1463
|
-
# ?????????-|=====new
|
|
2142
|
+
# --|=====| |=====new run=======| append
|
|
2143
|
+
# ?????????-|=====new run=======|-|===lower===|-- insert
|
|
1464
2144
|
#
|
|
1465
|
-
# |=====new
|
|
2145
|
+
# |=====new run=======|
|
|
1466
2146
|
# ---------??=======lower=======??--------------- noop
|
|
1467
2147
|
#
|
|
1468
2148
|
# ---------??===lower==|--|==| join remaining
|
|
1469
2149
|
# ---------??===lower==|--|==|----|===upper===|-- join until upper
|
|
1470
2150
|
# ---------??===lower==|--|==|--|=====upper===|-- join to upper
|
|
1471
|
-
def
|
|
2151
|
+
def add_minmax(minmax)
|
|
1472
2152
|
modifying!
|
|
1473
|
-
min, max
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
2153
|
+
min, max = minmax
|
|
2154
|
+
lower_idx = bsearch_index(min - 1)
|
|
2155
|
+
lmin, lmax = min_at(lower_idx), max_at(lower_idx) if lower_idx
|
|
2156
|
+
if lmin.nil? then append_minmax min, max
|
|
2157
|
+
elsif (max + 1) < lmin then insert_minmax lower_idx, min, max
|
|
2158
|
+
else add_coalesced_minmax(lower_idx, lmin, lmax, min, max)
|
|
1478
2159
|
end
|
|
1479
2160
|
end
|
|
1480
2161
|
|
|
1481
|
-
def
|
|
1482
|
-
return if
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
lower_idx
|
|
1486
|
-
return if
|
|
1487
|
-
tmax_adj =
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
2162
|
+
def add_coalesced_minmax(lower_idx, lmin, lmax, min, max)
|
|
2163
|
+
return if lmin <= min && max <= lmax
|
|
2164
|
+
set_min_at lower_idx, (lmin = min) if min < lmin
|
|
2165
|
+
set_max_at lower_idx, (lmax = max) if lmax < max
|
|
2166
|
+
next_idx = lower_idx + 1
|
|
2167
|
+
return if next_idx == runs.count
|
|
2168
|
+
tmax_adj = lmax + 1
|
|
2169
|
+
if (upper_idx = bsearch_index(tmax_adj))
|
|
2170
|
+
if tmax_adj < min_at(upper_idx)
|
|
2171
|
+
upper_idx -= 1
|
|
2172
|
+
else
|
|
2173
|
+
set_max_at lower_idx, max_at(upper_idx)
|
|
2174
|
+
end
|
|
1491
2175
|
end
|
|
1492
|
-
|
|
2176
|
+
slice_runs! next_idx..upper_idx
|
|
1493
2177
|
end
|
|
1494
2178
|
|
|
1495
|
-
# |====
|
|
2179
|
+
# |====subtracted run=======|
|
|
1496
2180
|
# --|====| no more 1. noop
|
|
1497
2181
|
# --|====|---------------------------|====lower====|-- 2. noop
|
|
1498
2182
|
# -------|======lower================|---------------- 3. split
|
|
@@ -1505,61 +2189,59 @@ module Net
|
|
|
1505
2189
|
# -------??=====lower====|--|====| no more 6. delete rest
|
|
1506
2190
|
# -------??=====lower====|--|====|---|====upper====|-- 7. delete until
|
|
1507
2191
|
# -------??=====lower====|--|====|--|=====upper====|-- 8. delete and trim
|
|
1508
|
-
def
|
|
2192
|
+
def subtract_minmax(minmax)
|
|
1509
2193
|
modifying!
|
|
1510
|
-
min, max
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
elsif max <
|
|
1515
|
-
|
|
2194
|
+
min, max = minmax
|
|
2195
|
+
idx = bsearch_index(min)
|
|
2196
|
+
lmin, lmax = min_at(idx), max_at(idx) if idx
|
|
2197
|
+
if lmin.nil? then nil # case 1.
|
|
2198
|
+
elsif max < lmin then nil # case 2.
|
|
2199
|
+
elsif max < lmax then trim_or_split_minmax idx, lmin, min, max
|
|
2200
|
+
else trim_or_delete_minmax idx, lmin, lmax, min, max
|
|
1516
2201
|
end
|
|
1517
2202
|
end
|
|
1518
2203
|
|
|
1519
|
-
def
|
|
1520
|
-
|
|
1521
|
-
|
|
2204
|
+
def trim_or_split_minmax(idx, lmin, tmin, tmax)
|
|
2205
|
+
set_min_at idx, tmax + 1
|
|
2206
|
+
if lmin < tmin # split
|
|
2207
|
+
insert_minmax idx, lmin, tmin - 1
|
|
1522
2208
|
end
|
|
1523
|
-
lower[0] = tmax + 1
|
|
1524
2209
|
end
|
|
1525
2210
|
|
|
1526
|
-
def
|
|
1527
|
-
if
|
|
1528
|
-
|
|
2211
|
+
def trim_or_delete_minmax(lower_idx, lmin, lmax, tmin, tmax)
|
|
2212
|
+
if lmin < tmin # trim lower
|
|
2213
|
+
lmax = set_max_at lower_idx, tmin - 1
|
|
1529
2214
|
lower_idx += 1
|
|
1530
2215
|
end
|
|
1531
|
-
if tmax ==
|
|
1532
|
-
|
|
1533
|
-
elsif (
|
|
1534
|
-
upper_idx
|
|
1535
|
-
|
|
2216
|
+
if tmax == lmax # case 5
|
|
2217
|
+
delete_run_at lower_idx
|
|
2218
|
+
elsif (upper_idx = bsearch_index(tmax + 1))
|
|
2219
|
+
if min_at(upper_idx) <= tmax # case 8
|
|
2220
|
+
set_min_at upper_idx, tmax + 1
|
|
2221
|
+
end
|
|
2222
|
+
slice_runs! lower_idx..upper_idx - 1 # cases 7 and 8
|
|
2223
|
+
else # case 6
|
|
2224
|
+
truncate_runs! lower_idx
|
|
1536
2225
|
end
|
|
1537
|
-
tuples.slice!(lower_idx..upper_idx)
|
|
1538
2226
|
end
|
|
1539
2227
|
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
def range_gte_to(num)
|
|
1545
|
-
first, last = tuples.bsearch { _2 >= num }
|
|
1546
|
-
first..last if first
|
|
1547
|
-
end
|
|
1548
|
-
|
|
1549
|
-
def nz_number(num)
|
|
1550
|
-
String === num && !/\A[1-9]\d*\z/.match?(num) and
|
|
1551
|
-
raise DataFormatError, "%p is not a valid nz-number" % [num]
|
|
1552
|
-
NumValidator.ensure_nz_number Integer num
|
|
1553
|
-
rescue TypeError # To catch errors from Integer()
|
|
1554
|
-
raise DataFormatError, $!.message
|
|
1555
|
-
end
|
|
2228
|
+
alias add_runs add_minmaxes
|
|
2229
|
+
alias add_run add_minmax
|
|
2230
|
+
alias subtract_runs subtract_minmaxes
|
|
2231
|
+
alias subtract_run subtract_minmax
|
|
1556
2232
|
|
|
2233
|
+
######################################################################{{{2
|
|
1557
2234
|
# intentionally defined after the class implementation
|
|
1558
2235
|
|
|
2236
|
+
FULL_SET_DATA = [[1, STAR_INT].freeze].freeze
|
|
2237
|
+
private_constant :FULL_SET_DATA
|
|
2238
|
+
|
|
1559
2239
|
EMPTY = new.freeze
|
|
1560
2240
|
FULL = self["1:*"]
|
|
1561
2241
|
private_constant :EMPTY, :FULL
|
|
1562
2242
|
|
|
2243
|
+
# }}}
|
|
2244
|
+
# vim:foldmethod=marker
|
|
1563
2245
|
end
|
|
1564
2246
|
end
|
|
1565
2247
|
end
|