namespace Day20; public class SignalProcessor { private readonly Dictionary _modules = new Dictionary(); private HashSet _trackCycles = new HashSet(); private Dictionary> _cycleLengths = new Dictionary>(); public SignalProcessor(Dictionary modules) { foreach (var kvp in modules) { _modules[kvp.Key] = kvp.Value.m; foreach (var o in kvp.Value.outputs) { Module? dst; if (modules.TryGetValue(o, out var t)) { dst = modules[o].m; } else { _modules.TryAdd(o, new SinkModule(o)); dst = _modules[o]; } kvp.Value.m.AddOutput(dst); dst.AddInput(kvp.Value.m); } } } public HiLo Push(int count) { Reset(); var visited = new HashSet(); var stateCounts = new List(); var state = MachineState(); var iterations = 0; var result = HiLo.Zero; while (!visited.Contains(state) && iterations < count) { //Console.WriteLine(state); visited.Add(state); iterations++; var hilo = PushTheButton(iterations); stateCounts.Add(hilo); result = result.Add(hilo); state = MachineState(); } if (iterations == 0) { throw new InvalidOperationException("WTF?"); } if (iterations < count) { Console.WriteLine($"Cycle @ {iterations} with state {state}"); var cycles = count / iterations; result = result.Multiply(cycles); var remainder = count % iterations; for (var i = 0; i < remainder; i++) { result = result.Add(stateCounts[i]); } } return result; } public HiLo PushTheButton(int iteration) { var count = HiLo.Zero; var queue = new Queue(); queue.Enqueue(new Pulse("button", "broadcaster", false)); while (queue.Count > 0) { var pulse = queue.Dequeue(); if (_trackCycles.Contains(pulse.Source) && pulse.Value) { _cycleLengths.TryAdd(pulse.Source, new List()); _cycleLengths[pulse.Source].Add(iteration); } //Console.WriteLine($"Sending {pulse.Value} from {pulse.Source} to {pulse.Destination}"); var dst = _modules[pulse.Destination]; count = count.Add(pulse); foreach (var outPulse in dst.Process(pulse)) { queue.Enqueue(outPulse); } } return count; } private string MachineState() { return string.Join("|", _modules.Select(kvp => kvp.Value.GetState())); } public long WaitFor(string sentry) { Reset(); var iterations = 0; var sentryInputs = _modules[sentry].Inputs.Single().Inputs.Select(m => m.Name).ToList(); _trackCycles = new HashSet(sentryInputs); _cycleLengths = new Dictionary>(); while (iterations < 100000) { iterations++; PushTheButton(iterations); } var lcmFactors = new HashSet(); foreach (var kvp in _cycleLengths) { var first = kvp.Value.First(); if (kvp.Value.Any(x => x % first != 0)) { throw new Exception($"Mismatching cycle '{kvp.Key}' => {string.Join("\n", kvp.Value)}"); } foreach (var prime in Primes.Factorize(first)) { lcmFactors.Add(prime); } } var lcm = lcmFactors.Aggregate(1L, (acc, item) => acc * item); return lcm; } private void Reset() { foreach (var kvp in _modules) { kvp.Value.Reset(); } } }