diff --git a/2019/22.cpp b/2019/22.cpp new file mode 100644 index 0000000..6faae77 --- /dev/null +++ b/2019/22.cpp @@ -0,0 +1,165 @@ +#include +#include +#include +#include +#include + +#include + +#include +using namespace aocpp; + +template struct overloaded : Ts... { using Ts::operator()...; }; +template overloaded(Ts...) -> overloaded; + +namespace { + +template +class ZMod { + mpz_class value; + +public: + ZMod() : value {} {} + ZMod(long value) : ZMod{mpz_class{value}} {} + ZMod(mpz_class value) { + mpz_mod_ui(this->value.get_mpz_t(), value.get_mpz_t(), Mod); + } + + auto operator+(ZMod const& rhs) const -> ZMod { + return {value + rhs.value}; + } + + auto operator-() const -> ZMod { + return {-value}; + } + + auto operator-(ZMod const& rhs) const -> ZMod { + return {value - rhs.value}; + } + + auto operator*(ZMod const& rhs) const -> ZMod { + return {value * rhs.value}; + } + + auto inverse() const -> ZMod { + mpz_class m{Mod}; + ZMod result; + mpz_invert(result.value.get_mpz_t(), value.get_mpz_t(), m.get_mpz_t()); + return result; + } + + auto friend operator<<(std::ostream & out, ZMod const& x) -> std::ostream & { + return out << x.value.get_ui(); +} +}; + +template +struct LinearFunction { + Rep scale; + Rep offset; + + auto operator()(Rep x) const -> Rep { + return scale * x + offset; + } + + auto inverse() const -> LinearFunction { + // y = a*x+b + // y-b = a*x + // a' * (y-b) = x + auto scale_ = scale.inverse(); + return {scale_, - scale_ * offset}; + } +}; + +template +auto Compose( + LinearFunction const& f, + LinearFunction const& g +) -> LinearFunction +{ + return {f.scale * g.scale, f.offset + f.scale * g.offset}; +} + +template +using Shuffle = LinearFunction>; + +template +auto Cut(long cards) -> Shuffle { + return Shuffle{1, -cards}; +} + +template +auto Rev() -> Shuffle { + return Shuffle{-1, -1}; +} + +template +auto Deal(long inc) -> Shuffle { + return Shuffle{inc, 0}; +} + +struct DoCut { long n; }; +struct DoRev {}; +struct DoDeal { long n; }; +using Instruction = std::variant; + +auto Parse(std::istream & in) -> std::vector { + std::vector result; + std::string line; + while (std::getline(in, line)) { + if (line.starts_with("cut")) { + auto n = std::stol(line.substr(4)); + result.push_back(DoCut{n}); + } else if (line == "deal into new stack") { + result.push_back(DoRev{}); + } else if (line.starts_with("deal with increment")) { + auto n = std::stol(line.substr(20)); + result.push_back(DoDeal{n}); + } else { + throw std::runtime_error{"bad input command"}; + } + } + return result; +} + +template +auto FollowInstructions(std::vector const& instructions) -> Shuffle { + Shuffle shuffle = {1,0}; + for (auto const& instruction : instructions) { + auto step = std::visit(overloaded { + [](DoRev) { return Rev(); }, + [](DoCut c) { return Cut(c.n); }, + [](DoDeal c) { return Deal(c.n); } + }, instruction); + shuffle = Compose(step, shuffle); + } + return shuffle; +} + +template +auto Many(Shuffle shuffle, unsigned long n) -> Shuffle { + if (n == 0) { + return Shuffle{1,0}; + } else if (n == 1) { + return shuffle; + } else if (n & 1) { + auto shuffle2 = Many(shuffle, n/2); + return Compose(shuffle, Compose(shuffle2, shuffle2)); + } else { + auto shuffle2 = Many(shuffle, n/2); + return Compose(shuffle2, shuffle2); + } +} + +} // namespace + +auto main(int argc, char** argv) -> int { + auto instructions = Parse(Startup(argc, argv)); + + auto shuffle1 = FollowInstructions<10007>(instructions); + std::cout << "Part 1: " << shuffle1(2019) << std::endl; + + auto shuffle2 = FollowInstructions<119315717514047>(instructions); + shuffle2 = Many(shuffle2, 101741582076661); + std::cout << "Part 2: " << shuffle2.inverse()(2020) << std::endl; +} diff --git a/2019/CMakeLists.txt b/2019/CMakeLists.txt index 7a24aef..6a34a30 100644 --- a/2019/CMakeLists.txt +++ b/2019/CMakeLists.txt @@ -49,6 +49,9 @@ target_link_libraries(18 aocpp) add_executable(20 20.cpp) target_link_libraries(20 aocpp) +add_executable(22 22.cpp) +target_link_libraries(22 aocpp PkgConfig::GMP) + add_executable(23 23.cpp) target_link_libraries(23 aocpp intcode) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2600a25..071ae73 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,6 +14,9 @@ else() message(WARNING "IPO is not supported: ${output}") endif() +find_package(PkgConfig) +pkg_check_modules(GMP REQUIRED IMPORTED_TARGET gmp) + add_subdirectory(lib) add_subdirectory(intcode) add_subdirectory(2019)