xref: /dflybsd-src/contrib/mpc/src/asin.c (revision 04febcfb30580676d3e95f58a16c5137ee478b32)
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