123456789_123456789_123456789_123456789_123456789_

Class: Net::IMAP::SequenceSet

Relationships & Source Files
Inherits: Object
Defined in: lib/net/imap/sequence_set.rb

Overview

An IMAP sequence set is a set of message sequence numbers or unique identifier numbers ("UIDs"). It contains numbers and ranges of numbers. The numbers are all non-zero unsigned 32-bit integers and one special value ("*") that represents the largest value in the mailbox.

Certain types of IMAP responses will contain a SequenceSet, for example the data for a "MODIFIED" ResponseCode. Some IMAP commands may receive a SequenceSet as an argument, for example #search, #fetch, and #store.

Creating sequence sets

.new may receive a single optional argument: a non-zero 32 bit unsigned integer, a range, a sequence-set formatted string, another SequenceSet, a Set (containing only numbers or *), or an Array containing any of these (array inputs may be nested).

set = Net::IMAP::SequenceSet.new(1)
set.valid_string  #=> "1"
set = Net::IMAP::SequenceSet.new(1..100)
set.valid_string  #=> "1:100"
set = Net::IMAP::SequenceSet.new(1...100)
set.valid_string  #=> "1:99"
set = Net::IMAP::SequenceSet.new([1, 2, 5..])
set.valid_string  #=> "1:2,5:*"
set = Net::IMAP::SequenceSet.new("1,2,3:7,5,6:10,2048,1024")
set.valid_string  #=> "1,2,3:7,5,6:10,2048,1024"
set = Net::IMAP::SequenceSet.new(1, 2, 3..7, 5, 6..10, 2048, 1024)
set.valid_string  #=> "1:10,55,1024:2048"

.new with no arguments creates an empty sequence set. Note that an empty sequence set is invalid in the IMAP grammar.

set = Net::IMAP::SequenceSet.new
set.empty?        #=> true
set.valid?        #=> false
set.valid_string  #!> raises DataFormatError
set << 1..10
set.empty?        #=> false
set.valid?        #=> true
set.valid_string  #=> "1:10"

Using .new with another SequenceSet input behaves the same as calling #dup on the other set. The input's #string will be preserved.

input = Net::IMAP::SequenceSet.new("1,2,3:7,5,6:10,2048,1024")
copy  = Net::IMAP::SequenceSet.new(input)
input.valid_string  #=> "1,2,3:7,5,6:10,2048,1024"
copy.valid_string   #=> "1,2,3:7,5,6:10,2048,1024"
copy2 = input.dup   # same as calling new with a SequenceSet input
copy ==     input   #=> true,  same set membership
copy.eql?   input   #=> true,  same string value
copy.equal? input   #=> false, different objects

copy.normalize!
copy.valid_string   #=> "1:10,1024,2048"
copy ==   input     #=> true,  same set membership
copy.eql? input     #=> false, different string value

copy << 999
copy.valid_string   #=> "1:10,999,1024,2048"
copy ==   input     #=> false, different set membership
copy.eql? input     #=> false, different string value

Use SequenceSet() to coerce a single (optional) input. A SequenceSet input is returned without duplication, even when frozen.

set = Net::IMAP::SequenceSet()
set.string   #=> nil
set.frozen?  #=> false

# String order is preserved
set = Net::IMAP::SequenceSet("1,2,3:7,5,6:10,2048,1024")
set.valid_string  #=> "1,2,3:7,5,6:10,2048,1024"
set.frozen?       #=> false

# Other inputs are normalized
set = Net::IMAP::SequenceSet([1, 2, [3..7, 5], 6..10, 2048, 1024])
set.valid_string  #=> "1:10,1024,2048"
set.frozen?       #=> false

unfrozen = set
frozen   = set.dup.freeze
unfrozen.equal? Net::IMAP::SequenceSet(unfrozen)  #=> true
frozen.equal?   Net::IMAP::SequenceSet(frozen)    #=> true

Use .[] to coerce one or more arguments into a valid frozen SequenceSet. A valid frozen SequenceSet is returned directly, without allocating a new object. .[] will not create an invalid (empty) set.

Net::IMAP::SequenceSet[]     #!> raises ArgumentError
Net::IMAP::SequenceSet[nil]  #!> raises DataFormatError
Net::IMAP::SequenceSet[""]   #!> raises DataFormatError

# String order is preserved
set = Net::IMAP::SequenceSet["1,2,3:7,5,6:10,2048,1024"]
set.valid_string  #=> "1,2,3:7,5,6:10,2048,1024"
set.frozen?       #=> true

# Other inputs are normalized
set = Net::IMAP::SequenceSet[1, 2, [3..7, 5], 6..10, 2048, 1024]
set.valid_string  #=> "1:10,1024,2048"
set.frozen?       #=> true

frozen   = set
unfrozen = set.dup
frozen.equal?   Net::IMAP::SequenceSet[frozen]    #=> true
unfrozen.equal? Net::IMAP::SequenceSet[unfrozen]  #=> false

Objects which respond to #to_sequence_set (such as SearchResult and ThreadMember) can be coerced to a SequenceSet with .new, .try_convert, .[], or SequenceSet.

search = imap.uid_search(["SUBJECT", "hello", "NOT", "SEEN"])
seqset = Net::IMAP::SequenceSet(search) - already_fetched
fetch  = imap.uid_fetch(seqset, "FAST")

Ordered and Normalized sets

Sometimes the order of the set's members is significant, such as with the ESORT, CONTEXT=SORT, and UIDPLUS extensions. So, when a sequence set is created from a single string (such as by the parser), that #string representation is preserved. Assigning a string with #string= or #replace will also preserve that string. Use #each_entry, #entries, or #each_ordered_number to enumerate the entries in their #string order. Hash equality (using #eql?) is based on the string representation.

Internally, SequenceSet uses a normalized uint32 set representation which sorts and de-duplicates all numbers and coalesces adjacent or overlapping entries. Many methods use this sorted set representation for O(lg n) searches. Use #each_element, #elements, #each_range, #ranges, #each_number, or #numbers to enumerate the set in sorted order. Basic object equality (using #==) is based on set membership, without regard to #entry order or #string normalization.

Most modification methods reset #string to its #normalized form, so that #entries and #elements are identical. Use #append to preserve #entries order while modifying a set.

Non-normalized sets store both representations of the set, which can more than double memory usage. Very large sequence sets should avoid denormalizing methods (such as #append) unless order is significant.

Using *

IMAP sequence sets may contain a special value "*", which represents the largest number in use. From seq-number in RFC9051 §9:

In the case of message sequence numbers, it is the number of messages

in a non-empty mailbox. In the case of unique identifiers, it is the unique identifier of the last message in the mailbox or, if the mailbox is empty, the mailbox's current UIDNEXT value.

When creating a SequenceSet, may be input as -1, "", :*, an endless range, or a range ending in -1. When converting to #elements, #ranges, or #numbers, it will output as either :* or an endless range. For example:

Net::IMAP::SequenceSet["1,3,*"].to_a      #=> [1, 3, :*]
Net::IMAP::SequenceSet["1,234:*"].to_a    #=> [1, 234..]
Net::IMAP::SequenceSet[1234..-1].to_a     #=> [1234..]
Net::IMAP::SequenceSet[1234..].to_a       #=> [1234..]

Net::IMAP::SequenceSet[1234..].to_s       #=> "1234:*"
Net::IMAP::SequenceSet[1234..-1].to_s     #=> "1234:*"

Use #limit to convert "" to a maximum value. When a range includes "", the maximum value will always be matched:

Net::IMAP::SequenceSet["9999:*"].limit(max: 25)
#=> Net::IMAP::SequenceSet["25"]

Surprising * behavior

When a set includes *, some methods may have surprising behavior.

For example, #complement treats as its own member. This way, the #intersection of a set and its #complement will always be empty. And is sorted as greater than any other number in the set. This is not how an IMAP server interprets the set: it will convert to the number of messages in the mailbox, the UID of the last message in the mailbox, or UIDNEXT, as appropriate. Several methods have an argument for how should be interpreted.

But, for example, this means that there may be overlap between a set and its complement after #limit is applied to each:

~Net::IMAP::SequenceSet["*"]  == Net::IMAP::SequenceSet[1..(2**32-1)]
~Net::IMAP::SequenceSet[1..5] == Net::IMAP::SequenceSet["6:*"]

set = Net::IMAP::SequenceSet[1..5]
(set & ~set).empty? => true

(set.limit(max: 4) & (~set).limit(max: 4)).to_a => [4]

When counting the number of numbers in a set, * will be counted as if it were equal to UINT32_MAX:

UINT32_MAX = 2**32 - 1
Net::IMAP::SequenceSet["*"].count                   => 1
Net::IMAP::SequenceSet[1..UINT32_MAX - 1, :*].count => UINT32_MAX

Net::IMAP::SequenceSet["1:*"].count                 => UINT32_MAX
Net::IMAP::SequenceSet[UINT32_MAX, :*].count        => 1
Net::IMAP::SequenceSet[UINT32_MAX..].count          => 1

Use #cardinality to count the set members wxth * counted as a distinct member:

Net::IMAP::SequenceSet[1..].cardinality            #=> UINT32_MAX + 1
Net::IMAP::SequenceSet[UINT32_MAX, :*].cardinality #=> 2
Net::IMAP::SequenceSet[UINT32_MAX..].cardinality   #=> 2

What's here?

SequenceSet provides methods for:

Methods for Creating a SequenceSet

  • .[]: Creates a validated frozen sequence set from one or more inputs.
  • .new: Creates a new mutable sequence set, which may be empty (invalid).
  • .try_convert: Calls #to_sequence_set on an object and verifies that the result is a SequenceSet.
  • SequenceSet(): Coerce an input using .try_convert or .new.
  • .empty: Returns a frozen empty (invalid) SequenceSet.
  • .full: Returns a frozen SequenceSet containing every possible number.

Methods for Comparing

Comparison to another SequenceSet:

  • #==: Returns whether a given set contains the same numbers as self.
  • #eql?: Returns whether a given set uses the same #string as self.

Comparison to objects which are convertible to SequenceSet:

  • #===: Returns whether a given object is fully contained within self, or nil if the object cannot be converted to a compatible type.
  • #cover?: Returns whether a given object is fully contained within self.
  • #intersect? (aliased as #overlap?): Returns whether self and a given object have any common elements.
  • #disjoint?: Returns whether self and a given object have no common elements.

Comparison to a number:

  • #all_above?: Returns whether every number in self is greater than a given number.
  • #all_below?: Returns whether every number in self is less than a given number.
  • #any_above?: Returns whether self contains any numbers greater than a given number.
  • #any_below?: Returns whether self contains any numbers less than a given number.
  • #none_above?: Returns whether self contains no numbers greater than a given number.
  • #none_below?: Returns whether self contains no numbers less than a given number.

Methods for Querying

These methods do not modify self.

Set membership:

Minimum and maximum value elements:

  • #min: Returns one or more of the lowest numbers in the set.
  • #max: Returns one or more of the highest numbers in the set.
  • #minmax: Returns the lowest and highest numbers in the set.

Accessing value by offset in sorted set:

  • #[] (aliased as #slice): Returns the number or consecutive subset at a given offset or range of offsets in the sorted set.
  • #at: Returns the number at a given offset in the sorted set.
  • #find_index: Returns the given number's offset in the sorted set.

Accessing value by offset in ordered entries

  • #ordered_at: Returns the number at a given offset in the ordered entries.
  • #find_ordered_index: Returns the index of the given number's first occurrence in entries.

Set cardinality:

  • #cardinality: Returns the number of distinct members in the set. * is counted as its own member, distinct from UINT32_MAX.
  • #count: Returns the count of distinct numbers in the set. * is counted as equal to UINT32_MAX.
  • #empty?: Returns whether the set has no members. IMAP syntax does not allow empty sequence sets.
  • #valid?: Returns whether the set has any members.
  • #full?: Returns whether the set contains every possible value, including *.

Denormalized properties:

  • #normalized?: Returns whether #entries are sorted, deduplicated, and coalesced, and all #string entries are in normalized form.
  • #has_duplicates?: Returns whether the ordered entries repeat any numbers.
  • #size: Returns the total size of all #entries, including repeated numbers. * is counted as its own member, distinct from UINT32_MAX.
  • #count_with_duplicates: Returns the count of numbers in the ordered #entries, including repeated numbers. * is counted as equal to UINT32_MAX.
  • #count_duplicates: Returns the count of repeated numbers in the ordered #entries. * is counted as equal to UINT32_MAX.

Methods for Iterating

Normalized (sorted and coalesced):

  • #each_element: Yields each number and range in the set, sorted and coalesced, and returns self.
  • #elements (aliased as #to_a): Returns an Array of every number and range in the set, sorted and coalesced.
  • #each_range: Yields each element in the set as a Range and returns self.
  • #ranges: Returns an Array of every element in the set, converting numbers into ranges of a single value.
  • #each_number: Yields each number in the set and returns self.
  • #numbers: Returns an Array with every number in the set, expanding ranges into all of their contained numbers.
  • #to_set: Returns a Set containing all of the #numbers in the set.

Order preserving:

  • #each_entry: Yields each number and range in the set, unsorted and without deduplicating numbers or coalescing ranges, and returns self.
  • #entries: Returns an Array of every number and range in the set, unsorted and without deduplicating numbers or coalescing ranges.
  • #each_ordered_number: Yields each number in the ordered entries and returns self.

Methods for Set Operations

These methods do not modify self.

  • #| (aliased as #union and #+): Returns a new set combining all members from self with all members from the other set.
  • #& (aliased as #intersection): Returns a new set containing all members common to self and the other set.
  • #- (aliased as #difference): Returns a copy of self with all members in the other set removed.
  • #^ (aliased as #xor): Returns a new set containing all members from self and the other set except those common to both.
  • #~ (aliased as #complement): Returns a new set containing all members that are not in self
  • #above: Return a copy of self which only contains numbers above a given number.
  • #below: Return a copy of self which only contains numbers below a given number.
  • #limit: Returns a copy of self which has replaced * with a given maximum value and removed all members over that maximum.

Methods for Assigning

These methods add or replace numbers in self.

Normalized (sorted and coalesced):

These methods always update #string to be fully sorted and coalesced.

  • #add (aliased as #<<): Adds a given element to the set; returns self.
  • #add?: If the given element is not fully included the set, adds it and returns self; otherwise, returns nil.
  • #merge: In-place set #union. Adds all members of the given sets into this set; returns self.
  • #complement!: In-place set #complement. Replaces the contents of this set with its own #complement; returns self.
  • #xor!: In-place XOR operation. Adds numbers that are unique to the other set and removes numbers that are common to both; returns self.

Order preserving:

These methods may cause #string to not be sorted or coalesced.

  • #append: Adds the given entry to the set, appending it to the existing string, and returns self.
  • #string=: Assigns a new #string value and replaces #elements to match.
  • #replace: Replaces the contents of the set with the contents of a given object.

Methods for Deleting

These methods remove numbers from self, and update #string to be fully sorted and coalesced.

  • #clear: Removes all elements in the set; returns self.
  • #delete: Removes a given element from the set; returns self.
  • #delete?: If the given element is included in the set, removes it and returns it; otherwise, returns nil.
  • #delete_at: Removes the number at a given offset.
  • #intersect!: In-place set #intersection. Removes numbers that are not in the given set; returns self.
  • #slice!: Removes the number or consecutive numbers at a given offset or range of offsets.
  • #subtract: In-place set #difference. Removes all members of the given sets from this set; returns self.
  • #limit!: Replaces * with a given maximum value and removes all members over that maximum; returns self.

Methods for IMAP String Formatting

  • #to_s: Returns the sequence-set string, or an empty string when the set is empty.
  • #string: Returns the sequence-set string, or nil when empty.
  • #valid_string: Returns the sequence-set string, or raises DataFormatError when the set is empty.
  • #normalized_string: Returns a sequence-set string with its elements sorted and coalesced, or nil when the set is empty.
  • #normalize: Returns a new set with this set's normalized sequence-set representation.
  • #normalize!: Updates #string to its normalized sequence-set representation and returns self.

Constant Summary

Class Method Summary

Instance Attribute Summary

Instance Method Summary

Constructor Details

.new(input = nil) ⇒ SequenceSet

Create a new SequenceSet object from input, which may be another SequenceSet, an ::Net::IMAP formatted sequence-set string, a non-zero 32 bit unsigned integer, a range, :, a Set of numbers or , an object that responds to #to_sequence_set (such as SearchResult) or an Array of these (array inputs may be nested).

set = Net::IMAP::SequenceSet.new(1)
set.valid_string  #=> "1"
set = Net::IMAP::SequenceSet.new(1..100)
set.valid_string  #=> "1:100"
set = Net::IMAP::SequenceSet.new(1...100)
set.valid_string  #=> "1:99"
set = Net::IMAP::SequenceSet.new([1, 2, 5..])
set.valid_string  #=> "1:2,5:*"
set = Net::IMAP::SequenceSet.new("1,2,3:7,5,6:10,2048,1024")
set.valid_string  #=> "1,2,3:7,5,6:10,2048,1024"
set = Net::IMAP::SequenceSet.new(1, 2, 3..7, 5, 6..10, 2048, 1024)
set.valid_string  #=> "1:10,1024,2048"

With no arguments (or nil) creates an empty sequence set. Note that an empty sequence set is invalid in the IMAP grammar.

set = Net::IMAP::SequenceSet.new
set.empty?        #=> true
set.valid?        #=> false
set.valid_string  #!> raises DataFormatError
set << 1..10
set.empty?        #=> false
set.valid?        #=> true
set.valid_string  #=> "1:10"

When input is a SequenceSet, .new behaves the same as calling #dup on that other set. The input's #string will be preserved.

input = Net::IMAP::SequenceSet.new("1,2,3:7,5,6:10,2048,1024")
copy  = Net::IMAP::SequenceSet.new(input)
input.valid_string  #=> "1,2,3:7,5,6:10,2048,1024"
copy.valid_string   #=> "1,2,3:7,5,6:10,2048,1024"
copy2 = input.dup   # same as calling new with a SequenceSet input
copy ==     input   #=> true,  same set membership
copy.eql?   input   #=> true,  same string value
copy.equal? input   #=> false, different objects

copy.normalize!
copy.valid_string   #=> "1:10,1024,2048"
copy ==   input     #=> true,  same set membership
copy.eql? input     #=> false, different string value

copy << 999
copy.valid_string   #=> "1:10,999,1024,2048"
copy ==   input     #=> false, different set membership
copy.eql? input     #=> false, different string value

Alternative set creation methods

  • .[] returns a frozen validated (non-empty) SequenceSet, without allocating a new object when the input is already a valid frozen SequenceSet.
  • SequenceSet() coerces an input to SequenceSet, without allocating a new object when the input is already a SequenceSet.
  • .try_convert calls #to_sequence_set on inputs that support it and returns nil for inputs that don't.
  • .empty and .full both return frozen singleton sets which can be combined with set operations (#|, #&, #^, #-, etc) to make new sets.

See SequenceSet@Creating+sequence+sets.

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 572

def initialize(input = nil)
  @set_data = new_set_data
  @string = nil
  replace(input) unless input.nil?
end

Class Method Details

.[](*inputs) ⇒ valid frozen sequence set

Returns a frozen SequenceSet, constructed from inputs.

When only a single valid frozen SequenceSet is given, that same set is returned.

An empty SequenceSet is invalid and will raise a DataFormatError.

Use .new to create a mutable or empty SequenceSet.

Related: .new, SequenceSet(), .try_convert

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 469

def [](first, *rest)
  if rest.empty?
    set = try_convert(first)&.validate
    set&.frozen? ? set : (set&.dup || new(first).validate).freeze
  else
    new(first).merge(*rest).validate.freeze
  end
end

.empty

Returns a frozen empty set singleton. Note that valid IMAP sequence sets cannot be empty, so this set is invalid.

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 499

def empty; EMPTY end

.full

Returns a frozen full set singleton: "1:*"

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 502

def full;  FULL end

.try_convert(obj) ⇒ sequence set?

If obj is a SequenceSet, returns obj. If obj responds_to #to_sequence_set, calls obj.to_sequence_set and returns the result. Otherwise returns nil.

If obj.to_sequence_set doesn't return a SequenceSet or nil, an exception is raised.

Related: SequenceSet(), .new, .[]

Raises:

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 489

def try_convert(obj)
  return obj if obj.is_a?(SequenceSet)
  return nil unless obj.respond_to?(:to_sequence_set)
  return nil unless obj = obj.to_sequence_set
  return obj if obj.is_a?(SequenceSet)
  raise DataFormatError, "invalid object returned from to_sequence_set"
end

Instance Attribute Details

#empty? ⇒ Boolean (readonly)

Returns true if the set contains no elements

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1072

def empty?; runs.empty? end

#full? ⇒ Boolean (readonly)

Returns true if the set contains every possible element.

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1075

def full?; set_data == FULL_SET_DATA end

#has_duplicates? ⇒ Boolean (readonly)

Returns whether or not the ordered #entries repeat any numbers.

Always returns false when #string is normalized.

Related: #entries, #count_with_duplicates, #count_duplicates

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1681

def has_duplicates?
  return false unless @string
  count_with_duplicates != count
end

#include_star? ⇒ Boolean (readonly)

Returns true when the set contains *.

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 766

def include_star?; max_num == STAR_INT end

#minmaxes (readonly, protected)

This method is for internal use only.

Alias for #runs.

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2106

alias minmaxes runs

#normalized? ⇒ Boolean (readonly)

Returns whether #string is fully normalized: entries have been sorted, deduplicated, and coalesced, and all entries are in normal form. See SequenceSet@Ordered+and+Normalized+sets.

Net::IMAP::SequenceSet["1,3,5"].normalized?  #=> true
Net::IMAP::SequenceSet["20:30"].normalized?  #=> true

Net::IMAP::SequenceSet["3,5,1"].normalized?  #=> false, not sorted
Net::IMAP::SequenceSet["1,2,3"].normalized?  #=> false, not coalesced
Net::IMAP::SequenceSet["1:5,2"].normalized?  #=> false, repeated number

Net::IMAP::SequenceSet["1:1"].normalized?    #=> false, number as range
Net::IMAP::SequenceSet["5:1"].normalized?    #=> false, backwards range

Returns true if (and only if) #string is equal to #normalized_string:

seqset = Net::IMAP::SequenceSet["1:3,5"]
seqset.string             #=> "1:3,5"
seqset.normalized_string  #=> "1:3,5"
seqset.entries            #=> [1..3, 5]
seqset.elements           #=> [1..3, 5]
seqset.normalized?        #=> true

seqset = Net::IMAP::SequenceSet["3,1,2"]
seqset.string             #=> "3,1,2"
seqset.normalized_string  #=> "1:3"
seqset.entries            #=> [3, 1, 2]
seqset.elements           #=> [1..3]
seqset.normalized?        #=> false

Can return false even when #entries and #elements are the same:

seqset = Net::IMAP::SequenceSet["5:1"]
seqset.string             #=> "5:1"
seqset.normalized_string  #=> "1:5"
seqset.entries            #=> [1..5]
seqset.elements           #=> [1..5]
seqset.normalized?        #=> false

Note that empty sets are normalized, even though they are not #valid?:

seqset = Net::IMAP::SequenceSet.empty
seqset.normalized?        #=> true
seqset.valid?             #=> false

Related: #normalize, #normalize!, #normalized_string

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1978

def normalized?
  @string.nil? || normal_string?(@string)
end

#runs (readonly, protected) Also known as: #minmaxes

This method is for internal use only.

Alias for #set_data.

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2105

alias runs set_data

#set_data (readonly, protected) Also known as: #runs

This method is for internal use only.
[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2103

attr_reader :set_data

#string (rw)

Returns the IMAP sequence-set string representation, or nil when the set is empty. Note that an empty set is invalid in the IMAP syntax.

Use #valid_string to raise an exception when the set is empty, or #to_s to return an empty string.

If the set was created from a single string, it is not normalized. If the set is updated the string will be normalized.

Related: #valid_string, #normalized_string, #to_s, #inspect

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 626

def string; @string || normalized_string if valid? end

#string=(input) (rw)

Assigns a new string to #string and resets #elements to match. Assigning nil or an empty string are equivalent to calling #clear.

Non-empty strings are validated but not normalized.

Use #add, #merge, or #append to add a string to an existing set.

Related: #replace, #clear

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 640

def string=(input)
  if input.nil?
    clear
  elsif (str = String.try_convert(input))
    modifying! # short-circuit before parsing the string
    entries = each_parsed_entry(str).to_a
    clear
    if normalized_entries?(entries)
      replace_minmaxes entries.map!(&:minmax)
    else
      add_minmaxes entries.map!(&:minmax)
      @string = -str
    end
  else
    raise ArgumentError, "expected a string or nil, got #{input.class}"
  end
  input
end

#valid? ⇒ Boolean (readonly)

Returns false when the set is empty.

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1069

def valid?; !empty? end

Instance Method Details

#&(other) ⇒ sequence set #intersection(other) ⇒ sequence set
Also known as: #intersection

Returns a new sequence set containing only the numbers common to this set and other.

other may be any object that would be accepted by .new.

Net::IMAP::SequenceSet[1..5] & [2, 4, 6]
#=> Net::IMAP::SequenceSet["2,4"]

Related: #intersect?, #|, #-, #^, #~

Set identities

lhs & rhs is equivalent to:

  • rhs & lhs (commutative)
  • ~(~lhs | ~rhs) (De Morgan's Law)
  • lhs - ~rhs
  • lhs - (lhs - rhs)
  • lhs - (lhs ^ rhs)
  • lhs ^ (lhs - rhs)
[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1151

def &(other) remain_frozen dup.intersect! other end

#+(other)

Alias for #|.

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1099

alias :+    :|

#-(other) ⇒ sequence set #difference(other) ⇒ sequence set
Also known as: #difference

Returns a new sequence set built by duplicating this set and removing every number that appears in other.

other may be any object that would be accepted by .new.

Net::IMAP::SequenceSet[1..5] - 2 - 4 - 6
#=> Net::IMAP::SequenceSet["1,3,5"]

Related: #subtract, #|, #&, #^, #~

Set identities

lhs - rhs is equivalent to:

  • ~rhs - ~lhs
  • lhs & ~rhs
  • ~(~lhs | rhs)
  • lhs & (lhs ^ rhs)
  • lhs ^ (lhs & rhs)
  • rhs ^ (lhs | rhs)
[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1125

def -(other) remain_frozen dup.subtract other end

#<<(element)

Alias for #add.

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1219

alias << add

#==(other) ⇒ Boolean

Returns true when the other SequenceSet represents the same message identifiers. Encoding difference—such as order, overlaps, or duplicates—are ignored.

Net::IMAP::SequenceSet["1:3"]   == Net::IMAP::SequenceSet["1:3"]
#=> true
Net::IMAP::SequenceSet["1,2,3"] == Net::IMAP::SequenceSet["1:3"]
#=> true
Net::IMAP::SequenceSet["1,3"]   == Net::IMAP::SequenceSet["3,1"]
#=> true
Net::IMAP::SequenceSet["9,1:*"] == Net::IMAP::SequenceSet["1:*"]
#=> true

Related: #eql?, #normalize

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 689

def ==(other)
  self.class == other.class &&
    (to_s == other.to_s || set_data == other.set_data)
end

#===(other) ⇒ Boolean

Returns whether other is contained within the set. other may be any object that would be accepted by .new. Returns nil if StandardError is raised while converting other to a comparable type.

Related: #cover?, #include?, #include_star?

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 720

def ===(other)
  cover?(other)
rescue
  nil
end

#[](index) ⇒ Integer, ... #slice(index) ⇒ Integer, ... #[](start, length) ⇒ sequence set? #slice(start, length) ⇒ sequence set? #[](range) ⇒ sequence set? #slice(range) ⇒ sequence set?
Also known as: #slice

Returns a number or a subset from the sorted set, without modifying the set.

When an Integer argument index is given, the number at offset index in the sorted set is returned:

set = Net::IMAP::SequenceSet["10:15,20:23,26"]
set[0]   #=> 10
set[5]   #=> 15
set[10]  #=> 26

If index is negative, it counts relative to the end of the sorted set: set = Net::IMAP::SequenceSet set #=> 26 set #=> 22 set #=> 15

If index is out of range, nil is returned.

set = Net::IMAP::SequenceSet["10:15,20:23,26"]
set[11]  #=> nil
set[-12] #=> nil

The result is based on the sorted and de-duplicated set, not on the ordered #entries in #string.

set = Net::IMAP::SequenceSet["12,20:23,11:16,21"]
set[0]   #=> 11
set[-1]  #=> 23

Related: #at

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1778

def [](index, length = nil)
  if    length              then slice_length(index, length)
  elsif index.is_a?(Range)  then slice_range(index)
  else                           at(index)
  end
end

#^(other) ⇒ sequence set #xor(other) ⇒ sequence set
Also known as: #xor

Returns a new sequence set containing numbers that are exclusive between this set and other.

other may be any object that would be accepted by .new.

Net::IMAP::SequenceSet[1..5] ^ [2, 4, 6]
#=> Net::IMAP::SequenceSet["1,3,5:6"]

Related: #|, #&, #-, #~

Set identities

lhs ^ rhs is equivalent to:

  • rhs ^ lhs (commutative)
  • ~lhs ^ ~rhs
  • (lhs | rhs) - (lhs & rhs)
  • (lhs - rhs) | (rhs - lhs)
  • (lhs ^ other) ^ (other ^ rhs)
[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1176

def ^(other) remain_frozen dup.xor! other end

#above(num)

Returns a copy of self which only contains the numbers above num.

Net::IMAP::SequenceSet["5,10:22,50"].above(10) # to_s => "11:22,50"
Net::IMAP::SequenceSet["5,10:22,50"].above(20) # to_s => "21:22,50
Net::IMAP::SequenceSet["5,10:22,50"].above(30) # to_s => "50"

This returns the same result as #intersection with ((num+1)..) or #difference with (..num).

Net::IMAP::SequenceSet["5,10:22,50"] & (11..)   # to_s => "11:22,50"
Net::IMAP::SequenceSet["5,10:22,50"] - (..10)   # to_s => "11:22,50"
Net::IMAP::SequenceSet["5,10:22,50"] & (21..)   # to_s => "21:22,50"
Net::IMAP::SequenceSet["5,10:22,50"] - (..20)   # to_s => "21:22,50"

Related: #below, other set operations, #all_above?, #any_above?, #none_above?

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1804

def above(num)
  NumValidator.valid_nz_number?(num) or
    raise ArgumentError, "not a valid sequence set number"
  difference(..num)
end

#add(element) ⇒ self #<<(other) ⇒ self
Also known as: #<<

Adds a range or number to the set and returns self.

#string will be regenerated. Use #merge to add many elements at once.

Use #append to append new elements to #string. See SequenceSet@Ordered+and+Normalized+sets.

Related: #add?, #merge, #union, #append

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1214

def add(element)
  modifying! # short-circuit before import_run
  add_run import_run element
  normalize!
end

#add?(element) ⇒ self?

Adds a range or number to the set and returns self. Returns nil when the element is already included in the set.

#string will be regenerated. Use #merge to add many elements at once.

Related: #add, #merge, #union, #include?

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1286

def add?(element)
  modifying! # short-circuit before include?
  add element unless include? element
end

#add_coalesced_minmax(lower_idx, lmin, lmax, min, max) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2418

def add_coalesced_minmax(lower_idx, lmin, lmax, min, max)
  return if lmin <= min && max <= lmax
  set_min_at lower_idx, (lmin = min) if min < lmin
  set_max_at lower_idx, (lmax = max) if lmax < max
  next_idx = lower_idx + 1
  return if next_idx == runs.count
  tmax_adj = lmax + 1
  if (upper_idx = bsearch_index(tmax_adj))
    if tmax_adj < min_at(upper_idx)
      upper_idx -= 1
    else
      set_max_at lower_idx, max_at(upper_idx)
    end
  end
  slice_runs! next_idx..upper_idx
end

#add_minmax(minmax) (private) Also known as: #add_run

This method is for internal use only.

--|=====| |=====new run=======| append ?????????-|=====new run=======|-|===lower===|-- insert

|=====new run=======|

---------??=======lower=======??--------------- noop

---------??===lower==|--|==| join remaining ---------??===lower==|--|==|----|===upper===|-- join until upper ---------??===lower==|--|==|--|=====upper===|-- join to upper

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2407

def add_minmax(minmax)
  modifying!
  min, max   = minmax
  lower_idx  = bsearch_index(min - 1)
  lmin, lmax = min_at(lower_idx), max_at(lower_idx) if lower_idx
  if    lmin.nil?        then append_minmax min, max
  elsif (max + 1) < lmin then insert_minmax lower_idx, min, max
  else  add_coalesced_minmax(lower_idx, lmin, lmax, min, max)
  end
end

#add_minmaxes(minmaxes) (private) Also known as: #add_runs

This method is for internal use only.
[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2383

def add_minmaxes(minmaxes)
  minmaxes.each do |minmax|
    add_minmax minmax
  end
  self
end

#add_run(minmax) (private)

This method is for internal use only.

Alias for #add_minmax.

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2485

alias add_run       add_minmax

#add_runs(minmaxes) (private)

This method is for internal use only.

Alias for #add_minmaxes.

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2484

alias add_runs      add_minmaxes

#all_above?(number) ⇒ Boolean

Returns whether the set only contains numbers greater than number (not inclusive of number).

Net::IMAP::SequenceSet["5:8"].all_above?(9)  => true
Net::IMAP::SequenceSet["5:8"].all_above?(7)  => false
Net::IMAP::SequenceSet["5:8"].all_above?(5)  => false

"*" is evaluated as UINT32_MAX: Net::IMAP::SequenceSet.all_above?(UINT32_MAX - 1) => true Net::IMAP::SequenceSet.all_above?(UINT32_MAX) => false

This is roughly equivalent to several other comparisons:

# Given the following assumptions:
set.empty?        => false
set.include_star? => false
number            => ^(1...UINT32_MAX)

# Then these expressions will always return the same result:
set.all_above?(number)       =>  result
set.none_below?(number + 1)  => ^result
!set.any_below?(number + 1)  => ^result

set == set.above(number)     => ^result
set.disjoint?(..number)      => ^result
number < set.min             => ^result

Related: other comparison methods, #above

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 823

def all_above?(number)
  number = nz_number(number)
  empty? || number < min(star: UINT32_MAX)
end

#all_below?(number) ⇒ Boolean

Returns whether the set only contains numbers less than number (not inclusive of number).

Net::IMAP::SequenceSet["5:8"].all_below?(9)  => true
Net::IMAP::SequenceSet["5:8"].all_below?(8)  => false
Net::IMAP::SequenceSet["5:8"].all_below?(5)  => false

"*" is evaluated as UINT32_MAX: Net::IMAP::SequenceSet.all_below?(UINT32_MAX) => false

This is roughly equivalent to several other comparisons:

# Given the following assumptions:
set.empty?        => false
set.include_star? => false
number            => ^(2..UINT32_MAX)

# Then these expressions will always return the same result:
set.all_below?(number)       =>  result
set.none_above?(number - 1)  => ^result
!set.any_above?(number - 1)  => ^result

set == set.below(number)     => ^result
set.disjoint?(number..)      => ^result
set.max < number             => ^result

Related: other comparison methods, #below

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 859

def all_below?(number)
  number = nz_number(number)
  empty? || max(star: UINT32_MAX) < number
end

#any_above?(number) ⇒ Boolean

Returns whether the set contains any numbers greater than number (not inclusive of number).

Net::IMAP::SequenceSet["5:8"].any_above?(1)  => true
Net::IMAP::SequenceSet["5:8"].any_above?(7)  => true
Net::IMAP::SequenceSet["5:8"].any_above?(8)  => false

"*" is evaluated as UINT32_MAX: Net::IMAP::SequenceSet.any_above?(UINT32_MAX - 1) => true Net::IMAP::SequenceSet.any_above?(UINT32_MAX) => false

This is roughly equivalent to several other comparisons:

# Given the following assumptions:
set.empty?        => false
set.include_star? => false
number            => ^(1...UINT32_MAX)

# Then these expressions will always return the same result:
set.any_above?(number)         =>  result
!set.none_above?(number)       => ^result
!set.all_below?(number + 1)    => ^result

set.cover?(set.above(number))  => ^result
set.intersect?(number + 1..)   => ^result
number < set.max               => ^result

Related: other comparison methods, #above

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 896

def any_above?(number)
  number = nz_number(number)
  valid? && number < max(star: UINT32_MAX)
end

#any_below?(number) ⇒ Boolean

Returns whether the set contains any numbers less than number (not inclusive of number).

Net::IMAP::SequenceSet["5:8"].any_below? 5  => false
Net::IMAP::SequenceSet["5:8"].any_below? 6  => true
Net::IMAP::SequenceSet["5:8"].any_below? 9  => true

"*" is evaluated as UINT32_MAX: Net::IMAP::SequenceSet.any_below?(UINT32_MAX) => false

This is roughly equivalent to several other comparisons:

# Given the following assumptions:
set.empty?        => false
set.include_star? => false
number            => ^(2..UINT32_MAX)

# Then these expressions will always return the same result:
set.any_below?(number)         =>  result
!set.none_below?(number)       => ^result
!set.all_above?(number - 1)    => ^result

set.cover?(set.below(number))  => ^result
set.intersect?(...number)      => ^result
set.min < number               => ^result

Related: other comparison methods, #below

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 932

def any_below?(number)
  number = nz_number(number)
  valid? && min(star: UINT32_MAX) < number
end

#append(entry)

Adds a range or number to the set and returns self.

Unlike #add, #merge, or #union, the new value is appended to #string. This may result in a #string which has duplicates or is out-of-order.

set = Net::IMAP::SequenceSet.new
set.append(1..2)  # => Net::IMAP::SequenceSet("1:2")
set.append(5)     # => Net::IMAP::SequenceSet("1:2,5")
set.append(4)     # => Net::IMAP::SequenceSet("1:2,5,4")
set.append(3)     # => Net::IMAP::SequenceSet("1:2,5,4,3")
set.append(2)     # => Net::IMAP::SequenceSet("1:2,5,4,3,2")

If entry is a string, it will be converted into normal form.

set = Net::IMAP::SequenceSet("4:5,1:2")
set.append("6:6") # => Net::IMAP::SequenceSet("4:5,1:2,6")
set.append("9:8") # => Net::IMAP::SequenceSet("4:5,1:2,6,8:9")

If entry adjacently follows the last entry, they will coalesced:

set = Net::IMAP::SequenceSet.new("2,1,9:10")
set.append(11..12) # => Net::IMAP::SequenceSet("2,1,9:12")

Non-normalized sets store the string in addition to an internal normalized uint32 set representation. This can more than double memory usage, so large sets should avoid using #append unless preserving order is required. See SequenceSet@Ordered+and+Normalized+sets.

Related: #add, #merge, #union

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1249

def append(entry)
  modifying! # short-circuit before import_minmax
  minmax = import_minmax entry
  adj = minmax.first - 1
  if @string.nil? && (runs.empty? || max_num <= adj)
    # append to elements or coalesce with last element
    add_minmax minmax
    return self
  elsif @string.nil?
    # generate string for out-of-order append
    head, comma = normalized_string, ","
  else
    # @string already exists... maybe coalesce with last entry
    head, comma, last_entry = @string.rpartition(",")
    last_min, last_max = import_minmax last_entry
    if last_max == adj
      # coalesce with last entry
      minmax[0] = last_min
    else
      # append to existing string
      head, comma = @string, ","
    end
  end
  entry = export_minmax minmax
  add_minmax minmax
  @string = -"#{head}#{comma}#{entry}"
  self
end

#append_minmax(min, max) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2368

def append_minmax(min, max)      = minmaxes << [min, max]

#at(index) ⇒ Integer?

Returns the number at the given index in the sorted set, without modifying the set.

index is interpreted the same as in #[], except that #at only allows a single integer argument.

Related: #[], #slice, #ordered_at

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1722

def at(index)
  seek_number_in_minmaxes(minmaxes, index)
end

#below(num)

Returns a copy of self which only contains numbers below num.

Net::IMAP::SequenceSet["5,10:22,50"].below(10) # to_s => "5"
Net::IMAP::SequenceSet["5,10:22,50"].below(20) # to_s => "5,10:19"
Net::IMAP::SequenceSet["5,10:22,50"].below(30) # to_s => "5,10:22"

This returns the same result as #intersection with (..(num-1)) or #difference with (num..).

Net::IMAP::SequenceSet["5,10:22,50"] & (..9)    # to_s => "5"
Net::IMAP::SequenceSet["5,10:22,50"] - (10..)   # to_s => "5"
Net::IMAP::SequenceSet["5,10:22,50"] & (..19)   # to_s => "5,10:19"
Net::IMAP::SequenceSet["5,10:22,50"] - (20..)   # to_s => "5,10:19"

When the set does not contain , #below is identical to #limit with max: num - 1. When the set does contain , #below always drops it from the result. Use #limit when the ::Net::IMAP semantics for * must be enforced.

Net::IMAP::SequenceSet["5,10:22,50"].below(30)      # to_s => "5,10:22"
Net::IMAP::SequenceSet["5,10:22,50"].limit(max: 29) # to_s => "5,10:22"
Net::IMAP::SequenceSet["5,10:22,*"].below(30)       # to_s => "5,10:22"
Net::IMAP::SequenceSet["5,10:22,*"].limit(max: 29)  # to_s => "5,10:22,29"

Related: #above, other set operations, #all_below?, #any_below?, #none_below?

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1837

def below(num)
  NumValidator.valid_nz_number?(num) or
    raise ArgumentError, "not a valid sequence set number"
  difference(num..)
end

#bsearch_index(num) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2278

def bsearch_index(num)  = minmaxes.bsearch_index { _2 >= num }

#bsearch_minmax(num) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2279

def bsearch_minmax(num) = minmaxes.bsearch       { _2 >= num }

#bsearch_range(num) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2280

def bsearch_range(num)  = (min, max = bsearch_minmax(num)) && (min..max)

#cardinality

Returns the number of members in the set.

Unlike #count, "*" is considered to be distinct from 2³² - 1 (the maximum 32-bit unsigned integer value).

set = Net::IMAP::SequenceSet[1..10]
set.count        #=> 10
set.cardinality  #=> 10

set = Net::IMAP::SequenceSet["4294967295,*"]
set.count        #=> 1
set.cardinality  #=> 2

set = Net::IMAP::SequenceSet[1..]
set.count        #=> 4294967295
set.cardinality  #=> 4294967296

Related: #count, #count_with_duplicates

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1584

def cardinality = minmaxes.sum(runs.count) { _2 - _1 }

#clear

Removes all elements and returns self.

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 579

def clear
  modifying! # redundant check (normalizes the error message for JRuby)
  set_data.clear
  @string = nil
  self
end

#complement

Alias for #~.

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1200

alias complement :~

#complement! ⇒ self

In-place set #complement. Replaces the contents of this set with its own #complement. It will contain all possible values except for those currently in the set.

Related: #complement

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1890

def complement!
  modifying! # short-circuit before querying
  return replace(self.class.full) if empty?
  return clear                    if full?
  flat = minmaxes.flat_map { [_1 - 1, _2 + 1] }
  if flat.first < 1         then flat.shift else flat.unshift 1        end
  if STAR_INT   < flat.last then flat.pop   else flat.push    STAR_INT end
  replace_minmaxes flat.each_slice(2).to_a
  normalize!
end

#count

Returns the count of distinct #numbers in the set.

Unlike #cardinality, "*" is considered to be equal to 2³² - 1 (the maximum 32-bit unsigned integer value).

set = Net::IMAP::SequenceSet[1..10]
set.count        #=> 10
set.cardinality  #=> 10

set = Net::IMAP::SequenceSet["4294967295,*"]
set.count        #=> 1
set.cardinality  #=> 2

set = Net::IMAP::SequenceSet[1..]
set.count        #=> 4294967295
set.cardinality  #=> 4294967296

Related: #cardinality, #count_with_duplicates

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1604

def count
  cardinality + (include_star? && include?(UINT32_MAX) ? -1 : 0)
end

#count_duplicates

Returns the count of repeated numbers in the ordered #entries, the difference between #count_with_duplicates and #count.

When #string is normalized, this is zero.

Related: #entries, #count_with_duplicates, #has_duplicates?

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1669

def count_duplicates
  return 0 unless @string
  count_with_duplicates - count
end

#count_entries (private)

This method is for internal use only.

{{{2 Ordered entry methods

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2250

def count_entries
  @string ? @string.count(",") + 1 : runs.count
end

#count_with_duplicates

Returns the count of numbers in the ordered #entries, including any repeated numbers.

When #string is normalized, this returns the same as #count. Like #count, "*" is considered to be equal to 2³² - 1 (the maximum 32-bit unsigned integer value).

In a range, "*" is not considered a duplicate: set = Net::IMAP::SequenceSet set.count_with_duplicates #=> 1 set.size #=> 2 set.count #=> 1 set.cardinality #=> 2

In a separate entry, "*" is considered a duplicate: set = Net::IMAP::SequenceSet set.count_with_duplicates #=> 2 set.size #=> 2 set.count #=> 1 set.cardinality #=> 2

Related: #count, #cardinality, #size, #count_duplicates, #has_duplicates?, #entries

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1631

def count_with_duplicates
  return count unless @string
  each_entry_minmax.sum {|min, max|
    max - min + ((max == STAR_INT && min != STAR_INT) ? 0 : 1)
  }
end

#cover?(other) ⇒ Boolean

Returns whether other is contained within the set. other may be any object that would be accepted by .new.

Related: #===, #include?, #include_star?, #intersect?

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 732

def cover?(other) import_runs(other).none? { !include_run?(_1) } end

#deconstruct

Returns an array with #normalized_string when valid and an empty array otherwise.

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 630

def deconstruct; valid? ? [normalized_string] : [] end

#delete(element) ⇒ self

Deletes the given range or number from the set and returns self.

#string will be regenerated after deletion. Use #subtract to remove many elements at once.

Related: #delete?, #delete_at, #subtract, #difference

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1299

def delete(element)
  modifying! # short-circuit before import_run
  subtract_run import_run element
  normalize!
end

#delete?(number) ⇒ Integer? #delete?(star) ⇒ :*? #delete?(range) ⇒ sequence set?

Removes a specified value from the set, and returns the removed value. Returns nil if nothing was removed.

Returns an integer when the specified number argument was removed: set = Net::IMAP::SequenceSet.new [5..10, 20] set.delete?(7) #=> 7 set #=> #<Net::IMAP::SequenceSet "5:6,8:10,20"> set.delete?("20") #=> 20 set #=> #<Net::IMAP::SequenceSet "5:6,8:10"> set.delete?(30) #=> nil

Returns : when or -1 is specified and removed: set = Net::IMAP::SequenceSet.new "5:9,20,35," set.delete?(-1) #=> : set #=> #<Net::IMAP::SequenceSet "5:9,20,35">

And returns a new SequenceSet when a range is specified:

set = Net::IMAP::SequenceSet.new [5..10, 20]
set.delete?(9..)  #=> #<Net::IMAP::SequenceSet "9:10,20">
set               #=> #<Net::IMAP::SequenceSet "5:8">
set.delete?(21..) #=> nil

#string will be regenerated after deletion.

Related: #delete, #delete_at, #subtract, #difference, #disjoint?

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1337

def delete?(element)
  modifying! # short-circuit before import_minmax
  element = input_try_convert(element)
  minmax = import_minmax element
  if number_input?(element)
    return unless include_minmax? minmax
    subtract_minmax minmax
    normalize!
    export_num minmax.first
  else
    copy = dup
    subtract_minmax minmax
    normalize!
    copy if copy.subtract(self).valid?
  end
end

#delete_at(index) ⇒ Numeric, ...

Deletes a number the set, indicated by the given index. Returns the number that was removed, or nil if nothing was removed.

#string will be regenerated after deletion.

Related: #delete, #delete?, #slice!, #subtract, #difference

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1362

def delete_at(index)
  slice! Integer(index.to_int)
end

#delete_run_at(idx) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2370

def delete_run_at(idx)           = runs.delete_at(idx)

#difference(other)

Alias for #-.

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1126

alias difference :-

#disjoint?(other) ⇒ Boolean

Returns true if the set and a given object have no common elements, false otherwise.

Net::IMAP::SequenceSet["5:10"].disjoint? "7,9,11" #=> false
Net::IMAP::SequenceSet["5:10"].disjoint? "11:33"  #=> true

Related: #intersection, #intersect?

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 787

def disjoint?(other)
  empty? || import_runs(other).none? { intersect_run? _1 }
end

#dup_set_data (protected)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2350

def dup_set_data    = set_data.map { _1.dup }

#each_element

Yields each number or range (or :*) in #elements to the block and returns self. Returns an enumerator when called without a block.

The returned numbers are sorted and de-duplicated, even when the input #string is not. See #normalize, SequenceSet@Ordered+and+Normalized+sets.

Related: #elements, #each_entry

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1509

def each_element # :yields: integer or range or :*
  return to_enum(__method__) unless block_given?
  runs.each do yield export_run_entry _1 end
  self
end

#each_entry(&block)

Yields each number or range in #string to the block and returns self. Returns an enumerator when called without a block.

The entries are yielded in the same order they appear in #string, with no sorting, deduplication, or coalescing. When #string is in its normalized form, this will yield the same values as #each_element.

See SequenceSet@Ordered+and+Normalized+sets.

Related: #entries, #each_element

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1497

def each_entry(&block) # :yields: integer or range or :*
  return to_enum(__method__) unless block_given?
  each_entry_run do yield export_run_entry _1 end
end

#each_entry_minmax(&block) (private) Also known as: #each_entry_run

This method is for internal use only.
[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2254

def each_entry_minmax(&block)
  return to_enum(__method__) unless block_given?
  if @string
    @string.split(",") do block.call parse_minmax _1 end
  else
    minmaxes.each(&block)
  end
  self
end

#each_entry_run(&block) (private)

This method is for internal use only.

Alias for #each_entry_minmax.

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2263

alias each_entry_run each_entry_minmax

#each_minmax_with_index(minmaxes) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2299

def each_minmax_with_index(minmaxes)
  idx_min = 0
  minmaxes.each do |min, max|
    idx_max = idx_min + (max - min)
    yield min, max, idx_min, idx_max
    idx_min = idx_max + 1
  end
  idx_min
end

#each_number(&block)

Yields each number in #numbers to the block and returns self. If the set contains a *, RangeError will be raised.

Returns an enumerator when called without a block (even if the set contains *).

Related: #numbers, #each_ordered_number

Raises:

  • (RangeError)
[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1537

def each_number(&block) # :yields: integer
  return to_enum(__method__) unless block_given?
  raise RangeError, '%s contains "*"' % [self.class] if include_star?
  minmaxes.each do each_number_in_minmax _1, _2, &block end
  self
end

#each_number_in_minmax(min, max, &block) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2208

def each_number_in_minmax(min, max, &block)
  if    min == STAR_INT then yield :*
  elsif min == max      then yield min
  elsif max != STAR_INT then (min..max).each(&block)
  else
    raise RangeError, "#{SequenceSet} cannot enumerate range with '*'"
  end
end

#each_ordered_number(&block)

Yields each number in #entries to the block and returns self. If the set contains a *, RangeError will be raised.

Returns an enumerator when called without a block (even if the set contains *).

Related: #entries, #each_number

Raises:

  • (RangeError)
[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1551

def each_ordered_number(&block)
  return to_enum(__method__) unless block_given?
  raise RangeError, '%s contains "*"' % [self.class] if include_star?
  each_entry_minmax do each_number_in_minmax _1, _2, &block end
end

#each_parsed_entry(str) (private)

This method is for internal use only.

yields validated but unsorted [num] or [num, num]

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2230

def each_parsed_entry(str)
  return to_enum(__method__, str) unless block_given?
  str&.split(",", -1) do |entry| yield parse_entry(entry) end
end

#each_range

Yields each range in #ranges to the block and returns self. Returns an enumerator when called without a block.

Related: #ranges

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1519

def each_range # :yields: range
  return to_enum(__method__) unless block_given?
  minmaxes.each do |min, max|
    if    min == STAR_INT then yield :*..
    elsif max == STAR_INT then yield min..
    else                       yield min..max
    end
  end
  self
end

#elements Also known as: #to_a

Returns an array of ranges and integers and :*.

The returned elements are sorted and coalesced, even when the input #string is not. * will sort last. See #normalize, SequenceSet@Ordered+and+Normalized+sets.

By itself, * translates to :. A range containing translates to an endless range. Use #limit to translate both cases to a maximum value.

Net::IMAP::SequenceSet["2,5:9,6,*,12:11"].elements
#=> [2, 5..9, 11..12, :*]

Related: #each_element, #ranges, #numbers

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1438

def elements; each_element.to_a end

#encode_with(coder)

This method is for internal use only.

For YAML serialization

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2089

def encode_with(coder) # :nodoc:
  # we can perfectly reconstruct from the string
  coder['string'] = to_s
end

#entries

Returns an array of ranges and integers and :*.

The entries are in the same order they appear in #string, with no sorting, deduplication, or coalescing. When #string is in its normalized form, this will return the same result as #elements. This is useful when the given order is significant, for example in a ESEARCH response to Net::IMAP#sort.

See SequenceSet@Ordered+and+Normalized+sets.

Related: #each_entry, #elements

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1422

def entries; each_entry.to_a end

#eql?(other) ⇒ Boolean

Hash equality requires the same encoded #string representation.

Net::IMAP::SequenceSet["1:3"]  .eql? Net::IMAP::SequenceSet["1:3"]
#=> true
Net::IMAP::SequenceSet["1,2,3"].eql? Net::IMAP::SequenceSet["1:3"]
#=> false
Net::IMAP::SequenceSet["1,3"]  .eql? Net::IMAP::SequenceSet["3,1"]
#=> false
Net::IMAP::SequenceSet["9,1:*"].eql? Net::IMAP::SequenceSet["1:*"]
#=> false

Related: #==, #normalize

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 708

def eql?(other) self.class == other.class && string == other.string end

#export_minmax(minmax) (private) Also known as: #export_run

This method is for internal use only.
[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2194

def export_minmax(minmax) minmax.uniq.map { export_num _1 }.join(":") end

#export_minmax_entry(min, max) (private) Also known as: #export_run_entry

This method is for internal use only.
[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2199

def export_minmax_entry((min, max))
  if    min == STAR_INT then :*
  elsif max == STAR_INT then min..
  elsif min == max      then min
  else                       min..max
  end
end

#export_minmaxes(minmaxes) (private) Also known as: #export_runs

This method is for internal use only.
[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2190

def export_minmaxes(minmaxes)
  -minmaxes.map { export_minmax _1 }.join(",")
end

#export_num(num) (private)

This method is for internal use only.

{{{2 Export methods

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2188

def export_num(num) num == STAR_INT ? :* : num end

#export_run(minmax) (private)

This method is for internal use only.

Alias for #export_minmax.

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2197

alias export_run  export_minmax

#export_run_entry(min, max) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2206

alias export_run_entry export_minmax_entry

#export_runs(minmaxes) (private)

This method is for internal use only.

Alias for #export_minmaxes.

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2196

alias export_runs export_minmaxes

#find_index(number)

Returns the (sorted and deduplicated) index of number in the set, or nil if number isn't in the set.

Related: #[], #at, #find_ordered_index

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1690

def find_index(number)
  number = import_num number
  each_minmax_with_index(minmaxes) do |min, max, idx_min|
    number <  min and return nil
    number <= max and return idx_min + (number - min)
  end
  nil
end

#find_ordered_index(number)

Returns the first index of number in the ordered #entries, or nil if number isn't in the set.

Related: #find_index

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1703

def find_ordered_index(number)
  number = import_num number
  each_minmax_with_index(each_entry_minmax) do |min, max, idx_min|
    if min <= number && number <= max
      return idx_min + (number - min)
    end
  end
  nil
end

#freeze

Freezes and returns the set. A frozen SequenceSet is Ractor-safe.

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 667

def freeze
  return self if frozen?
  freeze_set_data
  super
end

#freeze_set_data (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2349

def freeze_set_data = set_data.each(&:freeze).freeze

#hash

See #eql?

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 711

def hash; [self.class, string].hash end

#import_minmax(input) (private) Also known as: #import_run

This method is for internal use only.

{{{2 Import methods

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2128

def import_minmax(input)
  entry = input_try_convert input
  case entry
  when *STARS, Integer then [int = import_num(entry), int]
  when Range           then import_range_minmax(entry)
  when String          then parse_minmax(entry)
  else
    raise DataFormatError, "expected number or range, got %p" % [input]
  end
end

#import_num(obj) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2182

def import_num(obj) STARS.include?(obj) ? STAR_INT : nz_number(obj) end

#import_range_minmax(range) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2172

def import_range_minmax(range)
  first = import_num(range.begin || 1)
  last  = import_num(range.end   || :*)
  last -= 1 if range.exclude_end? && range.end && last != STAR_INT
  unless first <= last
    raise DataFormatError, "invalid range for sequence-set: %p" % [range]
  end
  [first, last]
end

#import_run(input) (private)

This method is for internal use only.

Alias for #import_minmax.

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2138

alias import_run import_minmax

#import_runs(input) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2140

def import_runs(input)
  set = input_try_convert input
  case set
  when *STARS, Integer, Range then [import_run(set)]
  when String      then parse_runs set
  when SequenceSet then set.runs
  when Set         then set.map      { [import_num(_1)] * 2 }
  when Array       then set.flat_map { import_runs _1 }
  when nil         then []
  else
    raise DataFormatError, "expected nz-number, range, '*', Set, Array; " \
                           "got %p" % [input]
  end
end

#include?(element) ⇒ Boolean Also known as: #member?

Returns true when a given number or range is in self, and false otherwise. Returns nil when number isn't a valid SequenceSet element (Integer, Range, *, sequence-set string).

set = Net::IMAP::SequenceSet["5:10,100,111:115"]
set.include? 1      #=> false
set.include? 5..10  #=> true
set.include? 11..20 #=> false
set.include? 100    #=> true
set.include? 6      #=> true, covered by "5:10"
set.include? 6..9   #=> true, covered by "5:10"
set.include? "6:9"  #=> true, strings are parsed
set.include? 4..9   #=> false, intersection is not sufficient
set.include? "*"    #=> false, use #limit to re-interpret "*"
set.include? -1     #=> false, -1 is interpreted as "*"

set = Net::IMAP::SequenceSet["5:10,100,111:*"]
set.include? :*     #=> true
set.include? "*"    #=> true
set.include? -1     #=> true
set.include?(200..) #=> true
set.include?(100..) #=> false

Related: #include_star?, #cover?, #===, #intersect?

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 758

def include?(element)
  run = import_run element rescue nil
  !!include_run?(run) if run
end

#include_minmax?(min, max) ⇒ Boolean (private) Also known as: #include_run?

This method is for internal use only.

{{{2 Search methods

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2268

def include_minmax?((min, max)) bsearch_range(min)&.cover?(min..max) end

#include_run?(min, max) (private)

This method is for internal use only.

Alias for #include_minmax?.

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2275

alias include_run?   include_minmax?

#init_with(coder)

This method is for internal use only.

For YAML deserialization

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2095

def init_with(coder) # :nodoc:
  @set_data = new_set_data
  self.string = coder['string']
end

#initialize_clone(other, freeze: nil) (private)

This method is for internal use only.

frozen clones are shallow copied

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2114

def initialize_clone(other, freeze: nil)
  @set_data = other.dup_set_data unless other.frozen? && freeze != false
  freeze_set_data if freeze
  super
end

#initialize_dup(other) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2120

def initialize_dup(other)
  @set_data = other.dup_set_data
  super
end

#input_try_convert(input) (private)

This method is for internal use only.

unlike SequenceSet#try_convert, this returns an Integer, Range, String, Set, Array, or... any type of object.

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2157

def input_try_convert(input)
  SequenceSet.try_convert(input) ||
    Integer.try_convert(input) ||
    String.try_convert(input) ||
    input
end

#insert_minmax(idx, min, max) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2369

def insert_minmax(idx, min, max) = minmaxes.insert idx, [min, max]

#inspect

Returns an inspection string for the SequenceSet.

Net::IMAP::SequenceSet.new.inspect
#=> "Net::IMAP::SequenceSet()"

Net::IMAP::SequenceSet(1..5, 1024, 15, 2000).inspect
#=> 'Net::IMAP::SequenceSet("1:5,15,1024,2000")'

Frozen sets have slightly different output:

Net::IMAP::SequenceSet.empty.inspect
#=> "Net::IMAP::SequenceSet.empty"

Net::IMAP::SequenceSet[1..5, 1024, 15, 2000].inspect
#=> 'Net::IMAP::SequenceSet["1:5,15,1024,2000"]'

Large sets (by number of #entries) have abridged output, with only the first and last entries:

Net::IMAP::SequenceSet(((1..5000) % 2).to_a).inspect
#=> #<Net::IMAP::SequenceSet 2500 entries "1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31,...(2468 entries omitted)...,4969,4971,4973,4975,4977,4979,4981,4983,4985,4987,4989,4991,4993,4995,4997,4999">

Related: #to_s, #string

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2044

def inspect
  case (count = count_entries)
  when 0
    (frozen? ? "%s.empty" : "%s()") % [self.class]
  when ..INSPECT_MAX_LEN
    (frozen? ? "%s[%p]" : "%s(%p)") % [self.class, to_s]
  else
    if @string
      head = @string[INSPECT_ABRIDGED_HEAD_RE]
      tail = @string[INSPECT_ABRIDGED_TAIL_RE]
    else
      head = export_runs(runs.first(INSPECT_TRUNCATE_LEN)) + ","
      tail = "," + export_runs(runs.last(INSPECT_TRUNCATE_LEN))
    end
    '#<%s %d entries "%s...(%d entries omitted)...%s"%s>' % [
      self.class, count,
      head, count - INSPECT_TRUNCATE_LEN * 2, tail,
      frozen? ? " (frozen)" : "",
    ]
  end
end

#intersect!(other)

In-place set #intersection. Removes any elements that are missing from other from this set, keeping only the #intersection, and returns self.

other can be any object that would be accepted by .new.

set = Net::IMAP::SequenceSet.new(1..5)
set.intersect! [2, 4, 6]
set #=> Net::IMAP::SequenceSet("2,4")

Related: #intersection, #intersect?

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1912

def intersect!(other)
  modifying! # short-circuit before processing input
  subtract SequenceSet.new(other).complement!
end

#intersect?(other) ⇒ Boolean Also known as: #overlap?

Returns true if the set and a given object have any common elements, false otherwise.

Net::IMAP::SequenceSet["5:10"].intersect? "7,9,11" #=> true
Net::IMAP::SequenceSet["5:10"].intersect? "11:33"  #=> false

Related: #intersection, #disjoint?, #cover?, #include?

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 775

def intersect?(other)
  valid? && import_runs(other).any? { intersect_run? _1 }
end

#intersect_minmax?(min, max) ⇒ Boolean (private) Also known as: #intersect_run?

This method is for internal use only.
[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2270

def intersect_minmax?((min, max))
  range = bsearch_range(min) and
    range.include?(min) || range.include?(max) || (min..max).cover?(range)
end

#intersect_run?(min, max) (private)

This method is for internal use only.

Alias for #intersect_minmax?.

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2276

alias intersect_run? intersect_minmax?

#intersection(other)

Alias for #&.

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1152

alias intersection :&

#limit(max:)

Returns a frozen SequenceSet with * converted to #max, numbers and ranges over #max removed, and ranges containing #max converted to end at #max.

Net::IMAP::SequenceSet["5,10:22,50"].limit(max: 20).to_s
#=> "5,10:20"

is always interpreted as the maximum value. When the set contains , it will be set equal to the limit.

Net::IMAP::SequenceSet["*"].limit(max: 37)
#=> Net::IMAP::SequenceSet["37"]
Net::IMAP::SequenceSet["5:*"].limit(max: 37)
#=> Net::IMAP::SequenceSet["5:37"]
Net::IMAP::SequenceSet["500:*"].limit(max: 37)
#=> Net::IMAP::SequenceSet["37"]

Related: #limit!

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1861

def limit(max:)
  max = import_num(max)
  if    empty?                      then self.class.empty
  elsif !include_star? && max < min then self.class.empty
  elsif max(star: STAR_INT) <= max  then frozen? ? self : dup.freeze
  else                                   dup.limit!(max: max).freeze
  end
end

#limit!(max:)

Removes all members over #max and returns self. If * is a member, it will be converted to #max.

Related: #limit

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1874

def limit!(max:)
  modifying! # short-circuit before querying
  star = include_star?
  max  = import_num(max)
  subtract_minmax [max + 1, STAR_INT]
  add_minmax      [max,     max     ] if star
  normalize!
end

#max(star: :*) ⇒ Integer, ... #max(count) ⇒ SequenceSet

Returns the maximum value in self, star when the set includes *, or nil when the set is empty.

When #count is given, a new SequenceSet is returned, containing only the last #count numbers. An empty SequenceSet is returned when self is empty. (star is ignored when #count is given.)

Related: #min, #minmax, #slice

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1022

def max(count = nil, star: :*)
  if count
    count = Integer(count.to_int)
    raise ArgumentError, 'negative count' if count < 0
    return remain_frozen_empty if count == 0
    if cardinality <= count
      frozen? ? self : dup
    else
      slice(-count..) || remain_frozen_empty
    end
  elsif (val = max_num)
    val == STAR_INT ? star : val
  end
end

#max_at(idx) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2360

def max_at(idx)             = minmaxes[idx][1]

#max_num (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2357

def max_num                 = minmaxes.last&.last

#member?(element)

Alias for #include?.

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 763

alias member? include?

#merge(*sets)

In-place set #union. Merges all of the elements that appear in any of the sets into this set, and returns self.

The sets may be any objects that would be accepted by .new.

#string will be regenerated after all sets have been merged.

Related: #add, #add?, #union

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1393

def merge(*sets)
  modifying! # short-circuit before import_runs
  add_runs import_runs sets
  normalize!
end

#min(star: :*) ⇒ Integer, ... #min(count) ⇒ SequenceSet

Returns the minimum value in self, star when the only value in the set is *, or nil when the set is empty.

When #count is given, a new SequenceSet is returned, containing only the first #count numbers. An empty SequenceSet is returned when self is empty. (star is ignored when #count is given.)

Related: #max, #minmax, #slice

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1049

def min(count = nil, star: :*)
  if count
    count = Integer(count.to_int)
    raise ArgumentError, 'negative count' if count < 0
    slice(0...count) || remain_frozen_empty
  elsif (val = min_num)
    val != STAR_INT ? val : star
  end
end

#min_at(idx) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2359

def min_at(idx)             = minmaxes[idx][0]

#min_num (private)

This method is for internal use only.

{{{2 Core set data query/enumeration primitives

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2356

def min_num                 = minmaxes.first&.first

#minmax(star: :*) ⇒ Array, max

Returns a 2-element array containing the minimum and maximum numbers in self, or nil when the set is empty. star is handled the same way as by #min and #max.

Related: #min, #max

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1066

def minmax(star: :*); [min(star: star), max(star: star)] unless empty? end

#modifying! (private)

This method is for internal use only.

{{{2 Update methods

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2377

def modifying!
  if frozen?
    raise FrozenError, "can't modify frozen #{self.class}: %p" % [self]
  end
end

#new_set_data (private)

This method is for internal use only.

{{{2 Core set data create/freeze/dup primitives

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2348

def new_set_data    = []

#none_above?(number) ⇒ Boolean

Returns whether the set contains no numbers greater than number (not inclusive of number).

Net::IMAP::SequenceSet["5:8"].none_above?(9)  => true
Net::IMAP::SequenceSet["5:8"].none_above?(8)  => true
Net::IMAP::SequenceSet["5:8"].none_above?(7)  => false

"*" is evaluated as UINT32_MAX: Net::IMAP::SequenceSet.none_above?(UINT32_MAX - 1) => false Net::IMAP::SequenceSet.none_above?(UINT32_MAX) => true

This is roughly equivalent to several other comparisons:

# Given the following assumptions:
set.empty?        => false
set.include_star? => false
number            => ^(1...UINT32_MAX)

# Then these expressions will always return the same result:
set.none_above?(number)      =>  result
!set.any_above?(number)      => ^result
set.all_below?(number + 1)   => ^result

set.above(number).empty?     => ^result
set.disjoint?(number..)      => ^result
set.max <= number            => ^result

Related: other comparison methods, #above

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 969

def none_above?(number)
  number = nz_number(number)
  empty? || max(star: UINT32_MAX) <= number
end

#none_below?(number) ⇒ Boolean

Returns whether the set contains no numbers less than number (not inclusive of number).

Net::IMAP::SequenceSet["5:8"].none_below?(4)  => true
Net::IMAP::SequenceSet["5:8"].none_below?(5)  => true
Net::IMAP::SequenceSet["5:8"].none_below?(6)  => false

"*" is evaluated as UINT32_MAX: Net::IMAP::SequenceSet.none_below?(UINT32_MAX) => true

This is roughly equivalent to several other comparisons:

# Given the following assumptions:
set.empty?        => false
set.include_star? => false
number            => ^(2..UINT32_MAX)

# Then these expressions will always return the same result:
set.none_below?(number)      =>  result
!set.any_below?(number)      => ^result
set.all_above?(number - 1)   => ^result

set.above(number).empty?     => ^result
set.disjoint?(...number)     => ^result
number <= set.min            => ^result

Related: other comparison methods, #below

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1005

def none_below?(number)
  number = nz_number(number)
  empty? || number <= min(star: UINT32_MAX)
end

#normal_string?(str) ⇒ Boolean (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2235

def normal_string?(str) normalized_entries? each_parsed_entry str end

#normalize

Returns a SequenceSet with a normalized string representation: entries have been sorted, deduplicated, and coalesced, and all entries are in normal form. Returns self for frozen normalized sets, and a normalized duplicate otherwise.

See SequenceSet@Ordered+and+Normalized+sets.

Net::IMAP::SequenceSet["1:5,3:7,10:9,10:11"].normalize
#=> Net::IMAP::SequenceSet["1:7,9:11"]

Related: #normalize!, #normalized_string, #normalized?

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1993

def normalize
  frozen? && normalized? ? self : remain_frozen(dup.normalize!)
end

#normalize!

Resets #string to be sorted, deduplicated, and coalesced. Returns self. See SequenceSet@Ordered+and+Normalized+sets.

Related: #normalize, #normalized_string, #normalized?

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2001

def normalize!
  modifying! # redundant check (normalizes the error message for JRuby)
  @string = nil
  self
end

#normalized_entries?(entries) ⇒ Boolean (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2237

def normalized_entries?(entries)
  max = nil
  entries.each do |first, last|
    return false if last && last  <= first    # 1:1 or 2:1
    return false if max  && first <= max + 1  # 2,1 or 1,1 or 1,2
    max = last || first
  end
  true
end

#normalized_string

Returns a normalized sequence-set string representation, sorted and deduplicated. Adjacent or overlapping elements will be merged into a single larger range. See SequenceSet@Ordered+and+Normalized+sets.

Net::IMAP::SequenceSet["1:5,3:7,10:9,10:11"].normalized_string
#=> "1:7,9:11"

Returns nil when the set is empty.

Related: #normalize!, #normalize, #string, #to_s, #normalized?

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2017

def normalized_string
  export_runs(runs) unless runs.empty?
end

#number_input?(input) ⇒ Boolean (private)

This method is for internal use only.

NOTE: input_try_convert must be called on input first

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2165

def number_input?(input)
  case input
  when *STARS, Integer then true
  when String          then !input.match?(/[:,]/)
  end
end

#numbers

Returns a sorted array of all of the number values in the sequence set.

The returned numbers are sorted and de-duplicated, even when the input #string is not. See #normalize, SequenceSet@Ordered+and+Normalized+sets.

Net::IMAP::SequenceSet["2,5:9,6,12:11"].numbers
#=> [2, 5, 6, 7, 8, 9, 11, 12]

If the set contains a *, RangeError is raised. See #limit.

Net::IMAP::SequenceSet["10000:*"].numbers
#!> RangeError

WARNING: Even excluding sets with *, an enormous result can easily be created. An array with over 4 billion integers could be returned, requiring up to 32GiB of memory on a 64-bit architecture.

Net::IMAP::SequenceSet[10000..2**32-1].numbers
# ...probably freezes the process for a while...
#!> NoMemoryError (probably)

For safety, consider using #limit or #intersection to set an upper bound. Alternatively, use #each_element, #each_range, or even #each_number to avoid allocation of a result array.

Related: #elements, #ranges, #to_set

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1485

def numbers; each_number.to_a end

#nz_number(num) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2183

def nz_number(num) = NumValidator.coerce_nz_number(num)

#ordered_at(index) ⇒ Integer?

Returns the number at the given index in the ordered #entries, without modifying the set.

index is interpreted the same as in #at (and #[]), except that #ordered_at applies to the ordered #entries, not the sorted set.

Related: #[], #slice, #ordered_at

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1735

def ordered_at(index)
  seek_number_in_minmaxes(each_entry_minmax, index)
end

#overlap?(other)

Alias for #intersect?.

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 778

alias overlap? intersect?

#parse_entry(str) (private)

This method is for internal use only.

Raises:

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2224

def parse_entry(str)
  raise DataFormatError, "invalid sequence set string" if str.empty?
  str.split(":", 2).map! { import_num _1 }
end

#parse_minmax(str) (private) Also known as: #parse_run

This method is for internal use only.
[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2221

def parse_minmax(str) parse_entry(str).minmax end

#parse_run(str) (private)

This method is for internal use only.

Alias for #parse_minmax.

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2222

alias parse_run parse_minmax

#parse_runs(str) (private)

This method is for internal use only.

{{{2 Parse methods

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2220

def parse_runs(str) str.split(",", -1).map! { parse_run _1 } end

#ranges

Returns an array of ranges

The returned elements are sorted and coalesced, even when the input #string is not. * will sort last. See #normalize, SequenceSet@Ordered+and+Normalized+sets.

translates to an endless range. By itself, translates to :... Use #limit to set to a maximum value.

Net::IMAP::SequenceSet["2,5:9,6,*,12:11"].ranges
#=> [2..2, 5..9, 11..12, :*..]
Net::IMAP::SequenceSet["123,999:*,456:789"].ranges
#=> [123..123, 456..789, 999..]

Related: #each_range, #elements, #numbers, #to_set

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1457

def ranges; each_range.to_a end

#remain_frozen(set) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2110

def remain_frozen(set) frozen? ? set.freeze : set end

#remain_frozen_empty (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2111

def remain_frozen_empty; frozen? ? SequenceSet.empty : SequenceSet.new end

#replace(other)

Replace the contents of the set with the contents of other and returns self.

other may be another SequenceSet or any other object that would be accepted by .new.

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 591

def replace(other)
  case other
  when SequenceSet then
    modifying! # short circuit before doing any work
    @set_data = other.dup_set_data
    @string = other.instance_variable_get(:@string)
  when String      then self.string = other
  else                  clear; merge other
  end
  self
end

#replace_minmaxes(other) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2367

def replace_minmaxes(other)      = minmaxes.replace(other)

#reverse_each_minmax_with_index(minmaxes) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2309

def reverse_each_minmax_with_index(minmaxes)
  idx_max = -1
  minmaxes.reverse_each do |min, max|
    yield min, max, (idx_min = idx_max - (max - min)), idx_max
    idx_max = idx_min - 1
  end
  idx_max
end

#seek_number_in_minmaxes(minmaxes, index) (private)

This method is for internal use only.

{{{2 Number indexing methods

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2285

def seek_number_in_minmaxes(minmaxes, index)
  index = Integer(index.to_int)
  if index.negative?
    reverse_each_minmax_with_index(minmaxes) do |min, max, idx_min, idx_max|
      idx_min <= index and return export_num(min + (index - idx_min))
    end
  else
    each_minmax_with_index(minmaxes) do |min, _, idx_min, idx_max|
      index <= idx_max and return export_num(min + (index - idx_min))
    end
  end
  nil
end

#send_data(imap, tag)

This method is for internal use only.

Unstable API: for internal use only (Net::IMAP#send_data)

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2084

def send_data(imap, tag) # :nodoc:
  imap.__send__(:put_string, valid_string)
end

#set_max_at(idx, max) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2366

def set_max_at(idx, max)         = minmaxes[idx][1] = max

#set_min_at(idx, min) (private)

This method is for internal use only.

{{{2 Core set data modification primitives

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2365

def set_min_at(idx, min)         = minmaxes[idx][0] = min

#size

Returns the combined size of the ordered #entries, including any repeated numbers.

When #string is normalized, this returns the same as #cardinality. Like #cardinality, "*" is considered to be be distinct from 2³² - 1 (the maximum 32-bit unsigned integer value).

set = Net::IMAP::SequenceSet["4294967295:*"]
set.size                   #=> 2
set.count_with_duplicates  #=> 1
set.count                  #=> 1
set.cardinality            #=> 2

set = Net::IMAP::SequenceSet["4294967295,*"]
set.size                   #=> 2
set.count_with_duplicates  #=> 2
set.count                  #=> 1
set.cardinality            #=> 2

Related: #cardinality, #count_with_duplicates, #count, #entries

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1658

def size
  return cardinality unless @string
  each_entry_minmax.sum {|min, max| max - min + 1 }
end

#slice(index, length = nil)

Alias for #[].

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1785

alias slice :[]

#slice!(index) ⇒ Integer, ... #slice!(start, length) ⇒ sequence set? #slice!(range) ⇒ sequence set?

Deletes a number or consecutive numbers from the set, indicated by the given index, start and length, or range of offsets. Returns the number or sequence set that was removed, or nil if nothing was removed. Arguments are interpreted the same as for #slice or #[].

#string will be regenerated after deletion.

Related: #slice, #delete_at, #delete, #delete?, #subtract, #difference

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1379

def slice!(index, length = nil)
  modifying! # short-circuit before slice
  deleted = slice(index, length) and subtract deleted
  deleted
end

#slice_length(start, length) (private)

This method is for internal use only.

Raises:

  • (ArgumentError)
[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2318

def slice_length(start, length)
  start  = Integer(start.to_int)
  length = Integer(length.to_int)
  raise ArgumentError, "length must be positive" unless length.positive?
  last = start + length - 1 unless start.negative? && start.abs <= length
  slice_range(start..last)
end

#slice_range(range) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2326

def slice_range(range)
  first = range.begin ||  0
  last  = range.end   || -1
  if range.exclude_end?
    return remain_frozen_empty if last.zero?
    last -= 1 if range.end && last != STAR_INT
  end
  if (first * last).positive? && last < first
    remain_frozen_empty
  elsif (min = at(first))
    max = at(last)
    max = :* if max.nil?
    if    max == :*  then self & (min..)
    elsif min <= max then self & (min..max)
    else                  remain_frozen_empty
    end
  end
end

#slice_runs! (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2371

def slice_runs!(...)             = runs.slice!(...)

#subtract(*sets)

In-place set #difference. Removes all of the elements that appear in any of the given sets from this set, and returns self.

The sets may be any objects that would be accepted by .new.

Related: #difference

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1405

def subtract(*sets)
  modifying! # short-circuit before import_runs
  subtract_runs import_runs sets
  normalize!
end

#subtract_minmax(minmax) (private) Also known as: #subtract_run

This method is for internal use only.

|====subtracted run=======| --|====| no more 1. noop --|====|---------------------------|====lower====|-- 2. noop -------|======lower================|---------------- 3. split --------|=====lower================|---------------- 4. trim beginning

-------|======lower====????????????----------------- trim lower --------|=====lower====????????????----------------- delete lower

-------??=====lower===============|----------------- 5. trim/delete one -------??=====lower====|--|====| no more 6. delete rest -------??=====lower====|--|====|---|====upper====|-- 7. delete until -------??=====lower====|--|====|--|=====upper====|-- 8. delete and trim

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2448

def subtract_minmax(minmax)
  modifying!
  min, max   = minmax
  idx        = bsearch_index(min)
  lmin, lmax = min_at(idx), max_at(idx) if idx
  if    lmin.nil?  then nil # case 1.
  elsif max < lmin then nil # case 2.
  elsif max < lmax then trim_or_split_minmax  idx, lmin,       min, max
  else                  trim_or_delete_minmax idx, lmin, lmax, min, max
  end
end

#subtract_minmaxes(minmaxes) (private) Also known as: #subtract_runs

This method is for internal use only.
[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2390

def subtract_minmaxes(minmaxes)
  minmaxes.each do |minmax|
    subtract_minmax minmax
  end
  self
end

#subtract_run(minmax) (private)

This method is for internal use only.

Alias for #subtract_minmax.

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2487

alias subtract_run  subtract_minmax

#subtract_runs(minmaxes) (private)

This method is for internal use only.

Alias for #subtract_minmaxes.

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2486

alias subtract_runs subtract_minmaxes

#to_a

Alias for #elements.

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1439

alias to_a elements

#to_s

Returns the IMAP sequence-set string representation, or an empty string when the set is empty. Note that an empty set is invalid in the IMAP syntax.

Related: #string, #valid_string, #normalized_string, #inspect

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 664

def to_s; string || "" end

#to_sequence_set ⇒ self

Returns self

Related: .try_convert

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2067

rdoc_method :method: to_sequence_set

#to_set

Returns a Set with all of the #numbers in the sequence set.

If the set contains a *, RangeError will be raised.

See #numbers for the warning about very large sets.

Related: #elements, #ranges, #numbers

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1564

def to_set; Set.new(numbers) end

#trim_or_delete_minmax(lower_idx, lmin, lmax, tmin, tmax) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2467

def trim_or_delete_minmax(lower_idx, lmin, lmax, tmin, tmax)
  if lmin < tmin # trim lower
    lmax = set_max_at lower_idx, tmin - 1
    lower_idx += 1
  end
  if tmax == lmax                                 # case 5
    delete_run_at lower_idx
  elsif (upper_idx = bsearch_index(tmax + 1))
    if min_at(upper_idx) <= tmax                  # case 8
      set_min_at upper_idx, tmax + 1
    end
    slice_runs! lower_idx..upper_idx - 1          # cases 7 and 8
  else                                            # case 6
    truncate_runs! lower_idx
  end
end

#trim_or_split_minmax(idx, lmin, tmin, tmax) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2460

def trim_or_split_minmax(idx, lmin, tmin, tmax)
  set_min_at idx, tmax + 1
  if lmin < tmin # split
    insert_minmax idx, lmin, tmin - 1
  end
end

#truncate_runs!(idx) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2372

def truncate_runs!(idx)          = runs.slice!(idx..)

#union(other)

Alias for #|.

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1100

alias union :|

#valid_string

Returns the IMAP sequence-set string representation, or raises a DataFormatError when the set is empty.

Use #string to return nil or #to_s to return an empty string without error.

Related: #string, #normalized_string, #to_s

Raises:

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 610

def valid_string
  raise DataFormatError, "empty sequence-set" if empty?
  string
end

#validate

This method is for internal use only.

Unstable API: currently for internal use only (Net::IMAP#validate_data)

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 2078

def validate # :nodoc:
  empty? and raise DataFormatError, "empty sequence-set is invalid"
  self
end

#xor(other)

Alias for #^.

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1177

alias xor :^

#xor!(other)

In-place set #xor. Adds any numbers in other that are missing from this set, removes any numbers in other that are already in this set, and returns self.

other can be any object that would be accepted by .new.

set = Net::IMAP::SequenceSet.new(1..5)
set.xor! [2, 4, 6]
set #=> Net::IMAP::SequenceSet["1,3,5:6"]

Related: #xor, #merge, #subtract

[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1928

def xor!(other)
  modifying! # short-circuit before processing input
  other = SequenceSet.new(other)
  copy  = dup
  merge(other).subtract(other.subtract(copy.complement!))
end

#+(other) ⇒ sequence set #|(other) ⇒ sequence set #union(other) ⇒ sequence set
Also known as: #+, #union

Returns a new sequence set that has every number in the other object added.

other may be any object that would be accepted by .new.

Net::IMAP::SequenceSet["1:5"] | 2 | [4..6, 99]
#=> Net::IMAP::SequenceSet["1:6,99"]

Related: #add, #merge, #&, #-, #^, #~

Set identities

lhs | rhs is equivalent to:

  • rhs | lhs (commutative)
  • ~(~lhs & ~rhs) (De Morgan's Law)
  • (lhs & rhs) ^ (lhs ^ rhs)
[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1098

def |(other) remain_frozen dup.merge other end

#~(self) ⇒ sequence set #complement ⇒ sequence set
Also known as: #complement

Returns the complement of self, a SequenceSet which contains all numbers except for those in this set.

~Net::IMAP::SequenceSet.full  #=> Net::IMAP::SequenceSet.empty
~Net::IMAP::SequenceSet.empty #=> Net::IMAP::SequenceSet.full
~Net::IMAP::SequenceSet["1:5,100:222"]
#=> Net::IMAP::SequenceSet["6:99,223:*"]
~Net::IMAP::SequenceSet["6:99,223:*"]
#=> Net::IMAP::SequenceSet["1:5,100:222"]

Related: #complement!, #|, #&, #-, #^

Set identities

~set is equivalent to:

  • full - set, where "full" is .full
[ GitHub ]

  
# File 'lib/net/imap/sequence_set.rb', line 1199

def ~; remain_frozen dup.complement! end