1use std::sync::Mutex;
2use utils::md5;
3use utils::prelude::*;
4
5#[derive(Clone, Debug)]
12pub struct Day05<'a> {
13 prefix: &'a str,
14}
15
16impl<'a> Day05<'a> {
17 pub fn new(input: &'a str, _: InputType) -> Result<Self, InputError> {
18 Ok(Self { prefix: input })
19 }
20
21 #[must_use]
22 pub fn part1(&self) -> String {
23 let mutex = Mutex::new(Vec::new());
24
25 md5::find_hash_with_appended_count(self.prefix, 0, |i, [a, ..]| {
26 if a & 0xFFFF_F000 != 0 {
27 return false;
28 }
29
30 let character = match (a & 0x0000_0F00) >> 8 {
31 n @ 0..=9 => b'0' + n as u8,
32 n @ 10..=15 => b'a' + (n - 10) as u8,
33 _ => unreachable!(),
34 };
35
36 let mut guard = mutex.lock().unwrap();
37 guard.push((i, character));
38
39 guard.len() >= 8
40 });
41
42 let mut vec = mutex.into_inner().unwrap();
43 vec.sort_unstable();
44 vec.iter().take(8).map(|&(_, b)| b as char).collect()
45 }
46
47 #[must_use]
48 pub fn part2(&self) -> String {
49 let mutex = Mutex::new(([0u8; 8], [0u32; 8]));
50
51 md5::find_hash_with_appended_count(self.prefix, 0, |i, [a, ..]| {
52 if a & 0xFFFF_F800 != 0 {
53 return false;
54 }
55
56 let position = ((a & 0x0000_0F00) >> 8) as usize;
57 let character = match (a & 0x0000_00F0) >> 4 {
58 n @ 0..=9 => b'0' + n as u8,
59 n @ 10..=15 => b'a' + (n - 10) as u8,
60 _ => unreachable!(),
61 };
62
63 let mut guard = mutex.lock().unwrap();
64 let (password, counts) = &mut *guard;
65
66 if password[position] == 0 || i < counts[position] {
67 password[position] = character;
68 counts[position] = i;
69 }
70
71 password.iter().all(|&x| x > 0)
72 });
73
74 let (password, ..) = mutex.into_inner().unwrap();
75 String::from_utf8(password.to_vec()).unwrap()
76 }
77}
78
79examples!(Day05<'_> -> (&'static str, &'static str) [
80 {input: "abc", part1: "18f47a30", part2: "05ace8e3"},
81]);