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 .split(|c: char| !c.is_ascii_alphanumeric())
46 .filter(|s| !s.is_empty())
47 .rev()
48 .chunk::<2>();
49
50 let [kind, amount] = tokens.next().unwrap();
52 let chemical = lookup(kind);
53
54 let reaction = &mut reactions[chemical];
55 reaction.amount = amount.unsigned();
56 reaction.chemical = chemical;
57
58 for [kind, amount] in tokens {
59 let chemical = lookup(kind);
60 reaction.ingredients.push(Ingredient { amount: amount.unsigned(), chemical });
61 }
62 }
63
64 let mut order = vec![0; reactions.len()];
66 topological(&reactions, &mut order, 0, 0);
67 reactions.sort_unstable_by_key(|r| order[r.chemical]);
68 reactions
69}
70
71pub fn part1(input: &[Reaction]) -> u64 {
74 ore(input, 1)
75}
76
77pub fn part2(input: &[Reaction]) -> u64 {
79 let threshold = 1_000_000_000_000;
80 let mut start = 1_u64;
81 let mut end = threshold;
82
83 while start != end {
84 let middle = (start + end).div_ceil(2);
85
86 if ore(input, middle) > threshold {
87 end = middle - 1;
88 } else {
89 start = middle;
90 }
91 }
92
93 start
94}
95
96fn topological(reactions: &[Reaction], order: &mut [usize], chemical: usize, depth: usize) {
99 order[chemical] = order[chemical].max(depth);
100
101 for ingredient in &reactions[chemical].ingredients {
102 topological(reactions, order, ingredient.chemical, depth + 1);
103 }
104}
105
106fn ore(reactions: &[Reaction], amount: u64) -> u64 {
109 let mut total = vec![0; reactions.len()];
110 total[0] = amount;
111
112 for reaction in &reactions[..reactions.len() - 1] {
113 let multiplier = total[reaction.chemical].div_ceil(reaction.amount);
114
115 for ingredient in &reaction.ingredients {
116 total[ingredient.chemical] += multiplier * ingredient.amount;
117 }
118 }
119
120 total[1]
121}