1use crate::util::hash::*;
6use crate::util::iter::*;
7use crate::util::parse::*;
8use std::iter::repeat_with;
9
10struct Ingredient {
11 amount: u64,
12 chemical: usize,
13}
14
15pub struct Reaction {
16 amount: u64,
17 chemical: usize,
18 ingredients: Vec<Ingredient>,
19}
20
21pub fn parse(input: &str) -> Vec<Reaction> {
24 let lines: Vec<_> = input.lines().collect();
25
26 let mut reactions: Vec<_> =
28 repeat_with(|| Reaction { amount: 0, chemical: 1, ingredients: Vec::new() })
29 .take(lines.len() + 1)
30 .collect();
31
32 let mut indices = FastMap::new();
34 indices.insert("FUEL", 0);
35 indices.insert("ORE", 1);
36
37 let mut lookup = |s| {
38 let size = indices.len();
39 *indices.entry(s).or_insert(size)
40 };
41
42 for line in lines {
43 let mut tokens = line
44 .split(|c: char| !c.is_ascii_alphanumeric())
45 .filter(|s| !s.is_empty())
46 .rev()
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}