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<_> = repeat_with(|| {
27 Reaction {
28 amount: 0,
29 chemical: 1, ingredients: Vec::new(),
31 }
32 })
33 .take(lines.len() + 1)
34 .collect();
35
36 let mut indices = FastMap::new();
38 indices.insert("FUEL", 0);
39 indices.insert("ORE", 1);
40
41 for line in lines {
42 let mut tokens = line
43 .split(|c: char| !c.is_ascii_alphanumeric())
44 .filter(|s| !s.is_empty())
45 .rev()
46 .chunk::<2>();
47
48 let [kind, amount] = tokens.next().unwrap();
50 let size = indices.len();
51 let chemical = *indices.entry(kind).or_insert(size);
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 amount = amount.unsigned();
59 let size = indices.len();
60 let chemical = *indices.entry(kind).or_insert(size);
61 reaction.ingredients.push(Ingredient { amount, chemical });
62 }
63 }
64
65 let mut order = vec![0; reactions.len()];
67 topological(&reactions, &mut order, 0, 0);
68 reactions.sort_unstable_by_key(|r| order[r.chemical]);
69 reactions
70}
71
72pub fn part1(input: &[Reaction]) -> u64 {
75 ore(input, 1)
76}
77
78pub fn part2(input: &[Reaction]) -> u64 {
80 let threshold = 1_000_000_000_000;
81 let mut start = 1_u64;
82 let mut end = threshold;
83
84 while start != end {
85 let middle = (start + end).div_ceil(2);
86
87 if ore(input, middle) > threshold {
88 end = middle - 1;
89 } else {
90 start = middle;
91 }
92 }
93
94 start
95}
96
97fn topological(reactions: &[Reaction], order: &mut [usize], chemical: usize, depth: usize) {
100 order[chemical] = order[chemical].max(depth);
101
102 for ingredient in &reactions[chemical].ingredients {
103 topological(reactions, order, ingredient.chemical, depth + 1);
104 }
105}
106
107fn ore(reactions: &[Reaction], amount: u64) -> u64 {
110 let mut total = vec![0; reactions.len()];
111 total[0] = amount;
112
113 for reaction in &reactions[..reactions.len() - 1] {
114 let multiplier = total[reaction.chemical].div_ceil(reaction.amount);
115
116 for ingredient in &reaction.ingredients {
117 total[ingredient.chemical] += multiplier * ingredient.amount;
118 }
119 }
120
121 total[1]
122}