Skip to content

Commit ce76375

Browse files
authored
Merge pull request #178 from lukesandberg/previous
Add PreviousSet and PreviousClear functions
2 parents 7957b43 + 4d79cab commit ce76375

File tree

3 files changed

+163
-0
lines changed

3 files changed

+163
-0
lines changed

bitset.go

+50
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,56 @@ func (b *BitSet) NextClear(i uint) (uint, bool) {
602602
return 0, false
603603
}
604604

605+
// PreviousSet returns the previous set bit from the specified index,
606+
// including possibly the current index
607+
// along with an error code (true = valid, false = no bit found i.e. all bits are clear)
608+
func (b *BitSet) PreviousSet(i uint) (uint, bool) {
609+
x := int(i >> log2WordSize)
610+
if x >= len(b.set) {
611+
return 0, false
612+
}
613+
w := b.set[x]
614+
// Clear the bits above the index
615+
w = w & ((1 << (wordsIndex(i) + 1)) - 1)
616+
if w != 0 {
617+
return uint(x<<log2WordSize) + len64(w) - 1, true
618+
}
619+
for x--; x >= 0; x-- {
620+
w = b.set[x]
621+
if w != 0 {
622+
return uint(x<<log2WordSize) + len64(w) - 1, true
623+
}
624+
}
625+
return 0, false
626+
}
627+
628+
// PreviousClear returns the previous clear bit from the specified index,
629+
// including possibly the current index
630+
// along with an error code (true = valid, false = no clear bit found i.e. all bits are set)
631+
func (b *BitSet) PreviousClear(i uint) (uint, bool) {
632+
x := int(i >> log2WordSize)
633+
if x >= len(b.set) {
634+
return 0, false
635+
}
636+
w := b.set[x]
637+
// Flip all bits and find the highest one bit
638+
w = ^w
639+
// Clear the bits above the index
640+
w = w & ((1 << (wordsIndex(i) + 1)) - 1)
641+
if w != 0 {
642+
return uint(x<<log2WordSize) + len64(w) - 1, true
643+
}
644+
645+
for x--; x >= 0; x-- {
646+
w = b.set[x]
647+
w = ^w
648+
if w != 0 {
649+
return uint(x<<log2WordSize) + len64(w) - 1, true
650+
}
651+
}
652+
return 0, false
653+
}
654+
605655
// ClearAll clears the entire BitSet.
606656
// It does not free the memory.
607657
func (b *BitSet) ClearAll() *BitSet {

bitset_benchmark_test.go

+17
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,23 @@ func BenchmarkBitsetOps(b *testing.B) {
130130
}
131131
})
132132

133+
b.Run("PreviousSet", func(b *testing.B) {
134+
s = New(100000)
135+
b.ResetTimer()
136+
for i := 0; i < b.N; i++ {
137+
s.PreviousSet(99999)
138+
}
139+
})
140+
141+
b.Run("PreviousClear", func(b *testing.B) {
142+
s = New(100000)
143+
s.FlipRange(0, 100000)
144+
b.ResetTimer()
145+
for i := 0; i < b.N; i++ {
146+
s.PreviousClear(99999)
147+
}
148+
})
149+
133150
b.Run("DifferenceCardinality", func(b *testing.B) {
134151
empty := New(100000)
135152
b.ResetTimer()

bitset_test.go

+96
Original file line numberDiff line numberDiff line change
@@ -2135,3 +2135,99 @@ func TestWord(t *testing.T) {
21352135
})
21362136
}
21372137
}
2138+
2139+
func TestPreviousSet(t *testing.T) {
2140+
v := New(128)
2141+
v.Set(0)
2142+
v.Set(2)
2143+
v.Set(4)
2144+
v.Set(120)
2145+
for _, tt := range []struct {
2146+
index uint
2147+
want uint
2148+
wantFound bool
2149+
}{
2150+
{0, 0, true},
2151+
{1, 0, true},
2152+
{2, 2, true},
2153+
{3, 2, true},
2154+
{4, 4, true},
2155+
{5, 4, true},
2156+
{100, 4, true},
2157+
{120, 120, true},
2158+
{121, 120, true},
2159+
{1024, 0, false},
2160+
} {
2161+
t.Run(fmt.Sprintf("@%d", tt.index), func(t *testing.T) {
2162+
got, found := v.PreviousSet(tt.index)
2163+
if got != tt.want || found != tt.wantFound {
2164+
t.Errorf("PreviousSet(%d) = %d, %v, want %d, %v", tt.index, got, found, tt.want, tt.wantFound)
2165+
}
2166+
})
2167+
}
2168+
v.ClearAll()
2169+
for _, tt := range []struct {
2170+
index uint
2171+
want uint
2172+
wantFound bool
2173+
}{
2174+
{0, 0, false},
2175+
{120, 0, false},
2176+
{1024, 0, false},
2177+
} {
2178+
t.Run(fmt.Sprintf("@%d", tt.index), func(t *testing.T) {
2179+
got, found := v.PreviousSet(tt.index)
2180+
if got != tt.want || found != tt.wantFound {
2181+
t.Errorf("PreviousSet(%d) = %d, %v, want %d, %v", tt.index, got, found, tt.want, tt.wantFound)
2182+
}
2183+
})
2184+
}
2185+
}
2186+
2187+
func TestPreviousClear(t *testing.T) {
2188+
v := New(128)
2189+
v.Set(0)
2190+
v.Set(2)
2191+
v.Set(4)
2192+
v.Set(120)
2193+
for _, tt := range []struct {
2194+
index uint
2195+
want uint
2196+
wantFound bool
2197+
}{
2198+
{0, 0, false},
2199+
{1, 1, true},
2200+
{2, 1, true},
2201+
{3, 3, true},
2202+
{4, 3, true},
2203+
{5, 5, true},
2204+
{100, 100, true},
2205+
{120, 119, true},
2206+
{121, 121, true},
2207+
{1024, 0, false},
2208+
} {
2209+
t.Run(fmt.Sprintf("@%d", tt.index), func(t *testing.T) {
2210+
got, found := v.PreviousClear(tt.index)
2211+
if got != tt.want || found != tt.wantFound {
2212+
t.Errorf("PreviousClear(%d) = %d, %v, want %d, %v", tt.index, got, found, tt.want, tt.wantFound)
2213+
}
2214+
})
2215+
}
2216+
v.SetAll()
2217+
for _, tt := range []struct {
2218+
index uint
2219+
want uint
2220+
wantFound bool
2221+
}{
2222+
{0, 0, false},
2223+
{120, 0, false},
2224+
{1024, 0, false},
2225+
} {
2226+
t.Run(fmt.Sprintf("@%d", tt.index), func(t *testing.T) {
2227+
got, found := v.PreviousClear(tt.index)
2228+
if got != tt.want || found != tt.wantFound {
2229+
t.Errorf("PreviousClear(%d) = %d, %v, want %d, %v", tt.index, got, found, tt.want, tt.wantFound)
2230+
}
2231+
})
2232+
}
2233+
}

0 commit comments

Comments
 (0)