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