Skip to content

Commit eb2f0c9

Browse files
committed
Add Nand functionality
One useful use case for bitarrays is to do filtering of values, by removing the values in one bitarry from another. Add a Nand function that will do this sort of filtering.
1 parent 054d629 commit eb2f0c9

File tree

7 files changed

+525
-8
lines changed

7 files changed

+525
-8
lines changed

bitarray/and.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ func andSparseWithSparseBitArray(sba, other *sparseBitArray) BitArray {
2323

2424
selfIndex := 0
2525
otherIndex := 0
26+
var resultBlock block
2627

2728
// move through the array and compare the blocks if they happen to
2829
// intersect
@@ -52,9 +53,12 @@ func andSparseWithSparseBitArray(sba, other *sparseBitArray) BitArray {
5253
default:
5354
// Here, our indices match for both `sba` and `other`.
5455
// Time to do the bitwise AND operation and add a block
55-
// to our result list.
56-
indices = append(indices, selfValue)
57-
blocks = append(blocks, sba.blocks[selfIndex].and(other.blocks[otherIndex]))
56+
// to our result list if the block has values in it.
57+
resultBlock = sba.blocks[selfIndex].and(other.blocks[otherIndex])
58+
if resultBlock > 0 {
59+
indices = append(indices, selfValue)
60+
blocks = append(blocks, resultBlock)
61+
}
5862
selfIndex++
5963
otherIndex++
6064
}
@@ -92,7 +96,6 @@ func andSparseWithDenseBitArray(sba *sparseBitArray, other *bitArray) BitArray {
9296
// We're ready to return
9397
break
9498
}
95-
9699
ba.blocks[selfIndex] = ba.blocks[selfIndex].and(
97100
other.blocks[selfValue])
98101
}

bitarray/and_test.go

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ func TestAndSparseWithSparseBitArray(t *testing.T) {
4343
sba.SetBit(280)
4444
other.SetBit(9)
4545
other.SetBit(100)
46+
sba.SetBit(1000)
47+
other.SetBit(1001)
4648

4749
// bits for which both arrays are set
4850
sba.SetBit(1)
@@ -54,15 +56,23 @@ func TestAndSparseWithSparseBitArray(t *testing.T) {
5456

5557
ba := andSparseWithSparseBitArray(sba, other)
5658

59+
// Bits in both
5760
checkBit(t, ba, 1, true)
5861
checkBit(t, ba, 30, true)
5962
checkBit(t, ba, 2680, true)
6063

64+
// Bits in sba but not other
6165
checkBit(t, ba, 3, false)
66+
checkBit(t, ba, 280, false)
67+
checkBit(t, ba, 1000, false)
68+
69+
// Bits in other but not sba
6270
checkBit(t, ba, 9, false)
6371
checkBit(t, ba, 100, false)
6472
checkBit(t, ba, 2, false)
65-
checkBit(t, ba, 280, false)
73+
74+
nums := ba.ToNums()
75+
assert.Equal(t, []uint64{1, 30, 2680}, nums)
6676
}
6777

6878
func TestAndSparseWithDenseBitArray(t *testing.T) {
@@ -80,14 +90,20 @@ func TestAndSparseWithDenseBitArray(t *testing.T) {
8090

8191
ba := andSparseWithDenseBitArray(sba, other)
8292

93+
// Bits in both
8394
checkBit(t, ba, 1, true)
8495
checkBit(t, ba, 150, true)
96+
checkBit(t, ba, 300, true)
97+
98+
// Bits in sba but not other
8599
checkBit(t, ba, 155, false)
100+
101+
// Bits in other but not sba
86102
checkBit(t, ba, 156, false)
87-
checkBit(t, ba, 300, true)
103+
88104
}
89105

90-
// Maks sure that the sparse array is trimmed correctly if compared against a
106+
// Make sure that the sparse array is trimmed correctly if compared against a
91107
// smaller dense bit array.
92108
func TestAndSparseWithSmallerDenseBitArray(t *testing.T) {
93109
sba := newSparseBitArray()
@@ -106,13 +122,18 @@ func TestAndSparseWithSmallerDenseBitArray(t *testing.T) {
106122

107123
ba := andSparseWithDenseBitArray(sba, other)
108124

125+
// Bits in both
109126
checkBit(t, ba, 1, true)
110127
checkBit(t, ba, 150, true)
128+
129+
// Bits in sba but not other
111130
checkBit(t, ba, 155, false)
112-
checkBit(t, ba, 128, false)
113131
checkBit(t, ba, 500, false)
114132
checkBit(t, ba, 1200, false)
115133
checkBit(t, ba, 1500, false)
134+
135+
// Bits in other but not sba
136+
checkBit(t, ba, 128, false)
116137
}
117138

118139
func TestAndDenseWithDenseBitArray(t *testing.T) {
@@ -148,6 +169,7 @@ func TestAndSparseWithEmptySparse(t *testing.T) {
148169
sba.SetBit(5)
149170

150171
ba := andSparseWithSparseBitArray(sba, other)
172+
151173
checkBit(t, ba, 0, false)
152174
checkBit(t, ba, 5, false)
153175
checkBit(t, ba, 100, false)

bitarray/bitarray.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,16 @@ func (ba *bitArray) And(other BitArray) BitArray {
157157
return andSparseWithDenseBitArray(other.(*sparseBitArray), ba)
158158
}
159159

160+
// Nand will return the result of doing a bitwise and not of the bit array
161+
// with the other bit array on each block.
162+
func (ba *bitArray) Nand(other BitArray) BitArray {
163+
if dba, ok := other.(*bitArray); ok {
164+
return nandDenseWithDenseBitArray(ba, dba)
165+
}
166+
167+
return nandDenseWithSparseBitArray(ba, other.(*sparseBitArray))
168+
}
169+
160170
// Reset clears out the bit array.
161171
func (ba *bitArray) Reset() {
162172
for i := uint64(0); i < uint64(len(ba.blocks)); i++ {

bitarray/block.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@ func (b block) and(other block) block {
8181
return b & other
8282
}
8383

84+
func (b block) nand(other block) block {
85+
return b &^ other
86+
}
87+
8488
func (b block) get(position uint64) bool {
8589
return b&block(1<<position) != 0
8690
}

bitarray/nand.go

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
/*
2+
Copyright 2014 Workiva, LLC
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package bitarray
18+
19+
func nandSparseWithSparseBitArray(sba, other *sparseBitArray) BitArray {
20+
// nand is an operation on the incoming array only, so the size will never
21+
// be more than the incoming array, regardless of the size of the other
22+
max := len(sba.indices)
23+
indices := make(uintSlice, 0, max)
24+
blocks := make(blocks, 0, max)
25+
26+
selfIndex := 0
27+
otherIndex := 0
28+
var resultBlock block
29+
30+
// move through the array and compare the blocks if they happen to
31+
// intersect
32+
for {
33+
if selfIndex == len(sba.indices) {
34+
// The bitarray being operated on is exhausted, so just return
35+
break
36+
} else if otherIndex == len(other.indices) {
37+
// The other array is exhausted. In this case, we assume that we
38+
// are calling nand on empty bit arrays, which is the same as just
39+
// copying the value in the sba array
40+
indices = append(indices, sba.indices[selfIndex])
41+
blocks = append(blocks, sba.blocks[selfIndex])
42+
selfIndex++
43+
continue
44+
}
45+
46+
selfValue := sba.indices[selfIndex]
47+
otherValue := other.indices[otherIndex]
48+
49+
switch {
50+
case otherValue < selfValue:
51+
// The `sba` bitarray has a block with a index position
52+
// greater than us. We want to compare with that block
53+
// if possible, so move our `other` index closer to that
54+
// block's index.
55+
otherIndex++
56+
57+
case otherValue > selfValue:
58+
// Here, the sba array has blocks that the other array doesn't
59+
// have. In this case, we just copy exactly the sba array values
60+
indices = append(indices, selfValue)
61+
blocks = append(blocks, sba.blocks[selfIndex])
62+
63+
// This is the exact logical inverse of the above case.
64+
selfIndex++
65+
66+
default:
67+
// Here, our indices match for both `sba` and `other`.
68+
// Time to do the bitwise AND operation and add a block
69+
// to our result list if the block has values in it.
70+
resultBlock = sba.blocks[selfIndex].nand(other.blocks[otherIndex])
71+
if resultBlock > 0 {
72+
indices = append(indices, selfValue)
73+
blocks = append(blocks, resultBlock)
74+
}
75+
selfIndex++
76+
otherIndex++
77+
}
78+
}
79+
80+
return &sparseBitArray{
81+
indices: indices,
82+
blocks: blocks,
83+
}
84+
}
85+
86+
func nandSparseWithDenseBitArray(sba *sparseBitArray, other *bitArray) BitArray {
87+
// Since nand is non-commutative, the resulting array should be sparse,
88+
// and the same length or less than the sparse array
89+
indices := make(uintSlice, 0, len(sba.indices))
90+
blocks := make(blocks, 0, len(sba.indices))
91+
92+
var resultBlock block
93+
94+
// Loop through the sparse array and match it with the dense array.
95+
for selfIndex, selfValue := range sba.indices {
96+
if selfValue >= uint64(len(other.blocks)) {
97+
// Since the dense array is exhausted, just copy over the data
98+
// from the sparse array
99+
resultBlock = sba.blocks[selfIndex]
100+
indices = append(indices, selfValue)
101+
blocks = append(blocks, resultBlock)
102+
continue
103+
}
104+
105+
resultBlock = sba.blocks[selfIndex].nand(other.blocks[selfValue])
106+
if resultBlock > 0 {
107+
indices = append(indices, selfValue)
108+
blocks = append(blocks, resultBlock)
109+
}
110+
}
111+
112+
return &sparseBitArray{
113+
indices: indices,
114+
blocks: blocks,
115+
}
116+
}
117+
118+
func nandDenseWithSparseBitArray(sba *bitArray, other *sparseBitArray) BitArray {
119+
// Since nand is non-commutative, the resulting array should be dense,
120+
// and the same length or less than the dense array
121+
tmp := sba.copy()
122+
ret := tmp.(*bitArray)
123+
124+
// Loop through the other array and match it with the sba array.
125+
for otherIndex, otherValue := range other.indices {
126+
if otherValue >= uint64(len(ret.blocks)) {
127+
break
128+
}
129+
130+
ret.blocks[otherValue] = sba.blocks[otherValue].nand(other.blocks[otherIndex])
131+
}
132+
133+
ret.setLowest()
134+
ret.setHighest()
135+
136+
return ret
137+
}
138+
139+
func nandDenseWithDenseBitArray(dba, other *bitArray) BitArray {
140+
min := uint64(len(dba.blocks))
141+
142+
ba := newBitArray(min * s)
143+
144+
for i := uint64(0); i < min; i++ {
145+
ba.blocks[i] = dba.blocks[i].nand(other.blocks[i])
146+
}
147+
148+
ba.setLowest()
149+
ba.setHighest()
150+
151+
return ba
152+
}

0 commit comments

Comments
 (0)