diff --git a/2023/.clang-format b/2023/.clang-format new file mode 100644 index 0000000..1e2f8b3 --- /dev/null +++ b/2023/.clang-format @@ -0,0 +1,35 @@ +BasedOnStyle: Microsoft +AccessModifierOffset: -2 +AlignAfterOpenBracket: DontAlign +AllowAllParametersOfDeclarationOnNextLine: false +AlignConsecutiveMacros: AcrossEmptyLinesAndComments +AlignConsecutiveAssignments: None +AlignOperands: Align +AlignArrayOfStructures: Left +AlignTrailingComments: true +AllowShortBlocksOnASingleLine: Empty +AllowAllConstructorInitializersOnNextLine: true +BinPackArguments: true +BinPackParameters: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Attach +BreakConstructorInitializers: AfterColon +ColumnLimit: 100 +ContinuationIndentWidth: 2 +ConstructorInitializerAllOnOneLineOrOnePerLine: false +Cpp11BracedListStyle: true +PointerAlignment: Left +ExperimentalAutoDetectBinPacking: true +FixNamespaceComments: false +IndentWrappedFunctionNames: true +NamespaceIndentation: None +SortIncludes: false +SpaceBeforeCpp11BracedList: false +SpaceBeforeParens: ControlStatements +SpaceInEmptyBlock: true +StatementAttributeLikeMacros: [] +TabWidth: 2 +UseCRLF: false +UseTab: Never +AlwaysBreakTemplateDeclarations: Yes +IndentWidth: 2 diff --git a/2023/cmd/app.cpp b/2023/cmd/app.cpp index 587e997..0ae7999 100644 --- a/2023/cmd/app.cpp +++ b/2023/cmd/app.cpp @@ -11,8 +11,7 @@ namespace app { -std::string replace(std::string_view in, char target, - std::string_view replacement) { +std::string replace(std::string_view in, char target, std::string_view replacement) { std::string out; out.reserve(in.size()); @@ -34,27 +33,25 @@ std::string app::usage() { for (auto cmd : commands_flat) { s << '\n'; - for (auto &alias : cmd.flags) { + for (auto& alias : cmd.flags) { s << std::format("aoc2023 {}\n", alias); } constexpr auto prefix = " "; - std::string help = - prefix + replace(cmd.help, '\n', std::format("\n{}", prefix)); + std::string help = prefix + replace(cmd.help, '\n', std::format("\n{}", prefix)); s << help << '\n'; } return s.str(); } -void app::register_command(command const &cmd) { +void app::register_command(command const& cmd) { this->commands_flat.push_back(cmd); for (auto alias : cmd.flags) { auto it = this->commands.find(alias); if (it != this->commands.end()) { - throw std::runtime_error( - std::format("Flag {} is already defined", alias)); + throw std::runtime_error(std::format("Flag {} is already defined", alias)); } this->commands[alias] = cmd; @@ -82,8 +79,7 @@ int app::run(argv args) { auto argv_begin = it + 1; auto argv_end = - std::find_if(argv_begin, args.end(), - [](std::string_view s) { return s.starts_with("-"); }); + std::find_if(argv_begin, args.end(), [](std::string_view s) { return s.starts_with("-"); }); xlog::debug("Found command {} with {} arguments", c, argv_end - argv_begin); cmd.emplace_back(icmd->second, std::span{argv_begin, argv_end}); @@ -92,7 +88,7 @@ int app::run(argv args) { } // Execute commands - for (auto &command : cmd) { + for (auto& command : cmd) { auto r = command.c.run(*this, command.args); switch (r) { case exit_success: diff --git a/2023/cmd/app.hpp b/2023/cmd/app.hpp index 588b8fb..e017e5e 100644 --- a/2023/cmd/app.hpp +++ b/2023/cmd/app.hpp @@ -19,14 +19,12 @@ using argv = std::span; struct command { std::vector flags; std::string_view help; - std::function run = [](app &, argv) { - return exit_success; - }; + std::function run = [](app&, argv) { return exit_success; }; }; class app { public: - void register_command(command const &); + void register_command(command const&); std::string usage(); int run(argv args); diff --git a/2023/cmd/cmd.cpp b/2023/cmd/cmd.cpp index 0a48ef8..0a60b85 100644 --- a/2023/cmd/cmd.cpp +++ b/2023/cmd/cmd.cpp @@ -12,18 +12,18 @@ namespace app { -std::optional -solve_day(std::map>::value_type const - &solution, - bool verbose); +std::optional solve_day( + std::map>::value_type const& + solution, + bool verbose); solution_vector select_all_days() { try { populate_registry(); - } catch (std::runtime_error &e) { + } catch (std::runtime_error& e) { xlog::error("could not populate the registry fully: {}", e.what()); } - const auto &solutions = xmas::registered_solutions(); + const auto& solutions = xmas::registered_solutions(); solution_vector days; @@ -37,15 +37,15 @@ solution_vector select_all_days() { solution_vector select_days(argv days) { try { populate_registry(); - } catch (std::runtime_error &e) { + } catch (std::runtime_error& e) { xlog::error("could not populate the registry fully: {}", e.what()); } - const auto &solutions = xmas::registered_solutions(); + const auto& solutions = xmas::registered_solutions(); solution_vector out; out.reserve(days.size()); - for (auto &day : days) { + for (auto& day : days) { int d = -1; std::from_chars(day.begin(), day.end(), d); if (d == -1) { @@ -65,7 +65,7 @@ solution_vector select_days(argv days) { return out; } -bool execute_days(app &, solution_vector const &days) { +bool execute_days(app&, solution_vector const& days) { xmas::solution::duration total{}; bool total_success = true; @@ -80,9 +80,8 @@ bool execute_days(app &, solution_vector const &days) { } xlog::info("DONE"); - xlog::info( - "Total time was {} ms", - std::chrono::duration_cast(total).count()); + xlog::info("Total time was {} ms", + std::chrono::duration_cast(total).count()); return total_success; } @@ -91,8 +90,9 @@ std::int64_t microseconds(xmas::solution::duration d) { return std::chrono::duration_cast(d).count(); } -bool time_days(app &, solution_vector const &days, - xmas::solution::duration timeout) { +bool time_days(app&, + solution_vector const& days, + xmas::solution::duration timeout) { xmas::solution::duration total{}; bool total_success = true; @@ -100,7 +100,7 @@ bool time_days(app &, solution_vector const &days, xlog::info("Every day's solution will be executed non-stop for {} seconds to " "evaluate its performance.", - std::chrono::duration_cast(timeout).count()); + std::chrono::duration_cast(timeout).count()); constexpr std::string_view fmt = "{:>7} {:>8} {:>7} {:>7}"; xlog::info(""); @@ -138,9 +138,9 @@ bool time_days(app &, solution_vector const &days, } // Average - const auto mean = std::chrono::duration_cast( - daily_total / iter) - .count(); + const auto mean = + std::chrono::duration_cast(daily_total / iter) + .count(); const auto dev = static_cast(std::sqrt(S / iter)); // Report @@ -149,25 +149,26 @@ bool time_days(app &, solution_vector const &days, total += daily_total / iter; } - xlog::info( - fmt, "TOTAL", "-", - std::chrono::duration_cast(total).count(), - "-"); + xlog::info(fmt, + "TOTAL", + "-", + std::chrono::duration_cast(total).count(), + "-"); return total_success; } -std::optional -solve_day(std::map>::value_type const - &solution, - bool verbose) { +std::optional solve_day( + std::map>::value_type const& + solution, + bool verbose) { try { solution.second->set_input( - std::format("./data/{:02d}/input.txt", solution.second->day())); - } catch (std::runtime_error &e) { - xlog::error("day {} could not load: {}\n", solution.second->day(), - e.what()); + std::format("./data/{:02d}/input.txt", solution.second->day())); + } catch (std::runtime_error& e) { + xlog::error( + "day {} could not load: {}\n", solution.second->day(), e.what()); return {}; } diff --git a/2023/cmd/main.cpp b/2023/cmd/main.cpp index bd8ab30..51b23b5 100644 --- a/2023/cmd/main.cpp +++ b/2023/cmd/main.cpp @@ -9,7 +9,7 @@ #include #include -int main(int argc, char **argv) { +int main(int argc, char** argv) { std::vector args; args.reserve(static_cast(argc)); for (auto i = 1; i < argc; ++i) { @@ -19,83 +19,84 @@ int main(int argc, char **argv) { app::app a; a.register_command({ - .flags = {"-h", "--help"}, - .help = "Shows this message and exits", - .run = [](app::app &app, app::argv args) -> int { - if (args.size() != 0) { - xlog::error("help takes no arguments"); - return exit_bad_args; - } - std::cout << app.usage(); - return exit_success; - }, + .flags = {"-h", "--help"}, + .help = "Shows this message and exits", + .run = [](app::app& app, app::argv args) -> int { + if (args.size() != 0) { + xlog::error("help takes no arguments"); + return exit_bad_args; + } + std::cout << app.usage(); + return exit_success; + }, }); a.register_command({ - .flags = {"-r", "--run"}, - .help = "Run the solutions for the specified days", - .run = - [](app::app &app, app::argv args) { - auto selection = app::select_days(args); - if (selection.size() == 0) { - xlog::error("No days selected"); - return exit_bad_args; - } + .flags = {"-r", "--run"}, + .help = "Run the solutions for the specified days", + .run = + [](app::app& app, app::argv args) { + auto selection = app::select_days(args); + if (selection.size() == 0) { + xlog::error("No days selected"); + return exit_bad_args; + } - const bool success = app::execute_days(app, selection); - if (success) { - return exit_success; - } - return exit_failure; - }, + const bool success = app::execute_days(app, selection); + if (success) { + return exit_success; + } + return exit_failure; + }, }); a.register_command({ - .flags = {"-a", "--all"}, - .help = "Run all the solutions", - .run = - [](app::app &app, app::argv args) { - if (args.size() != 0) { - xlog::error("--all takes no arguments"); - return exit_bad_args; - } + .flags = {"-a", "--all"}, + .help = "Run all the solutions", + .run = + [](app::app& app, app::argv args) { + if (args.size() != 0) { + xlog::error("--all takes no arguments"); + return exit_bad_args; + } - auto selection = app::select_all_days(); - if (selection.size() == 0) { - xlog::error("No days available"); - return exit_failure; - } + auto selection = app::select_all_days(); + if (selection.size() == 0) { + xlog::error("No days available"); + return exit_failure; + } - const bool success = app::execute_days(app, selection); - if (success) { - return exit_success; - } - return exit_failure; - }, + const bool success = app::execute_days(app, selection); + if (success) { + return exit_success; + } + return exit_failure; + }, }); a.register_command({ - .flags = {"-t", "--time"}, - .help = "Run all the solutions many times to get an accurate profile", - .run = - [](app::app &app, app::argv args) { - if (args.size() != 0) { - xlog::error("--time takes no arguments"); - return exit_bad_args; - } + .flags = {"-t", "--time"}, + .help = "Run all the solutions many times to get an accurate profile", + .run = + [](app::app& app, app::argv args) { + if (args.size() != 0) { + xlog::error("--time takes no arguments"); + return exit_bad_args; + } - auto selection = app::select_all_days(); - if (selection.size() == 0) { - xlog::error("No days available"); - return exit_failure; - } + auto selection = app::select_all_days(); + if (selection.size() == 0) { + xlog::error("No days available"); + return exit_failure; + } - const bool success = app::time_days(app, selection, std::chrono::seconds(5)); - if (success) { - return exit_success; - } - return exit_failure; - }, + const bool success = + app::time_days(app, selection, std::chrono::seconds(5)); + if (success) { + return exit_success; + } + return exit_failure; + }, }); return a.run(args); diff --git a/2023/solvelib/01/day01.cpp b/2023/solvelib/01/day01.cpp index 60987f7..7031d4d 100644 --- a/2023/solvelib/01/day01.cpp +++ b/2023/solvelib/01/day01.cpp @@ -1,4 +1,5 @@ #include "day01.hpp" + #include #include #include @@ -6,35 +7,34 @@ #include #include -bool is_num(char ch) { return ch >= '0' && ch <= '9'; } +bool is_num(char ch) { + return ch >= '0' && ch <= '9'; +} std::uint64_t Day01::part1() { std::stringstream ss(this->input); - return std::transform_reduce( - std::istream_iterator(ss), - std::istream_iterator{}, 0u, std::plus{}, - [](std::string_view line) -> std::uint64_t{ - // First digit - const auto first = std::ranges::find_if(line, is_num); - if (first == line.end()) { - throw std::runtime_error( - std::format("line {} contains no numbers", line)); - } - - // Last digit - const auto last = - std::find_if(line.rbegin(), std::reverse_iterator(first), is_num); - - // compute - return static_cast(int(*first - '0') * 10 + int(*last - '0')); - }); + return std::transform_reduce(std::istream_iterator(ss), + std::istream_iterator{}, 0u, std::plus{}, + [](std::string_view line) -> std::uint64_t { + // First digit + const auto first = std::ranges::find_if(line, is_num); + if (first == line.end()) { + throw std::runtime_error(std::format("line {} contains no numbers", line)); + } + + // Last digit + const auto last = std::find_if(line.rbegin(), std::reverse_iterator(first), is_num); + + // compute + return static_cast(int(*first - '0') * 10 + int(*last - '0')); + }); } template std::pair find_number(Iterator begin, Iterator end) { - constexpr static std::array numbers{"one", "two", "three", "four", "five", - "six", "seven", "eight", "nine"}; + constexpr static std::array numbers{ + "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"}; if (begin == end) { return std::make_pair(end, 0); @@ -48,15 +48,15 @@ std::pair find_number(Iterator begin, Iterator end) { endtext = begin; } - for (Iterator it=begin; it != end; it += inc) { + for (Iterator it = begin; it != end; it += inc) { if (is_num(*it)) { return std::make_pair(it, int(*it - '0')); } std::string_view window(it, endtext); - auto r = std::ranges::find_if( - numbers, [window](auto number) { return window.starts_with(number); }); + auto r = + std::ranges::find_if(numbers, [window](auto number) { return window.starts_with(number); }); if (r == numbers.end()) { continue; } @@ -70,20 +70,18 @@ std::pair find_number(Iterator begin, Iterator end) { std::uint64_t Day01::part2() { std::stringstream ss(this->input); - return std::transform_reduce( - std::istream_iterator(ss), - std::istream_iterator{}, 0u, std::plus{}, - [](std::string_view line) { - // First digit - const auto first = find_number(line.begin(), line.end()); - if (first.first == line.end()) - throw std::runtime_error( - std::format("line {} contains no numbers", line)); - - // Last digit - const auto last = find_number(line.end(), first.first-1); - - // compute - return first.second * 10 + last.second; - }); + return std::transform_reduce(std::istream_iterator(ss), + std::istream_iterator{}, 0u, std::plus{}, + [](std::string_view line) { + // First digit + const auto first = find_number(line.begin(), line.end()); + if (first.first == line.end()) + throw std::runtime_error(std::format("line {} contains no numbers", line)); + + // Last digit + const auto last = find_number(line.end(), first.first - 1); + + // compute + return first.second * 10 + last.second; + }); } diff --git a/2023/solvelib/01/day01.hpp b/2023/solvelib/01/day01.hpp index 64c40bd..87aeb83 100644 --- a/2023/solvelib/01/day01.hpp +++ b/2023/solvelib/01/day01.hpp @@ -4,10 +4,11 @@ class Day01 : public xmas::solution { public: - int day() override { return 1; } + int day() override { + return 1; + } public: std::uint64_t part1() override; std::uint64_t part2() override; - }; diff --git a/2023/solvelib/01/day01_test.hpp b/2023/solvelib/01/day01_test.hpp index 7047757..8f159ca 100644 --- a/2023/solvelib/01/day01_test.hpp +++ b/2023/solvelib/01/day01_test.hpp @@ -2,28 +2,28 @@ #include TEST_CASE("Day 1") { - Day01 solution{}; + Day01 solution{}; - SUBCASE("Part 1") { - solution.set_input("./data/01/example1.txt"); - solution.load(); - REQUIRE_EQ(solution.day(), 1); - } + SUBCASE("Part 1") { + solution.set_input("./data/01/example1.txt"); + solution.load(); + REQUIRE_EQ(solution.day(), 1); + } - SUBCASE("Part 2") { - solution.set_input("./data/01/example2.txt"); - solution.load(); - REQUIRE_EQ(solution.part2(), 281); + SUBCASE("Part 2") { + solution.set_input("./data/01/example2.txt"); + solution.load(); + REQUIRE_EQ(solution.part2(), 281); - solution.set_input("./data/01/example3.txt"); - solution.load(); - REQUIRE_EQ(solution.part2(), 33); - } + solution.set_input("./data/01/example3.txt"); + solution.load(); + REQUIRE_EQ(solution.part2(), 33); + } - SUBCASE("Real data") { - solution.set_input("./data/01/input.txt"); - solution.load(); - REQUIRE_EQ(solution.part1(), 54561); - REQUIRE_EQ(solution.part2(), 54076); - } + SUBCASE("Real data") { + solution.set_input("./data/01/input.txt"); + solution.load(); + REQUIRE_EQ(solution.part1(), 54561); + REQUIRE_EQ(solution.part2(), 54076); + } } \ No newline at end of file diff --git a/2023/solvelib/02/day02.cpp b/2023/solvelib/02/day02.cpp index 7870cc8..16a175c 100644 --- a/2023/solvelib/02/day02.cpp +++ b/2023/solvelib/02/day02.cpp @@ -14,37 +14,35 @@ std::uint64_t Day02::part1() { constexpr std::int64_t max_blue = 14; return std::transform_reduce( - range.begin(), range.end(), 0u, std::plus{}, - [](std::string_view line) -> std::int64_t { - const game g(line); - - if (g.max.red > max_red) { - xlog::debug("Rejected because of red: {}", g); - return 0; - } - if (g.max.green > max_green) { - xlog::debug("Rejected because of green: {}", g); - return 0; - } - if (g.max.blue > max_blue) { - xlog::debug("Rejected because of blue: {}", g); - return 0; - } - - return g.id; - }); + range.begin(), range.end(), 0u, std::plus{}, [](std::string_view line) -> std::int64_t { + const game g(line); + + if (g.max.red > max_red) { + xlog::debug("Rejected because of red: {}", g); + return 0; + } + if (g.max.green > max_green) { + xlog::debug("Rejected because of green: {}", g); + return 0; + } + if (g.max.blue > max_blue) { + xlog::debug("Rejected because of blue: {}", g); + return 0; + } + + return g.id; + }); } std::uint64_t Day02::part2() { xmas::views::linewise range(this->input); - return std::transform_reduce(range.begin(), range.end(), 0u, std::plus{}, - [](std::string_view line) -> std::uint64_t { - const game g(line); + return std::transform_reduce( + range.begin(), range.end(), 0u, std::plus{}, [](std::string_view line) -> std::uint64_t { + const game g(line); - const auto p = static_cast( - g.max.red * g.max.green * g.max.blue); - xlog::debug("Power value is {} for {}", p, g); - return p; - }); + const auto p = static_cast(g.max.red * g.max.green * g.max.blue); + xlog::debug("Power value is {} for {}", p, g); + return p; + }); } diff --git a/2023/solvelib/02/day02.hpp b/2023/solvelib/02/day02.hpp index 6c01969..1555dab 100644 --- a/2023/solvelib/02/day02.hpp +++ b/2023/solvelib/02/day02.hpp @@ -4,10 +4,11 @@ class Day02 : public xmas::solution { public: - int day() override { return 2; } + int day() override { + return 2; + } public: std::uint64_t part1() override; std::uint64_t part2() override; - }; diff --git a/2023/solvelib/02/game.cpp b/2023/solvelib/02/game.cpp index 80bb026..d05381e 100644 --- a/2023/solvelib/02/game.cpp +++ b/2023/solvelib/02/game.cpp @@ -22,18 +22,15 @@ rgb parse_round(std::string_view data) { std::int64_t count; const auto result = std::from_chars(it, data.end(), count, 10); if (result.ptr == data.end()) { - throw std::runtime_error( - std::format("could not parse dice count in string {}", data)); + throw std::runtime_error(std::format("could not parse dice count in string {}", data)); } // Skip whitespace - it = - std::find_if(result.ptr, data.end(), [](auto ch) { return ch != ' '; }); + it = std::find_if(result.ptr, data.end(), [](auto ch) { return ch != ' '; }); // Parse colour if (it == data.end()) - throw std::runtime_error( - std::format("could not find colour in string {}", data)); + throw std::runtime_error(std::format("could not find colour in string {}", data)); switch (*it) { case 'r': @@ -46,8 +43,8 @@ rgb parse_round(std::string_view data) { round.blue += count; break; default: - throw std::runtime_error(std::format( - "unknown colour in string {} (after digit {})", data, count)); + throw std::runtime_error( + std::format("unknown colour in string {} (after digit {})", data, count)); } // Advance start of next dice @@ -64,19 +61,16 @@ game::game(std::string_view line) { // Parse prefix auto space = std::ranges::find(line, ' '); if (space == line.end()) - throw std::runtime_error( - std::format("could not find space in line {}", line)); + throw std::runtime_error(std::format("could not find space in line {}", line)); const auto result = std::from_chars(space + 1, line.end(), this->id, 10); if (result.ptr == line.end()) { - throw std::runtime_error( - std::format("could not parse game ID in line {}", line)); + throw std::runtime_error(std::format("could not parse game ID in line {}", line)); } // Parse rounds std::vector semicolons; - std::string_view::const_iterator it = - result.ptr + 1; // +1 to ignore the whitespace after : + std::string_view::const_iterator it = result.ptr + 1; // +1 to ignore the whitespace after : semicolons.push_back(it); while (it != line.end()) { it = std::find(it + 1, line.end(), ';'); @@ -85,20 +79,19 @@ game::game(std::string_view line) { try { this->max = std::transform_reduce( - std::execution::par_unseq, semicolons.cbegin(), semicolons.cend() - 1, - semicolons.cbegin() + 1, rgb{}, - [](rgb const &acc, rgb const &round) -> rgb { - return rgb{ - .red = std::max(acc.red, round.red), - .green = std::max(acc.green, round.green), - .blue = std::max(acc.blue, round.blue), - }; - }, - [](auto prev, auto next) -> rgb { - return parse_round(std::string_view{prev + 1, next}); - }); - } catch (std::runtime_error &e) { - throw std::runtime_error( - std::format("could not parse round in line {}: {}", line, e.what())); + std::execution::par_unseq, semicolons.cbegin(), semicolons.cend() - 1, + semicolons.cbegin() + 1, rgb{}, + [](rgb const& acc, rgb const& round) -> rgb { + return rgb{ + .red = std::max(acc.red, round.red), + .green = std::max(acc.green, round.green), + .blue = std::max(acc.blue, round.blue), + }; + }, + [](auto prev, auto next) -> rgb { + return parse_round(std::string_view{prev + 1, next}); + }); + } catch (std::runtime_error& e) { + throw std::runtime_error(std::format("could not parse round in line {}: {}", line, e.what())); } } \ No newline at end of file diff --git a/2023/solvelib/02/game.hpp b/2023/solvelib/02/game.hpp index fe8a72b..2a7f3ec 100644 --- a/2023/solvelib/02/game.hpp +++ b/2023/solvelib/02/game.hpp @@ -17,15 +17,17 @@ struct game { explicit game(std::string_view); }; -template <> struct std::formatter : std::formatter { - auto format(rgb const &r, format_context &ctx) const { +template <> +struct std::formatter : std::formatter { + auto format(rgb const& r, format_context& ctx) const { return formatter::format( - std::format("{{{}, {}, {} }}", r.red, r.green, r.blue), ctx); + std::format("{{{}, {}, {} }}", r.red, r.green, r.blue), ctx); } }; -template <> struct std::formatter : std::formatter { - auto format(game const &g, format_context &ctx) const { +template <> +struct std::formatter : std::formatter { + auto format(game const& g, format_context& ctx) const { auto f = std::format("{{ game: {}, max: {}}}", g.id, g.max); return formatter::format(f, ctx); diff --git a/2023/solvelib/03/day03.cpp b/2023/solvelib/03/day03.cpp index 377a29d..c9003de 100644 --- a/2023/solvelib/03/day03.cpp +++ b/2023/solvelib/03/day03.cpp @@ -1,7 +1,5 @@ #include "day03.hpp" -#include "xmaslib/log/log.hpp" -#include "xmaslib/solution/solution.hpp" #include #include #include @@ -11,13 +9,17 @@ #include #include -constexpr bool isnum(char ch) { return ch >= '0' && ch <= '9'; } +#include "xmaslib/log/log.hpp" +#include "xmaslib/solution/solution.hpp" + +constexpr bool isnum(char ch) { + return ch >= '0' && ch <= '9'; +} namespace { std::pair dimensions(std::string_view input) { const std::size_t ncols = - 1 + std::size_t(std::find(input.cbegin(), input.cend(), '\n') - - input.cbegin()); + 1 + std::size_t(std::find(input.cbegin(), input.cend(), '\n') - input.cbegin()); const std::size_t nrows = input.size() / ncols; xlog::debug("Detected {} rows and {} columns", nrows, ncols); @@ -25,17 +27,7 @@ std::pair dimensions(std::string_view input) { } } // namespace -void Day03::load() { - xmas::solution::load(); - - // Trailing newline is necessary - if (this->input.back() != '\n') { - this->input.push_back('\n'); - } -} - std::uint64_t Day03::part1() { - const auto [nrows, ncols] = dimensions(this->input); // Make a mask labeling all symbol-adjacent cells @@ -45,108 +37,105 @@ std::uint64_t Day03::part1() { { // Detect symbols std::vector symbols(this->input.size(), 0); - std::transform(std::execution::par_unseq, this->input.cbegin(), - this->input.cend(), symbols.begin(), [](char ch) { - if (ch == '.') - return 0; - if (ch == '\n') - return 0; - if (ch >= '0' && ch <= '9') - return 0; - - // Upper row - return 1; - }); + std::transform(std::execution::par_unseq, this->input.cbegin(), this->input.cend(), + symbols.begin(), [](char ch) { + if (ch == '.') + return 0; + if (ch == '\n') + return 0; + if (ch >= '0' && ch <= '9') + return 0; + + // Upper row + return 1; + }); std::vector iota(this->input.size(), 0); std::iota(iota.begin(), iota.end(), 0); // Expand the mask to cover symbol adjacency - std::transform(std::execution::par_unseq, iota.cbegin(), iota.cend(), - mask.begin(), - [nrows, ncols, &symbols](std::size_t idx) -> std::int64_t { - // Top row - const auto row = idx / ncols; - const auto col = idx % ncols; - - const bool leftmost = (col == 0); - const bool rightmost = (col == ncols - 1); - - if (rightmost) { - // Skip \n - return 0; - } - - // Same row - if (!leftmost && symbols[idx - 1] == 1) - return 1; - if (!rightmost && symbols[idx + 1] == 1) - return 1; - - // Top row - if (row > 0) { - const auto top = idx - ncols; - if (symbols[top] == 1) - return 1; - if (!leftmost && symbols[top - 1] == 1) - return 1; - if (!rightmost && symbols[top + 1] == 1) - return 1; - } - - // Bottom row - if (row < nrows - 1) { - const auto bot = idx + ncols; - if (symbols[bot] == 1) - return 1; - if (!leftmost && symbols[bot - 1] == 1) - return 1; - if (!rightmost && symbols[bot + 1] == 1) - return 1; - } - - return 0; - }); + std::transform(std::execution::par_unseq, iota.cbegin(), iota.cend(), mask.begin(), + [nrows, ncols, &symbols](std::size_t idx) -> std::int64_t { + // Top row + const auto row = idx / ncols; + const auto col = idx % ncols; + + const bool leftmost = (col == 0); + const bool rightmost = (col == ncols - 1); + + if (rightmost) { + // Skip \n + return 0; + } + + // Same row + if (!leftmost && symbols[idx - 1] == 1) + return 1; + if (!rightmost && symbols[idx + 1] == 1) + return 1; + + // Top row + if (row > 0) { + const auto top = idx - ncols; + if (symbols[top] == 1) + return 1; + if (!leftmost && symbols[top - 1] == 1) + return 1; + if (!rightmost && symbols[top + 1] == 1) + return 1; + } + + // Bottom row + if (row < nrows - 1) { + const auto bot = idx + ncols; + if (symbols[bot] == 1) + return 1; + if (!leftmost && symbols[bot - 1] == 1) + return 1; + if (!rightmost && symbols[bot + 1] == 1) + return 1; + } + + return 0; + }); } // Parse each row std::vector rows(nrows, 0); std::iota(rows.begin(), rows.end(), 0); - return std::transform_reduce( - std::execution::par_unseq, rows.begin(), rows.end(), 0u, - std::plus{}, [ncols, this, &mask](std::size_t row) { - auto begin = row * ncols; - auto end = begin + ncols; - - std::uint64_t total = 0; - std::uint64_t num = 0; - bool relevant = false; - - for (std::size_t idx = begin; idx != end; ++idx) { - - if (!isnum(input[idx])) { - if (relevant) { - total += num; - xlog::debug("Row {}: Adding number {}", row, num); - } else if (num != 0) { - xlog::debug("Row {}: Ignoring number {}", row, num); - } - num = 0; - relevant = false; - continue; + return std::transform_reduce(std::execution::par_unseq, rows.begin(), rows.end(), 0u, std::plus{}, + [ncols, this, &mask](std::size_t row) { + auto begin = row * ncols; + auto end = begin + ncols; + + std::uint64_t total = 0; + std::uint64_t num = 0; + bool relevant = false; + + for (std::size_t idx = begin; idx != end; ++idx) { + if (!isnum(input[idx])) { + if (relevant) { + total += num; + xlog::debug("Row {}: Adding number {}", row, num); + } else if (num != 0) { + xlog::debug("Row {}: Ignoring number {}", row, num); } + num = 0; + relevant = false; + continue; + } - // Parse next digit - num *= 10; - num += uint(input[idx] - '0'); + // Parse next digit + num *= 10; + num += uint(input[idx] - '0'); - // Is it in contact? - if (mask[idx] == 1) - relevant = true; - } + // Is it in contact? + if (mask[idx] == 1) + relevant = true; + } - return total; - }); + return total; + }); } std::uint64_t Day03::part2() { @@ -158,105 +147,99 @@ std::uint64_t Day03::part2() { // Build a matrix where each entry is the number that digit belongs to std::vector numbers(nrows, std::vector(ncols, std::int64_t(-1))); std::for_each(std::execution::par_unseq, rows.begin(), rows.end(), - [ncols, this, &numbers](const std::size_t row) { - auto row_begin = row * ncols; - std::size_t num_begin = 0; - - std::int64_t num = 0; - - for (std::size_t col = 0; col < ncols; ++col) { - const auto idx = row_begin + col; - - if (!isnum(input[idx])) { - if (col - num_begin != 0) { - xlog::debug("Found number {} in row {}, cols [{}, {})", - num, row, num_begin, col); - std::fill( - numbers[row].begin() + std::ptrdiff_t(num_begin), - numbers[row].begin() + std::ptrdiff_t(col), num); - } - - num = 0; - num_begin = col + 1; - continue; - } - - // Parse next digit - num *= 10; - num += input[idx] - '0'; - } - }); - - // Scan for gears - std::vector iota(nrows * ncols, 0); - std::iota(iota.begin(), iota.end(), 0); - return std::transform_reduce( - std::execution::par_unseq, iota.begin(), iota.end(), 0u, - std::plus{}, - [this, nrows, ncols, &numbers](const std::size_t idx) -> std::uint64_t { - if (this->input[idx] != '*') { - return 0; - } + [ncols, this, &numbers](const std::size_t row) { + auto row_begin = row * ncols; + std::size_t num_begin = 0; - const auto row = idx / ncols; - const auto col = idx % ncols; + std::int64_t num = 0; - std::vector neighbours; - neighbours.reserve(5); + for (std::size_t col = 0; col < ncols; ++col) { + const auto idx = row_begin + col; - const bool leftmost = (col == 0); - const bool rightmost = (col == ncols - 1); + if (!isnum(input[idx])) { + if (col - num_begin != 0) { + xlog::debug("Found number {} in row {}, cols [{}, {})", num, row, num_begin, col); + std::fill(numbers[row].begin() + std::ptrdiff_t(num_begin), + numbers[row].begin() + std::ptrdiff_t(col), num); + } - // Side neighbours - if (!leftmost && numbers[row][col - 1] != -1) { - neighbours.emplace_back(numbers[row][col - 1]); - } - if (!rightmost && numbers[row][col + 1] != -1) { - neighbours.emplace_back(numbers[row][col + 1]); + num = 0; + num_begin = col + 1; + continue; } - // Up neighbours - if (row != 0) { - auto r = numbers[row - 1]; - if (r[col] != -1) { - // Either a number on top - neighbours.emplace_back(r[col]); - } else { - // Or numbers on top-left and top-right - if (!leftmost && r[col - 1] != -1) { - neighbours.emplace_back(r[col - 1]); - } - if (!rightmost && r[col + 1] != -1) { - neighbours.emplace_back(r[col + 1]); - } + // Parse next digit + num *= 10; + num += input[idx] - '0'; + } + }); + + // Scan for gears + std::vector iota(nrows * ncols, 0); + std::iota(iota.begin(), iota.end(), 0); + return std::transform_reduce(std::execution::par_unseq, iota.begin(), iota.end(), 0u, std::plus{}, + [this, nrows, ncols, &numbers](const std::size_t idx) -> std::uint64_t { + if (this->input[idx] != '*') { + return 0; + } + + const auto row = idx / ncols; + const auto col = idx % ncols; + + std::vector neighbours; + neighbours.reserve(5); + + const bool leftmost = (col == 0); + const bool rightmost = (col == ncols - 1); + + // Side neighbours + if (!leftmost && numbers[row][col - 1] != -1) { + neighbours.emplace_back(numbers[row][col - 1]); + } + if (!rightmost && numbers[row][col + 1] != -1) { + neighbours.emplace_back(numbers[row][col + 1]); + } + + // Up neighbours + if (row != 0) { + auto r = numbers[row - 1]; + if (r[col] != -1) { + // Either a number on top + neighbours.emplace_back(r[col]); + } else { + // Or numbers on top-left and top-right + if (!leftmost && r[col - 1] != -1) { + neighbours.emplace_back(r[col - 1]); + } + if (!rightmost && r[col + 1] != -1) { + neighbours.emplace_back(r[col + 1]); } } - - // Down neighbours - if (row + 1 != nrows) { - auto r = numbers[row + 1]; - if (r[col] != -1) { - // Either a number below - neighbours.emplace_back(r[col]); - } else { - // Or numbers below-left and below-right - if (!leftmost && r[col - 1] != -1) { - neighbours.emplace_back(r[col - 1]); - } - if (!rightmost && r[col + 1] != -1) { - neighbours.emplace_back(r[col + 1]); - } + } + + // Down neighbours + if (row + 1 != nrows) { + auto r = numbers[row + 1]; + if (r[col] != -1) { + // Either a number below + neighbours.emplace_back(r[col]); + } else { + // Or numbers below-left and below-right + if (!leftmost && r[col - 1] != -1) { + neighbours.emplace_back(r[col - 1]); + } + if (!rightmost && r[col + 1] != -1) { + neighbours.emplace_back(r[col + 1]); } } + } - if (auto sz = neighbours.size(); sz != 2) { - xlog::debug("Gear in row {} has {} neighbour{}", row, sz, - sz > 1 ? "s" : ""); - return 0; - } + if (auto sz = neighbours.size(); sz != 2) { + xlog::debug("Gear in row {} has {} neighbour{}", row, sz, sz > 1 ? "s" : ""); + return 0; + } - xlog::debug("Gear in row {} has neighbours {} and {}", row, - neighbours[0], neighbours[1]); - return neighbours[0] * neighbours[1]; - }); + xlog::debug("Gear in row {} has neighbours {} and {}", row, neighbours[0], neighbours[1]); + return neighbours[0] * neighbours[1]; + }); } \ No newline at end of file diff --git a/2023/solvelib/03/day03.hpp b/2023/solvelib/03/day03.hpp index be5b6e0..544af03 100644 --- a/2023/solvelib/03/day03.hpp +++ b/2023/solvelib/03/day03.hpp @@ -4,8 +4,9 @@ class Day03 : public xmas::solution { public: - int day() override { return 3; } - void load() override; + int day() override { + return 3; + } public: std::uint64_t part1() override; diff --git a/2023/solvelib/04/day04.cpp b/2023/solvelib/04/day04.cpp index 71948ea..457a3dc 100644 --- a/2023/solvelib/04/day04.cpp +++ b/2023/solvelib/04/day04.cpp @@ -1,5 +1,4 @@ #include "day04.hpp" -#include "xmaslib/log/log.hpp" #include #include @@ -11,12 +10,12 @@ #include #include +#include "xmaslib/log/log.hpp" + namespace { -[[nodiscard]] std::pair -dimensions(std::string_view input) { +[[nodiscard]] std::pair dimensions(std::string_view input) { const std::size_t ncols = - 1 + std::size_t(std::find(input.cbegin(), input.cend(), '\n') - - input.cbegin()); + 1 + std::size_t(std::find(input.cbegin(), input.cend(), '\n') - input.cbegin()); const std::size_t nrows = input.size() / ncols; xlog::debug("Detected {} rows and {} columns", nrows, ncols); @@ -24,8 +23,8 @@ dimensions(std::string_view input) { } [[nodiscard]] std::uint64_t card_hits(const std::size_t row, - const std::string::const_iterator begin, - const std::string::const_iterator end) { + const std::string::const_iterator begin, + const std::string::const_iterator end) { auto it = std::find(begin, end, ':'); if (it == end) { throw std::runtime_error(std::format("Row {} has no colon (:)", row)); @@ -79,39 +78,27 @@ dimensions(std::string_view input) { } // namespace -void Day04::load() { - xmas::solution::load(); - - // Trailing newline is necessary - if (this->input.back() != '\n') { - this->input.push_back('\n'); - } -} - std::uint64_t Day04::part1() { const auto [nrows, ncols] = dimensions(this->input); std::vector rows(nrows, 0); std::iota(rows.begin(), rows.end(), 0); - return std::transform_reduce( - std::execution::par_unseq, rows.begin(), rows.end(), 0u, - std::plus{}, - [this, ncols](std::size_t const row) -> std::uint64_t { - const auto begin = - input.cbegin() + static_cast(row * ncols); - const auto end = begin + static_cast(ncols); + return std::transform_reduce(std::execution::par_unseq, rows.begin(), rows.end(), 0u, std::plus{}, + [this, ncols](std::size_t const row) -> std::uint64_t { + const auto begin = input.cbegin() + static_cast(row * ncols); + const auto end = begin + static_cast(ncols); - const auto hits = card_hits(row, begin, end); + const auto hits = card_hits(row, begin, end); - std::uint64_t score = 0; - if (hits > 0) { - score = 1 << (hits - 1); - } + std::uint64_t score = 0; + if (hits > 0) { + score = 1 << (hits - 1); + } - xlog::debug("Card {} has a score of {}", row + 1, score); - return score; - }); + xlog::debug("Card {} has a score of {}", row + 1, score); + return score; + }); } std::uint64_t Day04::part2() { @@ -123,32 +110,28 @@ std::uint64_t Day04::part2() { // Parse in parallel, compute how many hits per card std::vector hits(nrows); std::transform(rows.cbegin(), rows.cend(), hits.begin(), - [this, ncols](std::size_t const row) -> std::uint64_t { - const auto begin = input.cbegin() + - static_cast(row * ncols); - const auto end = begin + static_cast(ncols); - return card_hits(row, begin, end); - }); + [this, ncols](std::size_t const row) -> std::uint64_t { + const auto begin = input.cbegin() + static_cast(row * ncols); + const auto end = begin + static_cast(ncols); + return card_hits(row, begin, end); + }); // Must not be parallel because we overwrite copies std::vector copies(nrows, 1); std::transform(rows.begin(), rows.end(), copies.begin(), - [&hits, &copies](std::size_t const row) -> std::uint64_t { - const auto nhits = hits[row]; - const auto ncopies = copies[row]; - - xlog::debug("Game {}: there are {} copies with {} hits each", - row + 1, ncopies, nhits); - - // Add copies of following cards - auto mbegin = - copies.begin() + static_cast(row) + 1; - auto mend = mbegin + nhits; - std::transform(mbegin, mend, mbegin, - [ncopies](auto &m) { return m + ncopies; }); - - return ncopies; - }); + [&hits, &copies](std::size_t const row) -> std::uint64_t { + const auto nhits = hits[row]; + const auto ncopies = copies[row]; + + xlog::debug("Game {}: there are {} copies with {} hits each", row + 1, ncopies, nhits); + + // Add copies of following cards + auto mbegin = copies.begin() + static_cast(row) + 1; + auto mend = mbegin + nhits; + std::transform(mbegin, mend, mbegin, [ncopies](auto& m) { return m + ncopies; }); + + return ncopies; + }); // std::ranges::reduce does not exist >:( return std::reduce(copies.begin(), copies.end()); diff --git a/2023/solvelib/04/day04.hpp b/2023/solvelib/04/day04.hpp index a45a677..1c20470 100644 --- a/2023/solvelib/04/day04.hpp +++ b/2023/solvelib/04/day04.hpp @@ -4,11 +4,11 @@ class Day04 : public xmas::solution { public: - int day() override { return 4; } - void load() override; + int day() override { + return 4; + } public: std::uint64_t part1() override; std::uint64_t part2() override; - }; diff --git a/2023/solvelib/05/day05.cpp b/2023/solvelib/05/day05.cpp index e847f6d..035b27d 100644 --- a/2023/solvelib/05/day05.cpp +++ b/2023/solvelib/05/day05.cpp @@ -89,7 +89,7 @@ struct layer { // Process the input range from left to right, until we have no input range // left. - for (mapping const &m : mappings) { + for (mapping const& m : mappings) { if (m.src + m.len < in.begin) { // mapping is to the left of the input range continue; @@ -110,8 +110,8 @@ struct layer { // Extact the overlap (the rest of the input will be processed next // iteration) auto overlap = intrange{ - .begin = in.begin, - .end = std::min(in.end, m.src + m.len), + .begin = in.begin, + .end = std::min(in.end, m.src + m.len), }; in.begin = overlap.end; @@ -119,8 +119,8 @@ struct layer { auto begin_delta = overlap.begin - m.src; auto end_delta = overlap.end - m.src; out.push_back({ - .begin = m.dest + begin_delta, - .end = m.dest + end_delta, + .begin = m.dest + begin_delta, + .end = m.dest + end_delta, }); // Process right tail (if there is one) @@ -140,23 +140,18 @@ struct layer { // After parsing the mappings (source->dest pairs), we sort them according to // the source. void commit() { - std::ranges::sort(mappings, [](mapping const &a, mapping const &b) { - return a.src < b.src; - }); + std::ranges::sort(mappings, [](mapping const& a, mapping const& b) { return a.src < b.src; }); // Validate no overlap between ranges, throw in case there is // That would mean a bad input (or me misunderstanding the problem :P) #ifndef NDEBUG auto it = std::adjacent_find(mappings.begin(), mappings.end(), - [](mapping const &prev, mapping const &next) { - return prev.src + prev.len > next.src; - }); + [](mapping const& prev, mapping const& next) { return prev.src + prev.len > next.src; }); if (it != mappings.end()) { auto first = *it; auto second = *(it + 1); - throw std::runtime_error(std::format( - "Entry ({},{},{}) overlaps with ({},{},{})", first.src, first.dest, - first.len, second.src, second.dest, second.len)); + throw std::runtime_error(std::format("Entry ({},{},{}) overlaps with ({},{},{})", first.src, + first.dest, first.len, second.src, second.dest, second.len)); } #endif } @@ -168,9 +163,8 @@ struct layer { // // return.first: The layer that was parsed // return.second: An iterator pointing to the beginning of the next section -std::pair -parse_layer(xmas::views::linewise::iterator begin, - xmas::views::linewise::iterator end) { +std::pair parse_layer(xmas::views::linewise::iterator begin, + xmas::views::linewise::iterator end) { layer l; auto it = begin; @@ -184,14 +178,14 @@ parse_layer(xmas::views::linewise::iterator begin, break; } else if (size != 3) { // Wrong input (or more likely: found a bug!) - throw std::runtime_error(std::format( - "Line '{}' has the wrong number of integers ({})", *it, size)); + throw std::runtime_error( + std::format("Line '{}' has the wrong number of integers ({})", *it, size)); } l.mappings.emplace_back(mapping{ - .src = ints[1], - .dest = ints[0], - .len = ints[2], + .src = ints[1], + .dest = ints[0], + .len = ints[2], }); } @@ -208,13 +202,12 @@ std::vector coalesce_ranges(std::vector in) { } // Sort according to range start - std::sort(in.begin(), in.end(), - [](intrange a, intrange b) { return a.begin < b.begin; }); + std::sort(in.begin(), in.end(), [](intrange a, intrange b) { return a.begin < b.begin; }); // Merge any 2 or more consecutive ranges where prev.end >= next.begin std::vector out = {in.front()}; - for (auto &next : std::span(in.begin() + 1, in.end())) { - auto &prev = out.back(); + for (auto& next : std::span(in.begin() + 1, in.end())) { + auto& prev = out.back(); if (prev.end >= next.begin) { // Overlap with previous range: extend the previous one to absorb this one prev.end = std::max(prev.end, next.end); @@ -229,11 +222,11 @@ std::vector coalesce_ranges(std::vector in) { // Appends the contents of src at the end of dst template -void extend(std::vector &dst, std::vector const &src) { +void extend(std::vector& dst, std::vector const& src) { std::size_t old_size = dst.size(); dst.resize(old_size + src.size()); std::copy(std::execution::unseq, src.begin(), src.end(), - dst.begin() + static_cast(old_size)); + dst.begin() + static_cast(old_size)); } } // namespace @@ -255,7 +248,7 @@ std::uint64_t Day05::part1() { // Parse translation layers std::array layers; - for (auto &l : layers) { + for (auto& l : layers) { auto r = parse_layer(iline, rng.end()); l = r.first; iline = r.second; @@ -263,17 +256,17 @@ std::uint64_t Day05::part1() { // Computing each seed in parallel return std::transform_reduce( - std::execution::par_unseq, seeds.cbegin(), seeds.cend(), - std::numeric_limits::max(), - [](std::uint64_t x, std::uint64_t y) { return x < y ? x : y; }, /* min */ - [&layers](const std::uint64_t seed) -> std::uint64_t { - auto x = seed; - for (auto const &layer : layers) { - x = layer.translate(x); - } - xlog::debug("{}->{}", seed, x); - return x; - }); + std::execution::par_unseq, seeds.cbegin(), seeds.cend(), + std::numeric_limits::max(), + [](std::uint64_t x, std::uint64_t y) { return x < y ? x : y; }, /* min */ + [&layers](const std::uint64_t seed) -> std::uint64_t { + auto x = seed; + for (auto const& layer : layers) { + x = layer.translate(x); + } + xlog::debug("{}->{}", seed, x); + return x; + }); } std::uint64_t Day05::part2() { @@ -286,8 +279,8 @@ std::uint64_t Day05::part2() { std::vector seed_ranges; for (std::size_t i = 0; i + 1 < seed_pairs.size(); i += 2) { seed_ranges.push_back({ - .begin = seed_pairs[i], - .end = seed_pairs[i] + seed_pairs[i + 1], + .begin = seed_pairs[i], + .end = seed_pairs[i] + seed_pairs[i + 1], }); } @@ -301,7 +294,7 @@ std::uint64_t Day05::part2() { // Parse translation layers std::array layers; - for (auto &l : layers) { + for (auto& l : layers) { auto r = parse_layer(iline, rng.end()); l = r.first; iline = r.second; @@ -309,22 +302,22 @@ std::uint64_t Day05::part2() { // Computing each seed range in parallel return std::transform_reduce( - std::execution::par_unseq, seed_ranges.cbegin(), seed_ranges.cend(), - std::numeric_limits::max(), - [](std::uint64_t x, std::uint64_t y) { return x < y ? x : y; }, /* min */ - [&layers](const intrange seed_range) -> std::uint64_t { - std::vector in = {seed_range}; - for (auto const &layer : layers) { - std::vector out; - for (auto &i : in) { - extend(out, layer.translate(i)); - } - - in = coalesce_ranges(out); + std::execution::par_unseq, seed_ranges.cbegin(), seed_ranges.cend(), + std::numeric_limits::max(), + [](std::uint64_t x, std::uint64_t y) { return x < y ? x : y; }, /* min */ + [&layers](const intrange seed_range) -> std::uint64_t { + std::vector in = {seed_range}; + for (auto const& layer : layers) { + std::vector out; + for (auto& i : in) { + extend(out, layer.translate(i)); } - const auto best = in.front().begin; - xlog::debug("[{}, {}) -> {}", seed_range.begin, seed_range.end, best); - return best; - }); + in = coalesce_ranges(out); + } + const auto best = in.front().begin; + + xlog::debug("[{}, {}) -> {}", seed_range.begin, seed_range.end, best); + return best; + }); } diff --git a/2023/solvelib/05/day05.hpp b/2023/solvelib/05/day05.hpp index d2abe88..92d104f 100644 --- a/2023/solvelib/05/day05.hpp +++ b/2023/solvelib/05/day05.hpp @@ -4,7 +4,9 @@ class Day05 : public xmas::solution { public: - int day() override { return 5; } + int day() override { + return 5; + } public: std::uint64_t part1() override; diff --git a/2023/solvelib/06/day06.cpp b/2023/solvelib/06/day06.cpp index 66a6d5f..9c56779 100644 --- a/2023/solvelib/06/day06.cpp +++ b/2023/solvelib/06/day06.cpp @@ -63,9 +63,8 @@ std::uint64_t Day06::part1() { // It's faster with std::execution::unseq than with std::execution::par_unseq. // Probably because the input is too small to justify allocating extra // threads. - return std::transform_reduce( - std::execution::unseq, times.cbegin(), times.cend(), distances.cbegin(), - std::uint64_t{1}, std::multiplies{}, count_wins); + return std::transform_reduce(std::execution::unseq, times.cbegin(), times.cend(), + distances.cbegin(), std::uint64_t{1}, std::multiplies{}, count_wins); } std::uint64_t Day06::part2() { diff --git a/2023/solvelib/06/day06.hpp b/2023/solvelib/06/day06.hpp index d0fbee4..c19dd82 100644 --- a/2023/solvelib/06/day06.hpp +++ b/2023/solvelib/06/day06.hpp @@ -4,7 +4,9 @@ class Day06 : public xmas::solution { public: - int day() override { return 6; } + int day() override { + return 6; + } public: std::uint64_t part1() override; diff --git a/2023/solvelib/11/day11.cpp b/2023/solvelib/11/day11.cpp index b0293c8..3e1efd8 100644 --- a/2023/solvelib/11/day11.cpp +++ b/2023/solvelib/11/day11.cpp @@ -1,8 +1,5 @@ #include "day11.hpp" -#include "xmaslib/iota/iota.hpp" -#include "xmaslib/log/log.hpp" - #include #include #include @@ -12,6 +9,9 @@ #include #include +#include "xmaslib/iota/iota.hpp" +#include "xmaslib/log/log.hpp" + namespace { struct coords { @@ -22,19 +22,16 @@ std::uint64_t absolute_difference(std::uint64_t a, std::uint64_t b) { return (a > b) ? (a - b) : (b - a); } -[[nodiscard]] std::pair -dimensions(std::string_view input) { +[[nodiscard]] std::pair dimensions(std::string_view input) { const std::size_t ncols = - 1 + std::size_t(std::find(input.cbegin(), input.cend(), '\n') - - input.cbegin()); + 1 + std::size_t(std::find(input.cbegin(), input.cend(), '\n') - input.cbegin()); const std::size_t nrows = input.size() / ncols; xlog::debug("Detected {} rows and {} columns", nrows, ncols); return std::make_pair(nrows, ncols); } -auto parse_and_expand_universe(std::string_view input, - std::size_t expansion_rate) { +auto parse_and_expand_universe(std::string_view input, std::size_t expansion_rate) { const auto [nrows, ncols] = dimensions(input); // Find galaxies and expand rows @@ -64,7 +61,7 @@ auto parse_and_expand_universe(std::string_view input, { std::size_t empties = 0; std::ptrdiff_t prev_row = -1; - for (auto &map : column_mapping) { + for (auto& map : column_mapping) { auto row = std::ptrdiff_t(map.first); empties += std::size_t(row - prev_row - 1); prev_row = row; @@ -80,9 +77,8 @@ auto parse_and_expand_universe(std::string_view input, #endif // Expand columns - std::for_each( - std::execution::par_unseq, galaxies.begin(), galaxies.end(), - [&column_mapping](coords &g) { g.col = column_mapping.at(g.col); }); + std::for_each(std::execution::par_unseq, galaxies.begin(), galaxies.end(), + [&column_mapping](coords& g) { g.col = column_mapping.at(g.col); }); #ifndef NDEBUG for (auto i : xmas::views::iota(galaxies.size())) { @@ -95,15 +91,6 @@ auto parse_and_expand_universe(std::string_view input, } // namespace -void Day11::load() { - xmas::solution::load(); - - // Trailing newline is necessary - if (this->input.back() != '\n') { - this->input.push_back('\n'); - } -} - std::uint64_t Day11::solve(std::size_t expansion_rate) const { auto galaxies = parse_and_expand_universe(input, expansion_rate); @@ -112,28 +99,30 @@ std::uint64_t Day11::solve(std::size_t expansion_rate) const { std::vector distances((n * (n - 1)) / 2); xmas::views::iota iota(n); - std::for_each(std::execution::par_unseq, iota.begin(), iota.end(), - [&galaxies, &distances](std::size_t i) { - auto &g1 = galaxies[i]; - for (std::size_t j = i + 1; j != galaxies.size(); ++j) { - auto &g2 = galaxies[j]; - - // Taxicab distance - std::uint64_t vdistance = - absolute_difference(g2.row, g1.row); - std::uint64_t hdistance = - absolute_difference(g2.col, g1.col); - std::uint64_t d = vdistance + hdistance; - - xlog::debug("Distance between {} and {} is {}", i, j, d); - std::size_t dest = (j * (j - 1)) / 2 + i; // <3 Gauss - distances[dest] = d; - } - }); - - return std::reduce(std::execution::unseq, distances.begin(), distances.end(), - std::uint64_t{0}, std::plus{}); + std::for_each( + std::execution::par_unseq, iota.begin(), iota.end(), [&galaxies, &distances](std::size_t i) { + auto& g1 = galaxies[i]; + for (std::size_t j = i + 1; j != galaxies.size(); ++j) { + auto& g2 = galaxies[j]; + + // Taxicab distance + std::uint64_t vdistance = absolute_difference(g2.row, g1.row); + std::uint64_t hdistance = absolute_difference(g2.col, g1.col); + std::uint64_t d = vdistance + hdistance; + + xlog::debug("Distance between {} and {} is {}", i, j, d); + std::size_t dest = (j * (j - 1)) / 2 + i; // <3 Gauss + distances[dest] = d; + } + }); + + return std::reduce(std::execution::unseq, distances.begin(), distances.end(), std::uint64_t{0}, + std::plus{}); } -std::uint64_t Day11::part1() { return solve(2); } -std::uint64_t Day11::part2() { return solve(1'000'000); } +std::uint64_t Day11::part1() { + return solve(2); +} +std::uint64_t Day11::part2() { + return solve(1'000'000); +} diff --git a/2023/solvelib/11/day11.hpp b/2023/solvelib/11/day11.hpp index 183207b..767feba 100644 --- a/2023/solvelib/11/day11.hpp +++ b/2023/solvelib/11/day11.hpp @@ -4,8 +4,9 @@ class Day11 : public xmas::solution { public: - int day() override { return 11; } - void load() override; + int day() override { + return 11; + } public: std::uint64_t solve(std::size_t expansion_rate) const; diff --git a/2023/solvelib/11/day11_test.hpp b/2023/solvelib/11/day11_test.hpp index c173f07..ca19fa3 100644 --- a/2023/solvelib/11/day11_test.hpp +++ b/2023/solvelib/11/day11_test.hpp @@ -9,7 +9,7 @@ TEST_CASE("Day 11") { solution.load(); REQUIRE_EQ(solution.part1(), 374); } - + SUBCASE("Part 2, size 10") { Day11 solution{}; solution.set_input("./data/11/example.txt"); diff --git a/2023/solvelib/12/day12.cpp b/2023/solvelib/12/day12.cpp index 07f1860..4676357 100644 --- a/2023/solvelib/12/day12.cpp +++ b/2023/solvelib/12/day12.cpp @@ -27,7 +27,11 @@ namespace { -enum class cell : char { unknown = '?', set = '#', empty = '.' }; +enum class cell : char { + unknown = '?', + set = '#', + empty = '.' +}; auto parse_line(std::string_view line) { auto separator = std::ranges::find(line, ' '); @@ -46,8 +50,7 @@ auto parse_line(std::string_view line) { return std::make_pair(cells, ranges); } -std::tuple trim(auto cells_base, - auto ranges_base) { +std::tuple trim(auto cells_base, auto ranges_base) { auto cells = xmas::view{cells_base.begin(), cells_base.end()}; auto ranges = xmas::view{ranges_base.begin(), ranges_base.end()}; @@ -76,8 +79,7 @@ std::tuple trim(auto cells_base, } // Trimming prefixed ### - auto it = - std::find_if_not(cells.begin(), cells.end(), xmas::equals(cell::set)); + auto it = std::find_if_not(cells.begin(), cells.end(), xmas::equals(cell::set)); auto const prefix_size = it - cells.begin(); if (prefix_size > ranges.front()) { @@ -150,20 +152,17 @@ std::tuple trim(auto cells_base, return {{}, {}, false}; } - cells.pop_front(prefixable_size + - 1); // +1 because next . or ? is forced into . + cells.pop_front(prefixable_size + 1); // +1 because next . or ? is forced into . ranges.pop_front(); } - return {cells_base.size() - cells.size(), ranges_base.size() - ranges.size(), - true}; + return {cells_base.size() - cells.size(), ranges_base.size() - ranges.size(), true}; } -auto stringify_line(auto const &cells, auto const &ranges) { +auto stringify_line(auto const& cells, auto const& ranges) { return xmas::lazy_string{[=]() { std::stringstream ss; - ss << std::string_view{reinterpret_cast(&cells.front()), - cells.size()}; + ss << std::string_view{reinterpret_cast(&cells.front()), cells.size()}; ss << " "; if (ranges.size() == 0) { return ss.str(); @@ -178,7 +177,8 @@ auto stringify_line(auto const &cells, auto const &ranges) { } auto trim_left(xmas::view::const_iterator> cells, - xmas::view::const_iterator> ranges) { + xmas::view::const_iterator> + ranges) { auto const [ctrim, rtrim, ok] = trim(cells, ranges); cells.pop_front(ctrim); ranges.pop_front(rtrim); @@ -187,10 +187,11 @@ auto trim_left(xmas::view::const_iterator> cells, } auto trim_right(xmas::view::const_iterator> cells, - xmas::view::const_iterator> ranges) { + xmas::view::const_iterator> + ranges) { - auto const [ctrim, rtrim, ok] = trim(std::ranges::views::reverse(cells), - std::ranges::views::reverse(ranges)); + auto const [ctrim, rtrim, ok] = + trim(std::ranges::views::reverse(cells), std::ranges::views::reverse(ranges)); cells.pop_back(ctrim); ranges.pop_back(rtrim); @@ -206,11 +207,9 @@ struct state { xmas::view::const_iterator> cells; xmas::view::const_iterator> ranges; - bool operator==(state const &other) const noexcept { - return (&*cells.begin() == &*other.cells.begin()) && - (cells.size() == other.cells.size()) && - (&*ranges.begin() == &*other.ranges.begin()) && - (cells.size() == other.cells.size()); + bool operator==(state const& other) const noexcept { + return (&*cells.begin() == &*other.cells.begin()) && (cells.size() == other.cells.size()) && + (&*ranges.begin() == &*other.ranges.begin()) && (cells.size() == other.cells.size()); } std::size_t hash() const noexcept { @@ -223,25 +222,28 @@ struct state { } private: - static std::size_t hash16bit(auto const *p) { - return std::hash{}(reinterpret_cast(p)) & - 0xffff; + static std::size_t hash16bit(auto const* p) { + return std::hash{}(reinterpret_cast(p)) & 0xffff; } }; } // namespace -template <> struct std::hash { +template <> +struct std::hash { - std::size_t operator()(const state &s) const noexcept { return s.hash(); } + std::size_t operator()(const state& s) const noexcept { + return s.hash(); + } }; namespace { -[[nodiscard]] std::uint64_t -count_combinations(xmas::lru_cache &lru, - xmas::view::const_iterator> cells_base, - xmas::view::const_iterator> ranges_base) { +[[nodiscard]] std::uint64_t count_combinations(xmas::lru_cache& lru, + xmas::view::const_iterator> + cells_base, + xmas::view::const_iterator> + ranges_base) { if (auto opt = lru.get({cells_base, ranges_base}); opt.has_value()) { return *opt; @@ -264,8 +266,7 @@ count_combinations(xmas::lru_cache &lru, } const auto leftover = - static_cast(std::reduce(ranges.begin(), ranges.end())) + - ranges.size() - 1; + static_cast(std::reduce(ranges.begin(), ranges.end())) + ranges.size() - 1; if (cells.size() < leftover) { return 0; } @@ -275,47 +276,46 @@ count_combinations(xmas::lru_cache &lru, // We try to force ? into # and . and see if it breaks auto pound = std::ranges::find(cells, cell::set); auto size = std::min(1 + cells.size() - static_cast(leftover), - 1 + static_cast(pound - cells.begin())); + 1 + static_cast(pound - cells.begin())); const auto L = static_cast(ranges.front()); xmas::views::iota iota(size); - return std::transform_reduce( - iota.begin(), iota.end(), std::uint64_t{0u}, std::plus{}, - [&lru, cells, ranges, L](std::size_t idx) -> std::uint64_t { - if (cells[idx] == cell::empty) { - return 0; - } - - if (idx != 0 && cells[idx - 1] == cell::set) { - // Collision with prev block - return 0; - } - - auto v = cells.drop(idx); - - auto prefix = v.take(L); - auto it = std::ranges::find(prefix, cell::empty); - if (it != prefix.end()) { - // Range does not fit + return std::transform_reduce(iota.begin(), iota.end(), std::uint64_t{0u}, + std::plus{}, [&lru, cells, ranges, L](std::size_t idx) -> std::uint64_t { + if (cells[idx] == cell::empty) { + return 0; + } + + if (idx != 0 && cells[idx - 1] == cell::set) { + // Collision with prev block + return 0; + } + + auto v = cells.drop(idx); + + auto prefix = v.take(L); + auto it = std::ranges::find(prefix, cell::empty); + if (it != prefix.end()) { + // Range does not fit + return 0; + } + + if (it != v.end() && *it == cell::set) { + // Collision with next block + return 0; + } + + if (ranges.size() == 1) { + // End of sequence + if (contains(v.drop(ranges.front()), cell::set)) { return 0; } + return 1; + } - if (it != v.end() && *it == cell::set) { - // Collision with next block - return 0; - } - - if (ranges.size() == 1) { - // End of sequence - if (contains(v.drop(ranges.front()), cell::set)) { - return 0; - } - return 1; - } - - auto x = count_combinations(lru, v.drop(L + 1), ranges.drop(1)); - return x; - }); + auto x = count_combinations(lru, v.drop(L + 1), ranges.drop(1)); + return x; + }); }(); lru.set(state{cells_base, ranges_base}, result); @@ -339,19 +339,16 @@ std::uint64_t process_line(std::string_view line) { for (std::size_t i = 0; i < 4; ++i) { cells_base.push_back(cell::unknown); std::copy(cells0.begin(), cells0.end(), std::back_inserter(cells_base)); - std::copy(ranges0.begin(), ranges0.end(), - std::back_inserter(ranges_base)); + std::copy(ranges0.begin(), ranges0.end(), std::back_inserter(ranges_base)); } } // Trim right. Left trim is done inside count_combinations - auto [cells, ranges, ok] = - trim_right(xmas::view{cells_base.cbegin(), cells_base.cend()}, - xmas::view{ranges_base.cbegin(), ranges_base.cend()}); + auto [cells, ranges, ok] = trim_right(xmas::view{cells_base.cbegin(), cells_base.cend()}, + xmas::view{ranges_base.cbegin(), ranges_base.cend()}); if (!ok) { - xlog::warning("No combinations possible for line {} (error from the right)", - line); + xlog::warning("No combinations possible for line {} (error from the right)", line); return 0; } @@ -359,8 +356,7 @@ std::uint64_t process_line(std::string_view line) { const auto x = count_combinations(lru, cells, ranges); if (x == 0) { - xlog::warning("No combinations possible for line {} (error from the left)", - line); + xlog::warning("No combinations possible for line {} (error from the left)", line); } else { xlog::debug("{} -> ({}) -> {}", line, stringify_line(cells, ranges), x); } @@ -373,15 +369,13 @@ std::uint64_t process_line(std::string_view line) { std::uint64_t Day12::part1() { auto in = xmas::views::linewise(this->input); - return std::transform_reduce(std::execution::par_unseq, in.cbegin(), - in.cend(), std::uint64_t(0u), - std::plus{}, process_line); + return std::transform_reduce(std::execution::par_unseq, in.cbegin(), in.cend(), std::uint64_t(0u), + std::plus{}, process_line); } std::uint64_t Day12::part2() { auto in = xmas::views::linewise(this->input); - return std::transform_reduce(std::execution::par_unseq, in.cbegin(), - in.cend(), std::uint64_t(0u), - std::plus{}, process_line); + return std::transform_reduce(std::execution::par_unseq, in.cbegin(), in.cend(), std::uint64_t(0u), + std::plus{}, process_line); } \ No newline at end of file diff --git a/2023/solvelib/12/day12.hpp b/2023/solvelib/12/day12.hpp index 31b69b5..e6b9a45 100644 --- a/2023/solvelib/12/day12.hpp +++ b/2023/solvelib/12/day12.hpp @@ -4,7 +4,9 @@ class Day12 : public xmas::solution { public: - int day() override { return 12; } + int day() override { + return 12; + } public: std::uint64_t part1() override; diff --git a/2023/solvelib/13/day13.cpp b/2023/solvelib/13/day13.cpp index 890e37f..1ddba58 100644 --- a/2023/solvelib/13/day13.cpp +++ b/2023/solvelib/13/day13.cpp @@ -1,11 +1,5 @@ #include "day13.hpp" -#include "xmaslib/functional/functional.hpp" -#include "xmaslib/line_iterator/line_iterator.hpp" -#include "xmaslib/log/log.hpp" -#include "xmaslib/stride/stride.hpp" -#include "xmaslib/view/view.hpp" - #include #include #include @@ -17,10 +11,15 @@ #include #include +#include "xmaslib/functional/functional.hpp" +#include "xmaslib/line_iterator/line_iterator.hpp" +#include "xmaslib/log/log.hpp" +#include "xmaslib/stride/stride.hpp" +#include "xmaslib/view/view.hpp" + namespace { -std::vector -locate_block_begins(std::string_view input) { +std::vector locate_block_begins(std::string_view input) { std::vector block_begins{input.begin()}; if (input.size() == 0) { @@ -28,9 +27,8 @@ locate_block_begins(std::string_view input) { } for (auto it = input.begin(); it != input.end(); ++it) { - it = std::adjacent_find(it, input.end(), [](char l, char r) -> bool { - return l == '\n' && r == '\n'; - }); + it = std::adjacent_find( + it, input.end(), [](char l, char r) -> bool { return l == '\n' && r == '\n'; }); if (it == input.end()) { break; } @@ -43,18 +41,16 @@ locate_block_begins(std::string_view input) { std::pair block_dimensions(std::string_view input) { const std::size_t ncols = - 1 + std::size_t(std::find(input.cbegin(), input.cend(), '\n') - - input.cbegin()); + 1 + std::size_t(std::find(input.cbegin(), input.cend(), '\n') - input.cbegin()); const std::size_t nrows = input.size() / ncols; return std::make_pair(nrows, ncols); } template -void check_line_symmetry(std::vector &potential_axes, Iterator line_begin, - Iterator line_end) { +void check_line_symmetry( + std::vector& potential_axes, Iterator line_begin, Iterator line_end) { assert(line_begin < line_end); - assert(potential_axes.size() == - static_cast(line_end - line_begin)); + assert(potential_axes.size() == static_cast(line_end - line_begin)); auto v = xmas::view{line_begin, line_end}; @@ -79,10 +75,8 @@ void check_line_symmetry(std::vector &potential_axes, Iterator line_begin, } } -std::vector -check_block_horizontal_symmetries(std::string_view block, std::size_t nrows, - std::size_t ncols) { - +std::vector check_block_horizontal_symmetries( + std::string_view block, std::size_t nrows, std::size_t ncols) { const std::size_t width = ncols - 1; // -1 to skip the trailing endline std::vector potential_axes(width, true); @@ -102,9 +96,8 @@ check_block_horizontal_symmetries(std::string_view block, std::size_t nrows, return symmetries; } -std::vector check_block_vertical_symmetries(std::string_view block, - std::size_t nrows, - std::size_t ncols) { +std::vector check_block_vertical_symmetries( + std::string_view block, std::size_t nrows, std::size_t ncols) { std::vector potential_axes(nrows, true); for (std::size_t col = 0; col < ncols; ++col) { @@ -126,8 +119,8 @@ std::vector check_block_vertical_symmetries(std::string_view block, return symmetries; } -std::pair -solve_block(std::string_view block, std::size_t nrows, std::size_t ncols) { +std::pair solve_block( + std::string_view block, std::size_t nrows, std::size_t ncols) { auto h = check_block_horizontal_symmetries(block, nrows, ncols); if (h.size() != 0) { return {h.front(), 0}; @@ -152,29 +145,19 @@ char flip(char c) { } // namespace -void Day13::load() { - xmas::solution::load(); - - // Trailing newline is necessary - if (this->input.back() != '\n') { - this->input.push_back('\n'); - } -} - std::uint64_t Day13::part1() { auto block_begins = locate_block_begins(this->input); xlog::debug("Located {} blocks", block_begins.size() - 1); - return std::transform_reduce( - std::execution::par_unseq, block_begins.begin(), block_begins.end() - 1, - block_begins.begin() + 1, std::uint64_t{0}, std::plus{}, - [](auto begin, auto begin_next) { - std::string_view block{begin, begin_next - 1}; - const auto [nrows, ncols] = block_dimensions(block); - auto p1 = solve_block(block, nrows, ncols); - return p1.first + 100 * p1.second; - }); + return std::transform_reduce(std::execution::par_unseq, block_begins.begin(), + block_begins.end() - 1, block_begins.begin() + 1, std::uint64_t{0}, std::plus{}, + [](auto begin, auto begin_next) { + std::string_view block{begin, begin_next - 1}; + const auto [nrows, ncols] = block_dimensions(block); + auto p1 = solve_block(block, nrows, ncols); + return p1.first + 100 * p1.second; + }); } std::uint64_t Day13::part2() { @@ -182,39 +165,38 @@ std::uint64_t Day13::part2() { xlog::debug("Located {} blocks", block_begins.size() - 1); - return std::transform_reduce( - std::execution::par_unseq, block_begins.begin(), block_begins.end() - 1, - block_begins.begin() + 1, std::uint64_t{0}, std::plus{}, - [](auto begin, auto begin_next) -> std::uint64_t { - // Blocks are relatively small (smaller than 20x20) so we can - // brute-force it - std::string block{begin, begin_next - 1}; - const auto [nrows, ncols] = block_dimensions(block); - auto part1 = solve_block(block, nrows, ncols); - - for (char &c : block) { - if (c == '\n') { - continue; - } - - c = flip(c); - - auto sym = check_block_horizontal_symmetries(block, nrows, ncols); - auto it = std::ranges::find_if_not(sym, xmas::equals(part1.first)); - if (it != sym.end()) { - return *it; - } - - sym = check_block_vertical_symmetries(block, nrows, ncols); - it = std::ranges::find_if_not(sym, xmas::equals(part1.second)); - if (it != sym.end()) { - return 100 * (*it); - } - - c = flip(c); + return std::transform_reduce(std::execution::par_unseq, block_begins.begin(), + block_begins.end() - 1, block_begins.begin() + 1, std::uint64_t{0}, std::plus{}, + [](auto begin, auto begin_next) -> std::uint64_t { + // Blocks are relatively small (smaller than 20x20) so we can + // brute-force it + std::string block{begin, begin_next - 1}; + const auto [nrows, ncols] = block_dimensions(block); + auto part1 = solve_block(block, nrows, ncols); + + for (char& c : block) { + if (c == '\n') { + continue; } - xlog::warning("block with no possible symmetry"); - return 0; - }); + c = flip(c); + + auto sym = check_block_horizontal_symmetries(block, nrows, ncols); + auto it = std::ranges::find_if_not(sym, xmas::equals(part1.first)); + if (it != sym.end()) { + return *it; + } + + sym = check_block_vertical_symmetries(block, nrows, ncols); + it = std::ranges::find_if_not(sym, xmas::equals(part1.second)); + if (it != sym.end()) { + return 100 * (*it); + } + + c = flip(c); + } + + xlog::warning("block with no possible symmetry"); + return 0; + }); } diff --git a/2023/solvelib/13/day13.hpp b/2023/solvelib/13/day13.hpp index f811c91..0caa17e 100644 --- a/2023/solvelib/13/day13.hpp +++ b/2023/solvelib/13/day13.hpp @@ -4,8 +4,9 @@ class Day13 : public xmas::solution { public: - int day() override { return 13; } - void load() override; + int day() override { + return 13; + } public: std::uint64_t part1() override; diff --git a/2023/solvelib/14/day14.cpp b/2023/solvelib/14/day14.cpp index a51a565..89dabd0 100644 --- a/2023/solvelib/14/day14.cpp +++ b/2023/solvelib/14/day14.cpp @@ -1,10 +1,5 @@ #include "day14.hpp" -#include "xmaslib/iota/iota.hpp" -#include "xmaslib/log/log.hpp" -#include "xmaslib/lru/lru.hpp" -#include "xmaslib/matrix/text_matrix.hpp" - #include #include #include @@ -12,66 +7,60 @@ #include #include -void Day14::load() { - xmas::solution::load(); - - // Trailing newline is necessary - if (this->input.back() != '\n') { - this->input.push_back('\n'); - } -} +#include "xmaslib/iota/iota.hpp" +#include "xmaslib/log/log.hpp" +#include "xmaslib/lru/lru.hpp" +#include "xmaslib/matrix/text_matrix.hpp" std::uint64_t Day14::part1() { xmas::views::text_matrix matrix(input); xmas::views::iota iota(matrix.ncols()); - return std::transform_reduce( - std::execution::seq, iota.begin(), iota.end(), std::uint64_t{0}, - std::plus{}, [&matrix](std::size_t j) -> std::uint64_t { - auto col = matrix.col(j); - - std::size_t score = 0; - std::size_t next_score = matrix.nrows(); - for (std::size_t i = 0; i != matrix.nrows(); ++i) { - switch (col[i]) { - case '.': - continue; - case '#': - next_score = matrix.nrows() - i - 1; - continue; - case 'O': - score += next_score; - --next_score; - } + return std::transform_reduce(std::execution::seq, iota.begin(), iota.end(), std::uint64_t{0}, + std::plus{}, [&matrix](std::size_t j) -> std::uint64_t { + auto col = matrix.col(j); + + std::size_t score = 0; + std::size_t next_score = matrix.nrows(); + for (std::size_t i = 0; i != matrix.nrows(); ++i) { + switch (col[i]) { + case '.': + continue; + case '#': + next_score = matrix.nrows() - i - 1; + continue; + case 'O': + score += next_score; + --next_score; } + } - return score; - }); + return score; + }); } namespace { -#define def_flip_func(name, axis, reverse) \ - void name(xmas::views::text_matrix &matrix) { \ - xmas::views::iota iota(matrix.n##axis##s()); \ - std::for_each(std::execution::seq, iota.begin(), iota.end(), \ - [&matrix](std::size_t j) { \ - auto axis = matrix.axis(j); \ - auto fall_to = axis.reverse##begin(); \ - for (auto it = fall_to; it != axis.reverse##end(); ++it) { \ - switch (*it) { \ - case '.': \ - break; \ - case '#': \ - fall_to = it + 1; \ - break; \ - case 'O': \ - *it = '.'; \ - *fall_to = 'O'; \ - ++fall_to; \ - } \ - } \ - }); \ +#define def_flip_func(name, axis, reverse) \ + void name(xmas::views::text_matrix& matrix) { \ + xmas::views::iota iota(matrix.n##axis##s()); \ + std::for_each(std::execution::seq, iota.begin(), iota.end(), [&matrix](std::size_t j) { \ + auto axis = matrix.axis(j); \ + auto fall_to = axis.reverse##begin(); \ + for (auto it = fall_to; it != axis.reverse##end(); ++it) { \ + switch (*it) { \ + case '.': \ + break; \ + case '#': \ + fall_to = it + 1; \ + break; \ + case 'O': \ + *it = '.'; \ + *fall_to = 'O'; \ + ++fall_to; \ + } \ + } \ + }); \ } def_flip_func(flip_north, col, ); @@ -79,26 +68,23 @@ def_flip_func(flip_south, col, r); def_flip_func(flip_west, row, ); def_flip_func(flip_east, row, r); -void loop_the_loop(xmas::views::text_matrix &matrix) { +void loop_the_loop(xmas::views::text_matrix& matrix) { flip_north(matrix); flip_west(matrix); flip_south(matrix); flip_east(matrix); } -std::uint64_t compute_load(xmas::views::text_matrix &matrix) { +std::uint64_t compute_load(xmas::views::text_matrix& matrix) { xmas::views::iota iota(matrix.nrows()); - return std::transform_reduce( - std::execution::seq, iota.begin(), iota.end(), std::uint64_t{0}, - std::plus{}, [&matrix](std::size_t i) { - auto row = matrix.row(i); - const std::size_t weight = matrix.nrows() - i; - return std::transform_reduce(row.begin(), row.end(), std::uint64_t{0}, - std::plus{}, - [weight](char c) -> std::uint64_t { - return c == 'O' ? weight : 0u; - }); - }); + return std::transform_reduce(std::execution::seq, iota.begin(), iota.end(), std::uint64_t{0}, + std::plus{}, [&matrix](std::size_t i) { + auto row = matrix.row(i); + const std::size_t weight = matrix.nrows() - i; + return std::transform_reduce(row.begin(), row.end(), std::uint64_t{0}, + std::plus{}, + [weight](char c) -> std::uint64_t { return c == 'O' ? weight : 0u; }); + }); } } // namespace diff --git a/2023/solvelib/14/day14.hpp b/2023/solvelib/14/day14.hpp index bfeacfd..2117347 100644 --- a/2023/solvelib/14/day14.hpp +++ b/2023/solvelib/14/day14.hpp @@ -4,8 +4,9 @@ class Day14 : public xmas::solution { public: - int day() override { return 14; } - void load() override; + int day() override { + return 14; + } public: std::uint64_t part1() override; diff --git a/2023/solvelib/15/day15.cpp b/2023/solvelib/15/day15.cpp index 2e2d08f..4a74700 100644 --- a/2023/solvelib/15/day15.cpp +++ b/2023/solvelib/15/day15.cpp @@ -1,8 +1,5 @@ #include "day15.hpp" -#include "xmaslib/iota/iota.hpp" -#include "xmaslib/parsing/parsing.hpp" - #include #include #include @@ -15,6 +12,9 @@ #include #include +#include "xmaslib/iota/iota.hpp" +#include "xmaslib/parsing/parsing.hpp" + namespace { struct hash_map { @@ -25,13 +25,11 @@ struct hash_map { std::array buckets; - void insert(key_t &&k, value_t &&v) { - auto &bucket = buckets[Day15::hash256(k)]; - auto it = std::ranges::find_if( - bucket, [&k](auto const &pair) { return pair.first == k; }); + void insert(key_t&& k, value_t&& v) { + auto& bucket = buckets[Day15::hash256(k)]; + auto it = std::ranges::find_if(bucket, [&k](auto const& pair) { return pair.first == k; }); if (it == bucket.end()) { - bucket.insert( - it, std::make_pair(std::forward(k), std::forward(v))); + bucket.insert(it, std::make_pair(std::forward(k), std::forward(v))); return; } @@ -39,10 +37,9 @@ struct hash_map { it->second = std::forward(v); } - void remove(key_t const &k) { - auto &bucket = buckets[Day15::hash256(k)]; - auto it = std::ranges::find_if(bucket, - [&k](auto const& pair) { return pair.first == k; }); + void remove(key_t const& k) { + auto& bucket = buckets[Day15::hash256(k)]; + auto it = std::ranges::find_if(bucket, [&k](auto const& pair) { return pair.first == k; }); if (it == bucket.end()) { return; // Nothing to remove } @@ -52,7 +49,10 @@ struct hash_map { }; struct instruction { - enum { insert, remove } action; + enum { + insert, + remove + } action; std::string label; std::uint64_t value; }; @@ -70,42 +70,27 @@ instruction parse_instruction(auto str) { } // namespace -void Day15::load() { - xmas::solution::load(); - - // Trailing newline is necessary - if (this->input.back() != '\n') { - this->input.push_back('\n'); - } -} - std::uint64_t Day15::part1() { - auto view = std::string_view(input.begin(), input.end() - 1) | - std::ranges::views::split(','); - - return std::transform_reduce( - std::execution::par_unseq, view.begin(), view.end(), std::uint64_t{0}, - std::plus{}, [](auto substring) { - return hash256(substring.begin(), substring.end()); - }); + auto view = std::string_view(input.begin(), input.end() - 1) | std::ranges::views::split(','); + + return std::transform_reduce(std::execution::par_unseq, view.begin(), view.end(), + std::uint64_t{0}, std::plus{}, + [](auto substring) { return hash256(substring.begin(), substring.end()); }); } std::uint64_t Day15::part2() { - auto view = std::string_view(input.begin(), input.end() - 1) | - std::ranges::views::split(','); + auto view = std::string_view(input.begin(), input.end() - 1) | std::ranges::views::split(','); std::vector s; - std::transform( - view.begin(), view.end(), std::back_insert_iterator(s), - [](auto subs) -> instruction { return parse_instruction(subs); }); + std::transform(view.begin(), view.end(), std::back_insert_iterator(s), + [](auto subs) -> instruction { return parse_instruction(subs); }); hash_map hm; - for (auto &&inst : std::move(s)) { + for (auto&& inst : std::move(s)) { switch (inst.action) { case instruction::insert: - hm.insert(std::forward(inst.label), - std::forward(inst.value)); + hm.insert(std::forward(inst.label), std::forward(inst.value)); continue; case instruction::remove: hm.remove(inst.label); @@ -114,18 +99,16 @@ std::uint64_t Day15::part2() { xmas::views::iota iota(256); - return std::transform_reduce(std::execution::par_unseq, iota.begin(), - iota.end(), std::uint64_t{0}, - std::plus{}, - [&buckets = hm.buckets](std::size_t bucket_id) { - std::uint64_t lenses_score = 0; + return std::transform_reduce(std::execution::par_unseq, iota.begin(), iota.end(), + std::uint64_t{0}, std::plus{}, [&buckets = hm.buckets](std::size_t bucket_id) { + std::uint64_t lenses_score = 0; - std::size_t pos = 1; - for (auto &v : buckets[bucket_id]) { - lenses_score += pos * v.second; - ++pos; - } + std::size_t pos = 1; + for (auto& v : buckets[bucket_id]) { + lenses_score += pos * v.second; + ++pos; + } - return (bucket_id + 1) * lenses_score; - }); + return (bucket_id + 1) * lenses_score; + }); } \ No newline at end of file diff --git a/2023/solvelib/15/day15.hpp b/2023/solvelib/15/day15.hpp index 9b7f944..dbe67a3 100644 --- a/2023/solvelib/15/day15.hpp +++ b/2023/solvelib/15/day15.hpp @@ -5,24 +5,22 @@ class Day15 : public xmas::solution { public: - int day() override { return 15; } - void load() override; + int day() override { + return 15; + } public: // Modulo 256 comes built-in :D using hash256_t = std::uint8_t; template - [[nodiscard]] static constexpr hash256_t hash256(Iterator begin, - Iterator end) { + [[nodiscard]] static constexpr hash256_t hash256(Iterator begin, Iterator end) { return std::accumulate(begin, end, hash256_t{0}, - [](hash256_t acc, char ch) constexpr { - return 17 * (acc + hash256_t(ch)); - }); + [](hash256_t acc, char ch) constexpr { return 17 * (acc + hash256_t(ch)); }); } template - [[nodiscard]] static constexpr hash256_t hash256(Range const &sv) { + [[nodiscard]] static constexpr hash256_t hash256(Range const& sv) { return hash256(sv.begin(), sv.end()); } diff --git a/2023/solvelib/15/day15_test.hpp b/2023/solvelib/15/day15_test.hpp index d926ccd..d1da618 100644 --- a/2023/solvelib/15/day15_test.hpp +++ b/2023/solvelib/15/day15_test.hpp @@ -7,7 +7,7 @@ TEST_CASE("Day 15") { SUBCASE("Hashing") { using namespace std::string_view_literals; - + // Compile-time testing! static_assert(Day15::hash256(""sv) == 0); static_assert(Day15::hash256("H"sv) == 200); diff --git a/2023/solvelib/16/day16.cpp b/2023/solvelib/16/day16.cpp index 763f236..bee39fb 100644 --- a/2023/solvelib/16/day16.cpp +++ b/2023/solvelib/16/day16.cpp @@ -1,10 +1,3 @@ -#include "day16.hpp" -#include "xmaslib/functional/functional.hpp" -#include "xmaslib/iota/iota.hpp" -#include "xmaslib/lazy_string/lazy_string.hpp" -#include "xmaslib/log/log.hpp" -#include "xmaslib/matrix/text_matrix.hpp" - #include #include #include @@ -12,17 +5,14 @@ #include #include #include -#include #include -void Day16::load() { - xmas::solution::load(); - - // Trailing newline is necessary - if (this->input.back() != '\n') { - this->input.push_back('\n'); - } -} +#include "day16.hpp" +#include "xmaslib/functional/functional.hpp" +#include "xmaslib/iota/iota.hpp" +#include "xmaslib/lazy_string/lazy_string.hpp" +#include "xmaslib/log/log.hpp" +#include "xmaslib/matrix/text_matrix.hpp" namespace { @@ -33,7 +23,12 @@ enum class direction : std::uint8_t { up = 4, down = 8 }; -enum class orientation { none, vertical, horizontal }; + +enum class orientation { + none, + vertical, + horizontal +}; // The beam orientation a splitter interacts with static enum orientation splitter_orientation(char ch) { @@ -50,8 +45,12 @@ static enum orientation splitter_orientation(char ch) { struct cell { direction visited = direction::none; - bool history_contains(direction x) { return (int(visited) & int(x)) != 0; } - void set_history(direction x) { visited = direction(int(visited) | int(x)); } + bool history_contains(direction x) { + return (int(visited) & int(x)) != 0; + } + void set_history(direction x) { + visited = direction(int(visited) | int(x)); + } }; struct beam { @@ -59,7 +58,7 @@ struct beam { std::ptrdiff_t row; std::ptrdiff_t col; - bool operator==(beam const &other) const { + bool operator==(beam const& other) const { return row == other.row && col == other.col && towards == other.towards; } @@ -109,18 +108,22 @@ struct beam { std::pair split() { switch (beam_orientation()) { case orientation::vertical: - return {{.towards = direction::left, .row = row, .col = col}, - {.towards = direction::right, .row = row, .col = col}}; + return { + {.towards = direction::left, .row = row, .col = col}, + {.towards = direction::right, .row = row, .col = col} + }; case orientation::horizontal: - return {{.towards = direction::up, .row = row, .col = col}, - {.towards = direction::down, .row = row, .col = col}}; + return { + {.towards = direction::up, .row = row, .col = col}, + {.towards = direction::down, .row = row, .col = col} + }; default: assert(false); return {}; } } - bool off_limits(xmas::views::text_matrix const &map) { + bool off_limits(xmas::views::text_matrix const& map) { if (row < 0 || col < 0) { return true; } @@ -179,8 +182,8 @@ struct beam { return direction::none; } - std::string print_state(xmas::views::text_matrix const &map, - std::vector> &visited) const { + std::string print_state(xmas::views::text_matrix const& map, + std::vector>& visited) const { std::stringstream ss; for (std::size_t i = 0; i < map.nrows(); ++i) { for (std::size_t j = 0; j < map.ncols(); ++j) { @@ -199,8 +202,8 @@ struct beam { return ss.str(); } - std::vector advance(xmas::views::text_matrix const &map, - std::vector> &visited) { + std::vector advance(xmas::views::text_matrix const& map, + std::vector>& visited) { std::vector new_beams; while (true) { step(); @@ -210,7 +213,7 @@ struct beam { break; } - auto &cell = visited[srow()][scol()]; + auto& cell = visited[srow()][scol()]; if (cell.history_contains(towards)) { break; } @@ -240,7 +243,7 @@ struct beam { namespace { -std::uint64_t solve(xmas::views::text_matrix const &map, beam init_beam) { +std::uint64_t solve(xmas::views::text_matrix const& map, beam init_beam) { std::vector visited(map.nrows(), std::vector(map.ncols(), cell{})); std::vector beams{init_beam}; @@ -251,37 +254,34 @@ std::uint64_t solve(xmas::views::text_matrix const &map, beam init_beam) { for (auto b : curr) { auto new_beams = b.advance(map, visited); - for (auto &nb : new_beams) { + for (auto& nb : new_beams) { beams.push_back(nb); } } } - auto x = std::transform_reduce( - std::execution::unseq, visited.begin(), visited.end(), std::uint64_t{0}, - std::plus{}, [](auto const &row) { - return std::transform_reduce( - std::execution::unseq, row.begin(), row.end(), std::uint64_t{0}, - std::plus{}, - [](cell c) { return int(c.visited) != 0u; }); - }); + auto x = std::transform_reduce(std::execution::unseq, visited.begin(), visited.end(), + std::uint64_t{0}, std::plus{}, [](auto const& row) { + return std::transform_reduce(std::execution::unseq, row.begin(), row.end(), std::uint64_t{0}, + std::plus{}, [](cell c) { return int(c.visited) != 0u; }); + }); xlog::debug("Running {} from ({:>3},{:>3}) results in {:>3} visited cells", - xmas::lazy_string([&]() -> std::string { - switch (init_beam.towards) { - case direction::right: - return "LEFT "; - case direction::left: - return "RIGHT"; - case direction::up: - return "UP "; - case direction::down: - return "DOWN "; - default: - return std::format("{:>4}?", int(init_beam.towards)); - } - }), - init_beam.row, init_beam.col, x); + xmas::lazy_string([&]() -> std::string { + switch (init_beam.towards) { + case direction::right: + return "LEFT "; + case direction::left: + return "RIGHT"; + case direction::up: + return "UP "; + case direction::down: + return "DOWN "; + default: + return std::format("{:>4}?", int(init_beam.towards)); + } + }), + init_beam.row, init_beam.col, x); return x; } @@ -296,32 +296,27 @@ std::uint64_t Day16::part1() { std::uint64_t Day16::part2() { xmas::views::text_matrix map(this->input); -// Iterate over all rows, entering from left and right every iteration + // Iterate over all rows, entering from left and right every iteration xmas::views::iota rows(map.nrows()); - auto h = std::transform_reduce( - std::execution::par_unseq, rows.begin(), rows.end(), std::uint64_t{0}, - xmas::max{}, [&map](std::size_t row) { - auto from_left = solve(map, {.towards = direction::right, + auto h = std::transform_reduce(std::execution::par_unseq, rows.begin(), rows.end(), + std::uint64_t{0}, xmas::max{}, [&map](std::size_t row) { + auto from_left = + solve(map, {.towards = direction::right, .row = std::ptrdiff_t(row), .col = -1}); + auto from_right = solve(map, {.towards = direction::left, .row = std::ptrdiff_t(row), - .col = -1}); - auto from_right = solve(map, {.towards = direction::left, - .row = std::ptrdiff_t(row), - .col = std::ptrdiff_t(map.ncols())}); - return std::max(from_left, from_right); - }); - -// Iterate over all columns, entering from above and below every iteration + .col = std::ptrdiff_t(map.ncols())}); + return std::max(from_left, from_right); + }); + + // Iterate over all columns, entering from above and below every iteration xmas::views::iota cols(map.ncols()); - auto v = std::transform_reduce( - std::execution::par_unseq, cols.begin(), cols.end(), std::uint64_t{0}, - xmas::max{}, [&map](std::size_t col) { - auto from_above = solve(map, {.towards = direction::down, - .row = -1, - .col = std::ptrdiff_t(col)}); - auto from_below = solve(map, {.towards = direction::up, - .row = std::ptrdiff_t(map.nrows()), - .col = std::ptrdiff_t(col)}); - return std::max(from_above, from_below); - }); + auto v = std::transform_reduce(std::execution::par_unseq, cols.begin(), cols.end(), + std::uint64_t{0}, xmas::max{}, [&map](std::size_t col) { + auto from_above = + solve(map, {.towards = direction::down, .row = -1, .col = std::ptrdiff_t(col)}); + auto from_below = solve(map, + {.towards = direction::up, .row = std::ptrdiff_t(map.nrows()), .col = std::ptrdiff_t(col)}); + return std::max(from_above, from_below); + }); return std::max(h, v); } \ No newline at end of file diff --git a/2023/solvelib/16/day16.hpp b/2023/solvelib/16/day16.hpp index 5adc4f4..085eef1 100644 --- a/2023/solvelib/16/day16.hpp +++ b/2023/solvelib/16/day16.hpp @@ -4,8 +4,9 @@ class Day16 : public xmas::solution { public: - int day() override { return 16; } - void load() override; + int day() override { + return 16; + } public: std::uint64_t part1() override; diff --git a/2023/xmaslib/matrix/text_matrix.cpp b/2023/xmaslib/matrix/text_matrix.cpp index d176a9e..b67cd1b 100644 --- a/2023/xmaslib/matrix/text_matrix.cpp +++ b/2023/xmaslib/matrix/text_matrix.cpp @@ -9,18 +9,18 @@ namespace xmas { namespace views { -std::pair -text_matrix::dimensions(std::string_view input) { +std::pair text_matrix::dimensions( + std::string_view input) { const std::size_t ncols = - 1 + std::size_t(std::find(input.cbegin(), input.cend(), '\n') - - input.cbegin()); + 1 + + std::size_t(std::find(input.cbegin(), input.cend(), '\n') - input.cbegin()); const std::size_t nrows = input.size() / ncols; xlog::debug("Detected {} rows and {} columns", nrows, ncols); return std::make_pair(nrows, ncols); } -text_matrix::text_matrix(std::string &data) : text(data) { +text_matrix::text_matrix(std::string& data) : text(data) { auto [nrows, ncols] = dimensions(text); this->n_rows = nrows; this->n_cols = ncols - 1; // Skip \n @@ -30,13 +30,13 @@ view text_matrix::row(std::size_t i) { assert(i < n_rows); const std::size_t pos = i * (n_rows + 1); return {text.begin() + static_cast(pos), - text.begin() + static_cast(pos + n_cols)}; + text.begin() + static_cast(pos + n_cols)}; } strided text_matrix::col(std::size_t j) { assert(j < n_cols); - return {text.begin() + static_cast(j), text.end(), - n_cols + 1}; + return { + text.begin() + static_cast(j), text.end(), n_cols + 1}; } char text_matrix::at(std::size_t i, std::size_t j) const { @@ -45,7 +45,7 @@ char text_matrix::at(std::size_t i, std::size_t j) const { return text[i * (n_cols + 1) + j]; } -char &text_matrix::at(std::size_t i, std::size_t j) { +char& text_matrix::at(std::size_t i, std::size_t j) { assert(i < n_rows); assert(j < n_cols); return text[i * (n_cols + 1) + j]; diff --git a/2023/xmaslib/registry/registry.cpp b/2023/xmaslib/registry/registry.cpp index d6392df..4066ce9 100644 --- a/2023/xmaslib/registry/registry.cpp +++ b/2023/xmaslib/registry/registry.cpp @@ -4,13 +4,14 @@ namespace xmas { std::map> solutions{}; -std::map> const ®istered_solutions() noexcept { +std::map> const& + registered_solutions() noexcept { return solutions; } namespace internal { -std::map> ®istered_solutions() noexcept { +std::map>& registered_solutions() noexcept { return solutions; } diff --git a/2023/xmaslib/solution/solution.cpp b/2023/xmaslib/solution/solution.cpp index 048de0c..889a2a5 100644 --- a/2023/xmaslib/solution/solution.cpp +++ b/2023/xmaslib/solution/solution.cpp @@ -23,7 +23,7 @@ bool solution::run(bool verbose) noexcept { try { this->load(); - } catch (std::runtime_error &err) { + } catch (std::runtime_error& err) { xlog::error("Load failed with message: {}", err.what()); return false; } catch (...) { @@ -37,12 +37,11 @@ bool solution::run(bool verbose) noexcept { this->time_p1 = std::chrono::high_resolution_clock::now() - start; if (verbose) { - xlog::info( - "result 1: {} ({} μs)", result, - std::chrono::duration_cast(this->time_p1) - .count()); + xlog::info("result 1: {} ({} μs)", result, + std::chrono::duration_cast(this->time_p1) + .count()); } - } catch (std::runtime_error &err) { + } catch (std::runtime_error& err) { xlog::error("Part 1 failed with message: {}", err.what()); success = false; } catch (...) { @@ -56,12 +55,11 @@ bool solution::run(bool verbose) noexcept { this->time_p2 = std::chrono::high_resolution_clock::now() - start; if (verbose) { - xlog::info( - "Result 2: {} ({} μs)", result, - std::chrono::duration_cast(this->time_p2) - .count()); + xlog::info("Result 2: {} ({} μs)", result, + std::chrono::duration_cast(this->time_p2) + .count()); } - } catch (std::runtime_error &err) { + } catch (std::runtime_error& err) { xlog::error("Part 2 failed with message: {}", err.what()); success = false; } catch (...) { @@ -72,13 +70,15 @@ bool solution::run(bool verbose) noexcept { return success; } -void solution::set_input(std::string_view path) { this->data_path = path; } +void solution::set_input(std::string_view path) { + this->data_path = path; +} void solution::load() { std::ifstream f(this->data_path.data()); if (!f) { throw std::runtime_error( - std::format("could not open file {}", this->data_path)); + std::format("could not open file {}", this->data_path)); } std::stringstream buff; @@ -87,6 +87,11 @@ void solution::load() { if (input.size() == 0) { xlog::warning("did not load any data from {}", this->data_path); + return; + } + + if (input.back() != '\n') { + input.push_back('\n'); } }