net-imap 0.5.12 → 0.6.2
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 +2 -1
- data/lib/net/imap/command_data.rb +0 -68
- data/lib/net/imap/config/attr_inheritance.rb +14 -1
- data/lib/net/imap/config/attr_version_defaults.rb +93 -0
- data/lib/net/imap/config.rb +210 -122
- data/lib/net/imap/connection_state.rb +1 -1
- data/lib/net/imap/data_encoding.rb +77 -28
- data/lib/net/imap/esearch_result.rb +6 -0
- data/lib/net/imap/response_data.rb +2 -3
- data/lib/net/imap/response_parser.rb +8 -13
- data/lib/net/imap/search_result.rb +6 -0
- data/lib/net/imap/sequence_set.rb +635 -312
- data/lib/net/imap/uidplus_data.rb +2 -63
- data/lib/net/imap.rb +18 -24
- data/net-imap.gemspec +1 -1
- metadata +4 -4
- data/lib/net/imap/data_lite.rb +0 -226
|
@@ -145,6 +145,10 @@ module Net
|
|
|
145
145
|
# #entries and #elements are identical. Use #append to preserve #entries
|
|
146
146
|
# order while modifying a set.
|
|
147
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.
|
|
151
|
+
#
|
|
148
152
|
# == Using <tt>*</tt>
|
|
149
153
|
#
|
|
150
154
|
# \IMAP sequence sets may contain a special value <tt>"*"</tt>, which
|
|
@@ -179,7 +183,7 @@ module Net
|
|
|
179
183
|
#
|
|
180
184
|
# When a set includes <tt>*</tt>, some methods may have surprising behavior.
|
|
181
185
|
#
|
|
182
|
-
# For example, #complement treats <tt>*</tt> as its own
|
|
186
|
+
# For example, #complement treats <tt>*</tt> as its own member. This way,
|
|
183
187
|
# the #intersection of a set and its #complement will always be empty. And
|
|
184
188
|
# <tt>*</tt> is sorted as greater than any other number in the set. This is
|
|
185
189
|
# not how an \IMAP server interprets the set: it will convert <tt>*</tt> to
|
|
@@ -199,7 +203,7 @@ module Net
|
|
|
199
203
|
# (set.limit(max: 4) & (~set).limit(max: 4)).to_a => [4]
|
|
200
204
|
#
|
|
201
205
|
# When counting the number of numbers in a set, <tt>*</tt> will be counted
|
|
202
|
-
#
|
|
206
|
+
# as if it were equal to UINT32_MAX:
|
|
203
207
|
# UINT32_MAX = 2**32 - 1
|
|
204
208
|
# Net::IMAP::SequenceSet["*"].count => 1
|
|
205
209
|
# Net::IMAP::SequenceSet[1..UINT32_MAX - 1, :*].count => UINT32_MAX
|
|
@@ -208,6 +212,12 @@ module Net
|
|
|
208
212
|
# Net::IMAP::SequenceSet[UINT32_MAX, :*].count => 1
|
|
209
213
|
# Net::IMAP::SequenceSet[UINT32_MAX..].count => 1
|
|
210
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
|
+
#
|
|
211
221
|
# == What's here?
|
|
212
222
|
#
|
|
213
223
|
# SequenceSet provides methods for:
|
|
@@ -271,8 +281,10 @@ module Net
|
|
|
271
281
|
# occurrence in entries.
|
|
272
282
|
#
|
|
273
283
|
# <i>Set cardinality:</i>
|
|
274
|
-
# - #
|
|
275
|
-
#
|
|
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.
|
|
276
288
|
# - #empty?: Returns whether the set has no members. \IMAP syntax does not
|
|
277
289
|
# allow empty sequence sets.
|
|
278
290
|
# - #valid?: Returns whether the set has any members.
|
|
@@ -280,12 +292,18 @@ module Net
|
|
|
280
292
|
# <tt>*</tt>.
|
|
281
293
|
#
|
|
282
294
|
# <i>Denormalized properties:</i>
|
|
295
|
+
# - #normalized?: Returns whether #entries are sorted, deduplicated, and
|
|
296
|
+
# coalesced, and all #string entries are in normalized form.
|
|
283
297
|
# - #has_duplicates?: Returns whether the ordered entries repeat any
|
|
284
298
|
# numbers.
|
|
285
|
-
# - #
|
|
286
|
-
#
|
|
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.
|
|
287
302
|
# - #count_with_duplicates: Returns the count of numbers in the ordered
|
|
288
|
-
# 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.
|
|
289
307
|
#
|
|
290
308
|
# === Methods for Iterating
|
|
291
309
|
#
|
|
@@ -332,7 +350,7 @@ module Net
|
|
|
332
350
|
# given maximum value and removed all members over that maximum.
|
|
333
351
|
#
|
|
334
352
|
# === Methods for Assigning
|
|
335
|
-
# These methods add or replace
|
|
353
|
+
# These methods add or replace numbers in +self+.
|
|
336
354
|
#
|
|
337
355
|
# <i>Normalized (sorted and coalesced):</i>
|
|
338
356
|
#
|
|
@@ -341,8 +359,12 @@ module Net
|
|
|
341
359
|
# - #add (aliased as #<<): Adds a given element to the set; returns +self+.
|
|
342
360
|
# - #add?: If the given element is not fully included the set, adds it and
|
|
343
361
|
# returns +self+; otherwise, returns +nil+.
|
|
344
|
-
# - #merge: Adds all members of the given sets into
|
|
345
|
-
#
|
|
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+.
|
|
346
368
|
#
|
|
347
369
|
# <i>Order preserving:</i>
|
|
348
370
|
#
|
|
@@ -355,7 +377,7 @@ module Net
|
|
|
355
377
|
# of a given object.
|
|
356
378
|
#
|
|
357
379
|
# === Methods for Deleting
|
|
358
|
-
# These methods remove
|
|
380
|
+
# These methods remove numbers from +self+, and update #string to be fully
|
|
359
381
|
# sorted and coalesced.
|
|
360
382
|
#
|
|
361
383
|
# - #clear: Removes all elements in the set; returns +self+.
|
|
@@ -363,10 +385,12 @@ module Net
|
|
|
363
385
|
# - #delete?: If the given element is included in the set, removes it and
|
|
364
386
|
# returns it; otherwise, returns +nil+.
|
|
365
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+.
|
|
366
390
|
# - #slice!: Removes the number or consecutive numbers at a given offset or
|
|
367
391
|
# range of offsets.
|
|
368
|
-
# - #subtract: Removes all members of the given
|
|
369
|
-
# +self+.
|
|
392
|
+
# - #subtract: In-place set #difference. Removes all members of the given
|
|
393
|
+
# sets from this set; returns +self+.
|
|
370
394
|
# - #limit!: Replaces <tt>*</tt> with a given maximum value and removes all
|
|
371
395
|
# members over that maximum; returns +self+.
|
|
372
396
|
#
|
|
@@ -531,12 +555,17 @@ module Net
|
|
|
531
555
|
# combined with set operations (#|, #&, #^, #-, etc) to make new sets.
|
|
532
556
|
#
|
|
533
557
|
# See SequenceSet@Creating+sequence+sets.
|
|
534
|
-
def initialize(input = nil)
|
|
558
|
+
def initialize(input = nil)
|
|
559
|
+
@set_data = new_set_data
|
|
560
|
+
@string = nil
|
|
561
|
+
replace(input) unless input.nil?
|
|
562
|
+
end
|
|
535
563
|
|
|
536
564
|
# Removes all elements and returns self.
|
|
537
565
|
def clear
|
|
538
|
-
modifying! # redundant check
|
|
539
|
-
|
|
566
|
+
modifying! # redundant check (normalizes the error message for JRuby)
|
|
567
|
+
set_data.clear
|
|
568
|
+
@string = nil
|
|
540
569
|
self
|
|
541
570
|
end
|
|
542
571
|
|
|
@@ -549,7 +578,7 @@ module Net
|
|
|
549
578
|
case other
|
|
550
579
|
when SequenceSet then
|
|
551
580
|
modifying! # short circuit before doing any work
|
|
552
|
-
@
|
|
581
|
+
@set_data = other.dup_set_data
|
|
553
582
|
@string = other.instance_variable_get(:@string)
|
|
554
583
|
when String then self.string = other
|
|
555
584
|
else clear; merge other
|
|
@@ -580,7 +609,7 @@ module Net
|
|
|
580
609
|
# the set is updated the string will be normalized.
|
|
581
610
|
#
|
|
582
611
|
# Related: #valid_string, #normalized_string, #to_s, #inspect
|
|
583
|
-
def string; @string
|
|
612
|
+
def string; @string || normalized_string if valid? end
|
|
584
613
|
|
|
585
614
|
# Returns an array with #normalized_string when valid and an empty array
|
|
586
615
|
# otherwise.
|
|
@@ -599,13 +628,18 @@ module Net
|
|
|
599
628
|
clear
|
|
600
629
|
elsif (str = String.try_convert(input))
|
|
601
630
|
modifying! # short-circuit before parsing the string
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
631
|
+
entries = each_parsed_entry(str).to_a
|
|
632
|
+
clear
|
|
633
|
+
if normalized_entries?(entries)
|
|
634
|
+
replace_minmaxes entries.map!(&:minmax)
|
|
635
|
+
else
|
|
636
|
+
add_minmaxes entries.map!(&:minmax)
|
|
637
|
+
@string = -str
|
|
638
|
+
end
|
|
605
639
|
else
|
|
606
640
|
raise ArgumentError, "expected a string or nil, got #{input.class}"
|
|
607
641
|
end
|
|
608
|
-
|
|
642
|
+
input
|
|
609
643
|
end
|
|
610
644
|
|
|
611
645
|
# Returns the \IMAP +sequence-set+ string representation, or an empty
|
|
@@ -618,8 +652,7 @@ module Net
|
|
|
618
652
|
# Freezes and returns the set. A frozen SequenceSet is Ractor-safe.
|
|
619
653
|
def freeze
|
|
620
654
|
return self if frozen?
|
|
621
|
-
|
|
622
|
-
@tuples.each(&:freeze).freeze
|
|
655
|
+
freeze_set_data
|
|
623
656
|
super
|
|
624
657
|
end
|
|
625
658
|
|
|
@@ -641,7 +674,7 @@ module Net
|
|
|
641
674
|
# Related: #eql?, #normalize
|
|
642
675
|
def ==(other)
|
|
643
676
|
self.class == other.class &&
|
|
644
|
-
(to_s == other.to_s ||
|
|
677
|
+
(to_s == other.to_s || set_data == other.set_data)
|
|
645
678
|
end
|
|
646
679
|
|
|
647
680
|
# :call-seq: eql?(other) -> true or false
|
|
@@ -682,7 +715,7 @@ module Net
|
|
|
682
715
|
# object that would be accepted by ::new.
|
|
683
716
|
#
|
|
684
717
|
# Related: #===, #include?, #include_star?, #intersect?
|
|
685
|
-
def cover?(other)
|
|
718
|
+
def cover?(other) import_runs(other).none? { !include_run?(_1) } end
|
|
686
719
|
|
|
687
720
|
# Returns +true+ when a given number or range is in +self+, and +false+
|
|
688
721
|
# otherwise. Returns +nil+ when +number+ isn't a valid SequenceSet
|
|
@@ -709,14 +742,14 @@ module Net
|
|
|
709
742
|
#
|
|
710
743
|
# Related: #include_star?, #cover?, #===, #intersect?
|
|
711
744
|
def include?(element)
|
|
712
|
-
|
|
713
|
-
!!
|
|
745
|
+
run = import_run element rescue nil
|
|
746
|
+
!!include_run?(run) if run
|
|
714
747
|
end
|
|
715
748
|
|
|
716
749
|
alias member? include?
|
|
717
750
|
|
|
718
751
|
# Returns +true+ when the set contains <tt>*</tt>.
|
|
719
|
-
def include_star?;
|
|
752
|
+
def include_star?; max_num == STAR_INT end
|
|
720
753
|
|
|
721
754
|
# Returns +true+ if the set and a given object have any common elements,
|
|
722
755
|
# +false+ otherwise.
|
|
@@ -726,7 +759,7 @@ module Net
|
|
|
726
759
|
#
|
|
727
760
|
# Related: #intersection, #disjoint?, #cover?, #include?
|
|
728
761
|
def intersect?(other)
|
|
729
|
-
valid? &&
|
|
762
|
+
valid? && import_runs(other).any? { intersect_run? _1 }
|
|
730
763
|
end
|
|
731
764
|
alias overlap? intersect?
|
|
732
765
|
|
|
@@ -738,7 +771,7 @@ module Net
|
|
|
738
771
|
#
|
|
739
772
|
# Related: #intersection, #intersect?
|
|
740
773
|
def disjoint?(other)
|
|
741
|
-
empty? ||
|
|
774
|
+
empty? || import_runs(other).none? { intersect_run? _1 }
|
|
742
775
|
end
|
|
743
776
|
|
|
744
777
|
# :call-seq:
|
|
@@ -755,8 +788,12 @@ module Net
|
|
|
755
788
|
# Related: #min, #minmax, #slice
|
|
756
789
|
def max(count = nil, star: :*)
|
|
757
790
|
if count
|
|
758
|
-
|
|
759
|
-
|
|
791
|
+
if cardinality <= count
|
|
792
|
+
frozen? ? self : dup
|
|
793
|
+
else
|
|
794
|
+
slice(-count..) || remain_frozen_empty
|
|
795
|
+
end
|
|
796
|
+
elsif (val = max_num)
|
|
760
797
|
val == STAR_INT ? star : val
|
|
761
798
|
end
|
|
762
799
|
end
|
|
@@ -776,7 +813,7 @@ module Net
|
|
|
776
813
|
def min(count = nil, star: :*)
|
|
777
814
|
if count
|
|
778
815
|
slice(0...count) || remain_frozen_empty
|
|
779
|
-
elsif (val =
|
|
816
|
+
elsif (val = min_num)
|
|
780
817
|
val != STAR_INT ? val : star
|
|
781
818
|
end
|
|
782
819
|
end
|
|
@@ -794,10 +831,10 @@ module Net
|
|
|
794
831
|
def valid?; !empty? end
|
|
795
832
|
|
|
796
833
|
# Returns true if the set contains no elements
|
|
797
|
-
def empty?;
|
|
834
|
+
def empty?; runs.empty? end
|
|
798
835
|
|
|
799
836
|
# Returns true if the set contains every possible element.
|
|
800
|
-
def full?;
|
|
837
|
+
def full?; set_data == FULL_SET_DATA end
|
|
801
838
|
|
|
802
839
|
# :call-seq:
|
|
803
840
|
# self + other -> sequence set
|
|
@@ -873,9 +910,7 @@ module Net
|
|
|
873
910
|
# * <tt>lhs - (lhs - rhs)</tt>
|
|
874
911
|
# * <tt>lhs - (lhs ^ rhs)</tt>
|
|
875
912
|
# * <tt>lhs ^ (lhs - rhs)</tt>
|
|
876
|
-
def &(other)
|
|
877
|
-
remain_frozen dup.subtract SequenceSet.new(other).complement!
|
|
878
|
-
end
|
|
913
|
+
def &(other) remain_frozen dup.intersect! other end
|
|
879
914
|
alias intersection :&
|
|
880
915
|
|
|
881
916
|
# :call-seq:
|
|
@@ -900,7 +935,7 @@ module Net
|
|
|
900
935
|
# * <tt>(lhs | rhs) - (lhs & rhs)</tt>
|
|
901
936
|
# * <tt>(lhs - rhs) | (rhs - lhs)</tt>
|
|
902
937
|
# * <tt>(lhs ^ other) ^ (other ^ rhs)</tt>
|
|
903
|
-
def ^(other) remain_frozen
|
|
938
|
+
def ^(other) remain_frozen dup.xor! other end
|
|
904
939
|
alias xor :^
|
|
905
940
|
|
|
906
941
|
# :call-seq:
|
|
@@ -939,8 +974,8 @@ module Net
|
|
|
939
974
|
#
|
|
940
975
|
# Related: #add?, #merge, #union, #append
|
|
941
976
|
def add(element)
|
|
942
|
-
modifying! # short-circuit before
|
|
943
|
-
|
|
977
|
+
modifying! # short-circuit before import_run
|
|
978
|
+
add_run import_run element
|
|
944
979
|
normalize!
|
|
945
980
|
end
|
|
946
981
|
alias << add
|
|
@@ -950,16 +985,55 @@ module Net
|
|
|
950
985
|
# Unlike #add, #merge, or #union, the new value is appended to #string.
|
|
951
986
|
# This may result in a #string which has duplicates or is out-of-order.
|
|
952
987
|
#
|
|
953
|
-
#
|
|
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.
|
|
954
1009
|
#
|
|
955
1010
|
# Related: #add, #merge, #union
|
|
956
1011
|
def append(entry)
|
|
957
|
-
modifying! # short-circuit before
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
string
|
|
961
|
-
|
|
962
|
-
|
|
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}"
|
|
963
1037
|
self
|
|
964
1038
|
end
|
|
965
1039
|
|
|
@@ -985,8 +1059,8 @@ module Net
|
|
|
985
1059
|
#
|
|
986
1060
|
# Related: #delete?, #delete_at, #subtract, #difference
|
|
987
1061
|
def delete(element)
|
|
988
|
-
modifying! # short-circuit before
|
|
989
|
-
|
|
1062
|
+
modifying! # short-circuit before import_run
|
|
1063
|
+
subtract_run import_run element
|
|
990
1064
|
normalize!
|
|
991
1065
|
end
|
|
992
1066
|
|
|
@@ -1023,16 +1097,17 @@ module Net
|
|
|
1023
1097
|
#
|
|
1024
1098
|
# Related: #delete, #delete_at, #subtract, #difference, #disjoint?
|
|
1025
1099
|
def delete?(element)
|
|
1026
|
-
modifying! # short-circuit before
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
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
|
|
1031
1106
|
normalize!
|
|
1032
|
-
|
|
1107
|
+
export_num minmax.first
|
|
1033
1108
|
else
|
|
1034
1109
|
copy = dup
|
|
1035
|
-
|
|
1110
|
+
subtract_minmax minmax
|
|
1036
1111
|
normalize!
|
|
1037
1112
|
copy if copy.subtract(self).valid?
|
|
1038
1113
|
end
|
|
@@ -1069,8 +1144,8 @@ module Net
|
|
|
1069
1144
|
deleted
|
|
1070
1145
|
end
|
|
1071
1146
|
|
|
1072
|
-
# Merges all of the elements that appear in any of
|
|
1073
|
-
# 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+.
|
|
1074
1149
|
#
|
|
1075
1150
|
# The +sets+ may be any objects that would be accepted by ::new.
|
|
1076
1151
|
#
|
|
@@ -1078,19 +1153,20 @@ module Net
|
|
|
1078
1153
|
#
|
|
1079
1154
|
# Related: #add, #add?, #union
|
|
1080
1155
|
def merge(*sets)
|
|
1081
|
-
modifying! # short-circuit before
|
|
1082
|
-
|
|
1156
|
+
modifying! # short-circuit before import_runs
|
|
1157
|
+
add_runs import_runs sets
|
|
1083
1158
|
normalize!
|
|
1084
1159
|
end
|
|
1085
1160
|
|
|
1086
|
-
# Removes all of the elements that appear in
|
|
1087
|
-
# the set, and returns +self+.
|
|
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+.
|
|
1088
1163
|
#
|
|
1089
1164
|
# The +sets+ may be any objects that would be accepted by ::new.
|
|
1090
1165
|
#
|
|
1091
1166
|
# Related: #difference
|
|
1092
1167
|
def subtract(*sets)
|
|
1093
|
-
|
|
1168
|
+
modifying! # short-circuit before import_runs
|
|
1169
|
+
subtract_runs import_runs sets
|
|
1094
1170
|
normalize!
|
|
1095
1171
|
end
|
|
1096
1172
|
|
|
@@ -1182,7 +1258,7 @@ module Net
|
|
|
1182
1258
|
# Related: #entries, #each_element
|
|
1183
1259
|
def each_entry(&block) # :yields: integer or range or :*
|
|
1184
1260
|
return to_enum(__method__) unless block_given?
|
|
1185
|
-
|
|
1261
|
+
each_entry_run do yield export_run_entry _1 end
|
|
1186
1262
|
end
|
|
1187
1263
|
|
|
1188
1264
|
# Yields each number or range (or <tt>:*</tt>) in #elements to the block
|
|
@@ -1194,39 +1270,17 @@ module Net
|
|
|
1194
1270
|
# Related: #elements, #each_entry
|
|
1195
1271
|
def each_element # :yields: integer or range or :*
|
|
1196
1272
|
return to_enum(__method__) unless block_given?
|
|
1197
|
-
|
|
1198
|
-
self
|
|
1199
|
-
end
|
|
1200
|
-
|
|
1201
|
-
private
|
|
1202
|
-
|
|
1203
|
-
def each_entry_tuple(&block)
|
|
1204
|
-
return to_enum(__method__) unless block_given?
|
|
1205
|
-
if @string
|
|
1206
|
-
@string.split(",") do block.call str_to_tuple _1 end
|
|
1207
|
-
else
|
|
1208
|
-
@tuples.each(&block)
|
|
1209
|
-
end
|
|
1273
|
+
runs.each do yield export_run_entry _1 end
|
|
1210
1274
|
self
|
|
1211
1275
|
end
|
|
1212
1276
|
|
|
1213
|
-
def tuple_to_entry((min, max))
|
|
1214
|
-
if min == STAR_INT then :*
|
|
1215
|
-
elsif max == STAR_INT then min..
|
|
1216
|
-
elsif min == max then min
|
|
1217
|
-
else min..max
|
|
1218
|
-
end
|
|
1219
|
-
end
|
|
1220
|
-
|
|
1221
|
-
public
|
|
1222
|
-
|
|
1223
1277
|
# Yields each range in #ranges to the block and returns self.
|
|
1224
1278
|
# Returns an enumerator when called without a block.
|
|
1225
1279
|
#
|
|
1226
1280
|
# Related: #ranges
|
|
1227
1281
|
def each_range # :yields: range
|
|
1228
1282
|
return to_enum(__method__) unless block_given?
|
|
1229
|
-
|
|
1283
|
+
minmaxes.each do |min, max|
|
|
1230
1284
|
if min == STAR_INT then yield :*..
|
|
1231
1285
|
elsif max == STAR_INT then yield min..
|
|
1232
1286
|
else yield min..max
|
|
@@ -1245,7 +1299,7 @@ module Net
|
|
|
1245
1299
|
def each_number(&block) # :yields: integer
|
|
1246
1300
|
return to_enum(__method__) unless block_given?
|
|
1247
1301
|
raise RangeError, '%s contains "*"' % [self.class] if include_star?
|
|
1248
|
-
|
|
1302
|
+
minmaxes.each do each_number_in_minmax _1, _2, &block end
|
|
1249
1303
|
self
|
|
1250
1304
|
end
|
|
1251
1305
|
|
|
@@ -1259,16 +1313,7 @@ module Net
|
|
|
1259
1313
|
def each_ordered_number(&block)
|
|
1260
1314
|
return to_enum(__method__) unless block_given?
|
|
1261
1315
|
raise RangeError, '%s contains "*"' % [self.class] if include_star?
|
|
1262
|
-
|
|
1263
|
-
end
|
|
1264
|
-
|
|
1265
|
-
private def each_number_in_tuple(min, max, &block)
|
|
1266
|
-
if min == STAR_INT then yield :*
|
|
1267
|
-
elsif min == max then yield min
|
|
1268
|
-
elsif max != STAR_INT then (min..max).each(&block)
|
|
1269
|
-
else
|
|
1270
|
-
raise RangeError, "#{SequenceSet} cannot enumerate range with '*'"
|
|
1271
|
-
end
|
|
1316
|
+
each_entry_minmax do each_number_in_minmax _1, _2, &block end
|
|
1272
1317
|
end
|
|
1273
1318
|
|
|
1274
1319
|
# Returns a Set with all of the #numbers in the sequence set.
|
|
@@ -1280,35 +1325,103 @@ module Net
|
|
|
1280
1325
|
# Related: #elements, #ranges, #numbers
|
|
1281
1326
|
def to_set; Set.new(numbers) end
|
|
1282
1327
|
|
|
1283
|
-
# 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
|
|
1356
|
+
#
|
|
1357
|
+
# set = Net::IMAP::SequenceSet["4294967295,*"]
|
|
1358
|
+
# set.count #=> 1
|
|
1359
|
+
# set.cardinality #=> 2
|
|
1284
1360
|
#
|
|
1285
|
-
#
|
|
1286
|
-
#
|
|
1361
|
+
# set = Net::IMAP::SequenceSet[1..]
|
|
1362
|
+
# set.count #=> 4294967295
|
|
1363
|
+
# set.cardinality #=> 4294967296
|
|
1287
1364
|
#
|
|
1288
|
-
# Related: #count_with_duplicates
|
|
1365
|
+
# Related: #cardinality, #count_with_duplicates
|
|
1289
1366
|
def count
|
|
1290
|
-
|
|
1291
|
-
(include_star? && include?(UINT32_MAX) ? -1 : 0)
|
|
1367
|
+
cardinality + (include_star? && include?(UINT32_MAX) ? -1 : 0)
|
|
1292
1368
|
end
|
|
1293
1369
|
|
|
1294
|
-
alias size count
|
|
1295
|
-
|
|
1296
1370
|
# Returns the count of numbers in the ordered #entries, including any
|
|
1297
1371
|
# repeated numbers.
|
|
1298
1372
|
#
|
|
1299
|
-
#
|
|
1300
|
-
#
|
|
1301
|
-
#
|
|
1302
|
-
#
|
|
1303
|
-
#
|
|
1304
|
-
#
|
|
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
|
|
1305
1393
|
def count_with_duplicates
|
|
1306
1394
|
return count unless @string
|
|
1307
|
-
|
|
1395
|
+
each_entry_minmax.sum {|min, max|
|
|
1308
1396
|
max - min + ((max == STAR_INT && min != STAR_INT) ? 0 : 1)
|
|
1309
1397
|
}
|
|
1310
1398
|
end
|
|
1311
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
|
+
|
|
1312
1425
|
# Returns the count of repeated numbers in the ordered #entries, the
|
|
1313
1426
|
# difference between #count_with_duplicates and #count.
|
|
1314
1427
|
#
|
|
@@ -1326,7 +1439,7 @@ module Net
|
|
|
1326
1439
|
#
|
|
1327
1440
|
# Always returns +false+ when #string is normalized.
|
|
1328
1441
|
#
|
|
1329
|
-
# Related: #entries, #count_with_duplicates, #count_duplicates
|
|
1442
|
+
# Related: #entries, #count_with_duplicates, #count_duplicates
|
|
1330
1443
|
def has_duplicates?
|
|
1331
1444
|
return false unless @string
|
|
1332
1445
|
count_with_duplicates != count
|
|
@@ -1337,10 +1450,10 @@ module Net
|
|
|
1337
1450
|
#
|
|
1338
1451
|
# Related: #[], #at, #find_ordered_index
|
|
1339
1452
|
def find_index(number)
|
|
1340
|
-
number =
|
|
1341
|
-
|
|
1453
|
+
number = import_num number
|
|
1454
|
+
each_minmax_with_index(minmaxes) do |min, max, idx_min|
|
|
1342
1455
|
number < min and return nil
|
|
1343
|
-
number <= max and return
|
|
1456
|
+
number <= max and return export_num(idx_min + (number - min))
|
|
1344
1457
|
end
|
|
1345
1458
|
nil
|
|
1346
1459
|
end
|
|
@@ -1350,38 +1463,15 @@ module Net
|
|
|
1350
1463
|
#
|
|
1351
1464
|
# Related: #find_index
|
|
1352
1465
|
def find_ordered_index(number)
|
|
1353
|
-
number =
|
|
1354
|
-
|
|
1466
|
+
number = import_num number
|
|
1467
|
+
each_minmax_with_index(each_entry_minmax) do |min, max, idx_min|
|
|
1355
1468
|
if min <= number && number <= max
|
|
1356
|
-
return
|
|
1469
|
+
return export_num(idx_min + (number - min))
|
|
1357
1470
|
end
|
|
1358
1471
|
end
|
|
1359
1472
|
nil
|
|
1360
1473
|
end
|
|
1361
1474
|
|
|
1362
|
-
private
|
|
1363
|
-
|
|
1364
|
-
def each_tuple_with_index(tuples)
|
|
1365
|
-
idx_min = 0
|
|
1366
|
-
tuples.each do |min, max|
|
|
1367
|
-
idx_max = idx_min + (max - min)
|
|
1368
|
-
yield min, max, idx_min, idx_max
|
|
1369
|
-
idx_min = idx_max + 1
|
|
1370
|
-
end
|
|
1371
|
-
idx_min
|
|
1372
|
-
end
|
|
1373
|
-
|
|
1374
|
-
def reverse_each_tuple_with_index(tuples)
|
|
1375
|
-
idx_max = -1
|
|
1376
|
-
tuples.reverse_each do |min, max|
|
|
1377
|
-
yield min, max, (idx_min = idx_max - (max - min)), idx_max
|
|
1378
|
-
idx_max = idx_min - 1
|
|
1379
|
-
end
|
|
1380
|
-
idx_max
|
|
1381
|
-
end
|
|
1382
|
-
|
|
1383
|
-
public
|
|
1384
|
-
|
|
1385
1475
|
# :call-seq: at(index) -> integer or nil
|
|
1386
1476
|
#
|
|
1387
1477
|
# Returns the number at the given +index+ in the sorted set, without
|
|
@@ -1392,7 +1482,7 @@ module Net
|
|
|
1392
1482
|
#
|
|
1393
1483
|
# Related: #[], #slice, #ordered_at
|
|
1394
1484
|
def at(index)
|
|
1395
|
-
|
|
1485
|
+
seek_number_in_minmaxes(minmaxes, index)
|
|
1396
1486
|
end
|
|
1397
1487
|
|
|
1398
1488
|
# :call-seq: ordered_at(index) -> integer or nil
|
|
@@ -1405,21 +1495,7 @@ module Net
|
|
|
1405
1495
|
#
|
|
1406
1496
|
# Related: #[], #slice, #ordered_at
|
|
1407
1497
|
def ordered_at(index)
|
|
1408
|
-
|
|
1409
|
-
end
|
|
1410
|
-
|
|
1411
|
-
private def lookup_number_by_tuple_index(tuples, index)
|
|
1412
|
-
index = Integer(index.to_int)
|
|
1413
|
-
if index.negative?
|
|
1414
|
-
reverse_each_tuple_with_index(tuples) do |min, max, idx_min, idx_max|
|
|
1415
|
-
idx_min <= index and return from_tuple_int(min + (index - idx_min))
|
|
1416
|
-
end
|
|
1417
|
-
else
|
|
1418
|
-
each_tuple_with_index(tuples) do |min, _, idx_min, idx_max|
|
|
1419
|
-
index <= idx_max and return from_tuple_int(min + (index - idx_min))
|
|
1420
|
-
end
|
|
1421
|
-
end
|
|
1422
|
-
nil
|
|
1498
|
+
seek_number_in_minmaxes(each_entry_minmax, index)
|
|
1423
1499
|
end
|
|
1424
1500
|
|
|
1425
1501
|
# :call-seq:
|
|
@@ -1470,37 +1546,6 @@ module Net
|
|
|
1470
1546
|
|
|
1471
1547
|
alias slice :[]
|
|
1472
1548
|
|
|
1473
|
-
private
|
|
1474
|
-
|
|
1475
|
-
def slice_length(start, length)
|
|
1476
|
-
start = Integer(start.to_int)
|
|
1477
|
-
length = Integer(length.to_int)
|
|
1478
|
-
raise ArgumentError, "length must be positive" unless length.positive?
|
|
1479
|
-
last = start + length - 1 unless start.negative? && start.abs <= length
|
|
1480
|
-
slice_range(start..last)
|
|
1481
|
-
end
|
|
1482
|
-
|
|
1483
|
-
def slice_range(range)
|
|
1484
|
-
first = range.begin || 0
|
|
1485
|
-
last = range.end || -1
|
|
1486
|
-
if range.exclude_end?
|
|
1487
|
-
return remain_frozen_empty if last.zero?
|
|
1488
|
-
last -= 1 if range.end && last != STAR_INT
|
|
1489
|
-
end
|
|
1490
|
-
if (first * last).positive? && last < first
|
|
1491
|
-
remain_frozen_empty
|
|
1492
|
-
elsif (min = at(first))
|
|
1493
|
-
max = at(last)
|
|
1494
|
-
max = :* if max.nil?
|
|
1495
|
-
if max == :* then self & (min..)
|
|
1496
|
-
elsif min <= max then self & (min..max)
|
|
1497
|
-
else remain_frozen_empty
|
|
1498
|
-
end
|
|
1499
|
-
end
|
|
1500
|
-
end
|
|
1501
|
-
|
|
1502
|
-
public
|
|
1503
|
-
|
|
1504
1549
|
# Returns a copy of +self+ which only contains the numbers above +num+.
|
|
1505
1550
|
#
|
|
1506
1551
|
# Net::IMAP::SequenceSet["5,10:22,50"].above(10) # to_s => "11:22,50"
|
|
@@ -1572,7 +1617,7 @@ module Net
|
|
|
1572
1617
|
#
|
|
1573
1618
|
# Related: #limit!
|
|
1574
1619
|
def limit(max:)
|
|
1575
|
-
max =
|
|
1620
|
+
max = import_num(max)
|
|
1576
1621
|
if empty? then self.class.empty
|
|
1577
1622
|
elsif !include_star? && max < min then self.class.empty
|
|
1578
1623
|
elsif max(star: STAR_INT) <= max then frozen? ? self : dup.freeze
|
|
@@ -1585,53 +1630,134 @@ module Net
|
|
|
1585
1630
|
#
|
|
1586
1631
|
# Related: #limit
|
|
1587
1632
|
def limit!(max:)
|
|
1588
|
-
modifying! # short-circuit
|
|
1633
|
+
modifying! # short-circuit before querying
|
|
1589
1634
|
star = include_star?
|
|
1590
|
-
max =
|
|
1591
|
-
|
|
1592
|
-
|
|
1635
|
+
max = import_num(max)
|
|
1636
|
+
subtract_minmax [max + 1, STAR_INT]
|
|
1637
|
+
add_minmax [max, max ] if star
|
|
1593
1638
|
normalize!
|
|
1594
1639
|
end
|
|
1595
1640
|
|
|
1596
1641
|
# :call-seq: complement! -> self
|
|
1597
1642
|
#
|
|
1598
|
-
#
|
|
1599
|
-
# 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.
|
|
1600
1646
|
#
|
|
1601
1647
|
# Related: #complement
|
|
1602
1648
|
def complement!
|
|
1603
|
-
modifying! # short-circuit
|
|
1649
|
+
modifying! # short-circuit before querying
|
|
1604
1650
|
return replace(self.class.full) if empty?
|
|
1605
1651
|
return clear if full?
|
|
1606
|
-
flat =
|
|
1652
|
+
flat = minmaxes.flat_map { [_1 - 1, _2 + 1] }
|
|
1607
1653
|
if flat.first < 1 then flat.shift else flat.unshift 1 end
|
|
1608
1654
|
if STAR_INT < flat.last then flat.pop else flat.push STAR_INT end
|
|
1609
|
-
|
|
1655
|
+
replace_minmaxes flat.each_slice(2).to_a
|
|
1610
1656
|
normalize!
|
|
1611
1657
|
end
|
|
1612
1658
|
|
|
1613
|
-
#
|
|
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.
|
|
1664
|
+
#
|
|
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.
|
|
1614
1744
|
#
|
|
1615
|
-
# The returned set's #string is sorted and deduplicated. Adjacent or
|
|
1616
|
-
# overlapping elements will be merged into a single larger range.
|
|
1617
1745
|
# See SequenceSet@Ordered+and+Normalized+sets.
|
|
1618
1746
|
#
|
|
1619
1747
|
# Net::IMAP::SequenceSet["1:5,3:7,10:9,10:11"].normalize
|
|
1620
1748
|
# #=> Net::IMAP::SequenceSet["1:7,9:11"]
|
|
1621
1749
|
#
|
|
1622
|
-
# Related: #normalize!, #normalized_string
|
|
1750
|
+
# Related: #normalize!, #normalized_string, #normalized?
|
|
1623
1751
|
def normalize
|
|
1624
|
-
|
|
1625
|
-
return self if frozen? && str == string
|
|
1626
|
-
remain_frozen dup.instance_exec { @string = str&.-@; self }
|
|
1752
|
+
frozen? && normalized? ? self : remain_frozen(dup.normalize!)
|
|
1627
1753
|
end
|
|
1628
1754
|
|
|
1629
1755
|
# Resets #string to be sorted, deduplicated, and coalesced. Returns
|
|
1630
1756
|
# +self+. See SequenceSet@Ordered+and+Normalized+sets.
|
|
1631
1757
|
#
|
|
1632
|
-
# Related: #normalize, #normalized_string
|
|
1758
|
+
# Related: #normalize, #normalized_string, #normalized?
|
|
1633
1759
|
def normalize!
|
|
1634
|
-
modifying! # redundant check
|
|
1760
|
+
modifying! # redundant check (normalizes the error message for JRuby)
|
|
1635
1761
|
@string = nil
|
|
1636
1762
|
self
|
|
1637
1763
|
end
|
|
@@ -1645,9 +1771,9 @@ module Net
|
|
|
1645
1771
|
#
|
|
1646
1772
|
# Returns +nil+ when the set is empty.
|
|
1647
1773
|
#
|
|
1648
|
-
# Related: #normalize!, #normalize, #string, #to_s
|
|
1774
|
+
# Related: #normalize!, #normalize, #string, #to_s, #normalized?
|
|
1649
1775
|
def normalized_string
|
|
1650
|
-
|
|
1776
|
+
export_runs(runs) unless runs.empty?
|
|
1651
1777
|
end
|
|
1652
1778
|
|
|
1653
1779
|
# Returns an inspection string for the SequenceSet.
|
|
@@ -1684,8 +1810,8 @@ module Net
|
|
|
1684
1810
|
head = @string[INSPECT_ABRIDGED_HEAD_RE]
|
|
1685
1811
|
tail = @string[INSPECT_ABRIDGED_TAIL_RE]
|
|
1686
1812
|
else
|
|
1687
|
-
head =
|
|
1688
|
-
tail = "," +
|
|
1813
|
+
head = export_runs(runs.first(INSPECT_TRUNCATE_LEN)) + ","
|
|
1814
|
+
tail = "," + export_runs(runs.last(INSPECT_TRUNCATE_LEN))
|
|
1689
1815
|
end
|
|
1690
1816
|
'#<%s %d entries "%s...(%d entries omitted)...%s"%s>' % [
|
|
1691
1817
|
self.class, count,
|
|
@@ -1695,10 +1821,6 @@ module Net
|
|
|
1695
1821
|
end
|
|
1696
1822
|
end
|
|
1697
1823
|
|
|
1698
|
-
private def count_entries
|
|
1699
|
-
@string ? @string.count(",") + 1 : @tuples.count
|
|
1700
|
-
end
|
|
1701
|
-
|
|
1702
1824
|
##
|
|
1703
1825
|
# :method: to_sequence_set
|
|
1704
1826
|
# :call-seq: to_sequence_set -> self
|
|
@@ -1729,15 +1851,17 @@ module Net
|
|
|
1729
1851
|
|
|
1730
1852
|
# For YAML deserialization
|
|
1731
1853
|
def init_with(coder) # :nodoc:
|
|
1732
|
-
@
|
|
1854
|
+
@set_data = new_set_data
|
|
1733
1855
|
self.string = coder['string']
|
|
1734
1856
|
end
|
|
1735
1857
|
|
|
1858
|
+
# :stopdoc:
|
|
1736
1859
|
protected
|
|
1737
1860
|
|
|
1738
|
-
attr_reader :
|
|
1861
|
+
attr_reader :set_data
|
|
1739
1862
|
|
|
1740
|
-
|
|
1863
|
+
alias runs set_data
|
|
1864
|
+
alias minmaxes runs
|
|
1741
1865
|
|
|
1742
1866
|
private
|
|
1743
1867
|
|
|
@@ -1746,38 +1870,42 @@ module Net
|
|
|
1746
1870
|
|
|
1747
1871
|
# frozen clones are shallow copied
|
|
1748
1872
|
def initialize_clone(other)
|
|
1749
|
-
@
|
|
1873
|
+
@set_data = other.dup_set_data unless other.frozen?
|
|
1750
1874
|
super
|
|
1751
1875
|
end
|
|
1752
1876
|
|
|
1753
1877
|
def initialize_dup(other)
|
|
1754
|
-
@
|
|
1878
|
+
@set_data = other.dup_set_data
|
|
1755
1879
|
super
|
|
1756
1880
|
end
|
|
1757
1881
|
|
|
1758
|
-
|
|
1759
|
-
|
|
1882
|
+
######################################################################{{{2
|
|
1883
|
+
# Import methods
|
|
1884
|
+
|
|
1885
|
+
def import_minmax(input)
|
|
1886
|
+
entry = input_try_convert input
|
|
1760
1887
|
case entry
|
|
1761
|
-
when *STARS, Integer then [int =
|
|
1762
|
-
when Range then
|
|
1763
|
-
when String then
|
|
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)
|
|
1764
1891
|
else
|
|
1765
|
-
raise DataFormatError, "expected number or range, got %p" % [
|
|
1892
|
+
raise DataFormatError, "expected number or range, got %p" % [input]
|
|
1766
1893
|
end
|
|
1767
1894
|
end
|
|
1895
|
+
alias import_run import_minmax
|
|
1768
1896
|
|
|
1769
|
-
def
|
|
1770
|
-
set = input_try_convert
|
|
1897
|
+
def import_runs(input)
|
|
1898
|
+
set = input_try_convert input
|
|
1771
1899
|
case set
|
|
1772
|
-
when *STARS, Integer, Range then [
|
|
1773
|
-
when String then
|
|
1774
|
-
when SequenceSet then set.
|
|
1775
|
-
when Set then set.map { [
|
|
1776
|
-
when Array then set.flat_map {
|
|
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 }
|
|
1777
1905
|
when nil then []
|
|
1778
1906
|
else
|
|
1779
1907
|
raise DataFormatError, "expected nz-number, range, '*', Set, Array; " \
|
|
1780
|
-
"got %p" % [
|
|
1908
|
+
"got %p" % [input]
|
|
1781
1909
|
end
|
|
1782
1910
|
end
|
|
1783
1911
|
|
|
@@ -1790,9 +1918,17 @@ module Net
|
|
|
1790
1918
|
input
|
|
1791
1919
|
end
|
|
1792
1920
|
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
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 || :*)
|
|
1796
1932
|
last -= 1 if range.exclude_end? && range.end && last != STAR_INT
|
|
1797
1933
|
unless first <= last
|
|
1798
1934
|
raise DataFormatError, "invalid range for sequence-set: %p" % [range]
|
|
@@ -1800,71 +1936,260 @@ module Net
|
|
|
1800
1936
|
[first, last]
|
|
1801
1937
|
end
|
|
1802
1938
|
|
|
1803
|
-
def
|
|
1804
|
-
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
|
|
1805
1944
|
|
|
1806
|
-
def
|
|
1807
|
-
|
|
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
|
|
1808
1972
|
end
|
|
1809
1973
|
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
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
|
|
1980
|
+
|
|
1981
|
+
def parse_entry(str)
|
|
1813
1982
|
raise DataFormatError, "invalid sequence set string" if str.empty?
|
|
1814
|
-
str.split(":", 2).map! {
|
|
1983
|
+
str.split(":", 2).map! { import_num _1 }
|
|
1984
|
+
end
|
|
1985
|
+
|
|
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
|
|
2006
|
+
|
|
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
|
|
1815
2019
|
end
|
|
2020
|
+
alias each_entry_run each_entry_minmax
|
|
1816
2021
|
|
|
1817
|
-
|
|
2022
|
+
######################################################################{{{2
|
|
2023
|
+
# Search methods
|
|
1818
2024
|
|
|
1819
|
-
def
|
|
1820
|
-
|
|
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
|
|
1821
2029
|
range.include?(min) || range.include?(max) || (min..max).cover?(range)
|
|
1822
2030
|
end
|
|
1823
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
|
+
|
|
1824
2134
|
def modifying!
|
|
1825
2135
|
if frozen?
|
|
1826
2136
|
raise FrozenError, "can't modify frozen #{self.class}: %p" % [self]
|
|
1827
2137
|
end
|
|
1828
2138
|
end
|
|
1829
2139
|
|
|
1830
|
-
def
|
|
1831
|
-
|
|
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
|
|
1832
2153
|
|
|
1833
2154
|
#
|
|
1834
|
-
# --|=====| |=====new
|
|
1835
|
-
# ?????????-|=====new
|
|
2155
|
+
# --|=====| |=====new run=======| append
|
|
2156
|
+
# ?????????-|=====new run=======|-|===lower===|-- insert
|
|
1836
2157
|
#
|
|
1837
|
-
# |=====new
|
|
2158
|
+
# |=====new run=======|
|
|
1838
2159
|
# ---------??=======lower=======??--------------- noop
|
|
1839
2160
|
#
|
|
1840
2161
|
# ---------??===lower==|--|==| join remaining
|
|
1841
2162
|
# ---------??===lower==|--|==|----|===upper===|-- join until upper
|
|
1842
2163
|
# ---------??===lower==|--|==|--|=====upper===|-- join to upper
|
|
1843
|
-
def
|
|
2164
|
+
def add_minmax(minmax)
|
|
1844
2165
|
modifying!
|
|
1845
|
-
min, max
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
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)
|
|
1850
2172
|
end
|
|
1851
2173
|
end
|
|
1852
2174
|
|
|
1853
|
-
def
|
|
1854
|
-
return if
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
lower_idx
|
|
1858
|
-
return if
|
|
1859
|
-
tmax_adj =
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
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
|
|
1863
2188
|
end
|
|
1864
|
-
|
|
2189
|
+
slice_runs! next_idx..upper_idx
|
|
1865
2190
|
end
|
|
1866
2191
|
|
|
1867
|
-
# |====
|
|
2192
|
+
# |====subtracted run=======|
|
|
1868
2193
|
# --|====| no more 1. noop
|
|
1869
2194
|
# --|====|---------------------------|====lower====|-- 2. noop
|
|
1870
2195
|
# -------|======lower================|---------------- 3. split
|
|
@@ -1877,61 +2202,59 @@ module Net
|
|
|
1877
2202
|
# -------??=====lower====|--|====| no more 6. delete rest
|
|
1878
2203
|
# -------??=====lower====|--|====|---|====upper====|-- 7. delete until
|
|
1879
2204
|
# -------??=====lower====|--|====|--|=====upper====|-- 8. delete and trim
|
|
1880
|
-
def
|
|
2205
|
+
def subtract_minmax(minmax)
|
|
1881
2206
|
modifying!
|
|
1882
|
-
min, max
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
elsif max <
|
|
1887
|
-
|
|
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
|
|
1888
2214
|
end
|
|
1889
2215
|
end
|
|
1890
2216
|
|
|
1891
|
-
def
|
|
1892
|
-
|
|
1893
|
-
|
|
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
|
|
1894
2221
|
end
|
|
1895
|
-
lower[0] = tmax + 1
|
|
1896
2222
|
end
|
|
1897
2223
|
|
|
1898
|
-
def
|
|
1899
|
-
if
|
|
1900
|
-
|
|
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
|
|
1901
2227
|
lower_idx += 1
|
|
1902
2228
|
end
|
|
1903
|
-
if tmax ==
|
|
1904
|
-
|
|
1905
|
-
elsif (
|
|
1906
|
-
upper_idx
|
|
1907
|
-
|
|
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
|
|
1908
2238
|
end
|
|
1909
|
-
tuples.slice!(lower_idx..upper_idx)
|
|
1910
|
-
end
|
|
1911
|
-
|
|
1912
|
-
def tuple_gte_with_index(num)
|
|
1913
|
-
idx = tuples.bsearch_index { _2 >= num } and [tuples[idx], idx]
|
|
1914
2239
|
end
|
|
1915
2240
|
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
def nz_number(num)
|
|
1922
|
-
String === num && !/\A[1-9]\d*\z/.match?(num) and
|
|
1923
|
-
raise DataFormatError, "%p is not a valid nz-number" % [num]
|
|
1924
|
-
NumValidator.ensure_nz_number Integer num
|
|
1925
|
-
rescue TypeError # To catch errors from Integer()
|
|
1926
|
-
raise DataFormatError, $!.message
|
|
1927
|
-
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
|
|
1928
2245
|
|
|
2246
|
+
######################################################################{{{2
|
|
1929
2247
|
# intentionally defined after the class implementation
|
|
1930
2248
|
|
|
2249
|
+
FULL_SET_DATA = [[1, STAR_INT].freeze].freeze
|
|
2250
|
+
private_constant :FULL_SET_DATA
|
|
2251
|
+
|
|
1931
2252
|
EMPTY = new.freeze
|
|
1932
2253
|
FULL = self["1:*"]
|
|
1933
2254
|
private_constant :EMPTY, :FULL
|
|
1934
2255
|
|
|
2256
|
+
# }}}
|
|
2257
|
+
# vim:foldmethod=marker
|
|
1935
2258
|
end
|
|
1936
2259
|
end
|
|
1937
2260
|
end
|