This repository has been archived on 2023-10-10. You can view files and clone it, but cannot push or open issues/pull-requests.
umcs/src/rsalib/PrimeGenerator.cpp

182 lines
5.8 KiB
C++

/* ****************************************************************************
*
* PrimeGenerator.cpp
*
* Author: Nedim Srndic
* Release date: 14th of March 2008
*
* This file contains the implementation for the PrimeGenerator class.
*
* There is a static constant defined in this file:
*
* - RandMax : RAND_MAX (defined in cstdlib) of type BigInt
* Mainly used for speedup in the Generate member function.
* Represents the largest random unsigned long integer that a particular
* platform can generate. This is platform-specific.
*
* ****************************************************************************
*/
#include "PrimeGenerator.h"
#include <string>
#include <cstdlib> // rand()
/* Generates a random number with digitCount digits.
* Returns it by reference in the "number" parameter. */
void PrimeGenerator::MakeRandom(BigInt &number, unsigned long int digitCount)
{
//the new number will be created using a string object (newNum),
//and later converted into a BigInt
std::string newNum;
newNum.resize(digitCount);
unsigned long int tempDigitCount(0);
//generate random digits
while (tempDigitCount < digitCount)
{
unsigned long int newRand(std::rand());
//10 is chosen to skip the first digit, because it might be
//statistically <= n, where n is the first digit of RAND_MAX
while (newRand >= 10)
{
newNum[tempDigitCount++] = (newRand % 10) + '0';
newRand /= 10;
if (tempDigitCount == digitCount)
break;
}
}
//make sure the leading digit is not zero
if (newNum[0] == '0')
newNum[0] = (std::rand() % 9) + 1 + '0';
number = newNum;
}
/* Generates a random number such as 1 <= number < 'top'.
* Returns it by reference in the 'number' parameter. */
void PrimeGenerator::makeRandom(BigInt &number, const BigInt &top)
{
//randomly select the number of digits for the random number
unsigned long int newDigitCount = (rand() % top.Length()) + 1;
MakeRandom(number, newDigitCount);
//make sure number < top
while (number >= top)
MakeRandom(number, newDigitCount);
}
/* Creates an odd BigInt with the specified number of digits.
* Returns it by reference in the "number" parameter. */
void PrimeGenerator::makePrimeCandidate(BigInt &number,
unsigned long int digitCount)
{
PrimeGenerator::MakeRandom(number, digitCount);
//make the number odd
if (!number.IsOdd())
number.SetDigit(0, number.GetDigit(0) + 1);
//make sure the leading digit is not a zero
if (number.GetDigit(number.Length() - 1) == 0)
number.SetDigit(number.Length() - 1, (std::rand() % 9) + 1);
}
/* Tests the primality of the given _odd_ number using the
* Miller-Rabin probabilistic primality test. Returns true if
* the tested argument "number" is a probable prime with a
* probability of at least 1 - 4^(-k), otherwise false. */
bool PrimeGenerator::isProbablePrime( const BigInt &number,
unsigned long int k)
{
//first we need to calculate such a and b, that
//number - 1 = 2^a * b, a and b are integers, b is odd
BigInt numberMinusOne(number - BigIntOne);
unsigned long int a(0);
BigInt temp(numberMinusOne);
BigInt b, quotient;
static const BigInt two(BigIntOne + BigIntOne);
while (b.EqualsZero())
{
//temp = quotient * 2 + remainder
//PrimeGenerator used to be a friend of BigInt, so the following
//statement produced the result in one call to BigInt::divide()
// BigInt::divide(temp, two, quotient, b);
//That doesn't work any more, so we have to use two calls
quotient = temp / two;
b = temp % two;
temp = quotient;
a++;
}
b = temp * two + b;
a--;
//test with k different possible witnesses to ensure that the probability
//that "number" is prime is at least 1 - 4^(-k)
for (unsigned long int i = 0; i < k; i++)
{
PrimeGenerator::makeRandom(temp, number);
if (isWitness(temp, number, b, a, numberMinusOne))
return false; //definitely a composite number
}
return true; //a probable prime
}
/* Returns true if "candidate" is a witness for the compositeness
* of "number", false if "candidate" is a strong liar. "exponent"
* and "squareCount" are used for computation */
bool PrimeGenerator::isWitness( BigInt candidate,
const BigInt &number,
const BigInt &exponent,
unsigned long int squareCount,
const BigInt &numberMinusOne)
{
//calculate candidate = (candidate to the power of exponent) mod number
candidate.SetPowerMod(exponent, number);
//temporary variable, used to call the divide function
BigInt quotient;
for (unsigned long int i = 0; i < squareCount; i++)
{
bool maybeWitness(false);
if (candidate != BigIntOne && candidate != numberMinusOne)
maybeWitness = true;
//PrimeGenerator used to be a friend of BigInt, so the following
//statement produced the result in one call to BigInt::divide()
// BigInt::divide(candidate * candidate, number, quotient, candidate);
//That doesn't work any more, so we have to use two calls
candidate = candidate * candidate;
quotient = (candidate) / number;
candidate = (candidate) % number;
if (maybeWitness && candidate == BigIntOne)
return true; //definitely a composite number
}
if (candidate != BigIntOne)
return true; //definitely a composite number
return false; //probable prime
}
/* Returns a probable prime number "digitCount" digits long,
* with a probability of at least 1 - 4^(-k) that it is prime. */
BigInt PrimeGenerator::Generate(unsigned long int digitCount,
unsigned long int k)
{
if (digitCount < 3)
throw "Error PRIMEGENERATOR00: Primes less than 3 digits long "
"not supported.";
BigInt primeCandidate;
PrimeGenerator::makePrimeCandidate(primeCandidate, digitCount);
while (!isProbablePrime(primeCandidate, k))
{
//select the next odd number and try again
primeCandidate = primeCandidate + 2;
if (primeCandidate.Length() != digitCount)
PrimeGenerator::makePrimeCandidate(primeCandidate, digitCount);
}
return primeCandidate;
}