From 420547f03cb8ba672ae871299c31fd0dc29ab0d2 Mon Sep 17 00:00:00 2001 From: Jake Wang Date: Fri, 18 Sep 2026 22:34:08 -0400 Subject: [PATCH] roaring64: preserve the sign plane when cloning BSIs --- roaring64/bsi64.go | 2 +- roaring64/bsi64_test.go | 54 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/roaring64/bsi64.go b/roaring64/bsi64.go index 134eb58c..4b66e75d 100644 --- a/roaring64/bsi64.go +++ b/roaring64/bsi64.go @@ -1774,7 +1774,7 @@ func (b *BSI) NewBSIRetainSet(foundSet *Bitmap) *BSI { newBSI.eBM = *b.eBM.Clone() newBSI.eBM.And(foundSet) }() - for i := 0; i < b.BitCount(); i++ { + for i := 0; i <= b.BitCount(); i++ { wg.Add(1) go func(j int) { defer wg.Done() diff --git a/roaring64/bsi64_test.go b/roaring64/bsi64_test.go index 39dbdcde..54f36263 100644 --- a/roaring64/bsi64_test.go +++ b/roaring64/bsi64_test.go @@ -489,6 +489,60 @@ func TestLargeFile(t *testing.T) { } +func TestBSICopySignedValues(t *testing.T) { + values := map[uint64]*big.Int{ + 1: big.NewInt(-1), + 2: big.NewInt(-42), + 3: big.NewInt(Min64BitSigned), + 4: new(big.Int).Neg(new(big.Int).Lsh(big.NewInt(1), 100)), + 5: big.NewInt(0), + 6: big.NewInt(42), + } + for _, retain := range []bool{false, true} { + t.Run(fmt.Sprintf("retain=%v", retain), func(t *testing.T) { + bsi := NewDefaultBSI() + for columnID, value := range values { + bsi.SetBigValue(columnID, value) + } + var copied *BSI + if retain { + copied = bsi.NewBSIRetainSet(BitmapOf(1, 3, 4, 5, 6)) + assert.Equal(t, uint64(5), copied.GetCardinality()) + assert.False(t, copied.IsNegative(2)) + } else { + copied = bsi.Clone() + assert.Equal(t, uint64(6), copied.GetCardinality()) + } + for columnID, value := range values { + got, exists := copied.GetBigValue(columnID) + if retain && columnID == 2 { + assert.False(t, exists) + continue + } + require.True(t, exists) + assert.Equal(t, value, got, "column %d", columnID) + assert.Equal(t, value.Sign() < 0, copied.IsNegative(columnID), "column %d", columnID) + } + copied.SetValue(1, 7) + original, exists := bsi.GetBigValue(1) + require.True(t, exists) + assert.Equal(t, big.NewInt(-1), original) + assert.True(t, bsi.IsNegative(1)) + }) + } +} + +func BenchmarkBSIClone(b *testing.B) { + bsi := NewDefaultBSI() + for i := uint64(0); i < 1000; i++ { + bsi.SetValue(i, int64(i)-500) + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + bsi.Clone() + } +} + func TestClone(t *testing.T) { bsi := NewDefaultBSI() // Setup values