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::build([("FUEL", 0), ("ORE", 1)]);
35
36 let mut lookup = |s| {
37 let size = indices.len();
38 *indices.entry(s).or_insert(size)
39 };
40
41 for line in lines {
42 let mut tokens = line
43 .rsplit(|c: char| !c.is_ascii_alphanumeric())
44 .filter(|s| !s.is_empty())
45 .chunk::<2>();
46
47 let [kind, amount] = tokens.next().unwrap();
49 let chemical = lookup(kind);
50
51 let reaction = &mut reactions[chemical];
52 reaction.amount = amount.unsigned();
53 reaction.chemical = chemical;
54
55 for [kind, amount] in tokens {
56 let chemical = lookup(kind);
57 reaction.ingredients.push(Ingredient { amount: amount.unsigned(), chemical });
58 }
59 }
60
61 let mut order = vec![0; reactions.len()];
63 topological(&reactions, &mut order, 0, 0);
64 reactions.sort_unstable_by_key(|r| order[r.chemical]);
65 reactions
66}
67
68pub fn part1(input: &[Reaction]) -> u64 {
71 ore(input, 1)
72}
73
74pub fn part2(input: &[Reaction]) -> u64 {
76 let threshold = 1_000_000_000_000;
77 let mut start = 1_u64;
78 let mut end = threshold;
79
80 while start != end {
81 let middle = (start + end).div_ceil(2);
82
83 if ore(input, middle) > threshold {
84 end = middle - 1;
85 } else {
86 start = middle;
87 }
88 }
89
90 start
91}
92
93fn topological(reactions: &[Reaction], order: &mut [usize], chemical: usize, depth: usize) {
96 order[chemical] = order[chemical].max(depth);
97
98 for ingredient in &reactions[chemical].ingredients {
99 topological(reactions, order, ingredient.chemical, depth + 1);
100 }
101}
102
103fn ore(reactions: &[Reaction], amount: u64) -> u64 {
106 let mut total = vec![0; reactions.len()];
107 total[0] = amount;
108
109 for reaction in &reactions[..reactions.len() - 1] {
110 let multiplier = total[reaction.chemical].div_ceil(reaction.amount);
111
112 for ingredient in &reaction.ingredients {
113 total[ingredient.chemical] += multiplier * ingredient.amount;
114 }
115 }
116
117 total[1]
118}