View Javadoc

1   /*
2    * Copyright 2003-2005 The Apache Software Foundation.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
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 &plusmn; INFINITY i) = 1 &#x2213; INFINITY i
141      * cos(&plusmn;INFINITY + i) = NaN + NaN i
142      * cos(&plusmn;INFINITY &plusmn; 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 &plusmn; INFINITY i) = NaN + NaN i
179      * cosh(&plusmn;INFINITY + i) = INFINITY &plusmn; INFINITY i
180      * cosh(&plusmn;INFINITY &plusmn; 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 &plusmn; INFINITY i) = NaN + NaN i
218      * exp(INFINITY + i) = INFINITY + INFINITY i
219      * exp(-INFINITY + i) = 0 + 0i
220      * exp(&plusmn;INFINITY &plusmn; 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 &plusmn; INFINITY i) = INFINITY &plusmn; (&pi;/2)i
256      * log(INFINITY + i) = INFINITY + 0i
257      * log(-INFINITY + i) = INFINITY + &pi;i
258      * log(INFINITY &plusmn; INFINITY i) = INFINITY &plusmn; (&pi;/4)i
259      * log(-INFINITY &plusmn; INFINITY i) = INFINITY &plusmn; (3&pi;/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&middot;e<sup>i&middot;theta</sup></code>,
280      * computed as <code>r&middot;cos(theta) + r&middot;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, &pi;/4) = INFINITY + INFINITY i
291      * polar2Complex(INFINITY, 0) = INFINITY + NaN i
292      * polar2Complex(INFINITY, -&pi;/4) = INFINITY - INFINITY i
293      * polar2Complex(INFINITY, 5&pi;/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&middot;e<sup>i&middot;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&middot;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 &plusmn; INFINITY i) = 1 &plusmn; INFINITY i
350      * sin(&plusmn;INFINITY + i) = NaN + NaN i
351      * sin(&plusmn;INFINITY &plusmn; 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 &plusmn; INFINITY i) = NaN + NaN i
389      * sinh(&plusmn;INFINITY + i) = &plusmn; INFINITY + INFINITY i
390      * sinh(&plusmn;INFINITY &plusmn; 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 &#8805; 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 &plusmn; INFINITY i) = INFINITY + NaN i
432      * sqrt(INFINITY + i) = INFINITY + 0i
433      * sqrt(-INFINITY + i) = 0 + INFINITY i
434      * sqrt(INFINITY &plusmn; INFINITY i) = INFINITY + NaN i
435      * sqrt(-INFINITY &plusmn; INFINITY i) = NaN &plusmn; 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 &plusmn; INFINITY i) = 0 + NaN i
501      * tan(&plusmn;INFINITY + i) = NaN + NaN i
502      * tan(&plusmn;INFINITY &plusmn; INFINITY i) = NaN + NaN i
503      * tan(&plusmn;&pi/2 + 0 i) = &plusmn;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 &plusmn; INFINITY i) = NaN + NaN i
540      * tanh(&plusmn;INFINITY + i) = NaN + 0 i
541      * tanh(&plusmn;INFINITY &plusmn; 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 }