reflector_t makeM4Reflector(const reflector_t& thin, const reflector_t& greek, char offset, int ring) { reflector_t reflector = { thin.name + ":" + greek.name + ":" + offset }; offset -= 'A'; offset = static_cast<char> (SubMod(offset, ring - 1)); // make the inverse of the mappings int inverse_thin[26], inverse_greek[26]; for (int i = 0; i < 26; i++) { for (int j = 0; j < 26; j++) { if (thin.map[j] == i) inverse_thin[i] = j; if (greek.map[j] == i) inverse_greek[i] = j; } } // work out effective mapping for (int i = 0; i < 26; i++) { // enter greek rotor int ch = greek.map[AddMod(i, offset)]; // through the thin reflector ch = AddMod(inverse_thin[SubMod(ch, offset)], offset); // and back out the greek rotor reflector.map[i] = static_cast<char> (SubMod(inverse_greek[ch], offset)); } return reflector; }
bool intVecCRT(vec_ZZ& vp, const ZZ& p, const zzvec& vq, long q) { long pInv = InvMod(rem(p,q), q); // p^{-1} mod q long n = min(vp.length(),vq.length()); long q_over_2 = q/2; ZZ tmp; long vqi; mulmod_precon_t pqInv = PrepMulModPrecon(pInv, q); for (long i=0; i<n; i++) { conv(vqi, vq[i]); // convert to single precision long vq_minus_vp_mod_q = SubMod(vqi, rem(vp[i],q), q); long delta_times_pInv = MulModPrecon(vq_minus_vp_mod_q, pInv, q, pqInv); if (delta_times_pInv > q_over_2) delta_times_pInv -= q; mul(tmp, delta_times_pInv, p); // tmp = [(vq_i-vp_i)*p^{-1}]_q * p vp[i] += tmp; } // other entries (if any) are 0 mod q for (long i=vq.length(); i<vp.length(); i++) { long minus_vp_mod_q = NegateMod(rem(vp[i],q), q); long delta_times_pInv = MulModPrecon(minus_vp_mod_q, pInv, q, pqInv); if (delta_times_pInv > q_over_2) delta_times_pInv -= q; mul(tmp, delta_times_pInv, p); // tmp = [(vq_i-vp_i)*p^{-1}]_q * p vp[i] += tmp; } return (vp.length()==vq.length()); }
long CRT(vec_ZZ& gg, ZZ& a, const vec_zz_p& G) { long n = gg.length(); if (G.length() != n) Error("CRT: vector length mismatch"); long p = zz_p::modulus(); ZZ new_a; mul(new_a, a, p); long a_inv; a_inv = rem(a, p); a_inv = InvMod(a_inv, p); long p1; p1 = p >> 1; ZZ a1; RightShift(a1, a, 1); long p_odd = (p & 1); long modified = 0; long h; ZZ g; long i; for (i = 0; i < n; i++) { if (!CRTInRange(gg[i], a)) { modified = 1; rem(g, gg[i], a); if (g > a1) sub(g, g, a); } else g = gg[i]; h = rem(g, p); h = SubMod(rep(G[i]), h, p); h = MulMod(h, a_inv, p); if (h > p1) h = h - p; if (h != 0) { modified = 1; if (!p_odd && g > 0 && (h == p1)) MulSubFrom(g, a, h); else MulAddTo(g, a, h); } gg[i] = g; } a = new_a; return modified; }
// Sets the prime defining the field for the curve and stores certain values void Icart::setPrime(ZZ* p) { //ZZ_p::init(*p); // Icart hash function uses 1/3 root, which is equivalent to (2p-1)/3 exp = MulMod( SubMod( MulMod(ZZ(2), *p, *p), ZZ(1), *p), InvMod(ZZ(3),*p), *p); // Store inverse values to be used later ts = inv(ZZ_p(27)); th = inv(ZZ_p(3)); }
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"); long p = zz_p::modulus(); x.SetLength(n); const zz_p *ap = a.elts(); const zz_p *bp = b.elts(); zz_p *xp = x.elts(); long i; for (i = 0; i < n; i++) xp[i].LoopHole() = SubMod(rep(ap[i]), rep(bp[i]), p); }
ZZ ASTSub::eval(Environment &env) { pair<VarInfo,VarInfo> p = getTypes(env); VarInfo leftInfo = p.first; VarInfo rightInfo = p.second; bool isElmt = (leftInfo.type == VarInfo::ELEMENT || rightInfo.type == VarInfo::ELEMENT); // integers/exponents can just be subtracted, but if there is an // exponent we want to retain the group information if (isElmt == 0){ return lhs->eval(env) - rhs->eval(env); } else { const Group* lGroup = env.groups.at(leftInfo.group); const Group* rGroup = env.groups.at(rightInfo.group); const Group* retGroup = (lGroup != 0 ? lGroup : rGroup); assert(retGroup); ZZ mod = retGroup->getModulus(); return SubMod(lhs->eval(env), rhs->eval(env), mod); } }
// Проверка ЭЦП void verifysign ( ZZ &u, ZZ &r, ZZ &s, ZZ &H, ZZ &y, ZZ &p, ZZ &q, ZZ &a) { ZZ v, z1, z2; v = PowerMod(H, (q-2), q); z1 = (s * v) % q; z2 = (SubMod(q, r%q, q) * v) % q; u = ((PowerMod(a, z1, p) * PowerMod(y, z2, p)) % p) % q; cout << "\nCheck sign\n"; cout << "\nv = \n"; show_dec_in_hex (v, N); cout << endl; cout << "\nz1 = \n"; show_dec_in_hex (z1, N); cout << endl; cout << "\nz2 = \n"; show_dec_in_hex (z2, N); cout << endl; cout << "\nu = \n"; show_dec_in_hex (u, N); cout << endl; cout << endl; if ( u == r ) cout << "u = r Sign is OK" << endl; else cout << "Sign is FAILED" << endl; }
long CRT(mat_ZZ& gg, ZZ& a, const mat_zz_p& G) { long n = gg.NumRows(); long m = gg.NumCols(); if (G.NumRows() != n || G.NumCols() != m) Error("CRT: dimension mismatch"); long p = zz_p::modulus(); ZZ new_a; mul(new_a, a, p); long a_inv; a_inv = rem(a, p); a_inv = InvMod(a_inv, p); long p1; p1 = p >> 1; ZZ a1; RightShift(a1, a, 1); long p_odd = (p & 1); long modified = 0; long h; ZZ g; long i, j; for (i = 0; i < n; i++) { for (j = 0; j < m; j++) { if (!CRTInRange(gg[i][j], a)) { modified = 1; rem(g, gg[i][j], a); if (g > a1) sub(g, g, a); } else g = gg[i][j]; h = rem(g, p); h = SubMod(rep(G[i][j]), h, p); h = MulMod(h, a_inv, p); if (h > p1) h = h - p; if (h != 0) { modified = 1; if (!p_odd && g > 0 && (h == p1)) MulSubFrom(g, a, h); else MulAddTo(g, a, h); } gg[i][j] = g; } } a = new_a; return modified; }
void* worker(void* arg) { State& state = *((State*) arg); long k = state.k; #ifdef USE_THREADS pthread_mutex_lock(&state.lock); #endif while (1) { if (state.next * BLOCK_SIZE < state.bound) { // need to generate more modular data long next = state.next++; #ifdef USE_THREADS pthread_mutex_unlock(&state.lock); #endif Item* item = new Item; mpz_set_ui(item->modulus, 1); mpz_set_ui(item->residue, 0); for (long p = max(5, state.table->next_prime(next * BLOCK_SIZE)); p < state.bound && p < (next+1) * BLOCK_SIZE; p = state.table->next_prime(p)) { if (k % (p-1) == 0) continue; // compute B_k mod p long b = bern_modp(p, k); // CRT into running total long x = MulMod(SubMod(b, mpz_fdiv_ui(item->residue, p), p), InvMod(mpz_fdiv_ui(item->modulus, p), p), p); mpz_addmul_ui(item->residue, item->modulus, x); mpz_mul_ui(item->modulus, item->modulus, p); } #ifdef USE_THREADS pthread_mutex_lock(&state.lock); #endif state.items.insert(item); } else { // all modular data has been generated if (state.items.size() <= 1) { // no more CRTs for this thread to perform #ifdef USE_THREADS pthread_mutex_unlock(&state.lock); #endif return NULL; } // CRT two smallest items together Item* item1 = *(state.items.begin()); state.items.erase(state.items.begin()); Item* item2 = *(state.items.begin()); state.items.erase(state.items.begin()); #ifdef USE_THREADS pthread_mutex_unlock(&state.lock); #endif Item* item3 = CRT(item1, item2); delete item1; delete item2; #ifdef USE_THREADS pthread_mutex_lock(&state.lock); #endif state.items.insert(item3); } } }
void FFT(long* A, const long* a, long k, long q, const long* root, FFTMultipliers& tab) // performs a 2^k-point convolution modulo q { if (k <= 1) { if (k == 0) { A[0] = a[0]; return; } if (k == 1) { long a0 = AddMod(a[0], a[1], q); long a1 = SubMod(a[0], a[1], q); A[0] = a0; A[1] = a1; return; } } // assume k > 1 if (k > tab.MaxK) PrecompFFTMultipliers(k, q, root, tab); NTL_THREAD_LOCAL static Vec<long> AA_store; AA_store.SetLength(1L << k); long *AA = AA_store.elts(); BitReverseCopy(AA, a, k); long n = 1L << k; long s, m, m_half, m_fourth, i, j, t, u, t1, u1, tt, tt1; // s = 1 for (i = 0; i < n; i += 2) { t = AA[i + 1]; u = AA[i]; AA[i] = AddMod(u, t, q); AA[i+1] = SubMod(u, t, q); } for (s = 2; s < k; s++) { m = 1L << s; m_half = 1L << (s-1); m_fourth = 1L << (s-2); const long* wtab = tab.wtab_precomp[s].elts(); const mulmod_precon_t *wqinvtab = tab.wqinvtab_precomp[s].elts(); for (i = 0; i < n; i+= m) { long *AA0 = &AA[i]; long *AA1 = &AA[i + m_half]; #if (NTL_PIPELINE) // pipelining: seems to be faster t = AA1[0]; u = AA0[0]; t1 = MulModPrecon(AA1[1], wtab[1], q, wqinvtab[1]); u1 = AA0[1]; for (j = 0; j < m_half-2; j += 2) { long a02 = AA0[j+2]; long a03 = AA0[j+3]; long a12 = AA1[j+2]; long a13 = AA1[j+3]; long w2 = wtab[j+2]; long w3 = wtab[j+3]; mulmod_precon_t wqi2 = wqinvtab[j+2]; mulmod_precon_t wqi3 = wqinvtab[j+3]; tt = MulModPrecon(a12, w2, q, wqi2); long b00 = AddMod(u, t, q); long b10 = SubMod(u, t, q); tt1 = MulModPrecon(a13, w3, q, wqi3); long b01 = AddMod(u1, t1, q); long b11 = SubMod(u1, t1, q); AA0[j] = b00; AA1[j] = b10; AA0[j+1] = b01; AA1[j+1] = b11; t = tt; u = a02; t1 = tt1; u1 = a03; } AA0[j] = AddMod(u, t, q); AA1[j] = SubMod(u, t, q); AA0[j + 1] = AddMod(u1, t1, q); AA1[j + 1] = SubMod(u1, t1, q); } #else for (j = 0; j < m_half; j += 2) { const long a00 = AA0[j]; const long a01 = AA0[j+1]; const long a10 = AA1[j]; const long a11 = AA1[j+1]; const long w0 = wtab[j]; const long w1 = wtab[j+1]; const mulmod_precon_t wqi0 = wqinvtab[j]; const mulmod_precon_t wqi1 = wqinvtab[j+1]; const long tt = MulModPrecon(a10, w0, q, wqi0); const long uu = a00; const long b00 = AddMod(uu, tt, q); const long b10 = SubMod(uu, tt, q); const long tt1 = MulModPrecon(a11, w1, q, wqi1); const long uu1 = a01; const long b01 = AddMod(uu1, tt1, q); const long b11 = SubMod(uu1, tt1, q); AA0[j] = b00; AA0[j+1] = b01; AA1[j] = b10; AA1[j+1] = b11; } } #endif }
void FFT(long* A, const long* a, long k, long q, const long* root) // performs a 2^k-point convolution modulo q { if (k <= 1) { if (k == 0) { A[0] = a[0]; return; } if (k == 1) { long a0 = AddMod(a[0], a[1], q); long a1 = SubMod(a[0], a[1], q); A[0] = a0; A[1] = a1; return; } } // assume k > 1 NTL_THREAD_LOCAL static Vec<long> wtab_store; NTL_THREAD_LOCAL static Vec<mulmod_precon_t> wqinvtab_store; NTL_THREAD_LOCAL static Vec<long> AA_store; wtab_store.SetLength(1L << (k-2)); wqinvtab_store.SetLength(1L << (k-2)); AA_store.SetLength(1L << k); long * NTL_RESTRICT wtab = wtab_store.elts(); mulmod_precon_t * NTL_RESTRICT wqinvtab = wqinvtab_store.elts(); long *AA = AA_store.elts(); double qinv = 1/((double) q); wtab[0] = 1; wqinvtab[0] = PrepMulModPrecon(1, q, qinv); BitReverseCopy(AA, a, k); long n = 1L << k; long s, m, m_half, m_fourth, i, j, t, u, t1, u1, tt, tt1; long w; mulmod_precon_t wqinv; // s = 1 for (i = 0; i < n; i += 2) { t = AA[i + 1]; u = AA[i]; AA[i] = AddMod(u, t, q); AA[i+1] = SubMod(u, t, q); } for (s = 2; s < k; s++) { m = 1L << s; m_half = 1L << (s-1); m_fourth = 1L << (s-2); w = root[s]; wqinv = PrepMulModPrecon(w, q, qinv); // prepare wtab... if (s == 2) { wtab[1] = MulModPrecon(wtab[0], w, q, wqinv); wqinvtab[1] = PrepMulModPrecon(wtab[1], q, qinv); } else { // some software pipelining i = m_half-1; j = m_fourth-1; wtab[i-1] = wtab[j]; wqinvtab[i-1] = wqinvtab[j]; wtab[i] = MulModPrecon(wtab[i-1], w, q, wqinv); i -= 2; j --; for (; i >= 0; i -= 2, j --) { long wp2 = wtab[i+2]; long wm1 = wtab[j]; wqinvtab[i+2] = PrepMulModPrecon(wp2, q, qinv); wtab[i-1] = wm1; wqinvtab[i-1] = wqinvtab[j]; wtab[i] = MulModPrecon(wm1, w, q, wqinv); } wqinvtab[1] = PrepMulModPrecon(wtab[1], q, qinv); } for (i = 0; i < n; i+= m) { long * NTL_RESTRICT AA0 = &AA[i]; long * NTL_RESTRICT AA1 = &AA[i + m_half]; t = AA1[0]; u = AA0[0]; t1 = MulModPrecon(AA1[1], w, q, wqinv); u1 = AA0[1]; for (j = 0; j < m_half-2; j += 2) { long a02 = AA0[j+2]; long a03 = AA0[j+3]; long a12 = AA1[j+2]; long a13 = AA1[j+3]; long w2 = wtab[j+2]; long w3 = wtab[j+3]; mulmod_precon_t wqi2 = wqinvtab[j+2]; mulmod_precon_t wqi3 = wqinvtab[j+3]; tt = MulModPrecon(a12, w2, q, wqi2); long b00 = AddMod(u, t, q); long b10 = SubMod(u, t, q); t = tt; u = a02; tt1 = MulModPrecon(a13, w3, q, wqi3); long b01 = AddMod(u1, t1, q); long b11 = SubMod(u1, t1, q); t1 = tt1; u1 = a03; AA0[j] = b00; AA1[j] = b10; AA0[j+1] = b01; AA1[j+1] = b11; } AA0[j] = AddMod(u, t, q); AA1[j] = SubMod(u, t, q); AA0[j + 1] = AddMod(u1, t1, q); AA1[j + 1] = SubMod(u1, t1, q); } } // s == k...special case m = 1L << s; m_half = 1L << (s-1); m_fourth = 1L << (s-2); w = root[s]; wqinv = PrepMulModPrecon(w, q, qinv); // j = 0, 1 t = AA[m_half]; u = AA[0]; t1 = MulModPrecon(AA[1+ m_half], w, q, wqinv); u1 = AA[1]; A[0] = AddMod(u, t, q); A[m_half] = SubMod(u, t, q); A[1] = AddMod(u1, t1, q); A[1 + m_half] = SubMod(u1, t1, q); for (j = 2; j < m_half; j += 2) { t = MulModPrecon(AA[j + m_half], wtab[j >> 1], q, wqinvtab[j >> 1]); u = AA[j]; t1 = MulModPrecon(AA[j + 1+ m_half], wtab[j >> 1], q, wqinvtab[j >> 1]); t1 = MulModPrecon(t1, w, q, wqinv); u1 = AA[j + 1]; A[j] = AddMod(u, t, q); A[j + m_half] = SubMod(u, t, q); A[j + 1] = AddMod(u1, t1, q); A[j + 1 + m_half] = SubMod(u1, t1, q); } }
// Note: poly is passed by value, not by reference, so the calling routine // keeps its original polynomial long evalPolyTopLevel(ZZX poly, long x, long p, long k=0) { if (verbose) cerr << "\n* evalPolyTopLevel: p="<<p<<", x="<<x<<", poly="<<poly; if (deg(poly)<=2) { // nothing to optimize here if (deg(poly)<1) return to_long(coeff(poly, 0)); DynamicPtxtPowers babyStep(x, p, deg(poly)); long ret = simplePolyEval(poly, babyStep, p); totalDepth = babyStep.getDepth(deg(poly)); return ret; } // How many baby steps: set k~sqrt(n/2), rounded up/down to a power of two // FIXME: There may be some room for optimization here: it may be possible // to choose k as something other than a power of two and still maintain // optimal depth, in principle we can try all possible values of k between // the two powers of two and choose the one that goves the least number // of multiplies, conditioned on minimum depth. if (k<=0) { long kk = (long) sqrt(deg(poly)/2.0); k = 1L << NextPowerOfTwo(kk); // heuristic: if k>>kk then use a smaler power of two if ((k==16 && deg(poly)>167) || (k>16 && k>(1.44*kk))) k /= 2; } cerr << ", k="<<k; long n = divc(deg(poly),k); // deg(p) = k*n +delta if (verbose) cerr << ", n="<<n<<endl; DynamicPtxtPowers babyStep(x, p, k); long x2k = babyStep.getPower(k); // Special case when deg(p)>k*(2^e -1) if (n==(1L << NextPowerOfTwo(n))) { // n is a power of two DynamicPtxtPowers giantStep(x2k, p, n/2, babyStep.getDepth(k)); if (verbose) cerr << "babyStep="<<babyStep<<", giantStep="<<giantStep<<endl; long ret = degPowerOfTwo(poly, k, babyStep, giantStep, p, totalDepth); if (verbose) { cerr << " degPowerOfTwo("<<poly<<") returns "<<ret<<", depth="<<totalDepth<<endl; if (ret != polyEvalMod(poly,babyStep[0], p)) { cerr << " ## recursive call failed, ret="<<ret<<"!=" << polyEvalMod(poly,babyStep[0], p)<<endl; exit(0); } // cerr << " babyStep depth=["; // for (long i=0; i<babyStep.size(); i++) // cerr << babyStep.getDepth(i+1)<<" "; // cerr << "]\n"; // cerr << " giantStep depth=["; // for (long i=0; i<giantStep.size(); i++) // cerr<<giantStep.getDepth(i+1)<<" "; // cerr << "]\n"; } return ret; } // If n is not a power of two, ensure that poly is monic and that // its degree is divisible by k, then call the recursive procedure ZZ topInv; // the inverse mod p of the top coefficient of poly (if any) bool divisible = (n*k == deg(poly)); // is the degree divisible by k? long nonInvertibe = InvModStatus(topInv, LeadCoeff(poly), to_ZZ(p)); // 0 if invertible, 1 if not // FIXME: There may be some room for optimization below: instead of // adding a term X^{n*k} we can add X^{n'*k} for some n'>n, so long // as n' is smaller than the next power of two. We could save a few // multiplications since giantStep[n'] may be easier to compute than // giantStep[n] when n' has fewer 1's than n in its binary expansion. long extra = 0; // extra!=0 denotes an added term extra*X^{n*k} if (!divisible || nonInvertibe) { // need to add a term // set extra = 1 - current-coeff-of-X^{n*k} extra = SubMod(1, to_long(coeff(poly,n*k)), p); SetCoeff(poly, n*k); // set the top coefficient of X^{n*k} to one topInv = to_ZZ(1); // inverse of new top coefficient is one } long t = (extra==0)? divc(n,2) : n; DynamicPtxtPowers giantStep(x2k, p, t, babyStep.getDepth(k)); if (verbose) cerr << "babyStep="<<babyStep<<", giantStep="<<giantStep<<endl; long y; // the value to return long subDepth1 =0; if (!IsOne(topInv)) { long top = to_long(poly[n*k]); // record the current top coefficient // cerr << ", top-coeff="<<top; // Multiply by topInv modulo p to make into a monic polynomial poly *= topInv; for (long i=0; i<=n*k; i++) rem(poly[i], poly[i], to_ZZ(p)); poly.normalize(); y = recursivePolyEval(poly, k, babyStep, giantStep, p, subDepth1); if (verbose) { cerr << " recursivePolyEval("<<poly<<") returns "<<y<<", depth="<<subDepth1<<endl; if (y != polyEvalMod(poly,babyStep[0], p)) { cerr << "## recursive call failed, ret="<<y<<"!=" << polyEvalMod(poly,babyStep[0], p)<<endl; exit(0); } } y = MulMod(y, top, p); // multiply by the original top coefficient } else { y = recursivePolyEval(poly, k, babyStep, giantStep, p, subDepth1); if (verbose) { cerr << " recursivePolyEval("<<poly<<") returns "<<y<<", depth="<<subDepth1<<endl; if (y != polyEvalMod(poly,babyStep[0], p)) { cerr << "## recursive call failed, ret="<<y<<"!=" << polyEvalMod(poly,babyStep[0], p)<<endl; exit(0); } } } if (extra != 0) { // if we added a term, now is the time to subtract back if (verbose) cerr << ", subtracting "<<extra<<"*X^"<<k*n; extra = MulMod(extra, giantStep.getPower(n), p); totalDepth = max(subDepth1, giantStep.getDepth(n)); y = SubMod(y, extra, p); } else totalDepth = subDepth1; if (verbose) cerr << endl; return y; }
// Main entry point: Evaluate a cleartext polynomial on an encrypted input void polyEval(Ctxt& ret, ZZX poly, const Ctxt& x, long k) // Note: poly is passed by value, so caller keeps the original { if (deg(poly)<=2) { // nothing to optimize here if (deg(poly)<1) { // A constant ret.clear(); ret.addConstant(coeff(poly, 0)); } else { // A linear or quadratic polynomial DynamicCtxtPowers babyStep(x, deg(poly)); simplePolyEval(ret, poly, babyStep); } return; } // How many baby steps: set k~sqrt(n/2), rounded up/down to a power of two // FIXME: There may be some room for optimization here: it may be possible // to choose k as something other than a power of two and still maintain // optimal depth, in principle we can try all possible values of k between // two consecutive powers of two and choose the one that gives the least // number of multiplies, conditioned on minimum depth. if (k<=0) { long kk = (long) sqrt(deg(poly)/2.0); k = 1L << NextPowerOfTwo(kk); // heuristic: if k>>kk then use a smaler power of two if ((k==16 && deg(poly)>167) || (k>16 && k>(1.44*kk))) k /= 2; } #ifdef DEBUG_PRINTOUT cerr << " k="<<k; #endif long n = divc(deg(poly),k); // n = ceil(deg(p)/k), deg(p) >= k*n DynamicCtxtPowers babyStep(x, k); const Ctxt& x2k = babyStep.getPower(k); // Special case when deg(p)>k*(2^e -1) if (n==(1L << NextPowerOfTwo(n))) { // n is a power of two DynamicCtxtPowers giantStep(x2k, n/2); degPowerOfTwo(ret, poly, k, babyStep, giantStep); return; } // If n is not a power of two, ensure that poly is monic and that // its degree is divisible by k, then call the recursive procedure const ZZ p = to_ZZ(x.getPtxtSpace()); ZZ top = LeadCoeff(poly); ZZ topInv; // the inverse mod p of the top coefficient of poly (if any) bool divisible = (n*k == deg(poly)); // is the degree divisible by k? long nonInvertibe = InvModStatus(topInv, top, p); // 0 if invertible, 1 if not // FIXME: There may be some room for optimization below: instead of // adding a term X^{n*k} we can add X^{n'*k} for some n'>n, so long // as n' is smaller than the next power of two. We could save a few // multiplications since giantStep[n'] may be easier to compute than // giantStep[n] when n' has fewer 1's than n in its binary expansion. ZZ extra = ZZ::zero(); // extra!=0 denotes an added term extra*X^{n*k} if (!divisible || nonInvertibe) { // need to add a term top = to_ZZ(1); // new top coefficient is one topInv = top; // also the new inverse is one // set extra = 1 - current-coeff-of-X^{n*k} extra = SubMod(top, coeff(poly,n*k), p); SetCoeff(poly, n*k); // set the top coefficient of X^{n*k} to one } long t = IsZero(extra)? divc(n,2) : n; DynamicCtxtPowers giantStep(x2k, t); if (!IsOne(top)) { poly *= topInv; // Multiply by topInv to make into a monic polynomial for (long i=0; i<=n*k; i++) rem(poly[i], poly[i], p); poly.normalize(); } recursivePolyEval(ret, poly, k, babyStep, giantStep); if (!IsOne(top)) { ret.multByConstant(top); } if (!IsZero(extra)) { // if we added a term, now is the time to subtract back Ctxt topTerm = giantStep.getPower(n); topTerm.multByConstant(extra); ret -= topTerm; } }
void SubMod(ZZ& x, long a, const ZZ& b, const ZZ& n) { static ZZ A; conv(A, a); SubMod(x, A, b, n); }
void SubMod(ZZ& x, const ZZ& a, long b, const ZZ& n) { static ZZ B; conv(B, b); SubMod(x, a, B, n); }