1 1.1 mrg /* tballs -- test file for complex ball arithmetic. 2 1.1 mrg 3 1.1 mrg Copyright (C) 2018, 2020, 2021, 2022 INRIA 4 1.1 mrg 5 1.1 mrg This file is part of GNU MPC. 6 1.1 mrg 7 1.1 mrg GNU MPC is free software; you can redistribute it and/or modify it under 8 1.1 mrg the terms of the GNU Lesser General Public License as published by the 9 1.1 mrg Free Software Foundation; either version 3 of the License, or (at your 10 1.1 mrg option) any later version. 11 1.1 mrg 12 1.1 mrg GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY 13 1.1 mrg WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 14 1.1 mrg FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for 15 1.1 mrg more details. 16 1.1 mrg 17 1.1 mrg You should have received a copy of the GNU Lesser General Public License 18 1.1 mrg along with this program. If not, see http://www.gnu.org/licenses/ . 19 1.1 mrg */ 20 1.1 mrg 21 1.1 mrg #include "mpc-tests.h" 22 1.1 mrg #include "mpc-impl.h" 23 1.1 mrg /* For the alternative AGM implementation, we need all the power of 24 1.1 mrg this include file. */ 25 1.1 mrg 26 1.1 mrg static int 27 1.1 mrg mpc_mpcb_agm (mpc_ptr rop, mpc_srcptr opa, mpc_srcptr opb, mpc_rnd_t rnd) 28 1.1 mrg /* Alternative implementation of mpc_agm that uses complex balls. */ 29 1.1 mrg { 30 1.1 mrg mpfr_prec_t prec; 31 1.1 mrg mpc_t b0, diff; 32 1.1 mrg mpcb_t a, b, an, bn, anp1, bnp1, res; 33 1.1 mrg mpfr_exp_t exp_an, exp_diff; 34 1.1 mrg mpcr_t rab; 35 1.1 mrg int cmp, equal, re_zero, im_zero, ok, inex; 36 1.1 mrg 37 1.1 mrg if (!mpc_fin_p (opa) || !mpc_fin_p (opb) 38 1.1 mrg || mpc_zero_p (opa) || mpc_zero_p (opb) 39 1.1 mrg || mpc_cmp (opa, opb) == 0 40 1.1 mrg || ( mpfr_sgn (mpc_realref (opa)) == -mpfr_sgn (mpc_realref (opb)) 41 1.1 mrg && mpfr_sgn (mpc_imagref (opa)) == -mpfr_sgn (mpc_imagref (opb)) 42 1.1 mrg && mpfr_cmpabs (mpc_realref (opa), mpc_realref (opb)) == 0 43 1.1 mrg && mpfr_cmpabs (mpc_imagref (opa), mpc_imagref (opb)) == 0) 44 1.1 mrg || ( mpfr_zero_p (mpc_imagref (opa)) 45 1.1 mrg && mpfr_zero_p (mpc_imagref (opb)) 46 1.1 mrg && mpfr_sgn (mpc_realref (opa)) == mpfr_sgn (mpc_realref (opb))) 47 1.1 mrg || ( mpfr_zero_p (mpc_realref (opa)) 48 1.1 mrg && mpfr_zero_p (mpc_realref (opb)) 49 1.1 mrg && mpfr_sgn (mpc_imagref (opa)) == mpfr_sgn (mpc_imagref (opb)))) 50 1.1 mrg /* Special cases that are handled separately by mpc_agm; there is 51 1.1 mrg no need to rewrite them. */ 52 1.1 mrg return mpc_agm (rop, opa, opb, rnd); 53 1.1 mrg 54 1.1 mrg /* Exclude the case of angle 0, also handled separately by mpc_agm. */ 55 1.1 mrg mpc_init2 (b0, 2); 56 1.1 mrg mpc_div (b0, opb, opa, MPC_RNDZZ); 57 1.1 mrg if (mpfr_zero_p (mpc_imagref (b0)) && mpfr_sgn (mpc_realref (b0)) > 0) { 58 1.1 mrg mpc_clear (b0); 59 1.1 mrg return mpc_agm (rop, opa, opb, rnd); 60 1.1 mrg } 61 1.1 mrg mpc_clear (b0); 62 1.1 mrg 63 1.1 mrg cmp = mpc_cmp_abs (opa, opb); 64 1.1 mrg 65 1.1 mrg mpcb_init (a); 66 1.1 mrg mpcb_init (b); 67 1.1 mrg mpcb_init (an); 68 1.1 mrg mpcb_init (bn); 69 1.1 mrg mpcb_init (anp1); 70 1.1 mrg mpcb_init (bnp1); 71 1.1 mrg mpcb_init (res); 72 1.1 mrg prec = MPC_MAX (MPC_MAX (MPC_MAX_PREC (opa), MPC_MAX_PREC (opb)), 73 1.1 mrg MPC_MAX_PREC (rop) + 20); 74 1.1 mrg /* So copying opa and opb will be exact, and there is a small safety 75 1.1 mrg margin for the result. */ 76 1.1 mrg do { 77 1.1 mrg mpcb_set_prec (a, prec); 78 1.1 mrg mpcb_set_prec (b, prec); 79 1.1 mrg mpcb_set_prec (an, prec); 80 1.1 mrg mpcb_set_prec (bn, prec); 81 1.1 mrg mpcb_set_prec (anp1, prec); 82 1.1 mrg mpcb_set_prec (bnp1, prec); 83 1.1 mrg mpcb_set_prec (res, prec); 84 1.1 mrg /* TODO: Think about the mpcb_set variants; mpcb_set_c, for instance, 85 1.1 mrg modifies the precision. It is probably better to add a precision 86 1.1 mrg parameter to mpcb_init and potentially round with mpcb_set_xxx. */ 87 1.1 mrg mpc_set (a->c, opa, MPC_RNDNN); /* exact */ 88 1.1 mrg mpcr_set_zero (a->r); 89 1.1 mrg mpc_set (b->c, opb, MPC_RNDNN); 90 1.1 mrg mpcr_set_zero (b->r); 91 1.1 mrg mpc_set_ui_ui (an->c, 1, 0, MPC_RNDNN); 92 1.1 mrg mpcr_set_zero (an->r); 93 1.1 mrg if (cmp >= 0) 94 1.1 mrg mpcb_div (bn, b, a); 95 1.1 mrg else 96 1.1 mrg mpcb_div (bn, a, b); 97 1.1 mrg 98 1.1 mrg /* Iterate until there is a fixed point or (often one iteration 99 1.1 mrg earlier) the arithmetic and the geometric mean coincide. */ 100 1.1 mrg do { 101 1.1 mrg mpcb_add (anp1, an, bn); 102 1.1 mrg mpcb_div_2ui (anp1, anp1, 1); 103 1.1 mrg mpcb_mul (bnp1, an, bn); 104 1.1 mrg mpcb_sqrt (bnp1, bnp1); 105 1.1 mrg /* Be aware of the branch cut! The current function does 106 1.1 mrg what is needed here. */ 107 1.1 mrg equal = mpc_cmp (an->c, bn->c) == 0 108 1.1 mrg || ( mpc_cmp (an->c, anp1->c) == 0 109 1.1 mrg && mpc_cmp (bn->c, bnp1->c) == 0); 110 1.1 mrg mpcb_set (an, anp1); 111 1.1 mrg mpcb_set (bn, bnp1); 112 1.1 mrg } while (!equal); 113 1.1 mrg 114 1.1 mrg /* Check whether we can conclude, see the error analysis in 115 1.1 mrg algorithms.tex. */ 116 1.1 mrg if (mpcr_inf_p (anp1->r)) 117 1.1 mrg ok = 0; 118 1.1 mrg else { 119 1.1 mrg mpc_init2 (diff, prec); 120 1.1 mrg mpc_sub (diff, an->c, bn->c, MPC_RNDZZ); 121 1.1 mrg /* FIXME: We would need to round away, but this is not yet 122 1.1 mrg implemented. */ 123 1.1 mrg re_zero = mpfr_zero_p (mpc_realref (diff)); 124 1.1 mrg if (!re_zero) 125 1.1 mrg MPFR_ADD_ONE_ULP (mpc_realref (diff)); 126 1.1 mrg im_zero = mpfr_zero_p (mpc_imagref (diff)); 127 1.1 mrg if (!im_zero) 128 1.1 mrg MPFR_ADD_ONE_ULP (mpc_imagref (diff)); 129 1.1 mrg 130 1.1 mrg mpcb_set (res, anp1); 131 1.1 mrg 132 1.1 mrg if (re_zero && im_zero) 133 1.1 mrg mpcr_set_zero (rab); 134 1.1 mrg else { 135 1.1 mrg exp_an = MPC_MIN (mpfr_get_exp (mpc_realref (an->c)), 136 1.1 mrg mpfr_get_exp (mpc_imagref (an->c))) - 1; 137 1.1 mrg if (re_zero) 138 1.1 mrg exp_diff = mpfr_get_exp (mpc_imagref (diff)) + 1; 139 1.1 mrg else if (im_zero) 140 1.1 mrg exp_diff = mpfr_get_exp (mpc_realref (diff)) + 1; 141 1.1 mrg else 142 1.1 mrg exp_diff = MPC_MAX (mpfr_get_exp (mpc_realref (diff)), 143 1.1 mrg mpfr_get_exp (mpc_imagref (diff)) + 1); 144 1.1 mrg mpcr_set_one (rab); 145 1.1 mrg (rab->exp) += (exp_diff - exp_an); 146 1.1 mrg /* TODO: Should be done by an mpcr function. */ 147 1.1 mrg } 148 1.1 mrg mpcr_add (rab, rab, an->r); 149 1.1 mrg (rab->exp)++; 150 1.1 mrg mpcr_add (res->r, rab, bn->r); 151 1.1 mrg /* r = 2 * (rab + an->r) + bn->r */ 152 1.1 mrg if (cmp >= 0) 153 1.1 mrg mpcb_mul (res, res, a); 154 1.1 mrg else 155 1.1 mrg mpcb_mul (res, res, b); 156 1.1 mrg ok = mpcb_can_round (res, MPC_PREC_RE (rop), MPC_PREC_IM (rop), 157 1.1 mrg rnd); 158 1.1 mrg 159 1.1 mrg mpc_clear (diff); 160 1.1 mrg } 161 1.1 mrg 162 1.1 mrg if (!ok) 163 1.1 mrg prec += prec + mpcr_get_exp (res->r); 164 1.1 mrg } while (!ok); 165 1.1 mrg 166 1.1 mrg inex = mpcb_round (rop, res, rnd); 167 1.1 mrg 168 1.1 mrg mpcb_clear (a); 169 1.1 mrg mpcb_clear (b); 170 1.1 mrg mpcb_clear (an); 171 1.1 mrg mpcb_clear (bn); 172 1.1 mrg mpcb_clear (anp1); 173 1.1 mrg mpcb_clear (bnp1); 174 1.1 mrg mpcb_clear (res); 175 1.1 mrg 176 1.1 mrg return inex; 177 1.1 mrg } 178 1.1 mrg 179 1.1 mrg 180 1.1 mrg static int 181 1.1 mrg test_agm (void) 182 1.1 mrg { 183 1.1 mrg mpfr_prec_t prec; 184 1.1 mrg mpc_t a, b, agm1, agm2; 185 1.1 mrg mpc_rnd_t rnd = MPC_RNDDU; 186 1.1 mrg int inex, inexb, ok; 187 1.1 mrg 188 1.1 mrg prec = 1000; 189 1.1 mrg 190 1.1 mrg mpc_init2 (a, prec); 191 1.1 mrg mpc_init2 (b, prec); 192 1.1 mrg mpc_set_si_si (a, 100, 0, MPC_RNDNN); 193 1.1 mrg mpc_set_si_si (b, 0, 100, MPC_RNDNN); 194 1.1 mrg mpc_init2 (agm1, prec); 195 1.1 mrg mpc_init2 (agm2, prec); 196 1.1 mrg 197 1.1 mrg inex = mpc_agm (agm1, a, b, rnd); 198 1.1 mrg inexb = mpc_mpcb_agm (agm2, a, b, rnd); 199 1.1 mrg 200 1.1 mrg ok = (inex == inexb) && (mpc_cmp (agm1, agm2) == 0); 201 1.1 mrg 202 1.1 mrg mpc_clear (a); 203 1.1 mrg mpc_clear (b); 204 1.1 mrg mpc_clear (agm1); 205 1.1 mrg mpc_clear (agm2); 206 1.1 mrg 207 1.1 mrg return !ok; 208 1.1 mrg } 209 1.1 mrg 210 1.1 mrg 211 1.1 mrg int 212 1.1 mrg main (void) 213 1.1 mrg { 214 1.1 mrg return test_agm (); 215 1.1 mrg } 216 1.1 mrg 217