#include #include #include #include #include #include #include #include #include #include #include namespace { auto Eval( std::string const& input, std::map>> cfg ) -> std::int64_t { std::vector ops; std::vector vals; auto const eval1 = [&](){ if (ops.size() < 1 || vals.size() < 2) { throw std::runtime_error{"parse error"}; } if (auto opit = cfg.find(ops.back()); opit != cfg.end()) { std::int64_t y = vals.back(); vals.pop_back(); std::int64_t x = vals.back(); vals.pop_back(); vals.push_back(opit->second.second(x,y)); ops.pop_back(); } else { throw std::runtime_error{"parse error"}; } }; bool expect_operator = false; auto it = input.begin(); while (it != input.end()) { char const c = *it++; // skip whitespace if (c == ' ') continue; // number literal if (std::isdigit(c)) { if (expect_operator) { throw std::runtime_error{"unexpected literal"}; } expect_operator = true; std::int64_t acc = c - '0'; while (it != input.end() && std::isdigit(*it)) { acc = acc*10 + (*it++ - '0'); } vals.push_back(acc); continue; } // begin subexpression if ('(' == c) { if (expect_operator) { throw std::runtime_error{"unexpected '('"}; } ops.push_back('('); continue; } // end subexpression if (')' == c) { if (!expect_operator) { throw std::runtime_error{"unexpected ')'"}; } while (!ops.empty() && ops.back() != '(') eval1(); if (ops.empty()) throw std::runtime_error{"parse error"}; ops.pop_back(); // '(' continue; } // binary operator if (auto opit = cfg.find(c); opit != cfg.end()) { if (!expect_operator) { throw std::runtime_error{"unexpected operator"}; } expect_operator = false; while (!ops.empty() && cfg[ops.back()].first >= opit->second.first) eval1(); ops.push_back(c); continue; } throw std::runtime_error{"unknown character"}; } while (!ops.empty()) eval1(); if (vals.empty()) { throw std::runtime_error{"empty input"}; } return vals.back(); } auto Part1(std::string const& line) { return Eval(line, {{'+', {1, std::plus()}},{'*', {1, std::multiplies()}}}); } auto Part2(std::string const& line) { return Eval(line, {{'+', {2, std::plus()}},{'*', {1, std::multiplies()}}}); } } // namespace TEST_SUITE("documented examples") { TEST_CASE("part 1") { CHECK(Part1("1 + 2 * 3 + 4 * 5 + 6") == 71); CHECK(Part1("1 + (2 * 3) + (4 * (5 + 6))") == 51); CHECK(Part1("2 * 3 + (4 * 5)") == 26); CHECK(Part1("5 + (8 * 3 + 9 + 3 * 4 * 3)") == 437); CHECK(Part1("5 * 9 * (7 * 3 * 3 + 9 * 3 + (8 + 6 * 4))") == 12240); CHECK(Part1("((2 + 4 * 9) * (6 + 9 * 8 + 6) + 6) + 2 + 4 * 2") == 13632); } TEST_CASE("part 2") { CHECK(Part2("1 + 2 * 3 + 4 * 5 + 6") == 231); CHECK(Part2("1 + (2 * 3) + (4 * (5 + 6))") == 51); CHECK(Part2("2 * 3 + (4 * 5)") == 46); CHECK(Part2("5 + (8 * 3 + 9 + 3 * 4 * 3)") == 1445); CHECK(Part2("5 * 9 * (7 * 3 * 3 + 9 * 3 + (8 + 6 * 4))") == 669060); CHECK(Part2("((2 + 4 * 9) * (6 + 9 * 8 + 6) + 6) + 2 + 4 * 2") == 23340); } TEST_CASE("errors") { CHECK_THROWS_AS(Part1(""), std::runtime_error); CHECK_THROWS_AS(Part1("1 + ()"), std::runtime_error); CHECK_THROWS_AS(Part1("(1"), std::runtime_error); CHECK_THROWS_AS(Part1("1 (2)"), std::runtime_error); CHECK_THROWS_AS(Part1(")"), std::runtime_error); CHECK_THROWS_AS(Part1("1)"), std::runtime_error); CHECK_THROWS_AS(Part1("1 2"), std::runtime_error); CHECK_THROWS_AS(Part1("1 +"), std::runtime_error); CHECK_THROWS_AS(Part1("+ 1"), std::runtime_error); CHECK_THROWS_AS(Part1("1 /"), std::runtime_error); CHECK_THROWS_AS(Part1("1 / 2"), std::runtime_error); CHECK_THROWS_AS(Part1("1 + * 2"), std::runtime_error); } } auto main(int argc, char** argv) -> int { auto const in_ptr = aocpp::Startup(argc, argv); auto & in = *in_ptr; std::int64_t part1 = 0; std::int64_t part2 = 0; std::string line; while (std::getline(in, line)) { part1 += Part1(line); part2 += Part2(line); } std::cout << "Part 1: " << part1 << std::endl; std::cout << "Part 2: " << part2 << std::endl; }