/* This file is part of the crlibm library which is distributed under
   the LGPL. This file itself is distributed under the GPL. Copyright
   Florent de Dinechin and the Arenaire Team at ENS-Lyon 
*/

#include <stdio.h>
#include <stdlib.h>
#include "crlibm.h"
#include "crlibm_private.h"
#ifdef HAVE_MPFR_H  /* stop here if MPFR not present */
#include "test_common.h"
#include <gmp.h>
#include <mpfr.h>







void usage(char *fct_name){
  /* fprintf (stderr, "\n%s: Searches at random difficult-to-round cases for correctly rounded libraries \n", fct_name); */
  fprintf (stderr, "\nUsage: %s function n seed \n", fct_name);
  fprintf (stderr, " function : name of function to test \n");
  fprintf (stderr, " n        : integer, only print out numbers with at least \n");
  fprintf (stderr, "             n identical bits after the round bit (try 10)\n");
  fprintf (stderr, " seed     : integer seed for the random number generator \n");
  exit (1);
}



int main (int argc, char *argv[]) 
{ 
  char* function_name;
  int n;
  int seed;
  double worstcase;
  char* rounding_mode="RN" ; /* needed by init function, otherwise unused */
  db_number input, res_mpfr;
  mpfr_t mp_res, mp_res53, mp_res54, mp_res_ru, mp_res_rd, mp_inpt; 
  
  /* The random number generator*/
  double (*randfun)       () = NULL;
  /* Another unused random number generator*/
  double (*randfun_perf)       () = NULL;
  
  /* The function we test */
  double (*testfun_crlibm)() = NULL;
  /* The function we trust */
  int    (*testfun_mpfr)  () = NULL;
  double (*testfun_libm)  () = NULL;
  double (*testfun_libultim)   () = NULL;
  double (*testfun_libmcr)   () = NULL;
  
  if ((argc != 4)) usage(argv[0]);
  else{
    function_name = argv[1];
    sscanf(argv[2],"%d", &n);
    sscanf(argv[3],"%d", &seed);
    
    test_init(/* pointers to returned value */
	      &randfun_perf, /* unused here*/ 
	      &randfun, 
	      &testfun_crlibm, 
	      &testfun_mpfr,
	      &testfun_libultim,  /* unused here*/ 
	      &testfun_libmcr, /* unused here*/ 
	      &testfun_libm, /* unused here*/ 
	      &worstcase, /* unused here*/ 
	      /* arguments */
	      function_name,
	      rounding_mode  /* unused here*/ ) ;
    
    mpfr_init2(mp_res,  53+n+1);
    mpfr_init2(mp_res_ru, 53);
    mpfr_init2(mp_res_rd, 53);
    mpfr_init2(mp_res53, 53);
    mpfr_init2(mp_res54, 54);
    mpfr_init2(mp_inpt, 53);
    printf("# Bad cases generated by %s %s %s %s \n", argv[0], argv[1], argv[2], argv[3] );
    
    srand(seed);
    
    while(1+1==2){
      input.d = randfun();
      mpfr_set_d(mp_inpt, input.d, GMP_RNDN);
      testfun_mpfr(mp_res, mp_inpt, GMP_RNDN);
      testfun_mpfr(mp_res54, mp_inpt, GMP_RNDN);
      if(mpfr_cmp (mp_res54, mp_res)==0) {
	/* This is a difficult to round case. Now in which direction ? */
	/* First filter results equal to zero, infty, 1.0 */ 
	res_mpfr.d = mpfr_get_d(mp_res, GMP_RNDN);
	if(  ((res_mpfr.i[HI] & 0x7ff00000) != 0x7ff00000) && (res_mpfr.d!=0.0) && (res_mpfr.d!=1.0)  ){

	  /* is it difficult to round to nearest ? */
	  testfun_mpfr(mp_res53, mp_inpt, GMP_RNDN);
	  if(mpfr_cmp (mp_res54, mp_res53)!=0) {
	    res_mpfr.d = mpfr_get_d(mp_res53, GMP_RNDN);
	    printf("N %08x %08x  %08x %08x # %1.30e\n", 
		 input.i[HI], input.i[LO],
		 res_mpfr.i[HI], res_mpfr.i[LO],
		 input.d);
	  }
	  else{
	    /* now we have mpfr_cmp (mp_res, mp_res53)==0 
	       Test the three directed rounding modes  */
	    testfun_mpfr(mp_res53, mp_inpt, GMP_RNDD);
	    res_mpfr.d = mpfr_get_d(mp_res53, GMP_RNDN);
	    printf("M %08x %08x  %08x %08x # %1.30e\n", 
		   input.i[HI], input.i[LO],
		   res_mpfr.i[HI], res_mpfr.i[LO],
		   input.d);

	    testfun_mpfr(mp_res53, mp_inpt, GMP_RNDU);
	    res_mpfr.d = mpfr_get_d(mp_res53, GMP_RNDN);
	    printf("P %08x %08x  %08x %08x # %1.30e\n", 
		   input.i[HI], input.i[LO],
		   res_mpfr.i[HI], res_mpfr.i[LO],
		   input.d);
	    
	    testfun_mpfr(mp_res53, mp_inpt, GMP_RNDZ);
	    res_mpfr.d = mpfr_get_d(mp_res53, GMP_RNDN);
	    printf("Z %08x %08x  %08x %08x # %1.30e\n", 
		   input.i[HI], input.i[LO],
		   res_mpfr.i[HI], res_mpfr.i[LO],
		   input.d);
	  }
	}
      }
    }
  }
  return 0;
}





#if 0
int
main (int argc, char *argv[])
{
  double left = atof(argv[1]);   /* left side of the interval */
  double right = atof(argv[2]);  /* right side of the interval */
  int n = atoi (argv[1]);        /* how many bits do we want  */
  mpfr_t x, dx, pisur4, y, s, c, z, y2;
 i nt i, found;
  double j, k;
  mpfr_init2 (x, 53);
  mpfr_init2 (y, 53);
  mpfr_init2 (y2, 53);
  mpfr_init2 (z, 53);
  mpfr_init2 (dx, 53);
  mpfr_init2 (pisur4, 53);
  mpfr_init2 (s, 53 + n  - 1);

  /* Pi/4 */
/*   mpfr_const_pi (pisur4, GMP_RNDU); */
/*   mpfr_div_ui (pisur4, pisur4, 4, GMP_RNDU); */

/*   mpfr_set (dx, pisur4, GMP_RNDN); */
/*   mpfr_div_ui (dx, dx, n, GMP_RNDN); /\* Pi/(4n) *\/ */
/*   mpfr_div_ui (x, dx, 2, GMP_RNDN); /\* x0 = dx/2 *\/ */

  while(1+1==2) {
  }
  i = 0;
  while (mpfr_cmp_d (x, minx) < 0)
    {
      i ++;
      mpfr_add (x, x, dx, GMP_RNDN);
    }
  while (mpfr_cmp (x, pisur4) <= 0)
    {
      i ++;
      mpfr_set (y, x, GMP_RNDN);
      mpfr_set (y2, x, GMP_RNDN);
      found = 0;
      j = k = 0.0;
      while (!found)
        {
          mpfr_sin (s, y, GMP_RNDN);
          mpfr_set (z, s, GMP_RNDN);
          if (mpfr_cmp (z, s) == 0)
            {
              mpfr_cos (c, y, GMP_RNDN);
              mpfr_set (z, c, GMP_RNDN);
              found = mpfr_cmp (z, c) == 0;
              if (found)
                {
                  mpfr_t t;
                  mpfr_init2 (t, 200);
                  printf ("j=%1.0f ", j);
                  mpfr_sin (t, y, GMP_RNDN);
                  mpfr_sub (t, t, s, GMP_RNDN);
                  printf ("[%lu,", mpfr_get_exp (s) - mpfr_get_exp (t) - 53);
                  mpfr_cos (t, y, GMP_RNDN);
                  mpfr_sub (t, t, c, GMP_RNDN);
                  printf ("%lu] ", mpfr_get_exp (c) - mpfr_get_exp (t) - 53);
                  mpfr_clear (t);
                  mpfr_dump (y);
                  break;
                }
            }
          j++;
          mpfr_nextabove (y);
          k --;
          mpfr_nextbelow (y2);
          mpfr_sin (s, y2, GMP_RNDN);
          mpfr_set (z, s, GMP_RNDN);
          if (mpfr_cmp (z, s) == 0)
            {
              mpfr_cos (c, y2, GMP_RNDN);
              mpfr_set (z, c, GMP_RNDN);
              found = mpfr_cmp (z, c) == 0;
              if (found)
                {
                  mpfr_t t;
                  mpfr_init2 (t, 200);
                  printf ("j=%1.0f ", k);
                  mpfr_sin (t, y2, GMP_RNDN);
                  mpfr_sub (t, t, s, GMP_RNDN);
                  printf ("[%lu,", mpfr_get_exp (s) - mpfr_get_exp (t) - 53);
                  mpfr_cos (t, y2, GMP_RNDN);
                  mpfr_sub (t, t, c, GMP_RNDN);
                  printf ("%lu] ", mpfr_get_exp (c) - mpfr_get_exp (t) - 53);
                  mpfr_clear (t);
                  mpfr_dump (y2);
                  break;
                }
            }
        }
      mpfr_add (x, x, dx, GMP_RNDN);
    }
  mpfr_clear (x);
  mpfr_clear (dx);
  mpfr_clear (pisur4);
  mpfr_clear (y);
  mpfr_clear (y2);
  mpfr_clear (s);
  mpfr_clear (c);
  mpfr_clear (z);
  return 0;
}

#endif

#else
int main (int argc, char *argv[]) 
{ 
  printf("Sorry, I need to be compiled against MPFR\n");
  return 0;
}
#endif



