@@ -198,16 +198,21 @@ class IMAP
198198 # - #full?: Returns whether the set contains every possible value, including
199199 # <tt>*</tt>.
200200 #
201+ # <i>Denormalized properties:</i>
202+ # - #has_duplicates?: Returns whether the ordered entries repeat any
203+ # numbers.
204+ # - #count_duplicates: Returns the count of repeated numbers in the ordered
205+ # entries.
206+ # - #count_with_duplicates: Returns the count of numbers in the ordered
207+ # entries, including any repeated numbers.
208+ #
201209 # === Methods for Iterating
202210 #
211+ # <i>Normalized (sorted and coalesced):</i>
203212 # - #each_element: Yields each number and range in the set, sorted and
204213 # coalesced, and returns +self+.
205214 # - #elements (aliased as #to_a): Returns an Array of every number and range
206215 # in the set, sorted and coalesced.
207- # - #each_entry: Yields each number and range in the set, unsorted and
208- # without deduplicating numbers or coalescing ranges, and returns +self+.
209- # - #entries: Returns an Array of every number and range in the set,
210- # unsorted and without deduplicating numbers or coalescing ranges.
211216 # - #each_range:
212217 # Yields each element in the set as a Range and returns +self+.
213218 # - #ranges: Returns an Array of every element in the set, converting
@@ -217,6 +222,12 @@ class IMAP
217222 # ranges into all of their contained numbers.
218223 # - #to_set: Returns a Set containing all of the #numbers in the set.
219224 #
225+ # <i>Order preserving:</i>
226+ # - #each_entry: Yields each number and range in the set, unsorted and
227+ # without deduplicating numbers or coalescing ranges, and returns +self+.
228+ # - #entries: Returns an Array of every number and range in the set,
229+ # unsorted and without deduplicating numbers or coalescing ranges.
230+ #
220231 # === Methods for \Set Operations
221232 # These methods do not modify +self+.
222233 #
@@ -236,19 +247,29 @@ class IMAP
236247 # === Methods for Assigning
237248 # These methods add or replace elements in +self+.
238249 #
250+ # <i>Normalized (sorted and coalesced):</i>
251+ #
252+ # These methods always update #string to be fully sorted and coalesced.
253+ #
239254 # - #add (aliased as #<<): Adds a given object to the set; returns +self+.
240255 # - #add?: If the given object is not an element in the set, adds it and
241256 # returns +self+; otherwise, returns +nil+.
242257 # - #merge: Merges multiple elements into the set; returns +self+.
258+ # - #complement!: Replaces the contents of the set with its own #complement.
259+ #
260+ # <i>Order preserving:</i>
261+ #
262+ # These methods _may_ cause #string to not be sorted or coalesced.
263+ #
243264 # - #append: Adds a given object to the set, appending it to the existing
244265 # string, and returns +self+.
245266 # - #string=: Assigns a new #string value and replaces #elements to match.
246267 # - #replace: Replaces the contents of the set with the contents
247268 # of a given object.
248- # - #complement!: Replaces the contents of the set with its own #complement.
249269 #
250270 # === Methods for Deleting
251- # These methods remove elements from +self+.
271+ # These methods remove elements from +self+, and update #string to be fully
272+ # sorted and coalesced.
252273 #
253274 # - #clear: Removes all elements in the set; returns +self+.
254275 # - #delete: Removes a given object from the set; returns +self+.
@@ -910,9 +931,7 @@ def numbers; each_number.to_a end
910931 # Related: #entries, #each_element
911932 def each_entry ( &block ) # :yields: integer or range or :*
912933 return to_enum ( __method__ ) unless block_given?
913- return each_element ( &block ) unless @string
914- @string . split ( "," ) . each do yield tuple_to_entry str_to_tuple _1 end
915- self
934+ each_entry_tuple do yield tuple_to_entry _1 end
916935 end
917936
918937 # Yields each number or range (or <tt>:*</tt>) in #elements to the block
@@ -930,6 +949,16 @@ def each_element # :yields: integer or range or :*
930949
931950 private
932951
952+ def each_entry_tuple ( &block )
953+ return to_enum ( __method__ ) unless block_given?
954+ if @string
955+ @string . split ( "," ) do block . call str_to_tuple _1 end
956+ else
957+ @tuples . each ( &block )
958+ end
959+ self
960+ end
961+
933962 def tuple_to_entry ( ( min , max ) )
934963 if min == STAR_INT then :*
935964 elsif max == STAR_INT then min ..
@@ -988,12 +1017,49 @@ def to_set; Set.new(numbers) end
9881017 # If <tt>*</tt> and <tt>2**32 - 1</tt> (the maximum 32-bit unsigned
9891018 # integer value) are both in the set, they will only be counted once.
9901019 def count
991- @tuples . sum ( @tuples . count ) { _2 - _1 } +
992- ( include_star? && include? ( UINT32_MAX ) ? -1 : 0 )
1020+ count_numbers_in_tuples ( @tuples )
9931021 end
9941022
9951023 alias size count
9961024
1025+ # Returns the count of numbers in the ordered #entries, including any
1026+ # repeated numbers.
1027+ #
1028+ # When #string is normalized, this behaves the same as #count.
1029+ #
1030+ # Related: #entries, #count_duplicates, #has_duplicates?
1031+ def count_with_duplicates
1032+ return count unless @string
1033+ count_numbers_in_tuples ( each_entry_tuple )
1034+ end
1035+
1036+ # Returns the count of repeated numbers in the ordered #entries.
1037+ #
1038+ # When #string is normalized, this is zero.
1039+ #
1040+ # Related: #entries, #count_with_duplicates, #has_duplicates?
1041+ def count_duplicates
1042+ return 0 unless @string
1043+ count_with_duplicates - count
1044+ end
1045+
1046+ # :call-seq: has_duplicates? -> true | false
1047+ #
1048+ # Returns whether or not the ordered #entries repeat any numbers.
1049+ #
1050+ # Always returns +false+ when #string is normalized.
1051+ #
1052+ # Related: #entries, #count_with_duplicates, #count_duplicates?
1053+ def has_duplicates?
1054+ return false unless @string
1055+ count_with_duplicates != count
1056+ end
1057+
1058+ private def count_numbers_in_tuples ( tuples )
1059+ tuples . sum ( tuples . count ) { _2 - _1 } +
1060+ ( include_star? && include? ( UINT32_MAX ) ? -1 : 0 )
1061+ end
1062+
9971063 # Returns the index of +number+ in the set, or +nil+ if +number+ isn't in
9981064 # the set.
9991065 #
0 commit comments