package many_bits import "base:builtin" import "base:intrinsics" import "core:fmt" import "core:slice" @(private) ODIN_BOUNDS_CHECK :: !ODIN_NO_BOUNDS_CHECK // Number of bits in system uint UINT_NUM_BITS :: size_of(uint) * 8 UINT_MAX: uint : 1 << UINT_NUM_BITS - 1 // Power to which 2 is raised to get the size of uint in bits // For bitshift division which gives index of integer in int_bits_array INDEX_SHIFT :: uint(intrinsics.count_trailing_zeros(UINT_NUM_BITS)) // Value to & overall index by to get bit position BIT_POS_MASK :: UINT_NUM_BITS - 1 Int_Bits :: bit_set[0 ..< UINT_NUM_BITS;uint] // Use `core:container.Bit_Array` if dynamic length is needed. // This has a more specific purpose. Bits :: struct { int_array: []Int_Bits, length: int, // Total number of bits being stored } delete :: proc(using bits: Bits, allocator := context.allocator) { delete_slice(int_array, allocator) } make :: proc(#any_int length: int, allocator := context.allocator) -> Bits { return Bits { int_array = make_slice([]Int_Bits, ((length - 1) >> INDEX_SHIFT) + 1, allocator), length = length, } } // Sets all bits to 0 (false) zero :: #force_inline proc(bits: Bits) { slice.zero(bits.int_array) } set :: #force_inline proc(bits: Bits, #any_int index: int, set_to: bool) { when ODIN_BOUNDS_CHECK { if index >= bits.length { panic(fmt.tprintf("Bit position %i out of bounds for length %i.", index, bits.length)) } } if set_to == true { bits.int_array[index >> INDEX_SHIFT] += {index & BIT_POS_MASK} } else { bits.int_array[index >> INDEX_SHIFT] -= {index & BIT_POS_MASK} } } set_true :: #force_inline proc(bits: Bits, #any_int index: int) { when ODIN_BOUNDS_CHECK { if index >= bits.length { panic(fmt.tprintf("Bit position %i out of bounds for length %i.", index, bits.length)) } } bits.int_array[index >> INDEX_SHIFT] += {index & BIT_POS_MASK} } set_one :: set_true set_false :: #force_inline proc(bits: Bits, #any_int index: int) { when ODIN_BOUNDS_CHECK { if index >= bits.length { panic(fmt.tprintf("Bit position %i out of bounds for length %i.", index, bits.length)) } } bits.int_array[index >> INDEX_SHIFT] -= {index & BIT_POS_MASK} } set_zero :: set_false get :: #force_inline proc(bits: Bits, #any_int index: int) -> bool { when ODIN_BOUNDS_CHECK { if index >= bits.length { panic(fmt.tprintf("Bit position %i out of bounds for length %i.", index, bits.length)) } } return (index & BIT_POS_MASK) in bits.int_array[index >> INDEX_SHIFT] } // Returns true if all bits in range [start, end) are set [start is inclusive, end is exclusive) range_true :: proc(bits: Bits, #any_int start, end: int) -> bool { when ODIN_BOUNDS_CHECK { if start < 0 { panic(fmt.tprintf("Start %i is negative.", start)) } if start > end { panic(fmt.tprintf("Start %i is greater than end %i.", start, end)) } if end > bits.length { panic(fmt.tprintf("End %i out of bounds for length %i.", end, bits.length)) } } // Empty range is vacuously true if start == end do return true start_u := uint(start) end_u := uint(end) start_word := start_u >> INDEX_SHIFT end_word := (end_u - 1) >> INDEX_SHIFT start_bit := start_u & BIT_POS_MASK end_bit := end_u & BIT_POS_MASK // end is exclusive; 0 means "to end of word" // Range is within a single word if start_word == end_word { word := transmute(uint)bits.int_array[start_word] low_mask: uint = (uint(1) << start_bit) - 1 high_mask: uint = ((uint(1) << end_bit) - 1) | (UINT_MAX * uint(end_bit == 0)) mask := high_mask & ~low_mask return word & mask == mask } // Range spans multiple words // First word: [start_bit, UINT_NUM_BITS) if start_bit != 0 { first_word := transmute(uint)bits.int_array[start_word] start_mask: uint = ~((uint(1) << start_bit) - 1) if first_word & start_mask != start_mask { return false } start_word += 1 } // Last word: [0, end_bit) // If end_bit == 0, we need the whole last word, so include it in the middle scan. if end_bit != 0 { last_word := transmute(uint)bits.int_array[end_word] end_mask: uint = (uint(1) << end_bit) - 1 if last_word & end_mask != end_mask { return false } } else { end_word += 1 } // Middle words: all bits must be set for i := start_word; i < end_word; i += 1 { if transmute(uint)bits.int_array[i] != UINT_MAX { return false } } return true } range_ones :: range_true all_true :: proc(bits: Bits) -> bool { // Empty bit array is vacuously true if bits.length == 0 do return true bit_index := uint(bits.length - 1) int_index := bit_index >> INDEX_SHIFT // The last int needs special treatment because we only want to check part of it last_bit_pos := bit_index & BIT_POS_MASK last_bit_mask: uint = (1 << (last_bit_pos + 1)) - 1 int_val := transmute(uint)bits.int_array[int_index] if int_val & last_bit_mask != last_bit_mask { return false } if int_index == 0 { // If there was only 1 int in the array return true } int_index -= 1 // All other ints should be all 1s for { int_val := transmute(uint)bits.int_array[int_index] if int_val != UINT_MAX { return false } if int_index == 0 { return true } int_index -= 1 } } all_ones :: all_true // Returns ok = false if there are no 1 bits in the entire array. nearest_true :: proc(bits: Bits, index: int) -> (nearest: int, ok: bool) { when ODIN_BOUNDS_CHECK { if index >= bits.length { panic(fmt.tprintf("Bit position %i out of bounds for length %i.", index, bits.length)) } } bit_index := uint(index) word_index := bit_index >> INDEX_SHIFT bit_pos := bit_index & BIT_POS_MASK word_index_int := int(word_index) total_words := len(bits.int_array) max_left := word_index_int max_right := total_words - 1 - word_index_int max_offset := max(max_left, max_right) word_val := transmute(uint)bits.int_array[word_index_int] if word_val != 0 { if (word_val & (uint(1) << bit_pos)) != 0 do return index, true left_mask := (uint(1) << bit_pos) | ((uint(1) << bit_pos) - 1) left_bits_value := word_val & left_mask right_mask := ~((uint(1) << bit_pos) - 1) right_bits_value := word_val & right_mask nearest_left := 0 left_found := false if left_bits_value != 0 { left_offset_from_top := intrinsics.count_leading_zeros(left_bits_value) left_bit := (UINT_NUM_BITS - 1) - left_offset_from_top nearest_left = (word_index_int << INDEX_SHIFT) + int(left_bit) left_found = true } nearest_right := 0 right_found := false if right_bits_value != 0 { right_offset := intrinsics.count_trailing_zeros(right_bits_value) nearest_right = (word_index_int << INDEX_SHIFT) + int(right_offset) right_found = true } if left_found && right_found { left_dist := index - nearest_left right_dist := nearest_right - index if left_dist <= right_dist { return nearest_left, true } else { return nearest_right, true } } else if left_found { return nearest_left, true } else if right_found { return nearest_right, true } } for offset := 1; offset <= max_offset; offset += 1 { right_found := false left_found := false nearest_right := 0 nearest_left := 0 right_dist := 0 left_dist := 0 right_index := word_index_int + offset if right_index < total_words { word_val := transmute(uint)bits.int_array[right_index] if word_val != 0 { right_offset := intrinsics.count_trailing_zeros(word_val) nearest_right = (right_index << INDEX_SHIFT) + int(right_offset) right_found = true right_dist = nearest_right - index } } left_index := word_index_int - offset if left_index >= 0 { word_val := transmute(uint)bits.int_array[left_index] if word_val != 0 { left_offset_from_top := intrinsics.count_leading_zeros(word_val) left_bit := (UINT_NUM_BITS - 1) - left_offset_from_top nearest_left = (left_index << INDEX_SHIFT) + int(left_bit) left_found = true left_dist = index - nearest_left } } if left_found && right_found { if left_dist <= right_dist { return nearest_left, true } else { return nearest_right, true } } else if left_found { return nearest_left, true } else if right_found { return nearest_right, true } } return } nearest_one :: nearest_true // Returns ok = false if there are no 0 bits in the entire array. nearest_false :: proc(bits: Bits, index: int) -> (nearest: int, ok: bool) { when ODIN_BOUNDS_CHECK { if index >= bits.length { panic(fmt.tprintf("Bit position %i out of bounds for length %i.", index, bits.length)) } } bit_index := uint(index) word_index := bit_index >> INDEX_SHIFT bit_pos := bit_index & BIT_POS_MASK word_index_int := int(word_index) total_words := len(bits.int_array) max_left := word_index_int max_right := total_words - 1 - word_index_int max_offset := max(max_left, max_right) last_bit_index := uint(bits.length - 1) last_word_index := int(last_bit_index >> INDEX_SHIFT) last_bit_pos := last_bit_index & BIT_POS_MASK valid_bits_mask: uint if last_bit_pos == UINT_NUM_BITS - 1 { valid_bits_mask = UINT_MAX } else { valid_bits_mask = (uint(1) << (last_bit_pos + 1)) - 1 } word_val := transmute(uint)bits.int_array[word_index_int] word_val_search := ~word_val if word_index_int == last_word_index { word_val_search &= valid_bits_mask } if word_val_search != 0 { if (word_val & (uint(1) << bit_pos)) == 0 do return index, true left_mask := (uint(1) << bit_pos) | ((uint(1) << bit_pos) - 1) left_bits_value := word_val_search & left_mask right_mask := ~((uint(1) << bit_pos) - 1) right_bits_value := word_val_search & right_mask nearest_left := 0 left_found := false if left_bits_value != 0 { left_offset_from_top := intrinsics.count_leading_zeros(left_bits_value) left_bit := (UINT_NUM_BITS - 1) - left_offset_from_top nearest_left = (word_index_int << INDEX_SHIFT) + int(left_bit) left_found = true } nearest_right := 0 right_found := false if right_bits_value != 0 { right_offset := intrinsics.count_trailing_zeros(right_bits_value) nearest_right = (word_index_int << INDEX_SHIFT) + int(right_offset) right_found = true } if left_found && right_found { left_dist := index - nearest_left right_dist := nearest_right - index if left_dist <= right_dist { return nearest_left, true } else { return nearest_right, true } } else if left_found { return nearest_left, true } else if right_found { return nearest_right, true } } for offset := 1; offset <= max_offset; offset += 1 { right_found := false left_found := false nearest_right := 0 nearest_left := 0 right_dist := 0 left_dist := 0 right_index := word_index_int + offset if right_index < total_words { word_val := transmute(uint)bits.int_array[right_index] word_val_search := ~word_val if right_index == last_word_index { word_val_search &= valid_bits_mask } if word_val_search != 0 { right_offset := intrinsics.count_trailing_zeros(word_val_search) nearest_right = (right_index << INDEX_SHIFT) + int(right_offset) right_found = true right_dist = nearest_right - index } } left_index := word_index_int - offset if left_index >= 0 { word_val := transmute(uint)bits.int_array[left_index] word_val_search := ~word_val if word_val_search != 0 { left_offset_from_top := intrinsics.count_leading_zeros(word_val_search) left_bit := (UINT_NUM_BITS - 1) - left_offset_from_top nearest_left = (left_index << INDEX_SHIFT) + int(left_bit) left_found = true left_dist = index - nearest_left } } if left_found && right_found { if left_dist <= right_dist { return nearest_left, true } else { return nearest_right, true } } else if left_found { return nearest_left, true } else if right_found { return nearest_right, true } } return } nearest_zero :: nearest_false Iterator :: struct { bits: ^Bits, word_idx: int, bit_idx: uint, } iterator :: #force_inline proc(bits: ^Bits) -> Iterator { return {bits = bits} } iterate :: proc(iterator: ^Iterator) -> (is_true: bool, idx: int, cond: bool) { idx = iterator.word_idx * UINT_NUM_BITS + int(iterator.bit_idx) if idx >= iterator.bits.length { return false, 0, false } word := transmute(uint)iterator.bits.int_array[iterator.word_idx] is_true = (word >> iterator.bit_idx & 1) == 1 iterator.bit_idx += 1 if iterator.bit_idx >= UINT_NUM_BITS { iterator.bit_idx = 0 iterator.word_idx += 1 } return is_true, idx, true } @(private = "file") _iterate_kind :: #force_inline proc(iterator: ^Iterator, $ITERATE_ZEROS: bool) -> (idx: int, cond: bool) { for iterator.word_idx < len(iterator.bits.int_array) { word := transmute(uint)iterator.bits.int_array[iterator.word_idx] when ITERATE_ZEROS do word = ~word word >>= iterator.bit_idx // Mask out already-processed bits if word != 0 { // Found a bit - count_trailing_zeros gives position in shifted word iterator.bit_idx += uint(intrinsics.count_trailing_zeros(word)) idx = iterator.word_idx * UINT_NUM_BITS + int(iterator.bit_idx) // Advance for next call iterator.bit_idx += 1 if iterator.bit_idx >= UINT_NUM_BITS { iterator.bit_idx = 0 iterator.word_idx += 1 } return idx, idx < iterator.bits.length } // Word exhausted, move to next iterator.word_idx += 1 iterator.bit_idx = 0 } return 0, false } iterate_true :: proc(iterator: ^Iterator) -> (idx: int, cond: bool) { return _iterate_kind(iterator, ITERATE_ZEROS = false) } iterate_ones :: iterate_true iterate_false :: proc(iterator: ^Iterator) -> (idx: int, cond: bool) { return _iterate_kind(iterator, ITERATE_ZEROS = true) } iterate_zeros :: iterate_false // --------------------------------------------------------------------------------------------------------------------- // ----- Tests ------------------------ // --------------------------------------------------------------------------------------------------------------------- import "core:testing" @(test) test_set :: proc(t: ^testing.T) { bits := make(128) defer delete(bits) set(bits, 0, true) testing.expect_value(t, bits.int_array[0], Int_Bits{0}) set(bits, 3, true) testing.expect_value(t, bits.int_array[0], Int_Bits{0, 3}) set(bits, 64, true) testing.expect_value(t, bits.int_array[1], Int_Bits{0}) set(bits, 127, true) testing.expect_value(t, bits.int_array[1], Int_Bits{0, 63}) set(bits, 127, false) testing.expect_value(t, bits.int_array[1], Int_Bits{0}) } @(test) test_get :: proc(t: ^testing.T) { bits := make(128) defer delete(bits) // Default is false testing.expect(t, !get(bits, 0)) testing.expect(t, !get(bits, 63)) testing.expect(t, !get(bits, 64)) testing.expect(t, !get(bits, 127)) // Set and verify within first uint set(bits, 0, true) testing.expect(t, get(bits, 0)) testing.expect(t, !get(bits, 1)) set(bits, 3, true) testing.expect(t, get(bits, 3)) testing.expect(t, !get(bits, 2)) testing.expect(t, !get(bits, 4)) // Cross uint boundary set(bits, 64, true) testing.expect(t, get(bits, 64)) testing.expect(t, !get(bits, 63)) testing.expect(t, !get(bits, 65)) // Last bit set(bits, 127, true) testing.expect(t, get(bits, 127)) // Unset and verify set(bits, 127, false) testing.expect(t, !get(bits, 127)) } @(test) test_set_true_set_false :: proc(t: ^testing.T) { bits := make(128) defer delete(bits) // set_true within first uint set_true(bits, 0) testing.expect_value(t, bits.int_array[0], Int_Bits{0}) testing.expect(t, get(bits, 0)) set_true(bits, 3) testing.expect_value(t, bits.int_array[0], Int_Bits{0, 3}) testing.expect(t, get(bits, 3)) // set_true across uint boundary set_true(bits, 64) testing.expect_value(t, bits.int_array[1], Int_Bits{0}) testing.expect(t, get(bits, 64)) testing.expect(t, !get(bits, 63)) testing.expect(t, !get(bits, 65)) // set_true on last bit set_true(bits, 127) testing.expect_value(t, bits.int_array[1], Int_Bits{0, 63}) testing.expect(t, get(bits, 127)) // set_false to clear bits set_false(bits, 127) testing.expect_value(t, bits.int_array[1], Int_Bits{0}) testing.expect(t, !get(bits, 127)) set_false(bits, 0) testing.expect_value(t, bits.int_array[0], Int_Bits{3}) testing.expect(t, !get(bits, 0)) testing.expect(t, get(bits, 3)) // bit 3 still set // set_false on already-false bit (should be no-op) set_false(bits, 1) testing.expect_value(t, bits.int_array[0], Int_Bits{3}) testing.expect(t, !get(bits, 1)) } @(test) all_true_test :: proc(t: ^testing.T) { uint_max := UINT_MAX all_ones := transmute(Int_Bits)uint_max bits := make(132) defer delete(bits) bits.int_array[0] = all_ones bits.int_array[1] = all_ones bits.int_array[2] = {0, 1, 2, 3} testing.expect(t, all_true(bits)) bits.int_array[2] = {0, 1, 2} testing.expect(t, !all_true(bits)) bits2 := make(1) defer delete(bits2) bits2.int_array[0] = {0} testing.expect(t, all_true(bits2)) } @(test) test_range_true :: proc(t: ^testing.T) { uint_max := UINT_MAX all_ones := transmute(Int_Bits)uint_max bits := make(192) defer delete(bits) // Empty range is vacuously true testing.expect(t, range_true(bits, 0, 0)) testing.expect(t, range_true(bits, 50, 50)) // inverted range should panic under bounds checking; keep this test case out of here // Single word, partial range bits.int_array[0] = {3, 4, 5, 6} testing.expect(t, range_true(bits, 3, 7)) testing.expect(t, !range_true(bits, 2, 7)) // bit 2 not set testing.expect(t, !range_true(bits, 3, 8)) // bit 7 not set // Single word, full word bits.int_array[0] = all_ones testing.expect(t, range_true(bits, 0, 64)) // Range spanning two words bits.int_array[0] = all_ones bits.int_array[1] = {0, 1, 2, 3} testing.expect(t, range_true(bits, 60, 68)) // bits 60-63 in word 0, bits 0-3 in word 1 testing.expect(t, !range_true(bits, 60, 69)) // bit 68 (4 in word 1) not set // Range spanning three words with full middle word bits.int_array[0] = all_ones bits.int_array[1] = all_ones bits.int_array[2] = {0, 1, 2, 3} testing.expect(t, range_true(bits, 60, 132)) // partial first, full middle, partial last testing.expect(t, !range_true(bits, 60, 133)) // bit 132 (4 in word 2) not set // Middle word not all set bits.int_array[1] = all_ones - {32} testing.expect(t, !range_true(bits, 60, 132)) // Boundary: range ends exactly at word boundary bits.int_array[0] = all_ones bits.int_array[1] = all_ones testing.expect(t, range_true(bits, 32, 128)) // Boundary: range starts exactly at word boundary bits.int_array[1] = all_ones bits.int_array[2] = all_ones testing.expect(t, range_true(bits, 64, 192)) } @(test) nearest_true_handles_same_word_and_boundaries :: proc(t: ^testing.T) { bits := make(128, context.temp_allocator) set_true(bits, 0) set_true(bits, 10) set_true(bits, 20) set_true(bits, 63) nearest, ok := nearest_true(bits, 10) testing.expect(t, ok) testing.expect_value(t, nearest, 10) nearest, ok = nearest_true(bits, 12) testing.expect(t, ok) testing.expect_value(t, nearest, 10) nearest, ok = nearest_true(bits, 17) testing.expect(t, ok) testing.expect_value(t, nearest, 20) nearest, ok = nearest_true(bits, 15) testing.expect(t, ok) testing.expect_value(t, nearest, 10) nearest, ok = nearest_true(bits, 0) testing.expect(t, ok) testing.expect_value(t, nearest, 0) nearest, ok = nearest_true(bits, 63) testing.expect(t, ok) testing.expect_value(t, nearest, 63) } @(test) nearest_false_handles_same_word_and_boundaries :: proc(t: ^testing.T) { bits := make(128, context.temp_allocator) // Start with all bits true, then clear a few to false. for i := 0; i < bits.length; i += 1 { set_true(bits, i) } set_false(bits, 0) set_false(bits, 10) set_false(bits, 20) set_false(bits, 63) nearest, ok := nearest_false(bits, 10) testing.expect(t, ok) testing.expect_value(t, nearest, 10) nearest, ok = nearest_false(bits, 12) testing.expect(t, ok) testing.expect_value(t, nearest, 10) nearest, ok = nearest_false(bits, 17) testing.expect(t, ok) testing.expect_value(t, nearest, 20) nearest, ok = nearest_false(bits, 15) testing.expect(t, ok) testing.expect_value(t, nearest, 10) nearest, ok = nearest_false(bits, 0) testing.expect(t, ok) testing.expect_value(t, nearest, 0) nearest, ok = nearest_false(bits, 63) testing.expect(t, ok) testing.expect_value(t, nearest, 63) } @(test) nearest_false_scans_across_words_and_returns_false_when_all_true :: proc(t: ^testing.T) { bits := make(192, context.temp_allocator) // Start with all bits true, then clear a couple far apart. for i := 0; i < bits.length; i += 1 { set_true(bits, i) } set_false(bits, 5) set_false(bits, 130) nearest, ok := nearest_false(bits, 96) testing.expect(t, ok) testing.expect_value(t, nearest, 130) // Restore the only zero bits so there are no zeros left. set_true(bits, 5) set_true(bits, 130) nearest, ok = nearest_false(bits, 96) testing.expect(t, !ok) } @(test) nearest_true_scans_across_words_and_returns_false_when_empty :: proc(t: ^testing.T) { bits := make(192, context.temp_allocator) set_true(bits, 5) set_true(bits, 130) nearest, ok := nearest_true(bits, 96) testing.expect(t, ok) testing.expect_value(t, nearest, 130) zero(bits) nearest, ok = nearest_true(bits, 96) testing.expect(t, !ok) } @(test) nearest_false_handles_last_word_partial_length :: proc(t: ^testing.T) { bits := make(130, context.temp_allocator) // Start with all bits true, then clear the first and last valid bits. for i := 0; i < bits.length; i += 1 { set_true(bits, i) } set_false(bits, 0) set_false(bits, 129) nearest, ok := nearest_false(bits, 128) testing.expect(t, ok) testing.expect_value(t, nearest, 129) nearest, ok = nearest_false(bits, 127) testing.expect(t, ok) testing.expect_value(t, nearest, 129) } @(test) nearest_true_handles_last_word_partial_length :: proc(t: ^testing.T) { bits := make(130, context.temp_allocator) set_true(bits, 0) set_true(bits, 129) nearest, ok := nearest_true(bits, 128) testing.expect(t, ok) testing.expect_value(t, nearest, 129) nearest, ok = nearest_true(bits, 127) testing.expect(t, ok) testing.expect_value(t, nearest, 129) } @(test) iterator_basic_mixed_bits :: proc(t: ^testing.T) { // Use non-word-aligned length to test partial last word handling bits := make(100, context.temp_allocator) // Set specific bits: 0, 3, 64, 99 (last valid index) set_true(bits, 0) set_true(bits, 3) set_true(bits, 64) set_true(bits, 99) expected_true_indices := [?]int{0, 3, 64, 99} // Test iterate - should return all 100 bits with correct set state { it := iterator(&bits) count := 0 for is_set, idx in iterate(&it) { expected_set := slice.contains(expected_true_indices[:], idx) testing.expectf( t, is_set == expected_set, "iterate: bit %d expected is_set=%v, got %v", idx, expected_set, is_set, ) testing.expectf(t, idx == count, "iterate: expected sequential idx=%d, got %d", count, idx) count += 1 } testing.expectf(t, count == 100, "iterate: expected 100 iterations, got %d", count) } // Test iterate_true - should only return the 4 set bits { it := iterator(&bits) result_indices := builtin.make([dynamic]int, allocator = context.temp_allocator) for idx in iterate_true(&it) { append(&result_indices, idx) } testing.expectf( t, len(result_indices) == 4, "iterate_true: expected 4 set bits, got %d", len(result_indices), ) for expected_idx, i in expected_true_indices { testing.expectf( t, result_indices[i] == expected_idx, "iterate_true: at position %d expected idx=%d, got %d", i, expected_idx, result_indices[i], ) } } // Test iterate_false - should return all 96 unset bits { it := iterator(&bits) count := 0 for idx in iterate_false(&it) { testing.expectf( t, !slice.contains(expected_true_indices[:], idx), "iterate_false: returned set bit index %d", idx, ) count += 1 } testing.expectf(t, count == 96, "iterate_false: expected 96 unset bits, got %d", count) } } @(test) iterator_all_false_bits :: proc(t: ^testing.T) { // Use non-word-aligned length bits := make(100, context.temp_allocator) // All bits default to false, no need to set anything // Test iterate - should return all 100 bits as false { it := iterator(&bits) count := 0 for is_set, idx in iterate(&it) { testing.expectf(t, !is_set, "iterate: bit %d expected is_set=false, got true", idx) testing.expectf(t, idx == count, "iterate: expected sequential idx=%d, got %d", count, idx) count += 1 } testing.expectf(t, count == 100, "iterate: expected 100 iterations, got %d", count) } // Test iterate_true - should return nothing { it := iterator(&bits) count := 0 for idx in iterate_true(&it) { testing.expectf(t, false, "iterate_true: unexpectedly returned idx=%d when all bits are false", idx) count += 1 } testing.expectf(t, count == 0, "iterate_true: expected 0 iterations, got %d", count) } // Test iterate_false - should return all 100 indices { it := iterator(&bits) count := 0 for idx in iterate_false(&it) { testing.expectf(t, idx == count, "iterate_false: expected sequential idx=%d, got %d", count, idx) count += 1 } testing.expectf(t, count == 100, "iterate_false: expected 100 iterations, got %d", count) } } @(test) iterator_all_true_bits :: proc(t: ^testing.T) { // Use non-word-aligned length bits := make(100, context.temp_allocator) // Set all bits to true for i := 0; i < bits.length; i += 1 { set_true(bits, i) } // Test iterate - should return all 100 bits as true { it := iterator(&bits) count := 0 for is_set, idx in iterate(&it) { testing.expectf(t, is_set, "iterate: bit %d expected is_set=true, got false", idx) testing.expectf(t, idx == count, "iterate: expected sequential idx=%d, got %d", count, idx) count += 1 } testing.expectf(t, count == 100, "iterate: expected 100 iterations, got %d", count) } // Test iterate_true - should return all 100 indices { it := iterator(&bits) count := 0 for idx in iterate_true(&it) { testing.expectf(t, idx == count, "iterate_true: expected sequential idx=%d, got %d", count, idx) count += 1 } testing.expectf(t, count == 100, "iterate_true: expected 100 iterations, got %d", count) } // Test iterate_false - should return nothing { it := iterator(&bits) count := 0 for idx in iterate_false(&it) { testing.expectf(t, false, "iterate_false: unexpectedly returned idx=%d when all bits are true", idx) count += 1 } testing.expectf(t, count == 0, "iterate_false: expected 0 iterations, got %d", count) } }