<jupyter_start><jupyter_text>machine<jupyter_code>import operator
from functools import partial
from itertools import repeat, chain
from collections import namedtuple


class IntCode:
    Op = namedtuple('Op', ['func', 'num_params', 'write_param', 'resolve_modes'])

    def __init__(self, program, pointer=0, **kwargs):
        self.mem = program
        self.pointer = pointer
        self.state = "started"
        self.output = None
        self._kwargs = kwargs
        self._ops = {
            1: IntCode.Op(partial(IntCode._math, operator.add), 3, True, True),
            2: IntCode.Op(partial(IntCode._math, operator.mul), 3, True, True),
            3: IntCode.Op(IntCode._inp, 1, True, False),
            4: IntCode.Op(IntCode._out, 1, False, True),
            5: IntCode.Op(partial(IntCode._jump, lambda x: x != 0), 2, False, True),
            6: IntCode.Op(partial(IntCode._jump, lambda x: x == 0), 2, False, True),
            7: IntCode.Op(partial(IntCode._math, operator.lt), 3, True, True),
            8: IntCode.Op(partial(IntCode._math, operator.eq), 3, True, True),
            99: IntCode.Op(IntCode._halt, 0, False, False),
        }

    def run(self, inputs=None):
        inputs = iter(inputs or [])

        while self.state != "halted":
            instruction = self.mem[self.pointer]
            opcode, modes = self._opcode(instruction), self._modes(instruction)
            op = self._ops[opcode]
            params = self.mem[self.pointer + 1:self.pointer + op.num_params + 1]

            if op.resolve_modes:
                wparam = -int(op.write_param) or len(params)
                args = zip(repeat(self.mem), params[:wparam], modes[:wparam])
                params[:wparam] = map(lambda t: IntCode._resolve(*t), args)

            if self._kwargs.get('_debug', False):
                print(f"{opcode:2d}", modes, list(params))

            orig_pointer = self.pointer
            self.mem, self.pointer, output, self.state = \
                op.func(self.mem, self.pointer, *params, inputs=inputs, **self._kwargs)
            self.output = output if output is not None else self.output

            if output or (self.state == "blocked"):
                yield self

            if self.pointer == orig_pointer:
                self.pointer = self.pointer + op.num_params + 1
        yield self

    @staticmethod
    def _resolve(mem, val, mode):
        return mem[val] if int(mode) == 0 else val

    @staticmethod
    def _opcode(instruction):
        return int(str(instruction)[-2:])

    @staticmethod
    def _modes(instruction):
        return f"{instruction:05d}"[:3][::-1]

    @staticmethod
    def _math(func, mem, pointer, a, b, out, **kwargs):
        _mem = list(mem)
        _mem[out] = int(func(a, b))
        return _mem, pointer, None, "running"

    @staticmethod
    def _jump(func, mem, pointer, cond, val, **kwargs):
        return mem, (val if func(cond) else pointer), None, "running"

    @staticmethod
    def _inp(mem, pointer, out, inputs, **kwargs):
        try:
            _mem = list(mem)
            _mem[out] = int(next(inputs))
            return _mem, pointer, None, "running"
        except StopIteration:
            return mem, pointer, None, "blocked"

    @staticmethod
    def _out(mem, pointer, val, **kwargs):
        if kwargs.get('_debug', False):
            print(f"out: {val}")
        return mem, pointer, val, "running"

    @staticmethod
    def _halt(mem, pointer, *args, **kwargs):
        return mem, pointer, None, "halted"<jupyter_output><empty_output><jupyter_text>part 1<jupyter_code>from itertools import permutations


def part_1(program):
    max_signal = (0, None)
    for phase in permutations('01234'):
        prev_out = 0
        for p in phase:
            prev_out = next(IntCode(program).run([p, prev_out])).output
        if prev_out > max_signal[0]:
            max_signal = (prev_out, ''.join(phase))
    return max_signal


program = list(map(int, inp.split(',')))
print(part_1(program))<jupyter_output>(43812, '02143')<jupyter_text>part 2<jupyter_code>def part_2(program):
    max_signal = (0, None)
    for phase in permutations('56789'):
        inputs = [[p] for p in phase]
        inputs[0].append(0)
        amps = [IntCode(program).run(inp) for inp in inputs]
        i = 0
        while True:
            amp = next(amps[i])
            if amp.state == "halted":
                break
            j = (i + 1) % 5
            if amp.state != "blocked":
                inputs[j].append(amp.output)
                if amp.output > max_signal[0]:
                    max_signal = (amp.output, ''.join(phase))
            i = j
    return max_signal


program = list(map(int, inp.split(',')))
print(part_2(program))<jupyter_output>(59597414, '97865')
{'fraction_non_alphanumeric': 0.1343658434491419, 'fraction_numerical': 0.016952699874424444, 'mean_word_length': 2.32336578581363, 'pattern_counts': {'":': 3, '<': 11, '<?xml version=': 0, '>': 13, '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': '3', 'dir': 'jupyter-structured-clean-dedup', 'id': '666623', 'license': 'MIT', 'n_tokens_mistral': 1576, 'n_tokens_neox': 1487, 'n_words': 374, 'path': 'aoc/day_07.ipynb', 'repo_name': 'eferm/aoc-2019', 'size': '4778'}