#include #include #include #include #include #include #include #include #include "doctest.h" namespace day2 { const std::string sample = "11-22,95-115,998-1012,1188511880-1188511890,222220-222224,1698522-1698528," "446443-446449,38593856-38593862,565653-565659,824824821-824824827," "2121212118-2121212124"; struct Range { uint64_t start; uint64_t end; }; } // namespace day2 template<> struct std::formatter : std::formatter { static auto format(const day2::Range &i, std::format_context &ctx) { return std::format_to(ctx.out(), "Range[start: {}, end: {}]", i.start, i.end); } }; namespace day2 { Range parse_range(std::string_view s) { std::string_view start; std::string_view end; for (size_t i = 0; i < s.size(); i++) { const char c = s[i]; if (c == '-') { start = {s.data(), i}; end = {s.data() + i + 1, s.size() - start.size()}; } } Range range{}; uint64_t parsed; std::from_chars(start.data(), start.end(), parsed); range.start = parsed; std::from_chars(end.data(), end.end(), parsed); range.end = parsed; return range; } size_t find_divisor(const uint64_t n) { for (size_t i = 1;; i++) { size_t div = n / std::pow(10, i); if (div == 0) { return std::pow(10, i / 2); } } assert(0 && "Unreachable"); } uint64_t count_repetitions(const Range range) { uint64_t total = 0; for (uint64_t n = range.start; n <= range.end; n++) { auto div = find_divisor(n); auto left = n / div; auto right = n % div; if (left == right) total++; } return total; } uint64_t sum_repetitions(const Range range) { uint64_t total = 0; for (uint64_t n = range.start; n <= range.end; n++) { const auto div = find_divisor(n); const auto left = n / div; const auto right = n % div; if (left == right) total += n; } return total; } uint64_t solve_first(std::string_view input) { uint64_t result = 0; for (const auto range: std::views::split(input, ',') | std::views::transform([](auto s) { return parse_range(std::string_view(s)); })) { result += sum_repetitions(range); } return result; } uint64_t sum_repetitions_2(const Range range, std::string &buf) { uint64_t total = 0; for (uint64_t n = range.start; n <= range.end; n++) { std::format_to(std::back_inserter(buf), "{}", n); size_t size = buf.size(); for (int i = 0; i < size; ++i) { if (size % i != 0 || size / i == 1) continue; std::string_view first_partition(buf.data(), i); bool all_equal = true; for (int j = i; j < size; j += i) { std::string_view partition(buf.data() + j, i); if (partition != first_partition) { all_equal = false; break; } } if (all_equal) { total += n; break; } } buf.clear(); } return total; } uint64_t solve_second(std::string_view input) { std::string buf; buf.reserve(100); uint64_t result = 0; for (const auto range: std::views::split(input, ',') | std::views::transform([](auto s) { return parse_range(std::string_view(s)); })) { result += sum_repetitions_2(range, buf); buf.clear(); } return result; } TEST_SUITE_BEGIN("Day 2"); TEST_CASE("Can parse input") { auto range = parse_range("11-22"); CHECK(range.start == 11); CHECK(range.end == 22); } TEST_CASE("Solve first sample") { CHECK(solve_first(sample) == 1227775554); } TEST_CASE("Solve first input") { std::ifstream fs("day2input.txt"); std::stringstream ss{}; ss << fs.rdbuf(); CHECK(solve_first(ss.str()) == 21898734247); } TEST_CASE("Find divisor works") { CHECK(find_divisor(11) == 10); CHECK(find_divisor(1221) == 100); CHECK(find_divisor(222222) == 1000); } TEST_CASE("Count repetition works") { CHECK(count_repetitions({.start = 11, .end = 22}) == 2); CHECK(count_repetitions({.start = 1188511880, .end = 1188511890}) == 1); } TEST_CASE("sum_repetitions_2") { std::string buf; buf.reserve(100); CHECK(sum_repetitions_2({95, 115}, buf) == 210); buf.clear(); CHECK(sum_repetitions_2({998, 1012}, buf) == 999 + 1010); } TEST_CASE("Solves second sample") { CHECK(solve_second(sample) == 4174379265); } TEST_CASE( "Solves second input" * doctest::skip(true) ) { std::ifstream fs("day2input.txt"); std::stringstream ss{}; ss << fs.rdbuf(); CHECK(solve_second(ss.view()) == 28915664389); } TEST_SUITE_END(); } // namespace day2