1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* complex/math.c (from the GSL 1.1.1)
4 * Copyright (C) 1996, 1997, 1998, 1999, 2000 Jorma Olavi Tähtinen, Brian Gough
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or (at
9 * your option) any later version.
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <https://www.gnu.org/licenses/>.
20 /* Basic complex arithmetic functions
22 * Original version by Jorma Olavi Tähtinen <jotahtin@cc.hut.fi>
24 * Modified for GSL by Brian Gough, 3/2000
27 /* The following references describe the methods used in these
30 * T. E. Hull and Thomas F. Fairgrieve and Ping Tak Peter Tang,
31 * "Implementing Complex Elementary Functions Using Exception
32 * Handling", ACM Transactions on Mathematical Software, Volume 20
33 * (1994), pp 215-244, Corrigenda, p553
35 * Hull et al, "Implementing the complex arcsin and arccosine
36 * functions using exception handling", ACM Transactions on
37 * Mathematical Software, Volume 23 (1997) pp 299-335
39 * Abramowitz and Stegun, Handbook of Mathematical Functions, "Inverse
40 * Circular Functions in Terms of Real and Imaginary Parts", Formulas
41 * 4.4.37, 4.4.38, 4.4.39
45 * Gnumeric specific modifications written by Jukka-Pekka Iivonen
46 * (jiivonen@hutcs.cs.hut.fi)
48 * long double modifications by Morten Welinder.
51 #include <gnumeric-config.h>
52 #include <glib/gi18n-lib.h>
57 #include "gsl-complex.h"
58 #include <parse-util.h>
66 #define GSL_REAL(x) GNM_CRE(*(x))
67 #define GSL_IMAG(x) GNM_CIM(*(x))
69 /***********************************************************************
70 * Complex arithmetic operators
71 ***********************************************************************/
74 gsl_complex_mul_imag (gnm_complex
const *a
, gnm_float y
, gnm_complex
*res
)
76 *res
= GNM_CMAKE (-y
* GSL_IMAG (a
), y
* GSL_REAL (a
));
80 gsl_complex_inverse (gnm_complex
const *a
, gnm_complex
*res
)
82 gnm_float s
= 1.0 / GNM_CABS (*a
);
84 *res
= GNM_CMAKE ((GSL_REAL (a
) * s
) * s
, -(GSL_IMAG (a
) * s
) * s
);
87 /**********************************************************************
88 * Inverse Complex Trigonometric Functions
89 **********************************************************************/
92 gsl_complex_arcsin_real (gnm_float a
, gnm_complex
*res
)
94 if (gnm_abs (a
) <= 1.0) {
95 *res
= GNM_CMAKE (gnm_asin (a
), 0.0);
98 *res
= GNM_CMAKE (-M_PI_2gnum
, gnm_acosh (-a
));
100 *res
= GNM_CMAKE (M_PI_2gnum
, -gnm_acosh (a
));
106 gsl_complex_arcsin (gnm_complex
const *a
, gnm_complex
*res
)
107 { /* z = arcsin(a) */
108 gnm_float R
= GSL_REAL (a
), I
= GSL_IMAG (a
);
111 gsl_complex_arcsin_real (R
, res
);
113 gnm_float x
= gnm_abs (R
), y
= gnm_abs (I
);
114 gnm_float r
= gnm_hypot (x
+ 1, y
);
115 gnm_float s
= gnm_hypot (x
- 1, y
);
116 gnm_float A
= 0.5 * (r
+ s
);
118 gnm_float y2
= y
* y
;
120 gnm_float real
, imag
;
122 const gnm_float A_crossover
= 1.5, B_crossover
= 0.6417;
124 if (B
<= B_crossover
) {
128 gnm_float D
= 0.5 * (A
+ x
) *
129 (y2
/ (r
+ x
+ 1) + (s
+ (1 - x
)));
130 real
= gnm_atan (x
/ gnm_sqrt (D
));
132 gnm_float Apx
= A
+ x
;
133 gnm_float D
= 0.5 * (Apx
/ (r
+ x
+ 1)
134 + Apx
/ (s
+ (x
- 1)));
135 real
= gnm_atan (x
/ (y
* gnm_sqrt (D
)));
139 if (A
<= A_crossover
) {
143 Am1
= 0.5 * (y2
/ (r
+ (x
+ 1)) + y2
/
146 Am1
= 0.5 * (y2
/ (r
+ (x
+ 1)) +
150 imag
= gnm_log1p (Am1
+ gnm_sqrt (Am1
* (A
+ 1)));
152 imag
= gnm_log (A
+ gnm_sqrt (A
* A
- 1));
155 *res
= GNM_CMAKE ((R
>= 0) ? real
: -real
, (I
>= 0) ?
161 gsl_complex_arccos_real (gnm_float a
, gnm_complex
*res
)
162 { /* z = arccos(a) */
163 if (gnm_abs (a
) <= 1.0) {
164 *res
= GNM_CMAKE (gnm_acos (a
), 0);
167 *res
= GNM_CMAKE (M_PIgnum
, -gnm_acosh (-a
));
169 *res
= GNM_CMAKE (0, gnm_acosh (a
));
175 gsl_complex_arccos (gnm_complex
const *a
, gnm_complex
*res
)
176 { /* z = arccos(a) */
177 gnm_float R
= GSL_REAL (a
), I
= GSL_IMAG (a
);
180 gsl_complex_arccos_real (R
, res
);
182 gnm_float x
= gnm_abs (R
);
183 gnm_float y
= gnm_abs (I
);
184 gnm_float r
= gnm_hypot (x
+ 1, y
);
185 gnm_float s
= gnm_hypot (x
- 1, y
);
186 gnm_float A
= 0.5 * (r
+ s
);
188 gnm_float y2
= y
* y
;
190 gnm_float real
, imag
;
192 const gnm_float A_crossover
= 1.5;
193 const gnm_float B_crossover
= 0.6417;
195 if (B
<= B_crossover
) {
199 gnm_float D
= 0.5 * (A
+ x
) *
200 (y2
/ (r
+ x
+ 1) + (s
+ (1 - x
)));
201 real
= gnm_atan (gnm_sqrt (D
) / x
);
203 gnm_float Apx
= A
+ x
;
204 gnm_float D
= 0.5 * (Apx
/ (r
+ x
+ 1) + Apx
/
206 real
= gnm_atan ((y
* gnm_sqrt (D
)) / x
);
209 if (A
<= A_crossover
) {
213 Am1
= 0.5 * (y2
/ (r
+ (x
+ 1)) + y2
/
216 Am1
= 0.5 * (y2
/ (r
+ (x
+ 1)) +
220 imag
= gnm_log1p (Am1
+ gnm_sqrt (Am1
* (A
+ 1)));
222 imag
= gnm_log (A
+ gnm_sqrt (A
* A
- 1));
225 *res
= GNM_CMAKE ((R
>= 0) ? real
: M_PIgnum
- real
, (I
>= 0) ?
231 gsl_complex_arctan (gnm_complex
const *a
, gnm_complex
*res
)
232 { /* z = arctan(a) */
233 gnm_float R
= GSL_REAL (a
), I
= GSL_IMAG (a
);
236 *res
= GNM_CMAKE (gnm_atan (R
), 0);
238 /* FIXME: This is a naive implementation which does not fully
239 * take into account cancellation errors, overflow, underflow
240 * etc. It would benefit from the Hull et al treatment. */
242 gnm_float r
= gnm_hypot (R
, I
);
246 gnm_float u
= 2 * I
/ (1 + r
* r
);
248 /* FIXME: the following cross-over should be optimized but 0.1
249 * seems to work ok */
251 if (gnm_abs (u
) < 0.1) {
252 imag
= 0.25 * (gnm_log1p (u
) - gnm_log1p (-u
));
254 gnm_float A
= gnm_hypot (R
, I
+ 1);
255 gnm_float B
= gnm_hypot (R
, I
- 1);
256 imag
= 0.5 * gnm_log (A
/ B
);
260 *res
= GNM_CMAKE (M_PI_2gnum
, imag
);
262 *res
= GNM_CMAKE (-M_PI_2gnum
, imag
);
264 *res
= GNM_CMAKE (0, imag
);
267 *res
= GNM_CMAKE (0.5 * gnm_atan2 (2 * R
,
268 ((1 + r
) * (1 - r
))),
275 gsl_complex_arcsec (gnm_complex
const *a
, gnm_complex
*res
)
276 { /* z = arcsec(a) */
277 gsl_complex_inverse (a
, res
);
278 gsl_complex_arccos (res
, res
);
282 gsl_complex_arccsc (gnm_complex
const *a
, gnm_complex
*res
)
283 { /* z = arccsc(a) */
284 gsl_complex_inverse (a
, res
);
285 gsl_complex_arcsin (res
, res
);
289 gsl_complex_arccot (gnm_complex
const *a
, gnm_complex
*res
)
290 { /* z = arccot(a) */
291 if (GSL_REAL (a
) == 0.0 && GSL_IMAG (a
) == 0.0) {
292 *res
= GNM_CMAKE (M_PI_2gnum
, 0);
294 gsl_complex_inverse (a
, res
);
295 gsl_complex_arctan (res
, res
);
299 /**********************************************************************
300 * Complex Hyperbolic Functions
301 **********************************************************************/
304 gsl_complex_sinh (gnm_complex
const *a
, gnm_complex
*res
)
306 gnm_float R
= GSL_REAL (a
), I
= GSL_IMAG (a
);
308 *res
= GNM_CMAKE (gnm_sinh (R
) * gnm_cos (I
), gnm_cosh (R
) * gnm_sin (I
));
312 gsl_complex_cosh (gnm_complex
const *a
, gnm_complex
*res
)
314 gnm_float R
= GSL_REAL (a
), I
= GSL_IMAG (a
);
316 *res
= GNM_CMAKE (gnm_cosh (R
) * gnm_cos (I
), gnm_sinh (R
) * gnm_sin (I
));
320 gsl_complex_tanh (gnm_complex
const *a
, gnm_complex
*res
)
322 gnm_float R
= GSL_REAL (a
), I
= GSL_IMAG (a
);
324 if (gnm_abs (R
) < 1.0) {
326 gnm_pow (gnm_cos (I
), 2.0) +
327 gnm_pow (gnm_sinh (R
), 2.0);
329 *res
= GNM_CMAKE (gnm_sinh (R
) * gnm_cosh (R
) / D
,
330 0.5 * gnm_sin (2 * I
) / D
);
333 gnm_pow (gnm_cos (I
), 2.0) +
334 gnm_pow (gnm_sinh (R
), 2.0);
335 gnm_float F
= 1 + gnm_pow (gnm_cos (I
) / gnm_sinh (R
), 2.0);
337 *res
= GNM_CMAKE (1.0 / (gnm_tanh (R
) * F
),
338 0.5 * gnm_sin (2 * I
) / D
);
343 gsl_complex_sech (gnm_complex
const *a
, gnm_complex
*res
)
345 gsl_complex_cosh (a
, res
);
346 gsl_complex_inverse (res
, res
);
350 gsl_complex_csch (gnm_complex
const *a
, gnm_complex
*res
)
352 gsl_complex_sinh (a
, res
);
353 gsl_complex_inverse (res
, res
);
357 gsl_complex_coth (gnm_complex
const *a
, gnm_complex
*res
)
359 gsl_complex_tanh (a
, res
);
360 gsl_complex_inverse (res
, res
);
363 /**********************************************************************
364 * Inverse Complex Hyperbolic Functions
365 **********************************************************************/
368 gsl_complex_arcsinh (gnm_complex
const *a
, gnm_complex
*res
)
369 { /* z = arcsinh(a) */
370 gsl_complex_mul_imag (a
, 1.0, res
);
371 gsl_complex_arcsin (res
, res
);
372 gsl_complex_mul_imag (res
, -1.0, res
);
376 gsl_complex_arccosh (gnm_complex
const *a
, gnm_complex
*res
)
377 { /* z = arccosh(a) */
378 if (GSL_IMAG (a
) == 0 && GSL_REAL (a
) == 1)
381 gsl_complex_arccos (a
, res
);
382 gsl_complex_mul_imag (res
, GSL_IMAG (res
) > 0 ? -1.0 : 1.0, res
);
387 gsl_complex_arctanh_real (gnm_float a
, gnm_complex
*res
)
388 { /* z = arctanh(a) */
389 if (a
> -1.0 && a
< 1.0) {
390 *res
= GNM_CMAKE (gnm_atanh (a
), 0);
392 *res
= GNM_CMAKE (gnm_acoth (a
),
393 (a
< 0) ? M_PI_2gnum
: -M_PI_2gnum
);
398 gsl_complex_arctanh (gnm_complex
const *a
, gnm_complex
*res
)
399 { /* z = arctanh(a) */
400 if (GSL_IMAG (a
) == 0.0) {
401 gsl_complex_arctanh_real (GSL_REAL (a
), res
);
403 gsl_complex_mul_imag (a
, 1.0, res
);
404 gsl_complex_arctan (res
, res
);
405 gsl_complex_mul_imag (res
, -1.0, res
);
410 gsl_complex_arcsech (gnm_complex
const *a
, gnm_complex
*res
)
411 { /* z = arcsech(a); */
412 gsl_complex_inverse (a
, res
);
413 gsl_complex_arccosh (res
, res
);
417 gsl_complex_arccsch (gnm_complex
const *a
, gnm_complex
*res
)
418 { /* z = arccsch(a); */
419 gsl_complex_inverse (a
, res
);
420 gsl_complex_arcsinh (res
, res
);
424 gsl_complex_arccoth (gnm_complex
const *a
, gnm_complex
*res
)
425 { /* z = arccoth(a); */
426 gsl_complex_inverse (a
, res
);
427 gsl_complex_arctanh (res
, res
);