use std::collections::VecDeque;
const EXTRA: usize = 2_000;
pub enum State {
Input,
Output(i64),
Halted,
}
pub struct Computer {
pc: usize,
base: usize,
code: Vec<usize>,
input: VecDeque<usize>,
}
impl Computer {
pub fn new(input: &[i64]) -> Computer {
let mut code = Vec::with_capacity(input.len() + EXTRA);
code.extend(input.iter().map(|&i| i as usize));
code.resize(input.len() + EXTRA, 0);
Computer { pc: 0, base: 0, code, input: VecDeque::new() }
}
pub fn input(&mut self, value: i64) {
self.input.push_back(value as usize);
}
pub fn input_ascii(&mut self, ascii: &str) {
self.input.extend(ascii.bytes().map(|b| b as usize));
}
pub fn reset(&mut self) {
self.pc = 0;
self.base = 0;
self.input.clear();
}
pub fn run(&mut self) -> State {
loop {
let op = self.code[self.pc];
match op % 100 {
1 => {
let first = self.address(op / 100, 1);
let second = self.address(op / 1000, 2);
let third = self.address(op / 10000, 3);
self.code[third] = self.code[first].wrapping_add(self.code[second]);
self.pc += 4;
}
2 => {
let first = self.address(op / 100, 1);
let second = self.address(op / 1000, 2);
let third = self.address(op / 10000, 3);
self.code[third] = self.code[first].wrapping_mul(self.code[second]);
self.pc += 4;
}
3 => {
let Some(value) = self.input.pop_front() else {
break State::Input;
};
let first = self.address(op / 100, 1);
self.code[first] = value;
self.pc += 2;
}
4 => {
let first = self.address(op / 100, 1);
let value = self.code[first];
self.pc += 2;
break State::Output(value as i64);
}
5 => {
let first = self.address(op / 100, 1);
let second = self.address(op / 1000, 2);
let value = self.code[first] == 0;
self.pc = if value { self.pc + 3 } else { self.code[second] };
}
6 => {
let first = self.address(op / 100, 1);
let second = self.address(op / 1000, 2);
let value = self.code[first] == 0;
self.pc = if value { self.code[second] } else { self.pc + 3 };
}
7 => {
let first = self.address(op / 100, 1);
let second = self.address(op / 1000, 2);
let third = self.address(op / 10000, 3);
let value = (self.code[first] as i64) < (self.code[second] as i64);
self.code[third] = value as usize;
self.pc += 4;
}
8 => {
let first = self.address(op / 100, 1);
let second = self.address(op / 1000, 2);
let third = self.address(op / 10000, 3);
let value = self.code[first] == self.code[second];
self.code[third] = value as usize;
self.pc += 4;
}
9 => {
let first = self.address(op / 100, 1);
self.base = self.base.wrapping_add(self.code[first]);
self.pc += 2;
}
_ => break State::Halted,
}
}
}
#[inline]
fn address(&mut self, mode: usize, offset: usize) -> usize {
match mode % 10 {
0 => self.code[self.pc + offset],
1 => self.pc + offset,
2 => self.base.wrapping_add(self.code[self.pc + offset]),
_ => unreachable!(),
}
}
}