Skip to content

Commit

Permalink
Merge pull request #178 from lukesandberg/previous
Browse files Browse the repository at this point in the history
Add PreviousSet and PreviousClear functions
  • Loading branch information
lemire authored Nov 25, 2024
2 parents 7957b43 + 4d79cab commit ce76375
Show file tree
Hide file tree
Showing 3 changed files with 163 additions and 0 deletions.
50 changes: 50 additions & 0 deletions bitset.go
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,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 @@ -2135,3 +2135,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)
}
})
}
}

0 comments on commit ce76375

Please sign in to comment.