📄 nbtheory.cpp
字号:
// nbtheory.cpp - written and placed in the public domain by Wei Dai#include "pch.h"#ifndef CRYPTOPP_IMPORTS#include "nbtheory.h"#include "modarith.h"#include "algparam.h"#include <math.h>#include <vector>#ifdef _OPENMP// needed in MSVC 2005 to generate correct manifest#include <omp.h>#endifNAMESPACE_BEGIN(CryptoPP)const word s_lastSmallPrime = 32719;struct NewPrimeTable{ std::vector<word16> * operator()() const { const unsigned int maxPrimeTableSize = 3511; std::auto_ptr<std::vector<word16> > pPrimeTable(new std::vector<word16>); std::vector<word16> &primeTable = *pPrimeTable; primeTable.reserve(maxPrimeTableSize); primeTable.push_back(2); unsigned int testEntriesEnd = 1; for (unsigned int p=3; p<=s_lastSmallPrime; p+=2) { unsigned int j; for (j=1; j<testEntriesEnd; j++) if (p%primeTable[j] == 0) break; if (j == testEntriesEnd) { primeTable.push_back(p); testEntriesEnd = UnsignedMin(54U, primeTable.size()); } } return pPrimeTable.release(); }};const word16 * GetPrimeTable(unsigned int &size){ const std::vector<word16> &primeTable = Singleton<std::vector<word16>, NewPrimeTable>().Ref(); size = (unsigned int)primeTable.size(); return &primeTable[0];}bool IsSmallPrime(const Integer &p){ unsigned int primeTableSize; const word16 * primeTable = GetPrimeTable(primeTableSize); if (p.IsPositive() && p <= primeTable[primeTableSize-1]) return std::binary_search(primeTable, primeTable+primeTableSize, (word16)p.ConvertToLong()); else return false;}bool TrialDivision(const Integer &p, unsigned bound){ unsigned int primeTableSize; const word16 * primeTable = GetPrimeTable(primeTableSize); assert(primeTable[primeTableSize-1] >= bound); unsigned int i; for (i = 0; primeTable[i]<bound; i++) if ((p % primeTable[i]) == 0) return true; if (bound == primeTable[i]) return (p % bound == 0); else return false;}bool SmallDivisorsTest(const Integer &p){ unsigned int primeTableSize; const word16 * primeTable = GetPrimeTable(primeTableSize); return !TrialDivision(p, primeTable[primeTableSize-1]);}bool IsFermatProbablePrime(const Integer &n, const Integer &b){ if (n <= 3) return n==2 || n==3; assert(n>3 && b>1 && b<n-1); return a_exp_b_mod_c(b, n-1, n)==1;}bool IsStrongProbablePrime(const Integer &n, const Integer &b){ if (n <= 3) return n==2 || n==3; assert(n>3 && b>1 && b<n-1); if ((n.IsEven() && n!=2) || GCD(b, n) != 1) return false; Integer nminus1 = (n-1); unsigned int a; // calculate a = largest power of 2 that divides (n-1) for (a=0; ; a++) if (nminus1.GetBit(a)) break; Integer m = nminus1>>a; Integer z = a_exp_b_mod_c(b, m, n); if (z==1 || z==nminus1) return true; for (unsigned j=1; j<a; j++) { z = z.Squared()%n; if (z==nminus1) return true; if (z==1) return false; } return false;}bool RabinMillerTest(RandomNumberGenerator &rng, const Integer &n, unsigned int rounds){ if (n <= 3) return n==2 || n==3; assert(n>3); Integer b; for (unsigned int i=0; i<rounds; i++) { b.Randomize(rng, 2, n-2); if (!IsStrongProbablePrime(n, b)) return false; } return true;}bool IsLucasProbablePrime(const Integer &n){ if (n <= 1) return false; if (n.IsEven()) return n==2; assert(n>2); Integer b=3; unsigned int i=0; int j; while ((j=Jacobi(b.Squared()-4, n)) == 1) { if (++i==64 && n.IsSquare()) // avoid infinite loop if n is a square return false; ++b; ++b; } if (j==0) return false; else return Lucas(n+1, b, n)==2;}bool IsStrongLucasProbablePrime(const Integer &n){ if (n <= 1) return false; if (n.IsEven()) return n==2; assert(n>2); Integer b=3; unsigned int i=0; int j; while ((j=Jacobi(b.Squared()-4, n)) == 1) { if (++i==64 && n.IsSquare()) // avoid infinite loop if n is a square return false; ++b; ++b; } if (j==0) return false; Integer n1 = n+1; unsigned int a; // calculate a = largest power of 2 that divides n1 for (a=0; ; a++) if (n1.GetBit(a)) break; Integer m = n1>>a; Integer z = Lucas(m, b, n); if (z==2 || z==n-2) return true; for (i=1; i<a; i++) { z = (z.Squared()-2)%n; if (z==n-2) return true; if (z==2) return false; } return false;}struct NewLastSmallPrimeSquared{ Integer * operator()() const { return new Integer(Integer(s_lastSmallPrime).Squared()); }};bool IsPrime(const Integer &p){ if (p <= s_lastSmallPrime) return IsSmallPrime(p); else if (p <= Singleton<Integer, NewLastSmallPrimeSquared>().Ref()) return SmallDivisorsTest(p); else return SmallDivisorsTest(p) && IsStrongProbablePrime(p, 3) && IsStrongLucasProbablePrime(p);}bool VerifyPrime(RandomNumberGenerator &rng, const Integer &p, unsigned int level){ bool pass = IsPrime(p) && RabinMillerTest(rng, p, 1); if (level >= 1) pass = pass && RabinMillerTest(rng, p, 10); return pass;}unsigned int PrimeSearchInterval(const Integer &max){ return max.BitCount();}static inline bool FastProbablePrimeTest(const Integer &n){ return IsStrongProbablePrime(n,2);}AlgorithmParameters MakeParametersForTwoPrimesOfEqualSize(unsigned int productBitLength){ if (productBitLength < 16) throw InvalidArgument("invalid bit length"); Integer minP, maxP; if (productBitLength%2==0) { minP = Integer(182) << (productBitLength/2-8); maxP = Integer::Power2(productBitLength/2)-1; } else { minP = Integer::Power2((productBitLength-1)/2); maxP = Integer(181) << ((productBitLength+1)/2-8); } return MakeParameters("RandomNumberType", Integer::PRIME)("Min", minP)("Max", maxP);}class PrimeSieve{public: // delta == 1 or -1 means double sieve with p = 2*q + delta PrimeSieve(const Integer &first, const Integer &last, const Integer &step, signed int delta=0); bool NextCandidate(Integer &c); void DoSieve(); static void SieveSingle(std::vector<bool> &sieve, word16 p, const Integer &first, const Integer &step, word16 stepInv); Integer m_first, m_last, m_step; signed int m_delta; word m_next; std::vector<bool> m_sieve;};PrimeSieve::PrimeSieve(const Integer &first, const Integer &last, const Integer &step, signed int delta) : m_first(first), m_last(last), m_step(step), m_delta(delta), m_next(0){ DoSieve();}bool PrimeSieve::NextCandidate(Integer &c){ bool safe = SafeConvert(std::find(m_sieve.begin()+m_next, m_sieve.end(), false) - m_sieve.begin(), m_next); assert(safe); if (m_next == m_sieve.size()) { m_first += long(m_sieve.size())*m_step; if (m_first > m_last) return false; else { m_next = 0; DoSieve(); return NextCandidate(c); } } else { c = m_first + long(m_next)*m_step; ++m_next; return true; }}void PrimeSieve::SieveSingle(std::vector<bool> &sieve, word16 p, const Integer &first, const Integer &step, word16 stepInv){ if (stepInv) { size_t sieveSize = sieve.size(); size_t j = (word32(p-(first%p))*stepInv) % p; // if the first multiple of p is p, skip it if (first.WordCount() <= 1 && first + step*long(j) == p) j += p; for (; j < sieveSize; j += p) sieve[j] = true; }}void PrimeSieve::DoSieve(){ unsigned int primeTableSize; const word16 * primeTable = GetPrimeTable(primeTableSize); const unsigned int maxSieveSize = 32768; unsigned int sieveSize = STDMIN(Integer(maxSieveSize), (m_last-m_first)/m_step+1).ConvertToLong(); m_sieve.clear(); m_sieve.resize(sieveSize, false); if (m_delta == 0) { for (unsigned int i = 0; i < primeTableSize; ++i) SieveSingle(m_sieve, primeTable[i], m_first, m_step, (word16)m_step.InverseMod(primeTable[i])); } else { assert(m_step%2==0); Integer qFirst = (m_first-m_delta) >> 1; Integer halfStep = m_step >> 1; for (unsigned int i = 0; i < primeTableSize; ++i) { word16 p = primeTable[i]; word16 stepInv = (word16)m_step.InverseMod(p); SieveSingle(m_sieve, p, m_first, m_step, stepInv); word16 halfStepInv = 2*stepInv < p ? 2*stepInv : 2*stepInv-p; SieveSingle(m_sieve, p, qFirst, halfStep, halfStepInv); } }}bool FirstPrime(Integer &p, const Integer &max, const Integer &equiv, const Integer &mod, const PrimeSelector *pSelector){ assert(!equiv.IsNegative() && equiv < mod); Integer gcd = GCD(equiv, mod); if (gcd != Integer::One()) { // the only possible prime p such that p%mod==equiv where GCD(mod,equiv)!=1 is GCD(mod,equiv) if (p <= gcd && gcd <= max && IsPrime(gcd) && (!pSelector || pSelector->IsAcceptable(gcd))) { p = gcd; return true; } else return false; } unsigned int primeTableSize; const word16 * primeTable = GetPrimeTable(primeTableSize); if (p <= primeTable[primeTableSize-1]) { const word16 *pItr; --p; if (p.IsPositive()) pItr = std::upper_bound(primeTable, primeTable+primeTableSize, (word)p.ConvertToLong()); else pItr = primeTable; while (pItr < primeTable+primeTableSize && !(*pItr%mod == equiv && (!pSelector || pSelector->IsAcceptable(*pItr)))) ++pItr; if (pItr < primeTable+primeTableSize) { p = *pItr; return p <= max; } p = primeTable[primeTableSize-1]+1; } assert(p > primeTable[primeTableSize-1]); if (mod.IsOdd()) return FirstPrime(p, max, CRT(equiv, mod, 1, 2, 1), mod<<1, pSelector); p += (equiv-p)%mod; if (p>max) return false; PrimeSieve sieve(p, max, mod); while (sieve.NextCandidate(p)) { if ((!pSelector || pSelector->IsAcceptable(p)) && FastProbablePrimeTest(p) && IsPrime(p)) return true; } return false;}// the following two functions are based on code and comments provided by Preda Mihailescustatic bool ProvePrime(const Integer &p, const Integer &q){ assert(p < q*q*q); assert(p % q == 1);// this is the Quisquater test. Numbers p having passed the Lucas - Lehmer test// for q and verifying p < q^3 can only be built up of two factors, both = 1 mod q,// or be prime. The next two lines build the discriminant of a quadratic equation// which holds iff p is built up of two factors (excercise ... ) Integer r = (p-1)/q; if (((r%q).Squared()-4*(r/q)).IsSquare()) return false; unsigned int primeTableSize; const word16 * primeTable = GetPrimeTable(primeTableSize); assert(primeTableSize >= 50); for (int i=0; i<50; i++) { Integer b = a_exp_b_mod_c(primeTable[i], r, p); if (b != 1) return a_exp_b_mod_c(b, q, p) == 1; } return false;}Integer MihailescuProvablePrime(RandomNumberGenerator &rng, unsigned int pbits){ Integer p; Integer minP = Integer::Power2(pbits-1); Integer maxP = Integer::Power2(pbits) - 1; if (maxP <= Integer(s_lastSmallPrime).Squared()) { // Randomize() will generate a prime provable by trial division p.Randomize(rng, minP, maxP, Integer::PRIME); return p; } unsigned int qbits = (pbits+2)/3 + 1 + rng.GenerateWord32(0, pbits/36); Integer q = MihailescuProvablePrime(rng, qbits); Integer q2 = q<<1; while (true) { // this initializes the sieve to search in the arithmetic // progression p = p_0 + \lambda * q2 = p_0 + 2 * \lambda * q, // with q the recursively generated prime above. We will be able // to use Lucas tets for proving primality. A trick of Quisquater // allows taking q > cubic_root(p) rather then square_root: this // decreases the recursion. p.Randomize(rng, minP, maxP, Integer::ANY, 1, q2); PrimeSieve sieve(p, STDMIN(p+PrimeSearchInterval(maxP)*q2, maxP), q2); while (sieve.NextCandidate(p)) { if (FastProbablePrimeTest(p) && ProvePrime(p, q)) return p; } } // not reached return p;}Integer MaurerProvablePrime(RandomNumberGenerator &rng, unsigned int bits){ const unsigned smallPrimeBound = 29, c_opt=10; Integer p; unsigned int primeTableSize; const word16 * primeTable = GetPrimeTable(primeTableSize); if (bits < smallPrimeBound) { do p.Randomize(rng, Integer::Power2(bits-1), Integer::Power2(bits)-1, Integer::ANY, 1, 2); while (TrialDivision(p, 1 << ((bits+1)/2))); } else { const unsigned margin = bits > 50 ? 20 : (bits-10)/2; double relativeSize; do relativeSize = pow(2.0, double(rng.GenerateWord32())/0xffffffff - 1); while (bits * relativeSize >= bits - margin); Integer a,b; Integer q = MaurerProvablePrime(rng, unsigned(bits*relativeSize)); Integer I = Integer::Power2(bits-2)/q; Integer I2 = I << 1; unsigned int trialDivisorBound = (unsigned int)STDMIN((unsigned long)primeTable[primeTableSize-1], (unsigned long)bits*bits/c_opt); bool success = false; while (!success) { p.Randomize(rng, I, I2, Integer::ANY); p *= q; p <<= 1; ++p; if (!TrialDivision(p, trialDivisorBound)) { a.Randomize(rng, 2, p-1, Integer::ANY); b = a_exp_b_mod_c(a, (p-1)/q, p); success = (GCD(b-1, p) == 1) && (a_exp_b_mod_c(b, q, p) == 1); } } } return p;}Integer CRT(const Integer &xp, const Integer &p, const Integer &xq, const Integer &q, const Integer &u){ // isn't operator overloading great? return p * (u * (xq-xp) % q) + xp;/* Integer t1 = xq-xp; cout << hex << t1 << endl; Integer t2 = u * t1; cout << hex << t2 << endl; Integer t3 = t2 % q;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -