################ solver from itertools import chain, combinations from functools import reduce def powerset(iterable): "powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)" s = list(iterable) return chain.from_iterable(combinations(s, r) for r in range(len(s) + 1)) def check_solution(selection, operator): return reduce( lambda a, b: tuple(operator(x, y) for x, y in zip(a, b)), selection ) def tuple_selector(tuples, operator): solution_count = 0 for selection in powerset(tuples): if not selection: continue if all(check_solution(selection, operator)): print(f"found a solution! {selection}") solution_count += 1 print(f"{solution_count=}") ################# problem statement def operator_with_secret(a: int, b: int) -> int: either = {a, b} if 1 in either: return a + b % 2 # if 0 in either: # return 0 # final element that must be included if 2 in either: # The x's represent free choices. # 0bxxxxxxx00 secret = 0b010110100 return 3 if secret in either else 0 # build your own number kit return a | b tuple_selector( [ # make a choice to set a bit in the first number (0b000000100, 0, 1, 0, 0, 0, 0, 0, 0), (0b000001000, 0, 0, 1, 0, 0, 0, 0, 0), (0b000010000, 0, 0, 0, 1, 0, 0, 0, 0), (0b000100000, 0, 0, 0, 0, 1, 0, 0, 0), (0b001000000, 0, 0, 0, 0, 0, 1, 0, 0), (0b010000000, 0, 0, 0, 0, 0, 0, 1, 0), (0b100000000, 0, 0, 0, 0, 0, 0, 0, 1), # .. or not (0b000000000, 0, 1, 0, 0, 0, 0, 0, 0), (0b000000000, 0, 0, 1, 0, 0, 0, 0, 0), (0b000000000, 0, 0, 0, 1, 0, 0, 0, 0), (0b000000000, 0, 0, 0, 0, 1, 0, 0, 0), (0b000000000, 0, 0, 0, 0, 0, 1, 0, 0), (0b000000000, 0, 0, 0, 0, 0, 0, 1, 0), (0b000000000, 0, 0, 0, 0, 0, 0, 0, 1), # this final element that must be included (0b000000010, 1, 0, 0, 0, 0, 0, 0, 0), ], operator_with_secret, ) # Commutative operator variant def operator_with_secret(a: int, b: int) -> int: # We're going to create a bitpacked representation of the state of the # computation. The state is the number of records included, and the value # is a bitwise OR of the records included. The state is stored in the # first slot of each tuple, but this operator doesn't know which slot is # first, so we just check to see if the value is greater than 1. value_bits = 9 total_records_to_select = 8 def extract(bitpacked: int) -> Tuple[int, int]: count = bitpacked >> value_bits value = bitpacked & ((1 << value_bits) - 1) return (count if count else 1), value def pack(count: int, value: int) -> int: return (count << value_bits) | value either = {a, b} # If we're building our state in the first slot, handle it if any(x > 1 for x in either): # Unpack the state from each input # State is just how many records have been included so far, and the # value which has been built ac, av = extract(a) bc, bv = extract(b) count, value = ac + bc, av | bv # If we haven't inluded eight records yet, just build up the number if count < total_records_to_select: return pack(count, value) # If we've included eight records, we're done and need to check the # value is what we wanted # The x's represent free choices. # 0bxxxxxxx00 secret = 0b010110100 return 3 if value == secret else 0 # Otherwise, we're just checking that we select at least one truthy value return a | b