| /* | |
| * 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; | |
| } | |
| } |