xref: /dflybsd-src/contrib/mpc/src/atan.c (revision 0198f4322d324ad3433db9796d3380fa25061927)
1d30dc8cbSJohn Marino /* mpc_atan -- arctangent of a complex number.
2d30dc8cbSJohn Marino 
3*9b7aff4bSJohn Marino Copyright (C) 2009, 2010, 2011, 2012, 2013 INRIA
4d30dc8cbSJohn Marino 
5d30dc8cbSJohn Marino This file is part of GNU MPC.
6d30dc8cbSJohn Marino 
7d30dc8cbSJohn Marino GNU MPC is free software; you can redistribute it and/or modify it under
8d30dc8cbSJohn Marino the terms of the GNU Lesser General Public License as published by the
9d30dc8cbSJohn Marino Free Software Foundation; either version 3 of the License, or (at your
10d30dc8cbSJohn Marino option) any later version.
11d30dc8cbSJohn Marino 
12d30dc8cbSJohn Marino GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY
13d30dc8cbSJohn Marino WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14d30dc8cbSJohn Marino FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
15d30dc8cbSJohn Marino more details.
16d30dc8cbSJohn Marino 
17d30dc8cbSJohn Marino You should have received a copy of the GNU Lesser General Public License
18d30dc8cbSJohn Marino along with this program. If not, see http://www.gnu.org/licenses/ .
19d30dc8cbSJohn Marino */
20d30dc8cbSJohn Marino 
21d30dc8cbSJohn Marino #include <stdio.h>
22d30dc8cbSJohn Marino #include "mpc-impl.h"
23d30dc8cbSJohn Marino 
24d30dc8cbSJohn Marino /* set rop to
25d30dc8cbSJohn Marino    -pi/2 if s < 0
26d30dc8cbSJohn Marino    +pi/2 else
27d30dc8cbSJohn Marino    rounded in the direction rnd
28d30dc8cbSJohn Marino */
29d30dc8cbSJohn Marino int
set_pi_over_2(mpfr_ptr rop,int s,mpfr_rnd_t rnd)30d30dc8cbSJohn Marino set_pi_over_2 (mpfr_ptr rop, int s, mpfr_rnd_t rnd)
31d30dc8cbSJohn Marino {
32d30dc8cbSJohn Marino   int inex;
33d30dc8cbSJohn Marino 
34d30dc8cbSJohn Marino   inex = mpfr_const_pi (rop, s < 0 ? INV_RND (rnd) : rnd);
35d30dc8cbSJohn Marino   mpfr_div_2ui (rop, rop, 1, GMP_RNDN);
36d30dc8cbSJohn Marino   if (s < 0)
37d30dc8cbSJohn Marino     {
38d30dc8cbSJohn Marino       inex = -inex;
39d30dc8cbSJohn Marino       mpfr_neg (rop, rop, GMP_RNDN);
40d30dc8cbSJohn Marino     }
41d30dc8cbSJohn Marino 
42d30dc8cbSJohn Marino   return inex;
43d30dc8cbSJohn Marino }
44d30dc8cbSJohn Marino 
45d30dc8cbSJohn Marino int
mpc_atan(mpc_ptr rop,mpc_srcptr op,mpc_rnd_t rnd)46d30dc8cbSJohn Marino mpc_atan (mpc_ptr rop, mpc_srcptr op, mpc_rnd_t rnd)
47d30dc8cbSJohn Marino {
48d30dc8cbSJohn Marino   int s_re;
49d30dc8cbSJohn Marino   int s_im;
50d30dc8cbSJohn Marino   int inex_re;
51d30dc8cbSJohn Marino   int inex_im;
52d30dc8cbSJohn Marino   int inex;
53d30dc8cbSJohn Marino 
54d30dc8cbSJohn Marino   inex_re = 0;
55d30dc8cbSJohn Marino   inex_im = 0;
56d30dc8cbSJohn Marino   s_re = mpfr_signbit (mpc_realref (op));
57d30dc8cbSJohn Marino   s_im = mpfr_signbit (mpc_imagref (op));
58d30dc8cbSJohn Marino 
59d30dc8cbSJohn Marino   /* special values */
60d30dc8cbSJohn Marino   if (mpfr_nan_p (mpc_realref (op)) || mpfr_nan_p (mpc_imagref (op)))
61d30dc8cbSJohn Marino     {
62d30dc8cbSJohn Marino       if (mpfr_nan_p (mpc_realref (op)))
63d30dc8cbSJohn Marino         {
64d30dc8cbSJohn Marino           mpfr_set_nan (mpc_realref (rop));
65d30dc8cbSJohn Marino           if (mpfr_zero_p (mpc_imagref (op)) || mpfr_inf_p (mpc_imagref (op)))
66d30dc8cbSJohn Marino             {
67d30dc8cbSJohn Marino               mpfr_set_ui (mpc_imagref (rop), 0, GMP_RNDN);
68d30dc8cbSJohn Marino               if (s_im)
69d30dc8cbSJohn Marino                 mpc_conj (rop, rop, MPC_RNDNN);
70d30dc8cbSJohn Marino             }
71d30dc8cbSJohn Marino           else
72d30dc8cbSJohn Marino             mpfr_set_nan (mpc_imagref (rop));
73d30dc8cbSJohn Marino         }
74d30dc8cbSJohn Marino       else
75d30dc8cbSJohn Marino         {
76d30dc8cbSJohn Marino           if (mpfr_inf_p (mpc_realref (op)))
77d30dc8cbSJohn Marino             {
78d30dc8cbSJohn Marino               inex_re = set_pi_over_2 (mpc_realref (rop), -s_re, MPC_RND_RE (rnd));
79d30dc8cbSJohn Marino               mpfr_set_ui (mpc_imagref (rop), 0, GMP_RNDN);
80d30dc8cbSJohn Marino             }
81d30dc8cbSJohn Marino           else
82d30dc8cbSJohn Marino             {
83d30dc8cbSJohn Marino               mpfr_set_nan (mpc_realref (rop));
84d30dc8cbSJohn Marino               mpfr_set_nan (mpc_imagref (rop));
85d30dc8cbSJohn Marino             }
86d30dc8cbSJohn Marino         }
87d30dc8cbSJohn Marino       return MPC_INEX (inex_re, 0);
88d30dc8cbSJohn Marino     }
89d30dc8cbSJohn Marino 
90d30dc8cbSJohn Marino   if (mpfr_inf_p (mpc_realref (op)) || mpfr_inf_p (mpc_imagref (op)))
91d30dc8cbSJohn Marino     {
92d30dc8cbSJohn Marino       inex_re = set_pi_over_2 (mpc_realref (rop), -s_re, MPC_RND_RE (rnd));
93d30dc8cbSJohn Marino 
94d30dc8cbSJohn Marino       mpfr_set_ui (mpc_imagref (rop), 0, GMP_RNDN);
95d30dc8cbSJohn Marino       if (s_im)
96d30dc8cbSJohn Marino         mpc_conj (rop, rop, GMP_RNDN);
97d30dc8cbSJohn Marino 
98d30dc8cbSJohn Marino       return MPC_INEX (inex_re, 0);
99d30dc8cbSJohn Marino     }
100d30dc8cbSJohn Marino 
101d30dc8cbSJohn Marino   /* pure real argument */
102d30dc8cbSJohn Marino   if (mpfr_zero_p (mpc_imagref (op)))
103d30dc8cbSJohn Marino     {
104d30dc8cbSJohn Marino       inex_re = mpfr_atan (mpc_realref (rop), mpc_realref (op), MPC_RND_RE (rnd));
105d30dc8cbSJohn Marino 
106d30dc8cbSJohn Marino       mpfr_set_ui (mpc_imagref (rop), 0, GMP_RNDN);
107d30dc8cbSJohn Marino       if (s_im)
108d30dc8cbSJohn Marino         mpc_conj (rop, rop, GMP_RNDN);
109d30dc8cbSJohn Marino 
110d30dc8cbSJohn Marino       return MPC_INEX (inex_re, 0);
111d30dc8cbSJohn Marino     }
112d30dc8cbSJohn Marino 
113d30dc8cbSJohn Marino   /* pure imaginary argument */
114d30dc8cbSJohn Marino   if (mpfr_zero_p (mpc_realref (op)))
115d30dc8cbSJohn Marino     {
116d30dc8cbSJohn Marino       int cmp_1;
117d30dc8cbSJohn Marino 
118d30dc8cbSJohn Marino       if (s_im)
119d30dc8cbSJohn Marino         cmp_1 = -mpfr_cmp_si (mpc_imagref (op), -1);
120d30dc8cbSJohn Marino       else
121d30dc8cbSJohn Marino         cmp_1 = mpfr_cmp_ui (mpc_imagref (op), +1);
122d30dc8cbSJohn Marino 
123d30dc8cbSJohn Marino       if (cmp_1 < 0)
124d30dc8cbSJohn Marino         {
125d30dc8cbSJohn Marino           /* atan(+0+iy) = +0 +i*atanh(y), if |y| < 1
126d30dc8cbSJohn Marino              atan(-0+iy) = -0 +i*atanh(y), if |y| < 1 */
127d30dc8cbSJohn Marino 
128d30dc8cbSJohn Marino           mpfr_set_ui (mpc_realref (rop), 0, GMP_RNDN);
129d30dc8cbSJohn Marino           if (s_re)
130d30dc8cbSJohn Marino             mpfr_neg (mpc_realref (rop), mpc_realref (rop), GMP_RNDN);
131d30dc8cbSJohn Marino 
132d30dc8cbSJohn Marino           inex_im = mpfr_atanh (mpc_imagref (rop), mpc_imagref (op), MPC_RND_IM (rnd));
133d30dc8cbSJohn Marino         }
134d30dc8cbSJohn Marino       else if (cmp_1 == 0)
135d30dc8cbSJohn Marino         {
136*9b7aff4bSJohn Marino           /* atan(+/-0 +i) = +/-0 +i*inf
137*9b7aff4bSJohn Marino              atan(+/-0 -i) = +/-0 -i*inf */
138*9b7aff4bSJohn Marino           mpfr_set_zero (mpc_realref (rop), s_re ? -1 : +1);
139d30dc8cbSJohn Marino           mpfr_set_inf  (mpc_imagref (rop), s_im ? -1 : +1);
140d30dc8cbSJohn Marino         }
141d30dc8cbSJohn Marino       else
142d30dc8cbSJohn Marino         {
143d30dc8cbSJohn Marino           /* atan(+0+iy) = +pi/2 +i*atanh(1/y), if |y| > 1
144d30dc8cbSJohn Marino              atan(-0+iy) = -pi/2 +i*atanh(1/y), if |y| > 1 */
145d30dc8cbSJohn Marino           mpfr_rnd_t rnd_im, rnd_away;
146d30dc8cbSJohn Marino           mpfr_t y;
147d30dc8cbSJohn Marino           mpfr_prec_t p, p_im;
148d30dc8cbSJohn Marino           int ok;
149d30dc8cbSJohn Marino 
150d30dc8cbSJohn Marino           rnd_im = MPC_RND_IM (rnd);
151d30dc8cbSJohn Marino           mpfr_init (y);
152d30dc8cbSJohn Marino           p_im = mpfr_get_prec (mpc_imagref (rop));
153d30dc8cbSJohn Marino           p = p_im;
154d30dc8cbSJohn Marino 
155d30dc8cbSJohn Marino           /* a = o(1/y)      with error(a) < 1 ulp(a)
156d30dc8cbSJohn Marino              b = o(atanh(a)) with error(b) < (1+2^{1+Exp(a)-Exp(b)}) ulp(b)
157d30dc8cbSJohn Marino 
158d30dc8cbSJohn Marino              As |atanh (1/y)| > |1/y| we have Exp(a)-Exp(b) <=0 so, at most,
159d30dc8cbSJohn Marino              2 bits of precision are lost.
160d30dc8cbSJohn Marino 
161d30dc8cbSJohn Marino              We round atanh(1/y) away from 0.
162d30dc8cbSJohn Marino           */
163d30dc8cbSJohn Marino           do
164d30dc8cbSJohn Marino             {
165d30dc8cbSJohn Marino               p += mpc_ceil_log2 (p) + 2;
166d30dc8cbSJohn Marino               mpfr_set_prec (y, p);
167d30dc8cbSJohn Marino               rnd_away = s_im == 0 ? GMP_RNDU : GMP_RNDD;
168d30dc8cbSJohn Marino               inex_im = mpfr_ui_div (y, 1, mpc_imagref (op), rnd_away);
169d30dc8cbSJohn Marino               /* FIXME: should we consider the case with unreasonably huge
170d30dc8cbSJohn Marino                  precision prec(y)>3*exp_min, where atanh(1/Im(op)) could be
171d30dc8cbSJohn Marino                  representable while 1/Im(op) underflows ?
172d30dc8cbSJohn Marino                  This corresponds to |y| = 0.5*2^emin, in which case the
173d30dc8cbSJohn Marino                  result may be wrong. */
174d30dc8cbSJohn Marino 
175d30dc8cbSJohn Marino               /* atanh cannot underflow: |atanh(x)| > |x| for |x| < 1 */
176d30dc8cbSJohn Marino               inex_im |= mpfr_atanh (y, y, rnd_away);
177d30dc8cbSJohn Marino 
178d30dc8cbSJohn Marino               ok = inex_im == 0
179d30dc8cbSJohn Marino                 || mpfr_can_round (y, p - 2, rnd_away, GMP_RNDZ,
180d30dc8cbSJohn Marino                                    p_im + (rnd_im == GMP_RNDN));
181d30dc8cbSJohn Marino             } while (ok == 0);
182d30dc8cbSJohn Marino 
183d30dc8cbSJohn Marino           inex_re = set_pi_over_2 (mpc_realref (rop), -s_re, MPC_RND_RE (rnd));
184d30dc8cbSJohn Marino           inex_im = mpfr_set (mpc_imagref (rop), y, rnd_im);
185d30dc8cbSJohn Marino           mpfr_clear (y);
186d30dc8cbSJohn Marino         }
187d30dc8cbSJohn Marino       return MPC_INEX (inex_re, inex_im);
188d30dc8cbSJohn Marino     }
189d30dc8cbSJohn Marino 
190d30dc8cbSJohn Marino   /* regular number argument */
191d30dc8cbSJohn Marino   {
192d30dc8cbSJohn Marino     mpfr_t a, b, x, y;
193d30dc8cbSJohn Marino     mpfr_prec_t prec, p;
194d30dc8cbSJohn Marino     mpfr_exp_t err, expo;
195d30dc8cbSJohn Marino     int ok = 0;
196d30dc8cbSJohn Marino     mpfr_t minus_op_re;
197d30dc8cbSJohn Marino     mpfr_exp_t op_re_exp, op_im_exp;
198d30dc8cbSJohn Marino     mpfr_rnd_t rnd1, rnd2;
199d30dc8cbSJohn Marino 
200d30dc8cbSJohn Marino     mpfr_inits2 (MPFR_PREC_MIN, a, b, x, y, (mpfr_ptr) 0);
201d30dc8cbSJohn Marino 
202d30dc8cbSJohn Marino     /* real part: Re(arctan(x+i*y)) = [arctan2(x,1-y) - arctan2(-x,1+y)]/2 */
203d30dc8cbSJohn Marino     minus_op_re[0] = mpc_realref (op)[0];
204d30dc8cbSJohn Marino     MPFR_CHANGE_SIGN (minus_op_re);
205d30dc8cbSJohn Marino     op_re_exp = mpfr_get_exp (mpc_realref (op));
206d30dc8cbSJohn Marino     op_im_exp = mpfr_get_exp (mpc_imagref (op));
207d30dc8cbSJohn Marino 
208d30dc8cbSJohn Marino     prec = mpfr_get_prec (mpc_realref (rop)); /* result precision */
209d30dc8cbSJohn Marino 
210d30dc8cbSJohn Marino     /* a = o(1-y)         error(a) < 1 ulp(a)
211d30dc8cbSJohn Marino        b = o(atan2(x,a))  error(b) < [1+2^{3+Exp(x)-Exp(a)-Exp(b)}] ulp(b)
212d30dc8cbSJohn Marino                                      = kb ulp(b)
213d30dc8cbSJohn Marino        c = o(1+y)         error(c) < 1 ulp(c)
214d30dc8cbSJohn Marino        d = o(atan2(-x,c)) error(d) < [1+2^{3+Exp(x)-Exp(c)-Exp(d)}] ulp(d)
215d30dc8cbSJohn Marino                                      = kd ulp(d)
216d30dc8cbSJohn Marino        e = o(b - d)       error(e) < [1 + kb*2^{Exp(b}-Exp(e)}
217d30dc8cbSJohn Marino                                         + kd*2^{Exp(d)-Exp(e)}] ulp(e)
218d30dc8cbSJohn Marino                           error(e) < [1 + 2^{4+Exp(x)-Exp(a)-Exp(e)}
219d30dc8cbSJohn Marino                                         + 2^{4+Exp(x)-Exp(c)-Exp(e)}] ulp(e)
220d30dc8cbSJohn Marino                           because |atan(u)| < |u|
221d30dc8cbSJohn Marino                                    < [1 + 2^{5+Exp(x)-min(Exp(a),Exp(c))
222d30dc8cbSJohn Marino                                              -Exp(e)}] ulp(e)
223d30dc8cbSJohn Marino        f = e/2            exact
224d30dc8cbSJohn Marino     */
225d30dc8cbSJohn Marino 
226d30dc8cbSJohn Marino     /* p: working precision */
227d30dc8cbSJohn Marino     p = (op_im_exp > 0 || prec > SAFE_ABS (mpfr_prec_t, op_im_exp)) ? prec
228d30dc8cbSJohn Marino       : (prec - op_im_exp);
229d30dc8cbSJohn Marino     rnd1 = mpfr_sgn (mpc_realref (op)) > 0 ? GMP_RNDD : GMP_RNDU;
230d30dc8cbSJohn Marino     rnd2 = mpfr_sgn (mpc_realref (op)) < 0 ? GMP_RNDU : GMP_RNDD;
231d30dc8cbSJohn Marino 
232d30dc8cbSJohn Marino     do
233d30dc8cbSJohn Marino       {
234d30dc8cbSJohn Marino         p += mpc_ceil_log2 (p) + 2;
235d30dc8cbSJohn Marino         mpfr_set_prec (a, p);
236d30dc8cbSJohn Marino         mpfr_set_prec (b, p);
237d30dc8cbSJohn Marino         mpfr_set_prec (x, p);
238d30dc8cbSJohn Marino 
239d30dc8cbSJohn Marino         /* x = upper bound for atan (x/(1-y)). Since atan is increasing, we
240d30dc8cbSJohn Marino            need an upper bound on x/(1-y), i.e., a lower bound on 1-y for
241d30dc8cbSJohn Marino            x positive, and an upper bound on 1-y for x negative */
242d30dc8cbSJohn Marino         mpfr_ui_sub (a, 1, mpc_imagref (op), rnd1);
243d30dc8cbSJohn Marino         if (mpfr_sgn (a) == 0) /* y is near 1, thus 1+y is near 2, and
244d30dc8cbSJohn Marino                                   expo will be 1 or 2 below */
245d30dc8cbSJohn Marino           {
246d30dc8cbSJohn Marino             MPC_ASSERT (mpfr_cmp_ui (mpc_imagref(op), 1) == 0);
247d30dc8cbSJohn Marino                /* check for intermediate underflow */
248d30dc8cbSJohn Marino             err = 2; /* ensures err will be expo below */
249d30dc8cbSJohn Marino           }
250d30dc8cbSJohn Marino         else
251d30dc8cbSJohn Marino           err = mpfr_get_exp (a); /* err = Exp(a) with the notations above */
252d30dc8cbSJohn Marino         mpfr_atan2 (x, mpc_realref (op), a, GMP_RNDU);
253d30dc8cbSJohn Marino 
254d30dc8cbSJohn Marino         /* b = lower bound for atan (-x/(1+y)): for x negative, we need a
255d30dc8cbSJohn Marino            lower bound on -x/(1+y), i.e., an upper bound on 1+y */
256d30dc8cbSJohn Marino         mpfr_add_ui (a, mpc_imagref(op), 1, rnd2);
257d30dc8cbSJohn Marino         /* if a is exactly zero, i.e., Im(op) = -1, then the error on a is 0,
258d30dc8cbSJohn Marino            and we can simply ignore the terms involving Exp(a) in the error */
259d30dc8cbSJohn Marino         if (mpfr_sgn (a) == 0)
260d30dc8cbSJohn Marino           {
261d30dc8cbSJohn Marino             MPC_ASSERT (mpfr_cmp_si (mpc_imagref(op), -1) == 0);
262d30dc8cbSJohn Marino                /* check for intermediate underflow */
263d30dc8cbSJohn Marino             expo = err; /* will leave err unchanged below */
264d30dc8cbSJohn Marino           }
265d30dc8cbSJohn Marino         else
266d30dc8cbSJohn Marino           expo = mpfr_get_exp (a); /* expo = Exp(c) with the notations above */
267d30dc8cbSJohn Marino         mpfr_atan2 (b, minus_op_re, a, GMP_RNDD);
268d30dc8cbSJohn Marino 
269d30dc8cbSJohn Marino         err = err < expo ? err : expo; /* err = min(Exp(a),Exp(c)) */
270d30dc8cbSJohn Marino         mpfr_sub (x, x, b, GMP_RNDU);
271d30dc8cbSJohn Marino 
272d30dc8cbSJohn Marino         err = 5 + op_re_exp - err - mpfr_get_exp (x);
273d30dc8cbSJohn Marino         /* error is bounded by [1 + 2^err] ulp(e) */
274d30dc8cbSJohn Marino         err = err < 0 ? 1 : err + 1;
275d30dc8cbSJohn Marino 
276d30dc8cbSJohn Marino         mpfr_div_2ui (x, x, 1, GMP_RNDU);
277d30dc8cbSJohn Marino 
278d30dc8cbSJohn Marino         /* Note: using RND2=RNDD guarantees that if x is exactly representable
279d30dc8cbSJohn Marino            on prec + ... bits, mpfr_can_round will return 0 */
280d30dc8cbSJohn Marino         ok = mpfr_can_round (x, p - err, GMP_RNDU, GMP_RNDD,
281d30dc8cbSJohn Marino                              prec + (MPC_RND_RE (rnd) == GMP_RNDN));
282d30dc8cbSJohn Marino       } while (ok == 0);
283d30dc8cbSJohn Marino 
284d30dc8cbSJohn Marino     /* Imaginary part
285d30dc8cbSJohn Marino        Im(atan(x+I*y)) = 1/4 * [log(x^2+(1+y)^2) - log (x^2 +(1-y)^2)] */
286d30dc8cbSJohn Marino     prec = mpfr_get_prec (mpc_imagref (rop)); /* result precision */
287d30dc8cbSJohn Marino 
288d30dc8cbSJohn Marino     /* a = o(1+y)    error(a) < 1 ulp(a)
289d30dc8cbSJohn Marino        b = o(a^2)    error(b) < 5 ulp(b)
290d30dc8cbSJohn Marino        c = o(x^2)    error(c) < 1 ulp(c)
291d30dc8cbSJohn Marino        d = o(b+c)    error(d) < 7 ulp(d)
292d30dc8cbSJohn Marino        e = o(log(d)) error(e) < [1 + 7*2^{2-Exp(e)}] ulp(e) = ke ulp(e)
293d30dc8cbSJohn Marino        f = o(1-y)    error(f) < 1 ulp(f)
294d30dc8cbSJohn Marino        g = o(f^2)    error(g) < 5 ulp(g)
295d30dc8cbSJohn Marino        h = o(c+f)    error(h) < 7 ulp(h)
296d30dc8cbSJohn Marino        i = o(log(h)) error(i) < [1 + 7*2^{2-Exp(i)}] ulp(i) = ki ulp(i)
297d30dc8cbSJohn Marino        j = o(e-i)    error(j) < [1 + ke*2^{Exp(e)-Exp(j)}
298d30dc8cbSJohn Marino                                    + ki*2^{Exp(i)-Exp(j)}] ulp(j)
299d30dc8cbSJohn Marino                      error(j) < [1 + 2^{Exp(e)-Exp(j)} + 2^{Exp(i)-Exp(j)}
300d30dc8cbSJohn Marino                                    + 7*2^{3-Exp(j)}] ulp(j)
301d30dc8cbSJohn Marino                               < [1 + 2^{max(Exp(e),Exp(i))-Exp(j)+1}
302d30dc8cbSJohn Marino                                    + 7*2^{3-Exp(j)}] ulp(j)
303d30dc8cbSJohn Marino        k = j/4       exact
304d30dc8cbSJohn Marino     */
305d30dc8cbSJohn Marino     err = 2;
306d30dc8cbSJohn Marino     p = prec; /* working precision */
307d30dc8cbSJohn Marino 
308d30dc8cbSJohn Marino     do
309d30dc8cbSJohn Marino       {
310d30dc8cbSJohn Marino         p += mpc_ceil_log2 (p) + err;
311d30dc8cbSJohn Marino         mpfr_set_prec (a, p);
312d30dc8cbSJohn Marino         mpfr_set_prec (b, p);
313d30dc8cbSJohn Marino         mpfr_set_prec (y, p);
314d30dc8cbSJohn Marino 
315d30dc8cbSJohn Marino         /* a = upper bound for log(x^2 + (1+y)^2) */
316d30dc8cbSJohn Marino         ROUND_AWAY (mpfr_add_ui (a, mpc_imagref (op), 1, MPFR_RNDA), a);
317d30dc8cbSJohn Marino         mpfr_sqr (a, a, GMP_RNDU);
318d30dc8cbSJohn Marino         mpfr_sqr (y, mpc_realref (op), GMP_RNDU);
319d30dc8cbSJohn Marino         mpfr_add (a, a, y, GMP_RNDU);
320d30dc8cbSJohn Marino         mpfr_log (a, a, GMP_RNDU);
321d30dc8cbSJohn Marino 
322d30dc8cbSJohn Marino         /* b = lower bound for log(x^2 + (1-y)^2) */
323d30dc8cbSJohn Marino         mpfr_ui_sub (b, 1, mpc_imagref (op), GMP_RNDZ); /* round to zero */
324d30dc8cbSJohn Marino         mpfr_sqr (b, b, GMP_RNDZ);
325d30dc8cbSJohn Marino         /* we could write mpfr_sqr (y, mpc_realref (op), GMP_RNDZ) but it is
326d30dc8cbSJohn Marino            more efficient to reuse the value of y (x^2) above and subtract
327d30dc8cbSJohn Marino            one ulp */
328d30dc8cbSJohn Marino         mpfr_nextbelow (y);
329d30dc8cbSJohn Marino         mpfr_add (b, b, y, GMP_RNDZ);
330d30dc8cbSJohn Marino         mpfr_log (b, b, GMP_RNDZ);
331d30dc8cbSJohn Marino 
332d30dc8cbSJohn Marino         mpfr_sub (y, a, b, GMP_RNDU);
333d30dc8cbSJohn Marino 
334d30dc8cbSJohn Marino         if (mpfr_zero_p (y))
335d30dc8cbSJohn Marino            /* FIXME: happens when x and y have very different magnitudes;
336d30dc8cbSJohn Marino               could be handled more efficiently                           */
337d30dc8cbSJohn Marino           ok = 0;
338d30dc8cbSJohn Marino         else
339d30dc8cbSJohn Marino           {
340d30dc8cbSJohn Marino             expo = MPC_MAX (mpfr_get_exp (a), mpfr_get_exp (b));
341d30dc8cbSJohn Marino             expo = expo - mpfr_get_exp (y) + 1;
342d30dc8cbSJohn Marino             err = 3 - mpfr_get_exp (y);
343d30dc8cbSJohn Marino             /* error(j) <= [1 + 2^expo + 7*2^err] ulp(j) */
344d30dc8cbSJohn Marino             if (expo <= err) /* error(j) <= [1 + 2^{err+1}] ulp(j) */
345d30dc8cbSJohn Marino               err = (err < 0) ? 1 : err + 2;
346d30dc8cbSJohn Marino             else
347d30dc8cbSJohn Marino               err = (expo < 0) ? 1 : expo + 2;
348d30dc8cbSJohn Marino 
349d30dc8cbSJohn Marino             mpfr_div_2ui (y, y, 2, GMP_RNDN);
350d30dc8cbSJohn Marino             MPC_ASSERT (!mpfr_zero_p (y));
351d30dc8cbSJohn Marino                /* FIXME: underflow. Since the main term of the Taylor series
352d30dc8cbSJohn Marino                   in y=0 is 1/(x^2+1) * y, this means that y is very small
353d30dc8cbSJohn Marino                   and/or x very large; but then the mpfr_zero_p (y) above
354d30dc8cbSJohn Marino                   should be true. This needs a proof, or better yet,
355d30dc8cbSJohn Marino                   special code.                                              */
356d30dc8cbSJohn Marino 
357d30dc8cbSJohn Marino             ok = mpfr_can_round (y, p - err, GMP_RNDU, GMP_RNDD,
358d30dc8cbSJohn Marino                                  prec + (MPC_RND_IM (rnd) == GMP_RNDN));
359d30dc8cbSJohn Marino           }
360d30dc8cbSJohn Marino       } while (ok == 0);
361d30dc8cbSJohn Marino 
362d30dc8cbSJohn Marino     inex = mpc_set_fr_fr (rop, x, y, rnd);
363d30dc8cbSJohn Marino 
364d30dc8cbSJohn Marino     mpfr_clears (a, b, x, y, (mpfr_ptr) 0);
365d30dc8cbSJohn Marino     return inex;
366d30dc8cbSJohn Marino   }
367d30dc8cbSJohn Marino }
368