1use crate::util::grid::*;
18use crate::util::point::*;
19use implementation::*;
20
21type Input = (Vec<u8>, Grid<u8>);
22
23pub fn parse(input: &str) -> Input {
24 let (prefix, suffix) = input.split_once("\n\n").unwrap();
25
26 let algorithm = prefix.bytes().map(|b| u8::from(b == b'#')).collect();
27 let grid = Grid::parse(suffix);
28
29 (algorithm, grid)
30}
31
32pub fn part1(input: &Input) -> u32 {
33 enhance(input, 2)
34}
35
36pub fn part2(input: &Input) -> u32 {
37 enhance(input, 50)
38}
39
40#[cfg(not(feature = "simd"))]
41mod implementation {
42 use super::*;
43
44 pub(super) fn enhance(input: &Input, steps: i32) -> u32 {
45 let (algorithm, grid) = input;
46
47 let extra = steps + 1;
50 let offset = Point::new(extra, extra);
51 let mut pixels = Grid::new(grid.width + 2 * extra, grid.height + 2 * extra, 0);
52
53 for y in 0..grid.height {
54 for x in 0..grid.width {
55 let point = Point::new(x, y);
56 pixels[point + offset] = u8::from(grid[point] == b'#');
57 }
58 }
59
60 let mut next = pixels.clone();
61 let mut default = 0;
62
63 for step in 0..steps {
64 let start = extra - step;
66 let end = extra + grid.width + step;
67
68 for y in (start - 1)..(end + 1) {
69 let helper = |sx, sy, shift| {
72 let result = if sx < end && start <= sy && sy < end {
73 pixels[Point::new(sx, sy)]
74 } else {
75 default
76 };
77 (result as usize) << shift
78 };
79
80 let mut index = if default == 1 { 0b11011011 } else { 0b00000000 };
86
87 for x in (start - 1)..(end + 1) {
88 index = ((index << 1) & 0b110110110)
92 + helper(x + 1, y - 1, 6)
93 + helper(x + 1, y, 3)
94 + helper(x + 1, y + 1, 0);
95
96 next[Point::new(x, y)] = algorithm[index];
97 }
98 }
99
100 (pixels, next) = (next, pixels);
102 default = if default == 0 { algorithm[0] } else { algorithm[511] };
103 }
104
105 pixels.bytes.iter().map(|&b| b as u32).sum()
106 }
107}
108
109#[cfg(feature = "simd")]
110mod implementation {
111 use super::*;
112 use std::simd::Simd;
113 use std::simd::num::SimdUint as _;
114
115 const LANE_WIDTH: usize = 16;
116 type Vector = Simd<u16, LANE_WIDTH>;
117
118 pub(super) fn enhance(input: &Input, steps: i32) -> u32 {
119 let (algorithm, grid) = input;
120
121 let extra = steps + 1;
124 let offset = Point::new(extra, extra);
125 let mut pixels =
126 Grid::new(grid.width + 2 * extra + LANE_WIDTH as i32, grid.height + 2 * extra, 0);
127
128 for y in 0..grid.height {
129 for x in 0..grid.width {
130 let point = Point::new(x, y);
131 pixels[point + offset] = u8::from(grid[point] == b'#');
132 }
133 }
134
135 let mut next = pixels.clone();
136 let mut default = 0;
137
138 for step in 0..steps {
139 let start = extra - 1 - step;
141 let end = extra + grid.width + 1 + step;
142
143 for y in (start - 1)..(end + 1) {
145 pixels[Point::new(start - 1, y)] = default;
146 pixels[Point::new(start, y)] = default;
147 pixels[Point::new(end - 1, y)] = default;
148 pixels[Point::new(end, y)] = default;
149 }
150
151 for x in (start..end).step_by(LANE_WIDTH) {
152 let edge = Simd::splat(if default == 0 { 0b000 } else { 0b111 });
153 let mut above = edge;
154 let mut row = edge;
155
156 for y in start..end {
157 let below = if y < end - 2 { from_grid(&pixels, x, y + 1) } else { edge };
158
159 let indices = (above << 6) | (row << 3) | below;
160 above = row;
161 row = below;
162
163 let base = (pixels.width * y + x) as usize;
164 for (i, j) in indices.to_array().into_iter().enumerate() {
165 next.bytes[base + i] = algorithm[j as usize];
166 }
167 }
168 }
169
170 (pixels, next) = (next, pixels);
172 default = if default == 0 { algorithm[0] } else { algorithm[511] };
173 }
174
175 let end = extra + grid.width + 1 + steps;
177 let mut result = 0;
178
179 for y in 1..end - 1 {
180 for x in 1..end - 1 {
181 result += pixels[Point::new(x, y)] as u32;
182 }
183 }
184
185 result
186 }
187
188 #[inline]
189 fn from_grid(grid: &Grid<u8>, x: i32, y: i32) -> Vector {
190 let index = (grid.width * y + x) as usize;
191
192 let row = Simd::from_slice(&grid.bytes[index..]);
193 let left = row.shift_elements_right::<1>(grid[Point::new(x - 1, y)]);
194 let right = row.shift_elements_left::<1>(grid[Point::new(x + LANE_WIDTH as i32, y)]);
195
196 let result = (left << 2) | (row << 1) | right;
197 result.cast()
198 }
199}