Skip to content

Commit 569c68f

Browse files
committed
Add day 19
1 parent 837bbaf commit 569c68f

File tree

4 files changed

+296
-2
lines changed

4 files changed

+296
-2
lines changed

benches/bench_days.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,4 @@ macro_rules! benches {
3838
};
3939
}
4040

41-
benches!(18);
41+
benches!(19);

src/day19.rs

Lines changed: 292 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,292 @@
1+
use aoc_runner_derive::aoc;
2+
3+
// Transforms b, g, r, u ,w to 0, 1, 4, 3, 5
4+
#[inline(always)]
5+
fn to_idx(i: u8) -> usize {
6+
let o: usize;
7+
unsafe {
8+
std::arch::asm!(
9+
"mov {o:e}, 19",
10+
"pext {o:e}, {i:e}, {o:e}",
11+
"add {o:r}, -2",
12+
i = in(reg) i as u32,
13+
o = out(reg) o,
14+
options(pure, nomem, nostack)
15+
);
16+
}
17+
o
18+
}
19+
20+
#[derive(Debug, Copy, Clone)]
21+
enum NfaTrans {
22+
None,
23+
Start,
24+
Next(usize),
25+
Both(usize),
26+
}
27+
28+
impl NfaTrans {
29+
fn add_start(&mut self) {
30+
match *self {
31+
NfaTrans::None => *self = NfaTrans::Start,
32+
NfaTrans::Next(n) => *self = NfaTrans::Both(n),
33+
_ => {}
34+
}
35+
}
36+
37+
fn add_or_foolow(&mut self, new_nfa_node: impl FnOnce() -> usize) -> usize {
38+
match *self {
39+
NfaTrans::None => {
40+
let new = new_nfa_node();
41+
*self = NfaTrans::Next(new);
42+
new
43+
}
44+
NfaTrans::Start => {
45+
let new = new_nfa_node();
46+
*self = NfaTrans::Both(new);
47+
new
48+
}
49+
NfaTrans::Next(n) => n,
50+
NfaTrans::Both(n) => n,
51+
}
52+
}
53+
}
54+
55+
const NFA_SIZE: usize = 1024;
56+
57+
#[aoc(day19, part1)]
58+
pub fn part1(s: &str) -> u64 {
59+
#[expect(unused_unsafe)]
60+
unsafe {
61+
inner_part1(s.as_bytes())
62+
}
63+
}
64+
65+
// #[target_feature(enable = "avx2,bmi1,bmi2,cmpxchg16b,lzcnt,movbe,popcnt")]
66+
fn inner_part1(s: &[u8]) -> u64 {
67+
let mut nfa = heapless::Vec::<[NfaTrans; 6], NFA_SIZE>::new();
68+
nfa.push([NfaTrans::None; 6]).unwrap();
69+
70+
let mut i = 0;
71+
let mut nfa_node = 0;
72+
loop {
73+
let color = to_idx(s[i]);
74+
let next = s[i + 1];
75+
76+
if next == b',' || next == b'\n' {
77+
nfa[nfa_node][color].add_start();
78+
79+
nfa_node = 0;
80+
i += 3;
81+
if next == b'\n' {
82+
break;
83+
}
84+
} else {
85+
let mut nfa_trans = nfa[nfa_node][color];
86+
let next_nfa_node = nfa_trans.add_or_foolow(|| {
87+
let new_nfa_node = nfa.len();
88+
nfa.push([NfaTrans::None; 6]).unwrap();
89+
new_nfa_node
90+
});
91+
nfa[nfa_node][color] = nfa_trans;
92+
93+
nfa_node = next_nfa_node;
94+
i += 1;
95+
}
96+
}
97+
98+
let mut sum = 0;
99+
100+
let mut states1 = &mut (true, heapless::Vec::<usize, NFA_SIZE>::new());
101+
let mut states2 = &mut (false, heapless::Vec::<usize, NFA_SIZE>::new());
102+
103+
while i < s.len() {
104+
if s[i] == b'\n' {
105+
if states1.0 {
106+
sum += 1;
107+
}
108+
states1.1.clear();
109+
states1.0 = true;
110+
i += 1;
111+
continue;
112+
}
113+
let color = to_idx(s[i]);
114+
115+
states2.1.clear();
116+
states2.0 = false;
117+
118+
if states1.0 {
119+
let next = nfa[0][color];
120+
match next {
121+
NfaTrans::None => {}
122+
NfaTrans::Start => {
123+
states2.0 = true;
124+
}
125+
NfaTrans::Next(n) => {
126+
states2.1.push(n).unwrap();
127+
}
128+
NfaTrans::Both(n) => {
129+
states2.0 = true;
130+
states2.1.push(n).unwrap();
131+
}
132+
}
133+
}
134+
for s in states1.1.iter() {
135+
let next = nfa[*s][color];
136+
match next {
137+
NfaTrans::None => {}
138+
NfaTrans::Start => {
139+
states2.0 = true;
140+
}
141+
NfaTrans::Next(n) => {
142+
states2.1.push(n).unwrap();
143+
}
144+
NfaTrans::Both(n) => {
145+
states2.0 = true;
146+
states2.1.push(n).unwrap();
147+
}
148+
}
149+
}
150+
std::mem::swap(&mut states2, &mut states1);
151+
152+
if states1.0 == false && states1.1.is_empty() {
153+
while i < s.len() && s[i] != b'\n' {
154+
i += 1;
155+
}
156+
} else {
157+
i += 1;
158+
}
159+
}
160+
sum
161+
}
162+
163+
#[aoc(day19, part2)]
164+
pub fn part2(s: &str) -> u64 {
165+
#[expect(unused_unsafe)]
166+
unsafe {
167+
inner_part2(s.as_bytes())
168+
}
169+
}
170+
171+
// #[target_feature(enable = "avx2,bmi1,bmi2,cmpxchg16b,lzcnt,movbe,popcnt")]
172+
fn inner_part2(s: &[u8]) -> u64 {
173+
let mut nfa = heapless::Vec::<[NfaTrans; 6], NFA_SIZE>::new();
174+
nfa.push([NfaTrans::None; 6]).unwrap();
175+
176+
let mut i = 0;
177+
let mut nfa_node = 0;
178+
loop {
179+
let color = to_idx(s[i]);
180+
let next = s[i + 1];
181+
182+
if next == b',' || next == b'\n' {
183+
nfa[nfa_node][color].add_start();
184+
185+
nfa_node = 0;
186+
i += 3;
187+
if next == b'\n' {
188+
break;
189+
}
190+
} else {
191+
let mut nfa_trans = nfa[nfa_node][color];
192+
let next_nfa_node = nfa_trans.add_or_foolow(|| {
193+
let new_nfa_node = nfa.len();
194+
nfa.push([NfaTrans::None; 6]).unwrap();
195+
new_nfa_node
196+
});
197+
nfa[nfa_node][color] = nfa_trans;
198+
199+
nfa_node = next_nfa_node;
200+
i += 1;
201+
}
202+
}
203+
204+
let mut sum = 0;
205+
206+
let mut states1 = &mut (1, heapless::Vec::<(usize, u64), NFA_SIZE>::new());
207+
let mut states2 = &mut (0, heapless::Vec::<(usize, u64), NFA_SIZE>::new());
208+
209+
while i < s.len() {
210+
if s[i] == b'\n' {
211+
sum += states1.0;
212+
states1.1.clear();
213+
states1.0 = 1;
214+
i += 1;
215+
continue;
216+
}
217+
let color = to_idx(s[i]);
218+
219+
states2.1.clear();
220+
states2.0 = 0;
221+
222+
if states1.0 > 0 {
223+
let next = nfa[0][color];
224+
match next {
225+
NfaTrans::None => {}
226+
NfaTrans::Start => {
227+
states2.0 += states1.0;
228+
}
229+
NfaTrans::Next(n) => {
230+
states2.1.push((n, states1.0)).unwrap();
231+
}
232+
NfaTrans::Both(n) => {
233+
states2.0 += states1.0;
234+
states2.1.push((n, states1.0)).unwrap();
235+
}
236+
}
237+
}
238+
for (s, amount) in states1.1.iter() {
239+
let next = nfa[*s][color];
240+
match next {
241+
NfaTrans::None => {}
242+
NfaTrans::Start => {
243+
states2.0 += *amount;
244+
}
245+
NfaTrans::Next(n) => {
246+
states2.1.push((n, *amount)).unwrap();
247+
}
248+
NfaTrans::Both(n) => {
249+
states2.0 += *amount;
250+
states2.1.push((n, *amount)).unwrap();
251+
}
252+
}
253+
}
254+
255+
std::mem::swap(&mut states2, &mut states1);
256+
if states1.0 == 0 && states1.1.is_empty() {
257+
while i < s.len() && s[i] != b'\n' {
258+
i += 1;
259+
}
260+
} else {
261+
i += 1;
262+
}
263+
}
264+
sum
265+
}
266+
267+
#[cfg(test)]
268+
mod tests {
269+
use super::*;
270+
271+
const EXAMPLE: &str = r"r, wr, b, g, bwu, rb, gb, br
272+
273+
brwrr
274+
bggr
275+
gbbr
276+
rrbgbr
277+
ubwu
278+
bwurrg
279+
brgr
280+
bbrgwb
281+
";
282+
283+
#[test]
284+
fn example_part1() {
285+
assert_eq!(part1(EXAMPLE), 6);
286+
}
287+
288+
#[test]
289+
fn example_part2() {
290+
assert_eq!(part2(EXAMPLE), 16);
291+
}
292+
}

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ pub mod day15;
1212
pub mod day16;
1313
pub mod day17;
1414
pub mod day18;
15+
pub mod day19;
1516
pub mod day2;
1617
pub mod day3;
1718
pub mod day4;

tests/test_days.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,5 +50,6 @@ benches!(
5050
15 => ("1398947", "1397393"),
5151
16 => ("106512", "563"),
5252
17 => ("3,6,3,7,0,7,0,3,0", "136904920099226"),
53-
18 => ("338", "20,44"),
53+
// 18 => ("338", "20,44"),
54+
19 => ("267", "796449099271652"),
5455
);

0 commit comments

Comments
 (0)