--- /dev/null
+#[derive(Copy, Clone, Debug)]\r
+enum Mode {\r
+ Position,\r
+ Immediate,\r
+ Relative\r
+}\r
+\r
+// 'true' -> immediate mode, 'false' -> position mode.\r
+fn read_op_and_modes(mut code: i64) -> (i64, [Mode; 3]) {\r
+ let mut modes: [Mode; 3] = [ Mode::Position, Mode::Position, Mode::Position ];\r
+\r
+ for i in (2..=4).rev() {\r
+ let power = 10i64.pow(i);\r
+ if code >= 2 * power {\r
+ modes[i as usize - 2] = Mode::Relative;\r
+ code -= 2 * power;\r
+ } else if code >= power {\r
+ modes[i as usize - 2] = Mode::Immediate;\r
+ code -= power;\r
+ }\r
+ }\r
+\r
+ (code, modes)\r
+}\r
+\r
+pub fn execute_op_code(code: &[i64], input: &[i64]) -> Vec<i64> {\r
+ let mut code = Vec::from(code);\r
+ let mut cursor = 0;\r
+ let mut input_cursor = 0;\r
+ let mut output = Vec::<i64>::new();\r
+ let mut relative_base = 0;\r
+\r
+ fn read(position: usize, code: &[i64], mode: Mode, relative_base: i64) -> i64 {\r
+ if position >= code.len() { return 0 }\r
+\r
+ match mode {\r
+ Mode::Position => if code[position] as usize >= code.len() { 0 } else { code[code[position] as usize] },\r
+ Mode::Immediate => code[position],\r
+ Mode::Relative => if (code[position] + relative_base) as usize >= code.len() { 0 } else { code[(code[position] + relative_base) as usize] }\r
+ }\r
+ };\r
+\r
+ fn write(position: usize, value: i64, code: &mut Vec<i64>, mode: Mode, relative_base: i64) {\r
+ let ref_position =\r
+ match mode {\r
+ Mode::Position => code[position] as usize,\r
+ Mode::Immediate => panic!("Can't write un immediate mode"),\r
+ Mode::Relative => (code[position] + relative_base) as usize\r
+ };\r
+\r
+ if ref_position >= code.len() {\r
+ code.resize(ref_position + 1, 0);\r
+ }\r
+\r
+ code[ref_position] = value;\r
+ }\r
+\r
+ fn jump_if(cond: bool, cursor: usize, code: &[i64], modes: [Mode; 3], relative_base: i64) -> usize {\r
+ let value = read(cursor + 1, &code, modes[0], relative_base);\r
+ if cond == (value != 0) {\r
+ read(cursor + 2, &code, modes[1], relative_base) as usize\r
+ } else {\r
+ cursor + 3\r
+ }\r
+ };\r
+\r
+ loop {\r
+ let (op, modes) = read_op_and_modes(code[cursor]);\r
+\r
+ match op {\r
+ // Sum.\r
+ 1 => {\r
+ write(cursor + 3, read(cursor + 1, &code, modes[0], relative_base) + read(cursor + 2, &code, modes[1], relative_base), &mut code, modes[2], relative_base);\r
+ cursor += 4;\r
+ },\r
+\r
+ // Multiply.\r
+ 2 => {\r
+ write(cursor + 3, read(cursor + 1, &code, modes[0], relative_base) * read(cursor + 2, &code, modes[1], relative_base), &mut code, modes[2], relative_base);\r
+ cursor += 4;\r
+ },\r
+\r
+ // Input.\r
+ 3 => {\r
+ write(cursor + 1, input[input_cursor], &mut code, modes[0], relative_base);\r
+ input_cursor += 1;\r
+ cursor += 2;\r
+ }\r
+\r
+ // Output.\r
+ 4 => {\r
+ output.push(read(cursor + 1, &code, modes[0], relative_base));\r
+ cursor += 2;\r
+ }\r
+\r
+ // Jump-if-true.\r
+ 5 => cursor = jump_if(true, cursor, &code, modes, relative_base),\r
+\r
+ // Jump-if-false.\r
+ 6 => cursor = jump_if(false, cursor, &code, modes, relative_base),\r
+\r
+ // Less than.\r
+ 7 => {\r
+ write(cursor + 3, if read(cursor + 1, &code, modes[0], relative_base) < read(cursor + 2, &code, modes[1], relative_base) { 1 } else { 0 }, &mut code, modes[2], relative_base);\r
+ cursor += 4;\r
+ },\r
+\r
+ // Equals.\r
+ 8 => {\r
+ write(cursor + 3, if read(cursor + 1, &code, modes[0], relative_base) == read(cursor + 2, &code, modes[1], relative_base) { 1 } else { 0 }, &mut code, modes[2], relative_base);\r
+ cursor += 4;\r
+ },\r
+\r
+ // Change relative base.\r
+ 9 => {\r
+ relative_base += read(cursor + 1, &code, modes[0], relative_base);\r
+ cursor += 2;\r
+ }\r
+\r
+ 99 => break,\r
+\r
+ _ => panic!("Unkown code: {}", code[cursor])\r
+ }\r
+ }\r
+ output\r
+}\r
+\r
+#[cfg(test)]\r
+mod tests {\r
+ use super::*;\r
+\r
+ #[test]\r
+ fn copy_of_itself() {\r
+ let c = [109,1,204,-1,1001,100,1,100,1008,100,16,101,1006,101,0,99];\r
+ let r = execute_op_code(&c, &Vec::new());\r
+ assert_eq!(r, c);\r
+ }\r
+\r
+ #[test]\r
+ fn output_big_number() {\r
+ let c = [1102,34_915_192,34_915_192,7,4,7,99,0];\r
+ let r = execute_op_code(&c, &Vec::new());\r
+ assert_eq!(r[0], 1_219_070_632_396_864);\r
+ }\r
+\r
+ #[test]\r
+ fn input_big_number() {\r
+ let c = [104,1_125_899_906_842_624,99];\r
+ let r = execute_op_code(&c, &Vec::new());\r
+ assert_eq!(r[0], 1_125_899_906_842_624);\r
+ }\r
+}
\ No newline at end of file