/*
 * File `rabinmiller.ys' is an implementation of the
 *           Rabin-Miller primality test.
 */


/*
 * FastModularPower(a, b, n) computes a^b (mod n) efficiently.
 * This function is called by IsStronglyProbablyPrime. 
 */

FastModularPower(a_IsPositiveInteger, b_IsPositiveInteger, n_IsPositiveInteger) <-- 
[
  Local(p, j, r);
  p := a;
  j := b;
  r := 1;

  While (j > 0)
    [
      If (IsOdd(j), r := MathMod(r*p, n));
      p := MathMod(p*p, n);
      j := ShiftRight(j, 1);
    ];
  r;
];


/*
 * An integer n is `strongly-probably-prime' for base b if
 *
 *                   b^q = 1 (mod n) or
 * b^(q*2^i) = -1 (mod n) for some i such that 0 <= i < r
 *
 *    where q and r are such that n-1 = q*2^r and q is odd.
 *
 * If an integer is not strongly-probably-prime for a given
 * base b, then it is composed. The reciprocal is false.
 * Composed strongly-probably-prime numbers for base b
 * are called `strong pseudoprimes' for base b.
 */

IsStronglyProbablyPrime(b_IsPositiveInteger, n_IsPositiveInteger) <-- 
[
  Local(m, q, r, a, flag, i);
  m := n-1;
  q := m;
  r := 0;
  
  While (IsEven(q))
  [
    q := ShiftRight(q, 1);
    r++;
  ];
  
  a := FastModularPower(b, q, n);
  flag := (a = 1);
  i := 0;

  While (Not(flag) And (i < r))
  [
    flag := (a = m);
    a := MathMod(a*a, n);
    i++;
  ];

  flag;
];


/*
 * For numbers less than 3.4e14, exhaustive computations have
 * shown that there is no strong pseudoprime simultaneously for
 * bases 2, 3, 5, 7, 11, 13 and 17.
 * Function RabinMillerSmall is based on the results of these
 * computations. 
 */
 
10 # RabinMillerSmall(1) <-- False;

20 # RabinMillerSmall(2) <-- True;

30 # RabinMillerSmall(n_IsPositiveInteger) <--
[
  Local(continue, prime, i, primetable, pseudotable);
  continue := True;
  prime := True;
  i := 1;
  primetable := {2, 3, 5, 7, 11, 13, 17};
  pseudotable := {2047, 1373653, 25326001, 3215031751, 2152302898747,
                  3474749660383, 34155071728321};

  While (continue And prime And (i < 8))
  [
    prime := IsStronglyProbablyPrime(primetable[i], n);
    continue := (n >= pseudotable[i]);
    i++;
  ];

  If (continue And (i = 8), Overflow, prime);
];


/*
 * RabinMillerProbabilistic(n, p) tells whether n is prime.
 * If n is actually prime, the result will always be `True'.
 * If n is composed the probability to obtain the wrong
 * result is less than 10^(-p).
 */

10 # RabinMillerProbabilistic(1) <-- False;

20 # RabinMillerProbabilistic(2) <-- True;

30 # RabinMillerProbabilistic(n_IsPositiveInteger, p_IsPositiveInteger) <--
[
  Local(i, k, prime, b);
  i := 0;
  k := Ceil((MathLog(MathLog(n))+p*MathLog(10))/MathLog(4));
  prime := True;
  While (prime And (i < k))
    [
      b := 2 + Floor((n-3)*Random());
      prime := IsStronglyProbablyPrime(b, n);
      i++;
    ];
  prime;
];


/*
 * This is the frontend function, which uses RabinMillerSmall for
 * ``small'' numbers and RabinMillerProbabilistic for bigger ones.
 * 
 * The probability to err is set to 1e-25, hopping this is less
 * than the one to step on a rattlesnake in northern Groenland. :-)
 */

RabinMiller(n_IsPositiveInteger) <--
  If(n < 34155071728321,
     RabinMillerSmall(n),
     RabinMillerProbabilistic(n, 25));
