commit 831b48a3ddfb468a1e9a08dc2855f40de200cf6c Author: upstream Date: Fri Sep 4 09:13:33 2026 +0200 Upstream kelindar/bitmap state of the touched files before aa74fd8e7b diff --git a/bitmap_amd64.go b/bitmap_amd64.go new file mode 100644 index 0000000..1573905 --- /dev/null +++ b/bitmap_amd64.go @@ -0,0 +1,146 @@ +// Copyright (c) Roman Atachiants and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for details. + +//go:build !noasm && amd64 + +package bitmap + +import "unsafe" + +// And computes the intersection between two bitmaps and stores the result in the current bitmap +func (dst *Bitmap) And(other Bitmap, extra ...Bitmap) { + max := minlen(*dst, other, extra) + dst.shrink(max) + if max == 0 { + return + } + + switch hardware { + case isAccelerated: + switch len(extra) { + case 0: + _and(unsafe.Pointer(&(*dst)[0]), unsafe.Pointer(&other[0]), uint64(max)) + default: + vx, _ := pointersOf(other, extra) + _and_many(unsafe.Pointer(&(*dst)[0]), vx, dimensionsOf(max, len(extra)+1)) + } + case isAVX512: + switch len(extra) { + case 0: + _and_avx512(unsafe.Pointer(&(*dst)[0]), unsafe.Pointer(&other[0]), uint64(max)) + default: + vx, _ := pointersOf(other, extra) + _and_many_avx512(unsafe.Pointer(&(*dst)[0]), vx, dimensionsOf(max, len(extra)+1)) + } + default: + and(*dst, max, other, extra) + return + } +} + +// AndNot computes the difference between two bitmaps and stores the result in the current bitmap. +// Operation works as set subtract: dst - b +func (dst *Bitmap) AndNot(other Bitmap, extra ...Bitmap) { + max := minlen(*dst, other, extra) + if max == 0 { + return + } + + switch hardware { + case isAccelerated: + switch len(extra) { + case 0: + _andn(unsafe.Pointer(&(*dst)[0]), unsafe.Pointer(&other[0]), uint64(max)) + default: + vx, _ := pointersOf(other, extra) + _andn_many(unsafe.Pointer(&(*dst)[0]), vx, dimensionsOf(max, len(extra)+1)) + } + case isAVX512: + switch len(extra) { + case 0: + _andn_avx512(unsafe.Pointer(&(*dst)[0]), unsafe.Pointer(&other[0]), uint64(max)) + default: + vx, _ := pointersOf(other, extra) + _andn_many_avx512(unsafe.Pointer(&(*dst)[0]), vx, dimensionsOf(max, len(extra)+1)) + } + default: + andn(*dst, max, other, extra) + return + } +} + +// Or computes the union between two bitmaps and stores the result in the current bitmap +func (dst *Bitmap) Or(other Bitmap, extra ...Bitmap) { + max := maxlen(*dst, other, extra) + if max == 0 { + return + } + + dst.grow(max - 1) + switch hardware { + case isAccelerated: + switch len(extra) { + case 0: + _or(unsafe.Pointer(&(*dst)[0]), unsafe.Pointer(&other[0]), uint64(len(other))) + default: + vx, max := pointersOf(other, extra) + _or_many(unsafe.Pointer(&(*dst)[0]), vx, dimensionsOf(max, len(extra)+1)) + } + case isAVX512: + switch len(extra) { + case 0: + _or_avx512(unsafe.Pointer(&(*dst)[0]), unsafe.Pointer(&other[0]), uint64(len(other))) + default: + vx, max := pointersOf(other, extra) + _or_many_avx512(unsafe.Pointer(&(*dst)[0]), vx, dimensionsOf(max, len(extra)+1)) + } + default: + or(*dst, other, extra) + } +} + +// Xor computes the symmetric difference between two bitmaps and stores the result in the current bitmap +func (dst *Bitmap) Xor(other Bitmap, extra ...Bitmap) { + max := maxlen(*dst, other, extra) + if max == 0 { + return + } + + dst.grow(max - 1) + switch hardware { + case isAccelerated: + switch len(extra) { + case 0: + _xor(unsafe.Pointer(&(*dst)[0]), unsafe.Pointer(&other[0]), uint64(len(other))) + default: + vx, max := pointersOf(other, extra) + _xor_many(unsafe.Pointer(&(*dst)[0]), vx, dimensionsOf(max, len(extra)+1)) + } + case isAVX512: + switch len(extra) { + case 0: + _xor_avx512(unsafe.Pointer(&(*dst)[0]), unsafe.Pointer(&other[0]), uint64(len(other))) + default: + vx, max := pointersOf(other, extra) + _xor_many_avx512(unsafe.Pointer(&(*dst)[0]), vx, dimensionsOf(max, len(extra)+1)) + } + default: + xor(*dst, other, extra) + } +} + +// Count returns the number of elements in this bitmap +func (dst Bitmap) Count() int { + if len(dst) == 0 { + return 0 + } + + switch hardware { + case isAccelerated: + var res uint64 + _count(unsafe.Pointer(&dst[0]), uint64(len(dst)), unsafe.Pointer(&res)) + return int(res) + default: + return count(dst) + } +} diff --git a/bitmap_arm64.go b/bitmap_arm64.go new file mode 100644 index 0000000..694c52d --- /dev/null +++ b/bitmap_arm64.go @@ -0,0 +1,114 @@ +// Copyright (c) Roman Atachiants and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for details. + +//go:build !noasm && arm64 + +package bitmap + +import "unsafe" + +// And computes the intersection between two bitmaps and stores the result in the current bitmap +func (dst *Bitmap) And(other Bitmap, extra ...Bitmap) { + max := minlen(*dst, other, extra) + dst.shrink(max) + if max == 0 { + return + } + + switch hardware { + case isAccelerated: + switch len(extra) { + case 0: + _and(unsafe.Pointer(&(*dst)[0]), unsafe.Pointer(&other[0]), uint64(max)) + default: + vx, _ := pointersOf(other, extra) + _and_many(unsafe.Pointer(&(*dst)[0]), vx, dimensionsOf(max, len(extra)+1)) + } + default: + and(*dst, max, other, extra) + return + } +} + +// AndNot computes the difference between two bitmaps and stores the result in the current bitmap. +// Operation works as set subtract: dst - b +func (dst *Bitmap) AndNot(other Bitmap, extra ...Bitmap) { + max := minlen(*dst, other, extra) + if max == 0 { + return + } + + switch hardware { + case isAccelerated: + switch len(extra) { + case 0: + _andn(unsafe.Pointer(&(*dst)[0]), unsafe.Pointer(&other[0]), uint64(max)) + default: + vx, _ := pointersOf(other, extra) + _andn_many(unsafe.Pointer(&(*dst)[0]), vx, dimensionsOf(max, len(extra)+1)) + } + default: + andn(*dst, max, other, extra) + return + } +} + +// Or computes the union between two bitmaps and stores the result in the current bitmap +func (dst *Bitmap) Or(other Bitmap, extra ...Bitmap) { + max := maxlen(*dst, other, extra) + if max == 0 { + return + } + + dst.grow(max - 1) + switch hardware { + case isAccelerated: + switch len(extra) { + case 0: + _or(unsafe.Pointer(&(*dst)[0]), unsafe.Pointer(&other[0]), uint64(len(other))) + default: + vx, max := pointersOf(other, extra) + _or_many(unsafe.Pointer(&(*dst)[0]), vx, dimensionsOf(max, len(extra)+1)) + } + default: + or(*dst, other, extra) + } +} + +// Xor computes the symmetric difference between two bitmaps and stores the result in the current bitmap +func (dst *Bitmap) Xor(other Bitmap, extra ...Bitmap) { + max := maxlen(*dst, other, extra) + if max == 0 { + return + } + + dst.grow(max - 1) + switch hardware { + case isAccelerated: + switch len(extra) { + case 0: + _xor(unsafe.Pointer(&(*dst)[0]), unsafe.Pointer(&other[0]), uint64(len(other))) + default: + vx, max := pointersOf(other, extra) + _xor_many(unsafe.Pointer(&(*dst)[0]), vx, dimensionsOf(max, len(extra)+1)) + } + default: + xor(*dst, other, extra) + } +} + +// Count returns the number of elements in this bitmap +func (dst Bitmap) Count() int { + if len(dst) == 0 { + return 0 + } + + switch hardware { + case isAccelerated: + var res uint64 + _count(unsafe.Pointer(&dst[0]), uint64(len(dst)), unsafe.Pointer(&res)) + return int(res) + default: + return count(dst) + } +} diff --git a/bitmap_test.go b/bitmap_test.go new file mode 100644 index 0000000..14ab9db --- /dev/null +++ b/bitmap_test.go @@ -0,0 +1,631 @@ +// Copyright (c) Roman Atachiants and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for details. + +package bitmap + +import ( + "fmt" + "math" + "strconv" + "testing" + + "github.com/stretchr/testify/assert" +) + +/* +cpu: 13th Gen Intel(R) Core(TM) i7-13700K +BenchmarkBitmap/set-24 655739137 1.803 ns/op 0 B/op 0 allocs/op +BenchmarkBitmap/remove-24 1000000000 1.107 ns/op 0 B/op 0 allocs/op +BenchmarkBitmap/contains-24 1000000000 0.8975 ns/op 0 B/op 0 allocs/op +BenchmarkBitmap/clear-24 827574 1487 ns/op 0 B/op 0 allocs/op +BenchmarkBitmap/ones-24 571444 2088 ns/op 0 B/op 0 allocs/op +BenchmarkBitmap/min-24 979591036 1.252 ns/op 0 B/op 0 allocs/op +BenchmarkBitmap/max-24 944884120 1.229 ns/op 0 B/op 0 allocs/op +BenchmarkBitmap/min-zero-24 991736356 1.258 ns/op 0 B/op 0 allocs/op +BenchmarkBitmap/max-zero-24 1000000000 1.157 ns/op 0 B/op 0 allocs/op +BenchmarkBitmap/count-24 393440 3086 ns/op 0 B/op 0 allocs/op +BenchmarkBitmap/count-to-24 58537441 20.20 ns/op 0 B/op 0 allocs/op +BenchmarkBitmap/clone-24 648651 1875 ns/op 0 B/op 0 allocs/op +BenchmarkBitmap/and-24 685710 1733 ns/op 0 B/op 0 allocs/op +BenchmarkBitmap/andnot-24 705882 1709 ns/op 0 B/op 0 allocs/op +BenchmarkBitmap/or-24 705894 1702 ns/op 0 B/op 0 allocs/op +BenchmarkBitmap/xor-24 705919 1721 ns/op 0 B/op 0 allocs/op +*/ +func BenchmarkBitmap(b *testing.B) { + other := make(Bitmap, 1000000/64) + other.Set(1000000) + + run(b, "set", func(index Bitmap) { + index.Set(5000) + }) + + run(b, "remove", func(index Bitmap) { + index.Remove(5000) + }) + + run(b, "contains", func(index Bitmap) { + index.Contains(5000) + }) + + run(b, "clear", func(index Bitmap) { + index.Clear() + }) + + run(b, "ones", func(index Bitmap) { + index.Ones() + }) + + run(b, "min", func(index Bitmap) { + index.Min() + }) + + run(b, "max", func(index Bitmap) { + index.Max() + }) + + run(b, "min-zero", func(index Bitmap) { + index.MinZero() + }) + + run(b, "max-zero", func(index Bitmap) { + index.MaxZero() + }) + + run(b, "count", func(index Bitmap) { + index.Count() + }) + + run(b, "count-to", func(index Bitmap) { + index.CountTo(5001) + }) + + into := make(Bitmap, len(other)) + run(b, "clone", func(index Bitmap) { + index.Clone(&into) + }) + + run(b, "and", func(index Bitmap) { + index.And(other) + }) + + run(b, "andnot", func(index Bitmap) { + index.AndNot(other) + }) + + run(b, "or", func(index Bitmap) { + index.AndNot(other) + }) + + run(b, "xor", func(index Bitmap) { + index.AndNot(other) + }) +} + +/* +cpu: 13th Gen Intel(R) Core(TM) i7-13700K +BenchmarkMany/and4-noasm-24 66297 18139 ns/op 0 B/op 0 allocs/op +BenchmarkMany/and4-naive-24 179106 6803 ns/op 0 B/op 0 allocs/op +BenchmarkMany/and4-batch-24 258091 4679 ns/op 32 B/op 1 allocs/op +*/ +func BenchmarkMany(b *testing.B) { + other := make(Bitmap, 1000000/64) + other.Set(1000000) + + run(b, "and4-noasm", func(index Bitmap) { + max := minlen(index, other, nil) + index.shrink(max) + and(index, max, other, nil) + and(index, max, other, nil) + and(index, max, other, nil) + and(index, max, other, nil) + }) + + run(b, "and4-naive", func(index Bitmap) { + index.And(other) + index.And(other) + index.And(other) + index.And(other) + }) + + run(b, "and4-batch", func(index Bitmap) { + index.And(other, other, other, other) + }) +} + +func TestSetRemove(t *testing.T) { + index := Bitmap{} + for i := uint32(100); i < 200; i++ { + index.Set(i) + assert.True(t, index.Contains(i)) + } + + for i := uint32(150); i < 180; i++ { + index.Remove(i) + assert.False(t, index.Contains(i)) + } +} + +func TestClear(t *testing.T) { + index := Bitmap{} + for i := uint32(0); i < 500; i++ { + index.Set(i) + assert.True(t, index.Contains(i)) + } + + index.Clear() + index.Set(500) + for i := uint32(0); i < 500; i++ { + assert.False(t, index.Contains(i), i) + } + assert.True(t, index.Contains(500)) +} + +func TestAnd(t *testing.T) { + a, b := Bitmap{}, Bitmap{} + for i := uint32(0); i < 100; i += 2 { + a.Set(i) + b.Set(i) + } + + a.And(b) + assert.False(t, a.Contains(1)) + for i := uint32(0); i < 100; i += 2 { + assert.True(t, a.Contains(i)) + } +} + +func TestAndNot(t *testing.T) { + a, b := Bitmap{}, Bitmap{} + for i := uint32(0); i < 100; i += 2 { + a.Set(i) + b.Set(i) + } + + a.AndNot(b) + assert.False(t, a.Contains(1)) + for i := uint32(0); i < 100; i += 2 { + assert.False(t, a.Contains(i)) + } +} + +func TestAndNot_TheSameBitmap(t *testing.T) { + var a Bitmap + for i := uint32(0); i < 100; i += 2 { + a.Set(i) + } + + a.AndNot(a) + + for i := uint32(0); i < 100; i++ { + assert.Equal(t, false, a.Contains(i), "for "+strconv.Itoa(int(i))) + } + assert.Equal(t, 0, a.Count()) +} + +func TestAndNot_DifferentBitmapSizes(t *testing.T) { + var a, b, c, d Bitmap + for i := uint32(0); i < 100; i += 2 { + a.Set(i) + c.Set(i) + } + + for i := uint32(0); i < 200; i += 2 { + b.Set(i) + d.Set(i) + } + a.AndNot(b) + d.AndNot(c) + + for i := uint32(0); i < 100; i++ { + assert.Equal(t, false, a.Contains(i), "for "+strconv.Itoa(int(i))) + assert.Equal(t, false, d.Contains(i), "for "+strconv.Itoa(int(i))) + } + for i := uint32(100); i < 200; i++ { + assert.Equal(t, b.Contains(i), d.Contains(i), "for "+strconv.Itoa(int(i))) + } + assert.Equal(t, 0, a.Count()) + assert.Equal(t, 50, d.Count()) +} + +func TestOr(t *testing.T) { + a, b := Bitmap{}, Bitmap{} + for i := uint32(0); i < 100; i += 2 { + b.Set(i) + } + + a.Or(b) + assert.False(t, a.Contains(1)) + for i := uint32(0); i < 100; i += 2 { + assert.True(t, a.Contains(i)) + } +} + +func TestOr_DifferentBitmapSizes(t *testing.T) { + var a, b, c, d Bitmap + for i := uint32(0); i < 100; i += 2 { + a.Set(i) + c.Set(i) + } + + for i := uint32(0); i < 200; i += 2 { + b.Set(i) + d.Set(i) + } + a.Or(b) + d.Or(c) + + for i := uint32(0); i < 200; i++ { + assert.Equal(t, d.Contains(i), a.Contains(i), "for "+strconv.Itoa(int(i))) + } + assert.Equal(t, 100, a.Count()) + assert.Equal(t, 100, d.Count()) +} + +func TestXor(t *testing.T) { + a, b := Bitmap{}, Bitmap{} + for i := uint32(0); i < 100; i += 2 { + b.Set(i) + } + + a.Xor(b) + assert.False(t, a.Contains(1)) + for i := uint32(0); i < 100; i += 2 { + assert.True(t, a.Contains(i)) + } +} + +func TestXOr_DifferentBitmapSizes(t *testing.T) { + var a, b, c, d Bitmap + for i := uint32(0); i < 100; i += 2 { + a.Set(i) + c.Set(i) + } + + for i := uint32(0); i < 200; i += 2 { + b.Set(i) + d.Set(i) + } + a.Xor(b) + d.Xor(c) + + for i := uint32(0); i < 200; i++ { + assert.Equal(t, d.Contains(i), a.Contains(i), "for "+strconv.Itoa(int(i))) + } + assert.Equal(t, 50, a.Count()) + assert.Equal(t, 50, d.Count()) +} + +func TestMin(t *testing.T) { + { + a := Bitmap{0x0, 0x0, 0xffffffffffffff00} + v, ok := a.Min() + assert.True(t, ok) + assert.Equal(t, 64+64+8, int(v)) + assert.False(t, a.Contains(v-1)) + assert.True(t, a.Contains(v)) + } + + { + a := Bitmap{0x0, 0x0} + v, ok := a.Min() + assert.False(t, ok) + assert.Equal(t, 0, int(v)) + } +} + +func TestMax(t *testing.T) { + { + a := Bitmap{0x0, 0x0, 0x00000000000000f0} + v, ok := a.Max() + assert.True(t, ok) + assert.Equal(t, 64+64+7, int(v)) + + assert.False(t, a.Contains(v-4)) + assert.True(t, a.Contains(v-3)) + assert.True(t, a.Contains(v-2)) + assert.True(t, a.Contains(v-1)) + assert.True(t, a.Contains(v)) + assert.False(t, a.Contains(v+1)) + assert.False(t, a.Contains(v+2)) + } + + { + a := Bitmap{0x0, 0x0} + v, ok := a.Max() + assert.False(t, ok) + assert.Equal(t, 0, int(v)) + } +} + +func TestMinZero(t *testing.T) { + { + a := Bitmap{0xffffffffffffffff, 0xffffffffffffffff, 0xf0ffffffffffff0f} + v, ok := a.MinZero() + assert.True(t, ok) + assert.Equal(t, 64+64+4, int(v)) + assert.False(t, a.Contains(v)) + } + + { + a := Bitmap{0xffffffffffffffff, 0xffffffffffffffff} + v, ok := a.MinZero() + assert.False(t, ok) + assert.Equal(t, 0, int(v)) + } +} + +func TestMaxZero(t *testing.T) { + { + a := Bitmap{0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffff0f} + v, ok := a.MaxZero() + assert.True(t, ok) + assert.Equal(t, 64+64+7, int(v)) + assert.False(t, a.Contains(v)) + } + + { + a := Bitmap{0xffffffffffffffff, 0xffffffffffffffff} + v, ok := a.MaxZero() + assert.False(t, ok) + assert.Equal(t, 0, int(v)) + } +} + +func TestCount(t *testing.T) { + a := Bitmap{} + assert.Equal(t, 0, a.Count()) + assert.Equal(t, 0, a.CountTo(math.MaxUint32)) + + b := Bitmap{} + b.Set(1) + b.Set(2) + b.Set(5) + b.Set(6) + b.Set(101) + b.Set(102) + b.Set(105) + b.Set(106) + + assert.Equal(t, 8, b.Count()) + assert.Equal(t, 1, b.CountTo(2)) + assert.Equal(t, 2, b.CountTo(4)) + assert.Equal(t, 4, b.CountTo(100)) + assert.Equal(t, 4, b.CountTo(101)) + assert.Equal(t, 5, b.CountTo(102)) + assert.Equal(t, 8, b.CountTo(128)) + assert.Equal(t, 8, b.CountTo(math.MaxUint32)) + + b.Set(127) + + assert.Equal(t, 9, b.CountTo(128)) +} + +func TestGrow(t *testing.T) { + bitmap := make(Bitmap, 1, 5) + bitmap[0] = 42 + + assert.Equal(t, 1, len(bitmap)) + assert.Equal(t, 5, cap(bitmap)) + assert.Equal(t, Bitmap{42}, bitmap) + + bitmap.grow(0) + assert.Equal(t, 1, len(bitmap)) + assert.Equal(t, 5, cap(bitmap)) + assert.Equal(t, Bitmap{42}, bitmap) + + bitmap.grow(4) + assert.Equal(t, 5, len(bitmap)) + assert.Equal(t, 5, cap(bitmap)) + assert.Equal(t, Bitmap{42, 0, 0, 0, 0}, bitmap) + + bitmap.grow(5) + assert.Equal(t, 6, len(bitmap)) + assert.Equal(t, Bitmap{42, 0, 0, 0, 0, 0}, bitmap) + bitmap.Grow(6) +} + +func TestAnd_DifferentBitmapSizes(t *testing.T) { + var a, b, c, d Bitmap + for i := uint32(0); i < 100; i += 2 { + a.Set(i) + c.Set(i) + } + + for i := uint32(0); i < 200; i += 2 { + b.Set(i) + d.Set(i) + } + + a.And(b) + d.And(c) + + for i := uint32(0); i < 200; i++ { + assert.Equal(t, a.Contains(i), d.Contains(i), "for "+strconv.Itoa(int(i))) + } + assert.Equal(t, 50, a.Count()) + assert.Equal(t, 50, d.Count()) +} + +func TestAnd_ConsecutiveAnd_DifferentBitmapSizes(t *testing.T) { + var a, b, c Bitmap + for i := uint32(0); i < 200; i += 2 { + a.Set(i) + c.Set(i) + } + + for i := uint32(0); i < 100; i += 2 { + b.Set(i) + } + + a.And(b) + a.And(c) + + for i := uint32(0); i < 200; i++ { + assert.Equal(t, a.Contains(i), b.Contains(i), "for "+strconv.Itoa(int(i))) + } + assert.Equal(t, 50, a.Count()) +} + +func TestResizeBitmap(t *testing.T) { + assert.Equal(t, 1, resize(100, 0)) + assert.Equal(t, 2, resize(100, 1)) + assert.Equal(t, 4, resize(100, 2)) + assert.Equal(t, 16, resize(100, 11)) + assert.Equal(t, 256, resize(100, 255)) + assert.Equal(t, 1232, resize(100, 1000)) + assert.Equal(t, 1232, resize(200, 1000)) + assert.Equal(t, 1232, resize(512, 1000)) + assert.Equal(t, 1213, resize(500, 1000)) // Inconsistent + assert.Equal(t, 22504, resize(512, 20000)) + assert.Equal(t, 28322, resize(22504, 22600)) +} + +func TestMinInteger(t *testing.T) { + tests := [][3]int{ + {10, 20, 10}, + {20, 10, 10}, + {0, 10, 0}, + {10, 0, 0}, + {10, 10, 10}, + {10, -10, -10}, + {-10, 10, -10}, + {-10, 0, -10}, + {-10, -10, -10}, + } + + for _, tc := range tests { + assert.Equal(t, tc[2], minint(tc[0], tc[1]), fmt.Sprintf("min(%v, %v)", tc[0], tc[1])) + } +} + +func TestMaxInteger(t *testing.T) { + tests := [][3]int{ + {10, 20, 20}, + {20, 10, 20}, + {0, 10, 10}, + {10, 0, 10}, + {10, 10, 10}, + {10, -10, 10}, + {-10, 10, 10}, + {-10, 0, 0}, + {-10, -10, -10}, + } + + for _, tc := range tests { + assert.Equal(t, tc[2], maxint(tc[0], tc[1]), fmt.Sprintf("max(%v, %v)", tc[0], tc[1])) + } +} + +func TestBatched(t *testing.T) { + const bits = 0b0011 + + // Functions to test + tests := []func(Bitmap) func(Bitmap, ...Bitmap){ + func(b Bitmap) func(Bitmap, ...Bitmap) { + return b.And + }, + func(b Bitmap) func(Bitmap, ...Bitmap) { + return b.AndNot + }, + func(b Bitmap) func(Bitmap, ...Bitmap) { + return b.Or + }, + func(b Bitmap) func(Bitmap, ...Bitmap) { + return b.Xor + }, + } + + for _, withHw := range []int{isAccelerated, isUnsupported} { + for i, tc := range tests { + t.Run(fmt.Sprintf("%v,avx=%v", i, withHw), func(t *testing.T) { + hardware = withHw + naive := func(n int) Bitmap { + input := Bitmap{bits} + tc(input)(Bitmap{bits}) + for i := 0; i < n; i++ { + tc(input)(Bitmap{bits}) + } + return input + } + + for n := 0; n < 5; n++ { + input := Bitmap{bits} + other := Bitmap{bits} + + extra := make([]Bitmap, 0, n) + for i := 0; i < n; i++ { + extra = append(extra, Bitmap{bits}) + } + + tc(input)(other, extra...) + assert.Equal(t, naive(n), input) + } + }) + } + } +} + +func TestEmptyAnd(t *testing.T) { + var a, b Bitmap + a.And(b) + assert.Equal(t, 0, a.Count()) +} + +func TestEmptyAndNot(t *testing.T) { + var a, b Bitmap + a.AndNot(b) + assert.Equal(t, 0, a.Count()) +} + +func TestEmptyOr(t *testing.T) { + var a, b Bitmap + a.Or(b) + assert.Equal(t, 0, a.Count()) +} + +func TestEmptyXor(t *testing.T) { + var a, b Bitmap + a.Xor(b) + assert.Equal(t, 0, a.Count()) +} + +func TestTruthTables_NoSIMD(t *testing.T) { + hardware = isUnsupported + testTruthTables(t) +} + +func TestTruthTables_SIMD(t *testing.T) { + hardware = isAccelerated + testTruthTables(t) +} + +func testTruthTables(t *testing.T) { + { // AND + a := Bitmap{0b0011, 0b1011, 0b1100, 0b0000, 0b0011, 0b1011, 0b1100, 0b0000, 0b0011} + a.And(Bitmap{0b0101, 0b1101, 0b1010, 0b1111, 0b0101, 0b1101, 0b1010, 0b1111, 0b0101}) + assert.Equal(t, 0b0001, int(a[0])) + assert.Equal(t, 0b1001, int(a[1])) + assert.Equal(t, 0b1000, int(a[2])) + assert.Equal(t, 0b0000, int(a[3])) + assert.Equal(t, 0b0001, int(a[4])) + assert.Equal(t, 0b1001, int(a[5])) + assert.Equal(t, 0b1000, int(a[6])) + assert.Equal(t, 0b0000, int(a[7])) + assert.Equal(t, 0b0001, int(a[8])) + } + { // AND NOT + a := Bitmap{0b0011, 0, 0, 0} + a.AndNot(Bitmap{0b0101}) + assert.Equal(t, 0b0010, int(a[0])) + } + { // OR + a := Bitmap{0b0011, 0, 0, 0} + a.Or(Bitmap{0b0101}) + assert.Equal(t, 0b0111, int(a[0])) + } + { // XOR + a := Bitmap{0b0011, 0, 0, 0} + a.Xor(Bitmap{0b0101}) + assert.Equal(t, 0b0110, int(a[0])) + } +}