1use core::fmt::NumBuffer;
6use std::collections::{BTreeMap, BTreeSet};
7use std::sync::Mutex;
8
9use self::implementation::*;
10use crate::util::md5::*;
11use crate::util::thread::*;
12
13struct Shared<'a> {
15 input: &'a str,
16 part_two: bool,
17 iter: AtomicIter,
18 mutex: Mutex<Exclusive>,
19}
20
21struct Exclusive {
23 threes: BTreeMap<i32, u32>,
24 fives: BTreeMap<i32, u32>,
25 found: BTreeSet<i32>,
26}
27
28pub fn parse(input: &str) -> &str {
29 input.trim()
30}
31
32pub fn part1(input: &str) -> i32 {
34 generate_pad(input, false)
35}
36
37pub fn part2(input: &str) -> i32 {
39 generate_pad(input, true)
40}
41
42fn generate_pad(input: &str, part_two: bool) -> i32 {
44 let step = if cfg!(feature = "simd") { 32 } else { 1 };
45
46 let exclusive =
47 Exclusive { threes: BTreeMap::new(), fives: BTreeMap::new(), found: BTreeSet::new() };
48 let shared =
49 Shared { input, part_two, iter: AtomicIter::new(0, step), mutex: Mutex::new(exclusive) };
50
51 spawn(|| worker(&shared));
53
54 let exclusive = shared.mutex.into_inner().unwrap();
55 *exclusive.found.iter().nth(63).unwrap()
56}
57
58fn format_string(prefix: &str, n: i32) -> ([u8; 64], usize) {
60 let mut number = NumBuffer::new();
61 let digits = n.format_into(&mut number).as_bytes();
62 let size = prefix.len() + digits.len();
63
64 let mut buffer = [0; 64];
65 buffer[..prefix.len()].copy_from_slice(prefix.as_bytes());
66 buffer[prefix.len()..size].copy_from_slice(digits);
67
68 (buffer, size)
69}
70
71#[inline]
73fn to_ascii(n: u32) -> [u8; 8] {
74 let mut n = n as u64;
76 n = ((n << 16) & 0x0000ffff00000000) | (n & 0x000000000000ffff);
77 n = ((n << 8) & 0x00ff000000ff0000) | (n & 0x000000ff000000ff);
78 n = ((n << 4) & 0x0f000f000f000f00) | (n & 0x000f000f000f000f);
79
80 let mask = ((n + 0x0606060606060606) >> 4) & 0x0101010101010101;
90 n = n + 0x3030303030303030 + 0x27 * mask;
91 n.to_be_bytes()
92}
93
94#[cfg(not(feature = "simd"))]
95mod implementation {
96 use super::*;
97
98 pub(super) fn worker(shared: &Shared<'_>) {
99 while let Some(n) = shared.iter.next() {
100 let n = n as i32;
102
103 let (mut buffer, size) = format_string(shared.input, n);
105 let mut result = hash(&mut buffer, size);
106
107 if shared.part_two {
108 for _ in 0..2016 {
109 buffer[0..8].copy_from_slice(&to_ascii(result[0]));
110 buffer[8..16].copy_from_slice(&to_ascii(result[1]));
111 buffer[16..24].copy_from_slice(&to_ascii(result[2]));
112 buffer[24..32].copy_from_slice(&to_ascii(result[3]));
113 result = hash(&mut buffer, 32);
114 }
115 }
116
117 check(shared, n, result);
118 }
119 }
120
121 fn check(shared: &Shared<'_>, n: i32, hash: [u32; 4]) {
123 let [a, b, c, d] = hash;
124
125 let mut prev = u32::MAX;
126 let mut same = 1;
127 let mut three = 0;
128 let mut five = 0;
129
130 for mut word in [d, c, b, a] {
131 for _ in 0..8 {
132 let next = word & 0xf;
133
134 if next == prev {
135 same += 1;
136 } else {
137 same = 1;
138 }
139
140 if same == 3 {
141 three = 1 << next;
142 }
143 if same == 5 {
144 five |= 1 << next;
145 }
146
147 word >>= 4;
148 prev = next;
149 }
150 }
151
152 if three != 0 || five != 0 {
153 let mut exclusive = shared.mutex.lock().unwrap();
154 let mut candidates = Vec::new();
155
156 if three != 0 {
158 exclusive.threes.insert(n, three);
159
160 for (_, mask) in exclusive.fives.range(n + 1..n + 1001) {
161 if three & mask != 0 {
162 candidates.push(n);
163 }
164 }
165 }
166
167 if five != 0 {
169 exclusive.fives.insert(n, five);
170
171 for (&index, &mask) in exclusive.threes.range(n - 1000..n) {
172 if five & mask != 0 {
173 candidates.push(index);
174 }
175 }
176 }
177
178 exclusive.found.extend(candidates);
180
181 if exclusive.found.len() >= 64 {
182 shared.iter.stop();
183 }
184 }
185 }
186}
187
188#[cfg(feature = "simd")]
189mod implementation {
190 use std::simd::prelude::*;
191
192 use super::*;
193 use crate::util::bitset::*;
194 use crate::util::md5::simd::hash_fixed;
195
196 pub(super) fn worker(shared: &Shared<'_>) {
198 let mut result = [Simd::splat(0); 4];
199 let mut buffers = [[0; 64]; 32];
200
201 while let Some(start) = shared.iter.next() {
202 let start = start as i32;
204
205 for i in 0..32 {
207 let (mut buffer, size) = format_string(shared.input, start + i as i32);
208 let [a, b, c, d] = hash(&mut buffer, size);
209
210 result[0][i] = a;
211 result[1][i] = b;
212 result[2][i] = c;
213 result[3][i] = d;
214 }
215
216 if shared.part_two {
217 for _ in 0..2016 {
218 for i in 0..32 {
219 buffers[i][0..8].copy_from_slice(&to_ascii(result[0][i]));
220 buffers[i][8..16].copy_from_slice(&to_ascii(result[1][i]));
221 buffers[i][16..24].copy_from_slice(&to_ascii(result[2][i]));
222 buffers[i][24..32].copy_from_slice(&to_ascii(result[3][i]));
223 }
224 result = hash_fixed(&mut buffers, 32);
225 }
226 }
227
228 check(shared, start, &result);
229 }
230 }
231
232 #[inline]
234 fn check(shared: &Shared<'_>, start: i32, hash: &[Simd<u32, 32>; 4]) {
235 let &[a, b, c, d] = hash;
236
237 let mut prev: Simd<u32, 32> = Simd::splat(u32::MAX);
238 let mut same: Simd<u32, 32> = Simd::splat(1);
239 let mut three: Simd<u32, 32> = Simd::splat(0);
240 let mut five: Simd<u32, 32> = Simd::splat(0);
241
242 for mut word in [d, c, b, a] {
243 for _ in 0..8 {
244 let next = word & Simd::splat(0xf);
245 same = next.simd_eq(prev).select(same + Simd::splat(1), Simd::splat(1));
246
247 three = same.simd_eq(Simd::splat(3)).select(Simd::splat(1) << next, three);
248 five |= same.simd_eq(Simd::splat(5)).select(Simd::splat(1) << next, Simd::splat(0));
249
250 word >>= 4;
251 prev = next;
252 }
253 }
254
255 let three_mask = three.simd_ne(Simd::splat(0)).to_bitmask();
256 let five_mask = five.simd_ne(Simd::splat(0)).to_bitmask();
257
258 if three_mask != 0 || five_mask != 0 {
259 let mut exclusive = shared.mutex.lock().unwrap();
260 let mut candidates = Vec::new();
261
262 for i in three_mask.biterator() {
263 let three = three[i];
264 let n = start + i as i32;
265
266 if three != 0 {
268 exclusive.threes.insert(n, three);
269
270 for (_, mask) in exclusive.fives.range(n + 1..n + 1001) {
271 if three & mask != 0 {
272 candidates.push(n);
273 }
274 }
275 }
276 }
277
278 for i in five_mask.biterator() {
279 let five = five[i];
280 let n = start + i as i32;
281
282 if five != 0 {
284 exclusive.fives.insert(n, five);
285
286 for (&index, &mask) in exclusive.threes.range(n - 1000..n) {
287 if five & mask != 0 {
288 candidates.push(index);
289 }
290 }
291 }
292 }
293
294 exclusive.found.extend(candidates);
296
297 if exclusive.found.len() >= 64 {
298 shared.iter.stop();
299 }
300 }
301 }
302}