/* tgeneric.c -- File for generic tests. Copyright (C) 2008, 2009, 2010, 2011, 2012 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/ . */ #include "mpc-tests.h" /* Warning: unlike the MPFR macro (defined in mpfr-impl.h), this one returns true when b is singular */ #define MPFR_CAN_ROUND(b,err,prec,rnd) \ (mpfr_zero_p (b) || mpfr_inf_p (b) \ || mpfr_can_round (b, (long)mpfr_get_prec (b) - (err), (rnd), \ GMP_RNDZ, (prec) + ((rnd)==GMP_RNDN))) /* functions with one input, one output */ static void tgeneric_cc (mpc_function *function, mpc_ptr op, mpc_ptr rop, mpc_ptr rop4, mpc_ptr rop4rnd, mpc_rnd_t rnd) { known_signs_t ks = {1, 1}; /* We compute the result with four times the precision and check whether the rounding is correct. Error reports in this part of the algorithm might still be wrong, though, since there are two consecutive roundings (but we try to avoid them). */ function->pointer.CC (rop4, op, rnd); function->pointer.CC (rop, op, rnd); /* can't use the mpfr_can_round function when argument is singular, use a custom macro instead. */ if (MPFR_CAN_ROUND (mpc_realref (rop4), 1, MPC_PREC_RE (rop), MPC_RND_RE (rnd)) && MPFR_CAN_ROUND (mpc_imagref (rop4), 1, MPC_PREC_IM (rop), MPC_RND_IM (rnd))) mpc_set (rop4rnd, rop4, rnd); else /* avoid double rounding error */ return; if (same_mpc_value (rop, rop4rnd, ks)) return; /* rounding failed */ printf ("Rounding in %s might be incorrect for\n", function->name); MPC_OUT (op); printf ("with rounding mode (%s, %s)", mpfr_print_rnd_mode (MPC_RND_RE (rnd)), mpfr_print_rnd_mode (MPC_RND_IM (rnd))); printf ("\n%s gives ", function->name); MPC_OUT (rop); printf ("%s quadruple precision gives ", function->name); MPC_OUT (rop4); printf ("and is rounded to "); MPC_OUT (rop4rnd); exit (1); } static void tgeneric_cc_c (mpc_function *function, mpc_ptr op, mpc_ptr rop1, mpc_ptr rop2, mpc_ptr rop14, mpc_ptr rop24, mpc_ptr rop14rnd, mpc_ptr rop24rnd, mpc_rnd_t rnd1, mpc_rnd_t rnd2) { /* same as the previous function, but for mpc functions computing two results from one argument */ known_signs_t ks = {1, 1}; function->pointer.CC_C (rop14, rop24, op, rnd1, rnd2); function->pointer.CC_C (rop1, rop2, op, rnd1, rnd2); if ( MPFR_CAN_ROUND (mpc_realref (rop14), 1, MPC_PREC_RE (rop1), MPC_RND_RE (rnd1)) && MPFR_CAN_ROUND (mpc_imagref (rop14), 1, MPC_PREC_IM (rop1), MPC_RND_IM (rnd1)) && MPFR_CAN_ROUND (mpc_realref (rop24), 1, MPC_PREC_RE (rop2), MPC_RND_RE (rnd2)) && MPFR_CAN_ROUND (mpc_imagref (rop24), 1, MPC_PREC_IM (rop2), MPC_RND_IM (rnd2))) { mpc_set (rop14rnd, rop14, rnd1); mpc_set (rop24rnd, rop24, rnd2); } else return; if (!same_mpc_value (rop1, rop14rnd, ks)) { /* rounding failed for first result */ printf ("Rounding might be incorrect for the first result of %s at\n", function->name); MPC_OUT (op); printf ("with rounding mode (%s, %s)", mpfr_print_rnd_mode (MPC_RND_RE (rnd1)), mpfr_print_rnd_mode (MPC_RND_IM (rnd1))); printf ("\n%s gives ", function->name); MPC_OUT (rop1); printf ("%s quadruple precision gives ", function->name); MPC_OUT (rop14); printf ("and is rounded to "); MPC_OUT (rop14rnd); exit (1); } else if (!same_mpc_value (rop2, rop24rnd, ks)) { /* rounding failed for second result */ printf ("Rounding might be incorrect for the second result of %s at\n", function->name); MPC_OUT (op); printf ("with rounding mode (%s, %s)", mpfr_print_rnd_mode (MPC_RND_RE (rnd2)), mpfr_print_rnd_mode (MPC_RND_IM (rnd2))); printf ("\n%s gives ", function->name); MPC_OUT (rop2); printf ("%s quadruple precision gives ", function->name); MPC_OUT (rop24); printf ("and is rounded to "); MPC_OUT (rop24rnd); exit (1); } } static void tgeneric_fc (mpc_function *function, mpc_ptr op, mpfr_ptr rop, mpfr_ptr rop4, mpfr_ptr rop4rnd, mpfr_rnd_t rnd) { function->pointer.FC (rop4, op, rnd); function->pointer.FC (rop, op, rnd); if (MPFR_CAN_ROUND (rop4, 1, mpfr_get_prec (rop), rnd)) mpfr_set (rop4rnd, rop4, rnd); else return; if (same_mpfr_value (rop, rop4rnd, 1)) return; printf ("Rounding in %s might be incorrect for\n", function->name); MPC_OUT (op); printf ("with rounding mode %s", mpfr_print_rnd_mode (rnd)); printf ("\n%s gives ", function->name); MPFR_OUT (rop); printf ("%s quadruple precision gives ", function->name); MPFR_OUT (rop4); printf ("and is rounded to "); MPFR_OUT (rop4rnd); exit (1); } static void tgeneric_cfc (mpc_function *function, mpfr_ptr op1, mpc_ptr op2, mpc_ptr rop, mpc_ptr rop4, mpc_ptr rop4rnd, mpc_rnd_t rnd) { known_signs_t ks = {1, 1}; function->pointer.CFC (rop4, op1, op2, rnd); function->pointer.CFC (rop, op1, op2, rnd); if (MPFR_CAN_ROUND (mpc_realref (rop4), 1, MPC_PREC_RE (rop), MPC_RND_RE (rnd)) && MPFR_CAN_ROUND (mpc_imagref (rop4), 1, MPC_PREC_IM (rop), MPC_RND_IM (rnd))) mpc_set (rop4rnd, rop4, rnd); else return; if (same_mpc_value (rop, rop4rnd, ks)) return; printf ("Rounding in %s might be incorrect for\n", function->name); MPFR_OUT (op1); MPC_OUT (op2); printf ("with rounding mode (%s, %s)", mpfr_print_rnd_mode (MPC_RND_RE (rnd)), mpfr_print_rnd_mode (MPC_RND_IM (rnd))); printf ("\n%s gives ", function->name); MPC_OUT (rop); printf ("%s quadruple precision gives ", function->name); MPC_OUT (rop4); printf ("and is rounded to "); MPC_OUT (rop4rnd); exit (1); } static void tgeneric_ccf (mpc_function *function, mpc_ptr op1, mpfr_ptr op2, mpc_ptr rop, mpc_ptr rop4, mpc_ptr rop4rnd, mpc_rnd_t rnd) { known_signs_t ks = {1, 1}; function->pointer.CCF (rop4, op1, op2, rnd); function->pointer.CCF (rop, op1, op2, rnd); if (MPFR_CAN_ROUND (mpc_realref (rop4), 1, MPC_PREC_RE (rop), MPC_RND_RE (rnd)) && MPFR_CAN_ROUND (mpc_imagref (rop4), 1, MPC_PREC_IM (rop), MPC_RND_IM (rnd))) mpc_set (rop4rnd, rop4, rnd); else return; if (same_mpc_value (rop, rop4rnd, ks)) return; printf ("Rounding in %s might be incorrect for\n", function->name); MPC_OUT (op1); MPFR_OUT (op2); printf ("with rounding mode (%s, %s)", mpfr_print_rnd_mode (MPC_RND_RE (rnd)), mpfr_print_rnd_mode (MPC_RND_IM (rnd))); printf ("\n%s gives ", function->name); MPC_OUT (rop); printf ("%s quadruple precision gives ", function->name); MPC_OUT (rop4); printf ("and is rounded to "); MPC_OUT (rop4rnd); exit (1); } /* for functions with one mpc_t output, two mpc_t inputs */ static void tgeneric_c_cc (mpc_function *function, mpc_ptr op1, mpc_ptr op2, mpc_ptr rop, mpc_ptr rop4, mpc_ptr rop4rnd, mpc_rnd_t rnd) { known_signs_t ks = {1, 1}; /* We compute the result with four times the precision and check whether the rounding is correct. Error reports in this part of the algorithm might still be wrong, though, since there are two consecutive roundings (but we try to avoid them). */ function->pointer.C_CC (rop4, op1, op2, rnd); function->pointer.C_CC (rop, op1, op2, rnd); /* can't use mpfr_can_round when argument is singular */ if (MPFR_CAN_ROUND (mpc_realref (rop4), 1, MPC_PREC_RE (rop), MPC_RND_RE (rnd)) && MPFR_CAN_ROUND (mpc_imagref (rop4), 1, MPC_PREC_IM (rop), MPC_RND_IM (rnd))) mpc_set (rop4rnd, rop4, rnd); else /* avoid double rounding error */ return; if (same_mpc_value (rop, rop4rnd, ks)) return; /* rounding failed */ printf ("Rounding in %s might be incorrect for\n", function->name); MPC_OUT (op1); MPC_OUT (op2); printf ("with rounding mode (%s, %s)", mpfr_print_rnd_mode (MPC_RND_RE (rnd)), mpfr_print_rnd_mode (MPC_RND_IM (rnd))); printf ("\n%s gives ", function->name); MPC_OUT (rop); printf ("%s quadruple precision gives ", function->name); MPC_OUT (rop4); printf ("and is rounded to "); MPC_OUT (rop4rnd); exit (1); } static void tgeneric_cccc (mpc_function *function, mpc_ptr op1, mpc_ptr op2, mpc_ptr op3, mpc_ptr rop, mpc_ptr rop4, mpc_ptr rop4rnd, mpc_rnd_t rnd) { known_signs_t ks = {1, 1}; /* We compute the result with four times the precision and check whether the rounding is correct. Error reports in this part of the algorithm might still be wrong, though, since there are two consecutive roundings (but we try to avoid them). */ function->pointer.CCCC (rop4, op1, op2, op3, rnd); function->pointer.CCCC (rop, op1, op2, op3, rnd); /* can't use mpfr_can_round when argument is singular */ if (MPFR_CAN_ROUND (mpc_realref (rop4), 1, MPC_PREC_RE (rop), MPC_RND_RE (rnd)) && MPFR_CAN_ROUND (mpc_imagref (rop4), 1, MPC_PREC_IM (rop), MPC_RND_IM (rnd))) mpc_set (rop4rnd, rop4, rnd); else /* avoid double rounding error */ return; if (same_mpc_value (rop, rop4rnd, ks)) return; /* rounding failed */ printf ("Rounding in %s might be incorrect for\n", function->name); MPC_OUT (op1); MPC_OUT (op2); MPC_OUT (op3); printf ("with rounding mode (%s, %s)", mpfr_print_rnd_mode (MPC_RND_RE (rnd)), mpfr_print_rnd_mode (MPC_RND_IM (rnd))); printf ("\n%s gives ", function->name); MPC_OUT (rop); printf ("%s quadruple precision gives ", function->name); MPC_OUT (rop4); printf ("and is rounded to "); MPC_OUT (rop4rnd); exit (1); } static void tgeneric_ccu (mpc_function *function, mpc_ptr op1, unsigned long int op2, mpc_ptr rop, mpc_ptr rop4, mpc_ptr rop4rnd, mpc_rnd_t rnd) { known_signs_t ks = {1, 1}; function->pointer.CCU (rop4, op1, op2, rnd); function->pointer.CCU (rop, op1, op2, rnd); if (MPFR_CAN_ROUND (mpc_realref (rop4), 1, MPC_PREC_RE (rop), MPC_RND_RE (rnd)) && MPFR_CAN_ROUND (mpc_imagref (rop4), 1, MPC_PREC_IM (rop), MPC_RND_IM (rnd))) mpc_set (rop4rnd, rop4, rnd); else return; if (same_mpc_value (rop, rop4rnd, ks)) return; printf ("Rounding in %s might be incorrect for\n", function->name); MPC_OUT (op1); printf ("op2=%lu\n", op2); printf ("with rounding mode (%s, %s)", mpfr_print_rnd_mode (MPC_RND_RE (rnd)), mpfr_print_rnd_mode (MPC_RND_IM (rnd))); printf ("\n%s gives ", function->name); MPC_OUT (rop); printf ("%s quadruple precision gives ", function->name); MPC_OUT (rop4); printf ("and is rounded to "); MPC_OUT (rop4rnd); exit (1); } static void tgeneric_cuc (mpc_function *function, unsigned long int op1, mpc_ptr op2, mpc_ptr rop, mpc_ptr rop4, mpc_ptr rop4rnd, mpc_rnd_t rnd) { known_signs_t ks = {1, 1}; function->pointer.CUC (rop4, op1, op2, rnd); function->pointer.CUC (rop, op1, op2, rnd); if (MPFR_CAN_ROUND (mpc_realref (rop4), 1, MPC_PREC_RE (rop), MPC_RND_RE (rnd)) && MPFR_CAN_ROUND (mpc_imagref (rop4), 1, MPC_PREC_IM (rop), MPC_RND_IM (rnd))) mpc_set (rop4rnd, rop4, rnd); else return; if (same_mpc_value (rop, rop4rnd, ks)) return; printf ("Rounding in %s might be incorrect for\n", function->name); printf ("op1=%lu\n", op1); MPC_OUT (op2); printf ("with rounding mode (%s, %s)", mpfr_print_rnd_mode (MPC_RND_RE (rnd)), mpfr_print_rnd_mode (MPC_RND_IM (rnd))); printf ("\n%s gives ", function->name); MPC_OUT (rop); printf ("%s quadruple precision gives ", function->name); MPC_OUT (rop4); printf ("and is rounded to "); MPC_OUT (rop4rnd); exit (1); } static void tgeneric_ccs (mpc_function *function, mpc_ptr op1, long int op2, mpc_ptr rop, mpc_ptr rop4, mpc_ptr rop4rnd, mpc_rnd_t rnd) { known_signs_t ks = {1, 1}; function->pointer.CCS (rop4, op1, op2, rnd); function->pointer.CCS (rop, op1, op2, rnd); if (MPFR_CAN_ROUND (mpc_realref (rop4), 1, MPC_PREC_RE (rop), MPC_RND_RE (rnd)) && MPFR_CAN_ROUND (mpc_imagref (rop4), 1, MPC_PREC_IM (rop), MPC_RND_IM (rnd))) mpc_set (rop4rnd, rop4, rnd); else return; if (same_mpc_value (rop, rop4rnd, ks)) return; printf ("Rounding in %s might be incorrect for\n", function->name); MPC_OUT (op1); printf ("op2=%ld\n", op2); printf ("with rounding mode (%s, %s)", mpfr_print_rnd_mode (MPC_RND_RE (rnd)), mpfr_print_rnd_mode (MPC_RND_IM (rnd))); printf ("\n%s gives ", function->name); MPC_OUT (rop); printf ("%s quadruple precision gives ", function->name); MPC_OUT (rop4); printf ("and is rounded to "); MPC_OUT (rop4rnd); exit (1); } static void tgeneric_cci (mpc_function *function, mpc_ptr op1, int op2, mpc_ptr rop, mpc_ptr rop4, mpc_ptr rop4rnd, mpc_rnd_t rnd) { known_signs_t ks = {1, 1}; function->pointer.CCI (rop4, op1, op2, rnd); function->pointer.CCI (rop, op1, op2, rnd); if (MPFR_CAN_ROUND (mpc_realref (rop4), 1, MPC_PREC_RE (rop), MPC_RND_RE (rnd)) && MPFR_CAN_ROUND (mpc_imagref (rop4), 1, MPC_PREC_IM (rop), MPC_RND_IM (rnd))) mpc_set (rop4rnd, rop4, rnd); else return; if (same_mpc_value (rop, rop4rnd, ks)) return; printf ("Rounding in %s might be incorrect for\n", function->name); MPC_OUT (op1); printf ("op2=%d\n", op2); printf ("with rounding mode (%s, %s)", mpfr_print_rnd_mode (MPC_RND_RE (rnd)), mpfr_print_rnd_mode (MPC_RND_IM (rnd))); printf ("\n%s gives ", function->name); MPC_OUT (rop); printf ("%s quadruple precision gives ", function->name); MPC_OUT (rop4); printf ("and is rounded to "); MPC_OUT (rop4rnd); exit (1); } static void tgeneric_cuuc (mpc_function *function, unsigned long int op1, unsigned long int op2, mpc_ptr op3, mpc_ptr rop, mpc_ptr rop4, mpc_ptr rop4rnd, mpc_rnd_t rnd) { known_signs_t ks = {1, 1}; function->pointer.CUUC (rop4, op1, op2, op3, rnd); function->pointer.CUUC (rop, op1, op2, op3, rnd); if (MPFR_CAN_ROUND (mpc_realref (rop4), 1, MPC_PREC_RE (rop), MPC_RND_RE (rnd)) && MPFR_CAN_ROUND (mpc_imagref (rop4), 1, MPC_PREC_IM (rop), MPC_RND_IM (rnd))) mpc_set (rop4rnd, rop4, rnd); else return; if (same_mpc_value (rop, rop4rnd, ks)) return; printf ("Rounding in %s might be incorrect for\n", function->name); printf ("op1=%lu\n", op1); printf ("op2=%lu\n", op2); MPC_OUT (op3); printf ("with rounding mode (%s, %s)", mpfr_print_rnd_mode (MPC_RND_RE (rnd)), mpfr_print_rnd_mode (MPC_RND_IM (rnd))); printf ("\n%s gives ", function->name); MPC_OUT (rop); printf ("%s quadruple precision gives ", function->name); MPC_OUT (rop4); printf ("and is rounded to "); MPC_OUT (rop4rnd); exit (1); } /* Test parameter reuse: the function should not use its output parameter in internal computations. */ static void reuse_cc (mpc_function* function, mpc_srcptr z, mpc_ptr got, mpc_ptr expected) { known_signs_t ks = {1, 1}; mpc_set (got, z, MPC_RNDNN); /* exact */ function->pointer.CC (expected, z, MPC_RNDNN); function->pointer.CC (got, got, MPC_RNDNN); if (!same_mpc_value (got, expected, ks)) { printf ("Reuse error for %s(z, z) for\n", function->name); MPC_OUT (z); MPC_OUT (expected); MPC_OUT (got); exit (1); } } static void reuse_cc_c (mpc_function* function, mpc_srcptr z, mpc_ptr got1, mpc_ptr got2, mpc_ptr exp1, mpc_ptr exp2) { known_signs_t ks = {1, 1}; function->pointer.CC_C (exp1, exp2, z, MPC_RNDNN, MPC_RNDNN); mpc_set (got1, z, MPC_RNDNN); /* exact */ function->pointer.CC_C (got1, got2, got1, MPC_RNDNN, MPC_RNDNN); if ( !same_mpc_value (got1, exp1, ks) || !same_mpc_value (got2, exp2, ks)) { printf ("Reuse error in first result of %s for\n", function->name); MPC_OUT (z); MPC_OUT (exp1); MPC_OUT (got1); MPC_OUT (exp2); MPC_OUT (got2); exit (1); } mpc_set (got2, z, MPC_RNDNN); /* exact */ function->pointer.CC_C (got1, got2, got2, MPC_RNDNN, MPC_RNDNN); if ( !same_mpc_value (got1, exp1, ks) || !same_mpc_value (got2, exp2, ks)) { printf ("Reuse error in second result of %s for\n", function->name); MPC_OUT (z); MPC_OUT (exp1); MPC_OUT (got1); MPC_OUT (exp2); MPC_OUT (got2); exit (1); } } static void reuse_fc (mpc_function* function, mpc_ptr z, mpc_ptr x, mpfr_ptr expected) { mpc_set (x, z, MPC_RNDNN); /* exact */ function->pointer.FC (expected, z, GMP_RNDN); function->pointer.FC (mpc_realref (x), x, GMP_RNDN); if (!same_mpfr_value (mpc_realref (x), expected, 1)) { mpfr_t got; got[0] = mpc_realref(x)[0]; /* display sensible name */ printf ("Reuse error for %s(mpc_realref(z), z) for\n", function->name); MPC_OUT (z); MPFR_OUT (expected); MPFR_OUT (got); exit (1); } mpc_set (x, z, MPC_RNDNN); /* exact */ function->pointer.FC (mpc_imagref (x), x, GMP_RNDN); if (!same_mpfr_value (mpc_imagref (x), expected, 1)) { mpfr_t got; got[0] = mpc_imagref(x)[0]; /* display sensible name */ printf ("Reuse error for %s(mpc_imagref(z), z) for \n", function->name); MPC_OUT (z); MPFR_OUT (expected); MPFR_OUT (got); exit (1); } } static void reuse_cfc (mpc_function* function, mpc_srcptr z, mpfr_srcptr x, mpc_ptr got, mpc_ptr expected) { known_signs_t ks = {1, 1}; mpc_set (got, z, MPC_RNDNN); /* exact */ function->pointer.CFC (expected, x, z, MPC_RNDNN); function->pointer.CFC (got, x, got, MPC_RNDNN); if (!same_mpc_value (got, expected, ks)) { printf ("Reuse error for %s(z, x, z) for\n", function->name); MPFR_OUT (x); MPC_OUT (z); MPC_OUT (expected); MPC_OUT (got); exit (1); } } static void reuse_ccf (mpc_function* function, mpc_srcptr z, mpfr_srcptr x, mpc_ptr got, mpc_ptr expected) { known_signs_t ks = {1, 1}; mpc_set (got, z, MPC_RNDNN); /* exact */ function->pointer.CCF (expected, z, x, MPC_RNDNN); function->pointer.CCF (got, got, x, MPC_RNDNN); if (!same_mpc_value (got, expected, ks)) { printf ("Reuse error for %s(z, z, x, RNDNN) for\n", function->name); MPC_OUT (z); MPFR_OUT (x); MPC_OUT (expected); MPC_OUT (got); exit (1); } } /* for functions with one mpc_t output, two mpc_t inputs */ static void reuse_c_cc (mpc_function* function, mpc_srcptr z, mpc_srcptr x, mpc_ptr got, mpc_ptr expected) { known_signs_t ks = {1, 1}; mpc_set (got, z, MPC_RNDNN); /* exact */ function->pointer.C_CC (expected, z, x, MPC_RNDNN); function->pointer.C_CC (got, got, x, MPC_RNDNN); if (!same_mpc_value (got, expected, ks)) { printf ("Reuse error for %s(z, z, x) for\n", function->name); MPC_OUT (z); MPC_OUT (x); MPC_OUT (expected); MPC_OUT (got); exit (1); } mpc_set (got, x, MPC_RNDNN); /* exact */ function->pointer.C_CC (expected, z, x, MPC_RNDNN); function->pointer.C_CC (got, z, got, MPC_RNDNN); if (!same_mpc_value (got, expected, ks)) { printf ("Reuse error for %s(x, z, x) for\n", function->name); MPC_OUT (z); MPC_OUT (x); MPC_OUT (expected); MPC_OUT (got); exit (1); } mpc_set (got, x, MPC_RNDNN); /* exact */ function->pointer.C_CC (expected, x, x, MPC_RNDNN); function->pointer.C_CC (got, got, got, MPC_RNDNN); if (!same_mpc_value (got, expected, ks)) { printf ("Reuse error for %s(x, x, x) for\n", function->name); MPC_OUT (x); MPC_OUT (expected); MPC_OUT (got); exit (1); } } static void reuse_cccc (mpc_function* function, mpc_srcptr z, mpc_srcptr x, mpc_srcptr y, mpc_ptr got, mpc_ptr expected) { known_signs_t ks = {1, 1}; mpc_set (got, z, MPC_RNDNN); /* exact */ function->pointer.CCCC (expected, z, x, y, MPC_RNDNN); function->pointer.CCCC (got, got, x, y, MPC_RNDNN); if (!same_mpc_value (got, expected, ks)) { printf ("Reuse error for %s(z, z, x, y) for\n", function->name); MPC_OUT (z); MPC_OUT (x); MPC_OUT (y); MPC_OUT (expected); MPC_OUT (got); exit (1); } mpc_set (got, x, MPC_RNDNN); /* exact */ function->pointer.CCCC (expected, z, x, y, MPC_RNDNN); function->pointer.CCCC (got, z, got, y, MPC_RNDNN); if (!same_mpc_value (got, expected, ks)) { printf ("Reuse error for %s(x, z, x, y) for\n", function->name); MPC_OUT (z); MPC_OUT (x); MPC_OUT (y); MPC_OUT (expected); MPC_OUT (got); exit (1); } mpc_set (got, y, MPC_RNDNN); /* exact */ function->pointer.CCCC (expected, z, x, y, MPC_RNDNN); function->pointer.CCCC (got, z, x, got, MPC_RNDNN); if (!same_mpc_value (got, expected, ks)) { printf ("Reuse error for %s(y, z, x, y) for\n", function->name); MPC_OUT (z); MPC_OUT (x); MPC_OUT (y); MPC_OUT (expected); MPC_OUT (got); exit (1); } mpc_set (got, x, MPC_RNDNN); /* exact */ function->pointer.CCCC (expected, x, x, x, MPC_RNDNN); function->pointer.CCCC (got, got, got, got, MPC_RNDNN); if (!same_mpc_value (got, expected, ks)) { printf ("Reuse error for %s(x, x, x, x) for\n", function->name); MPC_OUT (x); MPC_OUT (expected); MPC_OUT (got); exit (1); } } static void reuse_ccu (mpc_function* function, mpc_srcptr z, unsigned long ul, mpc_ptr got, mpc_ptr expected) { known_signs_t ks = {1, 1}; mpc_set (got, z, MPC_RNDNN); /* exact */ function->pointer.CCU (expected, z, ul, MPC_RNDNN); function->pointer.CCU (got, got, ul, MPC_RNDNN); if (!same_mpc_value (got, expected, ks)) { printf ("Reuse error for %s(z, z, n) for\n", function->name); MPC_OUT (z); printf ("n=%lu\n", ul); MPC_OUT (expected); MPC_OUT (got); exit (1); } } static void reuse_cuc (mpc_function* function, unsigned long ul, mpc_srcptr z, mpc_ptr got, mpc_ptr expected) { known_signs_t ks = {1, 1}; mpc_set (got, z, MPC_RNDNN); /* exact */ function->pointer.CUC (expected, ul, z,MPC_RNDNN); function->pointer.CUC (got, ul, got, MPC_RNDNN); if (!same_mpc_value (got, expected, ks)) { printf ("Reuse error for %s(z, n, z) for\n", function->name); printf ("n=%lu\n", ul); MPC_OUT (z); MPC_OUT (expected); MPC_OUT (got); exit (1); } } static void reuse_ccs (mpc_function* function, mpc_srcptr z, long lo, mpc_ptr got, mpc_ptr expected) { known_signs_t ks = {1, 1}; mpc_set (got, z, MPC_RNDNN); /* exact */ function->pointer.CCS (expected, z, lo, MPC_RNDNN); function->pointer.CCS (got, got, lo, MPC_RNDNN); if (!same_mpc_value (got, expected, ks)) { printf ("Reuse error for %s(z, z, n) for\n", function->name); MPC_OUT (z); printf ("n=%ld\n", lo); MPC_OUT (expected); MPC_OUT (got); exit (1); } } static void reuse_cci (mpc_function* function, mpc_srcptr z, int i, mpc_ptr got, mpc_ptr expected) { known_signs_t ks = {1, 1}; mpc_set (got, z, MPC_RNDNN); /* exact */ function->pointer.CCI (expected, z, i, MPC_RNDNN); function->pointer.CCI (got, got, i, MPC_RNDNN); if (!same_mpc_value (got, expected, ks)) { printf ("Reuse error for %s(z, z, n) for\n", function->name); MPC_OUT (z); printf ("n=%d\n", i); MPC_OUT (expected); MPC_OUT (got); exit (1); } } static void reuse_cuuc (mpc_function* function, unsigned long ul1, unsigned long ul2, mpc_srcptr z, mpc_ptr got, mpc_ptr expected) { known_signs_t ks = {1, 1}; mpc_set (got, z, MPC_RNDNN); /* exact */ function->pointer.CUUC (expected, ul1, ul2, z,MPC_RNDNN); function->pointer.CUUC (got, ul1, ul2, got, MPC_RNDNN); if (!same_mpc_value (got, expected, ks)) { printf ("Reuse error for %s(z, m, n, z) for\n", function->name); printf ("m=%lu\n", ul1); printf ("n=%lu\n", ul2); MPC_OUT (z); MPC_OUT (expected); MPC_OUT (got); exit (1); } } /* helper functions for iterating over mpfr rounding modes */ static mpfr_rnd_t first_rnd_mode (void) { return GMP_RNDN; } static mpfr_rnd_t next_rnd_mode (mpfr_rnd_t curr) /* assumes that all rounding modes are non-negative, and returns -1 when curr is the last rounding mode */ { switch (curr) { case GMP_RNDN: return GMP_RNDZ; case GMP_RNDZ: return GMP_RNDU; case GMP_RNDU: return GMP_RNDD; default: /* return invalid guard value in mpfr_rnd_t */ #if MPFR_VERSION_MAJOR < 3 return GMP_RNDNA; #else return MPFR_RNDA; /* valid rounding type, but not (yet) used in mpc */ #endif } } static int is_valid_rnd_mode (mpfr_rnd_t curr) /* returns 1 if curr is a valid rounding mode, and 0otherwise */ { if ( curr == GMP_RNDN || curr == GMP_RNDZ || curr == GMP_RNDU || curr == GMP_RNDD) return 1; else return 0; } /* tgeneric(prec_min, prec_max, step, exp_max) checks rounding with random numbers: - with precision ranging from prec_min to prec_max with an increment of step, - with exponent between -exp_max and exp_max. It also checks parameter reuse (it is assumed here that either two mpc_t variables are equal or they are different, in the sense that the real part of one of them cannot be the imaginary part of the other). */ void tgeneric (mpc_function function, mpfr_prec_t prec_min, mpfr_prec_t prec_max, mpfr_prec_t step, mpfr_exp_t exp_max) { unsigned long ul1 = 0, ul2 = 0; long lo = 0; int i = 0; mpfr_t x1, x2, xxxx; mpc_t z1, z2, z3, z4, z5, zzzz, zzzz2; mpfr_rnd_t rnd_re, rnd_im, rnd2_re, rnd2_im; mpfr_prec_t prec; mpfr_exp_t exp_min; int special, special_cases; mpc_init2 (z1, prec_max); switch (function.type) { case C_CC: mpc_init2 (z2, prec_max); mpc_init2 (z3, prec_max); mpc_init2 (z4, prec_max); mpc_init2 (zzzz, 4*prec_max); special_cases = 8; break; case CCCC: mpc_init2 (z2, prec_max); mpc_init2 (z3, prec_max); mpc_init2 (z4, prec_max); mpc_init2 (z5, prec_max); mpc_init2 (zzzz, 4*prec_max); special_cases = 8; break; case FC: mpfr_init2 (x1, prec_max); mpfr_init2 (x2, prec_max); mpfr_init2 (xxxx, 4*prec_max); mpc_init2 (z2, prec_max); special_cases = 4; break; case CCF: case CFC: mpfr_init2 (x1, prec_max); mpc_init2 (z2, prec_max); mpc_init2 (z3, prec_max); mpc_init2 (zzzz, 4*prec_max); special_cases = 6; break; case CCI: case CCS: case CCU: case CUC: mpc_init2 (z2, prec_max); mpc_init2 (z3, prec_max); mpc_init2 (zzzz, 4*prec_max); special_cases = 5; break; case CUUC: mpc_init2 (z2, prec_max); mpc_init2 (z3, prec_max); mpc_init2 (zzzz, 4*prec_max); special_cases = 6; break; case CC_C: mpc_init2 (z2, prec_max); mpc_init2 (z3, prec_max); mpc_init2 (z4, prec_max); mpc_init2 (z5, prec_max); mpc_init2 (zzzz, 4*prec_max); mpc_init2 (zzzz2, 4*prec_max); special_cases = 4; break; case CC: default: mpc_init2 (z2, prec_max); mpc_init2 (z3, prec_max); mpc_init2 (zzzz, 4*prec_max); special_cases = 4; } exp_min = mpfr_get_emin (); if (exp_max <= 0 || exp_max > mpfr_get_emax ()) exp_max = mpfr_get_emax(); if (-exp_max > exp_min) exp_min = - exp_max; if (step < 1) step = 1; for (prec = prec_min, special = 0; prec <= prec_max || special <= special_cases; prec+=step, special += (prec > prec_max ? 1 : 0)) { /* In the end, test functions in special cases of purely real, purely imaginary or infinite arguments. */ /* probability of one zero part in 256th (25 is almost 10%) */ const unsigned int zero_probability = special != 0 ? 0 : 25; mpc_set_prec (z1, prec); test_default_random (z1, exp_min, exp_max, 128, zero_probability); switch (function.type) { case C_CC: mpc_set_prec (z2, prec); test_default_random (z2, exp_min, exp_max, 128, zero_probability); mpc_set_prec (z3, prec); mpc_set_prec (z4, prec); mpc_set_prec (zzzz, 4*prec); switch (special) { case 1: mpfr_set_ui (mpc_realref (z1), 0, GMP_RNDN); break; case 2: mpfr_set_inf (mpc_realref (z1), +1); break; case 3: mpfr_set_ui (mpc_imagref (z1), 0, GMP_RNDN); break; case 4: mpfr_set_inf (mpc_imagref (z1), -1); break; case 5: mpfr_set_ui (mpc_realref (z2), 0, GMP_RNDN); break; case 6: mpfr_set_inf (mpc_realref (z2), -1); break; case 7: mpfr_set_ui (mpc_imagref (z2), 0, GMP_RNDN); break; case 8: mpfr_set_inf (mpc_imagref (z2), +1); break; } break; case CCCC: mpc_set_prec (z2, prec); test_default_random (z2, exp_min, exp_max, 128, zero_probability); mpc_set_prec (z3, prec); mpc_set_prec (z4, prec); mpc_set_prec (z5, prec); mpc_set_prec (zzzz, 4*prec); switch (special) { case 1: mpfr_set_ui (mpc_realref (z1), 0, GMP_RNDN); break; case 2: mpfr_set_inf (mpc_realref (z1), +1); break; case 3: mpfr_set_ui (mpc_imagref (z1), 0, GMP_RNDN); break; case 4: mpfr_set_inf (mpc_imagref (z1), -1); break; case 5: mpfr_set_ui (mpc_realref (z2), 0, GMP_RNDN); break; case 6: mpfr_set_inf (mpc_realref (z2), -1); break; case 7: mpfr_set_ui (mpc_imagref (z2), 0, GMP_RNDN); break; case 8: mpfr_set_inf (mpc_imagref (z2), +1); break; } break; case FC: mpc_set_prec (z2, prec); mpfr_set_prec (x1, prec); mpfr_set_prec (x2, prec); mpfr_set_prec (xxxx, 4*prec); switch (special) { case 1: mpfr_set_ui (mpc_realref (z1), 0, GMP_RNDN); break; case 2: mpfr_set_inf (mpc_realref (z1), +1); break; case 3: mpfr_set_ui (mpc_imagref (z1), 0, GMP_RNDN); break; case 4: mpfr_set_inf (mpc_imagref (z1), -1); break; } break; case CCU: case CUC: mpc_set_prec (z2, 128); do { test_default_random (z2, 0, 64, 128, zero_probability); } while (!mpfr_fits_ulong_p (mpc_realref (z2), GMP_RNDN)); ul1 = mpfr_get_ui (mpc_realref(z2), GMP_RNDN); mpc_set_prec (z2, prec); mpc_set_prec (z3, prec); mpc_set_prec (zzzz, 4*prec); switch (special) { case 1: mpfr_set_ui (mpc_realref (z1), 0, GMP_RNDN); break; case 2: mpfr_set_inf (mpc_realref (z1), +1); break; case 3: mpfr_set_ui (mpc_imagref (z1), 0, GMP_RNDN); break; case 4: mpfr_set_inf (mpc_imagref (z1), -1); break; case 5: ul1 = 0; break; } break; case CUUC: mpc_set_prec (z2, 128); do { test_default_random (z2, 0, 64, 128, zero_probability); } while (!mpfr_fits_ulong_p (mpc_realref (z2), GMP_RNDN) ||!mpfr_fits_ulong_p (mpc_imagref (z2), GMP_RNDN)); ul1 = mpfr_get_ui (mpc_realref(z2), GMP_RNDN); ul2 = mpfr_get_ui (mpc_imagref(z2), GMP_RNDN); mpc_set_prec (z2, prec); mpc_set_prec (z3, prec); mpc_set_prec (zzzz, 4*prec); switch (special) { case 1: mpfr_set_ui (mpc_realref (z1), 0, GMP_RNDN); break; case 2: mpfr_set_inf (mpc_realref (z1), +1); break; case 3: mpfr_set_ui (mpc_imagref (z1), 0, GMP_RNDN); break; case 4: mpfr_set_inf (mpc_imagref (z1), -1); break; case 5: ul1 = 0; break; case 6: ul2 = 0; break; } break; case CCS: mpc_set_prec (z2, 128); do { test_default_random (z2, 0, 64, 128, zero_probability); } while (!mpfr_fits_slong_p (mpc_realref (z2), GMP_RNDN)); lo = mpfr_get_si (mpc_realref(z2), GMP_RNDN); mpc_set_prec (z2, prec); mpc_set_prec (z3, prec); mpc_set_prec (zzzz, 4*prec); switch (special) { case 1: mpfr_set_ui (mpc_realref (z1), 0, GMP_RNDN); break; case 2: mpfr_set_inf (mpc_realref (z1), +1); break; case 3: mpfr_set_ui (mpc_imagref (z1), 0, GMP_RNDN); break; case 4: mpfr_set_inf (mpc_imagref (z1), -1); break; case 5: lo = 0; break; } break; case CCI: mpc_set_prec (z2, 128); do { test_default_random (z2, 0, 64, 128, zero_probability); } while (!mpfr_fits_slong_p (mpc_realref (z2), GMP_RNDN)); i = (int)mpfr_get_si (mpc_realref(z2), GMP_RNDN); mpc_set_prec (z2, prec); mpc_set_prec (z3, prec); mpc_set_prec (zzzz, 4*prec); switch (special) { case 1: mpfr_set_ui (mpc_realref (z1), 0, GMP_RNDN); break; case 2: mpfr_set_inf (mpc_realref (z1), +1); break; case 3: mpfr_set_ui (mpc_imagref (z1), 0, GMP_RNDN); break; case 4: mpfr_set_inf (mpc_imagref (z1), -1); break; case 5: i = 0; break; } break; case CCF: case CFC: mpfr_set_prec (x1, prec); mpfr_set (x1, mpc_realref (z1), GMP_RNDN); test_default_random (z1, exp_min, exp_max, 128, zero_probability); mpc_set_prec (z2, prec); mpc_set_prec (z3, prec); mpc_set_prec (zzzz, 4*prec); switch (special) { case 1: mpfr_set_ui (mpc_realref (z1), 0, GMP_RNDN); break; case 2: mpfr_set_inf (mpc_realref (z1), +1); break; case 3: mpfr_set_ui (mpc_imagref (z1), 0, GMP_RNDN); break; case 4: mpfr_set_inf (mpc_imagref (z1), -1); break; case 5: mpfr_set_ui (x1, 0, GMP_RNDN); break; case 6: mpfr_set_inf (x1, +1); break; } break; case CC_C: mpc_set_prec (z2, prec); mpc_set_prec (z3, prec); mpc_set_prec (z4, prec); mpc_set_prec (z5, prec); mpc_set_prec (zzzz, 4*prec); mpc_set_prec (zzzz2, 4*prec); switch (special) { case 1: mpfr_set_ui (mpc_realref (z1), 0, GMP_RNDN); break; case 2: mpfr_set_inf (mpc_realref (z1), +1); break; case 3: mpfr_set_ui (mpc_imagref (z1), 0, GMP_RNDN); break; case 4: mpfr_set_inf (mpc_imagref (z1), -1); break; } break; case CC: default: mpc_set_prec (z2, prec); mpc_set_prec (z3, prec); mpc_set_prec (zzzz, 4*prec); switch (special) { case 1: mpfr_set_ui (mpc_realref (z1), 0, GMP_RNDN); break; case 2: mpfr_set_inf (mpc_realref (z1), +1); break; case 3: mpfr_set_ui (mpc_imagref (z1), 0, GMP_RNDN); break; case 4: mpfr_set_inf (mpc_imagref (z1), -1); break; } } for (rnd_re = first_rnd_mode (); is_valid_rnd_mode (rnd_re); rnd_re = next_rnd_mode (rnd_re)) switch (function.type) { case C_CC: for (rnd_im = first_rnd_mode (); is_valid_rnd_mode (rnd_im); rnd_im = next_rnd_mode (rnd_im)) tgeneric_c_cc (&function, z1, z2, z3, zzzz, z4, MPC_RND (rnd_re, rnd_im)); reuse_c_cc (&function, z1, z2, z3, z4); break; case CCCC: for (rnd_im = first_rnd_mode (); is_valid_rnd_mode (rnd_im); rnd_im = next_rnd_mode (rnd_im)) tgeneric_cccc (&function, z1, z2, z3, z4, zzzz, z5, MPC_RND (rnd_re, rnd_im)); reuse_cccc (&function, z1, z2, z3, z4, z5); break; case FC: tgeneric_fc (&function, z1, x1, xxxx, x2, rnd_re); reuse_fc (&function, z1, z2, x1); break; case CC: for (rnd_im = first_rnd_mode (); is_valid_rnd_mode (rnd_im); rnd_im = next_rnd_mode (rnd_im)) tgeneric_cc (&function, z1, z2, zzzz, z3, MPC_RND (rnd_re, rnd_im)); reuse_cc (&function, z1, z2, z3); break; case CC_C: for (rnd_im = first_rnd_mode (); is_valid_rnd_mode (rnd_im); rnd_im = next_rnd_mode (rnd_im)) for (rnd2_re = first_rnd_mode (); is_valid_rnd_mode (rnd2_re); rnd2_re = next_rnd_mode (rnd2_re)) for (rnd2_im = first_rnd_mode (); is_valid_rnd_mode (rnd2_im); rnd2_im = next_rnd_mode (rnd2_im)) tgeneric_cc_c (&function, z1, z2, z3, zzzz, zzzz2, z4, z5, MPC_RND (rnd_re, rnd_im), MPC_RND (rnd2_re, rnd2_im)); reuse_cc_c (&function, z1, z2, z3, z4, z5); break; case CFC: for (rnd_im = first_rnd_mode (); is_valid_rnd_mode (rnd_im); rnd_im = next_rnd_mode (rnd_im)) tgeneric_cfc (&function, x1, z1, z2, zzzz, z3, MPC_RND (rnd_re, rnd_im)); reuse_cfc (&function, z1, x1, z2, z3); break; case CCF: for (rnd_im = first_rnd_mode (); is_valid_rnd_mode (rnd_im); rnd_im = next_rnd_mode (rnd_im)) tgeneric_ccf (&function, z1, x1, z2, zzzz, z3, MPC_RND (rnd_re, rnd_im)); reuse_ccf (&function, z1, x1, z2, z3); break; case CCU: for (rnd_im = first_rnd_mode (); is_valid_rnd_mode (rnd_im); rnd_im = next_rnd_mode (rnd_im)) tgeneric_ccu (&function, z1, ul1, z2, zzzz, z3, MPC_RND (rnd_re, rnd_im)); reuse_ccu (&function, z1, ul1, z2, z3); break; case CUC: for (rnd_im = first_rnd_mode (); is_valid_rnd_mode (rnd_im); rnd_im = next_rnd_mode (rnd_im)) tgeneric_cuc (&function, ul1, z1, z2, zzzz, z3, MPC_RND (rnd_re, rnd_im)); reuse_cuc (&function, ul1, z1, z2, z3); break; case CCS: for (rnd_im = first_rnd_mode (); is_valid_rnd_mode (rnd_im); rnd_im = next_rnd_mode (rnd_im)) tgeneric_ccs (&function, z1, lo, z2, zzzz, z3, MPC_RND (rnd_re, rnd_im)); reuse_ccs (&function, z1, lo, z2, z3); break; case CCI: for (rnd_im = first_rnd_mode (); is_valid_rnd_mode (rnd_im); rnd_im = next_rnd_mode (rnd_im)) tgeneric_cci (&function, z1, i, z2, zzzz, z3, MPC_RND (rnd_re, rnd_im)); reuse_cci (&function, z1, i, z2, z3); break; case CUUC: for (rnd_im = first_rnd_mode (); is_valid_rnd_mode (rnd_im); rnd_im = next_rnd_mode (rnd_im)) tgeneric_cuuc (&function, ul1, ul2, z1, z2, zzzz, z3, MPC_RND (rnd_re, rnd_im)); reuse_cuuc (&function, ul1, ul2, z1, z2, z3); break; default: printf ("tgeneric not yet implemented for this kind of" "function\n"); exit (1); } } mpc_clear (z1); switch (function.type) { case C_CC: mpc_clear (z2); mpc_clear (z3); mpc_clear (z4); mpc_clear (zzzz); break; case CCCC: mpc_clear (z2); mpc_clear (z3); mpc_clear (z4); mpc_clear (z5); mpc_clear (zzzz); break; case FC: mpc_clear (z2); mpfr_clear (x1); mpfr_clear (x2); mpfr_clear (xxxx); break; case CCF: case CFC: mpfr_clear (x1); mpc_clear (z2); mpc_clear (z3); mpc_clear (zzzz); break; case CC_C: mpc_clear (z2); mpc_clear (z3); mpc_clear (z4); mpc_clear (z5); mpc_clear (zzzz); mpc_clear (zzzz2); break; case CUUC: case CCI: case CCS: case CCU: case CUC: case CC: default: mpc_clear (z2); mpc_clear (z3); mpc_clear (zzzz); } }