/* ****************************************************************************
 *
 * Copyright 2013 Nedim Srndic
 * 
 * This file is part of rsa - the RSA implementation in 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;
}
