1use self::implementation::*;
18use crate::util::grid::*;
19use crate::util::point::*;
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;
51 let offset = Point::new(extra, extra);
52 let mut pixels = Grid::new(grid.width + 2 * extra, grid.height + 2 * extra, 0);
53
54 for y in 0..grid.height {
55 for x in 0..grid.width {
56 let point = Point::new(x, y);
57 pixels[point + offset] = u8::from(grid[point] == b'#');
58 }
59 }
60
61 let mut next = pixels.clone();
62 let mut default = 0;
63
64 for step in 0..steps {
65 let start = extra - step;
67 let end = extra + grid.width + step;
68
69 for y in (start - 1)..(end + 1) {
70 let helper = |sx, sy, shift| {
73 let result = if sx < end && start <= sy && sy < end {
74 pixels[Point::new(sx, sy)]
75 } else {
76 default
77 };
78 (result as usize) << shift
79 };
80
81 let mut index = if default == 1 { 0b11011011 } else { 0b00000000 };
87
88 for x in (start - 1)..(end + 1) {
89 index = ((index << 1) & 0b110110110)
93 + helper(x + 1, y - 1, 6)
94 + helper(x + 1, y, 3)
95 + helper(x + 1, y + 1, 0);
96
97 next[Point::new(x, y)] = algorithm[index];
98 }
99 }
100
101 (pixels, next) = (next, pixels);
103 default = if default == 0 { algorithm[0] } else { algorithm[511] };
104 }
105
106 pixels.bytes.iter().map(|&b| b as u32).sum()
107 }
108}
109
110#[cfg(feature = "simd")]
111mod implementation {
112 use std::simd::prelude::*;
113
114 use super::*;
115
116 const LANE_WIDTH: usize = 16;
117 type Vector = Simd<u16, LANE_WIDTH>;
118
119 pub(super) fn enhance(input: &Input, steps: i32) -> u32 {
120 let (algorithm, grid) = input;
121
122 let extra = steps + 1;
126 let offset = Point::new(extra, extra);
127 let mut pixels =
128 Grid::new(grid.width + 2 * extra + LANE_WIDTH as i32, grid.height + 2 * extra, 0);
129
130 for y in 0..grid.height {
131 for x in 0..grid.width {
132 let point = Point::new(x, y);
133 pixels[point + offset] = u8::from(grid[point] == b'#');
134 }
135 }
136
137 let mut next = pixels.clone();
138 let mut default = 0;
139
140 for step in 0..steps {
141 let start = extra - 1 - step;
143 let end = extra + grid.width + 1 + step;
144
145 for y in (start - 1)..(end + 1) {
147 pixels[Point::new(start - 1, y)] = default;
148 pixels[Point::new(start, y)] = default;
149 pixels[Point::new(end - 1, y)] = default;
150 pixels[Point::new(end, y)] = default;
151 }
152
153 for x in (start..end).step_by(LANE_WIDTH) {
154 let edge = Simd::splat(if default == 0 { 0b000 } else { 0b111 });
155 let mut above = edge;
156 let mut row = edge;
157
158 for y in start..end {
159 let below = if y < end - 2 { from_grid(&pixels, x, y + 1) } else { edge };
160
161 let indices = (above << 6) | (row << 3) | below;
162 above = row;
163 row = below;
164
165 let base = (pixels.width * y + x) as usize;
166 for (i, j) in indices.to_array().into_iter().enumerate() {
167 next.bytes[base + i] = algorithm[j as usize];
168 }
169 }
170 }
171
172 (pixels, next) = (next, pixels);
174 default = if default == 0 { algorithm[0] } else { algorithm[511] };
175 }
176
177 let end = extra + grid.width + 1 + steps;
179 let mut result = 0;
180
181 for y in 1..end - 1 {
182 for x in 1..end - 1 {
183 result += pixels[Point::new(x, y)] as u32;
184 }
185 }
186
187 result
188 }
189
190 #[inline]
191 fn from_grid(grid: &Grid<u8>, x: i32, y: i32) -> Vector {
192 let index = (grid.width * y + x) as usize;
193
194 let row = Simd::from_slice(&grid.bytes[index..]);
195 let left = row.shift_elements_right::<1>(grid[Point::new(x - 1, y)]);
196 let right = row.shift_elements_left::<1>(grid[Point::new(x + LANE_WIDTH as i32, y)]);
197
198 let result = (left << 2) | (row << 1) | right;
199 result.cast()
200 }
201}