62 lines
1.5 KiB
Python
Executable file
62 lines
1.5 KiB
Python
Executable file
#!/usr/bin/env python
|
|
|
|
import math
|
|
import sys
|
|
from typing import NamedTuple
|
|
|
|
|
|
class Disc(NamedTuple):
|
|
num: int
|
|
positions: int
|
|
start_offset: int
|
|
|
|
@classmethod
|
|
def from_str(cls, input: str) -> "Disc":
|
|
input = input.removeprefix("Disc #").removesuffix(".")
|
|
split_input = input.split()
|
|
return Disc(
|
|
int(split_input[0]),
|
|
int(split_input[2]),
|
|
int(split_input[-1]),
|
|
)
|
|
|
|
|
|
def solve(input: str) -> int:
|
|
def parse(input: str) -> list[Disc]:
|
|
return [Disc.from_str(line) for line in input.splitlines()]
|
|
|
|
# https://rosettacode.org/wiki/Chinese_remainder_theorem#Python_3.6
|
|
def mul_inv(a: int, b: int) -> int:
|
|
b0 = b
|
|
x0, x1 = 0, 1
|
|
if b == 1:
|
|
return 1
|
|
while a > 1:
|
|
q = a // b
|
|
a, b = b, a % b
|
|
x0, x1 = x1 - q * x0, x0
|
|
if x1 < 0:
|
|
x1 += b0
|
|
return x1
|
|
|
|
def chinese_remainder(residue_mapping: dict[int, int]) -> int:
|
|
res = 0
|
|
prod = math.prod(residue_mapping)
|
|
for n_i, a_i in residue_mapping.items():
|
|
p = prod // n_i
|
|
res += a_i * mul_inv(p, n_i) * p
|
|
return res % prod
|
|
|
|
discs = parse(input)
|
|
discs.append(Disc(len(discs) + 1, 11, 0))
|
|
residues = {disc.positions: -(disc.start_offset + disc.num) for disc in discs}
|
|
return chinese_remainder(residues)
|
|
|
|
|
|
def main() -> None:
|
|
input = sys.stdin.read()
|
|
print(solve(input))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|