/** * Karatsuba algorithm. Fast multiplication of the first n * positions of fa and fb. * PS. Assumes n is power of 2 **/ void Karatsuba(int n, i64 *fa, i64 *fb, Poly &fab) { int m = n/2; /* Base */ if(n <= 16){ fab.assign(2*n - 1, 0); for(int i=0; i<n; i++) for(int j=0; j<n; j++) fab[i+j] += fa[i]*fb[j]; return; } Poly z0, z1, z2, tmp; /* Find z0 and z2 recursively */ Karatsuba(m, &fa[0], &fb[0], z0); Karatsuba(m, &fa[m], &fb[m], z2); /* Find z1 recursively */ Poly fai(m), fbi(m); for(int i=0; i<m; i++){ fai[i] = fa[i] + fa[i+m]; fbi[i] = fb[i] + fb[i+m]; } Karatsuba(m, &fai[0], &fbi[0], z1); for(int i=0; i<z1.size(); i++) z1[i] -= (z0[i] + z2[i]); /* Merge z0, z1 and z2 in fab */ fab.assign(2*m + z2.size(), 0); for(int i=0; i<z0.size(); i++){ fab[i] += z0[i]; fab[i+m] += z1[i]; fab[i+2*m] += z2[i]; } }
// Slow convolution, multiplying two polynomials. // Complexity: O(nu * nv). void conv(const Poly& u, const Poly& v, Poly& w) { int nu = u.size(), nv = v.size(); w.assign(nu + nv - 1, 0.0); for(int i = 0; i < nu; i++) for(int j = 0; j < nv; j++) w[i+j] += u[i] * v[j]; }
// Slow convolution, multiplying two polynomials. // Complexity: O(nu * nv). void conv(const Poly& u, const Poly& v, Poly& w) { if(u.empty() || v.empty()) {w.clear(); return;} int nu = u.size(), nv = v.size(); w.assign(nu + nv - 1, 0); for(int i = 0; i < nu; i++) for(int j = 0; j < nv; j++) w[i+j] ^= (u[i] & v[j]); tidy(w); }
// Slow deconvolution, polynomial dividing. // q returns the quotient, r returns the remainder. void deconv(const Poly& u, const Poly& v, Poly& q, Poly& r) { int n = u.size() - 1; int nv = v.size() - 1; q.assign(n+1, 0.0); r = u; for(int k = n-nv; k >= 0; k--) { q[k] = r[nv+k] / v[nv]; for(int j = nv+k-1; j >= k; j--) r[j] -= q[k] * v[j-k]; } r.resize(nv); }
// Extend GCD void extendEuclid(const Poly& , const Poly& b, Poly& d, Poly& x, Poly& y) { if(b.size() == 0) { d = a; x.assign(1, 1); y.clear(); return; } Poly q, r; deconv(a, b, q, r); extendEuclid(b, r, d, x, y); conv(q, y, r); sub(x, r); std::swap(x, y); }
// Slow deconvolution, polynomial dividing. // q returns the quotient, r returns the remainder. void deconv(const Poly& u, const Poly& v, Poly& q, Poly& r) { int n = u.size() - 1; int nv = v.size() - 1; q.assign(n + 1, 0); r = u; for(int k = n-nv; k >= 0; k--) { q[k] = r[nv+k]; for(int j = nv+k-1; j >= k; j--) r[j] ^= (q[k] & v[j-k]); } r.resize(nv); tidy(q); tidy(r); }