def apply_to(self, num: ThirtySixBitNumber) -> list[ThirtySixBitNumber]: if num in self.seen_applications: return self.seen_applications[num] number_of_copies = int(math.pow(2, self.number_of_xs_in_mask)) copies = [num.copy()] + ([num.copy()] * (number_of_copies - 1)) for i, m in enumerate(self.mask): if m == "1": for copy in copies: copy.set_bit_in_place(i, 1) seen_xs = 0 for index in range(0, 36): if self.mask[index] != "X": continue # now repeat by 2^seen_xs across the list # so 2^0 is 1 -> goes 01010101 # 2^1 is 2 -> goes 001100110011 # 2^2 is 4 -> goes 0000111100001111 for _ in range(0, number_of_copies): pattern = [0] * pow(2, seen_xs) + [1] * pow(2, seen_xs) pattern_generator = itertools.cycle(pattern) for copy_index, copy in enumerate(copies): copies[copy_index] = copy.set_bit(index, next(pattern_generator)) seen_xs += 1 self.seen_applications[num] = copies return copies
def test_set_bits_in_place(self): x = ThirtySixBitNumber(36) y = x.set_bit(31, 1) self.assertEqual(52, y.value) z = x.set_bit_in_place(31, 1) self.assertEqual(52, z.value) self.assertEqual(52, x.value)
def process_line(self, line: str): if line.startswith('mask'): self.bitmask = VersionTwoBitMask(line.split(" = ")[1].strip()) elif line.startswith("mem"): match = re.match(r"mem\[(\d+)] = (\d+)", line) address = int(match.group(1)) num = ThirtySixBitNumber(int(match.group(2))) decoded_addresses = self.bitmask.apply_to(ThirtySixBitNumber(address)) for decoded_address in decoded_addresses: self.memory[decoded_address.value] = num.value
def test_v2_bitmask_x_writes_all_possibilities(self): mask = VersionTwoBitMask("000000000000000000000000000000X1001X") num = ThirtySixBitNumber(42) results = mask.apply_to(num) self.assertEqual(len(results), 4) self.assertCountEqual( results, [ ThirtySixBitNumber(26), ThirtySixBitNumber(27), ThirtySixBitNumber(58), ThirtySixBitNumber(59) ] )
def test_apply_bitmask(self): program = """ mask = XXXXXXXXXXXXXXXXXXXXXXXXXXXXX1XXXX0X mem[8] = 11 mem[7] = 101 mem[8] = 0 """ parsed_program = parse(program) masked_program = apply_bitmask(parsed_program) self.assertEqual( masked_program, { 'mask': BitMask("XXXXXXXXXXXXXXXXXXXXXXXXXXXXX1XXXX0X"), 'values': [(8, ThirtySixBitNumber(73)), (7, ThirtySixBitNumber(101)), (8, ThirtySixBitNumber(64))] })
def test_parse_program(self): program = """ mask = XXXXXXXXXXXXXXXXXXXXXXXXXXXXX1XXXX0X mem[8] = 11 mem[7] = 101 mem[8] = 0 """ parsed_program = parse(program) print(parsed_program['values']) self.assertEqual( parsed_program, { 'mask': BitMask("XXXXXXXXXXXXXXXXXXXXXXXXXXXXX1XXXX0X"), 'values': [(8, ThirtySixBitNumber(11)), (7, ThirtySixBitNumber(101)), (8, ThirtySixBitNumber(0))] })
def test_example_values(self): self.assertEqual( ThirtySixBitNumber(73).bits, [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1 ]) self.assertEqual( ThirtySixBitNumber(101).bits, [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1 ]) self.assertEqual( ThirtySixBitNumber(64).bits, [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 ])
def test_can_create_number_from_bits(self): n = ThirtySixBitNumber([ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1 ]) self.assertEqual(n.bits, [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1 ]) self.assertEqual(3, n.value)
def test_v2_bitmask_zero_does_nothing(self): mask = VersionTwoBitMask("000000000000000000000000000000000000") bits = [0] * 36 bits[35] = 1 bits[34] = 0 num = ThirtySixBitNumber(bits) actual = mask.apply_to(num) self.assertEqual(actual[0].bits[35], 1) self.assertEqual(actual[0].bits[34], 0)
def parse(program: str): program_lines = [ line.strip() for line in program.splitlines() if len(line.strip()) > 0 ] instructions = [] for line in program_lines[1:]: match = re.match(r"mem\[(\d+)] = (\d+)", line) instructions.append( (int(match.group(1)), ThirtySixBitNumber(int(match.group(2))))) return { 'mask': BitMask(program_lines[0].split(" = ")[1].strip()), 'values': instructions }
def test_v2_bitmask_one_writes_one(self): mask = VersionTwoBitMask("000000000000000000000000000001010101") bits = [0] * 36 num = ThirtySixBitNumber(bits) actual = mask.apply_to(num) self.assertEqual(len(actual), 1) result = actual[0] self.assertEqual(result.bits[35], 1) self.assertEqual(result.bits[34], 0) self.assertEqual(result.bits[33], 1) self.assertEqual(result.bits[32], 0) self.assertEqual(result.bits[31], 1) self.assertEqual(result.bits[30], 0) self.assertEqual(result.bits[29], 1)
def test_three(self): n = ThirtySixBitNumber(3) self.assertEqual(n.bits, [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1 ])
def test_two(self): n = ThirtySixBitNumber(2) self.assertEqual(n.bits, [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 ])
def test_one(self): n = ThirtySixBitNumber(1) self.assertEqual(n.bits, [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 ])
def test_zero(self): zero = ThirtySixBitNumber(0) self.assertEqual(zero.bits, [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ])
def test_bitmask_can_have_impact(self): mask = BitMask("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX0X") x = mask.apply_to(ThirtySixBitNumber(2)) self.assertEqual(x, ThirtySixBitNumber(0))