1use crate::util::hash::*;
18use crate::util::iter::*;
19use crate::util::parse::*;
20
21pub struct Rule<'a> {
22 start: u32,
23 end: u32,
24 category: usize,
25 next: &'a str,
26}
27
28pub struct Input<'a> {
29 workflows: FastMap<&'a str, Vec<Rule<'a>>>,
30 parts: &'a str,
31}
32
33pub fn parse(input: &str) -> Input<'_> {
37 let (prefix, suffix) = input.split_once("\n\n").unwrap();
38 let mut workflows = FastMap::with_capacity(1000);
39
40 for line in prefix.lines() {
41 let mut rules = Vec::with_capacity(5);
42 let mut iter = line.split(['{', ':', ',', '}']);
43 let key = iter.next().unwrap();
44
45 for [first, second] in iter.chunk::<2>() {
46 let rule = if second.is_empty() {
47 Rule { start: 1, end: 4001, category: 0, next: first }
49 } else {
50 let category = match first.as_bytes()[0] {
53 b'x' => 0,
54 b'm' => 1,
55 b'a' => 2,
56 b's' => 3,
57 _ => unreachable!(),
58 };
59
60 let value: u32 = (&first[2..]).unsigned();
61 let next = second;
62
63 match first.as_bytes()[1] {
65 b'<' => Rule { start: 1, end: value, category, next },
66 b'>' => Rule { start: value + 1, end: 4001, category, next },
67 _ => unreachable!(),
68 }
69 };
70
71 rules.push(rule);
72 }
73
74 workflows.insert(key, rules);
75 }
76
77 Input { workflows, parts: suffix }
78}
79
80pub fn part1(input: &Input<'_>) -> u32 {
81 let Input { workflows, parts } = input;
82 let mut result = 0;
83
84 for part in parts.iter_unsigned::<u32>().chunk::<4>() {
86 let mut key = "in";
87
88 while key.len() > 1 {
89 for &Rule { start, end, category, next } in &workflows[key] {
91 if start <= part[category] && part[category] < end {
92 key = next;
93 break;
94 }
95 }
96 }
97
98 if key == "A" {
99 result += part.iter().sum::<u32>();
100 }
101 }
102
103 result
104}
105
106pub fn part2(input: &Input<'_>) -> u64 {
107 let Input { workflows, .. } = input;
108 let mut result = 0;
109 let mut todo = vec![("in", 0, [(1, 4001); 4])];
110
111 while let Some((key, index, mut part)) = todo.pop() {
112 if key.len() == 1 {
113 if key == "A" {
114 result += part.iter().map(|(s, e)| (e - s) as u64).product::<u64>();
115 }
116 continue;
117 }
118
119 let Rule { start: s2, end: e2, category, next } = workflows[key][index];
120 let (s1, e1) = part[category];
121
122 let x1 = s1.max(s2);
124 let x2 = e1.min(e2);
125
126 if x1 >= x2 {
127 todo.push((key, index + 1, part));
129 } else {
130 part[category] = (x1, x2);
132 todo.push((next, 0, part));
133
134 if s1 < x1 {
136 part[category] = (s1, x1);
137 todo.push((key, index + 1, part));
138 }
139
140 if x2 < e1 {
142 part[category] = (x2, e1);
143 todo.push((key, index + 1, part));
144 }
145 }
146 }
147
148 result
149}