net-imap 0.5.13 → 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 +1 -0
- data/lib/net/imap/command_data.rb +0 -68
- data/lib/net/imap/config/attr_inheritance.rb +14 -1
- data/lib/net/imap/config.rb +175 -35
- 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 +622 -327
- data/lib/net/imap/uidplus_data.rb +2 -63
- data/lib/net/imap.rb +17 -24
- data/net-imap.gemspec +1 -1
- metadata +3 -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,14 +788,8 @@ module Net
|
|
|
755
788
|
# Related: #min, #minmax, #slice
|
|
756
789
|
def max(count = nil, star: :*)
|
|
757
790
|
if count
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
if cardinality <= count
|
|
761
|
-
frozen? ? self : dup
|
|
762
|
-
else
|
|
763
|
-
slice(-count..) || remain_frozen_empty
|
|
764
|
-
end
|
|
765
|
-
elsif (val = @tuples.last&.last)
|
|
791
|
+
slice(-[count, size].min..) || remain_frozen_empty
|
|
792
|
+
elsif (val = max_num)
|
|
766
793
|
val == STAR_INT ? star : val
|
|
767
794
|
end
|
|
768
795
|
end
|
|
@@ -782,7 +809,7 @@ module Net
|
|
|
782
809
|
def min(count = nil, star: :*)
|
|
783
810
|
if count
|
|
784
811
|
slice(0...count) || remain_frozen_empty
|
|
785
|
-
elsif (val =
|
|
812
|
+
elsif (val = min_num)
|
|
786
813
|
val != STAR_INT ? val : star
|
|
787
814
|
end
|
|
788
815
|
end
|
|
@@ -800,10 +827,10 @@ module Net
|
|
|
800
827
|
def valid?; !empty? end
|
|
801
828
|
|
|
802
829
|
# Returns true if the set contains no elements
|
|
803
|
-
def empty?;
|
|
830
|
+
def empty?; runs.empty? end
|
|
804
831
|
|
|
805
832
|
# Returns true if the set contains every possible element.
|
|
806
|
-
def full?;
|
|
833
|
+
def full?; set_data == FULL_SET_DATA end
|
|
807
834
|
|
|
808
835
|
# :call-seq:
|
|
809
836
|
# self + other -> sequence set
|
|
@@ -879,9 +906,7 @@ module Net
|
|
|
879
906
|
# * <tt>lhs - (lhs - rhs)</tt>
|
|
880
907
|
# * <tt>lhs - (lhs ^ rhs)</tt>
|
|
881
908
|
# * <tt>lhs ^ (lhs - rhs)</tt>
|
|
882
|
-
def &(other)
|
|
883
|
-
remain_frozen dup.subtract SequenceSet.new(other).complement!
|
|
884
|
-
end
|
|
909
|
+
def &(other) remain_frozen dup.intersect! other end
|
|
885
910
|
alias intersection :&
|
|
886
911
|
|
|
887
912
|
# :call-seq:
|
|
@@ -906,7 +931,7 @@ module Net
|
|
|
906
931
|
# * <tt>(lhs | rhs) - (lhs & rhs)</tt>
|
|
907
932
|
# * <tt>(lhs - rhs) | (rhs - lhs)</tt>
|
|
908
933
|
# * <tt>(lhs ^ other) ^ (other ^ rhs)</tt>
|
|
909
|
-
def ^(other) remain_frozen
|
|
934
|
+
def ^(other) remain_frozen dup.xor! other end
|
|
910
935
|
alias xor :^
|
|
911
936
|
|
|
912
937
|
# :call-seq:
|
|
@@ -945,8 +970,8 @@ module Net
|
|
|
945
970
|
#
|
|
946
971
|
# Related: #add?, #merge, #union, #append
|
|
947
972
|
def add(element)
|
|
948
|
-
modifying! # short-circuit before
|
|
949
|
-
|
|
973
|
+
modifying! # short-circuit before import_run
|
|
974
|
+
add_run import_run element
|
|
950
975
|
normalize!
|
|
951
976
|
end
|
|
952
977
|
alias << add
|
|
@@ -956,16 +981,55 @@ module Net
|
|
|
956
981
|
# Unlike #add, #merge, or #union, the new value is appended to #string.
|
|
957
982
|
# This may result in a #string which has duplicates or is out-of-order.
|
|
958
983
|
#
|
|
959
|
-
#
|
|
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.
|
|
960
1005
|
#
|
|
961
1006
|
# Related: #add, #merge, #union
|
|
962
1007
|
def append(entry)
|
|
963
|
-
modifying! # short-circuit before
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
string
|
|
967
|
-
|
|
968
|
-
|
|
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}"
|
|
969
1033
|
self
|
|
970
1034
|
end
|
|
971
1035
|
|
|
@@ -991,8 +1055,8 @@ module Net
|
|
|
991
1055
|
#
|
|
992
1056
|
# Related: #delete?, #delete_at, #subtract, #difference
|
|
993
1057
|
def delete(element)
|
|
994
|
-
modifying! # short-circuit before
|
|
995
|
-
|
|
1058
|
+
modifying! # short-circuit before import_run
|
|
1059
|
+
subtract_run import_run element
|
|
996
1060
|
normalize!
|
|
997
1061
|
end
|
|
998
1062
|
|
|
@@ -1029,17 +1093,16 @@ module Net
|
|
|
1029
1093
|
#
|
|
1030
1094
|
# Related: #delete, #delete_at, #subtract, #difference, #disjoint?
|
|
1031
1095
|
def delete?(element)
|
|
1032
|
-
modifying! # short-circuit before
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
tuple_subtract tuple
|
|
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
|
|
1038
1101
|
normalize!
|
|
1039
|
-
|
|
1102
|
+
export_num minmax.first
|
|
1040
1103
|
else
|
|
1041
1104
|
copy = dup
|
|
1042
|
-
|
|
1105
|
+
subtract_minmax minmax
|
|
1043
1106
|
normalize!
|
|
1044
1107
|
copy if copy.subtract(self).valid?
|
|
1045
1108
|
end
|
|
@@ -1076,8 +1139,8 @@ module Net
|
|
|
1076
1139
|
deleted
|
|
1077
1140
|
end
|
|
1078
1141
|
|
|
1079
|
-
# Merges all of the elements that appear in any of
|
|
1080
|
-
# 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+.
|
|
1081
1144
|
#
|
|
1082
1145
|
# The +sets+ may be any objects that would be accepted by ::new.
|
|
1083
1146
|
#
|
|
@@ -1085,19 +1148,20 @@ module Net
|
|
|
1085
1148
|
#
|
|
1086
1149
|
# Related: #add, #add?, #union
|
|
1087
1150
|
def merge(*sets)
|
|
1088
|
-
modifying! # short-circuit before
|
|
1089
|
-
|
|
1151
|
+
modifying! # short-circuit before import_runs
|
|
1152
|
+
add_runs import_runs sets
|
|
1090
1153
|
normalize!
|
|
1091
1154
|
end
|
|
1092
1155
|
|
|
1093
|
-
# Removes all of the elements that appear in
|
|
1094
|
-
# 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+.
|
|
1095
1158
|
#
|
|
1096
1159
|
# The +sets+ may be any objects that would be accepted by ::new.
|
|
1097
1160
|
#
|
|
1098
1161
|
# Related: #difference
|
|
1099
1162
|
def subtract(*sets)
|
|
1100
|
-
|
|
1163
|
+
modifying! # short-circuit before import_runs
|
|
1164
|
+
subtract_runs import_runs sets
|
|
1101
1165
|
normalize!
|
|
1102
1166
|
end
|
|
1103
1167
|
|
|
@@ -1189,7 +1253,7 @@ module Net
|
|
|
1189
1253
|
# Related: #entries, #each_element
|
|
1190
1254
|
def each_entry(&block) # :yields: integer or range or :*
|
|
1191
1255
|
return to_enum(__method__) unless block_given?
|
|
1192
|
-
|
|
1256
|
+
each_entry_run do yield export_run_entry _1 end
|
|
1193
1257
|
end
|
|
1194
1258
|
|
|
1195
1259
|
# Yields each number or range (or <tt>:*</tt>) in #elements to the block
|
|
@@ -1201,39 +1265,17 @@ module Net
|
|
|
1201
1265
|
# Related: #elements, #each_entry
|
|
1202
1266
|
def each_element # :yields: integer or range or :*
|
|
1203
1267
|
return to_enum(__method__) unless block_given?
|
|
1204
|
-
|
|
1205
|
-
self
|
|
1206
|
-
end
|
|
1207
|
-
|
|
1208
|
-
private
|
|
1209
|
-
|
|
1210
|
-
def each_entry_tuple(&block)
|
|
1211
|
-
return to_enum(__method__) unless block_given?
|
|
1212
|
-
if @string
|
|
1213
|
-
@string.split(",") do block.call str_to_tuple _1 end
|
|
1214
|
-
else
|
|
1215
|
-
@tuples.each(&block)
|
|
1216
|
-
end
|
|
1268
|
+
runs.each do yield export_run_entry _1 end
|
|
1217
1269
|
self
|
|
1218
1270
|
end
|
|
1219
1271
|
|
|
1220
|
-
def tuple_to_entry((min, max))
|
|
1221
|
-
if min == STAR_INT then :*
|
|
1222
|
-
elsif max == STAR_INT then min..
|
|
1223
|
-
elsif min == max then min
|
|
1224
|
-
else min..max
|
|
1225
|
-
end
|
|
1226
|
-
end
|
|
1227
|
-
|
|
1228
|
-
public
|
|
1229
|
-
|
|
1230
1272
|
# Yields each range in #ranges to the block and returns self.
|
|
1231
1273
|
# Returns an enumerator when called without a block.
|
|
1232
1274
|
#
|
|
1233
1275
|
# Related: #ranges
|
|
1234
1276
|
def each_range # :yields: range
|
|
1235
1277
|
return to_enum(__method__) unless block_given?
|
|
1236
|
-
|
|
1278
|
+
minmaxes.each do |min, max|
|
|
1237
1279
|
if min == STAR_INT then yield :*..
|
|
1238
1280
|
elsif max == STAR_INT then yield min..
|
|
1239
1281
|
else yield min..max
|
|
@@ -1252,7 +1294,7 @@ module Net
|
|
|
1252
1294
|
def each_number(&block) # :yields: integer
|
|
1253
1295
|
return to_enum(__method__) unless block_given?
|
|
1254
1296
|
raise RangeError, '%s contains "*"' % [self.class] if include_star?
|
|
1255
|
-
|
|
1297
|
+
minmaxes.each do each_number_in_minmax _1, _2, &block end
|
|
1256
1298
|
self
|
|
1257
1299
|
end
|
|
1258
1300
|
|
|
@@ -1266,16 +1308,7 @@ module Net
|
|
|
1266
1308
|
def each_ordered_number(&block)
|
|
1267
1309
|
return to_enum(__method__) unless block_given?
|
|
1268
1310
|
raise RangeError, '%s contains "*"' % [self.class] if include_star?
|
|
1269
|
-
|
|
1270
|
-
end
|
|
1271
|
-
|
|
1272
|
-
private def each_number_in_tuple(min, max, &block)
|
|
1273
|
-
if min == STAR_INT then yield :*
|
|
1274
|
-
elsif min == max then yield min
|
|
1275
|
-
elsif max != STAR_INT then (min..max).each(&block)
|
|
1276
|
-
else
|
|
1277
|
-
raise RangeError, "#{SequenceSet} cannot enumerate range with '*'"
|
|
1278
|
-
end
|
|
1311
|
+
each_entry_minmax do each_number_in_minmax _1, _2, &block end
|
|
1279
1312
|
end
|
|
1280
1313
|
|
|
1281
1314
|
# Returns a Set with all of the #numbers in the sequence set.
|
|
@@ -1287,35 +1320,103 @@ module Net
|
|
|
1287
1320
|
# Related: #elements, #ranges, #numbers
|
|
1288
1321
|
def to_set; Set.new(numbers) end
|
|
1289
1322
|
|
|
1290
|
-
# Returns the
|
|
1323
|
+
# Returns the number of members in the set.
|
|
1324
|
+
#
|
|
1325
|
+
# Unlike #count, <tt>"*"</tt> is considered to be distinct from
|
|
1326
|
+
# <tt>2³² - 1</tt> (the maximum 32-bit unsigned integer value).
|
|
1327
|
+
#
|
|
1328
|
+
# set = Net::IMAP::SequenceSet[1..10]
|
|
1329
|
+
# set.count #=> 10
|
|
1330
|
+
# set.cardinality #=> 10
|
|
1331
|
+
#
|
|
1332
|
+
# set = Net::IMAP::SequenceSet["4294967295,*"]
|
|
1333
|
+
# set.count #=> 1
|
|
1334
|
+
# set.cardinality #=> 2
|
|
1335
|
+
#
|
|
1336
|
+
# set = Net::IMAP::SequenceSet[1..]
|
|
1337
|
+
# set.count #=> 4294967295
|
|
1338
|
+
# set.cardinality #=> 4294967296
|
|
1339
|
+
#
|
|
1340
|
+
# Related: #count, #count_with_duplicates
|
|
1341
|
+
def cardinality = minmaxes.sum(runs.count) { _2 - _1 }
|
|
1342
|
+
|
|
1343
|
+
# Returns the count of distinct #numbers in the set.
|
|
1344
|
+
#
|
|
1345
|
+
# Unlike #cardinality, <tt>"*"</tt> is considered to be equal to
|
|
1346
|
+
# <tt>2³² - 1</tt> (the maximum 32-bit unsigned integer value).
|
|
1347
|
+
#
|
|
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
|
|
1291
1355
|
#
|
|
1292
|
-
#
|
|
1293
|
-
#
|
|
1356
|
+
# set = Net::IMAP::SequenceSet[1..]
|
|
1357
|
+
# set.count #=> 4294967295
|
|
1358
|
+
# set.cardinality #=> 4294967296
|
|
1294
1359
|
#
|
|
1295
|
-
# Related: #count_with_duplicates
|
|
1360
|
+
# Related: #cardinality, #count_with_duplicates
|
|
1296
1361
|
def count
|
|
1297
|
-
|
|
1298
|
-
(include_star? && include?(UINT32_MAX) ? -1 : 0)
|
|
1362
|
+
cardinality + (include_star? && include?(UINT32_MAX) ? -1 : 0)
|
|
1299
1363
|
end
|
|
1300
1364
|
|
|
1301
|
-
alias size count
|
|
1302
|
-
|
|
1303
1365
|
# Returns the count of numbers in the ordered #entries, including any
|
|
1304
1366
|
# repeated numbers.
|
|
1305
1367
|
#
|
|
1306
|
-
#
|
|
1307
|
-
#
|
|
1308
|
-
#
|
|
1309
|
-
#
|
|
1310
|
-
#
|
|
1311
|
-
#
|
|
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
|
|
1312
1388
|
def count_with_duplicates
|
|
1313
1389
|
return count unless @string
|
|
1314
|
-
|
|
1390
|
+
each_entry_minmax.sum {|min, max|
|
|
1315
1391
|
max - min + ((max == STAR_INT && min != STAR_INT) ? 0 : 1)
|
|
1316
1392
|
}
|
|
1317
1393
|
end
|
|
1318
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
|
+
|
|
1319
1420
|
# Returns the count of repeated numbers in the ordered #entries, the
|
|
1320
1421
|
# difference between #count_with_duplicates and #count.
|
|
1321
1422
|
#
|
|
@@ -1333,7 +1434,7 @@ module Net
|
|
|
1333
1434
|
#
|
|
1334
1435
|
# Always returns +false+ when #string is normalized.
|
|
1335
1436
|
#
|
|
1336
|
-
# Related: #entries, #count_with_duplicates, #count_duplicates
|
|
1437
|
+
# Related: #entries, #count_with_duplicates, #count_duplicates
|
|
1337
1438
|
def has_duplicates?
|
|
1338
1439
|
return false unless @string
|
|
1339
1440
|
count_with_duplicates != count
|
|
@@ -1344,10 +1445,10 @@ module Net
|
|
|
1344
1445
|
#
|
|
1345
1446
|
# Related: #[], #at, #find_ordered_index
|
|
1346
1447
|
def find_index(number)
|
|
1347
|
-
number =
|
|
1348
|
-
|
|
1448
|
+
number = import_num number
|
|
1449
|
+
each_minmax_with_index(minmaxes) do |min, max, idx_min|
|
|
1349
1450
|
number < min and return nil
|
|
1350
|
-
number <= max and return
|
|
1451
|
+
number <= max and return export_num(idx_min + (number - min))
|
|
1351
1452
|
end
|
|
1352
1453
|
nil
|
|
1353
1454
|
end
|
|
@@ -1357,38 +1458,15 @@ module Net
|
|
|
1357
1458
|
#
|
|
1358
1459
|
# Related: #find_index
|
|
1359
1460
|
def find_ordered_index(number)
|
|
1360
|
-
number =
|
|
1361
|
-
|
|
1461
|
+
number = import_num number
|
|
1462
|
+
each_minmax_with_index(each_entry_minmax) do |min, max, idx_min|
|
|
1362
1463
|
if min <= number && number <= max
|
|
1363
|
-
return
|
|
1464
|
+
return export_num(idx_min + (number - min))
|
|
1364
1465
|
end
|
|
1365
1466
|
end
|
|
1366
1467
|
nil
|
|
1367
1468
|
end
|
|
1368
1469
|
|
|
1369
|
-
private
|
|
1370
|
-
|
|
1371
|
-
def each_tuple_with_index(tuples)
|
|
1372
|
-
idx_min = 0
|
|
1373
|
-
tuples.each do |min, max|
|
|
1374
|
-
idx_max = idx_min + (max - min)
|
|
1375
|
-
yield min, max, idx_min, idx_max
|
|
1376
|
-
idx_min = idx_max + 1
|
|
1377
|
-
end
|
|
1378
|
-
idx_min
|
|
1379
|
-
end
|
|
1380
|
-
|
|
1381
|
-
def reverse_each_tuple_with_index(tuples)
|
|
1382
|
-
idx_max = -1
|
|
1383
|
-
tuples.reverse_each do |min, max|
|
|
1384
|
-
yield min, max, (idx_min = idx_max - (max - min)), idx_max
|
|
1385
|
-
idx_max = idx_min - 1
|
|
1386
|
-
end
|
|
1387
|
-
idx_max
|
|
1388
|
-
end
|
|
1389
|
-
|
|
1390
|
-
public
|
|
1391
|
-
|
|
1392
1470
|
# :call-seq: at(index) -> integer or nil
|
|
1393
1471
|
#
|
|
1394
1472
|
# Returns the number at the given +index+ in the sorted set, without
|
|
@@ -1399,7 +1477,7 @@ module Net
|
|
|
1399
1477
|
#
|
|
1400
1478
|
# Related: #[], #slice, #ordered_at
|
|
1401
1479
|
def at(index)
|
|
1402
|
-
|
|
1480
|
+
seek_number_in_minmaxes(minmaxes, index)
|
|
1403
1481
|
end
|
|
1404
1482
|
|
|
1405
1483
|
# :call-seq: ordered_at(index) -> integer or nil
|
|
@@ -1412,21 +1490,7 @@ module Net
|
|
|
1412
1490
|
#
|
|
1413
1491
|
# Related: #[], #slice, #ordered_at
|
|
1414
1492
|
def ordered_at(index)
|
|
1415
|
-
|
|
1416
|
-
end
|
|
1417
|
-
|
|
1418
|
-
private def lookup_number_by_tuple_index(tuples, index)
|
|
1419
|
-
index = Integer(index.to_int)
|
|
1420
|
-
if index.negative?
|
|
1421
|
-
reverse_each_tuple_with_index(tuples) do |min, max, idx_min, idx_max|
|
|
1422
|
-
idx_min <= index and return from_tuple_int(min + (index - idx_min))
|
|
1423
|
-
end
|
|
1424
|
-
else
|
|
1425
|
-
each_tuple_with_index(tuples) do |min, _, idx_min, idx_max|
|
|
1426
|
-
index <= idx_max and return from_tuple_int(min + (index - idx_min))
|
|
1427
|
-
end
|
|
1428
|
-
end
|
|
1429
|
-
nil
|
|
1493
|
+
seek_number_in_minmaxes(each_entry_minmax, index)
|
|
1430
1494
|
end
|
|
1431
1495
|
|
|
1432
1496
|
# :call-seq:
|
|
@@ -1477,37 +1541,6 @@ module Net
|
|
|
1477
1541
|
|
|
1478
1542
|
alias slice :[]
|
|
1479
1543
|
|
|
1480
|
-
private
|
|
1481
|
-
|
|
1482
|
-
def slice_length(start, length)
|
|
1483
|
-
start = Integer(start.to_int)
|
|
1484
|
-
length = Integer(length.to_int)
|
|
1485
|
-
raise ArgumentError, "length must be positive" unless length.positive?
|
|
1486
|
-
last = start + length - 1 unless start.negative? && start.abs <= length
|
|
1487
|
-
slice_range(start..last)
|
|
1488
|
-
end
|
|
1489
|
-
|
|
1490
|
-
def slice_range(range)
|
|
1491
|
-
first = range.begin || 0
|
|
1492
|
-
last = range.end || -1
|
|
1493
|
-
if range.exclude_end?
|
|
1494
|
-
return remain_frozen_empty if last.zero?
|
|
1495
|
-
last -= 1 if range.end && last != STAR_INT
|
|
1496
|
-
end
|
|
1497
|
-
if (first * last).positive? && last < first
|
|
1498
|
-
remain_frozen_empty
|
|
1499
|
-
elsif (min = at(first))
|
|
1500
|
-
max = at(last)
|
|
1501
|
-
max = :* if max.nil?
|
|
1502
|
-
if max == :* then self & (min..)
|
|
1503
|
-
elsif min <= max then self & (min..max)
|
|
1504
|
-
else remain_frozen_empty
|
|
1505
|
-
end
|
|
1506
|
-
end
|
|
1507
|
-
end
|
|
1508
|
-
|
|
1509
|
-
public
|
|
1510
|
-
|
|
1511
1544
|
# Returns a copy of +self+ which only contains the numbers above +num+.
|
|
1512
1545
|
#
|
|
1513
1546
|
# Net::IMAP::SequenceSet["5,10:22,50"].above(10) # to_s => "11:22,50"
|
|
@@ -1579,7 +1612,7 @@ module Net
|
|
|
1579
1612
|
#
|
|
1580
1613
|
# Related: #limit!
|
|
1581
1614
|
def limit(max:)
|
|
1582
|
-
max =
|
|
1615
|
+
max = import_num(max)
|
|
1583
1616
|
if empty? then self.class.empty
|
|
1584
1617
|
elsif !include_star? && max < min then self.class.empty
|
|
1585
1618
|
elsif max(star: STAR_INT) <= max then frozen? ? self : dup.freeze
|
|
@@ -1592,53 +1625,134 @@ module Net
|
|
|
1592
1625
|
#
|
|
1593
1626
|
# Related: #limit
|
|
1594
1627
|
def limit!(max:)
|
|
1595
|
-
modifying! # short-circuit
|
|
1628
|
+
modifying! # short-circuit before querying
|
|
1596
1629
|
star = include_star?
|
|
1597
|
-
max =
|
|
1598
|
-
|
|
1599
|
-
|
|
1630
|
+
max = import_num(max)
|
|
1631
|
+
subtract_minmax [max + 1, STAR_INT]
|
|
1632
|
+
add_minmax [max, max ] if star
|
|
1600
1633
|
normalize!
|
|
1601
1634
|
end
|
|
1602
1635
|
|
|
1603
1636
|
# :call-seq: complement! -> self
|
|
1604
1637
|
#
|
|
1605
|
-
#
|
|
1606
|
-
# 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.
|
|
1607
1641
|
#
|
|
1608
1642
|
# Related: #complement
|
|
1609
1643
|
def complement!
|
|
1610
|
-
modifying! # short-circuit
|
|
1644
|
+
modifying! # short-circuit before querying
|
|
1611
1645
|
return replace(self.class.full) if empty?
|
|
1612
1646
|
return clear if full?
|
|
1613
|
-
flat =
|
|
1647
|
+
flat = minmaxes.flat_map { [_1 - 1, _2 + 1] }
|
|
1614
1648
|
if flat.first < 1 then flat.shift else flat.unshift 1 end
|
|
1615
1649
|
if STAR_INT < flat.last then flat.pop else flat.push STAR_INT end
|
|
1616
|
-
|
|
1650
|
+
replace_minmaxes flat.each_slice(2).to_a
|
|
1617
1651
|
normalize!
|
|
1618
1652
|
end
|
|
1619
1653
|
|
|
1620
|
-
#
|
|
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.
|
|
1621
1739
|
#
|
|
1622
|
-
# The returned set's #string is sorted and deduplicated. Adjacent or
|
|
1623
|
-
# overlapping elements will be merged into a single larger range.
|
|
1624
1740
|
# See SequenceSet@Ordered+and+Normalized+sets.
|
|
1625
1741
|
#
|
|
1626
1742
|
# Net::IMAP::SequenceSet["1:5,3:7,10:9,10:11"].normalize
|
|
1627
1743
|
# #=> Net::IMAP::SequenceSet["1:7,9:11"]
|
|
1628
1744
|
#
|
|
1629
|
-
# Related: #normalize!, #normalized_string
|
|
1745
|
+
# Related: #normalize!, #normalized_string, #normalized?
|
|
1630
1746
|
def normalize
|
|
1631
|
-
|
|
1632
|
-
return self if frozen? && str == string
|
|
1633
|
-
remain_frozen dup.instance_exec { @string = str&.-@; self }
|
|
1747
|
+
frozen? && normalized? ? self : remain_frozen(dup.normalize!)
|
|
1634
1748
|
end
|
|
1635
1749
|
|
|
1636
1750
|
# Resets #string to be sorted, deduplicated, and coalesced. Returns
|
|
1637
1751
|
# +self+. See SequenceSet@Ordered+and+Normalized+sets.
|
|
1638
1752
|
#
|
|
1639
|
-
# Related: #normalize, #normalized_string
|
|
1753
|
+
# Related: #normalize, #normalized_string, #normalized?
|
|
1640
1754
|
def normalize!
|
|
1641
|
-
modifying! # redundant check
|
|
1755
|
+
modifying! # redundant check (normalizes the error message for JRuby)
|
|
1642
1756
|
@string = nil
|
|
1643
1757
|
self
|
|
1644
1758
|
end
|
|
@@ -1652,9 +1766,9 @@ module Net
|
|
|
1652
1766
|
#
|
|
1653
1767
|
# Returns +nil+ when the set is empty.
|
|
1654
1768
|
#
|
|
1655
|
-
# Related: #normalize!, #normalize, #string, #to_s
|
|
1769
|
+
# Related: #normalize!, #normalize, #string, #to_s, #normalized?
|
|
1656
1770
|
def normalized_string
|
|
1657
|
-
|
|
1771
|
+
export_runs(runs) unless runs.empty?
|
|
1658
1772
|
end
|
|
1659
1773
|
|
|
1660
1774
|
# Returns an inspection string for the SequenceSet.
|
|
@@ -1691,8 +1805,8 @@ module Net
|
|
|
1691
1805
|
head = @string[INSPECT_ABRIDGED_HEAD_RE]
|
|
1692
1806
|
tail = @string[INSPECT_ABRIDGED_TAIL_RE]
|
|
1693
1807
|
else
|
|
1694
|
-
head =
|
|
1695
|
-
tail = "," +
|
|
1808
|
+
head = export_runs(runs.first(INSPECT_TRUNCATE_LEN)) + ","
|
|
1809
|
+
tail = "," + export_runs(runs.last(INSPECT_TRUNCATE_LEN))
|
|
1696
1810
|
end
|
|
1697
1811
|
'#<%s %d entries "%s...(%d entries omitted)...%s"%s>' % [
|
|
1698
1812
|
self.class, count,
|
|
@@ -1702,10 +1816,6 @@ module Net
|
|
|
1702
1816
|
end
|
|
1703
1817
|
end
|
|
1704
1818
|
|
|
1705
|
-
private def count_entries
|
|
1706
|
-
@string ? @string.count(",") + 1 : @tuples.count
|
|
1707
|
-
end
|
|
1708
|
-
|
|
1709
1819
|
##
|
|
1710
1820
|
# :method: to_sequence_set
|
|
1711
1821
|
# :call-seq: to_sequence_set -> self
|
|
@@ -1736,15 +1846,17 @@ module Net
|
|
|
1736
1846
|
|
|
1737
1847
|
# For YAML deserialization
|
|
1738
1848
|
def init_with(coder) # :nodoc:
|
|
1739
|
-
@
|
|
1849
|
+
@set_data = new_set_data
|
|
1740
1850
|
self.string = coder['string']
|
|
1741
1851
|
end
|
|
1742
1852
|
|
|
1853
|
+
# :stopdoc:
|
|
1743
1854
|
protected
|
|
1744
1855
|
|
|
1745
|
-
attr_reader :
|
|
1856
|
+
attr_reader :set_data
|
|
1746
1857
|
|
|
1747
|
-
|
|
1858
|
+
alias runs set_data
|
|
1859
|
+
alias minmaxes runs
|
|
1748
1860
|
|
|
1749
1861
|
private
|
|
1750
1862
|
|
|
@@ -1753,38 +1865,42 @@ module Net
|
|
|
1753
1865
|
|
|
1754
1866
|
# frozen clones are shallow copied
|
|
1755
1867
|
def initialize_clone(other)
|
|
1756
|
-
@
|
|
1868
|
+
@set_data = other.dup_set_data unless other.frozen?
|
|
1757
1869
|
super
|
|
1758
1870
|
end
|
|
1759
1871
|
|
|
1760
1872
|
def initialize_dup(other)
|
|
1761
|
-
@
|
|
1873
|
+
@set_data = other.dup_set_data
|
|
1762
1874
|
super
|
|
1763
1875
|
end
|
|
1764
1876
|
|
|
1765
|
-
|
|
1766
|
-
|
|
1877
|
+
######################################################################{{{2
|
|
1878
|
+
# Import methods
|
|
1879
|
+
|
|
1880
|
+
def import_minmax(input)
|
|
1881
|
+
entry = input_try_convert input
|
|
1767
1882
|
case entry
|
|
1768
|
-
when *STARS, Integer then [int =
|
|
1769
|
-
when Range then
|
|
1770
|
-
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)
|
|
1771
1886
|
else
|
|
1772
|
-
raise DataFormatError, "expected number or range, got %p" % [
|
|
1887
|
+
raise DataFormatError, "expected number or range, got %p" % [input]
|
|
1773
1888
|
end
|
|
1774
1889
|
end
|
|
1890
|
+
alias import_run import_minmax
|
|
1775
1891
|
|
|
1776
|
-
def
|
|
1777
|
-
set = input_try_convert
|
|
1892
|
+
def import_runs(input)
|
|
1893
|
+
set = input_try_convert input
|
|
1778
1894
|
case set
|
|
1779
|
-
when *STARS, Integer, Range then [
|
|
1780
|
-
when String then
|
|
1781
|
-
when SequenceSet then set.
|
|
1782
|
-
when Set then set.map { [
|
|
1783
|
-
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 }
|
|
1784
1900
|
when nil then []
|
|
1785
1901
|
else
|
|
1786
1902
|
raise DataFormatError, "expected nz-number, range, '*', Set, Array; " \
|
|
1787
|
-
"got %p" % [
|
|
1903
|
+
"got %p" % [input]
|
|
1788
1904
|
end
|
|
1789
1905
|
end
|
|
1790
1906
|
|
|
@@ -1797,17 +1913,9 @@ module Net
|
|
|
1797
1913
|
input
|
|
1798
1914
|
end
|
|
1799
1915
|
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
when *STARS, Integer then true
|
|
1804
|
-
when String then !input.include?(/[:,]/)
|
|
1805
|
-
end
|
|
1806
|
-
end
|
|
1807
|
-
|
|
1808
|
-
def range_to_tuple(range)
|
|
1809
|
-
first = to_tuple_int(range.begin || 1)
|
|
1810
|
-
last = to_tuple_int(range.end || :*)
|
|
1916
|
+
def import_range_minmax(range)
|
|
1917
|
+
first = import_num(range.begin || 1)
|
|
1918
|
+
last = import_num(range.end || :*)
|
|
1811
1919
|
last -= 1 if range.exclude_end? && range.end && last != STAR_INT
|
|
1812
1920
|
unless first <= last
|
|
1813
1921
|
raise DataFormatError, "invalid range for sequence-set: %p" % [range]
|
|
@@ -1815,71 +1923,260 @@ module Net
|
|
|
1815
1923
|
[first, last]
|
|
1816
1924
|
end
|
|
1817
1925
|
|
|
1818
|
-
def
|
|
1819
|
-
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
|
|
1820
1931
|
|
|
1821
|
-
def
|
|
1822
|
-
|
|
1932
|
+
def export_num(num) num == STAR_INT ? :* : num end
|
|
1933
|
+
|
|
1934
|
+
def export_minmaxes(minmaxes)
|
|
1935
|
+
-minmaxes.map { export_minmax _1 }.join(",")
|
|
1936
|
+
end
|
|
1937
|
+
|
|
1938
|
+
def export_minmax(minmax) minmax.uniq.map { export_num _1 }.join(":") end
|
|
1939
|
+
|
|
1940
|
+
alias export_runs export_minmaxes
|
|
1941
|
+
alias export_run export_minmax
|
|
1942
|
+
|
|
1943
|
+
def export_minmax_entry((min, max))
|
|
1944
|
+
if min == STAR_INT then :*
|
|
1945
|
+
elsif max == STAR_INT then min..
|
|
1946
|
+
elsif min == max then min
|
|
1947
|
+
else min..max
|
|
1948
|
+
end
|
|
1949
|
+
end
|
|
1950
|
+
alias export_run_entry export_minmax_entry
|
|
1951
|
+
|
|
1952
|
+
def each_number_in_minmax(min, max, &block)
|
|
1953
|
+
if min == STAR_INT then yield :*
|
|
1954
|
+
elsif min == max then yield min
|
|
1955
|
+
elsif max != STAR_INT then (min..max).each(&block)
|
|
1956
|
+
else
|
|
1957
|
+
raise RangeError, "#{SequenceSet} cannot enumerate range with '*'"
|
|
1958
|
+
end
|
|
1823
1959
|
end
|
|
1824
1960
|
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
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)
|
|
1828
1969
|
raise DataFormatError, "invalid sequence set string" if str.empty?
|
|
1829
|
-
str.split(":", 2).map! {
|
|
1970
|
+
str.split(":", 2).map! { import_num _1 }
|
|
1971
|
+
end
|
|
1972
|
+
|
|
1973
|
+
# yields validated but unsorted [num] or [num, num]
|
|
1974
|
+
def each_parsed_entry(str)
|
|
1975
|
+
return to_enum(__method__, str) unless block_given?
|
|
1976
|
+
str&.split(",", -1) do |entry| yield parse_entry(entry) end
|
|
1977
|
+
end
|
|
1978
|
+
|
|
1979
|
+
def normal_string?(str) normalized_entries? each_parsed_entry str end
|
|
1980
|
+
|
|
1981
|
+
def normalized_entries?(entries)
|
|
1982
|
+
max = nil
|
|
1983
|
+
entries.each do |first, last|
|
|
1984
|
+
return false if last && last <= first # 1:1 or 2:1
|
|
1985
|
+
return false if max && first <= max + 1 # 2,1 or 1,1 or 1,2
|
|
1986
|
+
max = last || first
|
|
1987
|
+
end
|
|
1988
|
+
true
|
|
1989
|
+
end
|
|
1990
|
+
|
|
1991
|
+
######################################################################{{{2
|
|
1992
|
+
# Ordered entry methods
|
|
1993
|
+
|
|
1994
|
+
def count_entries
|
|
1995
|
+
@string ? @string.count(",") + 1 : runs.count
|
|
1996
|
+
end
|
|
1997
|
+
|
|
1998
|
+
def each_entry_minmax(&block)
|
|
1999
|
+
return to_enum(__method__) unless block_given?
|
|
2000
|
+
if @string
|
|
2001
|
+
@string.split(",") do block.call parse_minmax _1 end
|
|
2002
|
+
else
|
|
2003
|
+
minmaxes.each(&block)
|
|
2004
|
+
end
|
|
2005
|
+
self
|
|
1830
2006
|
end
|
|
2007
|
+
alias each_entry_run each_entry_minmax
|
|
1831
2008
|
|
|
1832
|
-
|
|
2009
|
+
######################################################################{{{2
|
|
2010
|
+
# Search methods
|
|
1833
2011
|
|
|
1834
|
-
def
|
|
1835
|
-
|
|
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
|
|
1836
2016
|
range.include?(min) || range.include?(max) || (min..max).cover?(range)
|
|
1837
2017
|
end
|
|
1838
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
|
+
|
|
1839
2121
|
def modifying!
|
|
1840
2122
|
if frozen?
|
|
1841
2123
|
raise FrozenError, "can't modify frozen #{self.class}: %p" % [self]
|
|
1842
2124
|
end
|
|
1843
2125
|
end
|
|
1844
2126
|
|
|
1845
|
-
def
|
|
1846
|
-
|
|
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
|
|
1847
2140
|
|
|
1848
2141
|
#
|
|
1849
|
-
# --|=====| |=====new
|
|
1850
|
-
# ?????????-|=====new
|
|
2142
|
+
# --|=====| |=====new run=======| append
|
|
2143
|
+
# ?????????-|=====new run=======|-|===lower===|-- insert
|
|
1851
2144
|
#
|
|
1852
|
-
# |=====new
|
|
2145
|
+
# |=====new run=======|
|
|
1853
2146
|
# ---------??=======lower=======??--------------- noop
|
|
1854
2147
|
#
|
|
1855
2148
|
# ---------??===lower==|--|==| join remaining
|
|
1856
2149
|
# ---------??===lower==|--|==|----|===upper===|-- join until upper
|
|
1857
2150
|
# ---------??===lower==|--|==|--|=====upper===|-- join to upper
|
|
1858
|
-
def
|
|
2151
|
+
def add_minmax(minmax)
|
|
1859
2152
|
modifying!
|
|
1860
|
-
min, max
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
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)
|
|
1865
2159
|
end
|
|
1866
2160
|
end
|
|
1867
2161
|
|
|
1868
|
-
def
|
|
1869
|
-
return if
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
lower_idx
|
|
1873
|
-
return if
|
|
1874
|
-
tmax_adj =
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
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
|
|
1878
2175
|
end
|
|
1879
|
-
|
|
2176
|
+
slice_runs! next_idx..upper_idx
|
|
1880
2177
|
end
|
|
1881
2178
|
|
|
1882
|
-
# |====
|
|
2179
|
+
# |====subtracted run=======|
|
|
1883
2180
|
# --|====| no more 1. noop
|
|
1884
2181
|
# --|====|---------------------------|====lower====|-- 2. noop
|
|
1885
2182
|
# -------|======lower================|---------------- 3. split
|
|
@@ -1892,61 +2189,59 @@ module Net
|
|
|
1892
2189
|
# -------??=====lower====|--|====| no more 6. delete rest
|
|
1893
2190
|
# -------??=====lower====|--|====|---|====upper====|-- 7. delete until
|
|
1894
2191
|
# -------??=====lower====|--|====|--|=====upper====|-- 8. delete and trim
|
|
1895
|
-
def
|
|
2192
|
+
def subtract_minmax(minmax)
|
|
1896
2193
|
modifying!
|
|
1897
|
-
min, max
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
elsif max <
|
|
1902
|
-
|
|
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
|
|
1903
2201
|
end
|
|
1904
2202
|
end
|
|
1905
2203
|
|
|
1906
|
-
def
|
|
1907
|
-
|
|
1908
|
-
|
|
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
|
|
1909
2208
|
end
|
|
1910
|
-
lower[0] = tmax + 1
|
|
1911
2209
|
end
|
|
1912
2210
|
|
|
1913
|
-
def
|
|
1914
|
-
if
|
|
1915
|
-
|
|
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
|
|
1916
2214
|
lower_idx += 1
|
|
1917
2215
|
end
|
|
1918
|
-
if tmax ==
|
|
1919
|
-
|
|
1920
|
-
elsif (
|
|
1921
|
-
upper_idx
|
|
1922
|
-
|
|
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
|
|
1923
2225
|
end
|
|
1924
|
-
tuples.slice!(lower_idx..upper_idx)
|
|
1925
|
-
end
|
|
1926
|
-
|
|
1927
|
-
def tuple_gte_with_index(num)
|
|
1928
|
-
idx = tuples.bsearch_index { _2 >= num } and [tuples[idx], idx]
|
|
1929
2226
|
end
|
|
1930
2227
|
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
def nz_number(num)
|
|
1937
|
-
String === num && !/\A[1-9]\d*\z/.match?(num) and
|
|
1938
|
-
raise DataFormatError, "%p is not a valid nz-number" % [num]
|
|
1939
|
-
NumValidator.ensure_nz_number Integer num
|
|
1940
|
-
rescue TypeError # To catch errors from Integer()
|
|
1941
|
-
raise DataFormatError, $!.message
|
|
1942
|
-
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
|
|
1943
2232
|
|
|
2233
|
+
######################################################################{{{2
|
|
1944
2234
|
# intentionally defined after the class implementation
|
|
1945
2235
|
|
|
2236
|
+
FULL_SET_DATA = [[1, STAR_INT].freeze].freeze
|
|
2237
|
+
private_constant :FULL_SET_DATA
|
|
2238
|
+
|
|
1946
2239
|
EMPTY = new.freeze
|
|
1947
2240
|
FULL = self["1:*"]
|
|
1948
2241
|
private_constant :EMPTY, :FULL
|
|
1949
2242
|
|
|
2243
|
+
# }}}
|
|
2244
|
+
# vim:foldmethod=marker
|
|
1950
2245
|
end
|
|
1951
2246
|
end
|
|
1952
2247
|
end
|