1use std::iter::repeat_with;
6
7use crate::util::hash::*;
8use crate::util::iter::*;
9use crate::util::parse::*;
10
11struct Ingredient {
12 amount: u64,
13 chemical: usize,
14}
15
16pub struct Reaction {
17 amount: u64,
18 chemical: usize,
19 ingredients: Vec<Ingredient>,
20}
21
22pub fn parse(input: &str) -> Vec<Reaction> {
25 let lines: Vec<_> = input.lines().collect();
26
27 let mut reactions: Vec<_> =
29 repeat_with(|| Reaction { amount: 0, chemical: 1, ingredients: Vec::new() })
30 .take(lines.len() + 1)
31 .collect();
32
33 let mut indices = FastMap::new();
35 indices.insert("FUEL", 0);
36 indices.insert("ORE", 1);
37
38 let mut lookup = |s| {
39 let size = indices.len();
40 *indices.entry(s).or_insert(size)
41 };
42
43 for line in lines {
44 let mut tokens = line
45 .rsplit(|c: char| !c.is_ascii_alphanumeric())
46 .filter(|s| !s.is_empty())
47 .chunk::<2>();
48
49 let [kind, amount] = tokens.next().unwrap();
51 let chemical = lookup(kind);
52
53 let reaction = &mut reactions[chemical];
54 reaction.amount = amount.unsigned();
55 reaction.chemical = chemical;
56
57 for [kind, amount] in tokens {
58 let chemical = lookup(kind);
59 reaction.ingredients.push(Ingredient { amount: amount.unsigned(), chemical });
60 }
61 }
62
63 let mut order = vec![0; reactions.len()];
65 topological(&reactions, &mut order, 0, 0);
66 reactions.sort_unstable_by_key(|r| order[r.chemical]);
67 reactions
68}
69
70pub fn part1(input: &[Reaction]) -> u64 {
73 ore(input, 1)
74}
75
76pub fn part2(input: &[Reaction]) -> u64 {
78 let threshold = 1_000_000_000_000;
79 let mut start = 1_u64;
80 let mut end = threshold;
81
82 while start != end {
83 let middle = (start + end).div_ceil(2);
84
85 if ore(input, middle) > threshold {
86 end = middle - 1;
87 } else {
88 start = middle;
89 }
90 }
91
92 start
93}
94
95fn topological(reactions: &[Reaction], order: &mut [usize], chemical: usize, depth: usize) {
98 order[chemical] = order[chemical].max(depth);
99
100 for ingredient in &reactions[chemical].ingredients {
101 topological(reactions, order, ingredient.chemical, depth + 1);
102 }
103}
104
105fn ore(reactions: &[Reaction], amount: u64) -> u64 {
108 let mut total = vec![0; reactions.len()];
109 total[0] = amount;
110
111 for reaction in &reactions[..reactions.len() - 1] {
112 let multiplier = total[reaction.chemical].div_ceil(reaction.amount);
113
114 for ingredient in &reaction.ingredients {
115 total[ingredient.chemical] += multiplier * ingredient.amount;
116 }
117 }
118
119 total[1]
120}