You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by ah...@apache.org on 2019/12/04 17:57:10 UTC

[commons-numbers] 02/02: Complex: Increase ISO C99 compliance.

This is an automated email from the ASF dual-hosted git repository.

aherbert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-numbers.git

commit 152c0eb8c715964e2f35091e374add68ea6bb887
Author: aherbert <ah...@apache.org>
AuthorDate: Wed Dec 4 17:52:47 2019 +0000

    Complex: Increase ISO C99 compliance.
    
    Explicit testing of the conjugate equalities. These are implemented for
    zero and +/- infinity for the imaginary part.
---
 .../apache/commons/numbers/complex/Complex.java    | 579 +++++++++++---------
 .../commons/numbers/complex/CStandardTest.java     | 600 ++++++++++++++-------
 2 files changed, 739 insertions(+), 440 deletions(-)

diff --git a/commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/Complex.java b/commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/Complex.java
index d218a6a..4ced624 100644
--- a/commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/Complex.java
+++ b/commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/Complex.java
@@ -882,34 +882,53 @@ public final class Complex implements Serializable  {
      * @return the inverse cosine of this complex number.
      */
     public Complex acos() {
-        if (real == 0 &&
-            Double.isNaN(imaginary)) {
-            return new Complex(PI_OVER_2, Double.NaN);
-        } else if (neitherInfiniteNorZeroNorNaN(real) &&
-                   imaginary == Double.POSITIVE_INFINITY) {
-            return new Complex(PI_OVER_2, Double.NEGATIVE_INFINITY);
-        } else if (real == Double.NEGATIVE_INFINITY &&
-                   imaginary == 1) {
-            return new Complex(Math.PI, Double.NEGATIVE_INFINITY);
-        } else if (real == Double.POSITIVE_INFINITY &&
-                   imaginary == 1) {
-            return new Complex(0, Double.NEGATIVE_INFINITY);
-        } else if (real == Double.NEGATIVE_INFINITY &&
-                   imaginary == Double.POSITIVE_INFINITY) {
-            return new Complex(PI_3_OVER_4, Double.NEGATIVE_INFINITY);
-        } else if (real == Double.POSITIVE_INFINITY &&
-                   imaginary == Double.POSITIVE_INFINITY) {
-            return new Complex(PI_OVER_4, Double.NEGATIVE_INFINITY);
-        } else if (Double.isInfinite(real) &&
-                   Double.isNaN(imaginary)) {
+        if (Double.isFinite(real)) {
+            if (Double.isFinite(imaginary)) {
+                // Special case for zero
+                if (real == 0 && imaginary == 0) {
+                    return new Complex(PI_OVER_2, Math.copySign(0, -imaginary));
+                }
+                // ISO C99: Preserve the equality
+                // acos(conj(z)) = conj(acos(z))
+                Complex z;
+                ComplexConstructor constructor;
+                if (negative(imaginary)) {
+                    z = conj();
+                    constructor = Complex::ofCartesianConjugate;
+                } else {
+                    z = this;
+                    constructor = Complex::ofCartesian;
+                }
+                return z.add(z.sqrt1z().multiplyByI()).log().multiplyByNegI(constructor);
+            }
+            if (Double.isInfinite(imaginary)) {
+                return new Complex(PI_OVER_2, Math.copySign(Double.POSITIVE_INFINITY, -imaginary));
+            }
+            // imaginary is NaN
+            // Special case for real == 0
+            return real == 0 ? new Complex(PI_OVER_2, Double.NaN) : NAN;
+        }
+        if (Double.isInfinite(real)) {
+            if (Double.isFinite(imaginary)) {
+                final double re = real == Double.NEGATIVE_INFINITY ? Math.PI : 0;
+                return new Complex(re, Math.copySign(Double.POSITIVE_INFINITY, -imaginary));
+            }
+            if (Double.isInfinite(imaginary)) {
+                final double re = real == Double.NEGATIVE_INFINITY ? PI_3_OVER_4 : PI_OVER_4;
+                return new Complex(re, Math.copySign(Double.POSITIVE_INFINITY, -imaginary));
+            }
+            // imaginary is NaN
             // Swap real and imaginary
             return new Complex(Double.NaN, real);
-        } else if (Double.isNaN(real) &&
-                   imaginary == Double.POSITIVE_INFINITY) {
-            return new Complex(Double.NaN, Double.NEGATIVE_INFINITY);
         }
-        return add(sqrt1z().multiplyByI()).log().multiplyByNegI();
+        // real is NaN
+        if (Double.isInfinite(imaginary)) {
+            return new Complex(Double.NaN, -imaginary);
+        }
+        // optionally raises the ‘‘invalid’’ floating-point exception, for finite y.
+        return NAN;
     }
+
     /**
      * Compute the
      * <a href="http://mathworld.wolfram.com/InverseSine.html">
@@ -974,6 +993,16 @@ public final class Complex implements Serializable  {
     }
 
     /**
+     * Multiply the Complex by -I and create the result using the constructor.
+     *
+     * @param constructor Constructor
+     * @return the result (-iz)
+     */
+    private Complex multiplyByNegI(ComplexConstructor constructor) {
+        return constructor.create(imaginary, -real);
+    }
+
+    /**
      * Compute the
      * <a href="http://mathworld.wolfram.com/InverseHyperbolicSine.html">
      * inverse hyperbolic sine</a> of this complex number.
@@ -985,26 +1014,43 @@ public final class Complex implements Serializable  {
      * @return the inverse hyperbolic sine of this complex number
      */
     public Complex asinh() {
-        if (neitherInfiniteNorZeroNorNaN(real) &&
-            imaginary == Double.POSITIVE_INFINITY) {
-            return new Complex(Double.POSITIVE_INFINITY, PI_OVER_2);
-        } else if (real == Double.POSITIVE_INFINITY &&
-                   Double.isFinite(imaginary)) {
-            return new Complex(Double.POSITIVE_INFINITY, 0);
-        } else if (real == Double.POSITIVE_INFINITY &&
-                   imaginary == Double.POSITIVE_INFINITY) {
-            return new Complex(Double.POSITIVE_INFINITY, PI_OVER_4);
-        } else if (real == Double.POSITIVE_INFINITY &&
-                   Double.isNaN(imaginary)) {
-            return new Complex(Double.POSITIVE_INFINITY,  Double.NaN);
-        } else if (Double.isNaN(real) &&
-                   imaginary == 0) {
-            return new Complex(Double.NaN, 0);
-        } else if (Double.isNaN(real) &&
-                   imaginary == Double.POSITIVE_INFINITY) {
+        if (Double.isFinite(real)) {
+            if (Double.isFinite(imaginary)) {
+                // Special case for zero
+                if (real == 0 && imaginary == 0) {
+                    return this;
+                }
+                // ISO C99: Preserve the equality
+                // asinh(conj(z)) = conj(asinh(z))
+                final Complex z = negative(imaginary) ? conjugate() : this;
+                final Complex result = z.square().add(ONE).sqrt().add(z).log();
+                return z == this ? result : result.conjugate();
+            }
+            if (Double.isInfinite(imaginary)) {
+                return new Complex(Double.POSITIVE_INFINITY, Math.copySign(PI_OVER_2, imaginary));
+            }
+            // imaginary is NaN
+            return NAN;
+        }
+        if (Double.isInfinite(real)) {
+            if (Double.isFinite(imaginary)) {
+                return new Complex(real, Math.copySign(0, imaginary));
+            }
+            if (Double.isInfinite(imaginary)) {
+                return new Complex(real, Math.copySign(PI_OVER_4, imaginary));
+            }
+            // imaginary is NaN
+            return new Complex(real, Double.NaN);
+        }
+        // real is NaN
+        if (imaginary == 0) {
+            return new Complex(Double.NaN, Math.copySign(0, imaginary));
+        }
+        if (Double.isInfinite(imaginary)) {
             return new Complex(Double.POSITIVE_INFINITY, Double.NaN);
         }
-        return square().add(ONE).sqrt().add(this).log();
+        // optionally raises the ‘‘invalid’’ floating-point exception, for finite y.
+        return NAN;
     }
 
     /**
@@ -1019,29 +1065,44 @@ public final class Complex implements Serializable  {
      * @return the inverse hyperbolic tangent of this complex number
      */
     public Complex atanh() {
-        if (real == 0 &&
-            Double.isNaN(imaginary)) {
-            return new Complex(0, Double.NaN);
-        } else if (neitherInfiniteNorZeroNorNaN(real) &&
-                   imaginary == 0) {
-            return new Complex(Double.POSITIVE_INFINITY, 0);
-        } else if (neitherInfiniteNorZeroNorNaN(real) &&
-                   imaginary == Double.POSITIVE_INFINITY) {
-            return new Complex(0, PI_OVER_2);
-        } else if (real == Double.POSITIVE_INFINITY &&
-                   neitherInfiniteNorZeroNorNaN(imaginary)) {
-            return new Complex(0, PI_OVER_2);
-        } else if (real == Double.POSITIVE_INFINITY &&
-                   imaginary == Double.POSITIVE_INFINITY) {
-            return new Complex(0, PI_OVER_2);
-        } else if (real == Double.POSITIVE_INFINITY &&
-                   Double.isNaN(imaginary)) {
-            return new Complex(0, Double.NaN);
-        } else if (Double.isNaN(real) &&
-                   imaginary == Double.POSITIVE_INFINITY) {
-            return new Complex(0, PI_OVER_2);
+        if (Double.isFinite(real)) {
+            if (Double.isFinite(imaginary)) {
+                // Special case for zero
+                if (imaginary == 0) {
+                    if (real == 0) {
+                        return this;
+                    }
+                    if (Math.abs(real) == 1) {
+                        // raises the ‘‘divide-by-zero’’ floating-point exception.
+                        return new Complex(Math.copySign(Double.POSITIVE_INFINITY, real), imaginary);
+                    }
+                }
+                // ISO C99: Preserve the equality
+                // atanh(conj(z)) = conj(atanh(z))
+                final Complex z = negative(imaginary) ? conjugate() : this;
+                final Complex result = z.add(ONE).divide(ONE.subtract(z)).log().multiply(0.5);
+                return z == this ? result : result.conjugate();
+            }
+            if (Double.isInfinite(imaginary)) {
+                return new Complex(Math.copySign(0, real), Math.copySign(PI_OVER_2, imaginary));
+            }
+            // imaginary is NaN
+            // Special case for real == 0
+            return real == 0 ? new Complex(real, Double.NaN) : NAN;
         }
-        return add(ONE).divide(ONE.subtract(this)).log().multiply(0.5);
+        if (Double.isInfinite(real)) {
+            if (Double.isNaN(imaginary)) {
+                return new Complex(Math.copySign(0, real), Double.NaN);
+            }
+            // imaginary is finite or infinite
+            return new Complex(Math.copySign(0, real), Math.copySign(PI_OVER_2, imaginary));
+        }
+        // real is NaN
+        if (Double.isInfinite(imaginary)) {
+            return new Complex(0, Math.copySign(PI_OVER_2, imaginary));
+        }
+        // optionally raises the ‘‘invalid’’ floating-point exception, for finite y.
+        return NAN;
     }
 
     /**
@@ -1057,47 +1118,41 @@ public final class Complex implements Serializable  {
      */
     public Complex acosh() {
         if (Double.isFinite(real)) {
-            if (real == 0 &&
-                imaginary == 0) {
-                return new Complex(0, PI_OVER_2);
-            }
-            if (imaginary == Double.POSITIVE_INFINITY) {
-                return new Complex(Double.POSITIVE_INFINITY, PI_OVER_2);
-            }
-            if (Double.isNaN(imaginary)) {
-                return NAN;
-            }
-        } else if (Double.isInfinite(real)) {
-            if (real == Double.NEGATIVE_INFINITY &&
-                positiveSignedFinite(imaginary)) {
-                return new Complex(Double.POSITIVE_INFINITY, Math.PI);
-            }
-            if (real == Double.POSITIVE_INFINITY &&
-                positiveSignedFinite(imaginary)) {
-                return new Complex(Double.POSITIVE_INFINITY, 0);
+            if (Double.isFinite(imaginary)) {
+                // Special case for zero
+                if (real == 0 && imaginary == 0) {
+                    return new Complex(real, Math.copySign(PI_OVER_2, imaginary));
+                }
+                // ISO C99: Preserve the equality
+                // acosh(conj(z)) = conj(acosh(z))
+                final Complex z = negative(imaginary) ? conjugate() : this;
+                final Complex result = z.square().subtract(ONE).sqrt().add(z).log();
+                return z == this ? result : result.conjugate();
             }
-            if (real == Double.NEGATIVE_INFINITY &&
-                imaginary == Double.POSITIVE_INFINITY) {
-                return new Complex(Double.POSITIVE_INFINITY, PI_3_OVER_4);
+            if (Double.isInfinite(imaginary)) {
+                return new Complex(Double.POSITIVE_INFINITY, Math.copySign(PI_OVER_2, imaginary));
             }
-            if (real == Double.POSITIVE_INFINITY &&
-                imaginary == Double.POSITIVE_INFINITY) {
-                return new Complex(Double.POSITIVE_INFINITY, PI_OVER_4);
-            }
-            if (Double.isNaN(imaginary)) {
-                return new Complex(Double.POSITIVE_INFINITY, Double.NaN);
-            }
-        } else if (Double.isNaN(real)) {
-            if (imaginary == Double.POSITIVE_INFINITY) {
-                return new Complex(Double.POSITIVE_INFINITY, Double.NaN);
+            // imaginary is NaN
+            return NAN;
+        }
+        if (Double.isInfinite(real)) {
+            if (Double.isFinite(imaginary)) {
+                final double im = real == Double.NEGATIVE_INFINITY ? Math.PI : 0;
+                return new Complex(Double.POSITIVE_INFINITY, Math.copySign(im, imaginary));
             }
-            if (Double.isFinite(imaginary) || Double.isNaN(imaginary)) {
-                // optionally raises the ‘‘invalid’’ floating-point exception, for finite y.
-                // No condition for imaginary as negative infinity
-                return NAN;
+            if (Double.isInfinite(imaginary)) {
+                final double im = real == Double.NEGATIVE_INFINITY ? PI_3_OVER_4 : PI_OVER_4;
+                return new Complex(Double.POSITIVE_INFINITY, Math.copySign(im, imaginary));
             }
+            // imaginary is NaN
+            return new Complex(Double.POSITIVE_INFINITY, Double.NaN);
+        }
+        // real is NaN
+        if (Double.isInfinite(imaginary)) {
+            return new Complex(Double.POSITIVE_INFINITY, Double.NaN);
         }
-        return square().subtract(ONE).sqrt().add(this).log();
+        // optionally raises the ‘‘invalid’’ floating-point exception, for finite y.
+        return NAN;
     }
 
     /**
@@ -1165,25 +1220,36 @@ public final class Complex implements Serializable  {
      * @return the hyperbolic cosine of this complex number
      */
     private static Complex cosh(double real, double imaginary, ComplexConstructor constructor) {
-        if (real == 0 &&
-            imaginary == Double.POSITIVE_INFINITY) {
-            return constructor.create(Double.NaN, 0);
-        } else if (real == 0 &&
-                   Double.isNaN(imaginary)) {
-            return constructor.create(Double.NaN, 0);
-        } else if (real == Double.POSITIVE_INFINITY &&
-                   imaginary == 0) {
-            return constructor.create(Double.POSITIVE_INFINITY, 0);
-        } else if (real == Double.POSITIVE_INFINITY &&
-                   (imaginary == Double.POSITIVE_INFINITY || Double.isNaN(imaginary))) {
+        if (Double.isFinite(real)) {
+            if (Double.isFinite(imaginary)) {
+                return constructor.create(Math.cosh(real) * Math.cos(imaginary),
+                                          Math.sinh(real) * Math.sin(imaginary));
+            }
+            // Special case for real == 0
+            final double im = real == 0 ? Math.copySign(0, imaginary) : Double.NaN;
+            return constructor.create(Double.NaN, im);
+        }
+        if (Double.isInfinite(real)) {
+            if (Double.isFinite(imaginary)) {
+                if (imaginary == 0) {
+                    // Determine sign
+                    final double im = real > 0 ? imaginary : -imaginary;
+                    return constructor.create(Double.POSITIVE_INFINITY, im);
+                }
+                // inf * cis(y)
+                final double re = real * Math.cos(imaginary);
+                final double im = real * Math.sin(imaginary);
+                return constructor.create(re, im);
+            }
+            // imaginary is infinite or NaN
             return constructor.create(Double.POSITIVE_INFINITY, Double.NaN);
-        } else if (Double.isNaN(real) &&
-                   imaginary == 0) {
-            return constructor.create(Double.NaN, 0);
         }
-
-        return constructor.create(Math.cosh(real) * Math.cos(imaginary),
-                                  Math.sinh(real) * Math.sin(imaginary));
+        // real is NaN
+        if (imaginary == 0) {
+            return constructor.create(Double.NaN, Math.copySign(0, imaginary));
+        }
+        // optionally raises the ‘‘invalid’’ floating-point exception, for nonzero y.
+        return NAN;
     }
 
     /**
@@ -1201,25 +1267,48 @@ public final class Complex implements Serializable  {
      * @return <code><i>e</i><sup>this</sup></code>.
      */
     public Complex exp() {
-        if (real == Double.POSITIVE_INFINITY &&
-            imaginary == 0) {
-            return new Complex(Double.POSITIVE_INFINITY, 0);
-        } else if (real == Double.NEGATIVE_INFINITY &&
-                   imaginary == Double.POSITIVE_INFINITY) {
-            return Complex.ZERO;
-        } else if (real == Double.POSITIVE_INFINITY &&
-                   (imaginary == Double.POSITIVE_INFINITY || Double.isNaN(imaginary))) {
-            return new Complex(Double.POSITIVE_INFINITY, Double.NaN);
-        } else if (real == Double.NEGATIVE_INFINITY &&
-                   Double.isNaN(imaginary)) {
-            return Complex.ZERO;
-        } else if (Double.isNaN(real) &&
-                   imaginary == 0) {
-            return new Complex(Double.NaN, 0);
+        if (Double.isFinite(real)) {
+            if (Double.isFinite(imaginary)) {
+                final double expReal = Math.exp(real);
+                if (imaginary == 0) {
+                    // Preserve sign for conjugate equality
+                    return new Complex(expReal, imaginary);
+                }
+                return new Complex(expReal * Math.cos(imaginary),
+                                   expReal * Math.sin(imaginary));
+            }
+            // Imaginary is infinite or nan
+            return NAN;
+        }
+        if (Double.isInfinite(real)) {
+            if (Double.isFinite(imaginary)) {
+                if (real == Double.POSITIVE_INFINITY) {
+                    if (imaginary == 0) {
+                        return this;
+                    }
+                    // inf * cis(y)
+                    final double re = Double.POSITIVE_INFINITY * Math.cos(imaginary);
+                    final double im = Double.POSITIVE_INFINITY * Math.sin(imaginary);
+                    return new Complex(re, im);
+                }
+                // +0 * cis(y)
+                final double re = 0.0 * Math.cos(imaginary);
+                final double im = 0.0 * Math.sin(imaginary);
+                return new Complex(re, im);
+            }
+            // imaginary is infinite or NaN
+            if (real == Double.POSITIVE_INFINITY) {
+                return new Complex(Double.POSITIVE_INFINITY, Double.NaN);
+            }
+            // Preserve sign for conjugate equality
+            return new Complex(0, Math.copySign(0, imaginary));
+        }
+        // real is NaN
+        if (imaginary == 0) {
+            return new Complex(Double.NaN, Math.copySign(0, imaginary));
         }
-        final double expReal = Math.exp(real);
-        return new Complex(expReal *  Math.cos(imaginary),
-                           expReal * Math.sin(imaginary));
+        // optionally raises the ‘‘invalid’’ floating-point exception, for finite y.
+        return NAN;
     }
 
     /**
@@ -1231,22 +1320,13 @@ public final class Complex implements Serializable  {
      *   log(a + bi) = ln(|a + bi|) + arg(a + bi)i
      * </pre>
      * where ln on the right hand side is {@link Math#log},
-     * {@code |a + bi|} is the modulus, {@link Complex#abs},  and
+     * {@code |a + bi|} is the modulus, {@link Complex#abs}, and
      * {@code arg(a + bi) = }{@link Math#atan2}(b, a).
      *
-     * @return the value <code>ln &nbsp; this</code>, the natural logarithm
-     * of {@code this}.
+     * @return the natural logarithm of {@code this}.
      */
     public Complex log() {
-        if (real == Double.POSITIVE_INFINITY &&
-            imaginary == Double.POSITIVE_INFINITY) {
-            return new Complex(Double.POSITIVE_INFINITY, PI_OVER_4);
-        } else if ((real == Double.POSITIVE_INFINITY &&
-                    Double.isNaN(imaginary)) ||
-                   (Double.isNaN(real) &&
-                    imaginary == Double.POSITIVE_INFINITY)) {
-            return new Complex(Double.POSITIVE_INFINITY, Double.NaN);
-        }
+        // All edge cases satisfied by the Math library
         return new Complex(Math.log(abs()),
                            Math.atan2(imaginary, real));
     }
@@ -1255,21 +1335,19 @@ public final class Complex implements Serializable  {
      * Compute the base 10 or
      * <a href="http://mathworld.wolfram.com/CommonLogarithm.html">
      * common logarithm</a> of this complex number.
+     * Implements the formula:
+     * <pre>
+     *   log10(a + bi) = log(|a + bi|) + arg(a + bi)i
+     * </pre>
+     * where log on the right hand side is {@link Math#log10},
+     * {@code |a + bi|} is the modulus, {@link Complex#abs}, and
+     * {@code arg(a + bi) = }{@link Math#atan2}(b, a).
      *
-     * @return the base 10 logarithm of <code>this</code>.
+     * @return the base 10 logarithm of {@code this}.
      */
     public Complex log10() {
-        // Same edge cases as log()
-        if (real == Double.POSITIVE_INFINITY &&
-            imaginary == Double.POSITIVE_INFINITY) {
-            return new Complex(Double.POSITIVE_INFINITY, PI_OVER_4);
-        } else if ((real == Double.POSITIVE_INFINITY &&
-                    Double.isNaN(imaginary)) ||
-                   (Double.isNaN(real) &&
-                    imaginary == Double.POSITIVE_INFINITY)) {
-            return new Complex(Double.POSITIVE_INFINITY, Double.NaN);
-        }
-        return new Complex(Math.log(abs()) / Math.log(10),
+        // All edge cases satisfied by the Math library
+        return new Complex(Math.log10(abs()),
                            Math.atan2(imaginary, real));
     }
 
@@ -1380,33 +1458,34 @@ public final class Complex implements Serializable  {
      * @return the hyperbolic sine of this complex number
      */
     private static Complex sinh(double real, double imaginary, ComplexConstructor constructor) {
-        if (real == 0 &&
-            imaginary == 0) {
-            // Ignore the constructor.
-            // It is used in trignomic identities to multiply the result by -i.
-            // Here the result would still be zero.
-            return Complex.ZERO;
-        } else if (real == 0 &&
-                   imaginary == Double.POSITIVE_INFINITY) {
-            return constructor.create(0, Double.NaN);
-        } else if (real == 0 &&
-                   Double.isNaN(imaginary)) {
-            return constructor.create(0, Double.NaN);
-        } else if (real == Double.POSITIVE_INFINITY &&
-                   imaginary == 0) {
-            return constructor.create(Double.POSITIVE_INFINITY, 0);
-        } else if (real == Double.POSITIVE_INFINITY &&
-                   imaginary == Double.POSITIVE_INFINITY) {
-            return constructor.create(Double.POSITIVE_INFINITY, Double.NaN);
-        } else if (real == Double.POSITIVE_INFINITY &&
-                   Double.isNaN(imaginary)) {
+        if (Double.isFinite(real)) {
+            if (Double.isFinite(imaginary)) {
+                return constructor.create(Math.sinh(real) * Math.cos(imaginary),
+                                          Math.cosh(real) * Math.sin(imaginary));
+            }
+            // Special case for real == 0
+            final double re = real == 0 ? real : Double.NaN;
+            return constructor.create(re, Double.NaN);
+        }
+        if (Double.isInfinite(real)) {
+            if (Double.isFinite(imaginary)) {
+                if (imaginary == 0) {
+                    return constructor.create(real, imaginary);
+                }
+                // inf * cis(y)
+                final double re = real * Math.cos(imaginary);
+                final double im = real * Math.sin(imaginary);
+                return constructor.create(re, im);
+            }
+            // imaginary is infinite or NaN
             return constructor.create(Double.POSITIVE_INFINITY, Double.NaN);
-        } else if (Double.isNaN(real) &&
-                   imaginary == 0) {
-            return constructor.create(Double.NaN, 0);
         }
-        return constructor.create(Math.sinh(real) * Math.cos(imaginary),
-                                  Math.cosh(real) * Math.sin(imaginary));
+        // real is NaN
+        if (imaginary == 0) {
+            return constructor.create(Double.NaN, Math.copySign(0, imaginary));
+        }
+        // optionally raises the ‘‘invalid’’ floating-point exception, for nonzero y.
+        return NAN;
     }
 
     /**
@@ -1427,29 +1506,37 @@ public final class Complex implements Serializable  {
      * @return the square root of {@code this}.
      */
     public Complex sqrt() {
-        if (real == 0 &&
-            imaginary == 0) {
-            return ZERO;
-        } else if (neitherInfiniteNorZeroNorNaN(real) &&
-                   imaginary == Double.POSITIVE_INFINITY) {
-            return new Complex(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
-        } else if (real == Double.NEGATIVE_INFINITY &&
-                   neitherInfiniteNorZeroNorNaN(imaginary)) {
-            return new Complex(0, Double.NaN);
-        } else if (real == Double.NEGATIVE_INFINITY &&
-                   Double.isNaN(imaginary)) {
-            return new Complex(Double.NaN, Double.POSITIVE_INFINITY);
-        } else if (real == Double.POSITIVE_INFINITY &&
-                   Double.isNaN(imaginary)) {
-            return new Complex(Double.POSITIVE_INFINITY, Double.NaN);
+        // Special case for infinite imaginary for all real including nan
+        if (Double.isInfinite(imaginary)) {
+            return new Complex(Double.POSITIVE_INFINITY, imaginary);
         }
-
-        final double t = Math.sqrt((Math.abs(real) + abs()) / 2);
-        if (real >= 0) {
-            return new Complex(t, imaginary / (2 * t));
+        if (Double.isFinite(real)) {
+            if (Double.isFinite(imaginary)) {
+                // Handle zero
+                if (real == 0 && imaginary == 0) {
+                    return new Complex(0, imaginary);
+                }
+                final double t = Math.sqrt((Math.abs(real) + abs()) / 2);
+                if (real >= 0) {
+                    return new Complex(t, imaginary / (2 * t));
+                }
+                return new Complex(Math.abs(imaginary) / (2 * t),
+                                   Math.copySign(1d, imaginary) * t);
+            }
+            // Imaginary is nan
+            return NAN;
+        }
+        if (Double.isInfinite(real)) {
+            // imaginary is finite or NaN
+            final double part = Double.isNaN(imaginary) ? Double.NaN : 0;
+            if (real == Double.NEGATIVE_INFINITY) {
+                return new Complex(part, Math.copySign(Double.POSITIVE_INFINITY, imaginary));
+            }
+            return new Complex(Double.POSITIVE_INFINITY, Math.copySign(part, imaginary));
         }
-        return new Complex(Math.abs(imaginary) / (2 * t),
-                           Math.copySign(1d, imaginary) * t);
+        // real is NaN
+        // optionally raises the ‘‘invalid’’ floating-point exception, for finite y.
+        return NAN;
     }
 
     /**
@@ -1530,19 +1617,30 @@ public final class Complex implements Serializable  {
         // Math.cosh returns positive infinity for infinity.
         // Math.sinh returns the input infinity for infinity.
 
-        if (real == Double.POSITIVE_INFINITY &&
-            (imaginary == Double.POSITIVE_INFINITY || Double.isNaN(imaginary))) {
-            return constructor.create(1, 0);
-        } else if (Double.isNaN(real) &&
-                   imaginary == 0) {
-            return constructor.create(Double.NaN, 0);
-        }
-        final double real2 = 2 * real;
-        final double imaginary2 = 2 * imaginary;
-        final double d = Math.cosh(real2) + Math.cos(imaginary2);
+        if (Double.isFinite(real)) {
+            if (Double.isFinite(imaginary)) {
+                final double real2 = 2 * real;
+                final double imaginary2 = 2 * imaginary;
+                final double d = Math.cosh(real2) + Math.cos(imaginary2);
 
-        return constructor.create(Math.sinh(real2) / d,
-                                  Math.sin(imaginary2) / d);
+                return constructor.create(Math.sinh(real2) / d,
+                                          Math.sin(imaginary2) / d);
+            }
+            return NAN;
+        }
+        if (Double.isInfinite(real)) {
+            if (Double.isFinite(imaginary)) {
+                return constructor.create(Math.copySign(1, real), Math.copySign(0, Math.sin(2 * imaginary)));
+            }
+            // imaginary is infinite or NaN
+            return constructor.create(Math.copySign(1, real), Math.copySign(0, imaginary));
+        }
+        // real is NaN
+        if (imaginary == 0) {
+            return constructor.create(Double.NaN, Math.copySign(0, imaginary));
+        }
+        // optionally raises the ‘‘invalid’’ floating-point exception, for nonzero y.
+        return NAN;
     }
 
    /**
@@ -1647,37 +1745,19 @@ public final class Complex implements Serializable  {
     }
 
     /**
-     * Check that a value meets all the following conditions.
-     * <ul>
-     *  <li>it is not {@code NaN},</li>
-     *  <li>it is not infinite,</li>
-     *  <li>it is not zero,</li>
-     * </ul>
-     *
-     * @param d Value.
-     * @return {@code true} if {@code d} meets all the conditions and
-     * {@code false} otherwise.
-     */
-    private static boolean neitherInfiniteNorZeroNorNaN(double d) {
-        return Double.isFinite(d) && d != 0;
-    }
-
-    /**
-     * Check that a value meets all the following conditions.
+     * Check that a value is negative. It must meet all the following conditions:
      * <ul>
      *  <li>it is not {@code NaN},</li>
-     *  <li>it is not infinite,</li>
-     *  <li>it is positive signed,</li>
+     *  <li>it is negative signed,</li>
      * </ul>
      *
-     * <p>Note: This is false for negative zero.</p>
+     * <p>Note: This is true for negative zero.</p>
      *
      * @param d Value.
-     * @return {@code true} if {@code d} meets all the conditions and
-     * {@code false} otherwise.
+     * @return {@code true} if {@code d} is negative.
      */
-    private static boolean positiveSignedFinite(double d) {
-        return (Double.isFinite(d) && d > 0) || Double.doubleToLongBits(d) == 0;
+    private static boolean negative(double d) {
+        return d < 0 || equals(d, -0.0);
     }
 
     /**
@@ -1697,6 +1777,23 @@ public final class Complex implements Serializable  {
         return new Complex(imaginary, -real);
     }
 
+    /**
+     * Create the conjugate of a complex number given the real and imaginary parts.
+     * This is used in functions that implement conjugate identities. It is the functional
+     * equivalent of:
+     *
+     * <pre>
+     *   z = new Complex(real, imaginary).conjugate();
+     * </pre>
+     *
+     * @param real Real part.
+     * @param imaginary Imaginary part.
+     * @return {@code Complex} object
+     */
+    private static Complex ofCartesianConjugate(double real, double imaginary) {
+        return new Complex(real, -imaginary);
+    }
+
      /** See {@link #parse(String)}. */
     private static class ComplexParsingException extends NumberFormatException {
         /** Serializable version identifier. */
diff --git a/commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/CStandardTest.java b/commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/CStandardTest.java
index 269ab74..cddf068 100644
--- a/commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/CStandardTest.java
+++ b/commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/CStandardTest.java
@@ -17,7 +17,6 @@
 
 package org.apache.commons.numbers.complex;
 
-import org.apache.commons.numbers.core.Precision;
 import org.apache.commons.rng.UniformRandomProvider;
 import org.apache.commons.rng.simple.RandomSource;
 import org.junit.jupiter.api.Assertions;
@@ -26,6 +25,7 @@ import org.junit.jupiter.api.Test;
 import java.util.ArrayList;
 import java.util.function.BiFunction;
 import java.util.function.Predicate;
+import java.util.function.UnaryOperator;
 
 /**
  * Tests the standards defined by the C.99 standard for complex numbers
@@ -43,15 +43,19 @@ public class CStandardTest {
     private static final double piOverFour = Math.PI / 4.0;
     private static final double piOverTwo = Math.PI / 2.0;
     private static final double threePiOverFour = 3.0 * Math.PI / 4.0;
-    private static final Complex oneOne = complex(1, 1);
     private static final Complex oneZero = complex(1, 0);
     private static final Complex oneInf = complex(1, inf);
     private static final Complex oneNaN = complex(1, nan);
+    private static final Complex oneNegZero = complex(1, -0.0);
     private static final Complex zeroInf = complex(0, inf);
     private static final Complex zeroNegInf = complex(0, negInf);
+    private static final Complex zeroNegZero = complex(0, -0.0);
     private static final Complex zeroNaN = complex(0, nan);
     private static final Complex zeroPiTwo = complex(0.0, piOverTwo);
+    private static final Complex negZeroPiTwo = complex(-0.0, piOverTwo);
     private static final Complex negZeroZero = complex(-0.0, 0);
+    private static final Complex negZeroNegZero = complex(-0.0, -0.0);
+    private static final Complex negZeroNaN = complex(-0.0, nan);
     private static final Complex negI = complex(0.0, -1.0);
     private static final Complex infOne = complex(inf, 1);
     private static final Complex infZero = complex(inf, 0);
@@ -61,12 +65,16 @@ public class CStandardTest {
     private static final Complex infThreePiFour = complex(inf, threePiOverFour);
     private static final Complex infPiFour = complex(inf, piOverFour);
     private static final Complex infPi = complex(inf, Math.PI);
+    private static final Complex infNegZero = complex(inf, -0.0);
+    private static final Complex negOneZero = complex(-1, 0);
+    private static final Complex negOneNegZero = complex(-1, -0.0);
     private static final Complex negInfInf = complex(negInf, inf);
     private static final Complex negInfZero = complex(negInf, 0);
+    private static final Complex negInfNegZero = complex(negInf, -0.0);
     private static final Complex negInfOne = complex(negInf, 1);
     private static final Complex negInfNaN = complex(negInf, nan);
-    private static final Complex negInfPosInf = complex(negInf, inf);
     private static final Complex negInfPi = complex(negInf, Math.PI);
+    private static final Complex negInfPiFour = complex(negInf, piOverFour);
     private static final Complex nanInf = complex(nan, inf);
     private static final Complex nanNegInf = complex(nan, negInf);
     private static final Complex nanZero = complex(nan, 0);
@@ -83,33 +91,16 @@ public class CStandardTest {
     private static final Complex nanMax = complex(nan, max);
 
     /**
-     * Assert the two complex numbers have their real and imaginary components within
-     * the given tolerance.
-     *
-     * @param c1 the first complex
-     * @param c2 the second complex
-     * @param maxUlps {@code (maxUlps - 1)} is the number of floating point
-     * values between the real (resp. imaginary) parts of {@code x} and
-     * {@code y}.
-     */
-    public void assertComplex(Complex c1, Complex c2, int maxUlps) {
-        if (!Precision.equals(c1.getReal(), c2.getReal(), maxUlps) ||
-            !Precision.equals(c1.getImaginary(), c2.getImaginary(), maxUlps)) {
-            Assertions.fail(c1 + " != " + c2);
-        }
-    }
-
-    /**
      * Assert the two complex numbers have equivalent real and imaginary components as
      * defined by the {@code ==} operator.
      *
-     * @param c1 the first complex
-     * @param c2 the second complex
+     * @param c1 the first complex (actual)
+     * @param c2 the second complex (expected)
      */
-    public void assertComplex(Complex c1, Complex c2) {
+    private static void assertComplex(Complex c1, Complex c2) {
         // Use a delta of zero to allow comparison of -0.0 to 0.0
-        Assertions.assertEquals(c1.getReal(), c2.getReal(), 0.0, "real");
-        Assertions.assertEquals(c1.getImaginary(), c2.getImaginary(), 0.0, "imaginary");
+        Assertions.assertEquals(c2.getReal(), c1.getReal(), 0.0, "real");
+        Assertions.assertEquals(c2.getImaginary(), c1.getImaginary(), 0.0, "imaginary");
     }
 
     /**
@@ -131,6 +122,109 @@ public class CStandardTest {
     }
 
     /**
+     * Assert the operation on the complex number satisfies the conjugate equality.
+     *
+     * <pre>
+     * op(conj(z)) = conj(op(z))
+     * </pre>
+     *
+     * <p>The results must be binary equal. This includes the sign of zero.
+     *
+     * @param operation the operation
+     */
+    private static void assertConjugateEquality(UnaryOperator<Complex> operation) {
+        // Edge cases
+        final double[] parts = {Double.NEGATIVE_INFINITY, -1, -0.0, 0.0, 1,
+                                Double.POSITIVE_INFINITY, Double.NaN};
+        for (final double x : parts) {
+            for (final double y : parts) {
+                // No conjugate for imaginary NaN
+                if (!Double.isNaN(y)) {
+                    assertConjugateEquality(x, y, operation);
+                }
+            }
+        }
+        // Random numbers
+        final UniformRandomProvider rng = RandomSource.create(RandomSource.SPLIT_MIX_64);
+        for (int i = 0; i < 100; i++) {
+            final double x = next(rng);
+            final double y = next(rng);
+            assertConjugateEquality(x, y, operation);
+        }
+    }
+
+    /**
+     * Assert the operation on the complex number satisfies the conjugate equality.
+     *
+     * <pre>
+     * op(conj(z)) = conj(op(z))
+     * </pre>
+     *
+     * <p>The results must be binary equal. This includes the sign of zero.
+     *
+     * @param re the real component
+     * @param im the imaginary component
+     * @param maxUlps {@code (maxUlps - 1)} is the number of floating point
+     * values between the real (resp. imaginary) parts of {@code x} and
+     * {@code y}.
+     * @param operation the operation
+     */
+    private static void assertConjugateEquality(double re, double im,
+            UnaryOperator<Complex> operation) {
+        final Complex z = complex(re, im);
+        final Complex c1 = operation.apply(z.conj());
+        final Complex c2 = operation.apply(z).conj();
+
+        // Test for binary equality
+        if (!equals(c1.getReal(), c2.getReal()) ||
+            !equals(c1.getImaginary(), c2.getImaginary())) {
+            Assertions.fail(String.format("Conjugate equality failed (z=%s). Expected: %s but was: %s",
+                                          z, c2, c1));
+        }
+    }
+
+    /**
+     * Assert the operation on the complex number is equal to the expected value.
+     * If the imaginary part is not NaN the operation must also satisfy the conjugate equality.
+     *
+     * <pre>
+     * op(conj(z)) = conj(op(z))
+     * </pre>
+     *
+     * <p>The results must be binary equal. This includes the sign of zero.
+     *
+     * @param z the complex
+     * @param operation the operation
+     * @param expected the expected
+     */
+    private static void assertComplex(Complex z,
+            UnaryOperator<Complex> operation, Complex expected) {
+        // Test the operation
+        final Complex c1 = operation.apply(z);
+        if (!equals(c1.getReal(), expected.getReal()) ||
+            !equals(c1.getImaginary(), expected.getImaginary())) {
+            Assertions.fail(String.format("Operation failed (z=%s). Expected: %s but was: %s",
+                                          z, expected, c1));
+        }
+
+        if (!Double.isNaN(z.getImaginary())) {
+            assertConjugateEquality(z.getReal(), z.getImaginary(), operation);
+        }
+    }
+
+    /**
+     * 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);
+    }
+
+    /**
      * Utility to create a Complex.
      *
      * @param real the real
@@ -191,8 +285,8 @@ public class CStandardTest {
      */
     private static ArrayList<Complex> createCombinations(final double[] values, Predicate<Complex> condition) {
         final ArrayList<Complex> list = new ArrayList<>();
-        for (double re : values) {
-            for (double im : values) {
+        for (final double re : values) {
+            for (final double im : values) {
                 final Complex z = complex(re, im);
                 if (condition.test(z)) {
                     list.add(z);
@@ -230,18 +324,18 @@ public class CStandardTest {
 
         // "if one operand is an infinity and the other operand is a nonzero finite number or an
         // infinity, then the result of the * operator is an infinity;"
-        for (Complex z : infinites) {
-            for (Complex w : infinites) {
+        for (final Complex z : infinites) {
+            for (final Complex w : infinites) {
                 assertOperation(z, w, Complex::multiply, "*", Complex::isInfinite, "Inf");
                 assertOperation(w, z, Complex::multiply, "*", Complex::isInfinite, "Inf");
             }
-            for (Complex w : nonZeroFinites) {
+            for (final Complex w : nonZeroFinites) {
                 assertOperation(z, w, Complex::multiply, "*", Complex::isInfinite, "Inf");
                 assertOperation(w, z, Complex::multiply, "*", Complex::isInfinite, "Inf");
             }
             // C.99 refers to non-zero finites.
             // Infer that Complex multiplication of zero with infinites is not defined.
-            for (Complex w : zeroFinites) {
+            for (final Complex w : zeroFinites) {
                 assertOperation(z, w, Complex::multiply, "*", Complex::isNaN, "NaN");
                 assertOperation(w, z, Complex::multiply, "*", Complex::isNaN, "NaN");
             }
@@ -258,7 +352,7 @@ public class CStandardTest {
 
         // Check multiply with (NaN,NaN) is not corrected
         final double[] values = {0, 1, inf, negInf, nan};
-        for (Complex z : createCombinations(values, c -> true)) {
+        for (final Complex z : createCombinations(values, c -> true)) {
             assertOperation(z, NAN, Complex::multiply, "*", Complex::isNaN, "NaN");
             assertOperation(NAN, z, Complex::multiply, "*", Complex::isNaN, "NaN");
         }
@@ -285,34 +379,34 @@ public class CStandardTest {
 
         // "if the first operand is an infinity and the second operand is a finite number, then the
         // result of the / operator is an infinity;"
-        for (Complex z : infinites) {
-            for (Complex w : nonZeroFinites) {
+        for (final Complex z : infinites) {
+            for (final Complex w : nonZeroFinites) {
                 assertOperation(z, w, Complex::divide, "/", Complex::isInfinite, "Inf");
             }
-            for (Complex w : zeroFinites) {
+            for (final Complex w : zeroFinites) {
                 assertOperation(z, w, Complex::divide, "/", Complex::isInfinite, "Inf");
             }
             // Check inf/inf cannot be done
-            for (Complex w : infinites) {
+            for (final Complex w : infinites) {
                 assertOperation(z, w, Complex::divide, "/", Complex::isNaN, "NaN");
             }
         }
 
         // "if the first operand is a finite number and the second operand is an infinity, then the
         // result of the / operator is a zero;"
-        for (Complex z : finites) {
-            for (Complex w : infinites) {
+        for (final Complex z : finites) {
+            for (final Complex w : infinites) {
                 assertOperation(z, w, Complex::divide, "/", CStandardTest::isZero, "Zero");
             }
         }
 
         // "if the first operand is a nonzero finite number or an infinity and the second operand is
         // a zero, then the result of the / operator is an infinity."
-        for (Complex w : zeroFinites) {
-            for (Complex z : nonZeroFinites) {
+        for (final Complex w : zeroFinites) {
+            for (final Complex z : nonZeroFinites) {
                 assertOperation(z, w, Complex::divide, "/", Complex::isInfinite, "Inf");
             }
-            for (Complex z : infinites) {
+            for (final Complex z : infinites) {
                 assertOperation(z, w, Complex::divide, "/", Complex::isInfinite, "Inf");
             }
         }
@@ -320,20 +414,20 @@ public class CStandardTest {
         // ISO C Standard in Annex G is missing an explicit definition of how to handle NaNs.
         // The reference implementation does not correct for divide by NaN components unless
         // infinite.
-        for (Complex w : nans) {
-            for (Complex z : finites) {
+        for (final Complex w : nans) {
+            for (final Complex z : finites) {
                 assertOperation(z, w, Complex::divide, "/", c -> NAN.equals(c), "(NaN,NaN)");
             }
-            for (Complex z : infinites) {
+            for (final Complex z : infinites) {
                 assertOperation(z, w, Complex::divide, "/", c -> NAN.equals(c), "(NaN,NaN)");
             }
         }
 
         // Check (NaN,NaN) divide is not corrected for the edge case of divide by zero or infinite
-        for (Complex w : zeroFinites) {
+        for (final Complex w : zeroFinites) {
             assertOperation(NAN, w, Complex::divide, "/", Complex::isNaN, "NaN");
         }
-        for (Complex w : infinites) {
+        for (final Complex w : infinites) {
             assertOperation(NAN, w, Complex::divide, "/", Complex::isNaN, "NaN");
         }
     }
@@ -367,13 +461,13 @@ public class CStandardTest {
     }
 
     /**
-     * Create a number in the range {@code (-1,1)}.
+     * Create a number in the range {@code (-5,5)}.
      *
      * @param rng the random generator
      * @return the number
      */
     private static double next(UniformRandomProvider rng) {
-        return rng.nextDouble() * (rng.nextBoolean() ? -1 : 1);
+        return rng.nextDouble() * (rng.nextBoolean() ? -5 : 5);
     }
 
     /**
@@ -402,21 +496,23 @@ public class CStandardTest {
      */
     @Test
     public void testAcos() {
-        assertComplex(oneOne.acos().conj(), oneOne.conj().acos(), 1);
-        assertComplex(Complex.ZERO.acos(), piTwoNegZero);
-        assertComplex(negZeroZero.acos(), piTwoNegZero);
-        assertComplex(zeroNaN.acos(), piTwoNaN);
-        assertComplex(oneInf.acos(), piTwoNegInf);
-        assertComplex(oneNaN.acos(), NAN);
-        assertComplex(negInfOne.acos(), piNegInf);
-        assertComplex(infOne.acos(), zeroNegInf);
-        assertComplex(negInfPosInf.acos(), threePiFourNegInf);
-        assertComplex(infInf.acos(), piFourNegInf);
-        assertComplex(infNaN.acos(), nanInf);
-        assertComplex(negInfNaN.acos(), nanNegInf);
-        assertComplex(nanOne.acos(), NAN);
-        assertComplex(nanInf.acos(), nanNegInf);
-        assertComplex(NAN.acos(), NAN);
+        assertConjugateEquality(Complex::acos);
+        assertComplex(Complex.ZERO, Complex::acos, piTwoNegZero);
+        assertComplex(negZeroZero, Complex::acos, piTwoNegZero);
+        assertComplex(zeroNaN, Complex::acos, piTwoNaN);
+        assertComplex(oneNaN, Complex::acos, NAN);
+        assertComplex(oneInf, Complex::acos, piTwoNegInf);
+        assertComplex(negInfZero, Complex::acos, piNegInf);
+        assertComplex(negInfOne, Complex::acos, piNegInf);
+        assertComplex(infZero, Complex::acos, zeroNegInf);
+        assertComplex(infOne, Complex::acos, zeroNegInf);
+        assertComplex(negInfInf, Complex::acos, threePiFourNegInf);
+        assertComplex(infInf, Complex::acos, piFourNegInf);
+        assertComplex(infNaN, Complex::acos, nanInf);
+        assertComplex(negInfNaN, Complex::acos, nanNegInf);
+        assertComplex(nanOne, Complex::acos, NAN);
+        assertComplex(nanInf, Complex::acos, nanNegInf);
+        assertComplex(NAN, Complex::acos, NAN);
     }
 
     /**
@@ -424,51 +520,51 @@ public class CStandardTest {
      */
     @Test
     public void testAcosh() {
-        assertComplex(oneOne.acosh().conj(), oneOne.conj().acosh(), 1);
-        assertComplex(Complex.ZERO.acosh(), zeroPiTwo);
-        assertComplex(negZeroZero.acosh(), zeroPiTwo);
-        assertComplex(oneInf.acosh(), infPiTwo);
-        assertComplex(zeroNaN.acosh(), NAN);
-        assertComplex(oneNaN.acosh(), NAN);
-        assertComplex(negInfOne.acosh(), infPi);
-        assertComplex(negInfZero.acosh(), infPi);
-        assertComplex(infOne.acosh(), infZero);
-        assertComplex(infZero.acosh(), infZero);
-        assertComplex(negInfPosInf.acosh(), infThreePiFour);
-        assertComplex(infInf.acosh(), infPiFour);
-        assertComplex(infNaN.acosh(), infNaN);
-        assertComplex(negInfNaN.acosh(), infNaN);
-        assertComplex(nanOne.acosh(), NAN);
-        assertComplex(nanInf.acosh(), infNaN);
-        assertComplex(NAN.acosh(), NAN);
-        // The standard mentions positive-signed y
-        Assertions.assertThrows(AssertionError.class, () -> {
-            // Not −∞ + iy, positive signed y
-            assertComplex(complex(negInf, -0.0).acosh(), infPi);
-        });
-        Assertions.assertThrows(AssertionError.class, () -> {
-            // Not ∞ + iy, positive signed y
-            assertComplex(complex(inf, -0.0).acosh(), infPi);
-        });
+        assertConjugateEquality(Complex::acosh);
+        assertComplex(Complex.ZERO, Complex::acosh, zeroPiTwo);
+        assertComplex(negZeroZero, Complex::acosh, negZeroPiTwo);
+        assertComplex(zeroNaN, Complex::acosh, NAN);
+        assertComplex(oneNaN, Complex::acosh, NAN);
+        assertComplex(oneInf, Complex::acosh, infPiTwo);
+        assertComplex(negInfZero, Complex::acosh, infPi);
+        assertComplex(negInfOne, Complex::acosh, infPi);
+        assertComplex(infZero, Complex::acosh, infZero);
+        assertComplex(infOne, Complex::acosh, infZero);
+        assertComplex(negInfInf, Complex::acosh, infThreePiFour);
+        assertComplex(infInf, Complex::acosh, infPiFour);
+        assertComplex(infNaN, Complex::acosh, infNaN);
+        assertComplex(negInfNaN, Complex::acosh, infNaN);
+        assertComplex(nanOne, Complex::acosh, NAN);
+        assertComplex(nanInf, Complex::acosh, infNaN);
+        assertComplex(NAN, Complex::acosh, NAN);
     }
 
+    // TODO: test the 'IS ODD/ EVEN' specification
+
     /**
      * ISO C Standard G.6.2.2.
      */
     @Test
     public void testAsinh() {
-        // TODO: test for which Asinh is odd
-        assertComplex(oneOne.conj().asinh(), oneOne.asinh().conj());
-        assertComplex(Complex.ZERO.asinh(), Complex.ZERO);
-        assertComplex(oneInf.asinh(), infPiTwo);
-        assertComplex(oneNaN.asinh(), NAN);
-        assertComplex(infOne.asinh(), infZero);
-        assertComplex(infInf.asinh(), infPiFour);
-        assertComplex(infNaN.asinh(), infNaN);
-        assertComplex(nanZero.asinh(), nanZero);
-        assertComplex(nanOne.asinh(), NAN);
-        assertComplex(nanInf.asinh(), infNaN);
-        assertComplex(NAN, NAN);
+        assertConjugateEquality(Complex::asinh);
+        // AND ASINH IS ODD
+        assertComplex(Complex.ZERO, Complex::asinh, Complex.ZERO);
+        assertComplex(negZeroZero, Complex::asinh, negZeroZero);
+        assertComplex(zeroNaN, Complex::asinh, NAN);
+        assertComplex(oneNaN, Complex::asinh, NAN);
+        assertComplex(oneInf, Complex::asinh, infPiTwo);
+        assertComplex(negInfZero, Complex::asinh, negInfZero);
+        assertComplex(negInfOne, Complex::asinh, negInfZero);
+        assertComplex(infZero, Complex::asinh, infZero);
+        assertComplex(infOne, Complex::asinh, infZero);
+        assertComplex(negInfInf, Complex::asinh, negInfPiFour);
+        assertComplex(infInf, Complex::asinh, infPiFour);
+        assertComplex(infNaN, Complex::asinh, infNaN);
+        assertComplex(negInfNaN, Complex::asinh, negInfNaN);
+        assertComplex(nanZero, Complex::asinh, nanZero);
+        assertComplex(nanOne, Complex::asinh, NAN);
+        assertComplex(nanInf, Complex::asinh, infNaN);
+        assertComplex(NAN, Complex::asinh, NAN);
     }
 
     /**
@@ -476,18 +572,26 @@ public class CStandardTest {
      */
     @Test
     public void testAtanh() {
-        assertComplex(oneOne.conj().atanh(), oneOne.atanh().conj());
-        assertComplex(Complex.ZERO.atanh(), Complex.ZERO);
-        assertComplex(zeroNaN.atanh(), zeroNaN);
-        assertComplex(oneZero.atanh(), infZero);
-        assertComplex(oneInf.atanh(), zeroPiTwo);
-        assertComplex(oneNaN.atanh(), NAN);
-        assertComplex(infOne.atanh(), zeroPiTwo);
-        assertComplex(infInf.atanh(), zeroPiTwo);
-        assertComplex(infNaN.atanh(), zeroNaN);
-        assertComplex(nanOne.atanh(), NAN);
-        assertComplex(nanInf.atanh(), zeroPiTwo);
-        assertComplex(NAN.atanh(), NAN);
+        assertConjugateEquality(Complex::atanh);
+        // AND ATANH IS ODD
+        assertComplex(Complex.ZERO, Complex::atanh, Complex.ZERO);
+        assertComplex(negZeroZero, Complex::atanh, negZeroZero);
+        assertComplex(zeroNaN, Complex::atanh, zeroNaN);
+        assertComplex(oneNaN, Complex::atanh, NAN);
+        assertComplex(oneZero, Complex::atanh, infZero);
+        assertComplex(oneInf, Complex::atanh, zeroPiTwo);
+        assertComplex(negInfZero, Complex::atanh, negZeroPiTwo);
+        assertComplex(negInfOne, Complex::atanh, negZeroPiTwo);
+        assertComplex(infZero, Complex::atanh, zeroPiTwo);
+        assertComplex(infOne, Complex::atanh, zeroPiTwo);
+        assertComplex(negInfInf, Complex::atanh, negZeroPiTwo);
+        assertComplex(infInf, Complex::atanh, zeroPiTwo);
+        assertComplex(infNaN, Complex::atanh, zeroNaN);
+        assertComplex(negInfNaN, Complex::atanh, negZeroNaN);
+        assertComplex(nanZero, Complex::atanh, NAN);
+        assertComplex(nanOne, Complex::atanh, NAN);
+        assertComplex(nanInf, Complex::atanh, zeroPiTwo);
+        assertComplex(NAN, Complex::atanh, NAN);
     }
 
     /**
@@ -495,21 +599,38 @@ public class CStandardTest {
      */
     @Test
     public void testCosh() {
-        assertComplex(oneOne.cosh().conj(), oneOne.conj().cosh());
-        assertComplex(Complex.ZERO.cosh(), Complex.ONE);
-        assertComplex(zeroInf.cosh(), nanZero);
-        assertComplex(zeroNaN.cosh(), nanZero);
-        assertComplex(oneInf.cosh(), NAN);
-        assertComplex(oneNaN.cosh(), NAN);
-        assertComplex(infZero.cosh(), infZero);
-        // the next test does not appear to make sense:
-        // (inf + iy) = inf + cis(y)
-        // skipped
-        assertComplex(infInf.cosh(), infNaN);
-        assertComplex(infNaN.cosh(), infNaN);
-        assertComplex(nanZero.cosh(), nanZero);
-        assertComplex(nanOne.cosh(), NAN);
-        assertComplex(NAN.cosh(), NAN);
+        assertConjugateEquality(Complex::cosh);
+        // AND CCOSH IS EVEN
+        assertComplex(Complex.ZERO, Complex::cosh, Complex.ONE);
+        assertComplex(negZeroZero, Complex::cosh, oneNegZero);
+        assertComplex(zeroInf, Complex::cosh, nanZero);
+        assertComplex(oneInf, Complex::cosh, NAN);
+        assertComplex(zeroNaN, Complex::cosh, nanZero);
+        assertComplex(oneNaN, Complex::cosh, NAN);
+        // (inf + iy) = inf * cis(y)
+        // where cis(y) = cos(y) + i sin(y), and y is finite non-zero
+        //
+        // Note that y == 0: complex(1, 0).multiply(inf) = (inf, NaN)
+        // But the cosh is (inf, 0). This result is computed by g++ and we test it separately.
+        assertComplex(negInfNegZero, Complex::cosh, infZero);
+        assertComplex(negInfZero, Complex::cosh, infNegZero);
+        assertComplex(infNegZero, Complex::cosh, infNegZero);
+        assertComplex(infZero, Complex::cosh, infZero);
+        for (int i = 1; i < 10; i++) {
+            final double y = i * 0.5;
+            assertComplex(complex(inf, y), Complex::cosh, Complex.ofCis(y).multiply(inf));
+            assertComplex(complex(inf, -y), Complex::cosh, Complex.ofCis(-y).multiply(inf));
+            assertComplex(complex(-inf, y), Complex::cosh, Complex.ofCis(y).multiply(-inf));
+            assertComplex(complex(-inf, -y), Complex::cosh, Complex.ofCis(-y).multiply(-inf));
+        }
+        assertComplex(negInfInf, Complex::cosh, infNaN);
+        assertComplex(infInf, Complex::cosh, infNaN);
+        assertComplex(infNaN, Complex::cosh, infNaN);
+        assertComplex(negInfNaN, Complex::cosh, infNaN);
+        assertComplex(nanZero, Complex::cosh, nanZero);
+        assertComplex(nanOne, Complex::cosh, NAN);
+        assertComplex(nanInf, Complex::cosh, NAN);
+        assertComplex(NAN, Complex::cosh, NAN);
     }
 
     /**
@@ -517,19 +638,38 @@ public class CStandardTest {
      */
     @Test
     public void testSinh() {
-        assertComplex(oneOne.sinh().conj(), oneOne.conj().sinh()); // AND CSINH IS ODD
-        assertComplex(Complex.ZERO.sinh(), Complex.ZERO);
-        assertComplex(zeroInf.sinh(), zeroNaN);
-        assertComplex(zeroNaN.sinh(), zeroNaN);
-        assertComplex(oneInf.sinh(), NAN);
-        assertComplex(oneNaN.sinh(), NAN);
-        assertComplex(infZero.sinh(), infZero);
-        // skipped test similar to previous section
-        assertComplex(infInf.sinh(), infNaN);
-        assertComplex(infNaN.sinh(), infNaN);
-        assertComplex(nanZero.sinh(), nanZero);
-        assertComplex(nanOne.sinh(), NAN);
-        assertComplex(NAN.sinh(), NAN);
+        assertConjugateEquality(Complex::sinh);
+        // AND CSINH IS ODD
+        assertComplex(Complex.ZERO, Complex::sinh, Complex.ZERO);
+        assertComplex(negZeroZero, Complex::sinh, negZeroZero);
+        assertComplex(zeroInf, Complex::sinh, zeroNaN);
+        assertComplex(oneInf, Complex::sinh, NAN);
+        assertComplex(zeroNaN, Complex::sinh, zeroNaN);
+        assertComplex(oneNaN, Complex::sinh, NAN);
+        // (inf + iy) = inf * cis(y)
+        // where cis(y) = cos(y) + i sin(y), and y is finite non-zero
+        //
+        // Note that y == 0: complex(1, 0).multiply(inf) = (inf, NaN)
+        // But the sinh is (inf, 0). This result is computed by g++ and we test it separately.
+        assertComplex(negInfNegZero, Complex::sinh, negInfNegZero);
+        assertComplex(negInfZero, Complex::sinh, negInfZero);
+        assertComplex(infNegZero, Complex::sinh, infNegZero);
+        assertComplex(infZero, Complex::sinh, infZero);
+        for (int i = 1; i < 10; i++) {
+            final double y = i * 0.5;
+            assertComplex(complex(inf, y), Complex::sinh, Complex.ofCis(y).multiply(inf));
+            assertComplex(complex(inf, -y), Complex::sinh, Complex.ofCis(-y).multiply(inf));
+            assertComplex(complex(-inf, y), Complex::sinh, Complex.ofCis(y).multiply(-inf));
+            assertComplex(complex(-inf, -y), Complex::sinh, Complex.ofCis(-y).multiply(-inf));
+        }
+        assertComplex(negInfInf, Complex::sinh, infNaN);
+        assertComplex(infInf, Complex::sinh, infNaN);
+        assertComplex(infNaN, Complex::sinh, infNaN);
+        assertComplex(negInfNaN, Complex::sinh, infNaN);
+        assertComplex(nanZero, Complex::sinh, nanZero);
+        assertComplex(nanOne, Complex::sinh, NAN);
+        assertComplex(nanInf, Complex::sinh, NAN);
+        assertComplex(NAN, Complex::sinh, NAN);
     }
 
     /**
@@ -537,16 +677,36 @@ public class CStandardTest {
      */
     @Test
     public void testTanh() {
-        assertComplex(oneOne.tanh().conj(), oneOne.conj().tanh()); // AND CSINH IS ODD
-        assertComplex(Complex.ZERO.tanh(), Complex.ZERO);
-        assertComplex(oneInf.tanh(), NAN);
-        assertComplex(oneNaN.tanh(), NAN);
-        //Do Not Understand the Next Test
-        assertComplex(infInf.tanh(), oneZero);
-        assertComplex(infNaN.tanh(), oneZero);
-        assertComplex(nanZero.tanh(), nanZero);
-        assertComplex(nanOne.tanh(), NAN);
-        assertComplex(NAN.tanh(), NAN);
+        assertConjugateEquality(Complex::tanh);
+        // AND TANH IS ODD
+        assertComplex(Complex.ZERO, Complex::tanh, Complex.ZERO);
+        assertComplex(negZeroZero, Complex::tanh, negZeroZero);
+        assertComplex(zeroInf, Complex::tanh, NAN);
+        assertComplex(oneInf, Complex::tanh, NAN);
+        assertComplex(zeroNaN, Complex::tanh, NAN);
+        assertComplex(oneNaN, Complex::tanh, NAN);
+        // (inf + iy) = 1 + i0 sin(2y), and y is positive-signed finite
+        // Note: no specification for other -inf and/or negative y.
+        // g++ returns the result using (+/-1, i0 sin(2y)) where the sign of the 1 is from the inf.
+        assertComplex(negInfNegZero, Complex::tanh, negOneNegZero);
+        assertComplex(negInfZero, Complex::tanh, negOneZero);
+        assertComplex(infNegZero, Complex::tanh, oneNegZero);
+        assertComplex(infZero, Complex::tanh, oneZero);
+        for (int i = 1; i < 10; i++) {
+            final double y = i * 0.5;
+            assertComplex(complex(inf, y), Complex::tanh, complex(1.0, Math.copySign(0, Math.sin(2 * y))));
+            assertComplex(complex(inf, -y), Complex::tanh, complex(1.0, Math.copySign(0, Math.sin(2 * -y))));
+            assertComplex(complex(-inf, y), Complex::tanh, complex(-1.0, Math.copySign(0, Math.sin(2 * y))));
+            assertComplex(complex(-inf, -y), Complex::tanh, complex(-1.0, Math.copySign(0, Math.sin(2 * -y))));
+        }
+        assertComplex(negInfInf, Complex::tanh, negOneZero);
+        assertComplex(infInf, Complex::tanh, oneZero);
+        assertComplex(infNaN, Complex::tanh, oneZero);
+        assertComplex(negInfNaN, Complex::tanh, negOneZero);
+        assertComplex(nanZero, Complex::tanh, nanZero);
+        assertComplex(nanOne, Complex::tanh, NAN);
+        assertComplex(nanInf, Complex::tanh, NAN);
+        assertComplex(NAN, Complex::tanh, NAN);
     }
 
     /**
@@ -554,20 +714,37 @@ public class CStandardTest {
      */
     @Test
     public void testExp() {
-        assertComplex(oneOne.conj().exp(), oneOne.exp().conj());
-        assertComplex(Complex.ZERO.exp(), oneZero);
-        assertComplex(negZeroZero.exp(), oneZero);
-        assertComplex(oneInf.exp(), NAN);
-        assertComplex(oneNaN.exp(), NAN);
-        assertComplex(infZero.exp(), infZero);
-        // Do not understand next test
-        assertComplex(negInfInf.exp(), Complex.ZERO);
-        assertComplex(infInf.exp(), infNaN);
-        assertComplex(negInfNaN.exp(), Complex.ZERO);
-        assertComplex(infNaN.exp(), infNaN);
-        assertComplex(nanZero.exp(), nanZero);
-        assertComplex(nanOne.exp(), NAN);
-        assertComplex(NAN.exp(), NAN);
+        assertConjugateEquality(Complex::exp);
+        assertComplex(Complex.ZERO, Complex::exp, oneZero);
+        assertComplex(negZeroZero, Complex::exp, oneZero);
+        assertComplex(zeroInf, Complex::exp, NAN);
+        assertComplex(oneInf, Complex::exp, NAN);
+        assertComplex(zeroNaN, Complex::exp, NAN);
+        assertComplex(oneNaN, Complex::exp, NAN);
+        assertComplex(infNegZero, Complex::exp, infNegZero);
+        assertComplex(infZero, Complex::exp, infZero);
+        // (-inf + iy) = +0 cis(y)
+        // where cis(y) = cos(y) + i sin(y), and y is finite
+        for (int i = 0; i < 10; i++) {
+            final double y = i * 0.5;
+            assertComplex(complex(-inf, y), Complex::exp, Complex.ofCis(y).multiply(0.0));
+            assertComplex(complex(-inf, -y), Complex::exp, Complex.ofCis(-y).multiply(0.0));
+        }
+        // (inf + iy) = +inf cis(y)
+        // where cis(y) = cos(y) + i sin(y), and y is non-zero finite
+        for (int i = 1; i < 10; i++) {
+            final double y = i * 0.5;
+            assertComplex(complex(inf, y), Complex::exp, Complex.ofCis(y).multiply(inf));
+            assertComplex(complex(inf, -y), Complex::exp, Complex.ofCis(-y).multiply(inf));
+        }
+        assertComplex(negInfInf, Complex::exp, Complex.ZERO);
+        assertComplex(infInf, Complex::exp, infNaN);
+        assertComplex(negInfNaN, Complex::exp, Complex.ZERO);
+        assertComplex(infNaN, Complex::exp, infNaN);
+        assertComplex(nanZero, Complex::exp, nanZero);
+        assertComplex(nanOne, Complex::exp, NAN);
+        assertComplex(nanInf, Complex::exp, NAN);
+        assertComplex(NAN, Complex::exp, NAN);
     }
 
     /**
@@ -575,38 +752,52 @@ public class CStandardTest {
      */
     @Test
     public void testLog() {
-        assertComplex(oneOne.log().conj(), oneOne.conj().log());
-        assertComplex(negZeroZero.log(), negInfPi);
-        assertComplex(Complex.ZERO.log(), negInfZero);
-        assertComplex(oneInf.log(), infPiTwo);
-        assertComplex(oneNaN.log(), NAN);
-        assertComplex(negInfOne.log(), infPi);
-        assertComplex(infOne.log(), infZero);
-        assertComplex(infInf.log(), infPiFour);
-        assertComplex(infNaN.log(), infNaN);
-        assertComplex(nanOne.log(), NAN);
-        assertComplex(nanInf.log(), infNaN);
-        assertComplex(NAN.log(), NAN);
+        assertConjugateEquality(Complex::log);
+        assertComplex(negZeroZero, Complex::log, negInfPi);
+        assertComplex(Complex.ZERO, Complex::log, negInfZero);
+        assertComplex(zeroInf, Complex::log, infPiTwo);
+        assertComplex(oneInf, Complex::log, infPiTwo);
+        assertComplex(zeroNaN, Complex::log, NAN);
+        assertComplex(oneNaN, Complex::log, NAN);
+        assertComplex(negInfZero, Complex::log, infPi);
+        assertComplex(negInfOne, Complex::log, infPi);
+        assertComplex(infZero, Complex::log, infZero);
+        assertComplex(infOne, Complex::log, infZero);
+        assertComplex(negInfInf, Complex::log, infThreePiFour);
+        assertComplex(infInf, Complex::log, infPiFour);
+        assertComplex(negInfNaN, Complex::log, infNaN);
+        assertComplex(infNaN, Complex::log, infNaN);
+        assertComplex(nanZero, Complex::log, NAN);
+        assertComplex(nanOne, Complex::log, NAN);
+        assertComplex(nanInf, Complex::log, infNaN);
+        assertComplex(NAN, Complex::log, NAN);
     }
 
     /**
-     * Same edge cases as log() since the real component is divided by Math.log(10) whic
+     * Same edge cases as log() since the real component is divided by Math.log(10) which
      * has no effect on infinite or nan.
      */
     @Test
     public void testLog10() {
-        assertComplex(oneOne.log10().conj(), oneOne.conj().log10());
-        assertComplex(negZeroZero.log10(), negInfPi);
-        assertComplex(Complex.ZERO.log10(), negInfZero);
-        assertComplex(oneInf.log10(), infPiTwo);
-        assertComplex(oneNaN.log10(), NAN);
-        assertComplex(negInfOne.log10(), infPi);
-        assertComplex(infOne.log10(), infZero);
-        assertComplex(infInf.log10(), infPiFour);
-        assertComplex(infNaN.log10(), infNaN);
-        assertComplex(nanOne.log10(), NAN);
-        assertComplex(nanInf.log10(), infNaN);
-        assertComplex(NAN.log10(), NAN);
+        assertConjugateEquality(Complex::log10);
+        assertComplex(negZeroZero, Complex::log10, negInfPi);
+        assertComplex(Complex.ZERO, Complex::log10, negInfZero);
+        assertComplex(zeroInf, Complex::log10, infPiTwo);
+        assertComplex(oneInf, Complex::log10, infPiTwo);
+        assertComplex(zeroNaN, Complex::log10, NAN);
+        assertComplex(oneNaN, Complex::log10, NAN);
+        assertComplex(negInfZero, Complex::log10, infPi);
+        assertComplex(negInfOne, Complex::log10, infPi);
+        assertComplex(infZero, Complex::log10, infZero);
+        assertComplex(infOne, Complex::log10, infZero);
+        assertComplex(negInfInf, Complex::log10, infThreePiFour);
+        assertComplex(infInf, Complex::log10, infPiFour);
+        assertComplex(negInfNaN, Complex::log10, infNaN);
+        assertComplex(infNaN, Complex::log10, infNaN);
+        assertComplex(nanZero, Complex::log10, NAN);
+        assertComplex(nanOne, Complex::log10, NAN);
+        assertComplex(nanInf, Complex::log10, infNaN);
+        assertComplex(NAN, Complex::log10, NAN);
     }
 
     /**
@@ -614,14 +805,25 @@ public class CStandardTest {
      */
     @Test
     public void testSqrt2() {
-        assertComplex(oneOne.sqrt().conj(), oneOne.conj().sqrt());
-        assertComplex(Complex.ZERO.sqrt(), Complex.ZERO);
-        assertComplex(oneInf.sqrt(), infInf);
-        assertComplex(negInfOne.sqrt(), zeroNaN);
-        assertComplex(infOne.sqrt(), infZero);
-        assertComplex(negInfNaN.sqrt(), nanInf);
-        assertComplex(infNaN.sqrt(), infNaN);
-        assertComplex(nanOne.sqrt(), NAN);
-        assertComplex(NAN.sqrt(), NAN);
+        assertConjugateEquality(Complex::sqrt);
+        assertComplex(negZeroZero, Complex::sqrt, Complex.ZERO);
+        assertComplex(Complex.ZERO, Complex::sqrt, Complex.ZERO);
+        assertComplex(zeroNegZero, Complex::sqrt, zeroNegZero);
+        assertComplex(negZeroNegZero, Complex::sqrt, zeroNegZero);
+        assertComplex(zeroInf, Complex::sqrt, infInf);
+        assertComplex(oneInf, Complex::sqrt, infInf);
+        assertComplex(infInf, Complex::sqrt, infInf);
+        assertComplex(nanInf, Complex::sqrt, infInf);
+        assertComplex(zeroNaN, Complex::sqrt, NAN);
+        assertComplex(oneNaN, Complex::sqrt, NAN);
+        assertComplex(negInfZero, Complex::sqrt, zeroInf);
+        assertComplex(negInfOne, Complex::sqrt, zeroInf);
+        assertComplex(infZero, Complex::sqrt, infZero);
+        assertComplex(infOne, Complex::sqrt, infZero);
+        assertComplex(negInfNaN, Complex::sqrt, nanInf);
+        assertComplex(infNaN, Complex::sqrt, infNaN);
+        assertComplex(nanZero, Complex::sqrt, NAN);
+        assertComplex(nanOne, Complex::sqrt, NAN);
+        assertComplex(NAN, Complex::sqrt, NAN);
     }
 }