Path: blob/master/test/jdk/java/math/BigInteger/PrimeTest.java
41149 views
/*1* Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223/*24* @test25* @library /test/lib26* @build jdk.test.lib.RandomFactory27* @run main PrimeTest28* @bug 8026236 8074460 807867229* @summary test primality verification methods in BigInteger (use -Dseed=X to set PRNG seed)30* @author bpb31* @key randomness32*/33import java.math.BigInteger;34import java.util.BitSet;35import java.util.List;36import java.util.NavigableSet;37import java.util.Set;38import java.util.SplittableRandom;39import java.util.TreeSet;40import jdk.test.lib.RandomFactory;41import static java.util.stream.Collectors.toCollection;42import static java.util.stream.Collectors.toList;4344public class PrimeTest {4546private static final int DEFAULT_UPPER_BOUND = 1299709; // 100000th prime47private static final int DEFAULT_CERTAINTY = 100;48private static final int NUM_NON_PRIMES = 10000;4950/**51* Run the test.52*53* @param args The parameters.54* @throws Exception on failure55*/56public static void main(String[] args) throws Exception {57// Prepare arguments58int upperBound = args.length > 0 ? Integer.valueOf(args[0]) : DEFAULT_UPPER_BOUND;59int certainty = args.length > 1 ? Integer.valueOf(args[1]) : DEFAULT_CERTAINTY;60boolean parallel = args.length > 2 ? Boolean.valueOf(args[2]) : true;6162// Echo parameter settings63System.out.println("Upper bound = " + upperBound64+ "\nCertainty = " + certainty65+ "\nParallel = " + parallel);6667// Get primes through specified bound (inclusive) and Integer.MAX_VALUE68NavigableSet<BigInteger> primes = getPrimes(upperBound);6970// Check whether known primes are identified as such71boolean primeTest = checkPrime(primes, certainty, parallel);72System.out.println("Prime test result: " + (primeTest ? "SUCCESS" : "FAILURE"));73if (!primeTest) {74System.err.println("Prime test failed");75}7677// Check whether known non-primes are not identified as primes78boolean nonPrimeTest = checkNonPrime(primes, certainty);79System.out.println("Non-prime test result: " + (nonPrimeTest ? "SUCCESS" : "FAILURE"));8081boolean mersennePrimeTest = checkMersennePrimes(certainty);82System.out.println("Mersenne test result: " + (mersennePrimeTest ? "SUCCESS" : "FAILURE"));8384if (!primeTest || !nonPrimeTest || !mersennePrimeTest) {85throw new Exception("PrimeTest FAILED!");86}8788System.out.println("PrimeTest succeeded!");89}9091/**92* Create a {@code BitSet} wherein a set bit indicates the corresponding93* index plus 2 is prime. That is, if bit N is set, then the integer N + 294* is prime. The values 0 and 1 are intentionally excluded. See the95* <a96* href="http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes#Algorithm_description">97* Sieve of Eratosthenes</a> algorithm description for more information.98*99* @param upperBound The maximum prime to allow100* @return bits indicating which indexes represent primes101*/102private static BitSet createPrimes(int upperBound) {103int nbits = upperBound - 1;104BitSet bs = new BitSet(nbits);105for (int p = 2; p * p < upperBound;) {106for (int i = p * p; i < nbits + 2; i += p) {107bs.set(i - 2, true);108}109do {110++p;111} while (p > 1 && bs.get(p - 2));112}113bs.flip(0, nbits);114return bs;115}116117/**118* Load the primes up to the specified bound (inclusive) into a119* {@code NavigableSet}, appending the prime {@code Integer.MAX_VALUE}.120*121* @param upperBound The maximum prime to allow122* @return a set of primes123*/124private static NavigableSet<BigInteger> getPrimes(int upperBound) {125BitSet bs = createPrimes(upperBound);126NavigableSet<BigInteger> primes = bs.stream()127.mapToObj(p -> BigInteger.valueOf(p + 2))128.collect(toCollection(TreeSet::new));129primes.add(BigInteger.valueOf(Integer.MAX_VALUE));130System.out.println(String.format("Created %d primes", primes.size()));131return primes;132}133134/**135* Verifies whether the fraction of probable primes detected is at least 1 -136* 1/2^certainty.137*138* @return true if and only if the test succeeds139*/140private static boolean checkPrime(Set<BigInteger> primes,141int certainty,142boolean parallel) {143long probablePrimes = (parallel ? primes.parallelStream() : primes.stream())144.filter(bi -> bi.isProbablePrime(certainty))145.count();146147// N = certainty / 2148// Success if p/t >= 1 - 1/4^N149// or (p/t)*4^N >= 4^N - 1150// or p*4^N >= t*(4^N - 1)151BigInteger p = BigInteger.valueOf(probablePrimes);152BigInteger t = BigInteger.valueOf(primes.size());153BigInteger fourToTheC = BigInteger.valueOf(4).pow(certainty / 2);154BigInteger fourToTheCMinusOne = fourToTheC.subtract(BigInteger.ONE);155BigInteger left = p.multiply(fourToTheC);156BigInteger right = t.multiply(fourToTheCMinusOne);157158if (left.compareTo(right) < 0) {159System.err.println("Probable prime certainty test failed");160}161162return left.compareTo(right) >= 0;163}164165/**166* Verifies whether all {@code BigInteger}s in the tested range for which167* {@code isProbablePrime()} returns {@code false} are <i>not</i>168* prime numbers.169*170* @return true if and only if the test succeeds171*/172private static boolean checkNonPrime(NavigableSet<BigInteger> primes,173int certainty) {174int maxPrime = DEFAULT_UPPER_BOUND;175try {176maxPrime = primes.last().intValueExact();177} catch (ArithmeticException e) {178// ignore it179}180181// Create a list of non-prime BigIntegers.182SplittableRandom splitRandom = RandomFactory.getSplittableRandom();183List<BigInteger> nonPrimeBigInts = (splitRandom)184.ints(NUM_NON_PRIMES, 2, maxPrime).mapToObj(BigInteger::valueOf)185.filter(b -> !b.isProbablePrime(certainty)).collect(toList());186187// If there are any non-probable primes also in the primes list then fail.188boolean failed = nonPrimeBigInts.stream().anyMatch(primes::contains);189190// In the event, print which purported non-primes were actually prime.191if (failed) {192for (BigInteger bigInt : nonPrimeBigInts) {193if (primes.contains(bigInt)) {194System.err.println("Prime value thought to be non-prime: " + bigInt);195}196}197}198199return !failed;200}201202/**203* Verifies whether a specified subset of Mersenne primes are correctly204* identified as being prime. See205* <a href="https://en.wikipedia.org/wiki/Mersenne_prime">Mersenne prime</a>206* for more information.207*208* @return true if and only if the test succeeds209*/210private static boolean checkMersennePrimes(int certainty) {211int[] MERSENNE_EXPONENTS = {2122, 3, 5, 7, 13, 17, 19, 31, 61, 89, 107, 127, 521, 607, 1279, 2203,2132281, 3217, 4253, // uncomment remaining array elements to make this test run a long time214/* 4423, 9689, 9941, 11213, 19937, 21701, 23209, 44497,21586243, 110503, 132049, 216091, 756839, 859433, 1257787, 1398269,2162976221, 3021377, 6972593, 13466917, 20996011, 24036583, 25964951,21730402457, 32582657, 37156667, 42643801, 43112609, 57885161 */218};219System.out.println("Checking first "+MERSENNE_EXPONENTS.length+" Mersenne primes");220221boolean result = true;222for (int n : MERSENNE_EXPONENTS) {223BigInteger mp = BigInteger.ONE.shiftLeft(n).subtract(BigInteger.ONE);224if (!mp.isProbablePrime(certainty)) {225System.err.println("Mp with p = "+n+" not classified as prime");226result = false;227}228}229230return result;231}232}233234235