···11+"""
22+Credit to /u/mcpower_ for explaining the number theory involved in Part 2:
33+https://www.reddit.com/r/adventofcode/comments/ee0rqi/2019_day_22_solutions/fbnkaju/
44+"""
55+66+import fileinput
77+88+99+def solve_part_1(actions):
1010+ CARDS = 10007
1111+ POS = 2019
1212+1313+ for action, n in actions:
1414+ if action == 'deal':
1515+ if n is None:
1616+ POS = (CARDS - 1) - POS
1717+ else:
1818+ POS = (n * POS) % CARDS
1919+ else:
2020+ if POS >= n:
2121+ POS -= n
2222+ else:
2323+ POS = (CARDS - (n - POS))
2424+2525+ return POS
2626+2727+2828+def solve_part_2(actions):
2929+ CARDS = 119315717514047
3030+ SHUFFLES = 101741582076661
3131+3232+ def modinv(n, p):
3333+ """Returns the inverse of n mod p, assuming p is prime."""
3434+ return pow(n, p - 2, p)
3535+3636+ def get_card(offset, increment, i):
3737+ """Returns the ith card in the sequence given its offset and increment."""
3838+ return (offset + i * increment) % CARDS
3939+4040+ def get_sequence(iterations, increment_mul, offset_diff):
4141+ """Returns the final increment and offset after the given number of iterations."""
4242+ increment = pow(increment_mul, iterations, CARDS)
4343+ offset = (offset_diff * (1 - increment) * modinv(1 - increment_mul, CARDS)) % CARDS
4444+ return increment, offset
4545+4646+ # `increment` is the difference between two adajcent numbers
4747+ increment = 1
4848+4949+ # `offset` is the first number in the sequence
5050+ offset = 0
5151+5252+ for action, n in actions:
5353+ if action == 'deal':
5454+ # deal into new stack
5555+ if n is None:
5656+ # The sequence gets reversed...
5757+ increment = (increment * -1) % CARDS
5858+5959+ # ...and shifted one to the left
6060+ offset = (offset + increment) % CARDS
6161+6262+ # deal with increment n
6363+ else:
6464+ increment = (increment * modinv(n, CARDS)) % CARDS
6565+6666+ # cut n
6767+ else:
6868+ offset = (offset + (n * increment)) % CARDS
6969+7070+7171+ inc, off = get_sequence(SHUFFLES, increment, offset)
7272+ return get_card(off, inc, 2020)
7373+7474+7575+actions = []
7676+7777+for line in fileinput.input():
7878+ line = line.strip()
7979+ inst = line.split()
8080+ verb = inst[0]
8181+ if verb == 'cut':
8282+ actions.append(('cut', int(inst[1])))
8383+8484+ else:
8585+ if inst[1] == 'into':
8686+ actions.append(('deal', None))
8787+ else:
8888+ actions.append(('deal', int(inst[-1])))
8989+9090+9191+print "Position of card 2019:", solve_part_1(actions)
9292+print "Card ending up in position 2020:", solve_part_2(actions)