Skip to content

Commit

Permalink
✨ Add SequenceSet#find_ordered_index
Browse files Browse the repository at this point in the history
This is the ordered entries version of `#find_index`.
  • Loading branch information
nevans committed Feb 3, 2025
1 parent 97d3a21 commit 5e69ce1
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 9 deletions.
37 changes: 28 additions & 9 deletions lib/net/imap/sequence_set.rb
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,10 @@ class IMAP
# - #at: Returns the number at a given offset in the sorted set.
# - #find_index: Returns the given number's offset in the sorted set.
#
# <i>Accessing value by offset in ordered entries</i>
# - #find_ordered_index: Returns the index of the given number's first
# occurrence in entries.
#
# <i>Set cardinality:</i>
# - #count (aliased as #size): Returns the count of numbers in the set.
# Duplicated numbers are not counted.
Expand Down Expand Up @@ -1086,30 +1090,45 @@ def has_duplicates?
# Returns the (sorted and deduplicated) index of +number+ in the set, or
# +nil+ if +number+ isn't in the set.
#
# Related: #[], #at
# Related: #[], #at, #find_ordered_index
def find_index(number)
number = to_tuple_int number
each_tuple_with_index do |min, max, idx_min|
each_tuple_with_index(@tuples) do |min, max, idx_min|
number < min and return nil
number <= max and return from_tuple_int(idx_min + (number - min))
end
nil
end

# Returns the first index of +number+ in the ordered #entries, or
# +nil+ if +number+ isn't in the set.
#
# Related: #find_index
def find_ordered_index(number)
number = to_tuple_int number
each_tuple_with_index(each_entry_tuple) do |min, max, idx_min|
if min <= number && number <= max
return from_tuple_int(idx_min + (number - min))
end
end
nil
end

private

def each_tuple_with_index
def each_tuple_with_index(tuples)
idx_min = 0
@tuples.each do |min, max|
yield min, max, idx_min, (idx_max = idx_min + (max - min))
tuples.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

def reverse_each_tuple_with_index
def reverse_each_tuple_with_index(tuples)
idx_max = -1
@tuples.reverse_each do |min, max|
tuples.reverse_each do |min, max|
yield min, max, (idx_min = idx_max - (max - min)), idx_max
idx_max = idx_min - 1
end
Expand All @@ -1130,11 +1149,11 @@ def reverse_each_tuple_with_index
def at(index)
index = Integer(index.to_int)
if index.negative?
reverse_each_tuple_with_index do |min, max, idx_min, idx_max|
reverse_each_tuple_with_index(@tuples) do |min, max, idx_min, idx_max|
idx_min <= index and return from_tuple_int(min + (index - idx_min))
end
else
each_tuple_with_index do |min, _, idx_min, idx_max|
each_tuple_with_index(@tuples) do |min, _, idx_min, idx_max|
index <= idx_max and return from_tuple_int(min + (index - idx_min))
end
end
Expand Down
38 changes: 38 additions & 0 deletions test/net/imap/test_sequence_set.rb
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,44 @@ def obj.to_sequence_set; 192_168.001_255 end
assert_equal 2**32 - 1, SequenceSet.full.find_index(:*)
end

test "#find_ordered_index" do
assert_equal 9, SequenceSet.full.find_ordered_index(10)
assert_equal 99, SequenceSet.full.find_ordered_index(100)
assert_equal 2**32 - 1, SequenceSet.full.find_ordered_index(:*)
assert_nil SequenceSet.empty.find_index(1)
set = SequenceSet["9,8,7,6,5,4,3,2,1"]
assert_equal 0, set.find_ordered_index(9)
assert_equal 1, set.find_ordered_index(8)
assert_equal 2, set.find_ordered_index(7)
assert_equal 3, set.find_ordered_index(6)
assert_equal 4, set.find_ordered_index(5)
assert_equal 5, set.find_ordered_index(4)
assert_equal 6, set.find_ordered_index(3)
assert_equal 7, set.find_ordered_index(2)
assert_equal 8, set.find_ordered_index(1)
assert_nil set.find_ordered_index(10)
set = SequenceSet["7:9,5:6"]
assert_equal 0, set.find_ordered_index(7)
assert_equal 1, set.find_ordered_index(8)
assert_equal 2, set.find_ordered_index(9)
assert_equal 3, set.find_ordered_index(5)
assert_equal 4, set.find_ordered_index(6)
assert_nil set.find_ordered_index(4)
set = SequenceSet["1000:1111,1:100"]
assert_equal 0, set.find_ordered_index(1000)
assert_equal 100, set.find_ordered_index(1100)
assert_equal 112, set.find_ordered_index(1)
assert_equal 121, set.find_ordered_index(10)
set = SequenceSet["1,1,1,1,51,50,4,11"]
assert_equal 0, set.find_ordered_index(1)
assert_equal 4, set.find_ordered_index(51)
assert_equal 5, set.find_ordered_index(50)
assert_equal 6, set.find_ordered_index(4)
assert_equal 7, set.find_ordered_index(11)
assert_equal 1, SequenceSet["1,*"].find_ordered_index(-1)
assert_equal 0, SequenceSet["*,1"].find_ordered_index(-1)
end

test "#limit" do
set = SequenceSet["1:100,500"]
assert_equal [1..99], set.limit(max: 99).ranges
Expand Down

0 comments on commit 5e69ce1

Please sign in to comment.