@@ -183,7 +183,7 @@ class IMAP
183183 #
184184 # When a set includes <tt>*</tt>, some methods may have surprising behavior.
185185 #
186- # For example, #complement treats <tt>*</tt> as its own number . This way,
186+ # For example, #complement treats <tt>*</tt> as its own member . This way,
187187 # the #intersection of a set and its #complement will always be empty. And
188188 # <tt>*</tt> is sorted as greater than any other number in the set. This is
189189 # not how an \IMAP server interprets the set: it will convert <tt>*</tt> to
@@ -203,7 +203,7 @@ class IMAP
203203 # (set.limit(max: 4) & (~set).limit(max: 4)).to_a => [4]
204204 #
205205 # When counting the number of numbers in a set, <tt>*</tt> will be counted
206- # _except_ when UINT32_MAX is also in the set :
206+ # as if it were equal to UINT32_MAX :
207207 # UINT32_MAX = 2**32 - 1
208208 # Net::IMAP::SequenceSet["*"].count => 1
209209 # Net::IMAP::SequenceSet[1..UINT32_MAX - 1, :*].count => UINT32_MAX
@@ -212,6 +212,12 @@ class IMAP
212212 # Net::IMAP::SequenceSet[UINT32_MAX, :*].count => 1
213213 # Net::IMAP::SequenceSet[UINT32_MAX..].count => 1
214214 #
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+ #
215221 # == What's here?
216222 #
217223 # SequenceSet provides methods for:
@@ -275,6 +281,8 @@ class IMAP
275281 # occurrence in entries.
276282 #
277283 # <i>Set cardinality:</i>
284+ # - #cardinality: Returns the number of distinct members in the set.
285+ # <tt>*</tt> is counted as its own member, distinct from UINT32_MAX.
278286 # - #count (aliased as #size): Returns the count of numbers in the set.
279287 # Duplicated numbers are not counted.
280288 # - #empty?: Returns whether the set has no members. \IMAP syntax does not
@@ -1336,28 +1344,71 @@ def each_ordered_number(&block)
13361344 # Related: #elements, #ranges, #numbers
13371345 def to_set ; Set . new ( numbers ) end
13381346
1347+ # Returns the number of members in the set.
1348+ #
1349+ # Unlike #count, <tt>"*"</tt> is considered to be distinct from
1350+ # <tt>2³² - 1</tt> (the maximum 32-bit unsigned integer value).
1351+ #
1352+ # set = Net::IMAP::SequenceSet[1..10]
1353+ # set.count #=> 10
1354+ # set.cardinality #=> 10
1355+ #
1356+ # set = Net::IMAP::SequenceSet["4294967295,*"]
1357+ # set.count #=> 1
1358+ # set.cardinality #=> 2
1359+ #
1360+ # set = Net::IMAP::SequenceSet[1..]
1361+ # set.count #=> 4294967295
1362+ # set.cardinality #=> 4294967296
1363+ #
1364+ # Related: #count, #count_with_duplicates
1365+ def cardinality = minmaxes . sum ( @set_data . count ) { _2 - _1 }
1366+
13391367 # Returns the count of #numbers in the set.
13401368 #
1341- # <tt>*</tt> will be counted as <tt>2**32 - 1</tt> (the maximum 32-bit
1342- # unsigned integer value).
1369+ # Unlike #cardinality, <tt>"*"</tt> is considered to be equal to
1370+ # <tt>2³² - 1</tt> (the maximum 32-bit unsigned integer value).
1371+ #
1372+ # set = Net::IMAP::SequenceSet[1..10]
1373+ # set.count #=> 10
1374+ # set.cardinality #=> 10
1375+ #
1376+ # set = Net::IMAP::SequenceSet["4294967295,*"]
1377+ # set.count #=> 1
1378+ # set.cardinality #=> 2
13431379 #
1344- # Related: #count_with_duplicates
1380+ # set = Net::IMAP::SequenceSet[1..]
1381+ # set.count #=> 4294967295
1382+ # set.cardinality #=> 4294967296
1383+ #
1384+ # Related: #cardinality, #count_with_duplicates
13451385 def count
1346- minmaxes . sum ( minmaxes . count ) { _2 - _1 } +
1347- ( include_star? && include? ( UINT32_MAX ) ? -1 : 0 )
1386+ cardinality + ( include_star? && include? ( UINT32_MAX ) ? -1 : 0 )
13481387 end
13491388
13501389 alias size count
13511390
13521391 # Returns the count of numbers in the ordered #entries, including any
13531392 # repeated numbers.
13541393 #
1355- # <tt>*</tt> will be counted as <tt>2**32 - 1</tt> (the maximum 32-bit
1356- # unsigned integer value).
1394+ # When #string is normalized, this returns the same as #count.
1395+ # Like #count, <tt>"*"</tt> is be considered to be equal to
1396+ # <tt>2³² - 1</tt> (the maximum 32-bit unsigned integer value).
1397+ #
1398+ # In a range, <tt>"*"</tt> is _not_ considered a duplicate:
1399+ # set = Net::IMAP::SequenceSet["4294967295:*"]
1400+ # set.count_with_duplicates #=> 1
1401+ # set.count #=> 1
1402+ # set.cardinality #=> 2
13571403 #
1358- # When #string is normalized, this behaves the same as #count.
1404+ # In a separate entry, <tt>"*"</tt> _is_ considered a duplicate:
1405+ # set = Net::IMAP::SequenceSet["4294967295,*"]
1406+ # set.count_with_duplicates #=> 2
1407+ # set.count #=> 1
1408+ # set.cardinality #=> 2
13591409 #
1360- # Related: #entries, #count_duplicates, #has_duplicates?
1410+ # Related: #count, #cardinality, #count_duplicates, #has_duplicates?,
1411+ # #entries
13611412 def count_with_duplicates
13621413 return count unless @string
13631414 each_entry_minmax . sum { |min , max |
@@ -1382,7 +1433,7 @@ def count_duplicates
13821433 #
13831434 # Always returns +false+ when #string is normalized.
13841435 #
1385- # Related: #entries, #count_with_duplicates, #count_duplicates?
1436+ # Related: #entries, #count_with_duplicates, #count_duplicates
13861437 def has_duplicates?
13871438 return false unless @string
13881439 count_with_duplicates != count
0 commit comments