1use crate::util::hash::*;
6use crate::util::iter::*;
7use crate::util::parse::*;
8use std::cmp::Ordering;
9use std::iter::repeat_with;
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<_> = repeat_with(|| {
28 Reaction {
29 amount: 0,
30 chemical: 1, ingredients: Vec::new(),
32 }
33 })
34 .take(lines.len() + 1)
35 .collect();
36
37 let mut indices = FastMap::new();
39 indices.insert("FUEL", 0);
40 indices.insert("ORE", 1);
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 size = indices.len();
52 let chemical = *indices.entry(kind).or_insert(size);
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 amount = amount.unsigned();
60 let size = indices.len();
61 let chemical = *indices.entry(kind).or_insert(size);
62 reaction.ingredients.push(Ingredient { amount, chemical });
63 }
64 }
65
66 let mut order = vec![0; reactions.len()];
68 topological(&reactions, &mut order, 0, 0);
69 reactions.sort_unstable_by_key(|r| order[r.chemical]);
70 reactions
71}
72
73pub fn part1(input: &[Reaction]) -> u64 {
76 ore(input, 1)
77}
78
79pub fn part2(input: &[Reaction]) -> u64 {
81 let threshold = 1_000_000_000_000;
82 let mut start = 1_u64;
83 let mut end = threshold;
84
85 while start != end {
86 let middle = (start + end).div_ceil(2);
87
88 match ore(input, middle).cmp(&threshold) {
89 Ordering::Greater => end = middle - 1,
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}