year2016/
day05.rs

1use std::ops::DerefMut;
2use std::sync::Mutex;
3use utils::md5;
4use utils::prelude::*;
5
6/// Finding MD5 hashes, part two.
7///
8/// Very similar to [2015 Day 4](../year2015/struct.Day04.html), but with more complex logic to
9/// assemble the answer from the matching hashes.
10///
11/// See [`md5::find_hash_with_appended_count()`].
12#[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]);