1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.math.complex;
18
19 import org.apache.commons.math.util.MathUtils;
20
21 /**
22 * Static implementations of common
23 * {@link org.apache.commons.math.complex.Complex}-valued functions. Included
24 * are trigonometric, exponential, log, power and square root functions.
25 *<p>
26 * Reference:
27 * <ul>
28 * <li><a href="http://myweb.lmu.edu/dmsmith/ZMLIB.pdf">
29 * Multiple Precision Complex Arithmetic and Functions</a></li>
30 * </ul>
31 * See individual method javadocs for the computational formulas used.
32 * In general, NaN values in either real or imaginary parts of input arguments
33 * result in {@link Complex#NaN} returned. Otherwise, infinite or NaN values
34 * are returned as they arise in computing the real functions specified in the
35 * computational formulas. Null arguments result in NullPointerExceptions.
36 *
37 * @version $Revision: 349387 $ $Date: 2005-11-27 23:15:46 -0700 (Sun, 27 Nov 2005) $
38 */
39 public class ComplexUtils {
40
41 /**
42 * Default constructor.
43 */
44 private ComplexUtils() {
45 super();
46 }
47
48 /**
49 * Compute the
50 * <a href="http://mathworld.wolfram.com/InverseCosine.html" TARGET="_top">
51 * inverse cosine</a> for the given complex argument.
52 * <p>
53 * Implements the formula: <pre>
54 * <code> acos(z) = -i (log(z + i (sqrt(1 - z<sup>2</sup>))))</code></pre>
55 * <p>
56 * Returns {@link Complex#NaN} if either real or imaginary part of the
57 * input argument is <code>NaN</code> or infinite.
58 *
59 * @param z the value whose inverse cosine is to be returned
60 * @return the inverse cosine of <code>z</code>
61 * @throws NullPointerException if <code>z</code> is null
62 */
63 public static Complex acos(Complex z) {
64 if (z.isNaN()) {
65 return Complex.NaN;
66 }
67
68 return Complex.I.negate().multiply(log(z.add(
69 Complex.I.multiply(sqrt1z(z)))));
70 }
71
72 /**
73 * Compute the
74 * <a href="http://mathworld.wolfram.com/InverseSine.html" TARGET="_top">
75 * inverse sine</a> for the given complex argument.
76 * <p>
77 * Implements the formula: <pre>
78 * <code> asin(z) = -i (log(sqrt(1 - z<sup>2</sup>) + iz)) </code></pre>
79 * <p>
80 * Returns {@link Complex#NaN} if either real or imaginary part of the
81 * input argument is <code>NaN</code> or infinite.
82 *
83 * @param z the value whose inverse sine is to be returned.
84 * @return the inverse sine of <code>z</code>.
85 * @throws NullPointerException if <code>z</code> is null
86 */
87 public static Complex asin(Complex z) {
88 if (z.isNaN()) {
89 return Complex.NaN;
90 }
91
92 return Complex.I.negate().multiply(log(sqrt1z(z).add(
93 Complex.I.multiply(z))));
94 }
95
96 /**
97 * Compute the
98 * <a href="http://mathworld.wolfram.com/InverseTangent.html" TARGET="_top">
99 * inverse tangent</a> for the given complex argument.
100 * <p>
101 * Implements the formula: <pre>
102 * <code> atan(z) = (i/2) log((i + z)/(i - z)) </code></pre>
103 * <p>
104 * Returns {@link Complex#NaN} if either real or imaginary part of the
105 * input argument is <code>NaN</code> or infinite.
106 *
107 * @param z the value whose inverse tangent is to be returned
108 * @return the inverse tangent of <code>z</code>
109 * @throws NullPointerException if <code>z</code> is null
110 */
111 public static Complex atan(Complex z) {
112 if (z.isNaN()) {
113 return Complex.NaN;
114 }
115
116 return Complex.I.multiply(
117 log(Complex.I.add(z).divide(Complex.I.subtract(z))))
118 .divide(new Complex(2.0, 0.0));
119 }
120
121 /**
122 * Compute the
123 * <a href="http://mathworld.wolfram.com/Cosine.html" TARGET="_top">
124 * cosine</a>
125 * for the given complex argument.
126 * <p>
127 * Implements the formula: <pre>
128 * <code> cos(a + bi) = cos(a)cosh(b) - sin(a)sinh(b)i</code></pre>
129 * where the (real) functions on the right-hand side are
130 * {@link java.lang.Math#sin}, {@link java.lang.Math#cos},
131 * {@link MathUtils#cosh} and {@link MathUtils#sinh}.
132 * <p>
133 * Returns {@link Complex#NaN} if either real or imaginary part of the
134 * input argument is <code>NaN</code>.
135 * <p>
136 * Infinite values in real or imaginary parts of the input may result in
137 * infinite or NaN values returned in parts of the result.<pre>
138 * Examples:
139 * <code>
140 * cos(1 ± INFINITY i) = 1 ∓ INFINITY i
141 * cos(±INFINITY + i) = NaN + NaN i
142 * cos(±INFINITY ± INFINITY i) = NaN + NaN i</code></pre>
143 *
144 * @param z the value whose cosine is to be returned
145 * @return the cosine of <code>z</code>
146 * @throws NullPointerException if <code>z</code> is null
147 */
148 public static Complex cos(Complex z) {
149 if (z.isNaN()) {
150 return Complex.NaN;
151 }
152
153 double a = z.getReal();
154 double b = z.getImaginary();
155
156 return new Complex(Math.cos(a) * MathUtils.cosh(b),
157 -Math.sin(a) * MathUtils.sinh(b));
158 }
159
160 /**
161 * Compute the
162 * <a href="http://mathworld.wolfram.com/HyperbolicCosine.html" TARGET="_top">
163 * hyperbolic cosine</a> for the given complex argument.
164 * <p>
165 * Implements the formula: <pre>
166 * <code> cosh(a + bi) = cosh(a)cos(b) + sinh(a)sin(b)i</code></pre>
167 * where the (real) functions on the right-hand side are
168 * {@link java.lang.Math#sin}, {@link java.lang.Math#cos},
169 * {@link MathUtils#cosh} and {@link MathUtils#sinh}.
170 * <p>
171 * Returns {@link Complex#NaN} if either real or imaginary part of the
172 * input argument is <code>NaN</code>.
173 * <p>
174 * Infinite values in real or imaginary parts of the input may result in
175 * infinite or NaN values returned in parts of the result.<pre>
176 * Examples:
177 * <code>
178 * cosh(1 ± INFINITY i) = NaN + NaN i
179 * cosh(±INFINITY + i) = INFINITY ± INFINITY i
180 * cosh(±INFINITY ± INFINITY i) = NaN + NaN i</code></pre>
181 * <p>
182 * Throws <code>NullPointerException</code> if z is null.
183 *
184 * @param z the value whose hyperbolic cosine is to be returned.
185 * @return the hyperbolic cosine of <code>z</code>.
186 */
187 public static Complex cosh(Complex z) {
188 if (z.isNaN()) {
189 return Complex.NaN;
190 }
191
192 double a = z.getReal();
193 double b = z.getImaginary();
194
195 return new Complex(MathUtils.cosh(a) * Math.cos(b),
196 MathUtils.sinh(a) * Math.sin(b));
197 }
198
199 /**
200 * Compute the
201 * <a href="http://mathworld.wolfram.com/ExponentialFunction.html" TARGET="_top">
202 * exponential function</a> for the given complex argument.
203 * <p>
204 * Implements the formula: <pre>
205 * <code> exp(a + bi) = exp(a)cos(b) + exp(a)sin(b)i</code></pre>
206 * where the (real) functions on the right-hand side are
207 * {@link java.lang.Math#exp}, {@link java.lang.Math#cos}, and
208 * {@link java.lang.Math#sin}.
209 * <p>
210 * Returns {@link Complex#NaN} if either real or imaginary part of the
211 * input argument is <code>NaN</code>.
212 * <p>
213 * Infinite values in real or imaginary parts of the input may result in
214 * infinite or NaN values returned in parts of the result.<pre>
215 * Examples:
216 * <code>
217 * exp(1 ± INFINITY i) = NaN + NaN i
218 * exp(INFINITY + i) = INFINITY + INFINITY i
219 * exp(-INFINITY + i) = 0 + 0i
220 * exp(±INFINITY ± INFINITY i) = NaN + NaN i</code></pre>
221 * <p>
222 * Throws <code>NullPointerException</code> if z is null.
223 *
224 * @param z the value
225 * @return <i>e</i><sup><code>z</code></sup>
226 */
227 public static Complex exp(Complex z) {
228 if (z.isNaN()) {
229 return Complex.NaN;
230 }
231
232 double b = z.getImaginary();
233 double expA = Math.exp(z.getReal());
234 return new Complex(expA * Math.cos(b), expA * Math.sin(b));
235 }
236
237 /**
238 * Compute the
239 * <a href="http://mathworld.wolfram.com/NaturalLogarithm.html" TARGET="_top">
240 * natural logarithm</a> for the given complex argument.
241 * <p>
242 * Implements the formula: <pre>
243 * <code> log(a + bi) = ln(|a + bi|) + arg(a + bi)i</code></pre>
244 * where ln on the right hand side is {@link java.lang.Math#log},
245 * <code>|a + bi|</code> is the modulus, {@link Complex#abs}, and
246 * <code>arg(a + bi) = {@link java.lang.Math#atan2}(b, a)</code>
247 * <p>
248 * Returns {@link Complex#NaN} if either real or imaginary part of the
249 * input argument is <code>NaN</code>.
250 * <p>
251 * Infinite (or critical) values in real or imaginary parts of the input may
252 * result in infinite or NaN values returned in parts of the result.<pre>
253 * Examples:
254 * <code>
255 * log(1 ± INFINITY i) = INFINITY ± (π/2)i
256 * log(INFINITY + i) = INFINITY + 0i
257 * log(-INFINITY + i) = INFINITY + πi
258 * log(INFINITY ± INFINITY i) = INFINITY ± (π/4)i
259 * log(-INFINITY ± INFINITY i) = INFINITY ± (3π/4)i
260 * log(0 + 0i) = -INFINITY + 0i
261 * </code></pre>
262 * Throws <code>NullPointerException</code> if z is null.
263 *
264 * @param z the value.
265 * @return ln <code>z</code>.
266 */
267 public static Complex log(Complex z) {
268 if (z.isNaN()) {
269 return Complex.NaN;
270 }
271
272 return new Complex(Math.log(z.abs()),
273 Math.atan2(z.getImaginary(), z.getReal()));
274 }
275
276 /**
277 * Creates a complex number from the given polar representation.
278 * <p>
279 * The value returned is <code>r·e<sup>i·theta</sup></code>,
280 * computed as <code>r·cos(theta) + r·sin(theta)i</code>
281 * <p>
282 * If either <code>r</code> or <code>theta</code> is NaN, or
283 * <code>theta</code> is infinite, {@link Complex#NaN} is returned.
284 * <p>
285 * If <code>r</code> is infinite and <code>theta</code> is finite,
286 * infinite or NaN values may be returned in parts of the result, following
287 * the rules for double arithmetic.<pre>
288 * Examples:
289 * <code>
290 * polar2Complex(INFINITY, π/4) = INFINITY + INFINITY i
291 * polar2Complex(INFINITY, 0) = INFINITY + NaN i
292 * polar2Complex(INFINITY, -π/4) = INFINITY - INFINITY i
293 * polar2Complex(INFINITY, 5π/4) = -INFINITY - INFINITY i </code></pre>
294 *
295 * @param r the modulus of the complex number to create
296 * @param theta the argument of the complex number to create
297 * @return <code>r·e<sup>i·theta</sup></code>
298 * @throws IllegalArgumentException if r is negative
299 * @since 1.1
300 */
301 public static Complex polar2Complex(double r, double theta) {
302 if (r < 0) {
303 throw new IllegalArgumentException
304 ("Complex modulus must not be negative");
305 }
306 return new Complex(r * Math.cos(theta), r * Math.sin(theta));
307 }
308
309 /**
310 * Returns of value of <code>y</code> raised to the power of <code>x</code>.
311 * <p>
312 * Implements the formula: <pre>
313 * <code> y<sup>x</sup> = exp(x·log(y))</code></pre>
314 * where <code>exp</code> and <code>log</code> are {@link #exp} and
315 * {@link #log}, respectively.
316 * <p>
317 * Returns {@link Complex#NaN} if either real or imaginary part of the
318 * input argument is <code>NaN</code> or infinite, or if <code>y</code>
319 * equals {@link Complex#ZERO}.
320 *
321 * @param y the base.
322 * @param x the exponent.
323 * @return <code>y</code><sup><code>x</code></sup>
324 * @throws NullPointerException if either x or y is null
325 */
326 public static Complex pow(Complex y, Complex x) {
327 return exp(x.multiply(log(y)));
328 }
329
330 /**
331 * Compute the
332 * <a href="http://mathworld.wolfram.com/Sine.html" TARGET="_top">
333 * sine</a>
334 * for the given complex argument.
335 * <p>
336 * Implements the formula: <pre>
337 * <code> sin(a + bi) = sin(a)cosh(b) - cos(a)sinh(b)i</code></pre>
338 * where the (real) functions on the right-hand side are
339 * {@link java.lang.Math#sin}, {@link java.lang.Math#cos},
340 * {@link MathUtils#cosh} and {@link MathUtils#sinh}.
341 * <p>
342 * Returns {@link Complex#NaN} if either real or imaginary part of the
343 * input argument is <code>NaN</code>.
344 * <p>
345 * Infinite values in real or imaginary parts of the input may result in
346 * infinite or NaN values returned in parts of the result.<pre>
347 * Examples:
348 * <code>
349 * sin(1 ± INFINITY i) = 1 ± INFINITY i
350 * sin(±INFINITY + i) = NaN + NaN i
351 * sin(±INFINITY ± INFINITY i) = NaN + NaN i</code></pre>
352 *
353 * Throws <code>NullPointerException</code> if z is null.
354 *
355 * @param z the value whose sine is to be returned.
356 * @return the sine of <code>z</code>.
357 */
358 public static Complex sin(Complex z) {
359 if (z.isNaN()) {
360 return Complex.NaN;
361 }
362
363 double a = z.getReal();
364 double b = z.getImaginary();
365
366 return new Complex(Math.sin(a) * MathUtils.cosh(b),
367 Math.cos(a) * MathUtils.sinh(b));
368 }
369
370 /**
371 * Compute the
372 * <a href="http://mathworld.wolfram.com/HyperbolicSine.html" TARGET="_top">
373 * hyperbolic sine</a> for the given complex argument.
374 * <p>
375 * Implements the formula: <pre>
376 * <code> sinh(a + bi) = sinh(a)cos(b)) + cosh(a)sin(b)i</code></pre>
377 * where the (real) functions on the right-hand side are
378 * {@link java.lang.Math#sin}, {@link java.lang.Math#cos},
379 * {@link MathUtils#cosh} and {@link MathUtils#sinh}.
380 * <p>
381 * Returns {@link Complex#NaN} if either real or imaginary part of the
382 * input argument is <code>NaN</code>.
383 * <p>
384 * Infinite values in real or imaginary parts of the input may result in
385 * infinite or NaN values returned in parts of the result.<pre>
386 * Examples:
387 * <code>
388 * sinh(1 ± INFINITY i) = NaN + NaN i
389 * sinh(±INFINITY + i) = ± INFINITY + INFINITY i
390 * sinh(±INFINITY ± INFINITY i) = NaN + NaN i</code></pre
391 *
392 * @param z the value whose hyperbolic sine is to be returned
393 * @return the hyperbolic sine of <code>z</code>
394 * @throws NullPointerException if <code>z</code> is null
395 */
396 public static Complex sinh(Complex z) {
397 if (z.isNaN()) {
398 return Complex.NaN;
399 }
400
401 double a = z.getReal();
402 double b = z.getImaginary();
403
404 return new Complex(MathUtils.sinh(a) * Math.cos(b),
405 MathUtils.cosh(a) * Math.sin(b));
406 }
407
408 /**
409 * Compute the
410 * <a href="http://mathworld.wolfram.com/SquareRoot.html" TARGET="_top">
411 * square root</a> for the given complex argument.
412 * <p>
413 * Implements the following algorithm to compute <code>sqrt(a + bi)</code>:
414 * <ol><li>Let <code>t = sqrt((|a| + |a + bi|) / 2)</code></li>
415 * <li><pre>if <code> a ≥ 0</code> return <code>t + (b/2t)i</code>
416 * else return <code>|b|/2t + sign(b)t i </code></pre></li>
417 * </ol>
418 * where <ul>
419 * <li><code>|a| = {@link Math#abs}(a)</code></li>
420 * <li><code>|a + bi| = {@link Complex#abs}(a + bi) </code></li>
421 * <li><code>sign(b) = {@link MathUtils#indicator}(b) </code>
422 * </ul>
423 * <p>
424 * Returns {@link Complex#NaN} if either real or imaginary part of the
425 * input argument is <code>NaN</code>.
426 * <p>
427 * Infinite values in real or imaginary parts of the input may result in
428 * infinite or NaN values returned in parts of the result.<pre>
429 * Examples:
430 * <code>
431 * sqrt(1 ± INFINITY i) = INFINITY + NaN i
432 * sqrt(INFINITY + i) = INFINITY + 0i
433 * sqrt(-INFINITY + i) = 0 + INFINITY i
434 * sqrt(INFINITY ± INFINITY i) = INFINITY + NaN i
435 * sqrt(-INFINITY ± INFINITY i) = NaN ± INFINITY i
436 * </code></pre>
437 *
438 * @param z the value whose square root is to be returned
439 * @return the square root of <code>z</code>
440 * @throws NullPointerException if <code>z</code> is null
441 */
442 public static Complex sqrt(Complex z) {
443 if (z.isNaN()) {
444 return Complex.NaN;
445 }
446
447 double a = z.getReal();
448 double b = z.getImaginary();
449
450 double t = Math.sqrt((Math.abs(a) + z.abs()) / 2.0);
451 if (a >= 0.0) {
452 return new Complex(t, b / (2.0 * t));
453 } else {
454 return new Complex(Math.abs(b) / (2.0 * t),
455 MathUtils.indicator(b) * t);
456 }
457 }
458
459 /**
460 * Compute the
461 * <a href="http://mathworld.wolfram.com/SquareRoot.html" TARGET="_top">
462 * square root</a> of 1 - <code>z</code><sup>2</sup> for the given complex
463 * argument.
464 * <p>
465 * Computes the result directly as
466 * <code>sqrt(Complex.ONE.subtract(z.multiply(z)))</code>.
467 * <p>
468 * Returns {@link Complex#NaN} if either real or imaginary part of the
469 * input argument is <code>NaN</code>.
470 * <p>
471 * Infinite values in real or imaginary parts of the input may result in
472 * infinite or NaN values returned in parts of the result.
473 *
474 * @param z the value
475 * @return the square root of 1 - <code>z</code><sup>2</sup>
476 * @throws NullPointerException if <code>z</code> is null
477 */
478 public static Complex sqrt1z(Complex z) {
479 return sqrt(Complex.ONE.subtract(z.multiply(z)));
480 }
481
482 /**
483 * Compute the
484 * <a href="http://mathworld.wolfram.com/Tangent.html" TARGET="_top">
485 * tangent</a> for the given complex argument.
486 * <p>
487 * Implements the formula: <pre>
488 * <code>tan(a + bi) = sin(2a)/(cos(2a)+cosh(2b)) + [sinh(2b)/(cos(2a)+cosh(2b))]i</code></pre>
489 * where the (real) functions on the right-hand side are
490 * {@link java.lang.Math#sin}, {@link java.lang.Math#cos},
491 * {@link MathUtils#cosh} and {@link MathUtils#sinh}.
492 * <p>
493 * Returns {@link Complex#NaN} if either real or imaginary part of the
494 * input argument is <code>NaN</code>.
495 * <p>
496 * Infinite (or critical) values in real or imaginary parts of the input may
497 * result in infinite or NaN values returned in parts of the result.<pre>
498 * Examples:
499 * <code>
500 * tan(1 ± INFINITY i) = 0 + NaN i
501 * tan(±INFINITY + i) = NaN + NaN i
502 * tan(±INFINITY ± INFINITY i) = NaN + NaN i
503 * tan(±&pi/2 + 0 i) = ±INFINITY + NaN i</code></pre>
504 *
505 * @param z the value whose tangent is to be returned
506 * @return the tangent of <code>z</code>
507 * @throws NullPointerException if <code>z</code> is null
508 */
509 public static Complex tan(Complex z) {
510 if (z.isNaN()) {
511 return Complex.NaN;
512 }
513
514 double a2 = 2.0 * z.getReal();
515 double b2 = 2.0 * z.getImaginary();
516 double d = Math.cos(a2) + MathUtils.cosh(b2);
517
518 return new Complex(Math.sin(a2) / d, MathUtils.sinh(b2) / d);
519 }
520
521 /**
522 * Compute the
523 * <a href="http://mathworld.wolfram.com/HyperbolicTangent.html" TARGET="_top">
524 * hyperbolic tangent</a> for the given complex argument.
525 * <p>
526 * Implements the formula: <pre>
527 * <code>tan(a + bi) = sinh(2a)/(cosh(2a)+cos(2b)) + [sin(2b)/(cosh(2a)+cos(2b))]i</code></pre>
528 * where the (real) functions on the right-hand side are
529 * {@link java.lang.Math#sin}, {@link java.lang.Math#cos},
530 * {@link MathUtils#cosh} and {@link MathUtils#sinh}.
531 * <p>
532 * Returns {@link Complex#NaN} if either real or imaginary part of the
533 * input argument is <code>NaN</code>.
534 * <p>
535 * Infinite values in real or imaginary parts of the input may result in
536 * infinite or NaN values returned in parts of the result.<pre>
537 * Examples:
538 * <code>
539 * tanh(1 ± INFINITY i) = NaN + NaN i
540 * tanh(±INFINITY + i) = NaN + 0 i
541 * tanh(±INFINITY ± INFINITY i) = NaN + NaN i
542 * tanh(0 + (&pi/2)i) = NaN + INFINITY i</code></pre>
543 *
544 * @param z the value whose hyperbolic tangent is to be returned
545 * @return the hyperbolic tangent of <code>z</code>
546 * @throws NullPointerException if <code>z</code> is null
547 */
548 public static Complex tanh(Complex z) {
549 if (z.isNaN()) {
550 return Complex.NaN;
551 }
552
553 double a2 = 2.0 * z.getReal();
554 double b2 = 2.0 * z.getImaginary();
555 double d = MathUtils.cosh(a2) + Math.cos(b2);
556
557 return new Complex(MathUtils.sinh(a2) / d, Math.sin(b2) / d);
558 }
559 }