Skip to content

Commit ed38612

Browse files
committed
✨ Add `SequenceSet#xor!" for in-place XOR
This is simply a mutating version of `#^` or `#xor`. Although there is room for big performance improvement here, the algorithm remains unchanged, so any performance improvement is small.
1 parent 1fc7a52 commit ed38612

File tree

4 files changed

+27
-3
lines changed

4 files changed

+27
-3
lines changed

benchmarks/sequence_set-and.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,12 @@ prelude: |
3434
subtract dup.subtract(SequenceSet.new(other))
3535
end
3636
37-
# TODO: add this as a public method
3837
def xor!(other) # :nodoc:
3938
modifying!
4039
copy = dup
4140
other = SequenceSet.new(other)
4241
merge(other).subtract(other.subtract(copy.complement!))
43-
end
42+
end unless instance_methods.include?(:xor!)
4443
4544
# L - (L ^ R)
4645
def and2!(other)

benchmarks/sequence_set-xor.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ prelude: |
7070
7171
benchmark:
7272
" L ^ R": l, r = sets; l ^ r
73+
" L.xor! R": l, r = sets; l.xor! r
7374
" (L | R) - (R & L)": l, r = sets; (l | r) - (r & l)
7475
"0.5.8 (L | R) - (R & L)": l, r = sets; l.xor0 r
7576
"dup1 (L | R) - (R & L)": l, r = sets; l.xor1 r

lib/net/imap/sequence_set.rb

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,8 @@ class IMAP
345345
# this set; returns +self+.
346346
# - #complement!: In-place set #complement. Replaces the contents of this
347347
# set with its own #complement; returns +self+.
348+
# - #xor!: In-place +XOR+ operation. Adds numbers that are unique to the
349+
# other set and removes numbers that are common to both; returns +self+.
348350
#
349351
# <i>Order preserving:</i>
350352
#
@@ -902,7 +904,7 @@ def &(other) remain_frozen dup.intersect! other end
902904
# * <tt>(lhs | rhs) - (lhs & rhs)</tt>
903905
# * <tt>(lhs - rhs) | (rhs - lhs)</tt>
904906
# * <tt>(lhs ^ other) ^ (other ^ rhs)</tt>
905-
def ^(other) remain_frozen (dup | other).subtract(self & other) end
907+
def ^(other) remain_frozen dup.xor! other end
906908
alias xor :^
907909

908910
# :call-seq:
@@ -1629,6 +1631,24 @@ def intersect!(other)
16291631
subtract SequenceSet.new(other).complement!
16301632
end
16311633

1634+
# In-place set #xor. Adds any numbers in +other+ that are missing from
1635+
# this set, removes any numbers in +other+ that are already in this set,
1636+
# and returns +self+.
1637+
#
1638+
# +other+ can be any object that would be accepted by ::new.
1639+
#
1640+
# set = Net::IMAP::SequenceSet.new(1..5)
1641+
# set.xor! [2, 4, 6]
1642+
# set #=> Net::IMAP::SequenceSet["1,3,5:6"]
1643+
#
1644+
# Related: #xor, #merge, #subtract
1645+
def xor!(other)
1646+
modifying!
1647+
other = IMAP::SequenceSet(other)
1648+
both = self & other
1649+
merge(other).subtract(both)
1650+
end
1651+
16321652
# Returns a new SequenceSet with a normalized string representation.
16331653
#
16341654
# The returned set's #string is sorted and deduplicated. Adjacent or

test/net/imap/test_sequence_set.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,9 @@ def compare_to_reference_set(nums, set, seqset)
121121
assert_equal xor, (lhs | rhs) - (lhs & rhs)
122122
assert_equal xor, (lhs ^ mid) ^ (mid ^ rhs)
123123
assert_equal xor, ~lhs ^ ~rhs
124+
mutable = lhs.dup
125+
assert_equal xor, mutable.xor!(rhs)
126+
assert_equal xor, mutable
124127
end
125128
end
126129

@@ -172,6 +175,7 @@ def compare_to_reference_set(nums, set, seqset)
172175
data "#subtract", ->{ _1.subtract 1 }
173176
data "#limit!", ->{ _1.limit! max: 10 }
174177
data "#intersect!", ->{ _1.intersect! SequenceSet[1..100] }
178+
data "#xor!", ->{ _1.xor! SequenceSet[9..98] }
175179
data "#complement!", :complement!
176180
data "#normalize!", :normalize!
177181
test "frozen error message" do |modification|

0 commit comments

Comments
 (0)