void mul_aux(mat_ZZ_p& X, const mat_ZZ_p& A, const mat_ZZ_p& B) { long n = A.NumRows(); long l = A.NumCols(); long m = B.NumCols(); if (l != B.NumRows()) Error("matrix mul: dimension mismatch"); X.SetDims(n, m); long i, j, k; ZZ acc, tmp; for (i = 1; i <= n; i++) { for (j = 1; j <= m; j++) { clear(acc); for(k = 1; k <= l; k++) { mul(tmp, rep(A(i,k)), rep(B(k,j))); add(acc, acc, tmp); } conv(X(i,j), acc); } } }
void mul(mat_ZZ_p& X, const mat_ZZ_p& A, const mat_ZZ_p& B) { long n = A.NumRows(); long l = A.NumCols(); long m = B.NumCols(); if (l != B.NumRows()) LogicError("matrix mul: dimension mismatch"); if (NTL_USE_MM_MATMUL && n >= 24 && l >= 24 && m >= 24) multi_modular_mul(X, A, B); else plain_mul(X, A, B); }
void sub(mat_ZZ_p& X, const mat_ZZ_p& A, const mat_ZZ_p& B) { long n = A.NumRows(); long m = A.NumCols(); if (B.NumRows() != n || B.NumCols() != m) Error("matrix sub: dimension mismatch"); X.SetDims(n, m); long i, j; for (i = 1; i <= n; i++) for (j = 1; j <= m; j++) sub(X(i,j), A(i,j), B(i,j)); }
void transpose(mat_ZZ_p& X, const mat_ZZ_p& A) { long n = A.NumRows(); long m = A.NumCols(); long i, j; if (&X == & A) { if (n == m) for (i = 1; i <= n; i++) for (j = i+1; j <= n; j++) swap(X(i, j), X(j, i)); else { mat_ZZ_p tmp; tmp.SetDims(m, n); for (i = 1; i <= n; i++) for (j = 1; j <= m; j++) tmp(j, i) = A(i, j); X.kill(); X = tmp; } } else { X.SetDims(m, n); for (i = 1; i <= n; i++) for (j = 1; j <= m; j++) X(j, i) = A(i, j); } }
void power(mat_ZZ_p& X, const mat_ZZ_p& A, const ZZ& e) { if (A.NumRows() != A.NumCols()) Error("power: non-square matrix"); if (e == 0) { ident(X, A.NumRows()); return; } mat_ZZ_p T1, T2; long i, k; k = NumBits(e); T1 = A; for (i = k-2; i >= 0; i--) { sqr(T2, T1); if (bit(e, i)) mul(T1, T2, A); else T1 = T2; } if (e < 0) inv(X, T1); else X = T1; }
void to_mat_ZZ_p_crt_rep(mat_ZZ_p_crt_rep& X, const mat_ZZ_p& A) { long n = A.NumRows(); long m = A.NumCols(); const MatPrime_crt_helper& H = get_MatPrime_crt_helper_info(); long nprimes = H.GetNumPrimes(); if (NTL_OVERFLOW(nprimes, CRT_BLK, 0)) ResourceError("overflow"); // this is pretty academic X.rep.SetLength(nprimes); for (long k = 0; k < nprimes; k++) X.rep[k].SetDims(n, m); ZZ_pContext context; context.save(); bool seq = (double(n)*double(m)*H.GetCost() < PAR_THRESH); // FIXME: right now, we just partition the rows, but if // #cols > #rows, we should perhaps partition the cols NTL_GEXEC_RANGE(seq, n, first, last) NTL_IMPORT(n) NTL_IMPORT(m) NTL_IMPORT(nprimes) context.restore(); MatPrime_crt_helper_scratch scratch; Vec<MatPrime_residue_t> remainders_store; remainders_store.SetLength(nprimes*CRT_BLK); MatPrime_residue_t *remainders = remainders_store.elts(); for (long i = first; i < last; i++) { const ZZ_p *a = A[i].elts(); long jj = 0; for (; jj <= m-CRT_BLK; jj += CRT_BLK) { for (long j = 0; j < CRT_BLK; j++) reduce(H, rep(a[jj+j]), remainders + j*nprimes, scratch); for (long k = 0; k < nprimes; k++) { MatPrime_residue_t *x = X.rep[k][i].elts(); for (long j = 0; j < CRT_BLK; j++) x[jj+j] = remainders[j*nprimes+k]; } } if (jj < m) { for (long j = 0; j < m-jj; j++) reduce(H, rep(a[jj+j]), remainders + j*nprimes, scratch); for (long k = 0; k < nprimes; k++) { MatPrime_residue_t *x = X.rep[k][i].elts(); for (long j = 0; j < m-jj; j++) x[jj+j] = remainders[j*nprimes+k]; } } } NTL_GEXEC_RANGE_END }
NTL_START_IMPL void add(mat_ZZ_p& X, const mat_ZZ_p& A, const mat_ZZ_p& B) { long n = A.NumRows(); long m = A.NumCols(); if (B.NumRows() != n || B.NumCols() != m) LogicError("matrix add: dimension mismatch"); X.SetDims(n, m); long i, j; for (i = 1; i <= n; i++) for (j = 1; j <= m; j++) add(X(i,j), A(i,j), B(i,j)); }
void plain_mul_transpose_aux(mat_ZZ_p& X, const mat_ZZ_p& A, const mat_ZZ_p& B) { long n = A.NumRows(); long l = A.NumCols(); long m = B.NumRows(); if (l != B.NumCols()) LogicError("matrix mul: dimension mismatch"); X.SetDims(n, m); ZZ_pContext context; context.save(); long sz = ZZ_p::ModulusSize(); bool seq = (double(n)*double(l)*double(m)*double(sz)*double(sz) < PAR_THRESH); NTL_GEXEC_RANGE(seq, m, first, last) NTL_IMPORT(n) NTL_IMPORT(l) NTL_IMPORT(m) context.restore(); long i, j, k; ZZ acc, tmp; for (j = first; j < last; j++) { const ZZ_p *B_col = B[j].elts(); for (i = 0; i < n; i++) { clear(acc); for (k = 0; k < l; k++) { mul(tmp, rep(A[i][k]), rep(B_col[k])); add(acc, acc, tmp); } conv(X[i][j], acc); } } NTL_GEXEC_RANGE_END }
void negate(mat_ZZ_p& X, const mat_ZZ_p& A) { long n = A.NumRows(); long m = A.NumCols(); X.SetDims(n, m); long i, j; for (i = 1; i <= n; i++) for (j = 1; j <= m; j++) negate(X(i,j), A(i,j)); }
void mul(mat_ZZ_p& X, const mat_ZZ_p& A, long b_in) { NTL_ZZ_pRegister(b); b = b_in; long n = A.NumRows(); long m = A.NumCols(); X.SetDims(n, m); long i, j; for (i = 0; i < n; i++) for (j = 0; j < m; j++) mul(X[i][j], A[i][j], b); }
void multi_modular_mul_transpose(mat_ZZ_p& X, const mat_ZZ_p& A, const mat_ZZ_p_crt_rep& B) { long l = A.NumCols(); if (l != B.rep[0].NumCols()) LogicError("matrix mul: dimension mismatch"); if (l > NTL_MatPrimeLimit) ResourceError("matrix mul: dimension too large"); mat_ZZ_p_crt_rep x, a; to_mat_ZZ_p_crt_rep(a, A); mul_transpose(x, a, B); from_mat_ZZ_p_crt_rep(x, X); }
long IsDiag(const mat_ZZ_p& A, long n, const ZZ_p& d) { if (A.NumRows() != n || A.NumCols() != n) return 0; long i, j; for (i = 1; i <= n; i++) for (j = 1; j <= n; j++) if (i != j) { if (!IsZero(A(i, j))) return 0; } else { if (A(i, j) != d) return 0; } return 1; }
long IsIdent(const mat_ZZ_p& A, long n) { if (A.NumRows() != n || A.NumCols() != n) return 0; long i, j; for (i = 1; i <= n; i++) for (j = 1; j <= n; j++) if (i != j) { if (!IsZero(A(i, j))) return 0; } else { if (!IsOne(A(i, j))) return 0; } return 1; }
void Testbench::initMatrix(mat_ZZ_p & m, int int_size) { ZZ_p n; n.init(to_ZZ("56563749237498237498237493299999937129873912873981273129842343242399999799995999937492374982374982374932999999371298739128739812731298423432423999997999959999374923749823749823749329999993712987391287398127312984234324239999979999599993749237498237498237493299999937129873912873981273129842343242399999799995")); switch (int_size) { case 64: n = to_ZZ_p(to_ZZ("9999999999999999995")); break; case 128: n = to_ZZ_p(to_ZZ("99993749237498237498237493299999999995")); break; case 256: n = to_ZZ_p(to_ZZ("99993749237498237498237493299999937129873912873981273129842343242399999799995")); break; case 512: n = to_ZZ_p(to_ZZ("9999374923749823749823749329999993712987391287398127312984234324239999979999599993749237498237498237493299999937129873912873981273129842343242399999799995")); break; case 1024: n = to_ZZ_p(to_ZZ("99993749237498237498237493299999937129873912873981273129842343242399999799995999937492374982374982374932999999371298739128739812731298423432423999997999959999374923749823749823749329999993712987391287398127312984234324239999979999599993749237498237498237493299999937129873912873981273129842343242399999799995")); break; case 2048: n = to_ZZ_p(to_ZZ("9999374923749823749823749329999993712987391287398127312984234324239999979999599993749237498237498237493299999937129873912873981273129842343242399999799995999937492374982374982374932999999371298739128739812731298423432423999997999959999374923749823749823749329999993712987391287398127312984234324239999979999599993749237498237498237493299999937129873912873981273129842343242399999799995999937492374982374982374932999999371298739128739812731298423432423999997999959999374923749823749823749329999993712987391287398127312984234324239999979999599993749237498237498237493299999937129873912873981273129842343242399999799995")); break; case 4096: n = to_ZZ_p(to_ZZ("99993749237498237498237493299999937129873912873981273129842343242399999799995999937492374982374982374932999999371298739128739812731298423432423999997999959999374923749823749823749329999993712987391287398127312984234324239999979999599993749237498237498237493299999937129873912873981273129842343242399999799995999937492374982374982374932999999371298739128739812731298423432423999997999959999374923749823749823749329999993712987391287398127312984234324239999979999599993749237498237498237493299999937129873912873981273129842343242399999799995999937492374982374982374932999999371298739128739812731298423432423999997999959999374923749823749823749329999993712987391287398127312984234324239999979999599993749237498237498237493299999937129873912873981273129842343242399999799995999937492374982374982374932999999371298739128739812731298423432423999997999959999374923749823749823749329999993712987391287398127312984234324239999979999599993749237498237498237493299999937129873912873981273129842343242399999799995999937492374982374982374932999999371298739128739812731298423432423999997999959999374923749823749823749329999993712987391287398127312984234324239999979999599993749237498237498237493299999937129873912873981273129842343242399999799995")); break; case 8192: n = to_ZZ_p(to_ZZ("9999374923749823749823749329999993712987391287398127312984234324239999979999599993749237498237498237493299999937129873912873981273129842343242399999799995999937492374982374982374932999999371298739128739812731298423432423999997999959999374923749823749823749329999993712987391287398127312984234324239999979999599993749237498237498237493299999937129873912873981273129842343242399999799995999937492374982374982374932999999371298739128739812731298423432423999997999959999374923749823749823749329999993712987391287398127312984234324239999979999599993749237498237498237493299999937129873912873981273129842343242399999799995999937492374982374982374932999999371298739128739812731298423432423999997999959999374923749823749823749329999993712987391287398127312984234324239999979999599993749237498237498237493299999937129873912873981273129842343242399999799995999937492374982374982374932999999371298739128739812731298423432423999997999959999374923749823749823749329999993712987391287398127312984234324239999979999599993749237498237498237493299999937129873912873981273129842343242399999799995999937492374982374982374932999999371298739128739812731298423432423999997999959999374923749823749823749329999993712987391287398127312984234324239999979999599993749237498237498237493299999937129873912873981273129842343242399999799995999937492374982374982374932999999371298739128739812731298423432423999997999959999374923749823749823749329999993712987391287398127312984234324239999979999599993749237498237498237493299999937129873912873981273129842343242399999799995999937492374982374982374932999999371298739128739812731298423432423999997999959999374923749823749823749329999993712987391287398127312984234324239999979999599993749237498237498237493299999937129873912873981273129842343242399999799995999937492374982374982374932999999371298739128739812731298423432423999997999959999374923749823749823749329999993712987391287398127312984234324239999979999599993749237498237498237493299999937129873912873981273129842343242399999799995999937492374982374982374932999999371298739128739812731298423432423999997999959999374923749823749823749329999993712987391287398127312984234324239999979999599993749237498237498237493299999937129873912873981273129842343242399999799995999937492374982374982374932999999371298739128739812731298423432423999997999959999374923749823749823749329999993712987391287398127312984234324239999979999599993749237498237498237493299999937129873912873981273129842343242399999799995")); break; default: n = to_ZZ_p(to_ZZ("9999999999999999995")); break; } for (int i = 1; i <= m.NumRows(); i++) { for (int j = 1; j <= m.NumCols(); j++) { m(i,j) = n; } } }
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); } }
long NumCols() const { return body.NumCols(); }
static void solve_impl(ZZ_p& d, vec_ZZ_p& X, const mat_ZZ_p& A, const vec_ZZ_p& b, bool trans) { long n = A.NumRows(); if (A.NumCols() != n) LogicError("solve: nonsquare matrix"); if (b.length() != n) LogicError("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()); if (trans) for (j = 0; j < n; j++) M[i][j] = rep(A[j][i]); else for (j = 0; j < n; j++) M[i][j] = rep(A[i][j]); 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); }
NTL_START_IMPL void CharPoly(ZZ_pX& f, const mat_ZZ_p& M) { long n = M.NumRows(); if (M.NumCols() != n) Error("CharPoly: nonsquare matrix"); if (n == 0) { set(f); return; } ZZ_p t; if (n == 1) { SetX(f); negate(t, M(1, 1)); SetCoeff(f, 0, t); return; } mat_ZZ_p H; H = M; long i, j, m; ZZ_p u, t1; for (m = 2; m <= n-1; m++) { i = m; while (i <= n && IsZero(H(i, m-1))) i++; if (i <= n) { t = H(i, m-1); if (i > m) { swap(H(i), H(m)); // swap columns i and m for (j = 1; j <= n; j++) swap(H(j, i), H(j, m)); } for (i = m+1; i <= n; i++) { div(u, H(i, m-1), t); for (j = m; j <= n; j++) { mul(t1, u, H(m, j)); sub(H(i, j), H(i, j), t1); } for (j = 1; j <= n; j++) { mul(t1, u, H(j, i)); add(H(j, m), H(j, m), t1); } } } } vec_ZZ_pX F; F.SetLength(n+1); ZZ_pX T; T.SetMaxLength(n); set(F[0]); for (m = 1; m <= n; m++) { LeftShift(F[m], F[m-1], 1); mul(T, F[m-1], H(m, m)); sub(F[m], F[m], T); set(t); for (i = 1; i <= m-1; i++) { mul(t, t, H(m-i+1, m-i)); mul(t1, t, H(m-i, m)); mul(T, F[m-i-1], t1); sub(F[m], F[m], T); } } f = F[n]; }
void kernel(mat_ZZ_p& X, const mat_ZZ_p& A) { long m = A.NumRows(); long n = A.NumCols(); mat_ZZ_p M; long r; transpose(M, A); r = gauss(M); X.SetDims(m-r, m); long i, j, k, s; ZZ t1, t2; ZZ_p T3; vec_long D; D.SetLength(m); for (j = 0; j < m; j++) D[j] = -1; vec_ZZ_p inverses; inverses.SetLength(m); j = -1; for (i = 0; i < r; i++) { do { j++; } while (IsZero(M[i][j])); D[j] = i; inv(inverses[j], M[i][j]); } for (k = 0; k < m-r; k++) { vec_ZZ_p& v = X[k]; long pos = 0; for (j = m-1; j >= 0; j--) { if (D[j] == -1) { if (pos == k) set(v[j]); else clear(v[j]); pos++; } else { i = D[j]; clear(t1); for (s = j+1; s < m; s++) { mul(t2, rep(v[s]), rep(M[i][s])); add(t1, t1, t2); } conv(T3, t1); mul(T3, T3, inverses[j]); negate(v[j], T3); } } } }
long gauss(mat_ZZ_p& M) { return gauss(M, M.NumCols()); }
long gauss(mat_ZZ_p& M_in, long w) { long k, l; long i, j; long pos; ZZ t1, t2, t3; ZZ *x, *y; long n = M_in.NumRows(); long m = M_in.NumCols(); if (w < 0 || w > m) Error("gauss: bad args"); 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(m, t1.size()); for (j = 0; j < m; j++) { M[i][j] = rep(M_in[i][j]); } } l = 0; for (k = 0; k < w && l < n; k++) { pos = -1; for (i = l; i < n; i++) { rem(t1, M[i][k], p); M[i][k] = t1; if (pos == -1 && !IsZero(t1)) { pos = i; } } if (pos != -1) { swap(M[pos], M[l]); InvMod(t3, M[l][k], p); NegateMod(t3, t3, p); for (j = k+1; j < m; j++) { rem(M[l][j], M[l][j], p); } for (i = l+1; i < n; i++) { // M[i] = M[i] + M[l]*M[i,k]*t3 MulMod(t1, M[i][k], t3, p); clear(M[i][k]); x = M[i].elts() + (k+1); y = M[l].elts() + (k+1); for (j = k+1; j < m; j++, x++, y++) { // *x = *x + (*y)*t1 mul(t2, *y, t1); add(t2, t2, *x); *x = t2; } } l++; } } for (i = 0; i < n; i++) for (j = 0; j < m; j++) conv(M_in[i][j], M[i][j]); return l; }
void inv(ZZ_p& d, mat_ZZ_p& X, const mat_ZZ_p& A) { long n = A.NumRows(); if (A.NumCols() != n) Error("inv: nonsquare matrix"); if (n == 0) { set(d); X.SetDims(0, 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(2*n, t1.size()); for (j = 0; j < n; j++) { M[i][j] = rep(A[i][j]); clear(M[i][n+j]); } set(M[i][n+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 < 2*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 < 2*n; j++, x++, y++) { // *x = *x + (*y)*t1 mul(t2, *y, t1); add(*x, *x, t2); } } } else { clear(d); return; } } X.SetDims(n, n); for (k = 0; k < n; k++) { for (i = n-1; i >= 0; i--) { clear(t1); for (j = i+1; j < n; j++) { mul(t2, rep(X[j][k]), M[i][j]); add(t1, t1, t2); } sub(t1, t1, M[i][n+k]); conv(X[i][k], t1); } } conv(d, det); }
void determinant(ZZ_p& d, const mat_ZZ_p& M_in) { long k, n; long i, j; long pos; ZZ t1, t2; ZZ *x, *y; const ZZ& p = ZZ_p::modulus(); n = M_in.NumRows(); if (M_in.NumCols() != n) Error("determinant: nonsquare matrix"); if (n == 0) { set(d); return; } vec_ZZVec M; sqr(t1, p); mul(t1, t1, n); M.SetLength(n); for (i = 0; i < n; i++) { M[i].SetSize(n, t1.size()); for (j = 0; j < n; j++) M[i][j] = rep(M_in[i][j]); } 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; } } conv(d, det); }
NTL_START_IMPL // ******************** Matrix Multiplication ************************ #ifdef NTL_HAVE_LL_TYPE #define NTL_USE_MM_MATMUL (1) #else #define NTL_USE_MM_MATMUL (0) #endif #define PAR_THRESH (40000.0) // *********************** Plain Matrix Multiplication *************** void plain_mul_aux(mat_ZZ_p& X, const mat_ZZ_p& A, const mat_ZZ_p& B) { long n = A.NumRows(); long l = A.NumCols(); long m = B.NumCols(); if (l != B.NumRows()) LogicError("matrix mul: dimension mismatch"); X.SetDims(n, m); ZZ_pContext context; context.save(); long sz = ZZ_p::ModulusSize(); bool seq = (double(n)*double(l)*double(m)*double(sz)*double(sz) < PAR_THRESH); NTL_GEXEC_RANGE(seq, m, first, last) NTL_IMPORT(n) NTL_IMPORT(l) NTL_IMPORT(m) context.restore(); long i, j, k; ZZ acc, tmp; vec_ZZ_p B_col; B_col.SetLength(l); for (j = first; j < last; j++) { for (k = 0; k < l; k++) B_col[k] = B[k][j]; for (i = 0; i < n; i++) { clear(acc); for (k = 0; k < l; k++) { mul(tmp, rep(A[i][k]), rep(B_col[k])); add(acc, acc, tmp); } conv(X[i][j], acc); } } NTL_GEXEC_RANGE_END }