.
def linear_recurrence_kitamasa(coef, seed, n, mod):
def polymul(a, b):
ret = [0] * (len(a) + len(b) - 1)
for i, a_i in enumerate(a):
for j, b_j in enumerate(b):
ret[i + j] += a_i * b_j
return [x % MOD for x in ret]
def polymod(a, b):
ret = a[:]
for i in reversed(range(len(b) - 1, len(a))):
k = i - len(b) + 1
for j, b_j in enumerate(b):
ret[k + j] -= ret[i] * b_j
ret = [x % MOD for x in ret]
return ret[:len(b)]
d = [1]
xn = [0, 1]
f = [-c for c in reversed(coef)] + [1]
while n:
if n & 1:
d = polymod(polymul(d, xn), f)
n >>= 1
xn = polymod(polymul(xn, xn), f)
return sum(s_i * d_i for s_i, d_i in zip(seed, d)) % mod
def linear_recurrence(coef, seed, n, mod):
d = len(coef)
convolution_func = _naive_convolution if d <= FFT_THRE else fft.multiply
q = [1] + [(-x) % mod for x in coef]
p = [x % mod for x in convolution_func(seed, q)[:d]]
while n:
r = q[:]
r[1::2] = [mod - x for x in q[1::2]]
p = [x % mod for x in convolution_func(p, r)[n & 1 :: 2]]
q = [x % mod for x in convolution_func(q, r)[::2]]
n >>= 1
return p[0]
def _naive_convolution(a, b):
ret = [0] * (len(a) + len(b) - 1)
for i, a_i in enumerate(a):
for j, b_j in enumerate(b):
ret[i + j] += a_i * b_j
return ret
seeds = compute_seeds()
coefs = combinatorics.find_linear_recurrence(seeds, MOD)
answer = combinatorics.linear_recurrence(coefs, seeds, n, MOD)