Skip to main content

year2016/
day05.rs

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