1use crate::util::md5::*;
16use crate::util::thread::*;
17use std::sync::{Condvar, Mutex};
18
19type Input = (String, usize);
20type Item = (u8, u8, usize, Vec<u8>);
21
22struct State {
23 todo: Vec<Item>,
24 min: String,
25 max: usize,
26}
27
28struct Exclusive {
29 global: State,
30 inflight: usize,
31}
32
33struct Shared {
34 prefix: usize,
35 mutex: Mutex<Exclusive>,
36 not_empty: Condvar,
37}
38
39pub fn parse(input: &str) -> Input {
40 let input = input.trim().as_bytes();
42 let prefix = input.len();
43 let start = (0, 0, prefix, extend(input, prefix, 0));
44
45 let global = State { todo: vec![start], min: String::new(), max: 0 };
47 let exclusive = Exclusive { global, inflight: 0 };
48 let shared = Shared { prefix, mutex: Mutex::new(exclusive), not_empty: Condvar::new() };
49
50 spawn(|| worker(&shared));
52
53 let global = shared.mutex.into_inner().unwrap().global;
54 (global.min, global.max)
55}
56
57pub fn part1(input: &Input) -> &str {
58 &input.0
59}
60
61pub fn part2(input: &Input) -> usize {
62 input.1
63}
64
65fn worker(shared: &Shared) {
68 let mut local = State { todo: Vec::new(), min: String::new(), max: 0 };
69
70 loop {
71 let mut exclusive = shared.mutex.lock().unwrap();
72 let item = loop {
73 if let Some(item) = exclusive.global.todo.pop() {
75 exclusive.inflight += 1;
76 break item;
77 }
78 if exclusive.inflight == 0 {
80 return;
81 }
82 exclusive = shared.not_empty.wait(exclusive).unwrap();
85 };
86
87 drop(exclusive);
89
90 local.todo.push(item);
92 explore(shared, &mut local);
93
94 let mut exclusive = shared.mutex.lock().unwrap();
96 let global = &mut exclusive.global;
97
98 global.todo.append(&mut local.todo);
99 if global.min.is_empty() || local.min.len() < global.min.len() {
100 global.min = local.min.clone();
101 }
102 global.max = global.max.max(local.max);
103
104 exclusive.inflight -= 1;
106 shared.not_empty.notify_all();
107 }
108}
109
110fn explore(shared: &Shared, local: &mut State) {
116 for _ in 0..100 {
117 let Some((x, y, size, mut path)) = local.todo.pop() else { break };
118
119 if x == 3 && y == 3 {
120 let adjusted = size - shared.prefix;
122 if local.min.is_empty() || adjusted < local.min.len() {
123 let middle = path[shared.prefix..size].to_vec();
125 local.min = String::from_utf8(middle).unwrap();
126 }
127 local.max = local.max.max(adjusted);
128 } else {
129 let (result, ..) = hash(&mut path, size);
131
132 if y > 0 && ((result >> 28) & 0xf) > 0xa {
133 local.todo.push((x, y - 1, size + 1, extend(&path, size, b'U')));
134 }
135 if y < 3 && ((result >> 24) & 0xf) > 0xa {
136 local.todo.push((x, y + 1, size + 1, extend(&path, size, b'D')));
137 }
138 if x > 0 && ((result >> 20) & 0xf) > 0xa {
139 local.todo.push((x - 1, y, size + 1, extend(&path, size, b'L')));
140 }
141 if x < 3 && ((result >> 16) & 0xf) > 0xa {
142 local.todo.push((x + 1, y, size + 1, extend(&path, size, b'R')));
143 }
144 }
145 }
146}
147
148fn extend(src: &[u8], size: usize, b: u8) -> Vec<u8> {
150 let padded = buffer_size(size + 1);
152 let mut next = vec![0; padded];
153 next[0..size].copy_from_slice(&src[0..size]);
155 next[size] = b;
156 next
157}