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