367e0a6556
Reviewed-by: rgiulietti
1405 lines
52 KiB
Java
1405 lines
52 KiB
Java
/*
|
|
* Copyright (c) 1998, 2024, 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
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
|
|
/*
|
|
* @test
|
|
* @library /test/lib
|
|
* @build jdk.test.lib.RandomFactory
|
|
* @run main BigIntegerTest
|
|
* @bug 4181191 4161971 4227146 4194389 4823171 4624738 4812225 4837946 4026465 8074460 8078672 8032027 8229845
|
|
* @summary tests methods in BigInteger (use -Dseed=X to set PRNG seed)
|
|
* @run main/timeout=400 BigIntegerTest
|
|
* @author madbot
|
|
* @key randomness
|
|
*/
|
|
|
|
import java.io.File;
|
|
import java.io.FileInputStream;
|
|
import java.io.FileOutputStream;
|
|
import java.io.ObjectInputStream;
|
|
import java.io.ObjectOutputStream;
|
|
import java.math.BigDecimal;
|
|
import java.math.BigInteger;
|
|
import java.util.Random;
|
|
import java.util.function.ToIntFunction;
|
|
import java.util.stream.Collectors;
|
|
import java.util.stream.DoubleStream;
|
|
import java.util.stream.IntStream;
|
|
import java.util.stream.LongStream;
|
|
import java.util.stream.Stream;
|
|
import jdk.test.lib.RandomFactory;
|
|
|
|
/**
|
|
* This is a simple test class created to ensure that the results
|
|
* generated by BigInteger adhere to certain identities. Passing
|
|
* this test is a strong assurance that the BigInteger operations
|
|
* are working correctly.
|
|
*
|
|
* Four arguments may be specified which give the number of
|
|
* decimal digits you desire in the four batches of test numbers.
|
|
*
|
|
* The tests are performed on arrays of random numbers which are
|
|
* generated by a Random class as well as special cases which
|
|
* throw in boundary numbers such as 0, 1, maximum sized, etc.
|
|
*
|
|
*/
|
|
public class BigIntegerTest {
|
|
//
|
|
// Bit large number thresholds based on the int thresholds
|
|
// defined in BigInteger itself:
|
|
//
|
|
// KARATSUBA_THRESHOLD = 80 ints = 2560 bits
|
|
// TOOM_COOK_THRESHOLD = 240 ints = 7680 bits
|
|
// KARATSUBA_SQUARE_THRESHOLD = 128 ints = 4096 bits
|
|
// TOOM_COOK_SQUARE_THRESHOLD = 216 ints = 6912 bits
|
|
//
|
|
// SCHOENHAGE_BASE_CONVERSION_THRESHOLD = 20 ints = 640 bits
|
|
//
|
|
// BURNIKEL_ZIEGLER_THRESHOLD = 80 ints = 2560 bits
|
|
//
|
|
static final int BITS_KARATSUBA = 2560;
|
|
static final int BITS_TOOM_COOK = 7680;
|
|
static final int BITS_KARATSUBA_SQUARE = 4096;
|
|
static final int BITS_TOOM_COOK_SQUARE = 6912;
|
|
static final int BITS_SCHOENHAGE_BASE = 640;
|
|
static final int BITS_BURNIKEL_ZIEGLER = 2560;
|
|
static final int BITS_BURNIKEL_ZIEGLER_OFFSET = 1280;
|
|
|
|
static final int ORDER_SMALL = 60;
|
|
static final int ORDER_MEDIUM = 100;
|
|
// #bits for testing Karatsuba
|
|
static final int ORDER_KARATSUBA = 2760;
|
|
// #bits for testing Toom-Cook and Burnikel-Ziegler
|
|
static final int ORDER_TOOM_COOK = 8000;
|
|
// #bits for testing Karatsuba squaring
|
|
static final int ORDER_KARATSUBA_SQUARE = 4200;
|
|
// #bits for testing Toom-Cook squaring
|
|
static final int ORDER_TOOM_COOK_SQUARE = 7000;
|
|
|
|
static final int SIZE = 1000; // numbers per batch
|
|
|
|
private static Random random = RandomFactory.getRandom();
|
|
|
|
static boolean failure = false;
|
|
|
|
public static void constructor() {
|
|
int failCount = 0;
|
|
|
|
// --- guard condition tests for array indexing ---
|
|
|
|
int arrayLength = 23;
|
|
int halfLength = arrayLength/2;
|
|
byte[] array = new byte[arrayLength];
|
|
random.nextBytes(array);
|
|
|
|
int[][] offLen = new int[][] { // offset, length, num exceptions
|
|
{-1, arrayLength, 1}, // negative offset
|
|
{0, arrayLength, 0}, // OK
|
|
{1, arrayLength, 1}, // length overflow
|
|
{arrayLength - 1, 1, 0}, // OK
|
|
{arrayLength, 1, 1}, // offset overflow
|
|
{0, -1, 1}, // negative length
|
|
{halfLength, arrayLength - halfLength + 1, 1} // length overflow
|
|
};
|
|
|
|
// two's complement
|
|
for (int[] ol : offLen) {
|
|
int numExceptions = 0;
|
|
try {
|
|
BigInteger bi = new BigInteger(array, ol[0], ol[1]);
|
|
} catch (IndexOutOfBoundsException e) {
|
|
numExceptions++;
|
|
}
|
|
if (numExceptions != ol[2]) {
|
|
System.err.println("IndexOutOfBoundsException did not occur for "
|
|
+ " two's complement constructor with parameters offset "
|
|
+ ol[0] + " and length " + ol[1]);
|
|
failCount++;
|
|
}
|
|
}
|
|
|
|
// sign-magnitude
|
|
for (int[] ol : offLen) {
|
|
int numExceptions = 0;
|
|
try {
|
|
BigInteger bi = new BigInteger(1, array, ol[0], ol[1]);
|
|
} catch (IndexOutOfBoundsException e) {
|
|
numExceptions++;
|
|
}
|
|
if (numExceptions != ol[2]) {
|
|
System.err.println("IndexOutOfBoundsException did not occur for "
|
|
+ " sign-magnitude constructor with parameters offset "
|
|
+ ol[0] + " and length " + ol[1]);
|
|
failCount++;
|
|
}
|
|
}
|
|
|
|
// --- tests for creation of zero-valued BigIntegers ---
|
|
|
|
byte[] magZeroLength = new byte[0];
|
|
for (int signum = -1; signum <= 1; signum++) {
|
|
BigInteger bi = new BigInteger(signum, magZeroLength);
|
|
if (bi.compareTo(BigInteger.ZERO) != 0) {
|
|
System.err.println("A: Zero length BigInteger != 0 for signum " + signum);
|
|
failCount++;
|
|
}
|
|
}
|
|
|
|
for (int signum = -1; signum <= 1; signum++) {
|
|
BigInteger bi = new BigInteger(signum, magZeroLength, 0, 0);
|
|
if (bi.compareTo(BigInteger.ZERO) != 0) {
|
|
System.err.println("B: Zero length BigInteger != 0 for signum " + signum);
|
|
failCount++;
|
|
}
|
|
}
|
|
|
|
byte[] magNonZeroLength = new byte[42];
|
|
random.nextBytes(magNonZeroLength);
|
|
for (int signum = -1; signum <= 1; signum++) {
|
|
BigInteger bi = new BigInteger(signum, magNonZeroLength, 0, 0);
|
|
if (bi.compareTo(BigInteger.ZERO) != 0) {
|
|
System.err.println("C: Zero length BigInteger != 0 for signum " + signum);
|
|
failCount++;
|
|
}
|
|
}
|
|
|
|
// --- tests for accurate creation of non-zero BigIntegers ---
|
|
|
|
for (int i = 0; i < SIZE; i++) {
|
|
// create reference value via a different code path from those tested
|
|
BigInteger reference = new BigInteger(2 + random.nextInt(336), 4, random);
|
|
|
|
byte[] refArray = reference.toByteArray();
|
|
int refLen = refArray.length;
|
|
int factor = random.nextInt(5);
|
|
int objLen = refArray.length + factor*random.nextInt(refArray.length) + 1;
|
|
int offset = random.nextInt(objLen - refLen);
|
|
byte[] objArray = new byte[objLen];
|
|
System.arraycopy(refArray, 0, objArray, offset, refLen);
|
|
|
|
BigInteger twosComp = new BigInteger(objArray, offset, refLen);
|
|
if (twosComp.compareTo(reference) != 0) {
|
|
System.err.println("Two's-complement BigInteger not equal for offset " +
|
|
offset + " and length " + refLen);
|
|
failCount++;
|
|
}
|
|
|
|
boolean isNegative = random.nextBoolean();
|
|
BigInteger signMag = new BigInteger(isNegative ? -1 : 1, objArray, offset, refLen);
|
|
if (signMag.compareTo(isNegative ? reference.negate() : reference) != 0) {
|
|
System.err.println("Sign-magnitude BigInteger not equal for offset " +
|
|
offset + " and length " + refLen);
|
|
failCount++;
|
|
}
|
|
}
|
|
|
|
report("Constructor", failCount);
|
|
}
|
|
|
|
public static void pow(int order) {
|
|
int failCount1 = 0;
|
|
|
|
for (int i=0; i<SIZE; i++) {
|
|
// Test identity x^power == x*x*x ... *x
|
|
int power = random.nextInt(6) + 2;
|
|
BigInteger x = fetchNumber(order);
|
|
BigInteger y = x.pow(power);
|
|
BigInteger z = x;
|
|
|
|
for (int j=1; j<power; j++)
|
|
z = z.multiply(x);
|
|
|
|
if (!y.equals(z))
|
|
failCount1++;
|
|
}
|
|
report("pow for " + order + " bits", failCount1);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
private static void printErr(String msg) {
|
|
System.err.println(msg);
|
|
}
|
|
|
|
private static int checkResult(BigInteger expected, BigInteger actual,
|
|
String failureMessage) {
|
|
if (expected.compareTo(actual) != 0) {
|
|
printErr(failureMessage + " - expected: " + expected
|
|
+ ", actual: " + actual);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
private static void squareRootSmall() {
|
|
int failCount = 0;
|
|
|
|
// A negative value should cause an exception.
|
|
BigInteger n = BigInteger.ONE.negate();
|
|
BigInteger s;
|
|
try {
|
|
s = n.sqrt();
|
|
// If sqrt() does not throw an exception that is a failure.
|
|
failCount++;
|
|
printErr("sqrt() of negative number did not throw an exception");
|
|
} catch (ArithmeticException expected) {
|
|
// A negative value should cause an exception and is not a failure.
|
|
}
|
|
|
|
// A zero value should return BigInteger.ZERO.
|
|
failCount += checkResult(BigInteger.ZERO, BigInteger.ZERO.sqrt(),
|
|
"sqrt(0) != BigInteger.ZERO");
|
|
|
|
// 1 <= value < 4 should return BigInteger.ONE.
|
|
long[] smalls = new long[] {1, 2, 3};
|
|
for (long small : smalls) {
|
|
failCount += checkResult(BigInteger.ONE,
|
|
BigInteger.valueOf(small).sqrt(), "sqrt("+small+") != 1");
|
|
}
|
|
|
|
report("squareRootSmall", failCount);
|
|
}
|
|
|
|
private static void perfectSquaresLong() {
|
|
/* For every long value n in [0, 2^32) such that x == n * n,
|
|
* n - 1 <= (long) Math.sqrt(x >= 0 ? x : x + 0x1p64) <= n
|
|
* must be true.
|
|
* This property is used to implement MutableBigInteger.unsignedLongSqrt().
|
|
*/
|
|
int failCount = 0;
|
|
|
|
long limit = 1L << 32;
|
|
for (long n = 0; n < limit; n++) {
|
|
long x = n * n;
|
|
long s = (long) Math.sqrt(x >= 0 ? x : x + 0x1p64);
|
|
if (!(s == n || s == n - 1)) {
|
|
failCount++;
|
|
System.err.println(s + "^2 != " + x + " && (" + s + "+1)^2 != " + x);
|
|
}
|
|
}
|
|
|
|
report("perfectSquaresLong", failCount);
|
|
}
|
|
|
|
public static void squareRoot() {
|
|
squareRootSmall();
|
|
perfectSquaresLong();
|
|
|
|
ToIntFunction<BigInteger> f = (n) -> {
|
|
int failCount = 0;
|
|
|
|
// square root of n^2 -> n
|
|
BigInteger n2 = n.pow(2);
|
|
failCount += checkResult(n, n2.sqrt(), "sqrt() n^2 -> n");
|
|
|
|
// square root of n^2 + 1 -> n
|
|
BigInteger n2up = n2.add(BigInteger.ONE);
|
|
failCount += checkResult(n, n2up.sqrt(), "sqrt() n^2 + 1 -> n");
|
|
|
|
// square root of (n + 1)^2 - 1 -> n
|
|
BigInteger up =
|
|
n.add(BigInteger.ONE).pow(2).subtract(BigInteger.ONE);
|
|
failCount += checkResult(n, up.sqrt(), "sqrt() (n + 1)^2 - 1 -> n");
|
|
|
|
// sqrt(n)^2 <= n
|
|
BigInteger s = n.sqrt();
|
|
if (s.multiply(s).compareTo(n) > 0) {
|
|
failCount++;
|
|
printErr("sqrt(n)^2 > n for n = " + n);
|
|
}
|
|
|
|
// (sqrt(n) + 1)^2 > n
|
|
if (s.add(BigInteger.ONE).pow(2).compareTo(n) <= 0) {
|
|
failCount++;
|
|
printErr("(sqrt(n) + 1)^2 <= n for n = " + n);
|
|
}
|
|
|
|
return failCount;
|
|
};
|
|
|
|
Stream.Builder<BigInteger> sb = Stream.builder();
|
|
int maxExponent = Double.MAX_EXPONENT + 1;
|
|
for (int i = 1; i <= maxExponent; i++) {
|
|
BigInteger p2 = BigInteger.ONE.shiftLeft(i);
|
|
sb.add(p2.subtract(BigInteger.ONE));
|
|
sb.add(p2);
|
|
sb.add(p2.add(BigInteger.ONE));
|
|
}
|
|
sb.add((new BigDecimal(Double.MAX_VALUE)).toBigInteger());
|
|
sb.add((new BigDecimal(Double.MAX_VALUE)).toBigInteger().add(BigInteger.ONE));
|
|
report("squareRoot for 2^N and 2^N - 1, 1 <= N <= Double.MAX_EXPONENT",
|
|
sb.build().collect(Collectors.summingInt(f)));
|
|
|
|
IntStream ints = random.ints(SIZE, 4, Integer.MAX_VALUE);
|
|
report("squareRoot for int", ints.mapToObj(x ->
|
|
BigInteger.valueOf(x)).collect(Collectors.summingInt(f)));
|
|
|
|
LongStream longs = random.longs(SIZE, (long)Integer.MAX_VALUE + 1L,
|
|
Long.MAX_VALUE);
|
|
report("squareRoot for long", longs.mapToObj(x ->
|
|
BigInteger.valueOf(x)).collect(Collectors.summingInt(f)));
|
|
|
|
DoubleStream doubles = random.doubles(SIZE,
|
|
(double) Long.MAX_VALUE + 1.0, Math.sqrt(Double.MAX_VALUE));
|
|
report("squareRoot for double", doubles.mapToObj(x ->
|
|
BigDecimal.valueOf(x).toBigInteger()).collect(Collectors.summingInt(f)));
|
|
}
|
|
|
|
public static void squareRootAndRemainder() {
|
|
ToIntFunction<BigInteger> g = (n) -> {
|
|
int failCount = 0;
|
|
BigInteger n2 = n.pow(2);
|
|
|
|
// square root of n^2 -> n
|
|
BigInteger[] actual = n2.sqrtAndRemainder();
|
|
failCount += checkResult(n, actual[0], "sqrtAndRemainder()[0]");
|
|
failCount += checkResult(BigInteger.ZERO, actual[1],
|
|
"sqrtAndRemainder()[1]");
|
|
|
|
// square root of n^2 + 1 -> n
|
|
BigInteger n2up = n2.add(BigInteger.ONE);
|
|
actual = n2up.sqrtAndRemainder();
|
|
failCount += checkResult(n, actual[0], "sqrtAndRemainder()[0]");
|
|
failCount += checkResult(BigInteger.ONE, actual[1],
|
|
"sqrtAndRemainder()[1]");
|
|
|
|
// square root of (n + 1)^2 - 1 -> n
|
|
BigInteger up =
|
|
n.add(BigInteger.ONE).pow(2).subtract(BigInteger.ONE);
|
|
actual = up.sqrtAndRemainder();
|
|
failCount += checkResult(n, actual[0], "sqrtAndRemainder()[0]");
|
|
BigInteger r = up.subtract(n2);
|
|
failCount += checkResult(r, actual[1], "sqrtAndRemainder()[1]");
|
|
|
|
return failCount;
|
|
};
|
|
|
|
IntStream bits = random.ints(SIZE, 3, Short.MAX_VALUE);
|
|
report("sqrtAndRemainder", bits.mapToObj(x ->
|
|
BigInteger.valueOf(x)).collect(Collectors.summingInt(g)));
|
|
}
|
|
|
|
public static void arithmetic(int order) {
|
|
int failCount = 0;
|
|
|
|
for (int i=0; i<SIZE; i++) {
|
|
BigInteger x = fetchNumber(order);
|
|
while(x.compareTo(BigInteger.ZERO) != 1)
|
|
x = fetchNumber(order);
|
|
BigInteger y = fetchNumber(order/2);
|
|
while(x.compareTo(y) == -1)
|
|
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));
|
|
baz = baz.subtract(x);
|
|
if (!baz.equals(BigInteger.ZERO))
|
|
failCount++;
|
|
}
|
|
report("Arithmetic I for " + order + " bits", failCount);
|
|
|
|
failCount = 0;
|
|
for (int i=0; i<100; i++) {
|
|
BigInteger x = fetchNumber(order);
|
|
while(x.compareTo(BigInteger.ZERO) != 1)
|
|
x = fetchNumber(order);
|
|
BigInteger y = fetchNumber(order/2);
|
|
while(x.compareTo(y) == -1)
|
|
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]);
|
|
baz[0] = baz[0].subtract(x);
|
|
if (!baz[0].equals(BigInteger.ZERO))
|
|
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 + random.nextInt(31);
|
|
BigInteger w = u.shiftLeft(a);
|
|
|
|
BigInteger y = fetchNumber(BITS_KARATSUBA - 32 - 1);
|
|
BigInteger v = base.add(y);
|
|
int b = 1 + random.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 + random.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 + random.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);
|
|
}
|
|
|
|
/**
|
|
* Sanity test for Burnikel-Ziegler division. The Burnikel-Ziegler division
|
|
* algorithm is used when each of the dividend and the divisor has at least
|
|
* a specified number of ints in its representation. This test is based on
|
|
* the observation that if {@code w = u*pow(2,a)} and {@code z = v*pow(2,b)}
|
|
* where {@code abs(u) > abs(v)} and {@code a > b && b > 0}, then if
|
|
* {@code w/z = q1*z + r1} and {@code u/v = q2*v + r2}, then
|
|
* {@code q1 = q2*pow(2,a-b)} and {@code r1 = r2*pow(2,b)}. The test
|
|
* ensures that {@code v} is just under the B-Z threshold, that {@code z} is
|
|
* over the threshold and {@code w} is much larger than {@code z}. This
|
|
* implies that {@code u/v} uses the standard division algorithm and
|
|
* {@code w/z} uses the B-Z algorithm. The results of the two algorithms
|
|
* are then compared using the observation described in the foregoing and
|
|
* if they are not equal a failure is logged.
|
|
*/
|
|
public static void divideLarge() {
|
|
int failCount = 0;
|
|
|
|
BigInteger base = BigInteger.ONE.shiftLeft(BITS_BURNIKEL_ZIEGLER + BITS_BURNIKEL_ZIEGLER_OFFSET - 33);
|
|
for (int i=0; i<SIZE; i++) {
|
|
BigInteger addend = new BigInteger(BITS_BURNIKEL_ZIEGLER + BITS_BURNIKEL_ZIEGLER_OFFSET - 34, random);
|
|
BigInteger v = base.add(addend);
|
|
|
|
BigInteger u = v.multiply(BigInteger.valueOf(2 + random.nextInt(Short.MAX_VALUE - 1)));
|
|
|
|
if(random.nextBoolean()) {
|
|
u = u.negate();
|
|
}
|
|
if(random.nextBoolean()) {
|
|
v = v.negate();
|
|
}
|
|
|
|
int a = BITS_BURNIKEL_ZIEGLER_OFFSET + random.nextInt(16);
|
|
int b = 1 + random.nextInt(16);
|
|
BigInteger w = u.multiply(BigInteger.ONE.shiftLeft(a));
|
|
BigInteger z = v.multiply(BigInteger.ONE.shiftLeft(b));
|
|
|
|
BigInteger[] divideResult = u.divideAndRemainder(v);
|
|
divideResult[0] = divideResult[0].multiply(BigInteger.ONE.shiftLeft(a - b));
|
|
divideResult[1] = divideResult[1].multiply(BigInteger.ONE.shiftLeft(b));
|
|
BigInteger[] bzResult = w.divideAndRemainder(z);
|
|
|
|
if (divideResult[0].compareTo(bzResult[0]) != 0 ||
|
|
divideResult[1].compareTo(bzResult[1]) != 0) {
|
|
failCount++;
|
|
}
|
|
}
|
|
|
|
report("divideLarge", failCount);
|
|
}
|
|
|
|
public static void bitCount() {
|
|
int failCount = 0;
|
|
|
|
for (int i=0; i<SIZE*10; i++) {
|
|
int x = random.nextInt();
|
|
BigInteger bigX = BigInteger.valueOf((long)x);
|
|
int bit = (x < 0 ? 0 : 1);
|
|
int tmp = x, bitCount = 0;
|
|
for (int j=0; j<32; j++) {
|
|
bitCount += ((tmp & 1) == bit ? 1 : 0);
|
|
tmp >>= 1;
|
|
}
|
|
|
|
if (bigX.bitCount() != bitCount) {
|
|
//System.err.println(x+": "+bitCount+", "+bigX.bitCount());
|
|
failCount++;
|
|
}
|
|
}
|
|
report("Bit Count", failCount);
|
|
}
|
|
|
|
public static void bitLength() {
|
|
int failCount = 0;
|
|
|
|
for (int i=0; i<SIZE*10; i++) {
|
|
int x = random.nextInt();
|
|
BigInteger bigX = BigInteger.valueOf((long)x);
|
|
int signBit = (x < 0 ? 0x80000000 : 0);
|
|
int tmp = x, bitLength, j;
|
|
for (j=0; j<32 && (tmp & 0x80000000)==signBit; j++)
|
|
tmp <<= 1;
|
|
bitLength = 32 - j;
|
|
|
|
if (bigX.bitLength() != bitLength) {
|
|
//System.err.println(x+": "+bitLength+", "+bigX.bitLength());
|
|
failCount++;
|
|
}
|
|
}
|
|
|
|
report("BitLength", failCount);
|
|
}
|
|
|
|
public static void bitOps(int order) {
|
|
int failCount1 = 0, failCount2 = 0, failCount3 = 0;
|
|
|
|
for (int i=0; i<SIZE*5; i++) {
|
|
BigInteger x = fetchNumber(order);
|
|
BigInteger y;
|
|
|
|
// Test setBit and clearBit (and testBit)
|
|
if (x.signum() < 0) {
|
|
y = BigInteger.valueOf(-1);
|
|
for (int j=0; j<x.bitLength(); j++)
|
|
if (!x.testBit(j))
|
|
y = y.clearBit(j);
|
|
} else {
|
|
y = BigInteger.ZERO;
|
|
for (int j=0; j<x.bitLength(); j++)
|
|
if (x.testBit(j))
|
|
y = y.setBit(j);
|
|
}
|
|
if (!x.equals(y))
|
|
failCount1++;
|
|
|
|
// 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))
|
|
y = y.flipBit(j);
|
|
if (!x.equals(y))
|
|
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(order);
|
|
|
|
// Test getLowestSetBit()
|
|
int k = x.getLowestSetBit();
|
|
if (x.signum() == 0) {
|
|
if (k != -1)
|
|
failCount3++;
|
|
} else {
|
|
BigInteger z = x.and(x.negate());
|
|
int j;
|
|
for (j=0; j<z.bitLength() && !z.testBit(j); j++)
|
|
;
|
|
if (k != j)
|
|
failCount3++;
|
|
}
|
|
}
|
|
report("getLowestSetBit for " + order + " bits", failCount3);
|
|
}
|
|
|
|
public static void bitwise(int order) {
|
|
|
|
// Test identity x^y == x|y &~ x&y
|
|
int failCount = 0;
|
|
for (int i=0; i<SIZE; i++) {
|
|
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 (^ | & ~) for " + order + " bits", failCount);
|
|
|
|
// Test identity x &~ y == ~(~x | y)
|
|
failCount = 0;
|
|
for (int i=0; i<SIZE; i++) {
|
|
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 (&~ | ~) for " + order + " bits", failCount);
|
|
}
|
|
|
|
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(order);
|
|
int n = Math.abs(random.nextInt()%200);
|
|
|
|
if (!x.shiftLeft(n).equals
|
|
(x.multiply(BigInteger.valueOf(2L).pow(n))))
|
|
failCount1++;
|
|
|
|
BigInteger y[] =x.divideAndRemainder(BigInteger.valueOf(2L).pow(n));
|
|
BigInteger z = (x.signum()<0 && y[1].signum()!=0
|
|
? y[0].subtract(BigInteger.ONE)
|
|
: y[0]);
|
|
|
|
BigInteger b = x.shiftRight(n);
|
|
|
|
if (!b.equals(z)) {
|
|
System.err.println("Input is "+x.toString(2));
|
|
System.err.println("shift is "+n);
|
|
|
|
System.err.println("Divided "+z.toString(2));
|
|
System.err.println("Shifted is "+b.toString(2));
|
|
if (b.toString().equals(z.toString()))
|
|
System.err.println("Houston, we have a problem.");
|
|
failCount2++;
|
|
}
|
|
|
|
if (!x.shiftLeft(n).shiftRight(n).equals(x))
|
|
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(int order) {
|
|
int failCount1 = 0;
|
|
|
|
for (int i=0; i<SIZE; i++) {
|
|
BigInteger x = fetchNumber(order).abs();
|
|
while(x.compareTo(BigInteger.valueOf(3L)) != 1)
|
|
x = fetchNumber(order).abs();
|
|
BigInteger z = x.divide(BigInteger.valueOf(2L));
|
|
BigInteger y[] = x.divideAndRemainder(x);
|
|
if (!y[0].equals(BigInteger.ONE)) {
|
|
failCount1++;
|
|
System.err.println("fail1 x :"+x);
|
|
System.err.println(" y :"+y);
|
|
}
|
|
else if (!y[1].equals(BigInteger.ZERO)) {
|
|
failCount1++;
|
|
System.err.println("fail2 x :"+x);
|
|
System.err.println(" y :"+y);
|
|
}
|
|
|
|
y = x.divideAndRemainder(z);
|
|
if (!y[0].equals(BigInteger.valueOf(2))) {
|
|
failCount1++;
|
|
System.err.println("fail3 x :"+x);
|
|
System.err.println(" y :"+y);
|
|
}
|
|
}
|
|
report("divideAndRemainder for " + order + " bits", failCount1);
|
|
}
|
|
|
|
public static void stringConv() {
|
|
int failCount = 0;
|
|
|
|
// Generic string conversion.
|
|
for (int i=0; i<100; i++) {
|
|
byte xBytes[] = new byte[Math.abs(random.nextInt())%200+1];
|
|
random.nextBytes(xBytes);
|
|
BigInteger x = new BigInteger(xBytes);
|
|
|
|
for (int radix=Character.MIN_RADIX; radix < Character.MAX_RADIX; radix++) {
|
|
String result = x.toString(radix);
|
|
BigInteger test = new BigInteger(result, radix);
|
|
if (!test.equals(x)) {
|
|
failCount++;
|
|
System.err.println("BigInteger toString: "+x);
|
|
System.err.println("Test: "+test);
|
|
System.err.println(radix);
|
|
}
|
|
}
|
|
}
|
|
|
|
// String conversion straddling the Schoenhage algorithm crossover
|
|
// threshold, and at twice and four times the threshold.
|
|
for (int k = 0; k <= 2; k++) {
|
|
int factor = 1 << k;
|
|
int upper = factor * BITS_SCHOENHAGE_BASE + 33;
|
|
int lower = upper - 35;
|
|
|
|
for (int bits = upper; bits >= lower; bits--) {
|
|
for (int i = 0; i < 50; i++) {
|
|
BigInteger x = BigInteger.ONE.shiftLeft(bits - 1).or(new BigInteger(bits - 2, random));
|
|
|
|
for (int radix = Character.MIN_RADIX; radix < Character.MAX_RADIX; radix++) {
|
|
String result = x.toString(radix);
|
|
BigInteger test = new BigInteger(result, radix);
|
|
if (!test.equals(x)) {
|
|
failCount++;
|
|
System.err.println("BigInteger toString: " + x);
|
|
System.err.println("Test: " + test);
|
|
System.err.println(radix);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check value with many trailing zeros.
|
|
String val = "123456789" + "0".repeat(200);
|
|
BigInteger b = new BigInteger(val);
|
|
String s = b.toString();
|
|
if (!val.equals(s)) {
|
|
System.err.format("Expected length %d but got %d%n",
|
|
val.length(), s.length());
|
|
failCount++;
|
|
}
|
|
|
|
report("String Conversion", failCount);
|
|
}
|
|
|
|
public static void byteArrayConv(int order) {
|
|
int failCount = 0;
|
|
|
|
for (int i=0; i<SIZE; i++) {
|
|
BigInteger x = fetchNumber(order);
|
|
while (x.equals(BigInteger.ZERO))
|
|
x = fetchNumber(order);
|
|
BigInteger y = new BigInteger(x.toByteArray());
|
|
if (!x.equals(y)) {
|
|
failCount++;
|
|
System.err.println("orig is "+x);
|
|
System.err.println("new is "+y);
|
|
}
|
|
}
|
|
report("Array Conversion for " + order + " bits", failCount);
|
|
}
|
|
|
|
public static void modInv(int order) {
|
|
int failCount = 0, successCount = 0, nonInvCount = 0;
|
|
|
|
for (int i=0; i<SIZE; i++) {
|
|
BigInteger x = fetchNumber(order);
|
|
while(x.equals(BigInteger.ZERO))
|
|
x = fetchNumber(order);
|
|
BigInteger m = fetchNumber(order).abs();
|
|
while(m.compareTo(BigInteger.ONE) != 1)
|
|
m = fetchNumber(order).abs();
|
|
|
|
try {
|
|
BigInteger inv = x.modInverse(m);
|
|
BigInteger prod = inv.multiply(x).remainder(m);
|
|
|
|
if (prod.signum() == -1)
|
|
prod = prod.add(m);
|
|
|
|
if (prod.equals(BigInteger.ONE))
|
|
successCount++;
|
|
else
|
|
failCount++;
|
|
} catch(ArithmeticException e) {
|
|
nonInvCount++;
|
|
}
|
|
}
|
|
report("Modular Inverse for " + order + " bits", failCount);
|
|
}
|
|
|
|
public static void modExp(int order1, int order2) {
|
|
int failCount = 0;
|
|
|
|
for (int i=0; i<SIZE/10; i++) {
|
|
BigInteger m = fetchNumber(order1).abs();
|
|
while(m.compareTo(BigInteger.ONE) != 1)
|
|
m = fetchNumber(order1).abs();
|
|
BigInteger base = fetchNumber(order2);
|
|
BigInteger exp = fetchNumber(8).abs();
|
|
|
|
BigInteger z = base.modPow(exp, m);
|
|
BigInteger w = base.pow(exp.intValue()).mod(m);
|
|
if (!z.equals(w)) {
|
|
System.err.println("z is "+z);
|
|
System.err.println("w is "+w);
|
|
System.err.println("mod is "+m);
|
|
System.err.println("base is "+base);
|
|
System.err.println("exp is "+exp);
|
|
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(int order) {
|
|
int failCount = 0;
|
|
|
|
for (int i=0; i<10; i++) {
|
|
BigInteger m = new BigInteger(100, 5, random);
|
|
while(m.compareTo(BigInteger.ONE) != 1)
|
|
m = new BigInteger(100, 5, random);
|
|
BigInteger exp = m.subtract(BigInteger.ONE);
|
|
BigInteger base = fetchNumber(order).abs();
|
|
while(base.compareTo(m) != -1)
|
|
base = fetchNumber(order).abs();
|
|
while(base.equals(BigInteger.ZERO))
|
|
base = fetchNumber(order).abs();
|
|
|
|
BigInteger one = base.modPow(exp, m);
|
|
if (!one.equals(BigInteger.ONE)) {
|
|
System.err.println("m is "+m);
|
|
System.err.println("base is "+base);
|
|
System.err.println("exp is "+exp);
|
|
failCount++;
|
|
}
|
|
}
|
|
report("Exponentiation II for " + order + " bits", failCount);
|
|
}
|
|
|
|
private static final int[] mersenne_powers = {
|
|
521, 607, 1279, 2203, 2281, 3217, 4253, 4423, 9689, 9941, 11213, 19937,
|
|
21701, 23209, 44497, 86243, 110503, 132049, 216091, 756839, 859433,
|
|
1257787, 1398269, 2976221, 3021377, 6972593, 13466917 };
|
|
|
|
private static final long[] carmichaels = {
|
|
561,1105,1729,2465,2821,6601,8911,10585,15841,29341,41041,46657,52633,
|
|
62745,63973,75361,101101,115921,126217,162401,172081,188461,252601,
|
|
278545,294409,314821,334153,340561,399001,410041,449065,488881,512461,
|
|
225593397919L };
|
|
|
|
// Note: testing the larger ones takes too long.
|
|
private static final int NUM_MERSENNES_TO_TEST = 7;
|
|
// Note: this constant used for computed Carmichaels, not the array above
|
|
private static final int NUM_CARMICHAELS_TO_TEST = 5;
|
|
|
|
private static final String[] customer_primes = {
|
|
"120000000000000000000000000000000019",
|
|
"633825300114114700748351603131",
|
|
"1461501637330902918203684832716283019651637554291",
|
|
"779626057591079617852292862756047675913380626199",
|
|
"857591696176672809403750477631580323575362410491",
|
|
"910409242326391377348778281801166102059139832131",
|
|
"929857869954035706722619989283358182285540127919",
|
|
"961301750640481375785983980066592002055764391999",
|
|
"1267617700951005189537696547196156120148404630231",
|
|
"1326015641149969955786344600146607663033642528339" };
|
|
|
|
private static final BigInteger ZERO = BigInteger.ZERO;
|
|
private static final BigInteger ONE = BigInteger.ONE;
|
|
private static final BigInteger TWO = new BigInteger("2");
|
|
private static final BigInteger SIX = new BigInteger("6");
|
|
private static final BigInteger TWELVE = new BigInteger("12");
|
|
private static final BigInteger EIGHTEEN = new BigInteger("18");
|
|
|
|
public static void prime() {
|
|
BigInteger p1, p2, c1;
|
|
int failCount = 0;
|
|
|
|
// Test consistency
|
|
for(int i=0; i<10; i++) {
|
|
p1 = BigInteger.probablePrime(100, random);
|
|
if (!p1.isProbablePrime(100)) {
|
|
System.err.println("Consistency "+p1.toString(16));
|
|
failCount++;
|
|
}
|
|
}
|
|
|
|
// Test some known Mersenne primes (2^n)-1
|
|
// The array holds the exponents, not the numbers being tested
|
|
for (int i=0; i<NUM_MERSENNES_TO_TEST; i++) {
|
|
p1 = new BigInteger("2");
|
|
p1 = p1.pow(mersenne_powers[i]);
|
|
p1 = p1.subtract(BigInteger.ONE);
|
|
if (!p1.isProbablePrime(100)) {
|
|
System.err.println("Mersenne prime "+i+ " failed.");
|
|
failCount++;
|
|
}
|
|
}
|
|
|
|
// Test some primes reported by customers as failing in the past
|
|
for (int i=0; i<customer_primes.length; i++) {
|
|
p1 = new BigInteger(customer_primes[i]);
|
|
if (!p1.isProbablePrime(100)) {
|
|
System.err.println("Customer prime "+i+ " failed.");
|
|
failCount++;
|
|
}
|
|
}
|
|
|
|
// Test some known Carmichael numbers.
|
|
for (int i=0; i<carmichaels.length; i++) {
|
|
c1 = BigInteger.valueOf(carmichaels[i]);
|
|
if(c1.isProbablePrime(100)) {
|
|
System.err.println("Carmichael "+i+ " reported as prime.");
|
|
failCount++;
|
|
}
|
|
}
|
|
|
|
// Test some computed Carmichael numbers.
|
|
// Numbers of the form (6k+1)(12k+1)(18k+1) are Carmichael numbers if
|
|
// each of the factors is prime
|
|
int found = 0;
|
|
BigInteger f1 = new BigInteger(40, 100, random);
|
|
while (found < NUM_CARMICHAELS_TO_TEST) {
|
|
BigInteger k = null;
|
|
BigInteger f2, f3;
|
|
f1 = f1.nextProbablePrime();
|
|
BigInteger[] result = f1.subtract(ONE).divideAndRemainder(SIX);
|
|
if (result[1].equals(ZERO)) {
|
|
k = result[0];
|
|
f2 = k.multiply(TWELVE).add(ONE);
|
|
if (f2.isProbablePrime(100)) {
|
|
f3 = k.multiply(EIGHTEEN).add(ONE);
|
|
if (f3.isProbablePrime(100)) {
|
|
c1 = f1.multiply(f2).multiply(f3);
|
|
if (c1.isProbablePrime(100)) {
|
|
System.err.println("Computed Carmichael "
|
|
+c1.toString(16));
|
|
failCount++;
|
|
}
|
|
found++;
|
|
}
|
|
}
|
|
}
|
|
f1 = f1.add(TWO);
|
|
}
|
|
|
|
// Test some composites that are products of 2 primes
|
|
for (int i=0; i<50; i++) {
|
|
p1 = BigInteger.probablePrime(100, random);
|
|
p2 = BigInteger.probablePrime(100, random);
|
|
c1 = p1.multiply(p2);
|
|
if (c1.isProbablePrime(100)) {
|
|
System.err.println("Composite failed "+c1.toString(16));
|
|
failCount++;
|
|
}
|
|
}
|
|
|
|
for (int i=0; i<4; i++) {
|
|
p1 = BigInteger.probablePrime(600, random);
|
|
p2 = BigInteger.probablePrime(600, random);
|
|
c1 = p1.multiply(p2);
|
|
if (c1.isProbablePrime(100)) {
|
|
System.err.println("Composite failed "+c1.toString(16));
|
|
failCount++;
|
|
}
|
|
}
|
|
|
|
report("Prime", failCount);
|
|
}
|
|
|
|
private static final long[] primesTo100 = {
|
|
2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97
|
|
};
|
|
|
|
private static final long[] aPrimeSequence = {
|
|
1999999003L, 1999999013L, 1999999049L, 1999999061L, 1999999081L,
|
|
1999999087L, 1999999093L, 1999999097L, 1999999117L, 1999999121L,
|
|
1999999151L, 1999999171L, 1999999207L, 1999999219L, 1999999271L,
|
|
1999999321L, 1999999373L, 1999999423L, 1999999439L, 1999999499L,
|
|
1999999553L, 1999999559L, 1999999571L, 1999999609L, 1999999613L,
|
|
1999999621L, 1999999643L, 1999999649L, 1999999657L, 1999999747L,
|
|
1999999763L, 1999999777L, 1999999811L, 1999999817L, 1999999829L,
|
|
1999999853L, 1999999861L, 1999999871L, 1999999873
|
|
};
|
|
|
|
public static void nextProbablePrime() throws Exception {
|
|
int failCount = 0;
|
|
BigInteger p1, p2, p3;
|
|
p1 = p2 = p3 = ZERO;
|
|
|
|
// First test nextProbablePrime on the low range starting at zero
|
|
for (int i=0; i<primesTo100.length; i++) {
|
|
p1 = p1.nextProbablePrime();
|
|
if (p1.longValue() != primesTo100[i]) {
|
|
System.err.println("low range primes failed");
|
|
System.err.println("p1 is "+p1);
|
|
System.err.println("expected "+primesTo100[i]);
|
|
failCount++;
|
|
}
|
|
}
|
|
|
|
// Test nextProbablePrime on a relatively small, known prime sequence
|
|
p1 = BigInteger.valueOf(aPrimeSequence[0]);
|
|
for (int i=1; i<aPrimeSequence.length; i++) {
|
|
p1 = p1.nextProbablePrime();
|
|
if (p1.longValue() != aPrimeSequence[i]) {
|
|
System.err.println("prime sequence failed");
|
|
failCount++;
|
|
}
|
|
}
|
|
|
|
// Next, pick some large primes, use nextProbablePrime to find the
|
|
// next one, and make sure there are no primes in between
|
|
for (int i=0; i<100; i+=10) {
|
|
p1 = BigInteger.probablePrime(50 + i, random);
|
|
p2 = p1.add(ONE);
|
|
p3 = p1.nextProbablePrime();
|
|
while(p2.compareTo(p3) < 0) {
|
|
if (p2.isProbablePrime(100)){
|
|
System.err.println("nextProbablePrime failed");
|
|
System.err.println("along range "+p1.toString(16));
|
|
System.err.println("to "+p3.toString(16));
|
|
failCount++;
|
|
break;
|
|
}
|
|
p2 = p2.add(ONE);
|
|
}
|
|
}
|
|
|
|
report("nextProbablePrime", failCount);
|
|
}
|
|
|
|
public static void serialize() throws Exception {
|
|
int failCount = 0;
|
|
|
|
String bitPatterns[] = {
|
|
"ffffffff00000000ffffffff00000000ffffffff00000000",
|
|
"ffffffffffffffffffffffff000000000000000000000000",
|
|
"ffffffff0000000000000000000000000000000000000000",
|
|
"10000000ffffffffffffffffffffffffffffffffffffffff",
|
|
"100000000000000000000000000000000000000000000000",
|
|
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
|
"-ffffffff00000000ffffffff00000000ffffffff00000000",
|
|
"-ffffffffffffffffffffffff000000000000000000000000",
|
|
"-ffffffff0000000000000000000000000000000000000000",
|
|
"-10000000ffffffffffffffffffffffffffffffffffffffff",
|
|
"-100000000000000000000000000000000000000000000000",
|
|
"-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
|
};
|
|
|
|
for(int i = 0; i < bitPatterns.length; i++) {
|
|
BigInteger b1 = new BigInteger(bitPatterns[i], 16);
|
|
BigInteger b2 = null;
|
|
|
|
File f = new File("serialtest");
|
|
|
|
try (FileOutputStream fos = new FileOutputStream(f)) {
|
|
try (ObjectOutputStream oos = new ObjectOutputStream(fos)) {
|
|
oos.writeObject(b1);
|
|
oos.flush();
|
|
}
|
|
|
|
try (FileInputStream fis = new FileInputStream(f);
|
|
ObjectInputStream ois = new ObjectInputStream(fis))
|
|
{
|
|
b2 = (BigInteger)ois.readObject();
|
|
}
|
|
|
|
if (!b1.equals(b2) ||
|
|
!b1.equals(b1.or(b2))) {
|
|
failCount++;
|
|
System.err.println("Serialized failed for hex " +
|
|
b1.toString(16));
|
|
}
|
|
}
|
|
f.delete();
|
|
}
|
|
|
|
for(int i=0; i<10; i++) {
|
|
BigInteger b1 = fetchNumber(random.nextInt(100));
|
|
BigInteger b2 = null;
|
|
File f = new File("serialtest");
|
|
try (FileOutputStream fos = new FileOutputStream(f)) {
|
|
try (ObjectOutputStream oos = new ObjectOutputStream(fos)) {
|
|
oos.writeObject(b1);
|
|
oos.flush();
|
|
}
|
|
|
|
try (FileInputStream fis = new FileInputStream(f);
|
|
ObjectInputStream ois = new ObjectInputStream(fis))
|
|
{
|
|
b2 = (BigInteger)ois.readObject();
|
|
}
|
|
}
|
|
|
|
if (!b1.equals(b2) ||
|
|
!b1.equals(b1.or(b2)))
|
|
failCount++;
|
|
f.delete();
|
|
}
|
|
|
|
report("Serialize", failCount);
|
|
}
|
|
|
|
/**
|
|
* Main to interpret arguments and run several tests.
|
|
*
|
|
* Up to three arguments may be given to specify the SIZE of BigIntegers
|
|
* used for call parameters 1, 2, and 3. The SIZE is interpreted as
|
|
* the maximum number of decimal digits that the parameters will have.
|
|
*
|
|
*/
|
|
public static void main(String[] args) throws Exception {
|
|
// subset zero indicates to run all subsets
|
|
int subset = Integer.valueOf(System.getProperty("subset",
|
|
String.valueOf(1 + random.nextInt(3))));
|
|
if (subset < 0 || subset > 3) {
|
|
throw new RuntimeException("Unknown subset " + subset);
|
|
}
|
|
if (subset == 0)
|
|
System.out.println("Testing all subsets");
|
|
else
|
|
System.out.println("Testing subset " + subset);
|
|
|
|
// 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);
|
|
|
|
if (subset == 0 || subset == 1) {
|
|
constructor();
|
|
|
|
prime();
|
|
nextProbablePrime();
|
|
|
|
arithmetic(order1); // small numbers
|
|
arithmetic(order3); // Karatsuba range
|
|
arithmetic(order4); // Toom-Cook / Burnikel-Ziegler range
|
|
|
|
divideAndRemainder(order1); // small numbers
|
|
divideAndRemainder(order3); // Karatsuba range
|
|
divideAndRemainder(order4); // Toom-Cook / Burnikel-Ziegler range
|
|
|
|
pow(order1);
|
|
pow(order3);
|
|
pow(order4);
|
|
|
|
square(ORDER_MEDIUM);
|
|
square(ORDER_KARATSUBA_SQUARE);
|
|
square(ORDER_TOOM_COOK_SQUARE);
|
|
|
|
squareRoot();
|
|
squareRootAndRemainder();
|
|
|
|
bitCount();
|
|
bitLength();
|
|
bitOps(order1);
|
|
bitwise(order1);
|
|
|
|
shift(order1);
|
|
|
|
byteArrayConv(order1);
|
|
|
|
modInv(order1); // small numbers
|
|
modInv(order3); // Karatsuba range
|
|
}
|
|
if (subset == 0 || subset == 2) {
|
|
modInv(order4); // Toom-Cook / Burnikel-Ziegler range
|
|
|
|
modExp(order1, order2);
|
|
modExp2(order1);
|
|
}
|
|
if (subset == 0 || subset == 3) {
|
|
stringConv();
|
|
serialize();
|
|
|
|
multiplyLarge();
|
|
squareLarge();
|
|
divideLarge();
|
|
}
|
|
|
|
if (failure)
|
|
throw new RuntimeException("Failure in BigIntegerTest.");
|
|
}
|
|
|
|
/*
|
|
* Get a random or boundary-case number. This is designed to provide
|
|
* a lot of numbers that will find failure points, such as max sized
|
|
* numbers, empty BigIntegers, etc.
|
|
*
|
|
* If order is less than 2, order is changed to 2.
|
|
*/
|
|
private static BigInteger fetchNumber(int order) {
|
|
boolean negative = random.nextBoolean();
|
|
int numType = random.nextInt(7);
|
|
BigInteger result = null;
|
|
if (order < 2) order = 2;
|
|
|
|
switch (numType) {
|
|
case 0: // Empty
|
|
result = BigInteger.ZERO;
|
|
break;
|
|
|
|
case 1: // One
|
|
result = BigInteger.ONE;
|
|
break;
|
|
|
|
case 2: // All bits set in number
|
|
int numBytes = (order+7)/8;
|
|
byte[] fullBits = new byte[numBytes];
|
|
for(int i=0; i<numBytes; i++)
|
|
fullBits[i] = (byte)0xff;
|
|
int excessBits = 8*numBytes - order;
|
|
fullBits[0] &= (1 << (8-excessBits)) - 1;
|
|
result = new BigInteger(1, fullBits);
|
|
break;
|
|
|
|
case 3: // One bit in number
|
|
result = BigInteger.ONE.shiftLeft(random.nextInt(order));
|
|
break;
|
|
|
|
case 4: // Random bit density
|
|
byte[] val = new byte[(order+7)/8];
|
|
int iterations = random.nextInt(order);
|
|
for (int i=0; i<iterations; i++) {
|
|
int bitIdx = random.nextInt(order);
|
|
val[bitIdx/8] |= 1 << (bitIdx%8);
|
|
}
|
|
result = new BigInteger(1, val);
|
|
break;
|
|
case 5: // Runs of consecutive ones and zeros
|
|
result = ZERO;
|
|
int remaining = order;
|
|
int bit = random.nextInt(2);
|
|
while (remaining > 0) {
|
|
int runLength = Math.min(remaining, random.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, random);
|
|
}
|
|
|
|
if (negative)
|
|
result = result.negate();
|
|
|
|
return result;
|
|
}
|
|
|
|
static void report(String testName, int failCount) {
|
|
System.err.println(testName+": " +
|
|
(failCount==0 ? "Passed":"Failed("+failCount+")"));
|
|
if (failCount > 0)
|
|
failure = true;
|
|
}
|
|
}
|