1*d30dc8cbSJohn Marino /* mpc_acos -- arccosine of a complex number.
2*d30dc8cbSJohn Marino
3*d30dc8cbSJohn Marino Copyright (C) 2009, 2010, 2011, 2012 INRIA
4*d30dc8cbSJohn Marino
5*d30dc8cbSJohn Marino This file is part of GNU MPC.
6*d30dc8cbSJohn Marino
7*d30dc8cbSJohn Marino GNU MPC is free software; you can redistribute it and/or modify it under
8*d30dc8cbSJohn Marino the terms of the GNU Lesser General Public License as published by the
9*d30dc8cbSJohn Marino Free Software Foundation; either version 3 of the License, or (at your
10*d30dc8cbSJohn Marino option) any later version.
11*d30dc8cbSJohn Marino
12*d30dc8cbSJohn Marino GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY
13*d30dc8cbSJohn Marino WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14*d30dc8cbSJohn Marino FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
15*d30dc8cbSJohn Marino more details.
16*d30dc8cbSJohn Marino
17*d30dc8cbSJohn Marino You should have received a copy of the GNU Lesser General Public License
18*d30dc8cbSJohn Marino along with this program. If not, see http://www.gnu.org/licenses/ .
19*d30dc8cbSJohn Marino */
20*d30dc8cbSJohn Marino
21*d30dc8cbSJohn Marino #include <stdio.h> /* for MPC_ASSERT */
22*d30dc8cbSJohn Marino #include "mpc-impl.h"
23*d30dc8cbSJohn Marino
24*d30dc8cbSJohn Marino int
mpc_acos(mpc_ptr rop,mpc_srcptr op,mpc_rnd_t rnd)25*d30dc8cbSJohn Marino mpc_acos (mpc_ptr rop, mpc_srcptr op, mpc_rnd_t rnd)
26*d30dc8cbSJohn Marino {
27*d30dc8cbSJohn Marino int inex_re, inex_im, inex;
28*d30dc8cbSJohn Marino mpfr_prec_t p_re, p_im, p;
29*d30dc8cbSJohn Marino mpc_t z1;
30*d30dc8cbSJohn Marino mpfr_t pi_over_2;
31*d30dc8cbSJohn Marino mpfr_exp_t e1, e2;
32*d30dc8cbSJohn Marino mpfr_rnd_t rnd_im;
33*d30dc8cbSJohn Marino mpc_rnd_t rnd1;
34*d30dc8cbSJohn Marino
35*d30dc8cbSJohn Marino inex_re = 0;
36*d30dc8cbSJohn Marino inex_im = 0;
37*d30dc8cbSJohn Marino
38*d30dc8cbSJohn Marino /* special values */
39*d30dc8cbSJohn Marino if (mpfr_nan_p (mpc_realref (op)) || mpfr_nan_p (mpc_imagref (op)))
40*d30dc8cbSJohn Marino {
41*d30dc8cbSJohn Marino if (mpfr_inf_p (mpc_realref (op)) || mpfr_inf_p (mpc_imagref (op)))
42*d30dc8cbSJohn Marino {
43*d30dc8cbSJohn Marino mpfr_set_inf (mpc_imagref (rop), mpfr_signbit (mpc_imagref (op)) ? +1 : -1);
44*d30dc8cbSJohn Marino mpfr_set_nan (mpc_realref (rop));
45*d30dc8cbSJohn Marino }
46*d30dc8cbSJohn Marino else if (mpfr_zero_p (mpc_realref (op)))
47*d30dc8cbSJohn Marino {
48*d30dc8cbSJohn Marino inex_re = set_pi_over_2 (mpc_realref (rop), +1, MPC_RND_RE (rnd));
49*d30dc8cbSJohn Marino mpfr_set_nan (mpc_imagref (rop));
50*d30dc8cbSJohn Marino }
51*d30dc8cbSJohn Marino else
52*d30dc8cbSJohn Marino {
53*d30dc8cbSJohn Marino mpfr_set_nan (mpc_realref (rop));
54*d30dc8cbSJohn Marino mpfr_set_nan (mpc_imagref (rop));
55*d30dc8cbSJohn Marino }
56*d30dc8cbSJohn Marino
57*d30dc8cbSJohn Marino return MPC_INEX (inex_re, 0);
58*d30dc8cbSJohn Marino }
59*d30dc8cbSJohn Marino
60*d30dc8cbSJohn Marino if (mpfr_inf_p (mpc_realref (op)) || mpfr_inf_p (mpc_imagref (op)))
61*d30dc8cbSJohn Marino {
62*d30dc8cbSJohn Marino if (mpfr_inf_p (mpc_realref (op)))
63*d30dc8cbSJohn Marino {
64*d30dc8cbSJohn Marino if (mpfr_inf_p (mpc_imagref (op)))
65*d30dc8cbSJohn Marino {
66*d30dc8cbSJohn Marino if (mpfr_sgn (mpc_realref (op)) > 0)
67*d30dc8cbSJohn Marino {
68*d30dc8cbSJohn Marino inex_re =
69*d30dc8cbSJohn Marino set_pi_over_2 (mpc_realref (rop), +1, MPC_RND_RE (rnd));
70*d30dc8cbSJohn Marino mpfr_div_2ui (mpc_realref (rop), mpc_realref (rop), 1, GMP_RNDN);
71*d30dc8cbSJohn Marino }
72*d30dc8cbSJohn Marino else
73*d30dc8cbSJohn Marino {
74*d30dc8cbSJohn Marino
75*d30dc8cbSJohn Marino /* the real part of the result is 3*pi/4
76*d30dc8cbSJohn Marino a = o(pi) error(a) < 1 ulp(a)
77*d30dc8cbSJohn Marino b = o(3*a) error(b) < 2 ulp(b)
78*d30dc8cbSJohn Marino c = b/4 exact
79*d30dc8cbSJohn Marino thus 1 bit is lost */
80*d30dc8cbSJohn Marino mpfr_t x;
81*d30dc8cbSJohn Marino mpfr_prec_t prec;
82*d30dc8cbSJohn Marino int ok;
83*d30dc8cbSJohn Marino mpfr_init (x);
84*d30dc8cbSJohn Marino prec = mpfr_get_prec (mpc_realref (rop));
85*d30dc8cbSJohn Marino p = prec;
86*d30dc8cbSJohn Marino
87*d30dc8cbSJohn Marino do
88*d30dc8cbSJohn Marino {
89*d30dc8cbSJohn Marino p += mpc_ceil_log2 (p);
90*d30dc8cbSJohn Marino mpfr_set_prec (x, p);
91*d30dc8cbSJohn Marino mpfr_const_pi (x, GMP_RNDD);
92*d30dc8cbSJohn Marino mpfr_mul_ui (x, x, 3, GMP_RNDD);
93*d30dc8cbSJohn Marino ok =
94*d30dc8cbSJohn Marino mpfr_can_round (x, p - 1, GMP_RNDD, MPC_RND_RE (rnd),
95*d30dc8cbSJohn Marino prec+(MPC_RND_RE (rnd) == GMP_RNDN));
96*d30dc8cbSJohn Marino
97*d30dc8cbSJohn Marino } while (ok == 0);
98*d30dc8cbSJohn Marino inex_re =
99*d30dc8cbSJohn Marino mpfr_div_2ui (mpc_realref (rop), x, 2, MPC_RND_RE (rnd));
100*d30dc8cbSJohn Marino mpfr_clear (x);
101*d30dc8cbSJohn Marino }
102*d30dc8cbSJohn Marino }
103*d30dc8cbSJohn Marino else
104*d30dc8cbSJohn Marino {
105*d30dc8cbSJohn Marino if (mpfr_sgn (mpc_realref (op)) > 0)
106*d30dc8cbSJohn Marino mpfr_set_ui (mpc_realref (rop), 0, GMP_RNDN);
107*d30dc8cbSJohn Marino else
108*d30dc8cbSJohn Marino inex_re = mpfr_const_pi (mpc_realref (rop), MPC_RND_RE (rnd));
109*d30dc8cbSJohn Marino }
110*d30dc8cbSJohn Marino }
111*d30dc8cbSJohn Marino else
112*d30dc8cbSJohn Marino inex_re = set_pi_over_2 (mpc_realref (rop), +1, MPC_RND_RE (rnd));
113*d30dc8cbSJohn Marino
114*d30dc8cbSJohn Marino mpfr_set_inf (mpc_imagref (rop), mpfr_signbit (mpc_imagref (op)) ? +1 : -1);
115*d30dc8cbSJohn Marino
116*d30dc8cbSJohn Marino return MPC_INEX (inex_re, 0);
117*d30dc8cbSJohn Marino }
118*d30dc8cbSJohn Marino
119*d30dc8cbSJohn Marino /* pure real argument */
120*d30dc8cbSJohn Marino if (mpfr_zero_p (mpc_imagref (op)))
121*d30dc8cbSJohn Marino {
122*d30dc8cbSJohn Marino int s_im;
123*d30dc8cbSJohn Marino s_im = mpfr_signbit (mpc_imagref (op));
124*d30dc8cbSJohn Marino
125*d30dc8cbSJohn Marino if (mpfr_cmp_ui (mpc_realref (op), 1) > 0)
126*d30dc8cbSJohn Marino {
127*d30dc8cbSJohn Marino if (s_im)
128*d30dc8cbSJohn Marino inex_im = mpfr_acosh (mpc_imagref (rop), mpc_realref (op),
129*d30dc8cbSJohn Marino MPC_RND_IM (rnd));
130*d30dc8cbSJohn Marino else
131*d30dc8cbSJohn Marino inex_im = -mpfr_acosh (mpc_imagref (rop), mpc_realref (op),
132*d30dc8cbSJohn Marino INV_RND (MPC_RND_IM (rnd)));
133*d30dc8cbSJohn Marino
134*d30dc8cbSJohn Marino mpfr_set_ui (mpc_realref (rop), 0, GMP_RNDN);
135*d30dc8cbSJohn Marino }
136*d30dc8cbSJohn Marino else if (mpfr_cmp_si (mpc_realref (op), -1) < 0)
137*d30dc8cbSJohn Marino {
138*d30dc8cbSJohn Marino mpfr_t minus_op_re;
139*d30dc8cbSJohn Marino minus_op_re[0] = mpc_realref (op)[0];
140*d30dc8cbSJohn Marino MPFR_CHANGE_SIGN (minus_op_re);
141*d30dc8cbSJohn Marino
142*d30dc8cbSJohn Marino if (s_im)
143*d30dc8cbSJohn Marino inex_im = mpfr_acosh (mpc_imagref (rop), minus_op_re,
144*d30dc8cbSJohn Marino MPC_RND_IM (rnd));
145*d30dc8cbSJohn Marino else
146*d30dc8cbSJohn Marino inex_im = -mpfr_acosh (mpc_imagref (rop), minus_op_re,
147*d30dc8cbSJohn Marino INV_RND (MPC_RND_IM (rnd)));
148*d30dc8cbSJohn Marino inex_re = mpfr_const_pi (mpc_realref (rop), MPC_RND_RE (rnd));
149*d30dc8cbSJohn Marino }
150*d30dc8cbSJohn Marino else
151*d30dc8cbSJohn Marino {
152*d30dc8cbSJohn Marino inex_re = mpfr_acos (mpc_realref (rop), mpc_realref (op), MPC_RND_RE (rnd));
153*d30dc8cbSJohn Marino mpfr_set_ui (mpc_imagref (rop), 0, MPC_RND_IM (rnd));
154*d30dc8cbSJohn Marino }
155*d30dc8cbSJohn Marino
156*d30dc8cbSJohn Marino if (!s_im)
157*d30dc8cbSJohn Marino mpc_conj (rop, rop, MPC_RNDNN);
158*d30dc8cbSJohn Marino
159*d30dc8cbSJohn Marino return MPC_INEX (inex_re, inex_im);
160*d30dc8cbSJohn Marino }
161*d30dc8cbSJohn Marino
162*d30dc8cbSJohn Marino /* pure imaginary argument */
163*d30dc8cbSJohn Marino if (mpfr_zero_p (mpc_realref (op)))
164*d30dc8cbSJohn Marino {
165*d30dc8cbSJohn Marino inex_re = set_pi_over_2 (mpc_realref (rop), +1, MPC_RND_RE (rnd));
166*d30dc8cbSJohn Marino inex_im = -mpfr_asinh (mpc_imagref (rop), mpc_imagref (op),
167*d30dc8cbSJohn Marino INV_RND (MPC_RND_IM (rnd)));
168*d30dc8cbSJohn Marino mpc_conj (rop,rop, MPC_RNDNN);
169*d30dc8cbSJohn Marino
170*d30dc8cbSJohn Marino return MPC_INEX (inex_re, inex_im);
171*d30dc8cbSJohn Marino }
172*d30dc8cbSJohn Marino
173*d30dc8cbSJohn Marino /* regular complex argument: acos(z) = Pi/2 - asin(z) */
174*d30dc8cbSJohn Marino p_re = mpfr_get_prec (mpc_realref(rop));
175*d30dc8cbSJohn Marino p_im = mpfr_get_prec (mpc_imagref(rop));
176*d30dc8cbSJohn Marino p = p_re;
177*d30dc8cbSJohn Marino mpc_init3 (z1, p, p_im); /* we round directly the imaginary part to p_im,
178*d30dc8cbSJohn Marino with rounding mode opposite to rnd_im */
179*d30dc8cbSJohn Marino rnd_im = MPC_RND_IM(rnd);
180*d30dc8cbSJohn Marino /* the imaginary part of asin(z) has the same sign as Im(z), thus if
181*d30dc8cbSJohn Marino Im(z) > 0 and rnd_im = RNDZ, we want to round the Im(asin(z)) to -Inf
182*d30dc8cbSJohn Marino so that -Im(asin(z)) is rounded to zero */
183*d30dc8cbSJohn Marino if (rnd_im == GMP_RNDZ)
184*d30dc8cbSJohn Marino rnd_im = mpfr_sgn (mpc_imagref(op)) > 0 ? GMP_RNDD : GMP_RNDU;
185*d30dc8cbSJohn Marino else
186*d30dc8cbSJohn Marino rnd_im = rnd_im == GMP_RNDU ? GMP_RNDD
187*d30dc8cbSJohn Marino : rnd_im == GMP_RNDD ? GMP_RNDU
188*d30dc8cbSJohn Marino : rnd_im; /* both RNDZ and RNDA map to themselves for -asin(z) */
189*d30dc8cbSJohn Marino rnd1 = MPC_RND (GMP_RNDN, rnd_im);
190*d30dc8cbSJohn Marino mpfr_init2 (pi_over_2, p);
191*d30dc8cbSJohn Marino for (;;)
192*d30dc8cbSJohn Marino {
193*d30dc8cbSJohn Marino p += mpc_ceil_log2 (p) + 3;
194*d30dc8cbSJohn Marino
195*d30dc8cbSJohn Marino mpfr_set_prec (mpc_realref(z1), p);
196*d30dc8cbSJohn Marino mpfr_set_prec (pi_over_2, p);
197*d30dc8cbSJohn Marino
198*d30dc8cbSJohn Marino set_pi_over_2 (pi_over_2, +1, GMP_RNDN);
199*d30dc8cbSJohn Marino e1 = 1; /* Exp(pi_over_2) */
200*d30dc8cbSJohn Marino inex = mpc_asin (z1, op, rnd1); /* asin(z) */
201*d30dc8cbSJohn Marino MPC_ASSERT (mpfr_sgn (mpc_imagref(z1)) * mpfr_sgn (mpc_imagref(op)) > 0);
202*d30dc8cbSJohn Marino inex_im = MPC_INEX_IM(inex); /* inex_im is in {-1, 0, 1} */
203*d30dc8cbSJohn Marino e2 = mpfr_get_exp (mpc_realref(z1));
204*d30dc8cbSJohn Marino mpfr_sub (mpc_realref(z1), pi_over_2, mpc_realref(z1), GMP_RNDN);
205*d30dc8cbSJohn Marino if (!mpfr_zero_p (mpc_realref(z1)))
206*d30dc8cbSJohn Marino {
207*d30dc8cbSJohn Marino /* the error on x=Re(z1) is bounded by 1/2 ulp(x) + 2^(e1-p-1) +
208*d30dc8cbSJohn Marino 2^(e2-p-1) */
209*d30dc8cbSJohn Marino e1 = e1 >= e2 ? e1 + 1 : e2 + 1;
210*d30dc8cbSJohn Marino /* the error on x is bounded by 1/2 ulp(x) + 2^(e1-p-1) */
211*d30dc8cbSJohn Marino e1 -= mpfr_get_exp (mpc_realref(z1));
212*d30dc8cbSJohn Marino /* the error on x is bounded by 1/2 ulp(x) [1 + 2^e1] */
213*d30dc8cbSJohn Marino e1 = e1 <= 0 ? 0 : e1;
214*d30dc8cbSJohn Marino /* the error on x is bounded by 2^e1 * ulp(x) */
215*d30dc8cbSJohn Marino mpfr_neg (mpc_imagref(z1), mpc_imagref(z1), GMP_RNDN); /* exact */
216*d30dc8cbSJohn Marino inex_im = -inex_im;
217*d30dc8cbSJohn Marino if (mpfr_can_round (mpc_realref(z1), p - e1, GMP_RNDN, GMP_RNDZ,
218*d30dc8cbSJohn Marino p_re + (MPC_RND_RE(rnd) == GMP_RNDN)))
219*d30dc8cbSJohn Marino break;
220*d30dc8cbSJohn Marino }
221*d30dc8cbSJohn Marino }
222*d30dc8cbSJohn Marino inex = mpc_set (rop, z1, rnd);
223*d30dc8cbSJohn Marino inex_re = MPC_INEX_RE(inex);
224*d30dc8cbSJohn Marino mpc_clear (z1);
225*d30dc8cbSJohn Marino mpfr_clear (pi_over_2);
226*d30dc8cbSJohn Marino
227*d30dc8cbSJohn Marino return MPC_INEX(inex_re, inex_im);
228*d30dc8cbSJohn Marino }
229