codetrotter_aoc_2019_solutions 0.9.0

Advent of Code 2019 Solutions
Documentation
/*
 * Copyright (c) 2019, 2020 Erik Nordstrøm <erik@nordstroem.no>
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

use std::sync::mpsc::{Sender, Receiver};
use std::sync::mpsc;
use std::thread;

solution_printer!(7, print_solution, input_generator, INPUT, solve_part_1, solve_part_2);

pub const INPUT: &str = include_str!("../input/2019/day7.txt");

/// ### Day 7: Amplification Circuit
///
/// [https://adventofcode.com/2019/day/7](https://adventofcode.com/2019/day/7)
///
/// Based on the navigational maps, you're going to need to send more power to
/// your ship's thrusters to reach Santa in time. To do this, you'll need to
/// configure a series of [amplifiers](https://en.wikipedia.org/wiki/Amplifier) already installed on the ship.
///
/// There are five amplifiers connected in series; each one receives an input
/// signal and produces an output signal.  They are connected such that the
/// first amplifier's output leads to the second amplifier's input, the second
/// amplifier's output leads to the third amplifier's input, and so on.  The
/// first amplifier's input value is `0`, and the last amplifier's output leads
/// to your ship's thrusters.
///
/// ```text
///     O-------O  O-------O  O-------O  O-------O  O-------O
/// 0 ->| Amp A |->| Amp B |->| Amp C |->| Amp D |->| Amp E |-> (to thrusters)
///     O-------O  O-------O  O-------O  O-------O  O-------O
/// ```
///
/// The Elves have sent you some *Amplifier Controller Software* (your puzzle
/// input), a program that should run on your [existing Intcode computer](https://adventofcode.com/2019/day/5). Each
/// amplifier will need to run a copy of the program.
///
/// When a copy of the program starts running on an amplifier, it will first
/// use an input instruction to ask the amplifier for its current *phase setting*
/// (an integer from `0` to `4`). Each phase setting is used *exactly once*, but the
/// Elves can't remember which amplifier needs which phase setting.
///
/// The program will then call another input instruction to get the amplifier's
/// input signal, compute the correct output signal, and supply it back to the
/// amplifier with an output instruction. (If the amplifier has not yet
/// received an input signal, it waits until one arrives.)
///
/// Your job is to *find the largest output signal that can be sent to the
/// thrusters* by trying every possible combination of phase settings on the
/// amplifiers. Make sure that memory is not shared or reused between copies of
/// the program.
///
/// For example, suppose you want to try the phase setting sequence `3,1,2,4,0`,
/// which would mean setting amplifier `A` to phase setting `3`, amplifier `B` to
/// setting `1`, `C` to `2`, `D` to `4`, and `E` to `0`. Then, you could determine the output
/// signal that gets sent from amplifier `E` to the thrusters with the following
/// steps:
///
///   - Start the copy of the amplifier controller software that will run on
///     amplifier `A`. At its first input instruction, provide it the
///     amplifier's phase setting, `3`.  At its second input instruction, provide
///     it the input signal, `0`.  After some calculations, it will use an output
///     instruction to indicate the amplifier's output signal.
///   - Start the software for amplifier `B`. Provide it the phase setting (`1`)
///     and then whatever output signal was produced from amplifier `A`. It will
///     then produce a new output signal destined for amplifier `C`.
///   - Start the software for amplifier `C`, provide the phase setting (`2`) and
///     the value from amplifier `B`, then collect its output signal.
///   - Run amplifier `D`'s software, provide the phase setting (`4`) and input
///     value, and collect its output signal.
///   - Run amplifier `E`'s software, provide the phase setting (`0`) and input
///     value, and collect its output signal.
///
/// The final output signal from amplifier `E` would be sent to the thrusters.
/// However, this phase setting sequence may not have been the best one;
/// another sequence might have sent a higher signal to the thrusters.
///
/// Here are some example programs:
///
///   - Max thruster signal *`43210`* (from phase setting sequence `4,3,2,1,0`):
///     ```text
///     3,15,3,16,1002,16,10,16,1,16,15,15,4,15,99,0,0
///     ```
///   - Max thruster signal *`54321`* (from phase setting sequence `0,1,2,3,4`):
///     ```text
///     3,23,3,24,1002,24,10,24,1002,23,-1,23,
///     101,5,23,23,1,24,23,23,4,23,99,0,0
///     ```
///   - Max thruster signal *`65210`* (from phase setting sequence `1,0,4,3,2`):
///     ```text
///     3,31,3,32,1002,32,10,32,1001,31,-2,31,1007,31,0,33,
///     1002,33,7,33,1,33,31,31,1,32,31,31,4,31,99,0,0,0
///     ```
///
/// Try every combination of phase settings on the amplifiers.  *What is the
/// highest signal that can be sent to the thrusters?*
///
/// ### Examples
///
/// ```
/// use codetrotter_aoc_2019_solutions::day_07::{input_generator, find_max_thruster_signal_serial_amps};
/// const EX1PROG: &'static str = "3,15,3,16,1002,16,10,16,1,16,15,15,4,15,99,0,0";
/// assert_eq!(find_max_thruster_signal_serial_amps(&input_generator(EX1PROG)),
///   (43210, [4,3,2,1,0]));
/// ```
///
/// ```
/// # use codetrotter_aoc_2019_solutions::day_07::{input_generator, find_max_thruster_signal_serial_amps};
/// const EX2PROG: &'static str =
///   "3,23,3,24,1002,24,10,24,1002,23,-1,23,\
///    101,5,23,23,1,24,23,23,4,23,99,0,0";
/// assert_eq!(find_max_thruster_signal_serial_amps(&input_generator(EX2PROG)),
///   (54321, [0,1,2,3,4]));
/// ```
///
/// ```
/// # use codetrotter_aoc_2019_solutions::day_07::{input_generator, find_max_thruster_signal_serial_amps};
/// const EX3PROG: &'static str =
///   "3,31,3,32,1002,32,10,32,1001,31,-2,31,1007,31,0,33,\
///    1002,33,7,33,1,33,31,31,1,32,31,31,4,31,99,0,0,0";
/// assert_eq!(find_max_thruster_signal_serial_amps(&input_generator(EX3PROG)),
///   (65210, [1,0,4,3,2]));
/// ```
///
/// ### Solution
///
/// ⚠️ SPOILER ALERT ⚠️
///
/// ```
/// use codetrotter_aoc_2019_solutions::day_07::{INPUT, input_generator, solve_part_1};
/// assert_eq!(solve_part_1(&mut input_generator(INPUT)), 338603);
/// ```
pub fn solve_part_1 (input_memory: &Memory) -> Intcode
{
  find_max_thruster_signal_serial_amps(input_memory).0
}

pub type PhaseSettings = [Intcode; NUM_ELEMS_IN_A_PERMUTATION];
pub fn find_max_thruster_signal_serial_amps (memory: &Memory) -> (Intcode, PhaseSettings)
{
  PermutationsIterator::new([0,1,2,3,4]).map(
    |phase_settings| (run_amplifiers_serially(memory, &phase_settings), phase_settings)).max().unwrap()
}

/// Iterate over permutations using non-recursive variant of Heap's algorithm.
///
/// See: [https://en.wikipedia.org/wiki/Heap%27s_algorithm](https://en.wikipedia.org/wiki/Heap%27s_algorithm)
pub struct PermutationsIterator<T: Copy>
{
  next_permutation: [T; NUM_ELEMS_IN_A_PERMUTATION],
  c: [usize; NUM_ELEMS_IN_A_PERMUTATION],
  i: usize,
}

const NUM_ELEMS_IN_A_PERMUTATION: usize = 5;

impl<T: Copy> PermutationsIterator<T>
{
  pub fn new (initial_permutation: [T; NUM_ELEMS_IN_A_PERMUTATION]) -> Self
  {
    Self
    {
      next_permutation: initial_permutation,
      c: [0; NUM_ELEMS_IN_A_PERMUTATION],
      i: 0,
    }
  }
}

impl<T: Copy> std::iter::Iterator for PermutationsIterator<T>
{
  type Item = [T; NUM_ELEMS_IN_A_PERMUTATION];

  fn next (&mut self) -> Option<Self::Item>
  {
    let curr = self.next_permutation;

    while self.i < NUM_ELEMS_IN_A_PERMUTATION
    {
      if self.c[self.i] < self.i
      {
        if self.i % 2 == 0
        {
          self.next_permutation.swap(0, self.i);
        }
        else
        {
          self.next_permutation.swap(self.c[self.i], self.i);
        }
        self.c[self.i] += 1;
        self.i = 0;

        return Some(curr);
      }
      else
      {
        self.c[self.i] = 0;
        self.i += 1;
      }
    }

    if self.i == NUM_ELEMS_IN_A_PERMUTATION
    {
      self.i += 1;
      Some(curr)
    }
    else
    {
      None
    }
  }
}

pub fn run_amplifiers_serially (memory: &Memory, phase_settings: &[Intcode; NUM_ELEMS_IN_A_PERMUTATION]) -> Intcode
{
  let output = run_intcode_program(memory, &[phase_settings[0], 0]).outputs_port_b;
  let output = run_intcode_program(memory, &[phase_settings[1], output[0]]).outputs_port_b;
  let output = run_intcode_program(memory, &[phase_settings[2], output[0]]).outputs_port_b;
  let output = run_intcode_program(memory, &[phase_settings[3], output[0]]).outputs_port_b;
  let output = run_intcode_program(memory, &[phase_settings[4], output[0]]).outputs_port_b;
  output[0]
}

pub type Intcode = i32;
pub type Memory = Vec<Intcode>;
pub type InputsPortA = [Intcode];
pub type OutputsPortB = Vec<Intcode>;

pub fn input_generator (input: &str) -> Memory
{
  input.trim_end_matches('\n').split(',').map(|intcode_str| intcode_str.parse::<Intcode>().unwrap()).collect()
}

pub struct EmulationFinished
{
  pub memory:         Memory,
  pub outputs_port_b: OutputsPortB,
}

enum Instruction
{
  Add,
  Multiply,
  ReadFromPortA,
  WriteToPortB,
  JumpIfTrue,
  JumpIfFalse,
  LessThan,
  Equals,
  Exit,
}

pub fn run_intcode_program (memory: &Memory, inputs_port_a: &InputsPortA) -> EmulationFinished
{
  let mut memory = memory.clone();
  let mut pc = 0;
  let mut outputs_port_b: OutputsPortB = vec![];
  let mut inputs_port_a_iter = inputs_port_a.iter();

  loop
  {
    let opcode = memory[pc];

    let (instruction, input_param_values_in_mem, output_param_value_in_mem) = match opcode % 100
    {
       1 => (Instruction::Add,           &memory[pc+1..=pc+2], Some(memory[pc+3])),
       2 => (Instruction::Multiply,      &memory[pc+1..=pc+2], Some(memory[pc+3])),
       3 => (Instruction::ReadFromPortA, &[][..],              Some(memory[pc+1])),
       4 => (Instruction::WriteToPortB,  &memory[pc+1..=pc+1], None),
       5 => (Instruction::JumpIfTrue,    &memory[pc+1..=pc+2], None),
       6 => (Instruction::JumpIfFalse,   &memory[pc+1..=pc+2], None),
       7 => (Instruction::LessThan,      &memory[pc+1..=pc+2], Some(memory[pc+3])),
       8 => (Instruction::Equals,        &memory[pc+1..=pc+2], Some(memory[pc+3])),
      99 => (Instruction::Exit,          &[][..],              None),
       _ => panic!("Invalid opcode {} at position {}", opcode, pc),
    };

    let mut input_param_modes = opcode / 100;
    let mut input_param_values = Vec::from(input_param_values_in_mem);
    for input_param_value in input_param_values.iter_mut()
    {
      if input_param_modes & 1 == 0 // position mode
        { *input_param_value = memory[*input_param_value as usize]; }
      input_param_modes /= 10;
    }
    assert_eq!(input_param_modes, 0);

    pc += input_param_values.len() + output_param_value_in_mem.is_some() as usize + 1;

    match instruction
    {
      Instruction::Add           => memory[output_param_value_in_mem.unwrap() as usize] = input_param_values[0] + input_param_values[1],
      Instruction::Multiply      => memory[output_param_value_in_mem.unwrap() as usize] = input_param_values[0] * input_param_values[1],
      Instruction::ReadFromPortA => memory[output_param_value_in_mem.unwrap() as usize] = *inputs_port_a_iter.next().unwrap(),
      Instruction::WriteToPortB  => outputs_port_b.push(input_param_values[0]),
      Instruction::JumpIfTrue    => if input_param_values[0] != 0 { pc = input_param_values[1] as usize; },
      Instruction::JumpIfFalse   => if input_param_values[0] == 0 { pc = input_param_values[1] as usize; },
      Instruction::LessThan      => memory[output_param_value_in_mem.unwrap() as usize] = (input_param_values[0]  < input_param_values[1]) as Intcode,
      Instruction::Equals        => memory[output_param_value_in_mem.unwrap() as usize] = (input_param_values[0] == input_param_values[1]) as Intcode,
      Instruction::Exit          => break,
    }
  }

  EmulationFinished
  {
    memory,
    outputs_port_b,
  }
}

/// ### Day 7, Part Two
///
/// [https://adventofcode.com/2019/day/7#part2](https://adventofcode.com/2019/day/7#part2)
///
/// It's no good - in this configuration, the amplifiers can't generate a large
/// enough output signal to produce the thrust you'll need.  The Elves quickly
/// talk you through rewiring the amplifiers into a *feedback loop*:
///
/// ```text
///       O-------O  O-------O  O-------O  O-------O  O-------O
/// 0 -+->| Amp A |->| Amp B |->| Amp C |->| Amp D |->| Amp E |-.
///    |  O-------O  O-------O  O-------O  O-------O  O-------O |
///    |                                                        |
///    '--------------------------------------------------------+
///                                                             |
///                                                             v
///                                                      (to thrusters)
/// ```
///
/// Most of the amplifiers are connected as they were before; amplifier `A`'s
/// output is connected to amplifier `B`'s input, and so on. *However,* the output
/// from amplifier `E` is now connected into amplifier `A`'s input. This creates
/// the feedback loop: the signal will be sent through the amplifiers *many
/// times*.
///
/// In feedback loop mode, the amplifiers need *totally different phase
/// settings*: integers from `5` to `9`, again each used exactly once. These
/// settings will cause the Amplifier Controller Software to repeatedly take
/// input and produce output many times before halting. Provide each amplifier
/// its phase setting at its first input instruction; all further input/output
/// instructions are for signals.
///
/// Don't restart the Amplifier Controller Software on any amplifier during
/// this process. Each one should continue receiving and sending signals until
/// it halts.
///
/// All signals sent or received in this process will be between pairs of
/// amplifiers except the very first signal and the very last signal. To start
/// the process, a `0` signal is sent to amplifier `A`'s input *exactly once*.
///
/// Eventually, the software on the amplifiers will halt after they have
/// processed the final loop. When this happens, the last output signal from
/// amplifier `E` is sent to the thrusters. Your job is to *find the largest
/// output signal that can be sent to the thrusters* using the new phase
/// settings and feedback loop arrangement.
///
/// Here are some example programs:
///
///   - Max thruster signal *`139629729`* (from phase setting sequence `9,8,7,6,5`):
///     ```text
///     3,26,1001,26,-4,26,3,27,1002,27,2,27,1,27,26,
///     27,4,27,1001,28,-1,28,1005,28,6,99,0,0,5
///     ```
///   - Max thruster signal *`18216`* (from phase setting sequence `9,7,8,5,6`):
///     ```text
///     3,52,1001,52,-5,52,3,53,1,52,56,54,1007,54,5,55,1005,55,26,1001,54,
///     -5,54,1105,1,12,1,53,54,53,1008,54,0,55,1001,55,1,55,2,53,55,53,4,
///     53,1001,56,-1,56,1005,56,6,99,0,0,0,0,10
///     ```
///
/// Try every combination of the new phase settings on the amplifier feedback
/// loop.  *What is the highest signal that can be sent to the thrusters?*
///
/// ### Examples
///
/// ```
/// use codetrotter_aoc_2019_solutions::day_07::{input_generator, find_max_thruster_signal_feedback_loop_amps};
/// const EX4PROG: &'static str =
///   "3,26,1001,26,-4,26,3,27,1002,27,2,27,1,27,26,\
///    27,4,27,1001,28,-1,28,1005,28,6,99,0,0,5";
/// assert_eq!(find_max_thruster_signal_feedback_loop_amps(&input_generator(EX4PROG)),
///   (139629729, [9,8,7,6,5]));
/// ```
///
/// ```
/// # use codetrotter_aoc_2019_solutions::day_07::{input_generator, find_max_thruster_signal_feedback_loop_amps};
/// const EX5PROG: &'static str =
///   "3,52,1001,52,-5,52,3,53,1,52,56,54,1007,54,5,55,1005,55,26,1001,54,\
///    -5,54,1105,1,12,1,53,54,53,1008,54,0,55,1001,55,1,55,2,53,55,53,4,\
///    53,1001,56,-1,56,1005,56,6,99,0,0,0,0,10";
/// assert_eq!(find_max_thruster_signal_feedback_loop_amps(&input_generator(EX5PROG)),
///   (18216, [9,7,8,5,6]));
/// ```
///
/// ### Solution
///
/// ⚠️ SPOILER ALERT ⚠️
///
/// ```
/// use codetrotter_aoc_2019_solutions::day_07::{INPUT, input_generator, solve_part_2};
/// assert_eq!(solve_part_2(&input_generator(INPUT)), 63103596);
/// ```
pub fn solve_part_2 (input_memory: &Memory) -> Intcode
{
  find_max_thruster_signal_feedback_loop_amps(input_memory).0
}

pub fn find_max_thruster_signal_feedback_loop_amps (memory: &Memory) -> (Intcode, PhaseSettings)
{
  PermutationsIterator::new([5,6,7,8,9]).map(
    |phase_settings| (run_amplifier_feedback_loop(memory, &phase_settings), phase_settings)).max().unwrap()
}

pub fn run_amplifier_feedback_loop (memory: &Memory, phase_settings: &[Intcode; NUM_ELEMS_IN_A_PERMUTATION]) -> Intcode
{
  let (amp_a_output_tx, amp_a_output_rx) = mpsc::channel();
  let (amp_b_output_tx, amp_b_output_rx) = mpsc::channel();
  let (amp_c_output_tx, amp_c_output_rx) = mpsc::channel();
  let (amp_d_output_tx, amp_d_output_rx) = mpsc::channel();
  let (amp_e_output_tx, amp_e_output_rx) = mpsc::channel();
  let (probe_output_tx, probe_output_rx) = mpsc::channel();

  let (amp_a_input_tx, amp_a_input_rx) = (probe_output_tx.clone(), probe_output_rx);
  let (amp_b_input_tx, amp_b_input_rx) = (amp_a_output_tx.clone(), amp_a_output_rx);
  let (amp_c_input_tx, amp_c_input_rx) = (amp_b_output_tx.clone(), amp_b_output_rx);
  let (amp_d_input_tx, amp_d_input_rx) = (amp_c_output_tx.clone(), amp_c_output_rx);
  let (amp_e_input_tx, amp_e_input_rx) = (amp_d_output_tx.clone(), amp_d_output_rx);
  let probe_input_rx = amp_e_output_rx;

  //debug_log!("Start probe");
  let probe_handle = thread::spawn(move ||
  {
    //debug_log!("Probe started");
    let mut most_recent_probe_value;
    let mut num_inputs_read_in_probe = 0;
    let mut num_outputs_written_in_probe = 0;
    loop
    {
      if let Ok(value) = probe_input_rx.try_recv()
      {
        //debug_log!(format_args!("Probe read {}", value));
        most_recent_probe_value = value;
        num_inputs_read_in_probe += 1;
        match probe_output_tx.send(value)
        {
          Ok(_)  => num_outputs_written_in_probe += 1,
          Err(_) => break,
        }
      }
    }
    //debug_log!("Probe shutting down");
    (most_recent_probe_value, num_inputs_read_in_probe, num_outputs_written_in_probe)
  });

  //debug_log!("Start Amp A");
  let memory_a = memory.clone();
  let amp_a_handle = thread::spawn(move ||
    run_intcode_program_b(memory_a, amp_a_input_rx,  amp_a_output_tx));
  amp_a_input_tx.send(phase_settings[0]).unwrap();

  //debug_log!("Start Amp B");
  let memory_b = memory.clone();
  let amp_b_handle = thread::spawn(move ||
    run_intcode_program_b(memory_b, amp_b_input_rx, amp_b_output_tx));
  amp_b_input_tx.send(phase_settings[1]).unwrap();

  //debug_log!("Start Amp C");
  let memory_c = memory.clone();
  let amp_c_handle = thread::spawn(move ||
    run_intcode_program_b(memory_c, amp_c_input_rx, amp_c_output_tx));
  amp_c_input_tx.send(phase_settings[2]).unwrap();

  //debug_log!("Start Amp D");
  let memory_d = memory.clone();
  let amp_d_handle = thread::spawn(move ||
    run_intcode_program_b(memory_d, amp_d_input_rx, amp_d_output_tx));
  amp_d_input_tx.send(phase_settings[3]).unwrap();

  //debug_log!("Start Amp E");
  let memory_e = memory.clone();
  let amp_e_handle = thread::spawn(move ||
    run_intcode_program_b(memory_e, amp_e_input_rx, amp_e_output_tx));
  amp_e_input_tx.send(phase_settings[4]).unwrap();

  //debug_log!("Send initial signal to Amp A");
  amp_a_input_tx.send(0).unwrap();

  //debug_log!("Join threads");
  amp_a_handle.join().unwrap();
  amp_b_handle.join().unwrap();
  amp_c_handle.join().unwrap();
  amp_d_handle.join().unwrap();
  amp_e_handle.join().unwrap();
  probe_handle.join().unwrap().0
}

pub struct EmulationFinishedB
{
  pub memory: Memory,
  pub pc: usize,
  pub num_inputs_read: u32,
  pub num_outputs_written: u32,
}

pub fn run_intcode_program_b (mut memory: Memory, inputs_port_a_rx: Receiver<Intcode>, outputs_port_b_tx: Sender<Intcode>) -> EmulationFinishedB
{
  let mut pc = 0;
  let mut num_inputs_read = 0;
  let mut num_outputs_written = 0;

  loop
  {
    let opcode = memory[pc];

    let (instruction, input_param_values_in_mem, output_param_value_in_mem) = match opcode % 100
    {
       1 => (Instruction::Add,           &memory[pc+1..=pc+2], Some(memory[pc+3])),
       2 => (Instruction::Multiply,      &memory[pc+1..=pc+2], Some(memory[pc+3])),
       3 => (Instruction::ReadFromPortA, &[][..],              Some(memory[pc+1])),
       4 => (Instruction::WriteToPortB,  &memory[pc+1..=pc+1], None),
       5 => (Instruction::JumpIfTrue,    &memory[pc+1..=pc+2], None),
       6 => (Instruction::JumpIfFalse,   &memory[pc+1..=pc+2], None),
       7 => (Instruction::LessThan,      &memory[pc+1..=pc+2], Some(memory[pc+3])),
       8 => (Instruction::Equals,        &memory[pc+1..=pc+2], Some(memory[pc+3])),
      99 => (Instruction::Exit,          &[][..],              None),
       _ => panic!("Invalid opcode {} at position {}", opcode, pc),
    };

    let mut input_param_modes = opcode / 100;
    let mut input_param_values = Vec::from(input_param_values_in_mem);
    for input_param_value in input_param_values.iter_mut()
    {
      if input_param_modes & 1 == 0 // position mode
        { *input_param_value = memory[*input_param_value as usize]; }
      input_param_modes /= 10;
    }
    assert_eq!(input_param_modes, 0);

    pc += input_param_values.len() + output_param_value_in_mem.is_some() as usize + 1;

    match instruction
    {
      Instruction::Add           => memory[output_param_value_in_mem.unwrap() as usize] = input_param_values[0] + input_param_values[1],
      Instruction::Multiply      => memory[output_param_value_in_mem.unwrap() as usize] = input_param_values[0] * input_param_values[1],
      Instruction::ReadFromPortA => { memory[output_param_value_in_mem.unwrap() as usize] = inputs_port_a_rx.recv().unwrap(); num_inputs_read += 1; },
      Instruction::WriteToPortB  => { outputs_port_b_tx.send(input_param_values[0]).unwrap(); num_outputs_written += 1; },
      Instruction::JumpIfTrue    => if input_param_values[0] != 0 { pc = input_param_values[1] as usize; },
      Instruction::JumpIfFalse   => if input_param_values[0] == 0 { pc = input_param_values[1] as usize; },
      Instruction::LessThan      => memory[output_param_value_in_mem.unwrap() as usize] = (input_param_values[0]  < input_param_values[1]) as Intcode,
      Instruction::Equals        => memory[output_param_value_in_mem.unwrap() as usize] = (input_param_values[0] == input_param_values[1]) as Intcode,
      Instruction::Exit          => break,
    }
  }

  EmulationFinishedB
  {
    memory,
    pc,
    num_inputs_read,
    num_outputs_written,
  }
}