#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; }