Download Reference Manual
The Developer's Library for D
About Wiki Forums Source Search Contact

Ticket #1879 (new enhancement)

Opened 14 years ago

Last modified 14 years ago

BigInt Enhancements

Reported by: Sage Rat Assigned to: Don Clugston
Priority: normal Milestone: 1.0
Component: Tango Version: 0.99.9 Kai
Keywords: BigInt Cc:

Description

The following methods would be useful for cryptography work:

bool isEven();
static BigInt? pow(BigInt? x, BigInt? y);
static BigInt? modPow(BigInt? x, BigInt? y, BigInt? m);
bool isPrime();
static BigInt? rand(BigInt? n); // returns a value 0 to (n-1)
static BigInt? gcd(BigInt? a, BigInt? b);

I have already built all of these, though I'd recommend double checking them.

bool isEven(); -- There's almost certainly a better way to do this with access to internals:

	bool isEven(BigInt n) {
		return cast(bool)(((n >> 1) << 1) == n);
	}

static BigInt? pow(BigInt? x, BigInt? y); -- Adapted from http://en.wikipedia.org/wiki/Exponentiation_by_squaring:

	BigInt bigPow(BigInt x, BigInt n) {
		BigInt ret;
		if (n == 0) {
			ret = 1;
		}
		else if (n == 1) {
			ret = x;
		}
		else if (n == 2) {
			ret = x * x;
		}
		else if (isEven(n)) {	// even
			ret = bigPow(x, n >> 1);
			ret *= ret;
		}
		else {	// odd
			ret = bigPow(x, ((n - 1) >> 1));
			ret *= ret;
			ret *= x;
		}
		return ret;
	}

static BigInt? modPow(BigInt? x, BigInt? y, BigInt? m); -- Adapted from http://en.wikipedia.org/wiki/Modular_exponentiation:

	BigInt modPow(BigInt base, BigInt exp, BigInt mod) {
		BigInt ret = 1;
		
		BigInt lexp = exp;
		while (lexp > 0) {
			if (!isEven(lexp)) {
				ret = (ret * base) % mod;
			}
			lexp >>= 1;
			base = (base * base) % mod;
		}
		
		return ret;
	}

bool isPrime(); -- Adapted from http://en.literateprograms.org/Miller-Rabin_primality_test_%28C%2C_GMP%29:

public {
	bool isPrime(BigInt n) {
		if (n < 2) return false;
		else if (n == 2) return true;
		
		BigInt a;
		int repeat;
		for(repeat=0; repeat<20; repeat++) {
			do {
				a = bigRand(n);
			} while (a == 0);
			if (!miller_rabin_pass(a, n)) {
				return false;
			}
		}
		return true;
	}
}
private {
	bool miller_rabin_pass(BigInt a, BigInt n) {
		int i, s;
		BigInt a_to_power, d, n_minus_one;

		n_minus_one = n - 1;
		
		s = 0;
		d = n_minus_one;
		while (isEven(d)) {
			d >>= 1;
			s++;
		}
		
		a_to_power = modPow(a, d, n);
		if (a_to_power == 1)  {
			return true;
		}
		for(i = 0; i < (s-1); i++) {
			if (a_to_power == n_minus_one) {
				return true;
			}
			a_to_power = (a_to_power * a_to_power) % n;
		}
		if (a_to_power == n_minus_one) {
			return true;
		}
		return false;
	}
}

static BigInt? rand(BigInt? n); -- Original methodology. Should be checked:

	BigInt bigRand(BigInt n) {
		if (n < 0) throw new Exception("Parameter must be positive");
		
		BigInt ret;
		Random r = new Random();
		
		ret = r.uniform!(uint);
		while (ret < n) {
			ret *= r.uniform!(uint);
		}
		ret %= n;
		
		return ret;
	}

static BigInt? gcd(BigInt? a, BigInt? b); -- Adapted from http://en.wikipedia.org/wiki/Greatest_common_divisor (Euclid's Algorithm):

	BigInt gcd(BigInt a, BigInt b) {
		BigInt ret;
		
		if (b == 0) {
			ret = a;
		}
		else {
			ret = gcd( b, a - (b * (a / b)) );
		}
		return ret;
	}

Change History

03/14/10 00:46:01 changed by Sage Rat

Sorry, it's bad form to post moving targets but I'm fairly certain that my code is now locked in so there won't be any more changes.

In retrospect I realize that bigPow() is entirely unnecessary. You couldn't handle numbers that large on any machine, so you don't need more than the long-based version that is already supported.

I also discovered that the implementation of gcd() overflows the stack for particularly large values. The below version doesn't have that issue:

// Lifted from http://code.google.com/p/rsa/source/browse/rsa/trunk/source/RSA.cpp
	BigInt gcd(BigInt a, BigInt b) {
		if (b == 0)
			return a;
		else
			return gcd(b, a % b);
	}

And isPrime() works faster with more simple checks at the start:

	bool isPrime(BigInt n) {
		if (n < 2) return false;
		else if (
			n == 2
			|| n == 3
			|| n == 5
			|| n == 7
		) return true;
		else if (
			n.isEven()
			|| (n % 3) == 0
			|| (n % 5) == 0
			|| (n % 7) == 0
		) return false;
		
		BigInt a;
		for (int repeat = 0; repeat < 20; repeat++) {
			do {
				a = bigRand(n);
			} while (a == 0);
			if (!miller_rabin_pass(a, n)) {
				return false;
			}
		}
		return true;
	}

03/14/10 19:12:47 changed by kris

awesome - thanks!

03/20/10 20:37:21 changed by kris

  • owner changed from kris to Don Clugston.

Can you provide a '.patch' file with the appropriate changes, please? Gonna assign this to Don, since he looks after this package

03/21/10 20:47:42 changed by Don Clugston

Great, your experience with which functions you need is very valuable. Some comments:

isEven() could be efficiently implemented if int = BigInt? & int; was implemented. I think that makes sense to add. OTOH I could make BigInt? % int really efficient for powers of 2. That's even simpler. (So bool isEven(BigInt? a) { return a%2==0; } ).

Your rand() isn't correct. Taking % skews the results. The low numbers get two chances to be picked, whereas the rest only have one.

gcd() should use the binary gcd algorithm internally. (Which isn't much different from Euclid's, just much faster).

As you mentioned, pow() explodes really quickly. I would have included BigInt? BigInt? if I thought there was any use for it.