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 iter = line.split(['{', ':', ',', '}']);
43 let key = iter.next().unwrap();
44
45 let rules = iter
46 .chunk::<2>()
47 .map(|[first, second]| {
48 if second.is_empty() {
49 return Rule { range: 1..4001, category: 0, next: first };
51 }
52
53 let category = match first.as_bytes()[0] {
56 b'x' => 0,
57 b'm' => 1,
58 b'a' => 2,
59 b's' => 3,
60 _ => unreachable!(),
61 };
62
63 let value = first[2..].unsigned();
65 let range = match first.as_bytes()[1] {
66 b'<' => 1..value,
67 b'>' => value + 1..4001,
68 _ => unreachable!(),
69 };
70
71 Rule { range, category, next: second }
72 })
73 .collect();
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()
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 .flatten()
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}