/*
   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 <gmp.h>


/* Produit des nombres premiers impairs <= n */
void pprime(mpz_t 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 */
  mpz_set_ui(p,1);
  for (i=0; i<l; i++) if (t[i]) mpz_mul_ui(p,p,2*i+3);
  free(t);
}

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

  mpz_init(p);
  mpz_init(q);
  mpz_init(n1);

  /* cherche la 2-valuation de n-1 */
  mpz_sub_ui(n1,n,1);
  for (i=0, x=mpz_getlimbn(n1,0); x==0; x=mpz_getlimbn(n1,++i));
  for (i*=8*sizeof(mp_limb_t); (x&1) == 0; i++, x>>=1);

  /* calcule a^((n-1)/2^i) (mod n) */
  mpz_tdiv_q_2exp(q,n1,i);
  mpz_powm(p,a,q,n);

  /* élève au carré jusqu'à trouver 1 ou -1 */
   if (mpz_cmp_ui(p,1)==0) {res=1;}
   else {
     while ((mpz_cmp_ui(p,1)) && (mpz_cmp(p,n1)) && (i > 1)) {
       mpz_mul(p,p,p);
       mpz_tdiv_r(p,p,n);
       i--;
     }
     res = (mpz_cmp(p,n1) == 0);
   }

   mpz_clear(p);
   mpz_clear(q);
   mpz_clear(n1);
   return(res);
}

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

  mpz_init(n);
  mpz_init(p);
  mpz_init(a);
  mpz_init(x);
  mpz_init(y);

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

  /* 10^n */
  mpz_set_ui(n,10); mpz_pow_ui(n,n,e);

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

  /* boucle sur n */
  for (mpz_add_ui(x,n,1); ;mpz_add_ui(x,x,2)) {
    mpz_gcd(y,p,x);
    if ((mpz_cmp_ui(y,1) == 0) && (rabin(x,a))) break;
  }
  mpz_sub(x,x,n);

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

  mpz_clear(n);
  mpz_clear(p);
  mpz_clear(a);
  mpz_clear(x);
  mpz_clear(y);
  return(0);

}
