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