Complex.java
/*
* 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 java.util.function.DoubleUnaryOperator;
/**
* Cartesian representation of a complex number. The complex number is expressed
* in the form \( a + ib \) where \( a \) and \( b \) are real numbers and \( i \)
* is the imaginary unit which satisfies the equation \( i^2 = -1 \). For the
* complex number \( a + ib \), \( a \) is called the <em>real part</em> and
* \( b \) is called the <em>imaginary part</em>.
*
* <p>This class is immutable. All arithmetic will create a new instance for the
* result.</p>
*
* <p>Arithmetic in this class conforms to the C99 standard for complex numbers
* defined in ISO/IEC 9899, Annex G. Methods have been named using the equivalent
* method in ISO C99. The behavior for special cases is listed as defined in C99.</p>
*
* <p>For functions \( f \) which obey the conjugate equality \( conj(f(z)) = f(conj(z)) \),
* the specifications for the upper half-plane imply the specifications for the lower
* half-plane.</p>
*
* <p>For functions that are either odd, \( f(z) = -f(-z) \), or even, \( f(z) = f(-z) \),
* the specifications for the first quadrant imply the specifications for the other three
* quadrants.</p>
*
* <p>Special cases of <a href="http://mathworld.wolfram.com/BranchCut.html">branch cuts</a>
* for multivalued functions adopt the principle value convention from C99. Specials cases
* from C99 that raise the "invalid" or "divide-by-zero"
* <a href="https://en.cppreference.com/w/c/numeric/fenv/FE_exceptions">floating-point
* exceptions</a> return the documented value without an explicit mechanism to notify
* of the exception case, that is no exceptions are thrown during computations in-line with
* the convention of the corresponding single-valued functions in
* {@link Math Math}.
* These cases are documented in the method special cases as "invalid" or "divide-by-zero"
* floating-point operation.
* Note: Invalid floating-point exception cases will result in a complex number where the
* cardinality of NaN component parts has increased as a real or imaginary part could
* not be computed and is set to NaN.
*
* @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 \( i \), the square root of \( -1 \).
*
* <p>\( (0 + i 1) \).
*/
public static final Complex I = new Complex(0, 1);
/**
* A complex number representing one.
*
* <p>\( (1 + i 0) \).
*/
public static final Complex ONE = new Complex(1, 0);
/**
* A complex number representing zero.
*
* <p>\( (0 + i 0) \).
*/
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);
/** π/2. */
private static final double PI_OVER_2 = 0.5 * Math.PI;
/** π/4. */
private static final double PI_OVER_4 = 0.25 * Math.PI;
/** Natural logarithm of 2 (ln(2)). */
private static final double LN_2 = Math.log(2);
/** Base 10 logarithm of 10 divided by 2 (log10(e)/2). */
private static final double LOG_10E_O_2 = Math.log10(Math.E) / 2;
/** Base 10 logarithm of 2 (log10(2)). */
private static final double LOG10_2 = Math.log10(2);
/** {@code 1/2}. */
private static final double HALF = 0.5;
/** {@code sqrt(2)}. */
private static final double ROOT2 = 1.4142135623730951;
/** {@code 1.0 / sqrt(2)}.
* This is pre-computed to the closest double from the exact result.
* It is 1 ULP different from 1.0 / Math.sqrt(2) but equal to Math.sqrt(2) / 2.
*/
private static final double ONE_OVER_ROOT2 = 0.7071067811865476;
/** The bit representation of {@code -0.0}. */
private static final long NEGATIVE_ZERO_LONG_BITS = Double.doubleToLongBits(-0.0);
/** Exponent offset in IEEE754 representation. */
private static final int EXPONENT_OFFSET = 1023;
/**
* 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>.
* Copied from o.a.c.numbers.Precision.
*
* @see <a href="http://en.wikipedia.org/wiki/Machine_epsilon">Machine epsilon</a>
*/
private static final double EPSILON = Double.longBitsToDouble((EXPONENT_OFFSET - 53L) << 52);
/** Mask to remove the sign bit from a long. */
private static final long UNSIGN_MASK = 0x7fff_ffff_ffff_ffffL;
/** Mask to extract the 52-bit mantissa from a long representation of a double. */
private static final long MANTISSA_MASK = 0x000f_ffff_ffff_ffffL;
/** The multiplier used to split the double value into hi and low parts. This must be odd
* and a value of 2^s + 1 in the range {@code p/2 <= s <= p-1} where p is the number of
* bits of precision of the floating point number. Here {@code s = 27}.*/
private static final double MULTIPLIER = 1.34217729E8;
/**
* 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.0;
/** 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;
/** The safe maximum double value {@code x} to avoid overflow in sqrt. */
private static final double SQRT_SAFE_UPPER = Double.MAX_VALUE / 8;
/**
* A safe maximum double value {@code m} where {@code e^m} is not infinite.
* This can be used when functions require approximations of sinh(x) or cosh(x)
* when x is large using exp(x):
* <pre>
* sinh(x) = (e^x - e^-x) / 2 = sign(x) * e^|x| / 2
* cosh(x) = (e^x + e^-x) / 2 = e^|x| / 2 </pre>
*
* <p>This value can be used to approximate e^x using a product:
*
* <pre>
* e^x = product_n (e^m) * e^(x-nm)
* n = (int) x/m
* e.g. e^2000 = e^m * e^m * e^(2000 - 2m) </pre>
*
* <p>The value should be below ln(max_value) ~ 709.783.
* The value m is set to an integer for less error when subtracting m and chosen as
* even (m=708) as it is used as a threshold in tanh with m/2.
*
* <p>The value is used to compute e^x multiplied by a small number avoiding
* overflow (sinh/cosh) or a small number divided by e^x without underflow due to
* infinite e^x (tanh). The following conditions are used:
* <pre>
* 0.5 * e^m * Double.MIN_VALUE * e^m * e^m = Infinity
* 2.0 / e^m / e^m = 0.0 </pre>
*/
private static final double SAFE_EXP = 708;
/**
* The value of Math.exp(SAFE_EXP): e^708.
* To be used in overflow/underflow safe products of e^m to approximate e^x where {@code x > m}.
*/
private static final double EXP_M = Math.exp(SAFE_EXP);
/** 54 shifted 20-bits to align with the exponent of the upper 32-bits of a double. */
private static final int EXP_54 = 0x36_00000;
/** Represents an exponent of 500 in unbiased form shifted 20-bits to align with the upper 32-bits of a double. */
private static final int EXP_500 = 0x5f3_00000;
/** Represents an exponent of 1024 in unbiased form (infinite or nan)
* shifted 20-bits to align with the upper 32-bits of a double. */
private static final int EXP_1024 = 0x7ff_00000;
/** Represents an exponent of -500 in unbiased form shifted 20-bits to align with the upper 32-bits of a double. */
private static final int EXP_NEG_500 = 0x20b_00000;
/** 2^600. */
private static final double TWO_POW_600 = 0x1.0p+600;
/** 2^-600. */
private static final double TWO_POW_NEG_600 = 0x1.0p-600;
/** 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);
}
/**
* 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} (\( \rho \))
* and phase angle {@code theta} (\( \theta \)).
*
* \[ \begin{aligned}
* x &= \rho \cos(\theta) \\
* y &= \rho \sin(\theta) \end{aligned} \]
*
* <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 {@code rho} 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 when {@code theta} is in the range
* \( -\pi\ \lt \theta \leq \pi \):
*
* <pre>
* Complex.ofPolar(rho, theta).abs() == rho
* Complex.ofPolar(rho, theta).arg() == theta</pre>
*
* <p>If {@code rho} is infinite then the resulting parts may be infinite or NaN
* following the rules for double arithmetic, for example:</p>
*
* <ul>
* <li>{@code ofPolar(}\( -0.0 \){@code , }\( 0 \){@code ) = }\( \text{NaN} + i \text{NaN} \)
* <li>{@code ofPolar(}\( 0.0 \){@code , }\( 0 \){@code ) = }\( 0 + i 0 \)
* <li>{@code ofPolar(}\( 1 \){@code , }\( 0 \){@code ) = }\( 1 + i 0 \)
* <li>{@code ofPolar(}\( 1 \){@code , }\( \pi \){@code ) = }\( -1 + i \sin(\pi) \)
* <li>{@code ofPolar(}\( \infty \){@code , }\( \pi \){@code ) = }\( -\infty + i \infty \)
* <li>{@code ofPolar(}\( \infty \){@code , }\( 0 \){@code ) = }\( -\infty + i \text{NaN} \)
* <li>{@code ofPolar(}\( \infty \){@code , }\( -\frac{\pi}{4} \){@code ) = }\( \infty - i \infty \)
* <li>{@code ofPolar(}\( \infty \){@code , }\( 5\frac{\pi}{4} \){@code ) = }\( -\infty - i \infty \)
* </ul>
*
* <p>This method is the functional equivalent of the C++ method {@code std::polar}.
*
* @param rho The modulus of the complex number.
* @param theta The argument of the complex number.
* @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:
*
* \[ \text{cis}(x) = e^{ix} = \cos(x) + i \sin(x) \]
*
* @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 parentheses 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(-1.23, 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 new NumberFormatException(
parsingExceptionMsg("Input too short, expected format",
FORMAT_START + "x" + FORMAT_SEP + "y" + FORMAT_END, s));
}
// Confirm start: '('
if (s.charAt(0) != FORMAT_START) {
throw new NumberFormatException(
parsingExceptionMsg("Expected start delimiter", FORMAT_START, s));
}
// Confirm end: ')'
if (s.charAt(len - 1) != FORMAT_END) {
throw new NumberFormatException(
parsingExceptionMsg("Expected end delimiter", FORMAT_END, s));
}
// 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 new NumberFormatException(
parsingExceptionMsg("Expected separator between two numbers", FORMAT_SEP, s));
}
// Should be no more separators
if (s.indexOf(FORMAT_SEP, sep + 1) != -1) {
throw new NumberFormatException(
parsingExceptionMsg("Incorrect number of parts, expected only 2 using separator",
FORMAT_SEP, s));
}
// 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 new NumberFormatException(
parsingExceptionMsg("Could not parse real part", rePart, s));
}
final String imPart = s.substring(sep + 1, len - 1);
final double im;
try {
im = Double.parseDouble(imPart);
} catch (final NumberFormatException ex) {
throw new NumberFormatException(
parsingExceptionMsg("Could not parse imaginary part", imPart, s));
}
return ofCartesian(re, im);
}
/**
* Creates an exception message.
*
* @param message Message prefix.
* @param error Input that caused the error.
* @param s String representation.
* @return A message.
*/
private static String parsingExceptionMsg(String message,
Object error,
String s) {
final StringBuilder sb = new StringBuilder(100)
.append(message)
.append(" '").append(error)
.append("' for input \"").append(s).append('"');
return sb.toString();
}
/**
* Gets the real part \( a \) of this complex number \( (a + i b) \).
*
* @return The real part.
*/
public double getReal() {
return real;
}
/**
* Gets the real part \( a \) of this complex number \( (a + i b) \).
*
* <p>This method is the equivalent of the C++ method {@code std::complex::real}.
*
* @return The real part.
* @see #getReal()
*/
public double real() {
return getReal();
}
/**
* Gets the imaginary part \( b \) of this complex number \( (a + i b) \).
*
* @return The imaginary part.
*/
public double getImaginary() {
return imaginary;
}
/**
* Gets the imaginary part \( b \) of this complex number \( (a + i b) \).
*
* <p>This method is the equivalent of the C++ method {@code std::complex::imag}.
*
* @return The imaginary part.
* @see #getImaginary()
*/
public double imag() {
return getImaginary();
}
/**
* Returns the absolute value of this complex number. This is also called complex norm, modulus,
* or magnitude.
*
* <p>\[ \text{abs}(x + i y) = \sqrt{(x^2 + y^2)} \]
*
* <p>Special cases:
*
* <ul>
* <li>{@code abs(x + iy) == abs(y + ix) == abs(x - iy)}.
* <li>If {@code z} is ±∞ + iy for any y, returns +∞.
* <li>If {@code z} is x + iNaN for non-infinite x, returns NaN.
* <li>If {@code z} is x + i0, returns |x|.
* </ul>
*
* <p>The cases ensure that 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 follows the
* <a href="http://www.iso-9899.info/wiki/The_Standard">ISO C Standard</a>, Annex G,
* in calculating the returned value without intermediate overflow or underflow.
*
* <p>The computed result will be within 1 ulp of the exact result.
*
* @return The absolute value.
* @see #isInfinite()
* @see #isNaN()
* @see <a href="http://mathworld.wolfram.com/ComplexModulus.html">Complex modulus</a>
*/
public double abs() {
return abs(real, imaginary);
}
/**
* Returns the absolute value of the complex number.
* <pre>abs(x + i y) = sqrt(x^2 + y^2)</pre>
*
* <p>This should satisfy the special cases of the hypot function in ISO C99 F.9.4.3:
* "The hypot functions compute the square root of the sum of the squares of x and y,
* without undue overflow or underflow."
*
* <ul>
* <li>hypot(x, y), hypot(y, x), and hypot(x, −y) are equivalent.
* <li>hypot(x, ±0) is equivalent to |x|.
* <li>hypot(±∞, y) returns +∞, even if y is a NaN.
* </ul>
*
* <p>This method is called by all methods that require the absolute value of the complex
* number, e.g. abs(), sqrt() and log().
*
* @param real Real part.
* @param imaginary Imaginary part.
* @return The absolute value.
*/
private static double abs(double real, double imaginary) {
// Specialised implementation of hypot.
// See NUMBERS-143
return hypot(real, imaginary);
}
/**
* Returns 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, then the result is NaN.
* 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 \( \frac{\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(y, x)} method for complex
* \( x + iy \).
*
* @return The argument of this complex number.
* @see Math#atan2(double, double)
*/
public double arg() {
// Delegate
return Math.atan2(imaginary, real);
}
/**
* Returns the squared norm value of this complex number. This is also called the absolute
* square.
*
* <p>\[ \text{norm}(x + i y) = x^2 + y^2 \]
*
* <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>Note: This method may not return the same value as the square of {@link #abs()} as
* that method uses an extended precision computation.
*
* <p>{@code norm()} can be used as a faster alternative than {@code abs()} for ranking by
* magnitude. If used for ranking any overflow to infinity will create an 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 {@code true} if either the real <em>or</em> imaginary component of the complex number is NaN
* <em>and</em> the complex number is not infinite.
*
* <p>Note that:
* <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 {@code true} if either real or imaginary component of the complex number is infinite.
*
* <p>Note: A complex number with at least one infinite part is regarded
* as an infinity (even if its other part is a NaN).
*
* @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 {@code true} if both real and imaginary component of the complex number 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 the
* <a href="http://mathworld.wolfram.com/ComplexConjugate.html">conjugate</a>
* \( \overline{z} \) of this complex number \( z \).
*
* <p>\[ \begin{aligned}
* z &= a + i b \\
* \overline{z} &= a - i b \end{aligned}\]
*
* @return The conjugate (\( \overline{z} \)) of this complex number.
*/
public Complex conj() {
return new Complex(real, -imaginary);
}
/**
* Returns a {@code Complex} whose value is the negation of both the real and imaginary parts
* of complex number \( z \).
*
* <p>\[ \begin{aligned}
* z &= a + i b \\
* -z &= -a - i b \end{aligned} \]
*
* @return \( -z \).
*/
public Complex negate() {
return new Complex(-real, -imaginary);
}
/**
* Returns the projection of this complex number onto the Riemann sphere.
*
* <p>\( z \) projects to \( 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 \( z \) has an infinite part, then {@code z.proj()} shall be equivalent to:
*
* <pre>return Complex.ofCartesian(Double.POSITIVE_INFINITY, Math.copySign(0.0, z.imag());</pre>
*
* @return \( 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;
}
/**
* Returns a {@code Complex} whose value is {@code (this + addend)}.
* Implements the formula:
*
* <p>\[ (a + i b) + (c + i d) = (a + c) + i (b + d) \]
*
* @param addend Value to be added to this complex number.
* @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:
*
* <p>\[ (a + i b) + c = (a + c) + i b \]
*
* <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 \( b \) if it is {@code -0.0}.
* The sign would be lost if adding \( (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 complex number.
* @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:
*
* <p>\[ (a + i b) + i d = a + i (b + d) \]
*
* <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 \( a \) if it is {@code -0.0}.
* The sign would be lost if adding \( (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 complex number.
* @return {@code this + addend}.
* @see #add(Complex)
* @see #ofCartesian(double, double)
*/
public Complex addImaginary(double addend) {
return new Complex(real, imaginary + addend);
}
/**
* Returns a {@code Complex} whose value is {@code (this - subtrahend)}.
* Implements the formula:
*
* <p>\[ (a + i b) - (c + i d) = (a - c) + i (b - d) \]
*
* @param subtrahend Value to be subtracted from this complex number.
* @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:
*
* <p>\[ (a + i b) - c = (a - c) + i b \]
*
* <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 complex number.
* @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:
*
* <p>\[ (a + i b) - i d = a + i (b - d) \]
*
* <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 complex number.
* @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:
* \[ c - (a + i b) = (c - a) - i b \]
*
* <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 \( b \) if it is {@code 0.0}.
* The sign would not be inverted if subtracting from \( 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 complex number 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:
* \[ i d - (a + i b) = -a + i (d - b) \]
*
* <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 \( a \) if it is {@code 0.0}.
* The sign would not be inverted if subtracting from \( 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 complex number 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);
}
/**
* Returns a {@code Complex} whose value is {@code this * factor}.
* Implements the formula:
*
* <p>\[ (a + i b)(c + i d) = (ac - bd) + i (ad + bc) \]
*
* <p>Recalculates to recover infinities as specified in C99 standard G.5.1.
*
* @param factor Value to be multiplied by this complex number.
* @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 C99 standard G.5.1.
*
* @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 C99 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;
}
if ((Double.isInfinite(c) || Double.isInfinite(d)) &&
isNotZero(a, b)) {
// The 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:
*
* <p>\[ (a + i b) c = (ac) + i (bc) \]
*
* <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 complex number.
* @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:
*
* <p>\[ (a + i b) id = (-bd) + i (ad) \]
*
* <p>This method can be used to compute the multiplication of this complex number \( z \)
* by \( i \) using a factor with magnitude 1.0. This should be used in preference to
* {@link #multiply(Complex) multiply(Complex.I)} with or without {@link #negate() negation}:</p>
*
* \[ \begin{aligned}
* iz &= (-b + i a) \\
* -iz &= (b - i a) \end{aligned} \]
*
* <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 complex number.
* @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 / divisor)}.
* Implements the formula:
*
* <p>\[ \frac{a + i b}{c + i d} = \frac{(ac + bd) + i (bc - ad)}{c^2+d^2} \]
*
* <p>Re-calculates NaN result values to recover infinities as specified in C99 standard G.5.1.
*
* @param divisor Value by which this complex number 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 C99
* 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 parts to the range [1, 2).
final int exponent = getScale(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;
// Note: Modification from the listing in ISO C99 G.5.1 (8):
// Avoid overflow if a or b are very big.
// Since (c, d) in the range [1, 2) the sum (ac + bd) could overflow
// when (a, b) are both above (Double.MAX_VALUE / 4). The same applies to
// (bc - ad) with large negative values.
// Use the maximum exponent as an approximation to the magnitude.
if (getMaxExponent(a, b) > Double.MAX_EXPONENT - 2) {
ilogbw -= 2;
a /= 4;
b /= 4;
}
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, ...
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 Complex.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 * (a * c + b * d);
y = Double.POSITIVE_INFINITY * (b * c - a * 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 * (a * c + b * d);
y = 0.0 * (b * c - a * d);
}
}
return new Complex(x, y);
}
/**
* Returns a {@code Complex} whose value is {@code (this / divisor)},
* with {@code divisor} interpreted as a real number.
* Implements the formula:
*
* <p>\[ \frac{a + i b}{c} = \frac{a}{c} + i \frac{b}{c} \]
*
* <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 infinite. 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 complex number 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:
*
* <p>\[ \frac{a + i b}{id} = \frac{b}{d} - i \frac{a}{d} \]
*
* <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 infinite. 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 infinite values may be negated.
*
* @param divisor Value by which this complex number 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
* <a href="http://mathworld.wolfram.com/ExponentialFunction.html">
* exponential function</a> of this complex number.
*
* <p>\[ \exp(z) = e^z \]
*
* <p>The exponential function of \( z \) is an entire function in the complex plane.
* Special cases:
*
* <ul>
* <li>{@code z.conj().exp() == z.exp().conj()}.
* <li>If {@code z} is ±0 + i0, returns 1 + i0.
* <li>If {@code z} is x + i∞ for finite x, returns NaN + iNaN ("invalid" floating-point operation).
* <li>If {@code z} is x + iNaN for finite x, returns NaN + iNaN ("invalid" floating-point operation).
* <li>If {@code z} is +∞ + i0, returns +∞ + i0.
* <li>If {@code z} is −∞ + iy for finite y, returns +0 cis(y) (see {@link #ofCis(double)}).
* <li>If {@code z} is +∞ + iy for finite nonzero y, returns +∞ cis(y).
* <li>If {@code z} is −∞ + i∞, returns ±0 ± i0 (where the signs of the real and imaginary parts of the result are unspecified).
* <li>If {@code z} is +∞ + i∞, returns ±∞ + iNaN (where the sign of the real part of the result is unspecified; "invalid" floating-point operation).
* <li>If {@code z} is −∞ + iNaN, returns ±0 ± i0 (where the signs of the real and imaginary parts of the result are unspecified).
* <li>If {@code z} is +∞ + iNaN, returns ±∞ + iNaN (where the sign of the real part of the result is unspecified).
* <li>If {@code z} is NaN + i0, returns NaN + i0.
* <li>If {@code z} is NaN + iy for all nonzero numbers y, returns NaN + iNaN ("invalid" floating-point operation).
* <li>If {@code z} is NaN + iNaN, returns NaN + iNaN.
* </ul>
*
* <p>Implements the formula:
*
* <p>\[ \exp(x + iy) = e^x (\cos(y) + i \sin(y)) \]
*
* @return The exponential of this complex number.
* @see <a href="http://functions.wolfram.com/ElementaryFunctions/Exp/">Exp</a>
*/
public Complex exp() {
if (Double.isInfinite(real)) {
// Set the scale factor applied to cis(y)
final double zeroOrInf;
if (real < 0) {
if (!Double.isFinite(imaginary)) {
// (−∞ + i∞) or (−∞ + iNaN) returns (±0 ± i0) (where the signs of the
// real and imaginary parts of the result are unspecified).
// Here we preserve the conjugate equality.
return new Complex(0, Math.copySign(0, imaginary));
}
// (−∞ + iy) returns +0 cis(y), for finite y
zeroOrInf = 0;
} else {
// (+∞ + i0) returns +∞ + i0.
if (imaginary == 0) {
return this;
}
// (+∞ + i∞) or (+∞ + iNaN) returns (±∞ + iNaN) and raises the invalid
// floating-point exception (where the sign of the real part of the
// result is unspecified).
if (!Double.isFinite(imaginary)) {
return new Complex(real, Double.NaN);
}
// (+∞ + iy) returns (+∞ cis(y)), for finite nonzero y.
zeroOrInf = real;
}
return new Complex(zeroOrInf * Math.cos(imaginary),
zeroOrInf * Math.sin(imaginary));
} else if (Double.isNaN(real)) {
// (NaN + i0) returns (NaN + i0)
// (NaN + iy) returns (NaN + iNaN) and optionally raises the invalid floating-point exception
// (NaN + iNaN) returns (NaN + iNaN)
return imaginary == 0 ? this : NAN;
} else if (!Double.isFinite(imaginary)) {
// (x + i∞) or (x + iNaN) returns (NaN + iNaN) and raises the invalid
// floating-point exception, for finite x.
return NAN;
}
// real and imaginary are finite.
// Compute e^a * (cos(b) + i sin(b)).
// Special case:
// (±0 + i0) returns (1 + i0)
final double exp = Math.exp(real);
if (imaginary == 0) {
return new Complex(exp, imaginary);
}
return new Complex(exp * Math.cos(imaginary),
exp * Math.sin(imaginary));
}
/**
* Returns the
* <a href="http://mathworld.wolfram.com/NaturalLogarithm.html">
* natural logarithm</a> of this complex number.
*
* <p>The natural logarithm of \( z \) is unbounded along the real axis and
* in the range \( [-\pi, \pi] \) along the imaginary axis. The imaginary part of the
* natural logarithm has a branch cut along the negative real axis \( (-infty,0] \).
* Special cases:
*
* <ul>
* <li>{@code z.conj().log() == z.log().conj()}.
* <li>If {@code z} is −0 + i0, returns −∞ + iπ ("divide-by-zero" floating-point operation).
* <li>If {@code z} is +0 + i0, returns −∞ + i0 ("divide-by-zero" floating-point operation).
* <li>If {@code z} is x + i∞ for finite x, returns +∞ + iπ/2.
* <li>If {@code z} is x + iNaN for finite x, returns NaN + iNaN ("invalid" floating-point operation).
* <li>If {@code z} is −∞ + iy for finite positive-signed y, returns +∞ + iπ.
* <li>If {@code z} is +∞ + iy for finite positive-signed y, returns +∞ + i0.
* <li>If {@code z} is −∞ + i∞, returns +∞ + i3π/4.
* <li>If {@code z} is +∞ + i∞, returns +∞ + iπ/4.
* <li>If {@code z} is ±∞ + iNaN, returns +∞ + iNaN.
* <li>If {@code z} is NaN + iy for finite y, returns NaN + iNaN ("invalid" floating-point operation).
* <li>If {@code z} is NaN + i∞, returns +∞ + iNaN.
* <li>If {@code z} is NaN + iNaN, returns NaN + iNaN.
* </ul>
*
* <p>Implements the formula:
*
* <p>\[ \ln(z) = \ln |z| + i \arg(z) \]
*
* <p>where \( |z| \) is the absolute and \( \arg(z) \) is the argument.
*
* <p>The implementation is based on the method described in:</p>
* <blockquote>
* T E Hull, Thomas F Fairgrieve and Ping Tak Peter Tang (1994)
* Implementing complex elementary functions using exception handling.
* ACM Transactions on Mathematical Software, Vol 20, No 2, pp 215-244.
* </blockquote>
*
* @return The natural logarithm of this complex number.
* @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, HALF, LN_2, Complex::ofCartesian);
}
/**
* Returns the base 10
* <a href="http://mathworld.wolfram.com/CommonLogarithm.html">
* common logarithm</a> of this complex number.
*
* <p>The common logarithm of \( z \) is unbounded along the real axis and
* in the range \( [-\pi, \pi] \) along the imaginary axis. The imaginary part of the
* common logarithm has a branch cut along the negative real axis \( (-infty,0] \).
* Special cases are as defined in the {@link #log() natural logarithm}:
*
* <p>Implements the formula:
*
* <p>\[ \log_{10}(z) = \log_{10} |z| + i \arg(z) \]
*
* <p>where \( |z| \) is the absolute and \( \arg(z) \) is the argument.
*
* @return The base 10 logarithm of this complex number.
* @see Math#log10(double)
* @see #abs()
* @see #arg()
*/
public Complex log10() {
return log(Math::log10, LOG_10E_O_2, LOG10_2, Complex::ofCartesian);
}
/**
* Returns the logarithm of this complex number using the provided function.
* Implements the formula:
*
* <pre>
* log(x + i y) = log(|x + i y|) + i arg(x + i y)</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 logOfeOver2 The log function applied to e, then divided by 2.
* @param logOf2 The log function applied to 2.
* @param constructor Constructor for the returned complex.
* @return The logarithm of this complex number.
* @see #abs()
* @see #arg()
*/
private Complex log(DoubleUnaryOperator log, double logOfeOver2, double logOf2, ComplexConstructor constructor) {
// Handle NaN
if (Double.isNaN(real) || Double.isNaN(imaginary)) {
// Return NaN unless infinite
if (isInfinite()) {
return constructor.create(Double.POSITIVE_INFINITY, Double.NaN);
}
// No-use of the input constructor
return NAN;
}
// Returns the real part:
// log(sqrt(x^2 + y^2))
// log(x^2 + y^2) / 2
// Compute with positive values
double x = Math.abs(real);
double y = Math.abs(imaginary);
// Find the larger magnitude.
if (x < y) {
final double tmp = x;
x = y;
y = tmp;
}
if (x == 0) {
// Handle zero: raises the ‘‘divide-by-zero’’ floating-point exception.
return constructor.create(Double.NEGATIVE_INFINITY,
negative(real) ? Math.copySign(Math.PI, imaginary) : imaginary);
}
double re;
// This alters the implementation of Hull et al (1994) which used a standard
// precision representation of |z|: sqrt(x*x + y*y).
// This formula should use the same definition of the magnitude returned
// by Complex.abs() which is a high precision computation with scaling.
// The checks for overflow thus only require ensuring the output of |z|
// will not overflow or underflow.
if (x > HALF && x < ROOT2) {
// x^2+y^2 close to 1. Use log1p(x^2+y^2 - 1) / 2.
re = Math.log1p(x2y2m1(x, y)) * logOfeOver2;
} else {
// Check for over/underflow in |z|
// When scaling:
// log(a / b) = log(a) - log(b)
// So initialize the result with the log of the scale factor.
re = 0;
if (x > Double.MAX_VALUE / 2) {
// Potential overflow.
if (isPosInfinite(x)) {
// Handle infinity
return constructor.create(x, arg());
}
// Scale down.
x /= 2;
y /= 2;
// log(2)
re = logOf2;
} else if (y < Double.MIN_NORMAL) {
// Potential underflow.
if (y == 0) {
// Handle real only number
return constructor.create(log.applyAsDouble(x), arg());
}
// Scale up sub-normal numbers to make them normal by scaling by 2^54,
// i.e. more than the mantissa digits.
x *= 0x1.0p54;
y *= 0x1.0p54;
// log(2^-54) = -54 * log(2)
re = -54 * logOf2;
}
re += log.applyAsDouble(abs(x, y));
}
// All ISO C99 edge cases for the imaginary are satisfied by the Math library.
return constructor.create(re, arg());
}
/**
* Returns the complex power of this complex number raised to the power of {@code x}.
* Implements the formula:
*
* <p>\[ z^x = e^{x \ln(z)} \]
*
* <p>If this complex number 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 + iNaN.
*
* @param x The exponent to which this complex number is to be raised.
* @return This complex number raised to the power of {@code x}.
* @see #log()
* @see #multiply(Complex)
* @see #exp()
* @see <a href="http://mathworld.wolfram.com/ComplexExponentiation.html">Complex exponentiation</a>
* @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 the complex power of this complex number raised to the power of {@code x},
* with {@code x} interpreted as a real number.
* Implements the formula:
*
* <p>\[ z^x = e^{x \ln(z)} \]
*
* <p>If this complex number is zero then this method returns zero if {@code x} is positive;
* otherwise it returns NaN + iNaN.
*
* @param x The exponent to which this complex number is to be raised.
* @return This complex number raised to the power of {@code x}.
* @see #log()
* @see #multiply(double)
* @see #exp()
* @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();
}
/**
* Returns the
* <a href="http://mathworld.wolfram.com/SquareRoot.html">
* square root</a> of this complex number.
*
* <p>\[ \sqrt{x + iy} = \frac{1}{2} \sqrt{2} \left( \sqrt{ \sqrt{x^2 + y^2} + x } + i\ \text{sgn}(y) \sqrt{ \sqrt{x^2 + y^2} - x } \right) \]
*
* <p>The square root of \( z \) is in the range \( [0, +\infty) \) along the real axis and
* is unbounded along the imaginary axis. The imaginary part of the square root has a
* branch cut along the negative real axis \( (-infty,0) \). Special cases:
*
* <ul>
* <li>{@code z.conj().sqrt() == z.sqrt().conj()}.
* <li>If {@code z} is ±0 + i0, returns +0 + i0.
* <li>If {@code z} is x + i∞ for all x (including NaN), returns +∞ + i∞.
* <li>If {@code z} is x + iNaN for finite x, returns NaN + iNaN ("invalid" floating-point operation).
* <li>If {@code z} is −∞ + iy for finite positive-signed y, returns +0 + i∞.
* <li>If {@code z} is +∞ + iy for finite positive-signed y, returns +∞ + i0.
* <li>If {@code z} is −∞ + iNaN, returns NaN ± i∞ (where the sign of the imaginary part of the result is unspecified).
* <li>If {@code z} is +∞ + iNaN, returns +∞ + iNaN.
* <li>If {@code z} is NaN + iy for finite y, returns NaN + iNaN ("invalid" floating-point operation).
* <li>If {@code z} is NaN + iNaN, returns NaN + iNaN.
* </ul>
*
* <p>Implements the following algorithm to compute \( \sqrt{x + iy} \):
* <ol>
* <li>Let \( t = \sqrt{2 (|x| + |x + iy|)} \)
* <li>if \( x \geq 0 \) return \( \frac{t}{2} + i \frac{y}{t} \)
* <li>else return \( \frac{|y|}{t} + i\ \text{sgn}(y) \frac{t}{2} \)
* </ol>
* where:
* <ul>
* <li>\( |x| =\ \){@link Math#abs(double) abs}(x)
* <li>\( |x + y i| =\ \){@link Complex#abs}
* <li>\( \text{sgn}(y) =\ \){@link Math#copySign(double,double) copySign}(1.0, y)
* </ul>
*
* <p>The implementation is overflow and underflow safe based on the method described in:</p>
* <blockquote>
* T E Hull, Thomas F Fairgrieve and Ping Tak Peter Tang (1994)
* Implementing complex elementary functions using exception handling.
* ACM Transactions on Mathematical Software, Vol 20, No 2, pp 215-244.
* </blockquote>
*
* @return The square root of this complex number.
* @see <a href="http://functions.wolfram.com/ElementaryFunctions/Sqrt/">Sqrt</a>
*/
public Complex sqrt() {
return sqrt(real, imaginary);
}
/**
* Returns the square root of the complex number {@code sqrt(x + i y)}.
*
* @param real Real component.
* @param imaginary Imaginary component.
* @return The square root of the complex number.
*/
private static Complex sqrt(double real, double imaginary) {
// Handle NaN
if (Double.isNaN(real) || Double.isNaN(imaginary)) {
// Check for infinite
if (Double.isInfinite(imaginary)) {
return new Complex(Double.POSITIVE_INFINITY, imaginary);
}
if (Double.isInfinite(real)) {
if (real == Double.NEGATIVE_INFINITY) {
return new Complex(Double.NaN, Math.copySign(Double.POSITIVE_INFINITY, imaginary));
}
return new Complex(Double.POSITIVE_INFINITY, Double.NaN);
}
return NAN;
}
// Compute with positive values and determine sign at the end
final double x = Math.abs(real);
final double y = Math.abs(imaginary);
// Compute
final double t;
// This alters the implementation of Hull et al (1994) which used a standard
// precision representation of |z|: sqrt(x*x + y*y).
// This formula should use the same definition of the magnitude returned
// by Complex.abs() which is a high precision computation with scaling.
// Worry about overflow if 2 * (|z| + |x|) will overflow.
// Worry about underflow if |z| or |x| are sub-normal components.
if (inRegion(x, y, Double.MIN_NORMAL, SQRT_SAFE_UPPER)) {
// No over/underflow
t = Math.sqrt(2 * (abs(x, y) + x));
} else {
// Potential over/underflow. First check infinites and real/imaginary only.
// Check for infinite
if (isPosInfinite(y)) {
return new Complex(Double.POSITIVE_INFINITY, imaginary);
} else if (isPosInfinite(x)) {
if (real == Double.NEGATIVE_INFINITY) {
return new Complex(0, Math.copySign(Double.POSITIVE_INFINITY, imaginary));
}
return new Complex(Double.POSITIVE_INFINITY, Math.copySign(0, imaginary));
} else if (y == 0) {
// Real only
final double sqrtAbs = Math.sqrt(x);
if (real < 0) {
return new Complex(0, Math.copySign(sqrtAbs, imaginary));
}
return new Complex(sqrtAbs, imaginary);
} else if (x == 0) {
// Imaginary only. This sets the two components to the same magnitude.
// Note: In polar coordinates this does not happen:
// real = sqrt(abs()) * Math.cos(arg() / 2)
// imag = sqrt(abs()) * Math.sin(arg() / 2)
// arg() / 2 = pi/4 and cos and sin should both return sqrt(2)/2 but
// are different by 1 ULP.
final double sqrtAbs = Math.sqrt(y) * ONE_OVER_ROOT2;
return new Complex(sqrtAbs, Math.copySign(sqrtAbs, imaginary));
} else {
// Over/underflow.
// Full scaling is not required as this is done in the hypotenuse function.
// Keep the number as big as possible for maximum precision in the second sqrt.
// Note if we scale by an even power of 2, we can re-scale by sqrt of the number.
// a = sqrt(b)
// a = sqrt(b/4) * sqrt(4)
final double rescale;
final double sx;
final double sy;
if (Math.max(x, y) > SQRT_SAFE_UPPER) {
// Overflow. Scale down by 16 and rescale by sqrt(16).
sx = x / 16;
sy = y / 16;
rescale = 4;
} else {
// Sub-normal numbers. Make them normal by scaling by 2^54,
// i.e. more than the mantissa digits, and rescale by sqrt(2^54) = 2^27.
sx = x * 0x1.0p54;
sy = y * 0x1.0p54;
rescale = 0x1.0p-27;
}
t = rescale * Math.sqrt(2 * (abs(sx, sy) + sx));
}
}
if (real >= 0) {
return new Complex(t / 2, imaginary / t);
}
return new Complex(y / t, Math.copySign(t / 2, imaginary));
}
/**
* Returns the
* <a href="http://mathworld.wolfram.com/Sine.html">
* sine</a> of this complex number.
*
* <p>\[ \sin(z) = \frac{1}{2} i \left( e^{-iz} - e^{iz} \right) \]
*
* <p>This is an odd function: \( \sin(z) = -\sin(-z) \).
* The sine is an entire function and requires no branch cuts.
*
* <p>This is implemented using real \( x \) and imaginary \( y \) parts:
*
* <p>\[ \sin(x + iy) = \sin(x)\cosh(y) + i \cos(x)\sinh(y) \]
*
* <p>As per the C99 standard this function is computed using the trigonomic identity:
*
* <p>\[ \sin(z) = -i \sinh(iz) \]
*
* @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);
}
/**
* Returns the
* <a href="http://mathworld.wolfram.com/Cosine.html">
* cosine</a> of this complex number.
*
* <p>\[ \cos(z) = \frac{1}{2} \left( e^{iz} + e^{-iz} \right) \]
*
* <p>This is an even function: \( \cos(z) = \cos(-z) \).
* The cosine is an entire function and requires no branch cuts.
*
* <p>This is implemented using real \( x \) and imaginary \( y \) parts:
*
* <p>\[ \cos(x + iy) = \cos(x)\cosh(y) - i \sin(x)\sinh(y) \]
*
* <p>As per the C99 standard this function is computed using the trigonomic identity:
*
* <p>\[ cos(z) = cosh(iz) \]
*
* @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);
}
/**
* Returns the
* <a href="http://mathworld.wolfram.com/Tangent.html">
* tangent</a> of this complex number.
*
* <p>\[ \tan(z) = \frac{i(e^{-iz} - e^{iz})}{e^{-iz} + e^{iz}} \]
*
* <p>This is an odd function: \( \tan(z) = -\tan(-z) \).
* The tangent is an entire function and requires no branch cuts.
*
* <p>This is implemented using real \( x \) and imaginary \( y \) parts:</p>
* \[ \tan(x + iy) = \frac{\sin(2x)}{\cos(2x)+\cosh(2y)} + i \frac{\sinh(2y)}{\cos(2x)+\cosh(2y)} \]
*
* <p>As per the C99 standard this function is computed using the trigonomic identity:</p>
* \[ \tan(z) = -i \tanh(iz) \]
*
* @return The tangent of this complex number.
* @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);
}
/**
* Returns the
* <a href="http://mathworld.wolfram.com/InverseSine.html">
* inverse sine</a> of this complex number.
*
* <p>\[ \sin^{-1}(z) = - i \left(\ln{iz + \sqrt{1 - z^2}}\right) \]
*
* <p>The inverse sine of \( z \) is unbounded along the imaginary axis and
* in the range \( [-\pi, \pi] \) along the real axis. Special cases are handled
* as if the operation is implemented using \( \sin^{-1}(z) = -i \sinh^{-1}(iz) \).
*
* <p>The inverse sine is a multivalued function and requires a branch cut in
* the complex plane; the cut is conventionally placed at the line segments
* \( (\infty,-1) \) and \( (1,\infty) \) of the real axis.
*
* <p>This is implemented using real \( x \) and imaginary \( y \) parts:
*
* <p>\[ \begin{aligned}
* \sin^{-1}(z) &= \sin^{-1}(B) + i\ \text{sgn}(y)\ln \left(A + \sqrt{A^2-1} \right) \\
* A &= \frac{1}{2} \left[ \sqrt{(x+1)^2+y^2} + \sqrt{(x-1)^2+y^2} \right] \\
* B &= \frac{1}{2} \left[ \sqrt{(x+1)^2+y^2} - \sqrt{(x-1)^2+y^2} \right] \end{aligned} \]
*
* <p>where \( \text{sgn}(y) \) is the sign function implemented using
* {@link Math#copySign(double,double) copySign(1.0, y)}.
*
* <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>}.
*
* @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);
}
/**
* Returns the inverse sine of the complex number.
*
* <p>This function exists to allow implementation of the identity
* {@code asinh(z) = -i asin(iz)}.
*
* <p>Adapted from {@code <boost/math/complex/asin.hpp>}. This method only (and not
* invoked methods within) is distributed under the Boost Software License V1.0.
* The original notice is shown below and the licence is shown in full in LICENSE:
* <pre>
* (C) Copyright John Maddock 2005.
* Distributed under the Boost Software License, Version 1.0. (See accompanying
* file LICENSE or copy at https://www.boost.org/LICENSE_1_0.txt)
* </pre>
*
* @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)
final double re;
final 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) {
final 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 4
if (y <= (EPSILON * Math.abs(xm1))) {
if (x < 1) {
re = Math.asin(x);
im = y / Math.sqrt(xp1 * (1 - x));
} 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));
}
/**
* Returns the
* <a href="http://mathworld.wolfram.com/InverseCosine.html">
* inverse cosine</a> of this complex number.
*
* <p>\[ \cos^{-1}(z) = \frac{\pi}{2} + i \left(\ln{iz + \sqrt{1 - z^2}}\right) \]
*
* <p>The inverse cosine of \( z \) is in the range \( [0, \pi) \) along the real axis and
* unbounded along the imaginary axis. Special cases:
*
* <ul>
* <li>{@code z.conj().acos() == z.acos().conj()}.
* <li>If {@code z} is ±0 + i0, returns π/2 − i0.
* <li>If {@code z} is ±0 + iNaN, returns π/2 + iNaN.
* <li>If {@code z} is x + i∞ for finite x, returns π/2 − i∞.
* <li>If {@code z} is x + iNaN, returns NaN + iNaN ("invalid" floating-point operation).
* <li>If {@code z} is −∞ + iy for positive-signed finite y, returns π − i∞.
* <li>If {@code z} is +∞ + iy for positive-signed finite y, returns +0 − i∞.
* <li>If {@code z} is −∞ + i∞, returns 3π/4 − i∞.
* <li>If {@code z} is +∞ + i∞, returns π/4 − i∞.
* <li>If {@code z} is ±∞ + iNaN, returns NaN ± i∞ where the sign of the imaginary part of the result is unspecified.
* <li>If {@code z} is NaN + iy for finite y, returns NaN + iNaN ("invalid" floating-point operation).
* <li>If {@code z} is NaN + i∞, returns NaN − i∞.
* <li>If {@code z} is NaN + iNaN, returns NaN + iNaN.
* </ul>
*
* <p>The inverse cosine is a multivalued function and requires a branch cut in
* the complex plane; the cut is conventionally placed at the line segments
* \( (-\infty,-1) \) and \( (1,\infty) \) of the real axis.
*
* <p>This function is implemented using real \( x \) and imaginary \( y \) parts:
*
* <p>\[ \begin{aligned}
* \cos^{-1}(z) &= \cos^{-1}(B) - i\ \text{sgn}(y) \ln\left(A + \sqrt{A^2-1}\right) \\
* A &= \frac{1}{2} \left[ \sqrt{(x+1)^2+y^2} + \sqrt{(x-1)^2+y^2} \right] \\
* B &= \frac{1}{2} \left[ \sqrt{(x+1)^2+y^2} - \sqrt{(x-1)^2+y^2} \right] \end{aligned} \]
*
* <p>where \( \text{sgn}(y) \) is the sign function implemented using
* {@link Math#copySign(double,double) copySign(1.0, y)}.
*
* <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>}.
*
* @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);
}
/**
* Returns the inverse cosine of the complex number.
*
* <p>This function exists to allow implementation of the identity
* {@code acosh(z) = +-i acos(z)}.
*
* <p>Adapted from {@code <boost/math/complex/acos.hpp>}. This method only (and not
* invoked methods within) is distributed under the Boost Software License V1.0.
* The original notice is shown below and the licence is shown in full in LICENSE:
* <pre>
* (C) Copyright John Maddock 2005.
* Distributed under the Boost Software License, Version 1.0. (See accompanying
* file LICENSE or copy at https://www.boost.org/LICENSE_1_0.txt)
* </pre>
*
* @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)
final double re;
final 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) {
final 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);
}
/**
* Returns the
* <a href="http://mathworld.wolfram.com/InverseTangent.html">
* inverse tangent</a> of this complex number.
*
* <p>\[ \tan^{-1}(z) = \frac{i}{2} \ln \left( \frac{i + z}{i - z} \right) \]
*
* <p>The inverse hyperbolic tangent of \( z \) is unbounded along the imaginary axis and
* in the range \( [-\pi/2, \pi/2] \) along the real axis.
*
* <p>The inverse tangent is a multivalued function and requires a branch cut in
* the complex plane; the cut is conventionally placed at the line segments
* \( (i \infty,-i] \) and \( [i,i \infty) \) of the imaginary axis.
*
* <p>As per the C99 standard this function is computed using the trigonomic identity:
* \[ \tan^{-1}(z) = -i \tanh^{-1}(iz) \]
*
* @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);
}
/**
* Returns the
* <a href="http://mathworld.wolfram.com/HyperbolicSine.html">
* hyperbolic sine</a> of this complex number.
*
* <p>\[ \sinh(z) = \frac{1}{2} \left( e^{z} - e^{-z} \right) \]
*
* <p>The hyperbolic sine of \( z \) is an entire function in the complex plane
* and is periodic with respect to the imaginary component with period \( 2\pi i \).
* Special cases:
*
* <ul>
* <li>{@code z.conj().sinh() == z.sinh().conj()}.
* <li>This is an odd function: \( \sinh(z) = -\sinh(-z) \).
* <li>If {@code z} is +0 + i0, returns +0 + i0.
* <li>If {@code z} is +0 + i∞, returns ±0 + iNaN (where the sign of the real part of the result is unspecified; "invalid" floating-point operation).
* <li>If {@code z} is +0 + iNaN, returns ±0 + iNaN (where the sign of the real part of the result is unspecified).
* <li>If {@code z} is x + i∞ for positive finite x, returns NaN + iNaN ("invalid" floating-point operation).
* <li>If {@code z} is x + iNaN for finite nonzero x, returns NaN + iNaN ("invalid" floating-point operation).
* <li>If {@code z} is +∞ + i0, returns +∞ + i0.
* <li>If {@code z} is +∞ + iy for positive finite y, returns +∞ cis(y) (see {@link #ofCis(double)}.
* <li>If {@code z} is +∞ + i∞, returns ±∞ + iNaN (where the sign of the real part of the result is unspecified; "invalid" floating-point operation).
* <li>If {@code z} is +∞ + iNaN, returns ±∞ + iNaN (where the sign of the real part of the result is unspecified).
* <li>If {@code z} is NaN + i0, returns NaN + i0.
* <li>If {@code z} is NaN + iy for all nonzero numbers y, returns NaN + iNaN ("invalid" floating-point operation).
* <li>If {@code z} is NaN + iNaN, returns NaN + iNaN.
* </ul>
*
* <p>This is implemented using real \( x \) and imaginary \( y \) parts:
*
* <p>\[ \sinh(x + iy) = \sinh(x)\cos(y) + i \cosh(x)\sin(y) \]
*
* @return The hyperbolic sine of this complex number.
* @see <a href="http://functions.wolfram.com/ElementaryFunctions/Sinh/">Sinh</a>
*/
public Complex sinh() {
return sinh(real, imaginary, Complex::ofCartesian);
}
/**
* Returns 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)) {
return constructor.create(real, Double.NaN);
}
if (real == 0) {
// Imaginary-only sinh(iy) = i sin(y).
if (Double.isFinite(imaginary)) {
// Maintain periodic property with respect to the imaginary component.
// sinh(+/-0.0) * cos(+/-x) = +/-0 * cos(x)
return constructor.create(changeSign(real, Math.cos(imaginary)),
Math.sin(imaginary));
}
// If imaginary is inf/NaN the sign of the real part is unspecified.
// Returning the same real value maintains the conjugate equality.
// It is not possible to also maintain the odd function (hence the unspecified sign).
return constructor.create(real, Double.NaN);
}
if (imaginary == 0) {
// Real-only sinh(x).
return constructor.create(Math.sinh(real), imaginary);
}
final double x = Math.abs(real);
if (x > SAFE_EXP) {
// Approximate sinh/cosh(x) using exp^|x| / 2
return coshsinh(x, real, imaginary, true, constructor);
}
// No overflow of sinh/cosh
return constructor.create(Math.sinh(real) * Math.cos(imaginary),
Math.cosh(real) * Math.sin(imaginary));
}
/**
* Returns the
* <a href="http://mathworld.wolfram.com/HyperbolicCosine.html">
* hyperbolic cosine</a> of this complex number.
*
* <p>\[ \cosh(z) = \frac{1}{2} \left( e^{z} + e^{-z} \right) \]
*
* <p>The hyperbolic cosine of \( z \) is an entire function in the complex plane
* and is periodic with respect to the imaginary component with period \( 2\pi i \).
* Special cases:
*
* <ul>
* <li>{@code z.conj().cosh() == z.cosh().conj()}.
* <li>This is an even function: \( \cosh(z) = \cosh(-z) \).
* <li>If {@code z} is +0 + i0, returns 1 + i0.
* <li>If {@code z} is +0 + i∞, returns NaN ± i0 (where the sign of the imaginary part of the result is unspecified; "invalid" floating-point operation).
* <li>If {@code z} is +0 + iNaN, returns NaN ± i0 (where the sign of the imaginary part of the result is unspecified).
* <li>If {@code z} is x + i∞ for finite nonzero x, returns NaN + iNaN ("invalid" floating-point operation).
* <li>If {@code z} is x + iNaN for finite nonzero x, returns NaN + iNaN ("invalid" floating-point operation).
* <li>If {@code z} is +∞ + i0, returns +∞ + i0.
* <li>If {@code z} is +∞ + iy for finite nonzero y, returns +∞ cis(y) (see {@link #ofCis(double)}).
* <li>If {@code z} is +∞ + i∞, returns ±∞ + iNaN (where the sign of the real part of the result is unspecified).
* <li>If {@code z} is +∞ + iNaN, returns +∞ + iNaN.
* <li>If {@code z} is NaN + i0, returns NaN ± i0 (where the sign of the imaginary part of the result is unspecified).
* <li>If {@code z} is NaN + iy for all nonzero numbers y, returns NaN + iNaN ("invalid" floating-point operation).
* <li>If {@code z} is NaN + iNaN, returns NaN + iNaN.
* </ul>
*
* <p>This is implemented using real \( x \) and imaginary \( y \) parts:
*
* <p>\[ \cosh(x + iy) = \cosh(x)\cos(y) + i \sinh(x)\sin(y) \]
*
* @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);
}
/**
* Returns 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) {
// Imaginary-only cosh(iy) = cos(y).
if (Double.isFinite(imaginary)) {
// Maintain periodic property with respect to the imaginary component.
// sinh(+/-0.0) * sin(+/-x) = +/-0 * sin(x)
return constructor.create(Math.cos(imaginary),
changeSign(real, Math.sin(imaginary)));
}
// If imaginary is inf/NaN the sign of the imaginary part is unspecified.
// Although not required by C99 changing the sign maintains the conjugate equality.
// It is not possible to also maintain the even function (hence the unspecified sign).
return constructor.create(Double.NaN, changeSign(real, imaginary));
}
if (imaginary == 0) {
// Real-only cosh(x).
// Change sign to preserve conjugate equality and even function.
// sin(+/-0) * sinh(+/-x) = +/-0 * +/-a (sinh is monotonic and same sign)
// => change the sign of imaginary using real. Handles special case of infinite real.
// If real is NaN the sign of the imaginary part is unspecified.
return constructor.create(Math.cosh(real), changeSign(imaginary, real));
}
final double x = Math.abs(real);
if (x > SAFE_EXP) {
// Approximate sinh/cosh(x) using exp^|x| / 2
return coshsinh(x, real, imaginary, false, constructor);
}
// No overflow of sinh/cosh
return constructor.create(Math.cosh(real) * Math.cos(imaginary),
Math.sinh(real) * Math.sin(imaginary));
}
/**
* Compute cosh or sinh when the absolute real component |x| is large. In this case
* cosh(x) and sinh(x) can be approximated by exp(|x|) / 2:
*
* <pre>
* cosh(x+iy) real = (e^|x| / 2) * cos(y)
* cosh(x+iy) imag = (e^|x| / 2) * sin(y) * sign(x)
* sinh(x+iy) real = (e^|x| / 2) * cos(y) * sign(x)
* sinh(x+iy) imag = (e^|x| / 2) * sin(y)
* </pre>
*
* @param x Absolute real component |x|.
* @param real Real part (x).
* @param imaginary Imaginary part (y).
* @param sinh Set to true to compute sinh, otherwise cosh.
* @param constructor Constructor.
* @return The hyperbolic sine/cosine of the complex number.
*/
private static Complex coshsinh(double x, double real, double imaginary, boolean sinh,
ComplexConstructor constructor) {
// Always require the cos and sin.
double re = Math.cos(imaginary);
double im = Math.sin(imaginary);
// Compute the correct function
if (sinh) {
re = changeSign(re, real);
} else {
im = changeSign(im, real);
}
// Multiply by (e^|x| / 2).
// Overflow safe computation since sin/cos can be very small allowing a result
// when e^x overflows: e^x / 2 = (e^m / 2) * e^m * e^(x-2m)
if (x > SAFE_EXP * 3) {
// e^x > e^m * e^m * e^m
// y * (e^m / 2) * e^m * e^m will overflow when starting with Double.MIN_VALUE.
// Note: Do not multiply by +inf to safeguard against sin(y)=0.0 which
// will create 0 * inf = nan.
re *= Double.MAX_VALUE * Double.MAX_VALUE * Double.MAX_VALUE;
im *= Double.MAX_VALUE * Double.MAX_VALUE * Double.MAX_VALUE;
} else {
// Initial part of (e^x / 2) using (e^m / 2)
re *= EXP_M / 2;
im *= EXP_M / 2;
final double xm;
if (x > SAFE_EXP * 2) {
// e^x = e^m * e^m * e^(x-2m)
re *= EXP_M;
im *= EXP_M;
xm = x - SAFE_EXP * 2;
} else {
// e^x = e^m * e^(x-m)
xm = x - SAFE_EXP;
}
final double exp = Math.exp(xm);
re *= exp;
im *= exp;
}
return constructor.create(re, im);
}
/**
* Returns the
* <a href="http://mathworld.wolfram.com/HyperbolicTangent.html">
* hyperbolic tangent</a> of this complex number.
*
* <p>\[ \tanh(z) = \frac{e^z - e^{-z}}{e^z + e^{-z}} \]
*
* <p>The hyperbolic tangent of \( z \) is an entire function in the complex plane
* and is periodic with respect to the imaginary component with period \( \pi i \)
* and has poles of the first order along the imaginary line, at coordinates
* \( (0, \pi(\frac{1}{2} + n)) \).
* Note that the {@code double} floating-point representation is unable to exactly represent
* \( \pi/2 \) and there is no value for which a pole error occurs. Special cases:
*
* <ul>
* <li>{@code z.conj().tanh() == z.tanh().conj()}.
* <li>This is an odd function: \( \tanh(z) = -\tanh(-z) \).
* <li>If {@code z} is +0 + i0, returns +0 + i0.
* <li>If {@code z} is 0 + i∞, returns 0 + iNaN.
* <li>If {@code z} is x + i∞ for finite non-zero x, returns NaN + iNaN ("invalid" floating-point operation).
* <li>If {@code z} is 0 + iNaN, returns 0 + iNAN.
* <li>If {@code z} is x + iNaN for finite non-zero x, returns NaN + iNaN ("invalid" floating-point operation).
* <li>If {@code z} is +∞ + iy for positive-signed finite y, returns 1 + i0 sin(2y).
* <li>If {@code z} is +∞ + i∞, returns 1 ± i0 (where the sign of the imaginary part of the result is unspecified).
* <li>If {@code z} is +∞ + iNaN, returns 1 ± i0 (where the sign of the imaginary part of the result is unspecified).
* <li>If {@code z} is NaN + i0, returns NaN + i0.
* <li>If {@code z} is NaN + iy for all nonzero numbers y, returns NaN + iNaN ("invalid" floating-point operation).
* <li>If {@code z} is NaN + iNaN, returns NaN + iNaN.
* </ul>
*
* <p>Special cases include the technical corrigendum
* <a href="http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1892.htm#dr_471">
* DR 471: Complex math functions cacosh and ctanh</a>.
*
* <p>This is defined using real \( x \) and imaginary \( y \) parts:
*
* <p>\[ \tan(x + iy) = \frac{\sinh(2x)}{\cosh(2x)+\cos(2y)} + i \frac{\sin(2y)}{\cosh(2x)+\cos(2y)} \]
*
* <p>The implementation uses double-angle identities to avoid overflow of {@code 2x}
* and {@code 2y}.
*
* @return The hyperbolic tangent of this complex number.
* @see <a href="http://functions.wolfram.com/ElementaryFunctions/Tanh/">Tanh</a>
*/
public Complex tanh() {
return tanh(real, imaginary, Complex::ofCartesian);
}
/**
* Returns 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) {
// Cache the absolute real value
final double x = Math.abs(real);
// Handle inf or nan.
if (!isPosFinite(x) || !Double.isFinite(imaginary)) {
if (isPosInfinite(x)) {
if (Double.isFinite(imaginary)) {
// The sign is copied from sin(2y)
// The identity sin(2a) = 2 sin(a) cos(a) is used for consistency
// with the computation below. Only the magnitude is important
// so drop the 2. When |y| is small sign(sin(2y)) = sign(y).
final double sign = Math.abs(imaginary) < PI_OVER_2 ?
imaginary :
Math.sin(imaginary) * Math.cos(imaginary);
return constructor.create(Math.copySign(1, real),
Math.copySign(0, sign));
}
// imaginary is infinite or NaN
return constructor.create(Math.copySign(1, real), Math.copySign(0, imaginary));
}
// Remaining cases:
// (0 + i inf), returns (0 + i NaN)
// (0 + i NaN), returns (0 + i NaN)
// (x + i inf), returns (NaN + i NaN) for non-zero x (including infinite)
// (x + i NaN), returns (NaN + i NaN) for non-zero x (including infinite)
// (NaN + i 0), returns (NaN + i 0)
// (NaN + i y), returns (NaN + i NaN) for non-zero y (including infinite)
// (NaN + i NaN), returns (NaN + i NaN)
return constructor.create(real == 0 ? real : Double.NaN,
imaginary == 0 ? imaginary : Double.NaN);
}
// Finite components
// tanh(x+iy) = (sinh(2x) + i sin(2y)) / (cosh(2x) + cos(2y))
if (real == 0) {
// Imaginary-only tanh(iy) = i tan(y)
// Identity: sin 2y / (1 + cos 2y) = tan(y)
return constructor.create(real, Math.tan(imaginary));
}
if (imaginary == 0) {
// Identity: sinh 2x / (1 + cosh 2x) = tanh(x)
return constructor.create(Math.tanh(real), imaginary);
}
// The double angles can be avoided using the identities:
// sinh(2x) = 2 sinh(x) cosh(x)
// sin(2y) = 2 sin(y) cos(y)
// cosh(2x) = 2 sinh^2(x) + 1
// cos(2y) = 2 cos^2(y) - 1
// tanh(x+iy) = (sinh(x)cosh(x) + i sin(y)cos(y)) / (sinh^2(x) + cos^2(y))
// To avoid a junction when swapping between the double angles and the identities
// the identities are used in all cases.
if (x > SAFE_EXP / 2) {
// Potential overflow in sinh/cosh(2x).
// Approximate sinh/cosh using exp^x.
// Ignore cos^2(y) in the divisor as it is insignificant.
// real = sinh(x)cosh(x) / sinh^2(x) = +/-1
final double re = Math.copySign(1, real);
// imag = sin(2y) / 2 sinh^2(x)
// sinh(x) -> sign(x) * e^|x| / 2 when x is large.
// sinh^2(x) -> e^2|x| / 4 when x is large.
// imag = sin(2y) / 2 (e^2|x| / 4) = 2 sin(2y) / e^2|x|
// = 4 * sin(y) cos(y) / e^2|x|
// Underflow safe divide as e^2|x| may overflow:
// imag = 4 * sin(y) cos(y) / e^m / e^(2|x| - m)
// (|im| is a maximum of 2)
double im = Math.sin(imaginary) * Math.cos(imaginary);
if (x > SAFE_EXP) {
// e^2|x| > e^m * e^m
// This will underflow 2.0 / e^m / e^m
im = Math.copySign(0.0, im);
} else {
// e^2|x| = e^m * e^(2|x| - m)
im = 4 * im / EXP_M / Math.exp(2 * x - SAFE_EXP);
}
return constructor.create(re, im);
}
// No overflow of sinh(2x) and cosh(2x)
// Note: This does not use the definitional formula but uses the identity:
// tanh(x+iy) = (sinh(x)cosh(x) + i sin(y)cos(y)) / (sinh^2(x) + cos^2(y))
final double sinhx = Math.sinh(real);
final double coshx = Math.cosh(real);
final double siny = Math.sin(imaginary);
final double cosy = Math.cos(imaginary);
final double divisor = sinhx * sinhx + cosy * cosy;
return constructor.create(sinhx * coshx / divisor,
siny * cosy / divisor);
}
/**
* Returns the
* <a href="http://mathworld.wolfram.com/InverseHyperbolicSine.html">
* inverse hyperbolic sine</a> of this complex number.
*
* <p>\[ \sinh^{-1}(z) = \ln \left(z + \sqrt{1 + z^2} \right) \]
*
* <p>The inverse hyperbolic sine of \( z \) is unbounded along the real axis and
* in the range \( [-\pi, \pi] \) along the imaginary axis. Special cases:
*
* <ul>
* <li>{@code z.conj().asinh() == z.asinh().conj()}.
* <li>This is an odd function: \( \sinh^{-1}(z) = -\sinh^{-1}(-z) \).
* <li>If {@code z} is +0 + i0, returns 0 + i0.
* <li>If {@code z} is x + i∞ for positive-signed finite x, returns +∞ + iπ/2.
* <li>If {@code z} is x + iNaN for finite x, returns NaN + iNaN ("invalid" floating-point operation).
* <li>If {@code z} is +∞ + iy for positive-signed finite y, returns +∞ + i0.
* <li>If {@code z} is +∞ + i∞, returns +∞ + iπ/4.
* <li>If {@code z} is +∞ + iNaN, returns +∞ + iNaN.
* <li>If {@code z} is NaN + i0, returns NaN + i0.
* <li>If {@code z} is NaN + iy for finite nonzero y, returns NaN + iNaN ("invalid" floating-point operation).
* <li>If {@code z} is NaN + i∞, returns ±∞ + iNaN (where the sign of the real part of the result is unspecified).
* <li>If {@code z} is NaN + iNaN, returns NaN + iNaN.
* </ul>
*
* <p>The inverse hyperbolic sine is a multivalued function and requires a branch cut in
* the complex plane; the cut is conventionally placed at the line segments
* \( (-i \infty,-i) \) and \( (i,i \infty) \) of the imaginary axis.
*
* <p>This function is computed using the trigonomic identity:
*
* <p>\[ \sinh^{-1}(z) = -i \sin^{-1}(iz) \]
*
* @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 to the identity defined in the C99 standard:
// asin(z) = -i asinh(iz)
// Multiply this number by I, compute asin, then multiply by back
return asin(-imaginary, real, Complex::multiplyNegativeI);
}
/**
* Returns the
* <a href="http://mathworld.wolfram.com/InverseHyperbolicCosine.html">
* inverse hyperbolic cosine</a> of this complex number.
*
* <p>\[ \cosh^{-1}(z) = \ln \left(z + \sqrt{z + 1} \sqrt{z - 1} \right) \]
*
* <p>The inverse hyperbolic cosine of \( z \) is in the range \( [0, \infty) \) along the
* real axis and in the range \( [-\pi, \pi] \) along the imaginary axis. Special cases:
*
* <ul>
* <li>{@code z.conj().acosh() == z.acosh().conj()}.
* <li>If {@code z} is ±0 + i0, returns +0 + iπ/2.
* <li>If {@code z} is x + i∞ for finite x, returns +∞ + iπ/2.
* <li>If {@code z} is 0 + iNaN, returns NaN + iπ/2 <sup>[1]</sup>.
* <li>If {@code z} is x + iNaN for finite non-zero x, returns NaN + iNaN ("invalid" floating-point operation).
* <li>If {@code z} is −∞ + iy for positive-signed finite y, returns +∞ + iπ.
* <li>If {@code z} is +∞ + iy for positive-signed finite y, returns +∞ + i0.
* <li>If {@code z} is −∞ + i∞, returns +∞ + i3π/4.
* <li>If {@code z} is +∞ + i∞, returns +∞ + iπ/4.
* <li>If {@code z} is ±∞ + iNaN, returns +∞ + iNaN.
* <li>If {@code z} is NaN + iy for finite y, returns NaN + iNaN ("invalid" floating-point operation).
* <li>If {@code z} is NaN + i∞, returns +∞ + iNaN.
* <li>If {@code z} is NaN + iNaN, returns NaN + iNaN.
* </ul>
*
* <p>Special cases include the technical corrigendum
* <a href="http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1892.htm#dr_471">
* DR 471: Complex math functions cacosh and ctanh</a>.
*
* <p>The inverse hyperbolic cosine is a multivalued function and requires a branch cut in
* the complex plane; the cut is conventionally placed at the line segment
* \( (-\infty,-1) \) of the real axis.
*
* <p>This function is computed using the trigonomic identity:
*
* <p>\[ \cosh^{-1}(z) = \pm i \cos^{-1}(z) \]
*
* <p>The sign of the multiplier is chosen to give {@code z.acosh().real() >= 0}
* and compatibility with the C99 standard.
*
* @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)
// Note the special case:
// acos(+-0 + iNaN) = π/2 + iNaN
// acosh(0 + iNaN) = NaN + iπ/2
// will not appropriately multiply by I to maintain positive imaginary if
// acos() imaginary computes as NaN. So do this explicitly.
if (Double.isNaN(imaginary) && real == 0) {
return new Complex(Double.NaN, PI_OVER_2);
}
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)
);
}
/**
* Returns the
* <a href="http://mathworld.wolfram.com/InverseHyperbolicTangent.html">
* inverse hyperbolic tangent</a> of this complex number.
*
* <p>\[ \tanh^{-1}(z) = \frac{1}{2} \ln \left( \frac{1 + z}{1 - z} \right) \]
*
* <p>The inverse hyperbolic tangent of \( z \) is unbounded along the real axis and
* in the range \( [-\pi/2, \pi/2] \) along the imaginary axis. Special cases:
*
* <ul>
* <li>{@code z.conj().atanh() == z.atanh().conj()}.
* <li>This is an odd function: \( \tanh^{-1}(z) = -\tanh^{-1}(-z) \).
* <li>If {@code z} is +0 + i0, returns +0 + i0.
* <li>If {@code z} is +0 + iNaN, returns +0 + iNaN.
* <li>If {@code z} is +1 + i0, returns +∞ + i0 ("divide-by-zero" floating-point operation).
* <li>If {@code z} is x + i∞ for finite positive-signed x, returns +0 + iπ/2.
* <li>If {@code z} is x+iNaN for nonzero finite x, returns NaN+iNaN ("invalid" floating-point operation).
* <li>If {@code z} is +∞ + iy for finite positive-signed y, returns +0 + iπ/2.
* <li>If {@code z} is +∞ + i∞, returns +0 + iπ/2.
* <li>If {@code z} is +∞ + iNaN, returns +0 + iNaN.
* <li>If {@code z} is NaN+iy for finite y, returns NaN+iNaN ("invalid" floating-point operation).
* <li>If {@code z} is NaN + i∞, returns ±0 + iπ/2 (where the sign of the real part of the result is unspecified).
* <li>If {@code z} is NaN + iNaN, returns NaN + iNaN.
* </ul>
*
* <p>The inverse hyperbolic tangent is a multivalued function and requires a branch cut in
* the complex plane; the cut is conventionally placed at the line segments
* \( (\infty,-1] \) and \( [1,\infty) \) of the real axis.
*
* <p>This is implemented using real \( x \) and imaginary \( y \) parts:
*
* <p>\[ \tanh^{-1}(z) = \frac{1}{4} \ln \left(1 + \frac{4x}{(1-x)^2+y^2} \right) + \\
* i \frac{1}{2} \left( \tan^{-1} \left(\frac{2y}{1-x^2-y^2} \right) + \frac{\pi}{2} \left(\text{sgn}(x^2+y^2-1)+1 \right) \text{sgn}(y) \right) \]
*
* <p>The imaginary part is computed using {@link Math#atan2(double, double)} to ensure the
* correct quadrant is returned from \( \tan^{-1} \left(\frac{2y}{1-x^2-y^2} \right) \).
*
* <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>}.
*
* @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);
}
/**
* Returns 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>Adapted from {@code <boost/math/complex/atanh.hpp>}. This method only (and not
* invoked methods within) is distributed under the Boost Software License V1.0.
* The original notice is shown below and the licence is shown in full in LICENSE:
* <pre>
* (C) Copyright John Maddock 2005.
* Distributed under the Boost Software License, Version 1.0. (See accompanying
* file LICENSE or copy at https://www.boost.org/LICENSE_1_0.txt)
* </pre>
*
* @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
double x = Math.abs(real);
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
// imag(atanh(z)) == tan^-1 (2y, (1 - x^2 - y^2) / 2
// The division is done at the end of the function.
re = Math.log1p(4 * x / (mxp1 * mxp1 + yy));
// Modified from boost which does not switch the magnitude of x and y.
// The denominator for atan2 is 1 - x^2 - y^2.
// This can be made more precise if |x| > |y|.
final double numerator = 2 * y;
final double denominator;
if (x < y) {
final double tmp = x;
x = y;
y = tmp;
}
// 1 - x is precise if |x| >= 1
if (x >= 1) {
denominator = (1 - x) * (1 + x) - y * y;
} else {
// |x| < 1: Use high precision if possible:
// 1 - x^2 - y^2 = -(x^2 + y^2 - 1)
// Modified from boost to use the custom high precision method.
denominator = -x2y2m1(x, y);
}
im = Math.atan2(numerator, denominator);
} 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 exclude this case.
// 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 {@code x^2 + y^2 - 1} in high precision.
* Assumes that the values x and y can be multiplied without overflow; that
* {@code x >= y}; and both values are positive.
*
* @param x the x value
* @param y the y value
* @return {@code x^2 + y^2 - 1}.
*/
private static double x2y2m1(double x, double y) {
// Hull et al used (x-1)*(x+1)+y*y.
// From the paper on page 236:
// If x == 1 there is no cancellation.
// If x > 1, there is also no cancellation, but the argument is now accurate
// only to within a factor of 1 + 3 EPSILSON (note that x – 1 is exact),
// so that error = 3 EPSILON.
// If x < 1, there can be serious cancellation:
// If 4 y^2 < |x^2 – 1| the cancellation is not serious ... the argument is accurate
// only to within a factor of 1 + 4 EPSILSON so that error = 4 EPSILON.
// Otherwise there can be serious cancellation and the relative error in the real part
// could be enormous.
final double xx = x * x;
final double yy = y * y;
// Modify to use high precision before the threshold set by Hull et al.
// This is to preserve the monotonic output of the computation at the switch.
// Set the threshold when x^2 + y^2 is above 0.5 thus subtracting 1 results in a number
// that can be expressed with a higher precision than any number in the range 0.5-1.0
// due to the variable exponent used below 0.5.
if (x < 1 && xx + yy > 0.5) {
// Large relative error.
// This does not use o.a.c.numbers.LinearCombination.value(x, x, y, y, 1, -1).
// It is optimised knowing that:
// - the products are squares
// - the final term is -1 (which does not require split multiplication and addition)
// - The answer will not be NaN as the terms are not NaN components
// - The order is known to be 1 > |x| >= |y|
// The squares are computed using a split multiply algorithm and
// the summation using an extended precision summation algorithm.
// Split x and y as one 26 bits number and one 27 bits number
final double xHigh = splitHigh(x);
final double xLow = x - xHigh;
final double yHigh = splitHigh(y);
final double yLow = y - yHigh;
// Accurate split multiplication x * x and y * y
final double x2Low = squareLow(xLow, xHigh, xx);
final double y2Low = squareLow(yLow, yHigh, yy);
return sumx2y2m1(xx, x2Low, yy, y2Low);
}
return (x - 1) * (x + 1) + yy;
}
/**
* Implement Dekker's method to split a value into two parts. Multiplying by (2^s + 1) create
* a big value from which to derive the two split parts.
* <pre>
* c = (2^s + 1) * a
* a_big = c - a
* a_hi = c - a_big
* a_lo = a - a_hi
* a = a_hi + a_lo
* </pre>
*
* <p>The multiplicand must be odd allowing a p-bit value to be split into
* (p-s)-bit value {@code a_hi} and a non-overlapping (s-1)-bit value {@code a_lo}.
* Combined they have (p-1) bits of significand but the sign bit of {@code a_lo}
* contains a bit of information.
*
* @param a Value.
* @return the high part of the value.
* @see <a href="https://doi.org/10.1007/BF01397083">
* Dekker (1971) A floating-point technique for extending the available precision</a>
*/
private static double splitHigh(double a) {
final double c = MULTIPLIER * a;
return c - (c - a);
}
/**
* Compute the round-off from the square of a split number with {@code low} and {@code high}
* components. Uses Dekker's algorithm for split multiplication modified for a square product.
*
* <p>Note: This is candidate to be replaced with {@code Math.fma(x, x, -x * x)} to compute
* the round-off from the square product {@code x * x}. This would remove the requirement
* to compute the split number and make this method redundant. {@code Math.fma} requires
* JDK 9 and FMA hardware support.
*
* @param low Low part of number.
* @param high High part of number.
* @param square Square of the number.
* @return <code>low * low - (((product - high * high) - low * high) - high * low)</code>
* @see <a href="http://www-2.cs.cmu.edu/afs/cs/project/quake/public/papers/robust-arithmetic.ps">
* Shewchuk (1997) Theorum 18</a>
*/
private static double squareLow(double low, double high, double square) {
final double lh = low * high;
return low * low - (((square - high * high) - lh) - lh);
}
/**
* Compute the round-off from the sum of two numbers {@code a} and {@code b} using
* Dekker's two-sum algorithm. The values are required to be ordered by magnitude:
* {@code |a| >= |b|}.
*
* @param a First part of sum.
* @param b Second part of sum.
* @param x Sum.
* @return <code>b - (x - a)</code>
* @see <a href="http://www-2.cs.cmu.edu/afs/cs/project/quake/public/papers/robust-arithmetic.ps">
* Shewchuk (1997) Theorum 6</a>
*/
private static double fastSumLow(double a, double b, double x) {
// x = a + b
// bVirtual = x - a
// y = b - bVirtual
return b - (x - a);
}
/**
* Compute the round-off from the sum of two numbers {@code a} and {@code b} using
* Knuth's two-sum algorithm. The values are not required to be ordered by magnitude.
*
* @param a First part of sum.
* @param b Second part of sum.
* @param x Sum.
* @return <code>(a - (x - (x - a))) + (b - (x - a))</code>
* @see <a href="http://www-2.cs.cmu.edu/afs/cs/project/quake/public/papers/robust-arithmetic.ps">
* Shewchuk (1997) Theorum 7</a>
*/
private static double sumLow(double a, double b, double x) {
// x = a + b
// bVirtual = x - a
// aVirtual = x - bVirtual
// bRoundoff = b - bVirtual
// aRoundoff = a - aVirtual
// y = aRoundoff + bRoundoff
final double bVirtual = x - a;
return (a - (x - bVirtual)) + (b - bVirtual);
}
/**
* Sum x^2 + y^2 - 1. It is assumed that {@code y <= x < 1}.
*
* <p>Implement Shewchuk's expansion-sum algorithm: [x2Low, x2High] + [-1] + [y2Low, y2High].
*
* @param x2High High part of x^2.
* @param x2Low Low part of x^2.
* @param y2High High part of y^2.
* @param y2Low Low part of y^2.
* @return x^2 + y^2 - 1
* @see <a href="http://www-2.cs.cmu.edu/afs/cs/project/quake/public/papers/robust-arithmetic.ps">
* Shewchuk (1997) Theorum 12</a>
*/
private static double sumx2y2m1(double x2High, double x2Low, double y2High, double y2Low) {
// Let e and f be non-overlapping expansions of components of length m and n.
// The following algorithm will produce a non-overlapping expansion h where the
// sum h_i = e + f and components of h are in increasing order of magnitude.
// Expansion-sum proceeds by a grow-expansion of the first part from one expansion
// into the other, extending its length by 1. The process repeats for the next part
// but the grow-expansion starts at the previous merge position + 1.
// Thus expansion-sum requires mn two-sum operations to merge length m into length n
// resulting in length m+n-1.
// Variables numbered from 1 as per Figure 7 (p.12). The output expansion h is placed
// into e increasing its length for each grow expansion.
// We have two expansions for x^2 and y^2 and the whole number -1.
// Expecting (x^2 + y^2) close to 1 we generate first the intermediate expansion
// (x^2 - 1) moving the result away from 1 where there are sparse floating point
// representations. This is then added to a similar magnitude y^2. Leaving the -1
// until last suffers from 1 ulp rounding errors more often and the requirement
// for a distillation sum to reduce rounding error frequency.
// Note: Do not use the alternative fast-expansion-sum of the parts sorted by magnitude.
// The parts can be ordered with a single comparison into:
// [y2Low, (y2High|x2Low), x2High, -1]
// The fast-two-sum saves 1 fast-two-sum and 3 two-sum operations (21 additions) and
// adds a penalty of a single branch condition.
// However the order in not "strongly non-overlapping" and the fast-expansion-sum
// output will not be strongly non-overlapping. The sum of the output has 1 ulp error
// on random cis numbers approximately 1 in 160 events. This can be removed by a
// distillation two-sum pass over the final expansion as a cost of 1 fast-two-sum and
// 3 two-sum operations! So we use the expansion sum with the same operations and
// no branches.
// q=running sum
double q = x2Low - 1;
double e1 = fastSumLow(-1, x2Low, q);
double e3 = q + x2High;
double e2 = sumLow(q, x2High, e3);
final double f1 = y2Low;
final double f2 = y2High;
// Grow expansion of f1 into e
q = f1 + e1;
e1 = sumLow(f1, e1, q);
double p = q + e2;
e2 = sumLow(q, e2, p);
double e4 = p + e3;
e3 = sumLow(p, e3, e4);
// Grow expansion of f2 into e (only required to start at e2)
q = f2 + e2;
e2 = sumLow(f2, e2, q);
p = q + e3;
e3 = sumLow(q, e3, p);
final double e5 = p + e4;
e4 = sumLow(p, e4, e5);
// Final summation:
// The sum of the parts is within 1 ulp of the true expansion value e:
// |e - sum| < ulp(sum).
// To achieve the exact result requires iteration of a distillation two-sum through
// the expansion until convergence, i.e. no smaller term changes higher terms.
// This requires (n-1) iterations for length n. Here we neglect this as
// although the method is not ensured to be exact is it robust on random
// cis numbers.
return e1 + e2 + e3 + e4 + e5;
}
/**
* Returns the n-th roots of this complex number.
* The nth roots are defined by the formula:
*
* <p>\[ z_k = |z|^{\frac{1}{n}} \left( \cos \left(\phi + \frac{2\pi k}{n} \right) + i \sin \left(\phi + \frac{2\pi k}{n} \right) \right) \]
*
* <p>for \( k=0, 1, \ldots, n-1 \), where \( |z| \) and \( \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 + i NaN} is returned.</p>
*
* @param n Degree of root.
* @return A list of all {@code n}-th roots of this complex number.
* @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(ofCartesian(realPart, imaginaryPart));
innerPart += slice;
}
return result;
}
/**
* 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>
* Arrays.equals(new double[]{c1.getReal(), c1.getImaginary()},
* new double[]{c2.getReal(), c2.getImaginary()}); </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 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;
}
/**
* Gets 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);
}
/**
* 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 \( x + i y \) is {@code "(x,y)"}, with \( x \) and
* \( y \) 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 || Double.doubleToLongBits(d) == NEGATIVE_ZERO_LONG_BITS;
}
/**
* 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 has 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;
}
/**
* Check that an absolute value is finite. Used to replace {@link Double#isFinite(double)}
* when the input value is known to be positive (i.e. in the case where it has been
* set using {@link Math#abs(double)}).
*
* @param d Value.
* @return {@code true} if {@code d} is +finite.
*/
private static boolean isPosFinite(double d) {
return d <= Double.MAX_VALUE;
}
/**
* 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 a scale suitable for use with {@link Math#scalb(double, int)} to normalise
* the number to the interval {@code [1, 2)}.
*
* <p>The scale is typically the largest unbiased exponent used in the representation of the
* two numbers. In contrast to {@link Math#getExponent(double)} this handles
* sub-normal numbers by computing the number of leading zeros in the mantissa
* and shifting the unbiased exponent. The result is that for all finite, non-zero,
* numbers {@code a, b}, the magnitude of {@code scalb(x, -getScale(a, b))} is
* always in the range {@code [1, 2)}, where {@code x = max(|a|, |b|)}.
*
* <p>This method is a functional equivalent of the c function ilogb(double) adapted for
* two input arguments.
*
* <p>The result is to be used to scale a complex number using {@link Math#scalb(double, int)}.
* Hence the special case of both zero arguments is handled using the return value for NaN
* as zero cannot be scaled. This is different from {@link Math#getExponent(double)}
* or {@link #getMaxExponent(double, double)}.
*
* <p>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, then the result is
* {@link Double#MAX_EXPONENT} + 1.
* </ul>
*
* @param a the first value
* @param b the second value
* @return The maximum unbiased exponent of the values to be used for scaling
* @see Math#getExponent(double)
* @see Math#scalb(double, int)
* @see <a href="http://www.cplusplus.com/reference/cmath/ilogb/">ilogb</a>
*/
private static int getScale(double a, double b) {
// Only interested in the exponent and mantissa so remove the sign bit
final long x = Double.doubleToRawLongBits(a) & UNSIGN_MASK;
final long y = Double.doubleToRawLongBits(b) & UNSIGN_MASK;
// Only interested in the maximum
final long bits = Math.max(x, y);
// Get the unbiased exponent
int exp = ((int) (bits >>> 52)) - EXPONENT_OFFSET;
// No case to distinguish nan/inf
// Handle sub-normal numbers
if (exp == Double.MIN_EXPONENT - 1) {
// Special case for zero, return as nan/inf to indicate scaling is not possible
if (bits == 0) {
return Double.MAX_EXPONENT + 1;
}
// A sub-normal number has an exponent below -1022. The amount below
// is defined by the number of shifts of the most significant bit in
// the mantissa that is required to get a 1 at position 53 (i.e. as
// if it were a normal number with assumed leading bit)
final long mantissa = bits & MANTISSA_MASK;
exp -= Long.numberOfLeadingZeros(mantissa << 12);
}
return exp;
}
/**
* 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>
*
* <p>This is used by {@link #divide(double, double, double, double)} as
* a simple detection that a number may overflow if multiplied
* by a value in the interval [1, 2).
*
* @param a the first value
* @param b the second value
* @return The maximum unbiased exponent of the values.
* @see Math#getExponent(double)
* @see #divide(double, double, double, 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;
}
/**
* Returns {@code sqrt(x^2 + y^2)} without intermediate overflow or underflow.
*
* <p>Special cases:
* <ul>
* <li>If either argument is infinite, then the result is positive infinity.
* <li>If either argument is NaN and neither argument is infinite, then the result is NaN.
* </ul>
*
* <p>The computed result is expected to be within 1 ulp of the exact result.
*
* <p>This method is a replacement for {@link Math#hypot(double, double)}. There
* will be differences between this method and {@code Math.hypot(double, double)} due
* to the use of a different algorithm to compute the high precision sum of
* {@code x^2 + y^2}. This method has been tested to have a lower maximum error from
* the exact result; any differences are expected to be 1 ULP indicating a rounding
* change in the sum.
*
* <p>JDK9 ported the hypot function to Java for bug JDK-7130085 due to the slow performance
* of the method as a native function. Benchmarks of the Complex class for functions that
* use hypot confirm this is slow pre-Java 9. This implementation outperforms the new faster
* {@code Math.hypot(double, double)} on JDK 11 (LTS). See the Commons numbers examples JMH
* module for benchmarks. Comparisons with alternative implementations indicate
* performance gains are related to edge case handling and elimination of an unpredictable
* branch in the computation of {@code x^2 + y^2}.
*
* <p>This port was adapted from the "Freely Distributable Math Library" hypot function.
* This method only (and not invoked methods within) is distributed under the terms of the
* original notice as shown below:
* <pre>
* ====================================================
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
*
* Developed at SunSoft, a Sun Microsystems, Inc. business.
* Permission to use, copy, modify, and distribute this
* software is freely granted, provided that this notice
* is preserved.
* ====================================================
* </pre>
*
* <p>Note: The fdlibm c code makes use of the language ability to read and write directly
* to the upper and lower 32-bits of the 64-double. The function performs
* checking on the upper 32-bits for the magnitude of the two numbers by accessing
* the exponent and 20 most significant bits of the mantissa. These upper bits
* are manipulated during scaling and then used to perform extended precision
* computation of the sum {@code x^2 + y^2} where the high part of the number has 20-bit
* precision. Manipulation of direct bits has no equivalent in Java
* other than use of {@link Double#doubleToLongBits(double)} and
* {@link Double#longBitsToDouble(long)}. To avoid conversion to and from long and double
* representations this implementation only scales the double representation. The high
* and low parts of a double for the extended precision computation are extracted
* using the method of Dekker (1971) to create two 26-bit numbers. This works for sub-normal
* numbers and reduces the maximum error in comparison to fdlibm hypot which does not
* use a split number algorithm for sub-normal numbers.
*
* @param x Value x
* @param y Value y
* @return sqrt(x^2 + y^2)
* @see Math#hypot(double, double)
* @see <a href="https://www.netlib.org/fdlibm/e_hypot.c">fdlibm e_hypot.c</a>
* @see <a href="https://bugs.java.com/bugdatabase/view_bug.do?bug_id=7130085">JDK-7130085 : Port fdlibm hypot to Java</a>
*/
private static double hypot(double x, double y) {
// Differences to the fdlibm reference:
//
// 1. fdlibm orders the two parts using the magnitude of the upper 32-bits.
// This incorrectly orders numbers which differ only in the lower 32-bits.
// This invalidates hypot(x, y) = hypot(y, x) for small sub-normal numbers and a minority
// of cases of normal numbers. This implementation forces the |x| >= |y| order
// using the entire 63-bits of the unsigned doubles to ensure the function
// is commutative.
//
// 2. fdlibm computed scaling by directly writing changes to the exponent bits
// and maintained the high part (ha) during scaling for use in the high
// precision sum x^2 + y^2. Since exponent scaling cannot be applied to sub-normals
// the original version dropped the split number representation for sub-normals
// and can produce maximum errors above 1 ULP for sub-normal numbers.
// This version uses Dekker's method to split the number. This can be applied to
// sub-normals and allows dropping the condition to check for sub-normal numbers
// since all small numbers are handled with a single scaling factor.
// The effect is increased precision for the majority of sub-normal cases where
// the implementations compute a different result.
//
// 3. An alteration is done here to add an 'else if' instead of a second
// 'if' statement. Thus you cannot scale down and up at the same time.
//
// 4. There is no use of the absolute double value. The magnitude comparison is
// performed using the long bit representation. The computation x^2+y^2 is
// insensitive to the sign bit. Thus use of Math.abs(double) is only in edge-case
// branches.
//
// 5. The exponent different to ignore the smaller component has changed from 60 to 54.
//
// Original comments from fdlibm are in c style: /* */
// Extra comments added for reference.
//
// Note that the high 32-bits are compared to constants.
// The lowest 20-bits are the upper bits of the 52-bit mantissa.
// The next 11-bits are the biased exponent. The sign bit has been cleared.
// Scaling factors are powers of two for exact scaling.
// For clarity the values have been refactored to named constants.
// The mask is used to remove the sign bit.
final long xbits = Double.doubleToRawLongBits(x) & UNSIGN_MASK;
final long ybits = Double.doubleToRawLongBits(y) & UNSIGN_MASK;
// Order by magnitude: |a| >= |b|
double a;
double b;
/* High word of x & y */
final int ha;
final int hb;
if (ybits > xbits) {
a = y;
b = x;
ha = (int) (ybits >>> 32);
hb = (int) (xbits >>> 32);
} else {
a = x;
b = y;
ha = (int) (xbits >>> 32);
hb = (int) (ybits >>> 32);
}
// Check if the smaller part is significant.
// a^2 is computed in extended precision for an effective mantissa of 106-bits.
// An exponent difference of 54 is where b^2 will not overlap a^2.
if ((ha - hb) > EXP_54) {
/* a/b > 2**54 */
// or a is Inf or NaN.
// No addition of a + b for sNaN.
return Math.abs(a);
}
double rescale = 1.0;
if (ha > EXP_500) {
/* a > 2^500 */
if (ha >= EXP_1024) {
/* Inf or NaN */
// Check b is infinite for the IEEE754 result.
// No addition of a + b for sNaN.
return Math.abs(b) == Double.POSITIVE_INFINITY ?
Double.POSITIVE_INFINITY :
Math.abs(a);
}
/* scale a and b by 2^-600 */
// Before scaling: a in [2^500, 2^1023].
// After scaling: a in [2^-100, 2^423].
// After scaling: b in [2^-154, 2^423].
a *= TWO_POW_NEG_600;
b *= TWO_POW_NEG_600;
rescale = TWO_POW_600;
} else if (hb < EXP_NEG_500) {
// No special handling of sub-normals.
// These do not matter when we do not manipulate the exponent bits
// for scaling the split representation.
// Intentional comparison with zero.
if (b == 0) {
return Math.abs(a);
}
/* scale a and b by 2^600 */
// Effective min exponent of a sub-normal = -1022 - 52 = -1074.
// Before scaling: b in [2^-1074, 2^-501].
// After scaling: b in [2^-474, 2^99].
// After scaling: a in [2^-474, 2^153].
a *= TWO_POW_600;
b *= TWO_POW_600;
rescale = TWO_POW_NEG_600;
}
// High precision x^2 + y^2
return Math.sqrt(x2y2(a, b)) * rescale;
}
/**
* Return {@code x^2 + y^2} with high accuracy.
*
* <p>It is assumed that {@code 2^500 > |x| >= |y| > 2^-500}. Thus there will be no
* overflow or underflow of the result. The inputs are not assumed to be unsigned.
*
* <p>The computation is performed using Dekker's method for extended precision
* multiplication of x and y and then summation of the extended precision squares.
*
* @param x Value x.
* @param y Value y
* @return x^2 + y^2
* @see <a href="https://doi.org/10.1007/BF01397083">
* Dekker (1971) A floating-point technique for extending the available precision</a>
*/
private static double x2y2(double x, double y) {
// Note:
// This method is different from the high-accuracy summation used in fdlibm for hypot.
// The summation could be any valid computation of x^2+y^2. However since this follows
// the re-scaling logic in hypot(x, y) the use of high precision has relatively
// less performance overhead than if used without scaling.
// The Dekker algorithm is branchless for better performance
// than the fdlibm method with a maximum ULP error of approximately 0.86.
//
// See NUMBERS-143 for analysis.
// Do a Dekker summation of double length products x*x and y*y
// (10 multiply and 20 additions).
final double xx = x * x;
final double yy = y * y;
// Compute the round-off from the products.
// With FMA hardware support in JDK 9+ this can be replaced with the much faster:
// xxLow = Math.fma(x, x, -xx)
// yyLow = Math.fma(y, y, -yy)
// Dekker mul12
final double xHigh = splitHigh(x);
final double xLow = x - xHigh;
final double xxLow = squareLow(xLow, xHigh, xx);
// Dekker mul12
final double yHigh = splitHigh(y);
final double yLow = y - yHigh;
final double yyLow = squareLow(yLow, yHigh, yy);
// Dekker add2
final double r = xx + yy;
// Note: The order is important. Assume xx > yy and drop Dekker's conditional
// check for which is the greater magnitude.
// s = xx - r + yy + yyLow + xxLow
// z = r + s
// zz = r - z + s
// Here we compute z inline and ignore computing the round-off zz.
// Note: The round-off could be used with Dekker's sqrt2 method.
// That adds 7 multiply, 1 division and 19 additions doubling the cost
// and reducing error to < 0.5 ulp for the final sqrt.
return xx - r + yy + yyLow + xxLow + r;
}
}