This is an automated email from the ASF dual-hosted git repository.

zeroshade pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-go.git


The following commit(s) were added to refs/heads/main by this push:
     new 5ad94933 fix: use xnor for boolean equals function (#505)
5ad94933 is described below

commit 5ad94933ed16b2b2434c32d7426c275f37fe93b4
Author: Dhruvit Maniya <61140756+dhruvi...@users.noreply.github.com>
AuthorDate: Wed Sep 17 21:39:14 2025 +0530

    fix: use xnor for boolean equals function (#505)
    
    ### Rationale for this change
    fixes https://github.com/apache/arrow-go/issues/493
    
    ### What changes are included in this PR?
    Add bitxnor operation, utilize bitxnor for boolean equality.
    
    ### Are these changes tested?
    Yes
    
    ### Are there any user-facing changes?
    Boolean equality operations now return correct results. No changes to
    the public API.
---
 arrow/bitutil/bitmap_ops.go                        | 23 ++++++++++++++++++++++
 arrow/bitutil/bitmaps.go                           | 13 ++++++++++++
 arrow/bitutil/bitmaps_test.go                      | 18 +++++++++++++++++
 .../compute/internal/kernels/scalar_comparisons.go | 10 ++++++----
 arrow/compute/scalar_compare_test.go               | 15 ++++++++++++++
 5 files changed, 75 insertions(+), 4 deletions(-)

diff --git a/arrow/bitutil/bitmap_ops.go b/arrow/bitutil/bitmap_ops.go
index 7db750a6..57b231f9 100644
--- a/arrow/bitutil/bitmap_ops.go
+++ b/arrow/bitutil/bitmap_ops.go
@@ -107,3 +107,26 @@ func alignedBitXorGo(left, right, out []byte) {
                out[i] = left[i] ^ right[i]
        }
 }
+
+func alignedBitXnorGo(left, right, out []byte) {
+       var (
+               nbytes = len(out)
+               i      = 0
+       )
+       if nbytes > uint64SizeBytes {
+               // case where we have enough bytes to operate on words
+               leftWords := bytesToUint64(left[i:])
+               rightWords := bytesToUint64(right[i:])
+               outWords := bytesToUint64(out[i:])
+
+               for w := range outWords {
+                       outWords[w] = ^(leftWords[w] ^ rightWords[w])
+               }
+
+               i += len(outWords) * uint64SizeBytes
+       }
+       // grab any remaining bytes that were fewer than a word
+       for ; i < nbytes; i++ {
+               out[i] = ^(left[i] ^ right[i])
+       }
+}
diff --git a/arrow/bitutil/bitmaps.go b/arrow/bitutil/bitmaps.go
index c6b156e7..666b904a 100644
--- a/arrow/bitutil/bitmaps.go
+++ b/arrow/bitutil/bitmaps.go
@@ -484,6 +484,11 @@ var (
                opByte:    func(l, r byte) byte { return l ^ r },
                opAligned: alignedBitXorGo,
        }
+       bitXnorOp = bitOp{
+               opWord:    func(l, r uint64) uint64 { return ^(l ^ r) },
+               opByte:    func(l, r byte) byte { return ^(l ^ r) },
+               opAligned: alignedBitXnorGo,
+       }
 )
 
 func alignedBitmapOp(op bitOp, left, right []byte, lOffset, rOffset int64, out 
[]byte, outOffset int64, length int64) {
@@ -592,6 +597,14 @@ func BitmapXorAlloc(mem memory.Allocator, left, right 
[]byte, lOffset, rOffset i
        return BitmapOpAlloc(mem, bitXorOp, left, right, lOffset, rOffset, 
length, outOffset)
 }
 
+func BitmapXnor(left, right []byte, lOffset, rOffset int64, out []byte, 
outOffset int64, length int64) {
+       BitmapOp(bitXnorOp, left, right, lOffset, rOffset, out, outOffset, 
length)
+}
+
+func BitmapXnorAlloc(mem memory.Allocator, left, right []byte, lOffset, 
rOffset int64, length, outOffset int64) *memory.Buffer {
+       return BitmapOpAlloc(mem, bitXnorOp, left, right, lOffset, rOffset, 
length, outOffset)
+}
+
 func BitmapEquals(left, right []byte, lOffset, rOffset int64, length int64) 
bool {
        if lOffset%8 == 0 && rOffset%8 == 0 {
                // byte aligned, fast path, can use bytes.Equal (memcmp)
diff --git a/arrow/bitutil/bitmaps_test.go b/arrow/bitutil/bitmaps_test.go
index a18c7c18..dd7e936a 100644
--- a/arrow/bitutil/bitmaps_test.go
+++ b/arrow/bitutil/bitmaps_test.go
@@ -517,6 +517,24 @@ func (s *BitmapOpSuite) TestBitmapOr() {
        })
 }
 
+func (s *BitmapOpSuite) TestBitmapXnor() {
+       op := bitmapOp{
+               noAlloc: bitutil.BitmapXnor,
+               alloc:   bitutil.BitmapXnorAlloc,
+       }
+
+       leftBits := []int{0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1}
+       rightBits := []int{0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0}
+       resultBits := []bool{true, false, true, false, false, false, true, 
false, false, true, false, false, false, false}
+
+       s.Run("aligned", func() {
+               s.testAligned(op, leftBits, rightBits, resultBits)
+       })
+       s.Run("unaligned", func() {
+               s.testUnaligned(op, leftBits, rightBits, resultBits)
+       })
+}
+
 func TestBitmapOps(t *testing.T) {
        suite.Run(t, new(BitmapOpSuite))
 }
diff --git a/arrow/compute/internal/kernels/scalar_comparisons.go 
b/arrow/compute/internal/kernels/scalar_comparisons.go
index 8cf23a2e..6d7611f7 100644
--- a/arrow/compute/internal/kernels/scalar_comparisons.go
+++ b/arrow/compute/internal/kernels/scalar_comparisons.go
@@ -36,9 +36,11 @@ import (
 
 type binaryKernel func(left, right, out []byte, offset int)
 
-type cmpFn[LeftT, RightT arrow.FixedWidthType] func([]LeftT, []RightT, 
[]uint32)
-type cmpScalarLeft[LeftT, RightT arrow.FixedWidthType] func(LeftT, []RightT, 
[]uint32)
-type cmpScalarRight[LeftT, RightT arrow.FixedWidthType] func([]LeftT, RightT, 
[]uint32)
+type (
+       cmpFn[LeftT, RightT arrow.FixedWidthType]          func([]LeftT, 
[]RightT, []uint32)
+       cmpScalarLeft[LeftT, RightT arrow.FixedWidthType]  func(LeftT, 
[]RightT, []uint32)
+       cmpScalarRight[LeftT, RightT arrow.FixedWidthType] func([]LeftT, 
RightT, []uint32)
+)
 
 type cmpOp[T arrow.FixedWidthType] struct {
        arrArr    cmpFn[T, T]
@@ -589,7 +591,7 @@ func compareTimestampKernel(ty exec.InputType, op 
CompareOperator) exec.ScalarKe
 var (
        boolEQ = binaryBoolOps{
                arrArr: func(_ *exec.KernelCtx, lhs, rhs, out bitutil.Bitmap) 
error {
-                       bitutil.BitmapAnd(lhs.Data, rhs.Data, lhs.Offset, 
rhs.Offset, out.Data, out.Offset, out.Len)
+                       bitutil.BitmapXnor(lhs.Data, rhs.Data, lhs.Offset, 
rhs.Offset, out.Data, out.Offset, out.Len)
                        return nil
                },
                arrScalar: func(_ *exec.KernelCtx, lhs bitutil.Bitmap, rhs 
bool, out bitutil.Bitmap) error {
diff --git a/arrow/compute/scalar_compare_test.go 
b/arrow/compute/scalar_compare_test.go
index 4b4d78bb..5a3078fc 100644
--- a/arrow/compute/scalar_compare_test.go
+++ b/arrow/compute/scalar_compare_test.go
@@ -264,6 +264,20 @@ func simpleArrArrCompare[T arrow.NumericType | string](mem 
memory.Allocator, op
        return compute.NewDatum(result)
 }
 
+type BooleanCompareSuite struct {
+       CompareSuite
+}
+
+func (b *BooleanCompareSuite) TestBooleanBasics() {
+       var (
+               example1JSON = `[true, false, true, false]`
+               example2JSON = `[true, false, false, true]`
+       )
+
+       b.validateCompare(kernels.CmpEQ, arrow.FixedWidthTypes.Boolean, 
example1JSON, example2JSON, `[true, true, false, false]`)
+       b.validateCompare(kernels.CmpNE, arrow.FixedWidthTypes.Boolean, 
example1JSON, example2JSON, `[false, false, true, true]`)
+}
+
 type NumericCompareSuite[T arrow.NumericType] struct {
        CompareSuite
 }
@@ -1224,6 +1238,7 @@ func (c *CompareStringSuite) 
TestRandomCompareArrayArray() {
 }
 
 func TestComparisons(t *testing.T) {
+       suite.Run(t, new(BooleanCompareSuite))
        suite.Run(t, new(NumericCompareSuite[int8]))
        suite.Run(t, new(NumericCompareSuite[int16]))
        suite.Run(t, new(NumericCompareSuite[int32]))

Reply via email to