1 /*
2  * Copyright (C) 1987-2008 Sun Microsystems, Inc. All Rights Reserved.
3  * Copyright (C) 2008-2011 Robert Ancell.
4  *
5  * This program is free software: you can redistribute it and/or modify it under
6  * the terms of the GNU General Public License as published by the Free Software
7  * Foundation, either version 2 of the License, or (at your option) any later
8  * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
9  * license.
10  */
11 
12 #include <stdlib.h>
13 #include <string.h>
14 #include <math.h>
15 #include <libintl.h>
16 
17 #include "mp.h"
18 
19 /* Convert x to radians */
20 void
convert_to_radians(const MPNumber * x,MPAngleUnit unit,MPNumber * z)21 convert_to_radians(const MPNumber *x, MPAngleUnit unit, MPNumber *z)
22 {
23     int i;
24 
25     switch(unit) {
26     default:
27     case MP_RADIANS:
28         mp_set_from_mp(x, z);
29         return;
30 
31     case MP_DEGREES:
32         i = 180;
33         break;
34 
35     case MP_GRADIANS:
36         i = 200;
37         break;
38     }
39     mpfr_t scale;
40     mpfr_init2(scale, PRECISION);
41     mpfr_const_pi(scale, MPFR_RNDN);
42     mpfr_div_si(scale, scale, i, MPFR_RNDN);
43     mpc_mul_fr(z->num, x->num, scale, MPC_RNDNN);
44     mpfr_clear(scale);
45 }
46 
47 void
convert_from_radians(const MPNumber * x,MPAngleUnit unit,MPNumber * z)48 convert_from_radians(const MPNumber *x, MPAngleUnit unit, MPNumber *z)
49 {
50     int i;
51 
52     switch(unit) {
53     default:
54     case MP_RADIANS:
55         mp_set_from_mp(x, z);
56         return;
57 
58     case MP_DEGREES:
59         i = 180;
60         break;
61 
62     case MP_GRADIANS:
63         i = 200;
64         break;
65     }
66     mpfr_t scale;
67     mpfr_init2(scale, PRECISION);
68     mpfr_const_pi(scale, MPFR_RNDN);
69     mpfr_si_div(scale, i, scale, MPFR_RNDN);
70     mpc_mul_fr(z->num, x->num, scale, MPC_RNDNN);
71     mpfr_clear(scale);
72 }
73 
74 
75 void
mp_get_pi(MPNumber * z)76 mp_get_pi (MPNumber *z)
77 {
78     mpfr_const_pi(mpc_realref(z->num), MPFR_RNDN);
79     mpfr_set_zero(mpc_imagref(z->num), 0);
80 }
81 
82 
83 void
mp_sin(const MPNumber * x,MPAngleUnit unit,MPNumber * z)84 mp_sin(const MPNumber *x, MPAngleUnit unit, MPNumber *z)
85 {
86     if (mp_is_complex(x))
87         mp_set_from_mp(x, z);
88     else
89         convert_to_radians(x, unit, z);
90     mpc_sin(z->num, z->num, MPC_RNDNN);
91 }
92 
93 
94 void
mp_cos(const MPNumber * x,MPAngleUnit unit,MPNumber * z)95 mp_cos(const MPNumber *x, MPAngleUnit unit, MPNumber *z)
96 {
97     if (mp_is_complex(x))
98         mp_set_from_mp(x, z);
99     else
100         convert_to_radians(x, unit, z);
101     mpc_cos(z->num, z->num, MPC_RNDNN);
102 }
103 
104 
105 void
mp_tan(const MPNumber * x,MPAngleUnit unit,MPNumber * z)106 mp_tan(const MPNumber *x, MPAngleUnit unit, MPNumber *z)
107 {
108     MPNumber x_radians = mp_new();
109     MPNumber pi = mp_new();
110     MPNumber t1 = mp_new();
111 
112     convert_to_radians(x, unit, &x_radians);
113     mp_get_pi(&pi);
114     mp_divide_integer(&pi, 2, &t1);
115     mp_subtract(&x_radians, &t1, &t1);
116     mp_divide(&t1, &pi, &t1);
117 
118     if (mp_is_integer(&t1)) {
119         /* Translators: Error displayed when tangent value is undefined */
120         mperr(_("Tangent is undefined for angles that are multiples of π (180°) from π∕2 (90°)"));
121         mp_set_from_integer(0, z);
122         mp_clear(&x_radians);
123         mp_clear(&pi);
124         mp_clear(&t1);
125         return;
126     }
127 
128     if (mp_is_complex(x))
129         mp_set_from_mp(x, z);
130     else
131         mp_set_from_mp(&x_radians, z);
132     mpc_tan(z->num, z->num, MPC_RNDNN);
133     mp_clear(&x_radians);
134     mp_clear(&pi);
135     mp_clear(&t1);
136 }
137 
138 
139 void
mp_asin(const MPNumber * x,MPAngleUnit unit,MPNumber * z)140 mp_asin(const MPNumber *x, MPAngleUnit unit, MPNumber *z)
141 {
142     MPNumber x_max = mp_new();
143     MPNumber x_min = mp_new();
144     mp_set_from_integer(1, &x_max);
145     mp_set_from_integer(-1, &x_min);
146 
147     if (mp_compare(x, &x_max) > 0 || mp_compare(x, &x_min) < 0)
148     {
149         /* Translators: Error displayed when inverse sine value is undefined */
150         mperr(_("Inverse sine is undefined for values outside [-1, 1]"));
151         mp_set_from_integer(0, z);
152         mp_clear(&x_max);
153         mp_clear(&x_min);
154         return;
155     }
156     mpc_asin(z->num, x->num, MPC_RNDNN);
157     if (!mp_is_complex(z))
158         convert_from_radians(z, unit, z);
159     mp_clear(&x_max);
160     mp_clear(&x_min);
161 }
162 
163 
164 void
mp_acos(const MPNumber * x,MPAngleUnit unit,MPNumber * z)165 mp_acos(const MPNumber *x, MPAngleUnit unit, MPNumber *z)
166 {
167     MPNumber x_max = mp_new();
168     MPNumber x_min = mp_new();
169     mp_set_from_integer(1, &x_max);
170     mp_set_from_integer(-1, &x_min);
171 
172     if (mp_compare(x, &x_max) > 0 || mp_compare(x, &x_min) < 0)
173     {
174         /* Translators: Error displayed when inverse sine value is undefined */
175         mperr(_("Inverse cosine is undefined for values outside [-1, 1]"));
176         mp_set_from_integer(0, z);
177         mp_clear(&x_max);
178         mp_clear(&x_min);
179         return;
180     }
181     mpc_acos(z->num, x->num, MPC_RNDNN);
182     if (!mp_is_complex(z))
183         convert_from_radians(z, unit, z);
184     mp_clear(&x_max);
185     mp_clear(&x_min);
186 }
187 
188 
189 void
mp_atan(const MPNumber * x,MPAngleUnit unit,MPNumber * z)190 mp_atan(const MPNumber *x, MPAngleUnit unit, MPNumber *z)
191 {
192     MPNumber i = mp_new();
193     MPNumber minus_i = mp_new();
194     mpc_set_si_si(i.num, 0, 1, MPC_RNDNN);
195     mpc_set_si_si(minus_i.num, 0, -1, MPC_RNDNN);
196 
197     /* Check x != i and x != -i */
198     if (mp_is_equal(x, &i) || mp_is_equal(x, &minus_i))
199     {
200         /* Translators: Error displayed when inverse sine value is undefined */
201         mperr(_("Arctangent function is undefined for values i and -i"));
202         mp_set_from_integer(0, z);
203         mp_clear(&i);
204         mp_clear(&minus_i);
205         return;
206     }
207     mpc_atan(z->num, x->num, MPC_RNDNN);
208     if (!mp_is_complex(z))
209         convert_from_radians(z, unit, z);
210     mp_clear(&i);
211     mp_clear(&minus_i);
212 }
213 
214 
215 void
mp_sinh(const MPNumber * x,MPNumber * z)216 mp_sinh(const MPNumber *x, MPNumber *z)
217 {
218     mpc_sinh(z->num, x->num, MPC_RNDNN);
219 }
220 
221 
222 void
mp_cosh(const MPNumber * x,MPNumber * z)223 mp_cosh(const MPNumber *x, MPNumber *z)
224 {
225     mpc_cosh(z->num, x->num, MPC_RNDNN);
226 }
227 
228 
229 void
mp_tanh(const MPNumber * x,MPNumber * z)230 mp_tanh(const MPNumber *x, MPNumber *z)
231 {
232     mpc_tanh(z->num, x->num, MPC_RNDNN);
233 }
234 
235 
236 void
mp_asinh(const MPNumber * x,MPNumber * z)237 mp_asinh(const MPNumber *x, MPNumber *z)
238 {
239     mpc_asinh(z->num, x->num, MPC_RNDNN);
240 }
241 
242 
243 void
mp_acosh(const MPNumber * x,MPNumber * z)244 mp_acosh(const MPNumber *x, MPNumber *z)
245 {
246     MPNumber t = mp_new();
247 
248     /* Check x >= 1 */
249     mp_set_from_integer(1, &t);
250     if (mp_is_less_than(x, &t))
251     {
252         /* Translators: Error displayed when inverse hyperbolic cosine value is undefined */
253         mperr(_("Inverse hyperbolic cosine is undefined for values less than one"));
254         mp_set_from_integer(0, z);
255         mp_clear(&t);
256         return;
257     }
258 
259     mpc_acosh(z->num, x->num, MPC_RNDNN);
260     mp_clear(&t);
261 }
262 
263 
264 void
mp_atanh(const MPNumber * x,MPNumber * z)265 mp_atanh(const MPNumber *x, MPNumber *z)
266 {
267     MPNumber x_max = mp_new();
268     MPNumber x_min = mp_new();
269     mp_set_from_integer(1, &x_max);
270     mp_set_from_integer(-1, &x_min);
271 
272     if (mp_compare(x, &x_max) >= 0 || mp_compare(x, &x_min) <= 0)
273     {
274          /* Translators: Error displayed when inverse hyperbolic tangent value is undefined */
275         mperr(_("Inverse hyperbolic tangent is undefined for values outside (-1, 1)"));
276         mp_set_from_integer(0, z);
277         mp_clear(&x_max);
278         mp_clear(&x_min);
279         return;
280     }
281     mpc_atanh(z->num, x->num, MPC_RNDNN);
282     mp_clear(&x_max);
283     mp_clear(&x_min);
284 }
285