1use std::ops::Range;
18
19use crate::util::hash::*;
20use crate::util::iter::*;
21use crate::util::parse::*;
22
23pub struct Rule<'a> {
24 range: Range<u32>,
25 category: usize,
26 next: &'a str,
27}
28
29pub struct Input<'a> {
30 workflows: FastMap<&'a str, Vec<Rule<'a>>>,
31 parts: &'a str,
32}
33
34pub fn parse(input: &str) -> Input<'_> {
38 let (prefix, suffix) = input.split_once("\n\n").unwrap();
39 let mut workflows = FastMap::with_capacity(1_000);
40
41 for line in prefix.lines() {
42 let mut rules = Vec::with_capacity(5);
43 let mut iter = line.split(['{', ':', ',', '}']);
44 let key = iter.next().unwrap();
45
46 for [first, second] in iter.chunk::<2>() {
47 let rule = if second.is_empty() {
48 Rule { range: 1..4001, category: 0, next: first }
50 } else {
51 let category = match first.as_bytes()[0] {
54 b'x' => 0,
55 b'm' => 1,
56 b'a' => 2,
57 b's' => 3,
58 _ => unreachable!(),
59 };
60
61 let value: u32 = first[2..].unsigned();
62 let next = second;
63
64 match first.as_bytes()[1] {
66 b'<' => Rule { range: 1..value, category, next },
67 b'>' => Rule { range: value + 1..4001, category, next },
68 _ => unreachable!(),
69 }
70 };
71
72 rules.push(rule);
73 }
74
75 workflows.insert(key, rules);
76 }
77
78 Input { workflows, parts: suffix }
79}
80
81pub fn part1(input: &Input<'_>) -> u32 {
82 let Input { workflows, parts } = input;
83
84 parts
86 .iter_unsigned::<u32>()
87 .chunk::<4>()
88 .filter(|part| {
89 let mut key = "in";
90
91 while key.len() > 1 {
92 key = workflows[key]
94 .iter()
95 .find(|rule| rule.range.contains(&part[rule.category]))
96 .unwrap()
97 .next;
98 }
99
100 key == "A"
101 })
102 .map(|part| part.iter().sum::<u32>())
103 .sum()
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 { range, category, next } = &workflows[key][index];
120 let category = *category;
121 let (s1, e1) = part[category];
122
123 let x1 = s1.max(range.start);
125 let x2 = e1.min(range.end);
126
127 if x1 >= x2 {
128 todo.push((key, index + 1, part));
130 } else {
131 part[category] = (x1, x2);
133 todo.push((next, 0, part));
134
135 if s1 < x1 {
137 part[category] = (s1, x1);
138 todo.push((key, index + 1, part));
139 }
140
141 if x2 < e1 {
143 part[category] = (x2, e1);
144 todo.push((key, index + 1, part));
145 }
146 }
147 }
148
149 result
150}