4837946: Faster multiplication and exponentiation of large integers

4646474: BigInteger.pow() algorithm slow in 1.4.0

Implement Karatsuba and 3-way Toom-Cook multiplication as well as exponentiation using Karatsuba and Toom-Cook squaring.

Reviewed-by: alanb, bpb, martin
This commit is contained in:
Alan Eliasen 2013-06-19 08:59:39 -07:00 committed by Brian Burkhalter
parent adc454c0fe
commit 3a76795991
3 changed files with 811 additions and 147 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -3538,24 +3538,7 @@ public class BigDecimal extends Number implements Comparable<BigDecimal> {
return expandBigIntegerTenPowers(n);
}
if (n < 1024*524288) {
// BigInteger.pow is slow, so make 10**n by constructing a
// BigInteger from a character string (still not very fast)
// which occupies no more than 1GB (!) of memory.
char tenpow[] = new char[n + 1];
tenpow[0] = '1';
for (int i = 1; i <= n; i++) {
tenpow[i] = '0';
}
return new BigInteger(tenpow, 1, tenpow.length);
}
if ((n & 0x1) == 0x1) {
return BigInteger.TEN.multiply(bigTenToThe(n - 1));
} else {
BigInteger tmp = bigTenToThe(n/2);
return tmp.multiply(tmp);
}
return BigInteger.TEN.pow(n);
}
/**

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -29,9 +29,12 @@
package java.math;
import java.util.Random;
import java.io.*;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamField;
import java.util.Arrays;
import java.util.Random;
/**
* Immutable arbitrary-precision integers. All operations behave as if
@ -94,6 +97,7 @@ import java.util.Arrays;
* @see BigDecimal
* @author Josh Bloch
* @author Michael McCloskey
* @author Alan Eliasen
* @since JDK1.1
*/
@ -174,6 +178,39 @@ public class BigInteger extends Number implements Comparable<BigInteger> {
*/
final static long LONG_MASK = 0xffffffffL;
/**
* The threshold value for using Karatsuba multiplication. If the number
* of ints in both mag arrays are greater than this number, then
* Karatsuba multiplication will be used. This value is found
* experimentally to work well.
*/
private static final int KARATSUBA_THRESHOLD = 50;
/**
* The threshold value for using 3-way Toom-Cook multiplication.
* If the number of ints in each mag array is greater than the
* Karatsuba threshold, and the number of ints in at least one of
* the mag arrays is greater than this threshold, then Toom-Cook
* multiplication will be used.
*/
private static final int TOOM_COOK_THRESHOLD = 75;
/**
* The threshold value for using Karatsuba squaring. If the number
* of ints in the number are larger than this value,
* Karatsuba squaring will be used. This value is found
* experimentally to work well.
*/
private static final int KARATSUBA_SQUARE_THRESHOLD = 90;
/**
* The threshold value for using Toom-Cook squaring. If the number
* of ints in the number are larger than this value,
* Toom-Cook squaring will be used. This value is found
* experimentally to work well.
*/
private static final int TOOM_COOK_SQUARE_THRESHOLD = 140;
//Constructors
/**
@ -522,15 +559,16 @@ public class BigInteger extends Number implements Comparable<BigInteger> {
if (bitLength < 2)
throw new ArithmeticException("bitLength < 2");
// The cutoff of 95 was chosen empirically for best performance
prime = (bitLength < 95 ? smallPrime(bitLength, certainty, rnd)
prime = (bitLength < SMALL_PRIME_THRESHOLD
? smallPrime(bitLength, certainty, rnd)
: largePrime(bitLength, certainty, rnd));
signum = 1;
mag = prime.mag;
}
// Minimum size in bits that the requested prime number has
// before we use the large prime number generating algorithms
// before we use the large prime number generating algorithms.
// The cutoff of 95 was chosen empirically for best performance.
private static final int SMALL_PRIME_THRESHOLD = 95;
// Certainty required to meet the spec of probablePrime
@ -553,7 +591,6 @@ public class BigInteger extends Number implements Comparable<BigInteger> {
if (bitLength < 2)
throw new ArithmeticException("bitLength < 2");
// The cutoff of 95 was chosen empirically for best performance
return (bitLength < SMALL_PRIME_THRESHOLD ?
smallPrime(bitLength, DEFAULT_PRIME_CERTAINTY, rnd) :
largePrime(bitLength, DEFAULT_PRIME_CERTAINTY, rnd));
@ -986,6 +1023,7 @@ public class BigInteger extends Number implements Comparable<BigInteger> {
private final static int MAX_CONSTANT = 16;
private static BigInteger posConst[] = new BigInteger[MAX_CONSTANT+1];
private static BigInteger negConst[] = new BigInteger[MAX_CONSTANT+1];
static {
for (int i = 1; i <= MAX_CONSTANT; i++) {
int[] magnitude = new int[1];
@ -1014,6 +1052,11 @@ public class BigInteger extends Number implements Comparable<BigInteger> {
*/
private static final BigInteger TWO = valueOf(2);
/**
* The BigInteger constant -1. (Not exported.)
*/
private static final BigInteger NEGATIVE_ONE = valueOf(-1);
/**
* The BigInteger constant ten.
*
@ -1290,17 +1333,29 @@ public class BigInteger extends Number implements Comparable<BigInteger> {
public BigInteger multiply(BigInteger val) {
if (val.signum == 0 || signum == 0)
return ZERO;
int resultSign = signum == val.signum ? 1 : -1;
if (val.mag.length == 1) {
return multiplyByInt(mag,val.mag[0], resultSign);
int xlen = mag.length;
int ylen = val.mag.length;
if ((xlen < KARATSUBA_THRESHOLD) || (ylen < KARATSUBA_THRESHOLD))
{
int resultSign = signum == val.signum ? 1 : -1;
if (val.mag.length == 1) {
return multiplyByInt(mag,val.mag[0], resultSign);
}
if(mag.length == 1) {
return multiplyByInt(val.mag,mag[0], resultSign);
}
int[] result = multiplyToLen(mag, xlen,
val.mag, ylen, null);
result = trustedStripLeadingZeroInts(result);
return new BigInteger(result, resultSign);
}
if(mag.length == 1) {
return multiplyByInt(val.mag,mag[0], resultSign);
}
int[] result = multiplyToLen(mag, mag.length,
val.mag, val.mag.length, null);
result = trustedStripLeadingZeroInts(result);
return new BigInteger(result, resultSign);
else
if ((xlen < TOOM_COOK_THRESHOLD) && (ylen < TOOM_COOK_THRESHOLD))
return multiplyKaratsuba(this, val);
else
return multiplyToomCook3(this, val);
}
private static BigInteger multiplyByInt(int[] x, int y, int sign) {
@ -1401,6 +1456,272 @@ public class BigInteger extends Number implements Comparable<BigInteger> {
return z;
}
/**
* Multiplies two BigIntegers using the Karatsuba multiplication
* algorithm. This is a recursive divide-and-conquer algorithm which is
* more efficient for large numbers than what is commonly called the
* "grade-school" algorithm used in multiplyToLen. If the numbers to be
* multiplied have length n, the "grade-school" algorithm has an
* asymptotic complexity of O(n^2). In contrast, the Karatsuba algorithm
* has complexity of O(n^(log2(3))), or O(n^1.585). It achieves this
* increased performance by doing 3 multiplies instead of 4 when
* evaluating the product. As it has some overhead, should be used when
* both numbers are larger than a certain threshold (found
* experimentally).
*
* See: http://en.wikipedia.org/wiki/Karatsuba_algorithm
*/
private static BigInteger multiplyKaratsuba(BigInteger x, BigInteger y)
{
int xlen = x.mag.length;
int ylen = y.mag.length;
// The number of ints in each half of the number.
int half = (Math.max(xlen, ylen)+1) / 2;
// xl and yl are the lower halves of x and y respectively,
// xh and yh are the upper halves.
BigInteger xl = x.getLower(half);
BigInteger xh = x.getUpper(half);
BigInteger yl = y.getLower(half);
BigInteger yh = y.getUpper(half);
BigInteger p1 = xh.multiply(yh); // p1 = xh*yh
BigInteger p2 = xl.multiply(yl); // p2 = xl*yl
// p3=(xh+xl)*(yh+yl)
BigInteger p3 = xh.add(xl).multiply(yh.add(yl));
// result = p1 * 2^(32*2*half) + (p3 - p1 - p2) * 2^(32*half) + p2
BigInteger result = p1.shiftLeft(32*half).add(p3.subtract(p1).subtract(p2)).shiftLeft(32*half).add(p2);
if (x.signum != y.signum)
return result.negate();
else
return result;
}
/**
* Multiplies two BigIntegers using a 3-way Toom-Cook multiplication
* algorithm. This is a recursive divide-and-conquer algorithm which is
* more efficient for large numbers than what is commonly called the
* "grade-school" algorithm used in multiplyToLen. If the numbers to be
* multiplied have length n, the "grade-school" algorithm has an
* asymptotic complexity of O(n^2). In contrast, 3-way Toom-Cook has a
* complexity of about O(n^1.465). It achieves this increased asymptotic
* performance by breaking each number into three parts and by doing 5
* multiplies instead of 9 when evaluating the product. Due to overhead
* (additions, shifts, and one division) in the Toom-Cook algorithm, it
* should only be used when both numbers are larger than a certain
* threshold (found experimentally). This threshold is generally larger
* than that for Karatsuba multiplication, so this algorithm is generally
* only used when numbers become significantly larger.
*
* The algorithm used is the "optimal" 3-way Toom-Cook algorithm outlined
* by Marco Bodrato.
*
* See: http://bodrato.it/toom-cook/
* http://bodrato.it/papers/#WAIFI2007
*
* "Towards Optimal Toom-Cook Multiplication for Univariate and
* Multivariate Polynomials in Characteristic 2 and 0." by Marco BODRATO;
* In C.Carlet and B.Sunar, Eds., "WAIFI'07 proceedings", p. 116-133,
* LNCS #4547. Springer, Madrid, Spain, June 21-22, 2007.
*
*/
private static BigInteger multiplyToomCook3(BigInteger a, BigInteger b)
{
int alen = a.mag.length;
int blen = b.mag.length;
int largest = Math.max(alen, blen);
// k is the size (in ints) of the lower-order slices.
int k = (largest+2)/3; // Equal to ceil(largest/3)
// r is the size (in ints) of the highest-order slice.
int r = largest - 2*k;
// Obtain slices of the numbers. a2 and b2 are the most significant
// bits of the numbers a and b, and a0 and b0 the least significant.
BigInteger a0, a1, a2, b0, b1, b2;
a2 = a.getToomSlice(k, r, 0, largest);
a1 = a.getToomSlice(k, r, 1, largest);
a0 = a.getToomSlice(k, r, 2, largest);
b2 = b.getToomSlice(k, r, 0, largest);
b1 = b.getToomSlice(k, r, 1, largest);
b0 = b.getToomSlice(k, r, 2, largest);
BigInteger v0, v1, v2, vm1, vinf, t1, t2, tm1, da1, db1;
v0 = a0.multiply(b0);
da1 = a2.add(a0);
db1 = b2.add(b0);
vm1 = da1.subtract(a1).multiply(db1.subtract(b1));
da1 = da1.add(a1);
db1 = db1.add(b1);
v1 = da1.multiply(db1);
v2 = da1.add(a2).shiftLeft(1).subtract(a0).multiply(
db1.add(b2).shiftLeft(1).subtract(b0));
vinf = a2.multiply(b2);
/* The algorithm requires two divisions by 2 and one by 3.
All divisions are known to be exact, that is, they do not produce
remainders, and all results are positive. The divisions by 2 are
implemented as right shifts which are relatively efficient, leaving
only an exact division by 3, which is done by a specialized
linear-time algorithm. */
t2 = v2.subtract(vm1).exactDivideBy3();
tm1 = v1.subtract(vm1).shiftRight(1);
t1 = v1.subtract(v0);
t2 = t2.subtract(t1).shiftRight(1);
t1 = t1.subtract(tm1).subtract(vinf);
t2 = t2.subtract(vinf.shiftLeft(1));
tm1 = tm1.subtract(t2);
// Number of bits to shift left.
int ss = k*32;
BigInteger result = vinf.shiftLeft(ss).add(t2).shiftLeft(ss).add(t1).shiftLeft(ss).add(tm1).shiftLeft(ss).add(v0);
if (a.signum != b.signum)
return result.negate();
else
return result;
}
/**
* Returns a slice of a BigInteger for use in Toom-Cook multiplication.
*
* @param lowerSize The size of the lower-order bit slices.
* @param upperSize The size of the higher-order bit slices.
* @param slice The index of which slice is requested, which must be a
* number from 0 to size-1. Slice 0 is the highest-order bits, and slice
* size-1 are the lowest-order bits. Slice 0 may be of different size than
* the other slices.
* @param fullsize The size of the larger integer array, used to align
* slices to the appropriate position when multiplying different-sized
* numbers.
*/
private BigInteger getToomSlice(int lowerSize, int upperSize, int slice,
int fullsize)
{
int start, end, sliceSize, len, offset;
len = mag.length;
offset = fullsize - len;
if (slice == 0)
{
start = 0 - offset;
end = upperSize - 1 - offset;
}
else
{
start = upperSize + (slice-1)*lowerSize - offset;
end = start + lowerSize - 1;
}
if (start < 0)
start = 0;
if (end < 0)
return ZERO;
sliceSize = (end-start) + 1;
if (sliceSize <= 0)
return ZERO;
// While performing Toom-Cook, all slices are positive and
// the sign is adjusted when the final number is composed.
if (start==0 && sliceSize >= len)
return this.abs();
int intSlice[] = new int[sliceSize];
System.arraycopy(mag, start, intSlice, 0, sliceSize);
return new BigInteger(trustedStripLeadingZeroInts(intSlice), 1);
}
/**
* Does an exact division (that is, the remainder is known to be zero)
* of the specified number by 3. This is used in Toom-Cook
* multiplication. This is an efficient algorithm that runs in linear
* time. If the argument is not exactly divisible by 3, results are
* undefined. Note that this is expected to be called with positive
* arguments only.
*/
private BigInteger exactDivideBy3()
{
int len = mag.length;
int[] result = new int[len];
long x, w, q, borrow;
borrow = 0L;
for (int i=len-1; i>=0; i--)
{
x = (mag[i] & LONG_MASK);
w = x - borrow;
if (borrow > x) // Did we make the number go negative?
borrow = 1L;
else
borrow = 0L;
// 0xAAAAAAAB is the modular inverse of 3 (mod 2^32). Thus,
// the effect of this is to divide by 3 (mod 2^32).
// This is much faster than division on most architectures.
q = (w * 0xAAAAAAABL) & LONG_MASK;
result[i] = (int) q;
// Now check the borrow. The second check can of course be
// eliminated if the first fails.
if (q >= 0x55555556L)
{
borrow++;
if (q >= 0xAAAAAAABL)
borrow++;
}
}
result = trustedStripLeadingZeroInts(result);
return new BigInteger(result, signum);
}
/**
* Returns a new BigInteger representing n lower ints of the number.
* This is used by Karatsuba multiplication and Karatsuba squaring.
*/
private BigInteger getLower(int n) {
int len = mag.length;
if (len <= n)
return this;
int lowerInts[] = new int[n];
System.arraycopy(mag, len-n, lowerInts, 0, n);
return new BigInteger(trustedStripLeadingZeroInts(lowerInts), 1);
}
/**
* Returns a new BigInteger representing mag.length-n upper
* ints of the number. This is used by Karatsuba multiplication and
* Karatsuba squaring.
*/
private BigInteger getUpper(int n) {
int len = mag.length;
if (len <= n)
return ZERO;
int upperLen = len - n;
int upperInts[] = new int[upperLen];
System.arraycopy(mag, 0, upperInts, 0, upperLen);
return new BigInteger(trustedStripLeadingZeroInts(upperInts), 1);
}
// Squaring
/**
* Returns a BigInteger whose value is {@code (this<sup>2</sup>)}.
*
@ -1409,8 +1730,18 @@ public class BigInteger extends Number implements Comparable<BigInteger> {
private BigInteger square() {
if (signum == 0)
return ZERO;
int[] z = squareToLen(mag, mag.length, null);
return new BigInteger(trustedStripLeadingZeroInts(z), 1);
int len = mag.length;
if (len < KARATSUBA_SQUARE_THRESHOLD)
{
int[] z = squareToLen(mag, len, null);
return new BigInteger(trustedStripLeadingZeroInts(z), 1);
}
else
if (len < TOOM_COOK_SQUARE_THRESHOLD)
return squareKaratsuba();
else
return squareToomCook3();
}
/**
@ -1480,6 +1811,83 @@ public class BigInteger extends Number implements Comparable<BigInteger> {
return z;
}
/**
* Squares a BigInteger using the Karatsuba squaring algorithm. It should
* be used when both numbers are larger than a certain threshold (found
* experimentally). It is a recursive divide-and-conquer algorithm that
* has better asymptotic performance than the algorithm used in
* squareToLen.
*/
private BigInteger squareKaratsuba()
{
int half = (mag.length+1) / 2;
BigInteger xl = getLower(half);
BigInteger xh = getUpper(half);
BigInteger xhs = xh.square(); // xhs = xh^2
BigInteger xls = xl.square(); // xls = xl^2
// xh^2 << 64 + (((xl+xh)^2 - (xh^2 + xl^2)) << 32) + xl^2
return xhs.shiftLeft(half*32).add(xl.add(xh).square().subtract(xhs.add(xls))).shiftLeft(half*32).add(xls);
}
/**
* Squares a BigInteger using the 3-way Toom-Cook squaring algorithm. It
* should be used when both numbers are larger than a certain threshold
* (found experimentally). It is a recursive divide-and-conquer algorithm
* that has better asymptotic performance than the algorithm used in
* squareToLen or squareKaratsuba.
*/
private BigInteger squareToomCook3()
{
int len = mag.length;
// k is the size (in ints) of the lower-order slices.
int k = (len+2)/3; // Equal to ceil(largest/3)
// r is the size (in ints) of the highest-order slice.
int r = len - 2*k;
// Obtain slices of the numbers. a2 is the most significant
// bits of the number, and a0 the least significant.
BigInteger a0, a1, a2;
a2 = getToomSlice(k, r, 0, len);
a1 = getToomSlice(k, r, 1, len);
a0 = getToomSlice(k, r, 2, len);
BigInteger v0, v1, v2, vm1, vinf, t1, t2, tm1, da1;
v0 = a0.square();
da1 = a2.add(a0);
vm1 = da1.subtract(a1).square();
da1 = da1.add(a1);
v1 = da1.square();
vinf = a2.square();
v2 = da1.add(a2).shiftLeft(1).subtract(a0).square();
/* The algorithm requires two divisions by 2 and one by 3.
All divisions are known to be exact, that is, they do not produce
remainders, and all results are positive. The divisions by 2 are
implemented as right shifts which are relatively efficient, leaving
only a division by 3.
The division by 3 is done by an optimized algorithm for this case.
*/
t2 = v2.subtract(vm1).exactDivideBy3();
tm1 = v1.subtract(vm1).shiftRight(1);
t1 = v1.subtract(v0);
t2 = t2.subtract(t1).shiftRight(1);
t1 = t1.subtract(tm1).subtract(vinf);
t2 = t2.subtract(vinf.shiftLeft(1));
tm1 = tm1.subtract(t2);
// Number of bits to shift left.
int ss = k*32;
return vinf.shiftLeft(ss).add(t2).shiftLeft(ss).add(t1).shiftLeft(ss).add(tm1).shiftLeft(ss).add(v0);
}
// Division
/**
* Returns a BigInteger whose value is {@code (this / val)}.
*
@ -1549,23 +1957,100 @@ public class BigInteger extends Number implements Comparable<BigInteger> {
if (signum==0)
return (exponent==0 ? ONE : this);
// Perform exponentiation using repeated squaring trick
int newSign = (signum<0 && (exponent&1)==1 ? -1 : 1);
int[] baseToPow2 = this.mag;
int[] result = {1};
BigInteger partToSquare = this.abs();
while (exponent != 0) {
if ((exponent & 1)==1) {
result = multiplyToLen(result, result.length,
baseToPow2, baseToPow2.length, null);
result = trustedStripLeadingZeroInts(result);
}
if ((exponent >>>= 1) != 0) {
baseToPow2 = squareToLen(baseToPow2, baseToPow2.length, null);
baseToPow2 = trustedStripLeadingZeroInts(baseToPow2);
}
// Factor out powers of two from the base, as the exponentiation of
// these can be done by left shifts only.
// The remaining part can then be exponentiated faster. The
// powers of two will be multiplied back at the end.
int powersOfTwo = partToSquare.getLowestSetBit();
int remainingBits;
// Factor the powers of two out quickly by shifting right, if needed.
if (powersOfTwo > 0)
{
partToSquare = partToSquare.shiftRight(powersOfTwo);
remainingBits = partToSquare.bitLength();
if (remainingBits == 1) // Nothing left but +/- 1?
if (signum<0 && (exponent&1)==1)
return NEGATIVE_ONE.shiftLeft(powersOfTwo*exponent);
else
return ONE.shiftLeft(powersOfTwo*exponent);
}
else
{
remainingBits = partToSquare.bitLength();
if (remainingBits == 1) // Nothing left but +/- 1?
if (signum<0 && (exponent&1)==1)
return NEGATIVE_ONE;
else
return ONE;
}
// This is a quick way to approximate the size of the result,
// similar to doing log2[n] * exponent. This will give an upper bound
// of how big the result can be, and which algorithm to use.
int scaleFactor = remainingBits * exponent;
// Use slightly different algorithms for small and large operands.
// See if the result will safely fit into a long. (Largest 2^63-1)
if (partToSquare.mag.length==1 && scaleFactor <= 62)
{
// Small number algorithm. Everything fits into a long.
int newSign = (signum<0 && (exponent&1)==1 ? -1 : 1);
long result = 1;
long baseToPow2 = partToSquare.mag[0] & LONG_MASK;
int workingExponent = exponent;
// Perform exponentiation using repeated squaring trick
while (workingExponent != 0) {
if ((workingExponent & 1)==1)
result = result * baseToPow2;
if ((workingExponent >>>= 1) != 0)
baseToPow2 = baseToPow2 * baseToPow2;
}
// Multiply back the powers of two (quickly, by shifting left)
if (powersOfTwo > 0)
{
int bitsToShift = powersOfTwo*exponent;
if (bitsToShift + scaleFactor <= 62) // Fits in long?
return valueOf((result << bitsToShift) * newSign);
else
return valueOf(result*newSign).shiftLeft(bitsToShift);
}
else
return valueOf(result*newSign);
}
else
{
// Large number algorithm. This is basically identical to
// the algorithm above, but calls multiply() and square()
// which may use more efficient algorithms for large numbers.
BigInteger answer = ONE;
int workingExponent = exponent;
// Perform exponentiation using repeated squaring trick
while (workingExponent != 0) {
if ((workingExponent & 1)==1)
answer = answer.multiply(partToSquare);
if ((workingExponent >>>= 1) != 0)
partToSquare = partToSquare.square();
}
// Multiply back the (exponentiated) powers of two (quickly,
// by shifting left)
if (powersOfTwo > 0)
answer = answer.shiftLeft(powersOfTwo*exponent);
if (signum<0 && (exponent&1)==1)
return answer.negate();
else
return answer;
}
return new BigInteger(result, newSign);
}
/**
@ -2117,7 +2602,7 @@ public class BigInteger extends Number implements Comparable<BigInteger> {
* Perform exponentiation using repeated squaring trick, chopping off
* high order bits as indicated by modulus.
*/
BigInteger result = valueOf(1);
BigInteger result = ONE;
BigInteger baseToPow2 = this.mod2(p);
int expOffset = 0;
@ -2850,6 +3335,7 @@ public class BigInteger extends Number implements Comparable<BigInteger> {
return buf.toString();
}
/* zero[i] is a string of i consecutive zeros. */
private static String zeros[] = new String[64];
static {
@ -3218,21 +3704,21 @@ public class BigInteger extends Number implements Comparable<BigInteger> {
* little-endian binary representation of the magnitude (int 0 is the
* least significant). If the magnitude is zero, return value is undefined.
*/
private int firstNonzeroIntNum() {
int fn = firstNonzeroIntNum - 2;
if (fn == -2) { // firstNonzeroIntNum not initialized yet
fn = 0;
private int firstNonzeroIntNum() {
int fn = firstNonzeroIntNum - 2;
if (fn == -2) { // firstNonzeroIntNum not initialized yet
fn = 0;
// Search for the first nonzero int
int i;
int mlen = mag.length;
for (i = mlen - 1; i >= 0 && mag[i] == 0; i--)
;
fn = mlen - i - 1;
firstNonzeroIntNum = fn + 2; // offset by two to initialize
}
return fn;
}
// Search for the first nonzero int
int i;
int mlen = mag.length;
for (i = mlen - 1; i >= 0 && mag[i] == 0; i--)
;
fn = mlen - i - 1;
firstNonzeroIntNum = fn + 2; // offset by two to initialize
}
return fn;
}
/** use serialVersionUID from JDK 1.1. for interoperability */
private static final long serialVersionUID = -8287574255936472291L;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1998, 2011, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -23,15 +23,19 @@
/*
* @test
* @bug 4181191 4161971 4227146 4194389 4823171 4624738 4812225
* @bug 4181191 4161971 4227146 4194389 4823171 4624738 4812225 4837946
* @summary tests methods in BigInteger
* @run main/timeout=400 BigIntegerTest
* @author madbot
*/
import java.util.Random;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.math.BigInteger;
import java.io.*;
import java.util.Random;
/**
* This is a simple test class created to ensure that the results
@ -48,21 +52,42 @@ import java.io.*;
*
*/
public class BigIntegerTest {
//
// Bit large number thresholds based on the int thresholds
// defined in BigInteger itself:
//
// KARATSUBA_THRESHOLD = 50 ints = 1600 bits
// TOOM_COOK_THRESHOLD = 75 ints = 2400 bits
// KARATSUBA_SQUARE_THRESHOLD = 90 ints = 2880 bits
// TOOM_COOK_SQUARE_THRESHOLD = 140 ints = 4480 bits
//
static final int BITS_KARATSUBA = 1600;
static final int BITS_TOOM_COOK = 2400;
static final int BITS_KARATSUBA_SQUARE = 2880;
static final int BITS_TOOM_COOK_SQUARE = 4480;
static final int ORDER_SMALL = 60;
static final int ORDER_MEDIUM = 100;
// #bits for testing Karatsuba and Burnikel-Ziegler
static final int ORDER_KARATSUBA = 1800;
// #bits for testing Toom-Cook
static final int ORDER_TOOM_COOK = 3000;
// #bits for testing Karatsuba squaring
static final int ORDER_KARATSUBA_SQUARE = 3200;
// #bits for testing Toom-Cook squaring
static final int ORDER_TOOM_COOK_SQUARE = 4600;
static Random rnd = new Random();
static int size = 1000; // numbers per batch
static boolean failure = false;
// Some variables for sizing test numbers in bits
private static int order1 = 100;
private static int order2 = 60;
private static int order3 = 30;
public static void pow() {
public static void pow(int order) {
int failCount1 = 0;
for (int i=0; i<size; i++) {
int power = rnd.nextInt(6) +2;
BigInteger x = fetchNumber(order1);
// Test identity x^power == x*x*x ... *x
int power = rnd.nextInt(6) + 2;
BigInteger x = fetchNumber(order);
BigInteger y = x.pow(power);
BigInteger z = x;
@ -72,22 +97,39 @@ public class BigIntegerTest {
if (!y.equals(z))
failCount1++;
}
report("pow", failCount1);
report("pow for " + order + " bits", failCount1);
}
public static void arithmetic() {
public static void square(int order) {
int failCount1 = 0;
for (int i=0; i<size; i++) {
// Test identity x^2 == x*x
BigInteger x = fetchNumber(order);
BigInteger xx = x.multiply(x);
BigInteger x2 = x.pow(2);
if (!x2.equals(xx))
failCount1++;
}
report("square for " + order + " bits", failCount1);
}
public static void arithmetic(int order) {
int failCount = 0;
for (int i=0; i<size; i++) {
BigInteger x = fetchNumber(order1);
BigInteger x = fetchNumber(order);
while(x.compareTo(BigInteger.ZERO) != 1)
x = fetchNumber(order1);
BigInteger y = fetchNumber(order1/2);
x = fetchNumber(order);
BigInteger y = fetchNumber(order/2);
while(x.compareTo(y) == -1)
y = fetchNumber(order1/2);
y = fetchNumber(order/2);
if (y.equals(BigInteger.ZERO))
y = y.add(BigInteger.ONE);
// Test identity ((x/y))*y + x%y - x == 0
// using separate divide() and remainder()
BigInteger baz = x.divide(y);
baz = baz.multiply(y);
baz = baz.add(x.remainder(y));
@ -95,19 +137,21 @@ public class BigIntegerTest {
if (!baz.equals(BigInteger.ZERO))
failCount++;
}
report("Arithmetic I", failCount);
report("Arithmetic I for " + order + " bits", failCount);
failCount = 0;
for (int i=0; i<100; i++) {
BigInteger x = fetchNumber(order1);
BigInteger x = fetchNumber(order);
while(x.compareTo(BigInteger.ZERO) != 1)
x = fetchNumber(order1);
BigInteger y = fetchNumber(order1/2);
x = fetchNumber(order);
BigInteger y = fetchNumber(order/2);
while(x.compareTo(y) == -1)
y = fetchNumber(order1/2);
y = fetchNumber(order/2);
if (y.equals(BigInteger.ZERO))
y = y.add(BigInteger.ONE);
// Test identity ((x/y))*y + x%y - x == 0
// using divideAndRemainder()
BigInteger baz[] = x.divideAndRemainder(y);
baz[0] = baz[0].multiply(y);
baz[0] = baz[0].add(baz[1]);
@ -115,7 +159,118 @@ public class BigIntegerTest {
if (!baz[0].equals(BigInteger.ZERO))
failCount++;
}
report("Arithmetic II", failCount);
report("Arithmetic II for " + order + " bits", failCount);
}
/**
* Sanity test for Karatsuba and 3-way Toom-Cook multiplication.
* For each of the Karatsuba and 3-way Toom-Cook multiplication thresholds,
* construct two factors each with a mag array one element shorter than the
* threshold, and with the most significant bit set and the rest of the bits
* random. Each of these numbers will therefore be below the threshold but
* if shifted left be above the threshold. Call the numbers 'u' and 'v' and
* define random shifts 'a' and 'b' in the range [1,32]. Then we have the
* identity
* <pre>
* (u << a)*(v << b) = (u*v) << (a + b)
* </pre>
* For Karatsuba multiplication, the right hand expression will be evaluated
* using the standard naive algorithm, and the left hand expression using
* the Karatsuba algorithm. For 3-way Toom-Cook multiplication, the right
* hand expression will be evaluated using Karatsuba multiplication, and the
* left hand expression using 3-way Toom-Cook multiplication.
*/
public static void multiplyLarge() {
int failCount = 0;
BigInteger base = BigInteger.ONE.shiftLeft(BITS_KARATSUBA - 32 - 1);
for (int i=0; i<size; i++) {
BigInteger x = fetchNumber(BITS_KARATSUBA - 32 - 1);
BigInteger u = base.add(x);
int a = 1 + rnd.nextInt(31);
BigInteger w = u.shiftLeft(a);
BigInteger y = fetchNumber(BITS_KARATSUBA - 32 - 1);
BigInteger v = base.add(y);
int b = 1 + rnd.nextInt(32);
BigInteger z = v.shiftLeft(b);
BigInteger multiplyResult = u.multiply(v).shiftLeft(a + b);
BigInteger karatsubaMultiplyResult = w.multiply(z);
if (!multiplyResult.equals(karatsubaMultiplyResult)) {
failCount++;
}
}
report("multiplyLarge Karatsuba", failCount);
failCount = 0;
base = base.shiftLeft(BITS_TOOM_COOK - BITS_KARATSUBA);
for (int i=0; i<size; i++) {
BigInteger x = fetchNumber(BITS_TOOM_COOK - 32 - 1);
BigInteger u = base.add(x);
BigInteger u2 = u.shiftLeft(1);
BigInteger y = fetchNumber(BITS_TOOM_COOK - 32 - 1);
BigInteger v = base.add(y);
BigInteger v2 = v.shiftLeft(1);
BigInteger multiplyResult = u.multiply(v).shiftLeft(2);
BigInteger toomCookMultiplyResult = u2.multiply(v2);
if (!multiplyResult.equals(toomCookMultiplyResult)) {
failCount++;
}
}
report("multiplyLarge Toom-Cook", failCount);
}
/**
* Sanity test for Karatsuba and 3-way Toom-Cook squaring.
* This test is analogous to {@link AbstractMethodError#multiplyLarge}
* with both factors being equal. The squaring methods will not be tested
* unless the <code>bigInteger.multiply(bigInteger)</code> tests whether
* the parameter is the same instance on which the method is being invoked
* and calls <code>square()</code> accordingly.
*/
public static void squareLarge() {
int failCount = 0;
BigInteger base = BigInteger.ONE.shiftLeft(BITS_KARATSUBA_SQUARE - 32 - 1);
for (int i=0; i<size; i++) {
BigInteger x = fetchNumber(BITS_KARATSUBA_SQUARE - 32 - 1);
BigInteger u = base.add(x);
int a = 1 + rnd.nextInt(31);
BigInteger w = u.shiftLeft(a);
BigInteger squareResult = u.multiply(u).shiftLeft(2*a);
BigInteger karatsubaSquareResult = w.multiply(w);
if (!squareResult.equals(karatsubaSquareResult)) {
failCount++;
}
}
report("squareLarge Karatsuba", failCount);
failCount = 0;
base = base.shiftLeft(BITS_TOOM_COOK_SQUARE - BITS_KARATSUBA_SQUARE);
for (int i=0; i<size; i++) {
BigInteger x = fetchNumber(BITS_TOOM_COOK_SQUARE - 32 - 1);
BigInteger u = base.add(x);
int a = 1 + rnd.nextInt(31);
BigInteger w = u.shiftLeft(a);
BigInteger squareResult = u.multiply(u).shiftLeft(2*a);
BigInteger toomCookSquareResult = w.multiply(w);
if (!squareResult.equals(toomCookSquareResult)) {
failCount++;
}
}
report("squareLarge Toom-Cook", failCount);
}
public static void bitCount() {
@ -160,14 +315,14 @@ public class BigIntegerTest {
report("BitLength", failCount);
}
public static void bitOps() {
public static void bitOps(int order) {
int failCount1 = 0, failCount2 = 0, failCount3 = 0;
for (int i=0; i<size*5; i++) {
BigInteger x = fetchNumber(order1);
BigInteger x = fetchNumber(order);
BigInteger y;
/* Test setBit and clearBit (and testBit) */
// Test setBit and clearBit (and testBit)
if (x.signum() < 0) {
y = BigInteger.valueOf(-1);
for (int j=0; j<x.bitLength(); j++)
@ -182,7 +337,7 @@ public class BigIntegerTest {
if (!x.equals(y))
failCount1++;
/* Test flipBit (and testBit) */
// Test flipBit (and testBit)
y = BigInteger.valueOf(x.signum()<0 ? -1 : 0);
for (int j=0; j<x.bitLength(); j++)
if (x.signum()<0 ^ x.testBit(j))
@ -190,13 +345,13 @@ public class BigIntegerTest {
if (!x.equals(y))
failCount2++;
}
report("clearBit/testBit", failCount1);
report("flipBit/testBit", failCount2);
report("clearBit/testBit for " + order + " bits", failCount1);
report("flipBit/testBit for " + order + " bits", failCount2);
for (int i=0; i<size*5; i++) {
BigInteger x = fetchNumber(order1);
BigInteger x = fetchNumber(order);
/* Test getLowestSetBit() */
// Test getLowestSetBit()
int k = x.getLowestSetBit();
if (x.signum() == 0) {
if (k != -1)
@ -210,43 +365,43 @@ public class BigIntegerTest {
failCount3++;
}
}
report("getLowestSetBit", failCount3);
report("getLowestSetBit for " + order + " bits", failCount3);
}
public static void bitwise() {
public static void bitwise(int order) {
/* Test identity x^y == x|y &~ x&y */
// Test identity x^y == x|y &~ x&y
int failCount = 0;
for (int i=0; i<size; i++) {
BigInteger x = fetchNumber(order1);
BigInteger y = fetchNumber(order1);
BigInteger x = fetchNumber(order);
BigInteger y = fetchNumber(order);
BigInteger z = x.xor(y);
BigInteger w = x.or(y).andNot(x.and(y));
if (!z.equals(w))
failCount++;
}
report("Logic (^ | & ~)", failCount);
report("Logic (^ | & ~) for " + order + " bits", failCount);
/* Test identity x &~ y == ~(~x | y) */
// Test identity x &~ y == ~(~x | y)
failCount = 0;
for (int i=0; i<size; i++) {
BigInteger x = fetchNumber(order1);
BigInteger y = fetchNumber(order1);
BigInteger x = fetchNumber(order);
BigInteger y = fetchNumber(order);
BigInteger z = x.andNot(y);
BigInteger w = x.not().or(y).not();
if (!z.equals(w))
failCount++;
}
report("Logic (&~ | ~)", failCount);
report("Logic (&~ | ~) for " + order + " bits", failCount);
}
public static void shift() {
public static void shift(int order) {
int failCount1 = 0;
int failCount2 = 0;
int failCount3 = 0;
for (int i=0; i<100; i++) {
BigInteger x = fetchNumber(order1);
BigInteger x = fetchNumber(order);
int n = Math.abs(rnd.nextInt()%200);
if (!x.shiftLeft(n).equals
@ -274,18 +429,18 @@ public class BigIntegerTest {
if (!x.shiftLeft(n).shiftRight(n).equals(x))
failCount3++;
}
report("baz shiftLeft", failCount1);
report("baz shiftRight", failCount2);
report("baz shiftLeft/Right", failCount3);
report("baz shiftLeft for " + order + " bits", failCount1);
report("baz shiftRight for " + order + " bits", failCount2);
report("baz shiftLeft/Right for " + order + " bits", failCount3);
}
public static void divideAndRemainder() {
public static void divideAndRemainder(int order) {
int failCount1 = 0;
for (int i=0; i<size; i++) {
BigInteger x = fetchNumber(order1).abs();
BigInteger x = fetchNumber(order).abs();
while(x.compareTo(BigInteger.valueOf(3L)) != 1)
x = fetchNumber(order1).abs();
x = fetchNumber(order).abs();
BigInteger z = x.divide(BigInteger.valueOf(2L));
BigInteger y[] = x.divideAndRemainder(x);
if (!y[0].equals(BigInteger.ONE)) {
@ -306,7 +461,7 @@ public class BigIntegerTest {
System.err.println(" y :"+y);
}
}
report("divideAndRemainder I", failCount1);
report("divideAndRemainder for " + order + " bits", failCount1);
}
public static void stringConv() {
@ -331,13 +486,13 @@ public class BigIntegerTest {
report("String Conversion", failCount);
}
public static void byteArrayConv() {
public static void byteArrayConv(int order) {
int failCount = 0;
for (int i=0; i<size; i++) {
BigInteger x = fetchNumber(order1);
BigInteger x = fetchNumber(order);
while (x.equals(BigInteger.ZERO))
x = fetchNumber(order1);
x = fetchNumber(order);
BigInteger y = new BigInteger(x.toByteArray());
if (!x.equals(y)) {
failCount++;
@ -345,19 +500,19 @@ public class BigIntegerTest {
System.err.println("new is "+y);
}
}
report("Array Conversion", failCount);
report("Array Conversion for " + order + " bits", failCount);
}
public static void modInv() {
public static void modInv(int order) {
int failCount = 0, successCount = 0, nonInvCount = 0;
for (int i=0; i<size; i++) {
BigInteger x = fetchNumber(order1);
BigInteger x = fetchNumber(order);
while(x.equals(BigInteger.ZERO))
x = fetchNumber(order1);
BigInteger m = fetchNumber(order1).abs();
x = fetchNumber(order);
BigInteger m = fetchNumber(order).abs();
while(m.compareTo(BigInteger.ONE) != 1)
m = fetchNumber(order1).abs();
m = fetchNumber(order).abs();
try {
BigInteger inv = x.modInverse(m);
@ -374,10 +529,10 @@ public class BigIntegerTest {
nonInvCount++;
}
}
report("Modular Inverse", failCount);
report("Modular Inverse for " + order + " bits", failCount);
}
public static void modExp() {
public static void modExp(int order1, int order2) {
int failCount = 0;
for (int i=0; i<size/10; i++) {
@ -398,13 +553,14 @@ public class BigIntegerTest {
failCount++;
}
}
report("Exponentiation I", failCount);
report("Exponentiation I for " + order1 + " and " +
order2 + " bits", failCount);
}
// This test is based on Fermat's theorem
// which is not ideal because base must not be multiple of modulus
// and modulus must be a prime or pseudoprime (Carmichael number)
public static void modExp2() {
public static void modExp2(int order) {
int failCount = 0;
for (int i=0; i<10; i++) {
@ -412,11 +568,11 @@ public class BigIntegerTest {
while(m.compareTo(BigInteger.ONE) != 1)
m = new BigInteger(100, 5, rnd);
BigInteger exp = m.subtract(BigInteger.ONE);
BigInteger base = fetchNumber(order1).abs();
BigInteger base = fetchNumber(order).abs();
while(base.compareTo(m) != -1)
base = fetchNumber(order1).abs();
base = fetchNumber(order).abs();
while(base.equals(BigInteger.ZERO))
base = fetchNumber(order1).abs();
base = fetchNumber(order).abs();
BigInteger one = base.modPow(exp, m);
if (!one.equals(BigInteger.ONE)) {
@ -426,7 +582,7 @@ public class BigIntegerTest {
failCount++;
}
}
report("Exponentiation II", failCount);
report("Exponentiation II for " + order + " bits", failCount);
}
private static final int[] mersenne_powers = {
@ -704,36 +860,62 @@ public class BigIntegerTest {
*/
public static void main(String[] args) throws Exception {
// Some variables for sizing test numbers in bits
int order1 = ORDER_MEDIUM;
int order2 = ORDER_SMALL;
int order3 = ORDER_KARATSUBA;
int order4 = ORDER_TOOM_COOK;
if (args.length >0)
order1 = (int)((Integer.parseInt(args[0]))* 3.333);
if (args.length >1)
order2 = (int)((Integer.parseInt(args[1]))* 3.333);
if (args.length >2)
order3 = (int)((Integer.parseInt(args[2]))* 3.333);
if (args.length >3)
order4 = (int)((Integer.parseInt(args[3]))* 3.333);
prime();
nextProbablePrime();
arithmetic();
divideAndRemainder();
pow();
arithmetic(order1); // small numbers
arithmetic(order3); // Karatsuba / Burnikel-Ziegler range
arithmetic(order4); // Toom-Cook range
divideAndRemainder(order1); // small numbers
divideAndRemainder(order3); // Karatsuba / Burnikel-Ziegler range
divideAndRemainder(order4); // Toom-Cook range
pow(order1);
pow(order3);
pow(order4);
square(ORDER_MEDIUM);
square(ORDER_KARATSUBA_SQUARE);
square(ORDER_TOOM_COOK_SQUARE);
bitCount();
bitLength();
bitOps();
bitwise();
bitOps(order1);
bitwise(order1);
shift();
shift(order1);
byteArrayConv();
byteArrayConv(order1);
modInv();
modExp();
modExp2();
modInv(order1); // small numbers
modInv(order3); // Karatsuba / Burnikel-Ziegler range
modInv(order4); // Toom-Cook range
modExp(order1, order2);
modExp2(order1);
stringConv();
serialize();
multiplyLarge();
squareLarge();
if (failure)
throw new RuntimeException("Failure in BigIntegerTest.");
}
@ -747,7 +929,7 @@ public class BigIntegerTest {
*/
private static BigInteger fetchNumber(int order) {
boolean negative = rnd.nextBoolean();
int numType = rnd.nextInt(6);
int numType = rnd.nextInt(7);
BigInteger result = null;
if (order < 2) order = 2;
@ -783,6 +965,19 @@ public class BigIntegerTest {
result = result.or(temp);
}
break;
case 5: // Runs of consecutive ones and zeros
result = ZERO;
int remaining = order;
int bit = rnd.nextInt(2);
while (remaining > 0) {
int runLength = Math.min(remaining, rnd.nextInt(order));
result = result.shiftLeft(runLength);
if (bit > 0)
result = result.add(ONE.shiftLeft(runLength).subtract(ONE));
remaining -= runLength;
bit = 1 - bit;
}
break;
default: // random bits
result = new BigInteger(order, rnd);