1use utils::bit::BitIterator;
2use utils::prelude::*;
3
4#[derive(Clone, Debug)]
8pub struct Day23 {
9 nodes: [[u64; (26usize * 26).div_ceil(64)]; 26 * 26],
10 degree: u32,
11}
12
13impl Day23 {
14 pub fn new(input: &str, _: InputType) -> Result<Self, InputError> {
15 let mut nodes = [[0u64; 11]; 676];
16
17 for item in parser::byte_range(b'a'..=b'z')
18 .repeat_n::<2, _>(parser::noop())
19 .repeat_n::<2, _>(b'-')
20 .with_suffix(parser::eol())
21 .parse_iterator(input)
22 {
23 let [n1, n2] = item?;
24
25 let index1 = 26 * (n1[0] - b'a') as usize + (n1[1] - b'a') as usize;
26 let index2 = 26 * (n2[0] - b'a') as usize + (n2[1] - b'a') as usize;
27
28 nodes[index1][index2 / 64] |= 1 << (index2 % 64);
29 nodes[index2][index1 / 64] |= 1 << (index1 % 64);
30 }
31
32 let Some(first_node) = nodes.iter().find(|&b| b.iter().any(|&n| n != 0)) else {
33 return Err(InputError::new(input, 0, "expected non-empty graph"));
34 };
35
36 let degree = first_node.iter().map(|&n| n.count_ones()).sum::<u32>();
37 if nodes.iter().any(|&b| {
38 let d = b.iter().map(|&n| n.count_ones()).sum::<u32>();
39 d != 0 && d != degree
40 }) {
41 return Err(InputError::new(
42 input,
43 0,
44 "expected all nodes to have same degree",
45 ));
46 }
47
48 Ok(Self { nodes, degree })
49 }
50
51 #[must_use]
52 pub fn part1(&self) -> u32 {
53 let mut count = 0;
54
55 for n1 in 19 * 26..20 * 26 {
57 for n2 in self.neighbors(n1) {
58 if n2 / 26 == 19 && n2 >= n1 {
60 continue;
61 }
62
63 for n3 in Self::iter(Self::intersect(self.nodes[n1], self.nodes[n2])) {
64 if n3 >= n2 {
65 break;
66 }
67 if n3 / 26 == 19 && n3 >= n1 {
68 continue;
69 }
70
71 count += 1;
72 }
73 }
74 }
75
76 count
77 }
78
79 #[must_use]
80 pub fn part2(&self) -> String {
81 for i in 0..self.nodes.len() {
82 'sets: for skip in self.neighbors(i) {
83 if skip > i {
84 break;
85 }
86
87 let mut connected = self.nodes[i];
89 connected[i / 64] |= 1 << (i % 64);
90 connected[skip / 64] &= !(1 << (skip % 64));
91
92 for n in self.neighbors(i).filter(|&n| n != skip) {
93 connected = Self::intersect(connected, self.nodes[n]);
94 connected[n / 64] |= 1 << (n % 64);
95
96 if connected.iter().map(|&n| n.count_ones()).sum::<u32>() != self.degree {
97 continue 'sets;
98 }
99 }
100
101 return Self::iter(connected).fold(String::new(), |mut acc, i| {
102 if !acc.is_empty() {
103 acc.push(',');
104 }
105 let name = [b'a' + (i / 26) as u8, b'a' + (i % 26) as u8];
106 acc.push(name[0] as char);
107 acc.push(name[1] as char);
108 acc
109 });
110 }
111 }
112
113 panic!("no solution found")
114 }
115
116 #[inline]
117 fn neighbors(&self, n: usize) -> impl Iterator<Item = usize> {
118 Self::iter(self.nodes[n])
119 }
120
121 #[inline]
122 fn iter(bitset: [u64; 11]) -> impl Iterator<Item = usize> {
123 bitset.into_iter().enumerate().flat_map(|(element, b)| {
124 BitIterator::ones(b).map(move |(bit, _)| element * 64 + bit as usize)
125 })
126 }
127
128 #[inline]
129 fn intersect(mut bitset1: [u64; 11], bitset2: [u64; 11]) -> [u64; 11] {
130 for (a, &b) in bitset1.iter_mut().zip(bitset2.iter()) {
131 *a &= b;
132 }
133 bitset1
134 }
135}
136
137examples!(Day23 -> (u32, &'static str) [
138 {file: "day23_example0.txt", part1: 7, part2: "co,de,ka,ta"},
139]);