1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
use std::ops::DerefMut;
use std::sync::Mutex;
use utils::md5;
use utils::prelude::*;

/// Finding MD5 hashes, part two.
///
/// Very similar to [2015 Day 4](../year2015/struct.Day04.html), but with more complex logic to
/// assemble the answer from the matching hashes.
///
/// See [`md5::find_hash_with_appended_count()`].
#[derive(Clone, Debug)]
pub struct Day05<'a> {
    prefix: &'a str,
}

impl<'a> Day05<'a> {
    pub fn new(input: &'a str, _: InputType) -> Result<Self, InputError> {
        Ok(Self { prefix: input })
    }

    #[must_use]
    pub fn part1(&self) -> String {
        let mutex = Mutex::new(Vec::new());

        md5::find_hash_with_appended_count(self.prefix, 0, |i, [a, ..]| {
            if a & 0xFFFF_F000 != 0 {
                return false;
            }

            let character = match (a & 0x00000F00) >> 8 {
                n @ 0..=9 => b'0' + n as u8,
                n @ 10..=15 => b'a' + (n - 10) as u8,
                _ => unreachable!(),
            };

            let mut guard = mutex.lock().unwrap();
            guard.push((i, character));

            guard.len() >= 8
        });

        let mut vec = mutex.into_inner().unwrap();
        vec.sort_unstable();
        vec.iter().take(8).map(|&(_, b)| b as char).collect()
    }

    #[must_use]
    pub fn part2(&self) -> String {
        let mutex = Mutex::new(([0u8; 8], [0u32; 8]));

        md5::find_hash_with_appended_count(self.prefix, 0, |i, [a, ..]| {
            if a & 0xFFFF_F800 != 0 {
                return false;
            }

            let position = ((a & 0x0000_0F00) >> 8) as usize;
            let character = match (a & 0x0000_00F0) >> 4 {
                n @ 0..=9 => b'0' + n as u8,
                n @ 10..=15 => b'a' + (n - 10) as u8,
                _ => unreachable!(),
            };

            let mut guard = mutex.lock().unwrap();
            let (password, counts) = guard.deref_mut();

            if password[position] == 0 || i < counts[position] {
                password[position] = character;
                counts[position] = i;
            }

            password.iter().all(|&x| x > 0)
        });

        let (password, ..) = mutex.into_inner().unwrap();
        String::from_utf8(password.to_vec()).unwrap()
    }
}

examples!(Day05<'_> -> (&'static str, &'static str) [
    {input: "abc", part1: "18f47a30", part2: "05ace8e3"},
]);