143 lines
3.7 KiB
Rust
143 lines
3.7 KiB
Rust
use std::io::BufRead;
|
|
use std::str::FromStr;
|
|
use std::collections::{HashMap, VecDeque};
|
|
|
|
|
|
#[derive(Debug)]
|
|
struct Mask {
|
|
value: VecDeque<char>
|
|
}
|
|
|
|
impl FromStr for Mask {
|
|
type Err = ();
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
Ok(Mask {
|
|
value: s.chars().collect()
|
|
})
|
|
}
|
|
}
|
|
|
|
impl Mask {
|
|
fn id() -> Self {
|
|
Mask {
|
|
value: "0".repeat(36).chars().collect()
|
|
}
|
|
}
|
|
fn apply(&self, value: u64) -> Vec<String> {
|
|
let input: VecDeque<_> = format!("{:036b}", value).chars().collect();
|
|
let mut res = Vec::new();
|
|
self.apply_rec(self.value.clone(), input, Vec::new(), &mut res);
|
|
res
|
|
}
|
|
|
|
fn apply_rec(&self, mut mask: VecDeque<char>, mut input: VecDeque<char>, mut output: Vec<char>, acc: &mut Vec<String>) {
|
|
match mask.pop_front() {
|
|
None => acc.push(output.iter().collect()),
|
|
Some('0') => {
|
|
output.push(input.pop_front().unwrap());
|
|
self.apply_rec(mask, input, output, acc)
|
|
},
|
|
Some('1') => {
|
|
input.pop_front().unwrap();
|
|
output.push('1');
|
|
self.apply_rec(mask, input, output, acc)
|
|
},
|
|
Some('X') => {
|
|
input.pop_front().unwrap();
|
|
let mut output_zero = output.clone();
|
|
output_zero.push('0');
|
|
self.apply_rec(mask.clone(), input.clone(), output_zero, acc);
|
|
|
|
output.push('1');
|
|
self.apply_rec(mask, input, output, acc);
|
|
},
|
|
_ => unreachable!()
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
enum Instruction {
|
|
ChangeMask(Mask),
|
|
AssignMemory {
|
|
address: u64,
|
|
value: u64,
|
|
}
|
|
}
|
|
|
|
impl FromStr for Instruction {
|
|
type Err = ();
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
let mut splitted = s.split(" = ");
|
|
let instruction = splitted.next().unwrap();
|
|
let value = splitted.next().unwrap();
|
|
|
|
Ok(if instruction == "mask" {
|
|
Instruction::ChangeMask(value.parse().unwrap())
|
|
} else {
|
|
Instruction::AssignMemory {
|
|
address: instruction.trim_end_matches(']').trim_start_matches("mem[").parse().unwrap(),
|
|
value: value.parse().unwrap()
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
struct Program {
|
|
instructions: Vec<Instruction>,
|
|
memory: HashMap<String, u64>,
|
|
mask: Mask,
|
|
}
|
|
|
|
impl Program {
|
|
fn run(&mut self) {
|
|
for instruction in self.instructions.drain(..) {
|
|
match instruction {
|
|
Instruction::ChangeMask(mask) => self.mask = mask,
|
|
Instruction::AssignMemory { value, address } => {
|
|
for addr_variant in self.mask.apply(address).drain(..) {
|
|
self.memory.insert(addr_variant, value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Program {
|
|
fn build(instructions: Vec<Instruction>) -> Self {
|
|
Program {
|
|
instructions,
|
|
mask: Mask::id(),
|
|
memory: HashMap::new(),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn run_part2<F: BufRead> (input: F) -> u64 {
|
|
let instructions: Vec<Instruction> = input.lines()
|
|
.filter_map(|line| line.unwrap().parse().ok())
|
|
.collect();
|
|
let mut program = Program::build(instructions);
|
|
program.run();
|
|
program.memory.values().sum()
|
|
}
|
|
|
|
pub fn part2<F: BufRead> (input: F) {
|
|
println!("{}", run_part2(input))
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use super::*;
|
|
#[test]
|
|
pub fn test_parse() {
|
|
let input = r#"mask = 000000000000000000000000000000X1001X
|
|
mem[42] = 100
|
|
mask = 00000000000000000000000000000000X0XX
|
|
mem[26] = 1"#.as_bytes();
|
|
assert_eq!(run_part2(input), 208);
|
|
}
|
|
}
|