<jupyter_start><jupyter_text>part 1<jupyter_code>def bitstream(hexstr):
    for c in hexstr:
        bitstr = format(int(c, 16), '04b')
        for bit in bitstr:
            yield bit
def take(n, iterable):
    return list(itertools.islice(iterable, n))
def int_from_bits(bits):
    return int(''.join(bits), 2)
def read_value(it):
    valbits = []
    last = False
    while True:
        prefix = next(it)
        if prefix == '0':
            last = True
        valbits.extend(take(4, it))
        if last:
            break
    return int_from_bits(valbits)
def get_packets(it, num=-1):
    packets = []
    while num != 0:
        num -= 1
        version_bits = take(3, it)
        if not version_bits:
            # iterator exhausted
            return packets
        version = int_from_bits(version_bits)
        typeid_bits = take(3, it)
        if not typeid_bits:
            # iterator exhausted
            return packets
        typeid = int_from_bits(typeid_bits)
        if typeid == 4:
            # value packet
            val = read_value(it)
            packet = {'version': version,
                      'typeid': typeid,
                      'value': val}
            packets.append(packet)
        else:
            # op packet
            try:
                lengthtype = next(it)
            except StopIteration:
                # iterator exhausted
                return packets
            if lengthtype == '0':
                # total length in bits
                bitlength_bits = take(15, it)
                if not bitlength_bits:
                    # iterator exhausted
                    return packets
                numbits = int_from_bits(bitlength_bits)
                if numbits == 0:
                    # We've just been reading hex padding 0s; we're finished
                    return packets
                newit = iter(take(numbits, it))
                subpackets = get_packets(newit)
                packet = {'version': version,
                          'typeid': typeid,
                          'subpackets': subpackets}
                packets.append(packet)
            elif lengthtype == '1':
                numsub = int_from_bits(take(11, it))
                subpackets = get_packets(it, numsub)
                packet = {'version': version,
                          'typeid': typeid,
                          'subpackets': subpackets}
                packets.append(packet)
            else:
                raise ValueError(f'Bad length type ID: {lengthtype}')
    return packets
def get_version_ids(packets, acc=None):
    if acc is None:
        acc = []
    for packet in packets:
        acc.append(packet['version'])
        if 'subpackets' in packet:
            acc = get_version_ids(packet['subpackets'], acc)
    return acc
for key in testvsums:
    packets = get_packets(iter(bitstream(key)))
    print(testvsums[key], sum(get_version_ids(packets)))
puzzlepackets = get_packets(iter(bitstream(puzzledata)))
sum(get_version_ids(puzzlepackets))<jupyter_output><empty_output><jupyter_text>part 2<jupyter_code>def gt(v):
    a, b = v
    if a > b:
        return 1
    else:
        return 0
    
def lt(v):
    a,b = v
    if a < b:
        return 1
    else:
        return 0
    
def eq(v):
    a,b = v
    if a == b:
        return 1
    else:
        return 0
    
    
ops = {0: sum,
       1: math.prod,
       2: min,
       3: max,
       5: gt,
       6: lt,
       7: eq,
      }
def calc(packet):
    if 'value' in packet:
        return packet['value']
    # operator packet
    return ops[packet['typeid']]([calc(sp) for sp in packet['subpackets']])
pzpkt = puzzlepackets[0]
calc(pzpkt)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09749528683005655, 'fraction_numerical': 0.009964987880420145, 'mean_word_length': 1.5791666666666666, 'pattern_counts': {'":': 0, '<': 10, '<?xml version=': 0, '>': 10, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '2', 'dir': 'jupyter-structured-clean-dedup', 'id': '464883', 'license': 'BSD-2-Clause', 'n_tokens_mistral': 1146, 'n_tokens_neox': 1117, 'n_words': 285, 'path': 'day16.ipynb', 'repo_name': 'g2boojum/AdventOfCode2021', 'size': '3713'}