void ProjectPowers(vec_ZZ_p& x, const vec_ZZ_p& a, long k, const ZZ_pXArgument& H, const ZZ_pXModulus& F) { long n = F.n; if (a.length() > n || k < 0) LogicError("ProjectPowers: bad args"); if (NTL_OVERFLOW(k, 1, 0)) ResourceError("ProjectPowers: excessive args"); long m = H.H.length()-1; long l = (k+m-1)/m - 1; ZZ_pXMultiplier M; build(M, H.H[m], F); vec_ZZ_p s(INIT_SIZE, n); s = a; StripZeroes(s); x.SetLength(k); for (long i = 0; i <= l; i++) { long m1 = min(m, k-i*m); ZZ_p* w = &x[i*m]; for (long j = 0; j < m1; j++) InnerProduct(w[j], H.H[j].rep, s); if (i < l) UpdateMap(s, s, M, F); } }
static void StripZeroes(vec_ZZ_p& x) { long n = x.length(); while (n > 0 && IsZero(x[n-1])) n--; x.SetLength(n); }
void negate(vec_ZZ_p& x, const vec_ZZ_p& a) { long n = a.length(); x.SetLength(n); long i; for (i = 0; i < n; i++) negate(x[i], a[i]); }
void sub(vec_ZZ_p& x, const vec_ZZ_p& a, const vec_ZZ_p& b) { long n = a.length(); if (b.length() != n) LogicError("vector sub: dimension mismatch"); x.SetLength(n); long i; for (i = 0; i < n; i++) sub(x[i], a[i], b[i]); }
void mul(vec_ZZ_p& x, const vec_ZZ_p& a, long b_in) { NTL_ZZ_pRegister(b); b = b_in; long n = a.length(); x.SetLength(n); long i; for (i = 0; i < n; i++) mul(x[i], a[i], b); }
void FindRoots(vec_ZZ_p& x, const ZZ_pX& ff) { ZZ_pX f = ff; if (!IsOne(LeadCoeff(f))) Error("FindRoots: bad args"); x.SetMaxLength(deg(f)); x.SetLength(0); RecFindRoots(x, f); }
void conv(vec_ZZ_p& x, const vec_ZZ& a) { long i, n; n = a.length(); x.SetLength(n); ZZ_p* xp = x.elts(); const ZZ* ap = a.elts(); for (i = 0; i < n; i++) conv(xp[i], ap[i]); }
void InnerProduct(ZZ_p& x, const vec_ZZ_p& a, const vec_ZZ_p& b) { long n = min(a.length(), b.length()); long i; NTL_ZZRegister(accum); NTL_ZZRegister(t); clear(accum); for (i = 0; i < n; i++) { mul(t, rep(a[i]), rep(b[i])); add(accum, accum, t); } conv(x, accum); }
void eval(vec_ZZ_p& b, const ZZ_pX& f, const vec_ZZ_p& a) // naive algorithm: repeats Horner { if (&b == &f.rep) { vec_ZZ_p bb; eval(bb, f, a); b = bb; return; } long m = a.length(); b.SetLength(m); long i; for (i = 0; i < m; i++) eval(b[i], f, a[i]); }
void clear(vec_ZZ_p& x) { long n = x.length(); long i; for (i = 0; i < n; i++) clear(x[i]); }
void InnerProduct(ZZ_pX& x, const vec_ZZ_p& v, long low, long high, const vec_ZZ_pX& H, long n, ZZVec& t) { NTL_ZZRegister(s); long i, j; for (j = 0; j < n; j++) clear(t[j]); high = min(high, v.length()-1); for (i = low; i <= high; i++) { const vec_ZZ_p& h = H[i-low].rep; long m = h.length(); const ZZ& w = rep(v[i]); for (j = 0; j < m; j++) { mul(s, w, rep(h[j])); add(t[j], t[j], s); } } x.rep.SetLength(n); for (j = 0; j < n; j++) conv(x.rep[j], t[j]); x.normalize(); }
static void ComputeTraceVec(vec_ZZ_p& S, const ZZ_pXModulus& F) { if (!F.UseFFT) { PlainTraceVec(S, F.f); return; } long i; long n = F.n; FFTRep R; ZZ_pX P, g; g.rep.SetLength(n-1); for (i = 1; i < n; i++) mul(g.rep[n-i-1], F.f.rep[n-i], i); g.normalize(); ToFFTRep(R, g, F.l); mul(R, R, F.HRep); FromFFTRep(P, R, n-2, 2*n-4); S.SetLength(n); S[0] = n; for (i = 1; i < n; i++) negate(S[i], coeff(P, n-1-i)); }
void PlainUpdateMap(vec_ZZ_p& xx, const vec_ZZ_p& a, const ZZ_pX& b, const ZZ_pX& f) { long n = deg(f); long i, m; if (IsZero(b)) { xx.SetLength(0); return; } m = n-1 - deg(b); vec_ZZ_p x(INIT_SIZE, n); for (i = 0; i <= m; i++) InnerProduct(x[i], a, b.rep, i); if (deg(b) != 0) { ZZ_pX c(INIT_SIZE, n); LeftShift(c, b, m); for (i = m+1; i < n; i++) { MulByXMod(c, c, f); InnerProduct(x[i], a, c.rep); } } xx = x; }
void PlainTraceVec(vec_ZZ_p& S, const ZZ_pX& ff) { if (deg(ff) <= 0) LogicError("TraceVec: bad args"); ZZ_pX f; f = ff; MakeMonic(f); long n = deg(f); S.SetLength(n); if (n == 0) return; long k, i; ZZ acc, t; ZZ_p t1; S[0] = n; for (k = 1; k < n; k++) { mul(acc, rep(f.rep[n-k]), k); for (i = 1; i < k; i++) { mul(t, rep(f.rep[n-i]), rep(S[k-i])); add(acc, acc, t); } conv(t1, acc); negate(S[k], t1); } }
void conv(vec_ZZ& x, const vec_ZZ_p& a) { long n = a.length(); x.SetLength(n); long i; for (i = 0; i < n; i++) x[i] = rep(a[i]); }
void VectorCopy(vec_ZZ_p& x, const vec_ZZ_p& a, long n) { if (n < 0) LogicError("VectorCopy: negative length"); if (NTL_OVERFLOW(n, 1, 0)) ResourceError("overflow in VectorCopy"); long m = min(n, a.length()); x.SetLength(n); long i; for (i = 0; i < m; i++) x[i] = a[i]; for (i = m; i < n; i++) clear(x[i]); }
void ProjectPowers(vec_ZZ_p& x, const vec_ZZ_p& a, long k, const ZZ_pX& h, const ZZ_pXModulus& F) { if (a.length() > F.n || k < 0) LogicError("ProjectPowers: bad args"); if (k == 0) { x.SetLength(0); return; } long m = SqrRoot(k); ZZ_pXArgument H; build(H, h, F, m); ProjectPowers(x, a, k, H, F); }
void FastTraceVec(vec_ZZ_p& S, const ZZ_pX& f) { long n = deg(f); if (n <= 0) LogicError("FastTraceVec: bad args"); if (n == 0) { S.SetLength(0); return; } if (n == 1) { S.SetLength(1); set(S[0]); return; } long i; ZZ_pX f1; f1.rep.SetLength(n-1); for (i = 0; i <= n-2; i++) f1.rep[i] = f.rep[n-i]; f1.normalize(); ZZ_pX f2; f2.rep.SetLength(n-1); for (i = 0; i <= n-2; i++) mul(f2.rep[i], f.rep[n-1-i], i+1); f2.normalize(); ZZ_pX f3; InvTrunc(f3, f1, n-1); MulTrunc(f3, f3, f2, n-1); S.SetLength(n); S[0] = n; for (i = 1; i < n; i++) negate(S[i], coeff(f3, i-1)); }
long IsZero(const vec_ZZ_p& a) { long n = a.length(); long i; for (i = 0; i < n; i++) if (!IsZero(a[i])) return 0; return 1; }
void MinPolySeq(ZZ_pX& h, const vec_ZZ_p& a, long m) { if (m < 0) LogicError("MinPoly: bad args"); if (NTL_OVERFLOW(m, 1, 0)) LogicError("MinPoly: bad args"); if (a.length() < 2*m) LogicError("MinPoly: sequence too short"); if (m > NTL_ZZ_pX_BERMASS_CROSSOVER) GCDMinPolySeq(h, a, m); else BerlekampMassey(h, a, m); }
static void FindFactors(vec_ZZ_pX& factors, const ZZ_pX& f, const ZZ_pX& g, const vec_ZZ_p& roots) { long r = roots.length(); factors.SetMaxLength(r); factors.SetLength(0); RecFindFactors(factors, f, g, roots, 0, r-1); }
void InnerProduct(ZZ_p& x, const vec_ZZ_p& a, const vec_ZZ_p& b, long offset) { if (offset < 0) LogicError("InnerProduct: negative offset"); if (NTL_OVERFLOW(offset, 1, 0)) ResourceError("InnerProduct: offset too big"); long n = min(a.length(), b.length()+offset); long i; NTL_ZZRegister(accum); NTL_ZZRegister(t); clear(accum); for (i = offset; i < n; i++) { mul(t, rep(a[i]), rep(b[i-offset])); add(accum, accum, t); } conv(x, accum); }
// inner product (svec_ZZ and svec_ZZ_p) inline void InnerProduct(ZZ_p& result, const svec_ZZ& a, const vec_ZZ_p& b) { if (a.length()!=b.length()) { cerr<<"InnerProduct() length mismatch\n"; exit(1); } clear(result); long an = a.nvalues(); const svec_ZZ::index_t* ai = a.indices(); const svec_ZZ::value_t* av = a.values(); for (long i=0; i<an; ++i) result += to_ZZ_p(av[i])*b[ai[i]]; }
static void mul_aux(vec_ZZ_p& x, const vec_ZZ_p& a, const mat_ZZ_p& B) { long n = B.NumRows(); long l = B.NumCols(); if (n != a.length()) Error("matrix mul: dimension mismatch"); x.SetLength(l); long i, k; ZZ acc, tmp; for (i = 1; i <= l; i++) { clear(acc); for (k = 1; k <= n; k++) { mul(tmp, rep(a(k)), rep(B(k,i))); add(acc, acc, tmp); } conv(x(i), acc); } }
static void mul_aux(vec_ZZ_p& x, const mat_ZZ_p& A, const vec_ZZ_p& b) { long n = A.NumRows(); long l = A.NumCols(); if (l != b.length()) Error("matrix mul: dimension mismatch"); x.SetLength(n); long i, k; ZZ acc, tmp; for (i = 1; i <= n; i++) { clear(acc); for (k = 1; k <= l; k++) { mul(tmp, rep(A(i,k)), rep(b(k))); add(acc, acc, tmp); } conv(x(i), acc); } }
static void IterFindFactors(vec_ZZ_pX& factors, const ZZ_pX& f, const ZZ_pX& g, const vec_ZZ_p& roots) { long r = roots.length(); long i; ZZ_pX h; factors.SetLength(r); for (i = 0; i < r; i++) { sub(h, g, roots[i]); GCD(factors[i], f, h); } }
static void RecFindRoots(vec_ZZ_p& x, const ZZ_pX& f) { if (deg(f) == 0) return; if (deg(f) == 1) { long k = x.length(); x.SetLength(k+1); negate(x[k], ConstTerm(f)); return; } ZZ_pX h; ZZ_p r; ZZ p1; RightShift(p1, ZZ_p::modulus(), 1); { ZZ_pXModulus F; build(F, f); do { random(r); PowerXPlusAMod(h, r, p1, F); add(h, h, -1); GCD(h, h, f); } while (deg(h) <= 0 || deg(h) == deg(f)); } RecFindRoots(x, h); div(h, f, h); RecFindRoots(x, h); }
void solve(ZZ_p& d, vec_ZZ_p& X, const mat_ZZ_p& A, const vec_ZZ_p& b) { long n = A.NumRows(); if (A.NumCols() != n) Error("solve: nonsquare matrix"); if (b.length() != n) Error("solve: dimension mismatch"); if (n == 0) { set(d); X.SetLength(0); return; } long i, j, k, pos; ZZ t1, t2; ZZ *x, *y; const ZZ& p = ZZ_p::modulus(); vec_ZZVec M; sqr(t1, p); mul(t1, t1, n); M.SetLength(n); for (i = 0; i < n; i++) { M[i].SetSize(n+1, t1.size()); for (j = 0; j < n; j++) M[i][j] = rep(A[j][i]); M[i][n] = rep(b[i]); } ZZ det; set(det); for (k = 0; k < n; k++) { pos = -1; for (i = k; i < n; i++) { rem(t1, M[i][k], p); M[i][k] = t1; if (pos == -1 && !IsZero(t1)) { pos = i; } } if (pos != -1) { if (k != pos) { swap(M[pos], M[k]); NegateMod(det, det, p); } MulMod(det, det, M[k][k], p); // make M[k, k] == -1 mod p, and make row k reduced InvMod(t1, M[k][k], p); NegateMod(t1, t1, p); for (j = k+1; j <= n; j++) { rem(t2, M[k][j], p); MulMod(M[k][j], t2, t1, p); } for (i = k+1; i < n; i++) { // M[i] = M[i] + M[k]*M[i,k] t1 = M[i][k]; // this is already reduced x = M[i].elts() + (k+1); y = M[k].elts() + (k+1); for (j = k+1; j <= n; j++, x++, y++) { // *x = *x + (*y)*t1 mul(t2, *y, t1); add(*x, *x, t2); } } } else { clear(d); return; } } X.SetLength(n); for (i = n-1; i >= 0; i--) { clear(t1); for (j = i+1; j < n; j++) { mul(t2, rep(X[j]), M[i][j]); add(t1, t1, t2); } sub(t1, t1, M[i][n]); conv(X[i], t1); } conv(d, det); }
void interpolate(ZZ_pX& f, const vec_ZZ_p& a, const vec_ZZ_p& b) { long m = a.length(); if (b.length() != m) LogicError("interpolate: vector length mismatch"); if (m == 0) { clear(f); return; } vec_ZZ_p prod; prod = a; ZZ_p t1, t2; long k, i; vec_ZZ_p res; res.SetLength(m); for (k = 0; k < m; k++) { const ZZ_p& aa = a[k]; set(t1); for (i = k-1; i >= 0; i--) { mul(t1, t1, aa); add(t1, t1, prod[i]); } clear(t2); for (i = k-1; i >= 0; i--) { mul(t2, t2, aa); add(t2, t2, res[i]); } inv(t1, t1); sub(t2, b[k], t2); mul(t1, t1, t2); for (i = 0; i < k; i++) { mul(t2, prod[i], t1); add(res[i], res[i], t2); } res[k] = t1; if (k < m-1) { if (k == 0) negate(prod[0], prod[0]); else { negate(t1, a[k]); add(prod[k], t1, prod[k-1]); for (i = k-1; i >= 1; i--) { mul(t2, prod[i], t1); add(prod[i], t2, prod[i-1]); } mul(prod[0], prod[0], t1); } } } while (m > 0 && IsZero(res[m-1])) m--; res.SetLength(m); f.rep = res; }
void BuildFromRoots(ZZ_pX& x, const vec_ZZ_p& a) { long n = a.length(); if (n == 0) { set(x); return; } long k0 = NextPowerOfTwo(NTL_ZZ_pX_FFT_CROSSOVER); long crossover = 1L << k0; if (n <= crossover) { x.rep.SetMaxLength(n+1); x.rep = a; IterBuild(&x.rep[0], n); x.rep.SetLength(n+1); SetCoeff(x, n); return; } long k = NextPowerOfTwo(n); long m = 1L << k; long i, j; long l, width; ZZ_pX b(INIT_SIZE, m+1); b.rep = a; b.rep.SetLength(m+1); for (i = n; i < m; i++) clear(b.rep[i]); set(b.rep[m]); FFTRep R1(INIT_SIZE, k), R2(INIT_SIZE, k); ZZ_p t1, one; set(one); vec_ZZ_p G(INIT_SIZE, crossover), H(INIT_SIZE, crossover); ZZ_p *g = G.elts(); ZZ_p *h = H.elts(); ZZ_p *tmp; for (i = 0; i < m; i+= crossover) { for (j = 0; j < crossover; j++) negate(g[j], b.rep[i+j]); if (k0 > 0) { for (j = 0; j < crossover; j+=2) { mul(t1, g[j], g[j+1]); add(g[j+1], g[j], g[j+1]); g[j] = t1; } } for (l = 1; l < k0; l++) { width = 1L << l; for (j = 0; j < crossover; j += 2*width) mul(&h[j], &g[j], &g[j+width], width); tmp = g; g = h; h = tmp; } for (j = 0; j < crossover; j++) b.rep[i+j] = g[j]; } for (l = k0; l < k; l++) { width = 1L << l; for (i = 0; i < m; i += 2*width) { t1 = b.rep[i+width]; set(b.rep[i+width]); ToFFTRep(R1, b, l+1, i, i+width); b.rep[i+width] = t1; t1 = b.rep[i+2*width]; set(b.rep[i+2*width]); ToFFTRep(R2, b, l+1, i+width, i+2*width); b.rep[i+2*width] = t1; mul(R1, R1, R2); FromFFTRep(&b.rep[i], R1, 0, 2*width-1); sub(b.rep[i], b.rep[i], one); } } x.rep.SetLength(n+1); long delta = m-n; for (i = 0; i <= n; i++) x.rep[i] = b.rep[i+delta]; // no need to normalize }