1 /*
   2  * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 /*
  27  * @test
  28  * @bug 8026236
  29  * @summary test primality verification methods in BigInteger
  30  * @author bpb
  31  */
  32 
  33 import java.io.BufferedReader;
  34 import java.io.File;
  35 import java.io.FileInputStream;
  36 import java.io.FileNotFoundException;
  37 import java.io.IOException;
  38 import java.io.InputStreamReader;
  39 import java.math.BigInteger;
  40 import java.util.List;
  41 import java.util.Random;
  42 import static java.util.stream.Collectors.toList;
  43 import java.util.stream.Stream;
  44 import java.util.zip.GZIPInputStream;
  45 
  46 public class PrimeTest {
  47     private static final String DEFAULT_PRIMES_FILE;
  48     private static final int DEFAULT_UPPER_BOUND = 1000000;
  49     private static final int DEFAULT_CERTAINTY = 100;
  50 
  51     static {
  52         try {
  53             String dir = System.getProperty("test.src", ".");
  54             File testFile = new File(dir, "primes-100000.txt.gz");
  55             DEFAULT_PRIMES_FILE = testFile.getAbsolutePath();
  56         } catch (Throwable t) {
  57             throw new Error(t);
  58         }
  59     }
  60 
  61     public static void main(String[] args) throws Throwable {
  62         // Prepare arguments
  63         File primesFile = args.length > 0 ? new File(args[0]) : new File(DEFAULT_PRIMES_FILE);
  64         int upperBound = args.length > 1 ? Integer.valueOf(args[1]) : DEFAULT_UPPER_BOUND;
  65         int certainty = args.length > 2 ? Integer.valueOf(args[2]) : DEFAULT_CERTAINTY;
  66         boolean parallel = args.length > 3 ? Boolean.valueOf(args[3]) : true;
  67 
  68         // Echo parameter settings
  69         System.out.println("Primes: " + primesFile + "\nupper bound = " + upperBound
  70                 + "\ncertainty = " + certainty
  71                 + "\nparallel = " + parallel);
  72 
  73         boolean primeTest = checkPrime(primesFile, upperBound, certainty, parallel);
  74         System.out.println("Prime test result: " + (primeTest ? "SUCCESS" : "FAILURE"));
  75         if (!primeTest) {
  76             System.err.println("Prime test failed");
  77         }
  78 
  79         boolean notPrimeTest = checkNotPrime(primesFile, upperBound, certainty);
  80         System.out.println("Not-prime test result: " + (notPrimeTest ? "SUCCESS" : "FAILURE"));
  81 
  82         if (!primeTest || !notPrimeTest) {
  83             throw new Exception("PrimeTest FAILED!");
  84         }
  85         System.out.println("PrimeTest succeeded!");
  86     }
  87 
  88     /**
  89      * Parse the primes into a list of [@code BigInteger}s.
  90      *
  91      * @param f File of prime numbers assumed to be a GZIPped text file with one
  92      * number per line in the default character set
  93      * @param n The maximum number of primes to load
  94      * @return A {@code List<BigInteger>} containing the loaded primes
  95      * @throws IOException If something nasty happens
  96      */
  97     private static List<BigInteger> parsePrimes(File f, long n) throws IOException {
  98         GZIPInputStream gis = new GZIPInputStream(new FileInputStream(f));
  99         BufferedReader br = new BufferedReader(new InputStreamReader(gis));
 100         try (Stream<String> lines = br.lines()) {
 101             return lines.limit(n).map(BigInteger::new).collect(toList());
 102         }
 103     }
 104 
 105     /**
 106      * Verifies whether the fraction of probable primes detected in the range
 107      * [2,upperBound) is at least 1 - 1/2^certainty.
 108      *
 109      * @return true if and only if the test succeeds
 110      */
 111     private static boolean checkPrime(File primesFile, int upperBound,
 112             int certainty,
 113             boolean parallel) throws FileNotFoundException, IOException {
 114 
 115         List<BigInteger> primes = parsePrimes(primesFile, upperBound);
 116 
 117         System.out.println(String.format("Loaded %d primes", primes.size()));
 118 
 119         long probablePrimes = (parallel ? primes.parallelStream() : primes.stream())
 120                 .filter(bi -> bi.isProbablePrime(certainty))
 121                 .count();
 122 
 123         System.out.println(probablePrimes + " probable primes out of " +
 124                 primes.size() + " candidates");
 125         if (probablePrimes != primes.size()) {
 126             System.err.println("Number of probable primes unequal to number of actual primes");
 127         }
 128 
 129         // N = certainty / 2
 130         // Success if p/t >= 1 - 1/4^N
 131         // or (p/t)*4^N >= 4^N - 1
 132         // or p*4^N >= t*(4^N - 1)
 133         BigInteger p = BigInteger.valueOf(probablePrimes);
 134         BigInteger t = BigInteger.valueOf(primes.size());
 135         BigInteger fourToTheC = BigInteger.valueOf(4).pow(certainty / 2);
 136         BigInteger fourToTheCMinusOne = fourToTheC.subtract(BigInteger.ONE);
 137         BigInteger left = p.multiply(fourToTheC);
 138         BigInteger right = t.multiply(fourToTheCMinusOne);
 139 
 140         if (left.compareTo(right) < 0) {
 141             System.err.println("Probable prime certainty test failed.");
 142         }
 143 
 144         return left.compareTo(right) >= 0;
 145     }
 146 
 147     /**
 148      * Verifies whether all {@code BigInteger}s in the range [2,upperBound) for
 149      * which {@code isProbablePrime()} returns {@code false} are <i>not</i>
 150      * prime numbers.
 151      *
 152      * @return true if and only if the test succeeds
 153      */
 154     private static boolean checkNotPrime(File primesFile, int upperBound,
 155             int certainty) throws FileNotFoundException, IOException {
 156         List<BigInteger> primes = parsePrimes(primesFile, upperBound);
 157 
 158         // If it's not a probable prime but is in the primes list then fail.
 159         boolean result = !(new Random()).ints(2500, 0, 100000).mapToObj(i -> {return BigInteger.valueOf(i);}).filter(b -> !b.isProbablePrime(certainty)).anyMatch(c -> primes.contains(c));
 160 
 161         return result;
 162     }
 163 }
 164