#include #include #include #include #include #include #include using namespace zmod; using namespace aocpp; template struct overloaded : Ts... { using Ts::operator()...; }; template overloaded(Ts...) -> overloaded; namespace { 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(std::istream & in, std::ostream & out) -> void { auto const instructions {Parse(in)}; auto const shuffle1 {FollowInstructions<10007>(instructions)}; out << "Part 1: " << shuffle1(2019) << std::endl; auto shuffle2 {FollowInstructions<119315717514047>(instructions)}; shuffle2 = Many(shuffle2, 101741582076661); shuffle2 = shuffle2.inverse(); out << "Part 2: " << shuffle2(2020) << std::endl; }