diff --git a/lib/net/imap/sequence_set.rb b/lib/net/imap/sequence_set.rb
index a4b038e2..2fd5b695 100644
--- a/lib/net/imap/sequence_set.rb
+++ b/lib/net/imap/sequence_set.rb
@@ -248,6 +248,10 @@ class IMAP
# +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 value.
# - #limit: Returns a copy of +self+ which has replaced * with a
# given maximum value and removed all members over that maximum.
#
@@ -1285,6 +1289,58 @@ def slice_range(range)
public
+ # 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: #above, #-, #&
+ def above(num)
+ NumValidator.valid_nz_number?(num) or
+ raise ArgumentError, "not a valid sequence set number"
+ difference(..num)
+ end
+
+ # 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 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, #-, #&, #limit
+ def below(num)
+ NumValidator.valid_nz_number?(num) or
+ raise ArgumentError, "not a valid sequence set number"
+ difference(num..)
+ end
+
# Returns a frozen SequenceSet with * converted to +max+, numbers
# and ranges over +max+ removed, and ranges containing +max+ converted to
# end at +max+.
diff --git a/test/net/imap/test_sequence_set.rb b/test/net/imap/test_sequence_set.rb
index 40cbdc9c..5983a3ca 100644
--- a/test/net/imap/test_sequence_set.rb
+++ b/test/net/imap/test_sequence_set.rb
@@ -94,6 +94,8 @@ def compare_to_reference_set(nums, set, seqset)
data "#xor", {transform: ->{ _1 ^ (1..100) }, }
data "#complement", {transform: ->{ ~_1 }, }
data "#normalize", {transform: ->{ _1.normalize }, }
+ data "#above", {transform: ->{ _1.above(22) }, }
+ data "#below", {transform: ->{ _1.below(22) }, }
data "#limit", {transform: ->{ _1.limit(max: 22) }, freeze: :always }
data "#limit => empty", {transform: ->{ _1.limit(max: 1) }, freeze: :always }
test "transforms keep frozen status" do |data|
@@ -387,6 +389,38 @@ def obj.to_sequence_set; 192_168.001_255 end
assert_equal 0, SequenceSet["*,1"].find_ordered_index(-1)
end
+ test "#above" do
+ set = SequenceSet["5,10:22,50"]
+ assert_equal SequenceSet.empty, set.above(2**32 - 1)
+ assert_equal SequenceSet.empty, set.above(99)
+ assert_equal SequenceSet.empty, set.above(50)
+ assert_equal SequenceSet["50"], set.above(40)
+ assert_equal SequenceSet["50"], set.above(30)
+ assert_equal SequenceSet["21:22,50"], set.above(20)
+ assert_equal SequenceSet["11:22,50"], set.above(10)
+ assert_equal SequenceSet["5,10:22,50"], set.above(1)
+ assert_raise ArgumentError do set.above(2**32) end
+ assert_raise ArgumentError do set.above(0) end
+ assert_raise ArgumentError do set.above(-1) end
+ assert_raise ArgumentError do set.above(:*) end
+ end
+
+ test "#below" do
+ set = SequenceSet["5,10:22,50"]
+ assert_equal SequenceSet["5,10:22,50"], set.below(99)
+ assert_equal SequenceSet["5,10:22"], set.below(50)
+ assert_equal SequenceSet["5,10:22"], set.below(40)
+ assert_equal SequenceSet["5,10:22"], set.below(30)
+ assert_equal SequenceSet["5,10:19"], set.below(20)
+ assert_equal SequenceSet["5"], set.below(10)
+ assert_equal SequenceSet.empty, set.below(1)
+ assert_equal SequenceSet.empty, set.below(1)
+ assert_raise ArgumentError do set.below(2**32) end
+ assert_raise ArgumentError do set.below(0) end
+ assert_raise ArgumentError do set.below(-1) end
+ assert_raise ArgumentError do set.below(:*) end
+ end
+
test "#limit" do
set = SequenceSet["1:100,500"]
assert_equal [1..99], set.limit(max: 99).ranges