#include #include #include #include #include #include #include using namespace aocpp; namespace { auto FFT(std::size_t const start, std::vector & partial_sums, std::vector & signal) { auto n = signal.size(); partial_sums.clear(); partial_sums.reserve(n+1); partial_sums.push_back(0); std::int32_t acc = 0; for (auto const x : signal) { partial_sums.push_back(acc += x); } for (std::size_t i = 0; i < n; i++) { std::int32_t acc = 0; auto step = i+start+1; for (std::size_t offset = i; offset < n; offset += 4*step) { acc += partial_sums[std::min(n, offset + 1*step)] - partial_sums[std::min(n, offset + 0*step)] + partial_sums[std::min(n, offset + 2*step)] - partial_sums[std::min(n, offset + 3*step)]; } signal[i] = std::abs(acc % 10); } } auto Parse(std::istream & in) -> std::vector { std::string line; std::getline(in, line); std::vector result; result.reserve(line.size()); for (auto const c : line) { result.push_back(c - '0'); } return result; } auto Compute( std::vector signal, std::size_t iterations, std::size_t offset = 0 ) -> std::string { std::vector partial_sums; for (std::size_t i = 0; i < iterations; i++) { FFT(offset, partial_sums, signal); } signal.resize(8); std::string result; for (auto const x : signal) { result += char(x + '0'); } return result; } auto Part2(std::vector const& input) -> std::string { // Compute offset from first 7 digits of the input std::size_t offset = 0; for (int i = 0; i < 7; i++) { offset = offset * 10 + input[i]; } // Allocate the storage for 10,000 copies of the input starting at the given offset std::vector signal; std::size_t size2 = 10000 * input.size() - offset; signal.reserve(size2); // Populate the massively copied signal vector for (std::size_t i = 0, j = offset % input.size(); i < size2; i++) { signal.push_back(input[j++]); if (j == input.size()) j = 0; } return Compute(signal, 100, offset); } } // namespace TEST_SUITE("documented examples") { std::istringstream in; TEST_CASE("part 1") { in = std::istringstream{"12345678"}; auto signal = Parse(in); REQUIRE(Compute(signal, 1) == "48226158"); REQUIRE(Compute(signal, 2) == "34040438"); REQUIRE(Compute(signal, 3) == "03415518"); REQUIRE(Compute(signal, 4) == "01029498"); in = std::istringstream{"80871224585914546619083218645595"}; REQUIRE(Compute(Parse(in), 100) == "24176176"); in = std::istringstream{"19617804207202209144916044189917"}; REQUIRE(Compute(Parse(in), 100) == "73745418"); in = std::istringstream{"69317163492948606335995924319873"}; REQUIRE(Compute(Parse(in), 100) == "52432133"); } TEST_CASE("part 2") { in = std::istringstream{"03036732577212944063491565474664"}; REQUIRE(Part2(Parse(in)) == "84462026"); in = std::istringstream{"02935109699940807407585447034323"}; REQUIRE(Part2(Parse(in)) == "78725270"); in = std::istringstream{"03081770884921959731165446850517"}; REQUIRE(Part2(Parse(in)) == "53553731"); } } auto Main(std::istream & in, std::ostream & out) -> void { auto const input = Parse(in); out << "Part 1: " << Compute(input, 100, 0) << std::endl; out << "Part 2: " << Part2(input) << std::endl; }