1use std::ops::ControlFlow;
2use utils::hash::FastMap;
3use utils::prelude::*;
4
5#[derive(Clone, Debug)]
7pub struct Day24 {
8 wires: Vec<Wire>,
9 wire_names: Vec<[u8; 3]>,
10 x_initial: u64,
11 y_initial: u64,
12 z_indexes: Vec<usize>,
13}
14
15#[derive(Copy, Clone, Debug, PartialEq)]
16enum Wire {
17 X(usize),
18 Y(usize),
19 And(usize, usize),
20 Or(usize, usize),
21 Xor(usize, usize),
22}
23
24impl Day24 {
25 pub fn new(input: &str, _: InputType) -> Result<Self, InputError> {
26 let Some((initial_str, gate_str)) = input
27 .split_once("\n\n")
28 .or_else(|| input.split_once("\r\n\r\n"))
29 else {
30 return Err(InputError::new(input, 0, "expected inputs and gates"));
31 };
32
33 let mut wires = Vec::new();
34 let mut wire_names = Vec::new();
35 let mut indexes = FastMap::new();
36 let mut x_initial = 0;
37 let mut y_initial = 0;
38 let mut input_bits = 64;
39
40 let mut next = (b'x', 0);
41 for item in parser::byte_range(b'x'..=b'y')
42 .then(parser::byte_range(b'0'..=b'9'))
43 .then(parser::byte_range(b'0'..=b'9'))
44 .with_suffix(": ")
45 .then(parser::byte_range(b'0'..=b'1'))
46 .with_consumed()
47 .with_eol()
48 .parse_iterator(initial_str)
49 {
50 let ((wire, b), line) = item?;
51 let n = ((wire.1 - b'0') * 10 + (wire.2 - b'0')) as usize;
52
53 if (wire.0, n) != next {
54 if next.0 == b'x' && wire == (b'y', b'0', b'0') {
55 input_bits = next.1;
56 } else {
57 return Err(InputError::new(input, line, "unexpected initial value"));
58 }
59 }
60
61 if wire.0 == b'x' {
62 x_initial |= u64::from(b == b'1') << n;
63 wires.push(Wire::X(n));
64 } else {
65 y_initial |= u64::from(b == b'1') << n;
66 wires.push(Wire::Y(n));
67 }
68 wire_names.push(wire.into());
69 indexes.insert(wire.into(), wires.len() - 1);
70
71 if n == input_bits - 1 {
72 next = (b'?', 0);
73 } else {
74 next = (wire.0, n + 1);
75 }
76 }
77
78 let mut z_indexes = vec![usize::MAX; input_bits + 1];
79 let wire = parser::byte().repeat_n::<3, _>(parser::noop());
80 for item in wire
81 .then(parser::literal_map!(
82 " AND " => Wire::And as fn(usize, usize) -> Wire,
83 " OR " => Wire::Or,
84 " XOR " => Wire::Xor,
85 ))
86 .then(wire.with_suffix(" -> "))
87 .then(wire)
88 .with_eol()
89 .parse_iterator(gate_str)
90 {
91 let (in1, gate, in2, out) = item?;
92
93 let mut index_of = |n| {
94 *indexes.entry(n).or_insert_with(|| {
95 wires.push(Wire::X(usize::MAX)); wire_names.push(n);
97 wires.len() - 1
98 })
99 };
100
101 let in1_index = index_of(in1);
102 let in2_index = index_of(in2);
103 let out_index = index_of(out);
104
105 if wires[out_index] != Wire::X(usize::MAX) {
106 return Err(InputError::new(input, 0, "duplicate wire definition"));
107 }
108 if out[0] == b'z' {
109 let index = ((out[1] - b'0') * 10 + (out[2] - b'0')) as usize;
110 if index < z_indexes.len() {
111 z_indexes[index] = out_index;
112 } else {
113 return Err(InputError::new(input, 0, "too many z outputs"));
114 }
115 }
116
117 wires[out_index] = gate(in1_index, in2_index);
118 }
119
120 if wires.contains(&Wire::X(usize::MAX)) {
121 return Err(InputError::new(input, 0, "undefined wire"));
122 }
123 if z_indexes.contains(&usize::MAX) {
124 return Err(InputError::new(input, 0, "undefined z output"));
125 }
126
127 Ok(Self {
128 wires,
129 wire_names,
130 x_initial,
131 y_initial,
132 z_indexes,
133 })
134 }
135
136 #[must_use]
137 pub fn part1(&self) -> u64 {
138 let mut z = 0;
139 let mut cache = vec![None; self.wires.len()];
140 for (i, &index) in self.z_indexes.iter().enumerate() {
141 z |= u64::from(Self::evaluate(
142 index,
143 &self.wires,
144 self.x_initial,
145 self.y_initial,
146 &mut cache,
147 )) << i;
148 }
149 z
150 }
151
152 fn evaluate(index: usize, wires: &[Wire], x: u64, y: u64, cache: &mut [Option<bool>]) -> bool {
153 if let Some(c) = cache[index] {
154 return c;
155 }
156 let v = match wires[index] {
157 Wire::X(n) => return x & (1 << n) != 0,
158 Wire::Y(n) => return y & (1 << n) != 0,
159 Wire::And(a, b) => {
160 Self::evaluate(a, wires, x, y, cache) && Self::evaluate(b, wires, x, y, cache)
161 }
162 Wire::Or(a, b) => {
163 Self::evaluate(a, wires, x, y, cache) || Self::evaluate(b, wires, x, y, cache)
164 }
165 Wire::Xor(a, b) => {
166 Self::evaluate(a, wires, x, y, cache) ^ Self::evaluate(b, wires, x, y, cache)
167 }
168 };
169 cache[index] = Some(v);
170 v
171 }
172
173 #[must_use]
174 pub fn part2(&self) -> String {
175 let mut test_cases = Vec::new();
176 for i in 0..self.z_indexes.len() - 1 {
177 test_cases.push((i, 1u64 << i, 0u64));
178 test_cases.push((i, 0u64, 1u64 << i));
179 test_cases.push((i + 1, 1u64 << i, 1u64 << i));
180 test_cases.push((i + 1, (1u64 << (i + 1)) - 1, 1u64));
181 test_cases.push((i + 1, 1u64, (1u64 << (i + 1)) - 1));
182 }
183
184 let mut wires = self.wires.clone();
185 assert!(
186 self.find_swaps(&test_cases, &mut wires, None).is_break(),
187 "failed to find working combination"
188 );
189
190 let mut changes = Vec::new();
191 for (i, (&wire, &orig)) in wires.iter().zip(&self.wires).enumerate() {
192 if wire != orig {
193 changes.push(self.wire_names[i]);
194 }
195 }
196 assert_eq!(changes.len(), 8, "found incorrect number of changes");
197
198 changes.sort_unstable();
199 changes.into_iter().fold(String::new(), |mut acc, name| {
200 if !acc.is_empty() {
201 acc.push(',');
202 }
203 acc.push(name[0] as char);
204 acc.push(name[1] as char);
205 acc.push(name[2] as char);
206 acc
207 })
208 }
209
210 fn find_swaps(
211 &self,
212 test_cases: &[(usize, u64, u64)],
213 wires: &mut [Wire],
214 last: Option<usize>,
215 ) -> ControlFlow<()> {
216 let mut cache = vec![None; wires.len()];
217 let mut used = vec![false; wires.len()];
218 for &(n, x, y) in test_cases {
219 if Some(n) < last {
220 continue;
223 }
224
225 cache.fill(None);
226 let sum = x + y;
227
228 for (i, &z_index) in self.z_indexes.iter().enumerate().take(n) {
230 let b = Self::evaluate(z_index, wires, x, y, &mut cache);
231 if ((sum >> i) & 1 != 0) != b {
232 return ControlFlow::Continue(());
234 }
235 }
236
237 for i in 0..wires.len() {
239 used[i] = cache[i].is_some();
240 }
241
242 let b = Self::evaluate(self.z_indexes[n], wires, x, y, &mut cache);
243 if ((sum >> n) & 1 != 0) == b {
244 continue;
246 }
247
248 if last == Some(n) {
250 return ControlFlow::Continue(());
252 }
253
254 let candidates1 = cache
256 .iter()
257 .zip(&used)
258 .enumerate()
259 .filter(|&(_, (&c, &u))| c.is_some() && !u)
260 .map(|(i, _)| i)
261 .collect::<Vec<_>>();
262 let mask = (1 << (n + 1)) - 1;
264 let candidates2 = Self::used_bits(wires)
265 .iter()
266 .zip(&used)
267 .enumerate()
268 .filter(|&(_, (&b, &u))| b != 0 && b & mask == b && !u)
269 .map(|(i, _)| i)
270 .collect::<Vec<_>>();
271
272 for &c1 in &candidates1 {
274 for &c2 in &candidates2 {
275 if c1 == c2 {
276 continue;
277 }
278
279 (wires[c1], wires[c2]) = (wires[c2], wires[c1]);
280
281 if !Self::loops(wires, &[c1, c2]) {
283 cache.fill(None);
286 let b = Self::evaluate(self.z_indexes[n], wires, x, y, &mut cache);
287 if ((sum >> n) & 1 != 0) == b
288 && self.find_swaps(test_cases, wires, Some(n)).is_break()
289 {
290 return ControlFlow::Break(());
292 }
293 }
294
295 (wires[c1], wires[c2]) = (wires[c2], wires[c1]);
297 }
298 }
299
300 return ControlFlow::Continue(());
302 }
303
304 ControlFlow::Break(())
305 }
306
307 fn used_bits(wires: &[Wire]) -> Vec<u64> {
308 fn eval(index: usize, wires: &[Wire], used_bits: &mut [u64]) -> u64 {
309 if used_bits[index] != 0 {
310 return used_bits[index];
311 }
312 let v = match wires[index] {
313 Wire::X(n) | Wire::Y(n) => return 1 << n,
314 Wire::And(a, b) | Wire::Or(a, b) | Wire::Xor(a, b) => {
315 eval(a, wires, used_bits) | eval(b, wires, used_bits)
316 }
317 };
318 used_bits[index] = v;
319 v
320 }
321
322 let mut used = vec![0; wires.len()];
323 for i in 0..wires.len() {
324 eval(i, wires, &mut used);
325 }
326 used
327 }
328
329 fn loops(wires: &[Wire], to_check: &[usize]) -> bool {
330 #[derive(Copy, Clone, PartialEq)]
331 enum State {
332 Unvisited,
333 InStack,
334 Visited,
335 }
336
337 fn eval(index: usize, wires: &[Wire], states: &mut [State]) -> bool {
338 if states[index] != State::Unvisited {
339 return states[index] == State::InStack;
340 }
341 match wires[index] {
342 Wire::X(_) | Wire::Y(_) => {}
343 Wire::And(a, b) | Wire::Or(a, b) | Wire::Xor(a, b) => {
344 states[index] = State::InStack;
345 if eval(a, wires, states) || eval(b, wires, states) {
346 return true;
347 }
348 states[index] = State::Visited;
349 }
350 }
351 false
352 }
353
354 let mut states = vec![State::Unvisited; wires.len()];
355 to_check.iter().any(|&i| eval(i, wires, &mut states))
356 }
357}
358
359examples!(Day24 -> (u64, &'static str) []);