/*
   This file is part of Numerix.  Numerix 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 of the License, or (at your option) any later version.

   This program 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; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA 
*/

/* +------------------------------------------------------------------------+
   |                                                                        |
   |        Recherche du premier nombre pseudopremier à N chifres           |
   |                                                                        |
   +------------------------------------------------------------------------+ */

/* M. Quercia, le 12/08/2001 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "c-long_int.h"


/* Produit des nombres premiers impairs <= n */
void pprime(xint *p, long n) {
  long l = (n-1)/2, i,j;
  char *t;

  /* crible d'Eratosthène : t[i] <=> 2i+3 est premier */
  t = (char *)malloc(l);
  if (t == NULL) {
    fprintf(stderr,"fatal error: can't allocate sieve\n");
    fflush(stderr);
    exit(1);
  }
  memset(t,1,l);
  for (i=0, j=3; j < l;) {
      t[j] = 0;
      j += 2*i + 3;
      if (j >= l) {
	i++;
	while ((i < l) && (!t[i])) i++;
	j = 2*i*(i+3) + 3;
      }
  }
    
  /* effectue le produit */
  xx(copy_int)(p,1);
  for (i=0; i<l; i++) if (t[i]) xx(mul_1)(p,*p,2*i+3);
  free(t);
}

/* Test de Rabin-Miller */
int rabin(xint n, xint a) {
  xint p  = xx(new)();
  xint q  = xx(new)();
  xint n1 = xx(new)();
  long i,x;
  int res;

  /* cherche la 2-valuation de n-1 */
  xx(sub_1)(&n1,n,1);
  for (i=0, x=xx(nth_word)(n1,0); x==0; x=xx(nth_word)(n1,++i));
  for (i*=16; (x&1) == 0; i++, x>>=1);

  /* calcule a^((n-1)/2^i) (mod n) */
  xx(shr)(&q,n1,i);
  xx(powmod)(&p,a,q,n);

  /* élève au carré jusqu'à trouver 1 ou -1 */
   if (xx(cmp_1)(p,1)==0) {res=1;}
   else {
     while ((xx(cmp_1)(p,1)) && (xx(cmp)(p,n1)) && (i > 1)) {
       xx(sqr)(&p,p);
       xx(quomod)(&q,&p,p,n);
       i--;
     }
     res = (xx(cmp)(p,n1) == 0);
   }

   xx(free)(&p);
   xx(free)(&q);
   xx(free)(&n1);
   return(res);
}

/* fonction principale */
int main(int argc, char **argv) {
  xint n  = xx(new)();
  xint p  = xx(new)();
  xint a  = xx(new)();
  xint x  = xx(new)();
  xint y  = xx(new)();
  long e;

  /* décode les arguments */
  if ((argc == 2) && (strcmp(argv[1],"-test")==0)) {
    e = 100; xx(copy_int)(&a,2);
  }
  else if ((argc == 3)) {
    e = atol(argv[1]);
    xx(copy_string)(&a,argv[2]);
  }
  else {
    fprintf(stderr,"syntaxe : nextpp <n> <a>\n");
    fflush(stderr);
    exit(1);
  }

  /* 10^n */
  xx(copy_int)(&n,10); xx(pow)(&n,n,e);

  /* produit des premiers premiers */
  pprime(&p,1000);

  /* boucle sur n */
  for (xx(add_1)(&x,n,1); ;xx(add_1)(&x,x,2)) {
    xx(gcd)(&y,p,x);
    if ((xx(cmp_1)(y,1) == 0) && (rabin(x,a))) break;
  }
  xx(sub)(&x,x,n);

  /* l'affiche */
  fprintf(stdout, "%ld\n",xx(int_of)(x));
  fflush(stdout);

  xx(free)(&n);
  xx(free)(&p);
  xx(free)(&a);
  xx(free)(&x);
  xx(free)(&y);
  return(0);

}
