1use crate::util::grid::*;
15use crate::util::point::*;
16
17pub fn parse(input: &str) -> Grid<u8> {
21 Grid::parse(input)
22}
23
24pub fn part1(input: &Grid<u8>) -> usize {
26 #[cfg(not(feature = "simd"))]
27 let result = scalar::three_dimensions(input);
28
29 #[cfg(feature = "simd")]
30 let result = simd::three_dimensions(input);
31
32 result
33}
34
35pub fn part2(input: &Grid<u8>) -> usize {
37 #[cfg(not(feature = "simd"))]
38 let result = scalar::four_dimensions(input);
39
40 #[cfg(feature = "simd")]
41 let result = simd::four_dimensions(input);
42
43 result
44}
45
46#[cfg(not(feature = "simd"))]
47mod scalar {
48 use super::*;
49
50 const X: i32 = 22;
55 const Y: i32 = 22;
56 const Z: i32 = 15;
57 const W: i32 = 15;
58
59 const STRIDE_X: i32 = 1;
62 const STRIDE_Y: i32 = X * STRIDE_X;
63 const STRIDE_Z: i32 = Y * STRIDE_Y;
64 const STRIDE_W: i32 = Z * STRIDE_Z;
65
66 pub(super) fn three_dimensions(input: &Grid<u8>) -> usize {
67 let size = X * Y * Z;
68 let base = STRIDE_X + STRIDE_Y + STRIDE_Z;
69 boot_process(input, size, base, &[0])
70 }
71
72 pub(super) fn four_dimensions(input: &Grid<u8>) -> usize {
73 let size = X * Y * Z * W;
74 let base = STRIDE_X + STRIDE_Y + STRIDE_Z + STRIDE_W;
75 boot_process(input, size, base, &[-1, 0, 1])
76 }
77
78 fn boot_process(input: &Grid<u8>, size: i32, base: i32, fourth_dimension: &[i32]) -> usize {
80 let dimension = [-1, 0, 1];
81 let mut neighbors = Vec::new();
82
83 for x in dimension {
85 for y in dimension {
86 for z in dimension {
87 for w in fourth_dimension {
88 let offset = x * STRIDE_X + y * STRIDE_Y + z * STRIDE_Z + w * STRIDE_W;
89 if offset != 0 {
90 neighbors.push(offset as usize);
91 }
92 }
93 }
94 }
95 }
96
97 let mut active = Vec::with_capacity(5_000);
98 let mut candidates = Vec::with_capacity(5_000);
99 let mut next_active = Vec::with_capacity(5_000);
100
101 for x in 0..input.width {
104 for y in 0..input.height {
105 if input[Point::new(x, y)] == b'#' {
106 let index = 7 * base + x + y * STRIDE_Y;
107 active.push(index as usize);
108 }
109 }
110 }
111
112 for _ in 0..6 {
113 let mut state: Vec<u8> = vec![0; size as usize];
114
115 for &cube in &active {
116 for &offset in &neighbors {
117 let index = cube.wrapping_add(offset);
121 state[index] += 1;
122
123 if state[index] == 3 {
124 candidates.push(index);
125 }
126 }
127 }
128
129 for &cube in &active {
131 if state[cube] == 2 {
132 next_active.push(cube);
133 }
134 }
135
136 for &cube in &candidates {
138 if state[cube] == 3 {
139 next_active.push(cube);
140 }
141 }
142
143 (active, next_active) = (next_active, active);
145 candidates.clear();
146 next_active.clear();
147 }
148
149 active.len()
150 }
151}
152
153#[cfg(feature = "simd")]
154mod simd {
155 use super::*;
156 use std::simd::cmp::SimdPartialEq as _;
157 use std::simd::*;
158
159 const LANE_WIDTH: usize = 32;
160 type Vector = Simd<u8, LANE_WIDTH>;
161
162 #[expect(clippy::needless_range_loop)]
163 pub(super) fn three_dimensions(input: &Grid<u8>) -> usize {
164 let mut current = [[[0; 32]; 22]; 15];
168 let mut next = current;
169 let mut first = current;
171
172 for x in 0..input.width {
174 for y in 0..input.height {
175 if input[Point::new(x, y)] == b'#' {
176 current[7][y as usize + 7][x as usize + 7] = 1;
177 }
178 }
179 }
180
181 let zero: Vector = Simd::splat(0);
182 let one: Vector = Simd::splat(1);
183 let three: Vector = Simd::splat(3);
184
185 for round in 0..6 {
186 let edge = 5 - round;
188
189 for z in (1 + edge)..(14 - edge) {
191 for y in (1 + edge)..(21 - edge) {
192 let above = xs_sum(¤t[z][y - 1]);
193 let row = xs_sum(¤t[z][y]);
194 let below = xs_sum(¤t[z][y + 1]);
195
196 (row + above + below).copy_to_slice(&mut first[z][y]);
197 }
198 }
199
200 for z in (1 + edge)..(14 - edge) {
202 for y in (1 + edge)..(21 - edge) {
203 let above = from_slice(&first[z - 1][y]);
204 let row = from_slice(&first[z][y]);
205 let below = from_slice(&first[z + 1][y]);
206 let state = from_slice(¤t[z][y]);
207
208 let total = row + above + below - state;
209 let result = (state | total).simd_eq(three).select(one, zero);
210 result.copy_to_slice(&mut next[z][y]);
211 }
212 }
213
214 (current, next) = (next, current);
215 }
216
217 let mut result = 0;
218
219 for z in 1..14 {
220 for y in 1..21 {
221 for x in 1..21 {
222 result += current[z][y][x] as usize;
223 }
224 }
225 }
226
227 result
228 }
229
230 #[expect(clippy::needless_range_loop)]
231 pub(super) fn four_dimensions(input: &Grid<u8>) -> usize {
232 let mut current = [[[[0; 32]; 22]; 15]; 15];
234 let mut next = current;
235 let mut first = current;
237 let mut second = current;
238
239 for x in 0..input.width {
241 for y in 0..input.height {
242 if input[Point::new(x, y)] == b'#' {
243 current[7][7][y as usize + 7][x as usize + 7] = 1;
244 }
245 }
246 }
247
248 let zero: Vector = Simd::splat(0);
249 let one: Vector = Simd::splat(1);
250 let three: Vector = Simd::splat(3);
251
252 for round in 0..6 {
253 let edge = 5 - round;
255
256 for w in (1 + edge)..(14 - edge) {
258 for z in (1 + edge)..(14 - edge) {
259 for y in (1 + edge)..(21 - edge) {
260 let above = xs_sum(¤t[w][z][y - 1]);
261 let row = xs_sum(¤t[w][z][y]);
262 let below = xs_sum(¤t[w][z][y + 1]);
263
264 (row + above + below).copy_to_slice(&mut first[w][z][y]);
265 }
266 }
267 }
268
269 for w in (1 + edge)..(14 - edge) {
271 for z in (1 + edge)..(14 - edge) {
272 for y in (1 + edge)..(21 - edge) {
273 let above = from_slice(&first[w][z - 1][y]);
274 let row = from_slice(&first[w][z][y]);
275 let below = from_slice(&first[w][z + 1][y]);
276
277 (row + above + below).copy_to_slice(&mut second[w][z][y]);
278 }
279 }
280 }
281
282 for w in (1 + edge)..(14 - edge) {
284 for z in (1 + edge)..(14 - edge) {
285 for y in (1 + edge)..(21 - edge) {
286 let above = from_slice(&second[w - 1][z][y]);
287 let row = from_slice(&second[w][z][y]);
288 let below = from_slice(&second[w + 1][z][y]);
289 let state = from_slice(¤t[w][z][y]);
290
291 let total = row + above + below - state;
292 let result = (state | total).simd_eq(three).select(one, zero);
293 result.copy_to_slice(&mut next[w][z][y]);
294 }
295 }
296 }
297
298 (current, next) = (next, current);
299 }
300
301 let mut result = 0;
302
303 for w in 1..14 {
304 for z in 1..14 {
305 for y in 1..21 {
306 for x in 1..21 {
307 result += current[w][z][y][x] as usize;
308 }
309 }
310 }
311 }
312
313 result
314 }
315
316 #[inline]
317 fn from_slice(slice: &[u8]) -> Vector {
318 Simd::from_slice(slice)
319 }
320
321 #[inline]
323 fn xs_sum(slice: &[u8]) -> Vector {
324 let center = Simd::from_slice(slice);
325 let left = center.shift_elements_left::<1>(0);
326 let right = center.shift_elements_right::<1>(0);
327
328 center + left + right
329 }
330}