blob: 23c79a42b76afc74a286acb8f3a9d5cd946f48fc [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.numbers.complex;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.numbers.core.Precision;
/**
* Cartesian representation of a Complex number, i.e. a number which has both a
* real and imaginary part.
*
* <p>This class is immutable. All arithmetic will create a new instance for the
* result.</p>
*
* <p>Arithmetic in this class conforms to the C.99 standard for complex numbers
* defined in ISO/IEC 9899, Annex G. All methods have been named using the equivalent
* method in ISO C.99.</p>
*
* <p>Operations ({@code op}) with no arguments obey the conjuagte equality:</p>
* <pre>z.op().conjugate() == z.conjugate().op()</pre>
*
* <p>Operations that are odd or even obey the equality:</p>
* <pre>
* Odd: f(z) = -f(-z)
* Even: f(z) = f(-z)
* </pre>
*
* @see <a href="http://www.open-std.org/JTC1/SC22/WG14/www/standards">
* ISO/IEC 9899 - Programming languages - C</a>
*/
public final class Complex implements Serializable {
/**
* A complex number representing {@code i}, the square root of -1.
* <pre>{@code 0 + i 1}</pre>
*/
public static final Complex I = new Complex(0, 1);
/**
* A complex number representing one.
* <pre>{@code 1 + i 0}</pre>
*/
public static final Complex ONE = new Complex(1, 0);
/**
* A complex number representing zero.
* <pre>{@code 0 + i 0}</pre>
*/
public static final Complex ZERO = new Complex(0, 0);
/** A complex number representing {@code NaN + i NaN}. */
private static final Complex NAN = new Complex(Double.NaN, Double.NaN);
/** &pi;/2. */
private static final double PI_OVER_2 = 0.5 * Math.PI;
/** &pi;/4. */
private static final double PI_OVER_4 = 0.25 * Math.PI;
/** Mask an integer number to even by discarding the lowest bit. */
private static final int MASK_INT_TO_EVEN = ~0x1;
/** Natural logarithm of 2 (ln(2)). */
private static final double LN_2 = Math.log(2);
/** Base 10 logarithm of 2 (log10(2)). */
private static final double LOG10_2 = Math.log10(2);
/**
* Crossover point to switch computation for asin/acos factor A.
* This has been updated from the 1.5 value used by Hull et al to 10
* as used in boost::math::complex.
* @see <a href="https://svn.boost.org/trac/boost/ticket/7290">Boost ticket 7290</a>
*/
private static final double A_CROSSOVER = 10;
/** Crossover point to switch computation for asin/acos factor B. */
private static final double B_CROSSOVER = 0.6471;
/**
* The safe maximum double value {@code x} to avoid loss of precision in asin/acos.
* Equal to sqrt(M) / 8 in Hull, et al (1997) with M the largest normalised floating-point value.
*/
private static final double SAFE_MAX = Math.sqrt(Double.MAX_VALUE) / 8;
/**
* The safe minimum double value {@code x} to avoid loss of precision/underflow in asin/acos.
* Equal to sqrt(u) * 4 in Hull, et al (1997) with u the smallest normalised floating-point value.
*/
private static final double SAFE_MIN = Math.sqrt(Double.MIN_NORMAL) * 4;
/**
* The safe maximum double value {@code x} to avoid loss of precision in atanh.
* Equal to sqrt(M) / 2 with M the largest normalised floating-point value.
*/
private static final double SAFE_UPPER = Math.sqrt(Double.MAX_VALUE) / 2;
/**
* The safe minimum double value {@code x} to avoid loss of precision/underflow in atanh.
* Equal to sqrt(u) * 2 with u the smallest normalised floating-point value.
*/
private static final double SAFE_LOWER = Math.sqrt(Double.MIN_NORMAL) * 2;
/** Exponent offset in IEEE754 representation. */
private static final long EXPONENT_OFFSET = 1023L;
/**
* Largest double-precision floating-point number such that
* {@code 1 + EPSILON} is numerically equal to 1. This value is an upper
* bound on the relative error due to rounding real numbers to double
* precision floating-point numbers.
*
* <p>In IEEE 754 arithmetic, this is 2<sup>-53</sup>.</p>
*
* <p>Copied from o.a.c.numbers.core.Precision
*
* @see <a href="http://en.wikipedia.org/wiki/Machine_epsilon">Machine epsilon</a>
*/
public static final double EPSILON = Double.longBitsToDouble((EXPONENT_OFFSET - 53L) << 52);
/** Serializable version identifier. */
private static final long serialVersionUID = 20180201L;
/**
* The size of the buffer for {@link #toString()}.
*
* <p>The longest double will require a sign, a maximum of 17 digits, the decimal place
* and the exponent, e.g. for max value this is 24 chars: -1.7976931348623157e+308.
* Set the buffer size to twice this and round up to a power of 2 thus
* allowing for formatting characters. The size is 64.
*/
private static final int TO_STRING_SIZE = 64;
/** The minimum number of characters in the format. This is 5, e.g. {@code "(0,0)"}. */
private static final int FORMAT_MIN_LEN = 5;
/** {@link #toString() String representation}. */
private static final char FORMAT_START = '(';
/** {@link #toString() String representation}. */
private static final char FORMAT_END = ')';
/** {@link #toString() String representation}. */
private static final char FORMAT_SEP = ',';
/** The minimum number of characters before the separator. This is 2, e.g. {@code "(0"}. */
private static final int BEFORE_SEP = 2;
/** The imaginary part. */
private final double imaginary;
/** The real part. */
private final double real;
/**
* Define a constructor for a Complex.
* This is used in functions that implement trigonomic identities.
*/
@FunctionalInterface
private interface ComplexConstructor {
/**
* Create a complex number given the real and imaginary parts.
*
* @param real Real part.
* @param imaginary Imaginary part.
* @return {@code Complex} object
*/
Complex create(double real, double imaginary);
}
/**
* Define a unary operation on a double.
* This is used in the log() and log10() functions.
*/
@FunctionalInterface
private interface UnaryOperation {
/**
* Apply an operation to a value.
*
* @param value The value.
* @return The result.
*/
double apply(double value);
}
/**
* Private default constructor.
*
* @param real Real part.
* @param imaginary Imaginary part.
*/
private Complex(double real, double imaginary) {
this.real = real;
this.imaginary = imaginary;
}
/**
* Create a complex number given the real and imaginary parts.
*
* @param real Real part.
* @param imaginary Imaginary part.
* @return {@code Complex} number
*/
public static Complex ofCartesian(double real, double imaginary) {
return new Complex(real, imaginary);
}
/**
* Creates a complex number from its polar representation using modulus {@code rho}
* and phase angle {@code theta}.
* <pre>
* x = rho * cos(theta)
* y = rho * sin(theta)
* </pre>
*
* <p>Requires that {@code rho} is non-negative and non-NaN and {@code theta} is finite;
* otherwise returns a complex with NaN real and imaginary parts. A value of {@code -0.0} is
* considered negative and an invalid modulus.
*
* <p>A non-NaN complex number constructed using this method will satisfy the following
* to within floating-point error:</p>
* <pre>
* Complex.ofPolar(rho, theta).abs() == rho
* Complex.ofPolar(rho, theta).arg() == theta; theta in (\(-\pi\), \(\pi\)]
* </pre>
*
* <p>If {@code rho} is infinite then the resulting parts may be infinite or NaN
* following the rules for double arithmetic.</p>
*
* <pre>
* Examples:
* {@code
* ofPolar(-0.0, 0.0) = NaN + NaN i
* ofPolar(0.0, 0.0) = 0.0 + 0.0 i
* ofPolar(1.0, 0.0) = 1.0 + 0.0 i
* ofPolar(1.0, \(\pi\)) = -1.0 + sin(\(\pi\)) i
* ofPolar(INFINITY, \(\pi\)) = -INFINITY + INFINITY i
* ofPolar(INFINITY, 0) = INFINITY + NaN i
* ofPolar(INFINITY, \(-\frac{\pi}{4}\)) = INFINITY - INFINITY i
* ofPolar(INFINITY, \(5\frac{\pi}{4}\)) = -INFINITY - INFINITY i }
* </pre>
*
* @param rho the modulus of the complex number to create
* @param theta the argument of the complex number to create
* @return {@code Complex} number
* @see <a href="http://mathworld.wolfram.com/PolarCoordinates.html">Polar Coordinates</a>
*/
public static Complex ofPolar(double rho, double theta) {
// Require finite theta and non-negative, non-nan rho
if (!Double.isFinite(theta) || negative(rho) || Double.isNaN(rho)) {
return NAN;
}
final double x = rho * Math.cos(theta);
final double y = rho * Math.sin(theta);
return new Complex(x, y);
}
/**
* Create a complex cis number. This is also known as the complex exponential:
* <pre>
* <code>
* cis(x) = e<sup>ix</sup> = cos(x) + i sin(x)
* </code>
* </pre>
*
* @param x {@code double} to build the cis number
* @return {@code Complex} cis number
* @see <a href="http://mathworld.wolfram.com/Cis.html">Cis</a>
*/
public static Complex ofCis(double x) {
return new Complex(Math.cos(x), Math.sin(x));
}
/**
* Returns a {@code Complex} instance representing the specified string {@code s}.
*
* <p>If {@code s} is {@code null}, then a {@code NullPointerException} is thrown.
*
* <p>The string must be in a format compatible with that produced by
* {@link #toString() Complex.toString()}.
* The format expects a start and end string surrounding two numeric parts split
* by a separator. Leading and trailing spaces are allowed around each numeric part.
* Each numeric part is parsed using {@link Double#parseDouble(String)}. The parts
* are interpreted as the real and imaginary parts of the complex number.
*
* <p>Examples of valid strings and the equivalent {@code Complex} are shown below:
*
* <pre>
* "(0,0)" = Complex.ofCartesian(0, 0)
* "(0.0,0.0)" = Complex.ofCartesian(0, 0)
* "(-0.0, 0.0)" = Complex.ofCartesian(-0.0, 0)
* "(-1.23, 4.56)" = Complex.ofCartesian(-123, 4.56)
* "(1e300,-1.1e-2)" = Complex.ofCartesian(1e300, -1.1e-2)
* </pre>
*
* @param s String representation.
* @return {@code Complex} number
* @throws NullPointerException if the string is null.
* @throws NumberFormatException if the string does not contain a parsable complex number.
* @see Double#parseDouble(String)
* @see #toString()
*/
public static Complex parse(String s) {
final int len = s.length();
if (len < FORMAT_MIN_LEN) {
throw parsingException("Expected format",
FORMAT_START + "real" + FORMAT_SEP + "imaginary" + FORMAT_END, null);
}
// Confirm start: '('
if (s.charAt(0) != FORMAT_START) {
throw parsingException("Expected start", FORMAT_START, null);
}
// Confirm end: ')'
if (s.charAt(len - 1) != FORMAT_END) {
throw parsingException("Expected end", FORMAT_END, null);
}
// Confirm separator ',' is between at least 2 characters from
// either end: "(x,x)"
// Count back from the end ignoring the last 2 characters.
final int sep = s.lastIndexOf(FORMAT_SEP, len - 3);
if (sep < BEFORE_SEP) {
throw parsingException("Expected separator between two numbers", FORMAT_SEP, null);
}
// Should be no more separators
if (s.indexOf(FORMAT_SEP, sep + 1) != -1) {
throw parsingException("Incorrect number of parts, expected only 2 using separator",
FORMAT_SEP, null);
}
// Try to parse the parts
final String rePart = s.substring(1, sep);
final double re;
try {
re = Double.parseDouble(rePart);
} catch (final NumberFormatException ex) {
throw parsingException("Could not parse real part", rePart, ex);
}
final String imPart = s.substring(sep + 1, len - 1);
final double im;
try {
im = Double.parseDouble(imPart);
} catch (final NumberFormatException ex) {
throw parsingException("Could not parse imaginary part", imPart, ex);
}
return ofCartesian(re, im);
}
/**
* Returns true if either the real <em>or</em> imaginary component of the Complex is NaN
* <em>and</em> the Complex is not infinite.
*
* <p>Note that in contrast to {@link Double#isNaN()}:
* <ul>
* <li>There is more than one complex number that can return {@code true}.
* <li>Different representations of NaN can be distinguished by the
* {@link #equals(Object) Complex.equals(Object)} method.
* </ul>
*
* @return {@code true} if this instance contains NaN and no infinite parts.
* @see Double#isNaN(double)
* @see #isInfinite()
* @see #equals(Object) Complex.equals(Object)
*/
public boolean isNaN() {
if (Double.isNaN(real) || Double.isNaN(imaginary)) {
return !isInfinite();
}
return false;
}
/**
* Returns true if either real or imaginary component of the Complex is infinite.
*
* <p>Note: A complex or imaginary value with at least one infinite part is regarded
* as an infinity (even if its other part is a NaN).</p>
*
* @return {@code true} if this instance contains an infinite value.
* @see Double#isInfinite(double)
*/
public boolean isInfinite() {
return Double.isInfinite(real) || Double.isInfinite(imaginary);
}
/**
* Returns true if both real and imaginary component of the Complex are finite.
*
* @return {@code true} if this instance contains finite values.
* @see Double#isFinite(double)
*/
public boolean isFinite() {
return Double.isFinite(real) && Double.isFinite(imaginary);
}
/**
* Returns projection of this complex number onto the Riemann sphere.
*
* <p>{@code z} projects to {@code z}, except that all complex infinities (even those
* with one infinite part and one NaN part) project to positive infinity on the real axis.
*
* If {@code z} has an infinite part, then {@code z.proj()} shall be equivalent to:</p>
* <pre>
* return Complex.ofCartesian(Double.POSITIVE_INFINITY, Math.copySign(0.0, imag());
* </pre>
*
* @return {@code z} projected onto the Riemann sphere.
* @see #isInfinite()
* @see <a href="http://pubs.opengroup.org/onlinepubs/9699919799/functions/cproj.html">
* IEEE and ISO C standards: cproj</a>
*/
public Complex proj() {
if (isInfinite()) {
return new Complex(Double.POSITIVE_INFINITY, Math.copySign(0.0, imaginary));
}
return this;
}
/**
* Return the absolute value of this complex number. This is also called complex norm, modulus,
* or magnitude.
* <pre>abs(a + b i) = sqrt(a^2 + b^2)</pre>
*
* <p>If either component is infinite then the result is positive infinity. If either
* component is NaN and this is not {@link #isInfinite() infinite} then the result is NaN.
*
* <p>This code follows the
* <a href="http://www.iso-9899.info/wiki/The_Standard">ISO C Standard</a>, Annex G,
* in calculating the returned value using the {@code hypot(a, b)} method for complex
* {@code a + b i}.
*
* @return the absolute value.
* @see #isInfinite()
* @see #isNaN()
* @see Math#hypot(double, double)
* @see <a href="http://mathworld.wolfram.com/ComplexModulus.html">Complex modulus</a>
*/
public double abs() {
// Delegate
return Math.hypot(real, imaginary);
}
/**
* Compute the absolute of the complex number.
*
* <p>This function exists for use in trigonomic functions.
*
* @param real Real part.
* @param imaginary Imaginary part.
* @return the absolute value.
* @see Math#hypot(double, double)
*/
private static double getAbsolute(double real, double imaginary) {
// Delegate
return Math.hypot(real, imaginary);
}
/**
* Return the squared norm value of this complex number. This is also called the absolute
* square.
* <pre>norm(a + b i) = a^2 + b^2</pre>
*
* <p>If either component is infinite then the result is positive infinity. If either
* component is NaN and this is not {@link #isInfinite() infinite} then the result is NaN.
*
* <p>This method will return the square of {@link #abs()}. It can be used as a faster
* alternative for ranking by magnitude although overflow to infinity will create equal
* ranking for values that may be still distinguished by {@code abs()}.
*
* @return the square norm value.
* @see #isInfinite()
* @see #isNaN()
* @see #abs()
* @see <a href="http://mathworld.wolfram.com/AbsoluteSquare.html">Absolute square</a>
*/
public double norm() {
if (isInfinite()) {
return Double.POSITIVE_INFINITY;
}
return real * real + imaginary * imaginary;
}
/**
* Returns a {@code Complex} whose value is {@code (this + addend)}.
* Implements the formula:
* <pre>
* (a + i b) + (c + i d) = (a + c) + i (b + d)
* </pre>
*
* @param addend Value to be added to this {@code Complex}.
* @return {@code this + addend}.
* @see <a href="http://mathworld.wolfram.com/ComplexAddition.html">Complex Addition</a>
*/
public Complex add(Complex addend) {
return new Complex(real + addend.real,
imaginary + addend.imaginary);
}
/**
* Returns a {@code Complex} whose value is {@code (this + addend)},
* with {@code addend} interpreted as a real number.
* Implements the formula:
* <pre>
* (a + i b) + c = (a + c) + i b
* </pre>
*
* <p>This method is included for compatibility with ISO C99 which defines arithmetic between
* real-only and complex numbers.</p>
*
* <p>Note: This method preserves the sign of the imaginary component {@code b} if it is {@code -0.0}.
* The sign would be lost if adding {@code (c + i 0)} using
* {@link #add(Complex) add(Complex.ofCartesian(addend, 0))} since
* {@code -0.0 + 0.0 = 0.0}.
*
* @param addend Value to be added to this {@code Complex}.
* @return {@code this + addend}.
* @see #add(Complex)
* @see #ofCartesian(double, double)
*/
public Complex add(double addend) {
return new Complex(real + addend, imaginary);
}
/**
* Returns a {@code Complex} whose value is {@code (this + addend)},
* with {@code addend} interpreted as an imaginary number.
* Implements the formula:
* <pre>
* (a + i b) + i d = a + i (b + d)
* </pre>
*
* <p>This method is included for compatibility with ISO C99 which defines arithmetic between
* imaginary-only and complex numbers.</p>
*
* <p>Note: This method preserves the sign of the real component {@code a} if it is {@code -0.0}.
* The sign would be lost if adding {@code (0 + i d)} using
* {@link #add(Complex) add(Complex.ofCartesian(0, addend))} since
* {@code -0.0 + 0.0 = 0.0}.
*
* @param addend Value to be added to this {@code Complex}.
* @return {@code this + addend}.
* @see #add(Complex)
* @see #ofCartesian(double, double)
*/
public Complex addImaginary(double addend) {
return new Complex(real, imaginary + addend);
}
/**
* Returns the
* <a href="http://mathworld.wolfram.com/ComplexConjugate.html">conjugate</a>
* z&#773; of this complex number z.
* <pre>
* z = a + b i
*
* z&#773; = a - b i
* </pre>
*
* @return the conjugate (z&#773;) of this complex object.
*/
public Complex conj() {
return new Complex(real, -imaginary);
}
/**
* Returns a {@code Complex} whose value is {@code (this / divisor)}.
* Implements the formula:
* <pre>
* <code>
* a + i b (ac + bd) + i (bc - ad)
* ------- = -----------------------
* c + i d c<sup>2</sup> + d<sup>2</sup>
* </code>
* </pre>
*
* <p>Recalculates to recover infinities as specified in C.99
* standard G.5.1. Method is fully in accordance with
* C++11 standards for complex numbers.</p>
*
* @param divisor Value by which this {@code Complex} is to be divided.
* @return {@code this / divisor}.
* @see <a href="http://mathworld.wolfram.com/ComplexDivision.html">Complex Division</a>
*/
public Complex divide(Complex divisor) {
return divide(real, imaginary, divisor.real, divisor.imaginary);
}
/**
* Returns a {@code Complex} whose value is:
* <pre>
* <code>
* a + i b (ac + bd) + i (bc - ad)
* ------- = -----------------------
* c + i d c<sup>2</sup> + d<sup>2</sup>
* </code>
* </pre>
*
* <p>Recalculates to recover infinities as specified in C.99
* standard G.5.1. Method is fully in accordance with
* C++11 standards for complex numbers.</p>
*
* <p>Note: In the event of divide by zero this method produces the same result
* as dividing by a real-only zero using {@link #divide(double)}.
*
* @param re1 Real component of first number.
* @param im1 Imaginary component of first number.
* @param re2 Real component of second number.
* @param im2 Imaginary component of second number.
* @return (a + i b) / (c + i d).
* @see <a href="http://mathworld.wolfram.com/ComplexDivision.html">Complex Division</a>
* @see #divide(double)
*/
private static Complex divide(double re1, double im1, double re2, double im2) {
double a = re1;
double b = im1;
double c = re2;
double d = im2;
int ilogbw = 0;
// Get the exponent to scale the divisor.
final int exponent = getMaxExponent(c, d);
if (exponent <= Double.MAX_EXPONENT) {
ilogbw = exponent;
c = Math.scalb(c, -ilogbw);
d = Math.scalb(d, -ilogbw);
}
final double denom = c * c + d * d;
double x = Math.scalb((a * c + b * d) / denom, -ilogbw);
double y = Math.scalb((b * c - a * d) / denom, -ilogbw);
// Recover infinities and zeros that computed as NaN+iNaN
// the only cases are nonzero/zero, infinite/finite, and finite/infinite, ...
// --------------
// Modification from the listing in ISO C.99 G.5.1 (8):
// Prevent overflow in (a * c + b * d) and (b * c - a * d).
// It is only the sign that is important. not the magnitude.
// --------------
if (Double.isNaN(x) && Double.isNaN(y)) {
if ((denom == 0.0) &&
(!Double.isNaN(a) || !Double.isNaN(b))) {
// nonzero/zero
// This case produces the same result as divide by a real-only zero
// using divide(+/-0.0).
x = Math.copySign(Double.POSITIVE_INFINITY, c) * a;
y = Math.copySign(Double.POSITIVE_INFINITY, c) * b;
} else if ((Double.isInfinite(a) || Double.isInfinite(b)) &&
Double.isFinite(c) && Double.isFinite(d)) {
// infinite/finite
a = boxInfinity(a);
b = boxInfinity(b);
x = Double.POSITIVE_INFINITY * computeACplusBD(a, b, c, d);
y = Double.POSITIVE_INFINITY * computeBCminusAD(a, b, c, d);
} else if ((Double.isInfinite(c) || Double.isInfinite(d)) &&
Double.isFinite(a) && Double.isFinite(b)) {
// finite/infinite
c = boxInfinity(c);
d = boxInfinity(d);
x = 0.0 * computeACplusBD(a, b, c, d);
y = 0.0 * computeBCminusAD(a, b, c, d);
}
}
return new Complex(x, y);
}
/**
* Compute {@code a*c + b*d} without overflow.
* It is assumed: either {@code a} and {@code b} or {@code c} and {@code d} are
* either zero or one (i.e. a boxed infinity); and the sign of the result is important,
* not the value.
*
* @param a the a
* @param b the b
* @param c the c
* @param d the d
* @return the result
*/
private static double computeACplusBD(double a, double b, double c, double d) {
final double ac = a * c;
final double bd = b * d;
final double result = ac + bd;
return Double.isFinite(result) ?
result :
// Overflow. Just divide by 2 as it is the sign of the result that matters.
ac * 0.5 + bd * 0.5;
}
/**
* Compute {@code b*c - a*d} without overflow.
* It is assumed: either {@code a} and {@code b} or {@code c} and {@code d} are
* either zero or one (i.e. a boxed infinity); and the sign of the result is important,
* not the value.
*
* @param a the a
* @param b the b
* @param c the c
* @param d the d
* @return the result
*/
private static double computeBCminusAD(double a, double b, double c, double d) {
final double bc = b * c;
final double ad = a * d;
final double result = bc - ad;
return Double.isFinite(result) ?
result :
// Overflow. Just divide by 2 as it is the sign of the result that matters.
bc * 0.5 - ad * 0.5;
}
/**
* Returns a {@code Complex} whose value is {@code (this / divisor)},
* with {@code divisor} interpreted as a real number.
* Implements the formula:
* <pre>
* (a + i b) / c = (a + i b) / (c + i 0)
* = (a/c) + i (b/c)
* </pre>
*
* <p>This method is included for compatibility with ISO C99 which defines arithmetic between
* real-only and complex numbers.</p>
*
* <p>Note: This method should be preferred over using
* {@link #divide(Complex) divide(Complex.ofCartesian(divisor, 0))}. Division
* can generate signed zeros if {@code this} complex has zeros for the real
* and/or imaginary component, or the divisor is infinity. The summation of signed zeros
* in {@link #divide(Complex)} may create zeros in the result that differ in sign
* from the equivalent call to divide by a real-only number.
*
* @param divisor Value by which this {@code Complex} is to be divided.
* @return {@code this / divisor}.
* @see #divide(Complex)
*/
public Complex divide(double divisor) {
return new Complex(real / divisor, imaginary / divisor);
}
/**
* Returns a {@code Complex} whose value is {@code (this / divisor)},
* with {@code divisor} interpreted as an imaginary number.
* Implements the formula:
* <pre>
* (a + i b) / id = (a + i b) / (0 + i d)
* = (b/d) + i (-a/d)
* </pre>
*
* <p>This method is included for compatibility with ISO C99 which defines arithmetic between
* imaginary-only and complex numbers.</p>
*
* <p>Note: This method should be preferred over using
* {@link #divide(Complex) divide(Complex.ofCartesian(0, divisor))}. Division
* can generate signed zeros if {@code this} complex has zeros for the real
* and/or imaginary component, or the divisor is infinity. The summation of signed zeros
* in {@link #divide(Complex)} may create zeros in the result that differ in sign
* from the equivalent call to divide by an imaginary-only number.
*
* <p>Warning: This method will generate a different result from
* {@link #divide(Complex) divide(Complex.ofCartesian(0, divisor))} if the divisor is zero.
* In this case the divide method using a zero-valued Complex will produce the same result
* as dividing by a real-only zero. The output from dividing by imaginary zero will create
* infinite and NaN values in the same component parts as the output from
* {@code this.divide(Complex.ZERO).multiplyImaginary(1)}, however the sign
* of some infinity values may be negated.
*
* @param divisor Value by which this {@code Complex} is to be divided.
* @return {@code this / divisor}.
* @see #divide(Complex)
* @see #divide(double)
*/
public Complex divideImaginary(double divisor) {
return new Complex(imaginary / divisor, -real / divisor);
}
/**
* Returns the multiplicative inverse of this instance.
*
* @return {@code 1 / this}.
* @see #divide(Complex)
*/
public Complex reciprocal() {
if (Math.abs(real) < Math.abs(imaginary)) {
final double q = real / imaginary;
final double scale = 1.0 / (real * q + imaginary);
double scaleQ = 0;
if (q != 0 &&
scale != 0) {
scaleQ = scale * q;
}
return new Complex(scaleQ, -scale);
}
final double q = imaginary / real;
final double scale = 1.0 / (imaginary * q + real);
double scaleQ = 0;
if (q != 0 &&
scale != 0) {
scaleQ = scale * q;
}
return new Complex(scale, -scaleQ);
}
/**
* Test for equality with another object. If the other object is a {@code Complex} then a
* comparison is made of the real and imaginary parts; otherwise {@code false} is returned.
*
* <p>If both the real and imaginary parts of two complex numbers
* are exactly the same the two {@code Complex} objects are considered to be equal.
* For this purpose, two {@code double} values are considered to be
* the same if and only if the method {@link Double #doubleToLongBits(double)}
* returns the identical {@code long} value when applied to each.
*
* <p>Note that in most cases, for two instances of class
* {@code Complex}, {@code c1} and {@code c2}, the
* value of {@code c1.equals(c2)} is {@code true} if and only if
*
* <pre>
* {@code c1.getReal() == c2.getReal() && c1.getImaginary() == c2.getImaginary()}
* </pre>
*
* <p>also has the value {@code true}. However, there are exceptions:
*
* <ul>
* <li>
* Instances that contain {@code NaN} values in the same part
* are considered to be equal for that part, even though {@code Double.NaN==Double.NaN}
* has the value {@code false}.
* </li>
* <li>
* Instances that share a {@code NaN} value in one part
* but have different values in the other part are <em>not</em> considered equal.
* </li>
* <li>
* Instances that contain different representations of zero in the same part
* are <em>not</em> considered to be equal for that part, even though {@code -0.0==0.0}
* has the value {@code true}.
* </li>
* </ul>
*
* <p>The behavior is the same as if the components of the two complex numbers were passed
* to {@link java.util.Arrays#equals(double[], double[]) Arrays.equals(double[], double[])}:
*
* <pre>
* <code>
* Arrays.equals(new double[]{c1.getReal(), c1.getImaginary()},
* new double[]{c2.getReal(), c2.getImaginary()});
* </code>
* </pre>
*
* @param other Object to test for equality with this instance.
* @return {@code true} if the objects are equal, {@code false} if object
* is {@code null}, not an instance of {@code Complex}, or not equal to
* this instance.
* @see java.lang.Double#doubleToLongBits(double)
* @see java.util.Arrays#equals(double[], double[])
*/
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (other instanceof Complex) {
final Complex c = (Complex) other;
return equals(real, c.real) &&
equals(imaginary, c.imaginary);
}
return false;
}
/**
* Test for the floating-point equality between Complex objects.
* It returns {@code true} if both arguments are equal or within the
* range of allowed error (inclusive).
*
* @param x First value (cannot be {@code null}).
* @param y Second value (cannot be {@code null}).
* @param maxUlps {@code (maxUlps - 1)} is the number of floating point
* values between the real (resp. imaginary) parts of {@code x} and
* {@code y}.
* @return {@code true} if there are fewer than {@code maxUlps} floating
* point values between the real (resp. imaginary) parts of {@code x}
* and {@code y}.
*
* @see Precision#equals(double,double,int)
*/
public static boolean equals(Complex x,
Complex y,
int maxUlps) {
return Precision.equals(x.real, y.real, maxUlps) &&
Precision.equals(x.imaginary, y.imaginary, maxUlps);
}
/**
* Returns {@code true} iff the values are equal as defined by
* {@link #equals(Complex,Complex,int) equals(x, y, 1)}.
*
* @param x First value (cannot be {@code null}).
* @param y Second value (cannot be {@code null}).
* @return {@code true} if the values are equal.
*/
public static boolean equals(Complex x,
Complex y) {
return equals(x, y, 1);
}
/**
* Returns {@code true} if, both for the real part and for the imaginary
* part, there is no double value strictly between the arguments or the
* difference between them is within the range of allowed error
* (inclusive). Returns {@code false} if either of the arguments is NaN.
*
* @param x First value (cannot be {@code null}).
* @param y Second value (cannot be {@code null}).
* @param eps Amount of allowed absolute error.
* @return {@code true} if the values are two adjacent floating point
* numbers or they are within range of each other.
*
* @see Precision#equals(double,double,double)
*/
public static boolean equals(Complex x,
Complex y,
double eps) {
return Precision.equals(x.real, y.real, eps) &&
Precision.equals(x.imaginary, y.imaginary, eps);
}
/**
* Returns {@code true} if, both for the real part and for the imaginary
* part, there is no double value strictly between the arguments or the
* relative difference between them is smaller or equal to the given
* tolerance. Returns {@code false} if either of the arguments is NaN.
*
* @param x First value (cannot be {@code null}).
* @param y Second value (cannot be {@code null}).
* @param eps Amount of allowed relative error.
* @return {@code true} if the values are two adjacent floating point
* numbers or they are within range of each other.
*
* @see Precision#equalsWithRelativeTolerance(double,double,double)
*/
public static boolean equalsWithRelativeTolerance(Complex x, Complex y,
double eps) {
return Precision.equalsWithRelativeTolerance(x.real, y.real, eps) &&
Precision.equalsWithRelativeTolerance(x.imaginary, y.imaginary, eps);
}
/**
* Get a hash code for the complex number.
*
* <p>The behavior is the same as if the components of the complex number were passed
* to {@link java.util.Arrays#hashCode(double[]) Arrays.hashCode(double[])}:
* <pre>
* {@code Arrays.hashCode(new double[] {getReal(), getImaginary()})}
* </pre>
*
* @return a hash code value for this object.
* @see java.util.Arrays#hashCode(double[]) Arrays.hashCode(double[])
*/
@Override
public int hashCode() {
return 31 * (31 + Double.hashCode(real)) + Double.hashCode(imaginary);
}
/**
* Access the imaginary part.
*
* @return the imaginary part.
*/
public double getImaginary() {
return imaginary;
}
/**
* Access the imaginary part (C++ grammar).
*
* @return the imaginary part.
* @see #getImaginary()
*/
public double imag() {
return getImaginary();
}
/**
* Access the real part.
*
* @return the real part.
*/
public double getReal() {
return real;
}
/**
* Access the real part (C++ grammar).
*
* @return the real part.
* @see #getReal()
*/
public double real() {
return getReal();
}
/**
* Returns a {@code Complex} whose value is {@code this * factor}.
* Implements the formula:
* <pre>
* (a + i b)(c + i d) = (ac - bd) + i (ad + bc)
* </pre>
*
* <p>Recalculates to recover infinities as specified in C.99
* standard G.5.1. Method is fully in accordance with
* C++11 standards for complex numbers.</p>
*
* @param factor value to be multiplied by this {@code Complex}.
* @return {@code this * factor}.
* @see <a href="http://mathworld.wolfram.com/ComplexMultiplication.html">Complex Muliplication</a>
*/
public Complex multiply(Complex factor) {
return multiply(real, imaginary, factor.real, factor.imaginary);
}
/**
* Returns a {@code Complex} whose value is:
* <pre>
* (a + i b)(c + i d) = (ac - bd) + i (ad + bc)
* </pre>
*
* <p>Recalculates to recover infinities as specified in C.99
* standard G.5.1. Method is fully in accordance with
* C++11 standards for complex numbers.</p>
*
* @param re1 Real component of first number.
* @param im1 Imaginary component of first number.
* @param re2 Real component of second number.
* @param im2 Imaginary component of second number.
* @return (a + b i)(c + d i).
*/
private static Complex multiply(double re1, double im1, double re2, double im2) {
double a = re1;
double b = im1;
double c = re2;
double d = im2;
final double ac = a * c;
final double bd = b * d;
final double ad = a * d;
final double bc = b * c;
double x = ac - bd;
double y = ad + bc;
// --------------
// NaN can occur if:
// - any of (a,b,c,d) are NaN (for NaN or Infinite complex numbers)
// - a multiplication of infinity by zero (ac,bd,ad,bc).
// - a subtraction of infinity from infinity (e.g. ac - bd)
// Note that (ac,bd,ad,bc) can be infinite due to overflow.
//
// Detect a NaN result and perform correction.
//
// Modification from the listing in ISO C.99 G.5.1 (6)
// Do not correct infinity multiplied by zero. This is left as NaN.
// --------------
if (Double.isNaN(x) && Double.isNaN(y)) {
// Recover infinities that computed as NaN+iNaN ...
boolean recalc = false;
if ((Double.isInfinite(a) || Double.isInfinite(b)) &&
isNotZero(c, d)) {
// This complex is infinite.
// "Box" the infinity and change NaNs in the other factor to 0.
a = boxInfinity(a);
b = boxInfinity(b);
c = changeNaNtoZero(c);
d = changeNaNtoZero(d);
recalc = true;
}
// (c, d) may have been corrected so do not use factor.isInfinite().
if ((Double.isInfinite(c) || Double.isInfinite(d)) &&
isNotZero(a, b)) {
// This other complex is infinite.
// "Box" the infinity and change NaNs in the other factor to 0.
c = boxInfinity(c);
d = boxInfinity(d);
a = changeNaNtoZero(a);
b = changeNaNtoZero(b);
recalc = true;
}
if (!recalc && (Double.isInfinite(ac) || Double.isInfinite(bd) ||
Double.isInfinite(ad) || Double.isInfinite(bc))) {
// The result overflowed to infinity.
// Recover infinities from overflow by changing NaNs to 0 ...
a = changeNaNtoZero(a);
b = changeNaNtoZero(b);
c = changeNaNtoZero(c);
d = changeNaNtoZero(d);
recalc = true;
}
if (recalc) {
x = Double.POSITIVE_INFINITY * (a * c - b * d);
y = Double.POSITIVE_INFINITY * (a * d + b * c);
}
}
return new Complex(x, y);
}
/**
* Box values for the real or imaginary component of an infinite complex number.
* Any infinite value will be returned as one. Non-infinite values will be returned as zero.
* The sign is maintained.
*
* <pre>
* inf = 1
* -inf = -1
* x = 0
* -x = -0
* </pre>
*
* @param component the component
* @return the boxed value
*/
private static double boxInfinity(double component) {
return Math.copySign(Double.isInfinite(component) ? 1.0 : 0.0, component);
}
/**
* Checks if the complex number is not zero.
*
* @param real the real component
* @param imaginary the imaginary component
* @return true if the complex is not zero
*/
private static boolean isNotZero(double real, double imaginary) {
// The use of equals is deliberate.
// This method must distinguish NaN from zero thus ruling out:
// (real != 0.0 || imaginary != 0.0)
return !(real == 0.0 && imaginary == 0.0);
}
/**
* Change NaN to zero preserving the sign; otherwise return the value.
*
* @param value the value
* @return the new value
*/
private static double changeNaNtoZero(double value) {
return Double.isNaN(value) ? Math.copySign(0.0, value) : value;
}
/**
* Returns a {@code Complex} whose value is {@code this * factor}, with {@code factor}
* interpreted as a real number.
* Implements the formula:
* <pre>
* (a + i b) c = (a + i b)(c + 0 i)
* = (ac) + i (bc)
* </pre>
*
* <p>This method is included for compatibility with ISO C99 which defines arithmetic between
* real-only and complex numbers.</p>
*
* <p>Note: This method should be preferred over using
* {@link #multiply(Complex) multiply(Complex.ofCartesian(factor, 0))}. Multiplication
* can generate signed zeros if either {@code this} complex has zeros for the real
* and/or imaginary component, or if the factor is zero. The summation of signed zeros
* in {@link #multiply(Complex)} may create zeros in the result that differ in sign
* from the equivalent call to multiply by a real-only number.
*
* @param factor value to be multiplied by this {@code Complex}.
* @return {@code this * factor}.
* @see #multiply(Complex)
*/
public Complex multiply(double factor) {
return new Complex(real * factor, imaginary * factor);
}
/**
* Returns a {@code Complex} whose value is {@code this * factor}, with {@code factor}
* interpreted as an imaginary number.
* Implements the formula:
* <pre>
* (a + i b) id = (a + i b)(0 + i d)
* = (-bd) + i (ad)
* </pre>
*
* <p>This method can be used to compute the multiplication of this complex number {@code z}
* by {@code i}. This should be used in preference to
* {@link #multiply(Complex) multiply(Complex.I)} with or without {@link #negate() negation}:</p>
*
* <pre>
* iz = (-b + i a) = this.multiply(1);
* -iz = (b + i -a) = this.multiply(-1);
* </pre>
*
* <p>This method is included for compatibility with ISO C99 which defines arithmetic between
* imaginary-only and complex numbers.</p>
*
* <p>Note: This method should be preferred over using
* {@link #multiply(Complex) multiply(Complex.ofCartesian(0, factor))}. Multiplication
* can generate signed zeros if either {@code this} complex has zeros for the real
* and/or imaginary component, or if the factor is zero. The summation of signed zeros
* in {@link #multiply(Complex)} may create zeros in the result that differ in sign
* from the equivalent call to multiply by an imaginary-only number.
*
* @param factor value to be multiplied by this {@code Complex}.
* @return {@code this * factor}.
* @see #multiply(Complex)
*/
public Complex multiplyImaginary(double factor) {
return new Complex(-imaginary * factor, real * factor);
}
/**
* Returns a {@code Complex} whose value is {@code (-this)}.
*
* @return {@code -this}.
*/
public Complex negate() {
return new Complex(-real, -imaginary);
}
/**
* Returns a {@code Complex} whose value is {@code (this - subtrahend)}.
* Implements the formula:
* <pre>
* (a + i b) - (c + i d) = (a - c) + i (b - d)
* </pre>
*
* @param subtrahend value to be subtracted from this {@code Complex}.
* @return {@code this - subtrahend}.
* @see <a href="http://mathworld.wolfram.com/ComplexSubtraction.html">Complex Subtraction</a>
*/
public Complex subtract(Complex subtrahend) {
return new Complex(real - subtrahend.real,
imaginary - subtrahend.imaginary);
}
/**
* Returns a {@code Complex} whose value is {@code (this - subtrahend)},
* with {@code subtrahend} interpreted as a real number.
* Implements the formula:
* <pre>
* (a + i b) - c = (a - c) + i b
* </pre>
*
* <p>This method is included for compatibility with ISO C99 which defines arithmetic between
* real-only and complex numbers.</p>
*
* @param subtrahend value to be subtracted from this {@code Complex}.
* @return {@code this - subtrahend}.
* @see #subtract(Complex)
*/
public Complex subtract(double subtrahend) {
return new Complex(real - subtrahend, imaginary);
}
/**
* Returns a {@code Complex} whose value is {@code (this - subtrahend)},
* with {@code subtrahend} interpreted as an imaginary number.
* Implements the formula:
* <pre>
* (a + i b) - i d = a + i (b - d)
* </pre>
*
* <p>This method is included for compatibility with ISO C99 which defines arithmetic between
* imaginary-only and complex numbers.</p>
*
* @param subtrahend value to be subtracted from this {@code Complex}.
* @return {@code this - subtrahend}.
* @see #subtract(Complex)
*/
public Complex subtractImaginary(double subtrahend) {
return new Complex(real, imaginary - subtrahend);
}
/**
* Returns a {@code Complex} whose value is {@code (minuend - this)},
* with {@code minuend} interpreted as a real number.
* Implements the formula:
* <pre>
* c - (a + i b) = (c - a) - i b
* </pre>
*
* <p>This method is included for compatibility with ISO C99 which defines arithmetic between
* real-only and complex numbers.</p>
*
* <p>Note: This method inverts the sign of the imaginary component {@code b} if it is {@code 0.0}.
* The sign would not be inverted if subtracting from {@code (c + i 0)} using
* {@link #subtract(Complex) Complex.ofCartesian(minuend, 0).subtract(this))} since
* {@code 0.0 - 0.0 = 0.0}.
*
* @param minuend value this {@code Complex} is to be subtracted from.
* @return {@code minuend - this}.
* @see #subtract(Complex)
* @see #ofCartesian(double, double)
*/
public Complex subtractFrom(double minuend) {
return new Complex(minuend - real, -imaginary);
}
/**
* Returns a {@code Complex} whose value is {@code (this - subtrahend)},
* with {@code minuend} interpreted as an imaginary number.
* Implements the formula:
* <pre>
* i d - (a + i b) = -a + i (d - b)
* </pre>
*
* <p>This method is included for compatibility with ISO C99 which defines arithmetic between
* imaginary-only and complex numbers.</p>
*
* <p>Note: This method inverts the sign of the real component {@code a} if it is {@code 0.0}.
* The sign would not be inverted if subtracting from {@code (0 + i d)} using
* {@link #subtract(Complex) Complex.ofCartesian(0, minuend).subtract(this))} since
* {@code 0.0 - 0.0 = 0.0}.
*
* @param minuend value this {@code Complex} is to be subtracted from.
* @return {@code this - subtrahend}.
* @see #subtract(Complex)
* @see #ofCartesian(double, double)
*/
public Complex subtractFromImaginary(double minuend) {
return new Complex(-real, minuend - imaginary);
}
/**
* Compute the
* <a href="http://mathworld.wolfram.com/InverseCosine.html">
* inverse cosine</a> of this complex number.
* Implements the formula:
* <pre>
* <code>
* acos(z) = (pi / 2) + i ln(iz + sqrt(1 - z<sup>2</sup>))
* </code>
* </pre>
*
* <p>This is implemented using real {@code x} and imaginary {@code y} parts:</p>
* <pre>
* <code>
* acos(z) = acos(B) - i ln(A + sqrt(A<sup>2</sup>-1))
* A = 0.5 [ sqrt((x+1)<sup>2</sup>+y<sup>2</sup>) + sqrt((x-1)<sup>2</sup>+y<sup>2</sup>) ]
* B = 0.5 [ sqrt((x+1)<sup>2</sup>+y<sup>2</sup>) - sqrt((x-1)<sup>2</sup>+y<sup>2</sup>) ]
* </code>
* </pre>
*
* <p>The implementation is based on the method described in:</p>
* <blockquote>
* T E Hull, Thomas F Fairgrieve and Ping Tak Peter Tang (1997)
* Implementing the complex Arcsine and Arccosine Functions using Exception Handling.
* ACM Transactions on Mathematical Software, Vol 23, No 3, pp 299-335.
* </blockquote>
*
* <p>The code has been adapted from the <a href="https://www.boost.org/">Boost</a>
* {@code c++} implementation {@code <boost/math/complex/acos.hpp>}. The function is well
* defined over the entire complex number range, and produces accurate values even at the
* extremes due to special handling of overflow and underflow conditions.</p>
*
* @return the inverse cosine of this complex number.
* @see <a href="http://functions.wolfram.com/ElementaryFunctions/ArcCos/">ArcCos</a>
*/
public Complex acos() {
return acos(real, imaginary, Complex::ofCartesian);
}
/**
* Compute the inverse cosine of the complex number.
*
* <p>This function exists to allow implementation of the identity
* {@code acosh(z) = +-i acos(z)}.<p>
*
* @param real Real part.
* @param imaginary Imaginary part.
* @param constructor Constructor.
* @return the inverse cosine of the complex number.
*/
private static Complex acos(final double real, final double imaginary,
final ComplexConstructor constructor) {
// Compute with positive values and determine sign at the end
final double x = Math.abs(real);
final double y = Math.abs(imaginary);
// The result (without sign correction)
double re;
double im;
// Handle C99 special cases
if (isPosInfinite(x)) {
if (isPosInfinite(y)) {
re = PI_OVER_4;
im = y;
} else if (Double.isNaN(y)) {
// sign of the imaginary part of the result is unspecified
return constructor.create(imaginary, real);
} else {
re = 0;
im = Double.POSITIVE_INFINITY;
}
} else if (Double.isNaN(x)) {
if (isPosInfinite(y)) {
return constructor.create(x, -imaginary);
}
// No-use of the input constructor
return NAN;
} else if (isPosInfinite(y)) {
re = PI_OVER_2;
im = y;
} else if (Double.isNaN(y)) {
return constructor.create(x == 0 ? PI_OVER_2 : y, y);
} else {
// Special case for real numbers:
if (y == 0 && x <= 1) {
return constructor.create(x == 0 ? PI_OVER_2 : Math.acos(real), -imaginary);
}
final double xp1 = x + 1;
final double xm1 = x - 1;
if (inRegion(x, y, SAFE_MIN, SAFE_MAX)) {
final double yy = y * y;
final double r = Math.sqrt(xp1 * xp1 + yy);
final double s = Math.sqrt(xm1 * xm1 + yy);
final double a = 0.5 * (r + s);
final double b = x / a;
if (b <= B_CROSSOVER) {
re = Math.acos(b);
} else {
final double apx = a + x;
if (x <= 1) {
re = Math.atan(Math.sqrt(0.5 * apx * (yy / (r + xp1) + (s - xm1))) / x);
} else {
re = Math.atan((y * Math.sqrt(0.5 * (apx / (r + xp1) + apx / (s + xm1)))) / x);
}
}
if (a <= A_CROSSOVER) {
double am1;
if (x < 1) {
am1 = 0.5 * (yy / (r + xp1) + yy / (s - xm1));
} else {
am1 = 0.5 * (yy / (r + xp1) + (s + xm1));
}
im = Math.log1p(am1 + Math.sqrt(am1 * (a + 1)));
} else {
im = Math.log(a + Math.sqrt(a * a - 1));
}
} else {
// Hull et al: Exception handling code from figure 6
if (y <= (EPSILON * Math.abs(xm1))) {
if (x < 1) {
re = Math.acos(x);
im = y / Math.sqrt(xp1 * (1 - x));
} else {
// This deviates from Hull et al's paper as per
// https://svn.boost.org/trac/boost/ticket/7290
if ((Double.MAX_VALUE / xp1) > xm1) {
// xp1 * xm1 won't overflow:
re = y / Math.sqrt(xm1 * xp1);
im = Math.log1p(xm1 + Math.sqrt(xp1 * xm1));
} else {
re = y / x;
im = LN_2 + Math.log(x);
}
}
} else if (y <= SAFE_MIN) {
// Hull et al: Assume x == 1.
// True if:
// E^2 > 8*sqrt(u)
//
// E = Machine epsilon: (1 + epsilon) = 1
// u = Double.MIN_NORMAL
re = Math.sqrt(y);
im = Math.sqrt(y);
} else if (EPSILON * y - 1 >= x) {
re = PI_OVER_2;
im = LN_2 + Math.log(y);
} else if (x > 1) {
re = Math.atan(y / x);
final double xoy = x / y;
im = LN_2 + Math.log(y) + 0.5 * Math.log1p(xoy * xoy);
} else {
re = PI_OVER_2;
final double a = Math.sqrt(1 + y * y);
im = 0.5 * Math.log1p(2 * y * (y + a));
}
}
}
return constructor.create(negative(real) ? Math.PI - re : re,
negative(imaginary) ? im : -im);
}
/**
* Compute the
* <a href="http://mathworld.wolfram.com/InverseSine.html">
* inverse sine</a> of this complex number.
* <pre>
* <code>
* asin(z) = -i (ln(iz + sqrt(1 - z<sup>2</sup>)))
* </code>
* </pre>
*
* <p>This is implemented using real {@code x} and imaginary {@code y} parts:</p>
* <pre>
* <code>
* asin(z) = asin(B) + i sign(y)ln(A + sqrt(A<sup>2</sup>-1))
* A = 0.5 [ sqrt((x+1)<sup>2</sup>+y<sup>2</sup>) + sqrt((x-1)<sup>2</sup>+y<sup>2</sup>) ]
* B = 0.5 [ sqrt((x+1)<sup>2</sup>+y<sup>2</sup>) - sqrt((x-1)<sup>2</sup>+y<sup>2</sup>) ]
* sign(y) = {@link Math#copySign(double,double) copySign(1.0, y)}
* </code>
* </pre>
*
* <p>The implementation is based on the method described in:</p>
* <blockquote>
* T E Hull, Thomas F Fairgrieve and Ping Tak Peter Tang (1997)
* Implementing the complex Arcsine and Arccosine Functions using Exception Handling.
* ACM Transactions on Mathematical Software, Vol 23, No 3, pp 299-335.
* </blockquote>
*
* <p>The code has been adapted from the <a href="https://www.boost.org/">Boost</a>
* {@code c++} implementation {@code <boost/math/complex/asin.hpp>}. The function is well
* defined over the entire complex number range, and produces accurate values even at the
* extremes due to special handling of overflow and underflow conditions.</p>
*
* @return the inverse sine of this complex number
* @see <a href="http://functions.wolfram.com/ElementaryFunctions/ArcSin/">ArcSin</a>
*/
public Complex asin() {
return asin(real, imaginary, Complex::ofCartesian);
}
/**
* Compute the inverse sine of the complex number.
*
* <p>This function exists to allow implementation of the identity
* {@code asinh(z) = -i asin(iz)}.<p>
*
* <p>The code has been adapted from the <a href="https://www.boost.org/">Boost</a>
* {@code c++} implementation {@code <boost/math/complex/asin.hpp>}.</p>
*
* @param real Real part.
* @param imaginary Imaginary part.
* @param constructor Constructor.
* @return the inverse sine of this complex number
*/
private static Complex asin(final double real, final double imaginary,
final ComplexConstructor constructor) {
// Compute with positive values and determine sign at the end
final double x = Math.abs(real);
final double y = Math.abs(imaginary);
// The result (without sign correction)
double re;
double im;
// Handle C99 special cases
if (Double.isNaN(x)) {
if (isPosInfinite(y)) {
re = x;
im = y;
} else {
// No-use of the input constructor
return NAN;
}
} else if (Double.isNaN(y)) {
if (x == 0) {
re = 0;
im = y;
} else if (isPosInfinite(x)) {
re = y;
im = x;
} else {
// No-use of the input constructor
return NAN;
}
} else if (isPosInfinite(x)) {
re = isPosInfinite(y) ? PI_OVER_4 : PI_OVER_2;
im = x;
} else if (isPosInfinite(y)) {
re = 0;
im = y;
} else {
// Special case for real numbers:
if (y == 0 && x <= 1) {
return constructor.create(Math.asin(real), imaginary);
}
final double xp1 = x + 1;
final double xm1 = x - 1;
if (inRegion(x, y, SAFE_MIN, SAFE_MAX)) {
final double yy = y * y;
final double r = Math.sqrt(xp1 * xp1 + yy);
final double s = Math.sqrt(xm1 * xm1 + yy);
final double a = 0.5 * (r + s);
final double b = x / a;
if (b <= B_CROSSOVER) {
re = Math.asin(b);
} else {
final double apx = a + x;
if (x <= 1) {
re = Math.atan(x / Math.sqrt(0.5 * apx * (yy / (r + xp1) + (s - xm1))));
} else {
re = Math.atan(x / (y * Math.sqrt(0.5 * (apx / (r + xp1) + apx / (s + xm1)))));
}
}
if (a <= A_CROSSOVER) {
double am1;
if (x < 1) {
am1 = 0.5 * (yy / (r + xp1) + yy / (s - xm1));
} else {
am1 = 0.5 * (yy / (r + xp1) + (s + xm1));
}
im = Math.log1p(am1 + Math.sqrt(am1 * (a + 1)));
} else {
im = Math.log(a + Math.sqrt(a * a - 1));
}
} else {
// Hull et al: Exception handling code from figure 3
if (y <= (EPSILON * Math.abs(xm1))) {
if (x < 1) {
re = Math.asin(x);
im = y / Math.sqrt(-xp1 * xm1);
} else {
re = PI_OVER_2;
if ((Double.MAX_VALUE / xp1) > xm1) {
// xp1 * xm1 won't overflow:
im = Math.log1p(xm1 + Math.sqrt(xp1 * xm1));
} else {
im = LN_2 + Math.log(x);
}
}
} else if (y <= SAFE_MIN) {
// Hull et al: Assume x == 1.
// True if:
// E^2 > 8*sqrt(u)
//
// E = Machine epsilon: (1 + epsilon) = 1
// u = Double.MIN_NORMAL
re = PI_OVER_2 - Math.sqrt(y);
im = Math.sqrt(y);
} else if (EPSILON * y - 1 >= x) {
// Possible underflow:
re = x / y;
im = LN_2 + Math.log(y);
} else if (x > 1) {
re = Math.atan(x / y);
final double xoy = x / y;
im = LN_2 + Math.log(y) + 0.5 * Math.log1p(xoy * xoy);
} else {
final double a = Math.sqrt(1 + y * y);
// Possible underflow:
re = x / a;
im = 0.5 * Math.log1p(2 * y * (y + a));
}
}
}
return constructor.create(changeSign(re, real),
changeSign(im, imaginary));
}
/**
* Compute the
* <a href="http://mathworld.wolfram.com/InverseTangent.html">
* inverse tangent</a> of this complex number.
* <pre>
* atan(z) = (i / 2) ln((i + z) / (i - z))
* </pre>
*
* <p>As per the C.99 standard this function is computed using the trigonomic identity:</p>
* <pre>
* atan(z) = -i atanh(iz)
* </pre>
*
* @return the inverse tangent of this complex number
* @see <a href="http://functions.wolfram.com/ElementaryFunctions/ArcTan/">ArcTan</a>
*/
public Complex atan() {
// Define in terms of atanh
// atan(z) = -i atanh(iz)
// Multiply this number by I, compute atanh, then multiply by back
return atanh(-imaginary, real, Complex::multiplyNegativeI);
}
/**
* Compute the
* <a href="http://mathworld.wolfram.com/InverseHyperbolicSine.html">
* inverse hyperbolic sine</a> of this complex number.
* Implements the formula:
* <pre>
* <code>
* asinh(z) = ln(z + sqrt(1 + z<sup>2</sup>))
* </code>
* </pre>
*
* <p>This is an odd function: {@code f(z) = -f(-z)}.
*
* <p>This function is computed using the trigonomic identity:</p>
* <pre>
* asinh(z) = -i asin(iz)
* </pre>
*
* @return the inverse hyperbolic sine of this complex number
* @see <a href="http://functions.wolfram.com/ElementaryFunctions/ArcSinh/">ArcSinh</a>
*/
public Complex asinh() {
// Define in terms of asin
// asinh(z) = -i asin(iz)
// Note: This is the opposite the the identity defined in the C.99 standard:
// asin(z) = -i asinh(iz)
// Multiply this number by I, compute asin, then multiply by back
return asin(-imaginary, real, Complex::multiplyNegativeI);
}
/**
* Compute the
* <a href="http://mathworld.wolfram.com/InverseHyperbolicTangent.html">
* inverse hyperbolic tangent</a> of this complex number.
* Implements the formula:
* <pre>
* atanh(z) = (1/2) ln((1 + z) / (1 - z))
* </pre>
*
* <p>This is an odd function: {@code f(z) = -f(-z)}.
*
* <p>This is implemented using real {@code x} and imaginary {@code y} parts:</p>
* <pre>
* <code>
* atanh(z) = 0.25 ln(1 + 4x/((1-x)<sup>2</sup>+y<sup>2</sup>) + i 0.5 tan<sup>-1</sup>(2y, 1-x<sup>2</sup>-y<sup>2</sup>)
* </code>
* </pre>
*
* <p>The code has been adapted from the <a href="https://www.boost.org/">Boost</a>
* {@code c++} implementation {@code <boost/math/complex/atanh.hpp>}. The function is well
* defined over the entire complex number range, and produces accurate values even at the
* extremes due to special handling of overflow and underflow conditions.</p>
*
* @return the inverse hyperbolic tangent of this complex number
* @see <a href="http://functions.wolfram.com/ElementaryFunctions/ArcTanh/">ArcTanh</a>
*/
public Complex atanh() {
return atanh(real, imaginary, Complex::ofCartesian);
}
/**
* Compute the inverse hyperbolic tangent of this complex number.
*
* <p>This function exists to allow implementation of the identity
* {@code atan(z) = -i atanh(iz)}.<p>
*
* @param real Real part.
* @param imaginary Imaginary part.
* @param constructor Constructor.
* @return the inverse hyperbolic tangent of the complex number
*/
private static Complex atanh(final double real, final double imaginary,
final ComplexConstructor constructor) {
// Compute with positive values and determine sign at the end
final double x = Math.abs(real);
final double y = Math.abs(imaginary);
// The result (without sign correction)
double re;
double im;
// Handle C99 special cases
if (Double.isNaN(x)) {
if (isPosInfinite(y)) {
// The sign of the real part of the result is unspecified
return constructor.create(0, Math.copySign(PI_OVER_2, imaginary));
}
// No-use of the input constructor.
// Optionally raises the ‘‘invalid’’ floating-point exception, for finite y.
return NAN;
} else if (Double.isNaN(y)) {
if (isPosInfinite(x)) {
return constructor.create(Math.copySign(0, real), Double.NaN);
}
if (x == 0) {
return constructor.create(real, Double.NaN);
}
// No-use of the input constructor
return NAN;
} else {
// x && y are finite or infinite.
// Check the safe region.
// The lower and upper bounds have been copied from boost::math::atanh.
// They are different from the safe region for asin and acos.
// x >= SAFE_UPPER: (1-x) == -x
// x <= SAFE_LOWER: 1 - x^2 = 1
if (inRegion(x, y, SAFE_LOWER, SAFE_UPPER)) {
// Normal computation within a safe region.
// minus x plus 1: (-x+1)
final double mxp1 = 1 - x;
final double yy = y * y;
// The definition of real component is:
// real = log( ((x+1)^2+y^2) / ((1-x)^2+y^2) ) / 4
// This simplifies by adding 1 and subtracting 1 as a fraction:
// = log(1 + ((x+1)^2+y^2) / ((1-x)^2+y^2) - ((1-x)^2+y^2)/((1-x)^2+y^2) ) / 4
//
// real(atanh(z)) == log(1 + 4*x / ((1-x)^2+y^2)) / 4
// imag(atanh(z)) == tan^-1 (2y, (1-x)(1+x) - y^2) / 2
// The division is done at the end of the function.
re = Math.log1p(4 * x / (mxp1 * mxp1 + yy));
im = Math.atan2(2 * y, mxp1 * (1 + x) - yy);
} else {
// This section handles exception cases that would normally cause
// underflow or overflow in the main formulas.
// C99. G.7: Special case for imaginary only numbers
if (x == 0) {
if (imaginary == 0) {
return constructor.create(real, imaginary);
}
// atanh(iy) = i atan(y)
return constructor.create(real, Math.atan(imaginary));
}
// Real part:
// real = Math.log1p(4x / ((1-x)^2 + y^2))
// real = Math.log1p(4x / (1 - 2x + x^2 + y^2))
// real = Math.log1p(4x / (1 + x(x-2) + y^2))
// without either overflow or underflow in the squared terms.
if (x >= SAFE_UPPER) {
// (1-x) = -x to machine precision:
// log1p(4x / (x^2 + y^2))
if (isPosInfinite(x) || isPosInfinite(y)) {
re = 0;
} else if (y >= SAFE_UPPER) {
// Big x and y: divide by x*y
re = Math.log1p((4 / y) / (x / y + y / x));
} else if (y > 1) {
// Big x: divide through by x:
re = Math.log1p(4 / (x + y * y / x));
} else {
// Big x small y, as above but neglect y^2/x:
re = Math.log1p(4 / x);
}
} else if (y >= SAFE_UPPER) {
if (x > 1) {
// Big y, medium x, divide through by y:
final double mxp1 = 1 - x;
re = Math.log1p((4 * x / y) / (mxp1 * mxp1 / y + y));
} else {
// Big y, small x, as above but neglect (1-x)^2/y:
// Note: log1p(v) == v - v^2/2 + v^3/3 ... Taylor series when v is small.
// Here v is so small only the first term matters.
re = 4 * x / y / y;
}
} else if (x == 1) {
// x = 1, small y:
// Special case when x == 1 as (1-x) is invalid.
// Simplify the following formula:
// real = log( sqrt((x+1)^2+y^2) ) / 2 - log( sqrt((1-x)^2+y^2) ) / 2
// = log( sqrt(4+y^2) ) / 2 - log(y) / 2
// if: 4+y^2 -> 4
// = log( 2 ) / 2 - log(y) / 2
// = (log(2) - log(y)) / 2
// Multiply by 2 as it will be divided by 4 at the end.
// C99: if y=0 raises the ‘‘divide-by-zero’’ floating-point exception.
re = 2 * (LN_2 - Math.log(y));
} else {
// Modified from boost which checks y > SAFE_LOWER.
// if y*y -> 0 it will be ignored so always include it.
final double mxp1 = 1 - x;
re = Math.log1p((4 * x) / (mxp1 * mxp1 + y * y));
}
// Imaginary part:
// imag = atan2(2y, (1-x)(1+x) - y^2)
// if x or y are large, then the formula:
// atan2(2y, (1-x)(1+x) - y^2)
// evaluates to +(PI - theta) where theta is negligible compared to PI.
if ((x >= SAFE_UPPER) || (y >= SAFE_UPPER)) {
im = Math.PI;
} else if (x <= SAFE_LOWER) {
// (1-x)^2 -> 1
if (y <= SAFE_LOWER) {
// 1 - y^2 -> 1
im = Math.atan2(2 * y, 1);
} else {
im = Math.atan2(2 * y, 1 - y * y);
}
} else {
// Medium x, small y.
// Modified from boost which checks (y == 0) && (x == 1) and sets re = 0.
// This is same as the result from calling atan2(0, 0) so just do that.
// 1 - y^2 = 1 so ignore subtracting y^2
im = Math.atan2(2 * y, (1 - x) * (1 + x));
}
}
}
re /= 4;
im /= 2;
return constructor.create(changeSign(re, real),
changeSign(im, imaginary));
}
/**
* Compute the
* <a href="http://mathworld.wolfram.com/InverseHyperbolicCosine.html">
* inverse hyperbolic cosine</a> of this complex number.
* <pre>
* acosh(z) = ln(z + sqrt(z + 1) sqrt(z - 1))
* </pre>
*
* <p>This function is computed using the trigonomic identity:</p>
* <pre>
* acosh(z) = +-i acos(z)
* </pre>
*
* <p>The sign of the multiplier is chosen to give {@code z.acosh().real() >= 0}
* and compatibility with the C.99 standard.</p>
*
* @return the inverse hyperbolic cosine of this complex number
* @see <a href="http://functions.wolfram.com/ElementaryFunctions/ArcCosh/">ArcCosh</a>
*/
public Complex acosh() {
// Define in terms of acos
// acosh(z) = +-i acos(z)
// Handle special case:
// acos(+-0 + iNaN) = π/2 + iNaN
// acosh(x + iNaN) = NaN + iNaN for all finite x (including zero)
if (Double.isNaN(imaginary) && Double.isFinite(real)) {
return NAN;
}
return acos(real, imaginary, (re, im) ->
// Set the sign appropriately for real >= 0
(negative(im)) ?
// Multiply by I
new Complex(-im, re) :
// Multiply by -I
new Complex(im, -re)
);
}
/**
* Compute the
* <a href="http://mathworld.wolfram.com/Cosine.html">
* cosine</a> of this complex number.
* Implements the formula:
* <pre>
* cos(a + b i) = cos(a)*cosh(b) - i sin(a)*sinh(b)
* </pre>
*
* <p>As per the C.99 standard this function is computed using the trigonomic identity:</p>
* <pre>
* cos(z) = cosh(iz)
* </pre>
*
* @return the cosine of this complex number.
* @see <a href="http://functions.wolfram.com/ElementaryFunctions/Cos/">Cos</a>
*/
public Complex cos() {
// Define in terms of cosh
// cos(z) = cosh(iz)
// Multiply this number by I and compute cosh.
return cosh(-imaginary, real, Complex::ofCartesian);
}
/**
* Compute the
* <a href="http://mathworld.wolfram.com/HyperbolicCosine.html">
* hyperbolic cosine</a> of this complex number.
* Implements the formula:
* <pre>
* cosh(a + b i) = cosh(a)cos(b) + i sinh(a)sin(b)
* </pre>
*
* <p>This is an even function: {@code f(z) = f(-z)}.
*
* @return the hyperbolic cosine of this complex number.
* @see <a href="http://functions.wolfram.com/ElementaryFunctions/Cosh/">Cosh</a>
*/
public Complex cosh() {
return cosh(real, imaginary, Complex::ofCartesian);
}
/**
* Compute the hyperbolic cosine of the complex number.
*
* <p>This function exists to allow implementation of the identity
* {@code cos(z) = cosh(iz)}.<p>
*
* @param real Real part.
* @param imaginary Imaginary part.
* @param constructor Constructor.
* @return the hyperbolic cosine of the complex number
*/
private static Complex cosh(double real, double imaginary, ComplexConstructor constructor) {
// ISO C99: Preserve the even function by mapping to positive
// f(z) = f(-z)
if (Double.isInfinite(real) && !Double.isFinite(imaginary)) {
return constructor.create(Math.abs(real), Double.NaN);
}
if (real == 0 && !Double.isFinite(imaginary)) {
return constructor.create(Double.NaN, changeSign(real, imaginary));
}
if (real == 0 && imaginary == 0) {
return constructor.create(1, changeSign(real, imaginary));
}
if (imaginary == 0 && !Double.isFinite(real)) {
return constructor.create(Math.abs(real), changeSign(imaginary, real));
}
return constructor.create(Math.cosh(real) * Math.cos(imaginary),
Math.sinh(real) * Math.sin(imaginary));
}
/**
* Compute the
* <a href="http://mathworld.wolfram.com/ExponentialFunction.html">
* exponential function</a> of this complex number.
* Implements the formula:
* <pre>
* exp(a + b i) = exp(a) (cos(b) + i sin(b))
* </pre>
*
* @return <code><i>e</i><sup>this</sup></code>.
* @see <a href="http://functions.wolfram.com/ElementaryFunctions/Exp/">Exp</a>
*/
public Complex exp() {
// Set the values used to compute exp(real) * cis(im)
double expReal;
double im = imaginary;
if (Double.isInfinite(real)) {
if (real < 0) {
expReal = 0;
if (!Double.isFinite(im)) {
// Preserve conjugate equality
im = Math.copySign(1, im);
}
} else {
if (im == 0 || !Double.isFinite(im)) {
return Double.isInfinite(im) ?
new Complex(real, Double.NaN) :
this;
}
expReal = real;
}
} else if (imaginary == 0) {
// Real-only number
return Double.isNaN(real) ?
this :
new Complex(Math.exp(real), imaginary);
} else if (Double.isNaN(real)) {
return NAN;
} else {
// real is finite
expReal = Math.exp(real);
}
return new Complex(expReal * Math.cos(im),
expReal * Math.sin(im));
}
/**
* Compute the
* <a href="http://mathworld.wolfram.com/NaturalLogarithm.html">
* natural logarithm</a> of this complex number.
* Implements the formula:
* <pre>
* ln(a + b i) = ln(|a + b i|) + i arg(a + b i)
* </pre>
*
* @return the natural logarithm of {@code this}.
* @see Math#log(double)
* @see #abs()
* @see #arg()
* @see <a href="http://functions.wolfram.com/ElementaryFunctions/Log/">Log</a>
*/
public Complex log() {
return log(Math::log, LN_2, Complex::ofCartesian);
}
/**
* Compute the base 10
* <a href="http://mathworld.wolfram.com/CommonLogarithm.html">
* common logarithm</a> of this complex number.
* Implements the formula:
* <pre>
* log10(a + bi) = log10(|a + b i|) + i arg(a + b i)
* </pre>
*
* @return the base 10 logarithm of {@code this}.
* @see Math#log10(double)
* @see #abs()
* @see #arg()
*/
public Complex log10() {
return log(Math::log10, LOG10_2, Complex::ofCartesian);
}
/**
* Compute the logarithm of this complex number using the provided function.
* Implements the formula:
* <pre>
* log(a + bi) = log(|a + b i|) + i arg(a + b i)
* </pre>
*
* <p>Warning: The argument {@code logOf2} must be equal to {@code log(2)} using the
* provided log function otherwise scaling using powers of 2 in the case of overflow
* will be incorrect. This is provided as an internal optimisation.
*
* @param log Log function.
* @param logOf2 The log function applied to 2.
* @param constructor Constructor for the returned complex.
* @return the logarithm of {@code this}.
* @see #abs()
* @see #arg()
*/
private Complex log(UnaryOperation log, double logOf2, ComplexConstructor constructor) {
// All ISO C99 edge cases satisfied by the Math library.
// Make computation overflow safe.
// Note:
// log(|a + b i|) = log(sqrt(a^2 + b^2)) = 0.5 * log(a^2 + b^2)
// If real and imaginary are with a safe region then omit the sqrt().
final double x = Math.abs(real);
final double y = Math.abs(imaginary);
// Use the safe region defined for atanh to avoid over/underflow for x^2
if (inRegion(x, y, SAFE_LOWER, SAFE_UPPER)) {
return constructor.create(0.5 * log.apply(x * x + y * y), arg());
}
final double abs = abs();
if (abs == Double.POSITIVE_INFINITY && isFinite()) {
// Edge-case where the |a + b i| overflows.
// |a + b i| = sqrt(a^2 + b^2)
// This can be scaled linearly.
// Scale the absolute and exploit:
// ln(abs / scale) = ln(abs) - ln(scale)
// ln(abs) = ln(abs / scale) + ln(scale)
// Use precise scaling with:
// scale ~ 2^exponent
final int exponent = getMaxExponent(real, imaginary);
// Implement scaling using 2^-exponent
final double absOs = Math.hypot(Math.scalb(real, -exponent), Math.scalb(imaginary, -exponent));
// log(2^exponent) = ln2(2^exponent) * log(2)
return constructor.create(log.apply(absOs) + exponent * logOf2, arg());
}
return constructor.create(log.apply(abs), arg());
}
/**
* Returns of value of this complex number raised to the power of {@code x}.
* Implements the formula:
* <pre>
* <code>
* y<sup>x</sup> = exp(x&middot;log(y))
* </code>
* </pre>
*
* <p>If this Complex is zero then this method returns zero if {@code x} is positive
* in the real component and zero in the imaginary component;
* otherwise it returns (NaN + i NaN).
*
* @param x exponent to which this {@code Complex} is to be raised.
* @return <code>this<sup>x</sup></code>.
* @see <a href="http://functions.wolfram.com/ElementaryFunctions/Power/">Power</a>
*/
public Complex pow(Complex x) {
if (real == 0 &&
imaginary == 0) {
// This value is zero. Test the other.
if (x.real > 0 &&
x.imaginary == 0) {
// 0 raised to positive number is 0
return ZERO;
}
// 0 raised to anything else is NaN
return NAN;
}
return log().multiply(x).exp();
}
/**
* Returns of value of this complex number raised to the power of {@code x}.
* Implements the formula:
* <pre>
* <code>
* y<sup>x</sup> = exp(x&middot;log(y))
* </code>
* </pre>
*
* <p>If this Complex is zero then this method returns zero if {@code x} is positive;
* otherwise it returns (NaN + i NaN).
*
* @param x exponent to which this {@code Complex} is to be raised.
* @return <code>this<sup>x</sup></code>.
* @see #pow(Complex)
* @see <a href="http://functions.wolfram.com/ElementaryFunctions/Power/">Power</a>
*/
public Complex pow(double x) {
if (real == 0 &&
imaginary == 0) {
// This value is zero. Test the other.
if (x > 0) {
// 0 raised to positive number is 0
return ZERO;
}
// 0 raised to anything else is NaN
return NAN;
}
return log().multiply(x).exp();
}
/**
* Compute the
* <a href="http://mathworld.wolfram.com/Sine.html">
* sine</a> of this complex number.
* Implements the formula:
* <pre>
* sin(a + b i) = sin(a)cosh(b) - i cos(a)sinh(b)
* </pre>
*
* <p>As per the C.99 standard this function is computed using the trigonomic identity:</p>
* <pre>
* sin(z) = -i sinh(iz)
* </pre>
*
* @return the sine of this complex number.
* @see <a href="http://functions.wolfram.com/ElementaryFunctions/Sin/">Sin</a>
*/
public Complex sin() {
// Define in terms of sinh
// sin(z) = -i sinh(iz)
// Multiply this number by I, compute sinh, then multiply by back
return sinh(-imaginary, real, Complex::multiplyNegativeI);
}
/**
* Compute the
* <a href="http://mathworld.wolfram.com/HyperbolicSine.html">
* hyperbolic sine</a> of this complex number.
* Implements the formula:
* <pre>
* sinh(a + b i) = sinh(a)cos(b)) + i cosh(a)sin(b)
* </pre>
*
* <p>This is an odd function: {@code f(z) = -f(-z)}.
*
* @return the hyperbolic sine of {@code this}.
* @see <a href="http://functions.wolfram.com/ElementaryFunctions/Sinh/">Sinh</a>
*/
public Complex sinh() {
return sinh(real, imaginary, Complex::ofCartesian);
}
/**
* Compute the hyperbolic sine of the complex number.
*
* <p>This function exists to allow implementation of the identity
* {@code sin(z) = -i sinh(iz)}.<p>
*
* @param real Real part.
* @param imaginary Imaginary part.
* @param constructor Constructor.
* @return the hyperbolic sine of the complex number
*/
private static Complex sinh(double real, double imaginary, ComplexConstructor constructor) {
if ((Double.isInfinite(real) && !Double.isFinite(imaginary)) ||
(real == 0 && !Double.isFinite(imaginary))) {
return constructor.create(real, Double.NaN);
}
if (imaginary == 0 && !Double.isFinite(real)) {
return constructor.create(real, imaginary);
}
return constructor.create(Math.sinh(real) * Math.cos(imaginary),
Math.cosh(real) * Math.sin(imaginary));
}
/**
* Compute the
* <a href="http://mathworld.wolfram.com/SquareRoot.html">
* square root</a> of this complex number.
* Implements the following algorithm to compute {@code sqrt(a + b i)}:
* <ol>
* <li>Let {@code t = sqrt((|a| + |a + b i|) / 2)}
* <li>if {@code (a >= 0)} return {@code t + (b / 2t) i}
* <li>else return {@code |b| / 2t + sign(b)t i }
* </ol>
* where:
* <ul>
* <li>{@code |a| = }{@link Math#abs}(a)
* <li>{@code |a + b i| = }{@link Complex#abs}(a + b i)
* <li>{@code sign(b) = }{@link Math#copySign(double,double) copySign(1.0, b)}
* </ul>
*
* @return the square root of {@code this}.
* @see <a href="http://functions.wolfram.com/ElementaryFunctions/Sqrt/">Sqrt</a>
*/
public Complex sqrt() {
return sqrt(real, imaginary);
}
/**
* Compute the square root of the complex number.
* Implements the following algorithm to compute {@code sqrt(a + b i)}:
* <ol>
* <li>Let {@code t = sqrt((|a| + |a + b i|) / 2)}
* <li>if {@code (a >= 0)} return {@code t + (b / 2t) i}
* <li>else return {@code |b| / 2t + sign(b)t i }
* </ol>
* where:
* <ul>
* <li>{@code |a| = }{@link Math#abs}(a)
* <li>{@code |a + b i| = }{@link Complex#abs}(a + b i)
* <li>{@code sign(b) = }{@link Math#copySign(double,double) copySign}(1.0, b)
* </ul>
*
* @param real Real component.
* @param imaginary Imaginary component.
* @return the square root of the complex number.
*/
private static Complex sqrt(double real, double imaginary) {
// Special case for infinite imaginary for all real including nan
if (Double.isInfinite(imaginary)) {
return new Complex(Double.POSITIVE_INFINITY, imaginary);
}
if (Double.isInfinite(real)) {
// imaginary is finite or NaN
final double part = Double.isNaN(imaginary) ? Double.NaN : 0;
if (real == Double.NEGATIVE_INFINITY) {
return new Complex(part, Math.copySign(Double.POSITIVE_INFINITY, imaginary));
}
return new Complex(Double.POSITIVE_INFINITY, Math.copySign(part, imaginary));
}
if (Double.isNaN(real) || Double.isNaN(imaginary)) {
return NAN;
}
// Finite real and imaginary
// Edge case for real numbers
if (imaginary == 0) {
final double sqrtAbs = Math.sqrt(Math.abs(real));
if (real < 0) {
return new Complex(0, Math.copySign(sqrtAbs, imaginary));
}
return new Complex(sqrtAbs, imaginary);
}
// Get the absolute of the real
final double absA = Math.abs(real);
// Compute |a + b i|
double absC = getAbsolute(real, imaginary);
// t = sqrt((|a| + |a + b i|) / 2)
// This is always representable as this complex is finite.
double t;
// Overflow safe
if (absC == Double.POSITIVE_INFINITY) {
// Complex is too large.
// Divide by the largest absolute component,
// compute the required sqrt and then scale back.
// Use the equality: sqrt(n) = sqrt(scale) * sqrt(n/scale)
// t = sqrt(max) * sqrt((|a|/max + |a + b i|/max) / 2)
// Note: The function may be non-monotonic at the junction.
// The alternative of returning infinity for a finite input is worse.
// Use precise scaling with:
// scale ~ 2^exponent
// Make exponent even for fast rescaling using sqrt(2^exponent).
final int exponent = getMaxExponent(absA, imaginary) & MASK_INT_TO_EVEN;
// Implement scaling using 2^-exponent
final double scaleA = Math.scalb(absA, -exponent);
final double scaleB = Math.scalb(imaginary, -exponent);
absC = getAbsolute(scaleA, scaleB);
// t = Math.sqrt(2^exponent) * Math.sqrt((scaleA + absC) / 2)
// This works if exponent is even:
// sqrt(2^exponent) = (2^exponent)^0.5 = 2^(exponent*0.5)
t = Math.scalb(Math.sqrt((scaleA + absC) / 2), exponent / 2);
} else {
// Over-flow safe average: absA < absC and abdC is finite.
t = Math.sqrt(absA + (absC - absA) / 2);
}
if (real >= 0) {
return new Complex(t, imaginary / (2 * t));
}
return new Complex(Math.abs(imaginary) / (2 * t),
Math.copySign(1.0, imaginary) * t);
}
/**
* Compute the
* <a href="http://mathworld.wolfram.com/Tangent.html">
* tangent</a> of this complex number.
* Implements the formula:
* <pre>
* tan(a + b i) = sin(2a)/(cos(2a)+cosh(2b)) + i [sinh(2b)/(cos(2a)+cosh(2b))]
* </pre>
*
* <p>As per the C.99 standard this function is computed using the trigonomic identity:</p>
* <pre>
* tan(z) = -i tanh(iz)
* </pre>
*
* @return the tangent of {@code this}.
* @see <a href="http://functions.wolfram.com/ElementaryFunctions/Tan/">Tangent</a>
*/
public Complex tan() {
// Define in terms of tanh
// tan(z) = -i tanh(iz)
// Multiply this number by I, compute tanh, then multiply by back
return tanh(-imaginary, real, Complex::multiplyNegativeI);
}
/**
* Compute the
* <a href="http://mathworld.wolfram.com/HyperbolicTangent.html">
* hyperbolic tangent</a> of this complex number.
* Implements the formula:
* <pre>
* tan(a + b i) = sinh(2a)/(cosh(2a)+cos(2b)) + i [sin(2b)/(cosh(2a)+cos(2b))]
* </pre>
*
* <p>This is an odd function: {@code f(z) = -f(-z)}.
*
* @return the hyperbolic tangent of {@code this}.
* @see <a href="http://functions.wolfram.com/ElementaryFunctions/Tanh/">Tanh</a>
*/
public Complex tanh() {
return tanh(real, imaginary, Complex::ofCartesian);
}
/**
* Compute the hyperbolic tangent of this complex number.
*
* <p>This function exists to allow implementation of the identity
* {@code tan(z) = -i tanh(iz)}.<p>
*
* @param real Real part.
* @param imaginary Imaginary part.
* @param constructor Constructor.
* @return the hyperbolic tangent of the complex number
*/
private static Complex tanh(double real, double imaginary, ComplexConstructor constructor) {
if (Double.isInfinite(real)) {
if (Double.isFinite(imaginary)) {
return constructor.create(Math.copySign(1, real), Math.copySign(0, sin2(imaginary)));
}
// imaginary is infinite or NaN
return constructor.create(Math.copySign(1, real), Math.copySign(0, imaginary));
}
if (real == 0) {
if (Double.isFinite(imaginary)) {
// Identity: sin x / (1 + cos x) = tan(x/2)
return constructor.create(real, Math.tan(imaginary));
}
return constructor.create(Double.NaN, Double.NaN);
}
if (imaginary == 0) {
if (Double.isNaN(real)) {
return constructor.create(Double.NaN, imaginary);
}
// Identity: sinh x / (1 + cosh x) = tanh(x/2)
return constructor.create(Math.tanh(real), imaginary);
}
final double real2 = 2 * real;
// Math.cosh returns positive infinity for infinity.
// cosh -> inf
final double divisor = Math.cosh(real2) + cos2(imaginary);
// Math.sinh returns the input infinity for infinity.
// sinh -> inf for positive x; else -inf
final double sinhRe2 = Math.sinh(real2);
// Avoid inf / inf
if (Double.isInfinite(sinhRe2) && Double.isInfinite(divisor)) {
// Handle as if real was infinite
return constructor.create(Math.copySign(1, real), Math.copySign(0, imaginary));
}
return constructor.create(sinhRe2 / divisor,
sin2(imaginary) / divisor);
}
/**
* Safely compute {@code cos(2*a)} when {@code a} is finite.
* Note that {@link Math#cos(double)} returns NaN when the input is infinite.
* If {@code 2*a} is finite use {@code Math.cos(2*a)}; otherwise use the identity:
* <pre>
* <code>
* cos(2a) = 2 cos<sup>2</sup>(a) - 1
* </code>
* </pre>
*
* @param a Angle a.
* @return the cosine of 2a
* @see Math#cos(double)
*/
private static double cos2(double a) {
final double twoA = 2 * a;
if (Double.isFinite(twoA)) {
return Math.cos(twoA);
}
final double cosA = Math.cos(a);
return 2 * cosA * cosA - 1;
}
/**
* Safely compute {@code sin(2*a)} when {@code a} is finite.
* Note that {@link Math#sin(double)} returns NaN when the input is infinite.
* If {@code 2*a} is finite use {@code Math.sin(2*a)}; otherwise use the identity:
* <pre>
* <code>
* sin(2a) = 2 sin(a) cos(a)
* </code>
* </pre>
*
* @param a Angle a.
* @return the sine of 2a
* @see Math#sin(double)
*/
private static double sin2(double a) {
final double twoA = 2 * a;
if (Double.isFinite(twoA)) {
return Math.sin(twoA);
}
return 2 * Math.sin(a) * Math.cos(a);
}
/**
* Compute the argument of this complex number.
*
* <p>The argument is the angle phi between the positive real axis and
* the point representing this number in the complex plane.
* The value returned is between -PI (not inclusive)
* and PI (inclusive), with negative values returned for numbers with
* negative imaginary parts.
*
* <p>If either real or imaginary part (or both) is NaN, NaN is returned.
* Infinite parts are handled as {@linkplain Math#atan2} handles them,
* essentially treating finite parts as zero in the presence of an
* infinite coordinate and returning a multiple of pi/4 depending on
* the signs of the infinite parts.
*
* <p>This code follows the
* <a href="http://www.iso-9899.info/wiki/The_Standard">ISO C Standard</a>, Annex G,
* in calculating the returned value using the {@code atan2(b, a)} method for complex
* {@code a + b i}.
*
* @return the argument of {@code this}.
* @see Math#atan2(double, double)
*/
public double arg() {
// Delegate
return Math.atan2(imaginary, real);
}
/**
* Computes the n-th roots of this complex number.
* The nth roots are defined by the formula:
* <pre>
* <code>
* z<sub>k</sub> = abs<sup>1/n</sup> (cos(phi + 2&pi;k/n) + i (sin(phi + 2&pi;k/n))
* </code>
* </pre>
* for <i>{@code k=0, 1, ..., n-1}</i>, where {@code abs} and {@code phi}
* are respectively the {@link #abs() modulus} and
* {@link #arg() argument} of this complex number.
*
* <p>If one or both parts of this complex number is NaN, a list with all
* all elements set to {@code NaN + NaN i} is returned.</p>
*
* @param n Degree of root.
* @return a List of all {@code n}-th roots of {@code this}.
* @throws IllegalArgumentException if {@code n} is zero.
* @see <a href="http://functions.wolfram.com/ElementaryFunctions/Root/">Root</a>
*/
public List<Complex> nthRoot(int n) {
if (n == 0) {
throw new IllegalArgumentException("cannot compute zeroth root");
}
final List<Complex> result = new ArrayList<>();
// nth root of abs -- faster / more accurate to use a solver here?
final double nthRootOfAbs = Math.pow(abs(), 1.0 / n);
// Compute nth roots of complex number with k = 0, 1, ... n-1
final double nthPhi = arg() / n;
final double slice = 2 * Math.PI / n;
double innerPart = nthPhi;
for (int k = 0; k < Math.abs(n); k++) {
// inner part
final double realPart = nthRootOfAbs * Math.cos(innerPart);
final double imaginaryPart = nthRootOfAbs * Math.sin(innerPart);
result.add(new Complex(realPart, imaginaryPart));
innerPart += slice;
}
return result;
}
/**
* Returns a string representation of the complex number.
*
* <p>The string will represent the numeric values of the real and imaginary parts.
* The values are split by a separator and surrounded by parentheses.
* The string can be {@link #parse(String) parsed} to obtain an instance with the same value.
*
* <p>The format for complex number {@code (a + i b)} is {@code "(a,b)"}, with {@code a} and
* {@code b} converted as if using {@link Double#toString(double)}.
*
* @return a string representation of the complex number.
* @see #parse(String)
* @see Double#toString(double)
*/
@Override
public String toString() {
return new StringBuilder(TO_STRING_SIZE)
.append(FORMAT_START)
.append(real).append(FORMAT_SEP)
.append(imaginary)
.append(FORMAT_END)
.toString();
}
/**
* Returns {@code true} if the values are equal according to semantics of
* {@link Double#equals(Object)}.
*
* @param x Value
* @param y Value
* @return {@code Double.valueof(x).equals(Double.valueOf(y))}
*/
private static boolean equals(double x, double y) {
return Double.doubleToLongBits(x) == Double.doubleToLongBits(y);
}
/**
* Check that a value is negative. It must meet all the following conditions:
* <ul>
* <li>it is not {@code NaN},</li>
* <li>it is negative signed,</li>
* </ul>
*
* <p>Note: This is true for negative zero.</p>
*
* @param d Value.
* @return {@code true} if {@code d} is negative.
*/
private static boolean negative(double d) {
return d < 0 || equals(d, -0.0);
}
/**
* Check that a value is positive infinity. Used to replace {@link Double#isInfinite()}
* when the input value is known to be positive (i.e. in the case where it have been
* set using {@link Math#abs(double)}).
*
* @param d Value.
* @return {@code true} if {@code d} is +inf.
*/
private static boolean isPosInfinite(double d) {
return d == Double.POSITIVE_INFINITY;
}
/**
* Create a complex number given the real and imaginary parts, then multiply by {@code -i}.
* This is used in functions that implement trigonomic identities. It is the functional
* equivalent of:
*
* <pre>
* z = new Complex(real, imaginary).multiplyImaginary(-1);
* </pre>
*
* @param real Real part.
* @param imaginary Imaginary part.
* @return {@code Complex} object
*/
private static Complex multiplyNegativeI(double real, double imaginary) {
return new Complex(imaginary, -real);
}
/**
* Change the sign of the magnitude based on the signed value.
*
* <p>If the signed value is negative then the result is {@code -magnitude}; otherwise
* return {@code magnitude}.
*
* <p>A signed value of {@code -0.0} is treated as negative. A signed value of {@code NaN}
* is treated as positive.
*
* <p>This is not the same as {@link Math#copySign(double, double)} as this method
* will change the sign based on the signed value rather than copy the sign.
*
* @param magnitude the magnitude
* @param signedValue the signed value
* @return magnitude or -magnitude
* @see #negative(double)
*/
private static double changeSign(double magnitude, double signedValue) {
return negative(signedValue) ? -magnitude : magnitude;
}
/**
* Returns the largest unbiased exponent used in the representation of the
* two numbers. Special cases:
*
* <ul>
* <li>If either argument is NaN or infinite, then the result is
* {@link Double#MAX_EXPONENT} + 1.
* <li>If both arguments are zero or subnormal, then the result is
* {@link Double#MIN_EXPONENT} -1.
* </ul>
*
* @param a the first value
* @param b the second value
* @return the maximum unbiased exponent of the values
* @see Math#getExponent(double)
*/
private static int getMaxExponent(double a, double b) {
// This could return:
// Math.getExponent(Math.max(Math.abs(a), Math.abs(b)))
// A speed test is required to determine performance.
return Math.max(Math.getExponent(a), Math.getExponent(b));
}
/**
* Checks if both x and y are in the region defined by the minimum and maximum.
*
* @param x x value.
* @param y y value.
* @param min the minimum (exclusive).
* @param max the maximum (exclusive).
* @return true if inside the region
*/
private static boolean inRegion(double x, double y, double min, double max) {
return (x < max) && (x > min) && (y < max) && (y > min);
}
/**
* Creates an exception.
*
* @param message Message prefix.
* @param error Input that caused the error.
* @param cause Underlying exception (if any).
* @return a new instance.
*/
private static NumberFormatException parsingException(String message,
Object error,
Throwable cause) {
// Not called with a null message or error
final StringBuilder sb = new StringBuilder(100)
.append(message)
.append(" '").append(error).append('\'');
if (cause != null) {
sb.append(": ").append(cause.getMessage());
}
return new NumberFormatException(sb.toString());
}
}