1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
//! # Day 1: Report Repair
//!
//! After saving Christmas [five years in a row], you've decided to take a vacation at a nice resort
//! on a tropical island. Surely, Christmas will go on without you.
//!
//! The tropical island has its own currency and is entirely cash-only. The gold coins used there
//! have a little picture of a starfish; the locals just call them **stars**. None of the currency
//! exchanges seem to have heard of them, but somehow, you'll need to find fifty of these coins by
//! the time you arrive so you can pay the deposit on your room.
//!
//! To save your vacation, you need to get all **fifty stars** by December 25th.
//!
//! Collect stars by solving puzzles. Two puzzles will be made available on each day in the Advent
//! calendar; the second puzzle is unlocked when you complete the first. Each puzzle grants **one
//! star**. Good luck!
//!
//! Before you leave, the Elves in accounting just need you to fix your **expense report** (your
//! puzzle input); apparently, something isn't quite adding up.
//!
//! Specifically, they need you to **find the two entries that sum to `2020`** and then multiply
//! those two numbers together.
//!
//! For example, suppose your expense report contained the following:
//!
//! ```txt
//! 1721
//! 979
//! 366
//! 299
//! 675
//! 1456
//! ```
//!
//! In this list, the two entries that sum to `2020` are `1721` and `299`. Multiplying them together
//! produces `1721 * 299 = 514579`, so the correct answer is **`514579`**.
//!
//! Of course, your expense report is much larger. **Find the two entries that sum to `2020`; what
//! do you get if you multiply them together?**
//!
//! [five years in a row]: https://adventofcode.com/events
//!
//! ## Part Two
//!
//! The Elves in accounting are thankful for your help; one of them even offers you a starfish coin
//! they had left over from a past vacation. They offer you a second one if you can find **three**
//! numbers in your expense report that meet the same criteria.
//!
//! Using the above example again, the three entries that sum to `2020` are `979`, `366`, and `675`.
//! Multiplying them together produces the answer, **`241861950`**.
//!
//! In your expense report, **what is the product of the three entries that sum to `2020`?**

use std::iter;

use anyhow::{Context, Result};

pub const INPUT: &str = include_str!("d01.txt");

pub fn solve_part_one(input: &str) -> Result<u32> {
    let bills = parse_input(input)?;

    bills
        .iter()
        .flat_map(|a| iter::repeat(a).zip(&bills))
        .find(|(a, b)| *a + *b == 2020)
        .map(|(a, b)| a * b)
        .context("no combination sums up to 2020")
}

pub fn solve_part_two(input: &str) -> Result<u32> {
    let bills = parse_input(input)?;

    bills
        .iter()
        .flat_map(|a| iter::repeat(a).zip(&bills))
        .flat_map(|a| iter::repeat(a).zip(&bills))
        .find(|((a, b), c)| *a + *b + *c == 2020)
        .map(|((a, b), c)| a * b * c)
        .context("no combination sums up to 2020")
}

fn parse_input(input: &str) -> Result<Vec<u32>> {
    input.lines().map(|i| i.parse().map_err(Into::into)).collect()
}

#[cfg(test)]
mod tests {
    use indoc::indoc;

    use super::*;

    const INPUT: &str = indoc! {"
        1721
        979
        366
        299
        675
        1456
    "};

    #[test]
    fn part_one() {
        assert_eq!(514579, solve_part_one(INPUT).unwrap());
    }

    #[test]
    fn part_two() {
        assert_eq!(241861950, solve_part_two(INPUT).unwrap());
    }
}