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