net-imap 0.5.12 → 0.6.1
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 +626 -312
- data/lib/net/imap/uidplus_data.rb +2 -63
- data/lib/net/imap.rb +17 -23
- 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,16 @@ 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
|
-
if
|
|
1029
|
-
return unless
|
|
1030
|
-
|
|
1100
|
+
modifying! # short-circuit before import_minmax
|
|
1101
|
+
minmax = import_minmax element
|
|
1102
|
+
if minmax.first == minmax.last
|
|
1103
|
+
return unless include_minmax? minmax
|
|
1104
|
+
subtract_minmax minmax
|
|
1031
1105
|
normalize!
|
|
1032
|
-
|
|
1106
|
+
export_num minmax.first
|
|
1033
1107
|
else
|
|
1034
1108
|
copy = dup
|
|
1035
|
-
|
|
1109
|
+
subtract_minmax minmax
|
|
1036
1110
|
normalize!
|
|
1037
1111
|
copy if copy.subtract(self).valid?
|
|
1038
1112
|
end
|
|
@@ -1069,8 +1143,8 @@ module Net
|
|
|
1069
1143
|
deleted
|
|
1070
1144
|
end
|
|
1071
1145
|
|
|
1072
|
-
# Merges all of the elements that appear in any of
|
|
1073
|
-
# set, and returns +self+.
|
|
1146
|
+
# In-place set #union. Merges all of the elements that appear in any of
|
|
1147
|
+
# the +sets+ into this set, and returns +self+.
|
|
1074
1148
|
#
|
|
1075
1149
|
# The +sets+ may be any objects that would be accepted by ::new.
|
|
1076
1150
|
#
|
|
@@ -1078,19 +1152,20 @@ module Net
|
|
|
1078
1152
|
#
|
|
1079
1153
|
# Related: #add, #add?, #union
|
|
1080
1154
|
def merge(*sets)
|
|
1081
|
-
modifying! # short-circuit before
|
|
1082
|
-
|
|
1155
|
+
modifying! # short-circuit before import_runs
|
|
1156
|
+
add_runs import_runs sets
|
|
1083
1157
|
normalize!
|
|
1084
1158
|
end
|
|
1085
1159
|
|
|
1086
|
-
# Removes all of the elements that appear in
|
|
1087
|
-
# the set, and returns +self+.
|
|
1160
|
+
# In-place set #difference. Removes all of the elements that appear in
|
|
1161
|
+
# any of the given +sets+ from this set, and returns +self+.
|
|
1088
1162
|
#
|
|
1089
1163
|
# The +sets+ may be any objects that would be accepted by ::new.
|
|
1090
1164
|
#
|
|
1091
1165
|
# Related: #difference
|
|
1092
1166
|
def subtract(*sets)
|
|
1093
|
-
|
|
1167
|
+
modifying! # short-circuit before import_runs
|
|
1168
|
+
subtract_runs import_runs sets
|
|
1094
1169
|
normalize!
|
|
1095
1170
|
end
|
|
1096
1171
|
|
|
@@ -1182,7 +1257,7 @@ module Net
|
|
|
1182
1257
|
# Related: #entries, #each_element
|
|
1183
1258
|
def each_entry(&block) # :yields: integer or range or :*
|
|
1184
1259
|
return to_enum(__method__) unless block_given?
|
|
1185
|
-
|
|
1260
|
+
each_entry_run do yield export_run_entry _1 end
|
|
1186
1261
|
end
|
|
1187
1262
|
|
|
1188
1263
|
# Yields each number or range (or <tt>:*</tt>) in #elements to the block
|
|
@@ -1194,39 +1269,17 @@ module Net
|
|
|
1194
1269
|
# Related: #elements, #each_entry
|
|
1195
1270
|
def each_element # :yields: integer or range or :*
|
|
1196
1271
|
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
|
|
1272
|
+
runs.each do yield export_run_entry _1 end
|
|
1210
1273
|
self
|
|
1211
1274
|
end
|
|
1212
1275
|
|
|
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
1276
|
# Yields each range in #ranges to the block and returns self.
|
|
1224
1277
|
# Returns an enumerator when called without a block.
|
|
1225
1278
|
#
|
|
1226
1279
|
# Related: #ranges
|
|
1227
1280
|
def each_range # :yields: range
|
|
1228
1281
|
return to_enum(__method__) unless block_given?
|
|
1229
|
-
|
|
1282
|
+
minmaxes.each do |min, max|
|
|
1230
1283
|
if min == STAR_INT then yield :*..
|
|
1231
1284
|
elsif max == STAR_INT then yield min..
|
|
1232
1285
|
else yield min..max
|
|
@@ -1245,7 +1298,7 @@ module Net
|
|
|
1245
1298
|
def each_number(&block) # :yields: integer
|
|
1246
1299
|
return to_enum(__method__) unless block_given?
|
|
1247
1300
|
raise RangeError, '%s contains "*"' % [self.class] if include_star?
|
|
1248
|
-
|
|
1301
|
+
minmaxes.each do each_number_in_minmax _1, _2, &block end
|
|
1249
1302
|
self
|
|
1250
1303
|
end
|
|
1251
1304
|
|
|
@@ -1259,16 +1312,7 @@ module Net
|
|
|
1259
1312
|
def each_ordered_number(&block)
|
|
1260
1313
|
return to_enum(__method__) unless block_given?
|
|
1261
1314
|
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
|
|
1315
|
+
each_entry_minmax do each_number_in_minmax _1, _2, &block end
|
|
1272
1316
|
end
|
|
1273
1317
|
|
|
1274
1318
|
# Returns a Set with all of the #numbers in the sequence set.
|
|
@@ -1280,35 +1324,103 @@ module Net
|
|
|
1280
1324
|
# Related: #elements, #ranges, #numbers
|
|
1281
1325
|
def to_set; Set.new(numbers) end
|
|
1282
1326
|
|
|
1283
|
-
# Returns the
|
|
1327
|
+
# Returns the number of members in the set.
|
|
1328
|
+
#
|
|
1329
|
+
# Unlike #count, <tt>"*"</tt> is considered to be distinct from
|
|
1330
|
+
# <tt>2³² - 1</tt> (the maximum 32-bit unsigned integer value).
|
|
1331
|
+
#
|
|
1332
|
+
# set = Net::IMAP::SequenceSet[1..10]
|
|
1333
|
+
# set.count #=> 10
|
|
1334
|
+
# set.cardinality #=> 10
|
|
1335
|
+
#
|
|
1336
|
+
# set = Net::IMAP::SequenceSet["4294967295,*"]
|
|
1337
|
+
# set.count #=> 1
|
|
1338
|
+
# set.cardinality #=> 2
|
|
1339
|
+
#
|
|
1340
|
+
# set = Net::IMAP::SequenceSet[1..]
|
|
1341
|
+
# set.count #=> 4294967295
|
|
1342
|
+
# set.cardinality #=> 4294967296
|
|
1343
|
+
#
|
|
1344
|
+
# Related: #count, #count_with_duplicates
|
|
1345
|
+
def cardinality = minmaxes.sum(runs.count) { _2 - _1 }
|
|
1346
|
+
|
|
1347
|
+
# Returns the count of distinct #numbers in the set.
|
|
1348
|
+
#
|
|
1349
|
+
# Unlike #cardinality, <tt>"*"</tt> is considered to be equal to
|
|
1350
|
+
# <tt>2³² - 1</tt> (the maximum 32-bit unsigned integer value).
|
|
1351
|
+
#
|
|
1352
|
+
# set = Net::IMAP::SequenceSet[1..10]
|
|
1353
|
+
# set.count #=> 10
|
|
1354
|
+
# set.cardinality #=> 10
|
|
1355
|
+
#
|
|
1356
|
+
# set = Net::IMAP::SequenceSet["4294967295,*"]
|
|
1357
|
+
# set.count #=> 1
|
|
1358
|
+
# set.cardinality #=> 2
|
|
1284
1359
|
#
|
|
1285
|
-
#
|
|
1286
|
-
#
|
|
1360
|
+
# set = Net::IMAP::SequenceSet[1..]
|
|
1361
|
+
# set.count #=> 4294967295
|
|
1362
|
+
# set.cardinality #=> 4294967296
|
|
1287
1363
|
#
|
|
1288
|
-
# Related: #count_with_duplicates
|
|
1364
|
+
# Related: #cardinality, #count_with_duplicates
|
|
1289
1365
|
def count
|
|
1290
|
-
|
|
1291
|
-
(include_star? && include?(UINT32_MAX) ? -1 : 0)
|
|
1366
|
+
cardinality + (include_star? && include?(UINT32_MAX) ? -1 : 0)
|
|
1292
1367
|
end
|
|
1293
1368
|
|
|
1294
|
-
alias size count
|
|
1295
|
-
|
|
1296
1369
|
# Returns the count of numbers in the ordered #entries, including any
|
|
1297
1370
|
# repeated numbers.
|
|
1298
1371
|
#
|
|
1299
|
-
#
|
|
1300
|
-
#
|
|
1301
|
-
#
|
|
1302
|
-
#
|
|
1303
|
-
#
|
|
1304
|
-
#
|
|
1372
|
+
# When #string is normalized, this returns the same as #count. Like
|
|
1373
|
+
# #count, <tt>"*"</tt> is considered to be equal to <tt>2³² - 1</tt> (the
|
|
1374
|
+
# maximum 32-bit unsigned integer value).
|
|
1375
|
+
#
|
|
1376
|
+
# In a range, <tt>"*"</tt> is _not_ considered a duplicate:
|
|
1377
|
+
# set = Net::IMAP::SequenceSet["4294967295:*"]
|
|
1378
|
+
# set.count_with_duplicates #=> 1
|
|
1379
|
+
# set.size #=> 2
|
|
1380
|
+
# set.count #=> 1
|
|
1381
|
+
# set.cardinality #=> 2
|
|
1382
|
+
#
|
|
1383
|
+
# In a separate entry, <tt>"*"</tt> _is_ considered a duplicate:
|
|
1384
|
+
# set = Net::IMAP::SequenceSet["4294967295,*"]
|
|
1385
|
+
# set.count_with_duplicates #=> 2
|
|
1386
|
+
# set.size #=> 2
|
|
1387
|
+
# set.count #=> 1
|
|
1388
|
+
# set.cardinality #=> 2
|
|
1389
|
+
#
|
|
1390
|
+
# Related: #count, #cardinality, #size, #count_duplicates,
|
|
1391
|
+
# #has_duplicates?, #entries
|
|
1305
1392
|
def count_with_duplicates
|
|
1306
1393
|
return count unless @string
|
|
1307
|
-
|
|
1394
|
+
each_entry_minmax.sum {|min, max|
|
|
1308
1395
|
max - min + ((max == STAR_INT && min != STAR_INT) ? 0 : 1)
|
|
1309
1396
|
}
|
|
1310
1397
|
end
|
|
1311
1398
|
|
|
1399
|
+
# Returns the combined size of the ordered #entries, including any
|
|
1400
|
+
# repeated numbers.
|
|
1401
|
+
#
|
|
1402
|
+
# When #string is normalized, this returns the same as #cardinality.
|
|
1403
|
+
# Like #cardinality, <tt>"*"</tt> is considered to be be distinct from
|
|
1404
|
+
# <tt>2³² - 1</tt> (the maximum 32-bit unsigned integer value).
|
|
1405
|
+
#
|
|
1406
|
+
# set = Net::IMAP::SequenceSet["4294967295:*"]
|
|
1407
|
+
# set.size #=> 2
|
|
1408
|
+
# set.count_with_duplicates #=> 1
|
|
1409
|
+
# set.count #=> 1
|
|
1410
|
+
# set.cardinality #=> 2
|
|
1411
|
+
#
|
|
1412
|
+
# set = Net::IMAP::SequenceSet["4294967295,*"]
|
|
1413
|
+
# set.size #=> 2
|
|
1414
|
+
# set.count_with_duplicates #=> 2
|
|
1415
|
+
# set.count #=> 1
|
|
1416
|
+
# set.cardinality #=> 2
|
|
1417
|
+
#
|
|
1418
|
+
# Related: #cardinality, #count_with_duplicates, #count, #entries
|
|
1419
|
+
def size
|
|
1420
|
+
return cardinality unless @string
|
|
1421
|
+
each_entry_minmax.sum {|min, max| max - min + 1 }
|
|
1422
|
+
end
|
|
1423
|
+
|
|
1312
1424
|
# Returns the count of repeated numbers in the ordered #entries, the
|
|
1313
1425
|
# difference between #count_with_duplicates and #count.
|
|
1314
1426
|
#
|
|
@@ -1326,7 +1438,7 @@ module Net
|
|
|
1326
1438
|
#
|
|
1327
1439
|
# Always returns +false+ when #string is normalized.
|
|
1328
1440
|
#
|
|
1329
|
-
# Related: #entries, #count_with_duplicates, #count_duplicates
|
|
1441
|
+
# Related: #entries, #count_with_duplicates, #count_duplicates
|
|
1330
1442
|
def has_duplicates?
|
|
1331
1443
|
return false unless @string
|
|
1332
1444
|
count_with_duplicates != count
|
|
@@ -1337,10 +1449,10 @@ module Net
|
|
|
1337
1449
|
#
|
|
1338
1450
|
# Related: #[], #at, #find_ordered_index
|
|
1339
1451
|
def find_index(number)
|
|
1340
|
-
number =
|
|
1341
|
-
|
|
1452
|
+
number = import_num number
|
|
1453
|
+
each_minmax_with_index(minmaxes) do |min, max, idx_min|
|
|
1342
1454
|
number < min and return nil
|
|
1343
|
-
number <= max and return
|
|
1455
|
+
number <= max and return export_num(idx_min + (number - min))
|
|
1344
1456
|
end
|
|
1345
1457
|
nil
|
|
1346
1458
|
end
|
|
@@ -1350,38 +1462,15 @@ module Net
|
|
|
1350
1462
|
#
|
|
1351
1463
|
# Related: #find_index
|
|
1352
1464
|
def find_ordered_index(number)
|
|
1353
|
-
number =
|
|
1354
|
-
|
|
1465
|
+
number = import_num number
|
|
1466
|
+
each_minmax_with_index(each_entry_minmax) do |min, max, idx_min|
|
|
1355
1467
|
if min <= number && number <= max
|
|
1356
|
-
return
|
|
1468
|
+
return export_num(idx_min + (number - min))
|
|
1357
1469
|
end
|
|
1358
1470
|
end
|
|
1359
1471
|
nil
|
|
1360
1472
|
end
|
|
1361
1473
|
|
|
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
1474
|
# :call-seq: at(index) -> integer or nil
|
|
1386
1475
|
#
|
|
1387
1476
|
# Returns the number at the given +index+ in the sorted set, without
|
|
@@ -1392,7 +1481,7 @@ module Net
|
|
|
1392
1481
|
#
|
|
1393
1482
|
# Related: #[], #slice, #ordered_at
|
|
1394
1483
|
def at(index)
|
|
1395
|
-
|
|
1484
|
+
seek_number_in_minmaxes(minmaxes, index)
|
|
1396
1485
|
end
|
|
1397
1486
|
|
|
1398
1487
|
# :call-seq: ordered_at(index) -> integer or nil
|
|
@@ -1405,21 +1494,7 @@ module Net
|
|
|
1405
1494
|
#
|
|
1406
1495
|
# Related: #[], #slice, #ordered_at
|
|
1407
1496
|
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
|
|
1497
|
+
seek_number_in_minmaxes(each_entry_minmax, index)
|
|
1423
1498
|
end
|
|
1424
1499
|
|
|
1425
1500
|
# :call-seq:
|
|
@@ -1470,37 +1545,6 @@ module Net
|
|
|
1470
1545
|
|
|
1471
1546
|
alias slice :[]
|
|
1472
1547
|
|
|
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
1548
|
# Returns a copy of +self+ which only contains the numbers above +num+.
|
|
1505
1549
|
#
|
|
1506
1550
|
# Net::IMAP::SequenceSet["5,10:22,50"].above(10) # to_s => "11:22,50"
|
|
@@ -1572,7 +1616,7 @@ module Net
|
|
|
1572
1616
|
#
|
|
1573
1617
|
# Related: #limit!
|
|
1574
1618
|
def limit(max:)
|
|
1575
|
-
max =
|
|
1619
|
+
max = import_num(max)
|
|
1576
1620
|
if empty? then self.class.empty
|
|
1577
1621
|
elsif !include_star? && max < min then self.class.empty
|
|
1578
1622
|
elsif max(star: STAR_INT) <= max then frozen? ? self : dup.freeze
|
|
@@ -1585,53 +1629,134 @@ module Net
|
|
|
1585
1629
|
#
|
|
1586
1630
|
# Related: #limit
|
|
1587
1631
|
def limit!(max:)
|
|
1588
|
-
modifying! # short-circuit
|
|
1632
|
+
modifying! # short-circuit before querying
|
|
1589
1633
|
star = include_star?
|
|
1590
|
-
max =
|
|
1591
|
-
|
|
1592
|
-
|
|
1634
|
+
max = import_num(max)
|
|
1635
|
+
subtract_minmax [max + 1, STAR_INT]
|
|
1636
|
+
add_minmax [max, max ] if star
|
|
1593
1637
|
normalize!
|
|
1594
1638
|
end
|
|
1595
1639
|
|
|
1596
1640
|
# :call-seq: complement! -> self
|
|
1597
1641
|
#
|
|
1598
|
-
#
|
|
1599
|
-
# possible values _except_ for those
|
|
1642
|
+
# In-place set #complement. Replaces the contents of this set with its
|
|
1643
|
+
# own #complement. It will contain all possible values _except_ for those
|
|
1644
|
+
# currently in the set.
|
|
1600
1645
|
#
|
|
1601
1646
|
# Related: #complement
|
|
1602
1647
|
def complement!
|
|
1603
|
-
modifying! # short-circuit
|
|
1648
|
+
modifying! # short-circuit before querying
|
|
1604
1649
|
return replace(self.class.full) if empty?
|
|
1605
1650
|
return clear if full?
|
|
1606
|
-
flat =
|
|
1651
|
+
flat = minmaxes.flat_map { [_1 - 1, _2 + 1] }
|
|
1607
1652
|
if flat.first < 1 then flat.shift else flat.unshift 1 end
|
|
1608
1653
|
if STAR_INT < flat.last then flat.pop else flat.push STAR_INT end
|
|
1609
|
-
|
|
1654
|
+
replace_minmaxes flat.each_slice(2).to_a
|
|
1610
1655
|
normalize!
|
|
1611
1656
|
end
|
|
1612
1657
|
|
|
1613
|
-
#
|
|
1658
|
+
# In-place set #intersection. Removes any elements that are missing from
|
|
1659
|
+
# +other+ from this set, keeping only the #intersection, and returns
|
|
1660
|
+
# +self+.
|
|
1661
|
+
#
|
|
1662
|
+
# +other+ can be any object that would be accepted by ::new.
|
|
1663
|
+
#
|
|
1664
|
+
# set = Net::IMAP::SequenceSet.new(1..5)
|
|
1665
|
+
# set.intersect! [2, 4, 6]
|
|
1666
|
+
# set #=> Net::IMAP::SequenceSet("2,4")
|
|
1667
|
+
#
|
|
1668
|
+
# Related: #intersection, #intersect?
|
|
1669
|
+
def intersect!(other)
|
|
1670
|
+
modifying! # short-circuit before processing input
|
|
1671
|
+
subtract SequenceSet.new(other).complement!
|
|
1672
|
+
end
|
|
1673
|
+
|
|
1674
|
+
# In-place set #xor. Adds any numbers in +other+ that are missing from
|
|
1675
|
+
# this set, removes any numbers in +other+ that are already in this set,
|
|
1676
|
+
# and returns +self+.
|
|
1677
|
+
#
|
|
1678
|
+
# +other+ can be any object that would be accepted by ::new.
|
|
1679
|
+
#
|
|
1680
|
+
# set = Net::IMAP::SequenceSet.new(1..5)
|
|
1681
|
+
# set.xor! [2, 4, 6]
|
|
1682
|
+
# set #=> Net::IMAP::SequenceSet["1,3,5:6"]
|
|
1683
|
+
#
|
|
1684
|
+
# Related: #xor, #merge, #subtract
|
|
1685
|
+
def xor!(other)
|
|
1686
|
+
modifying! # short-circuit before processing input
|
|
1687
|
+
other = SequenceSet.new(other)
|
|
1688
|
+
copy = dup
|
|
1689
|
+
merge(other).subtract(other.subtract(copy.complement!))
|
|
1690
|
+
end
|
|
1691
|
+
|
|
1692
|
+
# Returns whether #string is fully normalized: entries have been sorted,
|
|
1693
|
+
# deduplicated, and coalesced, and all entries are in normal form. See
|
|
1694
|
+
# SequenceSet@Ordered+and+Normalized+sets.
|
|
1695
|
+
#
|
|
1696
|
+
# Net::IMAP::SequenceSet["1,3,5"].normalized? #=> true
|
|
1697
|
+
# Net::IMAP::SequenceSet["20:30"].normalized? #=> true
|
|
1698
|
+
#
|
|
1699
|
+
# Net::IMAP::SequenceSet["3,5,1"].normalized? #=> false, not sorted
|
|
1700
|
+
# Net::IMAP::SequenceSet["1,2,3"].normalized? #=> false, not coalesced
|
|
1701
|
+
# Net::IMAP::SequenceSet["1:5,2"].normalized? #=> false, repeated number
|
|
1702
|
+
#
|
|
1703
|
+
# Net::IMAP::SequenceSet["1:1"].normalized? #=> false, number as range
|
|
1704
|
+
# Net::IMAP::SequenceSet["5:1"].normalized? #=> false, backwards range
|
|
1705
|
+
#
|
|
1706
|
+
# Returns +true+ if (and only if) #string is equal to #normalized_string:
|
|
1707
|
+
# seqset = Net::IMAP::SequenceSet["1:3,5"]
|
|
1708
|
+
# seqset.string #=> "1:3,5"
|
|
1709
|
+
# seqset.normalized_string #=> "1:3,5"
|
|
1710
|
+
# seqset.entries #=> [1..3, 5]
|
|
1711
|
+
# seqset.elements #=> [1..3, 5]
|
|
1712
|
+
# seqset.normalized? #=> true
|
|
1713
|
+
#
|
|
1714
|
+
# seqset = Net::IMAP::SequenceSet["3,1,2"]
|
|
1715
|
+
# seqset.string #=> "3,1,2"
|
|
1716
|
+
# seqset.normalized_string #=> "1:3"
|
|
1717
|
+
# seqset.entries #=> [3, 1, 2]
|
|
1718
|
+
# seqset.elements #=> [1..3]
|
|
1719
|
+
# seqset.normalized? #=> false
|
|
1720
|
+
#
|
|
1721
|
+
# Can return +false+ even when #entries and #elements are the same:
|
|
1722
|
+
# seqset = Net::IMAP::SequenceSet["5:1"]
|
|
1723
|
+
# seqset.string #=> "5:1"
|
|
1724
|
+
# seqset.normalized_string #=> "1:5"
|
|
1725
|
+
# seqset.entries #=> [1..5]
|
|
1726
|
+
# seqset.elements #=> [1..5]
|
|
1727
|
+
# seqset.normalized? #=> false
|
|
1728
|
+
#
|
|
1729
|
+
# Note that empty sets are normalized, even though they are not #valid?:
|
|
1730
|
+
# seqset = Net::IMAP::SequenceSet.empty
|
|
1731
|
+
# seqset.normalized? #=> true
|
|
1732
|
+
# seqset.valid? #=> false
|
|
1733
|
+
#
|
|
1734
|
+
# Related: #normalize, #normalize!, #normalized_string
|
|
1735
|
+
def normalized?
|
|
1736
|
+
@string.nil? || normal_string?(@string)
|
|
1737
|
+
end
|
|
1738
|
+
|
|
1739
|
+
# Returns a SequenceSet with a normalized string representation: entries
|
|
1740
|
+
# have been sorted, deduplicated, and coalesced, and all entries
|
|
1741
|
+
# are in normal form. Returns +self+ for frozen normalized sets, and a
|
|
1742
|
+
# normalized duplicate otherwise.
|
|
1614
1743
|
#
|
|
1615
|
-
# The returned set's #string is sorted and deduplicated. Adjacent or
|
|
1616
|
-
# overlapping elements will be merged into a single larger range.
|
|
1617
1744
|
# See SequenceSet@Ordered+and+Normalized+sets.
|
|
1618
1745
|
#
|
|
1619
1746
|
# Net::IMAP::SequenceSet["1:5,3:7,10:9,10:11"].normalize
|
|
1620
1747
|
# #=> Net::IMAP::SequenceSet["1:7,9:11"]
|
|
1621
1748
|
#
|
|
1622
|
-
# Related: #normalize!, #normalized_string
|
|
1749
|
+
# Related: #normalize!, #normalized_string, #normalized?
|
|
1623
1750
|
def normalize
|
|
1624
|
-
|
|
1625
|
-
return self if frozen? && str == string
|
|
1626
|
-
remain_frozen dup.instance_exec { @string = str&.-@; self }
|
|
1751
|
+
frozen? && normalized? ? self : remain_frozen(dup.normalize!)
|
|
1627
1752
|
end
|
|
1628
1753
|
|
|
1629
1754
|
# Resets #string to be sorted, deduplicated, and coalesced. Returns
|
|
1630
1755
|
# +self+. See SequenceSet@Ordered+and+Normalized+sets.
|
|
1631
1756
|
#
|
|
1632
|
-
# Related: #normalize, #normalized_string
|
|
1757
|
+
# Related: #normalize, #normalized_string, #normalized?
|
|
1633
1758
|
def normalize!
|
|
1634
|
-
modifying! # redundant check
|
|
1759
|
+
modifying! # redundant check (normalizes the error message for JRuby)
|
|
1635
1760
|
@string = nil
|
|
1636
1761
|
self
|
|
1637
1762
|
end
|
|
@@ -1645,9 +1770,9 @@ module Net
|
|
|
1645
1770
|
#
|
|
1646
1771
|
# Returns +nil+ when the set is empty.
|
|
1647
1772
|
#
|
|
1648
|
-
# Related: #normalize!, #normalize, #string, #to_s
|
|
1773
|
+
# Related: #normalize!, #normalize, #string, #to_s, #normalized?
|
|
1649
1774
|
def normalized_string
|
|
1650
|
-
|
|
1775
|
+
export_runs(runs) unless runs.empty?
|
|
1651
1776
|
end
|
|
1652
1777
|
|
|
1653
1778
|
# Returns an inspection string for the SequenceSet.
|
|
@@ -1684,8 +1809,8 @@ module Net
|
|
|
1684
1809
|
head = @string[INSPECT_ABRIDGED_HEAD_RE]
|
|
1685
1810
|
tail = @string[INSPECT_ABRIDGED_TAIL_RE]
|
|
1686
1811
|
else
|
|
1687
|
-
head =
|
|
1688
|
-
tail = "," +
|
|
1812
|
+
head = export_runs(runs.first(INSPECT_TRUNCATE_LEN)) + ","
|
|
1813
|
+
tail = "," + export_runs(runs.last(INSPECT_TRUNCATE_LEN))
|
|
1689
1814
|
end
|
|
1690
1815
|
'#<%s %d entries "%s...(%d entries omitted)...%s"%s>' % [
|
|
1691
1816
|
self.class, count,
|
|
@@ -1695,10 +1820,6 @@ module Net
|
|
|
1695
1820
|
end
|
|
1696
1821
|
end
|
|
1697
1822
|
|
|
1698
|
-
private def count_entries
|
|
1699
|
-
@string ? @string.count(",") + 1 : @tuples.count
|
|
1700
|
-
end
|
|
1701
|
-
|
|
1702
1823
|
##
|
|
1703
1824
|
# :method: to_sequence_set
|
|
1704
1825
|
# :call-seq: to_sequence_set -> self
|
|
@@ -1729,15 +1850,17 @@ module Net
|
|
|
1729
1850
|
|
|
1730
1851
|
# For YAML deserialization
|
|
1731
1852
|
def init_with(coder) # :nodoc:
|
|
1732
|
-
@
|
|
1853
|
+
@set_data = new_set_data
|
|
1733
1854
|
self.string = coder['string']
|
|
1734
1855
|
end
|
|
1735
1856
|
|
|
1857
|
+
# :stopdoc:
|
|
1736
1858
|
protected
|
|
1737
1859
|
|
|
1738
|
-
attr_reader :
|
|
1860
|
+
attr_reader :set_data
|
|
1739
1861
|
|
|
1740
|
-
|
|
1862
|
+
alias runs set_data
|
|
1863
|
+
alias minmaxes runs
|
|
1741
1864
|
|
|
1742
1865
|
private
|
|
1743
1866
|
|
|
@@ -1746,38 +1869,42 @@ module Net
|
|
|
1746
1869
|
|
|
1747
1870
|
# frozen clones are shallow copied
|
|
1748
1871
|
def initialize_clone(other)
|
|
1749
|
-
@
|
|
1872
|
+
@set_data = other.dup_set_data unless other.frozen?
|
|
1750
1873
|
super
|
|
1751
1874
|
end
|
|
1752
1875
|
|
|
1753
1876
|
def initialize_dup(other)
|
|
1754
|
-
@
|
|
1877
|
+
@set_data = other.dup_set_data
|
|
1755
1878
|
super
|
|
1756
1879
|
end
|
|
1757
1880
|
|
|
1758
|
-
|
|
1759
|
-
|
|
1881
|
+
######################################################################{{{2
|
|
1882
|
+
# Import methods
|
|
1883
|
+
|
|
1884
|
+
def import_minmax(input)
|
|
1885
|
+
entry = input_try_convert input
|
|
1760
1886
|
case entry
|
|
1761
|
-
when *STARS, Integer then [int =
|
|
1762
|
-
when Range then
|
|
1763
|
-
when String then
|
|
1887
|
+
when *STARS, Integer then [int = import_num(entry), int]
|
|
1888
|
+
when Range then import_range_minmax(entry)
|
|
1889
|
+
when String then parse_minmax(entry)
|
|
1764
1890
|
else
|
|
1765
|
-
raise DataFormatError, "expected number or range, got %p" % [
|
|
1891
|
+
raise DataFormatError, "expected number or range, got %p" % [input]
|
|
1766
1892
|
end
|
|
1767
1893
|
end
|
|
1894
|
+
alias import_run import_minmax
|
|
1768
1895
|
|
|
1769
|
-
def
|
|
1770
|
-
set = input_try_convert
|
|
1896
|
+
def import_runs(input)
|
|
1897
|
+
set = input_try_convert input
|
|
1771
1898
|
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 {
|
|
1899
|
+
when *STARS, Integer, Range then [import_run(set)]
|
|
1900
|
+
when String then parse_runs set
|
|
1901
|
+
when SequenceSet then set.runs
|
|
1902
|
+
when Set then set.map { [import_num(_1)] * 2 }
|
|
1903
|
+
when Array then set.flat_map { import_runs _1 }
|
|
1777
1904
|
when nil then []
|
|
1778
1905
|
else
|
|
1779
1906
|
raise DataFormatError, "expected nz-number, range, '*', Set, Array; " \
|
|
1780
|
-
"got %p" % [
|
|
1907
|
+
"got %p" % [input]
|
|
1781
1908
|
end
|
|
1782
1909
|
end
|
|
1783
1910
|
|
|
@@ -1790,9 +1917,9 @@ module Net
|
|
|
1790
1917
|
input
|
|
1791
1918
|
end
|
|
1792
1919
|
|
|
1793
|
-
def
|
|
1794
|
-
first =
|
|
1795
|
-
last =
|
|
1920
|
+
def import_range_minmax(range)
|
|
1921
|
+
first = import_num(range.begin || 1)
|
|
1922
|
+
last = import_num(range.end || :*)
|
|
1796
1923
|
last -= 1 if range.exclude_end? && range.end && last != STAR_INT
|
|
1797
1924
|
unless first <= last
|
|
1798
1925
|
raise DataFormatError, "invalid range for sequence-set: %p" % [range]
|
|
@@ -1800,71 +1927,260 @@ module Net
|
|
|
1800
1927
|
[first, last]
|
|
1801
1928
|
end
|
|
1802
1929
|
|
|
1803
|
-
def
|
|
1804
|
-
def
|
|
1930
|
+
def import_num(obj) STARS.include?(obj) ? STAR_INT : nz_number(obj) end
|
|
1931
|
+
def nz_number(num) = NumValidator.coerce_nz_number(num)
|
|
1932
|
+
|
|
1933
|
+
######################################################################{{{2
|
|
1934
|
+
# Export methods
|
|
1935
|
+
|
|
1936
|
+
def export_num(num) num == STAR_INT ? :* : num end
|
|
1937
|
+
|
|
1938
|
+
def export_minmaxes(minmaxes)
|
|
1939
|
+
-minmaxes.map { export_minmax _1 }.join(",")
|
|
1940
|
+
end
|
|
1941
|
+
|
|
1942
|
+
def export_minmax(minmax) minmax.uniq.map { export_num _1 }.join(":") end
|
|
1943
|
+
|
|
1944
|
+
alias export_runs export_minmaxes
|
|
1945
|
+
alias export_run export_minmax
|
|
1946
|
+
|
|
1947
|
+
def export_minmax_entry((min, max))
|
|
1948
|
+
if min == STAR_INT then :*
|
|
1949
|
+
elsif max == STAR_INT then min..
|
|
1950
|
+
elsif min == max then min
|
|
1951
|
+
else min..max
|
|
1952
|
+
end
|
|
1953
|
+
end
|
|
1954
|
+
alias export_run_entry export_minmax_entry
|
|
1805
1955
|
|
|
1806
|
-
def
|
|
1807
|
-
|
|
1956
|
+
def each_number_in_minmax(min, max, &block)
|
|
1957
|
+
if min == STAR_INT then yield :*
|
|
1958
|
+
elsif min == max then yield min
|
|
1959
|
+
elsif max != STAR_INT then (min..max).each(&block)
|
|
1960
|
+
else
|
|
1961
|
+
raise RangeError, "#{SequenceSet} cannot enumerate range with '*'"
|
|
1962
|
+
end
|
|
1808
1963
|
end
|
|
1809
1964
|
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1965
|
+
######################################################################{{{2
|
|
1966
|
+
# Parse methods
|
|
1967
|
+
|
|
1968
|
+
def parse_runs(str) str.split(",", -1).map! { parse_run _1 } end
|
|
1969
|
+
def parse_minmax(str) parse_entry(str).minmax end
|
|
1970
|
+
alias parse_run parse_minmax
|
|
1971
|
+
|
|
1972
|
+
def parse_entry(str)
|
|
1813
1973
|
raise DataFormatError, "invalid sequence set string" if str.empty?
|
|
1814
|
-
str.split(":", 2).map! {
|
|
1974
|
+
str.split(":", 2).map! { import_num _1 }
|
|
1975
|
+
end
|
|
1976
|
+
|
|
1977
|
+
# yields validated but unsorted [num] or [num, num]
|
|
1978
|
+
def each_parsed_entry(str)
|
|
1979
|
+
return to_enum(__method__, str) unless block_given?
|
|
1980
|
+
str&.split(",", -1) do |entry| yield parse_entry(entry) end
|
|
1981
|
+
end
|
|
1982
|
+
|
|
1983
|
+
def normal_string?(str) normalized_entries? each_parsed_entry str end
|
|
1984
|
+
|
|
1985
|
+
def normalized_entries?(entries)
|
|
1986
|
+
max = nil
|
|
1987
|
+
entries.each do |first, last|
|
|
1988
|
+
return false if last && last <= first # 1:1 or 2:1
|
|
1989
|
+
return false if max && first <= max + 1 # 2,1 or 1,1 or 1,2
|
|
1990
|
+
max = last || first
|
|
1991
|
+
end
|
|
1992
|
+
true
|
|
1993
|
+
end
|
|
1994
|
+
|
|
1995
|
+
######################################################################{{{2
|
|
1996
|
+
# Ordered entry methods
|
|
1997
|
+
|
|
1998
|
+
def count_entries
|
|
1999
|
+
@string ? @string.count(",") + 1 : runs.count
|
|
2000
|
+
end
|
|
2001
|
+
|
|
2002
|
+
def each_entry_minmax(&block)
|
|
2003
|
+
return to_enum(__method__) unless block_given?
|
|
2004
|
+
if @string
|
|
2005
|
+
@string.split(",") do block.call parse_minmax _1 end
|
|
2006
|
+
else
|
|
2007
|
+
minmaxes.each(&block)
|
|
2008
|
+
end
|
|
2009
|
+
self
|
|
1815
2010
|
end
|
|
2011
|
+
alias each_entry_run each_entry_minmax
|
|
2012
|
+
|
|
2013
|
+
######################################################################{{{2
|
|
2014
|
+
# Search methods
|
|
1816
2015
|
|
|
1817
|
-
def
|
|
2016
|
+
def include_minmax?((min, max)) bsearch_range(min)&.cover?(min..max) end
|
|
1818
2017
|
|
|
1819
|
-
def
|
|
1820
|
-
range =
|
|
2018
|
+
def intersect_minmax?((min, max))
|
|
2019
|
+
range = bsearch_range(min) and
|
|
1821
2020
|
range.include?(min) || range.include?(max) || (min..max).cover?(range)
|
|
1822
2021
|
end
|
|
1823
2022
|
|
|
2023
|
+
alias include_run? include_minmax?
|
|
2024
|
+
alias intersect_run? intersect_minmax?
|
|
2025
|
+
|
|
2026
|
+
def bsearch_index(num) = minmaxes.bsearch_index { _2 >= num }
|
|
2027
|
+
def bsearch_minmax(num) = minmaxes.bsearch { _2 >= num }
|
|
2028
|
+
def bsearch_range(num) = (min, max = bsearch_minmax(num)) && (min..max)
|
|
2029
|
+
|
|
2030
|
+
######################################################################{{{2
|
|
2031
|
+
# Number indexing methods
|
|
2032
|
+
|
|
2033
|
+
def seek_number_in_minmaxes(minmaxes, index)
|
|
2034
|
+
index = Integer(index.to_int)
|
|
2035
|
+
if index.negative?
|
|
2036
|
+
reverse_each_minmax_with_index(minmaxes) do |min, max, idx_min, idx_max|
|
|
2037
|
+
idx_min <= index and return export_num(min + (index - idx_min))
|
|
2038
|
+
end
|
|
2039
|
+
else
|
|
2040
|
+
each_minmax_with_index(minmaxes) do |min, _, idx_min, idx_max|
|
|
2041
|
+
index <= idx_max and return export_num(min + (index - idx_min))
|
|
2042
|
+
end
|
|
2043
|
+
end
|
|
2044
|
+
nil
|
|
2045
|
+
end
|
|
2046
|
+
|
|
2047
|
+
def each_minmax_with_index(minmaxes)
|
|
2048
|
+
idx_min = 0
|
|
2049
|
+
minmaxes.each do |min, max|
|
|
2050
|
+
idx_max = idx_min + (max - min)
|
|
2051
|
+
yield min, max, idx_min, idx_max
|
|
2052
|
+
idx_min = idx_max + 1
|
|
2053
|
+
end
|
|
2054
|
+
idx_min
|
|
2055
|
+
end
|
|
2056
|
+
|
|
2057
|
+
def reverse_each_minmax_with_index(minmaxes)
|
|
2058
|
+
idx_max = -1
|
|
2059
|
+
minmaxes.reverse_each do |min, max|
|
|
2060
|
+
yield min, max, (idx_min = idx_max - (max - min)), idx_max
|
|
2061
|
+
idx_max = idx_min - 1
|
|
2062
|
+
end
|
|
2063
|
+
idx_max
|
|
2064
|
+
end
|
|
2065
|
+
|
|
2066
|
+
def slice_length(start, length)
|
|
2067
|
+
start = Integer(start.to_int)
|
|
2068
|
+
length = Integer(length.to_int)
|
|
2069
|
+
raise ArgumentError, "length must be positive" unless length.positive?
|
|
2070
|
+
last = start + length - 1 unless start.negative? && start.abs <= length
|
|
2071
|
+
slice_range(start..last)
|
|
2072
|
+
end
|
|
2073
|
+
|
|
2074
|
+
def slice_range(range)
|
|
2075
|
+
first = range.begin || 0
|
|
2076
|
+
last = range.end || -1
|
|
2077
|
+
if range.exclude_end?
|
|
2078
|
+
return remain_frozen_empty if last.zero?
|
|
2079
|
+
last -= 1 if range.end && last != STAR_INT
|
|
2080
|
+
end
|
|
2081
|
+
if (first * last).positive? && last < first
|
|
2082
|
+
remain_frozen_empty
|
|
2083
|
+
elsif (min = at(first))
|
|
2084
|
+
max = at(last)
|
|
2085
|
+
max = :* if max.nil?
|
|
2086
|
+
if max == :* then self & (min..)
|
|
2087
|
+
elsif min <= max then self & (min..max)
|
|
2088
|
+
else remain_frozen_empty
|
|
2089
|
+
end
|
|
2090
|
+
end
|
|
2091
|
+
end
|
|
2092
|
+
|
|
2093
|
+
######################################################################{{{2
|
|
2094
|
+
# Core set data create/freeze/dup primitives
|
|
2095
|
+
|
|
2096
|
+
def new_set_data = []
|
|
2097
|
+
def freeze_set_data = set_data.each(&:freeze).freeze
|
|
2098
|
+
def dup_set_data = set_data.map { _1.dup }
|
|
2099
|
+
protected :dup_set_data
|
|
2100
|
+
|
|
2101
|
+
######################################################################{{{2
|
|
2102
|
+
# Core set data query/enumeration primitives
|
|
2103
|
+
|
|
2104
|
+
def min_num = minmaxes.first&.first
|
|
2105
|
+
def max_num = minmaxes.last&.last
|
|
2106
|
+
|
|
2107
|
+
def min_at(idx) = minmaxes[idx][0]
|
|
2108
|
+
def max_at(idx) = minmaxes[idx][1]
|
|
2109
|
+
|
|
2110
|
+
######################################################################{{{2
|
|
2111
|
+
# Core set data modification primitives
|
|
2112
|
+
|
|
2113
|
+
def set_min_at(idx, min) = minmaxes[idx][0] = min
|
|
2114
|
+
def set_max_at(idx, max) = minmaxes[idx][1] = max
|
|
2115
|
+
def replace_minmaxes(other) = minmaxes.replace(other)
|
|
2116
|
+
def append_minmax(min, max) = minmaxes << [min, max]
|
|
2117
|
+
def insert_minmax(idx, min, max) = minmaxes.insert idx, [min, max]
|
|
2118
|
+
def delete_run_at(idx) = runs.delete_at(idx)
|
|
2119
|
+
def slice_runs!(...) = runs.slice!(...)
|
|
2120
|
+
def truncate_runs!(idx) = runs.slice!(idx..)
|
|
2121
|
+
|
|
2122
|
+
######################################################################{{{2
|
|
2123
|
+
# Update methods
|
|
2124
|
+
|
|
1824
2125
|
def modifying!
|
|
1825
2126
|
if frozen?
|
|
1826
2127
|
raise FrozenError, "can't modify frozen #{self.class}: %p" % [self]
|
|
1827
2128
|
end
|
|
1828
2129
|
end
|
|
1829
2130
|
|
|
1830
|
-
def
|
|
1831
|
-
|
|
2131
|
+
def add_minmaxes(minmaxes)
|
|
2132
|
+
minmaxes.each do |minmax|
|
|
2133
|
+
add_minmax minmax
|
|
2134
|
+
end
|
|
2135
|
+
self
|
|
2136
|
+
end
|
|
2137
|
+
|
|
2138
|
+
def subtract_minmaxes(minmaxes)
|
|
2139
|
+
minmaxes.each do |minmax|
|
|
2140
|
+
subtract_minmax minmax
|
|
2141
|
+
end
|
|
2142
|
+
self
|
|
2143
|
+
end
|
|
1832
2144
|
|
|
1833
2145
|
#
|
|
1834
|
-
# --|=====| |=====new
|
|
1835
|
-
# ?????????-|=====new
|
|
2146
|
+
# --|=====| |=====new run=======| append
|
|
2147
|
+
# ?????????-|=====new run=======|-|===lower===|-- insert
|
|
1836
2148
|
#
|
|
1837
|
-
# |=====new
|
|
2149
|
+
# |=====new run=======|
|
|
1838
2150
|
# ---------??=======lower=======??--------------- noop
|
|
1839
2151
|
#
|
|
1840
2152
|
# ---------??===lower==|--|==| join remaining
|
|
1841
2153
|
# ---------??===lower==|--|==|----|===upper===|-- join until upper
|
|
1842
2154
|
# ---------??===lower==|--|==|--|=====upper===|-- join to upper
|
|
1843
|
-
def
|
|
2155
|
+
def add_minmax(minmax)
|
|
1844
2156
|
modifying!
|
|
1845
|
-
min, max
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
2157
|
+
min, max = minmax
|
|
2158
|
+
lower_idx = bsearch_index(min - 1)
|
|
2159
|
+
lmin, lmax = min_at(lower_idx), max_at(lower_idx) if lower_idx
|
|
2160
|
+
if lmin.nil? then append_minmax min, max
|
|
2161
|
+
elsif (max + 1) < lmin then insert_minmax lower_idx, min, max
|
|
2162
|
+
else add_coalesced_minmax(lower_idx, lmin, lmax, min, max)
|
|
1850
2163
|
end
|
|
1851
2164
|
end
|
|
1852
2165
|
|
|
1853
|
-
def
|
|
1854
|
-
return if
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
lower_idx
|
|
1858
|
-
return if
|
|
1859
|
-
tmax_adj =
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
2166
|
+
def add_coalesced_minmax(lower_idx, lmin, lmax, min, max)
|
|
2167
|
+
return if lmin <= min && max <= lmax
|
|
2168
|
+
set_min_at lower_idx, (lmin = min) if min < lmin
|
|
2169
|
+
set_max_at lower_idx, (lmax = max) if lmax < max
|
|
2170
|
+
next_idx = lower_idx + 1
|
|
2171
|
+
return if next_idx == runs.count
|
|
2172
|
+
tmax_adj = lmax + 1
|
|
2173
|
+
if (upper_idx = bsearch_index(tmax_adj))
|
|
2174
|
+
if tmax_adj < min_at(upper_idx)
|
|
2175
|
+
upper_idx -= 1
|
|
2176
|
+
else
|
|
2177
|
+
set_max_at lower_idx, max_at(upper_idx)
|
|
2178
|
+
end
|
|
1863
2179
|
end
|
|
1864
|
-
|
|
2180
|
+
slice_runs! next_idx..upper_idx
|
|
1865
2181
|
end
|
|
1866
2182
|
|
|
1867
|
-
# |====
|
|
2183
|
+
# |====subtracted run=======|
|
|
1868
2184
|
# --|====| no more 1. noop
|
|
1869
2185
|
# --|====|---------------------------|====lower====|-- 2. noop
|
|
1870
2186
|
# -------|======lower================|---------------- 3. split
|
|
@@ -1877,61 +2193,59 @@ module Net
|
|
|
1877
2193
|
# -------??=====lower====|--|====| no more 6. delete rest
|
|
1878
2194
|
# -------??=====lower====|--|====|---|====upper====|-- 7. delete until
|
|
1879
2195
|
# -------??=====lower====|--|====|--|=====upper====|-- 8. delete and trim
|
|
1880
|
-
def
|
|
2196
|
+
def subtract_minmax(minmax)
|
|
1881
2197
|
modifying!
|
|
1882
|
-
min, max
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
elsif max <
|
|
1887
|
-
|
|
2198
|
+
min, max = minmax
|
|
2199
|
+
idx = bsearch_index(min)
|
|
2200
|
+
lmin, lmax = min_at(idx), max_at(idx) if idx
|
|
2201
|
+
if lmin.nil? then nil # case 1.
|
|
2202
|
+
elsif max < lmin then nil # case 2.
|
|
2203
|
+
elsif max < lmax then trim_or_split_minmax idx, lmin, min, max
|
|
2204
|
+
else trim_or_delete_minmax idx, lmin, lmax, min, max
|
|
1888
2205
|
end
|
|
1889
2206
|
end
|
|
1890
2207
|
|
|
1891
|
-
def
|
|
1892
|
-
|
|
1893
|
-
|
|
2208
|
+
def trim_or_split_minmax(idx, lmin, tmin, tmax)
|
|
2209
|
+
set_min_at idx, tmax + 1
|
|
2210
|
+
if lmin < tmin # split
|
|
2211
|
+
insert_minmax idx, lmin, tmin - 1
|
|
1894
2212
|
end
|
|
1895
|
-
lower[0] = tmax + 1
|
|
1896
2213
|
end
|
|
1897
2214
|
|
|
1898
|
-
def
|
|
1899
|
-
if
|
|
1900
|
-
|
|
2215
|
+
def trim_or_delete_minmax(lower_idx, lmin, lmax, tmin, tmax)
|
|
2216
|
+
if lmin < tmin # trim lower
|
|
2217
|
+
lmax = set_max_at lower_idx, tmin - 1
|
|
1901
2218
|
lower_idx += 1
|
|
1902
2219
|
end
|
|
1903
|
-
if tmax ==
|
|
1904
|
-
|
|
1905
|
-
elsif (
|
|
1906
|
-
upper_idx
|
|
1907
|
-
|
|
2220
|
+
if tmax == lmax # case 5
|
|
2221
|
+
delete_run_at lower_idx
|
|
2222
|
+
elsif (upper_idx = bsearch_index(tmax + 1))
|
|
2223
|
+
if min_at(upper_idx) <= tmax # case 8
|
|
2224
|
+
set_min_at upper_idx, tmax + 1
|
|
2225
|
+
end
|
|
2226
|
+
slice_runs! lower_idx..upper_idx - 1 # cases 7 and 8
|
|
2227
|
+
else # case 6
|
|
2228
|
+
truncate_runs! lower_idx
|
|
1908
2229
|
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
2230
|
end
|
|
1915
2231
|
|
|
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
|
|
2232
|
+
alias add_runs add_minmaxes
|
|
2233
|
+
alias add_run add_minmax
|
|
2234
|
+
alias subtract_runs subtract_minmaxes
|
|
2235
|
+
alias subtract_run subtract_minmax
|
|
1928
2236
|
|
|
2237
|
+
######################################################################{{{2
|
|
1929
2238
|
# intentionally defined after the class implementation
|
|
1930
2239
|
|
|
2240
|
+
FULL_SET_DATA = [[1, STAR_INT].freeze].freeze
|
|
2241
|
+
private_constant :FULL_SET_DATA
|
|
2242
|
+
|
|
1931
2243
|
EMPTY = new.freeze
|
|
1932
2244
|
FULL = self["1:*"]
|
|
1933
2245
|
private_constant :EMPTY, :FULL
|
|
1934
2246
|
|
|
2247
|
+
# }}}
|
|
2248
|
+
# vim:foldmethod=marker
|
|
1935
2249
|
end
|
|
1936
2250
|
end
|
|
1937
2251
|
end
|