1*d30dc8cbSJohn Marino /* mpc_asin -- arcsine of a complex number.
2*d30dc8cbSJohn Marino
3*d30dc8cbSJohn Marino Copyright (C) 2009, 2010, 2011 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 "mpc-impl.h"
22*d30dc8cbSJohn Marino
23*d30dc8cbSJohn Marino int
mpc_asin(mpc_ptr rop,mpc_srcptr op,mpc_rnd_t rnd)24*d30dc8cbSJohn Marino mpc_asin (mpc_ptr rop, mpc_srcptr op, mpc_rnd_t rnd)
25*d30dc8cbSJohn Marino {
26*d30dc8cbSJohn Marino mpfr_prec_t p, p_re, p_im, incr_p = 0;
27*d30dc8cbSJohn Marino mpfr_rnd_t rnd_re, rnd_im;
28*d30dc8cbSJohn Marino mpc_t z1;
29*d30dc8cbSJohn Marino int inex;
30*d30dc8cbSJohn Marino
31*d30dc8cbSJohn Marino /* special values */
32*d30dc8cbSJohn Marino if (mpfr_nan_p (mpc_realref (op)) || mpfr_nan_p (mpc_imagref (op)))
33*d30dc8cbSJohn Marino {
34*d30dc8cbSJohn Marino if (mpfr_inf_p (mpc_realref (op)) || mpfr_inf_p (mpc_imagref (op)))
35*d30dc8cbSJohn Marino {
36*d30dc8cbSJohn Marino mpfr_set_nan (mpc_realref (rop));
37*d30dc8cbSJohn Marino mpfr_set_inf (mpc_imagref (rop), mpfr_signbit (mpc_imagref (op)) ? -1 : +1);
38*d30dc8cbSJohn Marino }
39*d30dc8cbSJohn Marino else if (mpfr_zero_p (mpc_realref (op)))
40*d30dc8cbSJohn Marino {
41*d30dc8cbSJohn Marino mpfr_set (mpc_realref (rop), mpc_realref (op), GMP_RNDN);
42*d30dc8cbSJohn Marino mpfr_set_nan (mpc_imagref (rop));
43*d30dc8cbSJohn Marino }
44*d30dc8cbSJohn Marino else
45*d30dc8cbSJohn Marino {
46*d30dc8cbSJohn Marino mpfr_set_nan (mpc_realref (rop));
47*d30dc8cbSJohn Marino mpfr_set_nan (mpc_imagref (rop));
48*d30dc8cbSJohn Marino }
49*d30dc8cbSJohn Marino
50*d30dc8cbSJohn Marino return 0;
51*d30dc8cbSJohn Marino }
52*d30dc8cbSJohn Marino
53*d30dc8cbSJohn Marino if (mpfr_inf_p (mpc_realref (op)) || mpfr_inf_p (mpc_imagref (op)))
54*d30dc8cbSJohn Marino {
55*d30dc8cbSJohn Marino int inex_re;
56*d30dc8cbSJohn Marino if (mpfr_inf_p (mpc_realref (op)))
57*d30dc8cbSJohn Marino {
58*d30dc8cbSJohn Marino int inf_im = mpfr_inf_p (mpc_imagref (op));
59*d30dc8cbSJohn Marino
60*d30dc8cbSJohn Marino inex_re = set_pi_over_2 (mpc_realref (rop),
61*d30dc8cbSJohn Marino (mpfr_signbit (mpc_realref (op)) ? -1 : 1), MPC_RND_RE (rnd));
62*d30dc8cbSJohn Marino mpfr_set_inf (mpc_imagref (rop), (mpfr_signbit (mpc_imagref (op)) ? -1 : 1));
63*d30dc8cbSJohn Marino
64*d30dc8cbSJohn Marino if (inf_im)
65*d30dc8cbSJohn Marino mpfr_div_2ui (mpc_realref (rop), mpc_realref (rop), 1, GMP_RNDN);
66*d30dc8cbSJohn Marino }
67*d30dc8cbSJohn Marino else
68*d30dc8cbSJohn Marino {
69*d30dc8cbSJohn Marino mpfr_set_zero (mpc_realref (rop), (mpfr_signbit (mpc_realref (op)) ? -1 : 1));
70*d30dc8cbSJohn Marino inex_re = 0;
71*d30dc8cbSJohn Marino mpfr_set_inf (mpc_imagref (rop), (mpfr_signbit (mpc_imagref (op)) ? -1 : 1));
72*d30dc8cbSJohn Marino }
73*d30dc8cbSJohn Marino
74*d30dc8cbSJohn Marino return MPC_INEX (inex_re, 0);
75*d30dc8cbSJohn Marino }
76*d30dc8cbSJohn Marino
77*d30dc8cbSJohn Marino /* pure real argument */
78*d30dc8cbSJohn Marino if (mpfr_zero_p (mpc_imagref (op)))
79*d30dc8cbSJohn Marino {
80*d30dc8cbSJohn Marino int inex_re;
81*d30dc8cbSJohn Marino int inex_im;
82*d30dc8cbSJohn Marino int s_im;
83*d30dc8cbSJohn Marino s_im = mpfr_signbit (mpc_imagref (op));
84*d30dc8cbSJohn Marino
85*d30dc8cbSJohn Marino if (mpfr_cmp_ui (mpc_realref (op), 1) > 0)
86*d30dc8cbSJohn Marino {
87*d30dc8cbSJohn Marino if (s_im)
88*d30dc8cbSJohn Marino inex_im = -mpfr_acosh (mpc_imagref (rop), mpc_realref (op),
89*d30dc8cbSJohn Marino INV_RND (MPC_RND_IM (rnd)));
90*d30dc8cbSJohn Marino else
91*d30dc8cbSJohn Marino inex_im = mpfr_acosh (mpc_imagref (rop), mpc_realref (op),
92*d30dc8cbSJohn Marino MPC_RND_IM (rnd));
93*d30dc8cbSJohn Marino inex_re = set_pi_over_2 (mpc_realref (rop),
94*d30dc8cbSJohn Marino (mpfr_signbit (mpc_realref (op)) ? -1 : 1), MPC_RND_RE (rnd));
95*d30dc8cbSJohn Marino if (s_im)
96*d30dc8cbSJohn Marino mpc_conj (rop, rop, MPC_RNDNN);
97*d30dc8cbSJohn Marino }
98*d30dc8cbSJohn Marino else if (mpfr_cmp_si (mpc_realref (op), -1) < 0)
99*d30dc8cbSJohn Marino {
100*d30dc8cbSJohn Marino mpfr_t minus_op_re;
101*d30dc8cbSJohn Marino minus_op_re[0] = mpc_realref (op)[0];
102*d30dc8cbSJohn Marino MPFR_CHANGE_SIGN (minus_op_re);
103*d30dc8cbSJohn Marino
104*d30dc8cbSJohn Marino if (s_im)
105*d30dc8cbSJohn Marino inex_im = -mpfr_acosh (mpc_imagref (rop), minus_op_re,
106*d30dc8cbSJohn Marino INV_RND (MPC_RND_IM (rnd)));
107*d30dc8cbSJohn Marino else
108*d30dc8cbSJohn Marino inex_im = mpfr_acosh (mpc_imagref (rop), minus_op_re,
109*d30dc8cbSJohn Marino MPC_RND_IM (rnd));
110*d30dc8cbSJohn Marino inex_re = set_pi_over_2 (mpc_realref (rop),
111*d30dc8cbSJohn Marino (mpfr_signbit (mpc_realref (op)) ? -1 : 1), MPC_RND_RE (rnd));
112*d30dc8cbSJohn Marino if (s_im)
113*d30dc8cbSJohn Marino mpc_conj (rop, rop, MPC_RNDNN);
114*d30dc8cbSJohn Marino }
115*d30dc8cbSJohn Marino else
116*d30dc8cbSJohn Marino {
117*d30dc8cbSJohn Marino inex_im = mpfr_set_ui (mpc_imagref (rop), 0, MPC_RND_IM (rnd));
118*d30dc8cbSJohn Marino if (s_im)
119*d30dc8cbSJohn Marino mpfr_neg (mpc_imagref (rop), mpc_imagref (rop), GMP_RNDN);
120*d30dc8cbSJohn Marino inex_re = mpfr_asin (mpc_realref (rop), mpc_realref (op), MPC_RND_RE (rnd));
121*d30dc8cbSJohn Marino }
122*d30dc8cbSJohn Marino
123*d30dc8cbSJohn Marino return MPC_INEX (inex_re, inex_im);
124*d30dc8cbSJohn Marino }
125*d30dc8cbSJohn Marino
126*d30dc8cbSJohn Marino /* pure imaginary argument */
127*d30dc8cbSJohn Marino if (mpfr_zero_p (mpc_realref (op)))
128*d30dc8cbSJohn Marino {
129*d30dc8cbSJohn Marino int inex_im;
130*d30dc8cbSJohn Marino int s;
131*d30dc8cbSJohn Marino s = mpfr_signbit (mpc_realref (op));
132*d30dc8cbSJohn Marino mpfr_set_ui (mpc_realref (rop), 0, GMP_RNDN);
133*d30dc8cbSJohn Marino if (s)
134*d30dc8cbSJohn Marino mpfr_neg (mpc_realref (rop), mpc_realref (rop), GMP_RNDN);
135*d30dc8cbSJohn Marino inex_im = mpfr_asinh (mpc_imagref (rop), mpc_imagref (op), MPC_RND_IM (rnd));
136*d30dc8cbSJohn Marino
137*d30dc8cbSJohn Marino return MPC_INEX (0, inex_im);
138*d30dc8cbSJohn Marino }
139*d30dc8cbSJohn Marino
140*d30dc8cbSJohn Marino /* regular complex: asin(z) = -i*log(i*z+sqrt(1-z^2)) */
141*d30dc8cbSJohn Marino p_re = mpfr_get_prec (mpc_realref(rop));
142*d30dc8cbSJohn Marino p_im = mpfr_get_prec (mpc_imagref(rop));
143*d30dc8cbSJohn Marino rnd_re = MPC_RND_RE(rnd);
144*d30dc8cbSJohn Marino rnd_im = MPC_RND_IM(rnd);
145*d30dc8cbSJohn Marino p = p_re >= p_im ? p_re : p_im;
146*d30dc8cbSJohn Marino mpc_init2 (z1, p);
147*d30dc8cbSJohn Marino while (1)
148*d30dc8cbSJohn Marino {
149*d30dc8cbSJohn Marino mpfr_exp_t ex, ey, err;
150*d30dc8cbSJohn Marino
151*d30dc8cbSJohn Marino p += mpc_ceil_log2 (p) + 3 + incr_p; /* incr_p is zero initially */
152*d30dc8cbSJohn Marino incr_p = p / 2;
153*d30dc8cbSJohn Marino mpfr_set_prec (mpc_realref(z1), p);
154*d30dc8cbSJohn Marino mpfr_set_prec (mpc_imagref(z1), p);
155*d30dc8cbSJohn Marino
156*d30dc8cbSJohn Marino /* z1 <- z^2 */
157*d30dc8cbSJohn Marino mpc_sqr (z1, op, MPC_RNDNN);
158*d30dc8cbSJohn Marino /* err(x) <= 1/2 ulp(x), err(y) <= 1/2 ulp(y) */
159*d30dc8cbSJohn Marino /* z1 <- 1-z1 */
160*d30dc8cbSJohn Marino ex = mpfr_get_exp (mpc_realref(z1));
161*d30dc8cbSJohn Marino mpfr_ui_sub (mpc_realref(z1), 1, mpc_realref(z1), GMP_RNDN);
162*d30dc8cbSJohn Marino mpfr_neg (mpc_imagref(z1), mpc_imagref(z1), GMP_RNDN);
163*d30dc8cbSJohn Marino ex = ex - mpfr_get_exp (mpc_realref(z1));
164*d30dc8cbSJohn Marino ex = (ex <= 0) ? 0 : ex;
165*d30dc8cbSJohn Marino /* err(x) <= 2^ex * ulp(x) */
166*d30dc8cbSJohn Marino ex = ex + mpfr_get_exp (mpc_realref(z1)) - p;
167*d30dc8cbSJohn Marino /* err(x) <= 2^ex */
168*d30dc8cbSJohn Marino ey = mpfr_get_exp (mpc_imagref(z1)) - p - 1;
169*d30dc8cbSJohn Marino /* err(y) <= 2^ey */
170*d30dc8cbSJohn Marino ex = (ex >= ey) ? ex : ey; /* err(x), err(y) <= 2^ex, i.e., the norm
171*d30dc8cbSJohn Marino of the error is bounded by |h|<=2^(ex+1/2) */
172*d30dc8cbSJohn Marino /* z1 <- sqrt(z1): if z1 = z + h, then sqrt(z1) = sqrt(z) + h/2/sqrt(t) */
173*d30dc8cbSJohn Marino ey = mpfr_get_exp (mpc_realref(z1)) >= mpfr_get_exp (mpc_imagref(z1))
174*d30dc8cbSJohn Marino ? mpfr_get_exp (mpc_realref(z1)) : mpfr_get_exp (mpc_imagref(z1));
175*d30dc8cbSJohn Marino /* we have |z1| >= 2^(ey-1) thus 1/|z1| <= 2^(1-ey) */
176*d30dc8cbSJohn Marino mpc_sqrt (z1, z1, MPC_RNDNN);
177*d30dc8cbSJohn Marino ex = (2 * ex + 1) - 2 - (ey - 1); /* |h^2/4/|t| <= 2^ex */
178*d30dc8cbSJohn Marino ex = (ex + 1) / 2; /* ceil(ex/2) */
179*d30dc8cbSJohn Marino /* express ex in terms of ulp(z1) */
180*d30dc8cbSJohn Marino ey = mpfr_get_exp (mpc_realref(z1)) <= mpfr_get_exp (mpc_imagref(z1))
181*d30dc8cbSJohn Marino ? mpfr_get_exp (mpc_realref(z1)) : mpfr_get_exp (mpc_imagref(z1));
182*d30dc8cbSJohn Marino ex = ex - ey + p;
183*d30dc8cbSJohn Marino /* take into account the rounding error in the mpc_sqrt call */
184*d30dc8cbSJohn Marino err = (ex <= 0) ? 1 : ex + 1;
185*d30dc8cbSJohn Marino /* err(x) <= 2^err * ulp(x), err(y) <= 2^err * ulp(y) */
186*d30dc8cbSJohn Marino /* z1 <- i*z + z1 */
187*d30dc8cbSJohn Marino ex = mpfr_get_exp (mpc_realref(z1));
188*d30dc8cbSJohn Marino ey = mpfr_get_exp (mpc_imagref(z1));
189*d30dc8cbSJohn Marino mpfr_sub (mpc_realref(z1), mpc_realref(z1), mpc_imagref(op), GMP_RNDN);
190*d30dc8cbSJohn Marino mpfr_add (mpc_imagref(z1), mpc_imagref(z1), mpc_realref(op), GMP_RNDN);
191*d30dc8cbSJohn Marino if (mpfr_cmp_ui (mpc_realref(z1), 0) == 0 || mpfr_cmp_ui (mpc_imagref(z1), 0) == 0)
192*d30dc8cbSJohn Marino continue;
193*d30dc8cbSJohn Marino ex -= mpfr_get_exp (mpc_realref(z1)); /* cancellation in x */
194*d30dc8cbSJohn Marino ey -= mpfr_get_exp (mpc_imagref(z1)); /* cancellation in y */
195*d30dc8cbSJohn Marino ex = (ex >= ey) ? ex : ey; /* maximum cancellation */
196*d30dc8cbSJohn Marino err += ex;
197*d30dc8cbSJohn Marino err = (err <= 0) ? 1 : err + 1; /* rounding error in sub/add */
198*d30dc8cbSJohn Marino /* z1 <- log(z1): if z1 = z + h, then log(z1) = log(z) + h/t with
199*d30dc8cbSJohn Marino |t| >= min(|z1|,|z|) */
200*d30dc8cbSJohn Marino ex = mpfr_get_exp (mpc_realref(z1));
201*d30dc8cbSJohn Marino ey = mpfr_get_exp (mpc_imagref(z1));
202*d30dc8cbSJohn Marino ex = (ex >= ey) ? ex : ey;
203*d30dc8cbSJohn Marino err += ex - p; /* revert to absolute error <= 2^err */
204*d30dc8cbSJohn Marino mpc_log (z1, z1, GMP_RNDN);
205*d30dc8cbSJohn Marino err -= ex - 1; /* 1/|t| <= 1/|z| <= 2^(1-ex) */
206*d30dc8cbSJohn Marino /* express err in terms of ulp(z1) */
207*d30dc8cbSJohn Marino ey = mpfr_get_exp (mpc_realref(z1)) <= mpfr_get_exp (mpc_imagref(z1))
208*d30dc8cbSJohn Marino ? mpfr_get_exp (mpc_realref(z1)) : mpfr_get_exp (mpc_imagref(z1));
209*d30dc8cbSJohn Marino err = err - ey + p;
210*d30dc8cbSJohn Marino /* take into account the rounding error in the mpc_log call */
211*d30dc8cbSJohn Marino err = (err <= 0) ? 1 : err + 1;
212*d30dc8cbSJohn Marino /* z1 <- -i*z1 */
213*d30dc8cbSJohn Marino mpfr_swap (mpc_realref(z1), mpc_imagref(z1));
214*d30dc8cbSJohn Marino mpfr_neg (mpc_imagref(z1), mpc_imagref(z1), GMP_RNDN);
215*d30dc8cbSJohn Marino if (mpfr_can_round (mpc_realref(z1), p - err, GMP_RNDN, GMP_RNDZ,
216*d30dc8cbSJohn Marino p_re + (rnd_re == GMP_RNDN)) &&
217*d30dc8cbSJohn Marino mpfr_can_round (mpc_imagref(z1), p - err, GMP_RNDN, GMP_RNDZ,
218*d30dc8cbSJohn Marino p_im + (rnd_im == GMP_RNDN)))
219*d30dc8cbSJohn Marino break;
220*d30dc8cbSJohn Marino }
221*d30dc8cbSJohn Marino
222*d30dc8cbSJohn Marino inex = mpc_set (rop, z1, rnd);
223*d30dc8cbSJohn Marino mpc_clear (z1);
224*d30dc8cbSJohn Marino
225*d30dc8cbSJohn Marino return inex;
226*d30dc8cbSJohn Marino }
227