119 lines
3.1 KiB
Python
119 lines
3.1 KiB
Python
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)
|