/* * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * 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 * @bug 4181191 4161971 4227146 4194389 4823171 4624738 4812225 4837946 * @summary tests methods in BigInteger * @run main/timeout=400 BigIntegerTest * @author madbot */ import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.math.BigInteger; import java.util.Random; import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; /** * 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. * * Five arguments may be specified which give the number of * decimal digits you desire in the five 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 = 50 ints = 1600 bits // TOOM_COOK_THRESHOLD = 75 ints = 2400 bits // KARATSUBA_SQUARE_THRESHOLD = 90 ints = 2880 bits // TOOM_COOK_SQUARE_THRESHOLD = 140 ints = 4480 bits // // SCHOENHAGE_BASE_CONVERSION_THRESHOLD = 8 ints = 256 bits // // BURNIKEL_ZIEGLER_THRESHOLD = 50 ints = 1600 bits // static final int BITS_KARATSUBA = 1600; static final int BITS_TOOM_COOK = 2400; static final int BITS_KARATSUBA_SQUARE = 2880; static final int BITS_TOOM_COOK_SQUARE = 4480; static final int BITS_SCHOENHAGE_BASE = 256; static final int BITS_BURNIKEL_ZIEGLER = 1600; static final int ORDER_SMALL = 60; static final int ORDER_MEDIUM = 100; // #bits for testing Karatsuba and Burnikel-Ziegler static final int ORDER_KARATSUBA = 1800; // #bits for testing Toom-Cook static final int ORDER_TOOM_COOK = 3000; // #bits for testing Schoenhage-Strassen and Barrett static final int ORDER_SS_BARRETT = 4000000; // #bits for testing Karatsuba squaring static final int ORDER_KARATSUBA_SQUARE = 3200; // #bits for testing Toom-Cook squaring static final int ORDER_TOOM_COOK_SQUARE = 4600; static final int SIZE = 1000; // numbers per batch static final int REDUCED_SIZE = 10; // numbers per batch in the Schoenhage-Strassen/Barrett range static Random rnd = new Random(); static boolean failure = false; public static void pow(int order) { int failCount1 = 0; for (int i=0; i * (u << a)*(v << b) = (u*v) << (a + b) * * 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; ibigInteger.multiply(bigInteger) tests whether * the parameter is the same instance on which the method is being invoked * and calls square() accordingly. */ public static void squareLarge() { int failCount = 0; BigInteger base = BigInteger.ONE.shiftLeft(BITS_KARATSUBA_SQUARE - 32 - 1); for (int i=0; i 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 and that {@code w} * and {@code z} are both over the threshold. 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 - 33); for (int i=0; i mutableModFnClass = Class.forName("java.math.MutableModFn"); Constructor mutableModFnCtor = mutableModFnClass.getDeclaredConstructor(long[].class); mutableModFnCtor.setAccessible(true); Field digitsField = mutableModFnClass.getDeclaredField("digits"); digitsField.setAccessible(true); Method dftMethod = BigInteger.class.getDeclaredMethod("dft", Array.newInstance(mutableModFnClass, 0).getClass(), int.class, int.class); dftMethod.setAccessible(true); Method idftMethod = BigInteger.class.getDeclaredMethod("idft", Array.newInstance(mutableModFnClass, 0).getClass(), int.class, int.class); idftMethod.setAccessible(true); for (int k=0; k<100; k++) { int m = 10 + rnd.nextInt(8); int n = m/2 + 1; long[][] a = createRandomDftArray(m, n); long[][] aOrig = new long[a.length][]; for (int i=0; i bigintCtor = BigInteger.class.getDeclaredConstructor(int.class, int[].class); bigintCtor.setAccessible(true); for (int i=0; i<100; i++) { int n = 6 + rnd.nextInt(10); long[] a = createRandomModFn(n); long[] b = createRandomModFn(n); int[] aInt = toIntArray(a); BigInteger aBigInt = bigintCtor.newInstance(1, aInt); int[] bInt = toIntArray(b); BigInteger bBigInt = bigintCtor.newInstance(1, bInt); BigInteger expected = aBigInt.multiply(bBigInt).mod(fermat(n)); Object aMutable = mutableModFnCtor.newInstance(a); Object bMutable = mutableModFnCtor.newInstance(b); multiplyMethod.invoke(aMutable, bMutable); long[] c = (long[])digitsField.get(aMutable); int[] cInt = toIntArray(c); BigInteger actual = bigintCtor.newInstance(1, cInt); if (!actual.equals(expected)) failCount++; } // test MutableModFn.square() Method squareModFnMethod = mutableModFnClass.getDeclaredMethod("square"); squareModFnMethod.setAccessible(true); for (int i=0; i<100; i++) { int n = 6 + rnd.nextInt(10); long[] a = createRandomModFn(n); int[] aInt = toIntArray(a); BigInteger aBigInt = bigintCtor.newInstance(1, aInt); BigInteger expected = aBigInt.multiply(aBigInt).mod(fermat(n)); Object aMutable = mutableModFnCtor.newInstance(a); squareModFnMethod.invoke(aMutable); long[] c = (long[])digitsField.get(aMutable); int[] cInt = toIntArray(c); BigInteger actual = bigintCtor.newInstance(1, cInt); if (!actual.equals(expected)) failCount++; } // test MutableModFn.add() Method addMethod = mutableModFnClass.getDeclaredMethod("add", mutableModFnClass); addMethod.setAccessible(true); Method toIntArrayOddMethod = mutableModFnClass.getDeclaredMethod("toIntArrayOdd", long[].class); toIntArrayOddMethod.setAccessible(true); for (int k = 0; k<100; k++) { int n = 6 + rnd.nextInt(10); long[] aArr = createRandomModFn(n); long[] bArr = createRandomModFn(n); int[] aArrInt = toIntArray(aArr); BigInteger a = bigintCtor.newInstance(1, aArrInt); int[] bArrInt = toIntArray(bArr); BigInteger b = bigintCtor.newInstance(1, bArrInt); BigInteger expected = a.add(b).mod(fermat(n)); Object aMutable = mutableModFnCtor.newInstance(aArr); Object bMutable = mutableModFnCtor.newInstance(bArr); addMethod.invoke(aMutable, bMutable); aArr = (long[])digitsField.get(aMutable); aArrInt = toIntArray(aArr); BigInteger actual = bigintCtor.newInstance(1, aArrInt); if (!actual.equals(expected)) failCount++; } // test MutableModFn.subtract() Method subtractMethod = mutableModFnClass.getDeclaredMethod("subtract", mutableModFnClass); subtractMethod.setAccessible(true); for (int k = 0; k<100; k++) { int n = 6 + rnd.nextInt(10); long[] aArr = createRandomModFn(n); long[] bArr = createRandomModFn(n); int[] aArrInt = toIntArray(aArr); BigInteger a = bigintCtor.newInstance(1, aArrInt); int[] bArrInt = toIntArray(bArr); BigInteger b = bigintCtor.newInstance(1, bArrInt); BigInteger expected = a.subtract(b).mod(fermat(n)); Object aMutable = mutableModFnCtor.newInstance(aArr); Object bMutable = mutableModFnCtor.newInstance(bArr); subtractMethod.invoke(aMutable, bMutable); aArr = (long[])digitsField.get(aMutable); aArrInt = toIntArray(aArr); BigInteger actual = bigintCtor.newInstance(1, aArrInt); if (!actual.equals(expected)) failCount++; } // test MutableModFn.shiftLeft() against BigInteger.shiftLeft().mod(Fn) Method shiftLeftMethod = mutableModFnClass.getDeclaredMethod("shiftLeft", int.class, mutableModFnClass); shiftLeftMethod.setAccessible(true); for (int k = 0; k<100; k++) { int n = 6 + rnd.nextInt(10); long[] aArr = createRandomModFn(n); long[] bArr = new long[aArr.length]; int shiftDistance = rnd.nextInt(1 << (n+1)); int[] aArrInt = toIntArray(aArr); BigInteger a = bigintCtor.newInstance(1, aArrInt); BigInteger expected = a.shiftLeft(shiftDistance).mod(fermat(n)); Object aMutable = mutableModFnCtor.newInstance(aArr); Object bMutable = mutableModFnCtor.newInstance(bArr); shiftLeftMethod.invoke(aMutable, shiftDistance, bMutable); bArr = (long[])digitsField.get(bMutable); int[] bArrInt = toIntArray(bArr); BigInteger actual = bigintCtor.newInstance(1, bArrInt); if (!actual.equals(expected)) failCount++; } // test MutableModFn.shiftRight() against BigInteger.multiply(2^(-shiftDistance)).mod(Fn) Method shiftRightMethod = mutableModFnClass.getDeclaredMethod("shiftRight", int.class, mutableModFnClass); shiftRightMethod.setAccessible(true); for (int k = 0; k<100; k++) { int n = 6 + rnd.nextInt(10); long[] aArr = createRandomModFn(n); long[] bArr = new long[aArr.length]; int shiftDistance = rnd.nextInt(1 << (n+1)); BigInteger Fn = fermat(n); BigInteger inv2 = Fn.add(ONE).divide(TWO); // 2^(-1) mod Fn BigInteger inv = inv2.modPow(BigInteger.valueOf(shiftDistance), Fn); // 2^(-shiftDistance) mod Fn int[] aArrInt = toIntArray(aArr); BigInteger a = bigintCtor.newInstance(1, aArrInt); BigInteger expected = a.multiply(inv).mod(Fn); Object aMutable = mutableModFnCtor.newInstance(aArr); Object bMutable = mutableModFnCtor.newInstance(bArr); shiftRightMethod.invoke(aMutable, shiftDistance, bMutable); bArr = (long[])digitsField.get(bMutable); int[] bArrInt = toIntArray(bArr); BigInteger actual = bigintCtor.newInstance(1, bArrInt); if (!actual.equals(expected)) failCount++; } // test MutableModFn.shiftLeft() and MutableModFn.shiftRight() by doing multiple shifts which should cancel each other out for (int k = 0; k<100; k++) { int n = 6 + rnd.nextInt(10); int bitLen = (1< amounts = new ArrayList(); int count = 1 + rnd.nextInt(20); int total = 0; for (int i = 0; i < count; i++) { int amount = rnd.nextInt(bitLen); amounts.add(amount); total += amount; } while (total > 0) { int amount = Math.min(rnd.nextInt(bitLen), total); amounts.add(-amount); total -= amount; } Collections.shuffle(amounts); long[] a = createRandomModFn(n); long[] aOrig = a.clone(); Object aMutable = mutableModFnCtor.newInstance(a); for (int amount: amounts) { Object bMutable = mutableModFnCtor.newInstance(new long[a.length]); if (amount > 0) shiftLeftMethod.invoke(aMutable, amount, bMutable); else shiftRightMethod.invoke(aMutable, -amount, bMutable); aMutable = bMutable; } reduceMethod.invoke(aMutable); a = (long[])digitsField.get(aMutable); if (!Arrays.equals(a, aOrig)) failCount++; } // test MutableModFn.reduce() for (int k = 0; k<100; k++) { int n = 6 + rnd.nextInt(15); long[] a = createRandomModFn(n); a[0] = rnd.nextLong(); // test all long values, not just 0 and 1 long[] aOrig = a.clone(); BigInteger Fn = fermat(n); int[] aOrigInt = toIntArray(aOrig); BigInteger expected = bigintCtor.newInstance(1, aOrigInt).mod(Fn); Object aMutable = mutableModFnCtor.newInstance(a); reduceMethod.invoke(aMutable); a = (long[])digitsField.get(aMutable); int[] aInt = toIntArray(a); BigInteger actual = bigintCtor.newInstance(1, aInt); if (!actual.equals(expected)) failCount++; if (actual.compareTo(Fn) >= 0) failCount++; } // test MutableModFn.reduceWide() Method reduceWideMethod = mutableModFnClass.getDeclaredMethod("reduceWide", long[].class); reduceWideMethod.setAccessible(true); for (int k = 0; k<100; k++) { int n = 6 + rnd.nextInt(15); int len = 1 << (n + 1 - 6); long[] a = createRandomArrayLong(len); long[] aOrig = a.clone(); BigInteger Fn = fermat(n); int[] aOrigInt = toIntArray(aOrig); BigInteger expected = bigintCtor.newInstance(1, aOrigInt).mod(Fn); reduceWideMethod.invoke(null, a); int[] aInt = (int[])toIntArrayOddMethod.invoke(null, a); BigInteger actual = bigintCtor.newInstance(1, aInt); if (!actual.equals(expected)) failCount++; if (actual.compareTo(Fn) >= 0) failCount++; } // test MutableModFn.toIntArray() and MutableModFn.toLongArray() Method toLongArrayEvenMethod = mutableModFnClass.getDeclaredMethod("toLongArrayEven", int[].class); toLongArrayEvenMethod.setAccessible(true); for (int i=0; i<1000; i++) { int n = 2 + rnd.nextInt(1000); int[] a = createRandomArray(n); long[] b = n%2==0 ? (long[])toLongArrayEvenMethod.invoke(null, a) : toLongArrayOdd(a); int[] c = n%2==0 ? toIntArray(b) : (int[])toIntArrayOddMethod.invoke(null, b); if (!Arrays.equals(a, c)) failCount++; } // test addModPow2 Method addModPow2Method = BigInteger.class.getDeclaredMethod("addModPow2", int[].class, int[].class, int.class); addModPow2Method.setAccessible(true); for (int k = 0; k<100; k++) { int n = 6 + rnd.nextInt(15); int len = 1 << (n + 1 - 5); int[] aArr = createRandomArray(len); aArr[0] &= 0x7FFFFFFF; // make sure the most significant int doesn't overflow BigInteger a = bigintCtor.newInstance(1, aArr); int[] bArr = createRandomArray(len); bArr[0] &= 0x7FFFFFFF; // make sure the most significant int doesn't overflow BigInteger b = bigintCtor.newInstance(1, bArr); int numBits = rnd.nextInt(n); BigInteger pow2 = BigInteger.valueOf(1<=0; i--) { int[] piece = pieces[i]; BigInteger pieceBigInt = bigintCtor.newInstance(1, piece); expected = expected.shiftLeft(pieceSize).add(pieceBigInt); } if (!actual.equals(expected)) failCount++; } // test addShifted Method addShiftedMethod = BigInteger.class.getDeclaredMethod("addShifted", int[].class, int[].class, int.class); addShiftedMethod.setAccessible(true); for (int k = 0; k<100; k++) { int[] aArr = createRandomArray(1 + rnd.nextInt(1000)); BigInteger a = bigintCtor.newInstance(1, aArr); int offset = rnd.nextInt(aArr.length); int[] bArr = createRandomArray(1 + rnd.nextInt(1000)); BigInteger b = bigintCtor.newInstance(1, bArr); addShiftedMethod.invoke(null, aArr, bArr, offset); BigInteger actual = bigintCtor.newInstance(1, aArr); BigInteger mask = ONE.shiftLeft(aArr.length*32).subtract(ONE); BigInteger expected = a.add(b.shiftLeft(offset*32)).and(mask); if (!actual.equals(expected)) failCount++; } // test appendBits Method appendBitsMethod = BigInteger.class.getDeclaredMethod("appendBits", int[].class, int.class, int[].class, int.class, int.class); appendBitsMethod.setAccessible(true); for (int k = 0; k<100; k++) { int[] src = createRandomArray(1 + rnd.nextInt(1000)); BigInteger srcBigInt = bigintCtor.newInstance(1, src); int[] dest = new int[src.length]; int valBits = 1 + smallRandom(src.length*32-1, rnd); int padBits = 1 + smallRandom(src.length*32-1, rnd); BigInteger mask = ONE.shiftLeft(valBits).subtract(ONE); int srcIndex = 0; int destBits = 0; BigInteger expected = ZERO; while (srcIndex+(valBits+31)/32 0) dest = Arrays.copyOfRange(dest, dest.length-(destBits+31)/32, dest.length); BigInteger actual = bigintCtor.newInstance(1, dest); if (!actual.equals(expected)) failCount++; } report("Schoenhage-Strassen", failCount); } // like Random.nextInt(int) but favors small numbers private static int smallRandom(int n, Random rnd) { return (int)(n * Math.pow(rnd.nextDouble(), 5)); } private static int[] createRandomArray(int length) { int[] a = new int[length]; for (int i=0; i>> 32); intDigits[2*i+1] = (int)(digits[i] & -1); } return intDigits; } /** digits.length must be an odd number */ public static long[] toLongArrayOdd(int[] digits) { long[] longDigits = new long[digits.length/2+1]; longDigits[0] = digits[0]; for (int i=1; i bigintCtor = BigInteger.class.getDeclaredConstructor(int.class, int[].class); bigintCtor.setAccessible(true); int length = (1<<(n-5)) + 1; int[] FnArr = new int[length]; FnArr[0] = 1; FnArr[FnArr.length-1] = 1; return bigintCtor.newInstance(1, FnArr); } private static void inverse() throws Exception { int failCount = 0; Method inverseMethod = BigInteger.class.getDeclaredMethod("inverse", int.class, int.class); inverseMethod.setAccessible(true); for (int i=0; i 1) failCount++; } report("Inverse", failCount); } public static void bitCount() { int failCount = 0; for (int i=0; i>= 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= lower; bits--) { for (int i = 0; i < 50; i++) { BigInteger x = BigInteger.ONE.shiftLeft(bits - 1).or(new BigInteger(bits - 2, rnd)); 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); } } } } } report("String Conversion", failCount); } public static void byteArrayConv(int order) { int failCount = 0; for (int i=0; i0) 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 (args.length >4) order5 = (int)((Integer.parseInt(args[4]))* 3.333); prime(); nextProbablePrime(); schoenhageStrassen(order5); inverse(); arithmetic(order1); // small numbers arithmetic(order3); // Karatsuba range arithmetic(order4); // Toom-Cook / Burnikel-Ziegler range arithmetic(order5); // SS/Barrett range divideAndRemainder(order1); // small numbers divideAndRemainder(order3); // Karatsuba range divideAndRemainder(order4); // Toom-Cook / Burnikel-Ziegler range divideAndRemainder(order5); // SS/Barrett range pow(order1); pow(order3); pow(order4); square(ORDER_MEDIUM); square(ORDER_KARATSUBA_SQUARE); square(ORDER_TOOM_COOK_SQUARE); square(order5); // SS/Barrett range bitCount(); bitLength(); bitOps(order1); bitwise(order1); shift(order1); byteArrayConv(order1); modInv(order1); // small numbers modInv(order3); // Karatsuba range modInv(order4); // Toom-Cook / Burnikel-Ziegler range modExp(order1, order2); modExp2(order1); 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 = rnd.nextBoolean(); int numType = rnd.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 0) { int runLength = Math.min(remaining, rnd.nextInt(order)); result = result.shiftLeft(runLength); if (bit > 0) result = result.add(ONE.shiftLeft(runLength).subtract(ONE)); remaining -= runLength; bit = 1 - bit; } break; default: // random bits result = new BigInteger(order, rnd); } 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; } }