net-imap 0.5.5 → 0.5.7

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.
@@ -56,18 +56,20 @@ module Net
56
56
  # set = Net::IMAP::SequenceSet[1, 2, [3..7, 5], 6..10, 2048, 1024]
57
57
  # set.valid_string #=> "1:10,55,1024:2048"
58
58
  #
59
- # == Normalized form
59
+ # == Ordered and Normalized sets
60
60
  #
61
- # When a sequence set is created with a single String value, that #string
62
- # representation is preserved. SequenceSet's internal representation
63
- # implicitly sorts all entries, de-duplicates numbers, and coalesces
64
- # adjacent or overlapping ranges. Most enumeration methods and offset-based
65
- # methods use this normalized representation. Most modification methods
66
- # will convert #string to its normalized form.
61
+ # Sometimes the order of the set's members is significant, such as with the
62
+ # +ESORT+, <tt>CONTEXT=SORT</tt>, and +UIDPLUS+ extensions. So, when a
63
+ # sequence set is created by the parser or with a single string value, that
64
+ # #string representation is preserved.
67
65
  #
68
- # In some cases the order of the string representation is significant, such
69
- # as the +ESORT+, <tt>CONTEXT=SORT</tt>, and +UIDPLUS+ extensions. Use
70
- # #entries or #each_entry to enumerate the set in its original order. To
66
+ # Internally, SequenceSet stores a normalized representation which sorts all
67
+ # entries, de-duplicates numbers, and coalesces adjacent or overlapping
68
+ # ranges. Most methods use this normalized representation to achieve
69
+ # <tt>O(lg n)</tt> porformance. Use #entries or #each_entry to enumerate
70
+ # the set in its original order.
71
+ #
72
+ # Most modification methods convert #string to its normalized form. To
71
73
  # preserve #string order while modifying a set, use #append, #string=, or
72
74
  # #replace.
73
75
  #
@@ -160,7 +162,7 @@ module Net
160
162
  # - #===:
161
163
  # Returns whether a given object is fully contained within +self+, or
162
164
  # +nil+ if the object cannot be converted to a compatible type.
163
- # - #cover? (aliased as #===):
165
+ # - #cover?:
164
166
  # Returns whether a given object is fully contained within +self+.
165
167
  # - #intersect? (aliased as #overlap?):
166
168
  # Returns whether +self+ and a given object have any common elements.
@@ -172,7 +174,7 @@ module Net
172
174
  #
173
175
  # <i>Set membership:</i>
174
176
  # - #include? (aliased as #member?):
175
- # Returns whether a given object (nz-number, range, or <tt>*</tt>) is
177
+ # Returns whether a given element (nz-number, range, or <tt>*</tt>) is
176
178
  # contained by the set.
177
179
  # - #include_star?: Returns whether the set contains <tt>*</tt>.
178
180
  #
@@ -181,30 +183,41 @@ module Net
181
183
  # - #max: Returns the maximum number in the set.
182
184
  # - #minmax: Returns the minimum and maximum numbers in the set.
183
185
  #
184
- # <i>Accessing value by offset:</i>
186
+ # <i>Accessing value by offset in sorted set:</i>
185
187
  # - #[] (aliased as #slice): Returns the number or consecutive subset at a
186
- # given offset or range of offsets.
187
- # - #at: Returns the number at a given offset.
188
- # - #find_index: Returns the given number's offset in the set
188
+ # given offset or range of offsets in the sorted set.
189
+ # - #at: Returns the number at a given offset in the sorted set.
190
+ # - #find_index: Returns the given number's offset in the sorted set.
191
+ #
192
+ # <i>Accessing value by offset in ordered entries</i>
193
+ # - #ordered_at: Returns the number at a given offset in the ordered entries.
194
+ # - #find_ordered_index: Returns the index of the given number's first
195
+ # occurrence in entries.
189
196
  #
190
197
  # <i>Set cardinality:</i>
191
198
  # - #count (aliased as #size): Returns the count of numbers in the set.
199
+ # Duplicated numbers are not counted.
192
200
  # - #empty?: Returns whether the set has no members. \IMAP syntax does not
193
201
  # allow empty sequence sets.
194
202
  # - #valid?: Returns whether the set has any members.
195
203
  # - #full?: Returns whether the set contains every possible value, including
196
204
  # <tt>*</tt>.
197
205
  #
206
+ # <i>Denormalized properties:</i>
207
+ # - #has_duplicates?: Returns whether the ordered entries repeat any
208
+ # numbers.
209
+ # - #count_duplicates: Returns the count of repeated numbers in the ordered
210
+ # entries.
211
+ # - #count_with_duplicates: Returns the count of numbers in the ordered
212
+ # entries, including any repeated numbers.
213
+ #
198
214
  # === Methods for Iterating
199
215
  #
216
+ # <i>Normalized (sorted and coalesced):</i>
200
217
  # - #each_element: Yields each number and range in the set, sorted and
201
218
  # coalesced, and returns +self+.
202
219
  # - #elements (aliased as #to_a): Returns an Array of every number and range
203
220
  # in the set, sorted and coalesced.
204
- # - #each_entry: Yields each number and range in the set, unsorted and
205
- # without deduplicating numbers or coalescing ranges, and returns +self+.
206
- # - #entries: Returns an Array of every number and range in the set,
207
- # unsorted and without deduplicating numbers or coalescing ranges.
208
221
  # - #each_range:
209
222
  # Yields each element in the set as a Range and returns +self+.
210
223
  # - #ranges: Returns an Array of every element in the set, converting
@@ -214,17 +227,25 @@ module Net
214
227
  # ranges into all of their contained numbers.
215
228
  # - #to_set: Returns a Set containing all of the #numbers in the set.
216
229
  #
230
+ # <i>Order preserving:</i>
231
+ # - #each_entry: Yields each number and range in the set, unsorted and
232
+ # without deduplicating numbers or coalescing ranges, and returns +self+.
233
+ # - #entries: Returns an Array of every number and range in the set,
234
+ # unsorted and without deduplicating numbers or coalescing ranges.
235
+ # - #each_ordered_number: Yields each number in the ordered entries and
236
+ # returns +self+.
237
+ #
217
238
  # === Methods for \Set Operations
218
239
  # These methods do not modify +self+.
219
240
  #
220
241
  # - #| (aliased as #union and #+): Returns a new set combining all members
221
- # from +self+ with all members from the other object.
242
+ # from +self+ with all members from the other set.
222
243
  # - #& (aliased as #intersection): Returns a new set containing all members
223
- # common to +self+ and the other object.
244
+ # common to +self+ and the other set.
224
245
  # - #- (aliased as #difference): Returns a copy of +self+ with all members
225
- # in the other object removed.
246
+ # in the other set removed.
226
247
  # - #^ (aliased as #xor): Returns a new set containing all members from
227
- # +self+ and the other object except those common to both.
248
+ # +self+ and the other set except those common to both.
228
249
  # - #~ (aliased as #complement): Returns a new set containing all members
229
250
  # that are not in +self+
230
251
  # - #limit: Returns a copy of +self+ which has replaced <tt>*</tt> with a
@@ -233,28 +254,39 @@ module Net
233
254
  # === Methods for Assigning
234
255
  # These methods add or replace elements in +self+.
235
256
  #
236
- # - #add (aliased as #<<): Adds a given object to the set; returns +self+.
237
- # - #add?: If the given object is not an element in the set, adds it and
257
+ # <i>Normalized (sorted and coalesced):</i>
258
+ #
259
+ # These methods always update #string to be fully sorted and coalesced.
260
+ #
261
+ # - #add (aliased as #<<): Adds a given element to the set; returns +self+.
262
+ # - #add?: If the given element is not fully included the set, adds it and
238
263
  # returns +self+; otherwise, returns +nil+.
239
- # - #merge: Merges multiple elements into the set; returns +self+.
240
- # - #append: Adds a given object to the set, appending it to the existing
264
+ # - #merge: Adds all members of the given sets into this set; returns +self+.
265
+ # - #complement!: Replaces the contents of the set with its own #complement.
266
+ #
267
+ # <i>Order preserving:</i>
268
+ #
269
+ # These methods _may_ cause #string to not be sorted or coalesced.
270
+ #
271
+ # - #append: Adds the given entry to the set, appending it to the existing
241
272
  # string, and returns +self+.
242
273
  # - #string=: Assigns a new #string value and replaces #elements to match.
243
274
  # - #replace: Replaces the contents of the set with the contents
244
275
  # of a given object.
245
- # - #complement!: Replaces the contents of the set with its own #complement.
246
276
  #
247
277
  # === Methods for Deleting
248
- # These methods remove elements from +self+.
278
+ # These methods remove elements from +self+, and update #string to be fully
279
+ # sorted and coalesced.
249
280
  #
250
281
  # - #clear: Removes all elements in the set; returns +self+.
251
- # - #delete: Removes a given object from the set; returns +self+.
252
- # - #delete?: If the given object is an element in the set, removes it and
282
+ # - #delete: Removes a given element from the set; returns +self+.
283
+ # - #delete?: If the given element is included in the set, removes it and
253
284
  # returns it; otherwise, returns +nil+.
254
285
  # - #delete_at: Removes the number at a given offset.
255
286
  # - #slice!: Removes the number or consecutive numbers at a given offset or
256
287
  # range of offsets.
257
- # - #subtract: Removes each given object from the set; returns +self+.
288
+ # - #subtract: Removes all members of the given sets from this set; returns
289
+ # +self+.
258
290
  # - #limit!: Replaces <tt>*</tt> with a given maximum value and removes all
259
291
  # members over that maximum; returns +self+.
260
292
  #
@@ -287,9 +319,12 @@ module Net
287
319
  class << self
288
320
 
289
321
  # :call-seq:
290
- # SequenceSet[*values] -> valid frozen sequence set
322
+ # SequenceSet[*inputs] -> valid frozen sequence set
323
+ #
324
+ # Returns a frozen SequenceSet, constructed from +inputs+.
291
325
  #
292
- # Returns a frozen SequenceSet, constructed from +values+.
326
+ # When only a single valid frozen SequenceSet is given, that same set is
327
+ # returned.
293
328
  #
294
329
  # An empty SequenceSet is invalid and will raise a DataFormatError.
295
330
  #
@@ -659,7 +694,7 @@ module Net
659
694
  alias complement :~
660
695
 
661
696
  # :call-seq:
662
- # add(object) -> self
697
+ # add(element) -> self
663
698
  # self << other -> self
664
699
  #
665
700
  # Adds a range or number to the set and returns +self+.
@@ -667,8 +702,8 @@ module Net
667
702
  # #string will be regenerated. Use #merge to add many elements at once.
668
703
  #
669
704
  # Related: #add?, #merge, #union
670
- def add(object)
671
- tuple_add input_to_tuple object
705
+ def add(element)
706
+ tuple_add input_to_tuple element
672
707
  normalize!
673
708
  end
674
709
  alias << add
@@ -677,28 +712,29 @@ module Net
677
712
  #
678
713
  # Unlike #add, #merge, or #union, the new value is appended to #string.
679
714
  # This may result in a #string which has duplicates or is out-of-order.
680
- def append(object)
715
+ def append(entry)
681
716
  modifying!
682
- tuple = input_to_tuple object
717
+ tuple = input_to_tuple entry
683
718
  entry = tuple_to_str tuple
719
+ string unless empty? # write @string before tuple_add
684
720
  tuple_add tuple
685
- @string = -(string ? "#{@string},#{entry}" : entry)
721
+ @string = -(@string ? "#{@string},#{entry}" : entry)
686
722
  self
687
723
  end
688
724
 
689
- # :call-seq: add?(object) -> self or nil
725
+ # :call-seq: add?(element) -> self or nil
690
726
  #
691
727
  # Adds a range or number to the set and returns +self+. Returns +nil+
692
- # when the object is already included in the set.
728
+ # when the element is already included in the set.
693
729
  #
694
730
  # #string will be regenerated. Use #merge to add many elements at once.
695
731
  #
696
732
  # Related: #add, #merge, #union, #include?
697
- def add?(object)
698
- add object unless include? object
733
+ def add?(element)
734
+ add element unless include? element
699
735
  end
700
736
 
701
- # :call-seq: delete(object) -> self
737
+ # :call-seq: delete(element) -> self
702
738
  #
703
739
  # Deletes the given range or number from the set and returns +self+.
704
740
  #
@@ -706,8 +742,8 @@ module Net
706
742
  # many elements at once.
707
743
  #
708
744
  # Related: #delete?, #delete_at, #subtract, #difference
709
- def delete(object)
710
- tuple_subtract input_to_tuple object
745
+ def delete(element)
746
+ tuple_subtract input_to_tuple element
711
747
  normalize!
712
748
  end
713
749
 
@@ -743,8 +779,8 @@ module Net
743
779
  # #string will be regenerated after deletion.
744
780
  #
745
781
  # Related: #delete, #delete_at, #subtract, #difference, #disjoint?
746
- def delete?(object)
747
- tuple = input_to_tuple object
782
+ def delete?(element)
783
+ tuple = input_to_tuple element
748
784
  if tuple.first == tuple.last
749
785
  return unless include_tuple? tuple
750
786
  tuple_subtract tuple
@@ -788,33 +824,31 @@ module Net
788
824
  deleted
789
825
  end
790
826
 
791
- # Merges all of the elements that appear in any of the +inputs+ into the
827
+ # Merges all of the elements that appear in any of the +sets+ into the
792
828
  # set, and returns +self+.
793
829
  #
794
- # The +inputs+ may be any objects that would be accepted by ::new:
795
- # non-zero 32 bit unsigned integers, ranges, <tt>sequence-set</tt>
796
- # formatted strings, other sequence sets, or enumerables containing any of
797
- # these.
830
+ # The +sets+ may be any objects that would be accepted by ::new: non-zero
831
+ # 32 bit unsigned integers, ranges, <tt>sequence-set</tt> formatted
832
+ # strings, other sequence sets, or enumerables containing any of these.
798
833
  #
799
- # #string will be regenerated after all inputs have been merged.
834
+ # #string will be regenerated after all sets have been merged.
800
835
  #
801
836
  # Related: #add, #add?, #union
802
- def merge(*inputs)
803
- tuples_add input_to_tuples inputs
837
+ def merge(*sets)
838
+ tuples_add input_to_tuples sets
804
839
  normalize!
805
840
  end
806
841
 
807
- # Removes all of the elements that appear in any of the given +objects+
808
- # from the set, and returns +self+.
842
+ # Removes all of the elements that appear in any of the given +sets+ from
843
+ # the set, and returns +self+.
809
844
  #
810
- # The +objects+ may be any objects that would be accepted by ::new:
811
- # non-zero 32 bit unsigned integers, ranges, <tt>sequence-set</tt>
812
- # formatted strings, other sequence sets, or enumerables containing any of
813
- # these.
845
+ # The +sets+ may be any objects that would be accepted by ::new: non-zero
846
+ # 32 bit unsigned integers, ranges, <tt>sequence-set</tt> formatted
847
+ # strings, other sequence sets, or enumerables containing any of these.
814
848
  #
815
849
  # Related: #difference
816
- def subtract(*objects)
817
- tuples_subtract input_to_tuples objects
850
+ def subtract(*sets)
851
+ tuples_subtract input_to_tuples sets
818
852
  normalize!
819
853
  end
820
854
 
@@ -838,8 +872,8 @@ module Net
838
872
  # <tt>*</tt> translates to an endless range. Use #limit to translate both
839
873
  # cases to a maximum value.
840
874
  #
841
- # If the original input was unordered or contains overlapping ranges, the
842
- # returned ranges will be ordered and coalesced.
875
+ # The returned elements will be sorted and coalesced, even when the input
876
+ # #string is not. <tt>*</tt> will sort last. See #normalize.
843
877
  #
844
878
  # Net::IMAP::SequenceSet["2,5:9,6,*,12:11"].elements
845
879
  # #=> [2, 5..9, 11..12, :*]
@@ -857,7 +891,7 @@ module Net
857
891
  # translates to <tt>:*..</tt>. Use #limit to set <tt>*</tt> to a maximum
858
892
  # value.
859
893
  #
860
- # The returned ranges will be ordered and coalesced, even when the input
894
+ # The returned ranges will be sorted and coalesced, even when the input
861
895
  # #string is not. <tt>*</tt> will sort last. See #normalize.
862
896
  #
863
897
  # Net::IMAP::SequenceSet["2,5:9,6,*,12:11"].ranges
@@ -906,9 +940,7 @@ module Net
906
940
  # Related: #entries, #each_element
907
941
  def each_entry(&block) # :yields: integer or range or :*
908
942
  return to_enum(__method__) unless block_given?
909
- return each_element(&block) unless @string
910
- @string.split(",").each do yield tuple_to_entry str_to_tuple _1 end
911
- self
943
+ each_entry_tuple do yield tuple_to_entry _1 end
912
944
  end
913
945
 
914
946
  # Yields each number or range (or <tt>:*</tt>) in #elements to the block
@@ -926,6 +958,16 @@ module Net
926
958
 
927
959
  private
928
960
 
961
+ def each_entry_tuple(&block)
962
+ return to_enum(__method__) unless block_given?
963
+ if @string
964
+ @string.split(",") do block.call str_to_tuple _1 end
965
+ else
966
+ @tuples.each(&block)
967
+ end
968
+ self
969
+ end
970
+
929
971
  def tuple_to_entry((min, max))
930
972
  if min == STAR_INT then :*
931
973
  elsif max == STAR_INT then min..
@@ -957,19 +999,36 @@ module Net
957
999
  # Returns an enumerator when called without a block (even if the set
958
1000
  # contains <tt>*</tt>).
959
1001
  #
960
- # Related: #numbers
1002
+ # Related: #numbers, #each_ordered_number
961
1003
  def each_number(&block) # :yields: integer
962
1004
  return to_enum(__method__) unless block_given?
963
1005
  raise RangeError, '%s contains "*"' % [self.class] if include_star?
964
- each_element do |elem|
965
- case elem
966
- when Range then elem.each(&block)
967
- when Integer then block.(elem)
968
- end
969
- end
1006
+ @tuples.each do each_number_in_tuple _1, _2, &block end
970
1007
  self
971
1008
  end
972
1009
 
1010
+ # Yields each number in #entries to the block and returns self.
1011
+ # If the set contains a <tt>*</tt>, RangeError will be raised.
1012
+ #
1013
+ # Returns an enumerator when called without a block (even if the set
1014
+ # contains <tt>*</tt>).
1015
+ #
1016
+ # Related: #entries, #each_number
1017
+ def each_ordered_number(&block)
1018
+ return to_enum(__method__) unless block_given?
1019
+ raise RangeError, '%s contains "*"' % [self.class] if include_star?
1020
+ each_entry_tuple do each_number_in_tuple _1, _2, &block end
1021
+ end
1022
+
1023
+ private def each_number_in_tuple(min, max, &block)
1024
+ if min == STAR_INT then yield :*
1025
+ elsif min == max then yield min
1026
+ elsif max != STAR_INT then (min..max).each(&block)
1027
+ else
1028
+ raise RangeError, "#{SequenceSet} cannot enumerate range with '*'"
1029
+ end
1030
+ end
1031
+
973
1032
  # Returns a Set with all of the #numbers in the sequence set.
974
1033
  #
975
1034
  # If the set contains a <tt>*</tt>, RangeError will be raised.
@@ -981,8 +1040,10 @@ module Net
981
1040
 
982
1041
  # Returns the count of #numbers in the set.
983
1042
  #
984
- # If <tt>*</tt> and <tt>2**32 - 1</tt> (the maximum 32-bit unsigned
985
- # integer value) are both in the set, they will only be counted once.
1043
+ # <tt>*</tt> will be counted as <tt>2**32 - 1</tt> (the maximum 32-bit
1044
+ # unsigned integer value).
1045
+ #
1046
+ # Related: #count_with_duplicates
986
1047
  def count
987
1048
  @tuples.sum(@tuples.count) { _2 - _1 } +
988
1049
  (include_star? && include?(UINT32_MAX) ? -1 : 0)
@@ -990,33 +1051,87 @@ module Net
990
1051
 
991
1052
  alias size count
992
1053
 
993
- # Returns the index of +number+ in the set, or +nil+ if +number+ isn't in
994
- # the set.
1054
+ # Returns the count of numbers in the ordered #entries, including any
1055
+ # repeated numbers.
1056
+ #
1057
+ # <tt>*</tt> will be counted as <tt>2**32 - 1</tt> (the maximum 32-bit
1058
+ # unsigned integer value).
1059
+ #
1060
+ # When #string is normalized, this behaves the same as #count.
1061
+ #
1062
+ # Related: #entries, #count_duplicates, #has_duplicates?
1063
+ def count_with_duplicates
1064
+ return count unless @string
1065
+ each_entry_tuple.sum {|min, max|
1066
+ max - min + ((max == STAR_INT && min != STAR_INT) ? 0 : 1)
1067
+ }
1068
+ end
1069
+
1070
+ # Returns the count of repeated numbers in the ordered #entries, the
1071
+ # difference between #count_with_duplicates and #count.
995
1072
  #
996
- # Related: #[]
1073
+ # When #string is normalized, this is zero.
1074
+ #
1075
+ # Related: #entries, #count_with_duplicates, #has_duplicates?
1076
+ def count_duplicates
1077
+ return 0 unless @string
1078
+ count_with_duplicates - count
1079
+ end
1080
+
1081
+ # :call-seq: has_duplicates? -> true | false
1082
+ #
1083
+ # Returns whether or not the ordered #entries repeat any numbers.
1084
+ #
1085
+ # Always returns +false+ when #string is normalized.
1086
+ #
1087
+ # Related: #entries, #count_with_duplicates, #count_duplicates?
1088
+ def has_duplicates?
1089
+ return false unless @string
1090
+ count_with_duplicates != count
1091
+ end
1092
+
1093
+ # Returns the (sorted and deduplicated) index of +number+ in the set, or
1094
+ # +nil+ if +number+ isn't in the set.
1095
+ #
1096
+ # Related: #[], #at, #find_ordered_index
997
1097
  def find_index(number)
998
1098
  number = to_tuple_int number
999
- each_tuple_with_index do |min, max, idx_min|
1099
+ each_tuple_with_index(@tuples) do |min, max, idx_min|
1000
1100
  number < min and return nil
1001
1101
  number <= max and return from_tuple_int(idx_min + (number - min))
1002
1102
  end
1003
1103
  nil
1004
1104
  end
1005
1105
 
1106
+ # Returns the first index of +number+ in the ordered #entries, or
1107
+ # +nil+ if +number+ isn't in the set.
1108
+ #
1109
+ # Related: #find_index
1110
+ def find_ordered_index(number)
1111
+ number = to_tuple_int number
1112
+ each_tuple_with_index(each_entry_tuple) do |min, max, idx_min|
1113
+ if min <= number && number <= max
1114
+ return from_tuple_int(idx_min + (number - min))
1115
+ end
1116
+ end
1117
+ nil
1118
+ end
1119
+
1006
1120
  private
1007
1121
 
1008
- def each_tuple_with_index
1122
+ def each_tuple_with_index(tuples)
1009
1123
  idx_min = 0
1010
- @tuples.each do |min, max|
1011
- yield min, max, idx_min, (idx_max = idx_min + (max - min))
1124
+ tuples.each do |min, max|
1125
+ idx_max = idx_min + (max - min)
1126
+ yield min, max, idx_min, idx_max
1012
1127
  idx_min = idx_max + 1
1013
1128
  end
1014
1129
  idx_min
1015
1130
  end
1016
1131
 
1017
- def reverse_each_tuple_with_index
1132
+ def reverse_each_tuple_with_index(tuples)
1018
1133
  idx_max = -1
1019
- @tuples.reverse_each do |min, max|
1134
+ tuples.reverse_each do |min, max|
1020
1135
  yield min, max, (idx_min = idx_max - (max - min)), idx_max
1021
1136
  idx_max = idx_min - 1
1022
1137
  end
@@ -1027,18 +1142,38 @@ module Net
1027
1142
 
1028
1143
  # :call-seq: at(index) -> integer or nil
1029
1144
  #
1030
- # Returns a number from +self+, without modifying the set. Behaves the
1031
- # same as #[], except that #at only allows a single integer argument.
1145
+ # Returns the number at the given +index+ in the sorted set, without
1146
+ # modifying the set.
1147
+ #
1148
+ # +index+ is interpreted the same as in #[], except that #at only allows a
1149
+ # single integer argument.
1032
1150
  #
1033
- # Related: #[], #slice
1151
+ # Related: #[], #slice, #ordered_at
1034
1152
  def at(index)
1153
+ lookup_number_by_tuple_index(tuples, index)
1154
+ end
1155
+
1156
+ # :call-seq: ordered_at(index) -> integer or nil
1157
+ #
1158
+ # Returns the number at the given +index+ in the ordered #entries, without
1159
+ # modifying the set.
1160
+ #
1161
+ # +index+ is interpreted the same as in #at (and #[]), except that
1162
+ # #ordered_at applies to the ordered #entries, not the sorted set.
1163
+ #
1164
+ # Related: #[], #slice, #ordered_at
1165
+ def ordered_at(index)
1166
+ lookup_number_by_tuple_index(each_entry_tuple, index)
1167
+ end
1168
+
1169
+ private def lookup_number_by_tuple_index(tuples, index)
1035
1170
  index = Integer(index.to_int)
1036
1171
  if index.negative?
1037
- reverse_each_tuple_with_index do |min, max, idx_min, idx_max|
1172
+ reverse_each_tuple_with_index(tuples) do |min, max, idx_min, idx_max|
1038
1173
  idx_min <= index and return from_tuple_int(min + (index - idx_min))
1039
1174
  end
1040
1175
  else
1041
- each_tuple_with_index do |min, _, idx_min, idx_max|
1176
+ each_tuple_with_index(tuples) do |min, _, idx_min, idx_max|
1042
1177
  index <= idx_max and return from_tuple_int(min + (index - idx_min))
1043
1178
  end
1044
1179
  end
@@ -1053,17 +1188,18 @@ module Net
1053
1188
  # seqset[range] -> sequence set or nil
1054
1189
  # slice(range) -> sequence set or nil
1055
1190
  #
1056
- # Returns a number or a subset from +self+, without modifying the set.
1191
+ # Returns a number or a subset from the _sorted_ set, without modifying
1192
+ # the set.
1057
1193
  #
1058
1194
  # When an Integer argument +index+ is given, the number at offset +index+
1059
- # is returned:
1195
+ # in the sorted set is returned:
1060
1196
  #
1061
1197
  # set = Net::IMAP::SequenceSet["10:15,20:23,26"]
1062
1198
  # set[0] #=> 10
1063
1199
  # set[5] #=> 15
1064
1200
  # set[10] #=> 26
1065
1201
  #
1066
- # If +index+ is negative, it counts relative to the end of +self+:
1202
+ # If +index+ is negative, it counts relative to the end of the sorted set:
1067
1203
  # set = Net::IMAP::SequenceSet["10:15,20:23,26"]
1068
1204
  # set[-1] #=> 26
1069
1205
  # set[-3] #=> 22
@@ -1075,13 +1211,14 @@ module Net
1075
1211
  # set[11] #=> nil
1076
1212
  # set[-12] #=> nil
1077
1213
  #
1078
- # The result is based on the normalized set—sorted and de-duplicatednot
1079
- # on the assigned value of #string.
1214
+ # The result is based on the sorted and de-duplicated set, not on the
1215
+ # ordered #entries in #string.
1080
1216
  #
1081
1217
  # set = Net::IMAP::SequenceSet["12,20:23,11:16,21"]
1082
1218
  # set[0] #=> 11
1083
1219
  # set[-1] #=> 23
1084
1220
  #
1221
+ # Related: #at
1085
1222
  def [](index, length = nil)
1086
1223
  if length then slice_length(index, length)
1087
1224
  elsif index.is_a?(Range) then slice_range(index)
@@ -1232,6 +1369,18 @@ module Net
1232
1369
  imap.__send__(:put_string, valid_string)
1233
1370
  end
1234
1371
 
1372
+ # For YAML serialization
1373
+ def encode_with(coder) # :nodoc:
1374
+ # we can perfectly reconstruct from the string
1375
+ coder['string'] = to_s
1376
+ end
1377
+
1378
+ # For YAML deserialization
1379
+ def init_with(coder) # :nodoc:
1380
+ @tuples = []
1381
+ self.string = coder['string']
1382
+ end
1383
+
1235
1384
  protected
1236
1385
 
1237
1386
  attr_reader :tuples # :nodoc:
@@ -1251,30 +1400,30 @@ module Net
1251
1400
  super
1252
1401
  end
1253
1402
 
1254
- def input_to_tuple(obj)
1255
- obj = input_try_convert obj
1256
- case obj
1257
- when *STARS, Integer then [int = to_tuple_int(obj), int]
1258
- when Range then range_to_tuple(obj)
1259
- when String then str_to_tuple(obj)
1403
+ def input_to_tuple(entry)
1404
+ entry = input_try_convert entry
1405
+ case entry
1406
+ when *STARS, Integer then [int = to_tuple_int(entry), int]
1407
+ when Range then range_to_tuple(entry)
1408
+ when String then str_to_tuple(entry)
1260
1409
  else
1261
- raise DataFormatError, "expected number or range, got %p" % [obj]
1410
+ raise DataFormatError, "expected number or range, got %p" % [entry]
1262
1411
  end
1263
1412
  end
1264
1413
 
1265
- def input_to_tuples(obj)
1266
- obj = input_try_convert obj
1267
- case obj
1268
- when *STARS, Integer, Range then [input_to_tuple(obj)]
1269
- when String then str_to_tuples obj
1270
- when SequenceSet then obj.tuples
1271
- when Set then obj.map { [to_tuple_int(_1)] * 2 }
1272
- when Array then obj.flat_map { input_to_tuples _1 }
1414
+ def input_to_tuples(set)
1415
+ set = input_try_convert set
1416
+ case set
1417
+ when *STARS, Integer, Range then [input_to_tuple(set)]
1418
+ when String then str_to_tuples set
1419
+ when SequenceSet then set.tuples
1420
+ when Set then set.map { [to_tuple_int(_1)] * 2 }
1421
+ when Array then set.flat_map { input_to_tuples _1 }
1273
1422
  when nil then []
1274
1423
  else
1275
1424
  raise DataFormatError,
1276
1425
  "expected nz-number, range, string, or enumerable; " \
1277
- "got %p" % [obj]
1426
+ "got %p" % [set]
1278
1427
  end
1279
1428
  end
1280
1429
 
@@ -1337,8 +1486,8 @@ module Net
1337
1486
  modifying!
1338
1487
  min, max = tuple
1339
1488
  lower, lower_idx = tuple_gte_with_index(min - 1)
1340
- if lower.nil? then tuples << tuple
1341
- elsif (max + 1) < lower.first then tuples.insert(lower_idx, tuple)
1489
+ if lower.nil? then tuples << [min, max]
1490
+ elsif (max + 1) < lower.first then tuples.insert(lower_idx, [min, max])
1342
1491
  else tuple_coalesce(lower, lower_idx, min, max)
1343
1492
  end
1344
1493
  end