Skip to content

Commit 2e73b24

Browse files
authored
Add some missing features to 64-bit bitmaps (#146)
* Add shrink_to_fit() to BitMap64 * Add shift() to BitMap64 * Add overwrite() to BitMap64
1 parent 6a6f5dd commit 2e73b24

4 files changed

Lines changed: 41 additions & 4 deletions

File tree

pyroaring/abstract_bitmap.pxi

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -856,6 +856,7 @@ cdef class AbstractBitMap64:
856856
self._h_val = 0
857857
if optimize:
858858
self.run_optimize()
859+
self.shrink_to_fit()
859860

860861
def __init__(self, values=None, copy_on_write=False, optimize=True):
861862
"""
@@ -895,6 +896,9 @@ cdef class AbstractBitMap64:
895896
def run_optimize(self):
896897
return croaring.roaring64_bitmap_run_optimize(self._c_bitmap)
897898

899+
def shrink_to_fit(self):
900+
return croaring.roaring64_bitmap_shrink_to_fit(self._c_bitmap)
901+
898902
def __dealloc__(self):
899903
if self._c_bitmap is not NULL:
900904
croaring.roaring64_bitmap_free(self._c_bitmap)
@@ -1044,6 +1048,21 @@ cdef class AbstractBitMap64:
10441048
"""
10451049
return self.from_ptr(croaring.roaring64_bitmap_flip(self._c_bitmap, start, end))
10461050

1051+
def shift(self, int64_t offset):
1052+
"""
1053+
Add the value 'offset' to each and every value of the bitmap.
1054+
1055+
If offset + element is outside of the range [0,2^64), that the element will be dropped.
1056+
1057+
>>> bm = BitMap64([3, 12])
1058+
>>> bm.shift(21)
1059+
BitMap64([24, 33])
1060+
"""
1061+
if offset >= 0:
1062+
return self.from_ptr(croaring.roaring64_bitmap_add_offset_signed(self._c_bitmap, True, <uint64_t>offset))
1063+
else:
1064+
return self.from_ptr(croaring.roaring64_bitmap_add_offset_signed(self._c_bitmap, False, <uint64_t>0 - <uint64_t>offset))
1065+
10471066
def get_statistics(self):
10481067
"""
10491068
Return relevant metrics about the bitmap.

pyroaring/bitmap.pxi

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,23 @@ cdef class BitMap64(AbstractBitMap64):
441441
"""
442442
self.__ixor__(other)
443443

444+
def overwrite(self, AbstractBitMap64 other):
445+
"""
446+
Clear the bitmap and overwrite it with another.
447+
448+
>>> bm = BitMap64([3, 12])
449+
>>> other = BitMap64([4, 14])
450+
>>> bm.overwrite(other)
451+
>>> other.remove(4)
452+
>>> bm
453+
BitMap64([4, 14])
454+
>>> other
455+
BitMap64([14])
456+
"""
457+
if self._c_bitmap == other._c_bitmap:
458+
raise ValueError('Cannot overwrite itself')
459+
croaring.roaring64_bitmap_overwrite(self._c_bitmap, other._c_bitmap)
460+
444461
def clear(self):
445462
"""
446463
Remove all elements from this set.

pyroaring/croaring.pxd

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ cdef extern from "roaring.h":
142142
uint64_t roaring64_bitmap_minimum(const roaring64_bitmap_t *r)
143143
uint64_t roaring64_bitmap_maximum(const roaring64_bitmap_t *r)
144144
bool roaring64_bitmap_run_optimize(roaring64_bitmap_t *r)
145+
size_t roaring64_bitmap_shrink_to_fit(roaring64_bitmap_t *r)
145146
size_t roaring64_bitmap_size_in_bytes(const roaring64_bitmap_t *r)
146147
bool roaring64_bitmap_equals(const roaring64_bitmap_t *r1, const roaring64_bitmap_t *r2)
147148
bool roaring64_bitmap_is_subset(const roaring64_bitmap_t *r1, const roaring64_bitmap_t *r2)
@@ -166,6 +167,8 @@ cdef extern from "roaring.h":
166167
size_t roaring64_bitmap_portable_deserialize_size(const char *buf, size_t maxbytes)
167168
roaring64_bitmap_t *roaring64_bitmap_portable_deserialize_safe(const char *buf, size_t maxbytes)
168169
bool roaring64_bitmap_internal_validate(const roaring64_bitmap_t *r, const char **reason)
170+
roaring64_bitmap_t *roaring64_bitmap_add_offset_signed(const roaring64_bitmap_t *r, bool positive, uint64_t offset)
171+
void roaring64_bitmap_overwrite(roaring64_bitmap_t *dest, const roaring64_bitmap_t *src)
169172
roaring64_iterator_t *roaring64_iterator_create(const roaring64_bitmap_t *r)
170173
void roaring64_iterator_free(roaring64_iterator_t *it)
171174
bool roaring64_iterator_has_value(const roaring64_iterator_t *it)

test.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1090,7 +1090,6 @@ def test_flip_inplace(
10901090
bm_after.flip_inplace(start, end)
10911091
self.check_flip(bm_before, bm_after, start, end)
10921092

1093-
@pytest.mark.skipif(not is_32_bits, reason="not supported yet")
10941093
class TestShift(Util):
10951094
@given(bitmap_cls, hyp_collection, int64, st.booleans())
10961095
def test_shift(
@@ -1104,7 +1103,8 @@ def test_shift(
11041103
bm_copy = cls(bm_before)
11051104
bm_after = bm_before.shift(offset)
11061105
assert bm_before == bm_copy
1107-
expected = cls([val + offset for val in values if val + offset in range(0, 2**32)], copy_on_write=cow)
1106+
upper = 2**32 if is_32_bits else 2**64
1107+
expected = cls([val + offset for val in values if val + offset in range(0, upper)], copy_on_write=cow)
11081108
assert bm_after == expected
11091109

11101110
@pytest.mark.skipif(not is_32_bits, reason="not supported yet")
@@ -1297,7 +1297,6 @@ def test_run_optimize(self, cls: type[EitherBitMap]) -> None:
12971297
bm3 = cls(bm1) # optimize is True by default
12981298
assert stats == bm3.get_statistics()
12991299

1300-
@pytest.mark.skipif(not is_32_bits, reason="not supported yet")
13011300
@given(bitmap_cls)
13021301
def test_shrink_to_fit(self, cls: type[EitherBitMap]) -> None:
13031302
bm1 = BitMap()
@@ -1561,7 +1560,6 @@ def test_copy_writable(self, list1: list[int], cow: bool) -> None:
15611560
assert new_element in b2
15621561
assert new_element not in b1
15631562

1564-
@pytest.mark.skipif(not is_32_bits, reason="not supported yet")
15651563
@given(bitmap_cls, small_integer_list, small_integer_list, st.booleans())
15661564
def test_overwrite(self, BitMapClass: type[EitherBitMap], list1: list[int], list2: list[int], cow: bool) -> None:
15671565
assume(set(list1) != set(list2))

0 commit comments

Comments
 (0)