/* mpc-tests.h -- Tests helper functions.

Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2020 INRIA

This file is part of GNU MPC.

GNU MPC is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the
Free Software Foundation; either version 3 of the License, or (at your
option) any later version.

GNU MPC 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 Lesser General Public License for
more details.

You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see http://www.gnu.org/licenses/ .
*/

#ifndef __MPC_TESTS_H
#define __MPC_TESTS_H

#include "config.h"
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include "mpc.h"

/* pieces copied from mpc-impl.h */
#define MPC_PREC_RE(x) (mpfr_get_prec(mpc_realref(x)))
#define MPC_PREC_IM(x) (mpfr_get_prec(mpc_imagref(x)))
#define MPC_MAX_PREC(x) MPC_MAX(MPC_PREC_RE(x), MPC_PREC_IM(x))
#define MPC_MAX(h,i) ((h) > (i) ? (h) : (i))

#define MPC_ASSERT(expr)                                        \
  do {                                                          \
    if (!(expr))                                                \
      {                                                         \
        fprintf (stderr, "%s:%d: MPC assertion failed: %s\n",   \
                 __FILE__, __LINE__, #expr);                    \
        abort();                                                \
      }                                                         \
  } while (0)

#if defined (__cplusplus)
extern "C" {
#endif
__MPC_DECLSPEC int  mpc_mul_naive (mpc_ptr, mpc_srcptr, mpc_srcptr, mpc_rnd_t);
__MPC_DECLSPEC int  mpc_mul_karatsuba (mpc_ptr, mpc_srcptr, mpc_srcptr, mpc_rnd_t);
__MPC_DECLSPEC int  mpc_fma_naive (mpc_ptr, mpc_srcptr, mpc_srcptr, mpc_srcptr, mpc_rnd_t);
#if defined (__cplusplus)
}
#endif
/* end pieces copied from mpc-impl.h */

#define MPC_OUT(x)                                              \
do {                                                            \
  printf (#x "[%lu,%lu]=", (unsigned long int) MPC_PREC_RE (x), \
      (unsigned long int) MPC_PREC_IM (x));                     \
  mpc_out_str (stdout, 2, 0, x, MPC_RNDNN);                     \
  printf ("\n");                                                \
} while (0)

#define MPFR_OUT(x)                                             \
do {                                                            \
  printf (#x "[%lu]=", (unsigned long int) mpfr_get_prec (x));  \
  mpfr_out_str (stdout, 2, 0, x, MPFR_RNDN);                     \
  printf ("\n");                                                \
} while (0)


#define MPC_INEX_STR(inex)                      \
  (inex) == 0 ? "(0, 0)"                        \
    : (inex) == 1 ? "(+1, 0)"                   \
    : (inex) == 2 ? "(-1, 0)"                   \
    : (inex) == 4 ? "(0, +1)"                   \
    : (inex) == 5 ? "(+1, +1)"                  \
    : (inex) == 6 ? "(-1, +1)"                  \
    : (inex) == 8 ? "(0, -1)"                   \
    : (inex) == 9 ? "(+1, -1)"                  \
    : (inex) == 10 ? "(-1, -1)" : "unknown"

#define TEST_FAILED(func,op,got,expected,rnd)			\
  do {								\
    printf ("%s(op) failed [rnd=%d]\n with", func, rnd);	\
    MPC_OUT (op);						\
    printf ("     ");						\
    MPC_OUT (got);						\
    MPC_OUT (expected);						\
    exit (1);							\
  } while (0)

#define QUOTE(X) NAME(X)
#define NAME(X) #X

/** RANDOM FUNCTIONS **/
/* the 3 following functions handle seed for random numbers. Usage:
   - add test_start at the beginning of your test function
   - use test_default_random (or use your random functions with
   gmp_randstate_t rands) in your tests
   - add test_end at the end the test function */
extern gmp_randstate_t  rands;

extern void test_start (void);
extern void test_end   (void);
extern void test_default_random (mpc_ptr, mpfr_exp_t, mpfr_exp_t,
                                 unsigned int, unsigned int);

void test_random_si   (long int *n, unsigned long emax,
                       unsigned int negative_probability);
void test_random_d    (double *x, unsigned int negative_probability);
void test_random_mpfr (mpfr_ptr x, mpfr_exp_t emin, mpfr_exp_t emax,
                       unsigned int negative_probability);
void test_random_mpc  (mpc_ptr z, mpfr_exp_t emin, mpfr_exp_t emax,
                       unsigned int negative_probability);

/** COMPARISON FUNCTIONS **/
/* some sign are unspecified in ISO C99, thus we record in struct known_signs_t
   whether the sign has to be checked */
typedef struct
{
  int re;  /* boolean value */
  int im;  /* boolean value */
} known_signs_t;

/* same_mpfr_value returns 1:
   - if got and ref have the same value and known_sign is true,
   or
   - if they have the same absolute value, got = 0 or got = inf, and known_sign is
   false.
   returns 0 in other cases.
   Unlike mpfr_cmp, same_mpfr_value(got, ref, x) return 1 when got and
   ref are both NaNs. */
extern int same_mpfr_value (mpfr_ptr got, mpfr_ptr ref, int known_sign);
extern int same_mpc_value  (mpc_ptr got, mpc_ptr ref,
                            known_signs_t known_signs);

/** READ FILE WITH TEST DATA SET **/
extern FILE * open_data_file         (const char *file_name);
extern void   close_data_file        (FILE *fp);

/* helper file reading functions */
extern void skip_whitespace_comments (FILE *fp);
extern void read_ternary             (FILE *fp, int* ternary);
extern void read_mpfr_rounding_mode  (FILE *fp, mpfr_rnd_t* rnd);
extern void read_mpc_rounding_mode   (FILE *fp, mpc_rnd_t* rnd);
extern mpfr_prec_t read_mpfr_prec    (FILE *fp);
extern void read_int                 (FILE *fp, int *n, const char *name);
extern size_t read_string            (FILE *fp, char **buffer_ptr,
                                      size_t buffer_length, const char *name);
extern void read_mpfr                (FILE *fp, mpfr_ptr x, int *known_sign);
extern void read_mpc                 (FILE *fp, mpc_ptr z, known_signs_t *ks);

void set_mpfr_flags   (int counter);
void check_mpfr_flags (int counter);

/*
  function descriptions
*/

/* type for return, output and input parameters */
typedef enum {
  NATIVE_INT,          /* int */
  NATIVE_UL,           /* unsigned long */
  NATIVE_L,            /* signed long */
  NATIVE_D,            /* double */
  NATIVE_LD,           /* long double */
  NATIVE_DC,           /* double _Complex */
  NATIVE_LDC,          /* long double _Complex */
  NATIVE_IM,           /* intmax_t */
  NATIVE_UIM,          /* uintmax_t */
  NATIVE_STRING,       /* char* */
  GMP_Z,               /* mpz_t */
  GMP_Q,               /* mpq_t */
  GMP_F,               /* mpf_t */
  MPFR_INEX,           /* mpfr_inex */
  MPFR,                /* mpfr_t  */
  MPFR_RND,            /* mpfr_rnd_t */
  MPC_INEX,            /* mpc_inex */
  MPC,                 /* mpc_t */
  MPC_RND,             /* mpc_rnd_t */
  MPCC_INEX            /* mpcc_inex */
} mpc_param_t;

/* additional information for checking mpfr_t result */
typedef struct {
  mpfr_t              mpfr; /* skip space for the variable */
  int                 known_sign;
} mpfr_data_t;

#define TERNARY_NOT_CHECKED 255
/* special value to indicate that the ternary value is not checked */
#define TERNARY_ERROR 254
/* special value to indicate that an error occurred in an mpc function */

/* mpc nonary value as a pair of ternary value for data from a file */
typedef struct {
  int                real;
  int                imag;
} mpc_inex_data_t;

/* additional information for checking mpc_t result */
typedef struct {
  mpc_t               mpc; /* skip space */
  int                 known_sign_real;
  int                 known_sign_imag;
} mpc_data_t;

/* string buffer information */
typedef struct {
  char*               string; /* skip space */
  int                 length;
} string_info_t;

/* abstract parameter type

   Let consider an abstract parameter p, which is declared as follows:
   mpc_operand_t p;
   we use the fact that a mpfr_t (respectively mpc_t) value can be accessed as
   'p.mpfr' (resp. 'p.mpc') as well as 'p.mpfr_info.mpfr'
   (resp. 'p.mpc_info.mpc'), the latter form permitting access to the
   'known_sign' field(s).
   Similarly, if the abstract parameter represent a string variable, we can
   access its value with 'p.string' or 'p.string_info.string' and its size
   with 'p.string_info.length'.

   The user uses the simple form when adding a test, the second form is used by the
   test suite itself when reading reference data and checking result against them.
*/
typedef union {
  int                  i;
  unsigned long        ui;
  signed long          si;
  double               d;
  long double          ld;
#ifdef _MPC_H_HAVE_INTMAX_T
  intmax_t             im;
  uintmax_t            uim;
#endif
#ifdef _Complex_I
  double _Complex      dc;
  long double _Complex ldc;
#endif
  char *               string;
  string_info_t        string_info;
  mpz_t                mpz;
  mpq_t                mpq;
  mpf_t                mpf;
  mpfr_t               mpfr;
  mpfr_data_t          mpfr_data;
  mpfr_rnd_t           mpfr_rnd;
  int                  mpfr_inex;
  mpc_t                mpc;
  mpc_data_t           mpc_data;
  mpc_rnd_t            mpc_rnd;
  int                  mpc_inex;
  mpc_inex_data_t      mpc_inex_data;
  int                  mpcc_inex;
} mpc_operand_t;

#define PARAMETER_ARRAY_SIZE 10

/* function name plus parameters in the following order:
   output parameters, input parameters (ending with rounding modes).
   The input parameters include one rounding mode per mpfr/mpc
   output starting from rnd_index.
   For the time being, there may be either one or two rounding modes;
   in the latter case, we assume that there are three outputs:
   the inexact value and two complex numbers.
 */
typedef struct {
  char         *name;  /* name of the function */
  int           nbout; /* number of output parameters */
  int           nbin;  /* number of input parameters, including rounding
                          modes */
  int           nbrnd; /* number of rounding mode parameters */
  mpc_operand_t P[PARAMETER_ARRAY_SIZE]; /* value of parameters */
  mpc_param_t   T[PARAMETER_ARRAY_SIZE]; /* type of parameters */
} mpc_fun_param_t;


void        read_description    (mpc_fun_param_t* param, const char *file);
const char* read_description_findname (mpc_param_t e);

/* file functions */
typedef struct {
  char *pathname;
  FILE *fd;
  unsigned long line_number;
  unsigned long test_line_number;
  int nextchar;
} mpc_datafile_context_t;

void    open_datafile       (mpc_datafile_context_t* datafile_context,
                             const char * data_filename);
void    close_datafile      (mpc_datafile_context_t *dc);

/* data file functions */
void    read_line           (mpc_datafile_context_t* datafile_context,
                             mpc_fun_param_t* params);
void    check_data          (mpc_datafile_context_t* datafile_context,
                             mpc_fun_param_t* params,
                             int index_reused_operand);

/* parameters templated functions */
int     data_check_template (const char* descr_file, const char * data_file);

void    init_parameters     (mpc_fun_param_t *params);
void    clear_parameters    (mpc_fun_param_t *params);
void    print_parameter     (mpc_fun_param_t *params, int index);
int     copy_parameter      (mpc_fun_param_t *params,
                             int index_dest, int index_src);

void    tpl_read_int        (mpc_datafile_context_t* datafile_context,
                             int *nread, const char *name);
void    tpl_read_ui         (mpc_datafile_context_t* datafile_context,
                             unsigned long int *ui);
void    tpl_read_si         (mpc_datafile_context_t* datafile_context,
                             long int *si);
void    tpl_read_mpz        (mpc_datafile_context_t* datafile_context,
                             mpz_t z);
void    tpl_skip_whitespace_comments (mpc_datafile_context_t* datafile_context);
void    tpl_read_ternary    (mpc_datafile_context_t* datafile_context,
                             int* ternary);
void    tpl_read_mpfr       (mpc_datafile_context_t* datafile_context,
                             mpfr_ptr x, int* known_sign);
void    tpl_read_mpfr_rnd   (mpc_datafile_context_t* datafile_context,
                             mpfr_rnd_t* rnd);
void    tpl_read_mpfr_inex  (mpc_datafile_context_t* datafile_context,
                             int *ternary);
void    tpl_read_mpc_inex   (mpc_datafile_context_t* datafile_context,
                             mpc_inex_data_t* ternarypair);
void    tpl_read_mpc        (mpc_datafile_context_t* datafile_context,
                             mpc_data_t* z);
void    tpl_read_mpc_rnd    (mpc_datafile_context_t* datafile_context,
                             mpc_rnd_t* rnd);

int     tpl_same_mpz_value  (mpz_ptr n1, mpz_ptr n2);
int     tpl_same_mpfr_value (mpfr_ptr x1, mpfr_ptr x2, int known_sign);
int     tpl_check_mpfr_data (mpfr_t got, mpfr_data_t expected);
int     tpl_check_mpc_data  (mpc_ptr got, mpc_data_t expected);

void    tpl_copy_int        (int *dest, const int * const src);
void    tpl_copy_ui         (unsigned long int *dest,
                             const unsigned long int * const src);
void    tpl_copy_si         (long int *dest, const long int * const src);
void    tpl_copy_d          (double *dest, const double * const src);
void    tpl_copy_mpz        (mpz_ptr dest, mpz_srcptr src);
void    tpl_copy_mpfr       (mpfr_ptr dest, mpfr_srcptr src);
void    tpl_copy_mpc        (mpc_ptr dest, mpc_srcptr src);

int     double_rounding     (mpc_fun_param_t *params);

/* iterating over rounding modes */
void    first_rnd_mode      (mpc_fun_param_t *params);
int     is_valid_rnd_mode   (mpc_fun_param_t *params);
void    next_rnd_mode       (mpc_fun_param_t *params);

/* parameter precision */
void    set_output_precision    (mpc_fun_param_t *params, mpfr_prec_t prec);
void    set_input_precision     (mpc_fun_param_t *params, mpfr_prec_t prec);
void    set_reference_precision (mpc_fun_param_t *params, mpfr_prec_t prec);

#endif /* __MPC_TESTS_H */
