diff --git a/bench_test.go b/bench_test.go index 6c08a83..4f11354 100755 --- a/bench_test.go +++ b/bench_test.go @@ -258,26 +258,47 @@ func BenchmarkXor(b *testing.B) { } func BenchmarkBitSet_Xor(b *testing.B) { - small1, small2 := New(1, 2, 3, 4, 5), New(3, 4, 5, 6, 7) - large1, large2 := New(), New() - for i := range 10000 { - if i%2 == 0 { - large1.Add(i) + b.Run("empty", func(b *testing.B) { + s1, s2 := New(), New() + for b.Loop() { + s1.Xor(s2) } - if i%3 == 0 { - large2.Add(i) + }) + + b.Run("5", func(b *testing.B) { + s1, s2 := New(1, 2, 3, 4, 5), New(3, 4, 5, 6, 7) + for b.Loop() { + s1.Xor(s2) } - } + }) - b.Run("small sets", func(b *testing.B) { + b.Run("10k", func(b *testing.B) { + s1, s2 := New(), New() + for i := range 10_000 { + switch { + case i%2 == 0: + s1.Add(i) + case i%3 == 0: + s2.Add(i) + } + } for b.Loop() { - small1.Xor(small2) + s1.Xor(s2) } }) - b.Run("large sets", func(b *testing.B) { + b.Run("1m", func(b *testing.B) { + s1, s2 := New(), New() + for i := range 1_000_000 { + switch { + case i%2 == 0: + s1.Add(i) + case i%3 == 0: + s2.Add(i) + } + } for b.Loop() { - large1.Xor(large2) + s1.Xor(s2) } }) } diff --git a/bitset.go b/bitset.go index 10403dc..d2a90c8 100755 --- a/bitset.go +++ b/bitset.go @@ -393,7 +393,7 @@ func Or(s1, s2 BitSet) BitSet { return BitSet{} } if otherLen == 0 { // s2 is empty, return a copy of s1 with trailing zeros removed - var last = bsLen - 1 + last := bsLen - 1 for last >= 0 && s1[last] == 0 { last-- } @@ -468,8 +468,27 @@ func (bs *BitSet) Xor(other BitSet) { if len(other) > len(*bs) { bs.resize(len(other)) } - for i := 0; i < len(other); i++ { - (*bs)[i] ^= other[i] + if len(other) < 8 { + for i := range other { + (*bs)[i] ^= other[i] + } + bs.trim() + return + } + + b := *bs + for ; len(other) > 7; b, other = b[8:], other[8:] { + (b)[0] ^= other[0] + (b)[1] ^= other[1] + (b)[2] ^= other[2] + (b)[3] ^= other[3] + (b)[4] ^= other[4] + (b)[5] ^= other[5] + (b)[6] ^= other[6] + (b)[7] ^= other[7] + } + for i := range other { + b[i] ^= other[i] } bs.trim() } diff --git a/bitset_test.go b/bitset_test.go index bc17c4a..b7564e1 100755 --- a/bitset_test.go +++ b/bitset_test.go @@ -258,10 +258,7 @@ func TestBitSet_Visit(t *testing.T) { count := 0 aborted := bs.Visit(func(n int) bool { count++ - if n == 1 { - return true - } - return false + return n == 1 }) require.True(t, aborted) require.Equal(t, 1, count) @@ -539,30 +536,6 @@ func TestOr(t *testing.T) { } } -func TestBitSet_Or(t *testing.T) { - tests := []struct { - name string - a, b BitSet - expect string - }{ - {"both empty", New(), New(), "{}"}, - {"a empty", New(), New(1), "{1}"}, - {"b empty", New(1), New(), "{1}"}, - {"same", New(1), New(1), "{1}"}, - {"no overlap", New(1), New(2), "{1 2}"}, - {"partial overlap", New(1, 2), New(2, 3), "{1..3}"}, - {"large", New(100, 200), New(200, 300), "{100 200 300}"}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - res := &tt.a - res.Or(tt.b) - require.Equal(t, tt.expect, res.String()) - }) - } -} - func TestXor(t *testing.T) { tests := []struct { name string @@ -572,40 +545,29 @@ func TestXor(t *testing.T) { {"both empty", New(), New(), "{}"}, {"a empty", New(), New(1), "{1}"}, {"b empty", New(1), New(), "{1}"}, - {"same", New(1), New(1), "{}"}, + {"equal", New(1), New(1), "{}"}, + {"2 elems equal", New(1, 2), New(1, 2), "{}"}, {"no overlap", New(1), New(2), "{1 2}"}, {"partial overlap", New(1, 2), New(2, 3), "{1 3}"}, - {"large", New(100, 200), New(200, 300), "{100 300}"}, + {"partial overlap trailing zero", New(1, 2, 0), New(2, 3, 0), "{1 3}"}, + {"partial overlap hundrets", New(100, 200), New(200, 300), "{100 300}"}, + { + "20 elems no overlap", + New(1, 100, 200, 300, 400, 500, 600, 700, 800, 900), + New(2, 101, 202, 303, 404, 505, 606, 707, 808, 909), + "{1 2 100 101 200 202 300 303 400 404 500 505 600 606 " + + "700 707 800 808 900 909}", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { res := Xor(tt.a, tt.b) require.Equal(t, tt.expect, res.String()) - }) - } -} -func TestBitSet_Xor(t *testing.T) { - tests := []struct { - name string - a, b BitSet - expect string - }{ - {"both empty", New(), New(), "{}"}, - {"a empty", New(), New(1), "{1}"}, - {"b empty", New(1), New(), "{1}"}, - {"same", New(1), New(1), "{}"}, - {"no overlap", New(1), New(2), "{1 2}"}, - {"partial overlap", New(1, 2), New(2, 3), "{1 3}"}, - {"large", New(100, 200), New(200, 300), "{100 300}"}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - res := &tt.a - res.Xor(tt.b) - require.Equal(t, tt.expect, res.String()) + cp := tt.a.Copy() + cp.Xor(tt.b) + require.Equal(t, tt.expect, cp.String()) }) } }