# SPDX-License-Identifier: Apache-2.0 # Standard import random # Third Party import pytest pytest.importorskip( "lmcache.native_storage_ops", reason="native_storage_ops extension not built", ) # First Party from lmcache.native_storage_ops import Bitmap class TestBitmapSetClearTest: """Test set, clear, and test for various sizes (including non-multiple-of-8).""" @pytest.mark.parametrize("size", [1, 7, 8, 9, 10, 15, 16, 17, 24, 25]) def test_set_and_test(self, size): b = Bitmap(size) for i in range(size): assert not b.test(i) for i in range(0, size, 2): b.set(i) for i in range(size): assert b.test(i) == (i % 2 == 0) @pytest.mark.parametrize("size", [1, 7, 8, 9, 10, 15, 16, 17]) def test_clear(self, size): b = Bitmap(size) for i in range(size): b.set(i) for i in range(size): assert b.test(i) for i in range(0, size, 2): b.clear(i) for i in range(size): assert b.test(i) == (i % 2 == 1) @pytest.mark.parametrize("size", [1, 5, 9, 12]) def test_out_of_range_no_op(self, size): b = Bitmap(size) b.set(size) # no-op b.set(size + 10) # no-op b.clear(size) b.clear(size + 10) for i in range(size): assert not b.test(i) assert not b.test(size) assert not b.test(size + 1) def test_single_bit_size_one(self): b = Bitmap(1) assert not b.test(0) b.set(0) assert b.test(0) b.clear(0) assert not b.test(0) class TestBitmapSetRange: """set_range([start, end)) must match the equivalent per-bit set, including single-byte, head/middle/tail spans, clamping, and empty ranges.""" @pytest.mark.parametrize( "size,start,end", [ (16, 3, 3), # empty range -> no-op (16, 0, 0), # empty at 0 (16, 2, 5), # within one byte (16, 0, 8), # exactly one full byte (16, 0, 16), # whole bitmap (24, 3, 20), # head + middle byte + tail (24, 8, 16), # exactly the middle byte (10, 5, 100), # end clamped to size (10, 9, 10), # last bit only (10, 100, 200), # fully out of range -> no-op ], ) def test_set_range_matches_per_bit(self, size, start, end): ranged = Bitmap(size) ranged.set_range(start, end) per_bit = Bitmap(size) for i in range(start, min(end, size)): per_bit.set(i) assert ranged.get_indices_list() == per_bit.get_indices_list() def test_set_range_accumulates(self): b = Bitmap(32) b.set_range(2, 6) b.set_range(10, 12) assert b.get_indices_list() == [2, 3, 4, 5, 10, 11] def test_set_range_random_against_per_bit(self): rng = random.Random(0) for _ in range(500): size = rng.randint(0, 40) start = rng.randint(0, size + 4) end = rng.randint(0, size + 4) ranged = Bitmap(size) ranged.set_range(start, end) per_bit = Bitmap(size) for i in range(start, min(end, size)): per_bit.set(i) assert ranged.get_indices_list() == per_bit.get_indices_list() class TestBitmapPopcount: """Test popcount (count of set bits), especially for size not multiple of 8.""" @pytest.mark.parametrize("size", [1, 7, 8, 9, 10, 15, 16, 17, 24, 25]) def test_popcount_all_zeros(self, size): b = Bitmap(size) assert b.popcount() == 0 @pytest.mark.parametrize("size", [1, 7, 8, 9, 10, 15, 16, 17]) def test_popcount_all_ones(self, size): b = Bitmap(size) for i in range(size): b.set(i) assert b.popcount() == size @pytest.mark.parametrize("size", [1, 5, 9, 12, 20]) def test_popcount_partial(self, size): b = Bitmap(size) for i in range(0, size, 2): b.set(i) expected = (size + 1) // 2 assert b.popcount() == expected, f"size={size}" @pytest.mark.parametrize("size", [9, 12, 20]) def test_popcount_random(self, size): b = Bitmap(size) one_counts = 5 one_positions = random.sample(range(size), one_counts) for i in one_positions: b.set(i) assert b.popcount() == one_counts class TestBitmapClzClo: """Test count leading zeros (clz) and count leading ones (clo).""" @pytest.mark.parametrize("size", [1, 7, 8, 9, 10, 15, 16, 17]) def test_clz_all_zeros(self, size): b = Bitmap(size) assert b.count_leading_zeros() == size @pytest.mark.parametrize("size", [1, 7, 8, 9, 10, 17]) def test_clz_first_bit_set(self, size): b = Bitmap(size) b.set(0) assert b.count_leading_zeros() == 0 @pytest.mark.parametrize("size", [1, 7, 8, 9, 10]) def test_clz_second_bit_set(self, size): b = Bitmap(size) b.set(1) assert b.count_leading_zeros() == 1 def test_clz_middle_bits_set(self): # size=9: bits 0..8; set 3,4,5 -> leading zeros = 3 b = Bitmap(9) b.set(3) b.set(4) b.set(5) assert b.count_leading_zeros() == 3 def test_clz_last_bit_only_size_9(self): b = Bitmap(9) b.set(8) # only last bit (index 8) assert b.count_leading_zeros() == 8 @pytest.mark.parametrize("size", [1, 7, 8, 9, 10, 17]) def test_clo_all_ones(self, size): b = Bitmap(size) for i in range(size): b.set(i) assert b.count_leading_ones() == size @pytest.mark.parametrize("size", [1, 7, 8, 9, 10]) def test_clo_all_zeros(self, size): b = Bitmap(size) assert b.count_leading_ones() == 0 def test_clo_first_zero_at_3(self): b = Bitmap(9) b.set(0) b.set(1) b.set(2) # bits 3..8 are 0 assert b.count_leading_ones() == 3 def test_clo_partial_byte_size_10(self): # 10 bits: all set -> clo = 10 b = Bitmap(10) for i in range(10): b.set(i) assert b.count_leading_ones() == 10 def test_clo_partial_byte_first_zero_at_5(self): b = Bitmap(10) for i in range(5): b.set(i) assert b.count_leading_ones() == 5 class TestBitmapAnd: """Test bitwise AND between two bitmaps.""" @pytest.mark.parametrize("size", [1, 7, 8, 9, 10, 16, 17]) def test_and_same_size_all_overlap(self, size): a = Bitmap(size) b = Bitmap(size) for i in range(0, size, 2): a.set(i) for i in range(1, size, 2): b.set(i) c = a & b assert c.popcount() == 0 for i in range(size): assert not c.test(i) @pytest.mark.parametrize("size", [1, 9, 10]) def test_and_same_size_intersection(self, size): a = Bitmap(size) b = Bitmap(size) for i in range(size): a.set(i) for i in range(0, size, 2): b.set(i) c = a & b assert c.popcount() == (size + 1) // 2 for i in range(size): assert c.test(i) == (i % 2 == 0) def test_and_different_sizes_result_truncated(self): a = Bitmap(10) b = Bitmap(5) for i in range(10): a.set(i) for i in range(5): b.set(i) c = a & b # Result is min(10,5)=5 bits, all set assert c.popcount() == 5 for i in range(5): assert c.test(i) def test_and_different_sizes_other_longer(self): a = Bitmap(5) b = Bitmap(10) for i in range(5): a.set(i) for i in range(10): b.set(i) c = a & b assert c.popcount() == 5 for i in range(5): assert c.test(i) class TestBitmapToString: """Test string representation (bit 0 = leftmost character).""" def test_to_string_empty_zero_bits(self): # Size 0 might not be allowed; skip or use size 1 b = Bitmap(1) b.clear(0) assert "0" in str(b) @pytest.mark.parametrize("size", [1, 7, 8, 9, 10]) def test_to_string_matches_set_bits(self, size): b = Bitmap(size) for i in range(0, size, 2): b.set(i) s = str(b) print(s) assert len(s) == size for i in range(size): assert s[i] == "1" if (i % 2 == 0) else "0" def test_repr_calls_to_string(self): b = Bitmap(3) b.set(1) r = repr(b) assert "1" in r and "0" in r class TestBitmapOr: """Test bitwise OR between two bitmaps.""" @pytest.mark.parametrize("size", [1, 7, 8, 9, 10, 16, 17]) def test_or_same_size_no_overlap(self, size): a = Bitmap(size) b = Bitmap(size) for i in range(0, size, 2): a.set(i) for i in range(1, size, 2): b.set(i) c = a | b assert c.popcount() == size for i in range(size): assert c.test(i) @pytest.mark.parametrize("size", [1, 9, 10]) def test_or_same_size_with_overlap(self, size): a = Bitmap(size) b = Bitmap(size) for i in range(size): a.set(i) for i in range(0, size, 2): b.set(i) c = a | b assert c.popcount() == size def test_or_different_sizes_result_truncated(self): a = Bitmap(10) b = Bitmap(5) for i in range(0, 10, 2): a.set(i) for i in range(1, 5, 2): b.set(i) c = a | b # Result is min(10,5)=5 bits assert c.popcount() == 5 for i in range(5): assert c.test(i) def test_or_both_empty(self): a = Bitmap(8) b = Bitmap(8) c = a | b assert c.popcount() == 0 class TestBitmapGetIndices: """Test get_indices_list and get_indices_set.""" @pytest.mark.parametrize("size", [1, 7, 8, 9, 10, 17]) def test_get_indices_list_all_zeros(self, size): b = Bitmap(size) assert b.get_indices_list() == [] @pytest.mark.parametrize("size", [1, 7, 8, 9, 10, 17]) def test_get_indices_list_all_ones(self, size): b = Bitmap(size) for i in range(size): b.set(i) assert b.get_indices_list() == list(range(size)) @pytest.mark.parametrize("size", [9, 10, 17, 25]) def test_get_indices_list_even_bits(self, size): b = Bitmap(size) expected = list(range(0, size, 2)) for i in expected: b.set(i) assert b.get_indices_list() == expected @pytest.mark.parametrize("size", [9, 12, 20]) def test_get_indices_list_random(self, size): b = Bitmap(size) positions = sorted(random.sample(range(size), 5)) for i in positions: b.set(i) assert b.get_indices_list() == positions @pytest.mark.parametrize("size", [1, 7, 8, 9, 10, 17]) def test_get_indices_set_all_zeros(self, size): b = Bitmap(size) assert b.get_indices_set() == set() @pytest.mark.parametrize("size", [1, 7, 8, 9, 10, 17]) def test_get_indices_set_all_ones(self, size): b = Bitmap(size) for i in range(size): b.set(i) assert b.get_indices_set() == set(range(size)) @pytest.mark.parametrize("size", [9, 12, 20]) def test_get_indices_set_random(self, size): b = Bitmap(size) positions = random.sample(range(size), 5) for i in positions: b.set(i) assert b.get_indices_set() == set(positions) def test_get_indices_list_and_set_consistent(self): b = Bitmap(17) for i in [0, 3, 8, 15, 16]: b.set(i) assert set(b.get_indices_list()) == b.get_indices_set() class TestBitmapGather: """Test gather operation.""" def test_gather_basic(self): b = Bitmap(5) b.set(0) b.set(2) b.set(4) items = ["a", "b", "c", "d", "e"] assert b.gather(items) == ["a", "c", "e"] def test_gather_all_set(self): b = Bitmap(4) for i in range(4): b.set(i) items = [10, 20, 30, 40] assert b.gather(items) == [10, 20, 30, 40] def test_gather_none_set(self): b = Bitmap(3) items = ["x", "y", "z"] assert b.gather(items) == [] def test_gather_mixed_types(self): b = Bitmap(4) b.set(1) b.set(3) items = [1, "two", 3.0, None] assert b.gather(items) == ["two", None] @pytest.mark.parametrize("size", [9, 10, 17]) def test_gather_preserves_order(self, size): b = Bitmap(size) positions = sorted(random.sample(range(size), 5)) for i in positions: b.set(i) items = list(range(size)) assert b.gather(items) == positions def test_gather_single_element(self): b = Bitmap(1) b.set(0) assert b.gather(["only"]) == ["only"] def test_gather_accepts_tuple(self): # gather takes any sequence, not just list (e.g. l2_orig_indices tuple). b = Bitmap(5) b.set(1) b.set(3) assert b.gather((10, 11, 12, 13, 14)) == [11, 13] class TestBitmapInvert: """Test bitwise NOT (~) operation.""" @pytest.mark.parametrize("size", [1, 7, 8, 9, 10, 16, 17]) def test_invert_all_zeros_gives_all_ones(self, size): b = Bitmap(size) inv = ~b assert inv.popcount() == size for i in range(size): assert inv.test(i) @pytest.mark.parametrize("size", [1, 7, 8, 9, 10, 16, 17]) def test_invert_all_ones_gives_all_zeros(self, size): b = Bitmap(size) for i in range(size): b.set(i) inv = ~b assert inv.popcount() == 0 for i in range(size): assert not inv.test(i) @pytest.mark.parametrize("size", [1, 7, 8, 9, 10, 17, 25]) def test_invert_flips_each_bit(self, size): b = Bitmap(size) for i in range(0, size, 2): b.set(i) inv = ~b for i in range(size): assert inv.test(i) == (i % 2 == 1) @pytest.mark.parametrize("size", [1, 7, 8, 9, 10, 17]) def test_double_invert_is_identity(self, size): b = Bitmap(size) positions = [i for i in range(size) if i % 3 == 0] for i in positions: b.set(i) result = ~(~b) for i in range(size): assert result.test(i) == b.test(i) def test_invert_does_not_mutate_original(self): b = Bitmap(10) b.set(0) b.set(5) _ = ~b assert b.popcount() == 2 assert b.test(0) assert b.test(5) @pytest.mark.parametrize("size", [9, 10, 17]) def test_invert_popcount_complement(self, size): b = Bitmap(size) for i in range(0, size, 3): b.set(i) inv = ~b assert b.popcount() + inv.popcount() == size def test_invert_and_original_gives_zero(self): b = Bitmap(10) for i in [1, 3, 5, 7, 9]: b.set(i) result = b & ~b assert result.popcount() == 0 def test_invert_or_original_gives_all_ones(self): b = Bitmap(10) for i in [1, 3, 5, 7, 9]: b.set(i) result = b | ~b assert result.popcount() == 10 class TestBitmapPrefixConstructor: """Test Bitmap(size, prefix_bits) constructor.""" @pytest.mark.parametrize("size", [1, 7, 8, 9, 10, 16, 17, 25]) def test_prefix_zero(self, size): b = Bitmap(size, 0) assert b.popcount() == 0 @pytest.mark.parametrize("size", [1, 7, 8, 9, 10, 16, 17, 25]) def test_prefix_all(self, size): b = Bitmap(size, size) assert b.popcount() == size assert b.count_leading_ones() == size @pytest.mark.parametrize("size", [8, 9, 10, 16, 17, 25]) def test_prefix_partial(self, size): prefix = size // 2 b = Bitmap(size, prefix) assert b.popcount() == prefix assert b.count_leading_ones() == prefix for i in range(prefix): assert b.test(i) for i in range(prefix, size): assert not b.test(i) @pytest.mark.parametrize("size", [1, 7, 8, 9, 16, 17]) def test_prefix_one(self, size): b = Bitmap(size, 1) assert b.popcount() == 1 assert b.test(0) for i in range(1, size): assert not b.test(i) def test_prefix_exceeds_size_clamped(self): b = Bitmap(5, 100) assert b.popcount() == 5 assert b.count_leading_ones() == 5 @pytest.mark.parametrize("size", [8, 16, 24]) def test_prefix_exact_byte_boundary(self, size): b = Bitmap(size, 8) assert b.popcount() == 8 assert b.count_leading_ones() == 8 for i in range(8): assert b.test(i) for i in range(8, size): assert not b.test(i) @pytest.mark.parametrize( "size,prefix", [(9, 3), (10, 5), (17, 9), (25, 13)], ) def test_prefix_matches_manual_set(self, size, prefix): """Bitmap(size, prefix) should be identical to setting bits 0..prefix-1.""" a = Bitmap(size, prefix) b = Bitmap(size) for i in range(prefix): b.set(i) assert a.popcount() == b.popcount() assert a.get_indices_list() == b.get_indices_list() assert str(a) == str(b) def test_prefix_zero_size(self): b = Bitmap(0, 0) assert b.popcount() == 0 class TestBitmapNonMultipleOfEight: """Focused tests when length is not a multiple of 8.""" @pytest.mark.parametrize("size", [1, 2, 3, 5, 7, 9, 10, 11, 15, 17, 23, 25]) def test_roundtrip_set_test_all_positions(self, size): b = Bitmap(size) for i in range(size): b.set(i) assert b.test(i) b.clear(i) assert not b.test(i) b.set(i) @pytest.mark.parametrize("size", [9, 10, 12, 17]) def test_popcount_only_low_bits_counted_in_last_byte(self, size): b = Bitmap(size) for i in range(size): b.set(i) assert b.popcount() == size @pytest.mark.parametrize("size", [9, 10, 12]) def test_clz_clo_consistent_with_test(self, size): b = Bitmap(size) b.set(size - 1) # only last bit set assert b.count_leading_zeros() == size - 1 assert b.count_leading_ones() == 0 b2 = Bitmap(size) for i in range(size - 1): b2.set(i) assert b2.count_leading_ones() == size - 1 assert b2.count_leading_zeros() == 0 class TestBatchedSet: """Bitmap.batched_set(indices): set every listed bit; positions >= size are ignored (bounds-checked, like set()).""" def test_sets_listed_bits(self): b = Bitmap(8) b.batched_set([1, 3, 7]) assert b.get_indices_set() == {1, 3, 7} def test_accumulates_with_existing(self): b = Bitmap(8) b.set(0) b.batched_set([2, 4]) assert b.get_indices_set() == {0, 2, 4} def test_out_of_range_ignored(self): b = Bitmap(5) b.batched_set([1, 5, 99]) # 5 and 99 are >= size -> dropped assert b.get_indices_set() == {1} def test_empty_is_noop(self): b = Bitmap(4) b.set(2) b.batched_set([]) assert b.get_indices_set() == {2} def test_duplicates_idempotent(self): b = Bitmap(4) b.batched_set([1, 1, 1]) assert b.get_indices_set() == {1} def test_accepts_tuple(self): b = Bitmap(4) b.batched_set((0, 3)) assert b.get_indices_set() == {0, 3} def test_matches_python_reference(self): rng = random.Random(1234) for _ in range(50): size = rng.randint(1, 64) indices = rng.sample(range(size), k=rng.randint(0, size)) b = Bitmap(size) b.batched_set(indices) assert b.get_indices_set() == set(indices) def test_combine_found_pattern(self): # The _combine_found composition: seed L1 indices, then batched_set the # L2 set bits gathered through their original-position map. l2 = Bitmap(3) # L2 result over the 3 L2-submitted keys l2.set(0) l2.set(2) l2_orig = (3, 5, 7) # L2-local i -> original position found = Bitmap(10) found.batched_set([0, 1, 2]) # L1 prefix found.batched_set(l2.gather(l2_orig)) # -> {3, 7} assert found.get_indices_set() == {0, 1, 2, 3, 7}