from lib import get_data, str_to_ints, mod_inverse from math import gcd data = get_data(__file__) deck = list(range(10007)) # part 1 for line in data.splitlines(): if "new stack" in line: deck = list(reversed(deck)) elif "cut" in line: (n,) = str_to_ints(line) deck = deck[n:] + deck[:n] elif "increment" in line: new_deck = [-1] * len(deck) pos = 0 (n,) = str_to_ints(line) deck = list(reversed(deck)) while deck: new_deck[pos] = deck.pop() pos = (pos + n) % len(new_deck) deck = new_deck else: assert False print(deck.index(2019)) len = 10007 orig_index = 2019 # figure out how to reverse... index = orig_index for line in data.splitlines(): if "new stack" in line: new_index = len - (index + 1) rev_index = -(new_index - len + 1) assert rev_index == index index = new_index elif "cut" in line: (cut,) = str_to_ints(line) cut = (len + cut) % len if index >= cut: new_index = index - cut else: new_index = (len - cut) + index rev_index = (new_index + cut) % len assert rev_index == index index = new_index # calculate index from new_index and store in rev_index elif "increment" in line: (n,) = str_to_ints(line) assert gcd(n, len) == 1 new_index = (n * index) % len m = mod_inverse(n, len) rev_index = (new_index * m) % len assert rev_index == index index = new_index assert index == deck.index(2019) # check that reverse approach works for line in reversed(data.splitlines()): if "new stack" in line: index = -(index - len + 1) elif "cut" in line: (cut,) = str_to_ints(line) cut = (len + cut) % len index = (index + cut) % len elif "increment" in line: (n,) = str_to_ints(line) assert gcd(n, len) == 1 m = mod_inverse(n, len) index = (index * m) % len assert index == orig_index lines = list(reversed(data.splitlines())) # new length of deck len = 119315717514047 # get expression for one loop using sympy from sympy import symbols, simplify index = symbols("index") expr = index for line in lines: if "new stack" in line: # index = -(index - len + 1) expr = -(expr - len + 1) elif "cut" in line: (cut,) = str_to_ints(line) cut = (len + cut) % len # index = (index + cut) % len expr = expr + cut elif "increment" in line: (n,) = str_to_ints(line) assert gcd(n, len) == 1 m = mod_inverse(n, len) # index = (index * m) % len expr = expr * m # we can see that expression is in the form (a - b * i) % m expr = simplify(expr % len) coeff_dict = expr.args[0].as_coefficients_dict() a = coeff_dict[1] b = -coeff_dict[index] # math n_shuffles = 101741582076661 r0 = 2020 m = len p = (-b) % m p_t = pow(p, n_shuffles, m) inv_b1 = mod_inverse(b + 1, m) term1 = (p_t * r0) % m term2 = (a * (1 - p_t) * inv_b1) % m r_t = (term1 + term2) % m print(r_t)