/* * 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.Constructor; 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 // 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 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 Random rnd = new Random(); static int size = 1000; // numbers per batch static int reducedSize = 10; // numbers per batch in the Schoenhage-Strassen/Barrett range 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 ctor = BigInteger.class.getDeclaredConstructor(int.class, int[].class); ctor.setAccessible(true); for (int i=0; i<100; i++) { int length = 1 << (1+rnd.nextInt(10)); int[] FnArr = new int[length]; FnArr[length/2-1] = 1; FnArr[length-1] = 1; BigInteger Fn = ctor.newInstance(1, FnArr); int[] a = createRandomArray(length); modFnMethod.invoke(null, new Object[] {a}); int[] b = createRandomArray(length); modFnMethod.invoke(null, new Object[] {b}); int[] c = (int[])multModFnMethod.invoke(null, a, b); modFnMethod.invoke(null, new Object[] {c}); BigInteger actual = ctor.newInstance(1, c); BigInteger aBigInt = ctor.newInstance(1, a); BigInteger bBigInt = ctor.newInstance(1, b); BigInteger expected = aBigInt.multiply(bBigInt).mod(Fn); if (!actual.equals(expected)) failCount++; } // test squareModFn modFnMethod = BigInteger.class.getDeclaredMethod("modFn", int[].class); // this is the other modFn method modFnMethod.setAccessible(true); Method squareModFnMethod = BigInteger.class.getDeclaredMethod("squareModFn", int[].class); squareModFnMethod.setAccessible(true); for (int i=0; i<100; i++) { int length = 1 << (1+rnd.nextInt(10)); int[] FnArr = new int[length]; FnArr[length/2-1] = 1; FnArr[length-1] = 1; BigInteger Fn = ctor.newInstance(1, FnArr); int[] a = createRandomArray(length); modFnMethod.invoke(null, new Object[] {a}); int[] c = (int[])squareModFnMethod.invoke(null, a); modFnMethod.invoke(null, new Object[] {c}); BigInteger actual = ctor.newInstance(1, c); BigInteger aBigInt = ctor.newInstance(1, a); BigInteger expected = (BigInteger)squareMethod.invoke(aBigInt); expected = expected.mod(Fn); if (!actual.equals(expected)) failCount++; } // test addModFn Method addModFnMethod = BigInteger.class.getDeclaredMethod("addModFn", int[].class, int[].class); addModFnMethod.setAccessible(true); for (int k = 0; k<100; k++) { int n = 5 + rnd.nextInt(10); int len = 1 << (n + 1 - 5); int[] aArr = createRandomArray(len); aArr[0] &= 0x7FFFFFFF; // make sure the most significant int doesn't overflow BigInteger a = ctor.newInstance(1, aArr); int[] bArr = createRandomArray(len); bArr[0] &= 0x7FFFFFFF; // make sure the most significant int doesn't overflow BigInteger b = ctor.newInstance(1, bArr); BigInteger Fn = BigInteger.valueOf(2).pow(1 << n).add(BigInteger.ONE); BigInteger expected = a.add(b).mod(Fn); addModFnMethod.invoke(null, aArr, bArr); modFnMethod.invoke(null, aArr); BigInteger actual = ctor.newInstance(1, aArr); if (!actual.equals(expected)) failCount++; } // test subModFn Method subModFnMethod = BigInteger.class.getDeclaredMethod("subModFn", int[].class, int[].class); subModFnMethod.setAccessible(true); for (int k = 0; k<100; k++) { int n = 5 + rnd.nextInt(10); int len = 1 << (n + 1 - 5); int[] aArr = createRandomArray(len); aArr[0] &= 0x7FFFFFFF; // make sure the most significant int doesn't overflow BigInteger a = ctor.newInstance(1, aArr); int[] bArr = createRandomArray(len); bArr[0] &= 0x7FFFFFFF; // make sure the most significant int doesn't overflow BigInteger b = ctor.newInstance(1, bArr); BigInteger Fn = BigInteger.valueOf(2).pow(1 << n).add(BigInteger.ONE); BigInteger expected = a.subtract(b).mod(Fn); subModFnMethod.invoke(null, aArr, bArr); modFnMethod.invoke(null, aArr); BigInteger actual = ctor.newInstance(1, aArr); if (!actual.equals(expected)) failCount++; } // test cyclicShiftLeftBits / cyclicShiftRightBits Method shiftLeftMethod = BigInteger.class.getDeclaredMethod("cyclicShiftLeftBits", int[].class, int.class, int[].class); shiftLeftMethod.setAccessible(true); Method shiftRightMethod = BigInteger.class.getDeclaredMethod("cyclicShiftRightBits", int[].class, int.class, int[].class); shiftRightMethod.setAccessible(true); for (int k = 0; k<100; k++) { int n = 1 << rnd.nextInt(20); int[] a = createRandomArray(n); int[] aOrig = a.clone(); // pick random shift amounts whose sum is zero List amounts = new ArrayList(); int count = 1 + rnd.nextInt(20); int total = 0; for (int i = 0; i < count; i++) { int amount = rnd.nextInt(n); amounts.add(amount); total += amount; } while (total > 0) { int amount = Math.min(rnd.nextInt(n), total); amounts.add(-amount); total -= amount; } Collections.shuffle(amounts); for (int amount : amounts) { int[] b = new int[a.length]; if (amount > 0) shiftLeftMethod.invoke(null, a, amount, b); else shiftRightMethod.invoke(null, a, -amount, b); a = b; } if (!Arrays.equals(a, aOrig)) failCount++; } // test modFn for (int k = 0; k<100; k++) { int n = 5 + rnd.nextInt(15); int len = 1 << (n + 1 - 5); int[] a = createRandomArray(len); int[] aOrig = a.clone(); modFnMethod.invoke(null, a); BigInteger actual = ctor.newInstance(1, a); BigInteger Fn = BigInteger.valueOf(2).pow(1 << n).add(BigInteger.ONE); BigInteger expected = ctor.newInstance(1, aOrig).mod(Fn); if (!actual.equals(expected)) 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 = 5 + 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 = ctor.newInstance(1, aArr); int[] bArr = createRandomArray(len); bArr[0] &= 0x7FFFFFFF; // make sure the most significant int doesn't overflow BigInteger b = ctor.newInstance(1, bArr); int numBits = rnd.nextInt(n); BigInteger pow2 = BigInteger.valueOf(1<=0; i--) { int[] piece = pieces[i]; BigInteger pieceBigInt = ctor.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 = ctor.newInstance(1, aArr); int offset = rnd.nextInt(aArr.length); int[] bArr = createRandomArray(1 + rnd.nextInt(1000)); BigInteger b = ctor.newInstance(1, bArr); addShiftedMethod.invoke(null, aArr, bArr, offset); BigInteger actual = ctor.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 = ctor.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 = ctor.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>= 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); square(order1); // small numbers square(order3); // Karatsuba / Burnikel-Ziegler range square(order4); // Toom-Cook range square(order5); // SS/Barrett range arithmetic(order1); // small numbers arithmetic(order3); // Karatsuba / Burnikel-Ziegler range arithmetic(order4); // Toom-Cook range arithmetic(order5); // SS/Barrett range divideAndRemainder(order1); // small numbers divideAndRemainder(order3); // Karatsuba / Burnikel-Ziegler range divideAndRemainder(order4); // Toom-Cook range divideAndRemainder(order5); // SS/Barrett range pow(order1); pow(order3); pow(order4); square(ORDER_MEDIUM); square(ORDER_KARATSUBA_SQUARE); square(ORDER_TOOM_COOK_SQUARE); bitCount(); bitLength(); bitOps(order1); bitwise(order1); shift(order1); byteArrayConv(order1); modInv(order1); // small numbers modInv(order3); // Karatsuba / Burnikel-Ziegler range modInv(order4); // Toom-Cook range modExp(order1, order2); modExp2(order1); stringConv(); serialize(); multiplyLarge(); squareLarge(); if (failure) throw new RuntimeException("Failure in BigIntegerTest."); } /* * 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; } }