net-imap 0.5.6 → 0.6.3
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_accessors.rb +8 -9
- data/lib/net/imap/config/attr_inheritance.rb +64 -1
- data/lib/net/imap/config/attr_type_coercion.rb +35 -22
- data/lib/net/imap/config/attr_version_defaults.rb +90 -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 +185 -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/parser_utils.rb +14 -23
- data/lib/net/imap/response_parser.rb +27 -17
- 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 +1126 -431
- data/lib/net/imap/uidplus_data.rb +2 -63
- data/lib/net/imap/vanished_data.rb +10 -1
- data/lib/net/imap.rb +292 -82
- 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"
|
|
53
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
|
|
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.
|
|
65
135
|
#
|
|
66
|
-
# Internally, SequenceSet
|
|
67
|
-
#
|
|
68
|
-
#
|
|
69
|
-
#
|
|
70
|
-
# the set in
|
|
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.
|
|
71
143
|
#
|
|
72
|
-
# Most modification methods
|
|
73
|
-
#
|
|
74
|
-
#
|
|
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.
|
|
147
|
+
#
|
|
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
|
|
322
444
|
#
|
|
323
|
-
# Returns a frozen SequenceSet, constructed from +
|
|
445
|
+
# Returns a frozen SequenceSet, constructed from +inputs+.
|
|
446
|
+
#
|
|
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
|
-
# The string is validated but not normalized.
|
|
618
|
+
# Assigns a new string to #string and resets #elements to match.
|
|
619
|
+
# Assigning +nil+ or an empty string are equivalent to calling #clear.
|
|
422
620
|
#
|
|
423
|
-
#
|
|
621
|
+
# Non-empty strings are validated but not normalized.
|
|
622
|
+
#
|
|
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?
|
|
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
|
|
428
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,70 @@ 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
|
+
if cardinality <= count
|
|
792
|
+
frozen? ? self : dup
|
|
793
|
+
else
|
|
794
|
+
slice(-count..) || remain_frozen_empty
|
|
795
|
+
end
|
|
796
|
+
elsif (val = max_num)
|
|
797
|
+
val == STAR_INT ? star : val
|
|
798
|
+
end
|
|
572
799
|
end
|
|
573
800
|
|
|
574
|
-
# :call-seq:
|
|
801
|
+
# :call-seq:
|
|
802
|
+
# min(star: :*) => integer or star or nil
|
|
803
|
+
# min(count) => SequenceSet
|
|
575
804
|
#
|
|
576
805
|
# Returns the minimum value in +self+, +star+ when the only value in the
|
|
577
806
|
# set is <tt>*</tt>, or +nil+ when the set is empty.
|
|
578
|
-
|
|
579
|
-
|
|
807
|
+
#
|
|
808
|
+
# When +count+ is given, a new SequenceSet is returned, containing only
|
|
809
|
+
# the first +count+ numbers. An empty SequenceSet is returned when +self+
|
|
810
|
+
# is empty. (+star+ is ignored when +count+ is given.)
|
|
811
|
+
#
|
|
812
|
+
# Related: #max, #minmax, #slice
|
|
813
|
+
def min(count = nil, star: :*)
|
|
814
|
+
if count
|
|
815
|
+
slice(0...count) || remain_frozen_empty
|
|
816
|
+
elsif (val = min_num)
|
|
817
|
+
val != STAR_INT ? val : star
|
|
818
|
+
end
|
|
580
819
|
end
|
|
581
820
|
|
|
582
|
-
# :call-seq: minmax(star: :*) =>
|
|
821
|
+
# :call-seq: minmax(star: :*) => [min, max] or nil
|
|
583
822
|
#
|
|
584
823
|
# Returns a 2-element array containing the minimum and maximum numbers in
|
|
585
|
-
# +self+, or +nil+ when the set is empty.
|
|
824
|
+
# +self+, or +nil+ when the set is empty. +star+ is handled the same way
|
|
825
|
+
# as by #min and #max.
|
|
826
|
+
#
|
|
827
|
+
# Related: #min, #max
|
|
586
828
|
def minmax(star: :*); [min(star: star), max(star: star)] unless empty? end
|
|
587
829
|
|
|
588
830
|
# Returns false when the set is empty.
|
|
589
831
|
def valid?; !empty? end
|
|
590
832
|
|
|
591
833
|
# Returns true if the set contains no elements
|
|
592
|
-
def empty?;
|
|
834
|
+
def empty?; runs.empty? end
|
|
593
835
|
|
|
594
836
|
# Returns true if the set contains every possible element.
|
|
595
|
-
def full?;
|
|
837
|
+
def full?; set_data == FULL_SET_DATA end
|
|
596
838
|
|
|
597
839
|
# :call-seq:
|
|
598
840
|
# self + other -> sequence set
|
|
@@ -602,14 +844,19 @@ module Net
|
|
|
602
844
|
# Returns a new sequence set that has every number in the +other+ object
|
|
603
845
|
# added.
|
|
604
846
|
#
|
|
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.
|
|
847
|
+
# +other+ may be any object that would be accepted by ::new.
|
|
608
848
|
#
|
|
609
849
|
# Net::IMAP::SequenceSet["1:5"] | 2 | [4..6, 99]
|
|
610
850
|
# #=> Net::IMAP::SequenceSet["1:6,99"]
|
|
611
851
|
#
|
|
612
|
-
# Related: #add, #merge
|
|
852
|
+
# Related: #add, #merge, #&, #-, #^, #~
|
|
853
|
+
#
|
|
854
|
+
# ==== Set identities
|
|
855
|
+
#
|
|
856
|
+
# <tt>lhs | rhs</tt> is equivalent to:
|
|
857
|
+
# * <tt>rhs | lhs</tt> (commutative)
|
|
858
|
+
# * <tt>~(~lhs & ~rhs)</tt> (De Morgan's Law)
|
|
859
|
+
# * <tt>(lhs & rhs) ^ (lhs ^ rhs)</tt>
|
|
613
860
|
def |(other) remain_frozen dup.merge other end
|
|
614
861
|
alias :+ :|
|
|
615
862
|
alias union :|
|
|
@@ -621,14 +868,22 @@ module Net
|
|
|
621
868
|
# Returns a new sequence set built by duplicating this set and removing
|
|
622
869
|
# every number that appears in +other+.
|
|
623
870
|
#
|
|
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.
|
|
871
|
+
# +other+ may be any object that would be accepted by ::new.
|
|
627
872
|
#
|
|
628
873
|
# Net::IMAP::SequenceSet[1..5] - 2 - 4 - 6
|
|
629
874
|
# #=> Net::IMAP::SequenceSet["1,3,5"]
|
|
630
875
|
#
|
|
631
|
-
# Related: #subtract
|
|
876
|
+
# Related: #subtract, #|, #&, #^, #~
|
|
877
|
+
#
|
|
878
|
+
# ==== Set identities
|
|
879
|
+
#
|
|
880
|
+
# <tt>lhs - rhs</tt> is equivalent to:
|
|
881
|
+
# * <tt>~rhs - ~lhs</tt>
|
|
882
|
+
# * <tt>lhs & ~rhs</tt>
|
|
883
|
+
# * <tt>~(~lhs | rhs)</tt>
|
|
884
|
+
# * <tt>lhs & (lhs ^ rhs)</tt>
|
|
885
|
+
# * <tt>lhs ^ (lhs & rhs)</tt>
|
|
886
|
+
# * <tt>rhs ^ (lhs | rhs)</tt>
|
|
632
887
|
def -(other) remain_frozen dup.subtract other end
|
|
633
888
|
alias difference :-
|
|
634
889
|
|
|
@@ -639,17 +894,23 @@ module Net
|
|
|
639
894
|
# Returns a new sequence set containing only the numbers common to this
|
|
640
895
|
# set and +other+.
|
|
641
896
|
#
|
|
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.
|
|
897
|
+
# +other+ may be any object that would be accepted by ::new.
|
|
645
898
|
#
|
|
646
899
|
# Net::IMAP::SequenceSet[1..5] & [2, 4, 6]
|
|
647
900
|
# #=> Net::IMAP::SequenceSet["2,4"]
|
|
648
901
|
#
|
|
649
|
-
#
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
902
|
+
# Related: #intersect?, #|, #-, #^, #~
|
|
903
|
+
#
|
|
904
|
+
# ==== Set identities
|
|
905
|
+
#
|
|
906
|
+
# <tt>lhs & rhs</tt> is equivalent to:
|
|
907
|
+
# * <tt>rhs & lhs</tt> (commutative)
|
|
908
|
+
# * <tt>~(~lhs | ~rhs)</tt> (De Morgan's Law)
|
|
909
|
+
# * <tt>lhs - ~rhs</tt>
|
|
910
|
+
# * <tt>lhs - (lhs - rhs)</tt>
|
|
911
|
+
# * <tt>lhs - (lhs ^ rhs)</tt>
|
|
912
|
+
# * <tt>lhs ^ (lhs - rhs)</tt>
|
|
913
|
+
def &(other) remain_frozen dup.intersect! other end
|
|
653
914
|
alias intersection :&
|
|
654
915
|
|
|
655
916
|
# :call-seq:
|
|
@@ -659,16 +920,22 @@ module Net
|
|
|
659
920
|
# Returns a new sequence set containing numbers that are exclusive between
|
|
660
921
|
# this set and +other+.
|
|
661
922
|
#
|
|
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.
|
|
923
|
+
# +other+ may be any object that would be accepted by ::new.
|
|
665
924
|
#
|
|
666
925
|
# Net::IMAP::SequenceSet[1..5] ^ [2, 4, 6]
|
|
667
926
|
# #=> Net::IMAP::SequenceSet["1,3,5:6"]
|
|
668
927
|
#
|
|
669
|
-
#
|
|
670
|
-
#
|
|
671
|
-
|
|
928
|
+
# Related: #|, #&, #-, #~
|
|
929
|
+
#
|
|
930
|
+
# ==== Set identities
|
|
931
|
+
#
|
|
932
|
+
# <tt>lhs ^ rhs</tt> is equivalent to:
|
|
933
|
+
# * <tt>rhs ^ lhs</tt> (commutative)
|
|
934
|
+
# * <tt>~lhs ^ ~rhs</tt>
|
|
935
|
+
# * <tt>(lhs | rhs) - (lhs & rhs)</tt>
|
|
936
|
+
# * <tt>(lhs - rhs) | (rhs - lhs)</tt>
|
|
937
|
+
# * <tt>(lhs ^ other) ^ (other ^ rhs)</tt>
|
|
938
|
+
def ^(other) remain_frozen dup.xor! other end
|
|
672
939
|
alias xor :^
|
|
673
940
|
|
|
674
941
|
# :call-seq:
|
|
@@ -685,21 +952,30 @@ module Net
|
|
|
685
952
|
# ~Net::IMAP::SequenceSet["6:99,223:*"]
|
|
686
953
|
# #=> Net::IMAP::SequenceSet["1:5,100:222"]
|
|
687
954
|
#
|
|
688
|
-
# Related: #complement
|
|
955
|
+
# Related: #complement!, #|, #&, #-, #^
|
|
956
|
+
#
|
|
957
|
+
# ==== Set identities
|
|
958
|
+
#
|
|
959
|
+
# <tt>~set</tt> is equivalent to:
|
|
960
|
+
# * <tt>full - set</tt>, where "full" is Net::IMAP::SequenceSet.full
|
|
689
961
|
def ~; remain_frozen dup.complement! end
|
|
690
962
|
alias complement :~
|
|
691
963
|
|
|
692
964
|
# :call-seq:
|
|
693
|
-
# add(
|
|
965
|
+
# add(element) -> self
|
|
694
966
|
# self << other -> self
|
|
695
967
|
#
|
|
696
968
|
# Adds a range or number to the set and returns +self+.
|
|
697
969
|
#
|
|
698
970
|
# #string will be regenerated. Use #merge to add many elements at once.
|
|
699
971
|
#
|
|
700
|
-
#
|
|
701
|
-
|
|
702
|
-
|
|
972
|
+
# Use #append to append new elements to #string. See
|
|
973
|
+
# SequenceSet@Ordered+and+Normalized+sets.
|
|
974
|
+
#
|
|
975
|
+
# Related: #add?, #merge, #union, #append
|
|
976
|
+
def add(element)
|
|
977
|
+
modifying! # short-circuit before import_run
|
|
978
|
+
add_run import_run element
|
|
703
979
|
normalize!
|
|
704
980
|
end
|
|
705
981
|
alias << add
|
|
@@ -708,29 +984,73 @@ module Net
|
|
|
708
984
|
#
|
|
709
985
|
# Unlike #add, #merge, or #union, the new value is appended to #string.
|
|
710
986
|
# This may result in a #string which has duplicates or is out-of-order.
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
987
|
+
#
|
|
988
|
+
# set = Net::IMAP::SequenceSet.new
|
|
989
|
+
# set.append(1..2) # => Net::IMAP::SequenceSet("1:2")
|
|
990
|
+
# set.append(5) # => Net::IMAP::SequenceSet("1:2,5")
|
|
991
|
+
# set.append(4) # => Net::IMAP::SequenceSet("1:2,5,4")
|
|
992
|
+
# set.append(3) # => Net::IMAP::SequenceSet("1:2,5,4,3")
|
|
993
|
+
# set.append(2) # => Net::IMAP::SequenceSet("1:2,5,4,3,2")
|
|
994
|
+
#
|
|
995
|
+
# If +entry+ is a string, it will be converted into normal form.
|
|
996
|
+
#
|
|
997
|
+
# set = Net::IMAP::SequenceSet("4:5,1:2")
|
|
998
|
+
# set.append("6:6") # => Net::IMAP::SequenceSet("4:5,1:2,6")
|
|
999
|
+
# set.append("9:8") # => Net::IMAP::SequenceSet("4:5,1:2,6,8:9")
|
|
1000
|
+
#
|
|
1001
|
+
# If +entry+ adjacently follows the last entry, they will coalesced:
|
|
1002
|
+
# set = Net::IMAP::SequenceSet.new("2,1,9:10")
|
|
1003
|
+
# set.append(11..12) # => Net::IMAP::SequenceSet("2,1,9:12")
|
|
1004
|
+
#
|
|
1005
|
+
# Non-normalized sets store the string <em>in addition to</em> an internal
|
|
1006
|
+
# normalized uint32 set representation. This can more than double memory
|
|
1007
|
+
# usage, so large sets should avoid using #append unless preserving order
|
|
1008
|
+
# is required. See SequenceSet@Ordered+and+Normalized+sets.
|
|
1009
|
+
#
|
|
1010
|
+
# Related: #add, #merge, #union
|
|
1011
|
+
def append(entry)
|
|
1012
|
+
modifying! # short-circuit before import_minmax
|
|
1013
|
+
minmax = import_minmax entry
|
|
1014
|
+
adj = minmax.first - 1
|
|
1015
|
+
if @string.nil? && (runs.empty? || max_num <= adj)
|
|
1016
|
+
# append to elements or coalesce with last element
|
|
1017
|
+
add_minmax minmax
|
|
1018
|
+
return self
|
|
1019
|
+
elsif @string.nil?
|
|
1020
|
+
# generate string for out-of-order append
|
|
1021
|
+
head, comma = normalized_string, ","
|
|
1022
|
+
else
|
|
1023
|
+
# @string already exists... maybe coalesce with last entry
|
|
1024
|
+
head, comma, last_entry = @string.rpartition(",")
|
|
1025
|
+
last_min, last_max = import_minmax last_entry
|
|
1026
|
+
if last_max == adj
|
|
1027
|
+
# coalesce with last entry
|
|
1028
|
+
minmax[0] = last_min
|
|
1029
|
+
else
|
|
1030
|
+
# append to existing string
|
|
1031
|
+
head, comma = @string, ","
|
|
1032
|
+
end
|
|
1033
|
+
end
|
|
1034
|
+
entry = export_minmax minmax
|
|
1035
|
+
add_minmax minmax
|
|
1036
|
+
@string = -"#{head}#{comma}#{entry}"
|
|
718
1037
|
self
|
|
719
1038
|
end
|
|
720
1039
|
|
|
721
|
-
# :call-seq: add?(
|
|
1040
|
+
# :call-seq: add?(element) -> self or nil
|
|
722
1041
|
#
|
|
723
1042
|
# Adds a range or number to the set and returns +self+. Returns +nil+
|
|
724
|
-
# when the
|
|
1043
|
+
# when the element is already included in the set.
|
|
725
1044
|
#
|
|
726
1045
|
# #string will be regenerated. Use #merge to add many elements at once.
|
|
727
1046
|
#
|
|
728
1047
|
# Related: #add, #merge, #union, #include?
|
|
729
|
-
def add?(
|
|
730
|
-
|
|
1048
|
+
def add?(element)
|
|
1049
|
+
modifying! # short-circuit before include?
|
|
1050
|
+
add element unless include? element
|
|
731
1051
|
end
|
|
732
1052
|
|
|
733
|
-
# :call-seq: delete(
|
|
1053
|
+
# :call-seq: delete(element) -> self
|
|
734
1054
|
#
|
|
735
1055
|
# Deletes the given range or number from the set and returns +self+.
|
|
736
1056
|
#
|
|
@@ -738,8 +1058,9 @@ module Net
|
|
|
738
1058
|
# many elements at once.
|
|
739
1059
|
#
|
|
740
1060
|
# Related: #delete?, #delete_at, #subtract, #difference
|
|
741
|
-
def delete(
|
|
742
|
-
|
|
1061
|
+
def delete(element)
|
|
1062
|
+
modifying! # short-circuit before import_run
|
|
1063
|
+
subtract_run import_run element
|
|
743
1064
|
normalize!
|
|
744
1065
|
end
|
|
745
1066
|
|
|
@@ -775,16 +1096,18 @@ module Net
|
|
|
775
1096
|
# #string will be regenerated after deletion.
|
|
776
1097
|
#
|
|
777
1098
|
# Related: #delete, #delete_at, #subtract, #difference, #disjoint?
|
|
778
|
-
def delete?(
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
1099
|
+
def delete?(element)
|
|
1100
|
+
modifying! # short-circuit before import_minmax
|
|
1101
|
+
element = input_try_convert(element)
|
|
1102
|
+
minmax = import_minmax element
|
|
1103
|
+
if number_input?(element)
|
|
1104
|
+
return unless include_minmax? minmax
|
|
1105
|
+
subtract_minmax minmax
|
|
783
1106
|
normalize!
|
|
784
|
-
|
|
1107
|
+
export_num minmax.first
|
|
785
1108
|
else
|
|
786
1109
|
copy = dup
|
|
787
|
-
|
|
1110
|
+
subtract_minmax minmax
|
|
788
1111
|
normalize!
|
|
789
1112
|
copy if copy.subtract(self).valid?
|
|
790
1113
|
end
|
|
@@ -816,37 +1139,34 @@ module Net
|
|
|
816
1139
|
#
|
|
817
1140
|
# Related: #slice, #delete_at, #delete, #delete?, #subtract, #difference
|
|
818
1141
|
def slice!(index, length = nil)
|
|
1142
|
+
modifying! # short-circuit before slice
|
|
819
1143
|
deleted = slice(index, length) and subtract deleted
|
|
820
1144
|
deleted
|
|
821
1145
|
end
|
|
822
1146
|
|
|
823
|
-
# Merges all of the elements that appear in any of
|
|
824
|
-
# set, and returns +self+.
|
|
1147
|
+
# In-place set #union. Merges all of the elements that appear in any of
|
|
1148
|
+
# the +sets+ into this set, and returns +self+.
|
|
825
1149
|
#
|
|
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.
|
|
1150
|
+
# The +sets+ may be any objects that would be accepted by ::new.
|
|
830
1151
|
#
|
|
831
|
-
# #string will be regenerated after all
|
|
1152
|
+
# #string will be regenerated after all sets have been merged.
|
|
832
1153
|
#
|
|
833
1154
|
# Related: #add, #add?, #union
|
|
834
|
-
def merge(*
|
|
835
|
-
|
|
1155
|
+
def merge(*sets)
|
|
1156
|
+
modifying! # short-circuit before import_runs
|
|
1157
|
+
add_runs import_runs sets
|
|
836
1158
|
normalize!
|
|
837
1159
|
end
|
|
838
1160
|
|
|
839
|
-
# Removes all of the elements that appear in
|
|
840
|
-
#
|
|
1161
|
+
# In-place set #difference. Removes all of the elements that appear in
|
|
1162
|
+
# any of the given +sets+ from this set, and returns +self+.
|
|
841
1163
|
#
|
|
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.
|
|
1164
|
+
# The +sets+ may be any objects that would be accepted by ::new.
|
|
846
1165
|
#
|
|
847
1166
|
# Related: #difference
|
|
848
|
-
def subtract(*
|
|
849
|
-
|
|
1167
|
+
def subtract(*sets)
|
|
1168
|
+
modifying! # short-circuit before import_runs
|
|
1169
|
+
subtract_runs import_runs sets
|
|
850
1170
|
normalize!
|
|
851
1171
|
end
|
|
852
1172
|
|
|
@@ -858,21 +1178,21 @@ module Net
|
|
|
858
1178
|
# This is useful when the given order is significant, for example in a
|
|
859
1179
|
# ESEARCH response to IMAP#sort.
|
|
860
1180
|
#
|
|
1181
|
+
# See SequenceSet@Ordered+and+Normalized+sets.
|
|
1182
|
+
#
|
|
861
1183
|
# Related: #each_entry, #elements
|
|
862
1184
|
def entries; each_entry.to_a end
|
|
863
1185
|
|
|
864
1186
|
# Returns an array of ranges and integers and <tt>:*</tt>.
|
|
865
1187
|
#
|
|
866
1188
|
# The returned elements are sorted and coalesced, even when the input
|
|
867
|
-
# #string is not. <tt>*</tt> will sort last. See #normalize
|
|
1189
|
+
# #string is not. <tt>*</tt> will sort last. See #normalize,
|
|
1190
|
+
# SequenceSet@Ordered+and+Normalized+sets.
|
|
868
1191
|
#
|
|
869
1192
|
# By itself, <tt>*</tt> translates to <tt>:*</tt>. A range containing
|
|
870
1193
|
# <tt>*</tt> translates to an endless range. Use #limit to translate both
|
|
871
1194
|
# cases to a maximum value.
|
|
872
1195
|
#
|
|
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
1196
|
# Net::IMAP::SequenceSet["2,5:9,6,*,12:11"].elements
|
|
877
1197
|
# #=> [2, 5..9, 11..12, :*]
|
|
878
1198
|
#
|
|
@@ -883,15 +1203,13 @@ module Net
|
|
|
883
1203
|
# Returns an array of ranges
|
|
884
1204
|
#
|
|
885
1205
|
# The returned elements are sorted and coalesced, even when the input
|
|
886
|
-
# #string is not. <tt>*</tt> will sort last. See #normalize
|
|
1206
|
+
# #string is not. <tt>*</tt> will sort last. See #normalize,
|
|
1207
|
+
# SequenceSet@Ordered+and+Normalized+sets.
|
|
887
1208
|
#
|
|
888
1209
|
# <tt>*</tt> translates to an endless range. By itself, <tt>*</tt>
|
|
889
1210
|
# translates to <tt>:*..</tt>. Use #limit to set <tt>*</tt> to a maximum
|
|
890
1211
|
# value.
|
|
891
1212
|
#
|
|
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
1213
|
# Net::IMAP::SequenceSet["2,5:9,6,*,12:11"].ranges
|
|
896
1214
|
# #=> [2..2, 5..9, 11..12, :*..]
|
|
897
1215
|
# Net::IMAP::SequenceSet["123,999:*,456:789"].ranges
|
|
@@ -903,7 +1221,7 @@ module Net
|
|
|
903
1221
|
# Returns a sorted array of all of the number values in the sequence set.
|
|
904
1222
|
#
|
|
905
1223
|
# The returned numbers are sorted and de-duplicated, even when the input
|
|
906
|
-
# #string is not. See #normalize.
|
|
1224
|
+
# #string is not. See #normalize, SequenceSet@Ordered+and+Normalized+sets.
|
|
907
1225
|
#
|
|
908
1226
|
# Net::IMAP::SequenceSet["2,5:9,6,12:11"].numbers
|
|
909
1227
|
# #=> [2, 5, 6, 7, 8, 9, 11, 12]
|
|
@@ -935,54 +1253,34 @@ module Net
|
|
|
935
1253
|
# no sorting, deduplication, or coalescing. When #string is in its
|
|
936
1254
|
# normalized form, this will yield the same values as #each_element.
|
|
937
1255
|
#
|
|
1256
|
+
# See SequenceSet@Ordered+and+Normalized+sets.
|
|
1257
|
+
#
|
|
938
1258
|
# Related: #entries, #each_element
|
|
939
1259
|
def each_entry(&block) # :yields: integer or range or :*
|
|
940
1260
|
return to_enum(__method__) unless block_given?
|
|
941
|
-
|
|
1261
|
+
each_entry_run do yield export_run_entry _1 end
|
|
942
1262
|
end
|
|
943
1263
|
|
|
944
1264
|
# Yields each number or range (or <tt>:*</tt>) in #elements to the block
|
|
945
1265
|
# and returns self. Returns an enumerator when called without a block.
|
|
946
1266
|
#
|
|
947
1267
|
# The returned numbers are sorted and de-duplicated, even when the input
|
|
948
|
-
# #string is not. See #normalize.
|
|
1268
|
+
# #string is not. See #normalize, SequenceSet@Ordered+and+Normalized+sets.
|
|
949
1269
|
#
|
|
950
1270
|
# Related: #elements, #each_entry
|
|
951
1271
|
def each_element # :yields: integer or range or :*
|
|
952
1272
|
return to_enum(__method__) unless block_given?
|
|
953
|
-
|
|
1273
|
+
runs.each do yield export_run_entry _1 end
|
|
954
1274
|
self
|
|
955
1275
|
end
|
|
956
1276
|
|
|
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
|
|
966
|
-
self
|
|
967
|
-
end
|
|
968
|
-
|
|
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
1277
|
# Yields each range in #ranges to the block and returns self.
|
|
980
1278
|
# Returns an enumerator when called without a block.
|
|
981
1279
|
#
|
|
982
1280
|
# Related: #ranges
|
|
983
1281
|
def each_range # :yields: range
|
|
984
1282
|
return to_enum(__method__) unless block_given?
|
|
985
|
-
|
|
1283
|
+
minmaxes.each do |min, max|
|
|
986
1284
|
if min == STAR_INT then yield :*..
|
|
987
1285
|
elsif max == STAR_INT then yield min..
|
|
988
1286
|
else yield min..max
|
|
@@ -1001,7 +1299,7 @@ module Net
|
|
|
1001
1299
|
def each_number(&block) # :yields: integer
|
|
1002
1300
|
return to_enum(__method__) unless block_given?
|
|
1003
1301
|
raise RangeError, '%s contains "*"' % [self.class] if include_star?
|
|
1004
|
-
|
|
1302
|
+
minmaxes.each do each_number_in_minmax _1, _2, &block end
|
|
1005
1303
|
self
|
|
1006
1304
|
end
|
|
1007
1305
|
|
|
@@ -1015,16 +1313,7 @@ module Net
|
|
|
1015
1313
|
def each_ordered_number(&block)
|
|
1016
1314
|
return to_enum(__method__) unless block_given?
|
|
1017
1315
|
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
|
|
1316
|
+
each_entry_minmax do each_number_in_minmax _1, _2, &block end
|
|
1028
1317
|
end
|
|
1029
1318
|
|
|
1030
1319
|
# Returns a Set with all of the #numbers in the sequence set.
|
|
@@ -1036,35 +1325,103 @@ module Net
|
|
|
1036
1325
|
# Related: #elements, #ranges, #numbers
|
|
1037
1326
|
def to_set; Set.new(numbers) end
|
|
1038
1327
|
|
|
1039
|
-
# Returns the
|
|
1328
|
+
# Returns the number of members in the set.
|
|
1329
|
+
#
|
|
1330
|
+
# Unlike #count, <tt>"*"</tt> is considered to be distinct from
|
|
1331
|
+
# <tt>2³² - 1</tt> (the maximum 32-bit unsigned integer value).
|
|
1332
|
+
#
|
|
1333
|
+
# set = Net::IMAP::SequenceSet[1..10]
|
|
1334
|
+
# set.count #=> 10
|
|
1335
|
+
# set.cardinality #=> 10
|
|
1336
|
+
#
|
|
1337
|
+
# set = Net::IMAP::SequenceSet["4294967295,*"]
|
|
1338
|
+
# set.count #=> 1
|
|
1339
|
+
# set.cardinality #=> 2
|
|
1340
|
+
#
|
|
1341
|
+
# set = Net::IMAP::SequenceSet[1..]
|
|
1342
|
+
# set.count #=> 4294967295
|
|
1343
|
+
# set.cardinality #=> 4294967296
|
|
1344
|
+
#
|
|
1345
|
+
# Related: #count, #count_with_duplicates
|
|
1346
|
+
def cardinality = minmaxes.sum(runs.count) { _2 - _1 }
|
|
1347
|
+
|
|
1348
|
+
# Returns the count of distinct #numbers in the set.
|
|
1349
|
+
#
|
|
1350
|
+
# Unlike #cardinality, <tt>"*"</tt> is considered to be equal to
|
|
1351
|
+
# <tt>2³² - 1</tt> (the maximum 32-bit unsigned integer value).
|
|
1352
|
+
#
|
|
1353
|
+
# set = Net::IMAP::SequenceSet[1..10]
|
|
1354
|
+
# set.count #=> 10
|
|
1355
|
+
# set.cardinality #=> 10
|
|
1040
1356
|
#
|
|
1041
|
-
#
|
|
1042
|
-
#
|
|
1357
|
+
# set = Net::IMAP::SequenceSet["4294967295,*"]
|
|
1358
|
+
# set.count #=> 1
|
|
1359
|
+
# set.cardinality #=> 2
|
|
1043
1360
|
#
|
|
1044
|
-
#
|
|
1361
|
+
# set = Net::IMAP::SequenceSet[1..]
|
|
1362
|
+
# set.count #=> 4294967295
|
|
1363
|
+
# set.cardinality #=> 4294967296
|
|
1364
|
+
#
|
|
1365
|
+
# Related: #cardinality, #count_with_duplicates
|
|
1045
1366
|
def count
|
|
1046
|
-
|
|
1047
|
-
(include_star? && include?(UINT32_MAX) ? -1 : 0)
|
|
1367
|
+
cardinality + (include_star? && include?(UINT32_MAX) ? -1 : 0)
|
|
1048
1368
|
end
|
|
1049
1369
|
|
|
1050
|
-
alias size count
|
|
1051
|
-
|
|
1052
1370
|
# Returns the count of numbers in the ordered #entries, including any
|
|
1053
1371
|
# repeated numbers.
|
|
1054
1372
|
#
|
|
1055
|
-
#
|
|
1056
|
-
#
|
|
1057
|
-
#
|
|
1058
|
-
#
|
|
1059
|
-
#
|
|
1060
|
-
#
|
|
1373
|
+
# When #string is normalized, this returns the same as #count. Like
|
|
1374
|
+
# #count, <tt>"*"</tt> is considered to be equal to <tt>2³² - 1</tt> (the
|
|
1375
|
+
# maximum 32-bit unsigned integer value).
|
|
1376
|
+
#
|
|
1377
|
+
# In a range, <tt>"*"</tt> is _not_ considered a duplicate:
|
|
1378
|
+
# set = Net::IMAP::SequenceSet["4294967295:*"]
|
|
1379
|
+
# set.count_with_duplicates #=> 1
|
|
1380
|
+
# set.size #=> 2
|
|
1381
|
+
# set.count #=> 1
|
|
1382
|
+
# set.cardinality #=> 2
|
|
1383
|
+
#
|
|
1384
|
+
# In a separate entry, <tt>"*"</tt> _is_ considered a duplicate:
|
|
1385
|
+
# set = Net::IMAP::SequenceSet["4294967295,*"]
|
|
1386
|
+
# set.count_with_duplicates #=> 2
|
|
1387
|
+
# set.size #=> 2
|
|
1388
|
+
# set.count #=> 1
|
|
1389
|
+
# set.cardinality #=> 2
|
|
1390
|
+
#
|
|
1391
|
+
# Related: #count, #cardinality, #size, #count_duplicates,
|
|
1392
|
+
# #has_duplicates?, #entries
|
|
1061
1393
|
def count_with_duplicates
|
|
1062
1394
|
return count unless @string
|
|
1063
|
-
|
|
1395
|
+
each_entry_minmax.sum {|min, max|
|
|
1064
1396
|
max - min + ((max == STAR_INT && min != STAR_INT) ? 0 : 1)
|
|
1065
1397
|
}
|
|
1066
1398
|
end
|
|
1067
1399
|
|
|
1400
|
+
# Returns the combined size of the ordered #entries, including any
|
|
1401
|
+
# repeated numbers.
|
|
1402
|
+
#
|
|
1403
|
+
# When #string is normalized, this returns the same as #cardinality.
|
|
1404
|
+
# Like #cardinality, <tt>"*"</tt> is considered to be be distinct from
|
|
1405
|
+
# <tt>2³² - 1</tt> (the maximum 32-bit unsigned integer value).
|
|
1406
|
+
#
|
|
1407
|
+
# set = Net::IMAP::SequenceSet["4294967295:*"]
|
|
1408
|
+
# set.size #=> 2
|
|
1409
|
+
# set.count_with_duplicates #=> 1
|
|
1410
|
+
# set.count #=> 1
|
|
1411
|
+
# set.cardinality #=> 2
|
|
1412
|
+
#
|
|
1413
|
+
# set = Net::IMAP::SequenceSet["4294967295,*"]
|
|
1414
|
+
# set.size #=> 2
|
|
1415
|
+
# set.count_with_duplicates #=> 2
|
|
1416
|
+
# set.count #=> 1
|
|
1417
|
+
# set.cardinality #=> 2
|
|
1418
|
+
#
|
|
1419
|
+
# Related: #cardinality, #count_with_duplicates, #count, #entries
|
|
1420
|
+
def size
|
|
1421
|
+
return cardinality unless @string
|
|
1422
|
+
each_entry_minmax.sum {|min, max| max - min + 1 }
|
|
1423
|
+
end
|
|
1424
|
+
|
|
1068
1425
|
# Returns the count of repeated numbers in the ordered #entries, the
|
|
1069
1426
|
# difference between #count_with_duplicates and #count.
|
|
1070
1427
|
#
|
|
@@ -1082,7 +1439,7 @@ module Net
|
|
|
1082
1439
|
#
|
|
1083
1440
|
# Always returns +false+ when #string is normalized.
|
|
1084
1441
|
#
|
|
1085
|
-
# Related: #entries, #count_with_duplicates, #count_duplicates
|
|
1442
|
+
# Related: #entries, #count_with_duplicates, #count_duplicates
|
|
1086
1443
|
def has_duplicates?
|
|
1087
1444
|
return false unless @string
|
|
1088
1445
|
count_with_duplicates != count
|
|
@@ -1093,10 +1450,10 @@ module Net
|
|
|
1093
1450
|
#
|
|
1094
1451
|
# Related: #[], #at, #find_ordered_index
|
|
1095
1452
|
def find_index(number)
|
|
1096
|
-
number =
|
|
1097
|
-
|
|
1453
|
+
number = import_num number
|
|
1454
|
+
each_minmax_with_index(minmaxes) do |min, max, idx_min|
|
|
1098
1455
|
number < min and return nil
|
|
1099
|
-
number <= max and return
|
|
1456
|
+
number <= max and return export_num(idx_min + (number - min))
|
|
1100
1457
|
end
|
|
1101
1458
|
nil
|
|
1102
1459
|
end
|
|
@@ -1106,38 +1463,15 @@ module Net
|
|
|
1106
1463
|
#
|
|
1107
1464
|
# Related: #find_index
|
|
1108
1465
|
def find_ordered_index(number)
|
|
1109
|
-
number =
|
|
1110
|
-
|
|
1466
|
+
number = import_num number
|
|
1467
|
+
each_minmax_with_index(each_entry_minmax) do |min, max, idx_min|
|
|
1111
1468
|
if min <= number && number <= max
|
|
1112
|
-
return
|
|
1469
|
+
return export_num(idx_min + (number - min))
|
|
1113
1470
|
end
|
|
1114
1471
|
end
|
|
1115
1472
|
nil
|
|
1116
1473
|
end
|
|
1117
1474
|
|
|
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
1475
|
# :call-seq: at(index) -> integer or nil
|
|
1142
1476
|
#
|
|
1143
1477
|
# Returns the number at the given +index+ in the sorted set, without
|
|
@@ -1148,7 +1482,7 @@ module Net
|
|
|
1148
1482
|
#
|
|
1149
1483
|
# Related: #[], #slice, #ordered_at
|
|
1150
1484
|
def at(index)
|
|
1151
|
-
|
|
1485
|
+
seek_number_in_minmaxes(minmaxes, index)
|
|
1152
1486
|
end
|
|
1153
1487
|
|
|
1154
1488
|
# :call-seq: ordered_at(index) -> integer or nil
|
|
@@ -1161,21 +1495,7 @@ module Net
|
|
|
1161
1495
|
#
|
|
1162
1496
|
# Related: #[], #slice, #ordered_at
|
|
1163
1497
|
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
|
|
1498
|
+
seek_number_in_minmaxes(each_entry_minmax, index)
|
|
1179
1499
|
end
|
|
1180
1500
|
|
|
1181
1501
|
# :call-seq:
|
|
@@ -1226,33 +1546,58 @@ module Net
|
|
|
1226
1546
|
|
|
1227
1547
|
alias slice :[]
|
|
1228
1548
|
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1549
|
+
# Returns a copy of +self+ which only contains the numbers above +num+.
|
|
1550
|
+
#
|
|
1551
|
+
# Net::IMAP::SequenceSet["5,10:22,50"].above(10) # to_s => "11:22,50"
|
|
1552
|
+
# Net::IMAP::SequenceSet["5,10:22,50"].above(20) # to_s => "21:22,50
|
|
1553
|
+
# Net::IMAP::SequenceSet["5,10:22,50"].above(30) # to_s => "50"
|
|
1554
|
+
#
|
|
1555
|
+
# This returns the same result as #intersection with <tt>((num+1)..)</tt>
|
|
1556
|
+
# or #difference with <tt>(..num)</tt>.
|
|
1557
|
+
#
|
|
1558
|
+
# Net::IMAP::SequenceSet["5,10:22,50"] & (11..) # to_s => "11:22,50"
|
|
1559
|
+
# Net::IMAP::SequenceSet["5,10:22,50"] - (..10) # to_s => "11:22,50"
|
|
1560
|
+
# Net::IMAP::SequenceSet["5,10:22,50"] & (21..) # to_s => "21:22,50"
|
|
1561
|
+
# Net::IMAP::SequenceSet["5,10:22,50"] - (..20) # to_s => "21:22,50"
|
|
1562
|
+
#
|
|
1563
|
+
# Related: #above, #-, #&
|
|
1564
|
+
def above(num)
|
|
1565
|
+
NumValidator.valid_nz_number?(num) or
|
|
1566
|
+
raise ArgumentError, "not a valid sequence set number"
|
|
1567
|
+
difference(..num)
|
|
1237
1568
|
end
|
|
1238
1569
|
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1570
|
+
# Returns a copy of +self+ which only contains numbers below +num+.
|
|
1571
|
+
#
|
|
1572
|
+
# Net::IMAP::SequenceSet["5,10:22,50"].below(10) # to_s => "5"
|
|
1573
|
+
# Net::IMAP::SequenceSet["5,10:22,50"].below(20) # to_s => "5,10:19"
|
|
1574
|
+
# Net::IMAP::SequenceSet["5,10:22,50"].below(30) # to_s => "5,10:22"
|
|
1575
|
+
#
|
|
1576
|
+
# This returns the same result as #intersection with <tt>(..(num-1))</tt>
|
|
1577
|
+
# or #difference with <tt>(num..)</tt>.
|
|
1578
|
+
#
|
|
1579
|
+
# Net::IMAP::SequenceSet["5,10:22,50"] & (..9) # to_s => "5"
|
|
1580
|
+
# Net::IMAP::SequenceSet["5,10:22,50"] - (10..) # to_s => "5"
|
|
1581
|
+
# Net::IMAP::SequenceSet["5,10:22,50"] & (..19) # to_s => "5,10:19"
|
|
1582
|
+
# Net::IMAP::SequenceSet["5,10:22,50"] - (20..) # to_s => "5,10:19"
|
|
1583
|
+
#
|
|
1584
|
+
# When the set does not contain <tt>*</tt>, #below is identical to #limit
|
|
1585
|
+
# with <tt>max: num - 1</tt>. When the set does contain <tt>*</tt>,
|
|
1586
|
+
# #below always drops it from the result. Use #limit when the IMAP
|
|
1587
|
+
# semantics for <tt>*</tt> must be enforced.
|
|
1588
|
+
#
|
|
1589
|
+
# Net::IMAP::SequenceSet["5,10:22,50"].below(30) # to_s => "5,10:22"
|
|
1590
|
+
# Net::IMAP::SequenceSet["5,10:22,50"].limit(max: 29) # to_s => "5,10:22"
|
|
1591
|
+
# Net::IMAP::SequenceSet["5,10:22,*"].below(30) # to_s => "5,10:22"
|
|
1592
|
+
# Net::IMAP::SequenceSet["5,10:22,*"].limit(max: 29) # to_s => "5,10:22,29"
|
|
1593
|
+
#
|
|
1594
|
+
# Related: #above, #-, #&, #limit
|
|
1595
|
+
def below(num)
|
|
1596
|
+
NumValidator.valid_nz_number?(num) or
|
|
1597
|
+
raise ArgumentError, "not a valid sequence set number"
|
|
1598
|
+
difference(num..)
|
|
1252
1599
|
end
|
|
1253
1600
|
|
|
1254
|
-
public
|
|
1255
|
-
|
|
1256
1601
|
# Returns a frozen SequenceSet with <tt>*</tt> converted to +max+, numbers
|
|
1257
1602
|
# and ranges over +max+ removed, and ranges containing +max+ converted to
|
|
1258
1603
|
# end at +max+.
|
|
@@ -1270,8 +1615,9 @@ module Net
|
|
|
1270
1615
|
# Net::IMAP::SequenceSet["500:*"].limit(max: 37)
|
|
1271
1616
|
# #=> Net::IMAP::SequenceSet["37"]
|
|
1272
1617
|
#
|
|
1618
|
+
# Related: #limit!
|
|
1273
1619
|
def limit(max:)
|
|
1274
|
-
max =
|
|
1620
|
+
max = import_num(max)
|
|
1275
1621
|
if empty? then self.class.empty
|
|
1276
1622
|
elsif !include_star? && max < min then self.class.empty
|
|
1277
1623
|
elsif max(star: STAR_INT) <= max then frozen? ? self : dup.freeze
|
|
@@ -1284,76 +1630,206 @@ module Net
|
|
|
1284
1630
|
#
|
|
1285
1631
|
# Related: #limit
|
|
1286
1632
|
def limit!(max:)
|
|
1633
|
+
modifying! # short-circuit before querying
|
|
1287
1634
|
star = include_star?
|
|
1288
|
-
max =
|
|
1289
|
-
|
|
1290
|
-
|
|
1635
|
+
max = import_num(max)
|
|
1636
|
+
subtract_minmax [max + 1, STAR_INT]
|
|
1637
|
+
add_minmax [max, max ] if star
|
|
1291
1638
|
normalize!
|
|
1292
1639
|
end
|
|
1293
1640
|
|
|
1294
1641
|
# :call-seq: complement! -> self
|
|
1295
1642
|
#
|
|
1296
|
-
#
|
|
1297
|
-
# possible values _except_ for those
|
|
1643
|
+
# In-place set #complement. Replaces the contents of this set with its
|
|
1644
|
+
# own #complement. It will contain all possible values _except_ for those
|
|
1645
|
+
# currently in the set.
|
|
1298
1646
|
#
|
|
1299
1647
|
# Related: #complement
|
|
1300
1648
|
def complement!
|
|
1649
|
+
modifying! # short-circuit before querying
|
|
1301
1650
|
return replace(self.class.full) if empty?
|
|
1302
1651
|
return clear if full?
|
|
1303
|
-
flat =
|
|
1652
|
+
flat = minmaxes.flat_map { [_1 - 1, _2 + 1] }
|
|
1304
1653
|
if flat.first < 1 then flat.shift else flat.unshift 1 end
|
|
1305
1654
|
if STAR_INT < flat.last then flat.pop else flat.push STAR_INT end
|
|
1306
|
-
|
|
1655
|
+
replace_minmaxes flat.each_slice(2).to_a
|
|
1307
1656
|
normalize!
|
|
1308
1657
|
end
|
|
1309
1658
|
|
|
1310
|
-
#
|
|
1659
|
+
# In-place set #intersection. Removes any elements that are missing from
|
|
1660
|
+
# +other+ from this set, keeping only the #intersection, and returns
|
|
1661
|
+
# +self+.
|
|
1662
|
+
#
|
|
1663
|
+
# +other+ can be any object that would be accepted by ::new.
|
|
1311
1664
|
#
|
|
1312
|
-
#
|
|
1313
|
-
#
|
|
1665
|
+
# set = Net::IMAP::SequenceSet.new(1..5)
|
|
1666
|
+
# set.intersect! [2, 4, 6]
|
|
1667
|
+
# set #=> Net::IMAP::SequenceSet("2,4")
|
|
1668
|
+
#
|
|
1669
|
+
# Related: #intersection, #intersect?
|
|
1670
|
+
def intersect!(other)
|
|
1671
|
+
modifying! # short-circuit before processing input
|
|
1672
|
+
subtract SequenceSet.new(other).complement!
|
|
1673
|
+
end
|
|
1674
|
+
|
|
1675
|
+
# In-place set #xor. Adds any numbers in +other+ that are missing from
|
|
1676
|
+
# this set, removes any numbers in +other+ that are already in this set,
|
|
1677
|
+
# and returns +self+.
|
|
1678
|
+
#
|
|
1679
|
+
# +other+ can be any object that would be accepted by ::new.
|
|
1680
|
+
#
|
|
1681
|
+
# set = Net::IMAP::SequenceSet.new(1..5)
|
|
1682
|
+
# set.xor! [2, 4, 6]
|
|
1683
|
+
# set #=> Net::IMAP::SequenceSet["1,3,5:6"]
|
|
1684
|
+
#
|
|
1685
|
+
# Related: #xor, #merge, #subtract
|
|
1686
|
+
def xor!(other)
|
|
1687
|
+
modifying! # short-circuit before processing input
|
|
1688
|
+
other = SequenceSet.new(other)
|
|
1689
|
+
copy = dup
|
|
1690
|
+
merge(other).subtract(other.subtract(copy.complement!))
|
|
1691
|
+
end
|
|
1692
|
+
|
|
1693
|
+
# Returns whether #string is fully normalized: entries have been sorted,
|
|
1694
|
+
# deduplicated, and coalesced, and all entries are in normal form. See
|
|
1695
|
+
# SequenceSet@Ordered+and+Normalized+sets.
|
|
1696
|
+
#
|
|
1697
|
+
# Net::IMAP::SequenceSet["1,3,5"].normalized? #=> true
|
|
1698
|
+
# Net::IMAP::SequenceSet["20:30"].normalized? #=> true
|
|
1699
|
+
#
|
|
1700
|
+
# Net::IMAP::SequenceSet["3,5,1"].normalized? #=> false, not sorted
|
|
1701
|
+
# Net::IMAP::SequenceSet["1,2,3"].normalized? #=> false, not coalesced
|
|
1702
|
+
# Net::IMAP::SequenceSet["1:5,2"].normalized? #=> false, repeated number
|
|
1703
|
+
#
|
|
1704
|
+
# Net::IMAP::SequenceSet["1:1"].normalized? #=> false, number as range
|
|
1705
|
+
# Net::IMAP::SequenceSet["5:1"].normalized? #=> false, backwards range
|
|
1706
|
+
#
|
|
1707
|
+
# Returns +true+ if (and only if) #string is equal to #normalized_string:
|
|
1708
|
+
# seqset = Net::IMAP::SequenceSet["1:3,5"]
|
|
1709
|
+
# seqset.string #=> "1:3,5"
|
|
1710
|
+
# seqset.normalized_string #=> "1:3,5"
|
|
1711
|
+
# seqset.entries #=> [1..3, 5]
|
|
1712
|
+
# seqset.elements #=> [1..3, 5]
|
|
1713
|
+
# seqset.normalized? #=> true
|
|
1714
|
+
#
|
|
1715
|
+
# seqset = Net::IMAP::SequenceSet["3,1,2"]
|
|
1716
|
+
# seqset.string #=> "3,1,2"
|
|
1717
|
+
# seqset.normalized_string #=> "1:3"
|
|
1718
|
+
# seqset.entries #=> [3, 1, 2]
|
|
1719
|
+
# seqset.elements #=> [1..3]
|
|
1720
|
+
# seqset.normalized? #=> false
|
|
1721
|
+
#
|
|
1722
|
+
# Can return +false+ even when #entries and #elements are the same:
|
|
1723
|
+
# seqset = Net::IMAP::SequenceSet["5:1"]
|
|
1724
|
+
# seqset.string #=> "5:1"
|
|
1725
|
+
# seqset.normalized_string #=> "1:5"
|
|
1726
|
+
# seqset.entries #=> [1..5]
|
|
1727
|
+
# seqset.elements #=> [1..5]
|
|
1728
|
+
# seqset.normalized? #=> false
|
|
1729
|
+
#
|
|
1730
|
+
# Note that empty sets are normalized, even though they are not #valid?:
|
|
1731
|
+
# seqset = Net::IMAP::SequenceSet.empty
|
|
1732
|
+
# seqset.normalized? #=> true
|
|
1733
|
+
# seqset.valid? #=> false
|
|
1734
|
+
#
|
|
1735
|
+
# Related: #normalize, #normalize!, #normalized_string
|
|
1736
|
+
def normalized?
|
|
1737
|
+
@string.nil? || normal_string?(@string)
|
|
1738
|
+
end
|
|
1739
|
+
|
|
1740
|
+
# Returns a SequenceSet with a normalized string representation: entries
|
|
1741
|
+
# have been sorted, deduplicated, and coalesced, and all entries
|
|
1742
|
+
# are in normal form. Returns +self+ for frozen normalized sets, and a
|
|
1743
|
+
# normalized duplicate otherwise.
|
|
1744
|
+
#
|
|
1745
|
+
# See SequenceSet@Ordered+and+Normalized+sets.
|
|
1314
1746
|
#
|
|
1315
1747
|
# Net::IMAP::SequenceSet["1:5,3:7,10:9,10:11"].normalize
|
|
1316
1748
|
# #=> Net::IMAP::SequenceSet["1:7,9:11"]
|
|
1317
1749
|
#
|
|
1318
|
-
# Related: #normalize!, #normalized_string
|
|
1750
|
+
# Related: #normalize!, #normalized_string, #normalized?
|
|
1319
1751
|
def normalize
|
|
1320
|
-
|
|
1321
|
-
return self if frozen? && str == string
|
|
1322
|
-
remain_frozen dup.instance_exec { @string = str&.-@; self }
|
|
1752
|
+
frozen? && normalized? ? self : remain_frozen(dup.normalize!)
|
|
1323
1753
|
end
|
|
1324
1754
|
|
|
1325
1755
|
# Resets #string to be sorted, deduplicated, and coalesced. Returns
|
|
1326
|
-
# +self+.
|
|
1756
|
+
# +self+. See SequenceSet@Ordered+and+Normalized+sets.
|
|
1327
1757
|
#
|
|
1328
|
-
# Related: #normalize, #normalized_string
|
|
1758
|
+
# Related: #normalize, #normalized_string, #normalized?
|
|
1329
1759
|
def normalize!
|
|
1760
|
+
modifying! # redundant check (normalizes the error message for JRuby)
|
|
1330
1761
|
@string = nil
|
|
1331
1762
|
self
|
|
1332
1763
|
end
|
|
1333
1764
|
|
|
1334
1765
|
# Returns a normalized +sequence-set+ string representation, sorted
|
|
1335
1766
|
# and deduplicated. Adjacent or overlapping elements will be merged into
|
|
1336
|
-
# a single larger range.
|
|
1767
|
+
# a single larger range. See SequenceSet@Ordered+and+Normalized+sets.
|
|
1337
1768
|
#
|
|
1338
1769
|
# Net::IMAP::SequenceSet["1:5,3:7,10:9,10:11"].normalized_string
|
|
1339
1770
|
# #=> "1:7,9:11"
|
|
1340
1771
|
#
|
|
1341
|
-
#
|
|
1772
|
+
# Returns +nil+ when the set is empty.
|
|
1773
|
+
#
|
|
1774
|
+
# Related: #normalize!, #normalize, #string, #to_s, #normalized?
|
|
1342
1775
|
def normalized_string
|
|
1343
|
-
|
|
1776
|
+
export_runs(runs) unless runs.empty?
|
|
1344
1777
|
end
|
|
1345
1778
|
|
|
1779
|
+
# Returns an inspection string for the SequenceSet.
|
|
1780
|
+
#
|
|
1781
|
+
# Net::IMAP::SequenceSet.new.inspect
|
|
1782
|
+
# #=> "Net::IMAP::SequenceSet()"
|
|
1783
|
+
#
|
|
1784
|
+
# Net::IMAP::SequenceSet(1..5, 1024, 15, 2000).inspect
|
|
1785
|
+
# #=> 'Net::IMAP::SequenceSet("1:5,15,1024,2000")'
|
|
1786
|
+
#
|
|
1787
|
+
# Frozen sets have slightly different output:
|
|
1788
|
+
#
|
|
1789
|
+
# Net::IMAP::SequenceSet.empty.inspect
|
|
1790
|
+
# #=> "Net::IMAP::SequenceSet.empty"
|
|
1791
|
+
#
|
|
1792
|
+
# Net::IMAP::SequenceSet[1..5, 1024, 15, 2000].inspect
|
|
1793
|
+
# #=> 'Net::IMAP::SequenceSet["1:5,15,1024,2000"]'
|
|
1794
|
+
#
|
|
1795
|
+
# Large sets (by number of #entries) have abridged output, with only the
|
|
1796
|
+
# first and last entries:
|
|
1797
|
+
#
|
|
1798
|
+
# Net::IMAP::SequenceSet(((1..5000) % 2).to_a).inspect
|
|
1799
|
+
# #=> #<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">
|
|
1800
|
+
#
|
|
1801
|
+
# Related: #to_s, #string
|
|
1346
1802
|
def inspect
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1803
|
+
case (count = count_entries)
|
|
1804
|
+
when 0
|
|
1805
|
+
(frozen? ? "%s.empty" : "%s()") % [self.class]
|
|
1806
|
+
when ..INSPECT_MAX_LEN
|
|
1807
|
+
(frozen? ? "%s[%p]" : "%s(%p)") % [self.class, to_s]
|
|
1351
1808
|
else
|
|
1352
|
-
|
|
1809
|
+
if @string
|
|
1810
|
+
head = @string[INSPECT_ABRIDGED_HEAD_RE]
|
|
1811
|
+
tail = @string[INSPECT_ABRIDGED_TAIL_RE]
|
|
1812
|
+
else
|
|
1813
|
+
head = export_runs(runs.first(INSPECT_TRUNCATE_LEN)) + ","
|
|
1814
|
+
tail = "," + export_runs(runs.last(INSPECT_TRUNCATE_LEN))
|
|
1815
|
+
end
|
|
1816
|
+
'#<%s %d entries "%s...(%d entries omitted)...%s"%s>' % [
|
|
1817
|
+
self.class, count,
|
|
1818
|
+
head, count - INSPECT_TRUNCATE_LEN * 2, tail,
|
|
1819
|
+
frozen? ? " (frozen)" : "",
|
|
1820
|
+
]
|
|
1353
1821
|
end
|
|
1354
1822
|
end
|
|
1355
1823
|
|
|
1356
|
-
|
|
1824
|
+
##
|
|
1825
|
+
# :method: to_sequence_set
|
|
1826
|
+
# :call-seq: to_sequence_set -> self
|
|
1827
|
+
#
|
|
1828
|
+
# Returns +self+
|
|
1829
|
+
#
|
|
1830
|
+
# Related: ::try_convert
|
|
1831
|
+
|
|
1832
|
+
# :nodoc: (work around rdoc bug)
|
|
1357
1833
|
alias to_sequence_set itself
|
|
1358
1834
|
|
|
1359
1835
|
# Unstable API: currently for internal use only (Net::IMAP#validate_data)
|
|
@@ -1367,49 +1843,69 @@ module Net
|
|
|
1367
1843
|
imap.__send__(:put_string, valid_string)
|
|
1368
1844
|
end
|
|
1369
1845
|
|
|
1846
|
+
# For YAML serialization
|
|
1847
|
+
def encode_with(coder) # :nodoc:
|
|
1848
|
+
# we can perfectly reconstruct from the string
|
|
1849
|
+
coder['string'] = to_s
|
|
1850
|
+
end
|
|
1851
|
+
|
|
1852
|
+
# For YAML deserialization
|
|
1853
|
+
def init_with(coder) # :nodoc:
|
|
1854
|
+
@set_data = new_set_data
|
|
1855
|
+
self.string = coder['string']
|
|
1856
|
+
end
|
|
1857
|
+
|
|
1858
|
+
# :stopdoc:
|
|
1370
1859
|
protected
|
|
1371
1860
|
|
|
1372
|
-
attr_reader :
|
|
1861
|
+
attr_reader :set_data
|
|
1862
|
+
|
|
1863
|
+
alias runs set_data
|
|
1864
|
+
alias minmaxes runs
|
|
1373
1865
|
|
|
1374
1866
|
private
|
|
1375
1867
|
|
|
1376
1868
|
def remain_frozen(set) frozen? ? set.freeze : set end
|
|
1869
|
+
def remain_frozen_empty; frozen? ? SequenceSet.empty : SequenceSet.new end
|
|
1377
1870
|
|
|
1378
1871
|
# frozen clones are shallow copied
|
|
1379
1872
|
def initialize_clone(other)
|
|
1380
|
-
other.
|
|
1873
|
+
@set_data = other.dup_set_data unless other.frozen?
|
|
1874
|
+
super
|
|
1381
1875
|
end
|
|
1382
1876
|
|
|
1383
1877
|
def initialize_dup(other)
|
|
1384
|
-
@
|
|
1385
|
-
@string = other.string&.-@
|
|
1878
|
+
@set_data = other.dup_set_data
|
|
1386
1879
|
super
|
|
1387
1880
|
end
|
|
1388
1881
|
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1882
|
+
######################################################################{{{2
|
|
1883
|
+
# Import methods
|
|
1884
|
+
|
|
1885
|
+
def import_minmax(input)
|
|
1886
|
+
entry = input_try_convert input
|
|
1887
|
+
case entry
|
|
1888
|
+
when *STARS, Integer then [int = import_num(entry), int]
|
|
1889
|
+
when Range then import_range_minmax(entry)
|
|
1890
|
+
when String then parse_minmax(entry)
|
|
1395
1891
|
else
|
|
1396
|
-
raise DataFormatError, "expected number or range, got %p" % [
|
|
1892
|
+
raise DataFormatError, "expected number or range, got %p" % [input]
|
|
1397
1893
|
end
|
|
1398
1894
|
end
|
|
1895
|
+
alias import_run import_minmax
|
|
1399
1896
|
|
|
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
|
|
1897
|
+
def import_runs(input)
|
|
1898
|
+
set = input_try_convert input
|
|
1899
|
+
case set
|
|
1900
|
+
when *STARS, Integer, Range then [import_run(set)]
|
|
1901
|
+
when String then parse_runs set
|
|
1902
|
+
when SequenceSet then set.runs
|
|
1903
|
+
when Set then set.map { [import_num(_1)] * 2 }
|
|
1904
|
+
when Array then set.flat_map { import_runs _1 }
|
|
1408
1905
|
when nil then []
|
|
1409
1906
|
else
|
|
1410
|
-
raise DataFormatError,
|
|
1411
|
-
|
|
1412
|
-
"got %p" % [obj]
|
|
1907
|
+
raise DataFormatError, "expected nz-number, range, '*', Set, Array; " \
|
|
1908
|
+
"got %p" % [input]
|
|
1413
1909
|
end
|
|
1414
1910
|
end
|
|
1415
1911
|
|
|
@@ -1422,9 +1918,17 @@ module Net
|
|
|
1422
1918
|
input
|
|
1423
1919
|
end
|
|
1424
1920
|
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1921
|
+
# NOTE: input_try_convert must be called on input first
|
|
1922
|
+
def number_input?(input)
|
|
1923
|
+
case input
|
|
1924
|
+
when *STARS, Integer then true
|
|
1925
|
+
when String then !input.include?(/[:,]/)
|
|
1926
|
+
end
|
|
1927
|
+
end
|
|
1928
|
+
|
|
1929
|
+
def import_range_minmax(range)
|
|
1930
|
+
first = import_num(range.begin || 1)
|
|
1931
|
+
last = import_num(range.end || :*)
|
|
1428
1932
|
last -= 1 if range.exclude_end? && range.end && last != STAR_INT
|
|
1429
1933
|
unless first <= last
|
|
1430
1934
|
raise DataFormatError, "invalid range for sequence-set: %p" % [range]
|
|
@@ -1432,67 +1936,260 @@ module Net
|
|
|
1432
1936
|
[first, last]
|
|
1433
1937
|
end
|
|
1434
1938
|
|
|
1435
|
-
def
|
|
1436
|
-
def
|
|
1939
|
+
def import_num(obj) STARS.include?(obj) ? STAR_INT : nz_number(obj) end
|
|
1940
|
+
def nz_number(num) = NumValidator.coerce_nz_number(num)
|
|
1941
|
+
|
|
1942
|
+
######################################################################{{{2
|
|
1943
|
+
# Export methods
|
|
1944
|
+
|
|
1945
|
+
def export_num(num) num == STAR_INT ? :* : num end
|
|
1946
|
+
|
|
1947
|
+
def export_minmaxes(minmaxes)
|
|
1948
|
+
-minmaxes.map { export_minmax _1 }.join(",")
|
|
1949
|
+
end
|
|
1950
|
+
|
|
1951
|
+
def export_minmax(minmax) minmax.uniq.map { export_num _1 }.join(":") end
|
|
1952
|
+
|
|
1953
|
+
alias export_runs export_minmaxes
|
|
1954
|
+
alias export_run export_minmax
|
|
1955
|
+
|
|
1956
|
+
def export_minmax_entry((min, max))
|
|
1957
|
+
if min == STAR_INT then :*
|
|
1958
|
+
elsif max == STAR_INT then min..
|
|
1959
|
+
elsif min == max then min
|
|
1960
|
+
else min..max
|
|
1961
|
+
end
|
|
1962
|
+
end
|
|
1963
|
+
alias export_run_entry export_minmax_entry
|
|
1964
|
+
|
|
1965
|
+
def each_number_in_minmax(min, max, &block)
|
|
1966
|
+
if min == STAR_INT then yield :*
|
|
1967
|
+
elsif min == max then yield min
|
|
1968
|
+
elsif max != STAR_INT then (min..max).each(&block)
|
|
1969
|
+
else
|
|
1970
|
+
raise RangeError, "#{SequenceSet} cannot enumerate range with '*'"
|
|
1971
|
+
end
|
|
1972
|
+
end
|
|
1973
|
+
|
|
1974
|
+
######################################################################{{{2
|
|
1975
|
+
# Parse methods
|
|
1976
|
+
|
|
1977
|
+
def parse_runs(str) str.split(",", -1).map! { parse_run _1 } end
|
|
1978
|
+
def parse_minmax(str) parse_entry(str).minmax end
|
|
1979
|
+
alias parse_run parse_minmax
|
|
1437
1980
|
|
|
1438
|
-
def
|
|
1439
|
-
def str_to_tuples(str) str.split(",", -1).map! { str_to_tuple _1 } end
|
|
1440
|
-
def str_to_tuple(str)
|
|
1981
|
+
def parse_entry(str)
|
|
1441
1982
|
raise DataFormatError, "invalid sequence set string" if str.empty?
|
|
1442
|
-
str.split(":", 2).map! {
|
|
1983
|
+
str.split(":", 2).map! { import_num _1 }
|
|
1443
1984
|
end
|
|
1444
1985
|
|
|
1445
|
-
|
|
1986
|
+
# yields validated but unsorted [num] or [num, num]
|
|
1987
|
+
def each_parsed_entry(str)
|
|
1988
|
+
return to_enum(__method__, str) unless block_given?
|
|
1989
|
+
str&.split(",", -1) do |entry| yield parse_entry(entry) end
|
|
1990
|
+
end
|
|
1991
|
+
|
|
1992
|
+
def normal_string?(str) normalized_entries? each_parsed_entry str end
|
|
1993
|
+
|
|
1994
|
+
def normalized_entries?(entries)
|
|
1995
|
+
max = nil
|
|
1996
|
+
entries.each do |first, last|
|
|
1997
|
+
return false if last && last <= first # 1:1 or 2:1
|
|
1998
|
+
return false if max && first <= max + 1 # 2,1 or 1,1 or 1,2
|
|
1999
|
+
max = last || first
|
|
2000
|
+
end
|
|
2001
|
+
true
|
|
2002
|
+
end
|
|
2003
|
+
|
|
2004
|
+
######################################################################{{{2
|
|
2005
|
+
# Ordered entry methods
|
|
1446
2006
|
|
|
1447
|
-
def
|
|
1448
|
-
|
|
2007
|
+
def count_entries
|
|
2008
|
+
@string ? @string.count(",") + 1 : runs.count
|
|
2009
|
+
end
|
|
2010
|
+
|
|
2011
|
+
def each_entry_minmax(&block)
|
|
2012
|
+
return to_enum(__method__) unless block_given?
|
|
2013
|
+
if @string
|
|
2014
|
+
@string.split(",") do block.call parse_minmax _1 end
|
|
2015
|
+
else
|
|
2016
|
+
minmaxes.each(&block)
|
|
2017
|
+
end
|
|
2018
|
+
self
|
|
2019
|
+
end
|
|
2020
|
+
alias each_entry_run each_entry_minmax
|
|
2021
|
+
|
|
2022
|
+
######################################################################{{{2
|
|
2023
|
+
# Search methods
|
|
2024
|
+
|
|
2025
|
+
def include_minmax?((min, max)) bsearch_range(min)&.cover?(min..max) end
|
|
2026
|
+
|
|
2027
|
+
def intersect_minmax?((min, max))
|
|
2028
|
+
range = bsearch_range(min) and
|
|
1449
2029
|
range.include?(min) || range.include?(max) || (min..max).cover?(range)
|
|
1450
2030
|
end
|
|
1451
2031
|
|
|
2032
|
+
alias include_run? include_minmax?
|
|
2033
|
+
alias intersect_run? intersect_minmax?
|
|
2034
|
+
|
|
2035
|
+
def bsearch_index(num) = minmaxes.bsearch_index { _2 >= num }
|
|
2036
|
+
def bsearch_minmax(num) = minmaxes.bsearch { _2 >= num }
|
|
2037
|
+
def bsearch_range(num) = (min, max = bsearch_minmax(num)) && (min..max)
|
|
2038
|
+
|
|
2039
|
+
######################################################################{{{2
|
|
2040
|
+
# Number indexing methods
|
|
2041
|
+
|
|
2042
|
+
def seek_number_in_minmaxes(minmaxes, index)
|
|
2043
|
+
index = Integer(index.to_int)
|
|
2044
|
+
if index.negative?
|
|
2045
|
+
reverse_each_minmax_with_index(minmaxes) do |min, max, idx_min, idx_max|
|
|
2046
|
+
idx_min <= index and return export_num(min + (index - idx_min))
|
|
2047
|
+
end
|
|
2048
|
+
else
|
|
2049
|
+
each_minmax_with_index(minmaxes) do |min, _, idx_min, idx_max|
|
|
2050
|
+
index <= idx_max and return export_num(min + (index - idx_min))
|
|
2051
|
+
end
|
|
2052
|
+
end
|
|
2053
|
+
nil
|
|
2054
|
+
end
|
|
2055
|
+
|
|
2056
|
+
def each_minmax_with_index(minmaxes)
|
|
2057
|
+
idx_min = 0
|
|
2058
|
+
minmaxes.each do |min, max|
|
|
2059
|
+
idx_max = idx_min + (max - min)
|
|
2060
|
+
yield min, max, idx_min, idx_max
|
|
2061
|
+
idx_min = idx_max + 1
|
|
2062
|
+
end
|
|
2063
|
+
idx_min
|
|
2064
|
+
end
|
|
2065
|
+
|
|
2066
|
+
def reverse_each_minmax_with_index(minmaxes)
|
|
2067
|
+
idx_max = -1
|
|
2068
|
+
minmaxes.reverse_each do |min, max|
|
|
2069
|
+
yield min, max, (idx_min = idx_max - (max - min)), idx_max
|
|
2070
|
+
idx_max = idx_min - 1
|
|
2071
|
+
end
|
|
2072
|
+
idx_max
|
|
2073
|
+
end
|
|
2074
|
+
|
|
2075
|
+
def slice_length(start, length)
|
|
2076
|
+
start = Integer(start.to_int)
|
|
2077
|
+
length = Integer(length.to_int)
|
|
2078
|
+
raise ArgumentError, "length must be positive" unless length.positive?
|
|
2079
|
+
last = start + length - 1 unless start.negative? && start.abs <= length
|
|
2080
|
+
slice_range(start..last)
|
|
2081
|
+
end
|
|
2082
|
+
|
|
2083
|
+
def slice_range(range)
|
|
2084
|
+
first = range.begin || 0
|
|
2085
|
+
last = range.end || -1
|
|
2086
|
+
if range.exclude_end?
|
|
2087
|
+
return remain_frozen_empty if last.zero?
|
|
2088
|
+
last -= 1 if range.end && last != STAR_INT
|
|
2089
|
+
end
|
|
2090
|
+
if (first * last).positive? && last < first
|
|
2091
|
+
remain_frozen_empty
|
|
2092
|
+
elsif (min = at(first))
|
|
2093
|
+
max = at(last)
|
|
2094
|
+
max = :* if max.nil?
|
|
2095
|
+
if max == :* then self & (min..)
|
|
2096
|
+
elsif min <= max then self & (min..max)
|
|
2097
|
+
else remain_frozen_empty
|
|
2098
|
+
end
|
|
2099
|
+
end
|
|
2100
|
+
end
|
|
2101
|
+
|
|
2102
|
+
######################################################################{{{2
|
|
2103
|
+
# Core set data create/freeze/dup primitives
|
|
2104
|
+
|
|
2105
|
+
def new_set_data = []
|
|
2106
|
+
def freeze_set_data = set_data.each(&:freeze).freeze
|
|
2107
|
+
def dup_set_data = set_data.map { _1.dup }
|
|
2108
|
+
protected :dup_set_data
|
|
2109
|
+
|
|
2110
|
+
######################################################################{{{2
|
|
2111
|
+
# Core set data query/enumeration primitives
|
|
2112
|
+
|
|
2113
|
+
def min_num = minmaxes.first&.first
|
|
2114
|
+
def max_num = minmaxes.last&.last
|
|
2115
|
+
|
|
2116
|
+
def min_at(idx) = minmaxes[idx][0]
|
|
2117
|
+
def max_at(idx) = minmaxes[idx][1]
|
|
2118
|
+
|
|
2119
|
+
######################################################################{{{2
|
|
2120
|
+
# Core set data modification primitives
|
|
2121
|
+
|
|
2122
|
+
def set_min_at(idx, min) = minmaxes[idx][0] = min
|
|
2123
|
+
def set_max_at(idx, max) = minmaxes[idx][1] = max
|
|
2124
|
+
def replace_minmaxes(other) = minmaxes.replace(other)
|
|
2125
|
+
def append_minmax(min, max) = minmaxes << [min, max]
|
|
2126
|
+
def insert_minmax(idx, min, max) = minmaxes.insert idx, [min, max]
|
|
2127
|
+
def delete_run_at(idx) = runs.delete_at(idx)
|
|
2128
|
+
def slice_runs!(...) = runs.slice!(...)
|
|
2129
|
+
def truncate_runs!(idx) = runs.slice!(idx..)
|
|
2130
|
+
|
|
2131
|
+
######################################################################{{{2
|
|
2132
|
+
# Update methods
|
|
2133
|
+
|
|
1452
2134
|
def modifying!
|
|
1453
2135
|
if frozen?
|
|
1454
2136
|
raise FrozenError, "can't modify frozen #{self.class}: %p" % [self]
|
|
1455
2137
|
end
|
|
1456
2138
|
end
|
|
1457
2139
|
|
|
1458
|
-
def
|
|
1459
|
-
|
|
2140
|
+
def add_minmaxes(minmaxes)
|
|
2141
|
+
minmaxes.each do |minmax|
|
|
2142
|
+
add_minmax minmax
|
|
2143
|
+
end
|
|
2144
|
+
self
|
|
2145
|
+
end
|
|
2146
|
+
|
|
2147
|
+
def subtract_minmaxes(minmaxes)
|
|
2148
|
+
minmaxes.each do |minmax|
|
|
2149
|
+
subtract_minmax minmax
|
|
2150
|
+
end
|
|
2151
|
+
self
|
|
2152
|
+
end
|
|
1460
2153
|
|
|
1461
2154
|
#
|
|
1462
|
-
# --|=====| |=====new
|
|
1463
|
-
# ?????????-|=====new
|
|
2155
|
+
# --|=====| |=====new run=======| append
|
|
2156
|
+
# ?????????-|=====new run=======|-|===lower===|-- insert
|
|
1464
2157
|
#
|
|
1465
|
-
# |=====new
|
|
2158
|
+
# |=====new run=======|
|
|
1466
2159
|
# ---------??=======lower=======??--------------- noop
|
|
1467
2160
|
#
|
|
1468
2161
|
# ---------??===lower==|--|==| join remaining
|
|
1469
2162
|
# ---------??===lower==|--|==|----|===upper===|-- join until upper
|
|
1470
2163
|
# ---------??===lower==|--|==|--|=====upper===|-- join to upper
|
|
1471
|
-
def
|
|
2164
|
+
def add_minmax(minmax)
|
|
1472
2165
|
modifying!
|
|
1473
|
-
min, max
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
2166
|
+
min, max = minmax
|
|
2167
|
+
lower_idx = bsearch_index(min - 1)
|
|
2168
|
+
lmin, lmax = min_at(lower_idx), max_at(lower_idx) if lower_idx
|
|
2169
|
+
if lmin.nil? then append_minmax min, max
|
|
2170
|
+
elsif (max + 1) < lmin then insert_minmax lower_idx, min, max
|
|
2171
|
+
else add_coalesced_minmax(lower_idx, lmin, lmax, min, max)
|
|
1478
2172
|
end
|
|
1479
2173
|
end
|
|
1480
2174
|
|
|
1481
|
-
def
|
|
1482
|
-
return if
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
lower_idx
|
|
1486
|
-
return if
|
|
1487
|
-
tmax_adj =
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
2175
|
+
def add_coalesced_minmax(lower_idx, lmin, lmax, min, max)
|
|
2176
|
+
return if lmin <= min && max <= lmax
|
|
2177
|
+
set_min_at lower_idx, (lmin = min) if min < lmin
|
|
2178
|
+
set_max_at lower_idx, (lmax = max) if lmax < max
|
|
2179
|
+
next_idx = lower_idx + 1
|
|
2180
|
+
return if next_idx == runs.count
|
|
2181
|
+
tmax_adj = lmax + 1
|
|
2182
|
+
if (upper_idx = bsearch_index(tmax_adj))
|
|
2183
|
+
if tmax_adj < min_at(upper_idx)
|
|
2184
|
+
upper_idx -= 1
|
|
2185
|
+
else
|
|
2186
|
+
set_max_at lower_idx, max_at(upper_idx)
|
|
2187
|
+
end
|
|
1491
2188
|
end
|
|
1492
|
-
|
|
2189
|
+
slice_runs! next_idx..upper_idx
|
|
1493
2190
|
end
|
|
1494
2191
|
|
|
1495
|
-
# |====
|
|
2192
|
+
# |====subtracted run=======|
|
|
1496
2193
|
# --|====| no more 1. noop
|
|
1497
2194
|
# --|====|---------------------------|====lower====|-- 2. noop
|
|
1498
2195
|
# -------|======lower================|---------------- 3. split
|
|
@@ -1505,61 +2202,59 @@ module Net
|
|
|
1505
2202
|
# -------??=====lower====|--|====| no more 6. delete rest
|
|
1506
2203
|
# -------??=====lower====|--|====|---|====upper====|-- 7. delete until
|
|
1507
2204
|
# -------??=====lower====|--|====|--|=====upper====|-- 8. delete and trim
|
|
1508
|
-
def
|
|
2205
|
+
def subtract_minmax(minmax)
|
|
1509
2206
|
modifying!
|
|
1510
|
-
min, max
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
elsif max <
|
|
1515
|
-
|
|
2207
|
+
min, max = minmax
|
|
2208
|
+
idx = bsearch_index(min)
|
|
2209
|
+
lmin, lmax = min_at(idx), max_at(idx) if idx
|
|
2210
|
+
if lmin.nil? then nil # case 1.
|
|
2211
|
+
elsif max < lmin then nil # case 2.
|
|
2212
|
+
elsif max < lmax then trim_or_split_minmax idx, lmin, min, max
|
|
2213
|
+
else trim_or_delete_minmax idx, lmin, lmax, min, max
|
|
1516
2214
|
end
|
|
1517
2215
|
end
|
|
1518
2216
|
|
|
1519
|
-
def
|
|
1520
|
-
|
|
1521
|
-
|
|
2217
|
+
def trim_or_split_minmax(idx, lmin, tmin, tmax)
|
|
2218
|
+
set_min_at idx, tmax + 1
|
|
2219
|
+
if lmin < tmin # split
|
|
2220
|
+
insert_minmax idx, lmin, tmin - 1
|
|
1522
2221
|
end
|
|
1523
|
-
lower[0] = tmax + 1
|
|
1524
2222
|
end
|
|
1525
2223
|
|
|
1526
|
-
def
|
|
1527
|
-
if
|
|
1528
|
-
|
|
2224
|
+
def trim_or_delete_minmax(lower_idx, lmin, lmax, tmin, tmax)
|
|
2225
|
+
if lmin < tmin # trim lower
|
|
2226
|
+
lmax = set_max_at lower_idx, tmin - 1
|
|
1529
2227
|
lower_idx += 1
|
|
1530
2228
|
end
|
|
1531
|
-
if tmax ==
|
|
1532
|
-
|
|
1533
|
-
elsif (
|
|
1534
|
-
upper_idx
|
|
1535
|
-
|
|
2229
|
+
if tmax == lmax # case 5
|
|
2230
|
+
delete_run_at lower_idx
|
|
2231
|
+
elsif (upper_idx = bsearch_index(tmax + 1))
|
|
2232
|
+
if min_at(upper_idx) <= tmax # case 8
|
|
2233
|
+
set_min_at upper_idx, tmax + 1
|
|
2234
|
+
end
|
|
2235
|
+
slice_runs! lower_idx..upper_idx - 1 # cases 7 and 8
|
|
2236
|
+
else # case 6
|
|
2237
|
+
truncate_runs! lower_idx
|
|
1536
2238
|
end
|
|
1537
|
-
tuples.slice!(lower_idx..upper_idx)
|
|
1538
2239
|
end
|
|
1539
2240
|
|
|
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
|
|
2241
|
+
alias add_runs add_minmaxes
|
|
2242
|
+
alias add_run add_minmax
|
|
2243
|
+
alias subtract_runs subtract_minmaxes
|
|
2244
|
+
alias subtract_run subtract_minmax
|
|
1556
2245
|
|
|
2246
|
+
######################################################################{{{2
|
|
1557
2247
|
# intentionally defined after the class implementation
|
|
1558
2248
|
|
|
2249
|
+
FULL_SET_DATA = [[1, STAR_INT].freeze].freeze
|
|
2250
|
+
private_constant :FULL_SET_DATA
|
|
2251
|
+
|
|
1559
2252
|
EMPTY = new.freeze
|
|
1560
2253
|
FULL = self["1:*"]
|
|
1561
2254
|
private_constant :EMPTY, :FULL
|
|
1562
2255
|
|
|
2256
|
+
# }}}
|
|
2257
|
+
# vim:foldmethod=marker
|
|
1563
2258
|
end
|
|
1564
2259
|
end
|
|
1565
2260
|
end
|