package gnu.crypto.util;

// ----------------------------------------------------------------------------
// $Id: Prime.java,v 1.11 2005/10/06 04:24:18 rsdio Exp $
//
// Copyright (C) 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
//
// This file is part of GNU Crypto.
//
// GNU Crypto is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2, or (at your option)
// any later version.
//
// GNU Crypto 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 for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; see the file COPYING.  If not, write to the
//
//    Free Software Foundation Inc.,
//    51 Franklin Street, Fifth Floor,
//    Boston, MA 02110-1301
//    USA
//
// Linking this library statically or dynamically with other modules is
// making a combined work based on this library.  Thus, the terms and
// conditions of the GNU General Public License cover the whole
// combination.
//
// As a special exception, the copyright holders of this library give
// you permission to link this library with independent modules to
// produce an executable, regardless of the license terms of these
// independent modules, and to copy and distribute the resulting
// executable under terms of your choice, provided that you also meet,
// for each linked independent module, the terms and conditions of the
// license of that module.  An independent module is a module which is
// not derived from or based on this library.  If you modify this
// library, you may extend this exception to your version of the
// library, but you are not obligated to do so.  If you do not wish to
// do so, delete this exception statement from your version.
// ----------------------------------------------------------------------------

import gnu.crypto.Properties;

import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.math.BigInteger;
import java.util.Map;
import java.util.WeakHashMap;

/**
 * <p>A collection of prime number related utilities used in this library.</p>
 *
 * @version $Revision: 1.11 $
 */
public class Prime {

   // Debugging methods and variables
   // -------------------------------------------------------------------------

   private static final String NAME = "prime";
   private static final boolean DEBUG = false;
   private static final int debuglevel = 5;
   private static final PrintWriter err = new PrintWriter(System.out, true);
   private static void debug(String s) {
      err.println(">>> "+NAME+": "+s);
   }

   // Constants and variables
   // -------------------------------------------------------------------------

   private static final int DEFAULT_CERTAINTY = 20; // XXX is this a good value?
   private static final BigInteger ZERO = BigInteger.ZERO;
   private static final BigInteger ONE = BigInteger.ONE;
   private static final BigInteger TWO = BigInteger.valueOf(2L);

   /**
    * The first SMALL_PRIME primes: Algorithm P, section 1.3.2, The Art of
    * Computer Programming, Donald E. Knuth.
    */
   private static final int SMALL_PRIME_COUNT = 1000;
   private static final BigInteger[] SMALL_PRIME =
         new BigInteger[SMALL_PRIME_COUNT];
   static {
      long time = -System.currentTimeMillis();
      SMALL_PRIME[0] = TWO;
      int N = 3;
      int J = 0;
      int prime;
      P2: while (true) {
         SMALL_PRIME[++J] = BigInteger.valueOf(N);
         if (J >= 999) {
            break P2;
         }
         P4: while (true) {
            N += 2;
            P6: for (int K = 1; true; K++) {
               prime = SMALL_PRIME[K].intValue();
               if ((N % prime) == 0) {
                  continue P4;
               } else if ((N / prime) <= prime) {
                  continue P2;
               }
            }
         }
      }
      time += System.currentTimeMillis();
      if (DEBUG && debuglevel > 8) {
         StringBuffer sb;
         for (int i = 0; i < (SMALL_PRIME_COUNT / 10); i++) {
            sb = new StringBuffer();
            for (int j = 0; j < 10; j++) {
               sb.append(String.valueOf(SMALL_PRIME[i*10+j])).append(" ");
            }
            debug(sb.toString());
         }
      }
      if (DEBUG && debuglevel > 4) {
         debug("Generating first "+String.valueOf(SMALL_PRIME_COUNT)
               +" primes took: "+String.valueOf(time)+" ms.");
      }
   }

   private static final Map knownPrimes = new WeakHashMap();

   // Constructor(s)
   // -------------------------------------------------------------------------

   /** Trivial constructor to enforce Singleton pattern. */
   private Prime() {
      super();
   }

   // Class methods
   // -------------------------------------------------------------------------

   /**
    * <p>Trial division for the first 1000 small primes.</p>
    *
    * <p>Returns <code>true</code> if at least one small prime, among the first
    * 1000 ones, was found to divide the designated number. Retuens <code>false</code>
    * otherwise.</p>
    *
    * @param w the number to test.
    * @return <code>true</code> if at least one small prime was found to divide
    * the designated number.
    */
   public static boolean hasSmallPrimeDivisor(BigInteger w) {
      BigInteger prime;
      for (int i = 0; i < SMALL_PRIME_COUNT; i++) {
         prime = SMALL_PRIME[i];
         if (w.mod(prime).equals(ZERO)) {
            if (DEBUG && debuglevel > 4) {
               debug(prime.toString(16)+" | "+w.toString(16)+"...");
            }
            return true;
         }
      }
      if (DEBUG && debuglevel > 4) {
         debug(w.toString(16)+" has no small prime divisors...");
      }
      return false;
   }

   /**
    * <p>Java port of Colin Plumb primality test (Euler Criterion)
    * implementation for a base of 2 --from bnlib-1.1 release, function
    * primeTest() in prime.c. this is his comments; (bn is our w).</p>
    *
    * <p>"Now, check that bn is prime. If it passes to the base 2, it's prime
    * beyond all reasonable doubt, and everything else is just gravy, but it
    * gives people warm fuzzies to do it.</p>
    *
    * <p>This starts with verifying Euler's criterion for a base of 2. This is
    * the fastest pseudoprimality test that I know of, saving a modular squaring
    * over a Fermat test, as well as being stronger. 7/8 of the time, it's as
    * strong as a strong pseudoprimality test, too. (The exception being when
    * <code>bn == 1 mod 8</code> and <code>2</code> is a quartic residue, i.e.
    * <code>bn</code> is of the form <code>a^2 + (8*b)^2</code>.) The precise
    * series of tricks used here is not documented anywhere, so here's an
    * explanation. Euler's criterion states that if <code>p</code> is prime
    * then <code>a^((p-1)/2)</code> is congruent to <code>Jacobi(a,p)</code>,
    * modulo <code>p</code>. <code>Jacobi(a, p)</code> is a function which is
    * <code>+1</code> if a is a square modulo <code>p</code>, and <code>-1</code>
    * if it is not. For <code>a = 2</code>, this is particularly simple. It's
    * <code>+1</code> if <code>p == +/-1 (mod 8)</code>, and <code>-1</code> if
    * <code>m == +/-3 (mod 8)</code>. If <code>p == 3 (mod 4)</code>, then all
    * a strong test does is compute <code>2^((p-1)/2)</code>. and see if it's
    * <code>+1</code> or <code>-1</code>. (Euler's criterion says <i>which</i>
    * it should be.) If <code>p == 5 (mod 8)</code>, then <code>2^((p-1)/2)</code>
    * is <code>-1</code>, so the initial step in a strong test, looking at
    * <code>2^((p-1)/4)</code>, is wasted --you're not going to find a
    * <code>+/-1</code> before then if it <b>is</b> prime, and it shouldn't
    * have either of those values if it isn't. So don't bother.</p>
    *
    * <p>The remaining case is <code>p == 1 (mod 8)</code>. In this case, we
    * expect <code>2^((p-1)/2) == 1 (mod p)</code>, so we expect that the
    * square root of this, <code>2^((p-1)/4)</code>, will be <code>+/-1 (mod p)
    * </code>. Evaluating this saves us a modular squaring 1/4 of the time. If
    * it's <code>-1</code>, a strong pseudoprimality test would call <code>p</code>
    * prime as well. Only if the result is <code>+1</code>, indicating that
    * <code>2</code> is not only a quadratic residue, but a quartic one as well,
    * does a strong pseudoprimality test verify more things than this test does.
    * Good enough.</p>
    *
    * <p>We could back that down another step, looking at <code>2^((p-1)/8)</code>
    * if there was a cheap way to determine if <code>2</code> were expected to
    * be a quartic residue or not. Dirichlet proved that <code>2</code> is a
    * quadratic residue iff <code>p</code> is of the form <code>a^2 + (8*b^2)</code>.
    * All primes <code>== 1 (mod 4)</code> can be expressed as <code>a^2 +
    * (2*b)^2</code>, but I see no cheap way to evaluate this condition."</p>
    *
    * @param w the number to test.
    * @return <code>true</code> iff the designated number passes Euler criterion
    * as implemented by Colin Plumb in his <i>bnlib</i> version 1.1.
    */

   // FIXME this method incorrectly returns false for certain primes, notably
   // 38737, 61681, 65537, 229153, and 274177.

   public static boolean passEulerCriterion(final BigInteger w) {
      // first check if it's already a known prime
      WeakReference obj = (WeakReference) knownPrimes.get(w);
      if (obj != null && w.equals(obj.get())) {
         if (DEBUG && debuglevel > 4) {
            debug("found in known primes");
         }
         return true;
      }

      BigInteger w_minus_one = w.subtract(ONE);
      BigInteger e = w_minus_one;
      // l is the 3 least-significant bits of e
      int l = e.and(BigInteger.valueOf(7L)).intValue();
      int j = 1; // Where to start in prime array for strong prime tests
      BigInteger A;
      int k;

      if ((l & 7) != 0) {
         e = e.shiftRight(1);
         A = TWO.modPow(e, w);
         if ((l & 7) == 6) { // bn == 7 mod 8, expect +1
            if (A.bitCount() != 1) {
               if (DEBUG && debuglevel > 4) {
                  debug(w.toString(16)+" fails Euler criterion #1...");
               }
               return false; // Not prime
            }
            k = 1;
         } else { // bn == 3 or 5 mod 8, expect -1 == bn-1
            A = A.add(ONE);
            if (!A.equals(w)) {
               if (DEBUG && debuglevel > 4) {
                  debug(w.toString(16)+" fails Euler criterion #2...");
               }
               return false; // Not prime
            }
            k = 1;
            if ((l & 4) != 0) { // bn == 5 mod 8, make odd for strong tests
               e = e.shiftRight(1);
               k = 2;
            }
         }
      } else { // bn == 1 mod 8, expect 2^((bn-1)/4) == +/-1 mod bn
         e = e.shiftRight(2);
         A = TWO.modPow(e, w);
         if (A.bitCount() == 1) {
            j = 0; // Re-do strong prime test to base 2
         } else {
            A = A.add(ONE);
            if (!A.equals(w)) {
               if (DEBUG && debuglevel > 4) {
                  debug(w.toString(16)+" fails Euler criterion #3...");
               }
               return false; // Not prime
            }
         }
         // bnMakeOdd(n) = d * 2^s. Replaces n with d and returns s.
         k = e.getLowestSetBit();
         e = e.shiftRight(k);
         k += 2;
      }
      // It's prime!  Now go on to confirmation tests

      // Now, e = (bn-1)/2^k is odd.  k >= 1, and has a given value with
      // probability 2^-k, so its expected value is 2.  j = 1 in the usual case
      // when the previous test was as good as a strong prime test, but 1/8 of
      // the time, j = 0 because the strong prime test to the base 2 needs to
      // be re-done.
      //for (int i = j; i < SMALL_PRIME_COUNT; i++) {
      for (int i = j; i < 13; i++) { // try only the first 13 primes
         A = SMALL_PRIME[i];
         A = A.modPow(e, w);
         if (A.bitCount() == 1) {
            continue; // Passed this test
         }
         l = k;
         while (true) {
//            A = A.add(ONE);
//            if (A.equals(w)) { // Was result bn-1?
            if (A.equals(w_minus_one)) { // Was result bn-1?
               break; // Prime
            }
            if (--l == 0) { // Reached end, not -1? luck?
               if (DEBUG && debuglevel > 4) {
                  debug(w.toString(16)+" fails Euler criterion #4...");
               }
               return false; // Failed, not prime
            }
            // This portion is executed, on average, once
//            A = A.subtract(ONE); // Put a back where it was
            A = A.modPow(TWO, w);
            if (A.bitCount() == 1) {
               if (DEBUG && debuglevel > 4) {
                  debug(w.toString(16)+" fails Euler criterion #5...");
               }
               return false; // Failed, not prime
            }
         }
         // It worked (to the base primes[i])
      }
      if (DEBUG && debuglevel > 4) {
         debug(w.toString(16)+" passes Euler criterion...");
      }

      // store it in the known primes weak hash-map
      knownPrimes.put(w, new WeakReference(w));

      return true;
  }

   /**
    * <p>Checks Fermat's Little Theorem for base <i>b</i>; i.e.
    * <code><i>b</i>**(w-1) == 1 (mod w)</code>.</p>
    *
    * @param w the number to test.
    * @param t the number of random bases to test.
    * @return <code>true</code> iff <code><i>b</i>**(w-1) == 1 (mod w)</code>,
    * for some <i>b</i>.
    */
   public static boolean passFermatLittleTheorem(final BigInteger w, int t) {
      final BigInteger w_minus_one = w.subtract(ONE);
      if (t <= 0) {
         t = 10; // XXX
      }
      if (!TWO.modPow(w_minus_one, w).equals(ONE)) {
         if (DEBUG && debuglevel > 4) {
            debug(w.toString(16)+" fails Fermat's little theorem for base 2");
         }
         return false;
      }
      for (int i = 0; i < t; i++) {
         byte[] buf = new byte[(w.bitLength() + 7) / 8 - 1];
         BigInteger base = null;
         do {
            PRNG.nextBytes(buf);
            base = new BigInteger(1, buf);
         } while (base.compareTo(TWO) < 0 || base.compareTo(w_minus_one) > 0);
         if (!base.modPow(w_minus_one, w).equals(ONE)) {
            if (DEBUG && debuglevel > 4) {
               debug(w.toString(16)+" fails Fermat's little theorem for base "
                     +base.toString(16));
            }
            return false;
         }
      }
      if (DEBUG && debuglevel > 4) {
         debug(w.toString(16)+" passes Fermat's little theorem for "+t+" bases...");
      }
      return true;
   }

   /**
    * <p>Applies the Miller-Rabin strong probabilistic primality test.</p>
    *
    * <p>The HAC (Handbook of Applied Cryptography), Alfred Menezes & al. Note
    * 4.57 states that for <code>q</code>, <code>n=18</code> is enough while
    * for <code>p</code>, <code>n=6</code> (512 bits) or <code>n=3</cdoe> (1024
    * bits) are enough to yield <i>robust</i> primality tests. The values used
    * are from table 4.4 given in Note 4.49.</p>
    *
    * @param w the number to test.
    * @return <code>true</code> iff the designated number passes the Miller-
    * Rabin probabilistic primality test for a computed number of rounds.
    */
   public static boolean passMillerRabin(BigInteger n, int t) {
      int nbytes = (n.bitLength() + 7) / 8;
      byte[] ab = new byte[nbytes];

      // 1. Write n - 1 = 2^s * r.
      BigInteger n_minus_1 = n.subtract(ONE);
      BigInteger r = n_minus_1;
      int s = 0;
      while (!r.testBit(0)) {
         r = r.shiftRight(1);
         s++;
      }

      // 2. For i from 1 to t, do:
      for (int i = 0; i < t; i++) {

         // 2.1 Choose a random integer a, 2 <= a <= n - 2.
         BigInteger a;
         do {
            PRNG.nextBytes(ab);
            a = new BigInteger(1, ab);
         } while (a.compareTo(TWO) < 0 || a.compareTo(n) > 0);

         // 2.2 Compute y = a^r mod n.
         BigInteger y = a.modPow(r, n);

         // If y != 1 and y != n - 1, then:
         if (!y.equals(ONE) && !y.equals(n_minus_1)) {
            for (int j = 1; j < s - 1 && !y.equals(n_minus_1); j++) {
               // Compute y = y^2 mod n.
               y = y.modPow(TWO, n);

               // If y = 1 return "composite"
               if (y.equals(ONE)) {
                  if (DEBUG && debuglevel > 4) {
                     debug(n.toString(16)+" fails Miller-Rabin test...");
                  }
                  return false;
               }
            }
            // If y != n - 1 return "composite"
            if (!y.equals(n_minus_1)) {
               if (DEBUG && debuglevel > 4) {
                  debug(n.toString(16)+" fails Miller-Rabin test...");
               }
               return false;
            }
         }
      }
      if (DEBUG && debuglevel > 4) {
         debug(n.toString(16)+" passes Miller-Rabin test...");
      }
      return true;
   }

   public static boolean isProbablePrime(BigInteger w) {
      return isProbablePrime(w, DEFAULT_CERTAINTY);
   }

   /**
    * <p>This implementation does not rely solely on the Miller-Rabin strong
    * probabilistic primality test to claim the primality of the designated
    * number. It instead, tries dividing the designated number by the first 1000
    * small primes, and if no divisor was found, invokes a port of Colin Plumb's
    * implementation of the Euler Criterion, then tries the Miller-Rabin test.</p>
    *
    * @param w the integer to test.
    * @param certainty the certainty with which to compute the test.
    * @param doMillerRabin if <code>true</code> and the designated integer was
    * already found to be a probable prime, then also do a Miller-Rabin test.
    * @return <code>true</code> iff the designated number has no small prime
    * divisor passes the Euler criterion, and optionally a Miller-Rabin test.
    */
   public static boolean isProbablePrime(BigInteger w, int certainty) {
      // Nonnumbers are not prime.
      if (w == null) {
         return false;
      }

      // eliminate trivial cases when w == 0 or 1
      if (w.equals(ZERO) || w.equals(ONE)) {
         return false;
      }

      // Test if w is a known small prime.
      for (int i = 0; i < SMALL_PRIME_COUNT; i++) {
         if (w.equals(SMALL_PRIME[i])) {
            if (DEBUG && debuglevel > 4) {
               debug(w.toString(16)+" is a small prime");
            }
            return true;
         }
      }

      // trial division with first 1000 primes
      if (hasSmallPrimeDivisor(w)) {
         if (DEBUG && debuglevel > 4) {
            debug(w.toString(16)+" has a small prime divisor. Rejected...");
         }
         return false;
      }

      // Do a check with Fermat's little theorem.
//       if (passFermatLittleTheorem(w, certainty)) {
//          if (DEBUG && debuglevel > 4) {
//             debug(w.toString(16)+" passes Fermat's little theorem...");
//          }
//       } else {
//          if (DEBUG && debuglevel > 4) {
//             debug(w.toString(16)+" fails Fermat's little theorem. Rejected...");
//          }
//          return false;
//       }

      // Euler's criterion.
//       if (passEulerCriterion(w)) {
//          if (DEBUG && debuglevel > 4) {
//             debug(w.toString(16)+" passes Euler's criterion...");
//          }
//       } else {
//          if (DEBUG && debuglevel > 4) {
//             debug(w.toString(16)+" fails Euler's criterion. Rejected...");
//          }
//          return false;
//       }

      // Miller-Rabin probabilistic primality test.
      if (passMillerRabin(w, certainty)) {
         if (DEBUG && debuglevel > 4) {
            debug(w.toString(16)+" passes Miller-Rabin PPT...");
         }
      } else {
         if (DEBUG && debuglevel > 4) {
            debug(w.toString(16)+" fails Miller-Rabin PPT. Rejected...");
         }
         return false;
      }

      if (DEBUG && debuglevel > 4) {
         debug(w.toString(16)+" is probable prime. Accepted...");
      }

      // now compare to JDK primality test
      if (DEBUG && debuglevel > 0 && !w.isProbablePrime(certainty)) {
         System.err.println("The gnu.crypto library and the JDK disagree on "
            +"whether 0x"+w.toString(16)+" is a probable prime or not.");
         System.err.println("While this library claims it is, the JDK claims"
            +" the opposite.");
         System.err.println("Please contact the maintainer of this library, "
            +"and provide this message for further investigation. TIA");
      }

      return true;
   }
}
