<commit_before><commit_msg>2019: Day 2, exercise 2.<commit_after>use std::io;

type Code = usize;
type Codes = Vec<Code>;

const EXPECTED_OUTPUT: Code = 19690720;

fn main() -> io::Result<()> {
    let mut input: String = String::new();
    io::stdin().read_line(&mut input)?;

    let starting_codes: Codes = input
        .trim()
        .split(",")
        .map(|code| {
            code.parse::<Code>()
                .map_err(|err| io::Error::new(io::ErrorKind::Other, err))
        })
        .collect::<io::Result<_>>()?;

    let program_inputs: Vec<(Code, Code)> = (0..100)
        .flat_map(move |noun| (0..100).map(move |verb| (noun, verb)))
        .collect();

    let (noun, verb) = program_inputs
        .iter()
        .find(|(noun, verb)| {
            let mut codes = starting_codes.clone();
            codes[1] = *noun;
            codes[2] = *verb;

            let mut position = 0;

            loop {
                match codes[position] {
                    1 => {
                        operate(&mut codes, position, |a, b| a + b);
                        position += 4;
                    }
                    2 => {
                        operate(&mut codes, position, |a, b| a * b);
                        position += 4;
                    }
                    99 => break,
                    invalid => panic!("Invalid opcode: {}", invalid),
                }
            }

            codes[0] == EXPECTED_OUTPUT
        })
        .ok_or(io::ErrorKind::NotFound)?;

    println!("{}", noun * 100 + verb);

    Ok(())
}

fn operate<Op>(codes: &mut Codes, position: Code, op: Op)
where
    Op: Fn(Code, Code) -> Code,
{
    let a_position = codes[position + 1];
    let a = codes[a_position];
    let b_position = codes[position + 2];
    let b = codes[b_position];
    let destination = codes[position + 3];
    codes[destination] = op(a, b);
}

{'fraction_non_alphanumeric': 0.1598729486500794, 'fraction_numerical': 0.02011646373742721, 'mean_word_length': 1.5961538461538463, 'pattern_counts': {'":': 0, '<': 10, '<?xml version=': 0, '>': 16, '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}}
{'dir': 'git-commits-cleaned', 'id': '5347350', 'n_tokens_mistral': 610, 'n_tokens_neox': 572, 'n_words': 140}