001: /*
002: * Copyright 2003-2005 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.apache.commons.math.complex;
018:
019: import org.apache.commons.math.util.MathUtils;
020:
021: /**
022: * Static implementations of common
023: * {@link org.apache.commons.math.complex.Complex}-valued functions. Included
024: * are trigonometric, exponential, log, power and square root functions.
025: *<p>
026: * Reference:
027: * <ul>
028: * <li><a href="http://myweb.lmu.edu/dmsmith/ZMLIB.pdf">
029: * Multiple Precision Complex Arithmetic and Functions</a></li>
030: * </ul>
031: * See individual method javadocs for the computational formulas used.
032: * In general, NaN values in either real or imaginary parts of input arguments
033: * result in {@link Complex#NaN} returned. Otherwise, infinite or NaN values
034: * are returned as they arise in computing the real functions specified in the
035: * computational formulas. Null arguments result in NullPointerExceptions.
036: *
037: * @version $Revision: 349387 $ $Date: 2005-11-27 23:15:46 -0700 (Sun, 27 Nov 2005) $
038: */
039: public class ComplexUtils {
040:
041: /**
042: * Default constructor.
043: */
044: private ComplexUtils() {
045: super ();
046: }
047:
048: /**
049: * Compute the
050: * <a href="http://mathworld.wolfram.com/InverseCosine.html" TARGET="_top">
051: * inverse cosine</a> for the given complex argument.
052: * <p>
053: * Implements the formula: <pre>
054: * <code> acos(z) = -i (log(z + i (sqrt(1 - z<sup>2</sup>))))</code></pre>
055: * <p>
056: * Returns {@link Complex#NaN} if either real or imaginary part of the
057: * input argument is <code>NaN</code> or infinite.
058: *
059: * @param z the value whose inverse cosine is to be returned
060: * @return the inverse cosine of <code>z</code>
061: * @throws NullPointerException if <code>z</code> is null
062: */
063: public static Complex acos(Complex z) {
064: if (z.isNaN()) {
065: return Complex.NaN;
066: }
067:
068: return Complex.I.negate().multiply(
069: log(z.add(Complex.I.multiply(sqrt1z(z)))));
070: }
071:
072: /**
073: * Compute the
074: * <a href="http://mathworld.wolfram.com/InverseSine.html" TARGET="_top">
075: * inverse sine</a> for the given complex argument.
076: * <p>
077: * Implements the formula: <pre>
078: * <code> asin(z) = -i (log(sqrt(1 - z<sup>2</sup>) + iz)) </code></pre>
079: * <p>
080: * Returns {@link Complex#NaN} if either real or imaginary part of the
081: * input argument is <code>NaN</code> or infinite.
082: *
083: * @param z the value whose inverse sine is to be returned.
084: * @return the inverse sine of <code>z</code>.
085: * @throws NullPointerException if <code>z</code> is null
086: */
087: public static Complex asin(Complex z) {
088: if (z.isNaN()) {
089: return Complex.NaN;
090: }
091:
092: return Complex.I.negate().multiply(
093: log(sqrt1z(z).add(Complex.I.multiply(z))));
094: }
095:
096: /**
097: * Compute the
098: * <a href="http://mathworld.wolfram.com/InverseTangent.html" TARGET="_top">
099: * 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), -Math
157: .sin(a)
158: * MathUtils.sinh(b));
159: }
160:
161: /**
162: * Compute the
163: * <a href="http://mathworld.wolfram.com/HyperbolicCosine.html" TARGET="_top">
164: * hyperbolic cosine</a> for the given complex argument.
165: * <p>
166: * Implements the formula: <pre>
167: * <code> cosh(a + bi) = cosh(a)cos(b) + sinh(a)sin(b)i</code></pre>
168: * where the (real) functions on the right-hand side are
169: * {@link java.lang.Math#sin}, {@link java.lang.Math#cos},
170: * {@link MathUtils#cosh} and {@link MathUtils#sinh}.
171: * <p>
172: * Returns {@link Complex#NaN} if either real or imaginary part of the
173: * input argument is <code>NaN</code>.
174: * <p>
175: * Infinite values in real or imaginary parts of the input may result in
176: * infinite or NaN values returned in parts of the result.<pre>
177: * Examples:
178: * <code>
179: * cosh(1 ± INFINITY i) = NaN + NaN i
180: * cosh(±INFINITY + i) = INFINITY ± INFINITY i
181: * cosh(±INFINITY ± INFINITY i) = NaN + NaN i</code></pre>
182: * <p>
183: * Throws <code>NullPointerException</code> if z is null.
184: *
185: * @param z the value whose hyperbolic cosine is to be returned.
186: * @return the hyperbolic cosine of <code>z</code>.
187: */
188: public static Complex cosh(Complex z) {
189: if (z.isNaN()) {
190: return Complex.NaN;
191: }
192:
193: double a = z.getReal();
194: double b = z.getImaginary();
195:
196: return new Complex(MathUtils.cosh(a) * Math.cos(b), MathUtils
197: .sinh(a)
198: * Math.sin(b));
199: }
200:
201: /**
202: * Compute the
203: * <a href="http://mathworld.wolfram.com/ExponentialFunction.html" TARGET="_top">
204: * exponential function</a> for the given complex argument.
205: * <p>
206: * Implements the formula: <pre>
207: * <code> exp(a + bi) = exp(a)cos(b) + exp(a)sin(b)i</code></pre>
208: * where the (real) functions on the right-hand side are
209: * {@link java.lang.Math#exp}, {@link java.lang.Math#cos}, and
210: * {@link java.lang.Math#sin}.
211: * <p>
212: * Returns {@link Complex#NaN} if either real or imaginary part of the
213: * input argument is <code>NaN</code>.
214: * <p>
215: * Infinite values in real or imaginary parts of the input may result in
216: * infinite or NaN values returned in parts of the result.<pre>
217: * Examples:
218: * <code>
219: * exp(1 ± INFINITY i) = NaN + NaN i
220: * exp(INFINITY + i) = INFINITY + INFINITY i
221: * exp(-INFINITY + i) = 0 + 0i
222: * exp(±INFINITY ± INFINITY i) = NaN + NaN i</code></pre>
223: * <p>
224: * Throws <code>NullPointerException</code> if z is null.
225: *
226: * @param z the value
227: * @return <i>e</i><sup><code>z</code></sup>
228: */
229: public static Complex exp(Complex z) {
230: if (z.isNaN()) {
231: return Complex.NaN;
232: }
233:
234: double b = z.getImaginary();
235: double expA = Math.exp(z.getReal());
236: return new Complex(expA * Math.cos(b), expA * Math.sin(b));
237: }
238:
239: /**
240: * Compute the
241: * <a href="http://mathworld.wolfram.com/NaturalLogarithm.html" TARGET="_top">
242: * natural logarithm</a> for the given complex argument.
243: * <p>
244: * Implements the formula: <pre>
245: * <code> log(a + bi) = ln(|a + bi|) + arg(a + bi)i</code></pre>
246: * where ln on the right hand side is {@link java.lang.Math#log},
247: * <code>|a + bi|</code> is the modulus, {@link Complex#abs}, and
248: * <code>arg(a + bi) = {@link java.lang.Math#atan2}(b, a)</code>
249: * <p>
250: * Returns {@link Complex#NaN} if either real or imaginary part of the
251: * input argument is <code>NaN</code>.
252: * <p>
253: * Infinite (or critical) values in real or imaginary parts of the input may
254: * result in infinite or NaN values returned in parts of the result.<pre>
255: * Examples:
256: * <code>
257: * log(1 ± INFINITY i) = INFINITY ± (π/2)i
258: * log(INFINITY + i) = INFINITY + 0i
259: * log(-INFINITY + i) = INFINITY + πi
260: * log(INFINITY ± INFINITY i) = INFINITY ± (π/4)i
261: * log(-INFINITY ± INFINITY i) = INFINITY ± (3π/4)i
262: * log(0 + 0i) = -INFINITY + 0i
263: * </code></pre>
264: * Throws <code>NullPointerException</code> if z is null.
265: *
266: * @param z the value.
267: * @return ln <code>z</code>.
268: */
269: public static Complex log(Complex z) {
270: if (z.isNaN()) {
271: return Complex.NaN;
272: }
273:
274: return new Complex(Math.log(z.abs()), Math.atan2(z
275: .getImaginary(), z.getReal()));
276: }
277:
278: /**
279: * Creates a complex number from the given polar representation.
280: * <p>
281: * The value returned is <code>r·e<sup>i·theta</sup></code>,
282: * computed as <code>r·cos(theta) + r·sin(theta)i</code>
283: * <p>
284: * If either <code>r</code> or <code>theta</code> is NaN, or
285: * <code>theta</code> is infinite, {@link Complex#NaN} is returned.
286: * <p>
287: * If <code>r</code> is infinite and <code>theta</code> is finite,
288: * infinite or NaN values may be returned in parts of the result, following
289: * the rules for double arithmetic.<pre>
290: * Examples:
291: * <code>
292: * polar2Complex(INFINITY, π/4) = INFINITY + INFINITY i
293: * polar2Complex(INFINITY, 0) = INFINITY + NaN i
294: * polar2Complex(INFINITY, -π/4) = INFINITY - INFINITY i
295: * polar2Complex(INFINITY, 5π/4) = -INFINITY - INFINITY i </code></pre>
296: *
297: * @param r the modulus of the complex number to create
298: * @param theta the argument of the complex number to create
299: * @return <code>r·e<sup>i·theta</sup></code>
300: * @throws IllegalArgumentException if r is negative
301: * @since 1.1
302: */
303: public static Complex polar2Complex(double r, double theta) {
304: if (r < 0) {
305: throw new IllegalArgumentException(
306: "Complex modulus must not be negative");
307: }
308: return new Complex(r * Math.cos(theta), r * Math.sin(theta));
309: }
310:
311: /**
312: * Returns of value of <code>y</code> raised to the power of <code>x</code>.
313: * <p>
314: * Implements the formula: <pre>
315: * <code> y<sup>x</sup> = exp(x·log(y))</code></pre>
316: * where <code>exp</code> and <code>log</code> are {@link #exp} and
317: * {@link #log}, respectively.
318: * <p>
319: * Returns {@link Complex#NaN} if either real or imaginary part of the
320: * input argument is <code>NaN</code> or infinite, or if <code>y</code>
321: * equals {@link Complex#ZERO}.
322: *
323: * @param y the base.
324: * @param x the exponent.
325: * @return <code>y</code><sup><code>x</code></sup>
326: * @throws NullPointerException if either x or y is null
327: */
328: public static Complex pow(Complex y, Complex x) {
329: return exp(x.multiply(log(y)));
330: }
331:
332: /**
333: * Compute the
334: * <a href="http://mathworld.wolfram.com/Sine.html" TARGET="_top">
335: * sine</a>
336: * for the given complex argument.
337: * <p>
338: * Implements the formula: <pre>
339: * <code> sin(a + bi) = sin(a)cosh(b) - cos(a)sinh(b)i</code></pre>
340: * where the (real) functions on the right-hand side are
341: * {@link java.lang.Math#sin}, {@link java.lang.Math#cos},
342: * {@link MathUtils#cosh} and {@link MathUtils#sinh}.
343: * <p>
344: * Returns {@link Complex#NaN} if either real or imaginary part of the
345: * input argument is <code>NaN</code>.
346: * <p>
347: * Infinite values in real or imaginary parts of the input may result in
348: * infinite or NaN values returned in parts of the result.<pre>
349: * Examples:
350: * <code>
351: * sin(1 ± INFINITY i) = 1 ± INFINITY i
352: * sin(±INFINITY + i) = NaN + NaN i
353: * sin(±INFINITY ± INFINITY i) = NaN + NaN i</code></pre>
354: *
355: * Throws <code>NullPointerException</code> if z is null.
356: *
357: * @param z the value whose sine is to be returned.
358: * @return the sine of <code>z</code>.
359: */
360: public static Complex sin(Complex z) {
361: if (z.isNaN()) {
362: return Complex.NaN;
363: }
364:
365: double a = z.getReal();
366: double b = z.getImaginary();
367:
368: return new Complex(Math.sin(a) * MathUtils.cosh(b), Math.cos(a)
369: * MathUtils.sinh(b));
370: }
371:
372: /**
373: * Compute the
374: * <a href="http://mathworld.wolfram.com/HyperbolicSine.html" TARGET="_top">
375: * hyperbolic sine</a> for the given complex argument.
376: * <p>
377: * Implements the formula: <pre>
378: * <code> sinh(a + bi) = sinh(a)cos(b)) + cosh(a)sin(b)i</code></pre>
379: * where the (real) functions on the right-hand side are
380: * {@link java.lang.Math#sin}, {@link java.lang.Math#cos},
381: * {@link MathUtils#cosh} and {@link MathUtils#sinh}.
382: * <p>
383: * Returns {@link Complex#NaN} if either real or imaginary part of the
384: * input argument is <code>NaN</code>.
385: * <p>
386: * Infinite values in real or imaginary parts of the input may result in
387: * infinite or NaN values returned in parts of the result.<pre>
388: * Examples:
389: * <code>
390: * sinh(1 ± INFINITY i) = NaN + NaN i
391: * sinh(±INFINITY + i) = ± INFINITY + INFINITY i
392: * sinh(±INFINITY ± INFINITY i) = NaN + NaN i</code></pre
393: *
394: * @param z the value whose hyperbolic sine is to be returned
395: * @return the hyperbolic sine of <code>z</code>
396: * @throws NullPointerException if <code>z</code> is null
397: */
398: public static Complex sinh(Complex z) {
399: if (z.isNaN()) {
400: return Complex.NaN;
401: }
402:
403: double a = z.getReal();
404: double b = z.getImaginary();
405:
406: return new Complex(MathUtils.sinh(a) * Math.cos(b), MathUtils
407: .cosh(a)
408: * Math.sin(b));
409: }
410:
411: /**
412: * Compute the
413: * <a href="http://mathworld.wolfram.com/SquareRoot.html" TARGET="_top">
414: * square root</a> for the given complex argument.
415: * <p>
416: * Implements the following algorithm to compute <code>sqrt(a + bi)</code>:
417: * <ol><li>Let <code>t = sqrt((|a| + |a + bi|) / 2)</code></li>
418: * <li><pre>if <code> a ≥ 0</code> return <code>t + (b/2t)i</code>
419: * else return <code>|b|/2t + sign(b)t i </code></pre></li>
420: * </ol>
421: * where <ul>
422: * <li><code>|a| = {@link Math#abs}(a)</code></li>
423: * <li><code>|a + bi| = {@link Complex#abs}(a + bi) </code></li>
424: * <li><code>sign(b) = {@link MathUtils#indicator}(b) </code>
425: * </ul>
426: * <p>
427: * Returns {@link Complex#NaN} if either real or imaginary part of the
428: * input argument is <code>NaN</code>.
429: * <p>
430: * Infinite values in real or imaginary parts of the input may result in
431: * infinite or NaN values returned in parts of the result.<pre>
432: * Examples:
433: * <code>
434: * sqrt(1 ± INFINITY i) = INFINITY + NaN i
435: * sqrt(INFINITY + i) = INFINITY + 0i
436: * sqrt(-INFINITY + i) = 0 + INFINITY i
437: * sqrt(INFINITY ± INFINITY i) = INFINITY + NaN i
438: * sqrt(-INFINITY ± INFINITY i) = NaN ± INFINITY i
439: * </code></pre>
440: *
441: * @param z the value whose square root is to be returned
442: * @return the square root of <code>z</code>
443: * @throws NullPointerException if <code>z</code> is null
444: */
445: public static Complex sqrt(Complex z) {
446: if (z.isNaN()) {
447: return Complex.NaN;
448: }
449:
450: double a = z.getReal();
451: double b = z.getImaginary();
452:
453: double t = Math.sqrt((Math.abs(a) + z.abs()) / 2.0);
454: if (a >= 0.0) {
455: return new Complex(t, b / (2.0 * t));
456: } else {
457: return new Complex(Math.abs(b) / (2.0 * t), MathUtils
458: .indicator(b)
459: * t);
460: }
461: }
462:
463: /**
464: * Compute the
465: * <a href="http://mathworld.wolfram.com/SquareRoot.html" TARGET="_top">
466: * square root</a> of 1 - <code>z</code><sup>2</sup> for the given complex
467: * argument.
468: * <p>
469: * Computes the result directly as
470: * <code>sqrt(Complex.ONE.subtract(z.multiply(z)))</code>.
471: * <p>
472: * Returns {@link Complex#NaN} if either real or imaginary part of the
473: * input argument is <code>NaN</code>.
474: * <p>
475: * Infinite values in real or imaginary parts of the input may result in
476: * infinite or NaN values returned in parts of the result.
477: *
478: * @param z the value
479: * @return the square root of 1 - <code>z</code><sup>2</sup>
480: * @throws NullPointerException if <code>z</code> is null
481: */
482: public static Complex sqrt1z(Complex z) {
483: return sqrt(Complex.ONE.subtract(z.multiply(z)));
484: }
485:
486: /**
487: * Compute the
488: * <a href="http://mathworld.wolfram.com/Tangent.html" TARGET="_top">
489: * tangent</a> for the given complex argument.
490: * <p>
491: * Implements the formula: <pre>
492: * <code>tan(a + bi) = sin(2a)/(cos(2a)+cosh(2b)) + [sinh(2b)/(cos(2a)+cosh(2b))]i</code></pre>
493: * where the (real) functions on the right-hand side are
494: * {@link java.lang.Math#sin}, {@link java.lang.Math#cos},
495: * {@link MathUtils#cosh} and {@link MathUtils#sinh}.
496: * <p>
497: * Returns {@link Complex#NaN} if either real or imaginary part of the
498: * input argument is <code>NaN</code>.
499: * <p>
500: * Infinite (or critical) values in real or imaginary parts of the input may
501: * result in infinite or NaN values returned in parts of the result.<pre>
502: * Examples:
503: * <code>
504: * tan(1 ± INFINITY i) = 0 + NaN i
505: * tan(±INFINITY + i) = NaN + NaN i
506: * tan(±INFINITY ± INFINITY i) = NaN + NaN i
507: * tan(±&pi/2 + 0 i) = ±INFINITY + NaN i</code></pre>
508: *
509: * @param z the value whose tangent is to be returned
510: * @return the tangent of <code>z</code>
511: * @throws NullPointerException if <code>z</code> is null
512: */
513: public static Complex tan(Complex z) {
514: if (z.isNaN()) {
515: return Complex.NaN;
516: }
517:
518: double a2 = 2.0 * z.getReal();
519: double b2 = 2.0 * z.getImaginary();
520: double d = Math.cos(a2) + MathUtils.cosh(b2);
521:
522: return new Complex(Math.sin(a2) / d, MathUtils.sinh(b2) / d);
523: }
524:
525: /**
526: * Compute the
527: * <a href="http://mathworld.wolfram.com/HyperbolicTangent.html" TARGET="_top">
528: * hyperbolic tangent</a> for the given complex argument.
529: * <p>
530: * Implements the formula: <pre>
531: * <code>tan(a + bi) = sinh(2a)/(cosh(2a)+cos(2b)) + [sin(2b)/(cosh(2a)+cos(2b))]i</code></pre>
532: * where the (real) functions on the right-hand side are
533: * {@link java.lang.Math#sin}, {@link java.lang.Math#cos},
534: * {@link MathUtils#cosh} and {@link MathUtils#sinh}.
535: * <p>
536: * Returns {@link Complex#NaN} if either real or imaginary part of the
537: * input argument is <code>NaN</code>.
538: * <p>
539: * Infinite values in real or imaginary parts of the input may result in
540: * infinite or NaN values returned in parts of the result.<pre>
541: * Examples:
542: * <code>
543: * tanh(1 ± INFINITY i) = NaN + NaN i
544: * tanh(±INFINITY + i) = NaN + 0 i
545: * tanh(±INFINITY ± INFINITY i) = NaN + NaN i
546: * tanh(0 + (&pi/2)i) = NaN + INFINITY i</code></pre>
547: *
548: * @param z the value whose hyperbolic tangent is to be returned
549: * @return the hyperbolic tangent of <code>z</code>
550: * @throws NullPointerException if <code>z</code> is null
551: */
552: public static Complex tanh(Complex z) {
553: if (z.isNaN()) {
554: return Complex.NaN;
555: }
556:
557: double a2 = 2.0 * z.getReal();
558: double b2 = 2.0 * z.getImaginary();
559: double d = MathUtils.cosh(a2) + Math.cos(b2);
560:
561: return new Complex(MathUtils.sinh(a2) / d, Math.sin(b2) / d);
562: }
563: }
|