1use utils::prelude::*;
2
3#[derive(Clone, Debug)]
5pub struct Day04 {
6 part1: u32,
7 part2: u32,
8}
9
10impl Day04 {
11 pub fn new(input: &str, _: InputType) -> Result<Self, InputError> {
12 let [start, end] = parser::digit()
13 .repeat_n::<6, _>(parser::noop())
14 .map_res(|x| {
15 if x[0] == 0 {
16 Err("expected six digit number not starting with zero")
17 } else {
18 Ok(x)
19 }
20 })
21 .repeat_n(b'-')
22 .parse_complete(input)?;
23
24 let (mut part1, mut part2) = (0, 0);
25 for password in Self::packed_passwords(start, end) {
26 let adjacent_xor = password ^ (password >> 8);
30 let different_mask = (adjacent_xor + 0x0f0f_0f0f_0f0f) & 0x1010_1010_1010;
32
33 let pair = !different_mask & 0x1010_1010_1010;
35 part1 += u32::from(pair != 0);
36
37 let padded_mask = (different_mask << 8) | 0x1000_0000_0000_0010;
39 let exact_pair = !padded_mask & (padded_mask << 8) & (padded_mask >> 8);
41 part2 += u32::from(exact_pair != 0);
42 }
43
44 Ok(Self { part1, part2 })
45 }
46
47 #[must_use]
48 pub fn part1(&self) -> u32 {
49 self.part1
50 }
51
52 #[must_use]
53 pub fn part2(&self) -> u32 {
54 self.part2
55 }
56
57 fn packed_passwords(mut start: [u8; 6], end: [u8; 6]) -> impl Iterator<Item = u64> {
58 if let Some(i) = start.array_windows().position(|&[a, b]| a > b) {
60 let digit = start[i];
61 start[i + 1..].fill(digit);
62 }
63
64 let mut password = u64::from_be_bytes([
65 0, 0, start[0], start[1], start[2], start[3], start[4], start[5],
66 ]);
67 let end = u64::from_be_bytes([0, 0, end[0], end[1], end[2], end[3], end[4], end[5]]);
68
69 std::iter::from_fn(move || {
70 if password > end {
71 return None;
72 }
73 let current = password;
74
75 if password & 0xF != 9 {
76 password += 1;
78 return Some(current);
79 }
80
81 let nine_mask = (password + 0x0707_0707_0707) & 0x1010_1010_1010;
86 let leading_less_than_nine = (nine_mask << 16 | 0xFFFF).leading_zeros() / 8;
89
90 if leading_less_than_nine == 0 {
91 password = u64::MAX;
93 return Some(current);
94 }
95
96 let trailing_nine_count = 6 - leading_less_than_nine;
98 let last_non_nine_digit = (password >> (trailing_nine_count * 8)) & 0xf;
100 let next_digit = last_non_nine_digit + 1;
102 let next_fill = 0x0101_0101_0101 * next_digit;
104
105 let replace_count = trailing_nine_count + 1;
107 let keep_mask = u64::MAX << (replace_count * 8);
109
110 password = (password & keep_mask) | (next_fill & !keep_mask);
112
113 Some(current)
114 })
115 }
116}
117
118examples!(Day04 -> (u32, u32) []);