Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add PreviousSet and PreviousClear functions #178

Merged
merged 5 commits into from
Nov 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions bitset.go
Original file line number Diff line number Diff line change
Expand Up @@ -586,6 +586,56 @@ func (b *BitSet) NextClear(i uint) (uint, bool) {
return 0, false
}

// PreviousSet returns the previous set bit from the specified index,
// including possibly the current index
// along with an error code (true = valid, false = no bit found i.e. all bits are clear)
func (b *BitSet) PreviousSet(i uint) (uint, bool) {
x := int(i >> log2WordSize)
if x >= len(b.set) {
return 0, false
}
w := b.set[x]
// Clear the bits above the index
w = w & ((1 << (wordsIndex(i) + 1)) - 1)
if w != 0 {
return uint(x<<log2WordSize) + len64(w) - 1, true
}
for x--; x >= 0; x-- {
w = b.set[x]
if w != 0 {
return uint(x<<log2WordSize) + len64(w) - 1, true
}
}
return 0, false
}

// PreviousClear returns the previous clear bit from the specified index,
// including possibly the current index
// along with an error code (true = valid, false = no clear bit found i.e. all bits are set)
func (b *BitSet) PreviousClear(i uint) (uint, bool) {
x := int(i >> log2WordSize)
if x >= len(b.set) {
return 0, false
}
w := b.set[x]
// Flip all bits and find the highest one bit
w = ^w
// Clear the bits above the index
w = w & ((1 << (wordsIndex(i) + 1)) - 1)
if w != 0 {
return uint(x<<log2WordSize) + len64(w) - 1, true
}

for x--; x >= 0; x-- {
w = b.set[x]
w = ^w
if w != 0 {
return uint(x<<log2WordSize) + len64(w) - 1, true
}
}
return 0, false
}

// ClearAll clears the entire BitSet.
// It does not free the memory.
func (b *BitSet) ClearAll() *BitSet {
Expand Down
17 changes: 17 additions & 0 deletions bitset_benchmark_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,23 @@ func BenchmarkBitsetOps(b *testing.B) {
}
})

b.Run("PreviousSet", func(b *testing.B) {
s = New(100000)
b.ResetTimer()
for i := 0; i < b.N; i++ {
s.PreviousSet(99999)
}
})

b.Run("PreviousClear", func(b *testing.B) {
s = New(100000)
s.FlipRange(0, 100000)
b.ResetTimer()
for i := 0; i < b.N; i++ {
s.PreviousClear(99999)
}
})

b.Run("DifferenceCardinality", func(b *testing.B) {
empty := New(100000)
b.ResetTimer()
Expand Down
96 changes: 96 additions & 0 deletions bitset_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2087,3 +2087,99 @@ func TestWord(t *testing.T) {
})
}
}

func TestPreviousSet(t *testing.T) {
v := New(128)
v.Set(0)
v.Set(2)
v.Set(4)
v.Set(120)
for _, tt := range []struct {
index uint
want uint
wantFound bool
}{
{0, 0, true},
{1, 0, true},
{2, 2, true},
{3, 2, true},
{4, 4, true},
{5, 4, true},
{100, 4, true},
{120, 120, true},
{121, 120, true},
{1024, 0, false},
} {
t.Run(fmt.Sprintf("@%d", tt.index), func(t *testing.T) {
got, found := v.PreviousSet(tt.index)
if got != tt.want || found != tt.wantFound {
t.Errorf("PreviousSet(%d) = %d, %v, want %d, %v", tt.index, got, found, tt.want, tt.wantFound)
}
})
}
v.ClearAll()
for _, tt := range []struct {
index uint
want uint
wantFound bool
}{
{0, 0, false},
{120, 0, false},
{1024, 0, false},
} {
t.Run(fmt.Sprintf("@%d", tt.index), func(t *testing.T) {
got, found := v.PreviousSet(tt.index)
if got != tt.want || found != tt.wantFound {
t.Errorf("PreviousSet(%d) = %d, %v, want %d, %v", tt.index, got, found, tt.want, tt.wantFound)
}
})
}
}

func TestPreviousClear(t *testing.T) {
v := New(128)
v.Set(0)
v.Set(2)
v.Set(4)
v.Set(120)
for _, tt := range []struct {
index uint
want uint
wantFound bool
}{
{0, 0, false},
{1, 1, true},
{2, 1, true},
{3, 3, true},
{4, 3, true},
{5, 5, true},
{100, 100, true},
{120, 119, true},
{121, 121, true},
{1024, 0, false},
} {
t.Run(fmt.Sprintf("@%d", tt.index), func(t *testing.T) {
got, found := v.PreviousClear(tt.index)
if got != tt.want || found != tt.wantFound {
t.Errorf("PreviousClear(%d) = %d, %v, want %d, %v", tt.index, got, found, tt.want, tt.wantFound)
}
})
}
v.SetAll()
for _, tt := range []struct {
index uint
want uint
wantFound bool
}{
{0, 0, false},
{120, 0, false},
{1024, 0, false},
} {
t.Run(fmt.Sprintf("@%d", tt.index), func(t *testing.T) {
got, found := v.PreviousClear(tt.index)
if got != tt.want || found != tt.wantFound {
t.Errorf("PreviousClear(%d) = %d, %v, want %d, %v", tt.index, got, found, tt.want, tt.wantFound)
}
})
}
}
Loading