summaryrefslogtreecommitdiff
path: root/src/c/elementaryFunctions/atan/zatans.c
blob: 4b8e9640d68db66c59f9061d578bb2b5c2802f6a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
/*
 *  Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
 *  Copyright (C) 2006-2008 - INRIA - Bruno JOFRET
 *  Copyright (C) Bruno Pincon
 *
 *  This file must be used under the terms of the CeCILL.
 *  This source file is licensed as described in the file COPYING, which
 *  you should have received as part of this distribution.  The terms
 *  are also available at
 *  http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt
 *
 */

/*
  PURPOSE
  watan compute the arctangent of a complex number
  y = yr + i yi = atan(x), x = xr + i xi

  CALLING LIST / PARAMETERS
  subroutine watan(xr,xi,yr,yi)
  double precision xr,xi,yr,yi

  xr,xi: real and imaginary parts of the complex number
  yr,yi: real and imaginary parts of the result
  yr,yi may have the same memory cases than xr et xi

  COPYRIGHT (C) 2001 Bruno Pincon and Lydia van Dijk
  Written by Bruno Pincon <Bruno.Pincon@iecn.u-nancy.fr> so
  as to get more precision.  Also to fix the
  behavior at the singular points and at the branch cuts.
  Polished by Lydia van Dijk
  <lvandijk@hammersmith-consulting.com>

  CHANGES : - (Bruno on 2001 May 22) for ysptrk use a
  minimax polynome to enlarge the special
  evaluation zone |s| < SLIM. Also rename
  this function as lnp1m1.
  - (Bruno on 2001 June 7) better handling
  of spurious over/underflow ; remove
  the call to pythag ; better accuracy
  in the real part for z near +-i

  EXTERNALS FUNCTIONS
  dlamch
  lnp1m1  (at the end of this file)

  ALGORITHM : noting z = a + i*b, we have:
  Z = yr + yi*b = arctan(z) = (i/2) * log( (i+z)/(i-z) )

  This function has two branch points at +i and -i and the
  chosen  branch cuts are the two half-straight lines
  D1 = [i, i*oo) and D2 = (-i*oo, i].  The function is then
  analytic in C \ (D1 U D2)).

  From the definition it follows that:

  yr = 0.5 Arg ( (i+z)/(i-z) )                   (1)
  yi = 0.5 log (|(i+z)/(i-z)|)                   (2)

  so lim (z -> +- i) yr = undefined (and Nan is logical)
  lim (z -> +i)   yi = +oo
  lim (z -> -i)   yi = -oo

  The real part of arctan(z) is discontinuous across D1 and D2
  and we impose the following definitions:
  if imag(z) > 1 then
  Arg(arctan(z)) =  pi/2 (=lim real(z) -> 0+)
  if imag(z) < 1 then
  Arg(arctan(z)) = -pi/2 (=lim real(z) -> 0-)


  Basic evaluation: if we write (i+z)/(i-z) using
  z = a + i*b, we get:

  i+z    1-(a**2+b**2) + i*(2a)
  --- =  ----------------------
  i-z       a**2 + (1-b)**2

  then, with r2 = |z|^2 = a**2 + b**2 :

  yr = 0.5 * Arg(1-r2 + (2*a)*i)
  = 0.5 * atan2(2a, (1-r2))                      (3)

  This formula is changed when r2 > RMAX (max pos float)
  and also when |1-r2| and |a| are near 0 (see comments
  in the code).

  After some math:

  yi = 0.25 * log( (a**2 + (b + 1)**2) /
  (a**2 + (b - 1)**2) )            (4)

  Evaluation for "big" |z|
  ------------------------

  If |z| is "big", the direct evaluation of yi by (4) may
  suffer of innaccuracies and of spurious overflow.  Noting
  that  s = 2 b / (1 + |z|**2), we have:

  yi = 0.25 log ( (1 + s)/(1 - s) )                 (5)

  3        5
  yi = 0.25*( 2 * ( s + 1/3 s  + 1/5 s  + ... ))

  yi = 0.25 * lnp1m1(s)    if  |s| < SLIM

  So if |s| is less than SLIM we switch to a special
  evaluation done by the function lnp1m1. The
  threshold value SLIM is choosen by experiment
  (with the Pari-gp software). For |s|
  "very small" we used a truncated taylor dvp,
  else a minimax polynome (see lnp1m1).

  To avoid spurious overflows (which result in spurious
  underflows for s) in computing s with s= 2 b / (1 + |z|**2)
  when |z|^2 > RMAX (max positive float) we use :

  s = 2d0 / ( (a/b)*a + b )

  but if |b| = Inf  this formula leads to NaN when
  |a| is also Inf. As we have :

  |s| <= 2 / |b|

  we impose simply : s = 0  when |b| = Inf

  Evaluation for z very near to i or -i:
  --------------------------------------
  Floating point numbers of the form a+i or a-i with 0 <
  a**2 < tiny (approximately 1d-308) may lead to underflow
  (i.e., a**2 = 0) and the logarithm will break formula (4).
  So we switch to the following formulas:

  If b = +-1 and |a| < sqrt(tiny) approximately 1d-150 (say)
  then (by using that a**2 + 4 = 4 in machine for such a):

  yi = 0.5 * log( 2/|a| )   for b=1

  yi = 0.5 * log( |a|/2 )   for b=-1

  finally: yi = 0.5 * sign(b) * log( 2/|a| )
  yi = 0.5 * sign(b) * (log(2) - log(|a|)) (6)

  The last trick is to avoid overflow for |a|=tiny!  In fact
  this formula may be used until a**2 + 4 = 4 so that the
  threshold value may be larger.
*/

#include <math.h>
#include "lapack.h"
#include "atan.h"
#include "abs.h"
#include "lnp1m1.h"

#define _sign(a, b) b >=0 ? a : -a

doubleComplex		zatans(doubleComplex z) {
  static double sSlim	= 0.2;
  static double sAlim	= 1E-150;
  static double sTol	= 0.3;
  static double sLn2	= 0.6931471805599453094172321;

  double RMax				= getOverflowThreshold();
  double Pi_2				= 2.0 * datans(1);

  double _inReal = zreals(z);
  double _inImg = zimags(z);
  double _outReal = 0;
  double _outImg = 0;

  /* Temporary variables */
  double R2 = 0;
  double S = 0;


  if(_inImg == 0)
    {
      _outReal	= datans(_inReal);
      _outImg	= 0;
    }
  else
    {
      R2 = _inReal * _inReal + _inImg * _inImg; /* Oo */
      if(R2 > RMax)
	{
	  if( dabss(_inImg) > RMax)
	    S = 0;
	  else
	    S = 1 / (((0.5 * _inReal) / _inImg) * _inReal + 0.5 * _inImg );
	}
      else
	S = (2 * _inImg) / (1+R2);

      if(dabss(S) < sSlim)
	{
	  /*
	    s is small: |s| < SLIM  <=>  |z| outside the following disks:
	    D+ = D(center = [0;  1/slim], radius = sqrt(1/slim**2 - 1)) if b > 0
	    D- = D(center = [0; -1/slim], radius = sqrt(1/slim**2 - 1)) if b < 0
	    use the special evaluation of log((1+s)/(1-s)) (5)
	  */
	  _outImg = dlnp1m1s(S) * 0.25;
	}
      else
	{
	  if(dabss(S) == 1 && dabss(_inReal) <= sAlim)
	    {
	      /* |s| >= SLIM  => |z| is inside D+ or D- */
	      _outImg = _sign(0.5,_inImg) * ( sLn2 - log(dabss(_inReal)));
	    }
	  else
	    {
	      _outImg = 0.25 * log((pow(_inReal,2) + pow((_inImg + 1),2)) / (pow(_inReal,2) + pow((_inImg - 1),2)));
	    }
	}
      if(_inReal == 0)
	{/* z is purely imaginary */
	  if( dabss(_inImg) > 1)
	    {/* got sign(b) * pi/2 */
	      _outReal = _sign(1, _inImg) * Pi_2;
	    }
	  else if( dabss(_inImg) == 1)
	    {/* got a Nan with 0/0 */
	      _outReal = (_inReal - _inReal) / (_inReal - _inReal); /* Oo */
	    }
	  else
	    _outReal = 0;
	}
      else if(R2 > RMax)
	{/* _outImg is necessarily very near sign(a)* pi/2 */
	  _outReal = _sign(1, _inReal) * Pi_2;
	}
      else if(dabss(1 - R2) + dabss(_inReal) <= sTol)
	{/* |b| is very near 1 (and a is near 0)  some cancellation occur in the (next) generic formula */
	  _outReal = 0.5 * atan2(2 * _inReal, (1-_inImg) * (1 + _inImg) - pow(_inReal,2));
	}
      else
	_outReal = 0.5 * atan2(2 * _inReal, 1 - R2);
    }

  return DoubleComplex(_outReal, _outImg);
}