use std::cmp; use std::collections::{HashMap, VecDeque}; use std::fs::read_to_string; use std::time::Instant; use num::integer::lcm; struct Pulse { src: u16, dest: u16, high: bool, } enum ModuleKind { Broadcast, FlipFlop(bool), Conjunction(Vec), } struct Module { inputs: Vec, outputs: Vec, id: u16, kind: ModuleKind, } impl Module { fn new(id: u16, kind: ModuleKind) -> Module { Module { inputs: vec![], outputs: vec![], id, kind, } } fn new_broadcast(id: u16) -> Module { Module::new(id, ModuleKind::Broadcast) } fn new_flipflop(id: u16) -> Module { Module::new(id, ModuleKind::FlipFlop(false)) } fn new_conjunction(id: u16) -> Module { Module::new(id, ModuleKind::Conjunction(vec![])) } fn add_input(&mut self, id: u16) { self.inputs.push(id); match self.kind { ModuleKind::Conjunction(ref mut memory) => { memory.push(false); } _ => {} } } fn add_output(&mut self, id: u16) { self.outputs.push(id); } fn handle_pulse(&mut self, pulse: Pulse, queue: &mut VecDeque) { match self.kind { ModuleKind::Broadcast => { queue.extend(self.outputs.iter().map( |&id| Pulse { src: self.id, dest: id, high: pulse.high })); } ModuleKind::FlipFlop(ref mut on) => { if !pulse.high { *on = !*on; queue.extend(self.outputs.iter().map( |&id| Pulse { src: self.id, dest: id, high: *on })); } } ModuleKind::Conjunction(ref mut memory) => { if let Some(pos) = self.inputs.iter().position(|&id| id == pulse.src) { memory[pos] = pulse.high; let out_high = !memory.iter().all(|&v| v); queue.extend(self.outputs.iter().map( |&id| Pulse { src: self.id, dest: id, high: out_high })); } } } } } fn encode_module_name(name: &str) -> u16 { let bytes = &name.as_bytes()[0..cmp::min(name.as_bytes().len(), 3)]; let mut acc = 0u16; for &c in bytes { acc = acc * 26 + (c - b'a') as u16; } acc } fn main() { let time_start = Instant::now(); let input_str = read_to_string("input.txt").unwrap(); let time_start_no_io = Instant::now(); let mut modules: HashMap = HashMap::new(); // let rx_id = encode_module_name("rx"); // modules.insert(rx_id, Module::new_flipflop(rx_id)); for line in input_str.lines() { let mut split_whitespace = line.split_whitespace(); let name = split_whitespace.next().unwrap(); let new_module: Module; let id = encode_module_name(&name[1..]); match name.as_bytes()[0] { b'%' => new_module = Module::new_flipflop(id), b'&' => new_module = Module::new_conjunction(id), _ => new_module = Module::new_broadcast(id) } modules.insert(id, new_module); } for line in input_str.lines() { let mut split = line.split([' ', ',']). filter(|&v| !v.is_empty()); let name = split.next().unwrap(); let in_id = encode_module_name(&name[1..]); split.next(); while let Some(name) = split.next() { let id = encode_module_name(name); modules.get_mut(&in_id).unwrap().add_output(id); match modules.get_mut(&id) { None => {} Some(in_module) => { in_module.add_input(in_id); } } } } // Part 1 let broadcast_id = encode_module_name("roadcast"); let mut low_pulses = 0u64; let mut high_pulses = 0u64; let mut queue: VecDeque = VecDeque::new(); for _ in 0..1000 { let mut low_pulses_step = 0; let mut high_pulses_step = 0; queue.push_back(Pulse { src: u16::MAX, dest: broadcast_id, high: false }); while !queue.is_empty() { let pulse = queue.pop_front().unwrap(); match pulse.high { true => high_pulses_step += 1, false => low_pulses_step += 1 } match modules.get_mut(&pulse.dest) { None => {} Some(module) => { module.handle_pulse(pulse, &mut queue); } } } low_pulses += low_pulses_step; high_pulses += high_pulses_step; } let result1 = low_pulses * high_pulses; // Part 2 // Use the knowledge of the network, that it is that broadcast drives a series of counters let mut counter_periods: Vec = vec![]; for &node in &modules.get(&broadcast_id).unwrap().outputs { let mut period = 0u16; let mut current_node = node; let mut bits = 0; loop { bits += 1; let mut next_id = current_node; period >>= 1; let outputs = &modules.get(¤t_node).unwrap().outputs; for &output_id in outputs { match modules.get(&output_id).unwrap().kind { ModuleKind::Conjunction(_) => period |= 0x8000, _ => next_id = output_id } } if next_id == current_node { break; } else { current_node = next_id; } } period >>= 16 - bits; counter_periods.push(period as u64); } let result2 = counter_periods.iter().fold(1u64, |acc, &v| lcm(acc, v)); let elapsed = time_start.elapsed().as_micros(); let elapsed_no_io = time_start_no_io.elapsed().as_micros(); println!("Time: {}us", elapsed); println!("Time without file i/o: {}us", elapsed_no_io); println!("Result1: {}", result1); println!("Result2: {}", result2); }