/* * Copyright (c) 1998, 2017, 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 * @summary tests methods in BigInteger (use -Dseed=X to set PRNG seed) * @run main/timeout=400 BigIntegerTest * @author madbot * @key randomness */ package test.java.math.BigInteger; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.math.BigDecimal; import java.math.BigInteger; import java.util.Random; import java.util.function.Function; 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 org.testng.Assert; import org.testng.annotations.Test; /** * 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. * */ // Android-changed: Replace error counting with asserts. 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 = new Random(); static boolean failure = false; private static void constructor() { // --- 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) { try { BigInteger bi = new BigInteger(array, ol[0], ol[1]); if (ol[2] == 1) { Assert.fail("IndexOutOfBoundsException did not occur for " + " two's complement constructor with parameters offset " + ol[0] + " and length " + ol[1]); } } catch (IndexOutOfBoundsException e) { if (ol[2] == 0) { Assert.fail("Unexpected IndexOutOfBoundsException did occur for " + " two's complement constructor with parameters offset " + ol[0] + " and length " + ol[1]); } } } // sign-magnitude for (int[] ol : offLen) { try { BigInteger bi = new BigInteger(1, array, ol[0], ol[1]); if (ol[2] == 1) { Assert.fail("IndexOutOfBoundsException did not occur for " + " sign-magnitude constructor with parameters offset " + ol[0] + " and length " + ol[1]); } } catch (IndexOutOfBoundsException e) { if (ol[2] == 0) { Assert.fail("Unexpected IndexOutOfBoundsException did occur for " + " two's complement constructor with parameters offset " + ol[0] + " and length " + ol[1]); } } } // --- 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); Assert.assertEquals(bi.compareTo(BigInteger.ZERO), 0, "A: Zero length BigInteger != 0 for signum " + signum); } for (int signum = -1; signum <= 1; signum++) { BigInteger bi = new BigInteger(signum, magZeroLength, 0, 0); Assert.assertEquals(bi.compareTo(BigInteger.ZERO), 0, "B: Zero length BigInteger != 0 for signum " + signum); } byte[] magNonZeroLength = new byte[42]; random.nextBytes(magNonZeroLength); for (int signum = -1; signum <= 1; signum++) { BigInteger bi = new BigInteger(signum, magNonZeroLength, 0, 0); Assert.assertEquals(bi.compareTo(BigInteger.ZERO), 0, "C: Zero length BigInteger != 0 for signum " + signum); } // --- 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); Assert.assertEquals(twosComp.compareTo(reference), 0, "Two's-complement BigInteger not equal for offset " + offset + " and length " + refLen); boolean isNegative = random.nextBoolean(); BigInteger signMag = new BigInteger(isNegative ? -1 : 1, objArray, offset, refLen); Assert.assertEquals(signMag.compareTo(isNegative ? reference.negate() : reference), 0, "Sign-magnitude BigInteger not equal for offset " + offset + " and length " + refLen); } } private static void pow(int order) { 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); Assert.assertEquals(y, z); } } private static void square(int order) { 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); Assert.assertEquals(x2, xx); } } private static void checkResult(BigInteger expected, BigInteger actual, String failureMessage) { Assert.assertEquals(expected.compareTo(actual), 0, failureMessage + " - expected: " + expected + ", actual: " + actual); } private static void squareRootSmall() { // 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. Assert.fail("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. 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) { checkResult(BigInteger.ONE, BigInteger.valueOf(small).sqrt(), "sqrt("+small+") != 1"); } } private static void squareRoot() { squareRootSmall(); Function<BigInteger, Void> f = (n) -> { // square root of n^2 -> n BigInteger n2 = n.pow(2); checkResult(n, n2.sqrt(), "sqrt() n^2 -> n"); // square root of n^2 + 1 -> n BigInteger n2up = n2.add(BigInteger.ONE); 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); checkResult(n, up.sqrt(), "sqrt() (n + 1)^2 - 1 -> n"); // sqrt(n)^2 <= n BigInteger s = n.sqrt(); Assert.assertFalse(s.multiply(s).compareTo(n) > 0, "sqrt(n)^2 > n for n = " + n); // (sqrt(n) + 1)^2 > n Assert.assertFalse(s.add(BigInteger.ONE).pow(2).compareTo(n) <= 0, "(sqrt(n) + 1)^2 <= n for n = " + n); return null; }; 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)); // "squareRoot for 2^N and 2^N - 1, 1 <= N <= Double.MAX_EXPONENT" sb.build().map(f).collect(Collectors.toList()); IntStream ints = random.ints(SIZE, 4, Integer.MAX_VALUE); ints.mapToObj(BigInteger::valueOf).map(f).collect(Collectors.toList()); LongStream longs = random.longs(SIZE, (long)Integer.MAX_VALUE + 1L, Long.MAX_VALUE); longs.mapToObj(BigInteger::valueOf).map(f).collect(Collectors.toList()); DoubleStream doubles = random.doubles(SIZE, (double) Long.MAX_VALUE + 1.0, Math.sqrt(Double.MAX_VALUE)); doubles.mapToObj(x -> BigDecimal.valueOf(x).toBigInteger()).map(f).collect(Collectors.toList()); } private static void squareRootAndRemainder() { Function<BigInteger, Void> g = (n) -> { BigInteger n2 = n.pow(2); // square root of n^2 -> n BigInteger[] actual = n2.sqrtAndRemainder(); checkResult(n, actual[0], "sqrtAndRemainder()[0]"); checkResult(BigInteger.ZERO, actual[1], "sqrtAndRemainder()[1]"); // square root of n^2 + 1 -> n BigInteger n2up = n2.add(BigInteger.ONE); actual = n2up.sqrtAndRemainder(); checkResult(n, actual[0], "sqrtAndRemainder()[0]"); 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(); checkResult(n, actual[0], "sqrtAndRemainder()[0]"); BigInteger r = up.subtract(n2); checkResult(r, actual[1], "sqrtAndRemainder()[1]"); return null; }; IntStream bits = random.ints(SIZE, 3, Short.MAX_VALUE); bits.mapToObj(BigInteger::valueOf).map(g).collect(Collectors.toList()); } private static void arithmetic(int order) { 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); Assert.assertEquals(baz, BigInteger.ZERO); } 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); Assert.assertEquals(baz[0], BigInteger.ZERO); } } /** * 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. */ private static void multiplyLarge() { 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); Assert.assertEquals(karatsubaMultiplyResult, multiplyResult); } 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); Assert.assertEquals(toomCookMultiplyResult, multiplyResult); } } /** * 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. */ private static void squareLarge() { 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); Assert.assertEquals(karatsubaSquareResult, squareResult); } 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); Assert.assertEquals(toomCookSquareResult, squareResult); } } /** * 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. */ private static void divideLarge() { 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); Assert.assertEquals(divideResult[0].compareTo(bzResult[0]), 0); Assert.assertEquals(divideResult[1].compareTo(bzResult[1]), 0); } } private static void bitCount() { 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; } Assert.assertEquals (bigX.bitCount(), bitCount); } } private static void bitLength() { 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; Assert.assertEquals(bigX.bitLength(), bitLength); } } private static void bitOps(int order) { 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); } Assert.assertEquals(y, x); // 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); Assert.assertEquals(y, x); } for (int i=0; i<SIZE*5; i++) { BigInteger x = fetchNumber(order); // Test getLowestSetBit() int k = x.getLowestSetBit(); if (x.signum() == 0) { Assert.assertEquals(k, -1); } else { BigInteger z = x.and(x.negate()); int j; for (j=0; j<z.bitLength() && !z.testBit(j); j++) ; Assert.assertEquals(k, j); } } } private static void bitwise(int order) { // Test identity x^y == x|y &~ x&y 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)); Assert.assertEquals(z, w, "Test identity x^y == x|y &~ x&y"); } // Test identity x &~ y == ~(~x | y) 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(); Assert.assertEquals(z, w, "Test identity x &~ y == ~(~x | y)"); } } private static void shift(int order) { for (int i=0; i<100; i++) { BigInteger x = fetchNumber(order); int n = Math.abs(random.nextInt()%200); Assert.assertEquals(x.shiftLeft(n), x.multiply(BigInteger.valueOf(2L).pow(n))); 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); Assert.assertEquals(z, b); Assert.assertEquals(x.shiftLeft(n).shiftRight(n), x); } } private static void divideAndRemainder(int order) { 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); Assert.assertEquals(y[0], BigInteger.ONE); Assert.assertEquals(y[1], BigInteger.ZERO); y = x.divideAndRemainder(z); Assert.assertEquals(y[0], BigInteger.valueOf(2)); } } private static void stringConv_generic() { // Generic string conversion. for (int i=0; i<100; i++) { byte[] xBytes = new byte[Math.abs(random.nextInt())%100+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); Assert.assertEquals(test, x, "BigInteger toString: "+x+" Test: "+test+" radix: " + radix); } } } private static void stringConv_schoenhage(int k, int samples) { // String conversion straddling the Schoenhage algorithm crossover // threshold, and at twice and four times the threshold. 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 < samples; 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); Assert.assertEquals(test, x, "BigInteger toString: "+x+" Test: "+test+" radix: " + radix); } } } } private static void byteArrayConv(int order) { 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()); Assert.assertEquals(y, x, "orig is " + x + ", new is " + y); } } private static void modInv(int order) { 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); Assert.assertEquals(prod, BigInteger.ONE); } catch(ArithmeticException ignored) { } } } private static void modExp(int order1, int order2) { 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); Assert.assertEquals(z, w, "z is "+z+" w is "+w+" mod is "+m+" base is "+base+" exp is "+exp); } } // 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) private static void modExp2(int order) { 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); Assert.assertEquals(one, BigInteger.ONE, "m is "+m+" base is "+base+" exp is "+exp); } } 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"); private static void prime() { BigInteger p1, p2, c1; // Test consistency for(int i=0; i<10; i++) { p1 = BigInteger.probablePrime(100, random); Assert.assertTrue(p1.isProbablePrime(100), p1.toString(16)); } // 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); Assert.assertTrue(p1.isProbablePrime(100), "Mersenne prime "+i+ " failed."); } // 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]); Assert.assertTrue(p1.isProbablePrime(100), "Customer prime "+i+ " failed."); } // Test some known Carmichael numbers. for (int i=0; i<carmichaels.length; i++) { c1 = BigInteger.valueOf(carmichaels[i]); Assert.assertFalse(c1.isProbablePrime(100), "Carmichael "+i+ " reported as prime."); } // 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); Assert.assertFalse(c1.isProbablePrime(100), "Computed Carmichael " +c1.toString(16)); 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); Assert.assertFalse(c1.isProbablePrime(100), "Composite failed "+c1.toString(16)); } for (int i=0; i<4; i++) { p1 = BigInteger.probablePrime(600, random); p2 = BigInteger.probablePrime(600, random); c1 = p1.multiply(p2); Assert.assertFalse(c1.isProbablePrime(100), "Composite failed "+c1.toString(16)); } } 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 }; private static void nextProbablePrime() throws Exception { BigInteger p1, p2, p3; p1 = p2 = p3 = ZERO; // First test nextProbablePrime on the low range starting at zero for (long l : primesTo100) { p1 = p1.nextProbablePrime(); Assert.assertEquals(p1.longValue(), l, "low range primes failed: p1 is " + p1 + ", expected " + l); } // 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(); Assert.assertEquals(p1.longValue(), aPrimeSequence[i], "prime sequence failed"); } // 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) { Assert.assertFalse(p2.isProbablePrime(100), "nextProbablePrime failed along range "+p1.toString(16)+" to "+p3.toString(16)); p2 = p2.add(ONE); } } } // Android-changed: Replace File streams with ByteArray public static void serialize() throws Exception { String[] bitPatterns = { "ffffffff00000000ffffffff00000000ffffffff00000000", "ffffffffffffffffffffffff000000000000000000000000", "ffffffff0000000000000000000000000000000000000000", "10000000ffffffffffffffffffffffffffffffffffffffff", "100000000000000000000000000000000000000000000000", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "-ffffffff00000000ffffffff00000000ffffffff00000000", "-ffffffffffffffffffffffff000000000000000000000000", "-ffffffff0000000000000000000000000000000000000000", "-10000000ffffffffffffffffffffffffffffffffffffffff", "-100000000000000000000000000000000000000000000000", "-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" }; for (String bitPattern : bitPatterns) { BigInteger b1 = new BigInteger(bitPattern, 16); BigInteger b2 = null; try (ByteArrayOutputStream fos = new ByteArrayOutputStream()) { try (ObjectOutputStream oos = new ObjectOutputStream(fos)) { oos.writeObject(b1); oos.flush(); } try (ByteArrayInputStream fis = new ByteArrayInputStream(fos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(fis)) { b2 = (BigInteger) ois.readObject(); } Assert.assertEquals(b1, b2, "Serialized failed for hex " + b1.toString(16)); Assert.assertEquals(b1.or(b2), b1, "Serialized failed for hex " + b1.toString(16)); } } for(int i=0; i<10; i++) { BigInteger b1 = fetchNumber(random.nextInt(100)); BigInteger b2 = null; try (ByteArrayOutputStream fos = new ByteArrayOutputStream()) { try (ObjectOutputStream oos = new ObjectOutputStream(fos)) { oos.writeObject(b1); oos.flush(); } try (ByteArrayInputStream fis = new ByteArrayInputStream(fos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(fis)) { b2 = (BigInteger)ois.readObject(); } } Assert.assertEquals(b1, b2, "Serialized failed for hex " + b1.toString(16)); Assert.assertEquals(b1.or(b2), b1, "Serialized failed for hex " + b1.toString(16)); } } private static final int ORDER_1 = ORDER_MEDIUM; private static final int ORDER_2 = ORDER_SMALL; private static final int ORDER_3 = ORDER_KARATSUBA; private static final int ORDER_4 = ORDER_TOOM_COOK; @Test public void testConstructor() { constructor(); } @Test public void testPrime() { prime(); } @Test public void testNextProbablePrime() throws Exception { nextProbablePrime(); } @Test public void testArithmetic() { arithmetic(ORDER_1); // small numbers arithmetic(ORDER_3); // Karatsuba range arithmetic(ORDER_4); // Toom-Cook / Burnikel-Ziegler range } @Test public void testDivideAndReminder() { divideAndRemainder(ORDER_1); // small numbers divideAndRemainder(ORDER_3); // Karatsuba range divideAndRemainder(ORDER_4); // Toom-Cook / Burnikel-Ziegler range } @Test public void testPow() { pow(ORDER_1); pow(ORDER_3); pow(ORDER_4); } @Test public void testSquare() { square(ORDER_MEDIUM); square(ORDER_KARATSUBA_SQUARE); square(ORDER_TOOM_COOK_SQUARE); } @Test public void testSquareRoot() { squareRoot(); } @Test public void testSquareRootAndReminder() { squareRootAndRemainder(); } @Test public void testBitCount() { bitCount(); } @Test public void testBitLength() { bitLength(); } @Test public void testBitOps() { bitOps(ORDER_1); } @Test public void testBitwise() { bitwise(ORDER_1); } @Test public void testShift() { shift(ORDER_1); } @Test public void testByteArrayConv() { byteArrayConv(ORDER_1); } @Test public void testModInv() { modInv(ORDER_1); // small numbers modInv(ORDER_3); // Karatsuba range modInv(ORDER_4); // Toom-Cook / Burnikel-Ziegler range } @Test public void testModExp() { modExp(ORDER_1, ORDER_2); modExp2(ORDER_1); } @Test public void testStringConv_generic() { stringConv_generic(); } // String conversion straddling the Schoenhage algorithm crossover // threshold. @Test public void testStringConv_schoenhage_threshold_pow0() { stringConv_schoenhage(0, 50); } // String conversion straddling the Schoenhage algorithm crossover // at twice times the threshold. @Test public void testStringConv_schoenhage_threshold_pow1() { stringConv_schoenhage(1, 50); } // String conversion straddling the Schoenhage algorithm crossover // at four times the threshold. @Test public void testStringConv_schoenhage_threshold_pow2() { stringConv_schoenhage(2, 15); } @Test public void testSerialize() throws Exception { serialize(); } @Test public void testMultiplyLarge() { multiplyLarge(); } @Test public void testSquareLarge() { squareLarge(); } @Test public void testDivideLarge() { divideLarge(); } /* * 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; } }