Skip to content

Commit 970a8cb

Browse files
committed
making it return IntPeekable
1 parent c1aaede commit 970a8cb

File tree

2 files changed

+185
-2
lines changed

2 files changed

+185
-2
lines changed

iter_test.go

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,3 +308,157 @@ func TestUnset(t *testing.T) {
308308
assert.Equal(t, expected, actual)
309309
})
310310
}
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.AddInt(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.AddInt(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+
}

roaring.go

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -804,6 +804,35 @@ func (ui *unsetIterator) updateHasNext() {
804804
ui.hasNext = false
805805
}
806806

807+
// PeekNext returns the next value without advancing the iterator
808+
func (ui *unsetIterator) PeekNext() uint32 {
809+
if !ui.hasNext {
810+
panic("PeekNext() called when HasNext() returns false")
811+
}
812+
return uint32(ui.current)
813+
}
814+
815+
// AdvanceIfNeeded advances the iterator so that the next value is at least minval
816+
func (ui *unsetIterator) AdvanceIfNeeded(minval uint32) {
817+
if minval <= ui.min {
818+
return // Already at or before the start of our range
819+
}
820+
821+
if minval > ui.max {
822+
// Beyond our range, no more values
823+
ui.hasNext = false
824+
return
825+
}
826+
827+
// Set current to minval, but make sure we skip any set bits
828+
ui.current = uint64(minval)
829+
830+
// Advance the internal iterator to be at or beyond minval
831+
ui.it.AdvanceIfNeeded(minval)
832+
833+
ui.updateHasNext()
834+
}
835+
807836
// String creates a string representation of the Bitmap
808837
func (rb *Bitmap) String() string {
809838
// inspired by https://github.com/fzandona/goroar/
@@ -886,9 +915,9 @@ func (rb *Bitmap) ManyIterator() ManyIntIterable {
886915
return p
887916
}
888917

889-
// UnsetIterator creates a new IntIterable to iterate over values in the range [min, max] that are NOT contained in the bitmap.
918+
// UnsetIterator creates a new IntPeekable to iterate over values in the range [min, max] that are NOT contained in the bitmap.
890919
// The iterator becomes invalid if the bitmap is modified (e.g., with Add or Remove).
891-
func (rb *Bitmap) UnsetIterator(min, max uint32) IntIterable {
920+
func (rb *Bitmap) UnsetIterator(min, max uint32) IntPeekable {
892921
p := new(unsetIterator)
893922
p.Initialize(rb, min, max)
894923
return p

0 commit comments

Comments
 (0)