Skip to content

Commit 2e9b4b9

Browse files
authored
Merge pull request #492 from RoaringBitmap/issue22
fixing issue 22 : unset iterators
2 parents 64d53df + 872517e commit 2e9b4b9

File tree

3 files changed

+455
-0
lines changed

3 files changed

+455
-0
lines changed

iter.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,16 @@ func Backward(b *Bitmap) func(func(uint32) bool) {
2727
}
2828
}
2929
}
30+
31+
// Unset creates an iterator that yields values in the range [min, max] that are NOT contained in the bitmap.
32+
// The iterator becomes invalid if the bitmap is modified (e.g., with Add or Remove).
33+
func Unset(b *Bitmap, min, max uint32) func(func(uint32) bool) {
34+
return func(yield func(uint32) bool) {
35+
it := b.UnsetIterator(min, max)
36+
for it.HasNext() {
37+
if !yield(it.Next()) {
38+
return
39+
}
40+
}
41+
}
42+
}

iter_test.go

Lines changed: 343 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,3 +119,346 @@ func TestValues(t *testing.T) {
119119

120120
assert.Equal(t, testSize, n)
121121
}
122+
123+
func TestUnset(t *testing.T) {
124+
t.Run("empty bitmap", func(t *testing.T) {
125+
b := New()
126+
it := Unset(b, 5, 10)
127+
128+
expected := []uint32{5, 6, 7, 8, 9, 10}
129+
actual := make([]uint32, 0)
130+
131+
it(func(val uint32) bool {
132+
actual = append(actual, val)
133+
return true
134+
})
135+
136+
assert.Equal(t, expected, actual)
137+
})
138+
139+
t.Run("bitmap with some values set", func(t *testing.T) {
140+
b := New()
141+
b.AddInt(3)
142+
b.AddInt(7)
143+
b.AddInt(12)
144+
145+
it := Unset(b, 5, 10)
146+
147+
expected := []uint32{5, 6, 8, 9, 10}
148+
actual := make([]uint32, 0)
149+
150+
it(func(val uint32) bool {
151+
actual = append(actual, val)
152+
return true
153+
})
154+
155+
assert.Equal(t, expected, actual)
156+
})
157+
158+
t.Run("range completely outside bitmap", func(t *testing.T) {
159+
b := New()
160+
b.AddInt(1)
161+
b.AddInt(2)
162+
b.AddInt(3)
163+
164+
it := Unset(b, 10, 15)
165+
166+
expected := []uint32{10, 11, 12, 13, 14, 15}
167+
actual := make([]uint32, 0)
168+
169+
it(func(val uint32) bool {
170+
actual = append(actual, val)
171+
return true
172+
})
173+
174+
assert.Equal(t, expected, actual)
175+
})
176+
177+
t.Run("range includes set and unset values", func(t *testing.T) {
178+
b := New()
179+
b.AddInt(5)
180+
b.AddInt(8)
181+
b.AddInt(9)
182+
183+
it := Unset(b, 3, 12)
184+
185+
expected := []uint32{3, 4, 6, 7, 10, 11, 12}
186+
actual := make([]uint32, 0)
187+
188+
it(func(val uint32) bool {
189+
actual = append(actual, val)
190+
return true
191+
})
192+
193+
assert.Equal(t, expected, actual)
194+
})
195+
196+
t.Run("min greater than max", func(t *testing.T) {
197+
b := New()
198+
it := Unset(b, 10, 5)
199+
200+
count := 0
201+
it(func(val uint32) bool {
202+
count++
203+
return true
204+
})
205+
206+
assert.Equal(t, 0, count)
207+
})
208+
209+
t.Run("single value range - unset", func(t *testing.T) {
210+
b := New()
211+
b.AddInt(5)
212+
213+
it := Unset(b, 3, 3)
214+
215+
expected := []uint32{3}
216+
actual := make([]uint32, 0)
217+
218+
it(func(val uint32) bool {
219+
actual = append(actual, val)
220+
return true
221+
})
222+
223+
assert.Equal(t, expected, actual)
224+
})
225+
226+
t.Run("single value range - set", func(t *testing.T) {
227+
b := New()
228+
b.AddInt(5)
229+
230+
it := Unset(b, 5, 5)
231+
232+
count := 0
233+
it(func(val uint32) bool {
234+
count++
235+
return true
236+
})
237+
238+
assert.Equal(t, 0, count)
239+
})
240+
241+
t.Run("early termination", func(t *testing.T) {
242+
b := New()
243+
244+
it := Unset(b, 1, 10)
245+
246+
actual := make([]uint32, 0)
247+
it(func(val uint32) bool {
248+
actual = append(actual, val)
249+
return len(actual) < 3 // Stop after 3 values
250+
})
251+
252+
expected := []uint32{1, 2, 3}
253+
assert.Equal(t, expected, actual)
254+
})
255+
256+
t.Run("large range with sparse bitmap", func(t *testing.T) {
257+
b := New()
258+
b.AddInt(100)
259+
b.AddInt(500)
260+
b.AddInt(1000)
261+
262+
it := Unset(b, 50, 150)
263+
264+
actual := make([]uint32, 0)
265+
it(func(val uint32) bool {
266+
actual = append(actual, val)
267+
return true
268+
})
269+
270+
// Should include all values from 50-150 except 100
271+
assert.Equal(t, 100, len(actual)) // 150-50+1-1 = 100
272+
assert.Contains(t, actual, uint32(50))
273+
assert.Contains(t, actual, uint32(99))
274+
assert.NotContains(t, actual, uint32(100))
275+
assert.Contains(t, actual, uint32(101))
276+
assert.Contains(t, actual, uint32(150))
277+
})
278+
279+
t.Run("min is in the bitmap", func(t *testing.T) {
280+
b := New()
281+
b.AddInt(100)
282+
283+
it := Unset(b, 100, 105)
284+
285+
actual := make([]uint32, 0)
286+
it(func(val uint32) bool {
287+
actual = append(actual, val)
288+
return true
289+
})
290+
expected := []uint32{101, 102, 103, 104, 105}
291+
292+
assert.Equal(t, expected, actual)
293+
})
294+
295+
t.Run("extreme max", func(t *testing.T) {
296+
b := New()
297+
b.Add(4294967295)
298+
299+
it := Unset(b, 4294967294, 4294967295)
300+
301+
actual := make([]uint32, 0)
302+
it(func(val uint32) bool {
303+
actual = append(actual, val)
304+
return true
305+
})
306+
expected := []uint32{4294967294}
307+
308+
assert.Equal(t, expected, actual)
309+
})
310+
}
311+
312+
func TestUnsetIteratorPeekable(t *testing.T) {
313+
t.Run("peek next", func(t *testing.T) {
314+
b := New()
315+
b.AddInt(5)
316+
b.AddInt(8)
317+
318+
it := b.UnsetIterator(3, 10)
319+
320+
// First value should be 3
321+
assert.True(t, it.HasNext())
322+
assert.Equal(t, uint32(3), it.PeekNext())
323+
assert.Equal(t, uint32(3), it.Next())
324+
325+
// Next should be 4
326+
assert.True(t, it.HasNext())
327+
assert.Equal(t, uint32(4), it.PeekNext())
328+
assert.Equal(t, uint32(4), it.Next())
329+
330+
// Next should be 6 (skipping 5 which is set)
331+
assert.True(t, it.HasNext())
332+
assert.Equal(t, uint32(6), it.PeekNext())
333+
assert.Equal(t, uint32(6), it.Next())
334+
335+
// Next should be 7
336+
assert.True(t, it.HasNext())
337+
assert.Equal(t, uint32(7), it.PeekNext())
338+
assert.Equal(t, uint32(7), it.Next())
339+
340+
// Next should be 9 (skipping 8 which is set)
341+
assert.True(t, it.HasNext())
342+
assert.Equal(t, uint32(9), it.PeekNext())
343+
assert.Equal(t, uint32(9), it.Next())
344+
345+
// Next should be 10
346+
assert.True(t, it.HasNext())
347+
assert.Equal(t, uint32(10), it.PeekNext())
348+
assert.Equal(t, uint32(10), it.Next())
349+
350+
// No more values
351+
assert.False(t, it.HasNext())
352+
})
353+
354+
t.Run("advance if needed", func(t *testing.T) {
355+
b := New()
356+
b.AddInt(5)
357+
b.AddInt(8)
358+
b.AddInt(12)
359+
360+
it := b.UnsetIterator(1, 15)
361+
362+
// Skip to values >= 7
363+
it.AdvanceIfNeeded(7)
364+
365+
// Should now be at 7 (skipping 5 which is set)
366+
assert.True(t, it.HasNext())
367+
assert.Equal(t, uint32(7), it.PeekNext())
368+
assert.Equal(t, uint32(7), it.Next())
369+
370+
// Next should be 9 (skipping 8 which is set)
371+
assert.True(t, it.HasNext())
372+
assert.Equal(t, uint32(9), it.PeekNext())
373+
assert.Equal(t, uint32(9), it.Next())
374+
375+
// Skip to values >= 11
376+
it.AdvanceIfNeeded(11)
377+
378+
// Should now be at 11 (skipping 12 which is set)
379+
assert.True(t, it.HasNext())
380+
assert.Equal(t, uint32(11), it.PeekNext())
381+
assert.Equal(t, uint32(11), it.Next())
382+
383+
// Next should be 13
384+
assert.True(t, it.HasNext())
385+
assert.Equal(t, uint32(13), it.PeekNext())
386+
assert.Equal(t, uint32(13), it.Next())
387+
388+
// Skip beyond range
389+
it.AdvanceIfNeeded(20)
390+
assert.False(t, it.HasNext())
391+
})
392+
393+
t.Run("advance if needed before range", func(t *testing.T) {
394+
b := New()
395+
b.AddInt(5)
396+
397+
it := b.UnsetIterator(10, 15)
398+
399+
// Try to advance to a value before our range start
400+
it.AdvanceIfNeeded(5)
401+
402+
// Should still start from 10
403+
assert.True(t, it.HasNext())
404+
assert.Equal(t, uint32(10), it.PeekNext())
405+
})
406+
407+
t.Run("advance if needed beyond range", func(t *testing.T) {
408+
b := New()
409+
b.AddInt(5)
410+
411+
it := b.UnsetIterator(10, 15)
412+
413+
// Advance beyond our range
414+
it.AdvanceIfNeeded(20)
415+
416+
// Should have no more values
417+
assert.False(t, it.HasNext())
418+
})
419+
420+
t.Run("peek next on empty iterator", func(t *testing.T) {
421+
b := New()
422+
b.AddInt(5) // Set bit in middle of range
423+
424+
it := b.UnsetIterator(5, 5) // Range contains only the set bit
425+
426+
// Should have no values
427+
assert.False(t, it.HasNext())
428+
429+
// PeekNext should panic when HasNext is false
430+
assert.Panics(t, func() {
431+
it.PeekNext()
432+
})
433+
})
434+
435+
t.Run("range including max uint32 unset", func(t *testing.T) {
436+
b := New()
437+
b.Add(4294967294) // Set the value before max
438+
439+
it := b.UnsetIterator(4294967294, 4294967295)
440+
441+
// Should have 4294967295 (max uint32) as it's unset
442+
assert.True(t, it.HasNext())
443+
assert.Equal(t, uint32(4294967295), it.PeekNext())
444+
assert.Equal(t, uint32(4294967295), it.Next())
445+
446+
// No more values
447+
assert.False(t, it.HasNext())
448+
})
449+
450+
t.Run("max uint32 set", func(t *testing.T) {
451+
b := New()
452+
b.Add(4294967295) // Set max uint32
453+
454+
it := b.UnsetIterator(4294967294, 4294967295)
455+
456+
// Should have 4294967294 as it's unset, but not 4294967295
457+
assert.True(t, it.HasNext())
458+
assert.Equal(t, uint32(4294967294), it.PeekNext())
459+
assert.Equal(t, uint32(4294967294), it.Next())
460+
461+
// No more values
462+
assert.False(t, it.HasNext())
463+
})
464+
}

0 commit comments

Comments
 (0)